summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/data/big_json_plus_one.json2
-rw-r--r--spec/data/cookbooks/openldap/templates/default/helpers.erb14
-rw-r--r--spec/data/dsc_lcm.pfxbin0 -> 2597 bytes
-rw-r--r--spec/data/lwrp/providers/buck_passer.rb23
-rw-r--r--spec/data/lwrp/providers/buck_passer_2.rb20
-rw-r--r--spec/data/lwrp/providers/embedded_resource_accesses_providers_scope.rb16
-rw-r--r--spec/data/lwrp/resources/bar.rb2
-rw-r--r--spec/data/lwrp_override/resources/foo.rb5
-rw-r--r--spec/data/nested.json (renamed from spec/data/big_json.json)4
-rw-r--r--spec/data/run_context/cookbooks/include/recipes/default.rb24
-rw-r--r--spec/data/run_context/cookbooks/include/recipes/includee.rb3
-rw-r--r--spec/data/trusted_certs/opscode.pem109
-rw-r--r--spec/functional/audit/runner_spec.rb64
-rw-r--r--spec/functional/event_loggers/windows_eventlog_spec.rb14
-rw-r--r--spec/functional/file_content_management/deploy_strategies_spec.rb2
-rw-r--r--spec/functional/knife/ssh_spec.rb4
-rw-r--r--spec/functional/mixin/powershell_out_spec.rb43
-rw-r--r--spec/functional/provider/whyrun_safe_ruby_block_spec.rb2
-rw-r--r--spec/functional/rebooter_spec.rb4
-rwxr-xr-xspec/functional/resource/aixinit_service_spec.rb2
-rw-r--r--spec/functional/resource/cookbook_file_spec.rb2
-rw-r--r--spec/functional/resource/directory_spec.rb2
-rw-r--r--spec/functional/resource/dsc_resource_spec.rb93
-rw-r--r--spec/functional/resource/dsc_script_spec.rb93
-rwxr-xr-xspec/functional/resource/env_spec.rb7
-rw-r--r--spec/functional/resource/execute_spec.rb13
-rw-r--r--spec/functional/resource/file_spec.rb27
-rw-r--r--spec/functional/resource/group_spec.rb116
-rw-r--r--spec/functional/resource/link_spec.rb16
-rw-r--r--spec/functional/resource/package_spec.rb2
-rw-r--r--spec/functional/resource/powershell_script_spec.rb (renamed from spec/functional/resource/powershell_spec.rb)86
-rw-r--r--spec/functional/resource/remote_directory_spec.rb2
-rw-r--r--spec/functional/resource/remote_file_spec.rb2
-rw-r--r--spec/functional/resource/template_spec.rb2
-rw-r--r--spec/functional/resource/user/dscl_spec.rb3
-rw-r--r--spec/functional/resource/user/useradd_spec.rb68
-rw-r--r--spec/functional/resource/user/windows_spec.rb125
-rw-r--r--spec/functional/shell_spec.rb35
-rw-r--r--spec/functional/util/powershell/cmdlet_spec.rb6
-rw-r--r--spec/functional/win32/crypto_spec.rb57
-rw-r--r--spec/functional/win32/registry_helper_spec.rb12
-rw-r--r--spec/functional/win32/service_manager_spec.rb4
-rw-r--r--spec/functional/win32/sid_spec.rb55
-rw-r--r--spec/integration/client/client_spec.rb146
-rw-r--r--spec/integration/client/ipv6_spec.rb2
-rw-r--r--spec/integration/knife/chef_repo_path_spec.rb24
-rw-r--r--spec/integration/knife/common_options_spec.rb6
-rw-r--r--spec/integration/knife/deps_spec.rb22
-rw-r--r--spec/integration/knife/upload_spec.rb18
-rw-r--r--spec/integration/recipes/lwrp_inline_resources_spec.rb4
-rw-r--r--spec/integration/recipes/lwrp_spec.rb53
-rw-r--r--spec/integration/recipes/provider_choice.rb36
-rw-r--r--spec/integration/recipes/recipe_dsl_spec.rb1492
-rw-r--r--spec/integration/recipes/resource_action_spec.rb356
-rw-r--r--spec/integration/recipes/resource_converge_if_changed_spec.rb423
-rw-r--r--spec/integration/recipes/resource_load_spec.rb206
-rw-r--r--spec/integration/solo/solo_spec.rb12
-rw-r--r--spec/spec_helper.rb49
-rw-r--r--spec/support/key_helpers.rb104
-rw-r--r--spec/support/lib/chef/provider/openldap_includer.rb29
-rw-r--r--spec/support/lib/chef/resource/cat.rb1
-rw-r--r--spec/support/lib/chef/resource/one_two_three_four.rb6
-rw-r--r--spec/support/lib/chef/resource/openldap_includer.rb27
-rw-r--r--spec/support/lib/chef/resource/with_state.rb9
-rw-r--r--spec/support/lib/chef/resource/zen_follower.rb5
-rw-r--r--spec/support/lib/chef/resource/zen_master.rb7
-rw-r--r--spec/support/mock/platform.rb2
-rw-r--r--spec/support/platform_helpers.rb22
-rw-r--r--spec/support/shared/context/client.rb277
-rw-r--r--spec/support/shared/examples/client.rb53
-rw-r--r--spec/support/shared/functional/file_resource.rb4
-rw-r--r--spec/support/shared/functional/securable_resource.rb70
-rw-r--r--spec/support/shared/functional/securable_resource_with_reporting.rb18
-rw-r--r--spec/support/shared/functional/win32_service.rb3
-rw-r--r--spec/support/shared/functional/windows_script.rb2
-rw-r--r--spec/support/shared/integration/integration_helper.rb11
-rw-r--r--spec/support/shared/shared_examples.rb2
-rw-r--r--spec/support/shared/unit/api_versioning.rb77
-rw-r--r--spec/support/shared/unit/knife_shared.rb40
-rw-r--r--spec/support/shared/unit/provider/file.rb39
-rw-r--r--spec/support/shared/unit/user_and_client_shared.rb115
-rw-r--r--spec/unit/api_client_spec.rb20
-rw-r--r--spec/unit/api_client_v1_spec.rb457
-rw-r--r--spec/unit/application/client_spec.rb27
-rw-r--r--spec/unit/application/solo_spec.rb7
-rw-r--r--spec/unit/audit/audit_reporter_spec.rb74
-rw-r--r--spec/unit/audit/logger_spec.rb42
-rw-r--r--spec/unit/audit/runner_spec.rb4
-rw-r--r--spec/unit/chef_class_spec.rb110
-rw-r--r--spec/unit/chef_fs/file_pattern_spec.rb18
-rw-r--r--spec/unit/chef_fs/path_util_spec.rb108
-rw-r--r--spec/unit/client_spec.rb427
-rw-r--r--spec/unit/config_spec.rb551
-rw-r--r--spec/unit/cookbook/cookbook_version_loader_spec.rb2
-rw-r--r--spec/unit/cookbook/file_vendor_spec.rb36
-rw-r--r--spec/unit/cookbook/metadata_spec.rb41
-rw-r--r--spec/unit/cookbook/syntax_check_spec.rb3
-rw-r--r--spec/unit/cookbook_loader_spec.rb2
-rw-r--r--spec/unit/cookbook_manifest_spec.rb21
-rw-r--r--spec/unit/cookbook_site_streaming_uploader_spec.rb21
-rw-r--r--spec/unit/cookbook_spec.rb9
-rw-r--r--spec/unit/cookbook_uploader_spec.rb8
-rw-r--r--spec/unit/cookbook_version_spec.rb22
-rw-r--r--spec/unit/data_bag_item_spec.rb2
-rw-r--r--spec/unit/data_bag_spec.rb4
-rw-r--r--spec/unit/deprecation_spec.rb64
-rw-r--r--spec/unit/dsl/reboot_pending_spec.rb2
-rw-r--r--spec/unit/dsl/resources_spec.rb85
-rw-r--r--spec/unit/environment_spec.rb2
-rw-r--r--spec/unit/event_dispatch/dispatcher_spec.rb80
-rw-r--r--spec/unit/event_dispatch/dsl_spec.rb83
-rw-r--r--spec/unit/exceptions_spec.rb6
-rw-r--r--spec/unit/file_content_management/deploy/mv_windows_spec.rb60
-rw-r--r--spec/unit/formatters/doc_spec.rb52
-rw-r--r--spec/unit/formatters/error_inspectors/api_error_formatting_spec.rb77
-rw-r--r--spec/unit/formatters/error_inspectors/compile_error_inspector_spec.rb285
-rw-r--r--spec/unit/formatters/error_inspectors/resource_failure_inspector_spec.rb7
-rw-r--r--spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb10
-rw-r--r--spec/unit/http/authenticator_spec.rb78
-rw-r--r--spec/unit/http/basic_client_spec.rb65
-rw-r--r--spec/unit/http/socketless_chef_zero_client_spec.rb174
-rw-r--r--spec/unit/http_spec.rb14
-rw-r--r--spec/unit/json_compat_spec.rb30
-rw-r--r--spec/unit/key_spec.rb634
-rw-r--r--spec/unit/knife/bootstrap_spec.rb43
-rw-r--r--spec/unit/knife/client_bulk_delete_spec.rb8
-rw-r--r--spec/unit/knife/client_create_spec.rb173
-rw-r--r--spec/unit/knife/client_delete_spec.rb6
-rw-r--r--spec/unit/knife/client_edit_spec.rb15
-rw-r--r--spec/unit/knife/client_list_spec.rb2
-rw-r--r--spec/unit/knife/client_reregister_spec.rb4
-rw-r--r--spec/unit/knife/client_show_spec.rb4
-rw-r--r--spec/unit/knife/core/custom_manifest_loader_spec.rb41
-rw-r--r--spec/unit/knife/core/gem_glob_loader_spec.rb210
-rw-r--r--spec/unit/knife/core/hashed_command_loader_spec.rb93
-rw-r--r--spec/unit/knife/core/subcommand_loader_spec.rb208
-rw-r--r--spec/unit/knife/core/ui_spec.rb16
-rw-r--r--spec/unit/knife/data_bag_from_file_spec.rb2
-rw-r--r--spec/unit/knife/environment_from_file_spec.rb2
-rw-r--r--spec/unit/knife/key_create_spec.rb224
-rw-r--r--spec/unit/knife/key_delete_spec.rb135
-rw-r--r--spec/unit/knife/key_edit_spec.rb267
-rw-r--r--spec/unit/knife/key_helper.rb74
-rw-r--r--spec/unit/knife/key_list_spec.rb216
-rw-r--r--spec/unit/knife/key_show_spec.rb126
-rw-r--r--spec/unit/knife/osc_user_create_spec.rb93
-rw-r--r--spec/unit/knife/osc_user_delete_spec.rb44
-rw-r--r--spec/unit/knife/osc_user_edit_spec.rb52
-rw-r--r--spec/unit/knife/osc_user_list_spec.rb (renamed from spec/unit/provider/powershell_spec.rb)29
-rw-r--r--spec/unit/knife/osc_user_reregister_spec.rb58
-rw-r--r--spec/unit/knife/osc_user_show_spec.rb46
-rw-r--r--spec/unit/knife/ssh_spec.rb67
-rw-r--r--spec/unit/knife/ssl_check_spec.rb4
-rw-r--r--spec/unit/knife/status_spec.rb72
-rw-r--r--spec/unit/knife/user_create_spec.rb228
-rw-r--r--spec/unit/knife/user_delete_spec.rb42
-rw-r--r--spec/unit/knife/user_edit_spec.rb45
-rw-r--r--spec/unit/knife/user_list_spec.rb12
-rw-r--r--spec/unit/knife/user_reregister_spec.rb55
-rw-r--r--spec/unit/knife/user_show_spec.rb46
-rw-r--r--spec/unit/knife_spec.rb45
-rw-r--r--spec/unit/log/syslog_spec.rb53
-rw-r--r--spec/unit/log/winevt_spec.rb55
-rw-r--r--spec/unit/lwrp_spec.rb467
-rw-r--r--spec/unit/mixin/api_version_request_handling_spec.rb127
-rw-r--r--spec/unit/mixin/command_spec.rb3
-rw-r--r--spec/unit/mixin/params_validate_spec.rb142
-rw-r--r--spec/unit/mixin/path_sanity_spec.rb4
-rw-r--r--spec/unit/mixin/powershell_out_spec.rb70
-rw-r--r--spec/unit/mixin/powershell_type_coercions_spec.rb72
-rw-r--r--spec/unit/mixin/template_spec.rb4
-rw-r--r--spec/unit/mixin/unformatter_spec.rb61
-rw-r--r--spec/unit/mixin/uris_spec.rb57
-rw-r--r--spec/unit/mixin/windows_architecture_helper_spec.rb21
-rw-r--r--spec/unit/node_map_spec.rb17
-rw-r--r--spec/unit/node_spec.rb9
-rw-r--r--spec/unit/platform/query_helpers_spec.rb24
-rw-r--r--spec/unit/platform_spec.rb67
-rw-r--r--spec/unit/policy_builder/policyfile_spec.rb71
-rw-r--r--spec/unit/property/state_spec.rb506
-rw-r--r--spec/unit/property/validation_spec.rb663
-rw-r--r--spec/unit/property_spec.rb972
-rw-r--r--spec/unit/provider/deploy/revision_spec.rb2
-rw-r--r--spec/unit/provider/deploy_spec.rb4
-rw-r--r--spec/unit/provider/directory_spec.rb365
-rw-r--r--spec/unit/provider/dsc_resource_spec.rb84
-rw-r--r--spec/unit/provider/dsc_script_spec.rb4
-rw-r--r--spec/unit/provider/execute_spec.rb2
-rw-r--r--spec/unit/provider/ifconfig/debian_spec.rb10
-rw-r--r--spec/unit/provider/ifconfig_spec.rb24
-rw-r--r--spec/unit/provider/mount/aix_spec.rb3
-rw-r--r--spec/unit/provider/mount/mount_spec.rb6
-rw-r--r--spec/unit/provider/mount/windows_spec.rb14
-rw-r--r--spec/unit/provider/mount_spec.rb13
-rw-r--r--spec/unit/provider/package/aix_spec.rb44
-rw-r--r--spec/unit/provider/package/dpkg_spec.rb13
-rw-r--r--spec/unit/provider/package/freebsd/pkg_spec.rb26
-rw-r--r--spec/unit/provider/package/freebsd/pkgng_spec.rb18
-rw-r--r--spec/unit/provider/package/freebsd/port_spec.rb14
-rw-r--r--spec/unit/provider/package/ips_spec.rb44
-rw-r--r--spec/unit/provider/package/macports_spec.rb20
-rw-r--r--spec/unit/provider/package/openbsd_spec.rb104
-rw-r--r--spec/unit/provider/package/pacman_spec.rb10
-rw-r--r--spec/unit/provider/package/rpm_spec.rb459
-rw-r--r--spec/unit/provider/package/rubygems_spec.rb87
-rw-r--r--spec/unit/provider/package/smartos_spec.rb90
-rw-r--r--spec/unit/provider/package/solaris_spec.rb22
-rw-r--r--spec/unit/provider/package/windows_spec.rb129
-rw-r--r--spec/unit/provider/package/yum_spec.rb245
-rw-r--r--spec/unit/provider/package/zypper_spec.rb215
-rw-r--r--spec/unit/provider/package_spec.rb40
-rw-r--r--spec/unit/provider/powershell_script_spec.rb80
-rw-r--r--spec/unit/provider/registry_key_spec.rb12
-rw-r--r--spec/unit/provider/remote_directory_spec.rb4
-rw-r--r--spec/unit/provider/remote_file/fetcher_spec.rb21
-rw-r--r--spec/unit/provider/remote_file/local_file_spec.rb31
-rw-r--r--spec/unit/provider/remote_file/network_file_spec.rb45
-rw-r--r--spec/unit/provider/service/aix_service_spec.rb37
-rw-r--r--spec/unit/provider/service/freebsd_service_spec.rb12
-rw-r--r--spec/unit/provider/service/gentoo_service_spec.rb8
-rw-r--r--spec/unit/provider/service/macosx_spec.rb439
-rw-r--r--spec/unit/provider/service/openbsd_service_spec.rb18
-rw-r--r--spec/unit/provider/service/redhat_spec.rb96
-rw-r--r--spec/unit/provider/service/upstart_service_spec.rb18
-rw-r--r--spec/unit/provider/template/content_spec.rb41
-rw-r--r--spec/unit/provider/user/dscl_spec.rb2
-rw-r--r--spec/unit/provider/user_spec.rb6
-rw-r--r--spec/unit/provider_resolver_spec.rb747
-rw-r--r--spec/unit/provider_spec.rb24
-rw-r--r--spec/unit/recipe_spec.rb85
-rw-r--r--spec/unit/registry_helper_spec.rb16
-rw-r--r--spec/unit/resource/batch_spec.rb1
-rw-r--r--spec/unit/resource/breakpoint_spec.rb2
-rw-r--r--spec/unit/resource/cron_spec.rb2
-rw-r--r--spec/unit/resource/deploy_spec.rb35
-rw-r--r--spec/unit/resource/directory_spec.rb2
-rw-r--r--spec/unit/resource/dsc_resource_spec.rb85
-rw-r--r--spec/unit/resource/dsc_script_spec.rb8
-rw-r--r--spec/unit/resource/env_spec.rb2
-rw-r--r--spec/unit/resource/erl_call_spec.rb2
-rw-r--r--spec/unit/resource/file/verification_spec.rb38
-rw-r--r--spec/unit/resource/file_spec.rb2
-rw-r--r--spec/unit/resource/group_spec.rb2
-rw-r--r--spec/unit/resource/ifconfig_spec.rb16
-rw-r--r--spec/unit/resource/link_spec.rb17
-rw-r--r--spec/unit/resource/mdadm_spec.rb2
-rw-r--r--spec/unit/resource/mount_spec.rb2
-rw-r--r--spec/unit/resource/ohai_spec.rb2
-rw-r--r--spec/unit/resource/powershell_script_spec.rb (renamed from spec/unit/resource/powershell_spec.rb)1
-rw-r--r--spec/unit/resource/registry_key_spec.rb2
-rw-r--r--spec/unit/resource/remote_file_spec.rb15
-rw-r--r--spec/unit/resource/ruby_block_spec.rb4
-rw-r--r--spec/unit/resource/service_spec.rb8
-rw-r--r--spec/unit/resource/template_spec.rb2
-rw-r--r--spec/unit/resource/timestamped_deploy_spec.rb3
-rw-r--r--spec/unit/resource/user_spec.rb2
-rw-r--r--spec/unit/resource/windows_package_spec.rb18
-rw-r--r--spec/unit/resource/yum_package_spec.rb11
-rw-r--r--spec/unit/resource_collection_spec.rb2
-rw-r--r--spec/unit/resource_resolver_spec.rb53
-rw-r--r--spec/unit/resource_spec.rb275
-rw-r--r--spec/unit/rest_spec.rb39
-rw-r--r--spec/unit/role_spec.rb4
-rw-r--r--spec/unit/run_context/child_run_context_spec.rb133
-rw-r--r--spec/unit/run_context_spec.rb79
-rw-r--r--spec/unit/run_list/versioned_recipe_list_spec.rb163
-rw-r--r--spec/unit/run_list_spec.rb2
-rw-r--r--spec/unit/search/query_spec.rb24
-rw-r--r--spec/unit/shell_spec.rb10
-rw-r--r--spec/unit/user_spec.rb27
-rw-r--r--spec/unit/user_v1_spec.rb584
-rw-r--r--spec/unit/util/dsc/resource_store.rb76
-rw-r--r--spec/unit/util/path_helper_spec.rb233
-rw-r--r--spec/unit/util/powershell/ps_credential_spec.rb37
-rw-r--r--spec/unit/workstation_config_loader_spec.rb283
275 files changed, 17167 insertions, 4049 deletions
diff --git a/spec/data/big_json_plus_one.json b/spec/data/big_json_plus_one.json
deleted file mode 100644
index 8ea4b74644..0000000000
--- a/spec/data/big_json_plus_one.json
+++ /dev/null
@@ -1,2 +0,0 @@
-{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":"test"
-}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
diff --git a/spec/data/cookbooks/openldap/templates/default/helpers.erb b/spec/data/cookbooks/openldap/templates/default/helpers.erb
new file mode 100644
index 0000000000..b973a5287c
--- /dev/null
+++ b/spec/data/cookbooks/openldap/templates/default/helpers.erb
@@ -0,0 +1,14 @@
+<%= @cookbook_name %>
+<%= @recipe_name %>
+<%= @recipe_line_string %>
+<%= @recipe_path %>
+<%= @recipe_line %>
+<%= @template_name %>
+<%= @template_path %>
+<%= cookbook_name %>
+<%= recipe_name %>
+<%= recipe_line_string %>
+<%= recipe_path %>
+<%= recipe_line %>
+<%= template_name %>
+<%= template_path %>
diff --git a/spec/data/dsc_lcm.pfx b/spec/data/dsc_lcm.pfx
new file mode 100644
index 0000000000..3912ed3753
--- /dev/null
+++ b/spec/data/dsc_lcm.pfx
Binary files differ
diff --git a/spec/data/lwrp/providers/buck_passer.rb b/spec/data/lwrp/providers/buck_passer.rb
index c56ab94f85..2bbca07bf7 100644
--- a/spec/data/lwrp/providers/buck_passer.rb
+++ b/spec/data/lwrp/providers/buck_passer.rb
@@ -1,11 +1,28 @@
-provides 'buck_passer'
+provides :buck_passer
+
+def without_deprecation_warnings(&block)
+ old_treat_deprecation_warnings_as_errors = Chef::Config[:treat_deprecation_warnings_as_errors]
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ begin
+ block.call
+ ensure
+ Chef::Config[:treat_deprecation_warnings_as_errors] = old_treat_deprecation_warnings_as_errors
+ end
+end
+
action :pass_buck do
lwrp_foo :prepared_thumbs do
action :prepare_thumbs
- provider :lwrp_thumb_twiddler
+ # We know there will be a deprecation error here; head it off
+ without_deprecation_warnings do
+ provider :lwrp_thumb_twiddler
+ end
end
lwrp_foo :twiddled_thumbs do
action :twiddle_thumbs
- provider :lwrp_thumb_twiddler
+ # We know there will be a deprecation error here; head it off
+ without_deprecation_warnings do
+ provider :lwrp_thumb_twiddler
+ end
end
end
diff --git a/spec/data/lwrp/providers/buck_passer_2.rb b/spec/data/lwrp/providers/buck_passer_2.rb
index d34da3c378..c3bab7266f 100644
--- a/spec/data/lwrp/providers/buck_passer_2.rb
+++ b/spec/data/lwrp/providers/buck_passer_2.rb
@@ -1,10 +1,26 @@
+def without_deprecation_warnings(&block)
+ old_treat_deprecation_warnings_as_errors = Chef::Config[:treat_deprecation_warnings_as_errors]
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ begin
+ block.call
+ ensure
+ Chef::Config[:treat_deprecation_warnings_as_errors] = old_treat_deprecation_warnings_as_errors
+ end
+end
+
action :pass_buck do
lwrp_bar :prepared_eyes do
action :prepare_eyes
- provider :lwrp_paint_drying_watcher
+ # We know there will be a deprecation error here; head it off
+ without_deprecation_warnings do
+ provider :lwrp_paint_drying_watcher
+ end
end
lwrp_bar :dried_paint_watched do
action :watch_paint_dry
- provider :lwrp_paint_drying_watcher
+ # We know there will be a deprecation error here; head it off
+ without_deprecation_warnings do
+ provider :lwrp_paint_drying_watcher
+ end
end
end
diff --git a/spec/data/lwrp/providers/embedded_resource_accesses_providers_scope.rb b/spec/data/lwrp/providers/embedded_resource_accesses_providers_scope.rb
index f5841fb01c..77c1111ff5 100644
--- a/spec/data/lwrp/providers/embedded_resource_accesses_providers_scope.rb
+++ b/spec/data/lwrp/providers/embedded_resource_accesses_providers_scope.rb
@@ -3,11 +3,23 @@
# are passed properly (as demonstrated by the call to generate_new_name).
attr_reader :enclosed_resource
+def without_deprecation_warnings(&block)
+ old_treat_deprecation_warnings_as_errors = Chef::Config[:treat_deprecation_warnings_as_errors]
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ begin
+ block.call
+ ensure
+ Chef::Config[:treat_deprecation_warnings_as_errors] = old_treat_deprecation_warnings_as_errors
+ end
+end
+
action :twiddle_thumbs do
@enclosed_resource = lwrp_foo :foo do
monkey generate_new_name(new_resource.monkey){ 'the monkey' }
- action :twiddle_thumbs
- provider :lwrp_monkey_name_printer
+ # We know there will be a deprecation error here; head it off
+ without_deprecation_warnings do
+ provider :lwrp_monkey_name_printer
+ end
end
end
diff --git a/spec/data/lwrp/resources/bar.rb b/spec/data/lwrp/resources/bar.rb
index b6359648db..2ff35efd08 100644
--- a/spec/data/lwrp/resources/bar.rb
+++ b/spec/data/lwrp/resources/bar.rb
@@ -1,2 +1,2 @@
-provides "lwrp_bar" # This makes sure that we cover the case of lwrps using provides
+provides :lwrp_bar # This makes sure that we cover the case of lwrps using provides
actions :pass_buck, :prepare_eyes, :watch_paint_dry
diff --git a/spec/data/lwrp_override/resources/foo.rb b/spec/data/lwrp_override/resources/foo.rb
index 14decb9634..2fc13d32fd 100644
--- a/spec/data/lwrp_override/resources/foo.rb
+++ b/spec/data/lwrp_override/resources/foo.rb
@@ -3,3 +3,8 @@
actions :never_execute
attribute :ever, :kind_of => String
+
+class ::Chef
+ def method_created_by_override_lwrp_foo
+ end
+end
diff --git a/spec/data/big_json.json b/spec/data/nested.json
index 96c2818894..775bb21981 100644
--- a/spec/data/big_json.json
+++ b/spec/data/nested.json
@@ -1,2 +1,2 @@
-{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":"test"
-}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} \ No newline at end of file
+{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":{"key":"test"
+}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
diff --git a/spec/data/run_context/cookbooks/include/recipes/default.rb b/spec/data/run_context/cookbooks/include/recipes/default.rb
new file mode 100644
index 0000000000..8d22994252
--- /dev/null
+++ b/spec/data/run_context/cookbooks/include/recipes/default.rb
@@ -0,0 +1,24 @@
+module ::RanResources
+ def self.resources
+ @resources ||= []
+ end
+end
+class RunContextCustomResource < Chef::Resource
+ action :create do
+ ruby_block '4' do
+ block { RanResources.resources << 4 }
+ end
+ recipe_eval do
+ ruby_block '1' do
+ block { RanResources.resources << 1 }
+ end
+ include_recipe 'include::includee'
+ ruby_block '3' do
+ block { RanResources.resources << 3 }
+ end
+ end
+ ruby_block '5' do
+ block { RanResources.resources << 5 }
+ end
+ end
+end
diff --git a/spec/data/run_context/cookbooks/include/recipes/includee.rb b/spec/data/run_context/cookbooks/include/recipes/includee.rb
new file mode 100644
index 0000000000..87bb7f114e
--- /dev/null
+++ b/spec/data/run_context/cookbooks/include/recipes/includee.rb
@@ -0,0 +1,3 @@
+ruby_block '2' do
+ block { RanResources.resources << 2 }
+end
diff --git a/spec/data/trusted_certs/opscode.pem b/spec/data/trusted_certs/opscode.pem
index 37a3dd1ef2..e421a4e6e9 100644
--- a/spec/data/trusted_certs/opscode.pem
+++ b/spec/data/trusted_certs/opscode.pem
@@ -1,60 +1,57 @@
-----BEGIN CERTIFICATE-----
-MIIFrDCCBJSgAwIBAgIQB1O/fCb6cEytJ4BP3HTbCTANBgkqhkiG9w0BAQUFADBI
-MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMSIwIAYDVQQDExlE
-aWdpQ2VydCBTZWN1cmUgU2VydmVyIENBMB4XDTE0MDYxMDAwMDAwMFoXDTE1MDcw
-MTEyMDAwMFowaTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAO
-BgNVBAcTB1NlYXR0bGUxGzAZBgNVBAoTEkNoZWYgU29mdHdhcmUsIEluYzEWMBQG
-A1UEAwwNKi5vcHNjb2RlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
-ggEBAMm+rf2RcPGBlZoM+hI4BxlaHbdRg1GZJ/T46UWFOBnZFVP++TX/pyjDsvns
-xymcQywtoN/26+UIys6oWX1um9ikEokvf67LdsUeemQGFHFky8X1Ka2hVtKnxBhi
-XZfvyHDR4IyFWU9AwmhnqySzxqCtynUu8Gktx7JVfqbRFMZ186pDcSw8LoaqjTVG
-SzO7eNH2sM3doMueAHj7ITc2wUzmfa0Pdh+K8UoCn/HopU5LzycziJVPYvUkLT2m
-YCV7VWRc+kObZseHhZAbyaDk3RgPQ/eRMhytAgbruBHWDqNesNw+ZA70w856Oj2Y
-geO7JF+5V6WvkywrF8vydaoM2l8CAwEAAaOCAm8wggJrMB8GA1UdIwQYMBaAFJBx
-2zfrc8jv3NUeErY0uitaoKaSMB0GA1UdDgQWBBQK5zjZwbcmcMNLnI2h1ioAldEV
-ujCBygYDVR0RBIHCMIG/gg0qLm9wc2NvZGUuY29tghBjb3JwLm9wc2NvZGUuY29t
-ghIqLmNvcnAub3BzY29kZS5jb22CDyoubGVhcm5jaGVmLmNvbYISKi5jb3JwLmdl
-dGNoZWYuY29tgg0qLmdldGNoZWYuY29tggwqLm9wc2NvZGUudXOCC2dldGNoZWYu
-Y29tggtvcHNjb2RlLmNvbYIRYXBpLmJlcmtzaGVsZi5jb22CDWxlYXJuY2hlZi5j
-b22CCm9wc2NvZGUudXMwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUF
-BwMBBggrBgEFBQcDAjBhBgNVHR8EWjBYMCqgKKAmhiRodHRwOi8vY3JsMy5kaWdp
-Y2VydC5jb20vc3NjYS1nNi5jcmwwKqAooCaGJGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0
-LmNvbS9zc2NhLWc2LmNybDBCBgNVHSAEOzA5MDcGCWCGSAGG/WwBATAqMCgGCCsG
-AQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BTMHgGCCsGAQUFBwEB
-BGwwajAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEIGCCsG
-AQUFBzAChjZodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRTZWN1
-cmVTZXJ2ZXJDQS5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQUFAAOCAQEA
-kgBpJ2t+St7SmWfeNU9EWAhy0NuUnRIi1jnqXdapfPmS6V/M0i2wP/p+crMty78e
-+3ieuF5s0GJBLs85Hikcl3SlrrbIBJxozov1TY6zeOi6+TCsdXer6t6iQKz36zno
-+k+T6lnMCyo9+pk1PhcAWyfo1Fz4xVOBVec/71VovFkkGD2//KB+sbDs+yh21N9M
-ReO7duj16rQSctfO9R2h65djBNlgz6hXY2nlw8/x3uFfZobXOxDrTcH6Z8HIslkE
-MiTXGix6zdqJaFRCWi+prnAztWs+jEy+v95VSEHPj3xpwZ9WjsxQN0kFA2EX61v/
-kGunmyhehGjblQRt7bpyiA==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIEjzCCA3egAwIBAgIQBp4dt3/PHfupevXlyaJANzANBgkqhkiG9w0BAQUFADBh
+MIIElDCCA3ygAwIBAgIQAf2j627KdciIQ4tyS8+8kTANBgkqhkiG9w0BAQsFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
-QTAeFw0xMzAzMDgxMjAwMDBaFw0yMzAzMDgxMjAwMDBaMEgxCzAJBgNVBAYTAlVT
-MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxIjAgBgNVBAMTGURpZ2lDZXJ0IFNlY3Vy
-ZSBTZXJ2ZXIgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7V+Qh
-qdWbYDd+jqFhf4HiGsJ1ZNmRUAvkNkQkbjDSm3on+sJqrmpwCTi5IArIZRBKiKwx
-8tyS8mOhXYBjWYCSIxzm73ZKUDXJ2HE4ue3w5kKu0zgmeTD5IpTG26Y/QXiQ2N5c
-fml9+JAVOtChoL76srIZodgr0c6/a91Jq6OS/rWryME+7gEA2KlEuEJziMNh9atK
-gygK0tRJ+mqxzd9XLJTl4sqDX7e6YlwvaKXwwLn9K9HpH9gaYhW9/z2m98vv5ttl
-LyU47PvmIGZYljQZ0hXOIdMkzNkUb9j+Vcfnb7YPGoxJvinyulqagSY3JG/XSBJs
-Lln1nBi72fZo4t9FAgMBAAGjggFaMIIBVjASBgNVHRMBAf8ECDAGAQH/AgEAMA4G
-A1UdDwEB/wQEAwIBhjA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6
-Ly9vY3NwLmRpZ2ljZXJ0LmNvbTB7BgNVHR8EdDByMDegNaAzhjFodHRwOi8vY3Js
-My5kaWdpY2VydC5jb20vRGlnaUNlcnRHbG9iYWxSb290Q0EuY3JsMDegNaAzhjFo
-dHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRHbG9iYWxSb290Q0EuY3Js
-MD0GA1UdIAQ2MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5k
-aWdpY2VydC5jb20vQ1BTMB0GA1UdDgQWBBSQcds363PI79zVHhK2NLorWqCmkjAf
-BgNVHSMEGDAWgBQD3lA1VtFMu2bwo+IbG8OXsj3RVTANBgkqhkiG9w0BAQUFAAOC
-AQEAMM7RlVEArgYLoQ4CwBestn+PIPZAdXQczHixpE/q9NDEnaLegQcmH0CIUfAf
-z7dMQJnQ9DxxmHOIlywZ126Ej6QfnFog41FcsMWemWpPyGn3EP9OrRnZyVizM64M
-2ZYpnnGycGOjtpkWQh1l8/egHn3F1GUUsmKE1GxcCAzYbJMrtHZZitF//wPYwl24
-LyLWOPD2nGt9RuuZdPfrSg6ppgTre87wXGuYMVqYQOtpxAX0IKjKCDplbDgV9Vws
-slXkLGtB8L5cRspKKaBIXiDSRf8F3jSvcEuBOeLKB1d8tjHcISnivpcOd5AUUUDh
-v+PMGxmcJcqnBrJT3yOyzxIZow==
+QTAeFw0xMzAzMDgxMjAwMDBaFw0yMzAzMDgxMjAwMDBaME0xCzAJBgNVBAYTAlVT
+MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxJzAlBgNVBAMTHkRpZ2lDZXJ0IFNIQTIg
+U2VjdXJlIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ANyuWJBNwcQwFZA1W248ghX1LFy949v/cUP6ZCWA1O4Yok3wZtAKc24RmDYXZK83
+nf36QYSvx6+M/hpzTc8zl5CilodTgyu5pnVILR1WN3vaMTIa16yrBvSqXUu3R0bd
+KpPDkC55gIDvEwRqFDu1m5K+wgdlTvza/P96rtxcflUxDOg5B6TXvi/TC2rSsd9f
+/ld0Uzs1gN2ujkSYs58O09rg1/RrKatEp0tYhG2SS4HD2nOLEpdIkARFdRrdNzGX
+kujNVA075ME/OV4uuPNcfhCOhkEAjUVmR7ChZc6gqikJTvOX6+guqw9ypzAO+sf0
+/RR3w6RbKFfCs/mC/bdFWJsCAwEAAaOCAVowggFWMBIGA1UdEwEB/wQIMAYBAf8C
+AQAwDgYDVR0PAQH/BAQDAgGGMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYY
+aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMHsGA1UdHwR0MHIwN6A1oDOGMWh0dHA6
+Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RDQS5jcmwwN6A1
+oDOGMWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEdsb2JhbFJvb3RD
+QS5jcmwwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8v
+d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwHQYDVR0OBBYEFA+AYRyCMWHVLyjnjUY4tCzh
+xtniMB8GA1UdIwQYMBaAFAPeUDVW0Uy7ZvCj4hsbw5eyPdFVMA0GCSqGSIb3DQEB
+CwUAA4IBAQAjPt9L0jFCpbZ+QlwaRMxp0Wi0XUvgBCFsS+JtzLHgl4+mUwnNqipl
+5TlPHoOlblyYoiQm5vuh7ZPHLgLGTUq/sELfeNqzqPlt/yGFUzZgTHbO7Djc1lGA
+8MXW5dRNJ2Srm8c+cftIl7gzbckTB+6WohsYFfZcTEDts8Ls/3HB40f/1LkAtDdC
+2iDJ6m6K7hQGrn2iWZiIqBtvLfTyyRRfJs8sjX7tN8Cp1Tm5gr8ZDOo0rwAhaPit
+c+LJMto4JQtV05od8GiG7S5BNO98pVAdvzr508EIDObtHopYJeS4d60tbvVS3bR0
+j6tJLp07kzQoH3jOlOrHvdPJbRzeXDLz
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFDTCCA/WgAwIBAgIQBZ8R1sZP2Lbc8x554UUQ2DANBgkqhkiG9w0BAQsFADBN
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMScwJQYDVQQDEx5E
+aWdpQ2VydCBTSEEyIFNlY3VyZSBTZXJ2ZXIgQ0EwHhcNMTQxMTEwMDAwMDAwWhcN
+MTcxMTE0MTIwMDAwWjBlMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
+bjEQMA4GA1UEBxMHU2VhdHRsZTEbMBkGA1UEChMSQ2hlZiBTb2Z0d2FyZSwgSW5j
+MRIwEAYDVQQDDAkqLmNoZWYuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQC3xCIczkV10O5jTDpbd4YlPLC6kfnVoOkno2N/OOlcLQu3ulj/Lj1j4r6e
+2XthJLcFgTO+y+1/IKnnpLKDfkx1YngWEBXEBP+MrrpDUKKs053s45/bI9QBPISA
+tXgnYxMH9Glo6FWWd13TUq++OKGw1p1wazH64XK4MAf5y/lkmWXIWumNuO35ZqtB
+ME3wJISwVHzHB2CQjlDklt+Mb0APEiIFIZflgu9JNBYzLdvUtxiz15FUZQI7SsYL
+TfXOD1KBNMWqN8snG2e5gRAzB2D161DFvAZt8OiYUe+3QurNlTYVzeHv1ok6UqgM
+ZcLzg8m801rRip0D7FCGvMCU/ktdAgMBAAGjggHPMIIByzAfBgNVHSMEGDAWgBQP
+gGEcgjFh1S8o541GOLQs4cbZ4jAdBgNVHQ4EFgQUwldjw4Pb4HV+wxGZ7MSSRh+d
+pm4wHQYDVR0RBBYwFIIJKi5jaGVmLmlvggdjaGVmLmlvMA4GA1UdDwEB/wQEAwIF
+oDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwawYDVR0fBGQwYjAvoC2g
+K4YpaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3NzY2Etc2hhMi1nMy5jcmwwL6At
+oCuGKWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zc2NhLXNoYTItZzMuY3JsMEIG
+A1UdIAQ7MDkwNwYJYIZIAYb9bAEBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3
+LmRpZ2ljZXJ0LmNvbS9DUFMwfAYIKwYBBQUHAQEEcDBuMCQGCCsGAQUFBzABhhho
+dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wRgYIKwYBBQUHMAKGOmh0dHA6Ly9jYWNl
+cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFNIQTJTZWN1cmVTZXJ2ZXJDQS5jcnQw
+DAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAvcTWenNuvvrhX2omm8LQ
+zWOuu8jqpoflACwD4lOSZ4TgOe4pQGCjXq8aRBD5k+goqQrPVf9lHnelUHFQac0Q
+5WT4YUmisUbF0S4uY5OGQymM52MvUWG4ODL4gaWhFvN+HAXrDPP/9iitsjV0QOnl
+CDq7Q4/XYRYW3opu5nLLbfW6v4QvF5yzZagEACGs7Vt32p6l391UcU8f6wiB3uMD
+eioCvjpv/+2YOUNlDPCM3uBubjUhHOwO817wBxXkzdk1OSRe4jzcw/uX6wL7birt
+fbaSkpilvVX529pSzB2Lvi9xWOoGMM578dpQ0h3PwhmmvKhhCWP+pI05k3oSkYCP
+ng==
-----END CERTIFICATE-----
diff --git a/spec/functional/audit/runner_spec.rb b/spec/functional/audit/runner_spec.rb
index 494942889a..aae8fcf582 100644
--- a/spec/functional/audit/runner_spec.rb
+++ b/spec/functional/audit/runner_spec.rb
@@ -46,22 +46,12 @@ describe Chef::Audit::Runner do
RSpec::Core::Sandbox.sandboxed { ex.run }
end
- before do
- Chef::Config[:log_location] = stdout
- end
-
describe "#run" do
let(:audits) { {} }
let(:run_context) { instance_double(Chef::RunContext, :events => events, :audits => audits) }
let(:control_group_name) { "control_group_name" }
- it "Correctly runs an empty controls block" do
- in_sub_process do
- runner.run
- end
- end
-
shared_context "passing audit" do
let(:audits) do
should_pass = lambda do
@@ -84,50 +74,40 @@ describe Chef::Audit::Runner do
end
end
- context "there is a single successful control" do
- include_context "passing audit"
- it "correctly runs" do
- in_sub_process do
- runner.run
-
- expect(stdout.string).to match(/1 example, 0 failures/)
+ describe "log location is stdout" do
+ before do
+ allow(Chef::Log).to receive(:info) do |msg|
+ stdout.puts(msg)
end
end
- end
- context "there is a single failing control" do
- include_context "failing audit"
- it "correctly runs" do
+ it "Correctly runs an empty controls block" do
in_sub_process do
runner.run
-
- expect(stdout.string).to match(/Failure\/Error: expect\(2 - 1\)\.to eq\(0\)/)
- expect(stdout.string).to match(/1 example, 1 failure/)
- expect(stdout.string).to match(/# control_group_name should fail/)
end
end
- end
- describe "log location is a file" do
- let(:tmpfile) { Tempfile.new("audit") }
- before do
- Chef::Config[:log_location] = tmpfile.path
- end
+ context "there is a single successful control" do
+ include_context "passing audit"
+ it "correctly runs" do
+ in_sub_process do
+ runner.run
- after do
- tmpfile.close
- tmpfile.unlink
+ expect(stdout.string).to match(/1 example, 0 failures/)
+ end
+ end
end
- include_context "failing audit"
- it "correctly runs" do
- in_sub_process do
- runner.run
+ context "there is a single failing control" do
+ include_context "failing audit"
+ it "correctly runs" do
+ in_sub_process do
+ runner.run
- contents = tmpfile.read
- expect(contents).to match(/Failure\/Error: expect\(2 - 1\)\.to eq\(0\)/)
- expect(contents).to match(/1 example, 1 failure/)
- expect(contents).to match(/# control_group_name should fail/)
+ expect(stdout.string).to match(/Failure\/Error: expect\(2 - 1\)\.to eq\(0\)/)
+ expect(stdout.string).to match(/1 example, 1 failure/)
+ expect(stdout.string).to match(/# control_group_name should fail/)
+ end
end
end
end
diff --git a/spec/functional/event_loggers/windows_eventlog_spec.rb b/spec/functional/event_loggers/windows_eventlog_spec.rb
index 4e383dd429..0723e7b984 100644
--- a/spec/functional/event_loggers/windows_eventlog_spec.rb
+++ b/spec/functional/event_loggers/windows_eventlog_spec.rb
@@ -79,4 +79,18 @@ describe Chef::EventLoggers::WindowsEventLogger, :windows_only, :not_supported_o
end).to be_truthy
end
+ it 'writes run_failed event with event_id 10003 even when run_status is not set' do
+ logger.run_failed(mock_exception)
+
+ expect(event_log.read(flags, offset).any? do |e|
+ e.source == 'Chef' && e.event_id == 10003 &&
+ e.string_inserts[0].include?("UNKNOWN") &&
+ e.string_inserts[1].include?("UNKNOWN") &&
+ e.string_inserts[2].include?(mock_exception.class.name) &&
+ e.string_inserts[3].include?(mock_exception.message) &&
+ e.string_inserts[4].include?(mock_exception.backtrace[0]) &&
+ e.string_inserts[4].include?(mock_exception.backtrace[1])
+ end).to be_truthy
+ end
+
end
diff --git a/spec/functional/file_content_management/deploy_strategies_spec.rb b/spec/functional/file_content_management/deploy_strategies_spec.rb
index 03a6c504c1..8a995d0e41 100644
--- a/spec/functional/file_content_management/deploy_strategies_spec.rb
+++ b/spec/functional/file_content_management/deploy_strategies_spec.rb
@@ -43,7 +43,7 @@ shared_examples_for "a content deploy strategy" do
##
# UNIX Context
- let(:default_mode) { normalize_mode(0100666 - File.umask) }
+ let(:default_mode) { normalize_mode(0666 & ~File.umask) }
it "touches the file to create it (UNIX)", :unix_only do
content_deployer.create(target_file_path)
diff --git a/spec/functional/knife/ssh_spec.rb b/spec/functional/knife/ssh_spec.rb
index 5b8ad6f368..6608d05771 100644
--- a/spec/functional/knife/ssh_spec.rb
+++ b/spec/functional/knife/ssh_spec.rb
@@ -165,7 +165,7 @@ describe Chef::Knife::Ssh do
it "uses the ssh_attribute" do
@knife.run
- expect(@knife.config[:attribute]).to eq("ec2.public_hostname")
+ expect(@knife.get_ssh_attribute(Chef::Node.new)).to eq("ec2.public_hostname")
end
end
@@ -177,7 +177,7 @@ describe Chef::Knife::Ssh do
it "uses the default" do
@knife.run
- expect(@knife.config[:attribute]).to eq("fqdn")
+ expect(@knife.get_ssh_attribute(Chef::Node.new)).to eq("fqdn")
end
end
diff --git a/spec/functional/mixin/powershell_out_spec.rb b/spec/functional/mixin/powershell_out_spec.rb
new file mode 100644
index 0000000000..9cc8aeed7e
--- /dev/null
+++ b/spec/functional/mixin/powershell_out_spec.rb
@@ -0,0 +1,43 @@
+#
+# Copyright:: Copyright (c) 2014 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'chef/mixin/powershell_out'
+
+describe Chef::Mixin::PowershellOut, windows_only: true do
+ include Chef::Mixin::PowershellOut
+
+ describe "#powershell_out" do
+ it "runs a powershell command and collects stdout" do
+ expect(powershell_out("get-process").run_command.stdout).to match /Handles\s+NPM\(K\)\s+PM\(K\)\s+WS\(K\)\s+VM\(M\)\s+CPU\(s\)\s+Id\s+ProcessName/
+ end
+
+ it "does not raise exceptions when the command is invalid" do
+ powershell_out("this-is-not-a-valid-command").run_command
+ end
+ end
+
+ describe "#powershell_out!" do
+ it "runs a powershell command and collects stdout" do
+ expect(powershell_out!("get-process").run_command.stdout).to match /Handles\s+NPM\(K\)\s+PM\(K\)\s+WS\(K\)\s+VM\(M\)\s+CPU\(s\)\s+Id\s+ProcessName/
+ end
+
+ it "raises exceptions when the command is invalid" do
+ expect { powershell_out!("this-is-not-a-valid-command").run_command }.to raise_exception(Mixlib::ShellOut::ShellCommandFailed)
+ end
+ end
+end
diff --git a/spec/functional/provider/whyrun_safe_ruby_block_spec.rb b/spec/functional/provider/whyrun_safe_ruby_block_spec.rb
index b3c2333e9a..2b582feb05 100644
--- a/spec/functional/provider/whyrun_safe_ruby_block_spec.rb
+++ b/spec/functional/provider/whyrun_safe_ruby_block_spec.rb
@@ -43,7 +43,7 @@ describe Chef::Resource::WhyrunSafeRubyBlock do
end
it "updates the evil laugh, even in why-run mode" do
- new_resource.run_action(new_resource.action)
+ Array(new_resource.action).each {|action| new_resource.run_action(action) }
expect($evil_global_evil_laugh).to eq(:mwahahaha)
expect(new_resource).to be_updated
end
diff --git a/spec/functional/rebooter_spec.rb b/spec/functional/rebooter_spec.rb
index 763021607b..a0e2665de5 100644
--- a/spec/functional/rebooter_spec.rb
+++ b/spec/functional/rebooter_spec.rb
@@ -43,7 +43,7 @@ describe Chef::Platform::Rebooter do
let(:expected) do
{
- :windows => 'shutdown /r /t 5 /c "rebooter spec test"',
+ :windows => 'shutdown /r /t 300 /c "rebooter spec test"',
:linux => 'shutdown -r +5 "rebooter spec test"'
}
end
@@ -70,7 +70,7 @@ describe Chef::Platform::Rebooter do
shared_context 'test a reboot method' do
def test_rebooter_method(method_sym, is_windows, expected_reboot_str)
- allow(Chef::Platform).to receive(:windows?).and_return(is_windows)
+ allow(ChefConfig).to receive(:windows?).and_return(is_windows)
expect(rebooter).to receive(:shell_out!).once.with(expected_reboot_str)
expect(rebooter).to receive(method_sym).once.and_call_original
rebooter.send(method_sym, run_context.node)
diff --git a/spec/functional/resource/aixinit_service_spec.rb b/spec/functional/resource/aixinit_service_spec.rb
index 19b65ca2a0..3d9216158e 100755
--- a/spec/functional/resource/aixinit_service_spec.rb
+++ b/spec/functional/resource/aixinit_service_spec.rb
@@ -208,4 +208,4 @@ describe Chef::Resource::Service, :requires_root, :aix_only do
end
end
end
-end \ No newline at end of file
+end
diff --git a/spec/functional/resource/cookbook_file_spec.rb b/spec/functional/resource/cookbook_file_spec.rb
index 7797ed0041..6d4c5b4a8f 100644
--- a/spec/functional/resource/cookbook_file_spec.rb
+++ b/spec/functional/resource/cookbook_file_spec.rb
@@ -32,7 +32,7 @@ describe Chef::Resource::CookbookFile do
content
end
- let(:default_mode) { ((0100666 - File.umask) & 07777).to_s(8) }
+ let(:default_mode) { (0666 & ~File.umask).to_s(8) }
it_behaves_like "a securable resource with reporting"
diff --git a/spec/functional/resource/directory_spec.rb b/spec/functional/resource/directory_spec.rb
index 2c4025f83e..88a810964f 100644
--- a/spec/functional/resource/directory_spec.rb
+++ b/spec/functional/resource/directory_spec.rb
@@ -23,7 +23,7 @@ describe Chef::Resource::Directory do
let(:directory_base) { "directory_spec" }
- let(:default_mode) { ((0100777 - File.umask) & 07777).to_s(8) }
+ let(:default_mode) { (0777 & ~File.umask).to_s(8) }
def create_resource
events = Chef::EventDispatch::Dispatcher.new
diff --git a/spec/functional/resource/dsc_resource_spec.rb b/spec/functional/resource/dsc_resource_spec.rb
new file mode 100644
index 0000000000..6f453eeb9f
--- /dev/null
+++ b/spec/functional/resource/dsc_resource_spec.rb
@@ -0,0 +1,93 @@
+#
+# Author:: Jay Mundrawala (<jdm@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Resource::DscResource, :windows_powershell_dsc_only do
+ before(:all) do
+ @ohai = Ohai::System.new
+ @ohai.all_plugins(['platform', 'os', 'languages/powershell'])
+ end
+
+ let(:event_dispatch) { Chef::EventDispatch::Dispatcher.new }
+
+ let(:node) {
+ Chef::Node.new.tap do |n|
+ n.consume_external_attrs(@ohai.data, {})
+ end
+ }
+
+ let(:run_context) { Chef::RunContext.new(node, {}, event_dispatch) }
+
+ let(:new_resource) {
+ Chef::Resource::DscResource.new("dsc_resource_test", run_context)
+ }
+
+ context 'when Powershell does not support Invoke-DscResource'
+ context 'when Powershell supports Invoke-DscResource' do
+ before do
+ if !Chef::Platform.supports_dsc_invoke_resource?(node)
+ skip 'Requires Powershell >= 5.0.10018.0'
+ end
+ end
+ context 'with an invalid dsc resource' do
+ it 'raises an exception if the resource is not found' do
+ new_resource.resource 'thisdoesnotexist'
+ expect { new_resource.run_action(:run) }.to raise_error(
+ Chef::Exceptions::ResourceNotFound)
+ end
+ end
+
+ context 'with a valid dsc resource' do
+ let(:tmp_file_name) { Dir::Tmpname.create('tmpfile') {} }
+ let(:test_text) { "'\"!@#$%^&*)(}{][\u2713~n"}
+
+ before do
+ new_resource.resource :File
+ new_resource.property :Contents, test_text
+ new_resource.property :DestinationPath, tmp_file_name
+ end
+
+ after do
+ File.delete(tmp_file_name) if File.exists? tmp_file_name
+ end
+
+ it 'converges the resource if it is not converged' do
+ new_resource.run_action(:run)
+ contents = File.open(tmp_file_name, 'rb:bom|UTF-16LE') do |f|
+ f.read.encode('UTF-8')
+ end
+ expect(contents).to eq(test_text)
+ expect(new_resource).to be_updated
+ end
+
+ it 'does not converge the resource if it is already converged' do
+ new_resource.run_action(:run)
+ expect(new_resource).to be_updated
+ reresource =
+ Chef::Resource::DscResource.new("dsc_resource_retest", run_context)
+ reresource.resource :File
+ reresource.property :Contents, test_text
+ reresource.property :DestinationPath, tmp_file_name
+ reresource.run_action(:run)
+ expect(reresource).not_to be_updated
+ end
+ end
+
+ end
+end
diff --git a/spec/functional/resource/dsc_script_spec.rb b/spec/functional/resource/dsc_script_spec.rb
index a736949c6b..dc7704481f 100644
--- a/spec/functional/resource/dsc_script_spec.rb
+++ b/spec/functional/resource/dsc_script_spec.rb
@@ -19,6 +19,7 @@
require 'spec_helper'
require 'chef/mixin/shell_out'
require 'chef/mixin/windows_architecture_helper'
+require 'support/shared/integration/integration_helper'
describe Chef::Resource::DscScript, :windows_powershell_dsc_only do
include Chef::Mixin::WindowsArchitectureHelper
@@ -67,8 +68,7 @@ describe Chef::Resource::DscScript, :windows_powershell_dsc_only do
node = Chef::Node.new
node.automatic['platform'] = 'windows'
node.automatic['platform_version'] = '6.1'
- node.automatic['kernel'][:machine] =
- is_i386_process_on_x86_64_windows? ? :x86_64 : :i386
+ node.automatic['kernel'][:machine] = :x86_64 # Only 64-bit architecture is supported
node.automatic[:languages][:powershell][:version] = '4.0'
empty_events = Chef::EventDispatch::Dispatcher.new
Chef::RunContext.new(node, {}, empty_events)
@@ -379,4 +379,93 @@ EOH
it_behaves_like 'a dsc_script with configuration data that takes parameters'
it_behaves_like 'a dsc_script without configuration data that takes parameters'
end
+
+ context 'when using ps_credential' do
+ include IntegrationSupport
+
+ before(:each) do
+ delete_user(dsc_user)
+ ohai_reader = Ohai::System.new
+ ohai_reader.all_plugins(["platform", "os", "languages/powershell"])
+ dsc_test_run_context.node.consume_external_attrs(ohai_reader.data,{})
+ end
+
+ let(:configuration_data_path) { 'C:\\configurationdata.psd1' }
+
+ let(:self_signed_cert_path) do
+ File.join(CHEF_SPEC_DATA, 'dsc_lcm.pfx')
+ end
+
+ let(:dsc_configuration_script) do
+ <<-MYCODE
+cd c:\\
+configuration LCM
+{
+ param ($thumbprint)
+ localconfigurationmanager
+ {
+ RebootNodeIfNeeded = $false
+ ConfigurationMode = 'ApplyOnly'
+ CertificateID = $thumbprint
+ }
+}
+$cert = ls Cert:\\LocalMachine\\My\\ |
+ Where-Object {$_.Subject -match "ChefTest"} |
+ Select -first 1
+
+if($cert -eq $null) {
+ $pfxpath = '#{self_signed_cert_path}'
+ $password = ''
+ $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($pfxpath, $password, ([System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet -bor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeyset))
+ $store = New-Object System.Security.Cryptography.X509Certificates.X509Store "My", ([System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine)
+ $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
+ $store.Add($cert)
+ $store.Close()
+}
+
+lcm -thumbprint $cert.thumbprint
+set-dsclocalconfigurationmanager -path ./LCM
+$ConfigurationData = @"
+@{
+AllNodes = @(
+ @{
+ NodeName = "localhost";
+ CertificateID = '$($cert.thumbprint)';
+ };
+);
+}
+"@
+$ConfigurationData | out-file '#{configuration_data_path}' -force
+ MYCODE
+ end
+
+ let(:powershell_script_resource) do
+ Chef::Resource::PowershellScript.new('configure-lcm', dsc_test_run_context).tap do |r|
+ r.code(dsc_configuration_script)
+ r.architecture(:x86_64)
+ end
+ end
+
+ let(:dsc_script_resource) do
+ dsc_test_resource_base.tap do |r|
+ r.code <<-EOF
+User dsctestusercreate
+{
+ UserName = '#{dsc_user}'
+ Password = #{r.ps_credential('jf9a8m49jrajf4#')}
+ Ensure = "Present"
+}
+EOF
+ r.configuration_data_script(configuration_data_path)
+ end
+ end
+
+ it 'allows the use of ps_credential' do
+ expect(user_exists?(dsc_user)).to eq(false)
+ powershell_script_resource.run_action(:run)
+ expect(File).to exist(configuration_data_path)
+ dsc_script_resource.run_action(:run)
+ expect(user_exists?(dsc_user)).to eq(true)
+ end
+ end
end
diff --git a/spec/functional/resource/env_spec.rb b/spec/functional/resource/env_spec.rb
index 16caec14bf..b9dcd7b33a 100755
--- a/spec/functional/resource/env_spec.rb
+++ b/spec/functional/resource/env_spec.rb
@@ -29,14 +29,15 @@ describe Chef::Resource::Env, :windows_only do
let(:env_value_expandable) { '%SystemRoot%' }
let(:test_run_context) {
node = Chef::Node.new
+ node.default['os'] = 'windows'
node.default['platform'] = 'windows'
node.default['platform_version'] = '6.1'
empty_events = Chef::EventDispatch::Dispatcher.new
Chef::RunContext.new(node, {}, empty_events)
}
- let(:test_resource) {
- Chef::Resource::Env.new('unknown', test_run_context)
- }
+ let(:test_resource) {
+ Chef::Resource::Env.new('unknown', test_run_context)
+ }
before(:each) do
resource_lower = Chef::Resource::Env.new(chef_env_test_lower_case, test_run_context)
diff --git a/spec/functional/resource/execute_spec.rb b/spec/functional/resource/execute_spec.rb
index aaa1c772b7..692ccfb796 100644
--- a/spec/functional/resource/execute_spec.rb
+++ b/spec/functional/resource/execute_spec.rb
@@ -62,7 +62,7 @@ describe Chef::Resource::Execute do
end
describe "when parent resource sets :cwd" do
- let(:guard) { %{ruby -e 'exit 1 unless File.exists?("./big_json_plus_one.json")'} }
+ let(:guard) { %{ruby -e 'exit 1 unless File.exists?("./nested.json")'} }
it "guard inherits :cwd from resource and runs" do
resource.cwd CHEF_SPEC_DATA
@@ -137,9 +137,16 @@ describe Chef::Resource::Execute do
end
end
+ # Ensure that CommandTimeout is raised, and is caused by resource.timeout really expiring.
+ # https://github.com/chef/chef/issues/2985
+ #
+ # resource.timeout should be short, this is what we're testing
+ # resource.command ruby sleep timer should be longer than resource.timeout to give us something to timeout
+ # Timeout::timeout should be longer than resource.timeout, but less than the resource.command ruby sleep timer,
+ # so we fail if we finish on resource.command instead of resource.timeout, but raise CommandTimeout anyway (#2175).
it "times out when a timeout is set on the resource" do
- Timeout::timeout(5) do
- resource.command %{ruby -e 'sleep 600'}
+ Timeout::timeout(30) do
+ resource.command %{ruby -e 'sleep 300'}
resource.timeout 0.1
expect { resource.run_action(:run) }.to raise_error(Mixlib::ShellOut::CommandTimeout)
end
diff --git a/spec/functional/resource/file_spec.rb b/spec/functional/resource/file_spec.rb
index cf70c349fb..9e30e62111 100644
--- a/spec/functional/resource/file_spec.rb
+++ b/spec/functional/resource/file_spec.rb
@@ -64,7 +64,7 @@ describe Chef::Resource::File do
provider.current_resource
end
- let(:default_mode) { ((0100666 - File.umask) & 07777).to_s(8) }
+ let(:default_mode) { (0666 & ~File.umask).to_s(8) }
it_behaves_like "a file resource"
@@ -86,6 +86,31 @@ describe Chef::Resource::File do
end
end
+
+ describe "when using backup" do
+ before do
+ Chef::Config[:file_backup_path] = CHEF_SPEC_BACKUP_PATH
+ resource_without_content.backup(1)
+ resource_without_content.run_action(:create)
+ end
+
+ let(:backup_glob) { File.join(CHEF_SPEC_BACKUP_PATH, test_file_dir.sub(/^([A-Za-z]:)/, ""), "#{file_base}*") }
+
+ let(:path) do
+ # Use native system path
+ ChefConfig::PathHelper.canonical_path(File.join(test_file_dir, make_tmpname(file_base)), false)
+ end
+
+ it "only stores the number of requested backups" do
+ resource_without_content.content('foo')
+ resource_without_content.run_action(:create)
+ resource_without_content.content('bar')
+ resource_without_content.run_action(:create)
+ expect(Dir.glob(backup_glob).length).to eq(1)
+ end
+
+ end
+
# github issue 1842.
describe "when running action :create on a relative path" do
before do
diff --git a/spec/functional/resource/group_spec.rb b/spec/functional/resource/group_spec.rb
index 6676aa32e9..0862b8e15f 100644
--- a/spec/functional/resource/group_spec.rb
+++ b/spec/functional/resource/group_spec.rb
@@ -95,7 +95,7 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
def create_user(username)
user(username).run_action(:create) if ! windows_domain_user?(username)
- # TODO: User shouldn't exist
+ # TODO: User should exist
end
def remove_user(username)
@@ -135,45 +135,76 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
group_should_not_exist(group_name)
end
- describe "when append is not set" do
- let(:included_members) { [spec_members[1]] }
+ # dscl doesn't perform any error checking and will let you add users that don't exist.
+ describe "when no users exist", :not_supported_on_mac_osx do
+ describe "when append is not set" do
+ # excluded_members can only be used when append is set. It is ignored otherwise.
+ let(:excluded_members) { [] }
- before do
- create_user(spec_members[1])
- create_user(spec_members[0])
- add_members_to_group([spec_members[0]])
- end
-
- after do
- remove_user(spec_members[1])
- remove_user(spec_members[0])
+ it "should raise an error" do
+ expect { group_resource.run_action(tested_action) }.to raise_error()
+ end
end
- it "should remove the existing users and add the new users to the group" do
- group_resource.run_action(tested_action)
+ describe "when append is set" do
+ before do
+ group_resource.append(true)
+ end
- expect(user_exist_in_group?(spec_members[1])).to eq(true)
- expect(user_exist_in_group?(spec_members[0])).to eq(false)
+ it "should raise an error" do
+ expect { group_resource.run_action(tested_action) }.to raise_error()
+ end
end
end
- describe "when append is set" do
- before(:each) do
- group_resource.append(true)
+ describe "when the users exist" do
+ before do
+ (spec_members).each do |member|
+ create_user(member)
+ end
end
- describe "when the users exist" do
- before do
- (included_members + excluded_members).each do |member|
- create_user(member)
+ after do
+ (spec_members).each do |member|
+ remove_user(member)
+ end
+ end
+
+ describe "when append is not set" do
+ it "should set the group to to contain given members" do
+ group_resource.run_action(tested_action)
+
+ included_members.each do |member|
+ expect(user_exist_in_group?(member)).to eq(true)
+ end
+ (spec_members - included_members).each do |member|
+ expect(user_exist_in_group?(member)).to eq(false)
end
end
- after do
- (included_members + excluded_members).each do |member|
- remove_user(member)
+ describe "when group already contains some users" do
+ before do
+ add_members_to_group([included_members[0]])
+ add_members_to_group(spec_members - included_members)
+ end
+
+ it "should remove all existing users and only add the new users to the group" do
+ group_resource.run_action(tested_action)
+
+ included_members.each do |member|
+ expect(user_exist_in_group?(member)).to eq(true)
+ end
+ (spec_members - included_members).each do |member|
+ expect(user_exist_in_group?(member)).to eq(false)
+ end
end
end
+ end
+
+ describe "when append is set" do
+ before(:each) do
+ group_resource.append(true)
+ end
it "should add included members to the group" do
group_resource.run_action(tested_action)
@@ -186,9 +217,9 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
end
end
- describe "when group contains some users" do
+ describe "when group already contains some users" do
before(:each) do
- add_members_to_group([ spec_members[0], spec_members[2] ])
+ add_members_to_group([included_members[0], excluded_members[0]])
end
it "should add the included users and remove excluded users" do
@@ -203,20 +234,6 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
end
end
end
-
- describe "when the users doesn't exist" do
- describe "when append is not set" do
- it "should raise an error" do
- expect { @grp_resource.run_action(tested_action) }.to raise_error
- end
- end
-
- describe "when append is set" do
- it "should raise an error" do
- expect { @grp_resource.run_action(tested_action) }.to raise_error
- end
- end
- end
end
end
@@ -231,6 +248,12 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
group_should_exist(group_name)
end
+ after(:each) do
+ group_resource.run_action(:remove)
+ end
+
+ # TODO: The ones below might actually return ArgumentError now - but I don't have
+ # a way to verify that. Change it and delete this comment if that's the case.
describe "when updating membership" do
it "raises an error for a non well-formed domain name" do
group_resource.members [invalid_domain_user_name]
@@ -256,7 +279,7 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte
end
end
- let(:group_name) { "t-#{SecureRandom.random_number(9999)}" }
+ let(:group_name) { "group#{SecureRandom.random_number(9999)}" }
let(:included_members) { nil }
let(:excluded_members) { nil }
let(:group_resource) {
@@ -300,7 +323,7 @@ theoldmanwalkingdownthestreetalwayshadagoodsmileonhisfacetheoldmanwalking\
downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ" }
it "should not create a group" do
- expect { group_resource.run_action(:create) }.to raise_error
+ expect { group_resource.run_action(:create) }.to raise_error(ArgumentError)
group_should_not_exist(group_name)
end
end
@@ -372,6 +395,11 @@ downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ" }
let(:tested_action) { :manage }
describe "when there is no group" do
+ before(:each) do
+ group_resource.run_action(:remove)
+ group_should_not_exist(group_name)
+ end
+
it "raises an error on modify" do
expect { group_resource.run_action(:modify) }.to raise_error
end
diff --git a/spec/functional/resource/link_spec.rb b/spec/functional/resource/link_spec.rb
index d39a0c2ef6..7e903b30b4 100644
--- a/spec/functional/resource/link_spec.rb
+++ b/spec/functional/resource/link_spec.rb
@@ -348,8 +348,7 @@ describe Chef::Resource::Link do
end
it_behaves_like 'delete errors out'
end
- context 'and the link already exists and is not writeable to this user', :skip => true do
- end
+
it_behaves_like 'a securable resource without existing target' do
let(:path) { target_file }
def allowed_acl(sid, expected_perms)
@@ -360,7 +359,7 @@ describe Chef::Resource::Link do
end
def parent_inheritable_acls
dummy_file_path = File.join(test_file_dir, "dummy_file")
- dummy_file = FileUtils.touch(dummy_file_path)
+ FileUtils.touch(dummy_file_path)
dummy_desc = get_security_descriptor(dummy_file_path)
FileUtils.rm_rf(dummy_file_path)
dummy_desc
@@ -416,8 +415,6 @@ describe Chef::Resource::Link do
end
end
end
- context "when the link destination is not readable to this user", :skip => true do
- end
context "when the link destination does not exist" do
include_context 'create symbolic link succeeds'
include_context 'delete is noop'
@@ -518,8 +515,6 @@ describe Chef::Resource::Link do
end
it_behaves_like 'delete errors out'
end
- context "and the link already exists and is not writeable to this user", :skip => true do
- end
context "and specifies security attributes" do
before(:each) do
resource.owner(windows? ? 'Guest' : 'nobody')
@@ -559,10 +554,10 @@ describe Chef::Resource::Link do
end
context 'and the link does not yet exist' do
it 'links to the target file' do
+ skip('OS X/FreeBSD/AIX symlink? and readlink working on hard links to symlinks') if (os_x? or freebsd? or aix?)
resource.run_action(:create)
expect(File.exists?(target_file)).to be_truthy
# OS X gets angry about this sort of link. Bug in OS X, IMO.
- pending('OS X/FreeBSD/AIX symlink? and readlink working on hard links to symlinks') if (os_x? or freebsd? or aix?)
expect(symlink?(target_file)).to be_truthy
expect(readlink(target_file)).to eq(canonicalize(@other_target))
end
@@ -578,7 +573,7 @@ describe Chef::Resource::Link do
end
context 'and the link does not yet exist' do
it 'links to the target file' do
- pending('OS X/FreeBSD/AIX fails to create hardlinks to broken symlinks') if (os_x? or freebsd? or aix?)
+ skip('OS X/FreeBSD/AIX fails to create hardlinks to broken symlinks') if (os_x? or freebsd? or aix?)
resource.run_action(:create)
# Windows and Unix have different definitions of exists? here, and that's OK.
if windows?
@@ -593,8 +588,7 @@ describe Chef::Resource::Link do
end
end
end
- context "when the link destination is not readable to this user", :skip => true do
- end
+
context "when the link destination does not exist" do
context 'and the link does not yet exist' do
it 'create errors out' do
diff --git a/spec/functional/resource/package_spec.rb b/spec/functional/resource/package_spec.rb
index 5c17ca0107..8d37b072e8 100644
--- a/spec/functional/resource/package_spec.rb
+++ b/spec/functional/resource/package_spec.rb
@@ -386,5 +386,3 @@ describe Chef::Resource::Package, metadata do
end
end
-
-
diff --git a/spec/functional/resource/powershell_spec.rb b/spec/functional/resource/powershell_script_spec.rb
index 1b3ac844e0..be744e748b 100644
--- a/spec/functional/resource/powershell_spec.rb
+++ b/spec/functional/resource/powershell_script_spec.rb
@@ -56,13 +56,13 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
resource.run_action(:run)
end
- it "returns the -27 for a powershell script that exits with -27" do
+ it "returns the exit status 27 for a powershell script that exits with 27" do
file = Tempfile.new(['foo', '.ps1'])
begin
- file.write "exit -27"
+ file.write "exit 27"
file.close
resource.code(". \"#{file.path}\"")
- resource.returns(-27)
+ resource.returns(27)
resource.run_action(:run)
ensure
file.close
@@ -70,6 +70,30 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
end
end
+ let (:negative_exit_status) { -27 }
+ let (:unsigned_exit_status) { (-negative_exit_status ^ 65535) + 1 }
+ it "returns the exit status -27 as a signed integer or an unsigned 16-bit 2's complement value of 65509 for a powershell script that exits with -27" do
+ # Versions of PowerShell prior to 4.0 return a 16-bit unsigned value --
+ # PowerShell 4.0 and later versions return a 32-bit signed value.
+ file = Tempfile.new(['foo', '.ps1'])
+ begin
+ file.write "exit #{negative_exit_status.to_s}"
+ file.close
+ resource.code(". \"#{file.path}\"")
+
+ # PowerShell earlier than 4.0 takes negative exit codes
+ # and returns them as the underlying unsigned 16-bit
+ # 2's complement representation. We cover multiple versions
+ # of PowerShell in this example by including both the signed
+ # exit code and its converted counterpart as permitted return values.
+ # See http://support.microsoft.com/en-us/kb/2646183/zh-cn
+ resource.returns([negative_exit_status, unsigned_exit_status])
+ expect { resource.run_action(:run) }.not_to raise_error
+ ensure
+ file.close
+ file.unlink
+ end
+ end
it "returns the process exit code" do
resource.code(arbitrary_nonzero_process_exit_code_content)
@@ -98,7 +122,19 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
it "returns 1 if the last command was a cmdlet that failed and was preceded by a successfully executed non-cmdlet Windows binary" do
resource.code([windows_process_exit_code_success_content, cmdlet_exit_code_not_found_content].join(';'))
resource.returns(1)
- resource.run_action(:run)
+ expect { resource.run_action(:run) }.not_to raise_error
+ end
+
+ it "raises a Mixlib::ShellOut::ShellCommandFailed error if the script is not syntactically correct" do
+ resource.code('if({)')
+ resource.returns(0)
+ expect { resource.run_action(:run) }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
+ end
+
+ it "raises an error if the script is not syntactically correct even if returns is set to 1 which is what powershell.exe returns for syntactically invalid scripts" do
+ resource.code('if({)')
+ resource.returns(1)
+ expect { resource.run_action(:run) }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
end
# This somewhat ambiguous case, two failures of different types,
@@ -191,10 +227,25 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
expect { resource.should_skip?(:run) }.to raise_error(ArgumentError, /guard_interpreter does not support blocks/)
end
+ context "when dsc is supported", :windows_powershell_dsc_only do
+ it "can execute LCM configuration code" do
+ resource.code <<-EOF
+configuration LCM
+{
+ param ($thumbprint)
+ localconfigurationmanager
+ {
+ RebootNodeIfNeeded = $false
+ ConfigurationMode = 'ApplyOnly'
+ }
+}
+ EOF
+ expect { resource.run_action(:run) }.not_to raise_error
+ end
+ end
end
- context "when running on a 32-bit version of Windows", :windows32_only do
-
+ context "when running on a 32-bit version of Ruby", :ruby32_only do
it "executes a script with a 32-bit process if process architecture :i386 is specified" do
resource.code(processor_architecture_script_content + " | out-file -encoding ASCII #{script_output_path}")
resource.architecture(:i386)
@@ -204,15 +255,28 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
expect(source_contains_case_insensitive_content?( get_script_output, 'x86' )).to eq(true)
end
- it "raises an exception if :x86_64 process architecture is specified" do
- begin
- expect(resource.architecture(:x86_64)).to raise_error Chef::Exceptions::Win32ArchitectureIncorrect
- rescue Chef::Exceptions::Win32ArchitectureIncorrect
+ context "when running on a 64-bit version of Windows", :windows64_only do
+ it "executes a script with a 64-bit process if :x86_64 arch is specified" do
+ resource.code(processor_architecture_script_content + " | out-file -encoding ASCII #{script_output_path}")
+ resource.architecture(:x86_64)
+ resource.returns(0)
+ resource.run_action(:run)
+
+ expect(source_contains_case_insensitive_content?( get_script_output, 'AMD64' )).to eq(true)
+ end
+ end
+
+ context "when running on a 32-bit version of Windows", :windows32_only do
+ it "raises an exception if :x86_64 process architecture is specified" do
+ begin
+ expect(resource.architecture(:x86_64)).to raise_error Chef::Exceptions::Win32ArchitectureIncorrect
+ rescue Chef::Exceptions::Win32ArchitectureIncorrect
+ end
end
end
end
- context "when running on a 64-bit version of Windows", :windows64_only do
+ context "when running on a 64-bit version of Ruby", :ruby64_only do
it "executes a script with a 64-bit process if :x86_64 arch is specified" do
resource.code(processor_architecture_script_content + " | out-file -encoding ASCII #{script_output_path}")
resource.architecture(:x86_64)
diff --git a/spec/functional/resource/remote_directory_spec.rb b/spec/functional/resource/remote_directory_spec.rb
index bcafca7399..37ffbbc971 100644
--- a/spec/functional/resource/remote_directory_spec.rb
+++ b/spec/functional/resource/remote_directory_spec.rb
@@ -22,7 +22,7 @@ describe Chef::Resource::RemoteDirectory do
include_context Chef::Resource::Directory
let(:directory_base) { "directory_spec" }
- let(:default_mode) { ((0100777 - File.umask) & 07777).to_s(8) }
+ let(:default_mode) { (0777 & ~File.umask).to_s(8) }
def create_resource
cookbook_repo = File.expand_path(File.join(CHEF_SPEC_DATA, "cookbooks"))
diff --git a/spec/functional/resource/remote_file_spec.rb b/spec/functional/resource/remote_file_spec.rb
index 29091fd785..4fbcd2d24b 100644
--- a/spec/functional/resource/remote_file_spec.rb
+++ b/spec/functional/resource/remote_file_spec.rb
@@ -52,7 +52,7 @@ describe Chef::Resource::RemoteFile do
create_resource
end
- let(:default_mode) { ((0100666 - File.umask) & 07777).to_s(8) }
+ let(:default_mode) { (0666 & ~File.umask).to_s(8) }
context "when fetching files over HTTP" do
before(:all) do
diff --git a/spec/functional/resource/template_spec.rb b/spec/functional/resource/template_spec.rb
index d7b35e7450..35c5166e31 100644
--- a/spec/functional/resource/template_spec.rb
+++ b/spec/functional/resource/template_spec.rb
@@ -58,7 +58,7 @@ describe Chef::Resource::Template do
create_resource
end
- let(:default_mode) { ((0100666 - File.umask) & 07777).to_s(8) }
+ let(:default_mode) { (0666 & ~File.umask).to_s(8) }
it_behaves_like "a file resource"
diff --git a/spec/functional/resource/user/dscl_spec.rb b/spec/functional/resource/user/dscl_spec.rb
index 45b6754453..5d960daf11 100644
--- a/spec/functional/resource/user/dscl_spec.rb
+++ b/spec/functional/resource/user/dscl_spec.rb
@@ -19,9 +19,8 @@ require 'spec_helper'
require 'chef/mixin/shell_out'
metadata = {
- :unix_only => true,
+ :mac_osx_only => true,
:requires_root => true,
- :provider => {:user => Chef::Provider::User::Dscl},
:not_supported_on_mac_osx_106 => true,
}
diff --git a/spec/functional/resource/user/useradd_spec.rb b/spec/functional/resource/user/useradd_spec.rb
index 9ac88d7b60..474f6a4ecf 100644
--- a/spec/functional/resource/user/useradd_spec.rb
+++ b/spec/functional/resource/user/useradd_spec.rb
@@ -32,6 +32,7 @@ end
metadata = { :unix_only => true,
:requires_root => true,
+ :not_supported_on_mac_osx => true,
:provider => {:user => user_provider_for_platform}
}
@@ -64,8 +65,12 @@ describe Chef::Provider::User::Useradd, metadata do
end
end
- def supports_quote_in_username?
- OHAI_SYSTEM["platform_family"] == "debian"
+ def self.quote_in_username_unsupported?
+ if OHAI_SYSTEM["platform_family"] == "debian"
+ false
+ else
+ "Only debian family systems support quotes in username"
+ end
end
def password_should_be_set
@@ -76,9 +81,22 @@ describe Chef::Provider::User::Useradd, metadata do
end
end
+ def try_cleanup
+ ['/home/cheftestfoo', '/home/cheftestbar'].each do |f|
+ FileUtils.rm_rf(f) if File.exists? f
+ end
+
+ ['cf-test'].each do |u|
+ r = Chef::Resource::User.new("DELETE USER", run_context)
+ r.username('cf-test')
+ r.run_action(:remove)
+ end
+ end
+
before do
# Silence shell_out live stream
Chef::Log.level = :warn
+ try_cleanup
end
after do
@@ -94,7 +112,7 @@ describe Chef::Provider::User::Useradd, metadata do
break if status.exitstatus != 8
sleep 1
- max_retries = max_retries -1
+ max_retries = max_retries - 1
rescue UserNotFound
break
end
@@ -148,15 +166,10 @@ describe Chef::Provider::User::Useradd, metadata do
end
end
- let(:skip) { false }
-
describe "action :create" do
context "when the user does not exist beforehand" do
before do
- if reason = skip
- pending(reason)
- end
user_resource.run_action(:create)
expect(user_resource).to be_updated_by_last_action
end
@@ -172,14 +185,7 @@ describe Chef::Provider::User::Useradd, metadata do
# tabulation: '\t', etc.). Note that using a slash ('/') may break the
# default algorithm for the definition of the user's home directory.
- context "and the username contains a single quote" do
- let(:skip) do
- if supports_quote_in_username?
- false
- else
- "Platform #{OHAI_SYSTEM["platform"]} not expected to support username w/ quote"
- end
- end
+ context "and the username contains a single quote", skip: quote_in_username_unsupported? do
let(:username) { "t'bilisi" }
@@ -328,7 +334,7 @@ describe Chef::Provider::User::Useradd, metadata do
before do
if reason = skip
- pending(reason)
+ skip(reason)
end
existing_user.run_action(:create)
expect(existing_user).to be_updated_by_last_action
@@ -386,18 +392,18 @@ describe Chef::Provider::User::Useradd, metadata do
end
context "and home directory is updated" do
- let(:existing_home) { "/home/foo" }
- let(:home) { "/home/bar" }
+ let(:existing_home) { "/home/cheftestfoo" }
+ let(:home) { "/home/cheftestbar" }
it "ensures the home directory is set to the desired value" do
- expect(pw_entry.home).to eq("/home/bar")
+ expect(pw_entry.home).to eq("/home/cheftestbar")
end
context "and manage_home is enabled" do
let(:existing_manage_home) { true }
let(:manage_home) { true }
it "moves the home directory to the new location" do
- expect(File).not_to exist("/home/foo")
- expect(File).to exist("/home/bar")
+ expect(File).not_to exist("/home/cheftestfoo")
+ expect(File).to exist("/home/cheftestbar")
end
end
@@ -409,19 +415,19 @@ describe Chef::Provider::User::Useradd, metadata do
# Inconsistent behavior. See: CHEF-2205
it "created the home dir b/c of CHEF-2205 so it still exists" do
# This behavior seems contrary to expectation and non-convergent.
- expect(File).not_to exist("/home/foo")
- expect(File).to exist("/home/bar")
+ expect(File).not_to exist("/home/cheftestfoo")
+ expect(File).to exist("/home/cheftestbar")
end
elsif ohai[:platform] == "aix"
it "creates the home dir in the desired location" do
- expect(File).not_to exist("/home/foo")
- expect(File).to exist("/home/bar")
+ expect(File).not_to exist("/home/cheftestfoo")
+ expect(File).to exist("/home/cheftestbar")
end
else
it "does not create the home dir in the desired location (XXX)" do
# This behavior seems contrary to expectation and non-convergent.
- expect(File).not_to exist("/home/foo")
- expect(File).not_to exist("/home/bar")
+ expect(File).not_to exist("/home/cheftestfoo")
+ expect(File).not_to exist("/home/cheftestbar")
end
end
end
@@ -432,8 +438,8 @@ describe Chef::Provider::User::Useradd, metadata do
it "leaves the old home directory around (XXX)" do
# Would it be better to remove the old home?
- expect(File).to exist("/home/foo")
- expect(File).not_to exist("/home/bar")
+ expect(File).to exist("/home/cheftestfoo")
+ expect(File).not_to exist("/home/cheftestbar")
end
end
end
@@ -521,7 +527,7 @@ describe Chef::Provider::User::Useradd, metadata do
def aix_user_lock_status
lock_info = shell_out!("lsuser -a account_locked #{username}")
- status = /\S+\s+account_locked=(\S+)/.match(lock_info.stdout)[1]
+ /\S+\s+account_locked=(\S+)/.match(lock_info.stdout)[1]
end
def user_account_should_be_locked
diff --git a/spec/functional/resource/user/windows_spec.rb b/spec/functional/resource/user/windows_spec.rb
new file mode 100644
index 0000000000..5e68478b34
--- /dev/null
+++ b/spec/functional/resource/user/windows_spec.rb
@@ -0,0 +1,125 @@
+# Author:: Jay Mundrawala (<jdm@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'chef/mixin/shell_out'
+
+describe Chef::Provider::User::Windows, :windows_only do
+ include Chef::Mixin::ShellOut
+
+ let(:username) { 'ChefFunctionalTest' }
+ let(:password) { SecureRandom.uuid }
+
+ let(:node) do
+ n = Chef::Node.new
+ n.consume_external_attrs(OHAI_SYSTEM.data.dup, {})
+ n
+ end
+
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:new_resource) do
+ Chef::Resource::User.new(username, run_context).tap do |r|
+ r.provider(Chef::Provider::User::Windows)
+ r.password(password)
+ end
+ end
+
+ def delete_user(u)
+ shell_out("net user #{u} /delete")
+ end
+
+ before do
+ delete_user(username)
+ end
+
+ describe 'action :create' do
+ it 'creates a user when a username and password are given' do
+ new_resource.run_action(:create)
+ expect(new_resource).to be_updated_by_last_action
+ expect(shell_out("net user #{username}").exitstatus).to eq(0)
+ end
+
+ it 'reports no changes if there are no changes needed' do
+ new_resource.run_action(:create)
+ new_resource.run_action(:create)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+
+ it 'allows chaning the password' do
+ new_resource.run_action(:create)
+ new_resource.password(SecureRandom.uuid)
+ new_resource.run_action(:create)
+ expect(new_resource).to be_updated_by_last_action
+ end
+ end
+
+ describe 'action :remove' do
+ before do
+ new_resource.run_action(:create)
+ end
+
+ it 'deletes the user' do
+ new_resource.run_action(:remove)
+ expect(new_resource).to be_updated_by_last_action
+ expect(shell_out("net user #{username}").exitstatus).to eq(2)
+ end
+
+ it 'is idempotent' do
+ new_resource.run_action(:remove)
+ new_resource.run_action(:remove)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+ end
+
+ describe 'action :lock' do
+ before do
+ new_resource.run_action(:create)
+ end
+
+ it 'locks the user account' do
+ new_resource.run_action(:lock)
+ expect(new_resource).to be_updated_by_last_action
+ expect(shell_out("net user #{username}").stdout).to match(/Account active\s*No/)
+ end
+
+ it 'is idempotent' do
+ new_resource.run_action(:lock)
+ new_resource.run_action(:lock)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+ end
+
+ describe 'action :unlock' do
+ before do
+ new_resource.run_action(:create)
+ new_resource.run_action(:lock)
+ end
+
+ it 'unlocks the user account' do
+ new_resource.run_action(:unlock)
+ expect(new_resource).to be_updated_by_last_action
+ expect(shell_out("net user #{username}").stdout).to match(/Account active\s*Yes/)
+ end
+
+ it 'is idempotent' do
+ new_resource.run_action(:unlock)
+ new_resource.run_action(:unlock)
+ expect(new_resource).not_to be_updated_by_last_action
+ end
+ end
+end
diff --git a/spec/functional/shell_spec.rb b/spec/functional/shell_spec.rb
index fa9de77b0e..a753948c7f 100644
--- a/spec/functional/shell_spec.rb
+++ b/spec/functional/shell_spec.rb
@@ -29,6 +29,8 @@ describe Shell do
describe "smoke tests", :unix_only => true do
include Chef::Mixin::Command::Unix
+ TIMEOUT=300
+
def read_until(io, expected_value)
start = Time.new
buffer = ""
@@ -38,15 +40,30 @@ describe Shell do
rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EIO, EOFError
sleep 0.01
end
- if Time.new - start > 30
- STDERR.puts "did not read expected value `#{expected_value}' within 15s"
- STDERR.puts "Buffer so far: `#{buffer}'"
- break
+ if Time.new - start > TIMEOUT
+ raise "did not read expected value `#{expected_value}' within #{TIMEOUT}s\n" +
+ "Buffer so far: `#{buffer}'"
end
end
buffer
end
+ def flush_output(io)
+ start = Time.new
+ loop do
+ begin
+ io.read_nonblock(1)
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN
+ sleep 0.01
+ rescue EOFError, Errno::EIO
+ break
+ end
+ if Time.new - start > TIMEOUT
+ raise "timed out after #{TIMEOUT}s waiting for output to end"
+ end
+ end
+ end
+
def wait_or_die(pid)
start = Time.new
@@ -67,12 +84,12 @@ describe Shell do
path_to_chef_shell = File.expand_path("../../../bin/chef-shell", __FILE__)
output = ''
status = popen4("#{path_to_chef_shell} -c #{config} #{options}", :waitlast => true) do |pid, stdin, stdout, stderr|
- read_until(stdout, "chef >")
+ read_until(stdout, "chef (#{Chef::VERSION})>")
yield stdout, stdin if block_given?
stdin.write("'done'\n")
output = read_until(stdout, '=> "done"')
stdin.print("exit\n")
- read_until(stdout, "\n")
+ flush_output(stdout)
end
[output, status.exitstatus]
@@ -84,14 +101,12 @@ describe Shell do
config = File.expand_path("shef-config.rb", CHEF_SPEC_DATA)
path_to_chef_shell = File.expand_path("../../../bin/chef-shell", __FILE__)
reader, writer, pid = PTY.spawn("#{path_to_chef_shell} -c #{config} #{options}")
- read_until(reader, "chef >")
+ read_until(reader, "chef (#{Chef::VERSION})>")
yield reader, writer if block_given?
writer.puts('"done"')
output = read_until(reader, '=> "done"')
writer.print("exit\n")
- read_until(reader, "exit")
- read_until(reader, "\n")
- read_until(reader, "\n")
+ flush_output(reader)
writer.close
exitstatus = wait_or_die(pid)
diff --git a/spec/functional/util/powershell/cmdlet_spec.rb b/spec/functional/util/powershell/cmdlet_spec.rb
index b240a5ec12..201fb95af8 100644
--- a/spec/functional/util/powershell/cmdlet_spec.rb
+++ b/spec/functional/util/powershell/cmdlet_spec.rb
@@ -19,7 +19,7 @@
require 'chef/json_compat'
require File.expand_path('../../../../spec_helper', __FILE__)
-describe Chef::Util::Powershell::Cmdlet, :windows_only do
+describe Chef::Util::Powershell::Cmdlet, :windows_powershell_dsc_only do
before(:all) do
ohai = Ohai::System.new
ohai.load_plugins
@@ -88,7 +88,7 @@ describe Chef::Util::Powershell::Cmdlet, :windows_only do
context "when returning json" do
let(:cmd_output_format) { :json }
- it "returns json format data", :windows_powershell_dsc_only do
+ it "returns json format data" do
result = cmdlet_alias_requires_switch_or_argument.run({},{},'ls')
expect(result.succeeded?).to eq(true)
expect(lambda{Chef::JSONCompat.parse(result.return_value)}).not_to raise_error
@@ -97,7 +97,7 @@ describe Chef::Util::Powershell::Cmdlet, :windows_only do
context "when returning Ruby objects" do
let(:cmd_output_format) { :object }
- it "returns object format data", :windows_powershell_dsc_only do
+ it "returns object format data" do
result = simple_cmdlet.run({},{:cwd => etc_directory}, 'hosts')
expect(result.succeeded?).to eq(true)
data = result.return_value
diff --git a/spec/functional/win32/crypto_spec.rb b/spec/functional/win32/crypto_spec.rb
new file mode 100644
index 0000000000..1492995886
--- /dev/null
+++ b/spec/functional/win32/crypto_spec.rb
@@ -0,0 +1,57 @@
+#
+# Author:: Jay Mundrawala(<jdm@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+if Chef::Platform.windows?
+ require 'chef/win32/crypto'
+end
+
+describe 'Chef::ReservedNames::Win32::Crypto', :windows_only do
+ describe '#encrypt' do
+ before(:all) do
+ ohai_reader = Ohai::System.new
+ ohai_reader.all_plugins("platform")
+
+ new_node = Chef::Node.new
+ new_node.consume_external_attrs(ohai_reader.data,{})
+
+ events = Chef::EventDispatch::Dispatcher.new
+
+ @run_context = Chef::RunContext.new(new_node, {}, events)
+ end
+
+ let (:plaintext) { 'p@assword' }
+
+ it 'can be decrypted by powershell' do
+ encrypted = Chef::ReservedNames::Win32::Crypto.encrypt(plaintext)
+ resource = Chef::Resource::WindowsScript::PowershellScript.new("Powershell resource functional test", @run_context)
+ resource.code <<-EOF
+$encrypted = '#{encrypted}' | ConvertTo-SecureString
+$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($encrypted)
+$plaintext = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
+if ($plaintext -ne '#{plaintext}') {
+ Write-Error 'Got: ' $plaintext
+ exit 1
+}
+exit 0
+ EOF
+ resource.returns(0)
+ resource.run_action(:run)
+ end
+ end
+end
diff --git a/spec/functional/win32/registry_helper_spec.rb b/spec/functional/win32/registry_helper_spec.rb
index 7b070e6fe1..9ef6fd006f 100644
--- a/spec/functional/win32/registry_helper_spec.rb
+++ b/spec/functional/win32/registry_helper_spec.rb
@@ -130,6 +130,9 @@ describe 'Chef::Win32::Registry', :windows_only do
it "returns true if the value exists" do
expect(@registry.value_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals"})).to eq(true)
end
+ it "returns true if the value exists with a case mismatch on the value name" do
+ expect(@registry.value_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"petals"})).to eq(true)
+ end
it "returns false if the value does not exist" do
expect(@registry.value_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"FOOBAR"})).to eq(false)
end
@@ -145,6 +148,9 @@ describe 'Chef::Win32::Registry', :windows_only do
it "returns true if the value exists" do
expect(@registry.value_exists!("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals"})).to eq(true)
end
+ it "returns true if the value exists with a case mismatch on the value name" do
+ expect(@registry.value_exists!("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"petals"})).to eq(true)
+ end
it "throws an exception if the value does not exist" do
expect {@registry.value_exists!("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"FOOBAR"})}.to raise_error(Chef::Exceptions::Win32RegValueMissing)
end
@@ -160,6 +166,9 @@ describe 'Chef::Win32::Registry', :windows_only do
it "returns true if all the data matches" do
expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Pink", "Delicate"]})).to eq(true)
end
+ it "returns true if all the data matches with a case mismatch on the data name" do
+ expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"petals", :type=>:multi_string, :data=>["Pink", "Delicate"]})).to eq(true)
+ end
it "returns false if the name does not exist" do
expect(@registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"slateP", :type=>:multi_string, :data=>["Pink", "Delicate"]})).to eq(false)
end
@@ -181,6 +190,9 @@ describe 'Chef::Win32::Registry', :windows_only do
it "returns true if all the data matches" do
expect(@registry.data_exists!("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Pink", "Delicate"]})).to eq(true)
end
+ it "returns true if all the data matches with a case mismatch on the data name" do
+ expect(@registry.data_exists!("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"petals", :type=>:multi_string, :data=>["Pink", "Delicate"]})).to eq(true)
+ end
it "throws an exception if the name does not exist" do
expect {@registry.data_exists!("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"slateP", :type=>:multi_string, :data=>["Pink", "Delicate"]})}.to raise_error(Chef::Exceptions::Win32RegDataMissing)
end
diff --git a/spec/functional/win32/service_manager_spec.rb b/spec/functional/win32/service_manager_spec.rb
index d2474deace..a1ce36146f 100644
--- a/spec/functional/win32/service_manager_spec.rb
+++ b/spec/functional/win32/service_manager_spec.rb
@@ -33,7 +33,7 @@ end
# directories.
#
-describe "Chef::Application::WindowsServiceManager", :windows_only, :system_windows_service_gem_only do
+describe "Chef::Application::WindowsServiceManager", :windows_only, :system_windows_service_gem_only, :appveyor_only do
include_context "using Win32::Service"
@@ -43,7 +43,7 @@ describe "Chef::Application::WindowsServiceManager", :windows_only, :system_wind
end
it "throws an error with required missing options" do
- test_service.each do |key,value|
+ [:service_name, :service_display_name, :service_description, :service_file_path].each do |key|
service_def = test_service.dup
service_def.delete(key)
diff --git a/spec/functional/win32/sid_spec.rb b/spec/functional/win32/sid_spec.rb
new file mode 100644
index 0000000000..1f5f66178a
--- /dev/null
+++ b/spec/functional/win32/sid_spec.rb
@@ -0,0 +1,55 @@
+#
+# Author:: Dan Bjorge (<dbjorge@gmail.com>)
+# Copyright:: Copyright (c) 2015 Dan Bjorge
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+if Chef::Platform.windows?
+ require 'chef/win32/security'
+end
+
+describe 'Chef::ReservedNames::Win32::SID', :windows_only do
+ if Chef::Platform.windows?
+ SID ||= Chef::ReservedNames::Win32::Security::SID
+ end
+
+ it 'should resolve default_security_object_group as a sane user group', :windows_not_domain_joined_only do
+ # Domain accounts: domain-specific Domain Users SID
+ # Microsoft Accounts: SID.current_user
+ # Else: SID.None
+ expect(SID.default_security_object_group).to eq(SID.None).or eq(SID.current_user)
+ end
+
+ context 'running as an elevated administrator user' do
+ it 'should resolve default_security_object_owner as the Administrators group' do
+ expect(SID.default_security_object_owner).to eq(SID.Administrators)
+ end
+ end
+
+ context 'running as a non-elevated administrator user' do
+ it 'should resolve default_security_object_owner as the current user' do
+ skip 'requires user support in mixlib-shellout, see security_spec.rb'
+ expect(SID.default_security_object_owner).to eq(SID.Administrators)
+ end
+ end
+
+ context 'running as a non-elevated, non-administrator user' do
+ it 'should resolve default_security_object_owner as the current user' do
+ skip 'requires user support in mixlib-shellout, see security_spec.rb'
+ expect(SID.default_security_object_owner).to eq(SID.current_user)
+ end
+ end
+end
diff --git a/spec/integration/client/client_spec.rb b/spec/integration/client/client_spec.rb
index 8afb52e29a..1a030c130b 100644
--- a/spec/integration/client/client_spec.rb
+++ b/spec/integration/client/client_spec.rb
@@ -3,34 +3,35 @@ require 'chef/mixin/shell_out'
require 'tiny_server'
require 'tmpdir'
-def recipes_filename
- File.join(CHEF_SPEC_DATA, 'recipes.tgz')
-end
-def start_tiny_server(server_opts={})
- recipes_size = File::Stat.new(recipes_filename).size
- @server = TinyServer::Manager.new(server_opts)
- @server.start
- @api = TinyServer::API.instance
- @api.clear
- #
- # trivial endpoints
- #
- # just a normal file
- # (expected_content should be uncompressed)
- @api.get("/recipes.tgz", 200) {
- File.open(recipes_filename, "rb") do |f|
- f.read
- end
- }
-end
+describe "chef-client" do
-def stop_tiny_server
- @server.stop
- @server = @api = nil
-end
+ def recipes_filename
+ File.join(CHEF_SPEC_DATA, 'recipes.tgz')
+ end
+
+ def start_tiny_server(server_opts={})
+ @server = TinyServer::Manager.new(server_opts)
+ @server.start
+ @api = TinyServer::API.instance
+ @api.clear
+ #
+ # trivial endpoints
+ #
+ # just a normal file
+ # (expected_content should be uncompressed)
+ @api.get("/recipes.tgz", 200) {
+ File.open(recipes_filename, "rb") do |f|
+ f.read
+ end
+ }
+ end
+
+ def stop_tiny_server
+ @server.stop
+ @server = @api = nil
+ end
-describe "chef-client" do
include IntegrationSupport
include Chef::Mixin::ShellOut
@@ -45,7 +46,9 @@ describe "chef-client" do
# machine that has omnibus chef installed. In that case we need to ensure
# we're running `chef-client` from the source tree and not the external one.
# cf. CHEF-4914
- let(:chef_client) { "ruby '#{chef_dir}/chef-client'" }
+ let(:chef_client) { "ruby '#{chef_dir}/chef-client' --minimal-ohai" }
+
+ let(:critical_env_vars) { %w(PATH RUBYOPT BUNDLE_GEMFILE GEM_PATH).map {|o| "#{o}=#{ENV[o]}"} .join(' ') }
when_the_repository "has a cookbook with a no-op recipe" do
before { file 'cookbooks/x/recipes/default.rb', '' }
@@ -56,7 +59,38 @@ local_mode true
cookbook_path "#{path_to('cookbooks')}"
EOM
- result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default'", :cwd => chef_dir)
+ shell_out!("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default'", :cwd => chef_dir)
+ end
+
+ it "should complete successfully with no other environment variables", :skip => (Chef::Platform.windows?) do
+ file 'config/client.rb', <<EOM
+local_mode true
+cookbook_path "#{path_to('cookbooks')}"
+EOM
+
+ begin
+ result = shell_out("env -i #{critical_env_vars} #{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default'", :cwd => chef_dir)
+ result.error!
+ rescue
+ Chef::Log.info "Bare invocation will have the following load-path."
+ Chef::Log.info shell_out!("env -i #{critical_env_vars} ruby -e 'puts $:'").stdout
+ raise
+ end
+ end
+
+ it "should complete successfully with --no-listen" do
+ file 'config/client.rb', <<EOM
+local_mode true
+cookbook_path "#{path_to('cookbooks')}"
+EOM
+
+ result = shell_out("#{chef_client} --no-listen -c \"#{path_to('config/client.rb')}\" -o 'x::default'", :cwd => chef_dir)
+ result.error!
+ end
+
+ it "should be able to node.save with bad utf8 characters in the node data" do
+ file "cookbooks/x/attributes/default.rb", 'default["badutf8"] = "Elan Ruusam\xE4e"'
+ result = shell_out("#{chef_client} -z -r 'x::default' --disable-config", :cwd => path_to(''))
result.error!
end
@@ -269,6 +303,59 @@ EOM
end
+ when_the_repository "has a cookbook that generates deprecation warnings" do
+ before do
+ file 'cookbooks/x/recipes/default.rb', <<-EOM
+ class ::MyResource < Chef::Resource
+ use_automatic_resource_name
+ property :x, default: []
+ property :y, default: {}
+ end
+
+ my_resource 'blah' do
+ 1.upto(10) do
+ x nil
+ end
+ x nil
+ end
+ EOM
+ end
+
+ def match_indices(regex, str)
+ result = []
+ pos = 0
+ while match = regex.match(str, pos)
+ result << match.begin(0)
+ pos = match.end(0) + 1
+ end
+ result
+ end
+
+ it "should output each deprecation warning only once, at the end of the run" do
+ file 'config/client.rb', <<EOM
+local_mode true
+cookbook_path "#{path_to('cookbooks')}"
+# Mimick what happens when you are on the console
+formatters << :doc
+log_level :warn
+EOM
+
+ ENV.delete('CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS')
+
+ result = shell_out!("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default'", :cwd => chef_dir)
+ expect(result.error?).to be_falsey
+
+ # Search to the end of the client run in the output
+ run_complete = result.stdout.index("Running handlers complete")
+ expect(run_complete).to be >= 0
+
+ # Make sure there is exactly one result for each, and that it occurs *after* the complete message.
+ expect(match_indices(/MyResource.x has an array or hash default/, result.stdout)).to match([ be > run_complete ])
+ expect(match_indices(/MyResource.y has an array or hash default/, result.stdout)).to match([ be > run_complete ])
+ expect(match_indices(/nil currently does not overwrite the value of/, result.stdout)).to match([ be > run_complete ])
+ end
+ end
+
when_the_repository "has a cookbook with only an audit recipe" do
before do
@@ -308,7 +395,8 @@ end
end
end
- context "when using recipe-url" do
+ # Fails on appveyor, but works locally on windows and on windows hosts in Ci.
+ context "when using recipe-url", :skip_appveyor do
before(:all) do
start_tiny_server
end
@@ -329,7 +417,7 @@ EOM
it 'should fail when passed --recipe-url and not passed -z' do
result = shell_out("#{chef_client} --recipe-url=http://localhost:9000/recipes.tgz", :cwd => tmp_dir)
- expect(result.exitstatus).to eq(1)
+ expect(result.exitstatus).not_to eq(0)
end
end
end
diff --git a/spec/integration/client/ipv6_spec.rb b/spec/integration/client/ipv6_spec.rb
index 76dd1938f7..8be873edf4 100644
--- a/spec/integration/client/ipv6_spec.rb
+++ b/spec/integration/client/ipv6_spec.rb
@@ -76,7 +76,7 @@ END_CLIENT_RB
let(:chef_dir) { File.join(File.dirname(__FILE__), "..", "..", "..", "bin") }
- let(:chef_client_cmd) { %Q[ruby '#{chef_dir}/chef-client' -c "#{path_to('config/client.rb')}" -lwarn] }
+ let(:chef_client_cmd) { %Q[ruby '#{chef_dir}/chef-client' --minimal-ohai -c "#{path_to('config/client.rb')}" -lwarn] }
after do
FileUtils.rm_rf(cache_path)
diff --git a/spec/integration/knife/chef_repo_path_spec.rb b/spec/integration/knife/chef_repo_path_spec.rb
index 874b33901f..908657e5f7 100644
--- a/spec/integration/knife/chef_repo_path_spec.rb
+++ b/spec/integration/knife/chef_repo_path_spec.rb
@@ -24,6 +24,8 @@ describe 'chef_repo_path tests', :workstation do
include IntegrationSupport
include KnifeSupport
+ let(:error_rel_path_outside_repo) { /^ERROR: Attempt to use relative path '' when current directory is outside the repository path/ }
+
# TODO alternate repo_path / *_path
context 'alternate *_path' do
when_the_repository 'has clients and clients2, cookbooks and cookbooks2, etc.' do
@@ -109,14 +111,14 @@ EOM
context 'when cwd is at the top level' do
before { cwd '.' }
it 'knife list --local -Rfp fails' do
- knife('list --local -Rfp').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
+ knife('list --local -Rfp').should_fail(error_rel_path_outside_repo)
end
end
context 'when cwd is inside the data_bags directory' do
before { cwd 'data_bags' }
it 'knife list --local -Rfp fails' do
- knife('list --local -Rfp').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
+ knife('list --local -Rfp').should_fail(error_rel_path_outside_repo)
end
end
@@ -192,14 +194,14 @@ EOM
context 'when cwd is inside the data_bags directory' do
before { cwd 'data_bags' }
it 'knife list --local -Rfp fails' do
- knife('list --local -Rfp').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
+ knife('list --local -Rfp').should_fail(error_rel_path_outside_repo)
end
end
context 'when cwd is inside chef_repo2' do
before { cwd 'chef_repo2' }
it 'knife list -Rfp fails' do
- knife('list --local -Rfp').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
+ knife('list --local -Rfp').should_fail(error_rel_path_outside_repo)
end
end
@@ -225,14 +227,14 @@ EOM
context 'when cwd is at the top level' do
before { cwd '.' }
it 'knife list --local -Rfp fails' do
- knife('list --local -Rfp').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
+ knife('list --local -Rfp').should_fail(error_rel_path_outside_repo)
end
end
context 'when cwd is inside the data_bags directory' do
before { cwd 'data_bags' }
it 'knife list --local -Rfp fails' do
- knife('list --local -Rfp').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
+ knife('list --local -Rfp').should_fail(error_rel_path_outside_repo)
end
end
@@ -445,7 +447,7 @@ EOM
context 'when cwd is at the top level' do
before { cwd '.' }
it 'knife list --local -Rfp fails' do
- knife('list --local -Rfp').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
+ knife('list --local -Rfp').should_fail(error_rel_path_outside_repo)
end
end
@@ -621,14 +623,14 @@ EOM
context 'when cwd is at the top level' do
before { cwd '.' }
it 'knife list --local -Rfp fails' do
- knife('list --local -Rfp').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
+ knife('list --local -Rfp').should_fail(error_rel_path_outside_repo)
end
end
context 'when cwd is inside the data_bags directory' do
before { cwd 'data_bags' }
it 'knife list --local -Rfp fails' do
- knife('list --local -Rfp').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
+ knife('list --local -Rfp').should_fail(error_rel_path_outside_repo)
end
end
@@ -782,7 +784,7 @@ EOM
context 'when cwd is at the top level' do
before { cwd '.' }
it 'knife list --local -Rfp fails' do
- knife('list --local -Rfp').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
+ knife('list --local -Rfp').should_fail(error_rel_path_outside_repo)
end
end
@@ -823,7 +825,7 @@ EOM
context 'when cwd is inside chef_repo2/data_bags' do
before { cwd 'chef_repo2/data_bags' }
it 'knife list --local -Rfp fails' do
- knife('list --local -Rfp').should_fail("ERROR: Attempt to use relative path '' when current directory is outside the repository path\n")
+ knife('list --local -Rfp').should_fail(error_rel_path_outside_repo)
end
end
end
diff --git a/spec/integration/knife/common_options_spec.rb b/spec/integration/knife/common_options_spec.rb
index ec76738b6f..b2e2e3fc2a 100644
--- a/spec/integration/knife/common_options_spec.rb
+++ b/spec/integration/knife/common_options_spec.rb
@@ -39,7 +39,7 @@ describe 'knife common options', :workstation do
it 'knife raw /nodes/x should retrieve the node' do
knife('raw /nodes/x').should_succeed( /"name": "x"/ )
- expect(Chef::Config.chef_server_url).to eq('http://localhost:9999')
+ expect(Chef::Config.chef_server_url).to eq('chefzero://localhost:9999')
end
end
@@ -101,7 +101,7 @@ EOM
it 'knife raw -z --chef-zero-port=9999 /nodes/x retrieves the node' do
knife('raw -z --chef-zero-port=9999 /nodes/x').should_succeed( /"name": "x"/ )
- expect(Chef::Config.chef_server_url).to eq('http://localhost:9999')
+ expect(Chef::Config.chef_server_url).to eq('chefzero://localhost:9999')
end
context 'when the default port (8889) is already bound' do
@@ -149,7 +149,7 @@ EOM
it 'knife raw -z --chef-zero-port=9999 /nodes/x retrieves the node' do
knife('raw -z --chef-zero-port=9999 /nodes/x').should_succeed( /"name": "x"/ )
- expect(Chef::Config.chef_server_url).to eq('http://localhost:9999')
+ expect(Chef::Config.chef_server_url).to eq('chefzero://localhost:9999')
end
end
end
diff --git a/spec/integration/knife/deps_spec.rb b/spec/integration/knife/deps_spec.rb
index 3120db4940..b7333cefda 100644
--- a/spec/integration/knife/deps_spec.rb
+++ b/spec/integration/knife/deps_spec.rb
@@ -216,22 +216,16 @@ depends "self"'
end
it 'knife deps prints each once' do
- knife('deps /cookbooks/foo /cookbooks/self').should_succeed <<EOM
-/cookbooks/baz
-/cookbooks/bar
-/cookbooks/foo
-/cookbooks/self
-EOM
+ knife('deps /cookbooks/foo /cookbooks/self').should_succeed(
+ stdout: "/cookbooks/baz\n/cookbooks/bar\n/cookbooks/foo\n/cookbooks/self\n",
+ stderr: "WARN: Ignoring self-dependency in cookbook self, please remove it (in the future this will be fatal).\n"
+ )
end
it 'knife deps --tree prints each once' do
- knife('deps --tree /cookbooks/foo /cookbooks/self').should_succeed <<EOM
-/cookbooks/foo
- /cookbooks/bar
- /cookbooks/baz
- /cookbooks/foo
-/cookbooks/self
- /cookbooks/self
-EOM
+ knife('deps --tree /cookbooks/foo /cookbooks/self').should_succeed(
+ stdout: "/cookbooks/foo\n /cookbooks/bar\n /cookbooks/baz\n /cookbooks/foo\n/cookbooks/self\n",
+ stderr: "WARN: Ignoring self-dependency in cookbook self, please remove it (in the future this will be fatal).\n"
+ )
end
end
when_the_repository 'has roles with circular dependencies' do
diff --git a/spec/integration/knife/upload_spec.rb b/spec/integration/knife/upload_spec.rb
index cef4f54e97..826ecec364 100644
--- a/spec/integration/knife/upload_spec.rb
+++ b/spec/integration/knife/upload_spec.rb
@@ -154,6 +154,24 @@ EOM
end
end
+ context 'when cookbook metadata has a self-dependency' do
+ before do
+ file 'cookbooks/x/metadata.rb', "name 'x'; version '1.0.0'; depends 'x'"
+ end
+
+ it "should warn", :chef_lt_13_only do
+ knife('upload /cookbooks').should_succeed(
+ stdout: "Updated /cookbooks/x\n",
+ stderr: "WARN: Ignoring self-dependency in cookbook x, please remove it (in the future this will be fatal).\n"
+ )
+ knife('diff --name-status /').should_succeed ''
+ end
+ it "should fail in Chef 13", :chef_gte_13_only do
+ knife('upload /cookbooks').should_fail ''
+ # FIXME: include the error message here
+ end
+ end
+
context 'as well as one extra copy of each thing' do
before do
file 'clients/y.json', { 'public_key' => ChefZero::PUBLIC_KEY }
diff --git a/spec/integration/recipes/lwrp_inline_resources_spec.rb b/spec/integration/recipes/lwrp_inline_resources_spec.rb
index a3baba8b0f..e70605d3d3 100644
--- a/spec/integration/recipes/lwrp_inline_resources_spec.rb
+++ b/spec/integration/recipes/lwrp_inline_resources_spec.rb
@@ -5,7 +5,7 @@ describe "LWRPs with inline resources" do
include IntegrationSupport
include Chef::Mixin::ShellOut
- let(:chef_dir) { File.join(File.dirname(__FILE__), "..", "..", "..", "bin") }
+ let(:chef_dir) { File.expand_path("../../../../bin", __FILE__) }
# Invoke `chef-client` as `ruby PATH/TO/chef-client`. This ensures the
# following constraints are satisfied:
@@ -16,7 +16,7 @@ describe "LWRPs with inline resources" do
# machine that has omnibus chef installed. In that case we need to ensure
# we're running `chef-client` from the source tree and not the external one.
# cf. CHEF-4914
- let(:chef_client) { "ruby '#{chef_dir}/chef-client'" }
+ let(:chef_client) { "ruby '#{chef_dir}/chef-client' --minimal-ohai" }
when_the_repository "has a cookbook with a nested LWRP" do
before do
diff --git a/spec/integration/recipes/lwrp_spec.rb b/spec/integration/recipes/lwrp_spec.rb
new file mode 100644
index 0000000000..7ecdfc7c3a
--- /dev/null
+++ b/spec/integration/recipes/lwrp_spec.rb
@@ -0,0 +1,53 @@
+require 'support/shared/integration/integration_helper'
+require 'chef/mixin/shell_out'
+
+describe "LWRPs" do
+ include IntegrationSupport
+ include Chef::Mixin::ShellOut
+
+ let(:chef_dir) { File.expand_path("../../../../bin", __FILE__) }
+
+ # Invoke `chef-client` as `ruby PATH/TO/chef-client`. This ensures the
+ # following constraints are satisfied:
+ # * Windows: windows can only run batch scripts as bare executables. Rubygems
+ # creates batch wrappers for installed gems, but we don't have batch wrappers
+ # in the source tree.
+ # * Other `chef-client` in PATH: A common case is running the tests on a
+ # machine that has omnibus chef installed. In that case we need to ensure
+ # we're running `chef-client` from the source tree and not the external one.
+ # cf. CHEF-4914
+ let(:chef_client) { "ruby '#{chef_dir}/chef-client' --minimal-ohai" }
+
+ when_the_repository "has a cookbook named l-w-r-p" do
+ before do
+ directory 'cookbooks/l-w-r-p' do
+
+ file 'resources/foo.rb', <<EOM
+default_action :create
+EOM
+ file 'providers/foo.rb', <<EOM
+action :create do
+end
+EOM
+
+ file 'recipes/default.rb', <<EOM
+l_w_r_p_foo "me"
+EOM
+
+ end # directory 'cookbooks/x'
+ end
+
+ it "should complete with success" do
+ file 'config/client.rb', <<EOM
+local_mode true
+cookbook_path "#{path_to('cookbooks')}"
+log_level :warn
+EOM
+
+ result = shell_out("#{chef_client} -c \"#{path_to('config/client.rb')}\" --no-color -F doc -o 'l-w-r-p::default'", :cwd => chef_dir)
+ expect(result.stdout).to match(/\* l_w_r_p_foo\[me\] action create \(up to date\)/)
+ expect(result.stdout).not_to match(/WARN: You are overriding l_w_r_p_foo/)
+ result.error!
+ end
+ end
+end
diff --git a/spec/integration/recipes/provider_choice.rb b/spec/integration/recipes/provider_choice.rb
new file mode 100644
index 0000000000..01537b2c05
--- /dev/null
+++ b/spec/integration/recipes/provider_choice.rb
@@ -0,0 +1,36 @@
+require 'support/shared/integration/integration_helper'
+
+describe "Recipe DSL methods" do
+ include IntegrationSupport
+
+ context "With resource class providing 'provider_thingy'" do
+ before :context do
+ class Chef::Resource::ProviderThingy < Chef::Resource
+ resource_name :provider_thingy
+ default_action :create
+ def to_s
+ "provider_thingy resource class"
+ end
+ end
+ end
+ context "And class Chef::Provider::ProviderThingy with no provides" do
+ before :context do
+ class Chef::Provider::ProviderThingy < Chef::Provider
+ def load_current_resource
+ end
+ def action_create
+ Chef::Log.warn("hello from #{self.class.name}")
+ end
+ end
+ end
+
+ it "provider_thingy 'blah' runs the provider and warns" do
+ recipe = converge {
+ provider_thingy 'blah' do; end
+ }
+ expect(recipe.logged_warnings).to match /hello from Chef::Provider::ProviderThingy/
+ expect(recipe.logged_warnings).to match /you must use 'provides' to provide DSL/i
+ end
+ end
+ end
+end
diff --git a/spec/integration/recipes/recipe_dsl_spec.rb b/spec/integration/recipes/recipe_dsl_spec.rb
new file mode 100644
index 0000000000..52bca87c99
--- /dev/null
+++ b/spec/integration/recipes/recipe_dsl_spec.rb
@@ -0,0 +1,1492 @@
+require 'support/shared/integration/integration_helper'
+
+describe "Recipe DSL methods" do
+ include IntegrationSupport
+
+ module Namer
+ extend self
+ attr_accessor :current_index
+ end
+
+ before(:all) { Namer.current_index = 1 }
+ before { Namer.current_index += 1 }
+
+ context "with resource 'base_thingy' declared as BaseThingy" do
+ before(:context) {
+
+ class BaseThingy < Chef::Resource
+ resource_name 'base_thingy'
+ default_action :create
+
+ class<<self
+ attr_accessor :created_name
+ attr_accessor :created_resource
+ attr_accessor :created_provider
+ end
+
+ def provider
+ Provider
+ end
+ class Provider < Chef::Provider
+ def load_current_resource
+ end
+ def action_create
+ BaseThingy.created_name = new_resource.name
+ BaseThingy.created_resource = new_resource.class
+ BaseThingy.created_provider = self.class
+ end
+ end
+ end
+
+ # Modules to put stuff in
+ module RecipeDSLSpecNamespace; end
+ module RecipeDSLSpecNamespace::Bar; end
+
+ }
+
+ before :each do
+ BaseThingy.created_resource = nil
+ BaseThingy.created_provider = nil
+ end
+
+ it "creates base_thingy when you call base_thingy in a recipe" do
+ recipe = converge {
+ base_thingy 'blah' do; end
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_name).to eq 'blah'
+ expect(BaseThingy.created_resource).to eq BaseThingy
+ end
+
+ it "errors out when you call base_thingy do ... end in a recipe" do
+ expect_converge {
+ base_thingy do; end
+ }.to raise_error(ArgumentError, 'You must supply a name when declaring a base_thingy resource')
+ end
+
+ it "emits a warning when you call base_thingy 'foo', 'bar' do ... end in a recipe" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ recipe = converge {
+ base_thingy 'foo', 'bar' do
+ end
+ }
+ expect(recipe.logged_warnings).to match(/Cannot create resource base_thingy with more than one argument. All arguments except the name \("foo"\) will be ignored. This will cause an error in Chef 13. Arguments: \["foo", "bar"\]/)
+ expect(BaseThingy.created_name).to eq 'foo'
+ expect(BaseThingy.created_resource).to eq BaseThingy
+ end
+
+ context "Deprecated automatic resource DSL" do
+ before do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ end
+
+ context "with a resource 'backcompat_thingy' declared in Chef::Resource and Chef::Provider" do
+ before(:context) {
+
+ class Chef::Resource::BackcompatThingy < Chef::Resource
+ default_action :create
+ end
+ class Chef::Provider::BackcompatThingy < Chef::Provider
+ def load_current_resource
+ end
+ def action_create
+ BaseThingy.created_resource = new_resource.class
+ BaseThingy.created_provider = self.class
+ end
+ end
+
+ }
+
+ it "backcompat_thingy creates a Chef::Resource::BackcompatThingy" do
+ recipe = converge {
+ backcompat_thingy 'blah' do; end
+ }
+ expect(BaseThingy.created_resource).to eq Chef::Resource::BackcompatThingy
+ expect(BaseThingy.created_provider).to eq Chef::Provider::BackcompatThingy
+ end
+
+ context "and another resource 'backcompat_thingy' in BackcompatThingy with 'provides'" do
+ before(:context) {
+
+ class RecipeDSLSpecNamespace::BackcompatThingy < BaseThingy
+ provides :backcompat_thingy
+ resource_name :backcompat_thingy
+ end
+
+ }
+
+ it "backcompat_thingy creates a BackcompatThingy" do
+ recipe = converge {
+ backcompat_thingy 'blah' do; end
+ }
+ expect(recipe.logged_warnings).to match(/Class Chef::Provider::BackcompatThingy does not declare 'provides :backcompat_thingy'./)
+ expect(BaseThingy.created_resource).not_to be_nil
+ end
+ end
+ end
+
+ context "with a resource named RecipeDSLSpecNamespace::Bar::BarThingy" do
+ before(:context) {
+
+ class RecipeDSLSpecNamespace::Bar::BarThingy < BaseThingy
+ end
+
+ }
+
+ it "bar_thingy does not work" do
+ expect_converge {
+ bar_thingy 'blah' do; end
+ }.to raise_error(NoMethodError)
+ end
+ end
+
+ context "with a resource named Chef::Resource::NoNameThingy with resource_name nil" do
+ before(:context) {
+
+ class Chef::Resource::NoNameThingy < BaseThingy
+ resource_name nil
+ end
+
+ }
+
+ it "no_name_thingy does not work" do
+ expect_converge {
+ no_name_thingy 'blah' do; end
+ }.to raise_error(NoMethodError)
+ end
+ end
+
+ context "with a resource named AnotherNoNameThingy with resource_name :another_thingy_name" do
+ before(:context) {
+
+ class AnotherNoNameThingy < BaseThingy
+ resource_name :another_thingy_name
+ end
+
+ }
+
+ it "another_no_name_thingy does not work" do
+ expect_converge {
+ another_no_name_thingy 'blah' do; end
+ }.to raise_error(NoMethodError)
+ end
+
+ it "another_thingy_name works" do
+ recipe = converge {
+ another_thingy_name 'blah' do; end
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq(AnotherNoNameThingy)
+ end
+ end
+
+ context "with a resource named AnotherNoNameThingy2 with resource_name :another_thingy_name2; resource_name :another_thingy_name3" do
+ before(:context) {
+
+ class AnotherNoNameThingy2 < BaseThingy
+ resource_name :another_thingy_name2
+ resource_name :another_thingy_name3
+ end
+
+ }
+
+ it "another_no_name_thingy does not work" do
+ expect_converge {
+ another_no_name_thingy2 'blah' do; end
+ }.to raise_error(NoMethodError)
+ end
+
+ it "another_thingy_name2 does not work" do
+ expect_converge {
+ another_thingy_name2 'blah' do; end
+ }.to raise_error(NoMethodError)
+ end
+
+ it "yet_another_thingy_name3 works" do
+ recipe = converge {
+ another_thingy_name3 'blah' do; end
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq(AnotherNoNameThingy2)
+ end
+ end
+
+ context "provides overriding resource_name" do
+ context "with a resource named AnotherNoNameThingy3 with provides :another_no_name_thingy3, os: 'blarghle'" do
+ before(:context) {
+
+ class AnotherNoNameThingy3 < BaseThingy
+ resource_name :another_no_name_thingy_3
+ provides :another_no_name_thingy3, os: 'blarghle'
+ end
+
+ }
+
+ it "and os = linux, another_no_name_thingy3 does not work" do
+ expect_converge {
+ # TODO this is an ugly way to test, make Cheffish expose node attrs
+ run_context.node.automatic[:os] = 'linux'
+ another_no_name_thingy3 'blah' do; end
+ }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+ end
+
+ it "and os = blarghle, another_no_name_thingy3 works" do
+ recipe = converge {
+ # TODO this is an ugly way to test, make Cheffish expose node attrs
+ run_context.node.automatic[:os] = 'blarghle'
+ another_no_name_thingy3 'blah' do; end
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy3)
+ end
+ end
+
+ context "with a resource named AnotherNoNameThingy4 with two provides" do
+ before(:context) {
+
+ class AnotherNoNameThingy4 < BaseThingy
+ resource_name :another_no_name_thingy_4
+ provides :another_no_name_thingy4, os: 'blarghle'
+ provides :another_no_name_thingy4, platform_family: 'foo'
+ end
+
+ }
+
+ it "and os = linux, another_no_name_thingy4 does not work" do
+ expect_converge {
+ # TODO this is an ugly way to test, make Cheffish expose node attrs
+ run_context.node.automatic[:os] = 'linux'
+ another_no_name_thingy4 'blah' do; end
+ }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+ end
+
+ it "and os = blarghle, another_no_name_thingy4 works" do
+ recipe = converge {
+ # TODO this is an ugly way to test, make Cheffish expose node attrs
+ run_context.node.automatic[:os] = 'blarghle'
+ another_no_name_thingy4 'blah' do; end
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy4)
+ end
+
+ it "and platform_family = foo, another_no_name_thingy4 works" do
+ recipe = converge {
+ # TODO this is an ugly way to test, make Cheffish expose node attrs
+ run_context.node.automatic[:platform_family] = 'foo'
+ another_no_name_thingy4 'blah' do; end
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy4)
+ end
+ end
+
+ context "with a resource named AnotherNoNameThingy5, a different resource_name, and a provides with the original resource_name" do
+ before(:context) {
+
+ class AnotherNoNameThingy5 < BaseThingy
+ resource_name :another_thingy_name_for_another_no_name_thingy5
+ provides :another_no_name_thingy5, os: 'blarghle'
+ end
+
+ }
+
+ it "and os = linux, another_no_name_thingy5 does not work" do
+ expect_converge {
+ # this is an ugly way to test, make Cheffish expose node attrs
+ run_context.node.automatic[:os] = 'linux'
+ another_no_name_thingy5 'blah' do; end
+ }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+ end
+
+ it "and os = blarghle, another_no_name_thingy5 works" do
+ recipe = converge {
+ # this is an ugly way to test, make Cheffish expose node attrs
+ run_context.node.automatic[:os] = 'blarghle'
+ another_no_name_thingy5 'blah' do; end
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy5)
+ end
+
+ it "the new resource name can be used in a recipe" do
+ recipe = converge {
+ another_thingy_name_for_another_no_name_thingy5 'blah' do; end
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy5)
+ end
+ end
+
+ context "with a resource named AnotherNoNameThingy6, a provides with the original resource name, and a different resource_name" do
+ before(:context) {
+
+ class AnotherNoNameThingy6 < BaseThingy
+ provides :another_no_name_thingy6, os: 'blarghle'
+ resource_name :another_thingy_name_for_another_no_name_thingy6
+ end
+
+ }
+
+ it "and os = linux, another_no_name_thingy6 does not work" do
+ expect_converge {
+ # this is an ugly way to test, make Cheffish expose node attrs
+ run_context.node.automatic[:os] = 'linux'
+ another_no_name_thingy6 'blah' do; end
+ }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+ end
+
+ it "and os = blarghle, another_no_name_thingy6 works" do
+ recipe = converge {
+ # this is an ugly way to test, make Cheffish expose node attrs
+ run_context.node.automatic[:os] = 'blarghle'
+ another_no_name_thingy6 'blah' do; end
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy6)
+ end
+
+ it "the new resource name can be used in a recipe" do
+ recipe = converge {
+ another_thingy_name_for_another_no_name_thingy6 'blah' do; end
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy6)
+ end
+ end
+
+ context "with a resource named AnotherNoNameThingy7, a new resource_name, and provides with that new resource name" do
+ before(:context) {
+
+ class AnotherNoNameThingy7 < BaseThingy
+ resource_name :another_thingy_name_for_another_no_name_thingy7
+ provides :another_thingy_name_for_another_no_name_thingy7, os: 'blarghle'
+ end
+
+ }
+
+ it "and os = linux, another_thingy_name_for_another_no_name_thingy7 does not work" do
+ expect_converge {
+ # this is an ugly way to test, make Cheffish expose node attrs
+ run_context.node.automatic[:os] = 'linux'
+ another_thingy_name_for_another_no_name_thingy7 'blah' do; end
+ }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+ end
+
+ it "and os = blarghle, another_thingy_name_for_another_no_name_thingy7 works" do
+ recipe = converge {
+ # this is an ugly way to test, make Cheffish expose node attrs
+ run_context.node.automatic[:os] = 'blarghle'
+ another_thingy_name_for_another_no_name_thingy7 'blah' do; end
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy7)
+ end
+
+ it "the old resource name does not work" do
+ expect_converge {
+ # this is an ugly way to test, make Cheffish expose node attrs
+ run_context.node.automatic[:os] = 'linux'
+ another_no_name_thingy_7 'blah' do; end
+ }.to raise_error(NoMethodError)
+ end
+ end
+
+ # opposite order from the previous test (provides, then resource_name)
+ context "with a resource named AnotherNoNameThingy8, a provides with a new resource name, and resource_name with that new resource name" do
+ before(:context) {
+
+ class AnotherNoNameThingy8 < BaseThingy
+ provides :another_thingy_name_for_another_no_name_thingy8, os: 'blarghle'
+ resource_name :another_thingy_name_for_another_no_name_thingy8
+ end
+
+ }
+
+ it "and os = linux, another_thingy_name_for_another_no_name_thingy8 does not work" do
+ expect_converge {
+ # this is an ugly way to test, make Cheffish expose node attrs
+ run_context.node.automatic[:os] = 'linux'
+ another_thingy_name_for_another_no_name_thingy8 'blah' do; end
+ }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+ end
+
+ it "and os = blarghle, another_thingy_name_for_another_no_name_thingy8 works" do
+ recipe = converge {
+ # this is an ugly way to test, make Cheffish expose node attrs
+ run_context.node.automatic[:os] = 'blarghle'
+ another_thingy_name_for_another_no_name_thingy8 'blah' do; end
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq (AnotherNoNameThingy8)
+ end
+
+ it "the old resource name does not work" do
+ expect_converge {
+ # this is an ugly way to test, make Cheffish expose node attrs
+ run_context.node.automatic[:os] = 'linux'
+ another_thingy_name8 'blah' do; end
+ }.to raise_error(NoMethodError)
+ end
+ end
+ end
+ end
+
+ context "provides" do
+ context "when MySupplier provides :hemlock" do
+ before(:context) {
+
+ class RecipeDSLSpecNamespace::MySupplier < BaseThingy
+ resource_name :hemlock
+ end
+
+ }
+
+ it "my_supplier does not work in a recipe" do
+ expect_converge {
+ my_supplier 'blah' do; end
+ }.to raise_error(NoMethodError)
+ end
+
+ it "hemlock works in a recipe" do
+ expect_recipe {
+ hemlock 'blah' do; end
+ }.to emit_no_warnings_or_errors
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::MySupplier
+ end
+ end
+
+ context "when Thingy3 has resource_name :thingy3" do
+ before(:context) {
+
+ class RecipeDSLSpecNamespace::Thingy3 < BaseThingy
+ resource_name :thingy3
+ end
+
+ }
+
+ it "thingy3 works in a recipe" do
+ expect_recipe {
+ thingy3 'blah' do; end
+ }.to emit_no_warnings_or_errors
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy3
+ end
+
+ context "and Thingy4 has resource_name :thingy3" do
+ before(:context) {
+
+ class RecipeDSLSpecNamespace::Thingy4 < BaseThingy
+ resource_name :thingy3
+ end
+
+ }
+
+ it "thingy3 works in a recipe and yields Thingy3 (the alphabetical one)" do
+ recipe = converge {
+ thingy3 'blah' do; end
+ }
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy3
+ end
+
+ it "thingy4 does not work in a recipe" do
+ expect_converge {
+ thingy4 'blah' do; end
+ }.to raise_error(NoMethodError)
+ end
+
+ it "resource_matching_short_name returns Thingy4" do
+ expect(Chef::Resource.resource_matching_short_name(:thingy3)).to eq RecipeDSLSpecNamespace::Thingy3
+ end
+ end
+ end
+
+ context "when Thingy5 has resource_name :thingy5 and provides :thingy5reverse, :thingy5_2 and :thingy5_2reverse" do
+ before(:context) {
+
+ class RecipeDSLSpecNamespace::Thingy5 < BaseThingy
+ resource_name :thingy5
+ provides :thingy5reverse
+ provides :thingy5_2
+ provides :thingy5_2reverse
+ end
+
+ }
+
+ it "thingy5 works in a recipe" do
+ expect_recipe {
+ thingy5 'blah' do; end
+ }.to emit_no_warnings_or_errors
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy5
+ end
+
+ context "and Thingy6 provides :thingy5" do
+ before(:context) {
+
+ class RecipeDSLSpecNamespace::Thingy6 < BaseThingy
+ resource_name :thingy6
+ provides :thingy5
+ end
+
+ }
+
+ it "thingy6 works in a recipe and yields Thingy6" do
+ recipe = converge {
+ thingy6 'blah' do; end
+ }
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy6
+ end
+
+ it "thingy5 works in a recipe and yields Foo::Thingy5 (the alphabetical one)" do
+ recipe = converge {
+ thingy5 'blah' do; end
+ }
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy5
+ end
+
+ it "resource_matching_short_name returns Thingy5" do
+ expect(Chef::Resource.resource_matching_short_name(:thingy5)).to eq RecipeDSLSpecNamespace::Thingy5
+ end
+
+ context "and AThingy5 provides :thingy5reverse" do
+ before(:context) {
+
+ class RecipeDSLSpecNamespace::AThingy5 < BaseThingy
+ resource_name :thingy5reverse
+ end
+
+ }
+
+ it "thingy5reverse works in a recipe and yields AThingy5 (the alphabetical one)" do
+ recipe = converge {
+ thingy5reverse 'blah' do; end
+ }
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::AThingy5
+ end
+ end
+
+ context "and ZRecipeDSLSpecNamespace::Thingy5 provides :thingy5_2" do
+ before(:context) {
+
+ module ZRecipeDSLSpecNamespace
+ class Thingy5 < BaseThingy
+ resource_name :thingy5_2
+ end
+ end
+
+ }
+
+ it "thingy5_2 works in a recipe and yields the RecipeDSLSpaceNamespace one (the alphabetical one)" do
+ recipe = converge {
+ thingy5_2 'blah' do; end
+ }
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy5
+ end
+ end
+
+ context "and ARecipeDSLSpecNamespace::Thingy5 provides :thingy5_2" do
+ before(:context) {
+
+ module ARecipeDSLSpecNamespace
+ class Thingy5 < BaseThingy
+ resource_name :thingy5_2reverse
+ end
+ end
+
+ }
+
+ it "thingy5_2reverse works in a recipe and yields the ARecipeDSLSpaceNamespace one (the alphabetical one)" do
+ recipe = converge {
+ thingy5_2reverse 'blah' do; end
+ }
+ expect(BaseThingy.created_resource).to eq ARecipeDSLSpecNamespace::Thingy5
+ end
+ end
+ end
+
+ context "when Thingy3 has resource_name :thingy3" do
+ before(:context) {
+
+ class RecipeDSLSpecNamespace::Thingy3 < BaseThingy
+ resource_name :thingy3
+ end
+
+ }
+
+ it "thingy3 works in a recipe" do
+ expect_recipe {
+ thingy3 'blah' do; end
+ }.to emit_no_warnings_or_errors
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy3
+ end
+
+ context "and Thingy4 has resource_name :thingy3" do
+ before(:context) {
+
+ class RecipeDSLSpecNamespace::Thingy4 < BaseThingy
+ resource_name :thingy3
+ end
+
+ }
+
+ it "thingy3 works in a recipe and yields Thingy3 (the alphabetical one)" do
+ recipe = converge {
+ thingy3 'blah' do; end
+ }
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy3
+ end
+
+ it "thingy4 does not work in a recipe" do
+ expect_converge {
+ thingy4 'blah' do; end
+ }.to raise_error(NoMethodError)
+ end
+
+ it "resource_matching_short_name returns Thingy4" do
+ expect(Chef::Resource.resource_matching_short_name(:thingy3)).to eq RecipeDSLSpecNamespace::Thingy3
+ end
+ end
+
+ context "and Thingy4 has resource_name :thingy3" do
+ before(:context) {
+
+ class RecipeDSLSpecNamespace::Thingy4 < BaseThingy
+ resource_name :thingy3
+ end
+
+ }
+
+ it "thingy3 works in a recipe and yields Thingy3 (the alphabetical one)" do
+ recipe = converge {
+ thingy3 'blah' do; end
+ }
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy3
+ end
+
+ it "thingy4 does not work in a recipe" do
+ expect_converge {
+ thingy4 'blah' do; end
+ }.to raise_error(NoMethodError)
+ end
+
+ it "resource_matching_short_name returns Thingy4" do
+ expect(Chef::Resource.resource_matching_short_name(:thingy3)).to eq RecipeDSLSpecNamespace::Thingy3
+ end
+ end
+ end
+
+ end
+
+ context "when Thingy7 provides :thingy8" do
+ before(:context) {
+
+ class RecipeDSLSpecNamespace::Thingy7 < BaseThingy
+ resource_name :thingy7
+ provides :thingy8
+ end
+
+ }
+
+ context "and Thingy8 has resource_name :thingy8" do
+ before(:context) {
+
+ class RecipeDSLSpecNamespace::Thingy8 < BaseThingy
+ resource_name :thingy8
+ end
+
+ }
+
+ it "thingy7 works in a recipe and yields Thingy7" do
+ recipe = converge {
+ thingy7 'blah' do; end
+ }
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy7
+ end
+
+ it "thingy8 works in a recipe and yields Thingy7 (alphabetical)" do
+ recipe = converge {
+ thingy8 'blah' do; end
+ }
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy7
+ end
+
+ it "resource_matching_short_name returns Thingy8" do
+ expect(Chef::Resource.resource_matching_short_name(:thingy8)).to eq RecipeDSLSpecNamespace::Thingy8
+ end
+ end
+ end
+
+ context "when Thingy12 provides :thingy12, :twizzle and :twizzle2" do
+ before(:context) {
+
+ class RecipeDSLSpecNamespace::Thingy12 < BaseThingy
+ resource_name :thingy12
+ provides :twizzle
+ provides :twizzle2
+ end
+
+ }
+
+ it "thingy12 works in a recipe and yields Thingy12" do
+ expect_recipe {
+ thingy12 'blah' do; end
+ }.to emit_no_warnings_or_errors
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy12
+ end
+
+ it "twizzle works in a recipe and yields Thingy12" do
+ expect_recipe {
+ twizzle 'blah' do; end
+ }.to emit_no_warnings_or_errors
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy12
+ end
+
+ it "twizzle2 works in a recipe and yields Thingy12" do
+ expect_recipe {
+ twizzle2 'blah' do; end
+ }.to emit_no_warnings_or_errors
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy12
+ end
+ end
+
+ context "with platform-specific resources 'my_super_thingy_foo' and 'my_super_thingy_bar'" do
+ before(:context) {
+ class MySuperThingyFoo < BaseThingy
+ resource_name :my_super_thingy_foo
+ provides :my_super_thingy, platform: 'foo'
+ end
+
+ class MySuperThingyBar < BaseThingy
+ resource_name :my_super_thingy_bar
+ provides :my_super_thingy, platform: 'bar'
+ end
+ }
+
+ it "A run with platform 'foo' uses MySuperThingyFoo" do
+ r = Cheffish::ChefRun.new(chef_config)
+ r.client.run_context.node.automatic['platform'] = 'foo'
+ r.compile_recipe {
+ my_super_thingy 'blah' do; end
+ }
+ r.converge
+ expect(r).to emit_no_warnings_or_errors
+ expect(BaseThingy.created_resource).to eq MySuperThingyFoo
+ end
+
+ it "A run with platform 'bar' uses MySuperThingyBar" do
+ r = Cheffish::ChefRun.new(chef_config)
+ r.client.run_context.node.automatic['platform'] = 'bar'
+ r.compile_recipe {
+ my_super_thingy 'blah' do; end
+ }
+ r.converge
+ expect(r).to emit_no_warnings_or_errors
+ expect(BaseThingy.created_resource).to eq MySuperThingyBar
+ end
+
+ it "A run with platform 'x' reports that my_super_thingy is not supported" do
+ r = Cheffish::ChefRun.new(chef_config)
+ r.client.run_context.node.automatic['platform'] = 'x'
+ expect {
+ r.compile_recipe {
+ my_super_thingy 'blah' do; end
+ }
+ }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+ end
+ end
+
+ context "when Thingy10 provides :thingy10" do
+ before(:context) {
+ class RecipeDSLSpecNamespace::Thingy10 < BaseThingy
+ resource_name :thingy10
+ end
+ }
+
+ it "declaring a resource providing the same :thingy10 with override: true does not produce a warning" do
+ expect(Chef::Log).not_to receive(:warn)
+ class RecipeDSLSpecNamespace::Thingy10AlternateProvider < BaseThingy
+ provides :thingy10, override: true
+ end
+ end
+ end
+
+ context "when Thingy11 provides :thingy11" do
+ before(:context) {
+ class RecipeDSLSpecNamespace::Thingy11 < BaseThingy
+ resource_name :thingy10
+ end
+ }
+
+ it "declaring a resource providing the same :thingy11 with os: 'linux' does not produce a warning" do
+ expect(Chef::Log).not_to receive(:warn)
+ class RecipeDSLSpecNamespace::Thingy11AlternateProvider < BaseThingy
+ provides :thingy11, os: 'linux'
+ end
+ end
+ end
+ end
+
+ context "with a resource named 'B' with resource name :two_classes_one_dsl" do
+ let(:two_classes_one_dsl) { :"two_classes_one_dsl#{Namer.current_index}" }
+ let(:resource_class) {
+ result = Class.new(BaseThingy) do
+ def self.name
+ "B"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ end
+ result.resource_name two_classes_one_dsl
+ result
+ }
+ before { resource_class } # pull on it so it gets defined before the recipe runs
+
+ context "and another resource named 'A' with resource_name :two_classes_one_dsl" do
+ let(:resource_class_a) {
+ result = Class.new(BaseThingy) do
+ def self.name
+ "A"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ end
+ result.resource_name two_classes_one_dsl
+ result
+ }
+ before { resource_class_a } # pull on it so it gets defined before the recipe runs
+
+ it "two_classes_one_dsl resolves to A (alphabetically earliest)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class_a
+ end
+
+ it "resource_matching_short_name returns B" do
+ expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class_a
+ end
+ end
+
+ context "and another resource named 'Z' with resource_name :two_classes_one_dsl" do
+ let(:resource_class_z) {
+ result = Class.new(BaseThingy) do
+ def self.name
+ "Z"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ end
+ result.resource_name two_classes_one_dsl
+ result
+ }
+ before { resource_class_z } # pull on it so it gets defined before the recipe runs
+
+ it "two_classes_one_dsl resolves to B (alphabetically earliest)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class
+ end
+
+ it "resource_matching_short_name returns B" do
+ expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class
+ end
+
+ context "and a priority array [ Z, B ]" do
+ before do
+ Chef.set_resource_priority_array(two_classes_one_dsl, [ resource_class_z, resource_class ])
+ end
+
+ it "two_classes_one_dsl resolves to Z (respects the priority array)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class_z
+ end
+
+ it "resource_matching_short_name returns B" do
+ expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class
+ end
+
+ context "when Z provides(:two_classes_one_dsl) { false }" do
+ before do
+ resource_class_z.provides(two_classes_one_dsl) { false }
+ end
+
+ it "two_classes_one_dsl resolves to B (picks the next thing in the priority array)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class
+ end
+
+ it "resource_matching_short_name returns B" do
+ expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class
+ end
+ end
+ end
+
+ context "and priority arrays [ B ] and [ Z ]" do
+ before do
+ Chef.set_resource_priority_array(two_classes_one_dsl, [ resource_class ])
+ Chef.set_resource_priority_array(two_classes_one_dsl, [ resource_class_z ])
+ end
+
+ it "two_classes_one_dsl resolves to Z (respects the most recent priority array)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class_z
+ end
+
+ it "resource_matching_short_name returns B" do
+ expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class
+ end
+
+ context "when Z provides(:two_classes_one_dsl) { false }" do
+ before do
+ resource_class_z.provides(two_classes_one_dsl) { false }
+ end
+
+ it "two_classes_one_dsl resolves to B (picks the first match from the other priority array)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class
+ end
+
+ it "resource_matching_short_name returns B" do
+ expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class
+ end
+ end
+ end
+
+ context "and a priority array [ Z ]" do
+ before do
+ Chef.set_resource_priority_array(two_classes_one_dsl, [ resource_class_z ])
+ end
+
+ context "when Z provides(:two_classes_one_dsl) { false }" do
+ before do
+ resource_class_z.provides(two_classes_one_dsl) { false }
+ end
+
+ it "two_classes_one_dsl resolves to B (picks the first match outside the priority array)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class
+ end
+
+ it "resource_matching_short_name returns B" do
+ expect(Chef::Resource.resource_matching_short_name(two_classes_one_dsl)).to eq resource_class
+ end
+ end
+ end
+
+ end
+
+ context "and a provider named 'B' which provides :two_classes_one_dsl" do
+ before do
+ resource_class.send(:define_method, :provider) { nil }
+ end
+
+ let(:provider_class) {
+ result = Class.new(BaseThingy::Provider) do
+ def self.name
+ "B"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ end
+ result.provides two_classes_one_dsl
+ result
+ }
+ before { provider_class } # pull on it so it gets defined before the recipe runs
+
+ context "and another provider named 'A'" do
+ let(:provider_class_a) {
+ result = Class.new(BaseThingy::Provider) do
+ def self.name
+ "A"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ end
+ result
+ }
+ context "which provides :two_classes_one_dsl" do
+ before { provider_class_a.provides two_classes_one_dsl }
+
+ it "two_classes_one_dsl resolves to A (alphabetically earliest)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class_a
+ end
+ end
+ context "which provides(:two_classes_one_dsl) { false }" do
+ before { provider_class_a.provides(two_classes_one_dsl) { false } }
+
+ it "two_classes_one_dsl resolves to B (since A declined)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class
+ end
+ end
+ end
+
+ context "and another provider named 'Z'" do
+ let(:provider_class_z) {
+ result = Class.new(BaseThingy::Provider) do
+ def self.name
+ "Z"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ end
+ result
+ }
+ before { provider_class_z } # pull on it so it gets defined before the recipe runs
+
+ context "which provides :two_classes_one_dsl" do
+ before { provider_class_z.provides two_classes_one_dsl }
+
+ it "two_classes_one_dsl resolves to B (alphabetically earliest)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class
+ end
+
+ context "with a priority array [ Z, B ]" do
+ before { Chef.set_provider_priority_array two_classes_one_dsl, [ provider_class_z, provider_class ] }
+
+ it "two_classes_one_dsl resolves to Z (respects the priority map)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class_z
+ end
+ end
+ end
+
+ context "which provides(:two_classes_one_dsl) { false }" do
+ before { provider_class_z.provides(two_classes_one_dsl) { false } }
+
+ context "with a priority array [ Z, B ]" do
+ before { Chef.set_provider_priority_array two_classes_one_dsl, [ provider_class_z, provider_class ] }
+
+ it "two_classes_one_dsl resolves to B (the next one in the priority map)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class
+ end
+ end
+
+ context "with priority arrays [ B ] and [ Z ]" do
+ before { Chef.set_provider_priority_array two_classes_one_dsl, [ provider_class_z ] }
+ before { Chef.set_provider_priority_array two_classes_one_dsl, [ provider_class ] }
+
+ it "two_classes_one_dsl resolves to B (the one in the next priority map)" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ instance_eval("#{two_classes_one_dsl} 'blah'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class
+ end
+ end
+ end
+ end
+ end
+
+ context "and another resource Blarghle with provides :two_classes_one_dsl, os: 'blarghle'" do
+ let(:resource_class_blarghle) {
+ result = Class.new(BaseThingy) do
+ def self.name
+ "Blarghle"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ end
+ result.resource_name two_classes_one_dsl
+ result.provides two_classes_one_dsl, os: 'blarghle'
+ result
+ }
+ before { resource_class_blarghle } # pull on it so it gets defined before the recipe runs
+
+ it "on os = blarghle, two_classes_one_dsl resolves to Blarghle" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ # this is an ugly way to test, make Cheffish expose node attrs
+ run_context.node.automatic[:os] = 'blarghle'
+ instance_eval("#{two_classes_one_dsl} 'blah' do; end")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class_blarghle
+ end
+
+ it "on os = linux, two_classes_one_dsl resolves to B" do
+ two_classes_one_dsl = self.two_classes_one_dsl
+ recipe = converge {
+ # this is an ugly way to test, make Cheffish expose node attrs
+ run_context.node.automatic[:os] = 'linux'
+ instance_eval("#{two_classes_one_dsl} 'blah' do; end")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class
+ end
+ end
+ end
+
+ context "with a resource MyResource" do
+ let(:resource_class) { Class.new(BaseThingy) do
+ def self.called_provides
+ @called_provides
+ end
+ def to_s
+ "MyResource"
+ end
+ end }
+ let(:my_resource) { :"my_resource#{Namer.current_index}" }
+ let(:blarghle_blarghle_little_star) { :"blarghle_blarghle_little_star#{Namer.current_index}" }
+
+ context "with resource_name :my_resource" do
+ before {
+ resource_class.resource_name my_resource
+ }
+
+ context "with provides? returning true to my_resource" do
+ before {
+ my_resource = self.my_resource
+ resource_class.define_singleton_method(:provides?) do |node, resource_name|
+ @called_provides = true
+ resource_name == my_resource
+ end
+ }
+
+ it "my_resource returns the resource and calls provides?, but does not emit a warning" do
+ dsl_name = self.my_resource
+ recipe = converge {
+ instance_eval("#{dsl_name} 'foo'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_resource).to eq resource_class
+ expect(resource_class.called_provides).to be_truthy
+ end
+ end
+
+ context "with provides? returning true to blarghle_blarghle_little_star and not resource_name" do
+ before do
+ blarghle_blarghle_little_star = self.blarghle_blarghle_little_star
+ resource_class.define_singleton_method(:provides?) do |node, resource_name|
+ @called_provides = true
+ resource_name == blarghle_blarghle_little_star
+ end
+ end
+
+ it "my_resource does not return the resource" do
+ dsl_name = self.my_resource
+ expect_converge {
+ instance_eval("#{dsl_name} 'foo'")
+ }.to raise_error(Chef::Exceptions::NoSuchResourceType)
+ expect(resource_class.called_provides).to be_truthy
+ end
+
+ it "blarghle_blarghle_little_star 'foo' returns the resource and emits a warning" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ dsl_name = self.blarghle_blarghle_little_star
+ recipe = converge {
+ instance_eval("#{dsl_name} 'foo'")
+ }
+ expect(recipe.logged_warnings).to include "WARN: #{resource_class}.provides? returned true when asked if it provides DSL #{dsl_name}, but provides :#{dsl_name} was never called!"
+ expect(BaseThingy.created_resource).to eq resource_class
+ expect(resource_class.called_provides).to be_truthy
+ end
+ end
+
+ context "and a provider" do
+ let(:provider_class) do
+ Class.new(BaseThingy::Provider) do
+ def self.name
+ "MyProvider"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ def self.called_provides
+ @called_provides
+ end
+ end
+ end
+
+ before do
+ resource_class.send(:define_method, :provider) { nil }
+ end
+
+ context "that provides :my_resource" do
+ before do
+ provider_class.provides my_resource
+ end
+
+ context "with supports? returning true" do
+ before do
+ provider_class.define_singleton_method(:supports?) { |resource,action| true }
+ end
+
+ it "my_resource runs the provider and does not emit a warning" do
+ my_resource = self.my_resource
+ recipe = converge {
+ instance_eval("#{my_resource} 'foo'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class
+ end
+
+ context "and another provider supporting :my_resource with supports? false" do
+ let(:provider_class2) do
+ Class.new(BaseThingy::Provider) do
+ def self.name
+ "MyProvider2"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ def self.called_provides
+ @called_provides
+ end
+ provides my_resource
+ def self.supports?(resource, action)
+ false
+ end
+ end
+ end
+
+ it "my_resource runs the first provider" do
+ my_resource = self.my_resource
+ recipe = converge {
+ instance_eval("#{my_resource} 'foo'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class
+ end
+ end
+ end
+
+ context "with supports? returning false" do
+ before do
+ provider_class.define_singleton_method(:supports?) { |resource,action| false }
+ end
+
+ # TODO no warning? ick
+ it "my_resource runs the provider anyway" do
+ my_resource = self.my_resource
+ recipe = converge {
+ instance_eval("#{my_resource} 'foo'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class
+ end
+
+ context "and another provider supporting :my_resource with supports? true" do
+ let(:provider_class2) do
+ my_resource = self.my_resource
+ Class.new(BaseThingy::Provider) do
+ def self.name
+ "MyProvider2"
+ end
+ def self.to_s; name; end
+ def self.inspect; name.inspect; end
+ def self.called_provides
+ @called_provides
+ end
+ provides my_resource
+ def self.supports?(resource, action)
+ true
+ end
+ end
+ end
+ before { provider_class2 } # make sure the provider class shows up
+
+ it "my_resource runs the other provider" do
+ my_resource = self.my_resource
+ recipe = converge {
+ instance_eval("#{my_resource} 'foo'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class2
+ end
+ end
+ end
+ end
+
+ context "with provides? returning true" do
+ before {
+ my_resource = self.my_resource
+ provider_class.define_singleton_method(:provides?) do |node, resource|
+ @called_provides = true
+ resource.declared_type == my_resource
+ end
+ }
+
+ context "that provides :my_resource" do
+ before {
+ provider_class.provides my_resource
+ }
+
+ it "my_resource calls the provider (and calls provides?), but does not emit a warning" do
+ my_resource = self.my_resource
+ recipe = converge {
+ instance_eval("#{my_resource} 'foo'")
+ }
+ expect(recipe.logged_warnings).to eq ''
+ expect(BaseThingy.created_provider).to eq provider_class
+ expect(provider_class.called_provides).to be_truthy
+ end
+ end
+
+ context "that does not call provides :my_resource" do
+ it "my_resource calls the provider (and calls provides?), and emits a warning" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ my_resource = self.my_resource
+ recipe = converge {
+ instance_eval("#{my_resource} 'foo'")
+ }
+ expect(recipe.logged_warnings).to include("WARN: #{provider_class}.provides? returned true when asked if it provides DSL #{my_resource}, but provides :#{my_resource} was never called!")
+ expect(BaseThingy.created_provider).to eq provider_class
+ expect(provider_class.called_provides).to be_truthy
+ end
+ end
+ end
+
+ context "with provides? returning false to my_resource" do
+ before {
+ my_resource = self.my_resource
+ provider_class.define_singleton_method(:provides?) do |node, resource|
+ @called_provides = true
+ false
+ end
+ }
+
+ context "that provides :my_resource" do
+ before {
+ provider_class.provides my_resource
+ }
+
+ it "my_resource fails to find a provider (and calls provides)" do
+ my_resource = self.my_resource
+ expect_converge {
+ instance_eval("#{my_resource} 'foo'")
+ }.to raise_error(Chef::Exceptions::ProviderNotFound)
+ expect(provider_class.called_provides).to be_truthy
+ end
+ end
+
+ context "that does not provide :my_resource" do
+ it "my_resource fails to find a provider (and calls provides)" do
+ my_resource = self.my_resource
+ expect_converge {
+ instance_eval("#{my_resource} 'foo'")
+ }.to raise_error(Chef::Exceptions::ProviderNotFound)
+ expect(provider_class.called_provides).to be_truthy
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ before(:all) { Namer.current_index = 0 }
+ before { Namer.current_index += 1 }
+
+ context "with an LWRP that declares actions" do
+ let(:resource_class) {
+ Class.new(Chef::Resource::LWRPBase) do
+ provides :"recipe_dsl_spec#{Namer.current_index}"
+ actions :create
+ end
+ }
+ let(:resource) {
+ resource_class.new("blah", run_context)
+ }
+ it "The actions are part of actions along with :nothing" do
+ expect(resource_class.actions).to eq [ :nothing, :create ]
+ end
+ it "The actions are part of allowed_actions along with :nothing" do
+ expect(resource.allowed_actions).to eq [ :nothing, :create ]
+ end
+
+ context "and a subclass that declares more actions" do
+ let(:subresource_class) {
+ Class.new(Chef::Resource::LWRPBase) do
+ provides :"recipe_dsl_spec_sub#{Namer.current_index}"
+ actions :delete
+ end
+ }
+ let(:subresource) {
+ subresource_class.new("subblah", run_context)
+ }
+
+ it "The parent class actions are not part of actions" do
+ expect(subresource_class.actions).to eq [ :nothing, :delete ]
+ end
+ it "The parent class actions are not part of allowed_actions" do
+ expect(subresource.allowed_actions).to eq [ :nothing, :delete ]
+ end
+ it "The parent class actions do not change" do
+ expect(resource_class.actions).to eq [ :nothing, :create ]
+ expect(resource.allowed_actions).to eq [ :nothing, :create ]
+ end
+ end
+ end
+
+ context "with a dynamically defined resource and regular provider" do
+ before(:context) do
+ Class.new(Chef::Resource) do
+ resource_name :lw_resource_with_hw_provider_test_case
+ default_action :create
+ attr_accessor :created_provider
+ end
+ class Chef::Provider::LwResourceWithHwProviderTestCase < Chef::Provider
+ def load_current_resource
+ end
+ def action_create
+ new_resource.created_provider = self.class
+ end
+ end
+ end
+
+ it "looks up the provider in Chef::Provider converting the resource name from snake case to camel case" do
+ resource = nil
+ recipe = converge {
+ resource = lw_resource_with_hw_provider_test_case 'blah' do; end
+ }
+ expect(resource.created_provider).to eq(Chef::Provider::LwResourceWithHwProviderTestCase)
+ end
+ end
+end
diff --git a/spec/integration/recipes/resource_action_spec.rb b/spec/integration/recipes/resource_action_spec.rb
new file mode 100644
index 0000000000..53611c144f
--- /dev/null
+++ b/spec/integration/recipes/resource_action_spec.rb
@@ -0,0 +1,356 @@
+require 'support/shared/integration/integration_helper'
+
+describe "Resource.action" do
+ include IntegrationSupport
+
+ shared_context "ActionJackson" do
+ it "The default action is the first declared action" do
+ converge <<-EOM, __FILE__, __LINE__+1
+ #{resource_dsl} 'hi' do
+ foo 'foo!'
+ end
+ EOM
+ expect(ActionJackson.ran_action).to eq :access_recipe_dsl
+ expect(ActionJackson.succeeded).to eq true
+ end
+
+ it "The action can access recipe DSL" do
+ converge <<-EOM, __FILE__, __LINE__+1
+ #{resource_dsl} 'hi' do
+ foo 'foo!'
+ action :access_recipe_dsl
+ end
+ EOM
+ expect(ActionJackson.ran_action).to eq :access_recipe_dsl
+ expect(ActionJackson.succeeded).to eq true
+ end
+
+ it "The action can access attributes" do
+ converge <<-EOM, __FILE__, __LINE__+1
+ #{resource_dsl} 'hi' do
+ foo 'foo!'
+ action :access_attribute
+ end
+ EOM
+ expect(ActionJackson.ran_action).to eq :access_attribute
+ expect(ActionJackson.succeeded).to eq 'foo!'
+ end
+
+ it "The action can access public methods" do
+ converge <<-EOM, __FILE__, __LINE__+1
+ #{resource_dsl} 'hi' do
+ foo 'foo!'
+ action :access_method
+ end
+ EOM
+ expect(ActionJackson.ran_action).to eq :access_method
+ expect(ActionJackson.succeeded).to eq 'foo_public!'
+ end
+
+ it "The action can access protected methods" do
+ converge <<-EOM, __FILE__, __LINE__+1
+ #{resource_dsl} 'hi' do
+ foo 'foo!'
+ action :access_protected_method
+ end
+ EOM
+ expect(ActionJackson.ran_action).to eq :access_protected_method
+ expect(ActionJackson.succeeded).to eq 'foo_protected!'
+ end
+
+ it "The action cannot access private methods" do
+ expect {
+ converge(<<-EOM, __FILE__, __LINE__+1)
+ #{resource_dsl} 'hi' do
+ foo 'foo!'
+ action :access_private_method
+ end
+ EOM
+ }.to raise_error(NameError)
+ expect(ActionJackson.ran_action).to eq :access_private_method
+ end
+
+ it "The action cannot access resource instance variables" do
+ converge <<-EOM, __FILE__, __LINE__+1
+ #{resource_dsl} 'hi' do
+ foo 'foo!'
+ action :access_instance_variable
+ end
+ EOM
+ expect(ActionJackson.ran_action).to eq :access_instance_variable
+ expect(ActionJackson.succeeded).to be_nil
+ end
+
+ it "The action does not compile until the prior resource has converged" do
+ converge <<-EOM, __FILE__, __LINE__+1
+ ruby_block 'wow' do
+ block do
+ ActionJackson.ruby_block_converged = 'ruby_block_converged!'
+ end
+ end
+
+ #{resource_dsl} 'hi' do
+ foo 'foo!'
+ action :access_class_method
+ end
+ EOM
+ expect(ActionJackson.ran_action).to eq :access_class_method
+ expect(ActionJackson.succeeded).to eq 'ruby_block_converged!'
+ end
+
+ it "The action's resources converge before the next resource converges" do
+ converge <<-EOM, __FILE__, __LINE__+1
+ #{resource_dsl} 'hi' do
+ foo 'foo!'
+ action :access_attribute
+ end
+
+ ruby_block 'wow' do
+ block do
+ ActionJackson.ruby_block_converged = ActionJackson.succeeded
+ end
+ end
+ EOM
+ expect(ActionJackson.ran_action).to eq :access_attribute
+ expect(ActionJackson.succeeded).to eq 'foo!'
+ expect(ActionJackson.ruby_block_converged).to eq 'foo!'
+ end
+ end
+
+ context "With resource 'action_jackson'" do
+ before(:context) {
+ class ActionJackson < Chef::Resource
+ use_automatic_resource_name
+ def foo(value=nil)
+ @foo = value if value
+ @foo
+ end
+ def blarghle(value=nil)
+ @blarghle = value if value
+ @blarghle
+ end
+
+ class <<self
+ attr_accessor :ran_action
+ attr_accessor :succeeded
+ attr_accessor :ruby_block_converged
+ end
+
+ public
+ def foo_public
+ 'foo_public!'
+ end
+ protected
+ def foo_protected
+ 'foo_protected!'
+ end
+ private
+ def foo_private
+ 'foo_private!'
+ end
+
+ public
+ action :access_recipe_dsl do
+ ActionJackson.ran_action = :access_recipe_dsl
+ ruby_block 'hi there' do
+ block do
+ ActionJackson.succeeded = true
+ end
+ end
+ end
+ action :access_attribute do
+ ActionJackson.ran_action = :access_attribute
+ ActionJackson.succeeded = foo
+ ActionJackson.succeeded += " #{blarghle}" if blarghle
+ ActionJackson.succeeded += " #{bar}" if respond_to?(:bar)
+ end
+ action :access_attribute2 do
+ ActionJackson.ran_action = :access_attribute2
+ ActionJackson.succeeded = foo
+ ActionJackson.succeeded += " #{blarghle}" if blarghle
+ ActionJackson.succeeded += " #{bar}" if respond_to?(:bar)
+ end
+ action :access_method do
+ ActionJackson.ran_action = :access_method
+ ActionJackson.succeeded = foo_public
+ end
+ action :access_protected_method do
+ ActionJackson.ran_action = :access_protected_method
+ ActionJackson.succeeded = foo_protected
+ end
+ action :access_private_method do
+ ActionJackson.ran_action = :access_private_method
+ ActionJackson.succeeded = foo_private
+ end
+ action :access_instance_variable do
+ ActionJackson.ran_action = :access_instance_variable
+ ActionJackson.succeeded = @foo
+ end
+ action :access_class_method do
+ ActionJackson.ran_action = :access_class_method
+ ActionJackson.succeeded = ActionJackson.ruby_block_converged
+ end
+ end
+ }
+ before(:each) {
+ ActionJackson.ran_action = :error
+ ActionJackson.succeeded = :error
+ ActionJackson.ruby_block_converged = :error
+ }
+
+ it_behaves_like "ActionJackson" do
+ let(:resource_dsl) { :action_jackson }
+ end
+
+ context "And 'action_jackgrandson' inheriting from ActionJackson and changing nothing" do
+ before(:context) {
+ class ActionJackgrandson < ActionJackson
+ use_automatic_resource_name
+ end
+ }
+
+ it_behaves_like "ActionJackson" do
+ let(:resource_dsl) { :action_jackgrandson }
+ end
+ end
+
+ context "And 'action_jackalope' inheriting from ActionJackson with an extra attribute and action" do
+ before(:context) {
+ class ActionJackalope < ActionJackson
+ use_automatic_resource_name
+
+ def foo(value=nil)
+ @foo = "#{value}alope" if value
+ @foo
+ end
+ def bar(value=nil)
+ @bar = "#{value}alope" if value
+ @bar
+ end
+ class <<self
+ attr_accessor :jackalope_ran
+ end
+ action :access_jackalope do
+ ActionJackalope.jackalope_ran = :access_jackalope
+ ActionJackalope.succeeded = "#{foo} #{blarghle} #{bar}"
+ end
+ action :access_attribute do
+ super()
+ ActionJackalope.jackalope_ran = :access_attribute
+ ActionJackalope.succeeded = ActionJackson.succeeded
+ end
+ end
+ }
+ before do
+ ActionJackalope.jackalope_ran = nil
+ end
+
+ context "action_jackson still behaves the same" do
+ it_behaves_like "ActionJackson" do
+ let(:resource_dsl) { :action_jackson }
+ end
+ end
+
+ it "The default action remains the same even though new actions were specified first" do
+ converge {
+ action_jackalope 'hi' do
+ foo 'foo!'
+ bar 'bar!'
+ end
+ }
+ expect(ActionJackson.ran_action).to eq :access_recipe_dsl
+ expect(ActionJackson.succeeded).to eq true
+ end
+
+ it "new actions run, and can access overridden, new, and overridden attributes" do
+ converge {
+ action_jackalope 'hi' do
+ foo 'foo!'
+ bar 'bar!'
+ blarghle 'blarghle!'
+ action :access_jackalope
+ end
+ }
+ expect(ActionJackalope.jackalope_ran).to eq :access_jackalope
+ expect(ActionJackalope.succeeded).to eq "foo!alope blarghle! bar!alope"
+ end
+
+ it "overridden actions run, call super, and can access overridden, new, and overridden attributes" do
+ converge {
+ action_jackalope 'hi' do
+ foo 'foo!'
+ bar 'bar!'
+ blarghle 'blarghle!'
+ action :access_attribute
+ end
+ }
+ expect(ActionJackson.ran_action).to eq :access_attribute
+ expect(ActionJackson.succeeded).to eq "foo!alope blarghle! bar!alope"
+ expect(ActionJackalope.jackalope_ran).to eq :access_attribute
+ expect(ActionJackalope.succeeded).to eq "foo!alope blarghle! bar!alope"
+ end
+
+ it "non-overridden actions run and can access overridden and non-overridden variables (but not necessarily new ones)" do
+ converge {
+ action_jackalope 'hi' do
+ foo 'foo!'
+ bar 'bar!'
+ blarghle 'blarghle!'
+ action :access_attribute2
+ end
+ }
+ expect(ActionJackson.ran_action).to eq :access_attribute2
+ expect(ActionJackson.succeeded).to eq("foo!alope blarghle! bar!alope").or(eq("foo!alope blarghle!"))
+ end
+ end
+ end
+
+ context "With a resource with no actions" do
+ before(:context) {
+ class NoActionJackson < Chef::Resource
+ use_automatic_resource_name
+
+ def foo(value=nil)
+ @foo = value if value
+ @foo
+ end
+
+ class <<self
+ attr_accessor :action_was
+ end
+ end
+ }
+ it "The default action is :nothing" do
+ converge {
+ no_action_jackson 'hi' do
+ foo 'foo!'
+ NoActionJackson.action_was = action
+ end
+ }
+ expect(NoActionJackson.action_was).to eq [:nothing]
+ end
+ end
+
+ context "With a resource with action a-b-c d" do
+ before(:context) {
+ class WeirdActionJackson < Chef::Resource
+ use_automatic_resource_name
+
+ class <<self
+ attr_accessor :action_was
+ end
+
+ action "a-b-c d" do
+ WeirdActionJackson.action_was = action
+ end
+ end
+ }
+
+ it "Running the action works" do
+ expect_recipe {
+ weird_action_jackson 'hi'
+ }.to be_up_to_date
+ expect(WeirdActionJackson.action_was).to eq :"a-b-c d"
+ end
+ end
+end
diff --git a/spec/integration/recipes/resource_converge_if_changed_spec.rb b/spec/integration/recipes/resource_converge_if_changed_spec.rb
new file mode 100644
index 0000000000..d00252a717
--- /dev/null
+++ b/spec/integration/recipes/resource_converge_if_changed_spec.rb
@@ -0,0 +1,423 @@
+require 'support/shared/integration/integration_helper'
+
+describe "Resource::ActionProvider#converge_if_changed" do
+ include IntegrationSupport
+
+ module Namer
+ extend self
+ attr_accessor :current_index
+ def incrementing_value
+ @incrementing_value += 1
+ @incrementing_value
+ end
+ attr_writer :incrementing_value
+ end
+
+ before(:all) { Namer.current_index = 1 }
+ before { Namer.current_index += 1 }
+ before { Namer.incrementing_value = 0 }
+
+ context "when the resource has identity, state and control properties" do
+ let(:resource_name) { :"converge_if_changed_dsl#{Namer.current_index}" }
+ let(:resource_class) {
+ result = Class.new(Chef::Resource) do
+ def self.to_s; resource_name; end
+ def self.inspect; resource_name.inspect; end
+ property :identity1, identity: true, default: 'default_identity1'
+ property :control1, desired_state: false, default: 'default_control1'
+ property :state1, default: 'default_state1'
+ property :state2, default: 'default_state2'
+ attr_accessor :converged
+ def initialize(*args)
+ super
+ @converged = 0
+ end
+ end
+ result.resource_name resource_name
+ result
+ }
+ let(:converged_recipe) { converge(converge_recipe) }
+ let(:resource) { converged_recipe.resources.first }
+
+ context "and converge_if_changed with no parameters" do
+ before :each do
+ resource_class.action :create do
+ converge_if_changed do
+ new_resource.converged += 1
+ end
+ end
+ end
+
+ context "and current_resource with state1=current, state2=current" do
+ before :each do
+ resource_class.load_current_value do
+ state1 'current_state1'
+ state2 'current_state2'
+ end
+ end
+
+ context "and nothing is set" do
+ let(:converge_recipe) { "#{resource_name} 'blah'" }
+
+ it "the resource updates nothing" do
+ expect(resource.converged).to eq 0
+ expect(resource.updated?).to be_falsey
+ expect(converged_recipe.stdout).to eq <<-EOM
+* #{resource_name}[blah] action create (up to date)
+ EOM
+ end
+ end
+
+ context "and state1 is set to a new value" do
+ let(:converge_recipe) {
+ <<-EOM
+ #{resource_name} 'blah' do
+ state1 'new_state1'
+ end
+ EOM
+ }
+
+ it "the resource updates state1" do
+ expect(resource.converged).to eq 1
+ expect(resource.updated?).to be_truthy
+ expect(converged_recipe.stdout).to eq <<-EOM
+* #{resource_name}[blah] action create
+ - update default_identity1
+ - set state1 to "new_state1" (was "current_state1")
+ EOM
+ end
+ end
+
+ context "and state1 and state2 are set to new values" do
+ let(:converge_recipe) {
+ <<-EOM
+ #{resource_name} 'blah' do
+ state1 'new_state1'
+ state2 'new_state2'
+ end
+ EOM
+ }
+
+ it "the resource updates state1 and state2" do
+ expect(resource.converged).to eq 1
+ expect(resource.updated?).to be_truthy
+ expect(converged_recipe.stdout).to eq <<-EOM
+* #{resource_name}[blah] action create
+ - update default_identity1
+ - set state1 to "new_state1" (was "current_state1")
+ - set state2 to "new_state2" (was "current_state2")
+EOM
+ end
+ end
+
+ context "and state1 is set to its current value but state2 is set to a new value" do
+ let(:converge_recipe) {
+ <<-EOM
+ #{resource_name} 'blah' do
+ state1 'current_state1'
+ state2 'new_state2'
+ end
+ EOM
+ }
+
+ it "the resource updates state2" do
+ expect(resource.converged).to eq 1
+ expect(resource.updated?).to be_truthy
+ expect(converged_recipe.stdout).to eq <<-EOM
+* #{resource_name}[blah] action create
+ - update default_identity1
+ - set state2 to "new_state2" (was "current_state2")
+EOM
+ end
+ end
+
+ context "and state1 and state2 are set to their current values" do
+ let(:converge_recipe) {
+ <<-EOM
+ #{resource_name} 'blah' do
+ state1 'current_state1'
+ state2 'current_state2'
+ end
+ EOM
+ }
+
+ it "the resource updates nothing" do
+ expect(resource.converged).to eq 0
+ expect(resource.updated?).to be_falsey
+ expect(converged_recipe.stdout).to eq <<-EOM
+* #{resource_name}[blah] action create (up to date)
+EOM
+ end
+ end
+
+ context "and identity1 and control1 are set to new values" do
+ let(:converge_recipe) {
+ <<-EOM
+ #{resource_name} 'blah' do
+ identity1 'new_identity1'
+ control1 'new_control1'
+ end
+ EOM
+ }
+
+ # Because the identity value is copied over to the new resource, by
+ # default they do not register as "changed"
+ it "the resource updates nothing" do
+ expect(resource.converged).to eq 0
+ expect(resource.updated?).to be_falsey
+ expect(converged_recipe.stdout).to eq <<-EOM
+* #{resource_name}[blah] action create (up to date)
+EOM
+ end
+ end
+ end
+
+ context "and current_resource with identity1=current, control1=current" do
+ before :each do
+ resource_class.load_current_value do
+ identity1 'current_identity1'
+ control1 'current_control1'
+ end
+ end
+
+ context "and identity1 and control1 are set to new values" do
+ let(:converge_recipe) {
+ <<-EOM
+ #{resource_name} 'blah' do
+ identity1 'new_identity1'
+ control1 'new_control1'
+ end
+ EOM
+ }
+
+ # Control values are not desired state and are therefore not considered
+ # a reason for converging.
+ it "the resource updates identity1" do
+ expect(resource.converged).to eq 1
+ expect(resource.updated?).to be_truthy
+ expect(converged_recipe.stdout).to eq <<-EOM
+* #{resource_name}[blah] action create
+ - update current_identity1
+ - set identity1 to "new_identity1" (was "current_identity1")
+ EOM
+ end
+ end
+ end
+
+ context "and has no current_resource" do
+ before :each do
+ resource_class.load_current_value do
+ current_value_does_not_exist!
+ end
+ end
+
+ context "and nothing is set" do
+ let(:converge_recipe) { "#{resource_name} 'blah'" }
+
+ it "the resource is created" do
+ expect(resource.converged).to eq 1
+ expect(resource.updated?).to be_truthy
+ expect(converged_recipe.stdout).to eq <<-EOM
+* #{resource_name}[blah] action create
+ - create default_identity1
+ - set identity1 to "default_identity1" (default value)
+ - set state1 to "default_state1" (default value)
+ - set state2 to "default_state2" (default value)
+EOM
+ end
+ end
+
+ context "and state1 and state2 are set" do
+ let(:converge_recipe) {
+ <<-EOM
+ #{resource_name} 'blah' do
+ state1 'new_state1'
+ state2 'new_state2'
+ end
+ EOM
+ }
+
+ it "the resource is created" do
+ expect(resource.converged).to eq 1
+ expect(resource.updated?).to be_truthy
+ expect(converged_recipe.stdout).to eq <<-EOM
+* #{resource_name}[blah] action create
+ - create default_identity1
+ - set identity1 to "default_identity1" (default value)
+ - set state1 to "new_state1"
+ - set state2 to "new_state2"
+EOM
+ end
+ end
+ end
+ end
+
+ context "and separate converge_if_changed :state1 and converge_if_changed :state2" do
+ before :each do
+ resource_class.action :create do
+ converge_if_changed :state1 do
+ new_resource.converged += 1
+ end
+ converge_if_changed :state2 do
+ new_resource.converged += 1
+ end
+ end
+ end
+
+ context "and current_resource with state1=current, state2=current" do
+ before :each do
+ resource_class.load_current_value do
+ state1 'current_state1'
+ state2 'current_state2'
+ end
+ end
+
+ context "and nothing is set" do
+ let(:converge_recipe) { "#{resource_name} 'blah'" }
+
+ it "the resource updates nothing" do
+ expect(resource.converged).to eq 0
+ expect(resource.updated?).to be_falsey
+ expect(converged_recipe.stdout).to eq <<-EOM
+* #{resource_name}[blah] action create (up to date)
+EOM
+ end
+ end
+
+ context "and state1 is set to a new value" do
+
+ let(:converge_recipe) {
+ <<-EOM
+ #{resource_name} 'blah' do
+ state1 'new_state1'
+ end
+ EOM
+ }
+
+ it "the resource updates state1" do
+ expect(resource.converged).to eq 1
+ expect(resource.updated?).to be_truthy
+ expect(converged_recipe.stdout).to eq <<-EOM
+* #{resource_name}[blah] action create
+ - update default_identity1
+ - set state1 to "new_state1" (was "current_state1")
+EOM
+ end
+ end
+
+ context "and state1 and state2 are set to new values" do
+ let(:converge_recipe) {
+ <<-EOM
+ #{resource_name} 'blah' do
+ state1 'new_state1'
+ state2 'new_state2'
+ end
+ EOM
+ }
+
+ it "the resource updates state1 and state2" do
+ expect(resource.converged).to eq 2
+ expect(resource.updated?).to be_truthy
+ expect(converged_recipe.stdout).to eq <<-EOM
+* #{resource_name}[blah] action create
+ - update default_identity1
+ - set state1 to "new_state1" (was "current_state1")
+ - update default_identity1
+ - set state2 to "new_state2" (was "current_state2")
+EOM
+ end
+ end
+
+ context "and state1 is set to its current value but state2 is set to a new value" do
+ let(:converge_recipe) {
+ <<-EOM
+ #{resource_name} 'blah' do
+ state1 'current_state1'
+ state2 'new_state2'
+ end
+ EOM
+ }
+
+ it "the resource updates state2" do
+ expect(resource.converged).to eq 1
+ expect(resource.updated?).to be_truthy
+ expect(converged_recipe.stdout).to eq <<-EOM
+* #{resource_name}[blah] action create
+ - update default_identity1
+ - set state2 to "new_state2" (was "current_state2")
+EOM
+ end
+ end
+
+ context "and state1 and state2 are set to their current values" do
+ let(:converge_recipe) {
+ <<-EOM
+ #{resource_name} 'blah' do
+ state1 'current_state1'
+ state2 'current_state2'
+ end
+ EOM
+ }
+
+ it "the resource updates nothing" do
+ expect(resource.converged).to eq 0
+ expect(resource.updated?).to be_falsey
+ expect(converged_recipe.stdout).to eq <<-EOM
+* #{resource_name}[blah] action create (up to date)
+EOM
+ end
+ end
+ end
+
+ context "and no current_resource" do
+ before :each do
+ resource_class.load_current_value do
+ current_value_does_not_exist!
+ end
+ end
+
+ context "and nothing is set" do
+ let(:converge_recipe) {
+ "#{resource_name} 'blah'"
+ }
+
+ it "the resource is created" do
+ expect(resource.converged).to eq 2
+ expect(resource.updated?).to be_truthy
+ expect(converged_recipe.stdout).to eq <<-EOM
+* #{resource_name}[blah] action create
+ - create default_identity1
+ - set state1 to "default_state1" (default value)
+ - create default_identity1
+ - set state2 to "default_state2" (default value)
+EOM
+ end
+ end
+
+ context "and state1 and state2 are set to new values" do
+ let(:converge_recipe) {
+ <<-EOM
+ #{resource_name} 'blah' do
+ state1 'new_state1'
+ state2 'new_state2'
+ end
+ EOM
+ }
+
+ it "the resource is created" do
+ expect(resource.converged).to eq 2
+ expect(resource.updated?).to be_truthy
+ expect(converged_recipe.stdout).to eq <<-EOM
+* #{resource_name}[blah] action create
+ - create default_identity1
+ - set state1 to "new_state1"
+ - create default_identity1
+ - set state2 to "new_state2"
+EOM
+ end
+ end
+ end
+ end
+
+ end
+end
diff --git a/spec/integration/recipes/resource_load_spec.rb b/spec/integration/recipes/resource_load_spec.rb
new file mode 100644
index 0000000000..c29b877b59
--- /dev/null
+++ b/spec/integration/recipes/resource_load_spec.rb
@@ -0,0 +1,206 @@
+require 'support/shared/integration/integration_helper'
+
+describe "Resource.load_current_value" do
+ include IntegrationSupport
+
+ module Namer
+ extend self
+ attr_accessor :current_index
+ def incrementing_value
+ @incrementing_value += 1
+ @incrementing_value
+ end
+ attr_writer :incrementing_value
+ end
+
+ before(:all) { Namer.current_index = 1 }
+ before { Namer.current_index += 1 }
+ before { Namer.incrementing_value = 0 }
+
+ let(:resource_name) { :"load_current_value_dsl#{Namer.current_index}" }
+ let(:resource_class) {
+ result = Class.new(Chef::Resource) do
+ def self.to_s; resource_name; end
+ def self.inspect; resource_name.inspect; end
+ property :x, default: lazy { "default #{Namer.incrementing_value}" }
+ def self.created_x=(value)
+ @created = value
+ end
+ def self.created_x
+ @created
+ end
+ action :create do
+ new_resource.class.created_x = x
+ end
+ end
+ result.resource_name resource_name
+ result
+ }
+
+ # Pull on resource_class to initialize it
+ before { resource_class }
+
+ context "with a resource with load_current_value" do
+ before :each do
+ resource_class.load_current_value do
+ x "loaded #{Namer.incrementing_value} (#{self.class.properties.sort_by { |name,p| name }.
+ select { |name,p| p.is_set?(self) }.
+ map { |name,p| "#{name}=#{p.get(self)}" }.
+ join(", ") })"
+ end
+ end
+
+ context "and a resource with x set to a desired value" do
+ let(:resource) do
+ e = self
+ r = nil
+ converge {
+ r = public_send(e.resource_name, 'blah') do
+ x 'desired'
+ end
+ }
+ r
+ end
+
+ it "current_resource is passed name but not x" do
+ expect(resource.current_resource.x).to eq 'loaded 2 (name=blah)'
+ end
+
+ it "resource.current_resource returns a different resource" do
+ expect(resource.current_resource.x).to eq 'loaded 2 (name=blah)'
+ expect(resource.x).to eq 'desired'
+ end
+
+ it "resource.current_resource constructs the resource anew each time" do
+ expect(resource.current_resource.x).to eq 'loaded 2 (name=blah)'
+ expect(resource.current_resource.x).to eq 'loaded 3 (name=blah)'
+ end
+
+ it "the provider accesses the current value of x" do
+ expect(resource.class.created_x).to eq 'desired'
+ end
+
+ context "and identity: :i and :d with desired_state: false" do
+ before {
+ resource_class.class_eval do
+ property :i, identity: true
+ property :d, desired_state: false
+ end
+ }
+
+ before {
+ resource.i 'desired_i'
+ resource.d 'desired_d'
+ }
+
+ it "i, name and d are passed to load_current_value, but not x" do
+ expect(resource.current_resource.x).to eq 'loaded 2 (d=desired_d, i=desired_i, name=blah)'
+ end
+ end
+
+ context "and name_property: :i and :d with desired_state: false" do
+ before {
+ resource_class.class_eval do
+ property :i, name_property: true
+ property :d, desired_state: false
+ end
+ }
+
+ before {
+ resource.i 'desired_i'
+ resource.d 'desired_d'
+ }
+
+ it "i, name and d are passed to load_current_value, but not x" do
+ expect(resource.current_resource.x).to eq 'loaded 2 (d=desired_d, i=desired_i, name=blah)'
+ end
+ end
+ end
+
+ context "and a resource with no values set" do
+ let(:resource) do
+ e = self
+ r = nil
+ converge {
+ r = public_send(e.resource_name, 'blah') do
+ end
+ }
+ r
+ end
+
+ it "the provider accesses values from load_current_value" do
+ expect(resource.class.created_x).to eq 'loaded 1 (name=blah)'
+ end
+ end
+
+ let (:subresource_name) {
+ :"load_current_value_subresource_dsl#{Namer.current_index}"
+ }
+ let (:subresource_class) {
+ r = Class.new(resource_class) do
+ property :y, default: lazy { "default_y #{Namer.incrementing_value}" }
+ end
+ r.resource_name subresource_name
+ r
+ }
+
+ # Pull on subresource_class to initialize it
+ before { subresource_class }
+
+ let(:subresource) do
+ e = self
+ r = nil
+ converge {
+ r = public_send(e.subresource_name, 'blah') do
+ x 'desired'
+ end
+ }
+ r
+ end
+
+ context "and a child resource class with no load_current_value" do
+ it "the parent load_current_value is used" do
+ expect(subresource.current_resource.x).to eq 'loaded 2 (name=blah)'
+ end
+ it "load_current_value yields a copy of the child class" do
+ expect(subresource.current_resource).to be_kind_of(subresource_class)
+ end
+ end
+
+ context "And a child resource class with load_current_value" do
+ before {
+ subresource_class.load_current_value do
+ y "loaded_y #{Namer.incrementing_value} (#{self.class.properties.sort_by { |name,p| name }.
+ select { |name,p| p.is_set?(self) }.
+ map { |name,p| "#{name}=#{p.get(self)}" }.
+ join(", ") })"
+ end
+ }
+
+ it "the overridden load_current_value is used" do
+ current_resource = subresource.current_resource
+ expect(current_resource.x).to eq 'default 3'
+ expect(current_resource.y).to eq 'loaded_y 2 (name=blah)'
+ end
+ end
+
+ context "and a child resource class with load_current_value calling super()" do
+ before {
+ subresource_class.load_current_value do
+ super()
+ y "loaded_y #{Namer.incrementing_value} (#{self.class.properties.sort_by { |name,p| name }.
+ select { |name,p| p.is_set?(self) }.
+ map { |name,p| "#{name}=#{p.get(self)}" }.
+ join(", ") })"
+ end
+ }
+
+ it "the original load_current_value is called as well as the child one" do
+ current_resource = subresource.current_resource
+ expect(current_resource.x).to eq 'loaded 3 (name=blah)'
+ expect(current_resource.y).to eq 'loaded_y 4 (name=blah, x=loaded 3 (name=blah))'
+ end
+ end
+ end
+
+end
diff --git a/spec/integration/solo/solo_spec.rb b/spec/integration/solo/solo_spec.rb
index 41f5f5506f..f45933c799 100644
--- a/spec/integration/solo/solo_spec.rb
+++ b/spec/integration/solo/solo_spec.rb
@@ -15,6 +15,8 @@ describe "chef-solo" do
let(:cookbook_ancient_100_metadata_rb) { cb_metadata("ancient", "1.0.0") }
+ let(:chef_solo) { "ruby bin/chef-solo --minimal-ohai" }
+
when_the_repository "has a cookbook with a basic recipe" do
before do
file 'cookbooks/x/metadata.rb', cookbook_x_100_metadata_rb
@@ -26,7 +28,7 @@ describe "chef-solo" do
cookbook_path "#{path_to('cookbooks')}"
file_cache_path "#{path_to('config/cache')}"
EOM
- result = shell_out("ruby bin/chef-solo -c \"#{path_to('config/solo.rb')}\" -o 'x::default' -l debug", :cwd => chef_dir)
+ result = shell_out("#{chef_solo} -c \"#{path_to('config/solo.rb')}\" -o 'x::default' -l debug", :cwd => chef_dir)
result.error!
expect(result.stdout).to include("ITWORKS")
end
@@ -41,7 +43,7 @@ EOM
{"run_list":["x::default"]}
E
- result = shell_out("ruby bin/chef-solo -c \"#{path_to('config/solo.rb')}\" -j '#{path_to('config/node.json')}' -l debug", :cwd => chef_dir)
+ result = shell_out("#{chef_solo} -c \"#{path_to('config/solo.rb')}\" -j '#{path_to('config/node.json')}' -l debug", :cwd => chef_dir)
result.error!
expect(result.stdout).to include("ITWORKS")
end
@@ -62,7 +64,7 @@ E
cookbook_path "#{path_to('cookbooks')}"
file_cache_path "#{path_to('config/cache')}"
EOM
- result = shell_out("ruby bin/chef-solo -c \"#{path_to('config/solo.rb')}\" -o 'x::default' -l debug", :cwd => chef_dir)
+ result = shell_out("#{chef_solo} -c \"#{path_to('config/solo.rb')}\" -o 'x::default' -l debug", :cwd => chef_dir)
expect(result.exitstatus).to eq(0) # For CHEF-5120 this becomes 1
expect(result.stdout).to include("WARN: MissingCookbookDependency")
end
@@ -95,14 +97,14 @@ EOM
chef_dir = File.join(File.dirname(__FILE__), "..", "..", "..")
# Instantiate the first chef-solo run
- s1 = Process.spawn("ruby bin/chef-solo -c \"#{path_to('config/solo.rb')}\" -o 'x::default' \
+ s1 = Process.spawn("#{chef_solo} -c \"#{path_to('config/solo.rb')}\" -o 'x::default' \
-l debug -L #{path_to('logs/runs.log')}", :chdir => chef_dir)
# Give it some time to progress
sleep 1
# Instantiate the second chef-solo run
- s2 = Process.spawn("ruby bin/chef-solo -c \"#{path_to('config/solo.rb')}\" -o 'x::default' \
+ s2 = Process.spawn("#{chef_solo} -c \"#{path_to('config/solo.rb')}\" -o 'x::default' \
-l debug -L #{path_to('logs/runs.log')}", :chdir => chef_dir)
Process.waitpid(s1)
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 8888efc424..aadf55f64b 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -54,6 +54,9 @@ Dir['lib/chef/knife/**/*.rb'].
map {|f| f.gsub(%r[\.rb$], '') }.
each {|f| require f }
+require 'chef/resource_resolver'
+require 'chef/provider_resolver'
+
require 'chef/mixins'
require 'chef/dsl'
require 'chef/application'
@@ -84,12 +87,14 @@ Dir["spec/support/**/*.rb"].
OHAI_SYSTEM = Ohai::System.new
OHAI_SYSTEM.all_plugins("platform")
-TEST_PLATFORM =
- (OHAI_SYSTEM['platform'] ||
- 'unknown_test_platform').dup.freeze
-TEST_PLATFORM_VERSION =
- (OHAI_SYSTEM['platform_version'] ||
- 'unknown_platform_version').dup.freeze
+test_node = Chef::Node.new
+test_node.automatic['os'] = (OHAI_SYSTEM['os'] || 'unknown_os').dup.freeze
+test_node.automatic['platform_family'] = (OHAI_SYSTEM['platform_family'] || 'unknown_platform_family').dup.freeze
+test_node.automatic['platform'] = (OHAI_SYSTEM['platform'] || 'unknown_platform').dup.freeze
+test_node.automatic['platform_version'] = (OHAI_SYSTEM['platform_version'] || 'unknown_platform_version').dup.freeze
+TEST_NODE = test_node.freeze
+TEST_PLATFORM = TEST_NODE['platform']
+TEST_PLATFORM_VERSION = TEST_NODE['platform_version']
RSpec.configure do |config|
config.include(Matchers)
@@ -110,19 +115,27 @@ RSpec.configure do |config|
# Tests that randomly fail, but may have value.
config.filter_run_excluding :volatile => true
config.filter_run_excluding :volatile_on_solaris => true if solaris?
+ config.filter_run_excluding :volatile_from_verify => false
+
+ config.filter_run_excluding :skip_appveyor => true if ENV["APPVEYOR"]
+ config.filter_run_excluding :appveyor_only => true unless ENV["APPVEYOR"]
- # Add jruby filters here
config.filter_run_excluding :windows_only => true unless windows?
config.filter_run_excluding :not_supported_on_mac_osx_106 => true if mac_osx_106?
+ config.filter_run_excluding :not_supported_on_mac_osx=> true if mac_osx?
+ config.filter_run_excluding :mac_osx_only=> true if !mac_osx?
config.filter_run_excluding :not_supported_on_win2k3 => true if windows_win2k3?
config.filter_run_excluding :not_supported_on_solaris => true if solaris?
config.filter_run_excluding :win2k3_only => true unless windows_win2k3?
config.filter_run_excluding :windows_2008r2_or_later => true unless windows_2008r2_or_later?
config.filter_run_excluding :windows64_only => true unless windows64?
config.filter_run_excluding :windows32_only => true unless windows32?
+ config.filter_run_excluding :ruby64_only => true unless ruby_64bit?
+ config.filter_run_excluding :ruby32_only => true unless ruby_32bit?
config.filter_run_excluding :windows_powershell_dsc_only => true unless windows_powershell_dsc?
config.filter_run_excluding :windows_powershell_no_dsc_only => true unless ! windows_powershell_dsc?
config.filter_run_excluding :windows_domain_joined_only => true unless windows_domain_joined?
+ config.filter_run_excluding :windows_not_domain_joined_only => true if windows_domain_joined?
config.filter_run_excluding :solaris_only => true unless solaris?
config.filter_run_excluding :system_windows_service_gem_only => true unless system_windows_service_gem?
config.filter_run_excluding :unix_only => true unless unix?
@@ -143,7 +156,7 @@ RSpec.configure do |config|
config.filter_run_excluding :aes_256_gcm_only => true unless aes_256_gcm?
config.filter_run_excluding :broken => true
- running_platform_arch = `uname -m`.strip
+ running_platform_arch = `uname -m`.strip unless windows?
config.filter_run_excluding :arch => lambda {|target_arch|
running_platform_arch != target_arch
@@ -154,13 +167,17 @@ RSpec.configure do |config|
config.filter_run_excluding :provider => lambda {|criteria|
type, target_provider = criteria.first
- platform = TEST_PLATFORM.dup
- platform_version = TEST_PLATFORM_VERSION.dup
-
- begin
- provider_for_running_platform = Chef::Platform.find_provider(platform, platform_version, type)
- provider_for_running_platform != target_provider
- rescue ArgumentError # no provider for platform
+ node = TEST_NODE.dup
+ resource_class = Chef::ResourceResolver.resolve(type, node: node)
+ if resource_class
+ resource = resource_class.new('test', Chef::RunContext.new(node, nil, nil))
+ begin
+ provider = resource.provider_for_action(Array(resource_class.default_action).first)
+ provider.class != target_provider
+ rescue Chef::Exceptions::ProviderNotFound # no provider for platform
+ true
+ end
+ else
true
end
}
@@ -168,6 +185,8 @@ RSpec.configure do |config|
config.run_all_when_everything_filtered = true
config.before(:each) do
+ Chef.reset!
+
Chef::Config.reset
# By default, treat deprecation warnings as errors in tests.
diff --git a/spec/support/key_helpers.rb b/spec/support/key_helpers.rb
new file mode 100644
index 0000000000..076f709380
--- /dev/null
+++ b/spec/support/key_helpers.rb
@@ -0,0 +1,104 @@
+#
+# Author:: Tyler Cloke (<tyler@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+shared_examples_for "a knife key command" do
+ let(:stderr) { StringIO.new }
+ let(:command) do
+ c = described_class.new([])
+ c.ui.config[:disable_editing] = true
+ allow(c.ui).to receive(:stderr).and_return(stderr)
+ allow(c.ui).to receive(:stdout).and_return(stderr)
+ allow(c).to receive(:show_usage)
+ c
+ end
+
+ context "before apply_params! is called" do
+ context "when apply_params! is called with invalid args (missing actor)" do
+ let(:params) { [] }
+ it "shows the usage" do
+ expect(command).to receive(:show_usage)
+ expect { command.apply_params!(params) }.to exit_with_code(1)
+ end
+
+ it "outputs the proper error" do
+ expect { command.apply_params!(params) }.to exit_with_code(1)
+ expect(stderr.string).to include(command.actor_missing_error)
+ end
+
+ it "exits 1" do
+ expect { command.apply_params!(params) }.to exit_with_code(1)
+ end
+ end
+ end # before apply_params! is called
+
+ context "after apply_params! is called with valid args" do
+ before do
+ command.apply_params!(params)
+ end
+
+ it "properly defines the actor" do
+ expect(command.actor).to eq("charmander")
+ end
+ end # after apply_params! is called with valid args
+
+ context "when the command is run" do
+ before do
+ allow(command).to receive(:service_object).and_return(service_object)
+ allow(command).to receive(:name_args).and_return(["charmander"])
+ end
+
+ context "when the command is successful" do
+ before do
+ expect(service_object).to receive(:run)
+ end
+ end
+ end
+end # a knife key command
+
+shared_examples_for "a knife key command with a keyname as the second arg" do
+ let(:stderr) { StringIO.new }
+ let(:command) do
+ c = described_class.new([])
+ c.ui.config[:disable_editing] = true
+ allow(c.ui).to receive(:stderr).and_return(stderr)
+ allow(c.ui).to receive(:stdout).and_return(stderr)
+ allow(c).to receive(:show_usage)
+ c
+ end
+
+ context "before apply_params! is called" do
+ context "when apply_params! is called with invalid args (missing keyname)" do
+ let(:params) { ["charmander"] }
+ it "shows the usage" do
+ expect(command).to receive(:show_usage)
+ expect { command.apply_params!(params) }.to exit_with_code(1)
+ end
+
+ it "outputs the proper error" do
+ expect { command.apply_params!(params) }.to exit_with_code(1)
+ expect(stderr.string).to include(command.keyname_missing_error)
+ end
+
+ it "exits 1" do
+ expect { command.apply_params!(params) }.to exit_with_code(1)
+ end
+ end
+ end # before apply_params! is called
+end
diff --git a/spec/support/lib/chef/provider/openldap_includer.rb b/spec/support/lib/chef/provider/openldap_includer.rb
new file mode 100644
index 0000000000..afb0c7cf01
--- /dev/null
+++ b/spec/support/lib/chef/provider/openldap_includer.rb
@@ -0,0 +1,29 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class Chef
+ class Provider
+ class OpenldapIncluder < Chef::Provider::LWRPBase
+ provides :openldap_includer
+
+ def action_run
+ include_recipe "openldap::default"
+ end
+ end
+ end
+end
diff --git a/spec/support/lib/chef/resource/cat.rb b/spec/support/lib/chef/resource/cat.rb
index ecca50cb53..efc78aa59c 100644
--- a/spec/support/lib/chef/resource/cat.rb
+++ b/spec/support/lib/chef/resource/cat.rb
@@ -23,7 +23,6 @@ class Chef
attr_accessor :action
def initialize(name, run_context=nil)
- @resource_name = :cat
super
@action = "sell"
end
diff --git a/spec/support/lib/chef/resource/one_two_three_four.rb b/spec/support/lib/chef/resource/one_two_three_four.rb
index 296d2cd970..8f273a0cda 100644
--- a/spec/support/lib/chef/resource/one_two_three_four.rb
+++ b/spec/support/lib/chef/resource/one_two_three_four.rb
@@ -19,12 +19,8 @@
class Chef
class Resource
class OneTwoThreeFour < Chef::Resource
- attr_reader :i_can_count
- def initialize(name, run_context)
- @resource_name = :one_two_three_four
- super
- end
+ attr_reader :i_can_count
def i_can_count(tf)
@i_can_count = tf
diff --git a/spec/support/lib/chef/resource/openldap_includer.rb b/spec/support/lib/chef/resource/openldap_includer.rb
new file mode 100644
index 0000000000..6f443b4c7c
--- /dev/null
+++ b/spec/support/lib/chef/resource/openldap_includer.rb
@@ -0,0 +1,27 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008, 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+class Chef
+ class Resource
+ class OpenldapIncluder < Chef::Resource::LWRPBase
+ allowed_actions :run
+ default_action :run
+ end
+ end
+end
diff --git a/spec/support/lib/chef/resource/with_state.rb b/spec/support/lib/chef/resource/with_state.rb
index 226de0a6d2..773ae7ddb8 100644
--- a/spec/support/lib/chef/resource/with_state.rb
+++ b/spec/support/lib/chef/resource/with_state.rb
@@ -23,15 +23,6 @@ class Chef
class Resource
class WithState < Chef::Resource
attr_accessor :state
-
- def initialize(name, run_context=nil)
- @resource_name = :with_state
- super
- end
-
- def state
- @state
- end
end
end
end
diff --git a/spec/support/lib/chef/resource/zen_follower.rb b/spec/support/lib/chef/resource/zen_follower.rb
index ddc289e48d..155e6ae729 100644
--- a/spec/support/lib/chef/resource/zen_follower.rb
+++ b/spec/support/lib/chef/resource/zen_follower.rb
@@ -24,11 +24,6 @@ class Chef
provides :follower, platform: "zen"
- def initialize(name, run_context=nil)
- @resource_name = :zen_follower
- super
- end
-
def master(arg=nil)
if !arg.nil?
@master = arg
diff --git a/spec/support/lib/chef/resource/zen_master.rb b/spec/support/lib/chef/resource/zen_master.rb
index d47d174e28..4106549d79 100644
--- a/spec/support/lib/chef/resource/zen_master.rb
+++ b/spec/support/lib/chef/resource/zen_master.rb
@@ -22,13 +22,10 @@ require 'chef/json_compat'
class Chef
class Resource
class ZenMaster < Chef::Resource
+ allowed_actions :win, :score
+
attr_reader :peace
- def initialize(name, run_context=nil)
- @resource_name = :zen_master
- super
- allowed_actions << :win << :score
- end
def peace(tf)
@peace = tf
diff --git a/spec/support/mock/platform.rb b/spec/support/mock/platform.rb
index ab2c19baff..7eae82fe7d 100644
--- a/spec/support/mock/platform.rb
+++ b/spec/support/mock/platform.rb
@@ -6,7 +6,7 @@
# testing code that mixes in platform specific modules like +Chef::Mixin::Securable+
# or +Chef::FileAccessControl+
def platform_mock(platform = :unix, &block)
- allow(Chef::Platform).to receive(:windows?).and_return(platform == :windows ? true : false)
+ allow(ChefConfig).to receive(:windows?).and_return(platform == :windows ? true : false)
ENV['SYSTEMDRIVE'] = (platform == :windows ? 'C:' : nil)
if platform == :windows
diff --git a/spec/support/platform_helpers.rb b/spec/support/platform_helpers.rb
index a412fe38e1..1cfad05172 100644
--- a/spec/support/platform_helpers.rb
+++ b/spec/support/platform_helpers.rb
@@ -26,6 +26,14 @@ def ruby_20?
!!(RUBY_VERSION =~ /^2.0/)
end
+def ruby_64bit?
+ !!(RbConfig::CONFIG['host_cpu'] =~ /x86_64/)
+end
+
+def ruby_32bit?
+ !!(RbConfig::CONFIG['host_cpu'] =~ /i686/)
+end
+
def windows?
!!(RUBY_PLATFORM =~ /mswin|mingw|windows/)
end
@@ -88,6 +96,20 @@ def mac_osx_106?
false
end
+def mac_osx?
+ if File.exists? "/usr/bin/sw_vers"
+ result = ShellHelpers.shell_out("/usr/bin/sw_vers")
+ result.stdout.each_line do |line|
+ if line =~ /^ProductName:\sMac OS X.*$/
+ return true
+ end
+ end
+ end
+
+ false
+end
+
+
# detects if the hardware is 64-bit (evaluates to true in "WOW64" mode in a 32-bit app on a 64-bit system)
def windows64?
windows? && ( ENV['PROCESSOR_ARCHITECTURE'] == 'AMD64' || ENV['PROCESSOR_ARCHITEW6432'] == 'AMD64' )
diff --git a/spec/support/shared/context/client.rb b/spec/support/shared/context/client.rb
new file mode 100644
index 0000000000..eb537e9889
--- /dev/null
+++ b/spec/support/shared/context/client.rb
@@ -0,0 +1,277 @@
+
+require 'spec_helper'
+
+# Stubs a basic client object
+shared_context "client" do
+ let(:fqdn) { "hostname.example.org" }
+ let(:hostname) { "hostname" }
+ let(:machinename) { "machinename.example.org" }
+ let(:platform) { "example-platform" }
+ let(:platform_version) { "example-platform-1.0" }
+
+ let(:ohai_data) do
+ {
+ :fqdn => fqdn,
+ :hostname => hostname,
+ :machinename => machinename,
+ :platform => platform,
+ :platform_version => platform_version
+ }
+ end
+
+ let(:ohai_system) do
+ ohai = instance_double("Ohai::System", :all_plugins => true, :data => ohai_data)
+ allow(ohai).to receive(:[]) do |k|
+ ohai_data[k]
+ end
+ ohai
+ end
+
+ let(:node) do
+ Chef::Node.new.tap do |n|
+ n.name(fqdn)
+ n.chef_environment("_default")
+ end
+ end
+
+ let(:json_attribs) { nil }
+ let(:client_opts) { {} }
+
+ let(:client) do
+ Chef::Config[:event_loggers] = []
+ Chef::Client.new(json_attribs, client_opts).tap do |c|
+ c.node = node
+ end
+ end
+
+ before do
+ Chef::Log.logger = Logger.new(StringIO.new)
+
+ # Node/Ohai data
+ #Chef::Config[:node_name] = fqdn
+ allow(Ohai::System).to receive(:new).and_return(ohai_system)
+ end
+end
+
+# Stubs a client for a client run.
+# Requires a client object be defined in the scope of this included context.
+# e.g.:
+# describe "some functionality" do
+# include_context "client"
+# include_context "a client run"
+# ...
+# end
+shared_context "a client run" do
+ let(:stdout) { StringIO.new }
+ let(:stderr) { StringIO.new }
+
+ let(:api_client_exists?) { false }
+ let(:enable_fork) { false }
+
+ let(:http_cookbook_sync) { double("Chef::REST (cookbook sync)") }
+ let(:http_node_load) { double("Chef::REST (node)") }
+ let(:http_node_save) { double("Chef::REST (node save)") }
+
+ let(:runner) { instance_double("Chef::Runner") }
+ let(:audit_runner) { instance_double("Chef::Audit::Runner", :failed? => false) }
+
+ def stub_for_register
+ # --Client.register
+ # Make sure Client#register thinks the client key doesn't
+ # exist, so it tries to register and create one.
+ allow(File).to receive(:exists?).and_call_original
+ expect(File).to receive(:exists?).
+ with(Chef::Config[:client_key]).
+ exactly(:once).
+ and_return(api_client_exists?)
+
+ unless api_client_exists?
+ # Client.register will register with the validation client name.
+ expect_any_instance_of(Chef::ApiClient::Registration).to receive(:run)
+ end
+ end
+
+ def stub_for_node_load
+ # Client.register will then turn around create another
+ # Chef::REST object, this time with the client key it got from the
+ # previous step.
+ expect(Chef::REST).to receive(:new).
+ with(Chef::Config[:chef_server_url], fqdn, Chef::Config[:client_key]).
+ exactly(:once).
+ and_return(http_node_load)
+
+ # --Client#build_node
+ # looks up the node, which we will return, then later saves it.
+ expect(Chef::Node).to receive(:find_or_create).with(fqdn).and_return(node)
+
+ # --ResourceReporter#node_load_completed
+ # gets a run id from the server for storing resource history
+ # (has its own tests, so stubbing it here.)
+ expect_any_instance_of(Chef::ResourceReporter).to receive(:node_load_completed)
+ end
+
+ def stub_for_sync_cookbooks
+ # --Client#setup_run_context
+ # ---Client#sync_cookbooks -- downloads the list of cookbooks to sync
+ #
+ expect_any_instance_of(Chef::CookbookSynchronizer).to receive(:sync_cookbooks)
+ expect(Chef::REST).to receive(:new).with(Chef::Config[:chef_server_url]).and_return(http_cookbook_sync)
+ expect(http_cookbook_sync).to receive(:post).
+ with("environments/_default/cookbook_versions", {:run_list => []}).
+ and_return({})
+ end
+
+ def stub_for_converge
+ # define me
+ end
+
+ def stub_for_audit
+ # define me
+ end
+
+ def stub_for_node_save
+ # define me
+ end
+
+ def stub_for_run
+ # define me
+ end
+
+ before do
+ Chef::Config[:client_fork] = enable_fork
+ Chef::Config[:cache_path] = windows? ? 'C:\chef' : '/var/chef'
+ Chef::Config[:why_run] = false
+ Chef::Config[:audit_mode] = :enabled
+
+ stub_const("Chef::Client::STDOUT_FD", stdout)
+ stub_const("Chef::Client::STDERR_FD", stderr)
+
+ stub_for_register
+ stub_for_node_load
+ stub_for_sync_cookbooks
+ stub_for_converge
+ stub_for_audit
+ stub_for_node_save
+
+ expect_any_instance_of(Chef::RunLock).to receive(:acquire)
+ expect_any_instance_of(Chef::RunLock).to receive(:save_pid)
+ expect_any_instance_of(Chef::RunLock).to receive(:release)
+
+ # Post conditions: check that node has been filled in correctly
+ expect(client).to receive(:run_started)
+
+ stub_for_run
+ end
+end
+
+shared_context "converge completed" do
+ def stub_for_converge
+ # --Client#converge
+ expect(Chef::Runner).to receive(:new).and_return(runner)
+ expect(runner).to receive(:converge).and_return(true)
+ end
+
+ def stub_for_node_save
+ allow(node).to receive(:data_for_save).and_return(node.for_json)
+
+ # --Client#save_updated_node
+ expect(Chef::REST).to receive(:new).with(Chef::Config[:chef_server_url], fqdn, Chef::Config[:client_key], validate_utf8: false).and_return(http_node_save)
+ expect(http_node_save).to receive(:put_rest).with("nodes/#{fqdn}", node.for_json).and_return(true)
+ end
+end
+
+shared_context "converge failed" do
+ let(:converge_error) do
+ err = Chef::Exceptions::UnsupportedAction.new("Action unsupported")
+ err.set_backtrace([ "/path/recipe.rb:15", "/path/recipe.rb:12" ])
+ err
+ end
+
+ def stub_for_converge
+ expect(Chef::Runner).to receive(:new).and_return(runner)
+ expect(runner).to receive(:converge).and_raise(converge_error)
+ end
+
+ def stub_for_node_save
+ expect(client).to_not receive(:save_updated_node)
+ end
+end
+
+shared_context "audit phase completed" do
+ def stub_for_audit
+ # -- Client#run_audits
+ expect(Chef::Audit::Runner).to receive(:new).and_return(audit_runner)
+ expect(audit_runner).to receive(:run).and_return(true)
+ expect(client.events).to receive(:audit_phase_complete)
+ end
+end
+
+shared_context "audit phase failed with error" do
+ let(:audit_error) do
+ err = RuntimeError.new("Unexpected audit error")
+ err.set_backtrace([ "/path/recipe.rb:57", "/path/recipe.rb:55" ])
+ err
+ end
+
+ def stub_for_audit
+ expect(Chef::Audit::Runner).to receive(:new).and_return(audit_runner)
+ expect(Chef::Audit::Logger).to receive(:read_buffer).and_return("Audit mode output!")
+ expect(audit_runner).to receive(:run).and_raise(audit_error)
+ expect(client.events).to receive(:audit_phase_failed).with(audit_error, "Audit mode output!")
+ end
+end
+
+shared_context "audit phase completed with failed controls" do
+ let(:audit_runner) { instance_double("Chef::Audit::Runner", :failed? => true,
+ :num_failed => 1, :num_total => 3) }
+
+ let(:audit_error) do
+ err = Chef::Exceptions::AuditsFailed.new(audit_runner.num_failed, audit_runner.num_total)
+ err.set_backtrace([ "/path/recipe.rb:108", "/path/recipe.rb:103" ])
+ err
+ end
+
+ def stub_for_audit
+ expect(Chef::Audit::Runner).to receive(:new).and_return(audit_runner)
+ expect(Chef::Audit::Logger).to receive(:read_buffer).and_return("Audit mode output!")
+ expect(audit_runner).to receive(:run)
+ expect(Chef::Exceptions::AuditsFailed).to receive(:new).with(
+ audit_runner.num_failed, audit_runner.num_total
+ ).and_return(audit_error)
+ expect(client.events).to receive(:audit_phase_failed).with(audit_error, "Audit mode output!")
+ end
+end
+
+shared_context "run completed" do
+ def stub_for_run
+ expect(client).to receive(:run_completed_successfully)
+
+ # --ResourceReporter#run_completed
+ # updates the server with the resource history
+ # (has its own tests, so stubbing it here.)
+ expect_any_instance_of(Chef::ResourceReporter).to receive(:run_completed)
+ # --AuditReporter#run_completed
+ # posts the audit data to server.
+ # (has its own tests, so stubbing it here.)
+ expect_any_instance_of(Chef::Audit::AuditReporter).to receive(:run_completed)
+ end
+end
+
+shared_context "run failed" do
+ def stub_for_run
+ expect(client).to receive(:run_failed)
+
+ # --ResourceReporter#run_completed
+ # updates the server with the resource history
+ # (has its own tests, so stubbing it here.)
+ expect_any_instance_of(Chef::ResourceReporter).to receive(:run_failed)
+ # --AuditReporter#run_completed
+ # posts the audit data to server.
+ # (has its own tests, so stubbing it here.)
+ expect_any_instance_of(Chef::Audit::AuditReporter).to receive(:run_failed)
+ end
+
+ before do
+ expect(Chef::Application).to receive(:debug_stacktrace).with an_instance_of(Chef::Exceptions::RunFailedWrappingError)
+ end
+end
diff --git a/spec/support/shared/examples/client.rb b/spec/support/shared/examples/client.rb
new file mode 100644
index 0000000000..330cb40ac6
--- /dev/null
+++ b/spec/support/shared/examples/client.rb
@@ -0,0 +1,53 @@
+
+require 'spec_helper'
+require 'spec/support/shared/context/client'
+
+# requires platform and platform_version be defined
+shared_examples "a completed run" do
+ include_context "run completed"
+
+ it "runs ohai, sets up authentication, loads node state, synchronizes policy, converges, and runs audits" do
+ # This is what we're testing.
+ expect(client.run).to be true
+
+ # fork is stubbed, so we can see the outcome of the run
+ expect(node.automatic_attrs[:platform]).to eq(platform)
+ expect(node.automatic_attrs[:platform_version]).to eq(platform_version)
+ end
+end
+
+shared_examples "a completed run with audit failure" do
+ include_context "run completed"
+
+ before do
+ expect(Chef::Application).to receive(:debug_stacktrace).with an_instance_of(Chef::Exceptions::RunFailedWrappingError)
+ end
+
+ it "converges, runs audits, saves the node and raises the error in a wrapping error" do
+ expect{ client.run }.to raise_error(Chef::Exceptions::RunFailedWrappingError) do |error|
+ expect(error.wrapped_errors.size).to eq(run_errors.size)
+ run_errors.each do |run_error|
+ expect(error.wrapped_errors).to include(run_error)
+ expect(error.backtrace).to include(*run_error.backtrace)
+ end
+ end
+
+ # fork is stubbed, so we can see the outcome of the run
+ expect(node.automatic_attrs[:platform]).to eq(platform)
+ expect(node.automatic_attrs[:platform_version]).to eq(platform_version)
+ end
+end
+
+shared_examples "a failed run" do
+ include_context "run failed"
+
+ it "skips node save and raises the error in a wrapping error" do
+ expect{ client.run }.to raise_error(Chef::Exceptions::RunFailedWrappingError) do |error|
+ expect(error.wrapped_errors.size).to eq(run_errors.size)
+ run_errors.each do |run_error|
+ expect(error.wrapped_errors).to include(run_error)
+ expect(error.backtrace).to include(*run_error.backtrace)
+ end
+ end
+ end
+end
diff --git a/spec/support/shared/functional/file_resource.rb b/spec/support/shared/functional/file_resource.rb
index 4f8e2f5b71..3ce3c9c94e 100644
--- a/spec/support/shared/functional/file_resource.rb
+++ b/spec/support/shared/functional/file_resource.rb
@@ -592,10 +592,6 @@ shared_examples_for "a configured file resource" do
File.open(path, "wb") { |f| f.write(wrong_content) }
end
- it "updates the source file content" do
- skip
- end
-
it "marks the resource as updated" do
resource.run_action(:create)
expect(resource).to be_updated_by_last_action
diff --git a/spec/support/shared/functional/securable_resource.rb b/spec/support/shared/functional/securable_resource.rb
index e016bb685d..b3c32356aa 100644
--- a/spec/support/shared/functional/securable_resource.rb
+++ b/spec/support/shared/functional/securable_resource.rb
@@ -163,9 +163,6 @@ shared_examples_for "a securable resource with existing target" do
let(:desired_gid) { 1337 }
let(:expected_gid) { 1337 }
- skip "should set an owner (Rerun specs under root)", :requires_unprivileged_user => true
- skip "should set a group (Rerun specs under root)", :requires_unprivileged_user => true
-
describe "when setting the owner", :requires_root do
before do
resource.owner expected_user_name
@@ -205,11 +202,6 @@ shared_examples_for "a securable resource with existing target" do
resource.run_action(:create)
end
- it "should set permissions as specified" do
- pending("Linux does not support lchmod")
- expect{ File.lstat(path).mode & 007777 }.to eq(@mode_string.oct & 007777)
- end
-
it "is marked as updated only if changes are made" do
expect(resource.updated_by_last_action?).to eq(expect_updated?)
end
@@ -222,15 +214,28 @@ shared_examples_for "a securable resource with existing target" do
resource.run_action(:create)
end
- it "should set permissions in numeric form as a ruby-interpreted octal" do
- pending('Linux does not support lchmod')
- expect{ File.lstat(path).mode & 007777 }.to eq(@mode_integer & 007777)
- end
-
it "is marked as updated only if changes are made" do
expect(resource.updated_by_last_action?).to eq(expect_updated?)
end
end
+
+ describe "when setting the suid bit", :requires_root do
+ before do
+ @suid_mode = 04776
+ resource.mode @suid_mode
+ resource.run_action(:create)
+ end
+
+ it "should set the suid bit" do
+ expect(File.lstat(path).mode & 007777).to eq(@suid_mode & 007777)
+ end
+
+ it "should retain the suid bit when updating the user" do
+ resource.user 1338
+ resource.run_action(:create)
+ expect(File.lstat(path).mode & 007777).to eq(@suid_mode & 007777)
+ end
+ end
end
context "on Windows", :windows_only do
@@ -288,17 +293,13 @@ shared_examples_for "a securable resource without existing target" do
include_context "diff disabled"
- context "on Unix", :unix_only do
- skip "if we need any securable resource tests on Unix without existing target resource."
- end
-
context "on Windows", :windows_only do
include_context "use Windows permissions"
- it "sets owner to Administrators on create if owner is not specified" do
+ it "leaves owner as system default on create if owner is not specified" do
expect(File.exist?(path)).to eq(false)
resource.run_action(:create)
- expect(descriptor.owner).to eq(SID.Administrators)
+ expect(descriptor.owner).to eq(SID.default_security_object_owner)
end
it "sets owner when owner is specified" do
@@ -318,22 +319,24 @@ shared_examples_for "a securable resource without existing target" do
end
it "leaves owner alone if owner is not specified and resource already exists" do
- # Set owner to Guest so it's not the same as the current user (which is the default on create)
- resource.owner 'Guest'
+ arbitrary_non_default_owner = SID.Guest
+ expect(arbitrary_non_default_owner).not_to eq(SID.default_security_object_owner)
+
+ resource.owner 'Guest' # Change to arbitrary_non_default_owner once issue #1508 is fixed
resource.run_action(:create)
- expect(descriptor.owner).to eq(SID.Guest)
+ expect(descriptor.owner).to eq(arbitrary_non_default_owner)
new_resource = create_resource
expect(new_resource.owner).to eq(nil)
new_resource.run_action(:create)
- expect(descriptor.owner).to eq(SID.Guest)
+ expect(descriptor.owner).to eq(arbitrary_non_default_owner)
end
- it "sets group to None on create if group is not specified" do
+ it "leaves group as system default on create if group is not specified" do
expect(resource.group).to eq(nil)
expect(File.exist?(path)).to eq(false)
resource.run_action(:create)
- expect(descriptor.group).to eq(SID.None)
+ expect(descriptor.group).to eq(SID.default_security_object_group)
end
it "sets group when group is specified" do
@@ -346,23 +349,18 @@ shared_examples_for "a securable resource without existing target" do
expect { resource.group 'Lance "The Nose" Glindenberry III' }.to raise_error(Chef::Exceptions::ValidationFailed)
end
- it "sets group when group is specified with a \\" do
- pending("Need to find a group containing a backslash that is on most peoples' machines")
- resource.group "#{ENV['COMPUTERNAME']}\\Administrators"
- resource.run_action(:create)
- expect{ descriptor.group }.to eq(SID.Everyone)
- end
-
it "leaves group alone if group is not specified and resource already exists" do
- # Set group to Everyone so it's not the default (None)
- resource.group 'Everyone'
+ arbitrary_non_default_group = SID.Everyone
+ expect(arbitrary_non_default_group).not_to eq(SID.default_security_object_group)
+
+ resource.group 'Everyone' # Change to arbitrary_non_default_group once issue #1508 is fixed
resource.run_action(:create)
- expect(descriptor.group).to eq(SID.Everyone)
+ expect(descriptor.group).to eq(arbitrary_non_default_group)
new_resource = create_resource
expect(new_resource.group).to eq(nil)
new_resource.run_action(:create)
- expect(descriptor.group).to eq(SID.Everyone)
+ expect(descriptor.group).to eq(arbitrary_non_default_group)
end
describe "with rights and deny_rights attributes" do
diff --git a/spec/support/shared/functional/securable_resource_with_reporting.rb b/spec/support/shared/functional/securable_resource_with_reporting.rb
index 8a2ceed837..3176ebba0d 100644
--- a/spec/support/shared/functional/securable_resource_with_reporting.rb
+++ b/spec/support/shared/functional/securable_resource_with_reporting.rb
@@ -35,7 +35,7 @@ shared_examples_for "a securable resource with reporting" do
# Default mode varies based on implementation. Providers that use a tempfile
# will default to 0600. Providers that use File.open will default to 0666 -
# umask
- # let(:default_mode) { ((0100666 - File.umask) & 07777).to_s(8) }
+ # let(:default_mode) { (0666 & ~File.umask).to_s(8) }
describe "reading file security metadata for reporting on unix", :unix_only => true do
# According to POSIX standard created files get either the
@@ -185,7 +185,7 @@ shared_examples_for "a securable resource with reporting" do
# TODO: most stable way to specify?
expect(current_resource.owner).to eq(Etc.getpwuid(Process.uid).name)
expect(current_resource.group).to eq(@expected_group_name)
- expect(current_resource.mode).to eq("0#{((0100666 - File.umask) & 07777).to_s(8)}")
+ expect(current_resource.mode).to eq("0#{(0666 & ~File.umask).to_s(8)}")
end
end
@@ -239,8 +239,8 @@ shared_examples_for "a securable resource with reporting" do
end
context "and mode is specified as a String" do
- let(:default_create_mode) { (0100666 - File.umask) }
- let(:expected_mode) { "0#{(default_create_mode & 07777).to_s(8)}" }
+ let(:default_create_mode) { 0666 & ~File.umask }
+ let(:expected_mode) { "0#{default_create_mode.to_s(8)}" }
before do
resource.mode(expected_mode)
@@ -252,7 +252,7 @@ shared_examples_for "a securable resource with reporting" do
end
context "and mode is specified as an Integer" do
- let(:set_mode) { (0100666 - File.umask) & 07777 }
+ let(:set_mode) { 0666 & ~File.umask }
let(:expected_mode) { "0#{set_mode.to_s(8)}" }
before do
@@ -279,14 +279,14 @@ shared_examples_for "a securable resource with reporting" do
end
it "has empty values for file metadata in 'current_resource'" do
- pending "windows reporting not yet fully supported"
+ skip "windows reporting not yet fully supported"
expect(current_resource.owner).to be_nil
expect(current_resource.expanded_rights).to be_nil
end
context "and no security metadata is specified in new_resource" do
before do
- pending "windows reporting not yet fully supported"
+ skip "windows reporting not yet fully supported"
end
it "sets the metadata values on the new_resource as strings after creating" do
@@ -322,7 +322,7 @@ shared_examples_for "a securable resource with reporting" do
let(:expected_user_name) { 'domain\user' }
before do
- pending "windows reporting not yet fully supported"
+ skip "windows reporting not yet fully supported"
resource.owner(expected_user_name)
resource.run_action(:create)
end
@@ -336,7 +336,7 @@ shared_examples_for "a securable resource with reporting" do
context "when the target file exists" do
before do
- pending "windows reporting not yet fully supported"
+ skip "windows reporting not yet fully supported"
FileUtils.touch(resource.path)
resource.action(:create)
end
diff --git a/spec/support/shared/functional/win32_service.rb b/spec/support/shared/functional/win32_service.rb
index 7dd1920418..2ee1a8ad88 100644
--- a/spec/support/shared/functional/win32_service.rb
+++ b/spec/support/shared/functional/win32_service.rb
@@ -46,7 +46,8 @@ shared_context "using Win32::Service" do
:service_name => "spec-service",
:service_display_name => "Spec Test Service",
:service_description => "Service for testing Chef::Application::WindowsServiceManager.",
- :service_file_path => File.expand_path(File.join(File.dirname(__FILE__), '../../platforms/win32/spec_service.rb'))
+ :service_file_path => File.expand_path(File.join(File.dirname(__FILE__), '../../platforms/win32/spec_service.rb')),
+ :delayed_start => true
}
}
diff --git a/spec/support/shared/functional/windows_script.rb b/spec/support/shared/functional/windows_script.rb
index 35b86dc4e8..3499cc98ec 100644
--- a/spec/support/shared/functional/windows_script.rb
+++ b/spec/support/shared/functional/windows_script.rb
@@ -114,7 +114,7 @@ shared_context Chef::Resource::WindowsScript do
describe "when the run action is invoked on Windows" do
it "executes the script code" do
- resource.code("@whoami > #{script_output_path}")
+ resource.code("whoami > #{script_output_path}")
resource.returns(0)
resource.run_action(:run)
end
diff --git a/spec/support/shared/integration/integration_helper.rb b/spec/support/shared/integration/integration_helper.rb
index e6942c62af..927ff2f42b 100644
--- a/spec/support/shared/integration/integration_helper.rb
+++ b/spec/support/shared/integration/integration_helper.rb
@@ -22,14 +22,19 @@ require 'fileutils'
require 'chef/config'
require 'chef/json_compat'
require 'chef/server_api'
-require 'chef_zero/rspec'
require 'support/shared/integration/knife_support'
require 'support/shared/integration/app_server_support'
+require 'cheffish/rspec/chef_run_support'
require 'spec_helper'
module IntegrationSupport
include ChefZero::RSpec
+ def self.included(includer_class)
+ includer_class.extend(Cheffish::RSpec::ChefRunSupport)
+ includer_class.extend(ClassMethods)
+ end
+
module ClassMethods
include ChefZero::RSpec
@@ -49,10 +54,6 @@ module IntegrationSupport
end
end
- def self.included(includer_class)
- includer_class.extend(ClassMethods)
- end
-
def api
Chef::ServerAPI.new
end
diff --git a/spec/support/shared/shared_examples.rb b/spec/support/shared/shared_examples.rb
index b20c65f8b6..550fa2eb68 100644
--- a/spec/support/shared/shared_examples.rb
+++ b/spec/support/shared/shared_examples.rb
@@ -1,7 +1,7 @@
# For storing any examples shared between multiple tests
# Any object which defines a .to_json should import this test
-shared_examples "to_json equalivent to Chef::JSONCompat.to_json" do
+shared_examples "to_json equivalent to Chef::JSONCompat.to_json" do
let(:jsonable) {
raise "You must define the subject when including this test"
diff --git a/spec/support/shared/unit/api_versioning.rb b/spec/support/shared/unit/api_versioning.rb
new file mode 100644
index 0000000000..05a4117f8e
--- /dev/null
+++ b/spec/support/shared/unit/api_versioning.rb
@@ -0,0 +1,77 @@
+#
+# Author:: Tyler Cloke (<tyler@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef/exceptions"
+
+shared_examples_for "version handling" do
+ let(:response_406) { OpenStruct.new(:code => '406') }
+ let(:exception_406) { Net::HTTPServerException.new("406 Not Acceptable", response_406) }
+
+ before do
+ allow(rest_v1).to receive(http_verb).and_raise(exception_406)
+ end
+
+ context "when the server does not support the min or max server API version that Chef::UserV1 supports" do
+ before do
+ allow(object).to receive(:server_client_api_version_intersection).and_return([])
+ end
+
+ it "raises the original exception" do
+ expect{ object.send(method) }.to raise_error(exception_406)
+ end
+ end # when the server does not support the min or max server API version that Chef::UserV1 supports
+end # version handling
+
+shared_examples_for "user and client reregister" do
+ let(:response_406) { OpenStruct.new(:code => '406') }
+ let(:exception_406) { Net::HTTPServerException.new("406 Not Acceptable", response_406) }
+ let(:generic_exception) { Exception.new }
+ let(:min_version) { "2" }
+ let(:max_version) { "5" }
+ let(:return_hash_406) {
+ {
+ "min_version" => min_version,
+ "max_version" => max_version,
+ "request_version" => "30"
+ }
+ }
+
+ context "when V0 is not supported by the server" do
+ context "when the exception is 406 and returns x-ops-server-api-version header" do
+ before do
+ allow(rest_v0).to receive(:put).and_raise(exception_406)
+ allow(response_406).to receive(:[]).with('x-ops-server-api-version').and_return(Chef::JSONCompat.to_json(return_hash_406))
+ end
+
+ it "raises an error about only V0 being supported" do
+ expect(object).to receive(:reregister_only_v0_supported_error_msg).with(max_version, min_version)
+ expect{ object.reregister }.to raise_error(Chef::Exceptions::OnlyApiVersion0SupportedForAction)
+ end
+
+ end
+ context "when the exception is not versioning related" do
+ before do
+ allow(rest_v0).to receive(:put).and_raise(generic_exception)
+ end
+
+ it "raises the original error" do
+ expect{ object.reregister }.to raise_error(generic_exception)
+ end
+ end
+ end
+end
diff --git a/spec/support/shared/unit/knife_shared.rb b/spec/support/shared/unit/knife_shared.rb
new file mode 100644
index 0000000000..8c9010f3cf
--- /dev/null
+++ b/spec/support/shared/unit/knife_shared.rb
@@ -0,0 +1,40 @@
+#
+# Author:: Tyler Cloke (<tyler@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+shared_examples_for "mandatory field missing" do
+ context "when field is nil" do
+ before do
+ knife.name_args = name_args
+ end
+
+ it "exits 1" do
+ expect { knife.run }.to raise_error(SystemExit)
+ end
+
+ it "prints the usage" do
+ expect(knife).to receive(:show_usage)
+ expect { knife.run }.to raise_error(SystemExit)
+ end
+
+ it "prints a relevant error message" do
+ expect { knife.run }.to raise_error(SystemExit)
+ expect(stderr.string).to match /You must specify a #{fieldname}/
+ end
+ end
+end
diff --git a/spec/support/shared/unit/provider/file.rb b/spec/support/shared/unit/provider/file.rb
index 86f32c9e89..7de9698451 100644
--- a/spec/support/shared/unit/provider/file.rb
+++ b/spec/support/shared/unit/provider/file.rb
@@ -255,7 +255,7 @@ shared_examples_for Chef::Provider::File do
context "examining file security metadata on Unix with a file that exists" do
before do
# fake that we're on unix even if we're on windows
- allow(Chef::Platform).to receive(:windows?).and_return(false)
+ allow(ChefConfig).to receive(:windows?).and_return(false)
# mock up the filesystem to behave like unix
setup_normal_file
stat_struct = double("::File.stat", :mode => 0600, :uid => 0, :gid => 0, :mtime => 10000)
@@ -331,7 +331,7 @@ shared_examples_for Chef::Provider::File do
context "examining file security metadata on Unix with a file that does not exist" do
before do
# fake that we're on unix even if we're on windows
- allow(Chef::Platform).to receive(:windows?).and_return(false)
+ allow(ChefConfig).to receive(:windows?).and_return(false)
setup_missing_file
end
@@ -380,7 +380,7 @@ shared_examples_for Chef::Provider::File do
before do
# fake that we're on unix even if we're on windows
- allow(Chef::Platform).to receive(:windows?).and_return(false)
+ allow(ChefConfig).to receive(:windows?).and_return(false)
# mock up the filesystem to behave like unix
setup_normal_file
stat_struct = double("::File.stat", :mode => 0600, :uid => 0, :gid => 0, :mtime => 10000)
@@ -529,26 +529,49 @@ shared_examples_for Chef::Provider::File do
:for_reporting => diff_for_reporting )
allow(diff).to receive(:diff).with(resource_path, tempfile_path).and_return(true)
expect(provider).to receive(:diff).at_least(:once).and_return(diff)
- expect(provider).to receive(:managing_content?).at_least(:once).and_return(true)
expect(provider).to receive(:checksum).with(tempfile_path).and_return(tempfile_sha256)
- expect(provider).to receive(:checksum).with(resource_path).and_return(tempfile_sha256)
+ allow(provider).to receive(:managing_content?).and_return(true)
+ allow(provider).to receive(:checksum).with(resource_path).and_return(tempfile_sha256)
+ expect(resource).not_to receive(:checksum).with(tempfile_sha256) # do not mutate the new resource
expect(provider.deployment_strategy).to receive(:deploy).with(tempfile_path, normalized_path)
end
context "when the file was created" do
before { expect(provider).to receive(:needs_creating?).at_least(:once).and_return(true) }
- it "does not backup the file and does not produce a diff for reporting" do
+ it "does not backup the file" do
expect(provider).not_to receive(:do_backup)
provider.send(:do_contents_changes)
+ end
+
+ it "does not produce a diff for reporting" do
+ provider.send(:do_contents_changes)
expect(resource.diff).to be_nil
end
+
+ it "renders the final checksum correctly for reporting" do
+ provider.send(:do_contents_changes)
+ expect(resource.state_for_resource_reporter[:checksum]).to eql(tempfile_sha256)
+ end
end
context "when the file was not created" do
- before { expect(provider).to receive(:needs_creating?).at_least(:once).and_return(false) }
- it "backs up the file and produces a diff for reporting" do
+ before do
+ allow(provider).to receive(:do_backup) # stub do_backup
+ expect(provider).to receive(:needs_creating?).at_least(:once).and_return(false)
+ end
+
+ it "backs up the file" do
expect(provider).to receive(:do_backup)
provider.send(:do_contents_changes)
+ end
+
+ it "produces a diff for reporting" do
+ provider.send(:do_contents_changes)
expect(resource.diff).to eq(diff_for_reporting)
end
+
+ it "renders the final checksum correctly for reporting" do
+ provider.send(:do_contents_changes)
+ expect(resource.state_for_resource_reporter[:checksum]).to eql(tempfile_sha256)
+ end
end
end
diff --git a/spec/support/shared/unit/user_and_client_shared.rb b/spec/support/shared/unit/user_and_client_shared.rb
new file mode 100644
index 0000000000..bc5ffa07c2
--- /dev/null
+++ b/spec/support/shared/unit/user_and_client_shared.rb
@@ -0,0 +1,115 @@
+#
+# Author:: Tyler Cloke (<tyler@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+shared_examples_for "user or client create" do
+
+ context "when server API V1 is valid on the Chef Server receiving the request" do
+
+ it "creates a new object via the API" do
+ expect(rest_v1).to receive(:post).with(url, payload).and_return({})
+ object.create
+ end
+
+ it "creates a new object via the API with a public_key when it exists" do
+ object.public_key "some_public_key"
+ expect(rest_v1).to receive(:post).with(url, payload.merge({:public_key => "some_public_key"})).and_return({})
+ object.create
+ end
+
+ context "raise error when create_key and public_key are both set" do
+
+ before do
+ object.public_key "key"
+ object.create_key true
+ end
+
+ it "rasies the proper error" do
+ expect { object.create }.to raise_error(error)
+ end
+ end
+
+ context "when create_key == true" do
+ before do
+ object.create_key true
+ end
+
+ it "creates a new object via the API with create_key" do
+ expect(rest_v1).to receive(:post).with(url, payload.merge({:create_key => true})).and_return({})
+ object.create
+ end
+ end
+
+ context "when chef_key is returned by the server" do
+ let(:chef_key) {
+ {
+ "chef_key" => {
+ "public_key" => "some_public_key"
+ }
+ }
+ }
+
+ it "puts the public key into the objectr returned by create" do
+ expect(rest_v1).to receive(:post).with(url, payload).and_return(payload.merge(chef_key))
+ new_object = object.create
+ expect(new_object.public_key).to eq("some_public_key")
+ end
+
+ context "when private_key is returned in chef_key" do
+ let(:chef_key) {
+ {
+ "chef_key" => {
+ "public_key" => "some_public_key",
+ "private_key" => "some_private_key"
+ }
+ }
+ }
+
+ it "puts the private key into the object returned by create" do
+ expect(rest_v1).to receive(:post).with(url, payload).and_return(payload.merge(chef_key))
+ new_object = object.create
+ expect(new_object.private_key).to eq("some_private_key")
+ end
+ end
+ end # when chef_key is returned by the server
+
+ end # when server API V1 is valid on the Chef Server receiving the request
+
+ context "when server API V1 is not valid on the Chef Server receiving the request" do
+
+ context "when the server supports API V0" do
+ before do
+ allow(object).to receive(:server_client_api_version_intersection).and_return([0])
+ allow(rest_v1).to receive(:post).and_raise(exception_406)
+ end
+
+ it "creates a new object via the API" do
+ expect(rest_v0).to receive(:post).with(url, payload).and_return({})
+ object.create
+ end
+
+ it "creates a new object via the API with a public_key when it exists" do
+ object.public_key "some_public_key"
+ expect(rest_v0).to receive(:post).with(url, payload.merge({:public_key => "some_public_key"})).and_return({})
+ object.create
+ end
+
+ end # when the server supports API V0
+ end # when server API V1 is not valid on the Chef Server receiving the request
+
+end # user or client create
+
diff --git a/spec/unit/api_client_spec.rb b/spec/unit/api_client_spec.rb
index 7668e31f5a..a0e399b470 100644
--- a/spec/unit/api_client_spec.rb
+++ b/spec/unit/api_client_spec.rb
@@ -21,6 +21,11 @@ require 'spec_helper'
require 'chef/api_client'
require 'tempfile'
+# DEPRECATION NOTE
+#
+# This code will be removed in Chef 13 in favor of the code in Chef::ApiClientV1,
+# which will be moved to this namespace. New development should occur in
+# Chef::ApiClientV1 until the time before Chef 13.
describe Chef::ApiClient do
before(:each) do
@client = Chef::ApiClient.new
@@ -123,10 +128,6 @@ describe Chef::ApiClient do
it "does not include the private key if not present" do
expect(@json).not_to include("private_key")
end
-
- include_examples "to_json equalivent to Chef::JSONCompat.to_json" do
- let(:jsonable) { @client }
- end
end
describe "when deserializing from JSON (string) using ApiClient#from_json" do
@@ -222,8 +223,8 @@ describe Chef::ApiClient do
"validator" => true,
"json_class" => "Chef::ApiClient"
}
- @http_client = double("Chef::REST mock")
- allow(Chef::REST).to receive(:new).and_return(@http_client)
+ @http_client = double("Chef::ServerAPI mock")
+ allow(Chef::ServerAPI).to receive(:new).and_return(@http_client)
expect(@http_client).to receive(:get).with("clients/black").and_return(client)
@client = Chef::ApiClient.load(client['name'])
end
@@ -269,18 +270,13 @@ describe Chef::ApiClient do
File.open(Chef::Config[:client_key], "r") {|f| f.read.chomp }
end
- it "has an HTTP client configured with default credentials" do
- expect(@client.http_api).to be_a_kind_of(Chef::REST)
- expect(@client.http_api.client_name).to eq("silent-bob")
- expect(@client.http_api.signing_key.to_s).to eq(private_key_data)
- end
end
describe "when requesting a new key" do
before do
@http_client = double("Chef::REST mock")
- allow(Chef::REST).to receive(:new).and_return(@http_client)
+ allow(Chef::ServerAPI).to receive(:new).and_return(@http_client)
end
context "and the client does not exist on the server" do
diff --git a/spec/unit/api_client_v1_spec.rb b/spec/unit/api_client_v1_spec.rb
new file mode 100644
index 0000000000..17aba8c3af
--- /dev/null
+++ b/spec/unit/api_client_v1_spec.rb
@@ -0,0 +1,457 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+require 'chef/api_client_v1'
+require 'tempfile'
+
+describe Chef::ApiClientV1 do
+ before(:each) do
+ @client = Chef::ApiClientV1.new
+ end
+
+ it "has a name attribute" do
+ @client.name("ops_master")
+ expect(@client.name).to eq("ops_master")
+ end
+
+ it "does not allow spaces in the name" do
+ expect { @client.name "ops master" }.to raise_error(ArgumentError)
+ end
+
+ it "only allows string values for the name" do
+ expect { @client.name Hash.new }.to raise_error(ArgumentError)
+ end
+
+ it "has an admin flag attribute" do
+ @client.admin(true)
+ expect(@client.admin).to be_truthy
+ end
+
+ it "defaults to non-admin" do
+ expect(@client.admin).to be_falsey
+ end
+
+ it "allows only boolean values for the admin flag" do
+ expect { @client.admin(false) }.not_to raise_error
+ expect { @client.admin(Hash.new) }.to raise_error(ArgumentError)
+ end
+
+ it "has an create_key flag attribute" do
+ @client.create_key(true)
+ expect(@client.create_key).to be_truthy
+ end
+
+ it "create_key defaults to false" do
+ expect(@client.create_key).to be_falsey
+ end
+
+ it "allows only boolean values for the create_key flag" do
+ expect { @client.create_key(false) }.not_to raise_error
+ expect { @client.create_key(Hash.new) }.to raise_error(ArgumentError)
+ end
+
+ it "has a 'validator' flag attribute" do
+ @client.validator(true)
+ expect(@client.validator).to be_truthy
+ end
+
+ it "defaults to non-validator" do
+ expect(@client.validator).to be_falsey
+ end
+
+ it "allows only boolean values for the 'validator' flag" do
+ expect { @client.validator(false) }.not_to raise_error
+ expect { @client.validator(Hash.new) }.to raise_error(ArgumentError)
+ end
+
+ it "has a public key attribute" do
+ @client.public_key("super public")
+ expect(@client.public_key).to eq("super public")
+ end
+
+ it "accepts only String values for the public key" do
+ expect { @client.public_key "" }.not_to raise_error
+ expect { @client.public_key Hash.new }.to raise_error(ArgumentError)
+ end
+
+
+ it "has a private key attribute" do
+ @client.private_key("super private")
+ expect(@client.private_key).to eq("super private")
+ end
+
+ it "accepts only String values for the private key" do
+ expect { @client.private_key "" }.not_to raise_error
+ expect { @client.private_key Hash.new }.to raise_error(ArgumentError)
+ end
+
+ describe "when serializing to JSON" do
+ before(:each) do
+ @client.name("black")
+ @client.public_key("crowes")
+ @json = @client.to_json
+ end
+
+ it "serializes as a JSON object" do
+ expect(@json).to match(/^\{.+\}$/)
+ end
+
+ it "includes the name value" do
+ expect(@json).to include(%q{"name":"black"})
+ end
+
+ it "includes the public key value" do
+ expect(@json).to include(%{"public_key":"crowes"})
+ end
+
+ it "includes the 'admin' flag" do
+ expect(@json).to include(%q{"admin":false})
+ end
+
+ it "includes the 'validator' flag" do
+ expect(@json).to include(%q{"validator":false})
+ end
+
+ it "includes the 'create_key' flag when present" do
+ @client.create_key(true)
+ @json = @client.to_json
+ expect(@json).to include(%q{"create_key":true})
+ end
+
+ it "includes the private key when present" do
+ @client.private_key("monkeypants")
+ expect(@client.to_json).to include(%q{"private_key":"monkeypants"})
+ end
+
+ it "does not include the private key if not present" do
+ expect(@json).not_to include("private_key")
+ end
+
+ include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
+ let(:jsonable) { @client }
+ end
+ end
+
+ describe "when deserializing from JSON (string) using ApiClient#from_json" do
+ let(:client_string) do
+ "{\"name\":\"black\",\"public_key\":\"crowes\",\"private_key\":\"monkeypants\",\"admin\":true,\"validator\":true,\"create_key\":true}"
+ end
+
+ let(:client) do
+ Chef::ApiClientV1.from_json(client_string)
+ end
+
+ it "does not require a 'json_class' string" do
+ expect(Chef::JSONCompat.parse(client_string)["json_class"]).to eq(nil)
+ end
+
+ it "should deserialize to a Chef::ApiClientV1 object" do
+ expect(client).to be_a_kind_of(Chef::ApiClientV1)
+ end
+
+ it "preserves the name" do
+ expect(client.name).to eq("black")
+ end
+
+ it "preserves the public key" do
+ expect(client.public_key).to eq("crowes")
+ end
+
+ it "preserves the admin status" do
+ expect(client.admin).to be_truthy
+ end
+
+ it "preserves the create_key status" do
+ expect(client.create_key).to be_truthy
+ end
+
+ it "preserves the 'validator' status" do
+ expect(client.validator).to be_truthy
+ end
+
+ it "includes the private key if present" do
+ expect(client.private_key).to eq("monkeypants")
+ end
+ end
+
+ describe "when deserializing from JSON (hash) using ApiClientV1#from_json" do
+ let(:client_hash) do
+ {
+ "name" => "black",
+ "public_key" => "crowes",
+ "private_key" => "monkeypants",
+ "admin" => true,
+ "validator" => true,
+ "create_key" => true
+ }
+ end
+
+ let(:client) do
+ Chef::ApiClientV1.from_json(Chef::JSONCompat.to_json(client_hash))
+ end
+
+ it "should deserialize to a Chef::ApiClientV1 object" do
+ expect(client).to be_a_kind_of(Chef::ApiClientV1)
+ end
+
+ it "preserves the name" do
+ expect(client.name).to eq("black")
+ end
+
+ it "preserves the public key" do
+ expect(client.public_key).to eq("crowes")
+ end
+
+ it "preserves the admin status" do
+ expect(client.admin).to be_truthy
+ end
+
+ it "preserves the create_key status" do
+ expect(client.create_key).to be_truthy
+ end
+
+ it "preserves the 'validator' status" do
+ expect(client.validator).to be_truthy
+ end
+
+ it "includes the private key if present" do
+ expect(client.private_key).to eq("monkeypants")
+ end
+ end
+
+ describe "when loading from JSON" do
+ before do
+ end
+
+ before(:each) do
+ client = {
+ "name" => "black",
+ "clientname" => "black",
+ "public_key" => "crowes",
+ "private_key" => "monkeypants",
+ "admin" => true,
+ "create_key" => true,
+ "validator" => true
+ }
+
+ @http_client = double("Chef::ServerAPI mock")
+ allow(Chef::ServerAPI).to receive(:new).and_return(@http_client)
+ expect(@http_client).to receive(:get).with("clients/black").and_return(client)
+ @client = Chef::ApiClientV1.load(client['name'])
+ end
+
+ it "should deserialize to a Chef::ApiClientV1 object" do
+ expect(@client).to be_a_kind_of(Chef::ApiClientV1)
+ end
+
+ it "preserves the name" do
+ expect(@client.name).to eq("black")
+ end
+
+ it "preserves the public key" do
+ expect(@client.public_key).to eq("crowes")
+ end
+
+ it "preserves the admin status" do
+ expect(@client.admin).to be_a_kind_of(TrueClass)
+ end
+
+ it "preserves the create_key status" do
+ expect(@client.create_key).to be_a_kind_of(TrueClass)
+ end
+
+ it "preserves the 'validator' status" do
+ expect(@client.validator).to be_a_kind_of(TrueClass)
+ end
+
+ it "includes the private key if present" do
+ expect(@client.private_key).to eq("monkeypants")
+ end
+
+ end
+
+ describe "with correctly configured API credentials" do
+ before do
+ Chef::Config[:node_name] = "silent-bob"
+ Chef::Config[:client_key] = File.expand_path('ssl/private_key.pem', CHEF_SPEC_DATA)
+ end
+
+ after do
+ Chef::Config[:node_name] = nil
+ Chef::Config[:client_key] = nil
+ end
+
+ let :private_key_data do
+ File.open(Chef::Config[:client_key], "r") {|f| f.read.chomp }
+ end
+
+ end
+
+
+ describe "when requesting a new key" do
+ before do
+ @http_client = double("Chef::ServerAPI mock")
+ allow(Chef::ServerAPI).to receive(:new).and_return(@http_client)
+ end
+
+ context "and the client does not exist on the server" do
+ before do
+ @a_404_response = Net::HTTPNotFound.new("404 not found and such", nil, nil)
+ @a_404_exception = Net::HTTPServerException.new("404 not found exception", @a_404_response)
+
+ expect(@http_client).to receive(:get).with("clients/lost-my-key").and_raise(@a_404_exception)
+ end
+
+ it "raises a 404 error" do
+ expect { Chef::ApiClientV1.reregister("lost-my-key") }.to raise_error(Net::HTTPServerException)
+ end
+ end
+ end
+
+ describe "Versioned API Interactions" do
+ let(:response_406) { OpenStruct.new(:code => '406') }
+ let(:exception_406) { Net::HTTPServerException.new("406 Not Acceptable", response_406) }
+ let(:payload) {
+ {
+ :name => "some_name",
+ :validator => true,
+ :admin => true
+ }
+ }
+
+ before do
+ @client = Chef::ApiClientV1.new
+ allow(@client).to receive(:chef_rest_v0).and_return(double('chef rest root v0 object'))
+ allow(@client).to receive(:chef_rest_v1).and_return(double('chef rest root v1 object'))
+ @client.name "some_name"
+ @client.validator true
+ @client.admin true
+ end
+
+ describe "create" do
+
+ # from spec/support/shared/unit/user_and_client_shared.rb
+ it_should_behave_like "user or client create" do
+ let(:object) { @client }
+ let(:error) { Chef::Exceptions::InvalidClientAttribute }
+ let(:rest_v0) { @client.chef_rest_v0 }
+ let(:rest_v1) { @client.chef_rest_v1 }
+ let(:url) { "clients" }
+ end
+
+ context "when API V1 is not supported by the server" do
+ # from spec/support/shared/unit/api_versioning.rb
+ it_should_behave_like "version handling" do
+ let(:object) { @client }
+ let(:method) { :create }
+ let(:http_verb) { :post }
+ let(:rest_v1) { @client.chef_rest_v1 }
+ end
+ end
+
+ end # create
+
+ describe "update" do
+ context "when a valid client is defined" do
+
+ shared_examples_for "client updating" do
+ it "updates the client" do
+ expect(rest). to receive(:put).with("clients/some_name", payload).and_return(payload)
+ @client.update
+ end
+
+ context "when only the name field exists" do
+
+ before do
+ # needed since there is no way to set to nil via code
+ @client.instance_variable_set(:@validator, nil)
+ @client.instance_variable_set(:@admin, nil)
+ end
+
+ after do
+ @client.validator true
+ @client.admin true
+ end
+
+ it "updates the client with only the name" do
+ expect(rest). to receive(:put).with("clients/some_name", {:name => "some_name"}).and_return({:name => "some_name"})
+ @client.update
+ end
+ end
+
+ end
+
+ context "when API V1 is supported by the server" do
+
+ it_should_behave_like "client updating" do
+ let(:rest) { @client.chef_rest_v1 }
+ end
+
+ end # when API V1 is supported by the server
+
+ context "when API V1 is not supported by the server" do
+ context "when no version is supported" do
+ # from spec/support/shared/unit/api_versioning.rb
+ it_should_behave_like "version handling" do
+ let(:object) { @client }
+ let(:method) { :create }
+ let(:http_verb) { :post }
+ let(:rest_v1) { @client.chef_rest_v1 }
+ end
+ end # when no version is supported
+
+ context "when API V0 is supported" do
+
+ before do
+ allow(@client.chef_rest_v1).to receive(:put).and_raise(exception_406)
+ allow(@client).to receive(:server_client_api_version_intersection).and_return([0])
+ end
+
+ it_should_behave_like "client updating" do
+ let(:rest) { @client.chef_rest_v0 }
+ end
+
+ end
+
+ end # when API V1 is not supported by the server
+ end # when a valid client is defined
+ end # update
+
+ # DEPRECATION
+ # This can be removed after API V0 support is gone
+ describe "reregister" do
+ context "when server API V0 is valid on the Chef Server receiving the request" do
+ it "creates a new object via the API" do
+ expect(@client.chef_rest_v0).to receive(:put).with("clients/#{@client.name}", payload.merge({:private_key => true})).and_return({})
+ @client.reregister
+ end
+ end # when server API V0 is valid on the Chef Server receiving the request
+
+ context "when server API V0 is not supported by the Chef Server" do
+ # from spec/support/shared/unit/api_versioning.rb
+ it_should_behave_like "user and client reregister" do
+ let(:object) { @client }
+ let(:rest_v0) { @client.chef_rest_v0 }
+ end
+ end # when server API V0 is not supported by the Chef Server
+ end # reregister
+
+ end
+end
diff --git a/spec/unit/application/client_spec.rb b/spec/unit/application/client_spec.rb
index ea2ad473e5..64a6bcc9d2 100644
--- a/spec/unit/application/client_spec.rb
+++ b/spec/unit/application/client_spec.rb
@@ -60,7 +60,7 @@ describe Chef::Application::Client, "reconfigure" do
context "when interval is given" do
before do
Chef::Config[:interval] = 600
- allow(Chef::Platform).to receive(:windows?).and_return(false)
+ allow(ChefConfig).to receive(:windows?).and_return(false)
end
it "should terminate with message" do
@@ -77,7 +77,7 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config
context "when interval is given on windows" do
before do
Chef::Config[:interval] = 600
- allow(Chef::Platform).to receive(:windows?).and_return(true)
+ allow(ChefConfig).to receive(:windows?).and_return(true)
end
it "should not terminate" do
@@ -131,6 +131,16 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config
end
+ describe "when --no-listen is set" do
+
+ it "configures listen = false" do
+ app.config[:listen] = false
+ app.reconfigure
+ expect(Chef::Config[:listen]).to eq(false)
+ end
+
+ end
+
describe "when the json_attribs configuration option is specified" do
let(:json_attribs) { {"a" => "b"} }
@@ -155,11 +165,6 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config
before do
allow(Chef::Log).to receive(:warn)
end
-
- it "emits a warning that audit mode is an experimental feature" do
- expect(Chef::Log).to receive(:warn).with(/Audit mode is an experimental feature/)
- app.reconfigure
- end
end
shared_examples "unrecognized setting" do
@@ -305,7 +310,7 @@ describe Chef::Application::Client, "run_application", :unix_only do
allow(Chef::Daemon).to receive(:daemonize).and_return(true)
end
- it "should exit hard with exitstatus 3" do
+ it "should exit hard with exitstatus 3", :volatile do
pid = fork do
@app.run_application
end
@@ -320,9 +325,9 @@ describe Chef::Application::Client, "run_application", :unix_only do
end
expect(@pipe[0].gets).to eq("started\n")
Process.kill("TERM", pid)
- Process.wait
- sleep 1 # Make sure we give the converging child process enough time to finish
- expect(IO.select([@pipe[0]], nil, nil, 0)).not_to be_nil
+ Process.wait(pid)
+ # The timeout value needs to be large enough for the child process to finish
+ expect(IO.select([@pipe[0]], nil, nil, 15)).not_to be_nil
expect(@pipe[0].gets).to eq("finished\n")
end
end
diff --git a/spec/unit/application/solo_spec.rb b/spec/unit/application/solo_spec.rb
index 1785ecfc86..7013bfa0bc 100644
--- a/spec/unit/application/solo_spec.rb
+++ b/spec/unit/application/solo_spec.rb
@@ -106,7 +106,8 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config
describe "when the recipe_url configuration option is specified" do
let(:tarfile) { StringIO.new("remote_tarball_content") }
let(:target_file) { StringIO.new }
-
+ let(:shellout) { double(run_command: nil, error!: nil, stdout: '') }
+
before do
Chef::Config[:cookbook_path] = "#{Dir.tmpdir}/chef-solo/cookbooks"
Chef::Config[:recipe_url] = "http://junglist.gen.nz/recipes.tgz"
@@ -117,7 +118,7 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config
allow(app).to receive(:open).with("http://junglist.gen.nz/recipes.tgz").and_yield(tarfile)
allow(File).to receive(:open).with("#{Dir.tmpdir}/chef-solo/recipes.tgz", "wb").and_yield(target_file)
- allow(Chef::Mixin::Command).to receive(:run_command).and_return(true)
+ allow(Mixlib::ShellOut).to receive(:new).and_return(shellout)
end
it "should create the recipes path based on the parent of the cookbook path" do
@@ -136,7 +137,7 @@ Enable chef-client interval runs by setting `:client_fork = true` in your config
end
it "should untar the target file to the parent of the cookbook path" do
- expect(Chef::Mixin::Command).to receive(:run_command).with({:command => "tar zxvf #{Dir.tmpdir}/chef-solo/recipes.tgz -C #{Dir.tmpdir}/chef-solo"}).and_return(true)
+ expect(Mixlib::ShellOut).to receive(:new).with("tar zxvf #{Dir.tmpdir}/chef-solo/recipes.tgz -C #{Dir.tmpdir}/chef-solo")
app.reconfigure
end
end
diff --git a/spec/unit/audit/audit_reporter_spec.rb b/spec/unit/audit/audit_reporter_spec.rb
index 84d7ea82f0..46c2a96b4c 100644
--- a/spec/unit/audit/audit_reporter_spec.rb
+++ b/spec/unit/audit/audit_reporter_spec.rb
@@ -88,6 +88,29 @@ describe Chef::Audit::AuditReporter do
reporter.run_completed(node)
end
+ context "when audit phase failed" do
+
+ let(:audit_error) { double("AuditError", :class => "Chef::Exceptions::AuditError",
+ :message => "Audit phase failed with error message: derpderpderp",
+ :backtrace => ["/path/recipe.rb:57", "/path/library.rb:106"]) }
+
+ before do
+ reporter.instance_variable_set(:@audit_phase_error, audit_error)
+ end
+
+ it "reports an error" do
+ reporter.run_completed(node)
+ expect(run_data).to have_key(:error)
+ expect(run_data).to have_key(:error)
+ expect(run_data[:error]).to eq <<-EOM.strip!
+Chef::Exceptions::AuditError: Audit phase failed with error message: derpderpderp
+/path/recipe.rb:57
+/path/library.rb:106
+EOM
+ end
+
+ end
+
context "when unable to post to server" do
let(:error) do
@@ -203,7 +226,7 @@ describe Chef::Audit::AuditReporter do
it "doesn't send reports" do
expect(reporter).to receive(:auditing_enabled?).and_return(true)
expect(reporter).to receive(:run_status).and_return(nil)
- expect(Chef::Log).to receive(:debug).with("Run failed before audits were initialized, not sending audit report to server")
+ expect(Chef::Log).to receive(:debug).with("Run failed before audit mode was initialized, not sending audit report to server")
reporter.run_completed(node)
end
@@ -215,9 +238,13 @@ describe Chef::Audit::AuditReporter do
let(:audit_data) { Chef::Audit::AuditData.new(node.name, run_id) }
let(:run_data) { audit_data.to_hash }
- let(:error) { double("AuditError", :class => "Chef::Exception::AuditError",
- :message => "Well that certainly didn't work",
- :backtrace => ["line 0", "line 1", "line 2"]) }
+ let(:audit_error) { double("AuditError", :class => "Chef::Exceptions::AuditError",
+ :message => "Audit phase failed with error message: derpderpderp",
+ :backtrace => ["/path/recipe.rb:57", "/path/library.rb:106"]) }
+
+ let(:run_error) { double("RunError", :class => "Chef::Exceptions::RunError",
+ :message => "This error shouldn't be reported.",
+ :backtrace => ["fix it", "fix it", "fix it"]) }
before do
allow(reporter).to receive(:auditing_enabled?).and_return(true)
@@ -226,15 +253,32 @@ describe Chef::Audit::AuditReporter do
allow(audit_data).to receive(:to_hash).and_return(run_data)
end
- it "adds the error information to the reported data" do
- expect(rest).to receive(:create_url)
- expect(rest).to receive(:post)
- reporter.run_failed(error)
- expect(run_data).to have_key(:error)
- expect(run_data[:error]).to eq "Chef::Exception::AuditError: Well that certainly didn't work\n" +
- "line 0\nline 1\nline 2"
+ context "when no prior exception is stored" do
+ it "reports no error" do
+ expect(rest).to receive(:create_url)
+ expect(rest).to receive(:post)
+ reporter.run_failed(run_error)
+ expect(run_data).to_not have_key(:error)
+ end
end
+ context "when some prior exception is stored" do
+ before do
+ reporter.instance_variable_set(:@audit_phase_error, audit_error)
+ end
+
+ it "reports the prior error" do
+ expect(rest).to receive(:create_url)
+ expect(rest).to receive(:post)
+ reporter.run_failed(run_error)
+ expect(run_data).to have_key(:error)
+ expect(run_data[:error]).to eq <<-EOM.strip!
+Chef::Exceptions::AuditError: Audit phase failed with error message: derpderpderp
+/path/recipe.rb:57
+/path/library.rb:106
+EOM
+ end
+ end
end
shared_context "audit data" do
@@ -270,14 +314,14 @@ describe Chef::Audit::AuditReporter do
it "notifies audit phase finished to debug log" do
expect(Chef::Log).to receive(:debug).with(/Audit Reporter completed/)
- reporter.audit_phase_complete
+ reporter.audit_phase_complete("Output from audit mode")
end
it "collects audit data" do
ordered_control_groups.each do |_name, group|
expect(audit_data).to receive(:add_control_group).with(group)
end
- reporter.audit_phase_complete
+ reporter.audit_phase_complete("Output from audit mode")
end
end
@@ -288,14 +332,14 @@ describe Chef::Audit::AuditReporter do
it "notifies audit phase failed to debug log" do
expect(Chef::Log).to receive(:debug).with(/Audit Reporter failed/)
- reporter.audit_phase_failed(error)
+ reporter.audit_phase_failed(error, "Output from audit mode")
end
it "collects audit data" do
ordered_control_groups.each do |_name, group|
expect(audit_data).to receive(:add_control_group).with(group)
end
- reporter.audit_phase_failed(error)
+ reporter.audit_phase_failed(error, "Output from audit mode")
end
end
diff --git a/spec/unit/audit/logger_spec.rb b/spec/unit/audit/logger_spec.rb
new file mode 100644
index 0000000000..9dd9ce2cd9
--- /dev/null
+++ b/spec/unit/audit/logger_spec.rb
@@ -0,0 +1,42 @@
+#
+# Copyright:: Copyright (c) 2014 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Audit::Logger do
+
+ before(:each) do
+ Chef::Audit::Logger.instance_variable_set(:@buffer, nil)
+ end
+
+ it 'calling puts creates @buffer and adds the message' do
+ Chef::Audit::Logger.puts("Output message")
+ expect(Chef::Audit::Logger.read_buffer).to eq("Output message\n")
+ end
+
+ it 'calling puts multiple times adds to the message' do
+ Chef::Audit::Logger.puts("Output message")
+ Chef::Audit::Logger.puts("Output message")
+ Chef::Audit::Logger.puts("Output message")
+ expect(Chef::Audit::Logger.read_buffer).to eq("Output message\nOutput message\nOutput message\n")
+ end
+
+ it 'calling it before @buffer is set returns an empty string' do
+ expect(Chef::Audit::Logger.read_buffer).to eq("")
+ end
+
+end
diff --git a/spec/unit/audit/runner_spec.rb b/spec/unit/audit/runner_spec.rb
index 0bd4c18388..1de024260f 100644
--- a/spec/unit/audit/runner_spec.rb
+++ b/spec/unit/audit/runner_spec.rb
@@ -68,8 +68,8 @@ describe Chef::Audit::Runner do
in_sub_process do
runner.send(:setup)
- expect(RSpec.configuration.output_stream).to eq(log_location)
- expect(RSpec.configuration.error_stream).to eq(log_location)
+ expect(RSpec.configuration.output_stream).to eq(Chef::Audit::Logger)
+ expect(RSpec.configuration.error_stream).to eq(Chef::Audit::Logger)
expect(RSpec.configuration.formatters.size).to eq(2)
expect(RSpec.configuration.formatters).to include(instance_of(Chef::Audit::AuditEventProxy))
diff --git a/spec/unit/chef_class_spec.rb b/spec/unit/chef_class_spec.rb
new file mode 100644
index 0000000000..f1b877520c
--- /dev/null
+++ b/spec/unit/chef_class_spec.rb
@@ -0,0 +1,110 @@
+#
+# Author:: Lamont Granquist (<lamont@chef.io>)
+# Copyright:: Copyright (c) 2015 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe "Chef class" do
+ let(:platform) { "debian" }
+
+ let(:node) do
+ node = Chef::Node.new
+ node.automatic['platform'] = platform
+ node
+ end
+
+ let(:run_context) do
+ Chef::RunContext.new(node, nil, nil)
+ end
+
+ let(:resource_priority_map) do
+ double("Chef::Platform::ResourcePriorityMap")
+ end
+
+ let(:provider_priority_map) do
+ double("Chef::Platform::ProviderPriorityMap")
+ end
+
+ before do
+ Chef.set_run_context(run_context)
+ Chef.set_node(node)
+ Chef.set_resource_priority_map(resource_priority_map)
+ Chef.set_provider_priority_map(provider_priority_map)
+ end
+
+ context "priority maps" do
+ context "#get_provider_priority_array" do
+ it "should use the current node to get the right priority_map" do
+ expect(provider_priority_map).to receive(:get_priority_array).with(node, :http_request).and_return("stuff")
+ expect(Chef.get_provider_priority_array(:http_request)).to eql("stuff")
+ end
+ end
+ context "#get_resource_priority_array" do
+ it "should use the current node to get the right priority_map" do
+ expect(resource_priority_map).to receive(:get_priority_array).with(node, :http_request).and_return("stuff")
+ expect(Chef.get_resource_priority_array(:http_request)).to eql("stuff")
+ end
+ end
+ context "#set_provider_priority_array" do
+ it "should delegate to the provider_priority_map" do
+ expect(provider_priority_map).to receive(:set_priority_array).with(:http_request, ["a", "b"], platform: "debian").and_return("stuff")
+ expect(Chef.set_provider_priority_array(:http_request, ["a", "b"], platform: "debian")).to eql("stuff")
+ end
+ end
+ context "#set_priority_map_for_resource" do
+ it "should delegate to the resource_priority_map" do
+ expect(resource_priority_map).to receive(:set_priority_array).with(:http_request, ["a", "b"], platform: "debian").and_return("stuff")
+ expect(Chef.set_resource_priority_array(:http_request, ["a", "b"], platform: "debian")).to eql("stuff")
+ end
+ end
+ end
+
+ context "#run_context" do
+ it "should return the injected RunContext" do
+ expect(Chef.run_context).to eql(run_context)
+ end
+ end
+
+ context "#node" do
+ it "should return the injected Node" do
+ expect(Chef.node).to eql(node)
+ end
+ end
+
+ context '#event_handler' do
+ it 'adds a new handler' do
+ x = 1
+ Chef.event_handler do
+ on :converge_start do
+ x = 2
+ end
+ end
+ expect(Chef::Config[:event_handlers]).to_not be_empty
+ Chef::Config[:event_handlers].first.send(:converge_start)
+ expect(x).to eq(2)
+ end
+
+ it 'raise error if unknown event type is passed' do
+ expect do
+ Chef.event_handler do
+ on :yolo do
+ end
+ end
+ end.to raise_error(Chef::Exceptions::InvalidEventType)
+ end
+ end
+end
diff --git a/spec/unit/chef_fs/file_pattern_spec.rb b/spec/unit/chef_fs/file_pattern_spec.rb
index a9f06e8424..ed5f314605 100644
--- a/spec/unit/chef_fs/file_pattern_spec.rb
+++ b/spec/unit/chef_fs/file_pattern_spec.rb
@@ -157,7 +157,7 @@ describe Chef::ChefFS::FilePattern do
end
end
- context 'with simple pattern "a\*\b"', :pending => (Chef::Platform.windows?) do
+ context 'with simple pattern "a\*\b"', :skip => (Chef::Platform.windows?) do
let(:pattern) { Chef::ChefFS::FilePattern.new('a\*\b') }
it 'match?' do
expect(pattern.match?('a*b')).to be_truthy
@@ -264,7 +264,7 @@ describe Chef::ChefFS::FilePattern do
end
end
- context 'with star pattern "/abc/d[a-z][0-9]f/ghi"', :pending => (Chef::Platform.windows?) do
+ context 'with star pattern "/abc/d[a-z][0-9]f/ghi"', :skip => (Chef::Platform.windows?) do
let(:pattern) { Chef::ChefFS::FilePattern.new('/abc/d[a-z][0-9]f/ghi') }
it 'match?' do
expect(pattern.match?('/abc/de1f/ghi')).to be_truthy
@@ -352,11 +352,7 @@ describe Chef::ChefFS::FilePattern do
expect(pattern.could_match_children?('/abc/def/ghi')).to be_truthy
expect(pattern.could_match_children?('abc')).to be_falsey
end
- it 'could_match_children? /abc** returns false for /xyz' do
- pending 'Make could_match_children? more rigorous'
- # At the moment, we return false for this, but in the end it would be nice to return true:
- expect(pattern.could_match_children?('/xyz')).to be_falsey
- end
+
it 'exact_child_name_under' do
expect(pattern.exact_child_name_under('/')).to eq(nil)
expect(pattern.exact_child_name_under('/abc')).to eq(nil)
@@ -440,14 +436,6 @@ describe Chef::ChefFS::FilePattern do
expect(p('/.').exact_path).to eq('/')
expect(p('/.').match?('/')).to be_truthy
end
- it 'handles dot by itself', :pending => "decide what to do with dot by itself" do
- expect(p('.').normalized_pattern).to eq('.')
- expect(p('.').exact_path).to eq('.')
- expect(p('.').match?('.')).to be_truthy
- expect(p('./').normalized_pattern).to eq('.')
- expect(p('./').exact_path).to eq('.')
- expect(p('./').match?('.')).to be_truthy
- end
it 'handles dotdot' do
expect(p('abc/../def').normalized_pattern).to eq('def')
expect(p('abc/../def').exact_path).to eq('def')
diff --git a/spec/unit/chef_fs/path_util_spec.rb b/spec/unit/chef_fs/path_util_spec.rb
new file mode 100644
index 0000000000..42eb126dfb
--- /dev/null
+++ b/spec/unit/chef_fs/path_util_spec.rb
@@ -0,0 +1,108 @@
+#
+# Author:: Kartik Null Cating-Subramanian (<ksubramanian@chef.io>)
+# Copyright:: Copyright (c) 2015 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'chef/chef_fs/path_utils'
+
+describe Chef::ChefFS::PathUtils do
+ context 'invoking join' do
+ it 'joins well-behaved distinct path elements' do
+ expect(Chef::ChefFS::PathUtils.join('a', 'b', 'c')).to eq('a/b/c')
+ end
+
+ it 'strips extraneous slashes in the middle of paths' do
+ expect(Chef::ChefFS::PathUtils.join('a/', '/b', '/c/')).to eq('a/b/c')
+ expect(Chef::ChefFS::PathUtils.join('a/', '/b', '///c/')).to eq('a/b/c')
+ end
+
+ it 'preserves the whether the first element was absolute or not' do
+ expect(Chef::ChefFS::PathUtils.join('/a/', '/b', 'c/')).to eq('/a/b/c')
+ expect(Chef::ChefFS::PathUtils.join('///a/', '/b', 'c/')).to eq('/a/b/c')
+ end
+ end
+
+ context 'invoking is_absolute?' do
+ it 'confirms that paths starting with / are absolute' do
+ expect(Chef::ChefFS::PathUtils.is_absolute?('/foo/bar/baz')).to be true
+ expect(Chef::ChefFS::PathUtils.is_absolute?('/foo')).to be true
+ end
+
+ it 'confirms that paths starting with // are absolute even though that looks like some windows network path' do
+ expect(Chef::ChefFS::PathUtils.is_absolute?('//foo/bar/baz')).to be true
+ end
+
+ it 'confirms that root is indeed absolute' do
+ expect(Chef::ChefFS::PathUtils.is_absolute?('/')).to be true
+ end
+
+ it 'confirms that paths starting without / are relative' do
+ expect(Chef::ChefFS::PathUtils.is_absolute?('foo/bar/baz')).to be false
+ expect(Chef::ChefFS::PathUtils.is_absolute?('a')).to be false
+ end
+
+ it 'returns false for an empty path.' do
+ expect(Chef::ChefFS::PathUtils.is_absolute?('')).to be false
+ end
+ end
+
+ context 'invoking realest_path' do
+ let(:good_path) { File.dirname(__FILE__) }
+ let(:parent_path) { File.dirname(good_path) }
+
+ it 'handles paths with no wildcards or globs' do
+ expect(Chef::ChefFS::PathUtils.realest_path(good_path)).to eq(File.expand_path(good_path))
+ end
+
+ it 'handles paths with .. and .' do
+ expect(Chef::ChefFS::PathUtils.realest_path(good_path+'/../.')).to eq(File.expand_path(parent_path))
+ end
+
+ it 'handles paths with *' do
+ expect(Chef::ChefFS::PathUtils.realest_path(good_path + '/*/foo')).to eq(File.expand_path(good_path + '/*/foo'))
+ end
+
+ it 'handles directories that do not exist' do
+ expect(Chef::ChefFS::PathUtils.realest_path(good_path + '/something/or/other')).to eq(File.expand_path(good_path + '/something/or/other'))
+ end
+
+ it 'handles root correctly' do
+ if Chef::Platform.windows?
+ expect(Chef::ChefFS::PathUtils.realest_path('C:/')).to eq('C:/')
+ else
+ expect(Chef::ChefFS::PathUtils.realest_path('/')).to eq('/')
+ end
+ end
+ end
+
+ context 'invoking descendant_path' do
+ it 'handles paths with various casing on windows' do
+ allow(Chef::ChefFS).to receive(:windows?) { true }
+ expect(Chef::ChefFS::PathUtils.descendant_path('C:/ab/b/c', 'C:/AB/B')).to eq('c')
+ expect(Chef::ChefFS::PathUtils.descendant_path('C:/ab/b/c', 'c:/ab/B')).to eq('c')
+ end
+
+ it 'returns nil if the path does not have the given ancestor' do
+ expect(Chef::ChefFS::PathUtils.descendant_path('/D/E/F', '/A/B/C')).to be_nil
+ expect(Chef::ChefFS::PathUtils.descendant_path('/A/B/D', '/A/B/C')).to be_nil
+ end
+
+ it 'returns blank if the ancestor equals the path' do
+ expect(Chef::ChefFS::PathUtils.descendant_path('/A/B/D', '/A/B/D')).to eq('')
+ end
+ end
+end
diff --git a/spec/unit/client_spec.rb b/spec/unit/client_spec.rb
index 2ec32b32ac..8146774764 100644
--- a/spec/unit/client_spec.rb
+++ b/spec/unit/client_spec.rb
@@ -19,62 +19,31 @@
#
require 'spec_helper'
+require 'spec/support/shared/context/client'
+require 'spec/support/shared/examples/client'
require 'chef/run_context'
require 'chef/rest'
require 'rbconfig'
-describe Chef::Client do
+class FooError < RuntimeError
+end
- let(:hostname) { "hostname" }
- let(:machinename) { "machinename.example.org" }
- let(:fqdn) { "hostname.example.org" }
-
- let(:ohai_data) do
- { :fqdn => fqdn,
- :hostname => hostname,
- :machinename => machinename,
- :platform => 'example-platform',
- :platform_version => 'example-platform-1.0',
- :data => {}
- }
- end
+describe Chef::Client do
+ include_context "client"
- let(:ohai_system) do
- ohai_system = double( "Ohai::System",
- :all_plugins => true,
- :data => ohai_data)
- allow(ohai_system).to receive(:[]) do |key|
- ohai_data[key]
+ context "when minimal ohai is configured" do
+ before do
+ Chef::Config[:minimal_ohai] = true
end
- ohai_system
- end
- let(:node) do
- Chef::Node.new.tap do |n|
- n.name(fqdn)
- n.chef_environment("_default")
+ it "runs ohai with only the minimum required plugins" do
+ expected_filter = %w[fqdn machinename hostname platform platform_version os os_version]
+ expect(ohai_system).to receive(:all_plugins).with(expected_filter)
+ client.run_ohai
end
end
- let(:json_attribs) { nil }
- let(:client_opts) { {} }
-
- let(:client) do
- Chef::Config[:event_loggers] = []
- Chef::Client.new(json_attribs, client_opts).tap do |c|
- c.node = node
- end
- end
-
- before do
- Chef::Log.logger = Logger.new(StringIO.new)
-
- # Node/Ohai data
- #Chef::Config[:node_name] = fqdn
- allow(Ohai::System).to receive(:new).and_return(ohai_system)
- end
-
describe "authentication protocol selection" do
after do
Chef::Config[:authentication_protocol_version] = "1.0"
@@ -101,7 +70,6 @@ describe Chef::Client do
describe "configuring output formatters" do
context "when no formatter has been configured" do
-
context "and STDOUT is a TTY" do
before do
allow(STDOUT).to receive(:tty?).and_return(true)
@@ -187,135 +155,12 @@ describe Chef::Client do
end
describe "a full client run" do
- shared_context "a client run" do
- let(:http_node_load) { double("Chef::REST (node)") }
- let(:http_cookbook_sync) { double("Chef::REST (cookbook sync)") }
- let(:http_node_save) { double("Chef::REST (node save)") }
- let(:runner) { double("Chef::Runner") }
- let(:audit_runner) { instance_double("Chef::Audit::Runner", :failed? => false) }
-
- let(:api_client_exists?) { false }
-
- let(:stdout) { StringIO.new }
- let(:stderr) { StringIO.new }
-
- let(:enable_fork) { false }
-
- def stub_for_register
- # --Client.register
- # Make sure Client#register thinks the client key doesn't
- # exist, so it tries to register and create one.
- allow(File).to receive(:exists?).and_call_original
- expect(File).to receive(:exists?).
- with(Chef::Config[:client_key]).
- exactly(:once).
- and_return(api_client_exists?)
-
- unless api_client_exists?
- # Client.register will register with the validation client name.
- expect_any_instance_of(Chef::ApiClient::Registration).to receive(:run)
- end
- end
-
- def stub_for_node_load
- # Client.register will then turn around create another
- # Chef::REST object, this time with the client key it got from the
- # previous step.
- expect(Chef::REST).to receive(:new).
- with(Chef::Config[:chef_server_url], fqdn, Chef::Config[:client_key]).
- exactly(:once).
- and_return(http_node_load)
-
- # --Client#build_node
- # looks up the node, which we will return, then later saves it.
- expect(Chef::Node).to receive(:find_or_create).with(fqdn).and_return(node)
-
- # --ResourceReporter#node_load_completed
- # gets a run id from the server for storing resource history
- # (has its own tests, so stubbing it here.)
- expect_any_instance_of(Chef::ResourceReporter).to receive(:node_load_completed)
- end
-
- def stub_for_sync_cookbooks
- # --Client#setup_run_context
- # ---Client#sync_cookbooks -- downloads the list of cookbooks to sync
- #
- expect_any_instance_of(Chef::CookbookSynchronizer).to receive(:sync_cookbooks)
- expect(Chef::REST).to receive(:new).with(Chef::Config[:chef_server_url]).and_return(http_cookbook_sync)
- expect(http_cookbook_sync).to receive(:post).
- with("environments/_default/cookbook_versions", {:run_list => []}).
- and_return({})
- end
-
- def stub_for_converge
- # --Client#converge
- expect(Chef::Runner).to receive(:new).and_return(runner)
- expect(runner).to receive(:converge).and_return(true)
- end
-
- def stub_for_audit
- # -- Client#run_audits
- expect(Chef::Audit::Runner).to receive(:new).and_return(audit_runner)
- expect(audit_runner).to receive(:run).and_return(true)
- end
-
- def stub_for_node_save
- allow(node).to receive(:data_for_save).and_return(node.for_json)
-
- # --Client#save_updated_node
- expect(Chef::REST).to receive(:new).with(Chef::Config[:chef_server_url]).and_return(http_node_save)
- expect(http_node_save).to receive(:put_rest).with("nodes/#{fqdn}", node.for_json).and_return(true)
- end
-
- def stub_for_run
- expect_any_instance_of(Chef::RunLock).to receive(:acquire)
- expect_any_instance_of(Chef::RunLock).to receive(:save_pid)
- expect_any_instance_of(Chef::RunLock).to receive(:release)
-
- # Post conditions: check that node has been filled in correctly
- expect(client).to receive(:run_started)
- expect(client).to receive(:run_completed_successfully)
-
- # --ResourceReporter#run_completed
- # updates the server with the resource history
- # (has its own tests, so stubbing it here.)
- expect_any_instance_of(Chef::ResourceReporter).to receive(:run_completed)
- # --AuditReporter#run_completed
- # posts the audit data to server.
- # (has its own tests, so stubbing it here.)
- expect_any_instance_of(Chef::Audit::AuditReporter).to receive(:run_completed)
- end
-
- before do
- Chef::Config[:client_fork] = enable_fork
- Chef::Config[:cache_path] = windows? ? 'C:\chef' : '/var/chef'
- Chef::Config[:why_run] = false
- Chef::Config[:audit_mode] = :enabled
-
- stub_const("Chef::Client::STDOUT_FD", stdout)
- stub_const("Chef::Client::STDERR_FD", stderr)
-
- stub_for_register
- stub_for_node_load
- stub_for_sync_cookbooks
- stub_for_converge
- stub_for_audit
- stub_for_node_save
- stub_for_run
- end
- end
-
shared_examples_for "a successful client run" do
include_context "a client run"
+ include_context "converge completed"
+ include_context "audit phase completed"
- it "runs ohai, sets up authentication, loads node state, synchronizes policy, converges, and runs audits" do
- # This is what we're testing.
- client.run
-
- # fork is stubbed, so we can see the outcome of the run
- expect(node.automatic_attrs[:platform]).to eq("example-platform")
- expect(node.automatic_attrs[:platform_version]).to eq("example-platform-1.0")
- end
+ include_examples "a completed run"
end
describe "when running chef-client without fork" do
@@ -323,24 +168,19 @@ describe Chef::Client do
end
describe "when the client key already exists" do
- let(:api_client_exists?) { true }
- include_examples "a successful client run"
+ include_examples "a successful client run" do
+ let(:api_client_exists?) { true }
+ end
end
- describe "when an override run list is given" do
- let(:client_opts) { {:override_runlist => "recipe[override_recipe]"} }
-
- it "should permit spaces in overriding run list" do
+ context "when an override run list is given" do
+ it "permits spaces in overriding run list" do
Chef::Client.new(nil, :override_runlist => 'role[a], role[b]')
end
- describe "when running the client" do
+ describe "calling run" do
include_examples "a successful client run" do
-
- before do
- # Client will try to compile and run override_recipe
- expect_any_instance_of(Chef::RunContext::CookbookCompiler).to receive(:compile)
- end
+ let(:client_opts) { {:override_runlist => "recipe[override_recipe]"} }
def stub_for_sync_cookbooks
# --Client#setup_run_context
@@ -357,13 +197,22 @@ describe Chef::Client do
# Expect NO node save
expect(node).not_to receive(:save)
end
+
+ before do
+ # Client will try to compile and run override_recipe
+ expect_any_instance_of(Chef::RunContext::CookbookCompiler).to receive(:compile)
+ end
end
end
end
describe "when a permanent run list is passed as an option" do
- include_examples "a successful client run" do
+ it "sets the new run list on the node" do
+ client.run
+ expect(node.run_list).to eq(Chef::RunList.new(new_runlist))
+ end
+ include_examples "a successful client run" do
let(:new_runlist) { "recipe[new_run_list_recipe]" }
let(:client_opts) { {:runlist => new_runlist} }
@@ -383,168 +232,62 @@ describe Chef::Client do
# do not create a fixture for this.
expect_any_instance_of(Chef::RunContext::CookbookCompiler).to receive(:compile)
end
-
- it "sets the new run list on the node" do
- client.run
- expect(node.run_list).to eq(Chef::RunList.new(new_runlist))
- end
end
end
- describe "when converge fails" do
- include_context "a client run" do
- let(:e) { Exception.new }
- def stub_for_converge
- expect(Chef::Runner).to receive(:new).and_return(runner)
- expect(runner).to receive(:converge).and_raise(e)
- expect(Chef::Application).to receive(:debug_stacktrace).with an_instance_of(Chef::Exceptions::RunFailedWrappingError)
- end
-
- def stub_for_node_save
- expect(client).to_not receive(:save_updated_node)
- end
-
- def stub_for_run
- expect_any_instance_of(Chef::RunLock).to receive(:acquire)
- expect_any_instance_of(Chef::RunLock).to receive(:save_pid)
- expect_any_instance_of(Chef::RunLock).to receive(:release)
-
- # Post conditions: check that node has been filled in correctly
- expect(client).to receive(:run_started)
- expect(client).to receive(:run_failed)
-
- expect_any_instance_of(Chef::ResourceReporter).to receive(:run_failed)
- expect_any_instance_of(Chef::Audit::AuditReporter).to receive(:run_failed)
- end
- end
-
- it "runs the audits and raises the error" do
- expect{ client.run }.to raise_error(Chef::Exceptions::RunFailedWrappingError) do |error|
- expect(error.wrapped_errors.size).to eq(1)
- expect(error.wrapped_errors[0]).to eq(e)
- end
- end
- end
-
- describe "when the audit phase fails" do
- context "with an exception" do
- include_context "a client run" do
- let(:e) { Exception.new }
- def stub_for_audit
- expect(Chef::Audit::Runner).to receive(:new).and_return(audit_runner)
- expect(audit_runner).to receive(:run).and_raise(e)
- expect(Chef::Application).to receive(:debug_stacktrace).with an_instance_of(Chef::Exceptions::RunFailedWrappingError)
- end
-
- def stub_for_run
- expect_any_instance_of(Chef::RunLock).to receive(:acquire)
- expect_any_instance_of(Chef::RunLock).to receive(:save_pid)
- expect_any_instance_of(Chef::RunLock).to receive(:release)
-
- # Post conditions: check that node has been filled in correctly
- expect(client).to receive(:run_started)
- expect(client).to receive(:run_failed)
-
- expect_any_instance_of(Chef::ResourceReporter).to receive(:run_failed)
- expect_any_instance_of(Chef::Audit::AuditReporter).to receive(:run_failed)
- end
- end
-
- it "should save the node after converge and raise exception" do
- expect{ client.run }.to raise_error(Chef::Exceptions::RunFailedWrappingError) do |error|
- expect(error.wrapped_errors.size).to eq(1)
- expect(error.wrapped_errors[0]).to eq(e)
+ describe "when converge completes successfully" do
+ include_context "a client run"
+ include_context "converge completed"
+ context 'when audit mode is enabled' do
+ describe "when audit phase errors" do
+ include_context "audit phase failed with error"
+ include_examples "a completed run with audit failure" do
+ let(:run_errors) { [audit_error] }
end
end
- end
-
- context "with failed audits" do
- include_context "a client run" do
- let(:audit_runner) do
- instance_double("Chef::Audit::Runner", :run => true, :failed? => true, :num_failed => 1, :num_total => 1)
- end
-
- def stub_for_audit
- expect(Chef::Audit::Runner).to receive(:new).and_return(audit_runner)
- expect(Chef::Application).to receive(:debug_stacktrace).with an_instance_of(Chef::Exceptions::RunFailedWrappingError)
- end
-
- def stub_for_run
- expect_any_instance_of(Chef::RunLock).to receive(:acquire)
- expect_any_instance_of(Chef::RunLock).to receive(:save_pid)
- expect_any_instance_of(Chef::RunLock).to receive(:release)
- # Post conditions: check that node has been filled in correctly
- expect(client).to receive(:run_started)
- expect(client).to receive(:run_failed)
-
- expect_any_instance_of(Chef::ResourceReporter).to receive(:run_failed)
- expect_any_instance_of(Chef::Audit::AuditReporter).to receive(:run_failed)
- end
+ describe "when audit phase completed" do
+ include_context "audit phase completed"
+ include_examples "a completed run"
end
- it "should save the node after converge and raise exception" do
- expect{ client.run }.to raise_error(Chef::Exceptions::RunFailedWrappingError) do |error|
- expect(error.wrapped_errors.size).to eq(1)
- expect(error.wrapped_errors[0]).to be_instance_of(Chef::Exceptions::AuditsFailed)
+ describe "when audit phase completed with failed controls" do
+ include_context "audit phase completed with failed controls"
+ include_examples "a completed run with audit failure" do
+ let(:run_errors) { [audit_error] }
end
end
end
end
- describe "when why_run mode is enabled" do
- include_context "a client run" do
-
- before do
- Chef::Config[:why_run] = true
- end
-
- def stub_for_audit
- expect(Chef::Audit::Runner).to_not receive(:new)
- end
-
- def stub_for_node_save
- # This is how we should be mocking external calls - not letting it fall all the way through to the
- # REST call
- expect(node).to receive(:save)
- end
-
- it "runs successfully without enabling the audit runner" do
- client.run
+ describe "when converge errors" do
+ include_context "a client run"
+ include_context "converge failed"
- # fork is stubbed, so we can see the outcome of the run
- expect(node.automatic_attrs[:platform]).to eq("example-platform")
- expect(node.automatic_attrs[:platform_version]).to eq("example-platform-1.0")
+ describe "when audit phase errors" do
+ include_context "audit phase failed with error"
+ include_examples "a failed run" do
+ let(:run_errors) { [converge_error, audit_error] }
end
end
- end
-
- describe "when audits are disabled" do
- include_context "a client run" do
-
- before do
- Chef::Config[:audit_mode] = :disabled
- end
- def stub_for_audit
- expect(Chef::Audit::Runner).to_not receive(:new)
+ describe "when audit phase completed" do
+ include_context "audit phase completed"
+ include_examples "a failed run" do
+ let(:run_errors) { [converge_error] }
end
+ end
- it "runs successfully without enabling the audit runner" do
- client.run
-
- # fork is stubbed, so we can see the outcome of the run
- expect(node.automatic_attrs[:platform]).to eq("example-platform")
- expect(node.automatic_attrs[:platform_version]).to eq("example-platform-1.0")
+ describe "when audit phase completed with failed controls" do
+ include_context "audit phase completed with failed controls"
+ include_examples "a failed run" do
+ let(:run_errors) { [converge_error, audit_error] }
end
end
end
-
end
-
describe "when handling run failures" do
-
it "should remove the run_lock on failure of #load_node" do
@run_lock = double("Chef::RunLock", :acquire => true)
allow(Chef::RunLock).to receive(:new).and_return(@run_lock)
@@ -618,6 +361,7 @@ describe Chef::Client do
# check pre-conditions.
expect(node[:roles]).to be_nil
expect(node[:recipes]).to be_nil
+ expect(node[:expanded_run_list]).to be_nil
allow(client.policy_builder).to receive(:node).and_return(node)
@@ -630,7 +374,10 @@ describe Chef::Client do
expect(node[:roles]).to include("role_containing_cookbook1")
expect(node[:recipes]).not_to be_nil
expect(node[:recipes].length).to eq(1)
- expect(node[:recipes]).to include("cookbook1")
+ expect(node[:recipes]).to include("cookbook1::default")
+ expect(node[:expanded_run_list]).not_to be_nil
+ expect(node[:expanded_run_list].length).to eq(1)
+ expect(node[:expanded_run_list]).to include("cookbook1::default")
end
it "should set the environment from the specified configuration value" do
@@ -653,7 +400,7 @@ describe Chef::Client do
describe "windows_admin_check" do
context "platform is not windows" do
before do
- allow(Chef::Platform).to receive(:windows?).and_return(false)
+ allow(ChefConfig).to receive(:windows?).and_return(false)
end
it "shouldn't be called" do
@@ -664,7 +411,7 @@ describe Chef::Client do
context "platform is windows" do
before do
- allow(Chef::Platform).to receive(:windows?).and_return(true)
+ allow(ChefConfig).to receive(:windows?).and_return(true)
end
it "should be called" do
@@ -713,6 +460,7 @@ describe Chef::Client do
Chef::Config[:solo] = true
Chef::Config[:cookbook_path] = ["/path/to/invalid/cookbook_path"]
end
+
context "when any directory of cookbook_path contains no cookbook" do
it "raises CookbookNotFound error" do
expect do
@@ -757,4 +505,35 @@ describe Chef::Client do
end
end
+
+ describe "always attempt to run handlers" do
+ subject { client }
+ before do
+ # fail on the first thing in begin block
+ allow_any_instance_of(Chef::RunLock).to receive(:save_pid).and_raise(NoMethodError)
+ end
+
+ context 'when audit mode is enabled' do
+ before do
+ Chef::Config[:audit_mode] = :enabled
+ end
+ it "should run exception handlers on early fail" do
+ expect(subject).to receive(:run_failed)
+ expect { subject.run }.to raise_error(Chef::Exceptions::RunFailedWrappingError) do |error|
+ expect(error.wrapped_errors.size).to eq 1
+ expect(error.wrapped_errors).to include(NoMethodError)
+ end
+ end
+ end
+
+ context 'when audit mode is disabled' do
+ before do
+ Chef::Config[:audit_mode] = :disabled
+ end
+ it "should run exception handlers on early fail" do
+ expect(subject).to receive(:run_failed)
+ expect { subject.run }.to raise_error(NoMethodError)
+ end
+ end
+ end
end
diff --git a/spec/unit/config_spec.rb b/spec/unit/config_spec.rb
index 06178f7733..8d155c61ab 100644
--- a/spec/unit/config_spec.rb
+++ b/spec/unit/config_spec.rb
@@ -1,550 +1,31 @@
-#
-# Author:: Adam Jacob (<adam@opscode.com>)
-# Author:: Kyle Goodwin (<kgoodwin@primerevenue.com>)
-# Copyright:: Copyright (c) 2008 Opscode, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
require 'spec_helper'
-require 'chef/exceptions'
-require 'chef/util/path_helper'
-describe Chef::Config do
- describe "config attribute writer: chef_server_url" do
- before do
- Chef::Config.chef_server_url = "https://junglist.gen.nz"
- end
-
- it "sets the server url" do
- expect(Chef::Config.chef_server_url).to eq("https://junglist.gen.nz")
- end
-
- context "when the url has a leading space" do
- before do
- Chef::Config.chef_server_url = " https://junglist.gen.nz"
- end
-
- it "strips the space from the url when setting" do
- expect(Chef::Config.chef_server_url).to eq("https://junglist.gen.nz")
- end
-
- end
-
- context "when the url is a frozen string" do
- before do
- Chef::Config.chef_server_url = " https://junglist.gen.nz".freeze
- end
-
- it "strips the space from the url when setting without raising an error" do
- expect(Chef::Config.chef_server_url).to eq("https://junglist.gen.nz")
- end
- end
-
- end
-
- describe "when configuring formatters" do
- # if TTY and not(force-logger)
- # formatter = configured formatter or default formatter
- # formatter goes to STDOUT/ERR
- # if log file is writeable
- # log level is configured level or info
- # log location is file
- # else
- # log level is warn
- # log location is STDERR
- # end
- # elsif not(TTY) and force formatter
- # formatter = configured formatter or default formatter
- # if log_location specified
- # formatter goes to log_location
- # else
- # formatter goes to STDOUT/ERR
- # end
- # else
- # formatter = "null"
- # log_location = configured-value or defualt
- # log_level = info or defualt
- # end
- #
- it "has an empty list of formatters by default" do
- expect(Chef::Config.formatters).to eq([])
- end
+require 'chef/config'
- it "configures a formatter with a short name" do
- Chef::Config.add_formatter(:doc)
- expect(Chef::Config.formatters).to eq([[:doc, nil]])
- end
+RSpec.describe Chef::Config do
- it "configures a formatter with a file output" do
- Chef::Config.add_formatter(:doc, "/var/log/formatter.log")
- expect(Chef::Config.formatters).to eq([[:doc, "/var/log/formatter.log"]])
+ shared_examples_for "deprecated by ohai but not deprecated" do
+ it "does not emit a deprecation warning when set" do
+ expect(Chef::Log).to_not receive(:warn).
+ with(/Ohai::Config\[:#{option}\] is deprecated/)
+ Chef::Config[option] = value
+ expect(Chef::Config[option]).to eq(value)
end
-
end
- describe "class method: manage_secret_key" do
- before do
- allow(Chef::FileCache).to receive(:load).and_return(true)
- allow(Chef::FileCache).to receive(:has_key?).with("chef_server_cookie_id").and_return(false)
+ describe ":log_level" do
+ include_examples "deprecated by ohai but not deprecated" do
+ let(:option) { :log_level }
+ let(:value) { :debug }
end
-
- it "should generate and store a chef server cookie id" do
- expect(Chef::FileCache).to receive(:store).with("chef_server_cookie_id", /\w{40}/).and_return(true)
- Chef::Config.manage_secret_key
- end
-
- describe "when the filecache has a chef server cookie id key" do
- before do
- allow(Chef::FileCache).to receive(:has_key?).with("chef_server_cookie_id").and_return(true)
- end
-
- it "should not generate and store a chef server cookie id" do
- expect(Chef::FileCache).not_to receive(:store).with("chef_server_cookie_id", /\w{40}/)
- Chef::Config.manage_secret_key
- end
- end
-
end
- [ false, true ].each do |is_windows|
-
- context "On #{is_windows ? 'Windows' : 'Unix'}" do
- def to_platform(*args)
- Chef::Config.platform_specific_path(*args)
- end
-
- before :each do
- allow(Chef::Platform).to receive(:windows?).and_return(is_windows)
- end
-
- describe "class method: platform_specific_path" do
- if is_windows
- it "should return a windows path on windows systems" do
- path = "/etc/chef/cookbooks"
- allow(Chef::Config).to receive(:env).and_return({ 'SYSTEMDRIVE' => 'C:' })
- # match on a regex that looks for the base path with an optional
- # system drive at the beginning (c:)
- # system drive is not hardcoded b/c it can change and b/c it is not present on linux systems
- expect(Chef::Config.platform_specific_path(path)).to eq("C:\\chef\\cookbooks")
- end
- else
- it "should return given path on non-windows systems" do
- path = "/etc/chef/cookbooks"
- expect(Chef::Config.platform_specific_path(path)).to eq("/etc/chef/cookbooks")
- end
- end
- end
-
- describe "default values" do
- let :primary_cache_path do
- if is_windows
- "#{Chef::Config.env['SYSTEMDRIVE']}\\chef"
- else
- "/var/chef"
- end
- end
-
- let :secondary_cache_path do
- if is_windows
- "#{Chef::Config[:user_home]}\\.chef"
- else
- "#{Chef::Config[:user_home]}/.chef"
- end
- end
-
- before do
- if is_windows
- allow(Chef::Config).to receive(:env).and_return({ 'SYSTEMDRIVE' => 'C:' })
- Chef::Config[:user_home] = 'C:\Users\charlie'
- else
- Chef::Config[:user_home] = '/Users/charlie'
- end
-
- allow(Chef::Config).to receive(:path_accessible?).and_return(false)
- end
-
- describe "Chef::Config[:cache_path]" do
- context "when /var/chef exists and is accessible" do
- it "defaults to /var/chef" do
- allow(Chef::Config).to receive(:path_accessible?).with(to_platform("/var/chef")).and_return(true)
- expect(Chef::Config[:cache_path]).to eq(primary_cache_path)
- end
- end
-
- context "when /var/chef does not exist and /var is accessible" do
- it "defaults to /var/chef" do
- allow(File).to receive(:exists?).with(to_platform("/var/chef")).and_return(false)
- allow(Chef::Config).to receive(:path_accessible?).with(to_platform("/var")).and_return(true)
- expect(Chef::Config[:cache_path]).to eq(primary_cache_path)
- end
- end
-
- context "when /var/chef does not exist and /var is not accessible" do
- it "defaults to $HOME/.chef" do
- allow(File).to receive(:exists?).with(to_platform("/var/chef")).and_return(false)
- allow(Chef::Config).to receive(:path_accessible?).with(to_platform("/var")).and_return(false)
- expect(Chef::Config[:cache_path]).to eq(secondary_cache_path)
- end
- end
-
- context "when /var/chef exists and is not accessible" do
- it "defaults to $HOME/.chef" do
- allow(File).to receive(:exists?).with(to_platform("/var/chef")).and_return(true)
- allow(File).to receive(:readable?).with(to_platform("/var/chef")).and_return(true)
- allow(File).to receive(:writable?).with(to_platform("/var/chef")).and_return(false)
-
- expect(Chef::Config[:cache_path]).to eq(secondary_cache_path)
- end
- end
-
- context "when chef is running in local mode" do
- before do
- Chef::Config.local_mode = true
- end
-
- context "and config_dir is /a/b/c" do
- before do
- Chef::Config.config_dir to_platform('/a/b/c')
- end
-
- it "cache_path is /a/b/c/local-mode-cache" do
- expect(Chef::Config.cache_path).to eq(to_platform('/a/b/c/local-mode-cache'))
- end
- end
-
- context "and config_dir is /a/b/c/" do
- before do
- Chef::Config.config_dir to_platform('/a/b/c/')
- end
-
- it "cache_path is /a/b/c/local-mode-cache" do
- expect(Chef::Config.cache_path).to eq(to_platform('/a/b/c/local-mode-cache'))
- end
- end
- end
- end
-
- it "Chef::Config[:file_backup_path] defaults to /var/chef/backup" do
- allow(Chef::Config).to receive(:cache_path).and_return(primary_cache_path)
- backup_path = is_windows ? "#{primary_cache_path}\\backup" : "#{primary_cache_path}/backup"
- expect(Chef::Config[:file_backup_path]).to eq(backup_path)
- end
-
- it "Chef::Config[:ssl_verify_mode] defaults to :verify_peer" do
- expect(Chef::Config[:ssl_verify_mode]).to eq(:verify_peer)
- end
-
- it "Chef::Config[:ssl_ca_path] defaults to nil" do
- expect(Chef::Config[:ssl_ca_path]).to be_nil
- end
-
- # TODO can this be removed?
- if !is_windows
- it "Chef::Config[:ssl_ca_file] defaults to nil" do
- expect(Chef::Config[:ssl_ca_file]).to be_nil
- end
- end
-
- it "Chef::Config[:data_bag_path] defaults to /var/chef/data_bags" do
- allow(Chef::Config).to receive(:cache_path).and_return(primary_cache_path)
- data_bag_path = is_windows ? "#{primary_cache_path}\\data_bags" : "#{primary_cache_path}/data_bags"
- expect(Chef::Config[:data_bag_path]).to eq(data_bag_path)
- end
-
- it "Chef::Config[:environment_path] defaults to /var/chef/environments" do
- allow(Chef::Config).to receive(:cache_path).and_return(primary_cache_path)
- environment_path = is_windows ? "#{primary_cache_path}\\environments" : "#{primary_cache_path}/environments"
- expect(Chef::Config[:environment_path]).to eq(environment_path)
- end
-
- describe "setting the config dir" do
-
- context "when the config file is /etc/chef/client.rb" do
-
- before do
- Chef::Config.config_file = to_platform("/etc/chef/client.rb")
- end
-
- it "config_dir is /etc/chef" do
- expect(Chef::Config.config_dir).to eq(to_platform("/etc/chef"))
- end
-
- context "and chef is running in local mode" do
- before do
- Chef::Config.local_mode = true
- end
-
- it "config_dir is /etc/chef" do
- expect(Chef::Config.config_dir).to eq(to_platform("/etc/chef"))
- end
- end
-
- context "when config_dir is set to /other/config/dir/" do
- before do
- Chef::Config.config_dir = to_platform("/other/config/dir/")
- end
-
- it "yields the explicit value" do
- expect(Chef::Config.config_dir).to eq(to_platform("/other/config/dir/"))
- end
- end
-
- end
-
- context "when the user's home dir is /home/charlie/" do
- before do
- Chef::Config.user_home = to_platform("/home/charlie")
- end
-
- it "config_dir is /home/charlie/.chef/" do
- expect(Chef::Config.config_dir).to eq(Chef::Util::PathHelper.join(to_platform("/home/charlie/.chef"), ''))
- end
-
- context "and chef is running in local mode" do
- before do
- Chef::Config.local_mode = true
- end
-
- it "config_dir is /home/charlie/.chef/" do
- expect(Chef::Config.config_dir).to eq(Chef::Util::PathHelper.join(to_platform("/home/charlie/.chef"), ''))
- end
- end
- end
-
- end
-
- if is_windows
- describe "finding the windows embedded dir" do
- let(:default_config_location) { "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.0/lib/chef/config.rb" }
- let(:alternate_install_location) { "c:/my/alternate/install/place/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-11.6.0/lib/chef/config.rb" }
- let(:non_omnibus_location) { "c:/my/dev/stuff/lib/ruby/gems/1.9.1/gems/chef-11.6.0/lib/chef/config.rb" }
-
- let(:default_ca_file) { "c:/opscode/chef/embedded/ssl/certs/cacert.pem" }
-
- it "finds the embedded dir in the default location" do
- allow(Chef::Config).to receive(:_this_file).and_return(default_config_location)
- expect(Chef::Config.embedded_dir).to eq("c:/opscode/chef/embedded")
- end
-
- it "finds the embedded dir in a custom install location" do
- allow(Chef::Config).to receive(:_this_file).and_return(alternate_install_location)
- expect(Chef::Config.embedded_dir).to eq("c:/my/alternate/install/place/chef/embedded")
- end
-
- it "doesn't error when not in an omnibus install" do
- allow(Chef::Config).to receive(:_this_file).and_return(non_omnibus_location)
- expect(Chef::Config.embedded_dir).to be_nil
- end
-
- it "sets the ssl_ca_cert path if the cert file is available" do
- allow(Chef::Config).to receive(:_this_file).and_return(default_config_location)
- allow(File).to receive(:exist?).with(default_ca_file).and_return(true)
- expect(Chef::Config.ssl_ca_file).to eq(default_ca_file)
- end
- end
- end
- end
-
- describe "Chef::Config[:user_home]" do
- it "should set when HOME is provided" do
- expected = to_platform("/home/kitten")
- allow(Chef::Config).to receive(:env).and_return({ 'HOME' => expected })
- expect(Chef::Config[:user_home]).to eq(expected)
- end
-
- it "should be set when only USERPROFILE is provided" do
- expected = to_platform("/users/kitten")
- allow(Chef::Config).to receive(:env).and_return({ 'USERPROFILE' => expected })
- expect(Chef::Config[:user_home]).to eq(expected)
- end
-
- it "falls back to the current working directory when HOME and USERPROFILE is not set" do
- allow(Chef::Config).to receive(:env).and_return({})
- expect(Chef::Config[:user_home]).to eq(Dir.pwd)
- end
- end
-
- describe "Chef::Config[:encrypted_data_bag_secret]" do
- let(:db_secret_default_path){ to_platform("/etc/chef/encrypted_data_bag_secret") }
-
- before do
- allow(File).to receive(:exist?).with(db_secret_default_path).and_return(secret_exists)
- end
-
- context "/etc/chef/encrypted_data_bag_secret exists" do
- let(:secret_exists) { true }
- it "sets the value to /etc/chef/encrypted_data_bag_secret" do
- expect(Chef::Config[:encrypted_data_bag_secret]).to eq db_secret_default_path
- end
- end
-
- context "/etc/chef/encrypted_data_bag_secret does not exist" do
- let(:secret_exists) { false }
- it "sets the value to nil" do
- expect(Chef::Config[:encrypted_data_bag_secret]).to be_nil
- end
- end
- end
-
- describe "Chef::Config[:event_handlers]" do
- it "sets a event_handlers to an empty array by default" do
- expect(Chef::Config[:event_handlers]).to eq([])
- end
- it "should be able to add custom handlers" do
- o = Object.new
- Chef::Config[:event_handlers] << o
- expect(Chef::Config[:event_handlers]).to be_include(o)
- end
- end
-
- describe "Chef::Config[:user_valid_regex]" do
- context "on a platform that is not Windows" do
- it "allows one letter usernames" do
- any_match = Chef::Config[:user_valid_regex].any? { |regex| regex.match('a') }
- expect(any_match).to be_truthy
- end
- end
- end
-
- describe "Chef::Config[:internal_locale]" do
- let(:shell_out) do
- double("Chef::Mixin::ShellOut double", :exitstatus => 0, :stdout => locales)
- end
-
- let(:locales) { locale_array.join("\n") }
-
- before do
- allow(Chef::Config).to receive(:shell_out_with_systems_locale!).with("locale -a").and_return(shell_out)
- end
-
- shared_examples_for "a suitable locale" do
- it "returns an English UTF-8 locale" do
- expect(Chef::Log).to_not receive(:warn).with(/Please install an English UTF-8 locale for Chef to use/)
- expect(Chef::Log).to_not receive(:debug).with(/Defaulting to locale en_US.UTF-8 on Windows/)
- expect(Chef::Log).to_not receive(:debug).with(/No usable locale -a command found/)
- expect(Chef::Config.guess_internal_locale).to eq expected_locale
- end
- end
-
- context "when the result includes 'C.UTF-8'" do
- include_examples "a suitable locale" do
- let(:locale_array) { [expected_locale, "en_US.UTF-8"] }
- let(:expected_locale) { "C.UTF-8" }
- end
- end
-
- context "when the result includes 'en_US.UTF-8'" do
- include_examples "a suitable locale" do
- let(:locale_array) { ["en_CA.UTF-8", expected_locale, "en_NZ.UTF-8"] }
- let(:expected_locale) { "en_US.UTF-8" }
- end
- end
-
- context "when the result includes 'en_US.utf8'" do
- include_examples "a suitable locale" do
- let(:locale_array) { ["en_CA.utf8", "en_US.utf8", "en_NZ.utf8"] }
- let(:expected_locale) { "en_US.UTF-8" }
- end
- end
-
- context "when the result includes 'en.UTF-8'" do
- include_examples "a suitable locale" do
- let(:locale_array) { ["en.ISO8859-1", expected_locale] }
- let(:expected_locale) { "en.UTF-8" }
- end
- end
-
- context "when the result includes 'en_*.UTF-8'" do
- include_examples "a suitable locale" do
- let(:locale_array) { [expected_locale, "en_CA.UTF-8", "en_GB.UTF-8"] }
- let(:expected_locale) { "en_AU.UTF-8" }
- end
- end
-
- context "when the result includes 'en_*.utf8'" do
- include_examples "a suitable locale" do
- let(:locale_array) { ["en_AU.utf8", "en_CA.utf8", "en_GB.utf8"] }
- let(:expected_locale) { "en_AU.UTF-8" }
- end
- end
-
- context "when the result does not include 'en_*.UTF-8'" do
- let(:locale_array) { ["af_ZA", "af_ZA.ISO8859-1", "af_ZA.ISO8859-15", "af_ZA.UTF-8"] }
-
- it "should fall back to C locale" do
- expect(Chef::Log).to receive(:warn).with("Please install an English UTF-8 locale for Chef to use, falling back to C locale and disabling UTF-8 support.")
- expect(Chef::Config.guess_internal_locale).to eq 'C'
- end
- end
-
- context "on error" do
- let(:locale_array) { [] }
-
- before do
- allow(Chef::Config).to receive(:shell_out_with_systems_locale!).and_raise("THIS IS AN ERROR")
- end
-
- it "should default to 'en_US.UTF-8'" do
- if is_windows
- expect(Chef::Log).to receive(:debug).with("Defaulting to locale en_US.UTF-8 on Windows, until it matters that we do something else.")
- else
- expect(Chef::Log).to receive(:debug).with("No usable locale -a command found, assuming you have en_US.UTF-8 installed.")
- end
- expect(Chef::Config.guess_internal_locale).to eq "en_US.UTF-8"
- end
- end
- end
+ describe ":log_location" do
+ include_examples "deprecated by ohai but not deprecated" do
+ let(:option) { :log_location }
+ let(:value) { "path/to/log" }
end
end
- describe "Treating deprecation warnings as errors" do
-
- context "when using our default RSpec configuration" do
-
- it "defaults to treating deprecation warnings as errors" do
- expect(Chef::Config[:treat_deprecation_warnings_as_errors]).to be(true)
- end
-
- it "sets CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS environment variable" do
- expect(ENV['CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS']).to eq("1")
- end
-
- it "treats deprecation warnings as errors in child processes when testing" do
- # Doing a full integration test where we launch a child process is slow
- # and liable to break for weird reasons (bundler env stuff, etc.), so
- # we're just checking that the presence of the environment variable
- # causes treat_deprecation_warnings_as_errors to be set to true after a
- # config reset.
- Chef::Config.reset
- expect(Chef::Config[:treat_deprecation_warnings_as_errors]).to be(true)
- end
-
- end
-
- context "outside of our test environment" do
-
- before do
- ENV.delete('CHEF_TREAT_DEPRECATION_WARNINGS_AS_ERRORS')
- Chef::Config.reset
- end
-
- it "defaults to NOT treating deprecation warnings as errors" do
- expect(Chef::Config[:treat_deprecation_warnings_as_errors]).to be(false)
- end
- end
-
-
- end
end
diff --git a/spec/unit/cookbook/cookbook_version_loader_spec.rb b/spec/unit/cookbook/cookbook_version_loader_spec.rb
index 2c4ad11787..23ffc21f7f 100644
--- a/spec/unit/cookbook/cookbook_version_loader_spec.rb
+++ b/spec/unit/cookbook/cookbook_version_loader_spec.rb
@@ -20,7 +20,7 @@ require 'spec_helper'
describe Chef::Cookbook::CookbookVersionLoader do
before do
- allow(Chef::Platform).to receive(:windows?) { false }
+ allow(ChefConfig).to receive(:windows?) { false }
end
describe "loading a cookbook" do
diff --git a/spec/unit/cookbook/file_vendor_spec.rb b/spec/unit/cookbook/file_vendor_spec.rb
index 4fad7d5808..145541a63f 100644
--- a/spec/unit/cookbook/file_vendor_spec.rb
+++ b/spec/unit/cookbook/file_vendor_spec.rb
@@ -21,9 +21,6 @@ describe Chef::Cookbook::FileVendor do
let(:file_vendor_class) { Class.new(described_class) }
- # A manifest is a Hash of the format defined by Chef::CookbookVersion#manifest
- let(:manifest) { {:cookbook_name => "bob"} }
-
context "when configured to fetch files over http" do
let(:http) { double("Chef::REST") }
@@ -40,19 +37,42 @@ describe Chef::Cookbook::FileVendor do
expect(file_vendor_class.initialization_options).to eq(http)
end
- it "creates a RemoteFileVendor for a given manifest" do
- file_vendor = file_vendor_class.create_from_manifest(manifest)
- expect(file_vendor).to be_a_kind_of(Chef::Cookbook::RemoteFileVendor)
- expect(file_vendor.rest).to eq(http)
- expect(file_vendor.cookbook_name).to eq("bob")
+ context "with a manifest from a cookbook version" do
+
+ # A manifest is a Hash of the format defined by Chef::CookbookVersion#manifest
+ let(:manifest) { {:cookbook_name => "bob", :name => "bob-1.2.3"} }
+
+ it "creates a RemoteFileVendor for a given manifest" do
+ file_vendor = file_vendor_class.create_from_manifest(manifest)
+ expect(file_vendor).to be_a_kind_of(Chef::Cookbook::RemoteFileVendor)
+ expect(file_vendor.rest).to eq(http)
+ expect(file_vendor.cookbook_name).to eq("bob")
+ end
+
end
+ context "with a manifest from a cookbook artifact" do
+
+ # A manifest is a Hash of the format defined by Chef::CookbookVersion#manifest
+ let(:manifest) { {:name => "bob"} }
+
+ it "creates a RemoteFileVendor for a given manifest" do
+ file_vendor = file_vendor_class.create_from_manifest(manifest)
+ expect(file_vendor).to be_a_kind_of(Chef::Cookbook::RemoteFileVendor)
+ expect(file_vendor.rest).to eq(http)
+ expect(file_vendor.cookbook_name).to eq("bob")
+ end
+
+ end
end
context "when configured to load files from disk" do
let(:cookbook_path) { %w[/var/chef/cookbooks /var/chef/other_cookbooks] }
+ # A manifest is a Hash of the format defined by Chef::CookbookVersion#manifest
+ let(:manifest) { {:cookbook_name => "bob"} }
+
before do
file_vendor_class.fetch_from_disk(cookbook_path)
end
diff --git a/spec/unit/cookbook/metadata_spec.rb b/spec/unit/cookbook/metadata_spec.rb
index 760ae5dd2a..1b30286f51 100644
--- a/spec/unit/cookbook/metadata_spec.rb
+++ b/spec/unit/cookbook/metadata_spec.rb
@@ -30,7 +30,7 @@ describe Chef::Cookbook::Metadata do
:maintainer_email, :license, :platforms, :dependencies,
:recommendations, :suggestions, :conflicting, :providing,
:replacing, :attributes, :groupings, :recipes, :version,
- :source_url, :issues_url ]
+ :source_url, :issues_url, :privacy ]
end
it "does not depend on object identity for equality" do
@@ -148,6 +148,10 @@ describe Chef::Cookbook::Metadata do
it "has an empty issues_url string" do
expect(metadata.issues_url).to eq('')
end
+
+ it "is not private" do
+ expect(metadata.privacy).to eq(false)
+ end
end
describe "validation" do
@@ -198,7 +202,8 @@ describe Chef::Cookbook::Metadata do
:long_description => "Much Longer\nSeriously",
:version => "0.6.0",
:source_url => "http://example.com",
- :issues_url => "http://example.com/issues"
+ :issues_url => "http://example.com/issues",
+ :privacy => true
}
params.sort { |a,b| a.to_s <=> b.to_s }.each do |field, field_value|
describe field do
@@ -304,6 +309,21 @@ describe Chef::Cookbook::Metadata do
end
end
end
+
+ it "strips out self-dependencies", :chef_lt_13_only do
+ metadata.name('foo')
+ expect(Chef::Log).to receive(:warn).with(
+ "Ignoring self-dependency in cookbook foo, please remove it (in the future this will be fatal)."
+ )
+ metadata.depends('foo')
+ expect(metadata.dependencies).to eql({})
+ end
+
+ it "errors on self-dependencies", :chef_gte_13_only do
+ metadata.name('foo')
+ expect { metadata.depends('foo') }.to raise_error
+ # FIXME: add the error type
+ end
end
describe "attribute groupings" do
@@ -345,7 +365,8 @@ describe Chef::Cookbook::Metadata do
"recipes" => [ "mysql::server", "mysql::master" ],
"default" => [ ],
"source_url" => "http://example.com",
- "issues_url" => "http://example.com/issues"
+ "issues_url" => "http://example.com/issues",
+ "privacy" => true
}
expect(metadata.attribute("/db/mysql/databases", attrs)).to eq(attrs)
end
@@ -386,6 +407,18 @@ describe Chef::Cookbook::Metadata do
}.to raise_error(ArgumentError)
end
+ it "should not accept anything but true or false for the privacy flag" do
+ expect {
+ metadata.attribute("db/mysql/databases", :privacy => true)
+ }.not_to raise_error
+ expect {
+ metadata.attribute("db/mysql/databases", :privacy => false)
+ }.not_to raise_error
+ expect {
+ metadata.attribute("db/mysql/databases", :privacy => 'true')
+ }.to raise_error(ArgumentError)
+ end
+
it "should not accept anything but an array of strings for choice" do
expect {
metadata.attribute("db/mysql/databases", :choice => ['dedicated', 'shared'])
@@ -684,6 +717,7 @@ describe Chef::Cookbook::Metadata do
version
source_url
issues_url
+ privacy
}.each do |t|
it "should include '#{t}'" do
expect(deserialized_metadata[t]).to eq(metadata.send(t.to_sym))
@@ -719,6 +753,7 @@ describe Chef::Cookbook::Metadata do
version
source_url
issues_url
+ privacy
}.each do |t|
it "should match '#{t}'" do
expect(deserialized_metadata.send(t.to_sym)).to eq(metadata.send(t.to_sym))
diff --git a/spec/unit/cookbook/syntax_check_spec.rb b/spec/unit/cookbook/syntax_check_spec.rb
index 471fc01831..764829c387 100644
--- a/spec/unit/cookbook/syntax_check_spec.rb
+++ b/spec/unit/cookbook/syntax_check_spec.rb
@@ -21,7 +21,7 @@ require "chef/cookbook/syntax_check"
describe Chef::Cookbook::SyntaxCheck do
before do
- allow(Chef::Platform).to receive(:windows?) { false }
+ allow(ChefConfig).to receive(:windows?) { false }
end
let(:cookbook_path) { File.join(CHEF_SPEC_DATA, 'cookbooks', 'openldap') }
@@ -53,6 +53,7 @@ describe Chef::Cookbook::SyntaxCheck do
@ruby_files = @attr_files + @libr_files + @defn_files + @recipes + [File.join(cookbook_path, "metadata.rb")]
basenames = %w{ helpers_via_partial_test.erb
helper_test.erb
+ helpers.erb
openldap_stuff.conf.erb
openldap_variable_stuff.conf.erb
test.erb
diff --git a/spec/unit/cookbook_loader_spec.rb b/spec/unit/cookbook_loader_spec.rb
index 45a985bafd..b1384bffe7 100644
--- a/spec/unit/cookbook_loader_spec.rb
+++ b/spec/unit/cookbook_loader_spec.rb
@@ -20,7 +20,7 @@ require 'spec_helper'
describe Chef::CookbookLoader do
before do
- allow(Chef::Platform).to receive(:windows?) {false}
+ allow(ChefConfig).to receive(:windows?) {false}
end
let(:repo_paths) do
[
diff --git a/spec/unit/cookbook_manifest_spec.rb b/spec/unit/cookbook_manifest_spec.rb
index 938f72c743..f985942e09 100644
--- a/spec/unit/cookbook_manifest_spec.rb
+++ b/spec/unit/cookbook_manifest_spec.rb
@@ -24,6 +24,8 @@ describe Chef::CookbookManifest do
let(:version) { "1.2.3" }
+ let(:identifier) { "9e10455ce2b4a4e29424b7064b1d67a1a25c9d3b" }
+
let(:metadata) do
Chef::Cookbook::Metadata.new.tap do |m|
m.version(version)
@@ -35,6 +37,7 @@ describe Chef::CookbookManifest do
let(:cookbook_version) do
Chef::CookbookVersion.new("tatft", cookbook_root).tap do |c|
c.metadata = metadata
+ c.identifier = identifier
end
end
@@ -212,12 +215,26 @@ describe Chef::CookbookManifest do
let(:policy_mode) { true }
+ let(:cookbook_manifest_hash) { cookbook_manifest.to_hash }
+
+ it "sets the identifier in the manifest data" do
+ expect(cookbook_manifest_hash["identifier"]).to eq("9e10455ce2b4a4e29424b7064b1d67a1a25c9d3b")
+ end
+
+ it "sets the name to just the name" do
+ expect(cookbook_manifest_hash["name"]).to eq("tatft")
+ end
+
+ it "does not set a 'cookbook_name' field" do
+ expect(cookbook_manifest_hash).to_not have_key("cookbook_name")
+ end
+
it "gives the save URL" do
- expect(cookbook_manifest.save_url).to eq("cookbook_artifacts/tatft/1.2.3")
+ expect(cookbook_manifest.save_url).to eq("cookbook_artifacts/tatft/9e10455ce2b4a4e29424b7064b1d67a1a25c9d3b")
end
it "gives the force save URL" do
- expect(cookbook_manifest.force_save_url).to eq("cookbook_artifacts/tatft/1.2.3?force=true")
+ expect(cookbook_manifest.force_save_url).to eq("cookbook_artifacts/tatft/9e10455ce2b4a4e29424b7064b1d67a1a25c9d3b?force=true")
end
end
diff --git a/spec/unit/cookbook_site_streaming_uploader_spec.rb b/spec/unit/cookbook_site_streaming_uploader_spec.rb
index ef0f649163..0041a142dc 100644
--- a/spec/unit/cookbook_site_streaming_uploader_spec.rb
+++ b/spec/unit/cookbook_site_streaming_uploader_spec.rb
@@ -121,27 +121,6 @@ describe Chef::CookbookSiteStreamingUploader do
})
end
- describe "http verify mode" do
- before do
- @uri = "https://cookbooks.dummy.com/api/v1/cookbooks"
- uri_info = URI.parse(@uri)
- @http = Net::HTTP.new(uri_info.host, uri_info.port)
- expect(Net::HTTP).to receive(:new).with(uri_info.host, uri_info.port).and_return(@http)
- end
-
- it "should be VERIFY_NONE when ssl_verify_mode is :verify_none" do
- Chef::Config[:ssl_verify_mode] = :verify_none
- Chef::CookbookSiteStreamingUploader.make_request(:post, @uri, 'bill', @secret_filename)
- expect(@http.verify_mode).to eq(OpenSSL::SSL::VERIFY_NONE)
- end
-
- it "should be VERIFY_PEER when ssl_verify_mode is :verify_peer" do
- Chef::Config[:ssl_verify_mode] = :verify_peer
- Chef::CookbookSiteStreamingUploader.make_request(:post, @uri, 'bill', @secret_filename)
- expect(@http.verify_mode).to eq(OpenSSL::SSL::VERIFY_PEER)
- end
- end
-
end # make_request
describe "StreamPart" do
diff --git a/spec/unit/cookbook_spec.rb b/spec/unit/cookbook_spec.rb
index 7b3cda2af1..f36b031309 100644
--- a/spec/unit/cookbook_spec.rb
+++ b/spec/unit/cookbook_spec.rb
@@ -59,15 +59,6 @@ describe Chef::CookbookVersion do
expect(@cookbook.fully_qualified_recipe_names.include?("openldap::three")).to eq(true)
end
- it "should find a preferred file" do
- skip
- end
-
- it "should not return an unchanged preferred file" do
- pending
- expect(@cookbook.preferred_filename(@node, :files, 'a-filename', 'the-checksum')).to be_nil
- end
-
it "should raise an ArgumentException if you try to load a bad recipe name" do
expect { @cookbook.load_recipe("doesnt_exist", @node) }.to raise_error(ArgumentError)
end
diff --git a/spec/unit/cookbook_uploader_spec.rb b/spec/unit/cookbook_uploader_spec.rb
index 152e5373f0..76727c18e2 100644
--- a/spec/unit/cookbook_uploader_spec.rb
+++ b/spec/unit/cookbook_uploader_spec.rb
@@ -25,11 +25,17 @@ describe Chef::CookbookUploader do
let(:cookbook_loader) do
loader = Chef::CookbookLoader.new(File.join(CHEF_SPEC_DATA, "cookbooks"))
loader.load_cookbooks
+ loader.cookbooks_by_name["apache2"].identifier = apache2_identifier
+ loader.cookbooks_by_name["java"].identifier = java_identifier
loader
end
+ let(:apache2_identifier) { "6644e6cb2ade90b8aff2ebb44728958fbc939ebf" }
+
let(:apache2_cookbook) { cookbook_loader.cookbooks_by_name["apache2"] }
+ let(:java_identifier) { "edd40c30c4e0ebb3658abde4620597597d2e9c17" }
+
let(:java_cookbook) { cookbook_loader.cookbooks_by_name["java"] }
let(:cookbooks_to_upload) { [apache2_cookbook, java_cookbook] }
@@ -175,7 +181,7 @@ describe Chef::CookbookUploader do
let(:policy_mode) { true }
def expected_save_url(cookbook)
- "cookbook_artifacts/#{cookbook.name}/#{cookbook.version}"
+ "cookbook_artifacts/#{cookbook.name}/#{cookbook.identifier}"
end
it "uploads all files in a sandbox transaction, then creates cookbooks on the server using cookbook_artifacts API" do
diff --git a/spec/unit/cookbook_version_spec.rb b/spec/unit/cookbook_version_spec.rb
index 440dd9da6c..2bccddcaec 100644
--- a/spec/unit/cookbook_version_spec.rb
+++ b/spec/unit/cookbook_version_spec.rb
@@ -306,26 +306,6 @@ describe Chef::CookbookVersion do
subject(:cbv) { Chef::CookbookVersion.new("version validation", '/tmp/blah') }
- describe "HTTP Resource behaviors", pending: "will be deprected when CookbookManifest API is stablized" do
-
- it "errors on #save_url" do
- expect { cbv.save_url }.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
- end
-
- it "errors on #force_save_url" do
- expect { cbv.force_save_url }.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
- end
-
- it "errors on #to_hash" do
- expect { cbv.to_hash }.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
- end
-
- it "errors on #to_json" do
- expect { cbv.to_json }.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
- end
-
- end
-
it "errors on #status and #status=" do
expect { cbv.status = :wat }.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
expect { cbv.status }.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
@@ -356,7 +336,7 @@ describe Chef::CookbookVersion do
end
- include_examples "to_json equalivent to Chef::JSONCompat.to_json" do
+ include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
let(:jsonable) { Chef::CookbookVersion.new("tatft", '/tmp/blah') }
end
diff --git a/spec/unit/data_bag_item_spec.rb b/spec/unit/data_bag_item_spec.rb
index 4348252388..497817ecf1 100644
--- a/spec/unit/data_bag_item_spec.rb
+++ b/spec/unit/data_bag_item_spec.rb
@@ -193,7 +193,7 @@ describe Chef::DataBagItem do
expect(deserial["snooze"]).to eq({ "finally" => "world_will" })
end
- include_examples "to_json equalivent to Chef::JSONCompat.to_json" do
+ include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
let(:jsonable) { data_bag_item }
end
end
diff --git a/spec/unit/data_bag_spec.rb b/spec/unit/data_bag_spec.rb
index f6db1e222a..13b835d120 100644
--- a/spec/unit/data_bag_spec.rb
+++ b/spec/unit/data_bag_spec.rb
@@ -22,7 +22,7 @@ require 'chef/data_bag'
describe Chef::DataBag do
before(:each) do
@data_bag = Chef::DataBag.new
- allow(Chef::Platform)::to receive(:windows?) { false }
+ allow(ChefConfig).to receive(:windows?) { false }
end
describe "initialize" do
@@ -73,7 +73,7 @@ describe Chef::DataBag do
expect(@deserial.send(t.to_sym)).to eq(@data_bag.send(t.to_sym))
end
- include_examples "to_json equalivent to Chef::JSONCompat.to_json" do
+ include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
let(:jsonable) { @data_bag }
end
end
diff --git a/spec/unit/deprecation_spec.rb b/spec/unit/deprecation_spec.rb
index f824cb7c76..674de5ec1d 100644
--- a/spec/unit/deprecation_spec.rb
+++ b/spec/unit/deprecation_spec.rb
@@ -65,19 +65,16 @@ describe Chef::Deprecation do
end
context 'deprecation warning messages' do
- before(:each) do
- @warning_output = [ ]
- allow(Chef::Log).to receive(:warn) { |msg| @warning_output << msg }
- end
+ RSpec::Matchers.define_negated_matcher :a_non_empty_array, :be_empty
it 'should be enabled for deprecated methods' do
+ expect(Chef::Log).to receive(:warn).with(a_non_empty_array)
TestClass.new.deprecated_method(10)
- expect(@warning_output).not_to be_empty
end
it 'should contain stack trace' do
+ expect(Chef::Log).to receive(:warn).with(a_string_including(".rb"))
TestClass.new.deprecated_method(10)
- expect(@warning_output.join("").include?(".rb")).to be_truthy
end
end
@@ -95,4 +92,59 @@ describe Chef::Deprecation do
expect { test_instance.deprecated_method(10) }.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
end
+ context "When a class has deprecated_attr, _reader and _writer" do
+ before(:context) do
+ class DeprecatedAttrTest
+ extend Chef::Mixin::Deprecation
+ def initialize
+ @a = @r = @w = 1
+ end
+ deprecated_attr :a, "a"
+ deprecated_attr_reader :r, "r"
+ deprecated_attr_writer :w, "w"
+ end
+ end
+
+ it "The deprecated_attr emits warnings" do
+ test = DeprecatedAttrTest.new
+ expect { test.a = 10 }.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
+ expect { test.a }.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
+ end
+
+ it "The deprecated_attr_writer emits warnings, and does not create a reader" do
+ test = DeprecatedAttrTest.new
+ expect { test.w = 10 }.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
+ expect { test.w }.to raise_error(NoMethodError)
+ end
+
+ it "The deprecated_attr_reader emits warnings, and does not create a writer" do
+ test = DeprecatedAttrTest.new
+ expect { test.r = 10 }.to raise_error(NoMethodError)
+ expect { test.r }.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
+ end
+
+ context "With deprecation warnings not throwing exceptions" do
+ before do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ end
+
+ it "The deprecated_attr can be written to and read from" do
+ test = DeprecatedAttrTest.new
+ test.a = 10
+ expect(test.a).to eq 10
+ end
+
+ it "The deprecated_attr_reader can be read from" do
+ test = DeprecatedAttrTest.new
+ expect(test.r).to eq 1
+ end
+
+ it "The deprecated_attr_writer can be written to" do
+ test = DeprecatedAttrTest.new
+ test.w = 10
+ expect(test.instance_eval { @w }).to eq 10
+ end
+ end
+ end
+
end
diff --git a/spec/unit/dsl/reboot_pending_spec.rb b/spec/unit/dsl/reboot_pending_spec.rb
index 0f2288740f..a55f91d5e6 100644
--- a/spec/unit/dsl/reboot_pending_spec.rb
+++ b/spec/unit/dsl/reboot_pending_spec.rb
@@ -46,7 +46,7 @@ describe Chef::DSL::RebootPending do
end
it 'should return true if key "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootRequired" exists' do
- allow(recipe).to receive(:registry_key_exists?).with('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootRequired').and_return(true)
+ allow(recipe).to receive(:registry_key_exists?).with('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending').and_return(true)
expect(recipe.reboot_pending?).to be_truthy
end
diff --git a/spec/unit/dsl/resources_spec.rb b/spec/unit/dsl/resources_spec.rb
new file mode 100644
index 0000000000..581c835290
--- /dev/null
+++ b/spec/unit/dsl/resources_spec.rb
@@ -0,0 +1,85 @@
+#
+# Author:: Noah Kantrowitz (<noah@coderanger.net>)
+# Copyright:: Copyright (c) 2015 Noah Kantrowitz
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'chef/dsl/resources'
+
+describe Chef::DSL::Resources do
+ let(:declared_resources) { [] }
+ let(:test_class) do
+ r = declared_resources
+ Class.new do
+ include Chef::DSL::Resources
+ define_method(:declare_resource) do |dsl_name, name, _created_at, &_block|
+ r << [dsl_name, name]
+ end
+ end
+ end
+ subject { declared_resources }
+ after do
+ # Always clean up after ourselves.
+ described_class.remove_resource_dsl(:test_resource)
+ end
+
+ context 'with a resource added' do
+ before do
+ Chef::DSL::Resources.add_resource_dsl(:test_resource)
+ test_class.new.instance_eval do
+ test_resource 'test_name' do
+ end
+ end
+ end
+ it { is_expected.to eq [[:test_resource, 'test_name']]}
+ end
+
+ context 'with no resource added' do
+ subject do
+ test_class.new.instance_eval do
+ test_resource 'test_name' do
+ end
+ end
+ end
+
+ it { expect { subject }.to raise_error NoMethodError }
+ end
+
+ context 'with a resource added and removed' do
+ before do
+ Chef::DSL::Resources.add_resource_dsl(:test_resource)
+ Chef::DSL::Resources.remove_resource_dsl(:test_resource)
+ end
+ subject do
+ test_class.new.instance_eval do
+ test_resource 'test_name' do
+ end
+ end
+ end
+
+ it { expect { subject }.to raise_error NoMethodError }
+ end
+
+ context 'with a nameless resource' do
+ before do
+ Chef::DSL::Resources.add_resource_dsl(:test_resource)
+ test_class.new.instance_eval do
+ test_resource { }
+ end
+ end
+ it { is_expected.to eq [[:test_resource, nil]]}
+ end
+end
diff --git a/spec/unit/environment_spec.rb b/spec/unit/environment_spec.rb
index ee3b8b21e1..64617e0888 100644
--- a/spec/unit/environment_spec.rb
+++ b/spec/unit/environment_spec.rb
@@ -208,7 +208,7 @@ describe Chef::Environment do
expect(@json).to match(/"chef_type":"environment"/)
end
- include_examples "to_json equalivent to Chef::JSONCompat.to_json" do
+ include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
let(:jsonable) { @environment }
end
end
diff --git a/spec/unit/event_dispatch/dispatcher_spec.rb b/spec/unit/event_dispatch/dispatcher_spec.rb
new file mode 100644
index 0000000000..1014feea89
--- /dev/null
+++ b/spec/unit/event_dispatch/dispatcher_spec.rb
@@ -0,0 +1,80 @@
+#
+# Author:: Daniel DeLeo (<dan@chef.io>)
+#
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'chef/event_dispatch/dispatcher'
+
+describe Chef::EventDispatch::Dispatcher do
+
+ subject(:dispatcher) { Chef::EventDispatch::Dispatcher.new }
+
+ let(:event_sink) { instance_double("Chef::EventDispatch::Base") }
+
+ it "has no subscribers by default" do
+ expect(dispatcher.subscribers).to be_empty
+ end
+
+ context "when an event sink is registered" do
+
+ before do
+ dispatcher.register(event_sink)
+ end
+
+ it "it has the event sink as a subscriber" do
+ expect(dispatcher.subscribers.size).to eq(1)
+ expect(dispatcher.subscribers.first).to eq(event_sink)
+ end
+
+ it "forwards events to the subscribed event sink" do
+ # the events all have different arity and such so we just hit a few different events:
+
+ expect(event_sink).to receive(:run_start).with("12.4.0")
+ dispatcher.run_start("12.4.0")
+
+ cookbook_version = double("cookbook_version")
+ expect(event_sink).to receive(:synchronized_cookbook).with("apache2", cookbook_version)
+ dispatcher.synchronized_cookbook("apache2", cookbook_version)
+
+ exception = StandardError.new("foo")
+ expect(event_sink).to receive(:recipe_file_load_failed).with("/path/to/file.rb", exception)
+ dispatcher.recipe_file_load_failed("/path/to/file.rb", exception)
+ end
+
+ context "when an event sink has fewer arguments for an event" do
+ # Can't use a double because they don't report arity correctly.
+ let(:event_sink) do
+ Class.new(Chef::EventDispatch::Base) do
+ attr_reader :synchronized_cookbook_args
+ def synchronized_cookbook(cookbook_name)
+ @synchronized_cookbook_args = [cookbook_name]
+ end
+ end.new
+ end
+
+ it "trims the arugment list" do
+ cookbook_version = double("cookbook_version")
+ dispatcher.synchronized_cookbook("apache2", cookbook_version)
+ expect(event_sink.synchronized_cookbook_args).to eq ["apache2"]
+ end
+ end
+
+ end
+
+end
+
diff --git a/spec/unit/event_dispatch/dsl_spec.rb b/spec/unit/event_dispatch/dsl_spec.rb
new file mode 100644
index 0000000000..0f7adce7a8
--- /dev/null
+++ b/spec/unit/event_dispatch/dsl_spec.rb
@@ -0,0 +1,83 @@
+#
+# Author:: Ranjib Dey (<ranjib@linux.com>)
+#
+# Copyright:: Copyright (c) 2015 Ranjib Dey
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+require 'spec_helper'
+require 'chef/event_dispatch/dsl'
+
+describe Chef::EventDispatch::DSL do
+ let(:events) do
+ Chef::EventDispatch::Dispatcher.new
+ end
+
+ let(:run_context) do
+ Chef::RunContext.new(Chef::Node.new, nil, events)
+ end
+
+ before do
+ Chef.set_run_context(run_context)
+ end
+
+ subject{ described_class.new('test') }
+
+ it 'set handler name' do
+ subject.on(:run_started) {}
+ expect(events.subscribers.first.name).to eq('test')
+ end
+
+ it 'raise error when invalid event type is supplied' do
+ expect do
+ subject.on(:foo_bar) {}
+ end.to raise_error(Chef::Exceptions::InvalidEventType)
+ end
+
+ it 'register user hooks against valid event type' do
+ subject.on(:run_failed) {'testhook'}
+ expect(events.subscribers.first.run_failed).to eq('testhook')
+ end
+
+ it 'preserve state across event hooks' do
+ calls = []
+ Chef.event_handler do
+ on :resource_updated do
+ calls << :updated
+ end
+ on :resource_action_start do
+ calls << :started
+ end
+ end
+ resource = Chef::Resource::RubyBlock.new('foo', run_context)
+ resource.block { }
+ resource.run_action(:run)
+ expect(calls).to eq([:started, :updated])
+ end
+
+ it 'preserve instance variables across handler callbacks' do
+ Chef.event_handler do
+ on :resource_action_start do
+ @ivar = [1]
+ end
+ on :resource_updated do
+ @ivar << 2
+ end
+ end
+ resource = Chef::Resource::RubyBlock.new('foo', run_context)
+ resource.block { }
+ resource.run_action(:run)
+ expect(events.subscribers.first.instance_variable_get(:@ivar)).to eq([1, 2])
+ end
+end
diff --git a/spec/unit/exceptions_spec.rb b/spec/unit/exceptions_spec.rb
index d35ecc8ec8..85c54aa693 100644
--- a/spec/unit/exceptions_spec.rb
+++ b/spec/unit/exceptions_spec.rb
@@ -76,7 +76,7 @@ describe Chef::Exceptions do
end
if exception.methods.include?(:to_json)
- include_examples "to_json equalivent to Chef::JSONCompat.to_json" do
+ include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
let(:jsonable) { exception }
end
end
@@ -113,7 +113,7 @@ describe Chef::Exceptions do
context "initialized with 1 error and nil" do
let(:e) { Chef::Exceptions::RunFailedWrappingError.new(RuntimeError.new("foo"), nil) }
let(:num_errors) { 1 }
- let(:backtrace) { ["1) RuntimeError - foo", ""] }
+ let(:backtrace) { ["1) RuntimeError - foo"] }
include_examples "RunFailedWrappingError expectations"
end
@@ -121,7 +121,7 @@ describe Chef::Exceptions do
context "initialized with 2 errors" do
let(:e) { Chef::Exceptions::RunFailedWrappingError.new(RuntimeError.new("foo"), RuntimeError.new("bar")) }
let(:num_errors) { 2 }
- let(:backtrace) { ["1) RuntimeError - foo", "", "2) RuntimeError - bar", ""] }
+ let(:backtrace) { ["1) RuntimeError - foo", "", "2) RuntimeError - bar"] }
include_examples "RunFailedWrappingError expectations"
end
diff --git a/spec/unit/file_content_management/deploy/mv_windows_spec.rb b/spec/unit/file_content_management/deploy/mv_windows_spec.rb
index c52001cd26..2d1981befc 100644
--- a/spec/unit/file_content_management/deploy/mv_windows_spec.rb
+++ b/spec/unit/file_content_management/deploy/mv_windows_spec.rb
@@ -115,6 +115,66 @@ describe Chef::FileContentManagement::Deploy::MvWindows do
end
+ context "and the target file has null dacl and sacl" do
+
+ before do
+ allow(target_file_security_descriptor).to receive(:dacl_present?).and_return(true)
+ allow(target_file_security_descriptor).to receive(:dacl).and_return(nil)
+ allow(target_file_security_descriptor).to receive(:dacl_inherits?).and_return(false)
+
+ allow(target_file_security_descriptor).to receive(:sacl_present?).and_return(true)
+ allow(target_file_security_descriptor).to receive(:sacl).and_return(nil)
+ allow(target_file_security_descriptor).to receive(:sacl_inherits?).and_return(false)
+
+ expect(updated_target_security_object).to receive(:set_dacl).with(nil, false)
+ expect(updated_target_security_object).to receive(:set_sacl).with(nil, false)
+ end
+
+
+ it "fixes up permissions and moves the file into place" do
+ content_deployer.deploy(staging_file_path, target_file_path)
+ end
+
+ end
+
+ context "and the target has an empty dacl and sacl" do
+ let(:original_target_file_dacl) { [] }
+ let(:original_target_file_sacl) { [] }
+
+ let(:empty_dacl) { double("Windows ACL with no dacl ACEs") }
+ let(:empty_sacl) { double("Windows ACL with no sacl ACEs") }
+
+ before do
+ allow(target_file_security_descriptor).to receive(:dacl_present?).and_return(true)
+ allow(target_file_security_descriptor).to receive(:dacl_inherits?).and_return(false)
+
+ allow(target_file_security_descriptor).to receive(:dacl).and_return(original_target_file_dacl)
+ expect(Chef::ReservedNames::Win32::Security::ACL).
+ to receive(:create).
+ with([]).
+ and_return(empty_dacl)
+
+
+ allow(target_file_security_descriptor).to receive(:sacl_present?).and_return(true)
+ allow(target_file_security_descriptor).to receive(:sacl_inherits?).and_return(false)
+
+ allow(target_file_security_descriptor).to receive(:sacl).and_return(original_target_file_sacl)
+ expect(Chef::ReservedNames::Win32::Security::ACL).
+ to receive(:create).
+ with([]).
+ and_return(empty_sacl)
+
+
+ expect(updated_target_security_object).to receive(:set_dacl).with(empty_dacl, false)
+ expect(updated_target_security_object).to receive(:set_sacl).with(empty_sacl, false)
+ end
+
+
+ it "fixes up permissions and moves the file into place" do
+ content_deployer.deploy(staging_file_path, target_file_path)
+ end
+ end
+
context "and the target has a dacl and sacl" do
let(:inherited_dacl_ace) { double("Windows dacl ace (inherited)", :inherited? => true) }
let(:not_inherited_dacl_ace) { double("Windows dacl ace (not inherited)", :inherited? => false) }
diff --git a/spec/unit/formatters/doc_spec.rb b/spec/unit/formatters/doc_spec.rb
new file mode 100644
index 0000000000..eb98f5abd3
--- /dev/null
+++ b/spec/unit/formatters/doc_spec.rb
@@ -0,0 +1,52 @@
+#
+# Author:: Daniel DeLeo (<dan@chef.io>)
+#
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Formatters::Base do
+
+ let(:out) { StringIO.new }
+ let(:err) { StringIO.new }
+
+ subject(:formatter) { Chef::Formatters::Doc.new(out, err) }
+
+ it "prints a policyfile's name and revision ID" do
+ minimal_policyfile = {
+ "revision_id"=> "613f803bdd035d574df7fa6da525b38df45a74ca82b38b79655efed8a189e073",
+ "name"=> "jenkins",
+ "run_list"=> [
+ "recipe[apt::default]",
+ "recipe[java::default]",
+ "recipe[jenkins::master]",
+ "recipe[policyfile_demo::default]"
+ ],
+ "cookbook_locks"=> { }
+ }
+
+ formatter.policyfile_loaded(minimal_policyfile)
+ expect(out.string).to include("Using policy 'jenkins' at revision '613f803bdd035d574df7fa6da525b38df45a74ca82b38b79655efed8a189e073'")
+ end
+
+ it "prints cookbook name and version" do
+ cookbook_version = double(name: "apache2", version: "1.2.3")
+ formatter.synchronized_cookbook("apache2", cookbook_version)
+ expect(out.string).to include("- apache2 (1.2.3")
+ end
+
+end
diff --git a/spec/unit/formatters/error_inspectors/api_error_formatting_spec.rb b/spec/unit/formatters/error_inspectors/api_error_formatting_spec.rb
new file mode 100644
index 0000000000..b8c2de2b8b
--- /dev/null
+++ b/spec/unit/formatters/error_inspectors/api_error_formatting_spec.rb
@@ -0,0 +1,77 @@
+#
+# Author:: Tyler Cloke (<tyler@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'chef/formatters/error_inspectors/api_error_formatting'
+
+describe Chef::Formatters::APIErrorFormatting do
+ let(:class_instance) { (Class.new { include Chef::Formatters::APIErrorFormatting }).new }
+ let(:error_description) { instance_double(Chef::Formatters::ErrorDescription) }
+ let(:response) { double("response") }
+ before do
+ allow(response).to receive(:body)
+ end
+
+
+ context "when describe_406_error is called" do
+ context "when response['x-ops-server-api-version'] exists" do
+ let(:min_version) { "2" }
+ let(:max_version) { "5" }
+ let(:request_version) { "30" }
+ let(:return_hash) {
+ {
+ "min_version" => min_version,
+ "max_version" => max_version,
+ "request_version" => request_version
+ }
+ }
+
+ before do
+ # mock out the header
+ allow(response).to receive(:[]).with('x-ops-server-api-version').and_return(Chef::JSONCompat.to_json(return_hash))
+ end
+
+ it "prints an error about client and server API version incompatibility with a min API version" do
+ expect(error_description).to receive(:section).with("Incompatible server API version:",/a min API version of #{min_version}/)
+ class_instance.describe_406_error(error_description, response)
+ end
+
+ it "prints an error about client and server API version incompatibility with a max API version" do
+ expect(error_description).to receive(:section).with("Incompatible server API version:",/a max API version of #{max_version}/)
+ class_instance.describe_406_error(error_description, response)
+ end
+
+ it "prints an error describing the request API version" do
+ expect(error_description).to receive(:section).with("Incompatible server API version:",/a request with an API version of #{request_version}/)
+ class_instance.describe_406_error(error_description, response)
+ end
+ end
+
+ context "when response.body['error'] != 'invalid-x-ops-server-api-version'" do
+
+ before do
+ allow(response).to receive(:[]).with('x-ops-server-api-version').and_return(nil)
+ end
+
+ it "forwards the error_description to describe_http_error" do
+ expect(class_instance).to receive(:describe_http_error).with(error_description)
+ class_instance.describe_406_error(error_description, response)
+ end
+ end
+ end
+end
diff --git a/spec/unit/formatters/error_inspectors/compile_error_inspector_spec.rb b/spec/unit/formatters/error_inspectors/compile_error_inspector_spec.rb
index ac19e91922..3c8d5dfa29 100644
--- a/spec/unit/formatters/error_inspectors/compile_error_inspector_spec.rb
+++ b/spec/unit/formatters/error_inspectors/compile_error_inspector_spec.rb
@@ -37,69 +37,148 @@ end
E
describe Chef::Formatters::ErrorInspectors::CompileErrorInspector do
- before do
- @node_name = "test-node.example.com"
- @description = Chef::Formatters::ErrorDescription.new("Error Evaluating File:")
- @exception = NoMethodError.new("undefined method `this_is_not_a_valid_method' for Chef::Resource::File")
- @outputter = Chef::Formatters::IndentableOutputStream.new(StringIO.new, STDERR)
- #@outputter = Chef::Formatters::IndentableOutputStream.new(STDOUT, STDERR)
- end
+ let(:node_name) { "test-node.example.com" }
- describe "when scrubbing backtraces" do
- it "shows backtrace lines from cookbook files" do
- # Error inspector originally used file_cache_path which is incorrect on
- # chef-solo. Using cookbook_path should do the right thing for client and
- # solo.
- allow(Chef::Config).to receive(:cookbook_path).and_return([ "/home/someuser/dev-laptop/cookbooks" ])
- @trace = [
- "/home/someuser/dev-laptop/cookbooks/syntax-err/recipes/default.rb:14:in `from_file'",
- "/home/someuser/dev-laptop/cookbooks/syntax-err/recipes/default.rb:11:in `from_file'",
- "/home/someuser/.multiruby/gems/chef/lib/chef/client.rb:123:in `run'"
- ]
- @exception.set_backtrace(@trace)
- @path = "/var/chef/cache/cookbooks/syntax-err/recipes/default.rb"
- @inspector = described_class.new(@path, @exception)
+ let(:description) { Chef::Formatters::ErrorDescription.new("Error Evaluating File:") }
- @expected_filtered_trace = [
- "/home/someuser/dev-laptop/cookbooks/syntax-err/recipes/default.rb:14:in `from_file'",
- "/home/someuser/dev-laptop/cookbooks/syntax-err/recipes/default.rb:11:in `from_file'",
- ]
- expect(@inspector.filtered_bt).to eq(@expected_filtered_trace)
- end
+ let(:exception) do
+ e = NoMethodError.new("undefined method `this_is_not_a_valid_method' for Chef::Resource::File")
+ e.set_backtrace(trace)
+ e
end
- describe "when explaining an error in the compile phase" do
- before do
- allow(Chef::Config).to receive(:cookbook_path).and_return([ "/var/chef/cache/cookbooks" ])
- recipe_lines = BAD_RECIPE.split("\n").map {|l| l << "\n" }
- expect(IO).to receive(:readlines).with("/var/chef/cache/cookbooks/syntax-err/recipes/default.rb").and_return(recipe_lines)
- @trace = [
- "/var/chef/cache/cookbooks/syntax-err/recipes/default.rb:14:in `from_file'",
- "/var/chef/cache/cookbooks/syntax-err/recipes/default.rb:11:in `from_file'",
- "/usr/local/lib/ruby/gems/chef/lib/chef/client.rb:123:in `run'" # should not display
- ]
- @exception.set_backtrace(@trace)
- @path = "/var/chef/cache/cookbooks/syntax-err/recipes/default.rb"
- @inspector = described_class.new(@path, @exception)
- @inspector.add_explanation(@description)
+ # Change to $stdout to print error messages for manual inspection
+ let(:stdout) { StringIO.new }
+
+ let(:outputter) { Chef::Formatters::IndentableOutputStream.new(StringIO.new, STDERR) }
+
+ subject(:inspector) { described_class.new(path_to_failed_file, exception) }
+
+ describe "finding the code responsible for the error" do
+
+ context "when the stacktrace includes cookbook files" do
+
+ let(:trace) do
+ [
+ "/home/someuser/dev-laptop/cookbooks/syntax-err/recipes/default.rb:14:in `from_file'",
+ "/home/someuser/dev-laptop/cookbooks/syntax-err/recipes/default.rb:11:in `from_file'",
+ "/home/someuser/.multiruby/gems/chef/lib/chef/client.rb:123:in `run'"
+ ]
+ end
+
+ let(:expected_filtered_trace) do
+ [
+ "/home/someuser/dev-laptop/cookbooks/syntax-err/recipes/default.rb:14:in `from_file'",
+ "/home/someuser/dev-laptop/cookbooks/syntax-err/recipes/default.rb:11:in `from_file'",
+ ]
+ end
+
+ let(:path_to_failed_file) { "/home/someuser/dev-laptop/cookbooks/syntax-err/recipes/default.rb" }
+
+ before do
+ # Error inspector originally used file_cache_path which is incorrect on
+ # chef-solo. Using cookbook_path should do the right thing for client and
+ # solo.
+ allow(Chef::Config).to receive(:cookbook_path).and_return([ "/home/someuser/dev-laptop/cookbooks" ])
+ end
+
+ describe "when scrubbing backtraces" do
+ it "shows backtrace lines from cookbook files" do
+ expect(inspector.filtered_bt).to eq(expected_filtered_trace)
+ end
+ end
+
+ describe "when explaining an error in the compile phase" do
+ before do
+ recipe_lines = BAD_RECIPE.split("\n").map {|l| l << "\n" }
+ expect(IO).to receive(:readlines).with(path_to_failed_file).and_return(recipe_lines)
+ inspector.add_explanation(description)
+ end
+
+ it "reports the error was not located within cookbooks" do
+ expect(inspector.found_error_in_cookbooks?).to be(true)
+ end
+
+ it "finds the line number of the error from the stacktrace" do
+ expect(inspector.culprit_line).to eq(14)
+ end
+
+ it "prints a pretty message" do
+ description.display(outputter)
+ end
+ end
end
- it "finds the line number of the error from the stacktrace" do
- expect(@inspector.culprit_line).to eq(14)
+ context "when the error is a RuntimeError about frozen object" do
+ let(:exception) do
+ e = RuntimeError.new("can't modify frozen Array")
+ e.set_backtrace(trace)
+ e
+ end
+
+ let(:path_to_failed_file) { "/tmp/kitchen/cache/cookbooks/foo/recipes/default.rb" }
+
+ let(:trace) do
+ [
+ "/tmp/kitchen/cache/cookbooks/foo/recipes/default.rb:2:in `block in from_file'",
+ "/tmp/kitchen/cache/cookbooks/foo/recipes/default.rb:1:in `from_file'"
+ ]
+ end
+
+ describe "when explaining a runtime error in the compile phase" do
+ it "correctly detects RuntimeError for frozen objects" do
+ expect(inspector.exception_message_modifying_frozen?).to be(true)
+ end
+
+ # could also test for description.section to be called, but would have
+ # to adjust every other test to begin using a test double for description
+ end
end
- it "prints a pretty message" do
- @description.display(@outputter)
+ context "when the error does not contain any lines from cookbooks" do
+
+ let(:trace) do
+ [
+ "/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:144:in `rescue in block in load_libraries'",
+ "/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:138:in `block in load_libraries'",
+ "/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:230:in `call'",
+ "/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:230:in `block (2 levels) in foreach_cookbook_load_segment'",
+ "/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:229:in `each'",
+ "/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:229:in `block in foreach_cookbook_load_segment'",
+ "/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:227:in `each'",
+ "/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:227:in `foreach_cookbook_load_segment'",
+ "/opt/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:137:in `load_libraries'"
+ ]
+ end
+
+ let(:exception) do
+ e = Chef::Exceptions::RecipeNotFound.new("recipe nope:nope not found")
+ e.set_backtrace(trace)
+ e
+ end
+
+ let(:path_to_failed_file) { nil }
+
+ it "gives a full, non-filtered trace" do
+ expect(inspector.filtered_bt).to eq(trace)
+ end
+
+ it "does not error when displaying the error" do
+ expect { description.display(outputter) }.to_not raise_error
+ end
+
+ it "reports the error was not located within cookbooks" do
+ expect(inspector.found_error_in_cookbooks?).to be(false)
+ end
+
end
end
describe "when explaining an error on windows" do
- before do
- allow(Chef::Config).to receive(:cookbook_path).and_return([ "C:/opscode/chef/var/cache/cookbooks" ])
- recipe_lines = BAD_RECIPE.split("\n").map {|l| l << "\n" }
- expect(IO).to receive(:readlines).at_least(1).times.with(/:\/opscode\/chef\/var\/cache\/cookbooks\/foo\/recipes\/default.rb/).and_return(recipe_lines)
- @trace = [
+
+ let(:trace_with_upcase_drive) do
+ [
"C:/opscode/chef/var/cache/cookbooks/foo/recipes/default.rb:14 in `from_file'",
"C:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:144:in `rescue in block in load_libraries'",
"C:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:138:in `block in load_libraries'",
@@ -122,81 +201,65 @@ describe Chef::Formatters::ErrorInspectors::CompileErrorInspector do
"C:/opscode/chef/bin/chef-client:19:in `load'",
"C:/opscode/chef/bin/chef-client:19:in `<main>'"
]
- @exception.set_backtrace(@trace)
- @path = "/var/chef/cache/cookbooks/syntax-err/recipes/default.rb"
- @inspector = described_class.new(@path, @exception)
- @inspector.add_explanation(@description)
end
+ let(:trace) { trace_with_upcase_drive }
+
+ let(:path_to_failed_file) { "/var/cache/cookbooks/foo/recipes/default.rb" }
- describe "and examining the stack trace for a recipe" do
- it "find the culprit recipe name when the drive letter is upper case" do
- expect(@inspector.culprit_file).to eq("C:/opscode/chef/var/cache/cookbooks/foo/recipes/default.rb")
+ before do
+ allow(Chef::Config).to receive(:cookbook_path).and_return([ "C:/opscode/chef/var/cache/cookbooks" ])
+ recipe_lines = BAD_RECIPE.split("\n").map {|l| l << "\n" }
+ expect(IO).to receive(:readlines).at_least(1).times.with(full_path_to_failed_file).and_return(recipe_lines)
+ inspector.add_explanation(description)
+ end
+
+ context "when the drive letter in the path is uppercase" do
+
+ let(:full_path_to_failed_file) { "C:/opscode/chef#{path_to_failed_file}" }
+
+ it "reports the error was not located within cookbooks" do
+ expect(inspector.found_error_in_cookbooks?).to be(true)
end
- it "find the culprit recipe name when the drive letter is lower case" do
- @trace.each { |line| line.gsub!(/^C:/, "c:") }
- @exception.set_backtrace(@trace)
- @inspector = described_class.new(@path, @exception)
- @inspector.add_explanation(@description)
- expect(@inspector.culprit_file).to eq("c:/opscode/chef/var/cache/cookbooks/foo/recipes/default.rb")
+ it "finds the culprit recipe name" do
+ expect(inspector.culprit_file).to eq("C:/opscode/chef/var/cache/cookbooks/foo/recipes/default.rb")
end
- end
- it "finds the line number of the error from the stack trace" do
- expect(@inspector.culprit_line).to eq(14)
- end
+ it "finds the line number of the error from the stack trace" do
+ expect(inspector.culprit_line).to eq(14)
+ end
- it "prints a pretty message" do
- @description.display(@outputter)
+ it "prints a pretty message" do
+ description.display(outputter)
+ end
end
- end
- describe "when explaining an error on windows, and the backtrace lowercases the drive letter" do
- before do
- allow(Chef::Config).to receive(:cookbook_path).and_return([ "C:/opscode/chef/var/cache/cookbooks" ])
- recipe_lines = BAD_RECIPE.split("\n").map {|l| l << "\n" }
- expect(IO).to receive(:readlines).with("c:/opscode/chef/var/cache/cookbooks/foo/recipes/default.rb").and_return(recipe_lines)
- @trace = [
- "c:/opscode/chef/var/cache/cookbooks/foo/recipes/default.rb:14 in `from_file'",
- "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:144:in `rescue in block in load_libraries'",
- "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:138:in `block in load_libraries'",
- "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:230:in `call'",
- "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:230:in `block (2 levels) in foreach_cookbook_load_segment'",
- "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:229:in `each'",
- "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:229:in `block in foreach_cookbook_load_segment'",
- "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:227:in `each'",
- "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:227:in `foreach_cookbook_load_segment'",
- "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:137:in `load_libraries'",
- "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/run_context.rb:62:in `load'",
- "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/client.rb:198:in `setup_run_context'",
- "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/client.rb:418:in `do_run'",
- "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/client.rb:176:in `run'",
- "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/application/client.rb:283:in `block in run_application'",
- "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/application/client.rb:270:in `loop'",
- "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/application/client.rb:270:in `run_application'",
- "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/lib/chef/application.rb:70:in `run'",
- "c:/opscode/chef/embedded/lib/ruby/gems/1.9.1/gems/chef-10.14.0/bin/chef-client:26:in `<top (required)>'",
- "c:/opscode/chef/bin/chef-client:19:in `load'",
- "c:/opscode/chef/bin/chef-client:19:in `<main>'"
- ]
- @exception.set_backtrace(@trace)
- @path = "/var/chef/cache/cookbooks/syntax-err/recipes/default.rb"
- @inspector = described_class.new(@path, @exception)
- @inspector.add_explanation(@description)
- end
+ context "when the drive letter in the path is lowercase" do
- it "finds the culprit recipe name from the stacktrace" do
- expect(@inspector.culprit_file).to eq("c:/opscode/chef/var/cache/cookbooks/foo/recipes/default.rb")
- end
+ let(:trace) do
+ trace_with_upcase_drive.map { |line| line.gsub(/^C:/, "c:") }
+ end
- it "finds the line number of the error from the stack trace" do
- expect(@inspector.culprit_line).to eq(14)
- end
+ let(:full_path_to_failed_file) { "c:/opscode/chef#{path_to_failed_file}" }
+
+ it "reports the error was not located within cookbooks" do
+ expect(inspector.found_error_in_cookbooks?).to be(true)
+ end
+
+ it "finds the culprit recipe name from the stacktrace" do
+ expect(inspector.culprit_file).to eq("c:/opscode/chef/var/cache/cookbooks/foo/recipes/default.rb")
+ end
- it "prints a pretty message" do
- @description.display(@outputter)
+ it "finds the line number of the error from the stack trace" do
+ expect(inspector.culprit_line).to eq(14)
+ end
+
+ it "prints a pretty message" do
+ description.display(outputter)
+ end
end
+
end
end
diff --git a/spec/unit/formatters/error_inspectors/resource_failure_inspector_spec.rb b/spec/unit/formatters/error_inspectors/resource_failure_inspector_spec.rb
index a42d234601..5594d6e18a 100644
--- a/spec/unit/formatters/error_inspectors/resource_failure_inspector_spec.rb
+++ b/spec/unit/formatters/error_inspectors/resource_failure_inspector_spec.rb
@@ -126,6 +126,13 @@ describe Chef::Formatters::ErrorInspectors::ResourceFailureInspector do
expect(@inspector.recipe_snippet).to match(/^# In C:\/Users\/btm/)
end
+ it "parses a Windows path" do
+ source_line = "C:\\Windows\\Temp\\packer\\cookbooks\\fake_file.rb:2: undefined local variable or method `non_existent' for main:Object (NameError)"
+ @resource.source_line = source_line
+ @inspector = Chef::Formatters::ErrorInspectors::ResourceFailureInspector.new(@resource, :create, @exception)
+ expect(@inspector.recipe_snippet).to match(/^# In C:\\Windows\\Temp\\packer\\/)
+ end
+
it "parses a unix path" do
source_line = "/home/btm/src/chef/chef/spec/unit/fake_file.rb:2: undefined local variable or method `non_existent' for main:Object (NameError)"
@resource.source_line = source_line
diff --git a/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb b/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb
index 4cf3ba827a..acf1b15fd8 100644
--- a/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb
+++ b/spec/unit/guard_interpreter/resource_guard_interpreter_spec.rb
@@ -24,6 +24,7 @@ describe Chef::GuardInterpreter::ResourceGuardInterpreter do
node.default["kernel"] = Hash.new
node.default["kernel"][:machine] = :x86_64.to_s
+ node.automatic[:os] = 'windows'
node
end
@@ -83,6 +84,14 @@ describe Chef::GuardInterpreter::ResourceGuardInterpreter do
expect(guard_interpreter.evaluate).to eq(true)
end
+ it "does not corrupt the run_context of the node" do
+ node_run_context_before_guard_execution = parent_resource.run_context
+ expect(node_run_context_before_guard_execution.object_id).to eq(parent_resource.node.run_context.object_id)
+ guard_interpreter.evaluate
+ node_run_context_after_guard_execution = parent_resource.run_context
+ expect(node_run_context_after_guard_execution.object_id).to eq(parent_resource.node.run_context.object_id)
+ end
+
describe "script command opts switch" do
let(:command_opts) { {} }
let(:guard_interpreter) { Chef::GuardInterpreter::ResourceGuardInterpreter.new(parent_resource, "exit 0", command_opts) }
@@ -144,4 +153,3 @@ describe Chef::GuardInterpreter::ResourceGuardInterpreter do
end
end
end
-
diff --git a/spec/unit/http/authenticator_spec.rb b/spec/unit/http/authenticator_spec.rb
new file mode 100644
index 0000000000..48bbdcf76c
--- /dev/null
+++ b/spec/unit/http/authenticator_spec.rb
@@ -0,0 +1,78 @@
+#
+# Author:: Tyler Cloke (<tyler@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'chef/http/authenticator'
+
+describe Chef::HTTP::Authenticator do
+ let(:class_instance) { Chef::HTTP::Authenticator.new }
+ let(:method) { double("method") }
+ let(:url) { double("url") }
+ let(:headers) { Hash.new }
+ let(:data) { double("data") }
+
+ before do
+ allow(class_instance).to receive(:authentication_headers).and_return({})
+ end
+
+ context "when handle_request is called" do
+ shared_examples_for "merging the server API version into the headers" do
+ it "merges the default version of X-Ops-Server-API-Version into the headers" do
+ # headers returned
+ expect(class_instance.handle_request(method, url, headers, data)[2]).
+ to include({'X-Ops-Server-API-Version' => Chef::HTTP::Authenticator::DEFAULT_SERVER_API_VERSION})
+ end
+
+ context "when api_version is set to something other than the default" do
+ let(:class_instance) { Chef::HTTP::Authenticator.new({:api_version => '-10'}) }
+
+ it "merges the requested version of X-Ops-Server-API-Version into the headers" do
+ expect(class_instance.handle_request(method, url, headers, data)[2]).
+ to include({'X-Ops-Server-API-Version' => '-10'})
+ end
+ end
+ end
+
+ context "when !sign_requests?" do
+ before do
+ allow(class_instance).to receive(:sign_requests?).and_return(false)
+ end
+
+ it_behaves_like "merging the server API version into the headers"
+
+ it "authentication_headers is not called" do
+ expect(class_instance).to_not receive(:authentication_headers)
+ class_instance.handle_request(method, url, headers, data)
+ end
+
+ end
+
+ context "when sign_requests?" do
+ before do
+ allow(class_instance).to receive(:sign_requests?).and_return(true)
+ end
+
+ it_behaves_like "merging the server API version into the headers"
+
+ it "calls authentication_headers with the proper input" do
+ expect(class_instance).to receive(:authentication_headers).with(method, url, data).and_return({})
+ class_instance.handle_request(method, url, headers, data)
+ end
+ end
+ end
+end
diff --git a/spec/unit/http/basic_client_spec.rb b/spec/unit/http/basic_client_spec.rb
index eb133f943e..b7552f54aa 100644
--- a/spec/unit/http/basic_client_spec.rb
+++ b/spec/unit/http/basic_client_spec.rb
@@ -21,7 +21,7 @@ require 'chef/http/basic_client'
describe "HTTP Connection" do
let(:uri) { URI("https://example.com:4443") }
- subject { Chef::HTTP::BasicClient.new(uri) }
+ subject(:basic_client) { Chef::HTTP::BasicClient.new(uri) }
describe ".new" do
it "creates an instance" do
@@ -45,11 +45,6 @@ describe "HTTP Connection" do
let(:proxy_port) { 8080 }
let(:proxy) { "#{proxy_prefix}#{proxy_host}:#{proxy_port}" }
- before do
- Chef::Config["#{uri.scheme}_proxy"] = proxy
- Chef::Config[:no_proxy] = nil
- end
-
it "should contain the host" do
proxy_uri = subject.proxy_uri
expect(proxy_uri.host).to eq(proxy_host)
@@ -63,13 +58,71 @@ describe "HTTP Connection" do
context "when the config setting is normalized (does not contain the scheme)" do
include_examples "a proxy uri" do
+
let(:proxy_prefix) { "" }
+
+ before do
+ Chef::Config["#{uri.scheme}_proxy"] = proxy
+ Chef::Config[:no_proxy] = nil
+ end
+
end
end
context "when the config setting is not normalized (contains the scheme)" do
include_examples "a proxy uri" do
let(:proxy_prefix) { "#{uri.scheme}://" }
+
+ before do
+ Chef::Config["#{uri.scheme}_proxy"] = proxy
+ Chef::Config[:no_proxy] = nil
+ end
+
+ end
+ end
+
+ context "when the proxy is set by the environment" do
+
+ include_examples "a proxy uri" do
+
+ let(:env) do
+ {
+ "https_proxy" => "https://proxy.mycorp.com:8080",
+ "https_proxy_user" => "jane_username",
+ "https_proxy_pass" => "opensesame"
+ }
+ end
+
+ let(:proxy_uri) { URI.parse(env["https_proxy"]) }
+
+ before do
+ allow(basic_client).to receive(:env).and_return(env)
+ end
+
+ it "sets the proxy user" do
+ expect(basic_client.http_proxy_user(proxy_uri)).to eq("jane_username")
+ end
+
+ it "sets the proxy pass" do
+ expect(basic_client.http_proxy_pass(proxy_uri)).to eq("opensesame")
+ end
+ end
+
+ end
+
+ context "when an empty proxy is set by the environment" do
+ let(:env) do
+ {
+ "https_proxy" => ""
+ }
+ end
+
+ before do
+ allow(subject).to receive(:env).and_return(env)
+ end
+
+ it "to not fail with URI parse exception" do
+ expect { subject.proxy_uri }.to_not raise_error
end
end
end
diff --git a/spec/unit/http/socketless_chef_zero_client_spec.rb b/spec/unit/http/socketless_chef_zero_client_spec.rb
new file mode 100644
index 0000000000..963cc9e8c4
--- /dev/null
+++ b/spec/unit/http/socketless_chef_zero_client_spec.rb
@@ -0,0 +1,174 @@
+#--
+# Author:: Daniel DeLeo (<dan@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef/http/socketless_chef_zero_client'
+
+describe Chef::HTTP::SocketlessChefZeroClient do
+
+ let(:relative_url) { "" }
+ let(:uri_str) { "chefzero://localhost:1/#{relative_url}" }
+ let(:uri) { URI(uri_str) }
+
+ subject(:zero_client) { Chef::HTTP::SocketlessChefZeroClient.new(uri) }
+
+ it "has a host" do
+ expect(zero_client.host).to eq("localhost")
+ end
+
+ it "has a port" do
+ expect(zero_client.port).to eq(1)
+ end
+
+ describe "converting requests to rack format" do
+
+ let(:expected_rack_req) do
+ {
+ "SCRIPT_NAME" => "",
+ "SERVER_NAME" => "localhost",
+ "REQUEST_METHOD" => method.to_s.upcase,
+ "PATH_INFO" => uri.path,
+ "QUERY_STRING" => uri.query,
+ "SERVER_PORT" => uri.port,
+ "HTTP_HOST" => "localhost:#{uri.port}",
+ "rack.url_scheme" => "chefzero",
+ }
+ end
+
+ context "when the request has no body" do
+
+ let(:method) { :GET }
+ let(:relative_url) { "clients" }
+ let(:headers) { { "Accept" => "application/json" } }
+ let(:body) { false }
+ let(:expected_body_str) { "" }
+
+ let(:rack_req) { zero_client.req_to_rack(method, uri, body, headers) }
+
+ it "creates a rack request env" do
+ # StringIO doesn't implement == in a way that we can compare, so we
+ # check rack.input individually and then iterate over everything else
+ expect(rack_req["rack.input"].string).to eq(expected_body_str)
+ expected_rack_req.each do |key, value|
+ expect(rack_req[key]).to eq(value)
+ end
+ end
+
+ end
+
+ context "when the request has a body" do
+
+ let(:method) { :PUT }
+ let(:relative_url) { "clients/foo" }
+ let(:headers) { { "Accept" => "application/json" } }
+ let(:body) { "bunch o' JSON" }
+ let(:expected_body_str) { "bunch o' JSON" }
+
+ let(:rack_req) { zero_client.req_to_rack(method, uri, body, headers) }
+
+ it "creates a rack request env" do
+ # StringIO doesn't implement == in a way that we can compare, so we
+ # check rack.input individually and then iterate over everything else
+ expect(rack_req["rack.input"].string).to eq(expected_body_str)
+ expected_rack_req.each do |key, value|
+ expect(rack_req[key]).to eq(value)
+ end
+ end
+
+ end
+
+ end
+
+ describe "converting responses to Net::HTTP objects" do
+
+ let(:net_http_response) { zero_client.to_net_http(code, headers, body) }
+
+ context "when the request was successful (2XX)" do
+
+ let(:code) { 200 }
+ let(:headers) { { "Content-Type" => "Application/JSON" } }
+ let(:body) { [ "bunch o' JSON" ] }
+
+ it "creates a Net::HTTP success response object" do
+ expect(net_http_response).to be_a_kind_of(Net::HTTPOK)
+ expect(net_http_response.read_body).to eq("bunch o' JSON")
+ expect(net_http_response["content-type"]).to eq("Application/JSON")
+ end
+
+ it "does not fail when calling read_body with a block" do
+ expect(net_http_response.read_body {|chunk| chunk }).to eq("bunch o' JSON")
+ end
+
+ end
+
+ context "when the requested object doesn't exist (404)" do
+
+ let(:code) { 404 }
+ let(:headers) { { "Content-Type" => "Application/JSON" } }
+ let(:body) { [ "nope" ] }
+
+ it "creates a Net::HTTPNotFound response object" do
+ expect(net_http_response).to be_a_kind_of(Net::HTTPNotFound)
+ end
+ end
+
+ end
+
+ describe "request-response round trip" do
+
+ let(:method) { :GET }
+ let(:relative_url) { "clients" }
+ let(:headers) { { "Accept" => "application/json" } }
+ let(:body) { false }
+
+ let(:expected_rack_req) do
+ {
+ "SCRIPT_NAME" => "",
+ "SERVER_NAME" => "localhost",
+ "REQUEST_METHOD" => method.to_s.upcase,
+ "PATH_INFO" => uri.path,
+ "QUERY_STRING" => uri.query,
+ "SERVER_PORT" => uri.port,
+ "HTTP_HOST" => "localhost:#{uri.port}",
+ "rack.url_scheme" => "chefzero",
+ "rack.input" => an_instance_of(StringIO),
+ }
+ end
+
+
+ let(:response_code) { 200 }
+ let(:response_headers) { { "Content-Type" => "Application/JSON" } }
+ let(:response_body) { [ "bunch o' JSON" ] }
+
+ let(:rack_response) { [ response_code, response_headers, response_body ] }
+
+ let(:response) { zero_client.request(method, uri, body, headers) }
+
+ before do
+ expect(ChefZero::SocketlessServerMap).to receive(:request).with(1, expected_rack_req).and_return(rack_response)
+ end
+
+ it "makes a rack request to Chef Zero and returns the response as a Net::HTTP object" do
+ _client, net_http_response = response
+ expect(net_http_response).to be_a_kind_of(Net::HTTPOK)
+ expect(net_http_response.code).to eq("200")
+ expect(net_http_response.body).to eq("bunch o' JSON")
+ end
+
+ end
+
+end
diff --git a/spec/unit/http_spec.rb b/spec/unit/http_spec.rb
index ddfc56583d..4d851df951 100644
--- a/spec/unit/http_spec.rb
+++ b/spec/unit/http_spec.rb
@@ -20,6 +20,7 @@ require 'spec_helper'
require 'chef/http'
require 'chef/http/basic_client'
+require 'chef/http/socketless_chef_zero_client'
class Chef::HTTP
public :create_url
@@ -27,6 +28,19 @@ end
describe Chef::HTTP do
+ context "when given a chefzero:// URL" do
+
+ let(:uri) { URI("chefzero://localhost:1") }
+
+ subject(:http) { Chef::HTTP.new(uri) }
+
+ it "uses the SocketlessChefZeroClient to handle requests" do
+ expect(http.http_client).to be_a_kind_of(Chef::HTTP::SocketlessChefZeroClient)
+ expect(http.http_client.url).to eq(uri)
+ end
+
+ end
+
describe "create_url" do
it 'should return a correctly formatted url 1/3 CHEF-5261' do
diff --git a/spec/unit/json_compat_spec.rb b/spec/unit/json_compat_spec.rb
index 65d931df70..fd6469c146 100644
--- a/spec/unit/json_compat_spec.rb
+++ b/spec/unit/json_compat_spec.rb
@@ -67,37 +67,25 @@ describe Chef::JSONCompat do
expect(Chef::JSONCompat.to_json_pretty(f)).to eql("{\n \"foo\": 1234,\n \"bar\": {\n \"baz\": 5678\n }\n}\n")
end
- include_examples "to_json equalivent to Chef::JSONCompat.to_json" do
+ include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
let(:jsonable) { Foo.new }
end
end
- describe "with a file with 300 or less nested entries" do
- let(:json) { IO.read(File.join(CHEF_SPEC_DATA, 'big_json.json')) }
+ describe "with the file with 252 or less nested entries" do
+ let(:json) { IO.read(File.join(CHEF_SPEC_DATA, 'nested.json')) }
let(:hash) { Chef::JSONCompat.from_json(json) }
- describe "when a big json file is loaded" do
+ describe "when the 252 json file is loaded" do
it "should create a Hash from the file" do
expect(hash).to be_kind_of(Hash)
end
- it "should has 'test' as a 300th nested value" do
- expect(hash['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']).to eq('test')
- end
- end
- end
-
- describe "with a file with more than 300 nested entries" do
- let(:json) { IO.read(File.join(CHEF_SPEC_DATA, 'big_json_plus_one.json')) }
- let(:hash) { Chef::JSONCompat.from_json(json, {:max_nesting => 301}) }
-
- describe "when a big json file is loaded" do
- it "should create a Hash from the file" do
- expect(hash).to be_kind_of(Hash)
- end
-
- it "should has 'test' as a 301st nested value" do
- expect(hash['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']).to eq('test')
+ it "should has 'test' as a 252 nested value" do
+ v = 252.times.inject(hash) do |memo, _|
+ memo['key']
+ end
+ expect(v).to eq('test')
end
end
end
diff --git a/spec/unit/key_spec.rb b/spec/unit/key_spec.rb
new file mode 100644
index 0000000000..94ebbf6ae8
--- /dev/null
+++ b/spec/unit/key_spec.rb
@@ -0,0 +1,634 @@
+#
+# Author:: Tyler Cloke (tyler@chef.io)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+require 'chef/key'
+
+describe Chef::Key do
+ # whether user or client irrelevent to these tests
+ let(:key) { Chef::Key.new("original_actor", "user") }
+ let(:public_key_string) do
+ <<EOS
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvPo+oNPB7uuNkws0fC02
+KxSwdyqPLu0fhI1pOweNKAZeEIiEz2PkybathHWy8snSXGNxsITkf3eyvIIKa8OZ
+WrlqpI3yv/5DOP8HTMCxnFuMJQtDwMcevlqebX4bCxcByuBpNYDcAHjjfLGSfMjn
+E5lZpgYWwnpic4kSjYcL9ORK9nYvlWV9P/kCYmRhIjB4AhtpWRiOfY/TKi3P2LxT
+IjSmiN/ihHtlhV/VSnBJ5PzT/lRknlrJ4kACoz7Pq9jv+aAx5ft/xE9yDa2DYs0q
+Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
+0wIDAQAB
+-----END PUBLIC KEY-----
+EOS
+ end
+
+ shared_examples_for "fields with username type validation" do
+ context "when invalid input is passed" do
+ # It is not feasible to check all invalid characters. Here are a few
+ # that we probably care about.
+ it "should raise an ArgumentError" do
+ # capital letters
+ expect { key.send(field, "Bar") }.to raise_error(ArgumentError)
+ # slashes
+ expect { key.send(field, "foo/bar") }.to raise_error(ArgumentError)
+ # ?
+ expect { key.send(field, "foo?") }.to raise_error(ArgumentError)
+ # &
+ expect { key.send(field, "foo&") }.to raise_error(ArgumentError)
+ # spaces
+ expect { key.send(field, "foo ") }.to raise_error(ArgumentError)
+ end
+ end
+ end
+
+ shared_examples_for "string fields that are settable" do
+ context "when it is set with valid input" do
+ it "should set the field" do
+ key.send(field, valid_input)
+ expect(key.send(field)).to eq(valid_input)
+ end
+ end
+
+ context "when you feed it anything but a string" do
+ it "should raise an ArgumentError" do
+ expect { key.send(field, Hash.new) }.to raise_error(ArgumentError)
+ end
+ end
+ end
+
+
+ describe "when a new Chef::Key object is initialized with invalid input" do
+ it "should raise an InvalidKeyArgument" do
+ expect { Chef::Key.new("original_actor", "not_a_user_or_client") }.to raise_error(Chef::Exceptions::InvalidKeyArgument)
+ end
+ end
+
+ describe "when a new Chef::Key object is initialized with valid input" do
+ it "should be a Chef::Key" do
+ expect(key).to be_a_kind_of(Chef::Key)
+ end
+
+ it "should properly set the actor" do
+ expect(key.actor).to eq("original_actor")
+ end
+ end
+
+ describe "when actor field is set" do
+ it_should_behave_like "string fields that are settable" do
+ let(:field) { :actor }
+ let(:valid_input) { "new_field_value" }
+ end
+
+ it_should_behave_like "fields with username type validation" do
+ let(:field) { :actor }
+ end
+ end
+
+ describe "when the name field is set" do
+ it_should_behave_like "string fields that are settable" do
+ let(:field) { :name }
+ let(:valid_input) { "new_field_value" }
+ end
+ end
+
+ describe "when the private_key field is set" do
+ it_should_behave_like "string fields that are settable" do
+ let(:field) { :private_key }
+ let(:valid_input) { "new_field_value" }
+ end
+ end
+
+ describe "when the public_key field is set" do
+ it_should_behave_like "string fields that are settable" do
+ let(:field) { :public_key }
+ let(:valid_input) { "new_field_value" }
+ end
+
+ context "when create_key is true" do
+ before do
+ key.create_key true
+ end
+
+ it "should raise an InvalidKeyAttribute" do
+ expect { key.public_key public_key_string }.to raise_error(Chef::Exceptions::InvalidKeyAttribute)
+ end
+ end
+ end
+
+ describe "when the create_key field is set" do
+ context "when it is set to true" do
+ it "should set the field" do
+ key.create_key(true)
+ expect(key.create_key).to eq(true)
+ end
+ end
+
+ context "when it is set to false" do
+ it "should set the field" do
+ key.create_key(false)
+ expect(key.create_key).to eq(false)
+ end
+ end
+
+ context "when anything but a TrueClass or FalseClass is passed" do
+ it "should raise an ArgumentError" do
+ expect { key.create_key "not_a_boolean" }.to raise_error(ArgumentError)
+ end
+ end
+
+ context "when public_key is defined" do
+ before do
+ key.public_key public_key_string
+ end
+
+ it "should raise an InvalidKeyAttribute" do
+ expect { key.create_key true }.to raise_error(Chef::Exceptions::InvalidKeyAttribute)
+ end
+ end
+ end
+
+ describe "when the expiration_date field is set" do
+ context "when a valid date is passed" do
+ it_should_behave_like "string fields that are settable" do
+ let(:field) { :public_key }
+ let(:valid_input) { "2020-12-24T21:00:00Z" }
+ end
+ end
+
+ context "when infinity is passed" do
+ it_should_behave_like "string fields that are settable" do
+ let(:field) { :public_key }
+ let(:valid_input) { "infinity" }
+ end
+ end
+
+ context "when an invalid date is passed" do
+ it "should raise an ArgumentError" do
+ expect { key.expiration_date "invalid_date" }.to raise_error(ArgumentError)
+ # wrong years
+ expect { key.expiration_date "20-12-24T21:00:00Z" }.to raise_error(ArgumentError)
+ end
+
+ context "when it is a valid UTC date missing a Z" do
+ it "should raise an ArgumentError" do
+ expect { key.expiration_date "2020-12-24T21:00:00" }.to raise_error(ArgumentError)
+ end
+ end
+ end
+ end # when the expiration_date field is set
+
+ describe "when serializing to JSON" do
+ shared_examples_for "common json operations" do
+ it "should serializes as a JSON object" do
+ expect(json).to match(/^\{.+\}$/)
+ end
+
+ it "should include the actor value under the key relative to the actor_field_name passed" do
+ expect(json).to include(%Q("#{new_key.actor_field_name}":"original_actor"))
+ end
+
+ it "should include the name field when present" do
+ new_key.name("monkeypants")
+ expect(new_key.to_json).to include(%q{"name":"monkeypants"})
+ end
+
+ it "should not include the name if not present" do
+ expect(json).to_not include("name")
+ end
+
+ it "should include the public_key field when present" do
+ new_key.public_key "this_public_key"
+ expect(new_key.to_json).to include(%q("public_key":"this_public_key"))
+ end
+
+ it "should not include the public_key if not present" do
+ expect(json).to_not include("public_key")
+ end
+
+ it "should include the private_key field when present" do
+ new_key.private_key "this_public_key"
+ expect(new_key.to_json).to include(%q("private_key":"this_public_key"))
+ end
+
+ it "should not include the private_key if not present" do
+ expect(json).to_not include("private_key")
+ end
+
+ it "should include the expiration_date field when present" do
+ new_key.expiration_date "2020-12-24T21:00:00Z"
+ expect(new_key.to_json).to include(%Q("expiration_date":"2020-12-24T21:00:00Z"))
+ end
+
+ it "should not include the expiration_date if not present" do
+ expect(json).to_not include("expiration_date")
+ end
+
+ it "should include the create_key field when present" do
+ new_key.create_key true
+ expect(new_key.to_json).to include(%q("create_key":true))
+ end
+
+ it "should not include the create_key if not present" do
+ expect(json).to_not include("create_key")
+ end
+ end
+
+ context "when key is for a user" do
+ it_should_behave_like "common json operations" do
+ let(:new_key) { Chef::Key.new("original_actor", "user") }
+ let(:json) do
+ new_key.to_json
+ end
+ end
+ end
+
+ context "when key is for a client" do
+ it_should_behave_like "common json operations" do
+ let(:new_key) { Chef::Key.new("original_actor", "client") }
+ let(:json) do
+ new_key.to_json
+ end
+ end
+ end
+
+ end # when serializing to JSON
+
+ describe "when deserializing from JSON" do
+ shared_examples_for "a deserializable object" do
+ it "deserializes to a Chef::Key object" do
+ expect(key).to be_a_kind_of(Chef::Key)
+ end
+
+ it "preserves the actor" do
+ expect(key.actor).to eq("turtle")
+ end
+
+ it "preserves the name" do
+ expect(key.name).to eq("key_name")
+ end
+
+ it "includes the public key if present" do
+ expect(key.public_key).to eq(public_key_string)
+ end
+
+ it "includes the expiration_date if present" do
+ expect(key.expiration_date).to eq("infinity")
+ end
+
+ it "includes the private_key if present" do
+ expect(key.private_key).to eq("some_private_key")
+ end
+
+ it "includes the create_key if present" do
+ expect(key_with_create_key_field.create_key).to eq(true)
+ end
+ end
+
+ context "when deserializing a key for a user" do
+ it_should_behave_like "a deserializable object" do
+ let(:key) do
+ o = { "user" => "turtle",
+ "name" => "key_name",
+ "public_key" => public_key_string,
+ "private_key" => "some_private_key",
+ "expiration_date" => "infinity"}
+ Chef::Key.from_json(o.to_json)
+ end
+ let(:key_with_create_key_field) do
+ o = { "user" => "turtle",
+ "create_key" => true }
+ Chef::Key.from_json(o.to_json)
+ end
+ end
+ end
+
+ context "when deserializing a key for a client" do
+ it_should_behave_like "a deserializable object" do
+ let(:key) do
+ o = { "client" => "turtle",
+ "name" => "key_name",
+ "public_key" => public_key_string,
+ "private_key" => "some_private_key",
+ "expiration_date" => "infinity"}
+ Chef::Key.from_json(o.to_json)
+ end
+ let(:key_with_create_key_field) do
+ o = { "client" => "turtle",
+ "create_key" => true }
+ Chef::Key.from_json(o.to_json)
+ end
+ end
+ end
+ end # when deserializing from JSON
+
+
+ describe "API Interactions" do
+ let(:rest) do
+ Chef::Config[:chef_server_root] = "http://www.example.com"
+ Chef::Config[:chef_server_url] = "http://www.example.com/organizations/test_org"
+ r = double('rest')
+ allow(Chef::REST).to receive(:new).and_return(r)
+ r
+ end
+
+ let(:user_key) do
+ o = Chef::Key.new("foobar", "user")
+ o
+ end
+
+ let(:client_key) do
+ o = Chef::Key.new("foobar", "client")
+ o
+ end
+
+ describe "list" do
+ context "when listing keys for a user" do
+ let(:response) { [{"uri" => "http://www.example.com/users/keys/foobar", "name"=>"foobar", "expired"=>false}] }
+ let(:inflated_response) { {"foobar" => user_key} }
+
+ it "lists all keys" do
+ expect(rest).to receive(:get_rest).with("users/#{user_key.actor}/keys").and_return(response)
+ expect(Chef::Key.list_by_user("foobar")).to eq(response)
+ end
+
+ it "inflate all keys" do
+ allow(Chef::Key).to receive(:load_by_user).with(user_key.actor, "foobar").and_return(user_key)
+ expect(rest).to receive(:get_rest).with("users/#{user_key.actor}/keys").and_return(response)
+ expect(Chef::Key.list_by_user("foobar", true)).to eq(inflated_response)
+ end
+
+ end
+
+ context "when listing keys for a client" do
+ let(:response) { [{"uri" => "http://www.example.com/users/keys/foobar", "name"=>"foobar", "expired"=>false}] }
+ let(:inflated_response) { {"foobar" => client_key} }
+
+ it "lists all keys" do
+ expect(rest).to receive(:get_rest).with("clients/#{client_key.actor}/keys").and_return(response)
+ expect(Chef::Key.list_by_client("foobar")).to eq(response)
+ end
+
+ it "inflate all keys" do
+ allow(Chef::Key).to receive(:load_by_client).with(client_key.actor, "foobar").and_return(client_key)
+ expect(rest).to receive(:get_rest).with("clients/#{user_key.actor}/keys").and_return(response)
+ expect(Chef::Key.list_by_client("foobar", true)).to eq(inflated_response)
+ end
+
+ end
+ end
+
+
+ describe "create" do
+ shared_examples_for "create key" do
+ context "when a field is missing" do
+ it "should raise a MissingKeyAttribute" do
+ expect { key.create }.to raise_error(Chef::Exceptions::MissingKeyAttribute)
+ end
+ end
+
+ context "when the name field is missing" do
+ before do
+ key.public_key public_key_string
+ key.expiration_date "2020-12-24T21:00:00Z"
+ end
+
+ it "creates a new key via the API with the fingerprint as the name" do
+ expect(rest).to receive(:post_rest).with(url,
+ {"name" => "12:3e:33:73:0b:f4:ec:72:dc:f0:4c:51:62:27:08:76:96:24:f4:4a",
+ "public_key" => key.public_key,
+ "expiration_date" => key.expiration_date}).and_return({})
+ key.create
+ end
+ end
+
+ context "when every field is populated" do
+ before do
+ key.name "key_name"
+ key.public_key public_key_string
+ key.expiration_date "2020-12-24T21:00:00Z"
+ key.create_key false
+ end
+
+ context "when create_key is false" do
+ it "creates a new key via the API" do
+ expect(rest).to receive(:post_rest).with(url,
+ {"name" => key.name,
+ "public_key" => key.public_key,
+ "expiration_date" => key.expiration_date}).and_return({})
+ key.create
+ end
+ end
+
+ context "when create_key is true and public_key is nil" do
+
+ before do
+ key.delete_public_key
+ key.create_key true
+ $expected_output = {
+ actor_type => "foobar",
+ "name" => key.name,
+ "create_key" => true,
+ "expiration_date" => key.expiration_date
+ }
+ $expected_input = {
+ "name" => key.name,
+ "create_key" => true,
+ "expiration_date" => key.expiration_date
+ }
+ end
+
+ it "should create a new key via the API" do
+ expect(rest).to receive(:post_rest).with(url, $expected_input).and_return({})
+ key.create
+ end
+
+ context "when the server returns the private_key via key.create" do
+ before do
+ allow(rest).to receive(:post_rest).with(url, $expected_input).and_return({"private_key" => "this_private_key"})
+ end
+
+ it "key.create returns the original key plus the private_key" do
+ expect(key.create.to_hash).to eq($expected_output.merge({"private_key" => "this_private_key"}))
+ end
+ end
+ end
+
+ context "when create_key is false and public_key is nil" do
+ before do
+ key.delete_public_key
+ key.create_key false
+ end
+ it "should raise an InvalidKeyArgument" do
+ expect { key.create }.to raise_error(Chef::Exceptions::MissingKeyAttribute)
+ end
+ end
+ end
+ end
+
+ context "when creating a user key" do
+ it_should_behave_like "create key" do
+ let(:url) { "users/#{key.actor}/keys" }
+ let(:key) { user_key }
+ let(:actor_type) { "user" }
+ end
+ end
+
+ context "when creating a client key" do
+ it_should_behave_like "create key" do
+ let(:url) { "clients/#{client_key.actor}/keys" }
+ let(:key) { client_key }
+ let(:actor_type) { "client" }
+ end
+ end
+ end # create
+
+ describe "update" do
+ shared_examples_for "update key" do
+ context "when name is missing and no argument was passed to update" do
+ it "should raise an MissingKeyAttribute" do
+ expect { key.update }.to raise_error(Chef::Exceptions::MissingKeyAttribute)
+ end
+ end
+
+ context "when some fields are populated" do
+ before do
+ key.name "key_name"
+ key.expiration_date "2020-12-24T21:00:00Z"
+ end
+
+ it "should update the key via the API" do
+ expect(rest).to receive(:put_rest).with(url, key.to_hash).and_return({})
+ key.update
+ end
+ end
+
+ context "when @name is not nil and a arg is passed to update" do
+ before do
+ key.name "new_name"
+ end
+
+ it "passes @name in the body and the arg in the PUT URL" do
+ expect(rest).to receive(:put_rest).with(update_name_url, key.to_hash).and_return({})
+ key.update("old_name")
+ end
+ end
+
+ context "when the server returns a public_key and create_key is true" do
+ before do
+ key.name "key_name"
+ key.create_key true
+ allow(rest).to receive(:put_rest).with(url, key.to_hash).and_return({
+ "key" => "key_name",
+ "public_key" => public_key_string
+ })
+
+ end
+
+ it "returns a key with public_key populated" do
+ new_key = key.update
+ expect(new_key.public_key).to eq(public_key_string)
+ end
+
+ it "returns a key without create_key set" do
+ new_key = key.update
+ expect(new_key.create_key).to be_nil
+ end
+ end
+ end
+
+ context "when updating a user key" do
+ it_should_behave_like "update key" do
+ let(:url) { "users/#{key.actor}/keys/#{key.name}" }
+ let(:update_name_url) { "users/#{key.actor}/keys/old_name" }
+ let(:key) { user_key }
+ end
+ end
+
+ context "when updating a client key" do
+ it_should_behave_like "update key" do
+ let(:url) { "clients/#{client_key.actor}/keys/#{key.name}" }
+ let(:update_name_url) { "clients/#{client_key.actor}/keys/old_name" }
+ let(:key) { client_key }
+ end
+ end
+
+ end #update
+
+ describe "load" do
+ shared_examples_for "load" do
+ it "should load a named key from the API" do
+ expect(rest).to receive(:get_rest).with(url).and_return({"user" => "foobar", "name" => "test_key_name", "public_key" => public_key_string, "expiration_date" => "infinity"})
+ key = Chef::Key.send(load_method, "foobar", "test_key_name")
+ expect(key.actor).to eq("foobar")
+ expect(key.name).to eq("test_key_name")
+ expect(key.public_key).to eq(public_key_string)
+ expect(key.expiration_date).to eq("infinity")
+ end
+ end
+
+ describe "load_by_user" do
+ it_should_behave_like "load" do
+ let(:load_method) { :load_by_user }
+ let(:url) { "users/foobar/keys/test_key_name" }
+ end
+ end
+
+ describe "load_by_client" do
+ it_should_behave_like "load" do
+ let(:load_method) { :load_by_client }
+ let(:url) { "clients/foobar/keys/test_key_name" }
+ end
+ end
+
+ end #load
+
+ describe "destroy" do
+ shared_examples_for "destroy key" do
+ context "when name is missing" do
+ it "should raise an MissingKeyAttribute" do
+ expect { Chef::Key.new("username", "user").destroy }.to raise_error(Chef::Exceptions::MissingKeyAttribute)
+ end
+ end
+
+ before do
+ key.name "key_name"
+ end
+ context "when name is not missing" do
+ it "should delete the key via the API" do
+ expect(rest).to receive(:delete_rest).with(url).and_return({})
+ key.destroy
+ end
+ end
+ end
+
+ context "when destroying a user key" do
+ it_should_behave_like "destroy key" do
+ let(:url) { "users/#{key.actor}/keys/#{key.name}" }
+ let(:key) { user_key }
+ end
+ end
+
+ context "when destroying a client key" do
+ it_should_behave_like "destroy key" do
+ let(:url) { "clients/#{client_key.actor}/keys/#{key.name}" }
+ let(:key) { client_key }
+ end
+ end
+ end
+ end # API Interactions
+end
diff --git a/spec/unit/knife/bootstrap_spec.rb b/spec/unit/knife/bootstrap_spec.rb
index 848af11db5..0195e6d406 100644
--- a/spec/unit/knife/bootstrap_spec.rb
+++ b/spec/unit/knife/bootstrap_spec.rb
@@ -23,7 +23,7 @@ require 'net/ssh'
describe Chef::Knife::Bootstrap do
before do
- allow(Chef::Platform).to receive(:windows?) { false }
+ allow(ChefConfig).to receive(:windows?) { false }
end
let(:knife) do
Chef::Log.logger = Logger.new(StringIO.new)
@@ -115,7 +115,7 @@ describe Chef::Knife::Bootstrap do
end
def configure_env_home
- ENV['HOME'] = "/env/home"
+ allow(Chef::Util::PathHelper).to receive(:home).with(".chef", "bootstrap", "example.erb").and_yield(env_home_template_path)
end
def configure_gem_files
@@ -123,15 +123,9 @@ describe Chef::Knife::Bootstrap do
end
before(:each) do
- @original_home = ENV['HOME']
- ENV['HOME'] = nil
expect(File).to receive(:exists?).with(bootstrap_template).and_return(false)
end
- after(:each) do
- ENV['HOME'] = @original_home
- end
-
context "when file is available everywhere" do
before do
configure_chef_config_dir
@@ -161,7 +155,7 @@ describe Chef::Knife::Bootstrap do
end
end
- context "when file is available in ENV['HOME']" do
+ context "when file is available in home directory" do
before do
configure_chef_config_dir
configure_env_home
@@ -180,10 +174,28 @@ describe Chef::Knife::Bootstrap do
context "when file is available in Gem files" do
before do
configure_chef_config_dir
+ configure_env_home
configure_gem_files
expect(File).to receive(:exists?).with(builtin_template_path).and_return(false)
expect(File).to receive(:exists?).with(chef_config_dir_template_path).and_return(false)
+ expect(File).to receive(:exists?).with(env_home_template_path).and_return(false)
+ expect(File).to receive(:exists?).with(gem_files_template_path).and_return(true)
+ end
+
+ it "should load the template from Gem files" do
+ expect(knife.find_template).to eq(gem_files_template_path)
+ end
+ end
+
+ context "when file is available in Gem files and home dir doesn't exist" do
+ before do
+ configure_chef_config_dir
+ configure_gem_files
+ allow(Chef::Util::PathHelper).to receive(:home).with(".chef", "bootstrap", "example.erb").and_return(nil)
+
+ expect(File).to receive(:exists?).with(builtin_template_path).and_return(false)
+ expect(File).to receive(:exists?).with(chef_config_dir_template_path).and_return(false)
expect(File).to receive(:exists?).with(gem_files_template_path).and_return(true)
end
@@ -519,6 +531,7 @@ describe Chef::Knife::Bootstrap do
describe "when running the bootstrap" do
let(:knife_ssh) do
knife.name_args = ["foo.example.com"]
+ knife.config[:chef_node_name] = "foo.example.com"
knife.config[:ssh_user] = "rooty"
knife.config[:identity_file] = "~/.ssh/me.rsa"
allow(knife).to receive(:render_template).and_return("")
@@ -578,6 +591,12 @@ describe Chef::Knife::Bootstrap do
expect(knife.chef_vault_handler).not_to receive(:run).with(node_name: knife.config[:chef_node_name])
knife.run
end
+
+ it "raises an exception if the config[:chef_node_name] is not present" do
+ knife.config[:chef_node_name] = nil
+
+ expect { knife.run }.to raise_error(SystemExit)
+ end
end
context "when the validation key is not present" do
@@ -592,6 +611,12 @@ describe Chef::Knife::Bootstrap do
expect(knife.chef_vault_handler).to receive(:run).with(node_name: knife.config[:chef_node_name])
knife.run
end
+
+ it "raises an exception if the config[:chef_node_name] is not present" do
+ knife.config[:chef_node_name] = nil
+
+ expect { knife.run }.to raise_error(SystemExit)
+ end
end
context "when the validation_key is nil" do
diff --git a/spec/unit/knife/client_bulk_delete_spec.rb b/spec/unit/knife/client_bulk_delete_spec.rb
index 45bb4dd16c..1a6317ac00 100644
--- a/spec/unit/knife/client_bulk_delete_spec.rb
+++ b/spec/unit/knife/client_bulk_delete_spec.rb
@@ -45,7 +45,7 @@ describe Chef::Knife::ClientBulkDelete do
clients = Hash.new
nonvalidator_client_names.each do |client_name|
- client = Chef::ApiClient.new()
+ client = Chef::ApiClientV1.new()
client.name(client_name)
allow(client).to receive(:destroy).and_return(true)
clients[client_name] = client
@@ -59,7 +59,7 @@ describe Chef::Knife::ClientBulkDelete do
clients = Hash.new
validator_client_names.each do |validator_client_name|
- validator_client = Chef::ApiClient.new()
+ validator_client = Chef::ApiClientV1.new()
validator_client.name(validator_client_name)
allow(validator_client).to receive(:validator).and_return(true)
allow(validator_client).to receive(:destroy).and_return(true)
@@ -75,7 +75,7 @@ describe Chef::Knife::ClientBulkDelete do
}
before(:each) do
- allow(Chef::ApiClient).to receive(:list).and_return(clients)
+ allow(Chef::ApiClientV1).to receive(:list).and_return(clients)
end
describe "run" do
@@ -89,7 +89,7 @@ describe Chef::Knife::ClientBulkDelete do
describe "with any clients" do
it "should get the list of the clients" do
- expect(Chef::ApiClient).to receive(:list)
+ expect(Chef::ApiClientV1).to receive(:list)
knife.run
end
diff --git a/spec/unit/knife/client_create_spec.rb b/spec/unit/knife/client_create_spec.rb
index 10d386b5ff..a1dcc564e2 100644
--- a/spec/unit/knife/client_create_spec.rb
+++ b/spec/unit/knife/client_create_spec.rb
@@ -22,6 +22,8 @@ Chef::Knife::ClientCreate.load_deps
describe Chef::Knife::ClientCreate do
let(:stderr) { StringIO.new }
+ let(:stdout) { StringIO.new }
+
let(:default_client_hash) do
{
@@ -32,84 +34,153 @@ describe Chef::Knife::ClientCreate do
end
let(:client) do
- c = double("Chef::ApiClient")
- allow(c).to receive(:save).and_return({"private_key" => ""})
- allow(c).to receive(:to_s).and_return("client[adam]")
- c
+ Chef::ApiClientV1.new
end
let(:knife) do
k = Chef::Knife::ClientCreate.new
- k.name_args = [ "adam" ]
- k.ui.config[:disable_editing] = true
+ k.name_args = []
+ allow(k).to receive(:client).and_return(client)
+ allow(k).to receive(:edit_data).with(client).and_return(client)
allow(k.ui).to receive(:stderr).and_return(stderr)
- allow(k.ui).to receive(:stdout).and_return(stderr)
+ allow(k.ui).to receive(:stdout).and_return(stdout)
k
end
+ before do
+ allow(client).to receive(:to_s).and_return("client[adam]")
+ allow(knife).to receive(:create_client).and_return(client)
+ end
+
before(:each) do
Chef::Config[:node_name] = "webmonkey.example.com"
end
describe "run" do
- it "should create and save the ApiClient" do
- expect(Chef::ApiClient).to receive(:from_hash).and_return(client)
- expect(client).to receive(:save)
- knife.run
+ context "when nothing is passed" do
+ # from spec/support/shared/unit/knife_shared.rb
+ it_should_behave_like "mandatory field missing" do
+ let(:name_args) { [] }
+ let(:fieldname) { 'client name' }
+ end
end
- it "should print a message upon creation" do
- expect(Chef::ApiClient).to receive(:from_hash).and_return(client)
- expect(client).to receive(:save)
- knife.run
- expect(stderr.string).to match /Created client.*adam/i
- end
+ context "when clientname is passed" do
+ before do
+ knife.name_args = ['adam']
+ end
- it "should set the Client name" do
- expect(Chef::ApiClient).to receive(:from_hash).with(hash_including("name" => "adam")).and_return(client)
- knife.run
- end
+ context "when public_key and prevent_keygen are passed" do
+ before do
+ knife.config[:public_key] = "some_key"
+ knife.config[:prevent_keygen] = true
+ end
+
+ it "prints the usage" do
+ expect(knife).to receive(:show_usage)
+ expect { knife.run }.to raise_error(SystemExit)
+ end
+
+ it "prints a relevant error message" do
+ expect { knife.run }.to raise_error(SystemExit)
+ expect(stderr.string).to match /You cannot pass --public-key and --prevent-keygen/
+ end
+ end
- it "by default it is not an admin" do
- expect(Chef::ApiClient).to receive(:from_hash).with(hash_including("admin" => false)).and_return(client)
- knife.run
- end
+ it "should create the ApiClient" do
+ expect(knife).to receive(:create_client)
+ knife.run
+ end
- it "by default it is not a validator" do
- expect(Chef::ApiClient).to receive(:from_hash).with(hash_including("validator" => false)).and_return(client)
- knife.run
- end
+ it "should print a message upon creation" do
+ expect(knife).to receive(:create_client)
+ knife.run
+ expect(stderr.string).to match /Created client.*adam/i
+ end
- it "should allow you to edit the data" do
- expect(knife).to receive(:edit_hash).with(default_client_hash).and_return(default_client_hash)
- allow(Chef::ApiClient).to receive(:from_hash).and_return(client)
- knife.run
- end
+ it "should set the Client name" do
+ knife.run
+ expect(client.name).to eq("adam")
+ end
- describe "with -f or --file" do
- it "should write the private key to a file" do
- knife.config[:file] = "/tmp/monkeypants"
- allow_any_instance_of(Chef::ApiClient).to receive(:save).and_return({ 'private_key' => "woot" })
- filehandle = double("Filehandle")
- expect(filehandle).to receive(:print).with('woot')
- expect(File).to receive(:open).with("/tmp/monkeypants", "w").and_yield(filehandle)
+ it "by default it is not an admin" do
knife.run
+ expect(client.admin).to be_falsey
end
- end
- describe "with -a or --admin" do
- it "should create an admin client" do
- knife.config[:admin] = true
- expect(Chef::ApiClient).to receive(:from_hash).with(hash_including("admin" => true)).and_return(client)
+ it "by default it is not a validator" do
knife.run
+ expect(client.admin).to be_falsey
end
- end
- describe "with --validator" do
- it "should create an validator client" do
- knife.config[:validator] = true
- expect(Chef::ApiClient).to receive(:from_hash).with(hash_including("validator" => true)).and_return(client)
+ it "by default it should set create_key to true" do
knife.run
+ expect(client.create_key).to be_truthy
+ end
+
+ it "should allow you to edit the data" do
+ expect(knife).to receive(:edit_data).with(client).and_return(client)
+ knife.run
+ end
+
+ describe "with -f or --file" do
+ before do
+ client.private_key "woot"
+ end
+
+ it "should write the private key to a file" do
+ knife.config[:file] = "/tmp/monkeypants"
+ filehandle = double("Filehandle")
+ expect(filehandle).to receive(:print).with('woot')
+ expect(File).to receive(:open).with("/tmp/monkeypants", "w").and_yield(filehandle)
+ knife.run
+ end
+ end
+
+ describe "with -a or --admin" do
+ before do
+ knife.config[:admin] = true
+ end
+
+ it "should create an admin client" do
+ knife.run
+ expect(client.admin).to be_truthy
+ end
+ end
+
+ describe "with -p or --public-key" do
+ before do
+ knife.config[:public_key] = 'some_key'
+ allow(File).to receive(:read).and_return('some_key')
+ allow(File).to receive(:expand_path)
+ end
+
+ it "sets the public key" do
+ knife.run
+ expect(client.public_key).to eq('some_key')
+ end
+ end
+
+ describe "with -k or --prevent-keygen" do
+ before do
+ knife.config[:prevent_keygen] = true
+ end
+
+ it "does not set create_key" do
+ knife.run
+ expect(client.create_key).to be_falsey
+ end
+ end
+
+ describe "with --validator" do
+ before do
+ knife.config[:validator] = true
+ end
+
+ it "should create an validator client" do
+ knife.run
+ expect(client.validator).to be_truthy
+ end
end
end
end
diff --git a/spec/unit/knife/client_delete_spec.rb b/spec/unit/knife/client_delete_spec.rb
index 0fb5e0bab7..619009979b 100644
--- a/spec/unit/knife/client_delete_spec.rb
+++ b/spec/unit/knife/client_delete_spec.rb
@@ -30,7 +30,7 @@ describe Chef::Knife::ClientDelete do
describe 'run' do
it 'should delete the client' do
- expect(@knife).to receive(:delete_object).with(Chef::ApiClient, 'adam', 'client')
+ expect(@knife).to receive(:delete_object).with(Chef::ApiClientV1, 'adam', 'client')
@knife.run
end
@@ -46,8 +46,8 @@ describe Chef::Knife::ClientDelete do
before(:each) do
allow(Chef::Knife::UI).to receive(:confirm).and_return(true)
allow(@knife).to receive(:confirm).and_return(true)
- @client = Chef::ApiClient.new
- expect(Chef::ApiClient).to receive(:load).and_return(@client)
+ @client = Chef::ApiClientV1.new
+ expect(Chef::ApiClientV1).to receive(:load).and_return(@client)
end
it 'should delete non-validator client if --delete-validators is not set' do
diff --git a/spec/unit/knife/client_edit_spec.rb b/spec/unit/knife/client_edit_spec.rb
index c040c5e2f2..ad56d9212d 100644
--- a/spec/unit/knife/client_edit_spec.rb
+++ b/spec/unit/knife/client_edit_spec.rb
@@ -17,16 +17,29 @@
#
require 'spec_helper'
+require 'chef/api_client_v1'
describe Chef::Knife::ClientEdit do
before(:each) do
@knife = Chef::Knife::ClientEdit.new
@knife.name_args = [ 'adam' ]
+ @knife.config[:disable_editing] = true
end
describe 'run' do
+ let(:data) {
+ {
+ "name" => "adam",
+ "validator" => false,
+ "admin" => false,
+ "chef_type" => "client",
+ "create_key" => true
+ }
+ }
+
it 'should edit the client' do
- expect(@knife).to receive(:edit_object).with(Chef::ApiClient, 'adam')
+ allow(Chef::ApiClientV1).to receive(:load).with('adam').and_return(data)
+ expect(@knife).to receive(:edit_data).with(data).and_return(data)
@knife.run
end
diff --git a/spec/unit/knife/client_list_spec.rb b/spec/unit/knife/client_list_spec.rb
index eff01da4e9..ce0fa4f5e8 100644
--- a/spec/unit/knife/client_list_spec.rb
+++ b/spec/unit/knife/client_list_spec.rb
@@ -26,7 +26,7 @@ describe Chef::Knife::ClientList do
describe 'run' do
it 'should list the clients' do
- expect(Chef::ApiClient).to receive(:list)
+ expect(Chef::ApiClientV1).to receive(:list)
expect(@knife).to receive(:format_list_for_display)
@knife.run
end
diff --git a/spec/unit/knife/client_reregister_spec.rb b/spec/unit/knife/client_reregister_spec.rb
index f1be4ed570..7e763242e4 100644
--- a/spec/unit/knife/client_reregister_spec.rb
+++ b/spec/unit/knife/client_reregister_spec.rb
@@ -41,7 +41,7 @@ describe Chef::Knife::ClientReregister do
context 'when not configured for file output' do
it 'reregisters the client and prints the key' do
- expect(Chef::ApiClient).to receive(:reregister).with('adam').and_return(@client_mock)
+ expect(Chef::ApiClientV1).to receive(:reregister).with('adam').and_return(@client_mock)
@knife.run
expect(@stdout.string).to match( /foo_key/ )
end
@@ -49,7 +49,7 @@ describe Chef::Knife::ClientReregister do
context 'when configured for file output' do
it 'should write the private key to a file' do
- expect(Chef::ApiClient).to receive(:reregister).with('adam').and_return(@client_mock)
+ expect(Chef::ApiClientV1).to receive(:reregister).with('adam').and_return(@client_mock)
@knife.config[:file] = '/tmp/monkeypants'
filehandle = StringIO.new
diff --git a/spec/unit/knife/client_show_spec.rb b/spec/unit/knife/client_show_spec.rb
index 8404e8d019..73a876cee0 100644
--- a/spec/unit/knife/client_show_spec.rb
+++ b/spec/unit/knife/client_show_spec.rb
@@ -27,7 +27,7 @@ describe Chef::Knife::ClientShow do
describe 'run' do
it 'should list the client' do
- expect(Chef::ApiClient).to receive(:load).with('adam').and_return(@client_mock)
+ expect(Chef::ApiClientV1).to receive(:load).with('adam').and_return(@client_mock)
expect(@knife).to receive(:format_for_display).with(@client_mock)
@knife.run
end
@@ -37,7 +37,7 @@ describe Chef::Knife::ClientShow do
@stdout = StringIO.new
allow(@knife.ui).to receive(:stdout).and_return(@stdout)
fake_client_contents = {"foo"=>"bar", "baz"=>"qux"}
- expect(Chef::ApiClient).to receive(:load).with('adam').and_return(fake_client_contents)
+ expect(Chef::ApiClientV1).to receive(:load).with('adam').and_return(fake_client_contents)
@knife.run
expect(@stdout.string).to eql("{\n \"foo\": \"bar\",\n \"baz\": \"qux\"\n}\n")
end
diff --git a/spec/unit/knife/core/custom_manifest_loader_spec.rb b/spec/unit/knife/core/custom_manifest_loader_spec.rb
new file mode 100644
index 0000000000..1edbedd3c8
--- /dev/null
+++ b/spec/unit/knife/core/custom_manifest_loader_spec.rb
@@ -0,0 +1,41 @@
+#
+# Copyright:: Copyright (c) 2015 Chef Software, Inc
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::SubcommandLoader::CustomManifestLoader do
+ let(:ec2_server_create_plugin) { "/usr/lib/ruby/gems/knife-ec2-0.5.12/lib/chef/knife/ec2_server_create.rb" }
+ let(:manifest_content) do
+ { "plugins" => {
+ "knife-ec2" => {
+ "paths" => [
+ ec2_server_create_plugin
+ ]
+ }
+ }
+ }
+ end
+ let(:loader) do
+ Chef::Knife::SubcommandLoader::CustomManifestLoader.new(File.join(CHEF_SPEC_DATA, 'knife-site-subcommands'),
+ manifest_content)
+ end
+
+ it "uses paths from the manifest instead of searching gems" do
+ expect(Gem::Specification).not_to receive(:latest_specs).and_call_original
+ expect(loader.subcommand_files).to include(ec2_server_create_plugin)
+ end
+end
diff --git a/spec/unit/knife/core/gem_glob_loader_spec.rb b/spec/unit/knife/core/gem_glob_loader_spec.rb
new file mode 100644
index 0000000000..465eea2656
--- /dev/null
+++ b/spec/unit/knife/core/gem_glob_loader_spec.rb
@@ -0,0 +1,210 @@
+#
+# Copyright:: Copyright (c) 2015 Chef Software, Inc
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::SubcommandLoader::GemGlobLoader do
+ let(:loader) { Chef::Knife::SubcommandLoader::GemGlobLoader.new(File.join(CHEF_SPEC_DATA, 'knife-site-subcommands')) }
+ let(:home) { File.join(CHEF_SPEC_DATA, 'knife-home') }
+ let(:plugin_dir) { File.join(home, '.chef', 'plugins', 'knife') }
+
+ before do
+ allow(ChefConfig).to receive(:windows?) { false }
+ Chef::Util::PathHelper.class_variable_set(:@@home_dir, home)
+ end
+
+ after do
+ Chef::Util::PathHelper.class_variable_set(:@@home_dir, nil)
+ end
+
+ it "builds a list of the core subcommand file require paths" do
+ expect(loader.subcommand_files).not_to be_empty
+ loader.subcommand_files.each do |require_path|
+ expect(require_path).to match(/chef\/knife\/.*|plugins\/knife\/.*/)
+ end
+ end
+
+ it "finds files installed via rubygems" do
+ expect(loader.find_subcommands_via_rubygems).to include('chef/knife/node_create')
+ loader.find_subcommands_via_rubygems.each {|rel_path, abs_path| expect(abs_path).to match(%r[chef/knife/.+])}
+ end
+
+ it "finds files from latest version of installed gems" do
+ gems = [ double('knife-ec2-0.5.12') ]
+ gem_files = [
+ '/usr/lib/ruby/gems/knife-ec2-0.5.12/lib/chef/knife/ec2_base.rb',
+ '/usr/lib/ruby/gems/knife-ec2-0.5.12/lib/chef/knife/ec2_otherstuff.rb'
+ ]
+ expect($LOAD_PATH).to receive(:map).and_return([])
+ if Gem::Specification.respond_to? :latest_specs
+ expect(Gem::Specification).to receive(:latest_specs).with(true).and_return(gems)
+ expect(gems[0]).to receive(:matches_for_glob).with(/chef\/knife\/\*\.rb\{(.*),\.rb,(.*)\}/).and_return(gem_files)
+ else
+ expect(Gem.source_index).to receive(:latest_specs).with(true).and_return(gems)
+ expect(gems[0]).to receive(:require_paths).twice.and_return(['lib'])
+ expect(gems[0]).to receive(:full_gem_path).and_return('/usr/lib/ruby/gems/knife-ec2-0.5.12')
+ expect(Dir).to receive(:[]).with('/usr/lib/ruby/gems/knife-ec2-0.5.12/lib/chef/knife/*.rb').and_return(gem_files)
+ end
+ expect(loader).to receive(:find_subcommands_via_dirglob).and_return({})
+ expect(loader.subcommand_files.select { |file| file =~ /knife-ec2/ }.sort).to eq(gem_files)
+ end
+
+ it "finds files using a dirglob when rubygems is not available" do
+ expect(loader.find_subcommands_via_dirglob).to include('chef/knife/node_create')
+ loader.find_subcommands_via_dirglob.each {|rel_path, abs_path| expect(abs_path).to match(%r[chef/knife/.+])}
+ end
+
+ it "finds user-specific subcommands in the user's ~/.chef directory" do
+ expected_command = File.join(home, '.chef', 'plugins', 'knife', 'example_home_subcommand.rb')
+ expect(loader.site_subcommands).to include(expected_command)
+ end
+
+ it "finds repo specific subcommands by searching for a .chef directory" do
+ expected_command = File.join(CHEF_SPEC_DATA, 'knife-site-subcommands', 'plugins', 'knife', 'example_subcommand.rb')
+ expect(loader.site_subcommands).to include(expected_command)
+ end
+
+ # https://github.com/opscode/chef-dk/issues/227
+ #
+ # `knife` in ChefDK isn't from a gem install, it's directly run from a clone
+ # of the source, but there can be one or more versions of chef also installed
+ # as a gem. If the gem install contains a command that doesn't exist in the
+ # source tree of the "primary" chef install, it can be loaded and cause an
+ # error. We also want to ensure that we only load builtin commands from the
+ # "primary" chef install.
+ context "when a different version of chef is also installed as a gem" do
+
+ let(:all_found_commands) do
+ [
+ "/opt/chefdk/embedded/apps/chef/lib/chef/knife/bootstrap.rb",
+ "/opt/chefdk/embedded/apps/chef/lib/chef/knife/client_bulk_delete.rb",
+ "/opt/chefdk/embedded/apps/chef/lib/chef/knife/client_create.rb",
+
+ # We use the fake version 1.0.0 because that version doesn't exist,
+ # which ensures it won't ever equal "chef-#{Chef::VERSION}"
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-1.0.0/lib/chef/knife/bootstrap.rb",
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-1.0.0/lib/chef/knife/client_bulk_delete.rb",
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-1.0.0/lib/chef/knife/client_create.rb",
+
+ # Test that we don't accept a version number that is different only in
+ # trailing characters, e.g. we are running Chef 12.0.0 but there is a
+ # Chef 12.0.0.rc.0 gem also:
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-#{Chef::VERSION}.rc.0/lib/chef/knife/thing.rb",
+
+ # Test that we ignore the platform suffix when checking for different
+ # gem versions.
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-#{Chef::VERSION}-x86-mingw32/lib/chef/knife/valid.rb",
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-#{Chef::VERSION}-i386-mingw64/lib/chef/knife/valid-too.rb",
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-#{Chef::VERSION}-mswin32/lib/chef/knife/also-valid.rb",
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-#{Chef::VERSION}-universal-mingw32/lib/chef/knife/universal-is-valid.rb",
+ # ...but don't ignore the .rc / .dev parts in the case when we have
+ # platform suffixes
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-#{Chef::VERSION}.rc.0-x86-mingw32/lib/chef/knife/invalid.rb",
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-#{Chef::VERSION}.dev-mswin32/lib/chef/knife/invalid-too.rb",
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-#{Chef::VERSION}.dev.0-x86-mingw64/lib/chef/knife/still-invalid.rb",
+
+ # This command is "extra" compared to what's in the embedded/apps/chef install:
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-1.0.0/lib/chef/knife/data_bag_secret_options.rb",
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-vault-2.2.4/lib/chef/knife/decrypt.rb",
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/knife-spork-1.4.1/lib/chef/knife/spork-bump.rb",
+
+ # These are fake commands that have names designed to test that the
+ # regex is strict enough
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-foo-#{Chef::VERSION}/lib/chef/knife/chef-foo.rb",
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/foo-chef-#{Chef::VERSION}/lib/chef/knife/foo-chef.rb",
+
+ # In a real scenario, we'd use rubygems APIs to only select the most
+ # recent gem, but for this test we want to check that we're doing the
+ # right thing both when the plugin version matches and does not match
+ # the current chef version. Looking at
+ # `SubcommandLoader::MATCHES_THIS_CHEF_GEM` and
+ # `SubcommandLoader::MATCHES_CHEF_GEM` should make it clear why we want
+ # to test these two cases.
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-bar-1.0.0/lib/chef/knife/chef-bar.rb",
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/bar-chef-1.0.0/lib/chef/knife/bar-chef.rb"
+ ]
+ end
+
+ let(:expected_valid_commands) do
+ [
+ "/opt/chefdk/embedded/apps/chef/lib/chef/knife/bootstrap.rb",
+ "/opt/chefdk/embedded/apps/chef/lib/chef/knife/client_bulk_delete.rb",
+ "/opt/chefdk/embedded/apps/chef/lib/chef/knife/client_create.rb",
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-#{Chef::VERSION}-x86-mingw32/lib/chef/knife/valid.rb",
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-#{Chef::VERSION}-i386-mingw64/lib/chef/knife/valid-too.rb",
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-#{Chef::VERSION}-mswin32/lib/chef/knife/also-valid.rb",
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-#{Chef::VERSION}-universal-mingw32/lib/chef/knife/universal-is-valid.rb",
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-vault-2.2.4/lib/chef/knife/decrypt.rb",
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/knife-spork-1.4.1/lib/chef/knife/spork-bump.rb",
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-foo-#{Chef::VERSION}/lib/chef/knife/chef-foo.rb",
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/foo-chef-#{Chef::VERSION}/lib/chef/knife/foo-chef.rb",
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-bar-1.0.0/lib/chef/knife/chef-bar.rb",
+ "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/bar-chef-1.0.0/lib/chef/knife/bar-chef.rb"
+ ]
+ end
+
+ before do
+ expect(loader).to receive(:find_files_latest_gems).with("chef/knife/*.rb").and_return(all_found_commands)
+ expect(loader).to receive(:find_subcommands_via_dirglob).and_return({})
+ end
+
+ it "ignores commands from the non-matching gem install" do
+ expect(loader.find_subcommands_via_rubygems.values).to eq(expected_valid_commands)
+ end
+
+ end
+
+ describe "finding 3rd party plugins" do
+ let(:env_home) { "/home/alice" }
+ let(:manifest_path) { env_home + "/.chef/plugin_manifest.json" }
+
+ before do
+ env_dup = ENV.to_hash
+ allow(ENV).to receive(:[]) { |key| env_dup[key] }
+ allow(ENV).to receive(:[]).with("HOME").and_return(env_home)
+ end
+
+
+ it "searches rubygems for plugins" do
+ if Gem::Specification.respond_to?(:latest_specs)
+ expect(Gem::Specification).to receive(:latest_specs).and_call_original
+ else
+ expect(Gem.source_index).to receive(:latest_specs).and_call_original
+ end
+ loader.subcommand_files.each do |require_path|
+ expect(require_path).to match(/chef\/knife\/.*|plugins\/knife\/.*/)
+ end
+ end
+
+ context "and HOME environment variable is not set" do
+ before do
+ allow(ENV).to receive(:[]).with("HOME").and_return(nil)
+ end
+
+ it "searches rubygems for plugins" do
+ if Gem::Specification.respond_to?(:latest_specs)
+ expect(Gem::Specification).to receive(:latest_specs).and_call_original
+ else
+ expect(Gem.source_index).to receive(:latest_specs).and_call_original
+ end
+ loader.subcommand_files.each do |require_path|
+ expect(require_path).to match(/chef\/knife\/.*|plugins\/knife\/.*/)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/unit/knife/core/hashed_command_loader_spec.rb b/spec/unit/knife/core/hashed_command_loader_spec.rb
new file mode 100644
index 0000000000..00e7ba377b
--- /dev/null
+++ b/spec/unit/knife/core/hashed_command_loader_spec.rb
@@ -0,0 +1,93 @@
+#
+# Copyright:: Copyright (c) 2015 Chef Software, Inc
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Knife::SubcommandLoader::HashedCommandLoader do
+ before do
+ allow(ChefConfig).to receive(:windows?) { false }
+ end
+
+ let(:plugin_manifest) {
+ {
+ "_autogenerated_command_paths" => {
+ "plugins_paths" => {
+ "cool_a" => ["/file/for/plugin/a"],
+ "cooler_b" => ["/file/for/plugin/b"]
+ },
+ "plugins_by_category" => {
+ "cool" => [
+ "cool_a"
+ ],
+ "cooler" => [
+ "cooler_b"
+ ]
+ }
+ }
+ }
+ }
+
+ let(:loader) { Chef::Knife::SubcommandLoader::HashedCommandLoader.new(
+ File.join(CHEF_SPEC_DATA, 'knife-site-subcommands'),
+ plugin_manifest)}
+
+ describe "#list_commands" do
+ it "lists all commands by category when no argument is given" do
+ expect(loader.list_commands).to eq({"cool" => ["cool_a"], "cooler" => ["cooler_b"]})
+ end
+
+ it "lists only commands in the given category when a category is given" do
+ expect(loader.list_commands("cool")).to eq({"cool" => ["cool_a"]})
+ end
+ end
+
+ describe "#subcommand_files" do
+ it "lists all the files" do
+ expect(loader.subcommand_files).to eq(["/file/for/plugin/a", "/file/for/plugin/b"])
+ end
+ end
+
+ describe "#load_commands" do
+ before do
+ allow(Kernel).to receive(:load).and_return(true)
+ end
+
+ it "returns false for non-existant commands" do
+ expect(loader.load_command(["nothere"])).to eq(false)
+ end
+
+ it "loads the correct file and returns true if the command exists" do
+ allow(File).to receive(:exists?).and_return(true)
+ expect(Kernel).to receive(:load).with("/file/for/plugin/a").and_return(true)
+ expect(loader.load_command(["cool_a"])).to eq(true)
+ end
+ end
+
+ describe "#subcommand_for_args" do
+ it "returns the subcommands for an exact match" do
+ expect(loader.subcommand_for_args(["cooler_b"])).to eq("cooler_b")
+ end
+
+ it "finds the right subcommand even when _'s are elided" do
+ expect(loader.subcommand_for_args(["cooler", "b"])).to eq("cooler_b")
+ end
+
+ it "returns nil if the the subcommand isn't in our manifest" do
+ expect(loader.subcommand_for_args(["cooler c"])).to eq(nil)
+ end
+ end
+end
diff --git a/spec/unit/knife/core/subcommand_loader_spec.rb b/spec/unit/knife/core/subcommand_loader_spec.rb
index df42cff2ea..2386465c75 100644
--- a/spec/unit/knife/core/subcommand_loader_spec.rb
+++ b/spec/unit/knife/core/subcommand_loader_spec.rb
@@ -1,6 +1,5 @@
#
-# Author:: Daniel DeLeo (<dan@opscode.com>)
-# Copyright:: Copyright (c) 2011 Opscode, Inc.
+# Copyright:: Copyright (c) 2015 Chef Software, Inc
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,206 +18,47 @@
require 'spec_helper'
describe Chef::Knife::SubcommandLoader do
- before do
- allow(Chef::Platform).to receive(:windows?) { false }
- @home = File.join(CHEF_SPEC_DATA, 'knife-home')
- @env = {'HOME' => @home}
- @loader = Chef::Knife::SubcommandLoader.new(File.join(CHEF_SPEC_DATA, 'knife-site-subcommands'), @env)
- end
-
- it "builds a list of the core subcommand file require paths" do
- expect(@loader.subcommand_files).not_to be_empty
- @loader.subcommand_files.each do |require_path|
- expect(require_path).to match(/chef\/knife\/.*|plugins\/knife\/.*/)
- end
- end
-
- it "finds files installed via rubygems" do
- expect(@loader.find_subcommands_via_rubygems).to include('chef/knife/node_create')
- @loader.find_subcommands_via_rubygems.each {|rel_path, abs_path| expect(abs_path).to match(%r[chef/knife/.+])}
- end
-
- it "finds files from latest version of installed gems" do
- gems = [ double('knife-ec2-0.5.12') ]
- gem_files = [
- '/usr/lib/ruby/gems/knife-ec2-0.5.12/lib/chef/knife/ec2_base.rb',
- '/usr/lib/ruby/gems/knife-ec2-0.5.12/lib/chef/knife/ec2_otherstuff.rb'
- ]
- expect($LOAD_PATH).to receive(:map).and_return([])
- if Gem::Specification.respond_to? :latest_specs
- expect(Gem::Specification).to receive(:latest_specs).with(true).and_return(gems)
- expect(gems[0]).to receive(:matches_for_glob).with(/chef\/knife\/\*\.rb\{(.*),\.rb,(.*)\}/).and_return(gem_files)
- else
- expect(Gem.source_index).to receive(:latest_specs).with(true).and_return(gems)
- expect(gems[0]).to receive(:require_paths).twice.and_return(['lib'])
- expect(gems[0]).to receive(:full_gem_path).and_return('/usr/lib/ruby/gems/knife-ec2-0.5.12')
- expect(Dir).to receive(:[]).with('/usr/lib/ruby/gems/knife-ec2-0.5.12/lib/chef/knife/*.rb').and_return(gem_files)
- end
- expect(@loader).to receive(:find_subcommands_via_dirglob).and_return({})
- expect(@loader.find_subcommands_via_rubygems.values.select { |file| file =~ /knife-ec2/ }.sort).to eq(gem_files)
- end
+ let(:loader) { Chef::Knife::SubcommandLoader.new(File.join(CHEF_SPEC_DATA, 'knife-site-subcommands')) }
+ let(:home) { File.join(CHEF_SPEC_DATA, 'knife-home') }
+ let(:plugin_dir) { File.join(home, '.chef', 'plugins', 'knife') }
- it "finds files using a dirglob when rubygems is not available" do
- expect(@loader.find_subcommands_via_dirglob).to include('chef/knife/node_create')
- @loader.find_subcommands_via_dirglob.each {|rel_path, abs_path| expect(abs_path).to match(%r[chef/knife/.+])}
- end
-
- it "finds user-specific subcommands in the user's ~/.chef directory" do
- expected_command = File.join(@home, '.chef', 'plugins', 'knife', 'example_home_subcommand.rb')
- expect(@loader.site_subcommands).to include(expected_command)
+ before do
+ allow(ChefConfig).to receive(:windows?) { false }
+ Chef::Util::PathHelper.class_variable_set(:@@home_dir, home)
end
- it "finds repo specific subcommands by searching for a .chef directory" do
- expected_command = File.join(CHEF_SPEC_DATA, 'knife-site-subcommands', 'plugins', 'knife', 'example_subcommand.rb')
- expect(@loader.site_subcommands).to include(expected_command)
+ after do
+ Chef::Util::PathHelper.class_variable_set(:@@home_dir, nil)
end
- # https://github.com/opscode/chef-dk/issues/227
- #
- # `knife` in ChefDK isn't from a gem install, it's directly run from a clone
- # of the source, but there can be one or more versions of chef also installed
- # as a gem. If the gem install contains a command that doesn't exist in the
- # source tree of the "primary" chef install, it can be loaded and cause an
- # error. We also want to ensure that we only load builtin commands from the
- # "primary" chef install.
- context "when a different version of chef is also installed as a gem" do
-
- let(:all_found_commands) do
- [
- "/opt/chefdk/embedded/apps/chef/lib/chef/knife/bootstrap.rb",
- "/opt/chefdk/embedded/apps/chef/lib/chef/knife/client_bulk_delete.rb",
- "/opt/chefdk/embedded/apps/chef/lib/chef/knife/client_create.rb",
-
- # We use the fake version 1.0.0 because that version doesn't exist,
- # which ensures it won't ever equal "chef-#{Chef::VERSION}"
- "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-1.0.0/lib/chef/knife/bootstrap.rb",
- "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-1.0.0/lib/chef/knife/client_bulk_delete.rb",
- "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-1.0.0/lib/chef/knife/client_create.rb",
-
- # Test that we don't accept a version number that is different only in
- # trailing characters, e.g. we are running Chef 12.0.0 but there is a
- # Chef 12.0.0.rc.0 gem also:
- "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-#{Chef::VERSION}.rc.0/lib/chef/knife/thing.rb",
-
- # This command is "extra" compared to what's in the embedded/apps/chef install:
- "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-1.0.0/lib/chef/knife/data_bag_secret_options.rb",
- "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-vault-2.2.4/lib/chef/knife/decrypt.rb",
- "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/knife-spork-1.4.1/lib/chef/knife/spork-bump.rb",
-
- # These are fake commands that have names designed to test that the
- # regex is strict enough
- "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-foo-#{Chef::VERSION}/lib/chef/knife/chef-foo.rb",
- "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/foo-chef-#{Chef::VERSION}/lib/chef/knife/foo-chef.rb",
-
- # In a real scenario, we'd use rubygems APIs to only select the most
- # recent gem, but for this test we want to check that we're doing the
- # right thing both when the plugin version matches and does not match
- # the current chef version. Looking at
- # `SubcommandLoader::MATCHES_THIS_CHEF_GEM` and
- # `SubcommandLoader::MATCHES_CHEF_GEM` should make it clear why we want
- # to test these two cases.
- "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-bar-1.0.0/lib/chef/knife/chef-bar.rb",
- "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/bar-chef-1.0.0/lib/chef/knife/bar-chef.rb"
- ]
- end
+ let(:config_dir) { File.join(CHEF_SPEC_DATA, 'knife-site-subcommands') }
- let(:expected_valid_commands) do
- [
- "/opt/chefdk/embedded/apps/chef/lib/chef/knife/bootstrap.rb",
- "/opt/chefdk/embedded/apps/chef/lib/chef/knife/client_bulk_delete.rb",
- "/opt/chefdk/embedded/apps/chef/lib/chef/knife/client_create.rb",
- "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-vault-2.2.4/lib/chef/knife/decrypt.rb",
- "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/knife-spork-1.4.1/lib/chef/knife/spork-bump.rb",
- "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-foo-#{Chef::VERSION}/lib/chef/knife/chef-foo.rb",
- "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/foo-chef-#{Chef::VERSION}/lib/chef/knife/foo-chef.rb",
- "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/chef-bar-1.0.0/lib/chef/knife/chef-bar.rb",
- "/opt/chefdk/embedded/lib/ruby/gems/2.1.0/gems/bar-chef-1.0.0/lib/chef/knife/bar-chef.rb"
- ]
- end
-
- before do
- expect(@loader).to receive(:find_files_latest_gems).with("chef/knife/*.rb").and_return(all_found_commands)
- expect(@loader).to receive(:find_subcommands_via_dirglob).and_return({})
- end
-
- it "ignores commands from the non-matching gem install" do
- expect(@loader.find_subcommands_via_rubygems.values).to eq(expected_valid_commands)
- end
-
- end
-
- describe "finding 3rd party plugins" do
- let(:env_home) { "/home/alice" }
- let(:manifest_path) { env_home + "/.chef/plugin_manifest.json" }
-
- before do
- env_dup = ENV.to_hash
- allow(ENV).to receive(:[]) { |key| env_dup[key] }
- allow(ENV).to receive(:[]).with("HOME").and_return(env_home)
- end
-
- context "when there is not a ~/.chef/plugin_manifest.json file" do
+ describe "#for_config" do
+ context "when ~/.chef/plugin_manifest.json exists" do
before do
- allow(File).to receive(:exist?).with(manifest_path).and_return(false)
+ allow(File).to receive(:exist?).with(File.join(home, '.chef', 'plugin_manifest.json')).and_return(true)
end
- it "searches rubygems for plugins" do
- if Gem::Specification.respond_to?(:latest_specs)
- expect(Gem::Specification).to receive(:latest_specs).and_call_original
- else
- expect(Gem.source_index).to receive(:latest_specs).and_call_original
- end
- @loader.subcommand_files.each do |require_path|
- expect(require_path).to match(/chef\/knife\/.*|plugins\/knife\/.*/)
- end
+ it "creates a HashedCommandLoader with the manifest has _autogenerated_command_paths" do
+ allow(File).to receive(:read).with(File.join(home, '.chef', 'plugin_manifest.json')).and_return("{ \"_autogenerated_command_paths\": {}}")
+ expect(Chef::Knife::SubcommandLoader.for_config(config_dir)).to be_a Chef::Knife::SubcommandLoader::HashedCommandLoader
end
- context "and HOME environment variable is not set" do
- before do
- allow(ENV).to receive(:[]).with("HOME").and_return(nil)
- end
-
- it "searches rubygems for plugins" do
- if Gem::Specification.respond_to?(:latest_specs)
- expect(Gem::Specification).to receive(:latest_specs).and_call_original
- else
- expect(Gem.source_index).to receive(:latest_specs).and_call_original
- end
- @loader.subcommand_files.each do |require_path|
- expect(require_path).to match(/chef\/knife\/.*|plugins\/knife\/.*/)
- end
- end
+ it "creates a CustomManifestLoader with then manifest has a key other than _autogenerated_command_paths" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ allow(File).to receive(:read).with(File.join(home, '.chef', 'plugin_manifest.json')).and_return("{ \"plugins\": {}}")
+ expect(Chef::Knife::SubcommandLoader.for_config(config_dir)).to be_a Chef::Knife::SubcommandLoader::CustomManifestLoader
end
-
end
- context "when there is a ~/.chef/plugin_manifest.json file" do
- let(:ec2_server_create_plugin) { "/usr/lib/ruby/gems/knife-ec2-0.5.12/lib/chef/knife/ec2_server_create.rb" }
-
- let(:manifest_content) do
- { "plugins" => {
- "knife-ec2" => {
- "paths" => [
- ec2_server_create_plugin
- ]
- }
- }
- }
- end
-
- let(:manifest_json) { Chef::JSONCompat.to_json(manifest_content) }
-
+ context "when ~/.chef/plugin_manifest.json does not exist" do
before do
- allow(File).to receive(:exist?).with(manifest_path).and_return(true)
- allow(File).to receive(:read).with(manifest_path).and_return(manifest_json)
+ allow(File).to receive(:exist?).with(File.join(home, '.chef', 'plugin_manifest.json')).and_return(false)
end
- it "uses paths from the manifest instead of searching gems" do
- expect(Gem::Specification).not_to receive(:latest_specs).and_call_original
- expect(@loader.subcommand_files).to include(ec2_server_create_plugin)
+ it "creates a GemGlobLoader" do
+ expect(Chef::Knife::SubcommandLoader.for_config(config_dir)).to be_a Chef::Knife::SubcommandLoader::GemGlobLoader
end
-
end
end
-
end
diff --git a/spec/unit/knife/core/ui_spec.rb b/spec/unit/knife/core/ui_spec.rb
index ac42ad6dd6..ab420518a3 100644
--- a/spec/unit/knife/core/ui_spec.rb
+++ b/spec/unit/knife/core/ui_spec.rb
@@ -368,6 +368,20 @@ EOM
@ui.config[:attribute] = "keys.keys"
expect(@ui.format_for_display(input)).to eq({ "sample-data-bag-item" => { "keys.keys" => "values" } })
end
+
+ it "should return the name attribute" do
+ allow_any_instance_of(Chef::Node).to receive(:name).and_return("chef.localdomain")
+ input = Chef::Node.new
+ @ui.config[:attribute] = "name"
+ expect(@ui.format_for_display(input)).to eq( {"chef.localdomain"=>{"name"=>"chef.localdomain"} })
+ end
+
+ it "returns nil when given an attribute path that isn't a name or attribute" do
+ input = { "keys" => {"keys" => "values"}, "hi" => "ho", "id" => "sample-data-bag-item" }
+ non_existing_path = "nope.nada.nothingtoseehere"
+ @ui.config[:attribute] = non_existing_path
+ expect(@ui.format_for_display(input)).to eq({ "sample-data-bag-item" => { non_existing_path => nil } })
+ end
end
describe "with --run-list passed" do
@@ -420,7 +434,7 @@ EOM
before(:each) do
stdout = double('StringIO', :tty? => true)
allow(@ui).to receive(:stdout).and_return(stdout)
- allow(Chef::Platform).to receive(:windows?) { true }
+ allow(ChefConfig).to receive(:windows?) { true }
Chef::Config.reset
end
diff --git a/spec/unit/knife/data_bag_from_file_spec.rb b/spec/unit/knife/data_bag_from_file_spec.rb
index 3882bff349..8b6502145c 100644
--- a/spec/unit/knife/data_bag_from_file_spec.rb
+++ b/spec/unit/knife/data_bag_from_file_spec.rb
@@ -26,7 +26,7 @@ Chef::Knife::DataBagFromFile.load_deps
describe Chef::Knife::DataBagFromFile do
before :each do
- allow(Chef::Platform).to receive(:windows?) { false }
+ allow(ChefConfig).to receive(:windows?) { false }
Chef::Config[:node_name] = "webmonkey.example.com"
FileUtils.mkdir_p([db_folder, db_folder2])
db_file.write(Chef::JSONCompat.to_json(plain_data))
diff --git a/spec/unit/knife/environment_from_file_spec.rb b/spec/unit/knife/environment_from_file_spec.rb
index d150e5ee64..11ad23c919 100644
--- a/spec/unit/knife/environment_from_file_spec.rb
+++ b/spec/unit/knife/environment_from_file_spec.rb
@@ -23,7 +23,7 @@ Chef::Knife::EnvironmentFromFile.load_deps
describe Chef::Knife::EnvironmentFromFile do
before(:each) do
- allow(Chef::Platform).to receive(:windows?) { false }
+ allow(ChefConfig).to receive(:windows?) { false }
@knife = Chef::Knife::EnvironmentFromFile.new
@stdout = StringIO.new
allow(@knife.ui).to receive(:stdout).and_return(@stdout)
diff --git a/spec/unit/knife/key_create_spec.rb b/spec/unit/knife/key_create_spec.rb
new file mode 100644
index 0000000000..5998e10274
--- /dev/null
+++ b/spec/unit/knife/key_create_spec.rb
@@ -0,0 +1,224 @@
+#
+# Author:: Tyler Cloke (<tyler@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'chef/knife/user_key_create'
+require 'chef/knife/client_key_create'
+require 'chef/knife/key_create'
+require 'chef/key'
+
+describe "key create commands that inherit knife" do
+ shared_examples_for "a key create command" do
+ let(:stderr) { StringIO.new }
+ let(:params) { [] }
+ let(:service_object) { instance_double(Chef::Knife::KeyCreate) }
+ let(:command) do
+ c = described_class.new([])
+ c.ui.config[:disable_editing] = true
+ allow(c.ui).to receive(:stderr).and_return(stderr)
+ allow(c.ui).to receive(:stdout).and_return(stderr)
+ allow(c).to receive(:show_usage)
+ c
+ end
+
+ context "after apply_params! is called with valid args" do
+ let(:params) { ["charmander"] }
+ before do
+ command.apply_params!(params)
+ end
+
+ context "when the service object is called" do
+ it "creates a new instance of Chef::Knife::KeyCreate with the correct args" do
+ expect(Chef::Knife::KeyCreate).to receive(:new).
+ with("charmander", command.actor_field_name, command.ui, command.config).
+ and_return(service_object)
+ command.service_object
+ end
+ end # when the service object is called
+ end # after apply_params! is called with valid args
+ end # a key create command
+
+ describe Chef::Knife::UserKeyCreate do
+ it_should_behave_like "a key create command"
+ # defined in key_helper.rb
+ it_should_behave_like "a knife key command" do
+ let(:service_object) { instance_double(Chef::Knife::KeyCreate) }
+ let(:params) { ["charmander"] }
+ end
+ end
+
+ describe Chef::Knife::ClientKeyCreate do
+ it_should_behave_like "a key create command"
+ # defined in key_helper.rb
+ it_should_behave_like "a knife key command" do
+ let(:service_object) { instance_double(Chef::Knife::KeyCreate) }
+ let(:params) { ["charmander"] }
+ end
+ end
+end
+
+describe Chef::Knife::KeyCreate do
+ let(:public_key) {
+ "-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvPo+oNPB7uuNkws0fC02
+KxSwdyqPLu0fhI1pOweNKAZeEIiEz2PkybathHWy8snSXGNxsITkf3eyvIIKa8OZ
+WrlqpI3yv/5DOP8HTMCxnFuMJQtDwMcevlqebX4bCxcByuBpNYDcAHjjfLGSfMjn
+E5lZpgYWwnpic4kSjYcL9ORK9nYvlWV9P/kCYmRhIjB4AhtpWRiOfY/TKi3P2LxT
+IjSmiN/ihHtlhV/VSnBJ5PzT/lRknlrJ4kACoz7Pq9jv+aAx5ft/xE9yDa2DYs0q
+Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
+0wIDAQAB
+-----END PUBLIC KEY-----"
+ }
+ let(:config) { Hash.new }
+ let(:actor) { "charmander" }
+ let(:ui) { instance_double("Chef::Knife::UI") }
+
+ shared_examples_for "key create run command" do
+ let(:key_create_object) {
+ described_class.new(actor, actor_field_name, ui, config)
+ }
+
+ context "when public_key and key_name weren't passed" do
+ it "raises a Chef::Exceptions::KeyCommandInputError with the proper error message" do
+ expect{ key_create_object.run }.to raise_error(Chef::Exceptions::KeyCommandInputError, key_create_object.public_key_or_key_name_error_msg)
+ end
+ end
+
+ context "when the command is run" do
+ let(:expected_hash) {
+ {
+ actor_field_name => "charmander"
+ }
+ }
+
+ before do
+ allow(File).to receive(:read).and_return(public_key)
+ allow(File).to receive(:expand_path)
+
+ allow(key_create_object).to receive(:output_private_key_to_file)
+ allow(key_create_object).to receive(:display_private_key)
+ allow(key_create_object).to receive(:edit_data).and_return(expected_hash)
+ allow(key_create_object).to receive(:create_key_from_hash).and_return(Chef::Key.from_hash(expected_hash))
+ allow(key_create_object).to receive(:display_info)
+ end
+
+ context "when a valid hash is passed" do
+ let(:key_name) { "charmander-key" }
+ let(:valid_expiration_date) { "2020-12-24T21:00:00Z" }
+ let(:expected_hash) {
+ {
+ actor_field_name => "charmander",
+ "public_key" => public_key,
+ "expiration_date" => valid_expiration_date,
+ "key_name" => key_name
+ }
+ }
+ before do
+ key_create_object.config[:public_key] = "public_key_path"
+ key_create_object.config[:expiration_Date] = valid_expiration_date,
+ key_create_object.config[:key_name] = key_name
+ end
+
+ it "creates the proper hash" do
+ expect(key_create_object).to receive(:create_key_from_hash).with(expected_hash)
+ key_create_object.run
+ end
+ end
+
+ context "when public_key is passed" do
+ let(:expected_hash) {
+ {
+ actor_field_name => "charmander",
+ "public_key" => public_key
+ }
+ }
+ before do
+ key_create_object.config[:public_key] = "public_key_path"
+ end
+
+ it "calls File.expand_path with the public_key input" do
+ expect(File).to receive(:expand_path).with("public_key_path")
+ key_create_object.run
+ end
+ end # when public_key is passed
+
+ context "when public_key isn't passed and key_name is" do
+ let(:expected_hash) {
+ {
+ actor_field_name => "charmander",
+ "name" => "charmander-key",
+ "create_key" => true
+ }
+ }
+ before do
+ key_create_object.config[:key_name] = "charmander-key"
+ end
+
+ it "should set create_key to true" do
+ expect(key_create_object).to receive(:create_key_from_hash).with(expected_hash)
+ key_create_object.run
+ end
+ end
+
+ context "when the server returns a private key" do
+ let(:expected_hash) {
+ {
+ actor_field_name => "charmander",
+ "public_key" => public_key,
+ "private_key" => "super_private"
+ }
+ }
+
+ before do
+ key_create_object.config[:public_key] = "public_key_path"
+ end
+
+ context "when file is not passed" do
+ it "calls display_private_key with the private_key" do
+ expect(key_create_object).to receive(:display_private_key).with("super_private")
+ key_create_object.run
+ end
+ end
+
+ context "when file is passed" do
+ before do
+ key_create_object.config[:file] = "/fake/file"
+ end
+
+ it "calls output_private_key_to_file with the private_key" do
+ expect(key_create_object).to receive(:output_private_key_to_file).with("super_private")
+ key_create_object.run
+ end
+ end
+ end # when the server returns a private key
+ end # when the command is run
+ end #key create run command"
+
+ context "when actor_field_name is 'user'" do
+ it_should_behave_like "key create run command" do
+ let(:actor_field_name) { "user" }
+ end
+ end
+
+ context "when actor_field_name is 'client'" do
+ it_should_behave_like "key create run command" do
+ let(:actor_field_name) { "client" }
+ end
+ end
+end
+
diff --git a/spec/unit/knife/key_delete_spec.rb b/spec/unit/knife/key_delete_spec.rb
new file mode 100644
index 0000000000..1d4b9f825f
--- /dev/null
+++ b/spec/unit/knife/key_delete_spec.rb
@@ -0,0 +1,135 @@
+#
+# Author:: Tyler Cloke (<tyler@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'chef/knife/user_key_delete'
+require 'chef/knife/client_key_delete'
+require 'chef/knife/key_delete'
+require 'chef/key'
+
+describe "key delete commands that inherit knife" do
+ shared_examples_for "a key delete command" do
+ let(:stderr) { StringIO.new }
+ let(:params) { [] }
+ let(:service_object) { instance_double(Chef::Knife::KeyDelete) }
+ let(:command) do
+ c = described_class.new([])
+ c.ui.config[:disable_editing] = true
+ allow(c.ui).to receive(:stderr).and_return(stderr)
+ allow(c.ui).to receive(:stdout).and_return(stderr)
+ allow(c).to receive(:show_usage)
+ c
+ end
+
+ context "after apply_params! is called with valid args" do
+ let(:params) { ["charmander", "charmander-key"] }
+ before do
+ command.apply_params!(params)
+ end
+
+ context "when the service object is called" do
+ it "creates a new instance of Chef::Knife::KeyDelete with the correct args" do
+ expect(Chef::Knife::KeyDelete).to receive(:new).
+ with("charmander-key", "charmander", command.actor_field_name, command.ui).
+ and_return(service_object)
+ command.service_object
+ end
+ end # when the service object is called
+ end # after apply_params! is called with valid args
+ end # a key delete command
+
+ describe Chef::Knife::UserKeyDelete do
+ it_should_behave_like "a key delete command"
+ # defined in key_helpers.rb
+ it_should_behave_like "a knife key command with a keyname as the second arg"
+ it_should_behave_like "a knife key command" do
+ let(:service_object) { instance_double(Chef::Knife::KeyDelete) }
+ let(:params) { ["charmander", "charmander-key"] }
+ end
+ end
+
+ describe Chef::Knife::ClientKeyDelete do
+ it_should_behave_like "a key delete command"
+ # defined in key_helpers.rb
+ it_should_behave_like "a knife key command with a keyname as the second arg"
+ it_should_behave_like "a knife key command" do
+ let(:service_object) { instance_double(Chef::Knife::KeyDelete) }
+ let(:params) { ["charmander", "charmander-key"] }
+ end
+ end
+end
+
+describe Chef::Knife::KeyDelete do
+ let(:actor) { "charmander" }
+ let(:keyname) { "charmander-key" }
+ let(:ui) { instance_double("Chef::Knife::UI") }
+
+ shared_examples_for "key delete run command" do
+ let(:key_delete_object) {
+ described_class.new(keyname, actor, actor_field_name, ui)
+ }
+
+ before do
+ allow_any_instance_of(Chef::Key).to receive(:destroy)
+ allow(key_delete_object).to receive(:print_destroyed)
+ allow(key_delete_object).to receive(:confirm!)
+ end
+
+ context "when the command is run" do
+ it "calls Chef::Key.new with the proper input" do
+ expect(Chef::Key).to receive(:new).with(actor, actor_field_name).and_call_original
+ key_delete_object.run
+ end
+
+ it "calls name on the Chef::Key instance with the proper input" do
+ expect_any_instance_of(Chef::Key).to receive(:name).with(keyname)
+ key_delete_object.run
+ end
+
+ it "calls destroy on the Chef::Key instance" do
+ expect_any_instance_of(Chef::Key).to receive(:destroy).once
+ key_delete_object.run
+ end
+
+ it "calls confirm!" do
+ expect(key_delete_object).to receive(:confirm!)
+ key_delete_object.run
+ end
+
+ it "calls print_destroyed" do
+ expect(key_delete_object).to receive(:print_destroyed)
+ key_delete_object.run
+ end
+ end # when the command is run
+
+
+ end # key delete run command
+
+ context "when actor_field_name is 'user'" do
+ it_should_behave_like "key delete run command" do
+ let(:actor_field_name) { "user" }
+ end
+ end
+
+ context "when actor_field_name is 'client'" do
+ it_should_behave_like "key delete run command" do
+ let(:actor_field_name) { "client" }
+ end
+ end
+end
+
diff --git a/spec/unit/knife/key_edit_spec.rb b/spec/unit/knife/key_edit_spec.rb
new file mode 100644
index 0000000000..538b91de2d
--- /dev/null
+++ b/spec/unit/knife/key_edit_spec.rb
@@ -0,0 +1,267 @@
+#
+# Author:: Tyler Cloke (<tyler@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'chef/knife/user_key_edit'
+require 'chef/knife/client_key_edit'
+require 'chef/knife/key_edit'
+require 'chef/key'
+
+describe "key edit commands that inherit knife" do
+ shared_examples_for "a key edit command" do
+ let(:stderr) { StringIO.new }
+ let(:params) { [] }
+ let(:service_object) { instance_double(Chef::Knife::KeyEdit) }
+ let(:command) do
+ c = described_class.new([])
+ c.ui.config[:disable_editing] = true
+ allow(c.ui).to receive(:stderr).and_return(stderr)
+ allow(c.ui).to receive(:stdout).and_return(stderr)
+ allow(c).to receive(:show_usage)
+ c
+ end
+
+ context "after apply_params! is called with valid args" do
+ let(:params) { ["charmander", "charmander-key"] }
+ before do
+ command.apply_params!(params)
+ end
+
+ context "when the service object is called" do
+ it "creates a new instance of Chef::Knife::KeyEdit with the correct args" do
+ expect(Chef::Knife::KeyEdit).to receive(:new).
+ with("charmander-key", "charmander", command.actor_field_name, command.ui, command.config).
+ and_return(service_object)
+ command.service_object
+ end
+ end # when the service object is called
+ end # after apply_params! is called with valid args
+ end # a key edit command
+
+ describe Chef::Knife::UserKeyEdit do
+ it_should_behave_like "a key edit command"
+ # defined in key_helpers.rb
+ it_should_behave_like "a knife key command with a keyname as the second arg"
+ it_should_behave_like "a knife key command" do
+ let(:service_object) { instance_double(Chef::Knife::KeyEdit) }
+ let(:params) { ["charmander", "charmander-key"] }
+ end
+ end
+
+ describe Chef::Knife::ClientKeyEdit do
+ it_should_behave_like "a key edit command"
+ # defined in key_helpers.rb
+ it_should_behave_like "a knife key command with a keyname as the second arg"
+ it_should_behave_like "a knife key command" do
+ let(:service_object) { instance_double(Chef::Knife::KeyEdit) }
+ let(:params) { ["charmander", "charmander-key"] }
+ end
+ end
+end
+
+describe Chef::Knife::KeyEdit do
+ let(:public_key) {
+ "-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvPo+oNPB7uuNkws0fC02
+KxSwdyqPLu0fhI1pOweNKAZeEIiEz2PkybathHWy8snSXGNxsITkf3eyvIIKa8OZ
+WrlqpI3yv/5DOP8HTMCxnFuMJQtDwMcevlqebX4bCxcByuBpNYDcAHjjfLGSfMjn
+E5lZpgYWwnpic4kSjYcL9ORK9nYvlWV9P/kCYmRhIjB4AhtpWRiOfY/TKi3P2LxT
+IjSmiN/ihHtlhV/VSnBJ5PzT/lRknlrJ4kACoz7Pq9jv+aAx5ft/xE9yDa2DYs0q
+Tfuc9dUYsFjptWYrV6pfEQ+bgo1OGBXORBFcFL+2D7u9JYquKrMgosznHoEkQNLo
+0wIDAQAB
+-----END PUBLIC KEY-----"
+ }
+ let(:config) { Hash.new }
+ let(:actor) { "charmander" }
+ let(:keyname) { "charmander-key" }
+ let(:ui) { instance_double("Chef::Knife::UI") }
+
+ shared_examples_for "key edit run command" do
+ let(:key_edit_object) {
+ described_class.new(keyname, actor, actor_field_name, ui, config)
+ }
+
+ context "when the command is run" do
+ let(:expected_hash) {
+ {
+ actor_field_name => "charmander"
+ }
+ }
+ let(:new_keyname) { "charizard-key" }
+
+ before do
+ allow(File).to receive(:read).and_return(public_key)
+ allow(File).to receive(:expand_path)
+
+ allow(key_edit_object).to receive(:output_private_key_to_file)
+ allow(key_edit_object).to receive(:display_private_key)
+ allow(key_edit_object).to receive(:edit_data).and_return(expected_hash)
+ allow(key_edit_object).to receive(:display_info)
+ end
+
+
+ context "when public_key and create_key are passed" do
+ before do
+ key_edit_object.config[:public_key] = "public_key_path"
+ key_edit_object.config[:create_key] = true
+ end
+
+ it "raises a Chef::Exceptions::KeyCommandInputError with the proper error message" do
+ expect{ key_edit_object.run }.to raise_error(Chef::Exceptions::KeyCommandInputError, key_edit_object.public_key_and_create_key_error_msg)
+ end
+ end
+
+ context "when key_name is passed" do
+ let(:expected_hash) {
+ {
+ actor_field_name => "charmander",
+ "name" => new_keyname
+ }
+ }
+ before do
+ key_edit_object.config[:key_name] = new_keyname
+ allow_any_instance_of(Chef::Key).to receive(:update)
+ end
+
+ it "update_key_from_hash gets passed a hash with new key name" do
+ expect(key_edit_object).to receive(:update_key_from_hash).with(expected_hash).and_return(Chef::Key.from_hash(expected_hash))
+ key_edit_object.run
+ end
+
+ it "Chef::Key.update is passed a string containing the original keyname" do
+ expect_any_instance_of(Chef::Key).to receive(:update).with(/#{keyname}/).and_return(Chef::Key.from_hash(expected_hash))
+ key_edit_object.run
+ end
+
+ it "Chef::Key.update is not passed a string containing the new keyname" do
+ expect_any_instance_of(Chef::Key).not_to receive(:update).with(/#{new_keyname}/)
+ allow_any_instance_of(Chef::Key).to receive(:update).and_return(Chef::Key.from_hash(expected_hash))
+ key_edit_object.run
+ end
+ end
+
+ context "when public_key, key_name, and expiration_date are passed" do
+ let(:expected_hash) {
+ {
+ actor_field_name => "charmander",
+ "public_key" => public_key,
+ "name" => new_keyname,
+ "expiration_date" => "infinity"
+ }
+ }
+ before do
+ key_edit_object.config[:public_key] = "this-public-key"
+ key_edit_object.config[:key_name] = new_keyname
+ key_edit_object.config[:expiration_date] = "infinity"
+ allow(key_edit_object).to receive(:update_key_from_hash).and_return(Chef::Key.from_hash(expected_hash))
+ end
+
+ it "passes the right hash to update_key_from_hash" do
+ expect(key_edit_object).to receive(:update_key_from_hash).with(expected_hash)
+ key_edit_object.run
+ end
+ end
+
+ context "when create_key is passed" do
+ let(:expected_hash) {
+ {
+ actor_field_name => "charmander",
+ "create_key" => true
+ }
+ }
+
+ before do
+ key_edit_object.config[:create_key] = true
+ allow(key_edit_object).to receive(:update_key_from_hash).and_return(Chef::Key.from_hash(expected_hash))
+ end
+
+ it "passes the right hash to update_key_from_hash" do
+ expect(key_edit_object).to receive(:update_key_from_hash).with(expected_hash)
+ key_edit_object.run
+ end
+ end
+
+ context "when public_key is passed" do
+ let(:expected_hash) {
+ {
+ actor_field_name => "charmander",
+ "public_key" => public_key
+ }
+ }
+ before do
+ allow(key_edit_object).to receive(:update_key_from_hash).and_return(Chef::Key.from_hash(expected_hash))
+ key_edit_object.config[:public_key] = "public_key_path"
+ end
+
+ it "calls File.expand_path with the public_key input" do
+ expect(File).to receive(:expand_path).with("public_key_path")
+ key_edit_object.run
+ end
+ end # when public_key is passed
+
+ context "when the server returns a private key" do
+ let(:expected_hash) {
+ {
+ actor_field_name => "charmander",
+ "public_key" => public_key,
+ "private_key" => "super_private"
+ }
+ }
+
+ before do
+ allow(key_edit_object).to receive(:update_key_from_hash).and_return(Chef::Key.from_hash(expected_hash))
+ key_edit_object.config[:public_key] = "public_key_path"
+ end
+
+ context "when file is not passed" do
+ it "calls display_private_key with the private_key" do
+ expect(key_edit_object).to receive(:display_private_key).with("super_private")
+ key_edit_object.run
+ end
+ end
+
+ context "when file is passed" do
+ before do
+ key_edit_object.config[:file] = "/fake/file"
+ end
+
+ it "calls output_private_key_to_file with the private_key" do
+ expect(key_edit_object).to receive(:output_private_key_to_file).with("super_private")
+ key_edit_object.run
+ end
+ end
+ end # when the server returns a private key
+
+ end # when the command is run
+
+
+
+ end # key edit run command
+
+ context "when actor_field_name is 'user'" do
+ it_should_behave_like "key edit run command" do
+ let(:actor_field_name) { "user" }
+ end
+ end
+
+ context "when actor_field_name is 'client'" do
+ it_should_behave_like "key edit run command" do
+ let(:actor_field_name) { "client" }
+ end
+ end
+end
diff --git a/spec/unit/knife/key_helper.rb b/spec/unit/knife/key_helper.rb
new file mode 100644
index 0000000000..36ababc09a
--- /dev/null
+++ b/spec/unit/knife/key_helper.rb
@@ -0,0 +1,74 @@
+#
+# Author:: Tyler Cloke (<tyler@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+shared_examples_for "a knife key command" do
+ let(:stderr) { StringIO.new }
+ let(:params) { [] }
+ let(:command) do
+ c = described_class.new([])
+ c.ui.config[:disable_editing] = true
+ allow(c.ui).to receive(:stderr).and_return(stderr)
+ allow(c.ui).to receive(:stdout).and_return(stderr)
+ allow(c).to receive(:show_usage)
+ c
+ end
+
+ context "before apply_params! is called" do
+ context "when apply_params! is called with invalid args" do
+ it "shows the usage" do
+ expect(command).to receive(:show_usage)
+ expect { command.apply_params!(params) }.to exit_with_code(1)
+ end
+
+ it "outputs the proper error" do
+ expect { command.apply_params!(params) }.to exit_with_code(1)
+ expect(stderr.string).to include(command.actor_missing_error)
+ end
+
+ it "exits 1" do
+ expect { command.apply_params!(params) }.to exit_with_code(1)
+ end
+ end
+ end # before apply_params! is called
+
+ context "after apply_params! is called with valid args" do
+ let(:params) { ["charmander"] }
+ before do
+ command.apply_params!(params)
+ end
+
+ it "properly defines the actor" do
+ expect(command.actor).to eq("charmander")
+ end
+ end # after apply_params! is called with valid args
+
+ context "when the command is run" do
+ before do
+ allow(command).to receive(:service_object).and_return(service_object)
+ allow(command).to receive(:name_args).and_return(["charmander"])
+ end
+
+ context "when the command is successful" do
+ before do
+ expect(service_object).to receive(:run)
+ end
+ end
+ end
+end # a knife key command
diff --git a/spec/unit/knife/key_list_spec.rb b/spec/unit/knife/key_list_spec.rb
new file mode 100644
index 0000000000..aabe02ac02
--- /dev/null
+++ b/spec/unit/knife/key_list_spec.rb
@@ -0,0 +1,216 @@
+#
+# Author:: Tyler Cloke (<tyler@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'chef/knife/user_key_list'
+require 'chef/knife/client_key_list'
+require 'chef/knife/key_list'
+require 'chef/key'
+
+describe "key list commands that inherit knife" do
+ shared_examples_for "a key list command" do
+ let(:stderr) { StringIO.new }
+ let(:params) { [] }
+ let(:service_object) { instance_double(Chef::Knife::KeyList) }
+ let(:command) do
+ c = described_class.new([])
+ c.ui.config[:disable_editing] = true
+ allow(c.ui).to receive(:stderr).and_return(stderr)
+ allow(c.ui).to receive(:stdout).and_return(stderr)
+ allow(c).to receive(:show_usage)
+ c
+ end
+
+ context "after apply_params! is called with valid args" do
+ let(:params) { ["charmander"] }
+ before do
+ command.apply_params!(params)
+ end
+
+ context "when the service object is called" do
+ it "creates a new instance of Chef::Knife::KeyList with the correct args" do
+ expect(Chef::Knife::KeyList).to receive(:new).
+ with("charmander", command.list_method, command.ui, command.config).
+ and_return(service_object)
+ command.service_object
+ end
+ end # when the service object is called
+ end # after apply_params! is called with valid args
+ end # a key list command
+
+ describe Chef::Knife::UserKeyList do
+ it_should_behave_like "a key list command"
+ # defined in key_helpers.rb
+ it_should_behave_like "a knife key command" do
+ let(:service_object) { instance_double(Chef::Knife::KeyList) }
+ let(:params) { ["charmander"] }
+ end
+ end
+
+ describe Chef::Knife::ClientKeyList do
+ it_should_behave_like "a key list command"
+ # defined in key_helpers.rb
+ it_should_behave_like "a knife key command" do
+ let(:service_object) { instance_double(Chef::Knife::KeyList) }
+ let(:params) { ["charmander"] }
+ end
+ end
+end
+
+describe Chef::Knife::KeyList do
+ let(:config) { Hash.new }
+ let(:actor) { "charmander" }
+ let(:ui) { instance_double("Chef::Knife::UI") }
+
+ shared_examples_for "key list run command" do
+ let(:key_list_object) {
+ described_class.new(actor, list_method, ui, config)
+ }
+
+ before do
+ allow(Chef::Key).to receive(list_method).and_return(http_response)
+ allow(key_list_object).to receive(:display_info)
+ # simply pass the string though that colorize takes in
+ allow(key_list_object).to receive(:colorize).with(kind_of(String)) do |input|
+ input
+ end
+ end
+
+ context "when only_expired and only_non_expired were both passed" do
+ before do
+ key_list_object.config[:only_expired] = true
+ key_list_object.config[:only_non_expired] = true
+ end
+
+ it "raises a Chef::Exceptions::KeyCommandInputError with the proper error message" do
+ expect{ key_list_object.run }.to raise_error(Chef::Exceptions::KeyCommandInputError, key_list_object.expired_and_non_expired_msg)
+ end
+ end
+
+ context "when the command is run" do
+ before do
+ key_list_object.config[:only_expired] = false
+ key_list_object.config[:only_non_expired] = false
+ key_list_object.config[:with_details] = false
+ end
+
+ it "calls Chef::Key with the proper list command and input" do
+ expect(Chef::Key).to receive(list_method).with(actor)
+ key_list_object.run
+ end
+
+ it "displays all the keys" do
+ expect(key_list_object).to receive(:display_info).with(/non-expired/).twice
+ expect(key_list_object).to receive(:display_info).with(/out-of-date/).once
+ key_list_object.run
+ end
+
+ context "when only_expired is called" do
+ before do
+ key_list_object.config[:only_expired] = true
+ end
+
+ it "excludes displaying non-expired keys" do
+ expect(key_list_object).to receive(:display_info).with(/non-expired/).exactly(0).times
+ key_list_object.run
+ end
+
+ it "displays the expired keys" do
+ expect(key_list_object).to receive(:display_info).with(/out-of-date/).once
+ key_list_object.run
+ end
+ end # when only_expired is called
+
+ context "when only_non_expired is called" do
+ before do
+ key_list_object.config[:only_non_expired] = true
+ end
+
+ it "excludes displaying expired keys" do
+ expect(key_list_object).to receive(:display_info).with(/out-of-date/).exactly(0).times
+ key_list_object.run
+ end
+
+ it "displays the non-expired keys" do
+ expect(key_list_object).to receive(:display_info).with(/non-expired/).twice
+ key_list_object.run
+ end
+ end # when only_expired is called
+
+ context "when with_details is false" do
+ before do
+ key_list_object.config[:with_details] = false
+ end
+
+ it "does not display the uri" do
+ expect(key_list_object).to receive(:display_info).with(/https/).exactly(0).times
+ key_list_object.run
+ end
+
+ it "does not display the expired status" do
+ expect(key_list_object).to receive(:display_info).with(/\(expired\)/).exactly(0).times
+ key_list_object.run
+ end
+ end # when with_details is false
+
+ context "when with_details is true" do
+ before do
+ key_list_object.config[:with_details] = true
+ end
+
+ it "displays the uri" do
+ expect(key_list_object).to receive(:display_info).with(/https/).exactly(3).times
+ key_list_object.run
+ end
+
+ it "displays the expired status" do
+ expect(key_list_object).to receive(:display_info).with(/\(expired\)/).once
+ key_list_object.run
+ end
+ end # when with_details is true
+
+ end # when the command is run
+
+ end # key list run command
+
+ context "when list_method is :list_by_user" do
+ it_should_behave_like "key list run command" do
+ let(:list_method) { :list_by_user }
+ let(:http_response) {
+ [
+ {"uri"=>"https://api.opscode.piab/users/charmander/keys/non-expired1", "name"=>"non-expired1", "expired"=>false},
+ {"uri"=>"https://api.opscode.piab/users/charmander/keys/non-expired2", "name"=>"non-expired2", "expired"=>false},
+ {"uri"=>"https://api.opscode.piab/users/mary/keys/out-of-date", "name"=>"out-of-date", "expired"=>true}
+ ]
+ }
+ end
+ end
+
+ context "when list_method is :list_by_client" do
+ it_should_behave_like "key list run command" do
+ let(:list_method) { :list_by_client }
+ let(:http_response) {
+ [
+ {"uri"=>"https://api.opscode.piab/organizations/pokemon/clients/charmander/keys/non-expired1", "name"=>"non-expired1", "expired"=>false},
+ {"uri"=>"https://api.opscode.piab/organizations/pokemon/clients/charmander/keys/non-expired2", "name"=>"non-expired2", "expired"=>false},
+ {"uri"=>"https://api.opscode.piab/organizations/pokemon/clients/mary/keys/out-of-date", "name"=>"out-of-date", "expired"=>true}
+ ]
+ }
+ end
+ end
+end
diff --git a/spec/unit/knife/key_show_spec.rb b/spec/unit/knife/key_show_spec.rb
new file mode 100644
index 0000000000..5a0d839e4f
--- /dev/null
+++ b/spec/unit/knife/key_show_spec.rb
@@ -0,0 +1,126 @@
+#
+# Author:: Tyler Cloke (<tyler@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'chef/knife/user_key_show'
+require 'chef/knife/client_key_show'
+require 'chef/knife/key_show'
+require 'chef/key'
+
+describe "key show commands that inherit knife" do
+ shared_examples_for "a key show command" do
+ let(:stderr) { StringIO.new }
+ let(:params) { [] }
+ let(:service_object) { instance_double(Chef::Knife::KeyShow) }
+ let(:command) do
+ c = described_class.new([])
+ c.ui.config[:disable_editing] = true
+ allow(c.ui).to receive(:stderr).and_return(stderr)
+ allow(c.ui).to receive(:stdout).and_return(stderr)
+ allow(c).to receive(:show_usage)
+ c
+ end
+
+ context "after apply_params! is called with valid args" do
+ let(:params) { ["charmander", "charmander-key"] }
+ before do
+ command.apply_params!(params)
+ end
+
+ context "when the service object is called" do
+ it "creates a new instance of Chef::Knife::KeyShow with the correct args" do
+ expect(Chef::Knife::KeyShow).to receive(:new).
+ with("charmander-key", "charmander", command.load_method, command.ui).
+ and_return(service_object)
+ command.service_object
+ end
+ end # when the service object is called
+ end # after apply_params! is called with valid args
+ end # a key show command
+
+ describe Chef::Knife::UserKeyShow do
+ it_should_behave_like "a key show command"
+ # defined in key_helpers.rb
+ it_should_behave_like "a knife key command with a keyname as the second arg"
+ it_should_behave_like "a knife key command" do
+ let(:service_object) { instance_double(Chef::Knife::KeyShow) }
+ let(:params) { ["charmander", "charmander-key"] }
+ end
+ end
+
+ describe Chef::Knife::ClientKeyShow do
+ it_should_behave_like "a key show command"
+ # defined in key_helpers.rb
+ it_should_behave_like "a knife key command with a keyname as the second arg"
+ it_should_behave_like "a knife key command" do
+ let(:service_object) { instance_double(Chef::Knife::KeyShow) }
+ let(:params) { ["charmander", "charmander-key"] }
+ end
+ end
+end
+
+describe Chef::Knife::KeyShow do
+ let(:actor) { "charmander" }
+ let(:keyname) { "charmander" }
+ let(:ui) { instance_double("Chef::Knife::UI") }
+ let(:expected_hash) {
+ {
+ actor_field_name => "charmander",
+ "name" => "charmander-key",
+ "public_key" => "some-public-key",
+ "expiration_date" => "infinity"
+ }
+ }
+
+ shared_examples_for "key show run command" do
+ let(:key_show_object) {
+ described_class.new(keyname, actor, load_method, ui)
+ }
+
+ before do
+ allow(key_show_object).to receive(:display_output)
+ allow(Chef::Key).to receive(load_method).and_return(Chef::Key.from_hash(expected_hash))
+ end
+
+ context "when the command is run" do
+ it "loads the key using the proper method and args" do
+ expect(Chef::Key).to receive(load_method).with(actor, keyname)
+ key_show_object.run
+ end
+
+ it "displays the key" do
+ expect(key_show_object).to receive(:display_output)
+ key_show_object.run
+ end
+ end
+ end
+
+ context "when load_method is :load_by_user" do
+ it_should_behave_like "key show run command" do
+ let(:load_method) { :load_by_user }
+ let(:actor_field_name) { 'user' }
+ end
+ end
+
+ context "when load_method is :load_by_client" do
+ it_should_behave_like "key show run command" do
+ let(:load_method) { :load_by_client }
+ let(:actor_field_name) { 'user' }
+ end
+ end
+end
diff --git a/spec/unit/knife/osc_user_create_spec.rb b/spec/unit/knife/osc_user_create_spec.rb
new file mode 100644
index 0000000000..e4ed78fe2b
--- /dev/null
+++ b/spec/unit/knife/osc_user_create_spec.rb
@@ -0,0 +1,93 @@
+#
+# Author:: Steven Danna (<steve@opscode.com>)
+# Copyright:: Copyright (c) 2012 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+Chef::Knife::OscUserCreate.load_deps
+
+# DEPRECATION NOTE
+# This code only remains to support users still operating with
+# Open Source Chef Server 11 and should be removed once support
+# for OSC 11 ends. New development should occur in user_create_spec.rb.
+
+describe Chef::Knife::OscUserCreate do
+ before(:each) do
+ @knife = Chef::Knife::OscUserCreate.new
+
+ @stdout = StringIO.new
+ @stderr = StringIO.new
+ allow(@knife.ui).to receive(:stdout).and_return(@stdout)
+ allow(@knife.ui).to receive(:stderr).and_return(@stderr)
+
+ @knife.name_args = [ 'a_user' ]
+ @knife.config[:user_password] = "foobar"
+ @user = Chef::User.new
+ @user.name "a_user"
+ @user_with_private_key = Chef::User.new
+ @user_with_private_key.name "a_user"
+ @user_with_private_key.private_key 'private_key'
+ allow(@user).to receive(:create).and_return(@user_with_private_key)
+ allow(Chef::User).to receive(:new).and_return(@user)
+ allow(Chef::User).to receive(:from_hash).and_return(@user)
+ allow(@knife).to receive(:edit_data).and_return(@user.to_hash)
+ end
+
+ it "creates a new user" do
+ expect(Chef::User).to receive(:new).and_return(@user)
+ expect(@user).to receive(:create)
+ @knife.run
+ expect(@stderr.string).to match /created user.+a_user/i
+ end
+
+ it "sets the password" do
+ @knife.config[:user_password] = "a_password"
+ expect(@user).to receive(:password).with("a_password")
+ @knife.run
+ end
+
+ it "exits with an error if password is blank" do
+ @knife.config[:user_password] = ''
+ expect { @knife.run }.to raise_error SystemExit
+ expect(@stderr.string).to match /You must specify a non-blank password/
+ end
+
+ it "sets the user name" do
+ expect(@user).to receive(:name).with("a_user")
+ @knife.run
+ end
+
+ it "sets the public key if given" do
+ @knife.config[:user_key] = "/a/filename"
+ allow(File).to receive(:read).with(File.expand_path("/a/filename")).and_return("a_key")
+ expect(@user).to receive(:public_key).with("a_key")
+ @knife.run
+ end
+
+ it "allows you to edit the data" do
+ expect(@knife).to receive(:edit_data).with(@user)
+ @knife.run
+ end
+
+ it "writes the private key to a file when --file is specified" do
+ @knife.config[:file] = "/tmp/a_file"
+ filehandle = double("filehandle")
+ expect(filehandle).to receive(:print).with('private_key')
+ expect(File).to receive(:open).with("/tmp/a_file", "w").and_yield(filehandle)
+ @knife.run
+ end
+end
diff --git a/spec/unit/knife/osc_user_delete_spec.rb b/spec/unit/knife/osc_user_delete_spec.rb
new file mode 100644
index 0000000000..4a3ec4228f
--- /dev/null
+++ b/spec/unit/knife/osc_user_delete_spec.rb
@@ -0,0 +1,44 @@
+#
+# Author:: Steven Danna (<steve@opscode.com>)
+# Copyright:: Copyright (c) 2012 Opscode, Inc
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+# DEPRECATION NOTE
+# This code only remains to support users still operating with
+# Open Source Chef Server 11 and should be removed once support
+# for OSC 11 ends. New development should occur in user_delete_spec.rb.
+
+describe Chef::Knife::OscUserDelete do
+ before(:each) do
+ Chef::Knife::OscUserDelete.load_deps
+ @knife = Chef::Knife::OscUserDelete.new
+ @knife.name_args = [ 'my_user' ]
+ end
+
+ it 'deletes the user' do
+ expect(@knife).to receive(:delete_object).with(Chef::User, 'my_user')
+ @knife.run
+ end
+
+ it 'prints usage and exits when a user name is not provided' do
+ @knife.name_args = []
+ expect(@knife).to receive(:show_usage)
+ expect(@knife.ui).to receive(:fatal)
+ expect { @knife.run }.to raise_error(SystemExit)
+ end
+end
diff --git a/spec/unit/knife/osc_user_edit_spec.rb b/spec/unit/knife/osc_user_edit_spec.rb
new file mode 100644
index 0000000000..279f2e30ef
--- /dev/null
+++ b/spec/unit/knife/osc_user_edit_spec.rb
@@ -0,0 +1,52 @@
+#
+# Author:: Steven Danna (<steve@opscode.com>)
+# Copyright:: Copyright (c) 2012 Opscode, Inc
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+# DEPRECATION NOTE
+# This code only remains to support users still operating with
+# Open Source Chef Server 11 and should be removed once support
+# for OSC 11 ends. New development should occur in user_edit_spec.rb.
+
+describe Chef::Knife::OscUserEdit do
+ before(:each) do
+ @stderr = StringIO.new
+ @stdout = StringIO.new
+
+ Chef::Knife::OscUserEdit.load_deps
+ @knife = Chef::Knife::OscUserEdit.new
+ allow(@knife.ui).to receive(:stderr).and_return(@stderr)
+ allow(@knife.ui).to receive(:stdout).and_return(@stdout)
+ @knife.name_args = [ 'my_user' ]
+ @knife.config[:disable_editing] = true
+ end
+
+ it 'loads and edits the user' do
+ data = { :name => "my_user" }
+ allow(Chef::User).to receive(:load).with("my_user").and_return(data)
+ expect(@knife).to receive(:edit_data).with(data).and_return(data)
+ @knife.run
+ end
+
+ it 'prints usage and exits when a user name is not provided' do
+ @knife.name_args = []
+ expect(@knife).to receive(:show_usage)
+ expect(@knife.ui).to receive(:fatal)
+ expect { @knife.run }.to raise_error(SystemExit)
+ end
+end
diff --git a/spec/unit/provider/powershell_spec.rb b/spec/unit/knife/osc_user_list_spec.rb
index 60dbcf80b0..f496a414b8 100644
--- a/spec/unit/provider/powershell_spec.rb
+++ b/spec/unit/knife/osc_user_list_spec.rb
@@ -1,6 +1,6 @@
#
-# Author:: Adam Edwards (<adamed@opscode.com>)
-# Copyright:: Copyright (c) 2013 Opscode, Inc.
+# Author:: Steven Danna
+# Copyright:: Copyright (c) 2012 Opscode, Inc
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,22 +17,21 @@
#
require 'spec_helper'
-describe Chef::Provider::PowershellScript, "action_run" do
- before(:each) do
- @node = Chef::Node.new
-
- @node.default["kernel"] = Hash.new
- @node.default["kernel"][:machine] = :x86_64.to_s
-
- @run_context = Chef::RunContext.new(@node, {}, @events)
- @new_resource = Chef::Resource::PowershellScript.new('run some powershell code', @run_context)
+# DEPRECATION NOTE
+# This code only remains to support users still operating with
+# Open Source Chef Server 11 and should be removed once support
+# for OSC 11 ends. New development should occur in user_list_spec.rb.
- @provider = Chef::Provider::PowershellScript.new(@new_resource, @run_context)
+describe Chef::Knife::OscUserList do
+ before(:each) do
+ Chef::Knife::OscUserList.load_deps
+ @knife = Chef::Knife::OscUserList.new
end
- it "should set the -File flag as the last flag" do
- expect(@provider.flags.split(' ').pop).to eq("-File")
+ it 'lists the users' do
+ expect(Chef::User).to receive(:list)
+ expect(@knife).to receive(:format_list_for_display)
+ @knife.run
end
-
end
diff --git a/spec/unit/knife/osc_user_reregister_spec.rb b/spec/unit/knife/osc_user_reregister_spec.rb
new file mode 100644
index 0000000000..989eb180f1
--- /dev/null
+++ b/spec/unit/knife/osc_user_reregister_spec.rb
@@ -0,0 +1,58 @@
+#
+# Author:: Steven Danna (<steve@opscode.com>)
+# Copyright:: Copyright (c) 2012 Opscode, Inc
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+# DEPRECATION NOTE
+# This code only remains to support users still operating with
+# Open Source Chef Server 11 and should be removed once support
+# for OSC 11 ends. New development should occur in user_reregister_spec.rb.
+
+describe Chef::Knife::OscUserReregister do
+ before(:each) do
+ Chef::Knife::OscUserReregister.load_deps
+ @knife = Chef::Knife::OscUserReregister.new
+ @knife.name_args = [ 'a_user' ]
+ @user_mock = double('user_mock', :private_key => "private_key")
+ allow(Chef::User).to receive(:load).and_return(@user_mock)
+ @stdout = StringIO.new
+ allow(@knife.ui).to receive(:stdout).and_return(@stdout)
+ end
+
+ it 'prints usage and exits when a user name is not provided' do
+ @knife.name_args = []
+ expect(@knife).to receive(:show_usage)
+ expect(@knife.ui).to receive(:fatal)
+ expect { @knife.run }.to raise_error(SystemExit)
+ end
+
+ it 'reregisters the user and prints the key' do
+ expect(@user_mock).to receive(:reregister).and_return(@user_mock)
+ @knife.run
+ expect(@stdout.string).to match( /private_key/ )
+ end
+
+ it 'writes the private key to a file when --file is specified' do
+ expect(@user_mock).to receive(:reregister).and_return(@user_mock)
+ @knife.config[:file] = '/tmp/a_file'
+ filehandle = StringIO.new
+ expect(File).to receive(:open).with('/tmp/a_file', 'w').and_yield(filehandle)
+ @knife.run
+ expect(filehandle.string).to eq("private_key")
+ end
+end
diff --git a/spec/unit/knife/osc_user_show_spec.rb b/spec/unit/knife/osc_user_show_spec.rb
new file mode 100644
index 0000000000..18d2086099
--- /dev/null
+++ b/spec/unit/knife/osc_user_show_spec.rb
@@ -0,0 +1,46 @@
+#
+# Author:: Steven Danna (<steve@opscode.com>)
+# Copyright:: Copyright (c) 2012 Opscode, Inc
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+# DEPRECATION NOTE
+# This code only remains to support users still operating with
+# Open Source Chef Server 11 and should be removed once support
+# for OSC 11 ends. New development should occur user_show_spec.rb.
+
+describe Chef::Knife::OscUserShow do
+ before(:each) do
+ Chef::Knife::OscUserShow.load_deps
+ @knife = Chef::Knife::OscUserShow.new
+ @knife.name_args = [ 'my_user' ]
+ @user_mock = double('user_mock')
+ end
+
+ it 'loads and displays the user' do
+ expect(Chef::User).to receive(:load).with('my_user').and_return(@user_mock)
+ expect(@knife).to receive(:format_for_display).with(@user_mock)
+ @knife.run
+ end
+
+ it 'prints usage and exits when a user name is not provided' do
+ @knife.name_args = []
+ expect(@knife).to receive(:show_usage)
+ expect(@knife.ui).to receive(:fatal)
+ expect { @knife.run }.to raise_error(SystemExit)
+ end
+end
diff --git a/spec/unit/knife/ssh_spec.rb b/spec/unit/knife/ssh_spec.rb
index 501b02c933..723280bead 100644
--- a/spec/unit/knife/ssh_spec.rb
+++ b/spec/unit/knife/ssh_spec.rb
@@ -28,10 +28,10 @@ describe Chef::Knife::Ssh do
before do
@knife = Chef::Knife::Ssh.new
@knife.merge_configs
- @knife.config[:attribute] = "fqdn"
@node_foo = Chef::Node.new
@node_foo.automatic_attrs[:fqdn] = "foo.example.org"
@node_foo.automatic_attrs[:ipaddress] = "10.0.0.1"
+
@node_bar = Chef::Node.new
@node_bar.automatic_attrs[:fqdn] = "bar.example.org"
@node_bar.automatic_attrs[:ipaddress] = "10.0.0.2"
@@ -52,15 +52,15 @@ describe Chef::Knife::Ssh do
def self.should_return_specified_attributes
it "returns an array of the attributes specified on the command line OR config file, if only one is set" do
@knife.config[:attribute] = "ipaddress"
- @knife.config[:attribute_from_cli] = "ipaddress"
+ Chef::Config[:knife][:ssh_attribute] = "ipaddress" # this value will be in the config file
configure_query([@node_foo, @node_bar])
expect(@knife).to receive(:session_from_list).with([['10.0.0.1', nil], ['10.0.0.2', nil]])
@knife.configure_session
end
it "returns an array of the attributes specified on the command line even when a config value is set" do
- @knife.config[:attribute] = "config_file" # this value will be the config file
- @knife.config[:attribute_from_cli] = "ipaddress" # this is the value of the command line via #configure_attribute
+ Chef::Config[:knife][:ssh_attribute] = "config_file" # this value will be in the config file
+ @knife.config[:attribute] = "ipaddress" # this is the value of the command line via #configure_attribute
configure_query([@node_foo, @node_bar])
expect(@knife).to receive(:session_from_list).with([['10.0.0.1', nil], ['10.0.0.2', nil]])
@knife.configure_session
@@ -83,7 +83,6 @@ describe Chef::Knife::Ssh do
@node_foo.automatic_attrs[:cloud][:public_hostname] = "ec2-10-0-0-1.compute-1.amazonaws.com"
@node_bar.automatic_attrs[:cloud][:public_hostname] = "ec2-10-0-0-2.compute-1.amazonaws.com"
end
-
it "returns an array of cloud public hostnames" do
configure_query([@node_foo, @node_bar])
expect(@knife).to receive(:session_from_list).with([
@@ -96,6 +95,24 @@ describe Chef::Knife::Ssh do
should_return_specified_attributes
end
+ context "when cloud hostnames are available but empty" do
+ before do
+ @node_foo.automatic_attrs[:cloud][:public_hostname] = ''
+ @node_bar.automatic_attrs[:cloud][:public_hostname] = ''
+ end
+
+ it "returns an array of fqdns" do
+ configure_query([@node_foo, @node_bar])
+ expect(@knife).to receive(:session_from_list).with([
+ ['foo.example.org', nil],
+ ['bar.example.org', nil]
+ ])
+ @knife.configure_session
+ end
+
+ should_return_specified_attributes
+ end
+
it "should raise an error if no host are found" do
configure_query([ ])
expect(@knife.ui).to receive(:fatal)
@@ -132,42 +149,40 @@ describe Chef::Knife::Ssh do
end
end
- describe "#configure_attribute" do
+ describe "#get_ssh_attribute" do
+ # Order of precedence for ssh target
+ # 1) command line attribute
+ # 2) configuration file
+ # 3) cloud attribute
+ # 4) fqdn
before do
Chef::Config[:knife][:ssh_attribute] = nil
@knife.config[:attribute] = nil
+ @node_foo.automatic_attrs[:cloud][:public_hostname] = "ec2-10-0-0-1.compute-1.amazonaws.com"
+ @node_bar.automatic_attrs[:cloud][:public_hostname] = ''
end
it "should return fqdn by default" do
- @knife.configure_attribute
- expect(@knife.config[:attribute]).to eq("fqdn")
+ expect(@knife.get_ssh_attribute(Chef::Node.new)).to eq("fqdn")
end
- it "should return the value set in the configuration file" do
- Chef::Config[:knife][:ssh_attribute] = "config_file"
- @knife.configure_attribute
- expect(@knife.config[:attribute]).to eq("config_file")
+ it "should return cloud.public_hostname attribute if available" do
+ expect(@knife.get_ssh_attribute(@node_foo)).to eq("cloud.public_hostname")
end
- it "should return the value set on the command line" do
+ it "should favor to attribute_from_cli over config file and cloud" do
@knife.config[:attribute] = "command_line"
- @knife.configure_attribute
- expect(@knife.config[:attribute]).to eq("command_line")
+ Chef::Config[:knife][:ssh_attribute] = "config_file"
+ expect( @knife.get_ssh_attribute(@node_foo)).to eq("command_line")
end
- it "should set attribute_from_cli to the value of attribute from the command line" do
- @knife.config[:attribute] = "command_line"
- @knife.configure_attribute
- expect(@knife.config[:attribute]).to eq("command_line")
- expect(@knife.config[:attribute_from_cli]).to eq("command_line")
+ it "should favor config file over cloud and default" do
+ Chef::Config[:knife][:ssh_attribute] = "config_file"
+ expect( @knife.get_ssh_attribute(@node_foo)).to eq("config_file")
end
- it "should prefer the command line over the config file for the value of attribute_from_cli" do
- Chef::Config[:knife][:ssh_attribute] = "config_file"
- @knife.config[:attribute] = "command_line"
- @knife.configure_attribute
- expect(@knife.config[:attribute]).to eq("command_line")
- expect(@knife.config[:attribute_from_cli]).to eq("command_line")
+ it "should return fqdn if cloud.hostname is empty" do
+ expect( @knife.get_ssh_attribute(@node_bar)).to eq("fqdn")
end
end
diff --git a/spec/unit/knife/ssl_check_spec.rb b/spec/unit/knife/ssl_check_spec.rb
index 8eda555108..fd46c47d99 100644
--- a/spec/unit/knife/ssl_check_spec.rb
+++ b/spec/unit/knife/ssl_check_spec.rb
@@ -163,6 +163,7 @@ E
expect(ssl_check).to receive(:verify_X509).and_return(true) # X509 valid certs (no warn)
expect(ssl_socket).to receive(:connect) # no error
expect(ssl_socket).to receive(:post_connection_check).with("foo.example.com") # no error
+ expect(ssl_socket).to receive(:hostname=).with("foo.example.com") # no error
end
it "prints a success message" do
@@ -197,6 +198,7 @@ E
expect(ssl_socket).to receive(:post_connection_check).
with("foo.example.com").
and_raise(OpenSSL::SSL::SSLError)
+ expect(ssl_socket).to receive(:hostname=).with("foo.example.com") # no error
expect(ssl_socket_for_debug).to receive(:connect)
expect(ssl_socket_for_debug).to receive(:peer_cert).and_return(self_signed_crt)
end
@@ -215,6 +217,8 @@ E
expect(ssl_check).to receive(:verify_X509).and_return(true) # X509 valid certs
expect(ssl_socket).to receive(:connect).
and_raise(OpenSSL::SSL::SSLError)
+ expect(ssl_socket).to receive(:hostname=).
+ with("foo.example.com") # no error
expect(ssl_socket_for_debug).to receive(:connect)
expect(ssl_socket_for_debug).to receive(:peer_cert).and_return(self_signed_crt)
end
diff --git a/spec/unit/knife/status_spec.rb b/spec/unit/knife/status_spec.rb
index 2522bc61b1..ee44f3b3fd 100644
--- a/spec/unit/knife/status_spec.rb
+++ b/spec/unit/knife/status_spec.rb
@@ -24,15 +24,81 @@ describe Chef::Knife::Status do
n.automatic_attrs["fqdn"] = "foobar"
n.automatic_attrs["ohai_time"] = 1343845969
end
- query = double("Chef::Search::Query")
- allow(query).to receive(:search).and_yield(node)
- allow(Chef::Search::Query).to receive(:new).and_return(query)
+ allow(Time).to receive(:now).and_return(Time.at(1428573420))
+ @query = double("Chef::Search::Query")
+ allow(@query).to receive(:search).and_yield(node)
+ allow(Chef::Search::Query).to receive(:new).and_return(@query)
@knife = Chef::Knife::Status.new
@stdout = StringIO.new
allow(@knife.ui).to receive(:stdout).and_return(@stdout)
end
describe "run" do
+ let(:opts) {{filter_result:
+ { name: ["name"], ipaddress: ["ipaddress"], ohai_time: ["ohai_time"],
+ ec2: ["ec2"], run_list: ["run_list"], platform: ["platform"],
+ platform_version: ["platform_version"], chef_environment: ["chef_environment"]}}}
+
+ it "should default to searching for everything" do
+ expect(@query).to receive(:search).with(:node, "*:*", opts)
+ @knife.run
+ end
+
+ it "should filter healthy nodes" do
+ @knife.config[:hide_healthy] = true
+ expect(@query).to receive(:search).with(:node, "NOT ohai_time:[1428569820 TO 1428573420]", opts)
+ @knife.run
+ end
+
+ it "should filter by environment" do
+ @knife.config[:environment] = "production"
+ expect(@query).to receive(:search).with(:node, "chef_environment:production", opts)
+ @knife.run
+ end
+
+ it "should filter by environment and health" do
+ @knife.config[:environment] = "production"
+ @knife.config[:hide_healthy] = true
+ expect(@query).to receive(:search).with(:node, "chef_environment:production NOT ohai_time:[1428569820 TO 1428573420]", opts)
+ @knife.run
+ end
+
+ it "should not use partial search with long output" do
+ @knife.config[:long_output] = true
+ expect(@query).to receive(:search).with(:node, "*:*", {})
+ @knife.run
+ end
+
+ context "with a custom query" do
+ before :each do
+ @knife.instance_variable_set(:@name_args, ["name:my_custom_name"])
+ end
+
+ it "should allow a custom query to be specified" do
+ expect(@query).to receive(:search).with(:node, "name:my_custom_name", opts)
+ @knife.run
+ end
+
+ it "should filter healthy nodes" do
+ @knife.config[:hide_healthy] = true
+ expect(@query).to receive(:search).with(:node, "name:my_custom_name NOT ohai_time:[1428569820 TO 1428573420]", opts)
+ @knife.run
+ end
+
+ it "should filter by environment" do
+ @knife.config[:environment] = "production"
+ expect(@query).to receive(:search).with(:node, "name:my_custom_name AND chef_environment:production", opts)
+ @knife.run
+ end
+
+ it "should filter by environment and health" do
+ @knife.config[:environment] = "production"
+ @knife.config[:hide_healthy] = true
+ expect(@query).to receive(:search).with(:node, "name:my_custom_name AND chef_environment:production NOT ohai_time:[1428569820 TO 1428573420]", opts)
+ @knife.run
+ end
+ end
+
it "should not colorize output unless it's writing to a tty" do
@knife.run
expect(@stdout.string.match(/foobar/)).not_to be_nil
diff --git a/spec/unit/knife/user_create_spec.rb b/spec/unit/knife/user_create_spec.rb
index ad8821cd0e..fa5c8324b4 100644
--- a/spec/unit/knife/user_create_spec.rb
+++ b/spec/unit/knife/user_create_spec.rb
@@ -1,6 +1,7 @@
#
-# Author:: Steven Danna (<steve@opscode.com>)
-# Copyright:: Copyright (c) 2012 Opscode, Inc.
+# Author:: Steven Danna (<steve@chef.io>)
+# Author:: Tyler Cloke (<tyler@chef.io>)
+# Copyright:: Copyright (c) 2012, 2015 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,68 +22,193 @@ require 'spec_helper'
Chef::Knife::UserCreate.load_deps
describe Chef::Knife::UserCreate do
+ let(:knife) { Chef::Knife::UserCreate.new }
+
+ let(:stderr) {
+ StringIO.new
+ }
+
+ let(:stdout) {
+ StringIO.new
+ }
+
before(:each) do
- @knife = Chef::Knife::UserCreate.new
-
- @stdout = StringIO.new
- @stderr = StringIO.new
- allow(@knife.ui).to receive(:stdout).and_return(@stdout)
- allow(@knife.ui).to receive(:stderr).and_return(@stderr)
-
- @knife.name_args = [ 'a_user' ]
- @knife.config[:user_password] = "foobar"
- @user = Chef::User.new
- @user.name "a_user"
- @user_with_private_key = Chef::User.new
- @user_with_private_key.name "a_user"
- @user_with_private_key.private_key 'private_key'
- allow(@user).to receive(:create).and_return(@user_with_private_key)
- allow(Chef::User).to receive(:new).and_return(@user)
- allow(Chef::User).to receive(:from_hash).and_return(@user)
- allow(@knife).to receive(:edit_data).and_return(@user.to_hash)
+ allow(knife.ui).to receive(:stdout).and_return(stdout)
+ allow(knife.ui).to receive(:stderr).and_return(stderr)
+ allow(knife.ui).to receive(:warn)
end
- it "creates a new user" do
- expect(Chef::User).to receive(:new).and_return(@user)
- expect(@user).to receive(:create)
- @knife.run
- expect(@stderr.string).to match /created user.+a_user/i
- end
+ # delete this once OSC11 support is gone
+ context "when only one name_arg is passed" do
+ before do
+ knife.name_args = ['some_user']
+ allow(knife).to receive(:run_osc_11_user_create).and_raise(SystemExit)
+ end
+
+ it "displays the osc warning" do
+ expect(knife.ui).to receive(:warn).with(knife.osc_11_warning)
+ expect{ knife.run }.to raise_error(SystemExit)
+ end
+
+ it "calls knife osc_user create" do
+ expect(knife).to receive(:run_osc_11_user_create)
+ expect{ knife.run }.to raise_error(SystemExit)
+ end
- it "sets the password" do
- @knife.config[:user_password] = "a_password"
- expect(@user).to receive(:password).with("a_password")
- @knife.run
end
- it "exits with an error if password is blank" do
- @knife.config[:user_password] = ''
- expect { @knife.run }.to raise_error SystemExit
- expect(@stderr.string).to match /You must specify a non-blank password/
+ context "when USERNAME isn't specified" do
+ # from spec/support/shared/unit/knife_shared.rb
+ it_should_behave_like "mandatory field missing" do
+ let(:name_args) { [] }
+ let(:fieldname) { 'username' }
+ end
end
- it "sets the user name" do
- expect(@user).to receive(:name).with("a_user")
- @knife.run
+ # uncomment once OSC11 support is gone,
+ # pending doesn't work for shared_examples_for by default
+ #
+ # context "when DISPLAY_NAME isn't specified" do
+ # # from spec/support/shared/unit/knife_shared.rb
+ # it_should_behave_like "mandatory field missing" do
+ # let(:name_args) { ['some_user'] }
+ # let(:fieldname) { 'display name' }
+ # end
+ # end
+
+ context "when FIRST_NAME isn't specified" do
+ # from spec/support/shared/unit/knife_shared.rb
+ it_should_behave_like "mandatory field missing" do
+ let(:name_args) { ['some_user', 'some_display_name'] }
+ let(:fieldname) { 'first name' }
+ end
end
- it "sets the public key if given" do
- @knife.config[:user_key] = "/a/filename"
- allow(File).to receive(:read).with(File.expand_path("/a/filename")).and_return("a_key")
- expect(@user).to receive(:public_key).with("a_key")
- @knife.run
+ context "when LAST_NAME isn't specified" do
+ # from spec/support/shared/unit/knife_shared.rb
+ it_should_behave_like "mandatory field missing" do
+ let(:name_args) { ['some_user', 'some_display_name', 'some_first_name'] }
+ let(:fieldname) { 'last name' }
+ end
end
- it "allows you to edit the data" do
- expect(@knife).to receive(:edit_data).with(@user)
- @knife.run
+ context "when EMAIL isn't specified" do
+ # from spec/support/shared/unit/knife_shared.rb
+ it_should_behave_like "mandatory field missing" do
+ let(:name_args) { ['some_user', 'some_display_name', 'some_first_name', 'some_last_name'] }
+ let(:fieldname) { 'email' }
+ end
end
- it "writes the private key to a file when --file is specified" do
- @knife.config[:file] = "/tmp/a_file"
- filehandle = double("filehandle")
- expect(filehandle).to receive(:print).with('private_key')
- expect(File).to receive(:open).with("/tmp/a_file", "w").and_yield(filehandle)
- @knife.run
+ context "when PASSWORD isn't specified" do
+ # from spec/support/shared/unit/knife_shared.rb
+ it_should_behave_like "mandatory field missing" do
+ let(:name_args) { ['some_user', 'some_display_name', 'some_first_name', 'some_last_name', 'some_email'] }
+ let(:fieldname) { 'password' }
+ end
end
+
+ context "when all mandatory fields are validly specified" do
+ before do
+ knife.name_args = ['some_user', 'some_display_name', 'some_first_name', 'some_last_name', 'some_email', 'some_password']
+ allow(knife).to receive(:edit_data).and_return(knife.user.to_hash)
+ allow(knife).to receive(:create_user_from_hash).and_return(knife.user)
+ end
+
+ before(:each) do
+ # reset the user field every run
+ knife.user_field = nil
+ end
+
+ it "sets all the mandatory fields" do
+ knife.run
+ expect(knife.user.username).to eq('some_user')
+ expect(knife.user.display_name).to eq('some_display_name')
+ expect(knife.user.first_name).to eq('some_first_name')
+ expect(knife.user.last_name).to eq('some_last_name')
+ expect(knife.user.email).to eq('some_email')
+ expect(knife.user.password).to eq('some_password')
+ end
+
+ context "when user_key and prevent_keygen are passed" do
+ before do
+ knife.config[:user_key] = "some_key"
+ knife.config[:prevent_keygen] = true
+ end
+ it "prints the usage" do
+ expect(knife).to receive(:show_usage)
+ expect { knife.run }.to raise_error(SystemExit)
+ end
+
+ it "prints a relevant error message" do
+ expect { knife.run }.to raise_error(SystemExit)
+ expect(stderr.string).to match /You cannot pass --user-key and --prevent-keygen/
+ end
+ end
+
+ context "when --prevent-keygen is passed" do
+ before do
+ knife.config[:prevent_keygen] = true
+ end
+
+ it "does not set user.create_key" do
+ knife.run
+ expect(knife.user.create_key).to be_falsey
+ end
+ end
+
+ context "when --prevent-keygen is not passed" do
+ it "sets user.create_key to true" do
+ knife.run
+ expect(knife.user.create_key).to be_truthy
+ end
+ end
+
+ context "when --user-key is passed" do
+ before do
+ knife.config[:user_key] = 'some_key'
+ allow(File).to receive(:read).and_return('some_key')
+ allow(File).to receive(:expand_path)
+ end
+
+ it "sets user.public_key" do
+ knife.run
+ expect(knife.user.public_key).to eq('some_key')
+ end
+ end
+
+ context "when --user-key is not passed" do
+ it "does not set user.public_key" do
+ knife.run
+ expect(knife.user.public_key).to be_nil
+ end
+ end
+
+ context "when a private_key is returned" do
+ before do
+ allow(knife).to receive(:create_user_from_hash).and_return(Chef::UserV1.from_hash(knife.user.to_hash.merge({"private_key" => "some_private_key"})))
+ end
+
+ context "when --file is passed" do
+ before do
+ knife.config[:file] = '/some/path'
+ end
+
+ it "creates a new file of the path passed" do
+ filehandle = double('filehandle')
+ expect(filehandle).to receive(:print).with('some_private_key')
+ expect(File).to receive(:open).with('/some/path', 'w').and_yield(filehandle)
+ knife.run
+ end
+ end
+
+ context "when --file is not passed" do
+ it "prints the private key to stdout" do
+ expect(knife.ui).to receive(:msg).with('some_private_key')
+ knife.run
+ end
+ end
+ end
+
+ end # when all mandatory fields are validly specified
end
diff --git a/spec/unit/knife/user_delete_spec.rb b/spec/unit/knife/user_delete_spec.rb
index 94cfbf3db1..a24160624a 100644
--- a/spec/unit/knife/user_delete_spec.rb
+++ b/spec/unit/knife/user_delete_spec.rb
@@ -19,21 +19,47 @@
require 'spec_helper'
describe Chef::Knife::UserDelete do
+ let(:knife) { Chef::Knife::UserDelete.new }
+ let(:user) { double('user_object') }
+ let(:stdout) { StringIO.new }
+
before(:each) do
Chef::Knife::UserDelete.load_deps
- @knife = Chef::Knife::UserDelete.new
- @knife.name_args = [ 'my_user' ]
+ knife.name_args = [ 'my_user' ]
+ allow(Chef::UserV1).to receive(:load).and_return(user)
+ allow(user).to receive(:username).and_return('my_user')
+ allow(knife.ui).to receive(:stderr).and_return(stdout)
+ allow(knife.ui).to receive(:stdout).and_return(stdout)
+ end
+
+ # delete this once OSC11 support is gone
+ context "when the username field is not supported by the server" do
+ before do
+ allow(knife).to receive(:run_osc_11_user_delete).and_raise(SystemExit)
+ allow(user).to receive(:username).and_return(nil)
+ end
+
+ it "displays the osc warning" do
+ expect(knife.ui).to receive(:warn).with(knife.osc_11_warning)
+ expect{ knife.run }.to raise_error(SystemExit)
+ end
+
+ it "forwards the command to knife osc_user edit" do
+ expect(knife).to receive(:run_osc_11_user_delete)
+ expect{ knife.run }.to raise_error(SystemExit)
+ end
end
it 'deletes the user' do
- expect(@knife).to receive(:delete_object).with(Chef::User, 'my_user')
- @knife.run
+ #expect(knife).to receive(:delete_object).with(Chef::UserV1, 'my_user')
+ expect(knife).to receive(:delete_object).with('my_user')
+ knife.run
end
it 'prints usage and exits when a user name is not provided' do
- @knife.name_args = []
- expect(@knife).to receive(:show_usage)
- expect(@knife.ui).to receive(:fatal)
- expect { @knife.run }.to raise_error(SystemExit)
+ knife.name_args = []
+ expect(knife).to receive(:show_usage)
+ expect(knife.ui).to receive(:fatal)
+ expect { knife.run }.to raise_error(SystemExit)
end
end
diff --git a/spec/unit/knife/user_edit_spec.rb b/spec/unit/knife/user_edit_spec.rb
index 0eb75cfa9b..a21d982d29 100644
--- a/spec/unit/knife/user_edit_spec.rb
+++ b/spec/unit/knife/user_edit_spec.rb
@@ -19,29 +19,48 @@
require 'spec_helper'
describe Chef::Knife::UserEdit do
+ let(:knife) { Chef::Knife::UserEdit.new }
+
before(:each) do
@stderr = StringIO.new
@stdout = StringIO.new
Chef::Knife::UserEdit.load_deps
- @knife = Chef::Knife::UserEdit.new
- allow(@knife.ui).to receive(:stderr).and_return(@stderr)
- allow(@knife.ui).to receive(:stdout).and_return(@stdout)
- @knife.name_args = [ 'my_user' ]
- @knife.config[:disable_editing] = true
+ allow(knife.ui).to receive(:stderr).and_return(@stderr)
+ allow(knife.ui).to receive(:stdout).and_return(@stdout)
+ knife.name_args = [ 'my_user' ]
+ knife.config[:disable_editing] = true
+ end
+
+ # delete this once OSC11 support is gone
+ context "when the username field is not supported by the server" do
+ before do
+ allow(knife).to receive(:run_osc_11_user_edit).and_raise(SystemExit)
+ allow(Chef::UserV1).to receive(:load).and_return({"username" => nil})
+ end
+
+ it "displays the osc warning" do
+ expect(knife.ui).to receive(:warn).with(knife.osc_11_warning)
+ expect{ knife.run }.to raise_error(SystemExit)
+ end
+
+ it "forwards the command to knife osc_user edit" do
+ expect(knife).to receive(:run_osc_11_user_edit)
+ expect{ knife.run }.to raise_error(SystemExit)
+ end
end
it 'loads and edits the user' do
- data = { :name => "my_user" }
- allow(Chef::User).to receive(:load).with("my_user").and_return(data)
- expect(@knife).to receive(:edit_data).with(data).and_return(data)
- @knife.run
+ data = { "username" => "my_user" }
+ allow(Chef::UserV1).to receive(:load).with("my_user").and_return(data)
+ expect(knife).to receive(:edit_data).with(data).and_return(data)
+ knife.run
end
it 'prints usage and exits when a user name is not provided' do
- @knife.name_args = []
- expect(@knife).to receive(:show_usage)
- expect(@knife.ui).to receive(:fatal)
- expect { @knife.run }.to raise_error(SystemExit)
+ knife.name_args = []
+ expect(knife).to receive(:show_usage)
+ expect(knife.ui).to receive(:fatal)
+ expect { knife.run }.to raise_error(SystemExit)
end
end
diff --git a/spec/unit/knife/user_list_spec.rb b/spec/unit/knife/user_list_spec.rb
index db097a5c16..fa2bac426e 100644
--- a/spec/unit/knife/user_list_spec.rb
+++ b/spec/unit/knife/user_list_spec.rb
@@ -19,14 +19,18 @@
require 'spec_helper'
describe Chef::Knife::UserList do
+ let(:knife) { Chef::Knife::UserList.new }
+ let(:stdout) { StringIO.new }
+
before(:each) do
Chef::Knife::UserList.load_deps
- @knife = Chef::Knife::UserList.new
+ allow(knife.ui).to receive(:stderr).and_return(stdout)
+ allow(knife.ui).to receive(:stdout).and_return(stdout)
end
it 'lists the users' do
- expect(Chef::User).to receive(:list)
- expect(@knife).to receive(:format_list_for_display)
- @knife.run
+ expect(Chef::UserV1).to receive(:list)
+ expect(knife).to receive(:format_list_for_display)
+ knife.run
end
end
diff --git a/spec/unit/knife/user_reregister_spec.rb b/spec/unit/knife/user_reregister_spec.rb
index 1268716f40..89aa6726cd 100644
--- a/spec/unit/knife/user_reregister_spec.rb
+++ b/spec/unit/knife/user_reregister_spec.rb
@@ -19,35 +19,56 @@
require 'spec_helper'
describe Chef::Knife::UserReregister do
- before(:each) do
+ let(:knife) { Chef::Knife::UserReregister.new }
+ let(:user_mock) { double('user_mock', :private_key => "private_key") }
+ let(:stdout) { StringIO.new }
+
+ before do
Chef::Knife::UserReregister.load_deps
- @knife = Chef::Knife::UserReregister.new
- @knife.name_args = [ 'a_user' ]
- @user_mock = double('user_mock', :private_key => "private_key")
- allow(Chef::User).to receive(:load).and_return(@user_mock)
- @stdout = StringIO.new
- allow(@knife.ui).to receive(:stdout).and_return(@stdout)
+ knife.name_args = [ 'a_user' ]
+ allow(Chef::UserV1).to receive(:load).and_return(user_mock)
+ allow(knife.ui).to receive(:stdout).and_return(stdout)
+ allow(knife.ui).to receive(:stderr).and_return(stdout)
+ allow(user_mock).to receive(:username).and_return('a_user')
+ end
+
+ # delete this once OSC11 support is gone
+ context "when the username field is not supported by the server" do
+ before do
+ allow(knife).to receive(:run_osc_11_user_reregister).and_raise(SystemExit)
+ allow(user_mock).to receive(:username).and_return(nil)
+ end
+
+ it "displays the osc warning" do
+ expect(knife.ui).to receive(:warn).with(knife.osc_11_warning)
+ expect{ knife.run }.to raise_error(SystemExit)
+ end
+
+ it "forwards the command to knife osc_user edit" do
+ expect(knife).to receive(:run_osc_11_user_reregister)
+ expect{ knife.run }.to raise_error(SystemExit)
+ end
end
it 'prints usage and exits when a user name is not provided' do
- @knife.name_args = []
- expect(@knife).to receive(:show_usage)
- expect(@knife.ui).to receive(:fatal)
- expect { @knife.run }.to raise_error(SystemExit)
+ knife.name_args = []
+ expect(knife).to receive(:show_usage)
+ expect(knife.ui).to receive(:fatal)
+ expect { knife.run }.to raise_error(SystemExit)
end
it 'reregisters the user and prints the key' do
- expect(@user_mock).to receive(:reregister).and_return(@user_mock)
- @knife.run
- expect(@stdout.string).to match( /private_key/ )
+ expect(user_mock).to receive(:reregister).and_return(user_mock)
+ knife.run
+ expect(stdout.string).to match( /private_key/ )
end
it 'writes the private key to a file when --file is specified' do
- expect(@user_mock).to receive(:reregister).and_return(@user_mock)
- @knife.config[:file] = '/tmp/a_file'
+ expect(user_mock).to receive(:reregister).and_return(user_mock)
+ knife.config[:file] = '/tmp/a_file'
filehandle = StringIO.new
expect(File).to receive(:open).with('/tmp/a_file', 'w').and_yield(filehandle)
- @knife.run
+ knife.run
expect(filehandle.string).to eq("private_key")
end
end
diff --git a/spec/unit/knife/user_show_spec.rb b/spec/unit/knife/user_show_spec.rb
index f97cbc3f13..7c39e428c0 100644
--- a/spec/unit/knife/user_show_spec.rb
+++ b/spec/unit/knife/user_show_spec.rb
@@ -19,23 +19,47 @@
require 'spec_helper'
describe Chef::Knife::UserShow do
- before(:each) do
+ let(:knife) { Chef::Knife::UserShow.new }
+ let(:user_mock) { double('user_mock') }
+ let(:stdout) { StringIO.new }
+
+ before do
Chef::Knife::UserShow.load_deps
- @knife = Chef::Knife::UserShow.new
- @knife.name_args = [ 'my_user' ]
- @user_mock = double('user_mock')
+ knife.name_args = [ 'my_user' ]
+ allow(user_mock).to receive(:username).and_return('my_user')
+ allow(knife.ui).to receive(:stderr).and_return(stdout)
+ allow(knife.ui).to receive(:stdout).and_return(stdout)
+ end
+
+ # delete this once OSC11 support is gone
+ context "when the username field is not supported by the server" do
+ before do
+ allow(knife).to receive(:run_osc_11_user_show).and_raise(SystemExit)
+ allow(Chef::UserV1).to receive(:load).with('my_user').and_return(user_mock)
+ allow(user_mock).to receive(:username).and_return(nil)
+ end
+
+ it "displays the osc warning" do
+ expect(knife.ui).to receive(:warn).with(knife.osc_11_warning)
+ expect{ knife.run }.to raise_error(SystemExit)
+ end
+
+ it "forwards the command to knife osc_user edit" do
+ expect(knife).to receive(:run_osc_11_user_show)
+ expect{ knife.run }.to raise_error(SystemExit)
+ end
end
it 'loads and displays the user' do
- expect(Chef::User).to receive(:load).with('my_user').and_return(@user_mock)
- expect(@knife).to receive(:format_for_display).with(@user_mock)
- @knife.run
+ expect(Chef::UserV1).to receive(:load).with('my_user').and_return(user_mock)
+ expect(knife).to receive(:format_for_display).with(user_mock)
+ knife.run
end
it 'prints usage and exits when a user name is not provided' do
- @knife.name_args = []
- expect(@knife).to receive(:show_usage)
- expect(@knife.ui).to receive(:fatal)
- expect { @knife.run }.to raise_error(SystemExit)
+ knife.name_args = []
+ expect(knife).to receive(:show_usage)
+ expect(knife.ui).to receive(:fatal)
+ expect { knife.run }.to raise_error(SystemExit)
end
end
diff --git a/spec/unit/knife_spec.rb b/spec/unit/knife_spec.rb
index 2ccf8493ad..022256f370 100644
--- a/spec/unit/knife_spec.rb
+++ b/spec/unit/knife_spec.rb
@@ -30,11 +30,20 @@ describe Chef::Knife do
let(:knife) { Chef::Knife.new }
+ let(:config_location) { File.expand_path("~/.chef/config.rb") }
+
+ let(:config_loader) do
+ instance_double("WorkstationConfigLoader", load: nil, no_config_found?: false, config_location: config_location)
+ end
+
before(:each) do
Chef::Log.logger = Logger.new(StringIO.new)
Chef::Config[:node_name] = "webmonkey.example.com"
+ allow(Chef::WorkstationConfigLoader).to receive(:new).and_return(config_loader)
+ allow(config_loader).to receive(:explicit_config_file=)
+
# Prevent gratuitous code reloading:
allow(Chef::Knife).to receive(:load_commands)
allow(knife.ui).to receive(:puts)
@@ -130,7 +139,8 @@ describe Chef::Knife do
"Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
'X-Chef-Version' => Chef::VERSION,
"Host"=>"api.opscode.piab",
- "X-REMOTE-REQUEST-ID"=>request_id}}
+ "X-REMOTE-REQUEST-ID"=>request_id,
+ 'X-Ops-Server-API-Version' => Chef::HTTP::Authenticator::DEFAULT_SERVER_API_VERSION}}
let(:request_id) {"1234"}
@@ -251,6 +261,18 @@ describe Chef::Knife do
:default => "default-value")
end
+ it "sets the default log_location to STDERR for Chef::Log warnings" do
+ knife_command = KnifeSpecs::TestYourself.new([])
+ knife_command.configure_chef
+ expect(Chef::Config[:log_location]).to eq(STDERR)
+ end
+
+ it "sets the default log_level to warn so we can issue Chef::Log.warn" do
+ knife_command = KnifeSpecs::TestYourself.new([])
+ knife_command.configure_chef
+ expect(Chef::Config[:log_level]).to eql(:warn)
+ end
+
it "prefers the default value if no config or command line value is present" do
knife_command = KnifeSpecs::TestYourself.new([]) #empty argv
knife_command.configure_chef
@@ -271,6 +293,11 @@ describe Chef::Knife do
expect(knife_command.config[:opt_with_default]).to eq("from-cli")
end
+ it "merges `listen` config to Chef::Config" do
+ Chef::Knife.run(%w[test yourself --no-listen], Chef::Application::Knife.options)
+ expect(Chef::Config[:listen]).to be(false)
+ end
+
context "verbosity is greater than zero" do
let(:fake_config) { "/does/not/exist/knife.rb" }
@@ -369,6 +396,22 @@ describe Chef::Knife do
expect(stderr.string).to match(%r[Response: nothing to see here])
end
+ it "formats 406s (non-supported API version error) nicely" do
+ response = Net::HTTPNotAcceptable.new("1.1", "406", "Not Acceptable")
+ response.instance_variable_set(:@read, true) # I hate you, net/http.
+
+ # set the header
+ response["x-ops-server-api-version"] = Chef::JSONCompat.to_json(:min_version => "0", :max_version => "1", :request_version => "10000000")
+
+ allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "sad trombone"))
+ allow(knife).to receive(:run).and_raise(Net::HTTPServerException.new("406 Not Acceptable", response))
+
+ knife.run_with_pretty_exceptions
+ expect(stderr.string).to include('The request that Knife sent was using API version 10000000')
+ expect(stderr.string).to include('The Chef server you sent the request to supports a min API verson of 0 and a max API version of 1')
+ expect(stderr.string).to include('Please either update your Chef client or server to be a compatible set')
+ end
+
it "formats 500s nicely" do
response = Net::HTTPInternalServerError.new("1.1", "500", "Internal Server Error")
response.instance_variable_set(:@read, true) # I hate you, net/http.
diff --git a/spec/unit/log/syslog_spec.rb b/spec/unit/log/syslog_spec.rb
new file mode 100644
index 0000000000..3db90e50c6
--- /dev/null
+++ b/spec/unit/log/syslog_spec.rb
@@ -0,0 +1,53 @@
+#
+# Author:: SAWANOBORI Yukihiko (<sawanoboriyu@higanworks.com>)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'chef'
+
+describe "Chef::Log::Syslog", :unix_only => true do
+ let(:syslog) { Chef::Log::Syslog.new }
+ let(:app) { Chef::Application.new }
+
+ before do
+ Chef::Log.init(MonoLogger.new(syslog))
+ @old_log_level = Chef::Log.level
+ Chef::Log.level = :info
+ @old_loggers = Chef::Log.loggers
+ Chef::Log.use_log_devices([syslog])
+ end
+
+ after do
+ Chef::Log.level = @old_log_level
+ Chef::Log.use_log_devices(@old_loggers)
+ end
+
+ it "should send message with severity info to syslog." do
+ expect(syslog).to receive(:info).with("*** Chef 12.4.0.dev.0 ***")
+ Chef::Log.info("*** Chef 12.4.0.dev.0 ***")
+ end
+
+ it "should send message with severity warning to syslog." do
+ expect(syslog).to receive(:warn).with("No config file found or specified on command line, using command line options.")
+ Chef::Log.warn("No config file found or specified on command line, using command line options.")
+ end
+
+ it "should fallback into send message with severity info to syslog when wrong format." do
+ expect(syslog).to receive(:info).with("chef message")
+ syslog.write("chef message")
+ end
+end
diff --git a/spec/unit/log/winevt_spec.rb b/spec/unit/log/winevt_spec.rb
new file mode 100644
index 0000000000..867ef55900
--- /dev/null
+++ b/spec/unit/log/winevt_spec.rb
@@ -0,0 +1,55 @@
+#
+# Author:: Jay Mundrawala (jdm@chef.io)
+# Author:: SAWANOBORI Yukihiko (<sawanoboriyu@higanworks.com>)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Log::WinEvt do
+ let(:evtlog) { instance_double("Win32::EventLog")}
+ let(:winevt) { Chef::Log::WinEvt.new(evtlog) }
+ let(:app) { Chef::Application.new }
+
+ before do
+
+ Chef::Log.init(MonoLogger.new(winevt))
+ @old_log_level = Chef::Log.level
+ Chef::Log.level = :info
+ @old_loggers = Chef::Log.loggers
+ Chef::Log.use_log_devices([winevt])
+ end
+
+ after do
+ Chef::Log.level = @old_log_level
+ Chef::Log.use_log_devices(@old_loggers)
+ end
+
+ it "should send message with severity info to Windows Event Log." do
+ expect(winevt).to receive(:info).with("*** Chef 12.4.0.dev.0 ***")
+ Chef::Log.info("*** Chef 12.4.0.dev.0 ***")
+ end
+
+ it "should send message with severity warning to Windows Event Log." do
+ expect(winevt).to receive(:warn).with("No config file found or specified on command line, using command line options.")
+ Chef::Log.warn("No config file found or specified on command line, using command line options.")
+ end
+
+ it "should fallback into send message with severity info to Windows Event Log when wrong format." do
+ expect(winevt).to receive(:info).with("chef message")
+ winevt.write("chef message")
+ end
+end
diff --git a/spec/unit/lwrp_spec.rb b/spec/unit/lwrp_spec.rb
index ec39174da6..bcb64cb21e 100644
--- a/spec/unit/lwrp_spec.rb
+++ b/spec/unit/lwrp_spec.rb
@@ -17,20 +17,40 @@
#
require 'spec_helper'
+require 'tmpdir'
+require 'fileutils'
+require 'chef/mixin/convert_to_class_name'
module LwrpConstScopingConflict
end
describe "LWRP" do
+ include Chef::Mixin::ConvertToClassName
+
before do
@original_VERBOSE = $VERBOSE
$VERBOSE = nil
+ Chef::Resource::LWRPBase.class_eval { @loaded_lwrps = {} }
end
after do
$VERBOSE = @original_VERBOSE
end
+ def get_lwrp(name)
+ Chef::ResourceResolver.resolve(name)
+ end
+
+ def get_lwrp_provider(name)
+ old_treat_deprecation_warnings_as_errors = Chef::Config[:treat_deprecation_warnings_as_errors]
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ begin
+ Chef::Provider.const_get(convert_to_class_name(name.to_s))
+ ensure
+ Chef::Config[:treat_deprecation_warnings_as_errors] = old_treat_deprecation_warnings_as_errors
+ end
+ end
+
describe "when overriding an existing class" do
before :each do
allow($stderr).to receive(:write)
@@ -43,7 +63,6 @@ describe "LWRP" do
expect(Chef::Log).not_to receive(:debug).with(/anymore/)
Chef::Resource::LWRPBase.build_from_file("lwrp", file, nil)
Object.send(:remove_const, 'LwrpFoo')
- Chef::Resource.send(:remove_const, 'LwrpFoo')
end
it "should not skip loading a provider when there's a top level symbol of the same name" do
@@ -53,7 +72,6 @@ describe "LWRP" do
expect(Chef::Log).not_to receive(:debug).with(/anymore/)
Chef::Provider::LWRPBase.build_from_file("lwrp", file, nil)
Object.send(:remove_const, 'LwrpBuckPasser')
- Chef::Provider.send(:remove_const, 'LwrpBuckPasser')
end
# @todo: we need a before block to manually remove_const all of the LWRPs that we
@@ -67,7 +85,6 @@ describe "LWRP" do
Dir[File.expand_path( "lwrp/resources/*", CHEF_SPEC_DATA)].each do |file|
expect(Chef::Log).to receive(:info).with(/Skipping/)
- expect(Chef::Log).to receive(:debug).with(/anymore/)
Chef::Resource::LWRPBase.build_from_file("lwrp", file, nil)
end
end
@@ -79,7 +96,6 @@ describe "LWRP" do
Dir[File.expand_path( "lwrp/providers/*", CHEF_SPEC_DATA)].each do |file|
expect(Chef::Log).to receive(:info).with(/Skipping/)
- expect(Chef::Log).to receive(:debug).with(/anymore/)
Chef::Provider::LWRPBase.build_from_file("lwrp", file, nil)
end
end
@@ -90,7 +106,7 @@ describe "LWRP" do
Dir[File.expand_path( "lwrp/resources/*", CHEF_SPEC_DATA)].each do |file|
Chef::Resource::LWRPBase.build_from_file("lwrp", file, nil)
end
- first_lwr_foo_class = Chef::Resource::LwrpFoo
+ first_lwr_foo_class = get_lwrp(:lwrp_foo)
expect(Chef::Resource.resource_classes).to include(first_lwr_foo_class)
Dir[File.expand_path( "lwrp/resources/*", CHEF_SPEC_DATA)].each do |file|
Chef::Resource::LWRPBase.build_from_file("lwrp", file, nil)
@@ -106,40 +122,91 @@ describe "LWRP" do
end
- describe "Lightweight Chef::Resource" do
+ context "When an LWRP resource in cookbook l-w-r-p is loaded" do
+ before do
+ @tmpdir = Dir.mktmpdir("lwrp_test")
+ resource_path = File.join(@tmpdir, "foo.rb")
+ IO.write(resource_path, "default_action :create")
+ provider_path = File.join(@tmpdir, "foo.rb")
+ IO.write(provider_path, <<-EOM)
+ action :create do
+ raise "hi"
+ end
+ EOM
+ end
+
+ it "Can find the resource at l_w_r_p_foo" do
+ end
+ end
+ context "When an LWRP resource lwrp_foo is loaded" do
before do
- Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp", "resources", "*"))].each do |file|
- Chef::Resource::LWRPBase.build_from_file("lwrp", file, nil)
+ @tmpdir = Dir.mktmpdir("lwrp_test")
+ @lwrp_path = File.join(@tmpdir, "foo.rb")
+ content = IO.read(File.expand_path("../../data/lwrp/resources/foo.rb", __FILE__))
+ IO.write(@lwrp_path, content)
+ Chef::Resource::LWRPBase.build_from_file("lwrp", @lwrp_path, nil)
+ @original_resource = Chef::ResourceResolver.resolve(:lwrp_foo)
+ end
+
+ after do
+ FileUtils.remove_entry @tmpdir
+ end
+
+ context "And the LWRP is asked to load again, this time with different code" do
+ before do
+ content = IO.read(File.expand_path("../../data/lwrp_override/resources/foo.rb", __FILE__))
+ IO.write(@lwrp_path, content)
+ Chef::Resource::LWRPBase.build_from_file("lwrp", @lwrp_path, nil)
+ end
+
+ it "Should load the old content, and not the new" do
+ resource = Chef::ResourceResolver.resolve(:lwrp_foo)
+ expect(resource).to eq @original_resource
+ expect(resource.default_action).to eq([:pass_buck])
+ expect(Chef.method_defined?(:method_created_by_override_lwrp_foo)).to be_falsey
end
+ end
+ end
+
+ describe "Lightweight Chef::Resource" do
- Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp_override", "resources", "*"))].each do |file|
+ before do
+ Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp", "resources", "*"))].each do |file|
Chef::Resource::LWRPBase.build_from_file("lwrp", file, nil)
end
end
- it "should load the resource into a properly-named class" do
- expect(Chef::Resource.const_get("LwrpFoo")).to be_kind_of(Class)
+ it "should be resolvable with Chef::ResourceResolver.resolve(:lwrp_foo)" do
+ expect(Chef::ResourceResolver.resolve(:lwrp_foo, node: Chef::Node.new)).to eq(get_lwrp(:lwrp_foo))
end
it "should set resource_name" do
- expect(Chef::Resource::LwrpFoo.new("blah").resource_name).to eql(:lwrp_foo)
+ expect(get_lwrp(:lwrp_foo).new("blah").resource_name).to eql(:lwrp_foo)
+ end
+
+ it "should output the resource_name in .to_s" do
+ expect(get_lwrp(:lwrp_foo).new("blah").to_s).to eq "lwrp_foo[blah]"
+ end
+
+ it "should have a class that outputs a reasonable string" do
+ expect(get_lwrp(:lwrp_foo).to_s).to eq "LWRP resource lwrp_foo from cookbook lwrp"
end
it "should add the specified actions to the allowed_actions array" do
- expect(Chef::Resource::LwrpFoo.new("blah").allowed_actions).to include(:pass_buck, :twiddle_thumbs)
+ expect(get_lwrp(:lwrp_foo).new("blah").allowed_actions).to include(:pass_buck, :twiddle_thumbs)
end
it "should set the specified action as the default action" do
- expect(Chef::Resource::LwrpFoo.new("blah").action).to eq(:pass_buck)
+ expect(get_lwrp(:lwrp_foo).new("blah").action).to eq([:pass_buck])
end
it "should create a method for each attribute" do
- expect(Chef::Resource::LwrpFoo.new("blah").methods.map{ |m| m.to_sym}).to include(:monkey)
+ expect(get_lwrp(:lwrp_foo).new("blah").methods.map{ |m| m.to_sym}).to include(:monkey)
end
it "should build attribute methods that respect validation rules" do
- expect { Chef::Resource::LwrpFoo.new("blah").monkey(42) }.to raise_error(ArgumentError)
+ expect { get_lwrp(:lwrp_foo).new("blah").monkey(42) }.to raise_error(ArgumentError)
end
it "should have access to the run context and node during class definition" do
@@ -151,7 +218,7 @@ describe "LWRP" do
Chef::Resource::LWRPBase.build_from_file("lwrp", file, run_context)
end
- cls = Chef::Resource.const_get("LwrpNodeattr")
+ cls = get_lwrp(:lwrp_nodeattr)
expect(cls.node).to be_kind_of(Chef::Node)
expect(cls.run_context).to be_kind_of(Chef::RunContext)
expect(cls.node[:penguin_name]).to eql("jackass")
@@ -175,14 +242,6 @@ describe "LWRP" do
expect(klass.resource_name).to eq(:foo)
end
- context "when creating a new instance" do
- it "raises an exception if resource_name is nil" do
- expect {
- klass.new('blah')
- }.to raise_error(Chef::Exceptions::InvalidResourceSpecification)
- end
- end
-
context "lazy default values" do
let(:klass) do
Class.new(Chef::Resource::LWRPBase) do
@@ -225,17 +284,17 @@ describe "LWRP" do
end
end
- context "when the child does not defined the methods" do
+ context "when the child does not define the methods" do
let(:child) do
Class.new(parent)
end
it "delegates #actions to the parent" do
- expect(child.actions).to eq([:eat, :sleep])
+ expect(child.actions).to eq([:nothing, :eat, :sleep])
end
it "delegates #default_action to the parent" do
- expect(child.default_action).to eq(:eat)
+ expect(child.default_action).to eq([:eat])
end
end
@@ -248,11 +307,11 @@ describe "LWRP" do
end
it "does not delegate #actions to the parent" do
- expect(child.actions).to eq([:dont_eat, :dont_sleep])
+ expect(child.actions).to eq([:nothing, :dont_eat, :dont_sleep])
end
it "does not delegate #default_action to the parent" do
- expect(child.default_action).to eq(:dont_eat)
+ expect(child.default_action).to eq([:dont_eat])
end
end
@@ -273,110 +332,193 @@ describe "LWRP" do
it "amends actions when they are already defined" do
raise_if_deprecated!
- expect(child.actions).to eq([:eat, :sleep, :drink])
+ expect(child.actions).to eq([:nothing, :eat, :sleep, :drink])
+ end
+ end
+ end
+
+ describe "when actions is set to an array" do
+ let(:resource_class) do
+ Class.new(Chef::Resource::LWRPBase) do
+ actions [ :eat, :sleep ]
end
end
+ let(:resource) do
+ resource_class.new('blah')
+ end
+ it "actions includes those actions" do
+ expect(resource_class.actions).to eq [ :nothing, :eat, :sleep ]
+ end
+ it "allowed_actions includes those actions" do
+ expect(resource_class.allowed_actions).to eq [ :nothing, :eat, :sleep ]
+ end
+ it "resource.allowed_actions includes those actions" do
+ expect(resource.allowed_actions).to eq [ :nothing, :eat, :sleep ]
+ end
end
+ describe "when allowed_actions is set to an array" do
+ let(:resource_class) do
+ Class.new(Chef::Resource::LWRPBase) do
+ allowed_actions [ :eat, :sleep ]
+ end
+ end
+ let(:resource) do
+ resource_class.new('blah')
+ end
+ it "actions includes those actions" do
+ expect(resource_class.actions).to eq [ :nothing, :eat, :sleep ]
+ end
+ it "allowed_actions includes those actions" do
+ expect(resource_class.allowed_actions).to eq [ :nothing, :eat, :sleep ]
+ end
+ it "resource.allowed_actions includes those actions" do
+ expect(resource.allowed_actions).to eq [ :nothing, :eat, :sleep ]
+ end
+ end
end
describe "Lightweight Chef::Provider" do
- before do
- @node = Chef::Node.new
- @node.automatic[:platform] = :ubuntu
- @node.automatic[:platform_version] = '8.10'
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, Chef::CookbookCollection.new({}), @events)
- @runner = Chef::Runner.new(@run_context)
- end
- before(:each) do
- Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp", "resources", "*"))].each do |file|
- Chef::Resource::LWRPBase.build_from_file("lwrp", file, @run_context)
+ let(:node) do
+ Chef::Node.new.tap do |n|
+ n.automatic[:platform] = :ubuntu
+ n.automatic[:platform_version] = '8.10'
end
+ end
- Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp_override", "resources", "*"))].each do |file|
- Chef::Resource::LWRPBase.build_from_file("lwrp", file, @run_context)
- end
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
- Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp", "providers", "*"))].each do |file|
- Chef::Provider::LWRPBase.build_from_file("lwrp", file, @run_context)
- end
+ let(:run_context) { Chef::RunContext.new(node, Chef::CookbookCollection.new({}), events) }
+
+ let(:runner) { Chef::Runner.new(run_context) }
+
+ let(:lwrp_cookbok_name) { "lwrp" }
+
+ before do
+ Chef::Provider::LWRPBase.class_eval { @loaded_lwrps = {} }
+ end
- Dir[File.expand_path(File.join(File.dirname(__FILE__), "..", "data", "lwrp_override", "providers", "*"))].each do |file|
- Chef::Provider::LWRPBase.build_from_file("lwrp", file, @run_context)
+ before(:each) do
+ Dir[File.expand_path(File.expand_path("../../data/lwrp/resources/*", __FILE__))].each do |file|
+ Chef::Resource::LWRPBase.build_from_file(lwrp_cookbok_name, file, run_context)
end
+ Dir[File.expand_path(File.expand_path("../../data/lwrp/providers/*", __FILE__))].each do |file|
+ Chef::Provider::LWRPBase.build_from_file(lwrp_cookbok_name, file, run_context)
+ end
end
it "should properly handle a new_resource reference" do
- resource = Chef::Resource::LwrpFoo.new("morpheus")
+ resource = get_lwrp(:lwrp_foo).new("morpheus", run_context)
resource.monkey("bob")
- resource.provider(:lwrp_monkey_name_printer)
- resource.run_context = @run_context
+ resource.provider(get_lwrp_provider(:lwrp_monkey_name_printer))
provider = Chef::Platform.provider_for_resource(resource, :twiddle_thumbs)
provider.action_twiddle_thumbs
end
- it "should load the provider into a properly-named class" do
- expect(Chef::Provider.const_get("LwrpBuckPasser")).to be_kind_of(Class)
- end
+ context "provider class created" do
+ before do
+ @old_treat_deprecation_warnings_as_errors = Chef::Config[:treat_deprecation_warnings_as_errors]
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ end
- it "should create a method for each attribute" do
- new_resource = double("new resource").as_null_object
- expect(Chef::Provider::LwrpBuckPasser.new(nil, new_resource).methods.map{|m|m.to_sym}).to include(:action_pass_buck)
- expect(Chef::Provider::LwrpThumbTwiddler.new(nil, new_resource).methods.map{|m|m.to_sym}).to include(:action_twiddle_thumbs)
+ after do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = @old_treat_deprecation_warnings_as_errors
+ end
+
+ it "should load the provider into a properly-named class" do
+ expect(Chef::Provider.const_get("LwrpBuckPasser")).to be_kind_of(Class)
+ expect(Chef::Provider::LwrpBuckPasser <= Chef::Provider::LWRPBase).to be_truthy
+ end
+
+ it "should create a method for each action" do
+ expect(get_lwrp_provider(:lwrp_buck_passer).instance_methods).to include(:action_pass_buck)
+ expect(get_lwrp_provider(:lwrp_thumb_twiddler).instance_methods).to include(:action_twiddle_thumbs)
+ end
+
+ it "sets itself as a provider for a resource of the same name" do
+ found_providers = Chef::Platform::ProviderHandlerMap.instance.list(node, :lwrp_buck_passer)
+ # we bypass the per-file loading to get the file to load each time,
+ # which creates the LWRP class repeatedly. New things get prepended to
+ # the list of providers.
+ expect(found_providers.first).to eq(get_lwrp_provider(:lwrp_buck_passer))
+ end
+
+ context "with a cookbook with an underscore in the name" do
+
+ let(:lwrp_cookbok_name) { "l_w_r_p" }
+
+ it "sets itself as a provider for a resource of the same name" do
+ found_providers = Chef::Platform::ProviderHandlerMap.instance.list(node, :l_w_r_p_buck_passer)
+ expect(found_providers.size).to eq(1)
+ expect(found_providers.last).to eq(get_lwrp_provider(:l_w_r_p_buck_passer))
+ end
+ end
+
+ context "with a cookbook with a hypen in the name" do
+
+ let(:lwrp_cookbok_name) { "l-w-r-p" }
+
+ it "sets itself as a provider for a resource of the same name" do
+ incorrect_providers = Chef::Platform::ProviderHandlerMap.instance.list(node, :'l-w-r-p_buck_passer')
+ expect(incorrect_providers).to eq([])
+
+ found_providers = Chef::Platform::ProviderHandlerMap.instance.list(node, :l_w_r_p_buck_passer)
+ expect(found_providers.first).to eq(get_lwrp_provider(:l_w_r_p_buck_passer))
+ end
+ end
end
it "should insert resources embedded in the provider into the middle of the resource collection" do
- injector = Chef::Resource::LwrpFoo.new("morpheus", @run_context)
+ injector = get_lwrp(:lwrp_foo).new("morpheus", run_context)
injector.action(:pass_buck)
- injector.provider(:lwrp_buck_passer)
- dummy = Chef::Resource::ZenMaster.new("keanu reeves", @run_context)
+ injector.provider(get_lwrp_provider(:lwrp_buck_passer))
+ dummy = Chef::Resource::ZenMaster.new("keanu reeves", run_context)
dummy.provider(Chef::Provider::Easy)
- @run_context.resource_collection.insert(injector)
- @run_context.resource_collection.insert(dummy)
+ run_context.resource_collection.insert(injector)
+ run_context.resource_collection.insert(dummy)
- Chef::Runner.new(@run_context).converge
+ Chef::Runner.new(run_context).converge
- expect(@run_context.resource_collection[0]).to eql(injector)
- expect(@run_context.resource_collection[1].name).to eql('prepared_thumbs')
- expect(@run_context.resource_collection[2].name).to eql('twiddled_thumbs')
- expect(@run_context.resource_collection[3]).to eql(dummy)
+ expect(run_context.resource_collection[0]).to eql(injector)
+ expect(run_context.resource_collection[1].name).to eql('prepared_thumbs')
+ expect(run_context.resource_collection[2].name).to eql('twiddled_thumbs')
+ expect(run_context.resource_collection[3]).to eql(dummy)
end
it "should insert embedded resources from multiple providers, including from the last position, properly into the resource collection" do
- injector = Chef::Resource::LwrpFoo.new("morpheus", @run_context)
+ injector = get_lwrp(:lwrp_foo).new("morpheus", run_context)
injector.action(:pass_buck)
- injector.provider(:lwrp_buck_passer)
+ injector.provider(get_lwrp_provider(:lwrp_buck_passer))
- injector2 = Chef::Resource::LwrpBar.new("tank", @run_context)
+ injector2 = get_lwrp(:lwrp_bar).new("tank", run_context)
injector2.action(:pass_buck)
- injector2.provider(:lwrp_buck_passer_2)
+ injector2.provider(get_lwrp_provider(:lwrp_buck_passer_2))
- dummy = Chef::Resource::ZenMaster.new("keanu reeves", @run_context)
+ dummy = Chef::Resource::ZenMaster.new("keanu reeves", run_context)
dummy.provider(Chef::Provider::Easy)
- @run_context.resource_collection.insert(injector)
- @run_context.resource_collection.insert(dummy)
- @run_context.resource_collection.insert(injector2)
+ run_context.resource_collection.insert(injector)
+ run_context.resource_collection.insert(dummy)
+ run_context.resource_collection.insert(injector2)
- Chef::Runner.new(@run_context).converge
+ Chef::Runner.new(run_context).converge
- expect(@run_context.resource_collection[0]).to eql(injector)
- expect(@run_context.resource_collection[1].name).to eql('prepared_thumbs')
- expect(@run_context.resource_collection[2].name).to eql('twiddled_thumbs')
- expect(@run_context.resource_collection[3]).to eql(dummy)
- expect(@run_context.resource_collection[4]).to eql(injector2)
- expect(@run_context.resource_collection[5].name).to eql('prepared_eyes')
- expect(@run_context.resource_collection[6].name).to eql('dried_paint_watched')
+ expect(run_context.resource_collection[0]).to eql(injector)
+ expect(run_context.resource_collection[1].name).to eql('prepared_thumbs')
+ expect(run_context.resource_collection[2].name).to eql('twiddled_thumbs')
+ expect(run_context.resource_collection[3]).to eql(dummy)
+ expect(run_context.resource_collection[4]).to eql(injector2)
+ expect(run_context.resource_collection[5].name).to eql('prepared_eyes')
+ expect(run_context.resource_collection[6].name).to eql('dried_paint_watched')
end
it "should properly handle a new_resource reference" do
- resource = Chef::Resource::LwrpFoo.new("morpheus", @run_context)
+ resource = get_lwrp(:lwrp_foo).new("morpheus", run_context)
resource.monkey("bob")
- resource.provider(:lwrp_monkey_name_printer)
+ resource.provider(get_lwrp_provider(:lwrp_monkey_name_printer))
provider = Chef::Platform.provider_for_resource(resource, :twiddle_thumbs)
provider.action_twiddle_thumbs
@@ -385,9 +527,9 @@ describe "LWRP" do
end
it "should properly handle an embedded Resource accessing the enclosing Provider's scope" do
- resource = Chef::Resource::LwrpFoo.new("morpheus", @run_context)
+ resource = get_lwrp(:lwrp_foo).new("morpheus", run_context)
resource.monkey("bob")
- resource.provider(:lwrp_embedded_resource_accesses_providers_scope)
+ resource.provider(get_lwrp_provider(:lwrp_embedded_resource_accesses_providers_scope))
provider = Chef::Platform.provider_for_resource(resource, :twiddle_thumbs)
#provider = @runner.build_provider(resource)
@@ -404,15 +546,15 @@ describe "LWRP" do
# Side effect of lwrp_inline_compiler provider for testing notifications.
$interior_ruby_block_2 = nil
# resource type doesn't matter, so make an existing resource type work with provider.
- @resource = Chef::Resource::LwrpFoo.new("morpheus", @run_context)
+ @resource = get_lwrp(:lwrp_foo).new("morpheus", run_context)
@resource.allowed_actions << :test
@resource.action(:test)
- @resource.provider(:lwrp_inline_compiler)
+ @resource.provider(get_lwrp_provider(:lwrp_inline_compiler))
end
it "does not add interior resources to the exterior resource collection" do
@resource.run_action(:test)
- expect(@run_context.resource_collection).to be_empty
+ expect(run_context.resource_collection).to be_empty
end
context "when interior resources are updated" do
@@ -437,7 +579,144 @@ describe "LWRP" do
end
end
-
end
+ context "resource class created" do
+ before(:context) do
+ @tmpdir = Dir.mktmpdir("lwrp_test")
+ resource_path = File.join(@tmpdir, "once.rb")
+ IO.write(resource_path, "default_action :create")
+
+ @old_treat_deprecation_warnings_as_errors = Chef::Config[:treat_deprecation_warnings_as_errors]
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ Chef::Resource::LWRPBase.build_from_file("lwrp", resource_path, nil)
+ end
+
+ after(:context) do
+ FileUtils.remove_entry @tmpdir
+ Chef::Config[:treat_deprecation_warnings_as_errors] = @old_treat_deprecation_warnings_as_errors
+ end
+
+ it "should load the resource into a properly-named class" do
+ expect(Chef::Resource::LwrpOnce).to be_kind_of(Class)
+ expect(Chef::Resource::LwrpOnce <= Chef::Resource::LWRPBase).to be_truthy
+ end
+
+ it "get_lwrp(:lwrp_once).new is a Chef::Resource::LwrpOnce" do
+ lwrp = get_lwrp(:lwrp_once).new('hi')
+ expect(lwrp.kind_of?(Chef::Resource::LwrpOnce)).to be_truthy
+ expect(lwrp.is_a?(Chef::Resource::LwrpOnce)).to be_truthy
+ expect(get_lwrp(:lwrp_once) === lwrp).to be_truthy
+ expect(Chef::Resource::LwrpOnce === lwrp).to be_truthy
+ end
+
+ it "Chef::Resource::LwrpOnce.new is a get_lwrp(:lwrp_once)" do
+ lwrp = Chef::Resource::LwrpOnce.new('hi')
+ expect(lwrp.kind_of?(get_lwrp(:lwrp_once))).to be_truthy
+ expect(lwrp.is_a?(get_lwrp(:lwrp_once))).to be_truthy
+ expect(get_lwrp(:lwrp_once) === lwrp).to be_truthy
+ expect(Chef::Resource::LwrpOnce === lwrp).to be_truthy
+ end
+
+ it "works even if LwrpOnce exists in the top level" do
+ module ::LwrpOnce
+ end
+ expect(Chef::Resource::LwrpOnce).not_to eq(::LwrpOnce)
+ end
+
+ it "allows monkey patching of the lwrp through Chef::Resource" do
+ monkey = Module.new do
+ def issue_3607
+ end
+ end
+ Chef::Resource::LwrpOnce.send(:include, monkey)
+ expect { get_lwrp(:lwrp_once).new("blah").issue_3607 }.not_to raise_error
+ end
+
+ context "with a subclass of get_lwrp(:lwrp_once)" do
+ let(:subclass) do
+ Class.new(get_lwrp(:lwrp_once))
+ end
+
+ it "subclass.new is a subclass" do
+ lwrp = subclass.new('hi')
+ expect(lwrp.kind_of?(subclass)).to be_truthy
+ expect(lwrp.is_a?(subclass)).to be_truthy
+ expect(subclass === lwrp).to be_truthy
+ expect(lwrp.class === subclass)
+ end
+ it "subclass.new is a Chef::Resource::LwrpOnce" do
+ lwrp = subclass.new('hi')
+ expect(lwrp.kind_of?(Chef::Resource::LwrpOnce)).to be_truthy
+ expect(lwrp.is_a?(Chef::Resource::LwrpOnce)).to be_truthy
+ expect(Chef::Resource::LwrpOnce === lwrp).to be_truthy
+ expect(lwrp.class === Chef::Resource::LwrpOnce)
+ end
+ it "subclass.new is a get_lwrp(:lwrp_once)" do
+ lwrp = subclass.new('hi')
+ expect(lwrp.kind_of?(get_lwrp(:lwrp_once))).to be_truthy
+ expect(lwrp.is_a?(get_lwrp(:lwrp_once))).to be_truthy
+ expect(get_lwrp(:lwrp_once) === lwrp).to be_truthy
+ expect(lwrp.class === get_lwrp(:lwrp_once))
+ end
+ it "Chef::Resource::LwrpOnce.new is *not* a subclass" do
+ lwrp = Chef::Resource::LwrpOnce.new('hi')
+ expect(lwrp.kind_of?(subclass)).to be_falsey
+ expect(lwrp.is_a?(subclass)).to be_falsey
+ expect(subclass === lwrp.class).to be_falsey
+ expect(subclass === Chef::Resource::LwrpOnce).to be_falsey
+ end
+ it "get_lwrp(:lwrp_once).new is *not* a subclass" do
+ lwrp = get_lwrp(:lwrp_once).new('hi')
+ expect(lwrp.kind_of?(subclass)).to be_falsey
+ expect(lwrp.is_a?(subclass)).to be_falsey
+ expect(subclass === lwrp.class).to be_falsey
+ expect(subclass === get_lwrp(:lwrp_once)).to be_falsey
+ end
+ end
+
+ context "with a subclass of Chef::Resource::LwrpOnce" do
+ let(:subclass) do
+ Class.new(Chef::Resource::LwrpOnce)
+ end
+
+ it "subclass.new is a subclass" do
+ lwrp = subclass.new('hi')
+ expect(lwrp.kind_of?(subclass)).to be_truthy
+ expect(lwrp.is_a?(subclass)).to be_truthy
+ expect(subclass === lwrp).to be_truthy
+ expect(lwrp.class === subclass)
+ end
+ it "subclass.new is a Chef::Resource::LwrpOnce" do
+ lwrp = subclass.new('hi')
+ expect(lwrp.kind_of?(Chef::Resource::LwrpOnce)).to be_truthy
+ expect(lwrp.is_a?(Chef::Resource::LwrpOnce)).to be_truthy
+ expect(Chef::Resource::LwrpOnce === lwrp).to be_truthy
+ expect(lwrp.class === Chef::Resource::LwrpOnce)
+ end
+ it "subclass.new is a get_lwrp(:lwrp_once)" do
+ lwrp = subclass.new('hi')
+ expect(lwrp.kind_of?(get_lwrp(:lwrp_once))).to be_truthy
+ expect(lwrp.is_a?(get_lwrp(:lwrp_once))).to be_truthy
+ expect(get_lwrp(:lwrp_once) === lwrp).to be_truthy
+ expect(lwrp.class === get_lwrp(:lwrp_once))
+ end
+ it "Chef::Resource::LwrpOnce.new is *not* a subclass" do
+ lwrp = Chef::Resource::LwrpOnce.new('hi')
+ expect(lwrp.kind_of?(subclass)).to be_falsey
+ expect(lwrp.is_a?(subclass)).to be_falsey
+ expect(subclass === lwrp.class).to be_falsey
+ expect(subclass === Chef::Resource::LwrpOnce).to be_falsey
+ end
+ it "get_lwrp(:lwrp_once).new is *not* a subclass" do
+ lwrp = get_lwrp(:lwrp_once).new('hi')
+ expect(lwrp.kind_of?(subclass)).to be_falsey
+ expect(lwrp.is_a?(subclass)).to be_falsey
+ expect(subclass === lwrp.class).to be_falsey
+ expect(subclass === get_lwrp(:lwrp_once)).to be_falsey
+ end
+ end
+ end
end
+
+
diff --git a/spec/unit/mixin/api_version_request_handling_spec.rb b/spec/unit/mixin/api_version_request_handling_spec.rb
new file mode 100644
index 0000000000..cc5340e424
--- /dev/null
+++ b/spec/unit/mixin/api_version_request_handling_spec.rb
@@ -0,0 +1,127 @@
+#
+# Author:: Tyler Cloke (tyler@chef.io)
+# Copyright:: Copyright 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Mixin::ApiVersionRequestHandling do
+ let(:dummy_class) { Class.new { include Chef::Mixin::ApiVersionRequestHandling } }
+ let(:object) { dummy_class.new }
+
+ describe ".server_client_api_version_intersection" do
+ let(:default_supported_client_versions) { [0,1,2] }
+
+
+ context "when the response code is not 406" do
+ let(:response) { OpenStruct.new(:code => '405') }
+ let(:exception) { Net::HTTPServerException.new("405 Something Else", response) }
+
+ it "returns nil" do
+ expect(object.server_client_api_version_intersection(exception, default_supported_client_versions)).
+ to be_nil
+ end
+
+ end # when the response code is not 406
+
+ context "when the response code is 406" do
+ let(:response) { OpenStruct.new(:code => '406') }
+ let(:exception) { Net::HTTPServerException.new("406 Not Acceptable", response) }
+
+ context "when x-ops-server-api-version header does not exist" do
+ it "returns nil" do
+ expect(object.server_client_api_version_intersection(exception, default_supported_client_versions)).
+ to be_nil
+ end
+ end # when x-ops-server-api-version header does not exist
+
+ context "when x-ops-server-api-version header exists" do
+ let(:min_server_version) { 2 }
+ let(:max_server_version) { 4 }
+ let(:return_hash) {
+ {
+ "min_version" => min_server_version,
+ "max_version" => max_server_version
+ }
+ }
+
+ before(:each) do
+ allow(response).to receive(:[]).with('x-ops-server-api-version').and_return(Chef::JSONCompat.to_json(return_hash))
+ end
+
+ context "when there is no intersection between client and server versions" do
+ shared_examples_for "no intersection between client and server versions" do
+ it "return an array" do
+ expect(object.server_client_api_version_intersection(exception, supported_client_versions)).
+ to be_a_kind_of(Array)
+ end
+
+ it "returns an empty array" do
+ expect(object.server_client_api_version_intersection(exception, supported_client_versions).length).
+ to eq(0)
+ end
+
+ end
+
+ context "when all the versions are higher than the max" do
+ it_should_behave_like "no intersection between client and server versions" do
+ let(:supported_client_versions) { [5,6,7] }
+ end
+ end
+
+ context "when all the versions are lower than the min" do
+ it_should_behave_like "no intersection between client and server versions" do
+ let(:supported_client_versions) { [0,1] }
+ end
+ end
+
+ end # when there is no intersection between client and server versions
+
+ context "when there is an intersection between client and server versions" do
+ context "when multiple versions intersect" do
+ let(:supported_client_versions) { [1,2,3,4,5] }
+
+ it "includes all of the intersection" do
+ expect(object.server_client_api_version_intersection(exception, supported_client_versions)).
+ to eq([2,3,4])
+ end
+ end # when multiple versions intersect
+
+ context "when only the min client version intersects" do
+ let(:supported_client_versions) { [0,1,2] }
+
+ it "includes the intersection" do
+ expect(object.server_client_api_version_intersection(exception, supported_client_versions)).
+ to eq([2])
+ end
+ end # when only the min client version intersects
+
+ context "when only the max client version intersects" do
+ let(:supported_client_versions) { [4,5,6] }
+
+ it "includes the intersection" do
+ expect(object.server_client_api_version_intersection(exception, supported_client_versions)).
+ to eq([4])
+ end
+ end # when only the max client version intersects
+
+ end # when there is an intersection between client and server versions
+
+ end # when x-ops-server-api-version header exists
+ end # when the response code is 406
+
+ end # .server_client_api_version_intersection
+end # Chef::Mixin::ApiVersionRequestHandling
diff --git a/spec/unit/mixin/command_spec.rb b/spec/unit/mixin/command_spec.rb
index e198e3addd..050b261256 100644
--- a/spec/unit/mixin/command_spec.rb
+++ b/spec/unit/mixin/command_spec.rb
@@ -22,7 +22,7 @@ describe Chef::Mixin::Command, :volatile do
if windows?
- pending("TODO MOVE: this is a platform specific integration test.")
+ skip("TODO MOVE: this is a platform specific integration test.")
else
@@ -61,7 +61,6 @@ describe Chef::Mixin::Command, :volatile do
it "returns immediately after the first child process exits" do
expect {Timeout.timeout(10) do
- pid, stdin,stdout,stderr = nil,nil,nil,nil
evil_forker="exit if fork; 10.times { sleep 1}"
popen4("ruby -e '#{evil_forker}'") do |pid,stdin,stdout,stderr|
end
diff --git a/spec/unit/mixin/params_validate_spec.rb b/spec/unit/mixin/params_validate_spec.rb
index 1b61f9b238..3724bbf583 100644
--- a/spec/unit/mixin/params_validate_spec.rb
+++ b/spec/unit/mixin/params_validate_spec.rb
@@ -21,6 +21,8 @@ require 'spec_helper'
class TinyClass
include Chef::Mixin::ParamsValidate
+ attr_reader :name
+
def music(is_good=true)
is_good
end
@@ -331,91 +333,77 @@ describe Chef::Mixin::ParamsValidate do
it "asserts that a value returns false from a predicate method" do
expect do
@vo.validate({:not_blank => "should pass"},
- {:not_blank => {:cannot_be => :nil, :cannot_be => :empty}})
+ {:not_blank => {:cannot_be => [ :nil, :empty ]}})
end.not_to raise_error
expect do
@vo.validate({:not_blank => ""},
- {:not_blank => {:cannot_be => :nil, :cannot_be => :empty}})
+ {:not_blank => {:cannot_be => [ :nil, :empty ]}})
end.to raise_error(Chef::Exceptions::ValidationFailed)
end
- def self.test_set_or_return_method(method)
- # caller is responsible for passing in the right arg to get 'return' behavior
- return_arg = method == :nillable_set_or_return ? TinyClass::NULL_ARG : nil
-
- it "#{method} should set and return a value, then return the same value" do
- value = "meow"
- expect(@vo.send(method,:test, value, {}).object_id).to eq(value.object_id)
- expect(@vo.send(method,:test, return_arg, {}).object_id).to eq(value.object_id)
- end
-
- it "#{method} should set and return a default value when the argument is nil, then return the same value" do
- value = "meow"
- expect(@vo.send(method,:test, return_arg, { :default => value }).object_id).to eq(value.object_id)
- expect(@vo.send(method,:test, return_arg, {}).object_id).to eq(value.object_id)
- end
-
- it "#{method} should raise an ArgumentError when argument is nil and required is true" do
- expect {
- @vo.send(method,:test, return_arg, { :required => true })
- }.to raise_error(ArgumentError)
- end
-
- it "#{method} should not raise an error when argument is nil and required is false" do
- expect {
- @vo.send(method,:test, return_arg, { :required => false })
- }.not_to raise_error
- end
-
- it "#{method} should set and return @name, then return @name for foo when argument is nil" do
- value = "meow"
- expect(@vo.send(method,:name, value, { }).object_id).to eq(value.object_id)
- expect(@vo.send(method,:foo, return_arg, { :name_attribute => true }).object_id).to eq(value.object_id)
- end
-
- it "#{method} should allow DelayedEvaluator instance to be set for value regardless of restriction" do
- value = Chef::DelayedEvaluator.new{ 'test' }
- @vo.send(method,:test, value, {:kind_of => Numeric})
- end
-
- it "#{method} should raise an error when delayed evaluated attribute is not valid" do
- value = Chef::DelayedEvaluator.new{ 'test' }
- @vo.send(method,:test, value, {:kind_of => Numeric})
- expect do
- @vo.send(method,:test, return_arg, {:kind_of => Numeric})
- end.to raise_error(Chef::Exceptions::ValidationFailed)
- end
-
- it "#{method} should create DelayedEvaluator instance when #lazy is used" do
- @vo.send(method,:delayed, @vo.lazy{ 'test' }, {})
- expect(@vo.instance_variable_get(:@delayed)).to be_a(Chef::DelayedEvaluator)
- end
-
- it "#{method} should execute block on each call when DelayedEvaluator" do
- value = 'fubar'
- @vo.send(method,:test, @vo.lazy{ value }, {})
- expect(@vo.send(method,:test, return_arg, {})).to eq('fubar')
- value = 'foobar'
- expect(@vo.send(method,:test, return_arg, {})).to eq('foobar')
- value = 'fauxbar'
- expect(@vo.send(method,:test, return_arg, {})).to eq('fauxbar')
- end
-
- it "#{method} should not evaluate non DelayedEvaluator instances" do
- value = lambda{ 'test' }
- @vo.send(method,:test, value, {})
- expect(@vo.send(method,:test, return_arg, {}).object_id).to eq(value.object_id)
- expect(@vo.send(method,:test, return_arg, {})).to be_a(Proc)
- end
+ it "should set and return a value, then return the same value" do
+ value = "meow"
+ expect(@vo.set_or_return(:test, value, {}).object_id).to eq(value.object_id)
+ expect(@vo.set_or_return(:test, nil, {}).object_id).to eq(value.object_id)
+ end
+
+ it "should set and return a default value when the argument is nil, then return the same value" do
+ value = "meow"
+ expect(@vo.set_or_return(:test, nil, { :default => value }).object_id).to eq(value.object_id)
+ expect(@vo.set_or_return(:test, nil, {}).object_id).to eq(value.object_id)
+ end
+
+ it "should raise an ArgumentError when argument is nil and required is true" do
+ expect {
+ @vo.set_or_return(:test, nil, { :required => true })
+ }.to raise_error(ArgumentError)
+ end
+
+ it "should not raise an error when argument is nil and required is false" do
+ expect {
+ @vo.set_or_return(:test, nil, { :required => false })
+ }.not_to raise_error
end
- test_set_or_return_method(:set_or_return)
- test_set_or_return_method(:nillable_set_or_return)
+ it "should set and return @name, then return @name for foo when argument is nil" do
+ value = "meow"
+ expect(@vo.set_or_return(:name, value, { }).object_id).to eq(value.object_id)
+ expect(@vo.set_or_return(:foo, nil, { :name_attribute => true }).object_id).to eq(value.object_id)
+ end
- it "nillable_set_or_return supports nilling values" do
- expect(@vo.nillable_set_or_return(:test, "meow", {})).to eq("meow")
- expect(@vo.nillable_set_or_return(:test, TinyClass::NULL_ARG, {})).to eq("meow")
- expect(@vo.nillable_set_or_return(:test, nil, {})).to be_nil
- expect(@vo.nillable_set_or_return(:test, TinyClass::NULL_ARG, {})).to be_nil
+ it "should allow DelayedEvaluator instance to be set for value regardless of restriction" do
+ value = Chef::DelayedEvaluator.new{ 'test' }
+ @vo.set_or_return(:test, value, {:kind_of => Numeric})
end
+
+ it "should raise an error when delayed evaluated attribute is not valid" do
+ value = Chef::DelayedEvaluator.new{ 'test' }
+ @vo.set_or_return(:test, value, {:kind_of => Numeric})
+ expect do
+ @vo.set_or_return(:test, nil, {:kind_of => Numeric})
+ end.to raise_error(Chef::Exceptions::ValidationFailed)
+ end
+
+ it "should create DelayedEvaluator instance when #lazy is used" do
+ @vo.set_or_return(:delayed, @vo.lazy{ 'test' }, {})
+ expect(@vo.instance_variable_get(:@delayed)).to be_a(Chef::DelayedEvaluator)
+ end
+
+ it "should execute block on each call when DelayedEvaluator" do
+ value = 'fubar'
+ @vo.set_or_return(:test, @vo.lazy{ value }, {})
+ expect(@vo.set_or_return(:test, nil, {})).to eq('fubar')
+ value = 'foobar'
+ expect(@vo.set_or_return(:test, nil, {})).to eq('foobar')
+ value = 'fauxbar'
+ expect(@vo.set_or_return(:test, nil, {})).to eq('fauxbar')
+ end
+
+ it "should not evaluate non DelayedEvaluator instances" do
+ value = lambda{ 'test' }
+ @vo.set_or_return(:test, value, {})
+ expect(@vo.set_or_return(:test, nil, {}).object_id).to eq(value.object_id)
+ expect(@vo.set_or_return(:test, nil, {})).to be_a(Proc)
+ end
+
end
diff --git a/spec/unit/mixin/path_sanity_spec.rb b/spec/unit/mixin/path_sanity_spec.rb
index ec8e182e3d..3a924b9538 100644
--- a/spec/unit/mixin/path_sanity_spec.rb
+++ b/spec/unit/mixin/path_sanity_spec.rb
@@ -35,7 +35,7 @@ describe Chef::Mixin::PathSanity do
@gem_bindir = '/some/gem/bin'
allow(Gem).to receive(:bindir).and_return(@gem_bindir)
allow(RbConfig::CONFIG).to receive(:[]).with('bindir').and_return(@ruby_bindir)
- allow(Chef::Platform).to receive(:windows?).and_return(false)
+ allow(ChefConfig).to receive(:windows?).and_return(false)
end
it "adds all useful PATHs even if environment is an empty hash" do
@@ -77,7 +77,7 @@ describe Chef::Mixin::PathSanity do
gem_bindir = 'C:\gems\bin'
allow(Gem).to receive(:bindir).and_return(gem_bindir)
allow(RbConfig::CONFIG).to receive(:[]).with('bindir').and_return(ruby_bindir)
- allow(Chef::Platform).to receive(:windows?).and_return(true)
+ allow(ChefConfig).to receive(:windows?).and_return(true)
env = {"PATH" => 'C:\Windows\system32;C:\mr\softie'}
@sanity.enforce_path_sanity(env)
expect(env["PATH"]).to eq("C:\\Windows\\system32;C:\\mr\\softie;#{ruby_bindir};#{gem_bindir}")
diff --git a/spec/unit/mixin/powershell_out_spec.rb b/spec/unit/mixin/powershell_out_spec.rb
new file mode 100644
index 0000000000..0fede582fa
--- /dev/null
+++ b/spec/unit/mixin/powershell_out_spec.rb
@@ -0,0 +1,70 @@
+#
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'chef/mixin/powershell_out'
+
+describe Chef::Mixin::PowershellOut do
+ let(:shell_out_class) { Class.new { include Chef::Mixin::PowershellOut } }
+ subject(:object) { shell_out_class.new }
+ let(:architecture) { "something" }
+ let(:flags) {
+ "-NoLogo -NonInteractive -NoProfile -ExecutionPolicy Unrestricted -InputFormat None"
+ }
+
+ describe "#powershell_out" do
+ it "runs a command and returns the shell_out object" do
+ ret = double("Mixlib::ShellOut")
+ expect(object).to receive(:shell_out).with(
+ "powershell.exe #{flags} -Command \"Get-Process\"",
+ {}
+ ).and_return(ret)
+ expect(object.powershell_out("Get-Process")).to eql(ret)
+ end
+
+ it "passes options" do
+ ret = double("Mixlib::ShellOut")
+ expect(object).to receive(:shell_out).with(
+ "powershell.exe #{flags} -Command \"Get-Process\"",
+ timeout: 600
+ ).and_return(ret)
+ expect(object.powershell_out("Get-Process", timeout: 600)).to eql(ret)
+ end
+ end
+
+ describe "#powershell_out!" do
+ it "runs a command and returns the shell_out object" do
+ mixlib_shellout = double("Mixlib::ShellOut")
+ expect(object).to receive(:shell_out).with(
+ "powershell.exe #{flags} -Command \"Get-Process\"",
+ {}
+ ).and_return(mixlib_shellout)
+ expect(mixlib_shellout).to receive(:error!)
+ expect(object.powershell_out!("Get-Process")).to eql(mixlib_shellout)
+ end
+
+ it "passes options" do
+ mixlib_shellout = double("Mixlib::ShellOut")
+ expect(object).to receive(:shell_out).with(
+ "powershell.exe #{flags} -Command \"Get-Process\"",
+ timeout: 600
+ ).and_return(mixlib_shellout)
+ expect(mixlib_shellout).to receive(:error!)
+ expect(object.powershell_out!("Get-Process", timeout: 600)).to eql(mixlib_shellout)
+ end
+ end
+end
diff --git a/spec/unit/mixin/powershell_type_coercions_spec.rb b/spec/unit/mixin/powershell_type_coercions_spec.rb
new file mode 100644
index 0000000000..988c3926c1
--- /dev/null
+++ b/spec/unit/mixin/powershell_type_coercions_spec.rb
@@ -0,0 +1,72 @@
+#
+# Author:: Jay Mundrawala (<jdm@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'chef/mixin/powershell_type_coercions'
+require 'base64'
+
+class Chef::PSTypeTester
+ include Chef::Mixin::PowershellTypeCoercions
+end
+
+describe Chef::Mixin::PowershellTypeCoercions do
+ let (:test_class) { Chef::PSTypeTester.new }
+
+ describe '#translate_type' do
+ it 'should single quote a string' do
+ expect(test_class.translate_type('foo')).to eq("'foo'")
+ end
+
+ ["'", '"', '#', '`'].each do |c|
+ it "should base64 encode a string that contains #{c}" do
+ expect(test_class.translate_type("#{c}")).to match(Base64.strict_encode64(c))
+ end
+ end
+
+ it 'should not quote an integer' do
+ expect(test_class.translate_type(123)).to eq('123')
+ end
+
+ it 'should not quote a floating point number' do
+ expect(test_class.translate_type(123.4)).to eq('123.4')
+ end
+
+ it 'should return $false when an instance of FalseClass is provided' do
+ expect(test_class.translate_type(false)).to eq('$false')
+ end
+
+ it 'should return $true when an instance of TrueClass is provided' do
+ expect(test_class.translate_type(true)).to eq('$true')
+ end
+
+ it 'should translate all members of a hash and wrap them in @{} separated by ;' do
+ expect(test_class.translate_type({"a" => 1, "b" => 1.2, "c" => false, "d" => true
+ })).to eq("@{a=1;b=1.2;c=$false;d=$true}")
+ end
+
+ it 'should translat all members of an array and them by a ,' do
+ expect(test_class.translate_type([true, false])).to eq('@($true,$false)')
+ end
+
+ it 'should fall back :to_psobject if we have not defined at explicit rule' do
+ ps_obj = double("PSObject")
+ expect(ps_obj).to receive(:to_psobject).and_return('$true')
+ expect(test_class.translate_type(ps_obj)).to eq('($true)')
+ end
+ end
+end
diff --git a/spec/unit/mixin/template_spec.rb b/spec/unit/mixin/template_spec.rb
index f02bd34b8f..6a867b5f9a 100644
--- a/spec/unit/mixin/template_spec.rb
+++ b/spec/unit/mixin/template_spec.rb
@@ -39,7 +39,7 @@ describe Chef::Mixin::Template, "render_template" do
describe "when running on windows" do
before do
- allow(Chef::Platform).to receive(:windows?).and_return(true)
+ allow(ChefConfig).to receive(:windows?).and_return(true)
end
it "should render the templates with windows line endings" do
@@ -54,7 +54,7 @@ describe Chef::Mixin::Template, "render_template" do
describe "when running on unix" do
before do
- allow(Chef::Platform).to receive(:windows?).and_return(false)
+ allow(ChefConfig).to receive(:windows?).and_return(false)
end
it "should render the templates with unix line endings" do
diff --git a/spec/unit/mixin/unformatter_spec.rb b/spec/unit/mixin/unformatter_spec.rb
new file mode 100644
index 0000000000..2eae0ac9bb
--- /dev/null
+++ b/spec/unit/mixin/unformatter_spec.rb
@@ -0,0 +1,61 @@
+#
+# Author:: Jay Mundrawala (<jdm@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'chef/mixin/unformatter'
+
+class Chef::UnformatterTest
+ include Chef::Mixin::Unformatter
+
+ def foo
+ end
+
+end
+
+describe Chef::Mixin::Unformatter do
+ let (:unformatter) { Chef::UnformatterTest.new }
+ let (:message) { "Test Message" }
+
+ describe "#write" do
+ context "with a timestamp" do
+ it "sends foo to itself when the message is of severity foo" do
+ expect(unformatter).to receive(:foo).with(message)
+ unformatter.write("[time] foo: #{message}")
+ end
+
+ it "sends foo to itself when the message is of severity FOO" do
+ expect(unformatter).to receive(:foo).with(message)
+ unformatter.write("[time] FOO: #{message}")
+ end
+ end
+
+ context "without a timestamp" do
+ it "sends foo to itself when the message is of severity foo" do
+ expect(unformatter).to receive(:foo).with(message)
+ unformatter.write("foo: #{message}")
+ end
+
+ it "sends foo to itself when the message is of severity FOO" do
+ expect(unformatter).to receive(:foo).with(message)
+ unformatter.write("FOO: #{message}")
+ end
+ end
+
+ end
+
+end
diff --git a/spec/unit/mixin/uris_spec.rb b/spec/unit/mixin/uris_spec.rb
new file mode 100644
index 0000000000..d4985c4f67
--- /dev/null
+++ b/spec/unit/mixin/uris_spec.rb
@@ -0,0 +1,57 @@
+#
+# Author:: Jay Mundrawala (<jdm@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'chef/mixin/uris'
+
+class Chef::UrisTest
+ include Chef::Mixin::Uris
+end
+
+describe Chef::Mixin::Uris do
+ let (:uris) { Chef::UrisTest.new }
+
+ describe "#uri_scheme?" do
+ it "matches 'scheme://foo.com'" do
+ expect(uris.uri_scheme?('scheme://foo.com')).to eq(true)
+ end
+
+ it "does not match 'c:/foo.com'" do
+ expect(uris.uri_scheme?('c:/foo.com')).to eq(false)
+ end
+
+ it "does not match '/usr/bin/foo.com'" do
+ expect(uris.uri_scheme?('/usr/bin/foo.com')).to eq(false)
+ end
+
+ it "does not match 'c:/foo.com://bar.com'" do
+ expect(uris.uri_scheme?('c:/foo.com://bar.com')).to eq(false)
+ end
+ end
+
+ describe "#as_uri" do
+ it "parses a file scheme uri with spaces" do
+ expect{ uris.as_uri("file:///c:/foo bar.txt") }.not_to raise_exception
+ end
+
+ it "returns a URI object" do
+ expect( uris.as_uri("file:///c:/foo bar.txt") ).to be_a(URI)
+ end
+ end
+
+end
diff --git a/spec/unit/mixin/windows_architecture_helper_spec.rb b/spec/unit/mixin/windows_architecture_helper_spec.rb
index 3803d69371..55eca28dc2 100644
--- a/spec/unit/mixin/windows_architecture_helper_spec.rb
+++ b/spec/unit/mixin/windows_architecture_helper_spec.rb
@@ -60,23 +60,28 @@ describe Chef::Mixin::WindowsArchitectureHelper do
end
end
- it "returns true for each supported desired architecture for all nodes with each valid architecture passed to node_supports_windows_architecture" do
- enumerate_architecture_node_combinations(true)
+ it "returns true only for supported desired architecture passed to node_supports_windows_architecture" do
+ with_node_architecture_combinations do | node, desired_arch |
+ expect(node_supports_windows_architecture?(node, desired_arch)).to be true if (node_windows_architecture(node) == :x86_64 || desired_arch == :i386 )
+ expect(node_supports_windows_architecture?(node, desired_arch)).to be false if (node_windows_architecture(node) == :i386 && desired_arch == :x86_64 )
+ end
end
- it "returns false for each unsupported desired architecture for all nodes with each valid architecture passed to node_supports_windows_architecture?" do
- enumerate_architecture_node_combinations(true)
+ it "returns true only when forced_32bit_override_required? has 64-bit node architecture and 32-bit desired architecture" do
+ with_node_architecture_combinations do | node, desired_arch |
+ expect(forced_32bit_override_required?(node, desired_arch)).to be true if ((node_windows_architecture(node) == :x86_64) && (desired_arch == :i386) && !is_i386_process_on_x86_64_windows?)
+ expect(forced_32bit_override_required?(node, desired_arch)).to be false if ! ((node_windows_architecture(node) == :x86_64) && (desired_arch == :i386))
+ end
end
- def enumerate_architecture_node_combinations(only_valid_combinations)
+ def with_node_architecture_combinations
@valid_architectures.each do | node_architecture |
new_node = Chef::Node.new
new_node.default["kernel"] = Hash.new
new_node.default["kernel"][:machine] = node_architecture.to_s
- @valid_architectures.each do | supported_architecture |
- expect(node_supports_windows_architecture?(new_node, supported_architecture)).to eq(true) if only_valid_combinations && (supported_architecture != :x86_64 && node_architecture != :i386 )
- expect(node_supports_windows_architecture?(new_node, supported_architecture)).to eq(false) if ! only_valid_combinations && (supported_architecture == :x86_64 && node_architecture == :i386 )
+ @valid_architectures.each do | architecture |
+ yield new_node, architecture if block_given?
end
end
end
diff --git a/spec/unit/node_map_spec.rb b/spec/unit/node_map_spec.rb
index fe7372961b..7b37ea59f4 100644
--- a/spec/unit/node_map_spec.rb
+++ b/spec/unit/node_map_spec.rb
@@ -131,9 +131,25 @@ describe Chef::NodeMap do
allow(node).to receive(:[]).with(:platform_version).and_return("6.0")
expect(node_map.get(node, :thing)).to eql(nil)
end
+
+ context "when there is a less specific definition" do
+ before do
+ node_map.set(:thing, :bar, platform_family: "rhel")
+ end
+
+ it "returns the value when the node matches" do
+ allow(node).to receive(:[]).with(:platform_family).and_return("rhel")
+ allow(node).to receive(:[]).with(:platform_version).and_return("7.0")
+ expect(node_map.get(node, :thing)).to eql(:foo)
+ end
+ end
end
describe "resource back-compat testing" do
+ before :each do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ end
+
it "should handle :on_platforms => :all" do
node_map.set(:chef_gem, :foo, :on_platforms => :all)
allow(node).to receive(:[]).with(:platform).and_return("windows")
@@ -152,4 +168,3 @@ describe Chef::NodeMap do
end
end
-
diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb
index 5939403ce6..b7752eb734 100644
--- a/spec/unit/node_spec.rb
+++ b/spec/unit/node_spec.rb
@@ -672,6 +672,13 @@ describe Chef::Node do
expect(node.run_list).to eq([ "role[base]", "recipe[chef::server]" ])
end
+ it "sets the node chef_environment" do
+ attrs = { "chef_environment" => "foo_environment", "bar" => "baz" }
+ expect(node.consume_chef_environment(attrs)).to eq({ "bar" => "baz" })
+ expect(node.chef_environment).to eq("foo_environment")
+ expect(node['chef_environment']).to be nil
+ end
+
it "should overwrites the run list with the run list it consumes" do
node.consume_run_list "recipes" => [ "one", "two" ]
node.consume_run_list "recipes" => [ "three" ]
@@ -1106,7 +1113,7 @@ describe Chef::Node do
expect(serialized_node.run_list).to eq(node.run_list)
end
- include_examples "to_json equalivent to Chef::JSONCompat.to_json" do
+ include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
let(:jsonable) {
node.from_file(File.expand_path("nodes/test.example.com.rb", CHEF_SPEC_DATA))
node
diff --git a/spec/unit/platform/query_helpers_spec.rb b/spec/unit/platform/query_helpers_spec.rb
index 7aafc287ea..33d4c2c3b7 100644
--- a/spec/unit/platform/query_helpers_spec.rb
+++ b/spec/unit/platform/query_helpers_spec.rb
@@ -20,7 +20,7 @@ require 'spec_helper'
describe "Chef::Platform#windows_server_2003?" do
it "returns false early when not on windows" do
- allow(Chef::Platform).to receive(:windows?).and_return(false)
+ allow(ChefConfig).to receive(:windows?).and_return(false)
expect(Chef::Platform).not_to receive(:require)
expect(Chef::Platform.windows_server_2003?).to be_falsey
end
@@ -53,3 +53,25 @@ describe 'Chef::Platform#supports_dsc?' do
end
end
end
+
+describe 'Chef::Platform#supports_dsc_invoke_resource?' do
+ it 'returns false if powershell is not present' do
+ node = Chef::Node.new
+ expect(Chef::Platform.supports_dsc_invoke_resource?(node)).to be_falsey
+ end
+
+ ['1.0', '2.0', '3.0', '4.0', '5.0.10017.9'].each do |version|
+ it "returns false for Powershell #{version}" do
+ node = Chef::Node.new
+ node.automatic[:languages][:powershell][:version] = version
+ expect(Chef::Platform.supports_dsc_invoke_resource?(node)).to be_falsey
+ end
+ end
+
+ it "returns true for Powershell 5.0.10018.0" do
+ node = Chef::Node.new
+ node.automatic[:languages][:powershell][:version] = "5.0.10018.0"
+ expect(Chef::Platform.supports_dsc_invoke_resource?(node)).to be_truthy
+ end
+end
+
diff --git a/spec/unit/platform_spec.rb b/spec/unit/platform_spec.rb
index fb65ef0fea..34b46f657f 100644
--- a/spec/unit/platform_spec.rb
+++ b/spec/unit/platform_spec.rb
@@ -18,34 +18,6 @@
require 'spec_helper'
-describe "Chef::Platform supports" do
- [
- :mac_os_x,
- :mac_os_x_server,
- :freebsd,
- :ubuntu,
- :debian,
- :centos,
- :fedora,
- :suse,
- :opensuse,
- :redhat,
- :oracle,
- :gentoo,
- :arch,
- :solaris,
- :mswin,
- :mingw32,
- :windows,
- :gcel,
- :ibm_powerkvm
- ].each do |platform|
- it "#{platform}" do
- expect(Chef::Platform.platforms).to have_key(platform)
- end
- end
-end
-
describe Chef::Platform do
context "while testing with fake data" do
@@ -131,7 +103,7 @@ describe Chef::Platform do
end
it "should raise an exception if a provider cannot be found for a resource type" do
- expect { Chef::Platform.find_provider("Darwin", "9.2.2", :coffee) }.to raise_error(ArgumentError)
+ expect { Chef::Platform.find_provider("Darwin", "9.2.2", :coffee) }.to raise_error(Chef::Exceptions::ProviderNotFound)
end
it "should look up a provider for a resource with a Chef::Resource object" do
@@ -266,41 +238,4 @@ describe Chef::Platform do
end
- context "while testing the configured platform data" do
-
- it "should use the solaris package provider on Solaris <11" do
- pmap = Chef::Platform.find("Solaris2", "5.9")
- expect(pmap[:package]).to eql(Chef::Provider::Package::Solaris)
- end
-
- it "should use the IPS package provider on Solaris 11" do
- pmap = Chef::Platform.find("Solaris2", "5.11")
- expect(pmap[:package]).to eql(Chef::Provider::Package::Ips)
- end
-
- it "should use the Redhat service provider on SLES11" do
- 1.upto(3) do |sp|
- pmap = Chef::Platform.find("SUSE", "11.#{sp}")
- expect(pmap[:service]).to eql(Chef::Provider::Service::Redhat)
- end
- end
-
- it "should use the Systemd service provider on SLES12" do
- pmap = Chef::Platform.find("SUSE", "12.0")
- expect(pmap[:service]).to eql(Chef::Provider::Service::Systemd)
- end
-
- it "should use the SUSE group provider on SLES11" do
- 1.upto(3) do |sp|
- pmap = Chef::Platform.find("SUSE", "11.#{sp}")
- expect(pmap[:group]).to eql(Chef::Provider::Group::Suse)
- end
- end
-
- it "should use the Gpasswd group provider on SLES12" do
- pmap = Chef::Platform.find("SUSE", "12.0")
- expect(pmap[:group]).to eql(Chef::Provider::Group::Gpasswd)
- end
- end
-
end
diff --git a/spec/unit/policy_builder/policyfile_spec.rb b/spec/unit/policy_builder/policyfile_spec.rb
index 8b6e928a46..5fa00d8f2b 100644
--- a/spec/unit/policy_builder/policyfile_spec.rb
+++ b/spec/unit/policy_builder/policyfile_spec.rb
@@ -166,13 +166,17 @@ describe Chef::PolicyBuilder::Policyfile do
end
before do
- # TODO: agree on this name and logic.
+ Chef::Config[:policy_document_native_api] = false
Chef::Config[:deployment_group] = "example-policy-stage"
allow(policy_builder).to receive(:http_api).and_return(http_api)
end
describe "when using compatibility mode (policy_document_native_api == false)" do
+ before do
+ Chef::Config[:deployment_group] = "example-policy-stage"
+ end
+
context "when the deployment group cannot be loaded" do
let(:error404) { Net::HTTPServerException.new("404 message", :body) }
@@ -256,7 +260,7 @@ describe Chef::PolicyBuilder::Policyfile do
context "and policy_name and policy_group are configured" do
- let(:policy_relative_url) { "policies/policy-stage/example" }
+ let(:policy_relative_url) { "policy_groups/policy-stage/policies/example" }
before do
expect(http_api).to receive(:get).with(policy_relative_url).and_return(parsed_policyfile_json)
@@ -386,8 +390,11 @@ describe Chef::PolicyBuilder::Policyfile do
describe "fetching the desired cookbook set" do
- let(:example1_cookbook_object) { double("Chef::CookbookVersion for example1 cookbook") }
- let(:example2_cookbook_object) { double("Chef::CookbookVersion for example2 cookbook") }
+ let(:example1_cookbook_data) { double("CookbookVersion Hash for example1 cookbook") }
+ let(:example2_cookbook_data) { double("CookbookVersion Hash for example2 cookbook") }
+
+ let(:example1_cookbook_object) { double("Chef::CookbookVersion for example1 cookbook", version: "0.1.2") }
+ let(:example2_cookbook_object) { double("Chef::CookbookVersion for example2 cookbook", version: "1.2.3") }
let(:expected_cookbook_hash) do
{ "example1" => example1_cookbook_object, "example2" => example2_cookbook_object }
@@ -396,9 +403,12 @@ describe Chef::PolicyBuilder::Policyfile do
let(:example1_xyz_version) { example1_lock_data["dotted_decimal_identifier"] }
let(:example2_xyz_version) { example2_lock_data["dotted_decimal_identifier"] }
+ let(:example1_identifier) { example1_lock_data["identifier"] }
+ let(:example2_identifier) { example2_lock_data["identifier"] }
+
let(:cookbook_synchronizer) { double("Chef::CookbookSynchronizer") }
- shared_examples_for "fetching cookbooks" do
+ shared_examples "fetching cookbooks when they don't exist" do
context "and a cookbook is missing" do
let(:error404) { Net::HTTPServerException.new("404 message", :body) }
@@ -418,7 +428,9 @@ describe Chef::PolicyBuilder::Policyfile do
end
end
+ end
+ shared_examples_for "fetching cookbooks when they exist" do
context "and the cookbooks can be fetched" do
before do
expect(Chef::Node).to receive(:find_or_create).with(node_name).and_return(node)
@@ -426,11 +438,6 @@ describe Chef::PolicyBuilder::Policyfile do
policy_builder.load_node
policy_builder.build_node
- expect(http_api).to receive(:get).with(cookbook1_url).
- and_return(example1_cookbook_object)
- expect(http_api).to receive(:get).with(cookbook2_url).
- and_return(example2_cookbook_object)
-
allow(Chef::CookbookSynchronizer).to receive(:new).
with(expected_cookbook_hash, events).
and_return(cookbook_synchronizer)
@@ -457,11 +464,23 @@ describe Chef::PolicyBuilder::Policyfile do
end # shared_examples_for "fetching cookbooks"
context "when using compatibility mode (policy_document_native_api == false)" do
- include_examples "fetching cookbooks" do
+ let(:cookbook1_url) { "cookbooks/example1/#{example1_xyz_version}" }
+ let(:cookbook2_url) { "cookbooks/example2/#{example2_xyz_version}" }
+
+ context "when the cookbooks don't exist on the server" do
+ include_examples "fetching cookbooks when they don't exist"
+ end
- let(:cookbook1_url) { "cookbooks/example1/#{example1_xyz_version}" }
- let(:cookbook2_url) { "cookbooks/example2/#{example2_xyz_version}" }
+ context "when the cookbooks exist on the server" do
+ before do
+ expect(http_api).to receive(:get).with(cookbook1_url).
+ and_return(example1_cookbook_object)
+ expect(http_api).to receive(:get).with(cookbook2_url).
+ and_return(example2_cookbook_object)
+ end
+
+ include_examples "fetching cookbooks when they exist"
end
end
@@ -474,13 +493,33 @@ describe Chef::PolicyBuilder::Policyfile do
Chef::Config[:policy_name] = "example"
end
- include_examples "fetching cookbooks" do
+ let(:cookbook1_url) { "cookbook_artifacts/example1/#{example1_identifier}" }
+ let(:cookbook2_url) { "cookbook_artifacts/example2/#{example2_identifier}" }
+
+ context "when the cookbooks don't exist on the server" do
+ include_examples "fetching cookbooks when they don't exist"
+ end
+
+
+ context "when the cookbooks exist on the server" do
+
+ before do
+ expect(http_api).to receive(:get).with(cookbook1_url).
+ and_return(example1_cookbook_data)
+ expect(http_api).to receive(:get).with(cookbook2_url).
+ and_return(example2_cookbook_data)
+
+ expect(Chef::CookbookVersion).to receive(:from_cb_artifact_data).with(example1_cookbook_data).
+ and_return(example1_cookbook_object)
+ expect(Chef::CookbookVersion).to receive(:from_cb_artifact_data).with(example2_cookbook_data).
+ and_return(example2_cookbook_object)
+ end
- let(:cookbook1_url) { "cookbook_artifacts/example1/#{example1_xyz_version}" }
- let(:cookbook2_url) { "cookbook_artifacts/example2/#{example2_xyz_version}" }
+ include_examples "fetching cookbooks when they exist"
end
+
end
end
diff --git a/spec/unit/property/state_spec.rb b/spec/unit/property/state_spec.rb
new file mode 100644
index 0000000000..e7fee0387f
--- /dev/null
+++ b/spec/unit/property/state_spec.rb
@@ -0,0 +1,506 @@
+require 'support/shared/integration/integration_helper'
+
+describe "Chef::Resource#identity and #state" do
+ include IntegrationSupport
+
+ class NewResourceNamer
+ @i = 0
+ def self.next
+ "chef_resource_property_spec_#{@i += 1}"
+ end
+ end
+
+ def self.new_resource_name
+ NewResourceNamer.next
+ end
+
+ let(:resource_class) do
+ new_resource_name = self.class.new_resource_name
+ Class.new(Chef::Resource) do
+ resource_name new_resource_name
+ end
+ end
+
+ let(:resource) do
+ resource_class.new("blah")
+ end
+
+ def self.english_join(values)
+ return '<nothing>' if values.size == 0
+ return values[0].inspect if values.size == 1
+ "#{values[0..-2].map { |v| v.inspect }.join(", ")} and #{values[-1].inspect}"
+ end
+
+ def self.with_property(*properties, &block)
+ tags_index = properties.find_index { |p| !p.is_a?(String)}
+ if tags_index
+ properties, tags = properties[0..tags_index-1], properties[tags_index..-1]
+ else
+ tags = []
+ end
+ properties = properties.map { |property| "property #{property}" }
+ context "With properties #{english_join(properties)}", *tags do
+ before do
+ properties.each do |property_str|
+ resource_class.class_eval(property_str, __FILE__, __LINE__)
+ end
+ end
+ instance_eval(&block)
+ end
+ end
+
+ # identity
+ context "Chef::Resource#identity_properties" do
+ with_property ":x" do
+ it "name is the default identity" do
+ expect(resource_class.identity_properties).to eq [ Chef::Resource.properties[:name] ]
+ expect(Chef::Resource.properties[:name].identity?).to be_falsey
+ expect(resource.name).to eq 'blah'
+ expect(resource.identity).to eq 'blah'
+ end
+
+ it "identity_properties :x changes the identity" do
+ expect(resource_class.identity_properties :x).to eq [ resource_class.properties[:x] ]
+ expect(resource_class.identity_properties).to eq [ resource_class.properties[:x] ]
+ expect(Chef::Resource.properties[:name].identity?).to be_falsey
+ expect(resource_class.properties[:x].identity?).to be_truthy
+
+ expect(resource.x 'woo').to eq 'woo'
+ expect(resource.x).to eq 'woo'
+
+ expect(resource.name).to eq 'blah'
+ expect(resource.identity).to eq 'woo'
+ end
+
+ with_property ":y, identity: true" do
+ context "and identity_properties :x" do
+ before do
+ resource_class.class_eval do
+ identity_properties :x
+ end
+ end
+
+ it "only returns :x as identity" do
+ resource.x 'foo'
+ resource.y 'bar'
+ expect(resource_class.identity_properties).to eq [ resource_class.properties[:x] ]
+ expect(resource.identity).to eq 'foo'
+ end
+ it "does not flip y.desired_state off" do
+ resource.x 'foo'
+ resource.y 'bar'
+ expect(resource_class.state_properties).to eq [
+ resource_class.properties[:x],
+ resource_class.properties[:y]
+ ]
+ expect(resource.state_for_resource_reporter).to eq(x: 'foo', y: 'bar')
+ end
+ end
+ end
+
+ context "With a subclass" do
+ let(:subresource_class) do
+ new_resource_name = self.class.new_resource_name
+ Class.new(resource_class) do
+ resource_name new_resource_name
+ end
+ end
+ let(:subresource) do
+ subresource_class.new('sub')
+ end
+
+ it "name is the default identity on the subclass" do
+ expect(subresource_class.identity_properties).to eq [ Chef::Resource.properties[:name] ]
+ expect(Chef::Resource.properties[:name].identity?).to be_falsey
+ expect(subresource.name).to eq 'sub'
+ expect(subresource.identity).to eq 'sub'
+ end
+
+ context "With identity_properties :x on the superclass" do
+ before do
+ resource_class.class_eval do
+ identity_properties :x
+ end
+ end
+
+ it "The subclass inherits :x as identity" do
+ expect(subresource_class.identity_properties).to eq [ subresource_class.properties[:x] ]
+ expect(Chef::Resource.properties[:name].identity?).to be_falsey
+ expect(subresource_class.properties[:x].identity?).to be_truthy
+
+ subresource.x 'foo'
+ expect(subresource.identity).to eq 'foo'
+ end
+
+ context "With property :y, identity: true on the subclass" do
+ before do
+ subresource_class.class_eval do
+ property :y, identity: true
+ end
+ end
+ it "The subclass's identity includes both x and y" do
+ expect(subresource_class.identity_properties).to eq [
+ subresource_class.properties[:x],
+ subresource_class.properties[:y]
+ ]
+ subresource.x 'foo'
+ subresource.y 'bar'
+ expect(subresource.identity).to eq(x: 'foo', y: 'bar')
+ end
+ end
+
+ with_property ":y, String" do
+ context "With identity_properties :y on the subclass" do
+ before do
+ subresource_class.class_eval do
+ identity_properties :y
+ end
+ end
+ it "y is part of state" do
+ subresource.x 'foo'
+ subresource.y 'bar'
+ expect(subresource.state_for_resource_reporter).to eq(x: 'foo', y: 'bar')
+ expect(subresource_class.state_properties).to eq [
+ subresource_class.properties[:x],
+ subresource_class.properties[:y]
+ ]
+ end
+ it "y is the identity" do
+ expect(subresource_class.identity_properties).to eq [ subresource_class.properties[:y] ]
+ subresource.x 'foo'
+ subresource.y 'bar'
+ expect(subresource.identity).to eq 'bar'
+ end
+ it "y still has validation" do
+ expect { subresource.y 12 }.to raise_error Chef::Exceptions::ValidationFailed
+ end
+ end
+ end
+ end
+ end
+ end
+
+ with_property ":string_only, String, identity: true", ":string_only2, String" do
+ it "identity_properties does not change validation" do
+ resource_class.identity_properties :string_only
+ expect { resource.string_only 12 }.to raise_error Chef::Exceptions::ValidationFailed
+ expect { resource.string_only2 12 }.to raise_error Chef::Exceptions::ValidationFailed
+ end
+ end
+
+ with_property ":x, desired_state: false" do
+ it "identity_properties does not change desired_state" do
+ resource_class.identity_properties :x
+ resource.x 'hi'
+ expect(resource.identity).to eq 'hi'
+ expect(resource_class.properties[:x].desired_state?).to be_falsey
+ expect(resource_class.state_properties).to eq []
+ expect(resource.state_for_resource_reporter).to eq({})
+ end
+ end
+
+ context "With custom property custom_property defined only as methods, using different variables for storage" do
+ before do
+ resource_class.class_eval do
+ def custom_property
+ @blarghle ? @blarghle*3 : nil
+ end
+ def custom_property=(x)
+ @blarghle = x*2
+ end
+ end
+ end
+
+ context "And identity_properties :custom_property" do
+ before do
+ resource_class.class_eval do
+ identity_properties :custom_property
+ end
+ end
+
+ it "identity_properties comes back as :custom_property" do
+ expect(resource_class.properties[:custom_property].identity?).to be_truthy
+ expect(resource_class.identity_properties).to eq [ resource_class.properties[:custom_property] ]
+ end
+ it "custom_property becomes part of desired_state" do
+ resource.custom_property = 1
+ expect(resource.state_for_resource_reporter).to eq(custom_property: 6)
+ expect(resource_class.properties[:custom_property].desired_state?).to be_truthy
+ expect(resource_class.state_properties).to eq [
+ resource_class.properties[:custom_property]
+ ]
+ end
+ it "identity_properties does not change custom_property's getter or setter" do
+ resource.custom_property = 1
+ expect(resource.custom_property).to eq 6
+ end
+ it "custom_property is returned as the identity" do
+ expect(resource.identity).to be_nil
+ resource.custom_property = 1
+ expect(resource.identity).to eq 6
+ end
+ end
+ end
+ end
+
+ context "Property#identity" do
+ with_property ":x, identity: true" do
+ it "name is only part of the identity if an identity attribute is defined" do
+ expect(resource_class.identity_properties).to eq [ resource_class.properties[:x] ]
+ resource.x 'woo'
+ expect(resource.identity).to eq 'woo'
+ end
+ end
+
+ with_property ":x, identity: true, default: 'xxx'",
+ ":y, identity: true, default: 'yyy'",
+ ":z, identity: true, default: 'zzz'" do
+ it "identity_property raises an error if multiple identity values are defined" do
+ expect { resource_class.identity_property }.to raise_error Chef::Exceptions::MultipleIdentityError
+ end
+ it "identity_attr raises an error if multiple identity values are defined" do
+ expect { resource_class.identity_attr }.to raise_error Chef::Exceptions::MultipleIdentityError
+ end
+ it "identity returns all identity values in a hash if multiple are defined" do
+ resource.x 'foo'
+ resource.y 'bar'
+ resource.z 'baz'
+ expect(resource.identity).to eq(x: 'foo', y: 'bar', z: 'baz')
+ end
+ it "identity returns all values whether any value is set or not" do
+ expect(resource.identity).to eq(x: 'xxx', y: 'yyy', z: 'zzz')
+ end
+ it "identity_properties wipes out any other identity attributes if multiple are defined" do
+ resource_class.identity_properties :y
+ resource.x 'foo'
+ resource.y 'bar'
+ resource.z 'baz'
+ expect(resource.identity).to eq 'bar'
+ end
+ end
+
+ with_property ":x, identity: true, name_property: true" do
+ it "identity when x is not defined returns the value of x" do
+ expect(resource.identity).to eq 'blah'
+ end
+ it "state when x is not defined returns the value of x" do
+ expect(resource.state_for_resource_reporter).to eq(x: 'blah')
+ end
+ end
+ end
+
+ # state_properties
+ context "Chef::Resource#state_properties" do
+ it "state_properties is empty by default" do
+ expect(Chef::Resource.state_properties).to eq []
+ expect(resource.state_for_resource_reporter).to eq({})
+ end
+
+ with_property ":x", ":y", ":z" do
+ it "x, y and z are state attributes" do
+ resource.x 1
+ resource.y 2
+ resource.z 3
+ expect(resource_class.state_properties).to eq [
+ resource_class.properties[:x],
+ resource_class.properties[:y],
+ resource_class.properties[:z]
+ ]
+ expect(resource.state_for_resource_reporter).to eq(x: 1, y: 2, z: 3)
+ end
+ it "values that are not set are not included in state" do
+ resource.x 1
+ expect(resource.state_for_resource_reporter).to eq(x: 1)
+ end
+ it "when no values are set, nothing is included in state" do
+ end
+ end
+
+ with_property ":x", ":y, desired_state: false", ":z, desired_state: true" do
+ it "x and z are state attributes, and y is not" do
+ resource.x 1
+ resource.y 2
+ resource.z 3
+ expect(resource_class.state_properties).to eq [
+ resource_class.properties[:x],
+ resource_class.properties[:z]
+ ]
+ expect(resource.state_for_resource_reporter).to eq(x: 1, z: 3)
+ end
+ end
+
+ with_property ":x, name_property: true" do
+ # it "Unset values with name_property are included in state" do
+ # expect(resource.state_for_resource_reporter).to eq({ x: 'blah' })
+ # end
+ it "Set values with name_property are included in state" do
+ resource.x 1
+ expect(resource.state_for_resource_reporter).to eq(x: 1)
+ end
+ end
+
+ with_property ":x, default: 1" do
+ it "Unset values with defaults are not included in state" do
+ expect(resource.state_for_resource_reporter).to eq({})
+ end
+ it "Set values with defaults are included in state" do
+ resource.x 1
+ expect(resource.state_for_resource_reporter).to eq(x: 1)
+ end
+ end
+
+ context "With a class with a normal getter and setter" do
+ before do
+ resource_class.class_eval do
+ def x
+ @blah*3
+ end
+ def x=(value)
+ @blah = value*2
+ end
+ end
+ end
+ it "state_properties(:x) causes the value to be included in properties" do
+ resource_class.state_properties(:x)
+ resource.x = 1
+
+ expect(resource.x).to eq 6
+ expect(resource.state_for_resource_reporter).to eq(x: 6)
+ end
+ end
+
+ context "When state_properties happens before properties are declared" do
+ before do
+ resource_class.class_eval do
+ state_properties :x
+ property :x
+ end
+ end
+ it "the property works and is in state_properties" do
+ expect(resource_class.state_properties).to include(resource_class.properties[:x])
+ resource.x = 1
+ expect(resource.x).to eq 1
+ expect(resource.state_for_resource_reporter).to eq(x: 1)
+ end
+ end
+
+ with_property ":x, Integer, identity: true" do
+ it "state_properties(:x) leaves the property in desired_state" do
+ resource_class.state_properties(:x)
+ resource.x 10
+
+ expect(resource_class.properties[:x].desired_state?).to be_truthy
+ expect(resource_class.state_properties).to eq [
+ resource_class.properties[:x]
+ ]
+ expect(resource.state_for_resource_reporter).to eq(x: 10)
+ end
+ it "state_properties(:x) does not turn off validation" do
+ resource_class.state_properties(:x)
+ expect { resource.x 'ouch' }.to raise_error Chef::Exceptions::ValidationFailed
+ end
+ it "state_properties(:x) does not turn off identity" do
+ resource_class.state_properties(:x)
+ resource.x 10
+
+ expect(resource_class.identity_properties).to eq [ resource_class.properties[:x] ]
+ expect(resource_class.properties[:x].identity?).to be_truthy
+ expect(resource.identity).to eq 10
+ end
+ end
+
+ with_property ":x, Integer, identity: true, desired_state: false" do
+ before do
+ resource_class.class_eval do
+ def y
+ 20
+ end
+ end
+ end
+
+ it "state_properties(:x) leaves x identical" do
+ old_value = resource_class.properties[:y]
+ resource_class.state_properties(:x)
+ resource.x 10
+
+ expect(resource_class.properties[:y].object_id).to eq old_value.object_id
+
+ expect(resource_class.properties[:x].desired_state?).to be_truthy
+ expect(resource_class.properties[:x].identity?).to be_truthy
+ expect(resource_class.identity_properties).to eq [
+ resource_class.properties[:x]
+ ]
+ expect(resource.identity).to eq(10)
+ expect(resource_class.state_properties).to eq [
+ resource_class.properties[:x]
+ ]
+ expect(resource.state_for_resource_reporter).to eq(x: 10)
+ end
+
+ it "state_properties(:y) adds y to desired state" do
+ old_value = resource_class.properties[:x]
+ resource_class.state_properties(:y)
+ resource.x 10
+
+ expect(resource_class.properties[:x].object_id).to eq old_value.object_id
+ expect(resource_class.properties[:x].desired_state?).to be_falsey
+ expect(resource_class.properties[:y].desired_state?).to be_truthy
+ expect(resource_class.state_properties).to eq [
+ resource_class.properties[:y]
+ ]
+ expect(resource.state_for_resource_reporter).to eq(y: 20)
+ end
+
+ context "With a subclassed resource" do
+ let(:subresource_class) do
+ new_resource_name = self.class.new_resource_name
+ Class.new(resource_class) do
+ resource_name new_resource_name
+ end
+ end
+ let(:subresource) do
+ subresource_class.new('blah')
+ end
+
+ it "state_properties(:x) adds x to desired state" do
+ old_value = resource_class.properties[:y]
+ subresource_class.state_properties(:x)
+ subresource.x 10
+
+ expect(subresource_class.properties[:y].object_id).to eq old_value.object_id
+
+ expect(subresource_class.properties[:x].desired_state?).to be_truthy
+ expect(subresource_class.properties[:x].identity?).to be_truthy
+ expect(subresource_class.identity_properties).to eq [
+ subresource_class.properties[:x]
+ ]
+ expect(subresource.identity).to eq(10)
+ expect(subresource_class.state_properties).to eq [
+ subresource_class.properties[:x]
+ ]
+ expect(subresource.state_for_resource_reporter).to eq(x: 10)
+ end
+
+ it "state_properties(:y) adds y to desired state" do
+ old_value = resource_class.properties[:x]
+ subresource_class.state_properties(:y)
+ subresource.x 10
+
+ expect(subresource_class.properties[:x].object_id).to eq old_value.object_id
+ expect(subresource_class.properties[:y].desired_state?).to be_truthy
+ expect(subresource_class.state_properties).to eq [
+ subresource_class.properties[:y]
+ ]
+ expect(subresource.state_for_resource_reporter).to eq(y: 20)
+
+ expect(subresource_class.properties[:x].identity?).to be_truthy
+ expect(subresource_class.identity_properties).to eq [
+ subresource_class.properties[:x]
+ ]
+ expect(subresource.identity).to eq(10)
+ end
+ end
+ end
+ end
+
+end
diff --git a/spec/unit/property/validation_spec.rb b/spec/unit/property/validation_spec.rb
new file mode 100644
index 0000000000..31bb3f0739
--- /dev/null
+++ b/spec/unit/property/validation_spec.rb
@@ -0,0 +1,663 @@
+require 'support/shared/integration/integration_helper'
+
+describe "Chef::Resource.property validation" do
+ include IntegrationSupport
+
+ module Namer
+ @i = 0
+ def self.next_resource_name
+ "chef_resource_property_spec_#{@i += 1}"
+ end
+ def self.reset_index
+ @current_index = 0
+ end
+ def self.current_index
+ @current_index
+ end
+ def self.next_index
+ @current_index += 1
+ end
+ end
+
+ def lazy(&block)
+ Chef::DelayedEvaluator.new(&block)
+ end
+
+ before do
+ Namer.reset_index
+ end
+
+ def self.new_resource_name
+ Namer.next_resource_name
+ end
+
+ let(:resource_class) do
+ new_resource_name = self.class.new_resource_name
+ Class.new(Chef::Resource) do
+ resource_name new_resource_name
+ def blah
+ Namer.next_index
+ end
+ def self.blah
+ "class#{Namer.next_index}"
+ end
+ end
+ end
+
+ let(:resource) do
+ resource_class.new("blah")
+ end
+
+ def self.english_join(values)
+ return '<nothing>' if values.size == 0
+ return values[0].inspect if values.size == 1
+ "#{values[0..-2].map { |v| v.inspect }.join(", ")} and #{values[-1].inspect}"
+ end
+
+ def self.with_property(*properties, &block)
+ tags_index = properties.find_index { |p| !p.is_a?(String)}
+ if tags_index
+ properties, tags = properties[0..tags_index-1], properties[tags_index..-1]
+ else
+ tags = []
+ end
+ properties = properties.map { |property| "property #{property}" }
+ context "With properties #{english_join(properties)}", *tags do
+ before do
+ properties.each do |property_str|
+ resource_class.class_eval(property_str, __FILE__, __LINE__)
+ end
+ end
+ instance_eval(&block)
+ end
+ end
+
+ def self.validation_test(validation, success_values, failure_values, getter_values=[], *tags)
+ with_property ":x, #{validation}", *tags do
+ it "gets nil when retrieving the initial (non-set) value" do
+ expect(resource.x).to be_nil
+ end
+ success_values.each do |v|
+ it "value #{v.inspect} is valid" do
+ resource.instance_eval { @x = 'default' }
+ expect(resource.x v).to eq v
+ expect(resource.x).to eq v
+ end
+ end
+ failure_values.each do |v|
+ it "value #{v.inspect} is invalid" do
+ expect { resource.x v }.to raise_error Chef::Exceptions::ValidationFailed
+ resource.instance_eval { @x = 'default' }
+ expect { resource.x v }.to raise_error Chef::Exceptions::ValidationFailed
+ end
+ end
+ getter_values.each do |v|
+ it "setting value to #{v.inspect} does not change the value" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ resource.instance_eval { @x = 'default' }
+ expect(resource.x v).to eq 'default'
+ expect(resource.x).to eq 'default'
+ end
+ end
+ end
+ end
+
+ context "basic get, set, and nil set" do
+ with_property ":x, kind_of: String" do
+ context "when the variable already has a value" do
+ before do
+ resource.instance_eval { @x = 'default' }
+ end
+ it "get succeeds" do
+ expect(resource.x).to eq 'default'
+ end
+ it "set to valid value succeeds" do
+ expect(resource.x 'str').to eq 'str'
+ expect(resource.x).to eq 'str'
+ end
+ it "set to invalid value raises ValidationFailed" do
+ expect { resource.x 10 }.to raise_error Chef::Exceptions::ValidationFailed
+ end
+ it "set to nil emits a deprecation warning and does a get" do
+ expect { resource.x nil }.to raise_error Chef::Exceptions::DeprecatedFeatureError
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ resource.x 'str'
+ expect(resource.x nil).to eq 'str'
+ expect(resource.x).to eq 'str'
+ end
+ end
+ context "when the variable does not have an initial value" do
+ it "get succeeds" do
+ expect(resource.x).to be_nil
+ end
+ it "set to valid value succeeds" do
+ expect(resource.x 'str').to eq 'str'
+ expect(resource.x).to eq 'str'
+ end
+ it "set to invalid value raises ValidationFailed" do
+ expect { resource.x 10 }.to raise_error Chef::Exceptions::ValidationFailed
+ end
+ it "set to nil emits a deprecation warning and does a get" do
+ expect { resource.x nil }.to raise_error Chef::Exceptions::DeprecatedFeatureError
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ resource.x 'str'
+ expect(resource.x nil).to eq 'str'
+ expect(resource.x).to eq 'str'
+ end
+ end
+ end
+ with_property ":x, [ String, nil ]" do
+ context "when the variable already has a value" do
+ before do
+ resource.instance_eval { @x = 'default' }
+ end
+ it "get succeeds" do
+ expect(resource.x).to eq 'default'
+ end
+ it "set(nil) sets the value" do
+ expect(resource.x nil).to be_nil
+ expect(resource.x).to be_nil
+ end
+ it "set to valid value succeeds" do
+ expect(resource.x 'str').to eq 'str'
+ expect(resource.x).to eq 'str'
+ end
+ it "set to invalid value raises ValidationFailed" do
+ expect { resource.x 10 }.to raise_error Chef::Exceptions::ValidationFailed
+ end
+ end
+ context "when the variable does not have an initial value" do
+ it "get succeeds" do
+ expect(resource.x).to be_nil
+ end
+ it "set(nil) sets the value" do
+ expect(resource.x nil).to be_nil
+ expect(resource.x).to be_nil
+ end
+ it "set to valid value succeeds" do
+ expect(resource.x 'str').to eq 'str'
+ expect(resource.x).to eq 'str'
+ end
+ it "set to invalid value raises ValidationFailed" do
+ expect { resource.x 10 }.to raise_error Chef::Exceptions::ValidationFailed
+ end
+ end
+ end
+ end
+
+ # Bare types
+ context "bare types" do
+ validation_test 'String',
+ [ 'hi' ],
+ [ 10 ],
+ [ nil ]
+
+ validation_test ':a',
+ [ :a ],
+ [ :b ],
+ [ nil ]
+
+ validation_test ':a, is: :b',
+ [ :a, :b ],
+ [ :c ],
+ [ nil ]
+
+ validation_test ':a, is: [ :b, :c ]',
+ [ :a, :b, :c ],
+ [ :d ],
+ [ nil ]
+
+ validation_test '[ :a, :b ], is: :c',
+ [ :a, :b, :c ],
+ [ :d ],
+ [ nil ]
+
+ validation_test '[ :a, :b ], is: [ :c, :d ]',
+ [ :a, :b, :c, :d ],
+ [ :e ],
+ [ nil ]
+
+ validation_test 'nil',
+ [ nil ],
+ [ :a ]
+
+ validation_test '[ nil ]',
+ [ nil ],
+ [ :a ]
+
+ validation_test '[]',
+ [],
+ [ :a ],
+ [ nil ]
+ end
+
+ # is
+ context "is" do
+ # Class
+ validation_test 'is: String',
+ [ 'a', '' ],
+ [ :a, 1 ],
+ [ nil ]
+
+ # Value
+ validation_test 'is: :a',
+ [ :a ],
+ [ :b ],
+ [ nil ]
+
+ validation_test 'is: [ :a, :b ]',
+ [ :a, :b ],
+ [ [ :a, :b ] ],
+ [ nil ]
+
+ validation_test 'is: [ [ :a, :b ] ]',
+ [ [ :a, :b ] ],
+ [ :a, :b ],
+ [ nil ]
+
+ # Regex
+ validation_test 'is: /abc/',
+ [ 'abc', 'wowabcwow' ],
+ [ '', 'abac' ],
+ [ nil ]
+
+ # Property
+ validation_test 'is: Chef::Property.new(is: :a)',
+ [ :a ],
+ [ :b, nil ]
+
+ # RSpec Matcher
+ class Globalses
+ extend RSpec::Matchers
+ end
+
+ validation_test "is: Globalses.eq(10)",
+ [ 10 ],
+ [ 1 ],
+ [ nil ]
+
+ # Proc
+ validation_test 'is: proc { |x| x }',
+ [ true, 1 ],
+ [ false ],
+ [ nil ]
+
+ validation_test 'is: proc { |x| x > blah }',
+ [ 10 ],
+ [ -1 ]
+
+ validation_test 'is: nil',
+ [ nil ],
+ [ 'a' ]
+
+ validation_test 'is: [ String, nil ]',
+ [ 'a', nil ],
+ [ :b ]
+
+ validation_test 'is: []',
+ [],
+ [ :a ],
+ [ nil ]
+ end
+
+ # Combination
+ context "combination" do
+ validation_test 'kind_of: String, equal_to: "a"',
+ [ 'a' ],
+ [ 'b' ],
+ [ nil ]
+ end
+
+ # equal_to
+ context "equal_to" do
+ # Value
+ validation_test 'equal_to: :a',
+ [ :a ],
+ [ :b ],
+ [ nil ]
+
+ validation_test 'equal_to: [ :a, :b ]',
+ [ :a, :b ],
+ [ [ :a, :b ] ],
+ [ nil ]
+
+ validation_test 'equal_to: [ [ :a, :b ] ]',
+ [ [ :a, :b ] ],
+ [ :a, :b ],
+ [ nil ]
+
+ validation_test 'equal_to: nil',
+ [ ],
+ [ 'a' ],
+ [ nil ]
+
+ validation_test 'equal_to: [ "a", nil ]',
+ [ 'a' ],
+ [ 'b' ],
+ [ nil ]
+
+ validation_test 'equal_to: [ nil, "a" ]',
+ [ 'a' ],
+ [ 'b' ],
+ [ nil ]
+
+ validation_test 'equal_to: []',
+ [],
+ [ :a ],
+ [ nil ]
+ end
+
+ # kind_of
+ context "kind_of" do
+ validation_test 'kind_of: String',
+ [ 'a' ],
+ [ :b ],
+ [ nil ]
+
+ validation_test 'kind_of: [ String, Symbol ]',
+ [ 'a', :b ],
+ [ 1 ],
+ [ nil ]
+
+ validation_test 'kind_of: [ Symbol, String ]',
+ [ 'a', :b ],
+ [ 1 ],
+ [ nil ]
+
+ validation_test 'kind_of: NilClass',
+ [ ],
+ [ 'a' ],
+ [ nil ]
+
+ validation_test 'kind_of: [ NilClass, String ]',
+ [ 'a' ],
+ [ :a ],
+ [ nil ]
+
+ validation_test 'kind_of: []',
+ [],
+ [ :a ],
+ [ nil ]
+
+ validation_test 'kind_of: nil',
+ [],
+ [ :a ],
+ [ nil ]
+ end
+
+ # regex
+ context "regex" do
+ validation_test 'regex: /abc/',
+ [ 'xabcy' ],
+ [ 'gbh', 123 ],
+ [ nil ]
+
+ validation_test 'regex: [ /abc/, /z/ ]',
+ [ 'xabcy', 'aza' ],
+ [ 'gbh', 123 ],
+ [ nil ]
+
+ validation_test 'regex: [ /z/, /abc/ ]',
+ [ 'xabcy', 'aza' ],
+ [ 'gbh', 123 ],
+ [ nil ]
+
+ validation_test 'regex: [ [ /z/, /abc/ ], [ /n/ ] ]',
+ [ 'xabcy', 'aza', 'ana' ],
+ [ 'gbh', 123 ],
+ [ nil ]
+
+ validation_test 'regex: []',
+ [],
+ [ :a ],
+ [ nil ]
+
+ validation_test 'regex: nil',
+ [],
+ [ :a ],
+ [ nil ]
+ end
+
+ # callbacks
+ context "callbacks" do
+ validation_test 'callbacks: { "a" => proc { |x| x > 10 }, "b" => proc { |x| x%2 == 0 } }',
+ [ 12 ],
+ [ 11, 4 ]
+
+ validation_test 'callbacks: { "a" => proc { |x| x%2 == 0 }, "b" => proc { |x| x > 10 } }',
+ [ 12 ],
+ [ 11, 4 ]
+
+ validation_test 'callbacks: { "a" => proc { |x| x.nil? } }',
+ [ ],
+ [ 'a' ],
+ [ nil ]
+
+ validation_test 'callbacks: {}',
+ [ :a ],
+ [],
+ [ nil ]
+ end
+
+ # respond_to
+ context "respond_to" do
+ validation_test 'respond_to: :split',
+ [ 'hi' ],
+ [ 1 ],
+ [ nil ]
+
+ validation_test 'respond_to: "split"',
+ [ 'hi' ],
+ [ 1 ],
+ [ nil ]
+
+ validation_test 'respond_to: :to_s',
+ [ :a ],
+ [],
+ [ nil ]
+
+ validation_test 'respond_to: [ :split, :to_s ]',
+ [ 'hi' ],
+ [ 1 ],
+ [ nil ]
+
+ validation_test 'respond_to: %w(split to_s)',
+ [ 'hi' ],
+ [ 1 ],
+ [ nil ]
+
+ validation_test 'respond_to: [ :to_s, :split ]',
+ [ 'hi' ],
+ [ 1, ],
+ [ nil ]
+
+ validation_test 'respond_to: []',
+ [ :a ],
+ [],
+ [ nil ]
+
+ validation_test 'respond_to: nil',
+ [ :a ],
+ [],
+ [ nil ]
+ end
+
+ context "cannot_be" do
+ validation_test 'cannot_be: :empty',
+ [ 1, [1,2], { a: 10 } ],
+ [ [] ],
+ [ nil ]
+
+ validation_test 'cannot_be: "empty"',
+ [ 1, [1,2], { a: 10 } ],
+ [ [] ],
+ [ nil ]
+
+ validation_test 'cannot_be: [ :empty, :nil ]',
+ [ 1, [1,2], { a: 10 } ],
+ [ [] ],
+ [ nil ]
+
+ validation_test 'cannot_be: [ "empty", "nil" ]',
+ [ 1, [1,2], { a: 10 } ],
+ [ [] ],
+ [ nil ]
+
+ validation_test 'cannot_be: [ :nil, :empty ]',
+ [ 1, [1,2], { a: 10 } ],
+ [ [] ],
+ [ nil ]
+
+ validation_test 'cannot_be: [ :empty, :nil, :blahblah ]',
+ [ 1, [1,2], { a: 10 } ],
+ [ [] ],
+ [ nil ]
+
+ validation_test 'cannot_be: []',
+ [ :a ],
+ [],
+ [ nil ]
+
+ validation_test 'cannot_be: nil',
+ [ :a ],
+ [],
+ [ nil ]
+
+ end
+
+ context "required" do
+ with_property ':x, required: true' do
+ it "if x is not specified, retrieval fails" do
+ expect { resource.x }.to raise_error Chef::Exceptions::ValidationFailed
+ end
+ it "value 1 is valid" do
+ expect(resource.x 1).to eq 1
+ expect(resource.x).to eq 1
+ end
+ it "value nil emits a deprecation warning and does a get" do
+ expect { resource.x nil }.to raise_error Chef::Exceptions::DeprecatedFeatureError
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ resource.x 1
+ expect(resource.x nil).to eq 1
+ expect(resource.x).to eq 1
+ end
+ end
+
+ with_property ':x, [String, nil], required: true' do
+ it "if x is not specified, retrieval fails" do
+ expect { resource.x }.to raise_error Chef::Exceptions::ValidationFailed
+ end
+ it "value nil is valid" do
+ expect(resource.x nil).to be_nil
+ expect(resource.x).to be_nil
+ end
+ it "value '1' is valid" do
+ expect(resource.x '1').to eq '1'
+ expect(resource.x).to eq '1'
+ end
+ it "value 1 is invalid" do
+ expect { resource.x 1 }.to raise_error Chef::Exceptions::ValidationFailed
+ end
+ end
+
+ with_property ':x, name_property: true, required: true' do
+ it "if x is not specified, the name property is returned" do
+ expect(resource.x).to eq 'blah'
+ end
+ it "value 1 is valid" do
+ expect(resource.x 1).to eq 1
+ expect(resource.x).to eq 1
+ end
+ it "value nil emits a deprecation warning and does a get" do
+ expect { resource.x nil }.to raise_error Chef::Exceptions::DeprecatedFeatureError
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ resource.x 1
+ expect(resource.x nil).to eq 1
+ expect(resource.x).to eq 1
+ end
+ end
+
+ with_property ':x, default: 10, required: true' do
+ it "if x is not specified, the default is returned" do
+ expect(resource.x).to eq 10
+ end
+ it "value 1 is valid" do
+ expect(resource.x 1).to eq 1
+ expect(resource.x).to eq 1
+ end
+ it "value nil is invalid" do
+ expect { resource.x nil }.to raise_error Chef::Exceptions::DeprecatedFeatureError
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ resource.x 1
+ expect(resource.x nil).to eq 1
+ expect(resource.x).to eq 1
+ end
+ end
+ end
+
+ context "custom validators (def _pv_blarghle)" do
+ before do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ end
+
+ with_property ':x, blarghle: 1' do
+ context "and a class that implements _pv_blarghle" do
+ before do
+ resource_class.class_eval do
+ def _pv_blarghle(opts, key, value)
+ if _pv_opts_lookup(opts, key) != value
+ raise Chef::Exceptions::ValidationFailed, "ouch"
+ end
+ end
+ end
+ end
+
+ it "value 1 is valid" do
+ expect(resource.x 1).to eq 1
+ expect(resource.x).to eq 1
+ end
+
+ it "value '1' is invalid" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ expect { resource.x '1' }.to raise_error Chef::Exceptions::ValidationFailed
+ end
+
+ it "value nil does a get" do
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ resource.x 1
+ resource.x nil
+ expect(resource.x).to eq 1
+ end
+ end
+ end
+
+ with_property ':x, blarghle: 1' do
+ context "and a class that implements _pv_blarghle" do
+ before do
+ resource_class.class_eval do
+ def _pv_blarghle(opts, key, value)
+ if _pv_opts_lookup(opts, key) != value
+ raise Chef::Exceptions::ValidationFailed, "ouch"
+ end
+ end
+ end
+ end
+
+ it "value 1 is valid" do
+ expect(resource.x 1).to eq 1
+ expect(resource.x).to eq 1
+ end
+
+ it "value '1' is invalid" do
+ expect { resource.x '1' }.to raise_error Chef::Exceptions::ValidationFailed
+ end
+
+ it "value nil does a get" do
+ resource.x 1
+ resource.x nil
+ expect(resource.x).to eq 1
+ end
+ end
+ end
+ end
+end
diff --git a/spec/unit/property_spec.rb b/spec/unit/property_spec.rb
new file mode 100644
index 0000000000..50764aa7a2
--- /dev/null
+++ b/spec/unit/property_spec.rb
@@ -0,0 +1,972 @@
+require 'support/shared/integration/integration_helper'
+
+describe "Chef::Resource.property" do
+ include IntegrationSupport
+
+ module Namer
+ @i = 0
+ def self.next_resource_name
+ "chef_resource_property_spec_#{@i += 1}"
+ end
+ def self.reset_index
+ @current_index = 0
+ end
+ def self.current_index
+ @current_index
+ end
+ def self.next_index
+ @current_index += 1
+ end
+ end
+
+ def lazy(&block)
+ Chef::DelayedEvaluator.new(&block)
+ end
+
+ before do
+ Namer.reset_index
+ end
+
+ def self.new_resource_name
+ Namer.next_resource_name
+ end
+
+ let(:resource_class) do
+ new_resource_name = self.class.new_resource_name
+ Class.new(Chef::Resource) do
+ resource_name new_resource_name
+ def next_index
+ Namer.next_index
+ end
+ end
+ end
+
+ let(:resource) do
+ resource_class.new("blah")
+ end
+
+ def self.english_join(values)
+ return '<nothing>' if values.size == 0
+ return values[0].inspect if values.size == 1
+ "#{values[0..-2].map { |v| v.inspect }.join(", ")} and #{values[-1].inspect}"
+ end
+
+ def self.with_property(*properties, &block)
+ tags_index = properties.find_index { |p| !p.is_a?(String)}
+ if tags_index
+ properties, tags = properties[0..tags_index-1], properties[tags_index..-1]
+ else
+ tags = []
+ end
+ if properties.size == 1
+ description = "With property #{properties.first}"
+ else
+ description = "With properties #{english_join(properties.map { |property| "#{property.inspect}" })}"
+ end
+ context description, *tags do
+ before do
+ properties.each do |property_str|
+ resource_class.class_eval("property #{property_str}", __FILE__, __LINE__)
+ end
+ end
+ instance_eval(&block)
+ end
+ end
+
+ # Basic properties
+ with_property ':bare_property' do
+ it "can be set" do
+ expect(resource.bare_property 10).to eq 10
+ expect(resource.bare_property).to eq 10
+ end
+ it "emits a deprecation warning and does a get, if set to nil" do
+ expect(resource.bare_property 10).to eq 10
+ expect { resource.bare_property nil }.to raise_error Chef::Exceptions::DeprecatedFeatureError
+ Chef::Config[:treat_deprecation_warnings_as_errors] = false
+ expect(resource.bare_property nil).to eq 10
+ expect(resource.bare_property).to eq 10
+ end
+ it "can be updated" do
+ expect(resource.bare_property 10).to eq 10
+ expect(resource.bare_property 20).to eq 20
+ expect(resource.bare_property).to eq 20
+ end
+ it "can be set with =" do
+ expect(resource.bare_property 10).to eq 10
+ expect(resource.bare_property).to eq 10
+ end
+ it "can be set to nil with =" do
+ expect(resource.bare_property 10).to eq 10
+ expect(resource.bare_property = nil).to be_nil
+ expect(resource.bare_property).to be_nil
+ end
+ it "can be updated with =" do
+ expect(resource.bare_property 10).to eq 10
+ expect(resource.bare_property = 20).to eq 20
+ expect(resource.bare_property).to eq 20
+ end
+ end
+
+ with_property ":x, Integer" do
+ context "and subclass" do
+ let(:subresource_class) do
+ new_resource_name = self.class.new_resource_name
+ Class.new(resource_class) do
+ resource_name new_resource_name
+ end
+ end
+ let(:subresource) do
+ subresource_class.new('blah')
+ end
+
+ it "x is inherited" do
+ expect(subresource.x 10).to eq 10
+ expect(subresource.x).to eq 10
+ expect(subresource.x = 20).to eq 20
+ expect(subresource.x).to eq 20
+ expect(subresource_class.properties[:x]).not_to be_nil
+ end
+
+ it "x's validation is inherited" do
+ expect { subresource.x 'ohno' }.to raise_error Chef::Exceptions::ValidationFailed
+ end
+
+ context "with property :y on the subclass" do
+ before do
+ subresource_class.class_eval do
+ property :y
+ end
+ end
+
+ it "x is still there" do
+ expect(subresource.x 10).to eq 10
+ expect(subresource.x).to eq 10
+ expect(subresource.x = 20).to eq 20
+ expect(subresource.x).to eq 20
+ expect(subresource_class.properties[:x]).not_to be_nil
+ end
+ it "y is there" do
+ expect(subresource.y 10).to eq 10
+ expect(subresource.y).to eq 10
+ expect(subresource.y = 20).to eq 20
+ expect(subresource.y).to eq 20
+ expect(subresource_class.properties[:y]).not_to be_nil
+ end
+ it "y is not on the superclass" do
+ expect { resource_class.y 10 }.to raise_error
+ expect(resource_class.properties[:y]).to be_nil
+ end
+ end
+
+ context "with property :x on the subclass" do
+ before do
+ subresource_class.class_eval do
+ property :x
+ end
+ end
+
+ it "x is still there" do
+ expect(subresource.x 10).to eq 10
+ expect(subresource.x).to eq 10
+ expect(subresource.x = 20).to eq 20
+ expect(subresource.x).to eq 20
+ expect(subresource_class.properties[:x]).not_to be_nil
+ expect(subresource_class.properties[:x]).not_to eq resource_class.properties[:x]
+ end
+
+ it "x's validation is inherited" do
+ expect { subresource.x 'ohno' }.to raise_error Chef::Exceptions::ValidationFailed
+ end
+ end
+
+ context "with property :x, default: 80 on the subclass" do
+ before do
+ subresource_class.class_eval do
+ property :x, default: 80
+ end
+ end
+
+ it "x is still there" do
+ expect(subresource.x 10).to eq 10
+ expect(subresource.x).to eq 10
+ expect(subresource.x = 20).to eq 20
+ expect(subresource.x).to eq 20
+ expect(subresource_class.properties[:x]).not_to be_nil
+ expect(subresource_class.properties[:x]).not_to eq resource_class.properties[:x]
+ end
+
+ it "x defaults to 80" do
+ expect(subresource.x).to eq 80
+ end
+
+ it "x's validation is inherited" do
+ expect { subresource.x 'ohno' }.to raise_error Chef::Exceptions::ValidationFailed
+ end
+ end
+
+ context "with property :x, String on the subclass" do
+ before do
+ subresource_class.class_eval do
+ property :x, String
+ end
+ end
+
+ it "x is still there" do
+ expect(subresource.x "10").to eq "10"
+ expect(subresource.x).to eq "10"
+ expect(subresource.x = "20").to eq "20"
+ expect(subresource.x).to eq "20"
+ expect(subresource_class.properties[:x]).not_to be_nil
+ expect(subresource_class.properties[:x]).not_to eq resource_class.properties[:x]
+ end
+
+ it "x's validation is overwritten" do
+ expect { subresource.x 10 }.to raise_error Chef::Exceptions::ValidationFailed
+ expect(subresource.x 'ohno').to eq 'ohno'
+ expect(subresource.x).to eq 'ohno'
+ end
+
+ it "the superclass's validation for x is still there" do
+ expect { resource.x 'ohno' }.to raise_error Chef::Exceptions::ValidationFailed
+ expect(resource.x 10).to eq 10
+ expect(resource.x).to eq 10
+ end
+ end
+ end
+ end
+
+ context "Chef::Resource::Property#reset_property" do
+ it "when a resource is newly created, reset_property(:name) sets property to nil" do
+ expect(resource.property_is_set?(:name)).to be_truthy
+ resource.reset_property(:name)
+ expect(resource.property_is_set?(:name)).to be_falsey
+ expect(resource.name).to be_nil
+ end
+
+ it "when referencing an undefined property, reset_property(:x) raises an error" do
+ expect { resource.reset_property(:x) }.to raise_error(ArgumentError)
+ end
+
+ with_property ':x' do
+ it "when the resource is newly created, reset_property(:x) does nothing" do
+ expect(resource.property_is_set?(:x)).to be_falsey
+ resource.reset_property(:x)
+ expect(resource.property_is_set?(:x)).to be_falsey
+ expect(resource.x).to be_nil
+ end
+ it "when x is set, reset_property resets it" do
+ resource.x 10
+ expect(resource.property_is_set?(:x)).to be_truthy
+ resource.reset_property(:x)
+ expect(resource.property_is_set?(:x)).to be_falsey
+ expect(resource.x).to be_nil
+ end
+ end
+
+ with_property ':x, Integer' do
+ it "when the resource is newly created, reset_property(:x) does nothing" do
+ expect(resource.property_is_set?(:x)).to be_falsey
+ resource.reset_property(:x)
+ expect(resource.property_is_set?(:x)).to be_falsey
+ expect(resource.x).to be_nil
+ end
+ it "when x is set, reset_property resets it even though `nil` is technically invalid" do
+ resource.x 10
+ expect(resource.property_is_set?(:x)).to be_truthy
+ resource.reset_property(:x)
+ expect(resource.property_is_set?(:x)).to be_falsey
+ expect(resource.x).to be_nil
+ end
+ end
+
+ with_property ':x, default: 10' do
+ it "when the resource is newly created, reset_property(:x) does nothing" do
+ expect(resource.property_is_set?(:x)).to be_falsey
+ resource.reset_property(:x)
+ expect(resource.property_is_set?(:x)).to be_falsey
+ expect(resource.x).to eq 10
+ end
+ it "when x is set, reset_property resets it and it returns the default" do
+ resource.x 20
+ resource.reset_property(:x)
+ expect(resource.property_is_set?(:x)).to be_falsey
+ expect(resource.x).to eq 10
+ end
+ end
+
+ with_property ':x, default: lazy { 10 }' do
+ it "when the resource is newly created, reset_property(:x) does nothing" do
+ expect(resource.property_is_set?(:x)).to be_falsey
+ resource.reset_property(:x)
+ expect(resource.property_is_set?(:x)).to be_falsey
+ expect(resource.x).to eq 10
+ end
+ it "when x is set, reset_property resets it and it returns the default" do
+ resource.x 20
+ resource.reset_property(:x)
+ expect(resource.property_is_set?(:x)).to be_falsey
+ expect(resource.x).to eq 10
+ end
+ end
+ end
+
+ context "Chef::Resource::Property#property_is_set?" do
+ it "when a resource is newly created, property_is_set?(:name) is true" do
+ expect(resource.property_is_set?(:name)).to be_truthy
+ end
+
+ it "when referencing an undefined property, property_is_set?(:x) raises an error" do
+ expect { resource.property_is_set?(:x) }.to raise_error(ArgumentError)
+ end
+
+ with_property ':x' do
+ it "when the resource is newly created, property_is_set?(:x) is false" do
+ expect(resource.property_is_set?(:x)).to be_falsey
+ end
+ it "when x is set, property_is_set?(:x) is true" do
+ resource.x 10
+ expect(resource.property_is_set?(:x)).to be_truthy
+ end
+ it "when x is set with =, property_is_set?(:x) is true" do
+ resource.x = 10
+ expect(resource.property_is_set?(:x)).to be_truthy
+ end
+ it "when x is set to a lazy value, property_is_set?(:x) is true" do
+ resource.x lazy { 10 }
+ expect(resource.property_is_set?(:x)).to be_truthy
+ end
+ it "when x is retrieved, property_is_set?(:x) is false" do
+ resource.x
+ expect(resource.property_is_set?(:x)).to be_falsey
+ end
+ end
+
+ with_property ':x, default: 10' do
+ it "when the resource is newly created, property_is_set?(:x) is false" do
+ expect(resource.property_is_set?(:x)).to be_falsey
+ end
+ it "when x is set, property_is_set?(:x) is true" do
+ resource.x 10
+ expect(resource.property_is_set?(:x)).to be_truthy
+ end
+ it "when x is set with =, property_is_set?(:x) is true" do
+ resource.x = 10
+ expect(resource.property_is_set?(:x)).to be_truthy
+ end
+ it "when x is set to a lazy value, property_is_set?(:x) is true" do
+ resource.x lazy { 10 }
+ expect(resource.property_is_set?(:x)).to be_truthy
+ end
+ it "when x is retrieved, property_is_set?(:x) is false" do
+ resource.x
+ expect(resource.property_is_set?(:x)).to be_falsey
+ end
+ end
+
+ with_property ':x, default: nil' do
+ it "when the resource is newly created, property_is_set?(:x) is false" do
+ expect(resource.property_is_set?(:x)).to be_falsey
+ end
+ it "when x is set, property_is_set?(:x) is true" do
+ resource.x 10
+ expect(resource.property_is_set?(:x)).to be_truthy
+ end
+ it "when x is set with =, property_is_set?(:x) is true" do
+ resource.x = 10
+ expect(resource.property_is_set?(:x)).to be_truthy
+ end
+ it "when x is set to a lazy value, property_is_set?(:x) is true" do
+ resource.x lazy { 10 }
+ expect(resource.property_is_set?(:x)).to be_truthy
+ end
+ it "when x is retrieved, property_is_set?(:x) is false" do
+ resource.x
+ expect(resource.property_is_set?(:x)).to be_falsey
+ end
+ end
+
+ with_property ':x, default: lazy { 10 }' do
+ it "when the resource is newly created, property_is_set?(:x) is false" do
+ expect(resource.property_is_set?(:x)).to be_falsey
+ end
+ it "when x is set, property_is_set?(:x) is true" do
+ resource.x 10
+ expect(resource.property_is_set?(:x)).to be_truthy
+ end
+ it "when x is set with =, property_is_set?(:x) is true" do
+ resource.x = 10
+ expect(resource.property_is_set?(:x)).to be_truthy
+ end
+ it "when x is retrieved, property_is_set?(:x) is false" do
+ resource.x
+ expect(resource.property_is_set?(:x)).to be_falsey
+ end
+ end
+ end
+
+ context "Chef::Resource::Property#default" do
+ with_property ':x, default: 10' do
+ it "when x is set, it returns its value" do
+ expect(resource.x 20).to eq 20
+ expect(resource.property_is_set?(:x)).to be_truthy
+ expect(resource.x).to eq 20
+ end
+ it "when x is not set, it returns 10" do
+ expect(resource.x).to eq 10
+ end
+ it "when x is not set, it is not included in state" do
+ expect(resource.state_for_resource_reporter).to eq({})
+ end
+ it "when x is set to nil, it returns nil" do
+ resource.instance_eval { @x = nil }
+ expect(resource.x).to be_nil
+ end
+
+ context "With a subclass" do
+ let(:subresource_class) do
+ new_resource_name = self.class.new_resource_name
+ Class.new(resource_class) do
+ resource_name new_resource_name
+ end
+ end
+ let(:subresource) { subresource_class.new('blah') }
+ it "The default is inherited" do
+ expect(subresource.x).to eq 10
+ end
+ end
+ end
+
+ with_property ':x, default: 10, identity: true' do
+ it "when x is not set, it is included in identity" do
+ expect(resource.identity).to eq(10)
+ end
+ end
+
+ with_property ':x, default: 1, identity: true', ':y, default: 2, identity: true' do
+ it "when x is not set, it is still included in identity" do
+ resource.y 20
+ expect(resource.identity).to eq(x: 1, y: 20)
+ end
+ end
+
+ with_property ':x, default: nil' do
+ it "when x is not set, it returns nil" do
+ expect(resource.x).to be_nil
+ end
+ end
+
+ with_property ':x' do
+ it "when x is not set, it returns nil" do
+ expect(resource.x).to be_nil
+ end
+ end
+
+ context "hash default" do
+ context "(deprecations allowed)" do
+ before { Chef::Config[:treat_deprecation_warnings_as_errors] = false }
+
+ with_property ':x, default: {}' do
+ it "when x is not set, it returns {}" do
+ expect(resource.x).to eq({})
+ end
+ it "The same exact value is returned multiple times in a row" do
+ value = resource.x
+ expect(value).to eq({})
+ expect(resource.x.object_id).to eq(value.object_id)
+ end
+ it "Multiple instances of x receive the exact same value" do
+ expect(resource.x.object_id).to eq(resource_class.new('blah2').x.object_id)
+ end
+ end
+ end
+
+ it "when a property is declared with default: {}, a warning is issued" do
+ expect(Chef::Log).to receive(:deprecation).with( /^Property .+\.x has an array or hash default \(\{\}\)\. This means that if one resource modifies or appends to it, all other resources of the same type will also see the changes\. Either freeze the constant with \`\.freeze\` to prevent appending, or use lazy \{ \{\} \}\.$/, /property_spec\.rb/ )
+ resource_class.class_eval("property :x, default: {}", __FILE__, __LINE__)
+ expect(resource.x).to eq({})
+ end
+
+ with_property ':x, default: lazy { {} }' do
+ it "when x is not set, it returns {}" do
+ expect(resource.x).to eq({})
+ end
+ # it "The value is different each time it is called" do
+ # value = resource.x
+ # expect(value).to eq({})
+ # expect(resource.x.object_id).not_to eq(value.object_id)
+ # end
+ it "Multiple instances of x receive different values" do
+ expect(resource.x.object_id).not_to eq(resource_class.new('blah2').x.object_id)
+ end
+ end
+ end
+
+ context "with a class with 'blah' as both class and instance methods" do
+ before do
+ resource_class.class_eval do
+ def self.blah
+ 'class'
+ end
+ def blah
+ "#{name}#{next_index}"
+ end
+ end
+ end
+
+ with_property ':x, default: lazy { blah }' do
+ it "x is run in context of the instance" do
+ expect(resource.x).to eq "blah1"
+ end
+ it "x is run in the context of each instance it is run in" do
+ expect(resource.x).to eq "blah1"
+ expect(resource_class.new('another').x).to eq "another2"
+ # expect(resource.x).to eq "blah3"
+ end
+ end
+
+ with_property ':x, default: lazy { |x| "#{blah}#{x.blah}" }' do
+ it "x is run in context of the class (where it was defined) and passed the instance" do
+ expect(resource.x).to eq "classblah1"
+ end
+ it "x is passed the value of each instance it is run in" do
+ expect(resource.x).to eq "classblah1"
+ expect(resource_class.new('another').x).to eq "classanother2"
+ # expect(resource.x).to eq "classblah3"
+ end
+ end
+ end
+
+ context "validation of defaults" do
+ with_property ':x, String, default: 10' do
+ it "when the resource is created, no error is raised" do
+ resource
+ end
+ it "when x is set, no error is raised" do
+ expect(resource.x 'hi').to eq 'hi'
+ expect(resource.x).to eq 'hi'
+ end
+ it "when x is retrieved, no validation error is raised" do
+ expect(resource.x).to eq 10
+ end
+ # it "when x is retrieved, a validation error is raised" do
+ # expect { resource.x }.to raise_error Chef::Exceptions::ValidationFailed
+ # end
+ end
+
+ with_property ":x, String, default: lazy { Namer.next_index }" do
+ it "when the resource is created, no error is raised" do
+ resource
+ end
+ it "when x is set, no error is raised" do
+ expect(resource.x 'hi').to eq 'hi'
+ expect(resource.x).to eq 'hi'
+ end
+ it "when x is retrieved, no validation error is raised" do
+ expect(resource.x).to eq 1
+ expect(Namer.current_index).to eq 1
+ end
+ # it "when x is retrieved, a validation error is raised" do
+ # expect { resource.x }.to raise_error Chef::Exceptions::ValidationFailed
+ # expect(Namer.current_index).to eq 1
+ # end
+ end
+
+ with_property ":x, default: lazy { Namer.next_index.to_s }, is: proc { |v| Namer.next_index; true }" do
+ it "validation is not run at all on the default value" do
+ expect(resource.x).to eq '1'
+ expect(Namer.current_index).to eq 1
+ end
+ # it "validation is run each time" do
+ # expect(resource.x).to eq '1'
+ # expect(Namer.current_index).to eq 2
+ # expect(resource.x).to eq '1'
+ # expect(Namer.current_index).to eq 2
+ # end
+ end
+
+ with_property ":x, default: lazy { Namer.next_index.to_s.freeze }, is: proc { |v| Namer.next_index; true }" do
+ it "validation is not run at all on the default value" do
+ expect(resource.x).to eq '1'
+ expect(Namer.current_index).to eq 1
+ end
+ # it "validation is only run the first time" do
+ # expect(resource.x).to eq '1'
+ # expect(Namer.current_index).to eq 2
+ # expect(resource.x).to eq '1'
+ # expect(Namer.current_index).to eq 2
+ # end
+ end
+ end
+
+ context "coercion of defaults" do
+ # Frozen default, non-frozen coerce
+ with_property ':x, coerce: proc { |v| "#{v}#{next_index}" }, default: 10' do
+ it "when the resource is created, the proc is not yet run" do
+ resource
+ expect(Namer.current_index).to eq 0
+ end
+ it "when x is set, coercion is run" do
+ expect(resource.x 'hi').to eq 'hi1'
+ expect(resource.x).to eq 'hi1'
+ expect(Namer.current_index).to eq 1
+ end
+ it "when x is retrieved, coercion is run exactly once" do
+ expect(resource.x).to eq '101'
+ expect(resource.x).to eq '101'
+ expect(Namer.current_index).to eq 1
+ end
+ end
+
+ # Frozen default, frozen coerce
+ with_property ':x, coerce: proc { |v| "#{v}#{next_index}".freeze }, default: 10' do
+ it "when the resource is created, the proc is not yet run" do
+ resource
+ expect(Namer.current_index).to eq 0
+ end
+ it "when x is set, coercion is run" do
+ expect(resource.x 'hi').to eq 'hi1'
+ expect(resource.x).to eq 'hi1'
+ expect(Namer.current_index).to eq 1
+ end
+ it "when x is retrieved, coercion is run each time" do
+ expect(resource.x).to eq '101'
+ expect(resource.x).to eq '102'
+ expect(Namer.current_index).to eq 2
+ end
+ end
+
+ # Frozen lazy default, non-frozen coerce
+ with_property ':x, coerce: proc { |v| "#{v}#{next_index}" }, default: lazy { 10 }' do
+ it "when the resource is created, the proc is not yet run" do
+ resource
+ expect(Namer.current_index).to eq 0
+ end
+ it "when x is set, coercion is run" do
+ expect(resource.x 'hi').to eq 'hi1'
+ expect(resource.x).to eq 'hi1'
+ expect(Namer.current_index).to eq 1
+ end
+ it "when x is retrieved, coercion is run exactly once" do
+ expect(resource.x).to eq '101'
+ expect(resource.x).to eq '101'
+ expect(Namer.current_index).to eq 1
+ end
+ end
+
+ # Non-frozen lazy default, frozen coerce
+ with_property ':x, coerce: proc { |v| "#{v}#{next_index}".freeze }, default: lazy { "10" }' do
+ it "when the resource is created, the proc is not yet run" do
+ resource
+ expect(Namer.current_index).to eq 0
+ end
+ it "when x is set, coercion is run" do
+ expect(resource.x 'hi').to eq 'hi1'
+ expect(resource.x).to eq 'hi1'
+ expect(Namer.current_index).to eq 1
+ end
+ it "when x is retrieved, coercion is run each time" do
+ expect(resource.x).to eq '101'
+ expect(resource.x).to eq '102'
+ expect(Namer.current_index).to eq 2
+ end
+ end
+
+ with_property ':x, proc { |v| Namer.next_index; true }, coerce: proc { |v| "#{v}#{next_index}" }, default: lazy { 10 }' do
+ it "coercion is only run the first time x is retrieved, and validation is not run" do
+ expect(Namer.current_index).to eq 0
+ expect(resource.x).to eq '101'
+ expect(Namer.current_index).to eq 1
+ expect(resource.x).to eq '101'
+ expect(Namer.current_index).to eq 1
+ end
+ end
+
+ context "validation and coercion of defaults" do
+ with_property ':x, String, coerce: proc { |v| "#{v}#{next_index}" }, default: 10' do
+ it "when x is retrieved, it is coerced before validating and passes" do
+ expect(resource.x).to eq '101'
+ end
+ end
+ with_property ':x, Integer, coerce: proc { |v| "#{v}#{next_index}" }, default: 10' do
+ it "when x is retrieved, it is coerced and not validated" do
+ expect(resource.x).to eq '101'
+ end
+ # it "when x is retrieved, it is coerced before validating and fails" do
+ # expect { resource.x }.to raise_error Chef::Exceptions::ValidationFailed
+ # end
+ end
+ with_property ':x, String, coerce: proc { |v| "#{v}#{next_index}" }, default: lazy { 10 }' do
+ it "when x is retrieved, it is coerced before validating and passes" do
+ expect(resource.x).to eq '101'
+ end
+ end
+ with_property ':x, Integer, coerce: proc { |v| "#{v}#{next_index}" }, default: lazy { 10 }' do
+ it "when x is retrieved, it is coerced and not validated" do
+ expect(resource.x).to eq '101'
+ end
+ # it "when x is retrieved, it is coerced before validating and fails" do
+ # expect { resource.x }.to raise_error Chef::Exceptions::ValidationFailed
+ # end
+ end
+ with_property ':x, proc { |v| Namer.next_index; true }, coerce: proc { |v| "#{v}#{next_index}" }, default: lazy { 10 }' do
+ it "coercion is only run the first time x is retrieved, and validation is not run" do
+ expect(Namer.current_index).to eq 0
+ expect(resource.x).to eq '101'
+ expect(Namer.current_index).to eq 1
+ expect(resource.x).to eq '101'
+ expect(Namer.current_index).to eq 1
+ end
+ end
+ end
+ end
+ end
+
+ context "Chef::Resource#lazy" do
+ with_property ':x' do
+ it "setting x to a lazy value does not run it immediately" do
+ resource.x lazy { Namer.next_index }
+ expect(Namer.current_index).to eq 0
+ end
+ it "you can set x to a lazy value in the instance" do
+ resource.instance_eval do
+ x lazy { Namer.next_index }
+ end
+ expect(resource.x).to eq 1
+ expect(Namer.current_index).to eq 1
+ end
+ it "retrieving a lazy value pops it open" do
+ resource.x lazy { Namer.next_index }
+ expect(resource.x).to eq 1
+ expect(Namer.current_index).to eq 1
+ end
+ it "retrieving a lazy value twice evaluates it twice" do
+ resource.x lazy { Namer.next_index }
+ expect(resource.x).to eq 1
+ expect(resource.x).to eq 2
+ expect(Namer.current_index).to eq 2
+ end
+ it "setting the same lazy value on two different instances runs it on each instancee" do
+ resource2 = resource_class.new("blah2")
+ l = lazy { Namer.next_index }
+ resource.x l
+ resource2.x l
+ expect(resource2.x).to eq 1
+ expect(resource.x).to eq 2
+ expect(resource2.x).to eq 3
+ end
+
+ context "when the class has a class and instance method named blah" do
+ before do
+ resource_class.class_eval do
+ def self.blah
+ "class"
+ end
+ def blah
+ "#{name}#{Namer.next_index}"
+ end
+ end
+ end
+ def blah
+ "example"
+ end
+ # it "retrieving lazy { blah } gets the instance variable" do
+ # resource.x lazy { blah }
+ # expect(resource.x).to eq "blah1"
+ # end
+ # it "retrieving lazy { blah } from two different instances gets two different instance variables" do
+ # resource2 = resource_class.new("another")
+ # l = lazy { blah }
+ # resource2.x l
+ # resource.x l
+ # expect(resource2.x).to eq "another1"
+ # expect(resource.x).to eq "blah2"
+ # expect(resource2.x).to eq "another3"
+ # end
+ it 'retrieving lazy { |x| "#{blah}#{x.blah}" } gets the example and instance variables' do
+ resource.x lazy { |x| "#{blah}#{x.blah}" }
+ expect(resource.x).to eq "exampleblah1"
+ end
+ it 'retrieving lazy { |x| "#{blah}#{x.blah}" } from two different instances gets two different instance variables' do
+ resource2 = resource_class.new("another")
+ l = lazy { |x| "#{blah}#{x.blah}" }
+ resource2.x l
+ resource.x l
+ expect(resource2.x).to eq "exampleanother1"
+ expect(resource.x).to eq "exampleblah2"
+ expect(resource2.x).to eq "exampleanother3"
+ end
+ end
+ end
+
+ with_property ':x, coerce: proc { |v| "#{v}#{Namer.next_index}" }' do
+ it "lazy values are not coerced on set" do
+ resource.x lazy { Namer.next_index }
+ expect(Namer.current_index).to eq 0
+ end
+ it "lazy values are coerced on get" do
+ resource.x lazy { Namer.next_index }
+ expect(resource.x).to eq "12"
+ expect(Namer.current_index).to eq 2
+ end
+ it "lazy values are coerced on each access" do
+ resource.x lazy { Namer.next_index }
+ expect(resource.x).to eq "12"
+ expect(Namer.current_index).to eq 2
+ expect(resource.x).to eq "34"
+ expect(Namer.current_index).to eq 4
+ end
+ end
+
+ with_property ':x, String' do
+ it "lazy values are not validated on set" do
+ resource.x lazy { Namer.next_index }
+ expect(Namer.current_index).to eq 0
+ end
+ it "lazy values are validated on get" do
+ resource.x lazy { Namer.next_index }
+ expect { resource.x }.to raise_error Chef::Exceptions::ValidationFailed
+ expect(Namer.current_index).to eq 1
+ end
+ end
+
+ with_property ':x, is: proc { |v| Namer.next_index; true }' do
+ it "lazy values are validated on each access" do
+ resource.x lazy { Namer.next_index }
+ expect(resource.x).to eq 1
+ expect(Namer.current_index).to eq 2
+ expect(resource.x).to eq 3
+ expect(Namer.current_index).to eq 4
+ end
+ end
+
+ with_property ':x, Integer, coerce: proc { |v| "#{v}#{Namer.next_index}" }' do
+ it "lazy values are not validated or coerced on set" do
+ resource.x lazy { Namer.next_index }
+ expect(Namer.current_index).to eq 0
+ end
+ it "lazy values are coerced before being validated, which fails" do
+ resource.x lazy { Namer.next_index }
+ expect(Namer.current_index).to eq 0
+ expect { resource.x }.to raise_error Chef::Exceptions::ValidationFailed
+ expect(Namer.current_index).to eq 2
+ end
+ end
+
+ with_property ':x, coerce: proc { |v| "#{v}#{Namer.next_index}" }, is: proc { |v| Namer.next_index; true }' do
+ it "lazy values are coerced and validated exactly once" do
+ resource.x lazy { Namer.next_index }
+ expect(resource.x).to eq "12"
+ expect(Namer.current_index).to eq 3
+ expect(resource.x).to eq "45"
+ expect(Namer.current_index).to eq 6
+ end
+ end
+
+ with_property ':x, String, coerce: proc { |v| "#{v}#{Namer.next_index}" }' do
+ it "lazy values are coerced before being validated, which succeeds" do
+ resource.x lazy { Namer.next_index }
+ expect(resource.x).to eq "12"
+ expect(Namer.current_index).to eq 2
+ end
+ end
+ end
+
+ context "Chef::Resource::Property#coerce" do
+ with_property ':x, coerce: proc { |v| "#{v}#{Namer.next_index}" }' do
+ it "coercion runs on set" do
+ expect(resource.x 10).to eq "101"
+ expect(Namer.current_index).to eq 1
+ end
+ it "coercion sets the value (and coercion does not run on get)" do
+ expect(resource.x 10).to eq "101"
+ expect(resource.x).to eq "101"
+ expect(Namer.current_index).to eq 1
+ end
+ it "coercion runs each time set happens" do
+ expect(resource.x 10).to eq "101"
+ expect(Namer.current_index).to eq 1
+ expect(resource.x 10).to eq "102"
+ expect(Namer.current_index).to eq 2
+ end
+ end
+ with_property ':x, coerce: proc { |x| Namer.next_index; raise "hi" if x == 10; x }, is: proc { |x| Namer.next_index; x != 10 }' do
+ it "failed coercion fails to set the value" do
+ resource.x 20
+ expect(resource.x).to eq 20
+ expect(Namer.current_index).to eq 2
+ expect { resource.x 10 }.to raise_error 'hi'
+ expect(resource.x).to eq 20
+ expect(Namer.current_index).to eq 3
+ end
+ it "validation does not run if coercion fails" do
+ expect { resource.x 10 }.to raise_error 'hi'
+ expect(Namer.current_index).to eq 1
+ end
+ end
+ end
+
+ context "Chef::Resource::Property validation" do
+ with_property ':x, is: proc { |v| Namer.next_index; v.is_a?(Integer) }' do
+ it "validation runs on set" do
+ expect(resource.x 10).to eq 10
+ expect(Namer.current_index).to eq 1
+ end
+ it "validation sets the value (and validation does not run on get)" do
+ expect(resource.x 10).to eq 10
+ expect(resource.x).to eq 10
+ expect(Namer.current_index).to eq 1
+ end
+ it "validation runs each time set happens" do
+ expect(resource.x 10).to eq 10
+ expect(Namer.current_index).to eq 1
+ expect(resource.x 10).to eq 10
+ expect(Namer.current_index).to eq 2
+ end
+ it "failed validation fails to set the value" do
+ expect(resource.x 10).to eq 10
+ expect(Namer.current_index).to eq 1
+ expect { resource.x 'blah' }.to raise_error Chef::Exceptions::ValidationFailed
+ expect(resource.x).to eq 10
+ expect(Namer.current_index).to eq 2
+ end
+ end
+ end
+
+ [ 'name_attribute', 'name_property' ].each do |name|
+ context "Chef::Resource::Property##{name}" do
+ with_property ":x, #{name}: true" do
+ it "defaults x to resource.name" do
+ expect(resource.x).to eq 'blah'
+ end
+ it "does not pick up resource.name if set" do
+ expect(resource.x 10).to eq 10
+ expect(resource.x).to eq 10
+ end
+ it "binds to the latest resource.name when run" do
+ resource.name 'foo'
+ expect(resource.x).to eq 'foo'
+ end
+ it "caches resource.name" do
+ expect(resource.x).to eq 'blah'
+ resource.name 'foo'
+ expect(resource.x).to eq 'blah'
+ end
+ end
+ with_property ":x, default: 10, #{name}: true" do
+ it "chooses default over #{name}" do
+ expect(resource.x).to eq 10
+ end
+ end
+ with_property ":x, #{name}: true, default: 10" do
+ it "chooses default over #{name}" do
+ expect(resource.x).to eq 10
+ end
+ end
+ with_property ":x, #{name}: true, required: true" do
+ it "defaults x to resource.name" do
+ expect(resource.x).to eq 'blah'
+ end
+ end
+ end
+ end
+end
diff --git a/spec/unit/provider/deploy/revision_spec.rb b/spec/unit/provider/deploy/revision_spec.rb
index 4ca64e3445..caa60878e1 100644
--- a/spec/unit/provider/deploy/revision_spec.rb
+++ b/spec/unit/provider/deploy/revision_spec.rb
@@ -21,7 +21,7 @@ require 'spec_helper'
describe Chef::Provider::Deploy::Revision do
before do
- allow(Chef::Platform).to receive(:windows?) { false }
+ allow(ChefConfig).to receive(:windows?) { false }
@temp_dir = Dir.mktmpdir
Chef::Config[:file_cache_path] = @temp_dir
@resource = Chef::Resource::Deploy.new("/my/deploy/dir")
diff --git a/spec/unit/provider/deploy_spec.rb b/spec/unit/provider/deploy_spec.rb
index c95a9b3d57..f6bb78823f 100644
--- a/spec/unit/provider/deploy_spec.rb
+++ b/spec/unit/provider/deploy_spec.rb
@@ -21,7 +21,7 @@ require 'spec_helper'
describe Chef::Provider::Deploy do
before do
- allow(Chef::Platform).to receive(:windows?) { false }
+ allow(ChefConfig).to receive(:windows?) { false }
@release_time = Time.utc( 2004, 8, 15, 16, 23, 42)
allow(Time).to receive(:now).and_return(@release_time)
@expected_release_dir = "/my/deploy/dir/releases/20040815162342"
@@ -622,7 +622,7 @@ describe Chef::Provider::Deploy do
gems = @provider.send(:gem_packages)
- expect(gems.map { |g| g.action }).to eq([[:install]])
+ expect(gems.map { |g| g.action }).to eq([%i{install}])
expect(gems.map { |g| g.name }).to eq(%w{eventmachine})
expect(gems.map { |g| g.version }).to eq(%w{0.12.9})
end
diff --git a/spec/unit/provider/directory_spec.rb b/spec/unit/provider/directory_spec.rb
index 13c57bfe56..4fad8c8906 100644
--- a/spec/unit/provider/directory_spec.rb
+++ b/spec/unit/provider/directory_spec.rb
@@ -16,173 +16,272 @@
# limitations under the License.
#
-require 'ostruct'
-
require 'spec_helper'
require 'tmpdir'
describe Chef::Provider::Directory do
- before(:each) do
- @new_resource = Chef::Resource::Directory.new(Dir.tmpdir)
- if !windows?
- @new_resource.owner(500)
- @new_resource.group(500)
- @new_resource.mode(0644)
+ let(:tmp_dir) { Dir.mktmpdir }
+ let(:new_resource) { Chef::Resource::Directory.new(tmp_dir) }
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:directory) { Chef::Provider::Directory.new(new_resource, run_context) }
+
+ describe "#load_current_resource" do
+ describe "scanning file security metadata"
+ describe "on unix", unix_only: true do
+ describe "when the directory exists" do
+ let(:dir_stat) { File::Stat.new(tmp_dir) }
+ let(:expected_uid) { dir_stat.uid }
+ let(:expected_gid) { dir_stat.gid }
+ let(:expected_mode) { "0%o" % ( dir_stat.mode & 007777 ) }
+ let(:expected_pwnam) { Etc.getpwuid(expected_uid).name }
+ let(:expected_grnam) { Etc.getgrgid(expected_gid).name }
+
+ it "describes the access mode as a String of octal integers" do
+ directory.load_current_resource
+ expect(directory.current_resource.mode).to eq(expected_mode)
+ end
+
+ it "when the new_resource.owner is numeric, describes the owner as a numeric uid" do
+ new_resource.owner(500)
+ directory.load_current_resource
+ expect(directory.current_resource.owner).to eql(expected_uid)
+ end
+
+ it "when the new_resource.group is numeric, describes the group as a numeric gid" do
+ new_resource.group(500)
+ directory.load_current_resource
+ expect(directory.current_resource.group).to eql(expected_gid)
+ end
+
+ it "when the new_resource.owner is a string, describes the owner as a string" do
+ new_resource.owner("foo")
+ directory.load_current_resource
+ expect(directory.current_resource.owner).to eql(expected_pwnam)
+ end
+
+ it "when the new_resource.group is a string, describes the group as a string" do
+ new_resource.group("bar")
+ directory.load_current_resource
+ expect(directory.current_resource.group).to eql(expected_grnam)
+ end
+ end
end
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
- @directory = Chef::Provider::Directory.new(@new_resource, @run_context)
- end
+ describe "on windows", windows_only: true do
+ describe "when the directory exists" do
+ it "the mode is always nil" do
+ directory.load_current_resource
+ expect(directory.current_resource.mode).to be nil
+ end
+
+ it "the owner is always nil" do
+ directory.load_current_resource
+ expect(directory.current_resource.owner).to be nil
+ end
+ it "the group is always nil" do
+ directory.load_current_resource
+ expect(directory.current_resource.group).to be nil
+ end
- describe "scanning file security metadata on windows" do
- before do
+ it "rights are always nil (incorrectly)" do
+ directory.load_current_resource
+ expect(directory.current_resource.rights).to be nil
+ end
+
+ it "inherits is always nil (incorrectly)" do
+ directory.load_current_resource
+ expect(directory.current_resource.inherits).to be nil
+ end
+ end
end
- it "describes the directory's access rights" do
- skip
+ describe "when the directory does not exist" do
+ before do
+ FileUtils.rmdir tmp_dir
+ end
+
+ it "sets the mode, group and owner to nil" do
+ directory.load_current_resource
+ expect(directory.current_resource.mode).to eq(nil)
+ expect(directory.current_resource.group).to eq(nil)
+ expect(directory.current_resource.owner).to eq(nil)
+ end
end
+
end
- describe "scanning file security metadata on unix" do
- before do
- allow(Chef::Platform).to receive(:windows?).and_return(false)
- end
- let(:mock_stat) do
- cstats = double("stats")
- allow(cstats).to receive(:uid).and_return(500)
- allow(cstats).to receive(:gid).and_return(500)
- allow(cstats).to receive(:mode).and_return(0755)
- cstats
+ describe "#define_resource_requirements" do
+ describe "on unix", unix_only: true do
+ it "raises an exception if the user does not exist" do
+ new_resource.owner("arglebargle_iv")
+ expect(Etc).to receive(:getpwnam).with("arglebargle_iv").and_raise(ArgumentError)
+ directory.action = :create
+ directory.load_current_resource
+ expect(directory.access_controls).to receive(:define_resource_requirements).and_call_original
+ directory.define_resource_requirements
+ expect { directory.process_resource_requirements }.to raise_error(ArgumentError)
+ end
+
+ it "raises an exception if the group does not exist" do
+ new_resource.group("arglebargle_iv")
+ expect(Etc).to receive(:getgrnam).with("arglebargle_iv").and_raise(ArgumentError)
+ directory.action = :create
+ directory.load_current_resource
+ expect(directory.access_controls).to receive(:define_resource_requirements).and_call_original
+ directory.define_resource_requirements
+ expect { directory.process_resource_requirements }.to raise_error(ArgumentError)
+ end
end
+ end
+
+ describe "#run_action(:create)" do
+ describe "when the directory exists" do
+ it "does not create the directory" do
+ expect(Dir).not_to receive(:mkdir).with(new_resource.path)
+ directory.run_action(:create)
+ end
- it "describes the access mode as a String of octal integers" do
- allow(File).to receive(:exists?).and_return(true)
- expect(File).to receive(:stat).and_return(mock_stat)
- @directory.load_current_resource
- expect(@directory.current_resource.mode).to eq("0755")
+ it "should not set the resource as updated" do
+ directory.run_action(:create)
+ expect(new_resource).not_to be_updated
+ end
end
- context "when user and group are specified with UID/GID" do
- it "describes the current owner and group as UID and GID" do
- allow(File).to receive(:exists?).and_return(true)
- expect(File).to receive(:stat).and_return(mock_stat)
- @directory.load_current_resource
- expect(@directory.current_resource.path).to eql(@new_resource.path)
- expect(@directory.current_resource.owner).to eql(500)
- expect(@directory.current_resource.group).to eql(500)
+ describe "when the directory does not exist" do
+ before do
+ FileUtils.rmdir tmp_dir
+ end
+
+ it "creates the directory" do
+ directory.run_action(:create)
+ expect(File.exist?(tmp_dir)).to be true
+ end
+
+ it "sets the new resource as updated" do
+ directory.run_action(:create)
+ expect(new_resource).to be_updated
end
end
- context "when user/group are specified with user/group names" do
+ describe "when the parent directory does not exist" do
+ before do
+ new_resource.path "#{tmp_dir}/foobar"
+ FileUtils.rmdir tmp_dir
+ end
+
+ it "raises an exception when recursive is false" do
+ new_resource.recursive false
+ expect { directory.run_action(:create) }.to raise_error(Chef::Exceptions::EnclosingDirectoryDoesNotExist)
+ end
+
+ it "creates the directories when recursive is true" do
+ new_resource.recursive true
+ directory.run_action(:create)
+ expect(new_resource).to be_updated
+ expect(File.exist?("#{tmp_dir}/foobar")).to be true
+ end
+
+ it "raises an exception when the parent directory is a file and recursive is true" do
+ FileUtils.touch tmp_dir
+ new_resource.recursive true
+ expect { directory.run_action(:create) }.to raise_error
+ end
+
+ it "raises the right exception when the parent directory is a file and recursive is true" do
+ pending "this seems to return the wrong error" # FIXME
+ FileUtils.touch tmp_dir
+ new_resource.recursive true
+ expect { directory.run_action(:create) }.to raise_error(Chef::Exceptions::EnclosingDirectoryDoesNotExist)
+ end
end
- end
- # Unix only for now. While file security attribute reporting for windows is
- # disabled, unix and windows differ in the number of exists? calls that are
- # made by the provider.
- it "should create a new directory on create, setting updated to true", :unix_only do
- @new_resource.path "/tmp/foo"
+ describe "on OS X" do
+ before do
+ allow(node).to receive(:[]).with("platform").and_return('mac_os_x')
+ new_resource.path "/usr/bin/chef_test"
+ new_resource.recursive false
+ allow_any_instance_of(Chef::Provider::File).to receive(:do_selinux)
+ end
- expect(File).to receive(:exists?).at_least(:once).and_return(false)
- expect(File).to receive(:directory?).with("/tmp").and_return(true)
- expect(Dir).to receive(:mkdir).with(@new_resource.path).once.and_return(true)
+ it "os x 10.10 can write to sip locations" do
+ allow(node).to receive(:[]).with("platform_version").and_return('10.10')
+ allow(Dir).to receive(:mkdir).and_return([true], [])
+ allow(::File).to receive(:directory?).and_return(true)
+ allow(Chef::FileAccessControl).to receive(:writable?).and_return(true)
+ directory.run_action(:create)
+ expect(new_resource).to be_updated
+ end
- expect(@directory).to receive(:do_acl_changes)
- allow(@directory).to receive(:do_selinux)
- @directory.run_action(:create)
- expect(@directory.new_resource).to be_updated
- end
+ it "os x 10.11 cannot write to sip locations" do
+ allow(node).to receive(:[]).with("platform_version").and_return('10.11')
+ allow(::File).to receive(:directory?).and_return(true)
+ allow(Chef::FileAccessControl).to receive(:writable?).and_return(false)
+ expect {directory.run_action(:create) }.to raise_error(Chef::Exceptions::InsufficientPermissions)
+ end
- it "should raise an exception if the parent directory does not exist and recursive is false" do
- @new_resource.path "/tmp/some/dir"
- @new_resource.recursive false
- expect { @directory.run_action(:create) }.to raise_error(Chef::Exceptions::EnclosingDirectoryDoesNotExist)
+ it "os x 10.11 can write to sip exlcusions" do
+ new_resource.path "/usr/local/chef_test"
+ allow(node).to receive(:[]).with("platform_version").and_return('10.11')
+ allow(::File).to receive(:directory?).and_return(true)
+ allow(Dir).to receive(:mkdir).and_return([true], [])
+ allow(Chef::FileAccessControl).to receive(:writable?).and_return(false)
+ directory.run_action(:create)
+ expect(new_resource).to be_updated
+ end
+ end
end
- # Unix only for now. While file security attribute reporting for windows is
- # disabled, unix and windows differ in the number of exists? calls that are
- # made by the provider.
- it "should create a new directory when parent directory does not exist if recursive is true and permissions are correct", :unix_only do
- @new_resource.path "/path/to/dir"
- @new_resource.recursive true
- expect(File).to receive(:exists?).with(@new_resource.path).ordered.and_return(false)
-
- expect(File).to receive(:exists?).with('/path/to').ordered.and_return(false)
- expect(File).to receive(:exists?).with('/path').ordered.and_return(true)
- expect(Chef::FileAccessControl).to receive(:writable?).with('/path').ordered.and_return(true)
- expect(File).to receive(:exists?).with(@new_resource.path).ordered.and_return(false)
-
- expect(FileUtils).to receive(:mkdir_p).with(@new_resource.path).and_return(true)
- expect(@directory).to receive(:do_acl_changes)
- allow(@directory).to receive(:do_selinux)
- @directory.run_action(:create)
- expect(@new_resource).to be_updated
- end
+ describe "#run_action(:create)" do
+ describe "when the directory exists" do
+ it "deletes the directory" do
+ directory.run_action(:delete)
+ expect(File.exist?(tmp_dir)).to be false
+ end
+ it "sets the new resource as updated" do
+ directory.run_action(:delete)
+ expect(new_resource).to be_updated
+ end
+ end
- it "should raise an error when creating a directory when parent directory is a file" do
- expect(File).to receive(:directory?).and_return(false)
- expect(Dir).not_to receive(:mkdir).with(@new_resource.path)
- expect { @directory.run_action(:create) }.to raise_error(Chef::Exceptions::EnclosingDirectoryDoesNotExist)
- expect(@directory.new_resource).not_to be_updated
- end
+ describe "when the directory does not exist" do
+ before do
+ FileUtils.rmdir tmp_dir
+ end
- # Unix only for now. While file security attribute reporting for windows is
- # disabled, unix and windows differ in the number of exists? calls that are
- # made by the provider.
- it "should not create the directory if it already exists", :unix_only do
- stub_file_cstats
- @new_resource.path "/tmp/foo"
- expect(File).to receive(:directory?).at_least(:once).and_return(true)
- expect(Chef::FileAccessControl).to receive(:writable?).with("/tmp").and_return(true)
- expect(File).to receive(:exists?).at_least(:once).and_return(true)
- expect(Dir).not_to receive(:mkdir).with(@new_resource.path)
- expect(@directory).to receive(:do_acl_changes)
- @directory.run_action(:create)
- end
+ it "does not delete the directory" do
+ expect(Dir).not_to receive(:delete).with(new_resource.path)
+ directory.run_action(:delete)
+ end
- it "should delete the directory if it exists, and is writable with action_delete" do
- expect(File).to receive(:directory?).and_return(true)
- expect(Chef::FileAccessControl).to receive(:writable?).once.and_return(true)
- expect(Dir).to receive(:delete).with(@new_resource.path).once.and_return(true)
- @directory.run_action(:delete)
- end
+ it "sets the new resource as updated" do
+ directory.run_action(:delete)
+ expect(new_resource).not_to be_updated
+ end
+ end
- it "should raise an exception if it cannot delete the directory due to bad permissions" do
- allow(File).to receive(:exists?).and_return(true)
- allow(Chef::FileAccessControl).to receive(:writable?).and_return(false)
- expect { @directory.run_action(:delete) }.to raise_error(RuntimeError)
- end
+ describe "when the directory is not writable" do
+ before do
+ allow(Chef::FileAccessControl).to receive(:writable?).and_return(false)
+ end
- it "should take no action when deleting a target directory that does not exist" do
- @new_resource.path "/an/invalid/path"
- allow(File).to receive(:exists?).and_return(false)
- expect(Dir).not_to receive(:delete).with(@new_resource.path)
- @directory.run_action(:delete)
- expect(@directory.new_resource).not_to be_updated
- end
+ it "cannot delete it and raises an exception" do
+ expect { directory.run_action(:delete) }.to raise_error(RuntimeError)
+ end
+ end
- it "should raise an exception when deleting a directory when target directory is a file" do
- stub_file_cstats
- @new_resource.path "/an/invalid/path"
- allow(File).to receive(:exists?).and_return(true)
- expect(File).to receive(:directory?).and_return(false)
- expect(Dir).not_to receive(:delete).with(@new_resource.path)
- expect { @directory.run_action(:delete) }.to raise_error(RuntimeError)
- expect(@directory.new_resource).not_to be_updated
- end
+ describe "when the target directory is a file" do
+ before do
+ FileUtils.rmdir tmp_dir
+ FileUtils.touch tmp_dir
+ end
- def stub_file_cstats
- cstats = double("stats")
- allow(cstats).to receive(:uid).and_return(500)
- allow(cstats).to receive(:gid).and_return(500)
- allow(cstats).to receive(:mode).and_return(0755)
- # File.stat is called in:
- # - Chef::Provider::File.load_current_resource_attrs
- # - Chef::ScanAccessControl via Chef::Provider::File.setup_acl
- allow(File).to receive(:stat).and_return(cstats)
+ it "cannot delete it and raises an exception" do
+ expect { directory.run_action(:delete) }.to raise_error(RuntimeError)
+ end
+ end
end
end
diff --git a/spec/unit/provider/dsc_resource_spec.rb b/spec/unit/provider/dsc_resource_spec.rb
new file mode 100644
index 0000000000..65c1c019f0
--- /dev/null
+++ b/spec/unit/provider/dsc_resource_spec.rb
@@ -0,0 +1,84 @@
+#
+# Author:: Jay Mundrawala (<jdm@chef.io>)
+#
+# Copyright:: Copyright (c) 2014 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef'
+require 'spec_helper'
+
+describe Chef::Provider::DscResource do
+ let (:events) { Chef::EventDispatch::Dispatcher.new }
+ let (:run_context) { Chef::RunContext.new(node, {}, events) }
+ let (:resource) { Chef::Resource::DscResource.new("dscresource", run_context) }
+ let (:provider) do
+ Chef::Provider::DscResource.new(resource, run_context)
+ end
+
+ context 'when Powershell does not support Invoke-DscResource' do
+ let (:node) {
+ node = Chef::Node.new
+ node.automatic[:languages][:powershell][:version] = '4.0'
+ node
+ }
+
+ it 'raises a ProviderNotFound exception' do
+ expect(provider).not_to receive(:meta_configuration)
+ expect{provider.run_action(:run)}.to raise_error(
+ Chef::Exceptions::ProviderNotFound, /5\.0\.10018\.0/)
+ end
+ end
+
+ context 'when Powershell supports Invoke-DscResource' do
+ let (:node) {
+ node = Chef::Node.new
+ node.automatic[:languages][:powershell][:version] = '5.0.10018.0'
+ node
+ }
+
+ context 'when RefreshMode is not set to Disabled' do
+ let (:meta_configuration) { {'RefreshMode' => 'AnythingElse'}}
+
+ it 'raises an exception' do
+ expect(provider).to receive(:meta_configuration).and_return(
+ meta_configuration)
+ expect { provider.run_action(:run) }.to raise_error(
+ Chef::Exceptions::ProviderNotFound, /Disabled/)
+ end
+ end
+
+ context 'when RefreshMode is set to Disabled' do
+ let (:meta_configuration) { {'RefreshMode' => 'Disabled'}}
+
+ it 'does not update the resource if it is up to date' do
+ expect(provider).to receive(:meta_configuration).and_return(
+ meta_configuration)
+ expect(provider).to receive(:test_resource).and_return(true)
+ provider.run_action(:run)
+ expect(resource).not_to be_updated
+ end
+
+ it 'converges the resource if it is not up to date' do
+ expect(provider).to receive(:meta_configuration).and_return(
+ meta_configuration)
+ expect(provider).to receive(:test_resource).and_return(false)
+ expect(provider).to receive(:set_resource)
+ provider.run_action(:run)
+ expect(resource).to be_updated
+ end
+ end
+ end
+end
diff --git a/spec/unit/provider/dsc_script_spec.rb b/spec/unit/provider/dsc_script_spec.rb
index d4b2eb3b22..76589e71c1 100644
--- a/spec/unit/provider/dsc_script_spec.rb
+++ b/spec/unit/provider/dsc_script_spec.rb
@@ -158,14 +158,14 @@ describe Chef::Provider::DscScript do
expect {
provider.run_action(:run)
- }.to raise_error(Chef::Exceptions::NoProviderAvailable)
+ }.to raise_error(Chef::Exceptions::ProviderNotFound)
end
end
it 'raises an exception if Powershell is not present' do
expect {
provider.run_action(:run)
- }.to raise_error(Chef::Exceptions::NoProviderAvailable)
+ }.to raise_error(Chef::Exceptions::ProviderNotFound)
end
end
diff --git a/spec/unit/provider/execute_spec.rb b/spec/unit/provider/execute_spec.rb
index 51305b6225..1274203ce3 100644
--- a/spec/unit/provider/execute_spec.rb
+++ b/spec/unit/provider/execute_spec.rb
@@ -39,7 +39,7 @@ describe Chef::Provider::Execute do
let(:new_resource) { Chef::Resource::Execute.new("foo_resource", run_context) }
before do
- allow(Chef::Platform).to receive(:windows?) { false }
+ allow(ChefConfig).to receive(:windows?) { false }
@original_log_level = Chef::Log.level
Chef::Log.level = :info
allow(STDOUT).to receive(:tty?).and_return(true)
diff --git a/spec/unit/provider/ifconfig/debian_spec.rb b/spec/unit/provider/ifconfig/debian_spec.rb
index 351e734040..0c02ae9cd4 100644
--- a/spec/unit/provider/ifconfig/debian_spec.rb
+++ b/spec/unit/provider/ifconfig/debian_spec.rb
@@ -144,11 +144,6 @@ EOF
expect(IO.read(tempfile.path)).to eq(expected_string)
end
- it "should not mark the resource as updated" do
- provider.run_action(:add)
- pending "superclass ifconfig provider is not idempotent"
- expect(new_resource.updated_by_last_action?).to be_falsey
- end
end
context "when the /etc/network/interfaces file does not have the source line" do
@@ -280,11 +275,6 @@ another line
expect(IO.read(tempfile.path)).to eq(expected_string)
end
- it "should not mark the resource as updated" do
- provider.run_action(:add)
- pending "superclass ifconfig provider is not idempotent"
- expect(new_resource.updated_by_last_action?).to be_falsey
- end
end
context "when the /etc/network/interfaces file does not have the source line" do
diff --git a/spec/unit/provider/ifconfig_spec.rb b/spec/unit/provider/ifconfig_spec.rb
index d290ab7066..4940f19a45 100644
--- a/spec/unit/provider/ifconfig_spec.rb
+++ b/spec/unit/provider/ifconfig_spec.rb
@@ -46,7 +46,7 @@ describe Chef::Provider::Ifconfig do
allow(@provider).to receive(:shell_out).and_return(@status)
@provider.load_current_resource
end
- it "should track state of ifconfig failure." do
+ it "should track state of ifconfig failure" do
expect(@provider.instance_variable_get("@status").exitstatus).not_to eq(0)
end
it "should thrown an exception when ifconfig fails" do
@@ -68,6 +68,16 @@ describe Chef::Provider::Ifconfig do
expect(@new_resource).to be_updated
end
+ it "should set the address to target if specified" do
+ allow(@provider).to receive(:load_current_resource)
+ @new_resource.target "172.16.32.2"
+ command = "ifconfig eth0 172.16.32.2 netmask 255.255.254.0 metric 1 mtu 1500"
+ expect(@provider).to receive(:run_command).with(:command => command)
+
+ @provider.run_action(:add)
+ expect(@new_resource).to be_updated
+ end
+
it "should not add an interface if it already exists" do
allow(@provider).to receive(:load_current_resource)
expect(@provider).not_to receive(:run_command)
@@ -85,7 +95,7 @@ describe Chef::Provider::Ifconfig do
describe Chef::Provider::Ifconfig, "action_enable" do
- it "should enable interface if does not exist" do
+ it "should enable interface if it does not exist" do
allow(@provider).to receive(:load_current_resource)
@current_resource.inet_addr nil
command = "ifconfig eth0 10.0.0.1 netmask 255.255.254.0 metric 1 mtu 1500"
@@ -96,6 +106,16 @@ describe Chef::Provider::Ifconfig do
expect(@new_resource).to be_updated
end
+ it "should set the address to target if specified" do
+ allow(@provider).to receive(:load_current_resource)
+ @new_resource.target "172.16.32.2"
+ command = "ifconfig eth0 172.16.32.2 netmask 255.255.254.0 metric 1 mtu 1500"
+ expect(@provider).to receive(:run_command).with(:command => command)
+
+ @provider.run_action(:enable)
+ expect(@new_resource).to be_updated
+ end
+
it "should not enable interface if it already exists" do
allow(@provider).to receive(:load_current_resource)
expect(@provider).not_to receive(:run_command)
diff --git a/spec/unit/provider/mount/aix_spec.rb b/spec/unit/provider/mount/aix_spec.rb
index ca0ddd006c..e232592275 100644
--- a/spec/unit/provider/mount/aix_spec.rb
+++ b/spec/unit/provider/mount/aix_spec.rb
@@ -126,9 +126,10 @@ ENABLED
@provider.run_action(:mount)
end
- it "should not mount resource if it is already mounted" do
+ it "should not mount resource if it is already mounted and the options have not changed" do
stub_mounted_enabled(@provider, @mounted_output, "")
+ allow(@provider).to receive(:mount_options_unchanged?).and_return(true)
expect(@provider).not_to receive(:mount_fs)
@provider.run_action(:mount)
diff --git a/spec/unit/provider/mount/mount_spec.rb b/spec/unit/provider/mount/mount_spec.rb
index 7a37ffe74e..dd13a62796 100644
--- a/spec/unit/provider/mount/mount_spec.rb
+++ b/spec/unit/provider/mount/mount_spec.rb
@@ -323,6 +323,12 @@ describe Chef::Provider::Mount::Mount do
@provider.mount_fs()
end
+ it "should not mount the filesystem if it is mounted and the options have not changed" do
+ allow(@current_resource).to receive(:mounted).and_return(true)
+ expect(@provider).to_not receive(:shell_out!)
+ @provider.mount_fs()
+ end
+
end
describe "umount_fs" do
diff --git a/spec/unit/provider/mount/windows_spec.rb b/spec/unit/provider/mount/windows_spec.rb
index 467d923c6a..2de6f11d43 100644
--- a/spec/unit/provider/mount/windows_spec.rb
+++ b/spec/unit/provider/mount/windows_spec.rb
@@ -111,6 +111,20 @@ describe Chef::Provider::Mount::Windows do
allow(@current_resource).to receive(:mounted).and_return(true)
@provider.mount_fs
end
+
+ it "should remount the filesystem if it is mounted and the options have changed" do
+ expect(@vol).to receive(:add).with(:remote => @new_resource.device,
+ :username => @new_resource.username,
+ :domainname => @new_resource.domain,
+ :password => @new_resource.password)
+ @provider.mount_fs
+ end
+
+ it "should not mount the filesystem if it is mounted and the options have not changed" do
+ expect(@vol).to_not receive(:add)
+ allow(@current_resource).to receive(:mounted).and_return(true)
+ @provider.mount_fs
+ end
end
describe "when unmounting a file system" do
diff --git a/spec/unit/provider/mount_spec.rb b/spec/unit/provider/mount_spec.rb
index e9fe3fa050..cc2a456440 100644
--- a/spec/unit/provider/mount_spec.rb
+++ b/spec/unit/provider/mount_spec.rb
@@ -61,8 +61,19 @@ describe Chef::Provider::Mount do
expect(new_resource).to be_updated_by_last_action
end
- it "should not mount the filesystem if it is mounted" do
+ it "should remount the filesystem if it is mounted and the options have changed" do
allow(current_resource).to receive(:mounted).and_return(true)
+ allow(provider).to receive(:mount_options_unchanged?).and_return(false)
+ expect(provider).to receive(:umount_fs).and_return(true)
+ expect(provider).to receive(:wait_until_unmounted)
+ expect(provider).to receive(:mount_fs).and_return(true)
+ provider.run_action(:mount)
+ expect(new_resource).to be_updated_by_last_action
+ end
+
+ it "should not mount the filesystem if it is mounted and the options have not changed" do
+ allow(current_resource).to receive(:mounted).and_return(true)
+ expect(provider).to receive(:mount_options_unchanged?).and_return(true)
expect(provider).not_to receive(:mount_fs)
provider.run_action(:mount)
expect(new_resource).not_to be_updated_by_last_action
diff --git a/spec/unit/provider/package/aix_spec.rb b/spec/unit/provider/package/aix_spec.rb
index 5bc861b849..13992cb8d1 100644
--- a/spec/unit/provider/package/aix_spec.rb
+++ b/spec/unit/provider/package/aix_spec.rb
@@ -36,23 +36,27 @@ describe Chef::Provider::Package::Aix do
@bffinfo ="/usr/lib/objrepos:samba.base:3.3.12.0::COMMITTED:I:Samba for AIX:
/etc/objrepos:samba.base:3.3.12.0::COMMITTED:I:Samba for AIX:"
- @status = double("Status", :stdout => "", :exitstatus => 0)
+ @empty_status = double("Status", :stdout => "", :exitstatus => 0)
end
it "should create a current resource with the name of new_resource" do
- allow(@provider).to receive(:shell_out).and_return(@status)
+ status = double("Status", :stdout => @bffinfo, :exitstatus => 0)
+ expect(@provider).to receive(:shell_out).with("installp -L -d /tmp/samba.base", timeout: 900).and_return(status)
+ expect(@provider).to receive(:shell_out).with("lslpp -lcq samba.base", timeout: 900).and_return(@empty_status)
@provider.load_current_resource
expect(@provider.current_resource.name).to eq("samba.base")
end
it "should set the current resource bff package name to the new resource bff package name" do
- allow(@provider).to receive(:shell_out).and_return(@status)
+ status = double("Status", :stdout => @bffinfo, :exitstatus => 0)
+ expect(@provider).to receive(:shell_out).with("installp -L -d /tmp/samba.base", timeout: 900).and_return(status)
+ expect(@provider).to receive(:shell_out).with("lslpp -lcq samba.base", timeout: 900).and_return(@empty_status)
@provider.load_current_resource
expect(@provider.current_resource.package_name).to eq("samba.base")
end
it "should raise an exception if a source is supplied but not found" do
- allow(@provider).to receive(:shell_out).and_return(@status)
+ allow(@provider).to receive(:shell_out).and_return(@empty_status)
allow(::File).to receive(:exists?).and_return(false)
@provider.load_current_resource
@provider.define_resource_requirements
@@ -61,8 +65,8 @@ describe Chef::Provider::Package::Aix do
it "should get the source package version from lslpp if provided" do
status = double("Status", :stdout => @bffinfo, :exitstatus => 0)
- expect(@provider).to receive(:shell_out).with("installp -L -d /tmp/samba.base").and_return(status)
- expect(@provider).to receive(:shell_out).with("lslpp -lcq samba.base").and_return(@status)
+ expect(@provider).to receive(:shell_out).with("installp -L -d /tmp/samba.base", timeout: 900).and_return(status)
+ expect(@provider).to receive(:shell_out).with("lslpp -lcq samba.base", timeout: 900).and_return(@empty_status)
@provider.load_current_resource
expect(@provider.current_resource.package_name).to eq("samba.base")
@@ -73,8 +77,8 @@ describe Chef::Provider::Package::Aix do
status = double("Status", :stdout => @bffinfo, :exitstatus => 0)
@stdout = StringIO.new(@bffinfo)
@stdin, @stderr = StringIO.new, StringIO.new
- expect(@provider).to receive(:shell_out).with("installp -L -d /tmp/samba.base").and_return(@status)
- expect(@provider).to receive(:shell_out).with("lslpp -lcq samba.base").and_return(status)
+ expect(@provider).to receive(:shell_out).with("installp -L -d /tmp/samba.base", timeout: 900).and_return(status)
+ expect(@provider).to receive(:shell_out).with("lslpp -lcq samba.base", timeout: 900).and_return(status)
@provider.load_current_resource
expect(@provider.current_resource.version).to eq("3.3.12.0")
end
@@ -94,12 +98,20 @@ describe Chef::Provider::Package::Aix do
end
it "should return a current resource with a nil version if the package is not found" do
- status = double(:stdout => "", :exitstatus => 0)
- expect(@provider).to receive(:shell_out).with("installp -L -d /tmp/samba.base").and_return(status)
- expect(@provider).to receive(:shell_out).with("lslpp -lcq samba.base").and_return(status)
+ status = double("Status", :stdout => @bffinfo, :exitstatus => 0)
+ expect(@provider).to receive(:shell_out).with("installp -L -d /tmp/samba.base", timeout: 900).and_return(status)
+ expect(@provider).to receive(:shell_out).with("lslpp -lcq samba.base", timeout: 900).and_return(@empty_status)
@provider.load_current_resource
expect(@provider.current_resource.version).to be_nil
end
+
+ it "should raise an exception if the source doesn't provide the requested package" do
+ wrongbffinfo = "/usr/lib/objrepos:openssl.base:0.9.8.2400::COMMITTED:I:Open Secure Socket Layer:
+/etc/objrepos:openssl.base:0.9.8.2400::COMMITTED:I:Open Secure Socket Layer:"
+ status = double("Status", :stdout => wrongbffinfo, :exitstatus => 0)
+ expect(@provider).to receive(:shell_out).with("installp -L -d /tmp/samba.base", timeout: 900).and_return(status)
+ expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Package)
+ end
end
describe "candidate_version" do
@@ -125,7 +137,7 @@ describe Chef::Provider::Package::Aix do
describe "install and upgrade" do
it "should run installp -aYF -d with the package source to install" do
- expect(@provider).to receive(:shell_out!).with("installp -aYF -d /tmp/samba.base samba.base")
+ expect(@provider).to receive(:shell_out!).with("installp -aYF -d /tmp/samba.base samba.base", timeout: 900)
@provider.install_package("samba.base", "3.3.12.0")
end
@@ -133,26 +145,26 @@ describe Chef::Provider::Package::Aix do
@new_resource = Chef::Resource::Package.new("/tmp/samba.base")
@provider = Chef::Provider::Package::Aix.new(@new_resource, @run_context)
expect(@new_resource.source).to eq("/tmp/samba.base")
- expect(@provider).to receive(:shell_out!).with("installp -aYF -d /tmp/samba.base /tmp/samba.base")
+ expect(@provider).to receive(:shell_out!).with("installp -aYF -d /tmp/samba.base /tmp/samba.base", timeout: 900)
@provider.install_package("/tmp/samba.base", "3.3.12.0")
end
it "should run installp with -eLogfile option." do
allow(@new_resource).to receive(:options).and_return("-e/tmp/installp.log")
- expect(@provider).to receive(:shell_out!).with("installp -aYF -e/tmp/installp.log -d /tmp/samba.base samba.base")
+ expect(@provider).to receive(:shell_out!).with("installp -aYF -e/tmp/installp.log -d /tmp/samba.base samba.base", timeout: 900)
@provider.install_package("samba.base", "3.3.12.0")
end
end
describe "remove" do
it "should run installp -u samba.base to remove the package" do
- expect(@provider).to receive(:shell_out!).with("installp -u samba.base")
+ expect(@provider).to receive(:shell_out!).with("installp -u samba.base", timeout: 900)
@provider.remove_package("samba.base", "3.3.12.0")
end
it "should run installp -u -e/tmp/installp.log with options -e/tmp/installp.log" do
allow(@new_resource).to receive(:options).and_return("-e/tmp/installp.log")
- expect(@provider).to receive(:shell_out!).with("installp -u -e/tmp/installp.log samba.base")
+ expect(@provider).to receive(:shell_out!).with("installp -u -e/tmp/installp.log samba.base", timeout: 900)
@provider.remove_package("samba.base", "3.3.12.0")
end
diff --git a/spec/unit/provider/package/dpkg_spec.rb b/spec/unit/provider/package/dpkg_spec.rb
index 3fd86218d2..b868128147 100644
--- a/spec/unit/provider/package/dpkg_spec.rb
+++ b/spec/unit/provider/package/dpkg_spec.rb
@@ -51,10 +51,9 @@ describe Chef::Provider::Package::Dpkg do
describe 'gets the source package version from dpkg-deb' do
def check_version(version)
@status = double(:stdout => "wget\t#{version}", :exitstatus => 0)
- allow(@provider).to receive(:shell_out).with("dpkg-deb -W #{@new_resource.source}").and_return(@status)
+ allow(@provider).to receive(:shell_out).with("dpkg-deb -W #{@new_resource.source}", timeout: 900).and_return(@status)
@provider.load_current_resource
expect(@provider.current_resource.package_name).to eq("wget")
- expect(@new_resource.version).to eq(version)
expect(@provider.candidate_version).to eq(version)
end
@@ -83,6 +82,14 @@ describe Chef::Provider::Package::Dpkg do
expect(@provider.current_resource.package_name).to eq("f.o.o-pkg++2")
end
+ it "gets the source package version from dpkg-deb correctly when the package version has `~', `-', `+' or `.' characters" do
+ stdout = "b.a.r-pkg++1\t1.2.3+3141592-1ubuntu1~lucid"
+ status = double(:stdout => stdout, :exitstatus => 1)
+ allow(@provider).to receive(:shell_out).and_return(status)
+ @provider.load_current_resource
+ expect(@provider.candidate_version).to eq('1.2.3+3141592-1ubuntu1~lucid')
+ end
+
it "should raise an exception if the source is not set but we are installing" do
@new_resource = Chef::Resource::Package.new("wget")
@provider.new_resource = @new_resource
@@ -106,7 +113,7 @@ Depends: libc6 (>= 2.8~20080505), libssl0.9.8 (>= 0.9.8f-5)
Conflicts: wget-ssl
DPKG_S
status = double(:stdout => stdout, :exitstatus => 1)
- allow(@provider).to receive(:shell_out).with("dpkg -s wget").and_return(status)
+ allow(@provider).to receive(:shell_out).with("dpkg -s wget", timeout: 900).and_return(status)
@provider.load_current_resource
expect(@provider.current_resource.version).to eq("1.11.4-1ubuntu1")
diff --git a/spec/unit/provider/package/freebsd/pkg_spec.rb b/spec/unit/provider/package/freebsd/pkg_spec.rb
index f67161930f..d1f5a649bc 100644
--- a/spec/unit/provider/package/freebsd/pkg_spec.rb
+++ b/spec/unit/provider/package/freebsd/pkg_spec.rb
@@ -77,7 +77,7 @@ describe Chef::Provider::Package::Freebsd::Pkg, "load_current_resource" do
it "should return the version number when it is installed" do
pkg_info = OpenStruct.new(:stdout => "zsh-4.3.6_7")
- expect(@provider).to receive(:shell_out!).with('pkg_info -E "zsh*"', :env => nil, :returns => [0,1]).and_return(pkg_info)
+ expect(@provider).to receive(:shell_out!).with('pkg_info -E "zsh*"', env: nil, returns: [0,1], timeout: 900).and_return(pkg_info)
#@provider.should_receive(:popen4).with('pkg_info -E "zsh*"').and_yield(@pid, @stdin, ["zsh-4.3.6_7"], @stderr).and_return(@status)
allow(@provider).to receive(:package_name).and_return("zsh")
expect(@provider.current_installed_version).to eq("4.3.6_7")
@@ -85,14 +85,14 @@ describe Chef::Provider::Package::Freebsd::Pkg, "load_current_resource" do
it "does not set the current version number when the package is not installed" do
pkg_info = OpenStruct.new(:stdout => "")
- expect(@provider).to receive(:shell_out!).with('pkg_info -E "zsh*"', :env => nil, :returns => [0,1]).and_return(pkg_info)
+ expect(@provider).to receive(:shell_out!).with('pkg_info -E "zsh*"', env: nil, returns: [0,1], timeout: 900).and_return(pkg_info)
allow(@provider).to receive(:package_name).and_return("zsh")
expect(@provider.current_installed_version).to be_nil
end
it "should return the port path for a valid port name" do
whereis = OpenStruct.new(:stdout => "zsh: /usr/ports/shells/zsh")
- expect(@provider).to receive(:shell_out!).with("whereis -s zsh", :env => nil).and_return(whereis)
+ expect(@provider).to receive(:shell_out!).with("whereis -s zsh", env: nil, timeout: 900).and_return(whereis)
#@provider.should_receive(:popen4).with("whereis -s zsh").and_yield(@pid, @stdin, ["zsh: /usr/ports/shells/zsh"], @stderr).and_return(@status)
allow(@provider).to receive(:port_name).and_return("zsh")
expect(@provider.port_path).to eq("/usr/ports/shells/zsh")
@@ -102,7 +102,7 @@ describe Chef::Provider::Package::Freebsd::Pkg, "load_current_resource" do
it "should return the ports candidate version when given a valid port path" do
allow(@provider).to receive(:port_path).and_return("/usr/ports/shells/zsh")
make_v = OpenStruct.new(:stdout => "4.3.6\n", :exitstatus => 0)
- expect(@provider).to receive(:shell_out!).with("make -V PORTVERSION", {:cwd=>"/usr/ports/shells/zsh", :returns=>[0, 1], :env=>nil}).and_return(make_v)
+ expect(@provider).to receive(:shell_out!).with("make -V PORTVERSION", {cwd: "/usr/ports/shells/zsh", returns: [0, 1], env: nil, timeout: 900}).and_return(make_v)
expect(@provider.ports_candidate_version).to eq("4.3.6")
end
@@ -110,7 +110,7 @@ describe Chef::Provider::Package::Freebsd::Pkg, "load_current_resource" do
allow(::File).to receive(:exist?).with('/usr/ports/Makefile').and_return(true)
allow(@provider).to receive(:port_path).and_return("/usr/ports/shells/zsh")
make_v = OpenStruct.new(:stdout => "zsh-4.3.6_7\n", :exitstatus => 0)
- expect(@provider).to receive(:shell_out!).with("make -V PKGNAME", {:cwd=>"/usr/ports/shells/zsh", :env=>nil, :returns=>[0, 1]}).and_return(make_v)
+ expect(@provider).to receive(:shell_out!).with("make -V PKGNAME", {cwd: "/usr/ports/shells/zsh", env: nil, returns: [0, 1], timeout: 900}).and_return(make_v)
#@provider.should_receive(:ports_makefile_variable_value).with("PKGNAME").and_return("zsh-4.3.6_7")
expect(@provider.package_name).to eq("zsh")
end
@@ -127,7 +127,7 @@ describe Chef::Provider::Package::Freebsd::Pkg, "load_current_resource" do
end
it "should run pkg_add -r with the package name" do
- expect(@provider).to receive(:shell_out!).with("pkg_add -r zsh", :env => nil).and_return(@cmd_result)
+ expect(@provider).to receive(:shell_out!).with("pkg_add -r zsh", env: nil, timeout: 900).and_return(@cmd_result)
@provider.install_package("zsh", "4.3.6_7")
end
end
@@ -142,7 +142,7 @@ describe Chef::Provider::Package::Freebsd::Pkg, "load_current_resource" do
it "should figure out the port path from the package_name using whereis" do
whereis = OpenStruct.new(:stdout => "zsh: /usr/ports/shells/zsh")
- expect(@provider).to receive(:shell_out!).with("whereis -s zsh", :env=>nil).and_return(whereis)
+ expect(@provider).to receive(:shell_out!).with("whereis -s zsh", env: nil, timeout: 900).and_return(whereis)
expect(@provider.port_path).to eq("/usr/ports/shells/zsh")
end
@@ -178,7 +178,7 @@ describe Chef::Provider::Package::Freebsd::Pkg, "load_current_resource" do
end
it "should run pkg_add -r with the package name" do
- expect(@provider).to receive(:shell_out!).with("pkg_add -r ruby18-iconv", :env => nil).and_return(@install_result)
+ expect(@provider).to receive(:shell_out!).with("pkg_add -r ruby18-iconv", env: nil, timeout: 900).and_return(@install_result)
@provider.install_package("ruby-iconv", "1.0")
end
end
@@ -193,7 +193,7 @@ describe Chef::Provider::Package::Freebsd::Pkg, "load_current_resource" do
end
it "should run pkg_delete with the package name and version" do
- expect(@provider).to receive(:shell_out!).with("pkg_delete zsh-4.3.6_7", :env => nil).and_return(@pkg_delete)
+ expect(@provider).to receive(:shell_out!).with("pkg_delete zsh-4.3.6_7", env: nil, timeout: 900).and_return(@pkg_delete)
@provider.remove_package("zsh", "4.3.6_7")
end
end
@@ -213,14 +213,14 @@ describe Chef::Provider::Package::Freebsd::Pkg, "load_current_resource" do
it "should return the port path for a valid port name" do
whereis = OpenStruct.new(:stdout => "bonnie++: /usr/ports/benchmarks/bonnie++")
- expect(@provider).to receive(:shell_out!).with("whereis -s bonnie++", :env => nil).and_return(whereis)
+ expect(@provider).to receive(:shell_out!).with("whereis -s bonnie++", env: nil, timeout: 900).and_return(whereis)
allow(@provider).to receive(:port_name).and_return("bonnie++")
expect(@provider.port_path).to eq("/usr/ports/benchmarks/bonnie++")
end
it "should return the version number when it is installed" do
pkg_info = OpenStruct.new(:stdout => "bonnie++-1.96")
- expect(@provider).to receive(:shell_out!).with('pkg_info -E "bonnie++*"', :env => nil, :returns => [0,1]).and_return(pkg_info)
+ expect(@provider).to receive(:shell_out!).with('pkg_info -E "bonnie++*"', env: nil, returns: [0,1], timeout: 900).and_return(pkg_info)
allow(@provider).to receive(:package_name).and_return("bonnie++")
expect(@provider.current_installed_version).to eq("1.96")
end
@@ -253,7 +253,7 @@ describe Chef::Provider::Package::Freebsd::Pkg, "load_current_resource" do
allow(@provider).to receive(:latest_link_name).and_return("perl")
cmd = OpenStruct.new(:status => true)
- expect(@provider).to receive(:shell_out!).with("pkg_add -r perl", :env => nil).and_return(cmd)
+ expect(@provider).to receive(:shell_out!).with("pkg_add -r perl", env: nil, timeout: 900).and_return(cmd)
@provider.install_package("perl5.8", "5.8.8_1")
end
@@ -267,7 +267,7 @@ describe Chef::Provider::Package::Freebsd::Pkg, "load_current_resource" do
allow(@provider).to receive(:latest_link_name).and_return("mysql50-server")
cmd = OpenStruct.new(:status => true)
- expect(@provider).to receive(:shell_out!).with("pkg_add -r mysql50-server", :env=>nil).and_return(cmd)
+ expect(@provider).to receive(:shell_out!).with("pkg_add -r mysql50-server", env: nil, timeout: 900).and_return(cmd)
@provider.install_package("mysql50-server", "5.0.45_1")
end
end
diff --git a/spec/unit/provider/package/freebsd/pkgng_spec.rb b/spec/unit/provider/package/freebsd/pkgng_spec.rb
index 0c1e89c7ab..59215f855b 100644
--- a/spec/unit/provider/package/freebsd/pkgng_spec.rb
+++ b/spec/unit/provider/package/freebsd/pkgng_spec.rb
@@ -71,7 +71,7 @@ describe Chef::Provider::Package::Freebsd::Port do
end
it "should query pkg database" do
- expect(@provider).to receive(:shell_out!).with('pkg info "zsh"', :env => nil, :returns => [0,70]).and_return(@pkg_info)
+ expect(@provider).to receive(:shell_out!).with('pkg info "zsh"', env: nil, returns: [0,70], timeout: 900).and_return(@pkg_info)
expect(@provider.current_installed_version).to eq("3.1.7")
end
end
@@ -80,14 +80,14 @@ describe Chef::Provider::Package::Freebsd::Port do
describe "determining candidate version" do
it "should query repository" do
pkg_query = OpenStruct.new(:stdout => "5.0.5\n", :exitstatus => 0)
- expect(@provider).to receive(:shell_out!).with("pkg rquery '%v' zsh", :env => nil).and_return(pkg_query)
+ expect(@provider).to receive(:shell_out!).with("pkg rquery '%v' zsh", env: nil, timeout: 900).and_return(pkg_query)
expect(@provider.candidate_version).to eq("5.0.5")
end
it "should query specified repository when given option" do
@provider.new_resource.options('-r LocalMirror') # This requires LocalMirror repo configuration.
pkg_query = OpenStruct.new(:stdout => "5.0.3\n", :exitstatus => 0)
- expect(@provider).to receive(:shell_out!).with("pkg rquery -r LocalMirror '%v' zsh", :env => nil).and_return(pkg_query)
+ expect(@provider).to receive(:shell_out!).with("pkg rquery -r LocalMirror '%v' zsh", env: nil, timeout: 900).and_return(pkg_query)
expect(@provider.candidate_version).to eq("5.0.3")
end
@@ -106,7 +106,7 @@ describe Chef::Provider::Package::Freebsd::Port do
it "should handle package source from file" do
@provider.new_resource.source("/nas/pkg/repo/zsh-5.0.1.txz")
expect(@provider).to receive(:shell_out!).
- with("pkg add /nas/pkg/repo/zsh-5.0.1.txz", :env => { 'LC_ALL' => nil }).
+ with("pkg add /nas/pkg/repo/zsh-5.0.1.txz", env: { 'LC_ALL' => nil }, timeout: 900).
and_return(@install_result)
@provider.install_package("zsh", "5.0.1")
end
@@ -114,21 +114,21 @@ describe Chef::Provider::Package::Freebsd::Port do
it "should handle package source over ftp or http" do
@provider.new_resource.source("http://repo.example.com/zsh-5.0.1.txz")
expect(@provider).to receive(:shell_out!).
- with("pkg add http://repo.example.com/zsh-5.0.1.txz", :env => { 'LC_ALL' => nil }).
+ with("pkg add http://repo.example.com/zsh-5.0.1.txz", env: { 'LC_ALL' => nil }, timeout: 900).
and_return(@install_result)
@provider.install_package("zsh", "5.0.1")
end
it "should handle a package name" do
expect(@provider).to receive(:shell_out!).
- with("pkg install -y zsh", :env => { 'LC_ALL' => nil }).and_return(@install_result)
+ with("pkg install -y zsh", env: { 'LC_ALL' => nil }, timeout: 900).and_return(@install_result)
@provider.install_package("zsh", "5.0.1")
end
it "should handle a package name with a specified repo" do
@provider.new_resource.options('-r LocalMirror') # This requires LocalMirror repo configuration.
expect(@provider).to receive(:shell_out!).
- with("pkg install -y -r LocalMirror zsh", :env => { 'LC_ALL' => nil }).and_return(@install_result)
+ with("pkg install -y -r LocalMirror zsh", env: { 'LC_ALL' => nil }, timeout: 900).and_return(@install_result)
@provider.install_package("zsh", "5.0.1")
end
end
@@ -141,14 +141,14 @@ describe Chef::Provider::Package::Freebsd::Port do
it "should call pkg delete" do
expect(@provider).to receive(:shell_out!).
- with("pkg delete -y zsh-5.0.1", :env => nil).and_return(@install_result)
+ with("pkg delete -y zsh-5.0.1", env: nil, timeout: 900).and_return(@install_result)
@provider.remove_package("zsh", "5.0.1")
end
it "should not include repo option in pkg delete" do
@provider.new_resource.options('-r LocalMirror') # This requires LocalMirror repo configuration.
expect(@provider).to receive(:shell_out!).
- with("pkg delete -y zsh-5.0.1", :env => nil).and_return(@install_result)
+ with("pkg delete -y zsh-5.0.1", env: nil, timeout: 900).and_return(@install_result)
@provider.remove_package("zsh", "5.0.1")
end
end
diff --git a/spec/unit/provider/package/freebsd/port_spec.rb b/spec/unit/provider/package/freebsd/port_spec.rb
index 2e32e88f97..4b23575740 100644
--- a/spec/unit/provider/package/freebsd/port_spec.rb
+++ b/spec/unit/provider/package/freebsd/port_spec.rb
@@ -72,7 +72,7 @@ describe Chef::Provider::Package::Freebsd::Port do
it "should check 'pkg_info' if system uses pkg_* tools" do
allow(@new_resource).to receive(:supports_pkgng?)
expect(@new_resource).to receive(:supports_pkgng?).and_return(false)
- expect(@provider).to receive(:shell_out!).with('pkg_info -E "zsh*"', :env => nil, :returns => [0,1]).and_return(@pkg_info)
+ expect(@provider).to receive(:shell_out!).with('pkg_info -E "zsh*"', env: nil, returns: [0,1], timeout: 900).and_return(@pkg_info)
expect(@provider.current_installed_version).to eq("3.1.7")
end
@@ -80,8 +80,8 @@ describe Chef::Provider::Package::Freebsd::Port do
pkg_enabled = OpenStruct.new(:stdout => "yes\n")
[1000016, 1000000, 901503, 902506, 802511].each do |__freebsd_version|
@node.automatic_attrs[:os_version] = __freebsd_version
- expect(@new_resource).to receive(:shell_out!).with('make -V WITH_PKGNG', :env => nil).and_return(pkg_enabled)
- expect(@provider).to receive(:shell_out!).with('pkg info "zsh"', :env => nil, :returns => [0,70]).and_return(@pkg_info)
+ expect(@new_resource).to receive(:shell_out!).with('make -V WITH_PKGNG', env: nil).and_return(pkg_enabled)
+ expect(@provider).to receive(:shell_out!).with('pkg info "zsh"', env: nil, returns: [0,70], timeout: 900).and_return(@pkg_info)
expect(@provider.current_installed_version).to eq("3.1.7")
end
end
@@ -89,7 +89,7 @@ describe Chef::Provider::Package::Freebsd::Port do
it "should check 'pkg info' if the freebsd version is greater than or equal to 1000017" do
__freebsd_version = 1000017
@node.automatic_attrs[:os_version] = __freebsd_version
- expect(@provider).to receive(:shell_out!).with('pkg info "zsh"', :env => nil, :returns => [0,70]).and_return(@pkg_info)
+ expect(@provider).to receive(:shell_out!).with('pkg info "zsh"', env: nil, returns: [0,70], timeout: 900).and_return(@pkg_info)
expect(@provider.current_installed_version).to eq("3.1.7")
end
end
@@ -102,7 +102,7 @@ describe Chef::Provider::Package::Freebsd::Port do
it "should return candidate version if port exists" do
allow(::File).to receive(:exist?).with('/usr/ports/Makefile').and_return(true)
allow(@provider).to receive(:port_dir).and_return('/usr/ports/shells/zsh')
- expect(@provider).to receive(:shell_out!).with("make -V PORTVERSION", :cwd => "/usr/ports/shells/zsh", :env => nil, :returns => [0,1]).
+ expect(@provider).to receive(:shell_out!).with("make -V PORTVERSION", cwd: "/usr/ports/shells/zsh", env: nil, returns: [0,1], timeout: 900).
and_return(@port_version)
expect(@provider.candidate_version).to eq("5.0.5")
end
@@ -127,13 +127,13 @@ describe Chef::Provider::Package::Freebsd::Port do
it "should query system for path given just a name" do
whereis = OpenStruct.new(:stdout => "zsh: /usr/ports/shells/zsh\n")
- expect(@provider).to receive(:shell_out!).with("whereis -s zsh", :env => nil).and_return(whereis)
+ expect(@provider).to receive(:shell_out!).with("whereis -s zsh", env: nil, timeout: 900).and_return(whereis)
expect(@provider.port_dir).to eq("/usr/ports/shells/zsh")
end
it "should raise exception if not found" do
whereis = OpenStruct.new(:stdout => "zsh:\n")
- expect(@provider).to receive(:shell_out!).with("whereis -s zsh", :env => nil).and_return(whereis)
+ expect(@provider).to receive(:shell_out!).with("whereis -s zsh", env: nil, timeout: 900).and_return(whereis)
expect { @provider.port_dir }.to raise_error(Chef::Exceptions::Package, "Could not find port with the name zsh")
end
end
diff --git a/spec/unit/provider/package/ips_spec.rb b/spec/unit/provider/package/ips_spec.rb
index 342ac4c040..ad69dffb10 100644
--- a/spec/unit/provider/package/ips_spec.rb
+++ b/spec/unit/provider/package/ips_spec.rb
@@ -65,28 +65,28 @@ PKG_STATUS
context "when loading current resource" do
it "should create a current resource with the name of the new_resource" do
- expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}").and_return(local_output)
- expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}").and_return(remote_output)
+ expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}", timeout: 900).and_return(local_output)
+ expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}", timeout: 900).and_return(remote_output)
expect(Chef::Resource::Package).to receive(:new).and_return(@current_resource)
@provider.load_current_resource
end
it "should set the current resources package name to the new resources package name" do
- expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}").and_return(local_output)
- expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}").and_return(remote_output)
+ expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}", timeout: 900).and_return(local_output)
+ expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}", timeout: 900).and_return(remote_output)
@provider.load_current_resource
expect(@current_resource.package_name).to eq(@new_resource.package_name)
end
it "should run pkg info with the package name" do
- expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}").and_return(local_output)
- expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}").and_return(remote_output)
+ expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}", timeout: 900).and_return(local_output)
+ expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}", timeout: 900).and_return(remote_output)
@provider.load_current_resource
end
it "should set the installed version to nil on the current resource if package state is not installed" do
- expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}").and_return(local_output)
- expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}").and_return(remote_output)
+ expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}", timeout: 900).and_return(local_output)
+ expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}", timeout: 900).and_return(remote_output)
@provider.load_current_resource
expect(@current_resource.version).to be_nil
end
@@ -108,27 +108,27 @@ Packaging Date: October 19, 2011 09:14:50 AM
Size: 8.07 MB
FMRI: pkg://solaris/crypto/gnupg@2.0.17,5.11-0.175.0.0.0.2.537:20111019T091450Z
INSTALLED
- expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}").and_return(local)
- expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}").and_return(remote_output)
+ expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}", timeout: 900).and_return(local)
+ expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}", timeout: 900).and_return(remote_output)
@provider.load_current_resource
expect(@current_resource.version).to eq("2.0.17")
end
it "should return the current resource" do
- expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}").and_return(local_output)
- expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}").and_return(remote_output)
+ expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}", timeout: 900).and_return(local_output)
+ expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}", timeout: 900).and_return(remote_output)
expect(@provider.load_current_resource).to eql(@current_resource)
end
end
context "when installing a package" do
it "should run pkg install with the package name and version" do
- expect(@provider).to receive(:shell_out).with("pkg install -q crypto/gnupg@2.0.17")
+ expect(@provider).to receive(:shell_out).with("pkg install -q crypto/gnupg@2.0.17", timeout: 900)
@provider.install_package("crypto/gnupg", "2.0.17")
end
it "should run pkg install with the package name and version and options if specified" do
- expect(@provider).to receive(:shell_out).with("pkg --no-refresh install -q crypto/gnupg@2.0.17")
+ expect(@provider).to receive(:shell_out).with("pkg --no-refresh install -q crypto/gnupg@2.0.17", timeout: 900)
allow(@new_resource).to receive(:options).and_return("--no-refresh")
@provider.install_package("crypto/gnupg", "2.0.17")
end
@@ -147,8 +147,8 @@ Packaging Date: April 1, 2012 05:55:52 PM
Size: 2.57 MB
FMRI: pkg://omnios/security/sudo@1.8.4.1,5.11-0.151002:20120401T175552Z
PKG_STATUS
- expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}").and_return(local_output)
- expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}").and_return(remote)
+ expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}", timeout: 900).and_return(local_output)
+ expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}", timeout: 900).and_return(remote)
@provider.load_current_resource
expect(@current_resource.version).to be_nil
expect(@provider.candidate_version).to eql("1.8.4.1")
@@ -188,8 +188,8 @@ Packaging Date: October 19, 2011 09:14:50 AM
FMRI: pkg://solaris/crypto/gnupg@2.0.18,5.11-0.175.0.0.0.2.537:20111019T091450Z
REMOTE
- expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}").and_return(local)
- expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}").and_return(remote)
+ expect(@provider).to receive(:shell_out).with("pkg info #{@new_resource.package_name}", timeout: 900).and_return(local)
+ expect(@provider).to receive(:shell_out!).with("pkg info -r #{@new_resource.package_name}", timeout: 900).and_return(remote)
expect(@provider).to receive(:install_package).exactly(0).times
@provider.run_action(:install)
end
@@ -200,7 +200,7 @@ REMOTE
end
it "should run pkg install with the --accept flag" do
- expect(@provider).to receive(:shell_out).with("pkg install -q --accept crypto/gnupg@2.0.17")
+ expect(@provider).to receive(:shell_out).with("pkg install -q --accept crypto/gnupg@2.0.17", timeout: 900)
@provider.install_package("crypto/gnupg", "2.0.17")
end
end
@@ -208,19 +208,19 @@ REMOTE
context "when upgrading a package" do
it "should run pkg install with the package name and version" do
- expect(@provider).to receive(:shell_out).with("pkg install -q crypto/gnupg@2.0.17")
+ expect(@provider).to receive(:shell_out).with("pkg install -q crypto/gnupg@2.0.17", timeout: 900)
@provider.upgrade_package("crypto/gnupg", "2.0.17")
end
end
context "when uninstalling a package" do
it "should run pkg uninstall with the package name and version" do
- expect(@provider).to receive(:shell_out!).with("pkg uninstall -q crypto/gnupg@2.0.17")
+ expect(@provider).to receive(:shell_out!).with("pkg uninstall -q crypto/gnupg@2.0.17", timeout: 900)
@provider.remove_package("crypto/gnupg", "2.0.17")
end
it "should run pkg uninstall with the package name and version and options if specified" do
- expect(@provider).to receive(:shell_out!).with("pkg --no-refresh uninstall -q crypto/gnupg@2.0.17")
+ expect(@provider).to receive(:shell_out!).with("pkg --no-refresh uninstall -q crypto/gnupg@2.0.17", timeout: 900)
allow(@new_resource).to receive(:options).and_return("--no-refresh")
@provider.remove_package("crypto/gnupg", "2.0.17")
end
diff --git a/spec/unit/provider/package/macports_spec.rb b/spec/unit/provider/package/macports_spec.rb
index 9822fb3928..eef84113b4 100644
--- a/spec/unit/provider/package/macports_spec.rb
+++ b/spec/unit/provider/package/macports_spec.rb
@@ -105,7 +105,7 @@ EOF
it "should run the port install command with the correct version" do
expect(@current_resource).to receive(:version).and_return("4.1.6")
@provider.current_resource = @current_resource
- expect(@provider).to receive(:shell_out!).with("port install zsh @4.2.7")
+ expect(@provider).to receive(:shell_out!).with("port install zsh @4.2.7", timeout: 900)
@provider.install_package("zsh", "4.2.7")
end
@@ -122,7 +122,7 @@ EOF
expect(@current_resource).to receive(:version).and_return("4.1.6")
@provider.current_resource = @current_resource
allow(@new_resource).to receive(:options).and_return("-f")
- expect(@provider).to receive(:shell_out!).with("port -f install zsh @4.2.7")
+ expect(@provider).to receive(:shell_out!).with("port -f install zsh @4.2.7", timeout: 900)
@provider.install_package("zsh", "4.2.7")
end
@@ -130,36 +130,36 @@ EOF
describe "purge_package" do
it "should run the port uninstall command with the correct version" do
- expect(@provider).to receive(:shell_out!).with("port uninstall zsh @4.2.7")
+ expect(@provider).to receive(:shell_out!).with("port uninstall zsh @4.2.7", timeout: 900)
@provider.purge_package("zsh", "4.2.7")
end
it "should purge the currently active version if no explicit version is passed in" do
- expect(@provider).to receive(:shell_out!).with("port uninstall zsh")
+ expect(@provider).to receive(:shell_out!).with("port uninstall zsh", timeout: 900)
@provider.purge_package("zsh", nil)
end
it "should add options to the port command when specified" do
allow(@new_resource).to receive(:options).and_return("-f")
- expect(@provider).to receive(:shell_out!).with("port -f uninstall zsh @4.2.7")
+ expect(@provider).to receive(:shell_out!).with("port -f uninstall zsh @4.2.7", timeout: 900)
@provider.purge_package("zsh", "4.2.7")
end
end
describe "remove_package" do
it "should run the port deactivate command with the correct version" do
- expect(@provider).to receive(:shell_out!).with("port deactivate zsh @4.2.7")
+ expect(@provider).to receive(:shell_out!).with("port deactivate zsh @4.2.7", timeout: 900)
@provider.remove_package("zsh", "4.2.7")
end
it "should remove the currently active version if no explicit version is passed in" do
- expect(@provider).to receive(:shell_out!).with("port deactivate zsh")
+ expect(@provider).to receive(:shell_out!).with("port deactivate zsh", timeout: 900)
@provider.remove_package("zsh", nil)
end
it "should add options to the port command when specified" do
allow(@new_resource).to receive(:options).and_return("-f")
- expect(@provider).to receive(:shell_out!).with("port -f deactivate zsh @4.2.7")
+ expect(@provider).to receive(:shell_out!).with("port -f deactivate zsh @4.2.7", timeout: 900)
@provider.remove_package("zsh", "4.2.7")
end
end
@@ -169,7 +169,7 @@ EOF
expect(@current_resource).to receive(:version).at_least(:once).and_return("4.1.6")
@provider.current_resource = @current_resource
- expect(@provider).to receive(:shell_out!).with("port upgrade zsh @4.2.7")
+ expect(@provider).to receive(:shell_out!).with("port upgrade zsh @4.2.7", timeout: 900)
@provider.upgrade_package("zsh", "4.2.7")
end
@@ -195,7 +195,7 @@ EOF
expect(@current_resource).to receive(:version).at_least(:once).and_return("4.1.6")
@provider.current_resource = @current_resource
- expect(@provider).to receive(:shell_out!).with("port -f upgrade zsh @4.2.7")
+ expect(@provider).to receive(:shell_out!).with("port -f upgrade zsh @4.2.7", timeout: 900)
@provider.upgrade_package("zsh", "4.2.7")
end
diff --git a/spec/unit/provider/package/openbsd_spec.rb b/spec/unit/provider/package/openbsd_spec.rb
index ee9c9e89fb..8407f83785 100644
--- a/spec/unit/provider/package/openbsd_spec.rb
+++ b/spec/unit/provider/package/openbsd_spec.rb
@@ -21,28 +21,95 @@ require 'ostruct'
describe Chef::Provider::Package::Openbsd do
+ let(:node) do
+ node = Chef::Node.new
+ node.default['kernel'] = {'name' => 'OpenBSD', 'release' => '5.5', 'machine' => 'amd64'}
+ node
+ end
+
+ let (:provider) do
+ events = Chef::EventDispatch::Dispatcher.new
+ run_context = Chef::RunContext.new(node, {}, events)
+ Chef::Provider::Package::Openbsd.new(new_resource, run_context)
+ end
+
+ let(:new_resource) { Chef::Resource::Package.new(name)}
+
before(:each) do
- @node = Chef::Node.new
- @node.default['kernel'] = {'name' => 'OpenBSD', 'release' => '5.5', 'machine' => 'amd64'}
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
ENV['PKG_PATH'] = nil
end
describe "install a package" do
- before do
- @name = 'ihavetoes'
- @new_resource = Chef::Resource::Package.new(@name)
- @current_resource = Chef::Resource::Package.new(@name)
- @provider = Chef::Provider::Package::Openbsd.new(@new_resource, @run_context)
- @provider.current_resource = @current_resource
- end
- it "should run the installation command" do
- expect(@provider).to receive(:shell_out!).with(
- "pkg_add -r #{@name}",
- {:env => {"PKG_PATH" => "http://ftp.OpenBSD.org/pub/OpenBSD/5.5/packages/amd64/"}}
- ) {OpenStruct.new :status => true}
- @provider.install_package(@name, nil)
+ let(:name) { 'ihavetoes' }
+ let(:version) {'0.0'}
+
+ context 'when not already installed' do
+ before do
+ allow(provider).to receive(:shell_out!).with("pkg_info -e \"#{name}->0\"", anything()).and_return(instance_double('shellout', :stdout => ''))
+ end
+
+ context 'when there is a single candidate' do
+
+ context 'when source is not provided' do
+ it 'should run the installation command' do
+ expect(provider).to receive(:shell_out!).with("pkg_info -I \"#{name}\"", anything()).and_return(
+ instance_double('shellout', :stdout => "#{name}-#{version}\n"))
+ expect(provider).to receive(:shell_out!).with(
+ "pkg_add -r #{name}-#{version}",
+ {:env => {"PKG_PATH" => "http://ftp.OpenBSD.org/pub/OpenBSD/5.5/packages/amd64/"}, timeout: 900}
+ ) {OpenStruct.new :status => true}
+ provider.run_action(:install)
+ end
+ end
+ end
+
+ context 'when there are multiple candidates' do
+ let(:flavor_a) { 'flavora' }
+ let(:flavor_b) { 'flavorb' }
+
+ context 'if no version is specified' do
+ it 'should raise an exception' do
+ expect(provider).to receive(:shell_out!).with("pkg_info -I \"#{name}\"", anything()).and_return(
+ instance_double('shellout', :stdout => "#{name}-#{version}-#{flavor_a}\n#{name}-#{version}-#{flavor_b}\n"))
+ expect { provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package, /multiple matching candidates/)
+ end
+ end
+
+ context 'if a flavor is specified' do
+
+ let(:flavor) { 'flavora' }
+ let(:package_name) {'ihavetoes' }
+ let(:name) { "#{package_name}--#{flavor}" }
+
+ context 'if no version is specified' do
+ it 'should run the installation command' do
+ expect(provider).to receive(:shell_out!).with("pkg_info -e \"#{package_name}->0\"", anything()).and_return(instance_double('shellout', :stdout => ''))
+ expect(provider).to receive(:shell_out!).with("pkg_info -I \"#{name}\"", anything()).and_return(
+ instance_double('shellout', :stdout => "#{name}-#{version}-#{flavor}\n"))
+ expect(provider).to receive(:shell_out!).with(
+ "pkg_add -r #{name}-#{version}-#{flavor}",
+ {env: {"PKG_PATH" => "http://ftp.OpenBSD.org/pub/OpenBSD/5.5/packages/amd64/"}, timeout: 900}
+ ) {OpenStruct.new :status => true}
+ provider.run_action(:install)
+ end
+ end
+
+ end
+
+ context 'if a version is specified' do
+ it 'should use the flavor from the version' do
+ expect(provider).to receive(:shell_out!).with("pkg_info -I \"#{name}-#{version}-#{flavor_b}\"", anything()).and_return(
+ instance_double('shellout', :stdout => "#{name}-#{version}-#{flavor_a}\n"))
+
+ new_resource.version("#{version}-#{flavor_b}")
+ expect(provider).to receive(:shell_out!).with(
+ "pkg_add -r #{name}-#{version}-#{flavor_b}",
+ {env: {"PKG_PATH" => "http://ftp.OpenBSD.org/pub/OpenBSD/5.5/packages/amd64/"}, timeout: 900}
+ ) {OpenStruct.new :status => true}
+ provider.run_action(:install)
+ end
+ end
+ end
end
end
@@ -56,11 +123,10 @@ describe Chef::Provider::Package::Openbsd do
end
it "should run the command to delete the installed package" do
expect(@provider).to receive(:shell_out!).with(
- "pkg_delete #{@name}", :env=>nil
+ "pkg_delete #{@name}", env: nil, timeout: 900
) {OpenStruct.new :status => true}
@provider.remove_package(@name, nil)
end
end
end
-
diff --git a/spec/unit/provider/package/pacman_spec.rb b/spec/unit/provider/package/pacman_spec.rb
index 3b8848c41b..fcb9f8a86c 100644
--- a/spec/unit/provider/package/pacman_spec.rb
+++ b/spec/unit/provider/package/pacman_spec.rb
@@ -51,7 +51,7 @@ ERR
end
it "should run pacman query with the package name" do
- expect(@provider).to receive(:shell_out).with("pacman -Qi #{@new_resource.package_name}").and_return(@status)
+ expect(@provider).to receive(:shell_out).with("pacman -Qi #{@new_resource.package_name}", {timeout: 900}).and_return(@status)
@provider.load_current_resource
end
@@ -152,12 +152,12 @@ PACMAN_CONF
describe Chef::Provider::Package::Pacman, "install_package" do
it "should run pacman install with the package name and version" do
- expect(@provider).to receive(:shell_out!).with("pacman --sync --noconfirm --noprogressbar nano")
+ expect(@provider).to receive(:shell_out!).with("pacman --sync --noconfirm --noprogressbar nano", {timeout: 900})
@provider.install_package("nano", "1.0")
end
it "should run pacman install with the package name and version and options if specified" do
- expect(@provider).to receive(:shell_out!).with("pacman --sync --noconfirm --noprogressbar --debug nano")
+ expect(@provider).to receive(:shell_out!).with("pacman --sync --noconfirm --noprogressbar --debug nano", {timeout: 900})
allow(@new_resource).to receive(:options).and_return("--debug")
@provider.install_package("nano", "1.0")
@@ -173,12 +173,12 @@ PACMAN_CONF
describe Chef::Provider::Package::Pacman, "remove_package" do
it "should run pacman remove with the package name" do
- expect(@provider).to receive(:shell_out!).with("pacman --remove --noconfirm --noprogressbar nano")
+ expect(@provider).to receive(:shell_out!).with("pacman --remove --noconfirm --noprogressbar nano", {timeout: 900})
@provider.remove_package("nano", "1.0")
end
it "should run pacman remove with the package name and options if specified" do
- expect(@provider).to receive(:shell_out!).with("pacman --remove --noconfirm --noprogressbar --debug nano")
+ expect(@provider).to receive(:shell_out!).with("pacman --remove --noconfirm --noprogressbar --debug nano", {timeout: 900})
allow(@new_resource).to receive(:options).and_return("--debug")
@provider.remove_package("nano", "1.0")
diff --git a/spec/unit/provider/package/rpm_spec.rb b/spec/unit/provider/package/rpm_spec.rb
index 411afd3755..e0e45d0b4f 100644
--- a/spec/unit/provider/package/rpm_spec.rb
+++ b/spec/unit/provider/package/rpm_spec.rb
@@ -23,183 +23,394 @@ describe Chef::Provider::Package::Rpm do
let(:node) { Chef::Node.new }
let(:events) { Chef::EventDispatch::Dispatcher.new }
let(:run_context) { Chef::RunContext.new(node, {}, events) }
+
+ let(:package_source) { "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm" }
+
+ let(:package_name) { "ImageMagick-c++" }
+
let(:new_resource) do
- Chef::Resource::Package.new("ImageMagick-c++").tap do |resource|
- resource.source "/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm"
+ Chef::Resource::Package.new(package_name).tap do |resource|
+ resource.source(package_source)
end
end
- let(:exitstatus) { 0 }
- let(:stdout) { String.new('') }
- let(:status) { double('Process::Status', exitstatus: exitstatus, stdout: stdout) }
+
+ # `rpm -qp [stuff] $source`
+ let(:rpm_qp_status) { instance_double('Mixlib::ShellOut', exitstatus: rpm_qp_exitstatus, stdout: rpm_qp_stdout) }
+
+ # `rpm -q [stuff] $package_name`
+ let(:rpm_q_status) { instance_double('Mixlib::ShellOut', exitstatus: rpm_q_exitstatus, stdout: rpm_q_stdout) }
before(:each) do
- allow(::File).to receive(:exists?).and_return(true)
- allow(provider).to receive(:shell_out!).and_return(status)
+ allow(::File).to receive(:exists?).with("PLEASE STUB File.exists? EXACTLY").and_return(true)
+
+ # Ensure all shell out usage is stubbed with exact arguments
+ allow(provider).to receive(:shell_out!).with("PLEASE STUB YOUR SHELLOUT CALLS").and_return(nil)
+ allow(provider).to receive(:shell_out).with("PLEASE STUB YOUR SHELLOUT CALLS").and_return(nil)
end
- describe "when determining the current state of the package" do
- it "should create a current resource with the name of new_resource" do
- provider.load_current_resource
- expect(provider.current_resource.name).to eq("ImageMagick-c++")
- end
+ describe "when the package source is not valid" do
- it "should set the current reource package name to the new resource package name" do
- provider.load_current_resource
- expect(provider.current_resource.package_name).to eq('ImageMagick-c++')
- end
+ context "when source is not defiend" do
+ let(:new_resource) { Chef::Resource::Package.new("ImageMagick-c++") }
- it "should raise an exception if a source is supplied but not found" do
- allow(::File).to receive(:exists?).and_return(false)
- expect { provider.run_action(:any) }.to raise_error(Chef::Exceptions::Package)
+ it "should raise an exception when attempting any action" do
+ expect { provider.run_action(:any) }.to raise_error(Chef::Exceptions::Package)
+ end
end
- context "installation exists" do
- let(:stdout) { "ImageMagick-c++ 6.5.4.7-7.el6_5" }
+ context "when the source is a file that doesn't exist" do
- it "should get the source package version from rpm if provided" do
- expect(provider).to receive(:shell_out!).with("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm").and_return(status)
- expect(provider).to receive(:shell_out).with("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' ImageMagick-c++").and_return(status)
- provider.load_current_resource
- expect(provider.current_resource.package_name).to eq("ImageMagick-c++")
- expect(provider.new_resource.version).to eq("6.5.4.7-7.el6_5")
+ it "should raise an exception when attempting any action" do
+ allow(::File).to receive(:exists?).with(package_source).and_return(false)
+ expect { provider.run_action(:any) }.to raise_error(Chef::Exceptions::Package)
end
+ end
- it "should return the current version installed if found by rpm" do
- expect(provider).to receive(:shell_out!).with("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm").and_return(status)
- expect(provider).to receive(:shell_out).with("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' ImageMagick-c++").and_return(status)
- provider.load_current_resource
- expect(provider.current_resource.version).to eq("6.5.4.7-7.el6_5")
+ context "when the source is an unsupported URI scheme" do
+
+ let(:package_source) { "foobar://example.com/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm" }
+
+ it "should raise an exception if an uri formed source is non-supported scheme" do
+ allow(::File).to receive(:exists?).with(package_source).and_return(false)
+
+ # verify let bindings are as we expect
+ expect(new_resource.source).to eq("foobar://example.com/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
+ expect(provider.load_current_resource).to be_nil
+ expect { provider.run_action(:any) }.to raise_error(Chef::Exceptions::Package)
end
end
- context "source is uri formed" do
- before(:each) do
- allow(::File).to receive(:exists?).and_return(false)
+ end
+
+ describe "when the package source is valid" do
+
+ before do
+ expect(provider).to receive(:shell_out!).
+ with("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{package_source}", timeout: 900).
+ and_return(rpm_qp_status)
+
+ expect(provider).to receive(:shell_out).
+ with("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{package_name}", timeout: 900).
+ and_return(rpm_q_status)
+ end
+
+ context "when rpm fails when querying package installed state" do
+
+ before do
+ allow(::File).to receive(:exists?).with(package_source).and_return(true)
end
- %w(http HTTP https HTTPS ftp FTP).each do |scheme|
- it "should accept uri formed source (#{scheme})" do
- new_resource.source "#{scheme}://example.com/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm"
- expect(provider.load_current_resource).not_to be_nil
+ let(:rpm_qp_stdout) { "ImageMagick-c++ 6.5.4.7-7.el6_5" }
+ let(:rpm_q_stdout) { "" }
+
+ let(:rpm_qp_exitstatus) { 0 }
+ let(:rpm_q_exitstatus) { -1 }
+
+ it "raises an exception when attempting any action" do
+ expected_message = "Unable to determine current version due to RPM failure."
+
+ expect { provider.run_action(:install) }.to raise_error do |error|
+ expect(error).to be_a_kind_of(Chef::Exceptions::Package)
+ expect(error.to_s).to include(expected_message)
end
end
+ end
+
+
+ context "when the package is installed" do
+
+ let(:rpm_qp_stdout) { "ImageMagick-c++ 6.5.4.7-7.el6_5" }
+ let(:rpm_q_stdout) { "ImageMagick-c++ 6.5.4.7-7.el6_5" }
+
+ let(:rpm_qp_exitstatus) { 0 }
+ let(:rpm_q_exitstatus) { 0 }
- %w(file FILE).each do |scheme|
- it "should accept uri formed source (#{scheme})" do
- new_resource.source "#{scheme}:///ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm"
- expect(provider.load_current_resource).not_to be_nil
+ let(:action) { :install }
+
+ context "when the source is a file system path" do
+
+ before do
+ allow(::File).to receive(:exists?).with(package_source).and_return(true)
+
+ provider.action = action
+
+ provider.load_current_resource
+ provider.define_resource_requirements
+ provider.process_resource_requirements
end
- end
- it "should raise an exception if an uri formed source is non-supported scheme" do
- new_resource.source "foobar://example.com/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm"
- expect(provider.load_current_resource).to be_nil
- expect { provider.run_action(:any) }.to raise_error(Chef::Exceptions::Package)
- end
- end
+ it "should get the source package version from rpm if provided" do
+ expect(provider.current_resource.package_name).to eq("ImageMagick-c++")
+ expect(provider.new_resource.version).to eq("6.5.4.7-7.el6_5")
+ end
- context "source is not defiend" do
- let(:new_resource) { Chef::Resource::Package.new("ImageMagick-c++") }
+ it "should return the current version installed if found by rpm" do
+ expect(provider.current_resource.version).to eq("6.5.4.7-7.el6_5")
+ end
+
+ describe "action install" do
+
+ context "when at the desired version already" do
+ it "does nothing when the correct version is installed" do
+ expect(provider).to_not receive(:shell_out!).with("rpm -i /tmp/imagemagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
+
+ provider.action_install
+ end
+ end
+
+ context "when a newer version is desired" do
+
+ let(:rpm_q_stdout) { "imagemagick-c++ 0.5.4.7-7.el6_5" }
+
+ it "runs rpm -u with the package source to upgrade" do
+ expect(provider).to receive(:shell_out!).with("rpm -U /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
+ provider.action_install
+ end
+ end
+
+ context "when an older version is desired" do
+ let(:new_resource) do
+ Chef::Resource::RpmPackage.new(package_name).tap do |r|
+ r.source(package_source)
+ r.allow_downgrade(true)
+ end
+ end
+
+ let(:rpm_q_stdout) { "imagemagick-c++ 21.4-19.el6_5" }
+
+ it "should run rpm -u --oldpackage with the package source to downgrade" do
+ expect(provider).to receive(:shell_out!).with("rpm -U --oldpackage /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
+ provider.action_install
+ end
+
+ end
+
+ end
+
+ describe "action upgrade" do
+
+ let(:action) { :upgrade }
+
+ context "when at the desired version already" do
+ it "does nothing when the correct version is installed" do
+ expect(provider).to_not receive(:shell_out!).with("rpm -i /tmp/imagemagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
+
+ provider.action_upgrade
+ end
+ end
+
+ context "when a newer version is desired" do
+
+ let(:rpm_q_stdout) { "imagemagick-c++ 0.5.4.7-7.el6_5" }
+
+ it "runs rpm -u with the package source to upgrade" do
+ expect(provider).to receive(:shell_out!).with("rpm -U /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
+ provider.action_upgrade
+ end
+ end
+
+ context "when an older version is desired" do
+ let(:new_resource) do
+ Chef::Resource::RpmPackage.new(package_name).tap do |r|
+ r.source(package_source)
+ r.allow_downgrade(true)
+ end
+ end
+
+ let(:rpm_q_stdout) { "imagemagick-c++ 21.4-19.el6_5" }
+
+ it "should run rpm -u --oldpackage with the package source to downgrade" do
+ expect(provider).to receive(:shell_out!).with("rpm -U --oldpackage /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
+ provider.action_upgrade
+ end
+
+ end
+ end
+
+ describe "action :remove" do
+
+ let(:action) { :remove }
+
+ it "should remove the package" do
+ expect(provider).to receive(:shell_out!).with("rpm -e ImageMagick-c++-6.5.4.7-7.el6_5", timeout: 900)
+ provider.action_remove
+ end
+ end
+
+
+ context "when the package name contains a tilde (chef#3503)" do
+
+ let(:package_name) { "supermarket" }
+
+ let(:package_source) { "/tmp/supermarket-1.10.1~alpha.0-1.el5.x86_64.rpm" }
+
+ let(:rpm_qp_stdout) { "supermarket 1.10.1~alpha.0-1.el5" }
+ let(:rpm_q_stdout) { "supermarket 1.10.1~alpha.0-1.el5" }
+
+ let(:rpm_qp_exitstatus) { 0 }
+ let(:rpm_q_exitstatus) { 0 }
+
+ it "should correctly determine the candidate version and installed version" do
+ expect(provider.current_resource.package_name).to eq("supermarket")
+ expect(provider.new_resource.version).to eq("1.10.1~alpha.0-1.el5")
+ end
+ end
- it "should raise an exception if the source is not set but we are installing" do
- expect { provider.run_action(:any) }.to raise_error(Chef::Exceptions::Package)
end
- end
- context "installation does not exist" do
- let(:stdout) { String.new("package openssh-askpass is not installed") }
- let(:exitstatus) { -1 }
- let(:new_resource) do
- Chef::Resource::Package.new("openssh-askpass").tap do |resource|
- resource.source "openssh-askpass"
+ context "when the source is given as an URI" do
+ before(:each) do
+ allow(::File).to receive(:exists?).with(package_source).and_return(false)
+
+ provider.action = action
+
+ provider.load_current_resource
+ provider.define_resource_requirements
+ provider.process_resource_requirements
+ end
+
+ %w(http HTTP https HTTPS ftp FTP file FILE).each do |scheme|
+
+ context "when the source URI uses protocol scheme '#{scheme}'" do
+
+ let(:package_source) { "#{scheme}://example.com/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm" }
+
+ it "should get the source package version from rpm if provided" do
+ expect(provider.current_resource.package_name).to eq("ImageMagick-c++")
+ expect(provider.new_resource.version).to eq("6.5.4.7-7.el6_5")
+ end
+
+ it "should return the current version installed if found by rpm" do
+ expect(provider.current_resource.version).to eq("6.5.4.7-7.el6_5")
+ end
+
+ end
end
+
end
- it "should raise an exception if rpm fails to run" do
- allow(provider).to receive(:shell_out).and_return(status)
- expect { provider.run_action(:any) }.to raise_error(Chef::Exceptions::Package)
+ end
+
+ context "when the package is not installed" do
+
+ let(:package_name) { "openssh-askpass" }
+
+ let(:package_source) { "/tmp/openssh-askpass-1.2.3-4.el6_5.x86_64.rpm" }
+
+ let(:rpm_qp_stdout) { "openssh-askpass 1.2.3-4.el6_5" }
+ let(:rpm_q_stdout) { "package openssh-askpass is not installed" }
+
+ let(:rpm_qp_exitstatus) { 0 }
+ let(:rpm_q_exitstatus) { 0 }
+
+ let(:action) { :install }
+
+ before do
+ allow(File).to receive(:exists?).with(package_source).and_return(true)
+
+ provider.action = action
+
+ provider.load_current_resource
+ provider.define_resource_requirements
+ provider.process_resource_requirements
end
it "should not detect the package name as version when not installed" do
- expect(provider).to receive(:shell_out!).with("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' openssh-askpass").and_return(status)
- expect(provider).to receive(:shell_out).with("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' openssh-askpass").and_return(status)
- provider.load_current_resource
expect(provider.current_resource.version).to be_nil
end
- end
- end
- describe "after the current resource is loaded" do
- let(:current_resource) { Chef::Resource::Package.new("ImageMagick-c++") }
- let(:provider) do
- Chef::Provider::Package::Rpm.new(new_resource, run_context).tap do |provider|
- provider.current_resource = current_resource
- end
- end
+ context "when the package name contains a tilde (chef#3503)" do
- describe "when installing or upgrading" do
- it "should run rpm -i with the package source to install" do
- expect(provider).to receive(:shell_out!).with("rpm -i /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
- provider.install_package("ImageMagick-c++", "6.5.4.7-7.el6_5")
- end
+ let(:package_name) { "supermarket" }
- it "should run rpm -U with the package source to upgrade" do
- current_resource.version("21.4-19.el5")
- expect(provider).to receive(:shell_out!).with("rpm -U /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
- provider.upgrade_package("ImageMagick-c++", "6.5.4.7-7.el6_5")
- end
+ let(:package_source) { "/tmp/supermarket-1.10.1~alpha.0-1.el5.x86_64.rpm" }
- it "should install package if missing and set to upgrade" do
- current_resource.version("ImageMagick-c++")
- expect(provider).to receive(:shell_out!).with("rpm -U /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
- provider.upgrade_package("ImageMagick-c++", "6.5.4.7-7.el6_5")
- end
+ let(:rpm_qp_stdout) { "supermarket 1.10.1~alpha.0-1.el5" }
+ let(:rpm_q_stdout) { "package supermarket is not installed" }
- context "allowing downgrade" do
- let(:new_resource) { Chef::Resource::RpmPackage.new("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm") }
- let(:current_resource) { Chef::Resource::RpmPackage.new("ImageMagick-c++") }
+ let(:rpm_qp_exitstatus) { 0 }
+ let(:rpm_q_exitstatus) { 0 }
- it "should run rpm -U --oldpackage with the package source to downgrade" do
- new_resource.allow_downgrade(true)
- current_resource.version("21.4-19.el5")
- expect(provider).to receive(:shell_out!).with("rpm -U --oldpackage /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
- provider.upgrade_package("ImageMagick-c++", "6.5.4.7-7.el6_5")
+ it "should correctly determine the candidate version" do
+ expect(provider.new_resource.version).to eq("1.10.1~alpha.0-1.el5")
end
end
- context "installing when the name is a path" do
- let(:new_resource) { Chef::Resource::Package.new("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm") }
- let(:current_resource) { Chef::Resource::Package.new("ImageMagick-c++") }
+ describe "managing the package" do
+
+ describe "action install" do
+
+ it "installs the package" do
+ expect(provider).to receive(:shell_out!).with("rpm -i #{package_source}", timeout: 900)
- it "should install from a path when the package is a path and the source is nil" do
- expect(new_resource.source).to eq("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
- provider.current_resource = current_resource
- expect(provider).to receive(:shell_out!).with("rpm -i /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
- provider.install_package("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", "6.5.4.7-7.el6_5")
+ provider.action_install
+ end
+
+ context "when custom resource options are given" do
+ it "installs with custom options specified in the resource" do
+ new_resource.options("--dbpath /var/lib/rpm")
+ expect(provider).to receive(:shell_out!).with("rpm --dbpath /var/lib/rpm -i #{package_source}", timeout: 900)
+ provider.action_install
+ end
+ end
end
- it "should uprgrade from a path when the package is a path and the source is nil" do
- expect(new_resource.source).to eq("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
- current_resource.version("21.4-19.el5")
- provider.current_resource = current_resource
- expect(provider).to receive(:shell_out!).with("rpm -U /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
- provider.upgrade_package("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", "6.5.4.7-7.el6_5")
+ describe "action upgrade" do
+
+ let(:action) { :upgrade }
+
+ it "installs the package" do
+ expect(provider).to receive(:shell_out!).with("rpm -i #{package_source}", timeout: 900)
+
+ provider.action_upgrade
+ end
+ end
+
+ describe "when removing the package" do
+
+ let(:action) { :remove }
+
+ it "should do nothing" do
+ expect(provider).to_not receive(:shell_out!).with("rpm -e ImageMagick-c++-6.5.4.7-7.el6_5", timeout: 900)
+ provider.action_remove
+ end
end
- end
- it "installs with custom options specified in the resource" do
- provider.candidate_version = '11'
- new_resource.options("--dbpath /var/lib/rpm")
- expect(provider).to receive(:shell_out!).with("rpm --dbpath /var/lib/rpm -i /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
- provider.install_package(new_resource.name, provider.candidate_version)
end
+
+
end
+ end
- describe "when removing the package" do
- it "should run rpm -e to remove the package" do
- expect(provider).to receive(:shell_out!).with("rpm -e ImageMagick-c++-6.5.4.7-7.el6_5")
- provider.remove_package("ImageMagick-c++", "6.5.4.7-7.el6_5")
- end
+ context "when the resource name is the path to the package" do
+
+ let(:new_resource) do
+ # When we pass a source in as the name, then #initialize in the
+ # provider will call File.exists?. Because of the ordering in our
+ # let() bindings and such, we have to set the stub here and not in a
+ # before block.
+ allow(::File).to receive(:exists?).with(package_source).and_return(true)
+ Chef::Resource::Package.new("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
+ end
+
+ let(:current_resource) { Chef::Resource::Package.new("ImageMagick-c++") }
+
+ it "should install from a path when the package is a path and the source is nil" do
+ expect(new_resource.source).to eq("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
+ provider.current_resource = current_resource
+ expect(provider).to receive(:shell_out!).with("rpm -i /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
+ provider.install_package("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", "6.5.4.7-7.el6_5")
+ end
+
+ it "should uprgrade from a path when the package is a path and the source is nil" do
+ expect(new_resource.source).to eq("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm")
+ current_resource.version("21.4-19.el5")
+ provider.current_resource = current_resource
+ expect(provider).to receive(:shell_out!).with("rpm -U /tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", timeout: 900)
+ provider.upgrade_package("/tmp/ImageMagick-c++-6.5.4.7-7.el6_5.x86_64.rpm", "6.5.4.7-7.el6_5")
end
end
+
+
end
+
diff --git a/spec/unit/provider/package/rubygems_spec.rb b/spec/unit/provider/package/rubygems_spec.rb
index b17c216ddd..f790bdb1ce 100644
--- a/spec/unit/provider/package/rubygems_spec.rb
+++ b/spec/unit/provider/package/rubygems_spec.rb
@@ -107,38 +107,6 @@ describe Chef::Provider::Package::Rubygems::CurrentGemEnvironment do
expect(@gem_env.candidate_version_from_remote(Gem::Dependency.new('rspec', '>= 0'))).to eq(Gem::Version.new('1.3.0'))
end
- context "when rubygems was upgraded from 1.8->2.0" do
- # https://github.com/rubygems/rubygems/issues/404
- # tl;dr rubygems 1.8 and 2.0 can both be in the load path, which means that
- # require "rubygems/format" will load even though rubygems 2.0 doesn't have
- # that file.
-
- before do
- if defined?(Gem::Format)
- # tests are running under rubygems 1.8, or 2.0 upgraded from 1.8
- @remove_gem_format = false
- else
- Gem.const_set(:Format, Object.new)
- @remove_gem_format = true
- end
- allow(Gem::Package).to receive(:respond_to?).and_call_original
- allow(Gem::Package).to receive(:respond_to?).with(:open).and_return(false)
- end
-
- after do
- if @remove_gem_format
- Gem.send(:remove_const, :Format)
- end
- end
-
- it "finds a matching gem candidate version on rubygems 2.0+ with some rubygems 1.8 code loaded" do
- package = double("Gem::Package", :spec => "a gemspec from package")
- expect(Gem::Package).to receive(:new).with("/path/to/package.gem").and_return(package)
- expect(@gem_env.spec_from_file("/path/to/package.gem")).to eq("a gemspec from package")
- end
-
- end
-
it "gives the candidate version as nil if none is found" do
dep = Gem::Dependency.new('rspec', '>= 0')
latest = []
@@ -222,8 +190,6 @@ describe Chef::Provider::Package::Rubygems::AlternateGemEnvironment do
end
it "uses the cached result for gem paths when available" do
- gem_env_output = ['/path/to/gems', '/another/path/to/gems'].join(File::PATH_SEPARATOR)
- shell_out_result = OpenStruct.new(:stdout => gem_env_output)
expect(@gem_env).not_to receive(:shell_out!)
expected = ['/path/to/gems', '/another/path/to/gems']
Chef::Provider::Package::Rubygems::AlternateGemEnvironment.gempath_cache['/usr/weird/bin/gem']= expected
@@ -261,7 +227,7 @@ describe Chef::Provider::Package::Rubygems::AlternateGemEnvironment do
else
`which gem`.strip
end
- pending("cant find your gem executable") if path_to_gem.empty?
+ skip("cant find your gem executable") if path_to_gem.empty?
gem_env = Chef::Provider::Package::Rubygems::AlternateGemEnvironment.new(path_to_gem)
expected = ['rspec-core', Gem::Version.new(RSpec::Core::Version::STRING)]
actual = gem_env.installed_versions(Gem::Dependency.new('rspec-core', nil)).map { |s| [s.name, s.version] }
@@ -403,6 +369,24 @@ describe Chef::Provider::Package::Rubygems do
expect(provider.gem_env.gem_binary_location).to eq('/usr/weird/bin/gem')
end
+ it "recognizes chef as omnibus" do
+ allow(RbConfig::CONFIG).to receive(:[]).with('bindir').and_return("/opt/chef/embedded/bin")
+ provider = Chef::Provider::Package::Rubygems.new(@new_resource, @run_context)
+ expect(provider.is_omnibus?).to be true
+ end
+
+ it "recognizes opscode as omnibus" do
+ allow(RbConfig::CONFIG).to receive(:[]).with('bindir').and_return("/opt/opscode/embedded/bin")
+ provider = Chef::Provider::Package::Rubygems.new(@new_resource, @run_context)
+ expect(provider.is_omnibus?).to be true
+ end
+
+ it "recognizes chefdk as omnibus" do
+ allow(RbConfig::CONFIG).to receive(:[]).with('bindir').and_return("/opt/chefdk/embedded/bin")
+ provider = Chef::Provider::Package::Rubygems.new(@new_resource, @run_context)
+ expect(provider.is_omnibus?).to be true
+ end
+
it "searches for a gem binary when running on Omnibus on Unix" do
platform_mock :unix do
allow(RbConfig::CONFIG).to receive(:[]).with('bindir').and_return("/opt/chef/embedded/bin")
@@ -542,7 +526,26 @@ describe Chef::Provider::Package::Rubygems do
it "installs the gem by shelling out when options are provided as a String" do
@new_resource.options('-i /alt/install/location')
expected ="gem install rspec-core -q --no-rdoc --no-ri -v \"#{@spec_version}\" -i /alt/install/location"
- expect(@provider).to receive(:shell_out!).with(expected, :env => nil)
+ expect(@provider).to receive(:shell_out!).with(expected, env: nil, timeout: 900)
+ @provider.run_action(:install)
+ expect(@new_resource).to be_updated_by_last_action
+ end
+
+ it "installs the gem with rubygems.org as an added source" do
+ @new_resource.gem_binary('/foo/bar')
+ @new_resource.source('http://mirror.ops.rhcloud.com/mirror/ruby')
+ expected ="/foo/bar install rspec-core -q --no-rdoc --no-ri -v \"#{@spec_version}\" --source=#{@new_resource.source} --source=https://rubygems.org"
+ expect(@provider).to receive(:shell_out!).with(expected, env: nil, timeout: 900)
+ @provider.run_action(:install)
+ expect(@new_resource).to be_updated_by_last_action
+ end
+
+ it "installs the gem with cleared sources and explict source when specified" do
+ @new_resource.gem_binary('/foo/bar')
+ @new_resource.source('http://mirror.ops.rhcloud.com/mirror/ruby')
+ @new_resource.clear_sources(true)
+ expected ="/foo/bar install rspec-core -q --no-rdoc --no-ri -v \"#{@spec_version}\" --clear-sources --source=#{@new_resource.source}"
+ expect(@provider).to receive(:shell_out!).with(expected, env: nil, timeout: 900)
@provider.run_action(:install)
expect(@new_resource).to be_updated_by_last_action
end
@@ -553,7 +556,7 @@ describe Chef::Provider::Package::Rubygems do
it "installs the gem by shelling out when options are provided but no version is given" do
@new_resource.options('-i /alt/install/location')
expected ="gem install rspec-core -q --no-rdoc --no-ri -v \"#{@provider.candidate_version}\" -i /alt/install/location"
- expect(@provider).to receive(:shell_out!).with(expected, :env => nil)
+ expect(@provider).to receive(:shell_out!).with(expected, env: nil, timeout: 900)
@provider.run_action(:install)
expect(@new_resource).to be_updated_by_last_action
end
@@ -599,7 +602,7 @@ describe Chef::Provider::Package::Rubygems do
describe "in an alternate gem environment" do
it "installs the gem by shelling out to gem install" do
@new_resource.gem_binary('/usr/weird/bin/gem')
- expect(@provider).to receive(:shell_out!).with("/usr/weird/bin/gem install rspec-core -q --no-rdoc --no-ri -v \"#{@spec_version}\"", :env=>nil)
+ expect(@provider).to receive(:shell_out!).with("/usr/weird/bin/gem install rspec-core -q --no-rdoc --no-ri -v \"#{@spec_version}\"", env: nil, timeout: 900)
@provider.run_action(:install)
expect(@new_resource).to be_updated_by_last_action
end
@@ -608,7 +611,7 @@ describe Chef::Provider::Package::Rubygems do
@new_resource.gem_binary('/usr/weird/bin/gem')
@new_resource.source(CHEF_SPEC_DATA + '/gems/chef-integration-test-0.1.0.gem')
@new_resource.version('>= 0')
- expect(@provider).to receive(:shell_out!).with("/usr/weird/bin/gem install #{CHEF_SPEC_DATA}/gems/chef-integration-test-0.1.0.gem -q --no-rdoc --no-ri -v \">= 0\"", :env=>nil)
+ expect(@provider).to receive(:shell_out!).with("/usr/weird/bin/gem install #{CHEF_SPEC_DATA}/gems/chef-integration-test-0.1.0.gem -q --no-rdoc --no-ri -v \">= 0\"", env: nil, timeout: 900)
@provider.run_action(:install)
expect(@new_resource).to be_updated_by_last_action
end
@@ -620,7 +623,7 @@ describe Chef::Provider::Package::Rubygems do
@new_resource.gem_binary('/usr/weird/bin/gem')
@new_resource.version('>= 0')
expect(@new_resource.source).to eq(CHEF_SPEC_DATA + '/gems/chef-integration-test-0.1.0.gem')
- expect(@provider).to receive(:shell_out!).with("/usr/weird/bin/gem install #{CHEF_SPEC_DATA}/gems/chef-integration-test-0.1.0.gem -q --no-rdoc --no-ri -v \">= 0\"", :env=>nil)
+ expect(@provider).to receive(:shell_out!).with("/usr/weird/bin/gem install #{CHEF_SPEC_DATA}/gems/chef-integration-test-0.1.0.gem -q --no-rdoc --no-ri -v \">= 0\"", env: nil, timeout: 900)
@provider.run_action(:install)
expect(@new_resource).to be_updated_by_last_action
end
@@ -659,7 +662,7 @@ describe Chef::Provider::Package::Rubygems do
it "uninstalls via the gem command when options are given as a String" do
@new_resource.options('-i /alt/install/location')
- expect(@provider).to receive(:shell_out!).with("gem uninstall rspec -q -x -I -a -i /alt/install/location", :env=>nil)
+ expect(@provider).to receive(:shell_out!).with("gem uninstall rspec -q -x -I -a -i /alt/install/location", env: nil, timeout: 900)
@provider.action_remove
end
@@ -673,7 +676,7 @@ describe Chef::Provider::Package::Rubygems do
describe "in an alternate gem environment" do
it "uninstalls via the gem command" do
@new_resource.gem_binary('/usr/weird/bin/gem')
- expect(@provider).to receive(:shell_out!).with("/usr/weird/bin/gem uninstall rspec -q -x -I -a", :env=>nil)
+ expect(@provider).to receive(:shell_out!).with("/usr/weird/bin/gem uninstall rspec -q -x -I -a", env: nil, timeout: 900)
@provider.action_remove
end
end
diff --git a/spec/unit/provider/package/smartos_spec.rb b/spec/unit/provider/package/smartos_spec.rb
index db39589b85..8f2d2bb8ea 100644
--- a/spec/unit/provider/package/smartos_spec.rb
+++ b/spec/unit/provider/package/smartos_spec.rb
@@ -29,45 +29,45 @@ describe Chef::Provider::Package::SmartOS, "load_current_resource" do
@current_resource = Chef::Resource::Package.new("varnish")
- @status = double("Status", :exitstatus => 0)
- @provider = Chef::Provider::Package::SmartOS.new(@new_resource, @run_context)
- allow(Chef::Resource::Package).to receive(:new).and_return(@current_resource)
- @stdin = StringIO.new
- @stdout = "varnish-2.1.5nb2\n"
- @stderr = StringIO.new
- @pid = 10
- @shell_out = OpenStruct.new(:stdout => @stdout, :stdin => @stdin, :stderr => @stderr, :status => @status, :exitstatus => 0)
+ @status = double("Status", :exitstatus => 0)
+ @provider = Chef::Provider::Package::SmartOS.new(@new_resource, @run_context)
+ allow(Chef::Resource::Package).to receive(:new).and_return(@current_resource)
+ @stdin = StringIO.new
+ @stdout = "varnish-2.1.5nb2\n"
+ @stderr = StringIO.new
+ @pid = 10
+ @shell_out = OpenStruct.new(:stdout => @stdout, :stdin => @stdin, :stderr => @stderr, :status => @status, :exitstatus => 0)
end
- describe "when loading current resource" do
+ describe "when loading current resource" do
it "should create a current resource with the name of the new_resource" do
- expect(@provider).to receive(:shell_out!).and_return(@shell_out)
- expect(Chef::Resource::Package).to receive(:new).and_return(@current_resource)
- @provider.load_current_resource
+ expect(@provider).to receive(:shell_out!).and_return(@shell_out)
+ expect(Chef::Resource::Package).to receive(:new).and_return(@current_resource)
+ @provider.load_current_resource
end
- it "should set the current resource package name" do
- expect(@provider).to receive(:shell_out!).and_return(@shell_out)
- expect(@current_resource).to receive(:package_name).with(@new_resource.package_name)
- @provider.load_current_resource
- end
+ it "should set the current resource package name" do
+ expect(@provider).to receive(:shell_out!).and_return(@shell_out)
+ expect(@current_resource).to receive(:package_name).with(@new_resource.package_name)
+ @provider.load_current_resource
+ end
- it "should set the installed version if it is installed" do
- expect(@provider).to receive(:shell_out!).and_return(@shell_out)
- @provider.load_current_resource
- expect(@current_resource.version).to eq("2.1.5nb2")
- end
+ it "should set the installed version if it is installed" do
+ expect(@provider).to receive(:shell_out!).and_return(@shell_out)
+ @provider.load_current_resource
+ expect(@current_resource.version).to eq("2.1.5nb2")
+ end
- it "should set the installed version to nil if it's not installed" do
- out = OpenStruct.new(:stdout => nil)
- expect(@provider).to receive(:shell_out!).and_return(out)
- @provider.load_current_resource
- expect(@current_resource.version).to eq(nil)
- end
+ it "should set the installed version to nil if it's not installed" do
+ out = OpenStruct.new(:stdout => nil)
+ expect(@provider).to receive(:shell_out!).and_return(out)
+ @provider.load_current_resource
+ expect(@current_resource.version).to eq(nil)
+ end
- end
+ end
describe "candidate_version" do
it "should return the candidate_version variable if already setup" do
@@ -76,27 +76,37 @@ describe Chef::Provider::Package::SmartOS, "load_current_resource" do
@provider.candidate_version
end
- it "should lookup the candidate_version if the variable is not already set" do
+ it "should lookup the candidate_version if the variable is not already set (pkgin separated by spaces)" do
search = double()
expect(search).to receive(:each_line).
- and_yield("something-varnish-1.1.1 something varnish like\n").
- and_yield("varnish-2.3.4 actual varnish\n")
+ and_yield("something-varnish-1.1.1 something varnish like\n").
+ and_yield("varnish-2.3.4 actual varnish\n")
@shell_out = double('shell_out!', :stdout => search)
- expect(@provider).to receive(:shell_out!).with('/opt/local/bin/pkgin se varnish', :env => nil, :returns => [0,1]).and_return(@shell_out)
+ expect(@provider).to receive(:shell_out!).with('/opt/local/bin/pkgin', 'se', 'varnish', :env => nil, :returns => [0,1], :timeout=>900).and_return(@shell_out)
+ expect(@provider.candidate_version).to eq("2.3.4")
+ end
+
+ it "should lookup the candidate_version if the variable is not already set (pkgin separated by semicolons)" do
+ search = double()
+ expect(search).to receive(:each_line).
+ and_yield("something-varnish-1.1.1;;something varnish like\n").
+ and_yield("varnish-2.3.4;;actual varnish\n")
+ @shell_out = double('shell_out!', :stdout => search)
+ expect(@provider).to receive(:shell_out!).with('/opt/local/bin/pkgin', 'se', 'varnish', :env => nil, :returns => [0,1], :timeout=>900).and_return(@shell_out)
expect(@provider.candidate_version).to eq("2.3.4")
end
end
- describe "when manipulating a resource" do
+ describe "when manipulating a resource" do
- it "run pkgin and install the package" do
- out = OpenStruct.new(:stdout => nil)
- expect(@provider).to receive(:shell_out!).with("/opt/local/sbin/pkg_info -E \"varnish*\"", {:env => nil, :returns=>[0,1]}).and_return(@shell_out)
- expect(@provider).to receive(:shell_out!).with("/opt/local/bin/pkgin -y install varnish-2.1.5nb2", {:env=>nil}).and_return(out)
+ it "run pkgin and install the package" do
+ out = OpenStruct.new(:stdout => nil)
+ expect(@provider).to receive(:shell_out!).with("/opt/local/sbin/pkg_info", "-E", "varnish*", {:env => nil, :returns=>[0,1], :timeout=>900}).and_return(@shell_out)
+ expect(@provider).to receive(:shell_out!).with("/opt/local/bin/pkgin", "-y", "install", "varnish-2.1.5nb2", {:env=>nil, :timeout=>900}).and_return(out)
@provider.load_current_resource
@provider.install_package("varnish", "2.1.5nb2")
- end
+ end
- end
+ end
end
diff --git a/spec/unit/provider/package/solaris_spec.rb b/spec/unit/provider/package/solaris_spec.rb
index c348d665e8..ae6c96da00 100644
--- a/spec/unit/provider/package/solaris_spec.rb
+++ b/spec/unit/provider/package/solaris_spec.rb
@@ -71,8 +71,8 @@ PKGINFO
it "should get the source package version from pkginfo if provided" do
status = double(:stdout => @pkginfo, :exitstatus => 0)
- expect(@provider).to receive(:shell_out).with("pkginfo -l -d /tmp/bash.pkg SUNWbash").and_return(status)
- expect(@provider).to receive(:shell_out).with("pkginfo -l SUNWbash").and_return(@status)
+ expect(@provider).to receive(:shell_out).with("pkginfo -l -d /tmp/bash.pkg SUNWbash", { timeout: 900 }).and_return(status)
+ expect(@provider).to receive(:shell_out).with("pkginfo -l SUNWbash", { timeout: 900 }).and_return(@status)
@provider.load_current_resource
expect(@provider.current_resource.package_name).to eq("SUNWbash")
@@ -81,8 +81,8 @@ PKGINFO
it "should return the current version installed if found by pkginfo" do
status = double(:stdout => @pkginfo, :exitstatus => 0)
- expect(@provider).to receive(:shell_out).with("pkginfo -l -d /tmp/bash.pkg SUNWbash").and_return(@status)
- expect(@provider).to receive(:shell_out).with("pkginfo -l SUNWbash").and_return(status)
+ expect(@provider).to receive(:shell_out).with("pkginfo -l -d /tmp/bash.pkg SUNWbash", { timeout: 900 }).and_return(@status)
+ expect(@provider).to receive(:shell_out).with("pkginfo -l SUNWbash", { timeout: 900 }).and_return(status)
@provider.load_current_resource
expect(@provider.current_resource.version).to eq("11.10.0,REV=2005.01.08.05.16")
end
@@ -101,8 +101,8 @@ PKGINFO
end
it "should return a current resource with a nil version if the package is not found" do
- expect(@provider).to receive(:shell_out).with("pkginfo -l -d /tmp/bash.pkg SUNWbash").and_return(@status)
- expect(@provider).to receive(:shell_out).with("pkginfo -l SUNWbash").and_return(@status)
+ expect(@provider).to receive(:shell_out).with("pkginfo -l -d /tmp/bash.pkg SUNWbash", { timeout: 900 }).and_return(@status)
+ expect(@provider).to receive(:shell_out).with("pkginfo -l SUNWbash", { timeout: 900 }).and_return(@status)
@provider.load_current_resource
expect(@provider.current_resource.version).to be_nil
end
@@ -132,7 +132,7 @@ PKGINFO
describe "install and upgrade" do
it "should run pkgadd -n -d with the package source to install" do
- expect(@provider).to receive(:shell_out!).with("pkgadd -n -d /tmp/bash.pkg all")
+ expect(@provider).to receive(:shell_out!).with("pkgadd -n -d /tmp/bash.pkg all", { timeout: 900 })
@provider.install_package("SUNWbash", "11.10.0,REV=2005.01.08.05.16")
end
@@ -140,26 +140,26 @@ PKGINFO
@new_resource = Chef::Resource::Package.new("/tmp/bash.pkg")
@provider = Chef::Provider::Package::Solaris.new(@new_resource, @run_context)
expect(@new_resource.source).to eq("/tmp/bash.pkg")
- expect(@provider).to receive(:shell_out!).with("pkgadd -n -d /tmp/bash.pkg all")
+ expect(@provider).to receive(:shell_out!).with("pkgadd -n -d /tmp/bash.pkg all", { timeout: 900 })
@provider.install_package("/tmp/bash.pkg", "11.10.0,REV=2005.01.08.05.16")
end
it "should run pkgadd -n -a /tmp/myadmin -d with the package options -a /tmp/myadmin" do
allow(@new_resource).to receive(:options).and_return("-a /tmp/myadmin")
- expect(@provider).to receive(:shell_out!).with("pkgadd -n -a /tmp/myadmin -d /tmp/bash.pkg all")
+ expect(@provider).to receive(:shell_out!).with("pkgadd -n -a /tmp/myadmin -d /tmp/bash.pkg all", { timeout: 900 })
@provider.install_package("SUNWbash", "11.10.0,REV=2005.01.08.05.16")
end
end
describe "remove" do
it "should run pkgrm -n to remove the package" do
- expect(@provider).to receive(:shell_out!).with("pkgrm -n SUNWbash")
+ expect(@provider).to receive(:shell_out!).with("pkgrm -n SUNWbash", { timeout: 900 })
@provider.remove_package("SUNWbash", "11.10.0,REV=2005.01.08.05.16")
end
it "should run pkgrm -n -a /tmp/myadmin with options -a /tmp/myadmin" do
allow(@new_resource).to receive(:options).and_return("-a /tmp/myadmin")
- expect(@provider).to receive(:shell_out!).with("pkgrm -n -a /tmp/myadmin SUNWbash")
+ expect(@provider).to receive(:shell_out!).with("pkgrm -n -a /tmp/myadmin SUNWbash", { timeout: 900 })
@provider.remove_package("SUNWbash", "11.10.0,REV=2005.01.08.05.16")
end
diff --git a/spec/unit/provider/package/windows_spec.rb b/spec/unit/provider/package/windows_spec.rb
index d402113d72..e5acc87694 100644
--- a/spec/unit/provider/package/windows_spec.rb
+++ b/spec/unit/provider/package/windows_spec.rb
@@ -19,50 +19,129 @@
require 'spec_helper'
describe Chef::Provider::Package::Windows, :windows_only do
+ before(:each) do
+ allow(Chef::Util::PathHelper).to receive(:windows?).and_return(true)
+ allow(Chef::FileCache).to receive(:create_cache_path).with("package/").and_return(cache_path)
+ end
+
let(:node) { double('Chef::Node') }
let(:events) { double('Chef::Events').as_null_object } # mock all the methods
let(:run_context) { double('Chef::RunContext', :node => node, :events => events) }
- let(:new_resource) { Chef::Resource::WindowsPackage.new("calculator.msi") }
+ let(:resource_source) { 'calculator.msi' }
+ let(:new_resource) { Chef::Resource::WindowsPackage.new(resource_source) }
let(:provider) { Chef::Provider::Package::Windows.new(new_resource, run_context) }
+ let(:cache_path) { 'c:\\cache\\' }
describe "load_current_resource" do
- before(:each) do
- allow(Chef::Util::PathHelper).to receive(:validate_path)
- allow(provider).to receive(:package_provider).and_return(double('package_provider',
+ shared_examples "a local file" do
+ before(:each) do
+ allow(Chef::Util::PathHelper).to receive(:validate_path)
+ allow(provider).to receive(:package_provider).and_return(double('package_provider',
:installed_version => "1.0", :package_version => "2.0"))
- end
+ end
- it "creates a current resource with the name of the new resource" do
- provider.load_current_resource
- expect(provider.current_resource).to be_a(Chef::Resource::WindowsPackage)
- expect(provider.current_resource.name).to eql("calculator.msi")
- end
+ it "creates a current resource with the name of the new resource" do
+ provider.load_current_resource
+ expect(provider.current_resource).to be_a(Chef::Resource::WindowsPackage)
+ expect(provider.current_resource.name).to eql(resource_source)
+ end
+
+ it "sets the current version if the package is installed" do
+ provider.load_current_resource
+ expect(provider.current_resource.version).to eql("1.0")
+ end
- it "sets the current version if the package is installed" do
- provider.load_current_resource
- expect(provider.current_resource.version).to eql("1.0")
+ it "sets the version to be installed" do
+ provider.load_current_resource
+ expect(provider.new_resource.version).to eql("2.0")
+ end
end
- it "sets the version to be installed" do
- provider.load_current_resource
- expect(provider.new_resource.version).to eql("2.0")
+ context "when the source is a uri" do
+ let(:resource_source) { 'https://foo.bar/calculator.msi' }
+
+ context "when the source has not been downloaded" do
+ before(:each) do
+ allow(provider).to receive(:downloadable_file_missing?).and_return(true)
+ end
+ it "sets the current version to unknown" do
+ provider.load_current_resource
+ expect(provider.current_resource.version).to eql("unknown")
+ end
+ end
+
+ context "when the source has been downloaded" do
+ before(:each) do
+ allow(provider).to receive(:downloadable_file_missing?).and_return(false)
+ end
+ it_behaves_like "a local file"
+ end
+
+ context "when remote_file_attributes are provided" do
+ let (:remote_file_attributes) { {:path => 'C:\\foobar.msi'} }
+ before(:each) do
+ new_resource.remote_file_attributes(remote_file_attributes)
+ end
+
+ it 'should override the attributes of the remote file resource used' do
+ expect(::File).to receive(:exists?).with(remote_file_attributes[:path])
+ provider.load_current_resource
+ end
+
+ end
end
- it "checks that the source path is valid" do
- expect(Chef::Util::PathHelper).to receive(:validate_path)
- provider.load_current_resource
+ context "when source is a local file" do
+ it_behaves_like "a local file"
end
end
describe "package_provider" do
- it "sets the package provider to MSI if the the installer type is :msi" do
- allow(provider).to receive(:installer_type).and_return(:msi)
- expect(provider.package_provider).to be_a(Chef::Provider::Package::Windows::MSI)
+ shared_examples "a local file" do
+ it "checks that the source path is valid" do
+ expect(Chef::Util::PathHelper).to receive(:validate_path)
+ provider.package_provider
+ end
+
+ it "sets the package provider to MSI if the the installer type is :msi" do
+ allow(provider).to receive(:installer_type).and_return(:msi)
+ expect(provider.package_provider).to be_a(Chef::Provider::Package::Windows::MSI)
+ end
+
+ it "raises an error if the installer_type is unknown" do
+ allow(provider).to receive(:installer_type).and_return(:apt_for_windows)
+ expect { provider.package_provider }.to raise_error
+ end
+ end
+
+ context "when the source is a uri" do
+ let(:resource_source) { 'https://foo.bar/calculator.msi' }
+
+ context "when the source has not been downloaded" do
+ before(:each) do
+ allow(provider).to receive(:should_download?).and_return(true)
+ end
+
+ it "should create a package provider with source pointing at the local file" do
+ expect(Chef::Provider::Package::Windows::MSI).to receive(:new) do |r|
+ expect(r.source).to eq("#{cache_path}#{::File.basename(resource_source)}")
+ end
+ provider.package_provider
+ end
+
+ it_behaves_like "a local file"
+ end
+
+ context "when the source has been downloaded" do
+ before(:each) do
+ allow(provider).to receive(:should_download?).and_return(false)
+ end
+ it_behaves_like "a local file"
+ end
end
- it "raises an error if the installer_type is unknown" do
- allow(provider).to receive(:installer_type).and_return(:apt_for_windows)
- expect { provider.package_provider }.to raise_error
+ context "when source is a local file" do
+ it_behaves_like "a local file"
end
end
diff --git a/spec/unit/provider/package/yum_spec.rb b/spec/unit/provider/package/yum_spec.rb
index cd2b3decf4..3fc0b807c9 100644
--- a/spec/unit/provider/package/yum_spec.rb
+++ b/spec/unit/provider/package/yum_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: Adam Jacob (<adam@opscode.com>)
-# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# Copyright:: Copyright (c) 2008-2015 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,13 +17,14 @@
#
require 'spec_helper'
+require 'securerandom'
describe Chef::Provider::Package::Yum do
before(:each) do
@node = Chef::Node.new
@events = Chef::EventDispatch::Dispatcher.new
@run_context = Chef::RunContext.new(@node, {}, @events)
- @new_resource = Chef::Resource::Package.new('cups')
+ @new_resource = Chef::Resource::YumPackage.new('cups')
@status = double("Status", :exitstatus => 0)
@yum_cache = double(
'Chef::Provider::Yum::YumCache',
@@ -38,6 +39,7 @@ describe Chef::Provider::Package::Yum do
:disable_extra_repo_control => true
)
allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
+ allow(@yum_cache).to receive(:yum_binary=).with("yum")
@provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
@pid = double("PID")
end
@@ -73,6 +75,60 @@ describe Chef::Provider::Package::Yum do
expect(@provider.load_current_resource).to eql(@provider.current_resource)
end
+ describe "when source is provided" do
+ it "should set the candidate version" do
+ @new_resource = Chef::Resource::YumPackage.new('testing.source')
+ @new_resource.source "chef-server-core-12.0.5-1.rpm"
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ allow(File).to receive(:exists?).with(@new_resource.source).and_return(true)
+ allow(@yum_cache).to receive(:installed_version).and_return(nil)
+ shellout_double = double(:stdout => 'chef-server-core 12.0.5-1')
+ allow(@provider).to receive(:shell_out!).and_return(shellout_double)
+ @provider.load_current_resource
+ expect(@provider.candidate_version).to eql('12.0.5-1')
+ end
+ end
+
+ describe "yum_binary accessor" do
+ it "when yum-deprecated exists" do
+ expect(File).to receive(:exist?).with("/usr/bin/yum-deprecated").and_return(true)
+ expect(@yum_cache).to receive(:yum_binary=).with("yum-deprecated")
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ expect(@provider.yum_binary).to eql("yum-deprecated")
+ end
+
+ it "when yum-deprecated does not exist" do
+ expect(File).to receive(:exist?).with("/usr/bin/yum-deprecated").and_return(false)
+ expect(@yum_cache).to receive(:yum_binary=).with("yum")
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ expect(@provider.yum_binary).to eql("yum")
+ end
+
+ it "when the yum_binary is set on the resource" do
+ @new_resource.yum_binary "/usr/bin/yum-something"
+ expect(File).not_to receive(:exist?)
+ expect(@yum_cache).to receive(:yum_binary=).with("/usr/bin/yum-something")
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ expect(@provider.yum_binary).to eql("/usr/bin/yum-something")
+ end
+
+ it "when the new_resource is a vanilla package class and yum-deprecated exists" do
+ @new_resource = Chef::Resource::Package.new('cups')
+ expect(File).to receive(:exist?).with("/usr/bin/yum-deprecated").and_return(true)
+ expect(@yum_cache).to receive(:yum_binary=).with("yum-deprecated")
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ expect(@provider.yum_binary).to eql("yum-deprecated")
+ end
+
+ it "when the new_resource is a vanilla package class and yum-deprecated does not exist" do
+ @new_resource = Chef::Resource::Package.new('cups')
+ expect(File).to receive(:exist?).with("/usr/bin/yum-deprecated").and_return(false)
+ expect(@yum_cache).to receive(:yum_binary=).with("yum")
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ expect(@provider.yum_binary).to eql("yum")
+ end
+ end
+
describe "when arch in package_name" do
it "should set the arch if no existing package_name is found and new_package_name+new_arch is available" do
@new_resource = Chef::Resource::YumPackage.new('testing.noarch')
@@ -94,6 +150,7 @@ describe Chef::Provider::Package::Yum do
allow(@yum_cache).to receive(:package_available?).and_return(true)
allow(@yum_cache).to receive(:disable_extra_repo_control).and_return(true)
allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
+ allow(@yum_cache).to receive(:yum_binary=).with("yum")
@provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
@provider.load_current_resource
expect(@provider.new_resource.package_name).to eq("testing")
@@ -108,6 +165,26 @@ describe Chef::Provider::Package::Yum do
expect(@provider.arch).to eq("noarch")
end
+ describe "when version constraint in package_name" do
+ it "should set package_version if no existing package_name is found and new_package_name is available" do
+ @new_resource = Chef::Resource::Package.new('cups = 1.2.4-11.18.el5_2.3')
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ allow(@yum_cache).to receive(:package_available?) { |pkg| pkg == 'cups' ? true : false }
+ allow(@yum_cache).to receive(:packages_from_require) do |pkg|
+ [Chef::Provider::Package::Yum::RPMDbPackage.new("cups", "1.2.4-11.18.el5_2.3", "noarch", [], false, true, "base"),
+ Chef::Provider::Package::Yum::RPMDbPackage.new("cups", "1.2.4-11.18.el5_2.2", "noarch", [], false, true, "base"),]
+ end
+ expect(Chef::Log).to receive(:debug).exactly(1).times.with(%r{checking yum info})
+ expect(Chef::Log).to receive(:debug).exactly(1).times.with(%r{installed version})
+ expect(Chef::Log).to receive(:debug).exactly(1).times.with(%r{matched 2 packages,})
+ @provider.load_current_resource
+ expect(@provider.new_resource.package_name).to eq("cups")
+ expect(@provider.new_resource.version).to eq("1.2.4-11.18.el5_2.3")
+ expect(@provider.send(:new_version_array)).to eq(["1.2.4-11.18.el5_2.3"])
+ expect(@provider.send(:package_name_array)).to eq(["cups"])
+ end
+ end
+
it "should not set the arch when an existing package_name is found" do
@new_resource = Chef::Resource::YumPackage.new('testing.beta3')
@yum_cache = double(
@@ -128,6 +205,7 @@ describe Chef::Provider::Package::Yum do
allow(@yum_cache).to receive(:package_available?).and_return(true)
allow(@yum_cache).to receive(:disable_extra_repo_control).and_return(true)
allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
+ allow(@yum_cache).to receive(:yum_binary=).with("yum")
@provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
# annoying side effect of the fun stub'ing above
@provider.load_current_resource
@@ -159,6 +237,7 @@ describe Chef::Provider::Package::Yum do
allow(@yum_cache).to receive(:package_available?).and_return(true)
allow(@yum_cache).to receive(:disable_extra_repo_control).and_return(true)
allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
+ allow(@yum_cache).to receive(:yum_binary=).with("yum")
@provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
@provider.load_current_resource
expect(@provider.new_resource.package_name).to eq("testing.beta3")
@@ -194,6 +273,7 @@ describe Chef::Provider::Package::Yum do
allow(@yum_cache).to receive(:package_available?).and_return(true)
allow(@yum_cache).to receive(:disable_extra_repo_control).and_return(true)
allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
+ allow(@yum_cache).to receive(:yum_binary=).with("yum")
@provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
@provider.load_current_resource
expect(@provider.new_resource.package_name).to eq("testing.i386")
@@ -246,6 +326,7 @@ describe Chef::Provider::Package::Yum do
before do
allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(yum_cache)
+ allow(yum_cache).to receive(:yum_binary=).with("yum")
@pkg = Chef::Provider::Package::Yum::RPMPackage.new("test-package", "2.0.1.el5", "x86_64", [])
expect(yum_cache).to receive(:packages_from_require).and_return([@pkg])
end
@@ -317,6 +398,7 @@ describe Chef::Provider::Package::Yum do
:disable_extra_repo_control => true
)
allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
+ allow(@yum_cache).to receive(:yum_binary=).with("yum")
pkg = Chef::Provider::Package::Yum::RPMPackage.new("test-package", "2.0.1.el5", "x86_64", [])
expect(@yum_cache).to receive(:packages_from_require).and_return([pkg])
@provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
@@ -338,6 +420,7 @@ describe Chef::Provider::Package::Yum do
:disable_extra_repo_control => true
)
allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
+ allow(@yum_cache).to receive(:yum_binary=).with("yum")
pkg = Chef::Provider::Package::Yum::RPMPackage.new("test-package", "2.0.1.el5", "x86_64", [])
expect(@yum_cache).to receive(:packages_from_require).and_return([pkg])
@new_resource = Chef::Resource::YumPackage.new('test-package = 2.0.1.el5')
@@ -360,6 +443,7 @@ describe Chef::Provider::Package::Yum do
:disable_extra_repo_control => true
)
allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
+ allow(@yum_cache).to receive(:yum_binary=).with("yum")
expect(@yum_cache).to receive(:packages_from_require).exactly(4).times.and_return([])
expect(@yum_cache).to receive(:reload_provides).twice
@@ -384,6 +468,7 @@ describe Chef::Provider::Package::Yum do
:disable_extra_repo_control => true
)
allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
+ allow(@yum_cache).to receive(:yum_binary=).with("yum")
expect(@yum_cache).to receive(:packages_from_require).twice.and_return([])
expect(@yum_cache).to receive(:reload_provides)
@provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
@@ -403,6 +488,7 @@ describe Chef::Provider::Package::Yum do
:disable_extra_repo_control => true
)
allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
+ allow(@yum_cache).to receive(:yum_binary=).with("yum")
@provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
expect(@yum_cache).to receive(:packages_from_require).once.and_return([])
expect(@yum_cache).not_to receive(:reload_provides)
@@ -427,6 +513,7 @@ describe Chef::Provider::Package::Yum do
:disable_extra_repo_control => true
)
allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
+ allow(@yum_cache).to receive(:yum_binary=).with("yum")
expect(@yum_cache).to receive(:packages_from_require).twice.and_return([])
@provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
@provider.load_current_resource
@@ -439,7 +526,7 @@ describe Chef::Provider::Package::Yum do
@provider.load_current_resource
allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1)
expect(@provider).to receive(:yum_command).with(
- "yum -d0 -e0 -y install cups-1.2.4-11.19.el5"
+ "-d0 -e0 -y install cups-1.2.4-11.19.el5"
)
@provider.install_package("cups", "1.2.4-11.19.el5")
end
@@ -447,7 +534,7 @@ describe Chef::Provider::Package::Yum do
it "should run yum localinstall if given a path to an rpm" do
allow(@new_resource).to receive(:source).and_return("/tmp/emacs-21.4-20.el5.i386.rpm")
expect(@provider).to receive(:yum_command).with(
- "yum -d0 -e0 -y localinstall /tmp/emacs-21.4-20.el5.i386.rpm"
+ "-d0 -e0 -y localinstall /tmp/emacs-21.4-20.el5.i386.rpm"
)
@provider.install_package("emacs", "21.4-20.el5")
end
@@ -458,7 +545,7 @@ describe Chef::Provider::Package::Yum do
@provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
expect(@new_resource.source).to eq("/tmp/emacs-21.4-20.el5.i386.rpm")
expect(@provider).to receive(:yum_command).with(
- "yum -d0 -e0 -y localinstall /tmp/emacs-21.4-20.el5.i386.rpm"
+ "-d0 -e0 -y localinstall /tmp/emacs-21.4-20.el5.i386.rpm"
)
@provider.install_package("/tmp/emacs-21.4-20.el5.i386.rpm", "21.4-20.el5")
end
@@ -468,7 +555,7 @@ describe Chef::Provider::Package::Yum do
allow(@new_resource).to receive(:arch).and_return("i386")
allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1)
expect(@provider).to receive(:yum_command).with(
- "yum -d0 -e0 -y install cups-1.2.4-11.19.el5.i386"
+ "-d0 -e0 -y install cups-1.2.4-11.19.el5.i386"
)
@provider.install_package("cups", "1.2.4-11.19.el5")
end
@@ -479,7 +566,7 @@ describe Chef::Provider::Package::Yum do
allow(@new_resource).to receive(:options).and_return("--disablerepo epmd")
allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1)
expect(@provider).to receive(:yum_command).with(
- "yum -d0 -e0 -y --disablerepo epmd install cups-11"
+ "-d0 -e0 -y --disablerepo epmd install cups-11"
)
@provider.install_package(@new_resource.name, @provider.candidate_version)
end
@@ -496,6 +583,7 @@ describe Chef::Provider::Package::Yum do
:disable_extra_repo_control => true
)
allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
+ allow(@yum_cache).to receive(:yum_binary=).with("yum")
@provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
expect { @provider.install_package("lolcats", "0.99") }.to raise_error(Chef::Exceptions::Package, %r{Version .* not found})
end
@@ -514,6 +602,7 @@ describe Chef::Provider::Package::Yum do
:disable_extra_repo_control => true
)
allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
+ allow(@yum_cache).to receive(:yum_binary=).with("yum")
@provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
@provider.load_current_resource
expect { @provider.install_package("cups", "1.2.4-11.15.el5") }.to raise_error(Chef::Exceptions::Package, %r{is newer than candidate package})
@@ -533,10 +622,11 @@ describe Chef::Provider::Package::Yum do
:disable_extra_repo_control => true
)
allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
+ allow(@yum_cache).to receive(:yum_binary=).with("yum")
@provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
@provider.load_current_resource
expect(@provider).to receive(:yum_command).with(
- "yum -d0 -e0 -y install cups-1.2.4-11.15.el5"
+ "-d0 -e0 -y install cups-1.2.4-11.15.el5"
)
@provider.install_package("cups", "1.2.4-11.15.el5")
end
@@ -556,10 +646,11 @@ describe Chef::Provider::Package::Yum do
:disable_extra_repo_control => true
)
allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
+ allow(@yum_cache).to receive(:yum_binary=).with("yum")
@provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
@provider.load_current_resource
expect(@provider).to receive(:yum_command).with(
- "yum -d0 -e0 -y downgrade cups-1.2.4-11.15.el5"
+ "-d0 -e0 -y downgrade cups-1.2.4-11.15.el5"
)
@provider.install_package("cups", "1.2.4-11.15.el5")
end
@@ -569,7 +660,7 @@ describe Chef::Provider::Package::Yum do
@provider.load_current_resource
allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1)
expect(@provider).to receive(:yum_command).with(
- "yum -d0 -e0 -y install cups-1.2.4-11.15.el5"
+ "-d0 -e0 -y install cups-1.2.4-11.15.el5"
)
expect(@yum_cache).to receive(:reload).once
@provider.install_package("cups", "1.2.4-11.15.el5")
@@ -580,7 +671,7 @@ describe Chef::Provider::Package::Yum do
@provider.load_current_resource
allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1)
expect(@provider).to receive(:yum_command).with(
- "yum -d0 -e0 -y install cups-1.2.4-11.15.el5"
+ "-d0 -e0 -y install cups-1.2.4-11.15.el5"
)
expect(@yum_cache).not_to receive(:reload)
@provider.install_package("cups", "1.2.4-11.15.el5")
@@ -593,7 +684,7 @@ describe Chef::Provider::Package::Yum do
allow(@provider).to receive(:candidate_version).and_return('11')
allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1)
expect(@provider).to receive(:yum_command).with(
- "yum -d0 -e0 -y install cups-11"
+ "-d0 -e0 -y install cups-11"
)
@provider.upgrade_package(@new_resource.name, @provider.candidate_version)
end
@@ -604,7 +695,7 @@ describe Chef::Provider::Package::Yum do
allow(@provider).to receive(:candidate_version).and_return('11')
allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1)
expect(@provider).to receive(:yum_command).with(
- "yum -d0 -e0 -y install cups-11"
+ "-d0 -e0 -y install cups-11"
)
@provider.upgrade_package(@new_resource.name, @provider.candidate_version)
end
@@ -622,6 +713,7 @@ describe Chef::Provider::Package::Yum do
:disable_extra_repo_control => true
)
allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
+ allow(@yum_cache).to receive(:yum_binary=).with("yum")
@provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
@provider.load_current_resource
expect { @provider.upgrade_package("cups", "1.2.4-11.15.el5") }.to raise_error(Chef::Exceptions::Package, %r{is newer than candidate package})
@@ -671,7 +763,7 @@ describe Chef::Provider::Package::Yum do
describe "when removing a package" do
it "should run yum remove with the package name" do
expect(@provider).to receive(:yum_command).with(
- "yum -d0 -e0 -y remove emacs-1.0"
+ "-d0 -e0 -y remove emacs-1.0"
)
@provider.remove_package("emacs", "1.0")
end
@@ -679,7 +771,7 @@ describe Chef::Provider::Package::Yum do
it "should run yum remove with the package name and arch" do
allow(@new_resource).to receive(:arch).and_return("x86_64")
expect(@provider).to receive(:yum_command).with(
- "yum -d0 -e0 -y remove emacs-1.0.x86_64"
+ "-d0 -e0 -y remove emacs-1.0.x86_64"
)
@provider.remove_package("emacs", "1.0")
end
@@ -688,7 +780,7 @@ describe Chef::Provider::Package::Yum do
describe "when purging a package" do
it "should run yum remove with the package name" do
expect(@provider).to receive(:yum_command).with(
- "yum -d0 -e0 -y remove emacs-1.0"
+ "-d0 -e0 -y remove emacs-1.0"
)
@provider.purge_package("emacs", "1.0")
end
@@ -702,7 +794,7 @@ describe Chef::Provider::Package::Yum do
"yum -d0 -e0 -y install emacs-1.0",
{:timeout => Chef::Config[:yum_timeout]}
)
- @provider.yum_command("yum -d0 -e0 -y install emacs-1.0")
+ @provider.yum_command("-d0 -e0 -y install emacs-1.0")
end
it "should run yum once if it exits with a return code > 0 and no scriptlet failures" do
@@ -712,7 +804,7 @@ describe Chef::Provider::Package::Yum do
"yum -d0 -e0 -y install emacs-1.0",
{:timeout => Chef::Config[:yum_timeout]}
)
- expect { @provider.yum_command("yum -d0 -e0 -y install emacs-1.0") }.to raise_error(Chef::Exceptions::Exec)
+ expect { @provider.yum_command("-d0 -e0 -y install emacs-1.0") }.to raise_error(Chef::Exceptions::Exec)
end
it "should run yum once if it exits with a return code of 1 and %pre scriptlet failures" do
@@ -724,7 +816,7 @@ describe Chef::Provider::Package::Yum do
{:timeout => Chef::Config[:yum_timeout]}
)
# will still raise an exception, can't stub out the subsequent call
- expect { @provider.yum_command("yum -d0 -e0 -y install emacs-1.0") }.to raise_error(Chef::Exceptions::Exec)
+ expect { @provider.yum_command("-d0 -e0 -y install emacs-1.0") }.to raise_error(Chef::Exceptions::Exec)
end
it "should run yum twice if it exits with a return code of 1 and %post scriptlet failures" do
@@ -736,7 +828,20 @@ describe Chef::Provider::Package::Yum do
{:timeout => Chef::Config[:yum_timeout]}
)
# will still raise an exception, can't stub out the subsequent call
- expect { @provider.yum_command("yum -d0 -e0 -y install emacs-1.0") }.to raise_error(Chef::Exceptions::Exec)
+ expect { @provider.yum_command("-d0 -e0 -y install emacs-1.0") }.to raise_error(Chef::Exceptions::Exec)
+ end
+
+ it "should pass the yum_binary to the command if its specified" do
+ @new_resource.yum_binary "yum-deprecated"
+ expect(@yum_cache).to receive(:yum_binary=).with("yum-deprecated")
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ @status = double("Status", :exitstatus => 0, :stdout => "", :stderr => "")
+ allow(@provider).to receive(:shell_out).and_return(@status)
+ expect(@provider).to receive(:shell_out).once.with(
+ "yum-deprecated -d0 -e0 -y install emacs-1.0",
+ {:timeout => Chef::Config[:yum_timeout]}
+ )
+ @provider.yum_command("-d0 -e0 -y install emacs-1.0")
end
end
end
@@ -1645,6 +1750,14 @@ describe Chef::Provider::Package::Yum::YumCache do
end
end
+ let(:yum_exe) {
+ StringIO.new("#!/usr/bin/python\n\naldsjfa\ldsajflkdsjf\lajsdfj")
+ }
+
+ let(:bin_exe) {
+ StringIO.new(SecureRandom.random_bytes)
+ }
+
before(:each) do
@stdin = double("STDIN", :nil_object => true)
@stdout = double("STDOUT", :nil_object => true)
@@ -1690,12 +1803,20 @@ file: file://///etc/yum.repos.d/CentOS-Base.repo, line: 12
'qeqwewe\n'
EOF
@status = double("Status", :exitstatus => 0, :stdin => @stdin, :stdout => @stdout_good, :stderr => @stderr)
-
# new singleton each time
Chef::Provider::Package::Yum::YumCache.reset_instance
@yc = Chef::Provider::Package::Yum::YumCache.instance
# load valid data
+ @yc.yum_binary = "yum"
allow(@yc).to receive(:shell_out!).and_return(@status)
+ allow_any_instance_of(described_class).to receive(:which).with("yum").and_return("/usr/bin/yum")
+ allow(::File).to receive(:open).with("/usr/bin/yum", "r") do |&block|
+ res = block.call(yum_exe)
+ # a bit of a hack. rewind this since it seem that no matter what
+ # I do, we get the same StringIO objects on multiple calls to
+ # ::File.open
+ yum_exe.rewind; res
+ end
end
describe "initialize" do
@@ -1712,6 +1833,24 @@ EOF
end
end
+ describe "python_bin" do
+ it "should return the default python if an error occurs" do
+ allow(::File).to receive(:open).with("/usr/bin/yum", "r").and_raise(StandardError)
+ expect(@yc.python_bin).to eq("/usr/bin/python")
+ end
+
+ it "should return the default python if the yum-executable doesn't start with #!" do
+ allow(::File).to receive(:open).with("/usr/bin/yum", "r") { |&b| r = b.call(bin_exe); bin_exe.rewind; r}
+ expect(@yc.python_bin).to eq("/usr/bin/python")
+ end
+
+ it "should return the interpreter for yum" do
+ other = StringIO.new("#!/usr/bin/super_python\n\nlasjdfdsaljf\nlasdjfs")
+ allow(::File).to receive(:open).with("/usr/bin/yum", "r") { |&b| r = b.call(other); other.rewind; r}
+ expect(@yc.python_bin).to eq("/usr/bin/super_python")
+ end
+ end
+
describe "refresh" do
it "should implicitly call yum-dump.py only once by default after being instantiated" do
expect(@yc).to receive(:shell_out!).once
@@ -1985,6 +2124,8 @@ describe "Chef::Provider::Package::Yum - Multi" do
:disable_extra_repo_control => true
)
allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache)
+ allow(@yum_cache).to receive(:yum_binary=).with("yum")
+ allow(@yum_cache).to receive(:yum_binary=).with("yum")
@provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
@pid = double("PID")
end
@@ -2027,6 +2168,36 @@ describe "Chef::Provider::Package::Yum - Multi" do
it "should return the current resouce" do
expect(@provider.load_current_resource).to eql(@provider.current_resource)
end
+
+ describe "when version constraint in package_name" do
+ it "should set package_version if no existing package_name is found and new_package_name is available" do
+ @new_resource = Chef::Resource::Package.new(['cups = 1.2.4-11.18.el5_2.3', 'emacs = 24.4'])
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ allow(@yum_cache).to receive(:package_available?) { |pkg| %w(cups emacs).include?(pkg) ? true : false }
+ allow(@yum_cache).to receive(:candidate_version) do |pkg|
+ if pkg == 'cups'
+ "1.2.4-11.18.el5_2.3"
+ elsif pkg == 'emacs'
+ "24.4"
+ end
+ end
+ allow(@yum_cache).to receive(:packages_from_require) do |pkg|
+ if pkg.name == 'cups'
+ [Chef::Provider::Package::Yum::RPMDbPackage.new("cups", "1.2.4-11.18.el5_2.3", "noarch", [], false, true, "base")]
+ elsif pkg.name == 'emacs'
+ [Chef::Provider::Package::Yum::RPMDbPackage.new("emacs", "24.4", "noarch", [], false, true, "base")]
+ end
+ end
+ expect(Chef::Log).to receive(:debug).exactly(2).times.with(%r{matched 1 package,})
+ expect(Chef::Log).to receive(:debug).exactly(1).times.with(%r{candidate version: \["1.2.4-11.18.el5_2.3", "24.4"\]})
+ expect(Chef::Log).to receive(:debug).at_least(2).times.with(%r{checking yum info})
+ @provider.load_current_resource
+ expect(@provider.new_resource.package_name).to eq(["cups", "emacs"])
+ expect(@provider.new_resource.version).to eq(["1.2.4-11.18.el5_2.3", "24.4"])
+ expect(@provider.send(:package_name_array)).to eq(["cups", "emacs"])
+ expect(@provider.send(:new_version_array)).to eq(["1.2.4-11.18.el5_2.3", "24.4"])
+ end
+ end
end
describe "when installing a package" do
@@ -2036,7 +2207,7 @@ describe "Chef::Provider::Package::Yum - Multi" do
allow(@yum_cache).to receive(:installed_version).with('cups', nil).and_return('1.2.4-11.18.el5')
allow(@yum_cache).to receive(:installed_version).with('vim', nil).and_return('0.9')
expect(@provider).to receive(:yum_command).with(
- "yum -d0 -e0 -y install cups-1.2.4-11.19.el5 vim-1.0"
+ "-d0 -e0 -y install cups-1.2.4-11.19.el5 vim-1.0"
)
@provider.install_package(["cups", "vim"], ["1.2.4-11.19.el5", '1.0'])
end
@@ -2046,7 +2217,7 @@ describe "Chef::Provider::Package::Yum - Multi" do
allow(@new_resource).to receive(:arch).and_return("i386")
allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1)
expect(@provider).to receive(:yum_command).with(
- "yum -d0 -e0 -y install cups-1.2.4-11.19.el5.i386 vim-1.0.i386"
+ "-d0 -e0 -y install cups-1.2.4-11.19.el5.i386 vim-1.0.i386"
)
@provider.install_package(["cups", "vim"], ["1.2.4-11.19.el5", "1.0"])
end
@@ -2057,10 +2228,36 @@ describe "Chef::Provider::Package::Yum - Multi" do
allow(@yum_cache).to receive(:installed_version).with('cups', nil).and_return('1.2.4-11.18.el5')
allow(@yum_cache).to receive(:installed_version).with('vim', nil).and_return('0.9')
expect(@provider).to receive(:yum_command).with(
- "yum -d0 -e0 -y --disablerepo epmd install cups-1.2.4-11.19.el5 vim-1.0"
+ "-d0 -e0 -y --disablerepo epmd install cups-1.2.4-11.19.el5 vim-1.0"
)
allow(@new_resource).to receive(:options).and_return("--disablerepo epmd")
@provider.install_package(["cups", "vim"], ["1.2.4-11.19.el5", '1.0'])
end
+
+ it "should run yum install with the package name and version when name has arch" do
+ @new_resource = Chef::Resource::Package.new(['cups.x86_64', 'vim'])
+ @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context)
+ allow(Chef::Provider::Package::Yum::RPMUtils).to receive(:rpmvercmp).and_return(-1)
+
+ # Inside of load_current_resource() we'll call parse_arch for cups,
+ # and we need to craft the right response. The default mock setup above
+ # will just return valid versions all the time which won't work for this
+ # test.
+ allow(@yum_cache).to receive(:installed_version).with('cups', 'x86_64').and_return('XXXX')
+ allow(@yum_cache).to receive(:candidate_version).with('cups', 'x86_64').and_return('1.2.4-11.18.el5')
+ allow(@yum_cache).to receive(:installed_version).with('cups.x86_64').and_return(nil)
+ allow(@yum_cache).to receive(:candidate_version).with('cups.x86_64').and_return(nil)
+
+ # Normal mock's for the idempotency check
+ allow(@yum_cache).to receive(:installed_version).with('cups', nil).and_return('1.2.4-11.18.el5')
+ allow(@yum_cache).to receive(:installed_version).with('vim', nil).and_return('0.9')
+
+ @provider.load_current_resource
+ expect(@provider).to receive(:yum_command).with(
+ "-d0 -e0 -y install cups-1.2.4-11.19.el5.x86_64 vim-1.0"
+ )
+ @provider.install_package(["cups", "vim"], ["1.2.4-11.19.el5", '1.0'])
+ end
+
end
end
diff --git a/spec/unit/provider/package/zypper_spec.rb b/spec/unit/provider/package/zypper_spec.rb
index 706ad722dd..18ff739bc6 100644
--- a/spec/unit/provider/package/zypper_spec.rb
+++ b/spec/unit/provider/package/zypper_spec.rb
@@ -19,126 +19,150 @@
require 'spec_helper'
describe Chef::Provider::Package::Zypper do
+ let!(:new_resource) { Chef::Resource::ZypperPackage.new("cups") }
+
+ let!(:current_resource) { Chef::Resource::ZypperPackage.new("cups") }
+
+ let(:provider) do
+ node = Chef::Node.new
+ events = Chef::EventDispatch::Dispatcher.new
+ run_context = Chef::RunContext.new(node, {}, events)
+ Chef::Provider::Package::Zypper.new(new_resource, run_context)
+ end
+
+ let(:status) { double(:stdout => "\n", :exitstatus => 0) }
+
before(:each) do
- @node = Chef::Node.new
- @events = Chef::EventDispatch::Dispatcher.new
- @run_context = Chef::RunContext.new(@node, {}, @events)
- @new_resource = Chef::Resource::Package.new("cups")
-
- @current_resource = Chef::Resource::Package.new("cups")
-
- @provider = Chef::Provider::Package::Zypper.new(@new_resource, @run_context)
- allow(Chef::Resource::Package).to receive(:new).and_return(@current_resource)
- @status = double(:stdout => "\n", :exitstatus => 0)
- allow(@provider).to receive(:shell_out).and_return(@status)
- allow(@provider).to receive(:`).and_return("2.0")
+ allow(Chef::Resource::Package).to receive(:new).and_return(current_resource)
+ allow(provider).to receive(:shell_out).and_return(status)
+ allow(provider).to receive(:`).and_return("2.0")
+ end
+
+ def shell_out_expectation(command, options=nil)
+ options ||= { timeout: 900 }
+ expect(provider).to receive(:shell_out).with(command, options)
+ end
+
+ def shell_out_expectation!(command, options=nil)
+ options ||= { timeout: 900 }
+ expect(provider).to receive(:shell_out!).with(command, options)
end
describe "when loading the current package state" do
it "should create a current resource with the name of the new_resource" do
- expect(Chef::Resource::Package).to receive(:new).and_return(@current_resource)
- @provider.load_current_resource
+ expect(Chef::Resource::Package).to receive(:new).with(new_resource.name).and_return(current_resource)
+ provider.load_current_resource
end
it "should set the current resources package name to the new resources package name" do
- expect(@current_resource).to receive(:package_name).with(@new_resource.package_name)
- @provider.load_current_resource
+ expect(current_resource).to receive(:package_name).with(new_resource.package_name)
+ provider.load_current_resource
end
it "should run zypper info with the package name" do
- expect(@provider).to receive(:shell_out).with("zypper --non-interactive info #{@new_resource.package_name}").and_return(@status)
- @provider.load_current_resource
+ shell_out_expectation(
+ "zypper --non-interactive info #{new_resource.package_name}"
+ ).and_return(status)
+ provider.load_current_resource
end
it "should set the installed version to nil on the current resource if zypper info installed version is (none)" do
- allow(@provider).to receive(:shell_out).and_return(@status)
- expect(@current_resource).to receive(:version).with(nil).and_return(true)
- @provider.load_current_resource
+ allow(provider).to receive(:shell_out).and_return(status)
+ expect(current_resource).to receive(:version).with(nil).and_return(true)
+ provider.load_current_resource
end
it "should set the installed version if zypper info has one" do
status = double(:stdout => "Version: 1.0\nInstalled: Yes\n", :exitstatus => 0)
- allow(@provider).to receive(:shell_out).and_return(status)
- expect(@current_resource).to receive(:version).with("1.0").and_return(true)
- @provider.load_current_resource
+ allow(provider).to receive(:shell_out).and_return(status)
+ expect(current_resource).to receive(:version).with("1.0").and_return(true)
+ provider.load_current_resource
end
it "should set the candidate version if zypper info has one" do
status = double(:stdout => "Version: 1.0\nInstalled: No\nStatus: out-of-date (version 0.9 installed)", :exitstatus => 0)
- allow(@provider).to receive(:shell_out).and_return(status)
- @provider.load_current_resource
- expect(@provider.candidate_version).to eql("1.0")
+ allow(provider).to receive(:shell_out).and_return(status)
+ provider.load_current_resource
+ expect(provider.candidate_version).to eql("1.0")
end
it "should raise an exception if zypper info fails" do
- expect(@status).to receive(:exitstatus).and_return(1)
- expect { @provider.load_current_resource }.to raise_error(Chef::Exceptions::Package)
+ expect(status).to receive(:exitstatus).and_return(1)
+ expect { provider.load_current_resource }.to raise_error(Chef::Exceptions::Package)
end
it "should not raise an exception if zypper info succeeds" do
- expect(@status).to receive(:exitstatus).and_return(0)
- expect { @provider.load_current_resource }.not_to raise_error
+ expect(status).to receive(:exitstatus).and_return(0)
+ expect { provider.load_current_resource }.not_to raise_error
end
it "should return the current resouce" do
- expect(@provider.load_current_resource).to eql(@current_resource)
+ expect(provider.load_current_resource).to eql(current_resource)
end
end
describe "install_package" do
it "should run zypper install with the package name and version" do
allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(true)
- expect(@provider).to receive(:shell_out!).with(
- "zypper --non-interactive install --auto-agree-with-licenses emacs=1.0")
- @provider.install_package("emacs", "1.0")
+ shell_out_expectation!(
+ "zypper --non-interactive install --auto-agree-with-licenses emacs=1.0"
+ )
+ provider.install_package("emacs", "1.0")
end
it "should run zypper install without gpg checks" do
allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(false)
- expect(@provider).to receive(:shell_out!).with(
+ shell_out_expectation!(
"zypper --non-interactive --no-gpg-checks install "+
- "--auto-agree-with-licenses emacs=1.0")
- @provider.install_package("emacs", "1.0")
+ "--auto-agree-with-licenses emacs=1.0"
+ )
+ provider.install_package("emacs", "1.0")
end
it "should warn about gpg checks on zypper install" do
expect(Chef::Log).to receive(:warn).with(
- /All packages will be installed without gpg signature checks/)
- expect(@provider).to receive(:shell_out!).with(
+ /All packages will be installed without gpg signature checks/
+ )
+ shell_out_expectation!(
"zypper --non-interactive --no-gpg-checks install "+
- "--auto-agree-with-licenses emacs=1.0")
- @provider.install_package("emacs", "1.0")
+ "--auto-agree-with-licenses emacs=1.0"
+ )
+ provider.install_package("emacs", "1.0")
end
end
describe "upgrade_package" do
it "should run zypper update with the package name and version" do
allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(true)
- expect(@provider).to receive(:shell_out!).with(
- "zypper --non-interactive install --auto-agree-with-licenses emacs=1.0")
- @provider.upgrade_package("emacs", "1.0")
+ shell_out_expectation!(
+ "zypper --non-interactive install --auto-agree-with-licenses emacs=1.0"
+ )
+ provider.upgrade_package("emacs", "1.0")
end
it "should run zypper update without gpg checks" do
allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(false)
- expect(@provider).to receive(:shell_out!).with(
+ shell_out_expectation!(
"zypper --non-interactive --no-gpg-checks install "+
- "--auto-agree-with-licenses emacs=1.0")
- @provider.upgrade_package("emacs", "1.0")
+ "--auto-agree-with-licenses emacs=1.0"
+ )
+ provider.upgrade_package("emacs", "1.0")
end
it "should warn about gpg checks on zypper upgrade" do
expect(Chef::Log).to receive(:warn).with(
- /All packages will be installed without gpg signature checks/)
- expect(@provider).to receive(:shell_out!).with(
+ /All packages will be installed without gpg signature checks/
+ )
+ shell_out_expectation!(
"zypper --non-interactive --no-gpg-checks install "+
- "--auto-agree-with-licenses emacs=1.0")
- @provider.upgrade_package("emacs", "1.0")
+ "--auto-agree-with-licenses emacs=1.0"
+ )
+ provider.upgrade_package("emacs", "1.0")
end
it "should run zypper upgrade without gpg checks" do
- expect(@provider).to receive(:shell_out!).with(
+ shell_out_expectation!(
"zypper --non-interactive --no-gpg-checks install "+
- "--auto-agree-with-licenses emacs=1.0")
-
- @provider.upgrade_package("emacs", "1.0")
+ "--auto-agree-with-licenses emacs=1.0"
+ )
+ provider.upgrade_package("emacs", "1.0")
end
end
@@ -147,83 +171,94 @@ describe Chef::Provider::Package::Zypper do
context "when package version is not explicitly specified" do
it "should run zypper remove with the package name" do
allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(true)
- expect(@provider).to receive(:shell_out!).with(
- "zypper --non-interactive remove emacs")
- @provider.remove_package("emacs", nil)
+ shell_out_expectation!(
+ "zypper --non-interactive remove emacs"
+ )
+ provider.remove_package("emacs", nil)
end
end
context "when package version is explicitly specified" do
it "should run zypper remove with the package name" do
allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(true)
- expect(@provider).to receive(:shell_out!).with(
- "zypper --non-interactive remove emacs=1.0")
- @provider.remove_package("emacs", "1.0")
+ shell_out_expectation!(
+ "zypper --non-interactive remove emacs=1.0"
+ )
+ provider.remove_package("emacs", "1.0")
end
it "should run zypper remove without gpg checks" do
allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(false)
- expect(@provider).to receive(:shell_out!).with(
- "zypper --non-interactive --no-gpg-checks remove emacs=1.0")
- @provider.remove_package("emacs", "1.0")
+ shell_out_expectation!(
+ "zypper --non-interactive --no-gpg-checks remove emacs=1.0"
+ )
+ provider.remove_package("emacs", "1.0")
end
it "should warn about gpg checks on zypper remove" do
expect(Chef::Log).to receive(:warn).with(
- /All packages will be installed without gpg signature checks/)
- expect(@provider).to receive(:shell_out!).with(
- "zypper --non-interactive --no-gpg-checks remove emacs=1.0")
-
- @provider.remove_package("emacs", "1.0")
+ /All packages will be installed without gpg signature checks/
+ )
+ shell_out_expectation!(
+ "zypper --non-interactive --no-gpg-checks remove emacs=1.0"
+ )
+ provider.remove_package("emacs", "1.0")
end
end
end
describe "purge_package" do
it "should run remove_package with the name and version" do
- expect(@provider).to receive(:shell_out!).with(
- "zypper --non-interactive --no-gpg-checks remove --clean-deps emacs=1.0")
- @provider.purge_package("emacs", "1.0")
+ shell_out_expectation!(
+ "zypper --non-interactive --no-gpg-checks remove --clean-deps emacs=1.0"
+ )
+ provider.purge_package("emacs", "1.0")
end
it "should run zypper purge without gpg checks" do
allow(Chef::Config).to receive(:[]).with(:zypper_check_gpg).and_return(false)
- expect(@provider).to receive(:shell_out!).with(
- "zypper --non-interactive --no-gpg-checks remove --clean-deps emacs=1.0")
- @provider.purge_package("emacs", "1.0")
+ shell_out_expectation!(
+ "zypper --non-interactive --no-gpg-checks remove --clean-deps emacs=1.0"
+ )
+ provider.purge_package("emacs", "1.0")
end
it "should warn about gpg checks on zypper purge" do
expect(Chef::Log).to receive(:warn).with(
- /All packages will be installed without gpg signature checks/)
- expect(@provider).to receive(:shell_out!).with(
- "zypper --non-interactive --no-gpg-checks remove --clean-deps emacs=1.0")
- @provider.purge_package("emacs", "1.0")
+ /All packages will be installed without gpg signature checks/
+ )
+ shell_out_expectation!(
+ "zypper --non-interactive --no-gpg-checks remove --clean-deps emacs=1.0"
+ )
+ provider.purge_package("emacs", "1.0")
end
end
describe "on an older zypper" do
before(:each) do
- allow(@provider).to receive(:`).and_return("0.11.6")
+ allow(provider).to receive(:`).and_return("0.11.6")
end
describe "install_package" do
it "should run zypper install with the package name and version" do
- expect(@provider).to receive(:shell_out!).with(
- "zypper --no-gpg-checks install --auto-agree-with-licenses -y emacs")
- @provider.install_package("emacs", "1.0")
+ shell_out_expectation!(
+ "zypper --no-gpg-checks install --auto-agree-with-licenses -y emacs"
+ )
+ provider.install_package("emacs", "1.0")
end
end
describe "upgrade_package" do
it "should run zypper update with the package name and version" do
- expect(@provider).to receive(:shell_out!).with(
- "zypper --no-gpg-checks install --auto-agree-with-licenses -y emacs")
- @provider.upgrade_package("emacs", "1.0")
+ shell_out_expectation!(
+ "zypper --no-gpg-checks install --auto-agree-with-licenses -y emacs"
+ )
+ provider.upgrade_package("emacs", "1.0")
end
end
describe "remove_package" do
it "should run zypper remove with the package name" do
- expect(@provider).to receive(:shell_out!).with(
- "zypper --no-gpg-checks remove -y emacs")
- @provider.remove_package("emacs", "1.0")
+ shell_out_expectation!(
+ "zypper --no-gpg-checks remove -y emacs"
+ )
+ provider.remove_package("emacs", "1.0")
end
end
end
diff --git a/spec/unit/provider/package_spec.rb b/spec/unit/provider/package_spec.rb
index 1633d18f9d..432d968906 100644
--- a/spec/unit/provider/package_spec.rb
+++ b/spec/unit/provider/package_spec.rb
@@ -37,6 +37,12 @@ describe Chef::Provider::Package do
allow(@provider).to receive(:install_package).and_return(true)
end
+ it "raises a Chef::Exceptions::InvalidResourceSpecification if both multipackage and source are provided" do
+ @new_resource.package_name(['a', 'b'])
+ @new_resource.source('foo')
+ expect { @provider.run_action(:install) }.to raise_error(Chef::Exceptions::InvalidResourceSpecification)
+ end
+
it "should raise a Chef::Exceptions::Package if no version is specified, and no candidate is available" do
@provider.candidate_version = nil
expect { @provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package)
@@ -698,4 +704,38 @@ describe "Chef::Provider::Package - Multi" do
expect(@new_resource).not_to be_updated_by_last_action
end
end
+
+ describe "shell_out helpers" do
+ [ :shell_out_with_timeout, :shell_out_with_timeout! ].each do |method|
+ stubbed_method = method == :shell_out_with_timeout! ? :shell_out! : :shell_out
+ [ %w{command arg1 arg2}, "command arg1 arg2" ].each do |command|
+ it "#{method} defaults to 900 seconds" do
+ expect(@provider).to receive(stubbed_method).with(*command, timeout: 900)
+ @provider.send(method, *command)
+ end
+ it "#{method} overrides the default timeout with its options" do
+ expect(@provider).to receive(stubbed_method).with(*command, timeout: 1)
+ @provider.send(method, *command, timeout: 1)
+ end
+ it "#{method} overrides both timeouts with the new_resource.timeout" do
+ @new_resource.timeout(99)
+ expect(@provider).to receive(stubbed_method).with(*command, timeout: 99)
+ @provider.send(method, *command, timeout: 1)
+ end
+ it "#{method} defaults to 900 seconds and preserves options" do
+ expect(@provider).to receive(stubbed_method).with(*command, env: nil, timeout: 900)
+ @provider.send(method, *command, env: nil)
+ end
+ it "#{method} overrides the default timeout with its options and preserves options" do
+ expect(@provider).to receive(stubbed_method).with(*command, timeout: 1, env: nil)
+ @provider.send(method, *command, timeout: 1, env: nil)
+ end
+ it "#{method} overrides both timeouts with the new_resource.timeout and preseves options" do
+ @new_resource.timeout(99)
+ expect(@provider).to receive(stubbed_method).with(*command, timeout: 99, env: nil)
+ @provider.send(method, *command, timeout: 1, env: nil)
+ end
+ end
+ end
+ end
end
diff --git a/spec/unit/provider/powershell_script_spec.rb b/spec/unit/provider/powershell_script_spec.rb
new file mode 100644
index 0000000000..855c18af9b
--- /dev/null
+++ b/spec/unit/provider/powershell_script_spec.rb
@@ -0,0 +1,80 @@
+#
+# Author:: Adam Edwards (<adamed@opscode.com>)
+# Copyright:: Copyright (c) 2013 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+describe Chef::Provider::PowershellScript, "action_run" do
+
+ let(:powershell_version) { nil }
+ let(:node) {
+ node = Chef::Node.new
+ node.default["kernel"] = Hash.new
+ node.default["kernel"][:machine] = :x86_64.to_s
+ if ! powershell_version.nil?
+ node.default[:languages] = { :powershell => { :version => powershell_version } }
+ end
+ node
+ }
+
+ let(:provider) {
+ empty_events = Chef::EventDispatch::Dispatcher.new
+ run_context = Chef::RunContext.new(node, {}, empty_events)
+ new_resource = Chef::Resource::PowershellScript.new('run some powershell code', run_context)
+ Chef::Provider::PowershellScript.new(new_resource, run_context)
+ }
+
+ context 'when setting interpreter flags' do
+ it "should set the -File flag as the last flag" do
+ expect(provider.flags.split(' ').pop).to eq("-File")
+ end
+
+ let(:execution_policy_flag) do
+ execution_policy_index = 0
+ provider_flags = provider.flags.split(' ')
+ execution_policy_specified = false
+
+ provider_flags.find do | value |
+ execution_policy_index += 1
+ execution_policy_specified = value.downcase == '-ExecutionPolicy'.downcase
+ end
+
+ execution_policy = execution_policy_specified ? provider_flags[execution_policy_index] : nil
+ end
+
+ context 'when running with an unspecified PowerShell version' do
+ let(:powershell_version) { nil }
+ it "should set the -ExecutionPolicy flag to 'Unrestricted' by default" do
+ expect(execution_policy_flag.downcase).to eq('unrestricted'.downcase)
+ end
+ end
+
+ { '2.0' => 'Unrestricted',
+ '2.5' => 'Unrestricted',
+ '3.0' => 'Bypass',
+ '3.6' => 'Bypass',
+ '4.0' => 'Bypass',
+ '5.0' => 'Bypass' }.each do | version_policy |
+ let(:powershell_version) { version_policy[0].to_f }
+ context "when running PowerShell version #{version_policy[0]}" do
+ let(:powershell_version) { version_policy[0].to_f }
+ it "should set the -ExecutionPolicy flag to '#{version_policy[1]}'" do
+ expect(execution_policy_flag.downcase).to eq(version_policy[1].downcase)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/unit/provider/registry_key_spec.rb b/spec/unit/provider/registry_key_spec.rb
index 79811fdab8..47543ffe39 100644
--- a/spec/unit/provider/registry_key_spec.rb
+++ b/spec/unit/provider/registry_key_spec.rb
@@ -77,6 +77,18 @@ shared_examples_for "a registry key" do
end
describe "action_create" do
+ context "when a case insensitive match for the key exists" do
+ before(:each) do
+ expect(@double_registry).to receive(:key_exists?).twice.with(keyname.downcase).and_return(true)
+ end
+ it "should do nothing if the if a case insensitive key and the value both exist" do
+ @provider.new_resource.key(keyname.downcase)
+ expect(@double_registry).to receive(:get_values).with(keyname.downcase).and_return( testval1 )
+ expect(@double_registry).not_to receive(:set_value)
+ @provider.load_current_resource
+ @provider.action_create
+ end
+ end
context "when the key exists" do
before(:each) do
expect(@double_registry).to receive(:key_exists?).twice.with(keyname).and_return(true)
diff --git a/spec/unit/provider/remote_directory_spec.rb b/spec/unit/provider/remote_directory_spec.rb
index 4434714ebc..99e2fe285c 100644
--- a/spec/unit/provider/remote_directory_spec.rb
+++ b/spec/unit/provider/remote_directory_spec.rb
@@ -194,8 +194,8 @@ describe Chef::Provider::RemoteDirectory do
expect(::File.exist?(symlinked_dir_path)).to be_falsey
expect(::File.exist?(tmp_dir)).to be_truthy
- rescue Chef::Exceptions::Win32APIError => e
- pending "This must be run as an Administrator to create symlinks"
+ rescue Chef::Exceptions::Win32APIError
+ skip "This must be run as an Administrator to create symlinks"
end
end
end
diff --git a/spec/unit/provider/remote_file/fetcher_spec.rb b/spec/unit/provider/remote_file/fetcher_spec.rb
index c049848fbf..8bd3b7c625 100644
--- a/spec/unit/provider/remote_file/fetcher_spec.rb
+++ b/spec/unit/provider/remote_file/fetcher_spec.rb
@@ -24,6 +24,26 @@ describe Chef::Provider::RemoteFile::Fetcher do
let(:new_resource) { double("new resource") }
let(:fetcher_instance) { double("fetcher") }
+ describe "when passed a network share" do
+ before do
+ expect(Chef::Provider::RemoteFile::NetworkFile).to receive(:new).and_return(fetcher_instance)
+ end
+
+ context "when host is a name" do
+ let(:source) { "\\\\foohost\\fooshare\\Foo.tar.gz" }
+ it "returns a network file fetcher" do
+ expect(described_class.for_resource(source, new_resource, current_resource)).to eq(fetcher_instance)
+ end
+ end
+
+ context "when host is an ip" do
+ let(:source) { "\\\\127.0.0.1\\fooshare\\Foo.tar.gz" }
+ it "returns a network file fetcher" do
+ expect(described_class.for_resource(source, new_resource, current_resource)).to eq(fetcher_instance)
+ end
+ end
+ end
+
describe "when passed an http url" do
let(:uri) { double("uri", :scheme => "http" ) }
before do
@@ -72,4 +92,3 @@ describe Chef::Provider::RemoteFile::Fetcher do
end
end
-
diff --git a/spec/unit/provider/remote_file/local_file_spec.rb b/spec/unit/provider/remote_file/local_file_spec.rb
index b33d82f624..575996a540 100644
--- a/spec/unit/provider/remote_file/local_file_spec.rb
+++ b/spec/unit/provider/remote_file/local_file_spec.rb
@@ -25,26 +25,45 @@ describe Chef::Provider::RemoteFile::LocalFile do
let(:new_resource) { Chef::Resource::RemoteFile.new("local file backend test (new_resource)") }
let(:current_resource) { Chef::Resource::RemoteFile.new("local file backend test (current_resource)") }
subject(:fetcher) { Chef::Provider::RemoteFile::LocalFile.new(uri, new_resource, current_resource) }
-
- context "when parsing source path" do
+
+ context "when parsing source path on windows" do
+
+ before do
+ allow(Chef::Platform).to receive(:windows?).and_return(true)
+ end
+
describe "when given local unix path" do
let(:uri) { URI.parse("file:///nyan_cat.png") }
it "returns a correct unix path" do
- expect(fetcher.fix_windows_path(uri.path)).to eq("/nyan_cat.png")
+ expect(fetcher.source_path).to eq("/nyan_cat.png")
end
end
describe "when given local windows path" do
let(:uri) { URI.parse("file:///z:/windows/path/file.txt") }
it "returns a valid windows local path" do
- expect(fetcher.fix_windows_path(uri.path)).to eq("z:/windows/path/file.txt")
+ expect(fetcher.source_path).to eq("z:/windows/path/file.txt")
+ end
+ end
+
+ describe "when given local windows path with spaces" do
+ let(:uri) { URI.parse(URI.escape("file:///z:/windows/path/foo & bar.txt")) }
+ it "returns a valid windows local path" do
+ expect(fetcher.source_path).to eq("z:/windows/path/foo & bar.txt")
end
end
describe "when given unc windows path" do
let(:uri) { URI.parse("file:////server/share/windows/path/file.txt") }
it "returns a valid windows unc path" do
- expect(fetcher.fix_windows_path(uri.path)).to eq("//server/share/windows/path/file.txt")
+ expect(fetcher.source_path).to eq("//server/share/windows/path/file.txt")
+ end
+ end
+
+ describe "when given unc windows path with spaces" do
+ let(:uri) { URI.parse(URI.escape("file:////server/share/windows/path/foo & bar.txt")) }
+ it "returns a valid windows unc path" do
+ expect(fetcher.source_path).to eq("//server/share/windows/path/foo & bar.txt")
end
end
end
@@ -73,7 +92,7 @@ describe Chef::Provider::RemoteFile::LocalFile do
it "stages the local file to a temporary file" do
expect(Chef::FileContentManagement::Tempfile).to receive(:new).with(new_resource).and_return(chef_tempfile)
expect(::FileUtils).to receive(:cp).with(uri.path, tempfile.path)
- expect(tempfile).to receive(:close)
+ expect(tempfile).to receive(:close)
result = fetcher.fetch
expect(result).to eq(tempfile)
diff --git a/spec/unit/provider/remote_file/network_file_spec.rb b/spec/unit/provider/remote_file/network_file_spec.rb
new file mode 100644
index 0000000000..3666a47468
--- /dev/null
+++ b/spec/unit/provider/remote_file/network_file_spec.rb
@@ -0,0 +1,45 @@
+#
+# Author:: Jay Mundrawala (<jdm@chef.io>)
+# Copyright:: Copyright (c) 2015 Chef Software
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Provider::RemoteFile::NetworkFile do
+
+ let(:source) { "\\\\foohost\\fooshare\\Foo.tar.gz" }
+
+ let(:new_resource) { Chef::Resource::RemoteFile.new("network file (new_resource)") }
+ let(:current_resource) { Chef::Resource::RemoteFile.new("network file (current_resource)") }
+ subject(:fetcher) { Chef::Provider::RemoteFile::NetworkFile.new(source, new_resource, current_resource) }
+
+ describe "when fetching the object" do
+
+ let(:tempfile) { double("Tempfile", :path => "/tmp/foo/bar/Foo.tar.gz", :close => nil) }
+ let(:chef_tempfile) { double("Chef::FileContentManagement::Tempfile", :tempfile => tempfile) }
+
+ it "stages the local file to a temporary file" do
+ expect(Chef::FileContentManagement::Tempfile).to receive(:new).with(new_resource).and_return(chef_tempfile)
+ expect(::FileUtils).to receive(:cp).with(source, tempfile.path)
+ expect(tempfile).to receive(:close)
+
+ result = fetcher.fetch
+ expect(result).to eq(tempfile)
+ end
+
+ end
+
+end
diff --git a/spec/unit/provider/service/aix_service_spec.rb b/spec/unit/provider/service/aix_service_spec.rb
index 796661145b..5cca7d6f0a 100644
--- a/spec/unit/provider/service/aix_service_spec.rb
+++ b/spec/unit/provider/service/aix_service_spec.rb
@@ -51,22 +51,35 @@ describe Chef::Provider::Service::Aix do
end
it "current resource is running" do
- expect(@provider).to receive(:shell_out!).with("lssrc -a | grep -w chef").and_return(@status)
- expect(@provider).to receive(:is_resource_group?).with(["chef chef 12345 active"])
+ expect(@provider).to receive(:shell_out!).with("lssrc -s chef").and_return(@status)
+ expect(@provider).to receive(:is_resource_group?).and_return false
@provider.load_current_resource
expect(@current_resource.running).to be_truthy
end
end
- context "when the service is inoprative" do
+ context "when the service is inoperative" do
before do
@status = double("Status", :exitstatus => 0, :stdout => "chef chef inoperative\n")
end
it "current resource is not running" do
- expect(@provider).to receive(:shell_out!).with("lssrc -a | grep -w chef").and_return(@status)
- expect(@provider).to receive(:is_resource_group?).with(["chef chef inoperative"])
+ expect(@provider).to receive(:shell_out!).with("lssrc -s chef").and_return(@status)
+ expect(@provider).to receive(:is_resource_group?).and_return false
+
+ @provider.load_current_resource
+ expect(@current_resource.running).to be_falsey
+ end
+ end
+
+ context "when there is no such service" do
+ before do
+ @status = double("Status", :exitstatus => 1, :stdout => "0513-085 The chef Subsystem is not on file.\n")
+ end
+ it "current resource is not running" do
+ expect(@provider).to receive(:shell_out!).with("lssrc -s chef").and_return(@status)
+ expect(@provider).to receive(:is_resource_group?).and_return false
@provider.load_current_resource
expect(@current_resource.running).to be_falsey
@@ -75,13 +88,13 @@ describe Chef::Provider::Service::Aix do
end
describe "is resource group" do
- context "when there are mutiple subsystems associated with group" do
+ context "when there are multiple subsystems associated with group" do
before do
@status = double("Status", :exitstatus => 0, :stdout => "chef1 chef 12345 active\nchef2 chef 12334 active\nchef3 chef inoperative")
end
it "service is a group" do
- expect(@provider).to receive(:shell_out!).with("lssrc -a | grep -w chef").and_return(@status)
+ expect(@provider).to receive(:shell_out).with("lssrc -g chef").and_return(@status)
@provider.load_current_resource
expect(@provider.instance_eval("@is_resource_group")).to be_truthy
end
@@ -93,19 +106,21 @@ describe Chef::Provider::Service::Aix do
end
it "service is a group" do
- expect(@provider).to receive(:shell_out!).with("lssrc -a | grep -w chef").and_return(@status)
+ expect(@provider).to receive(:shell_out).with("lssrc -g chef").and_return(@status)
@provider.load_current_resource
expect(@provider.instance_eval("@is_resource_group")).to be_truthy
end
end
- context "when there service is a subsytem" do
+ context "when the service is a subsystem" do
before do
- @status = double("Status", :exitstatus => 0, :stdout => "chef chef123 inoperative\n")
+ @group_status = double("Status", :exitstatus => 1, :stdout => "0513-086 The chef Group is not on file.\n")
+ @service_status = double("Status", :exitstatus => 0, :stdout => "chef chef inoperative\n")
end
it "service is a subsystem" do
- expect(@provider).to receive(:shell_out!).with("lssrc -a | grep -w chef").and_return(@status)
+ expect(@provider).to receive(:shell_out).with("lssrc -g chef").and_return(@group_status)
+ expect(@provider).to receive(:shell_out!).with("lssrc -s chef").and_return(@service_status)
@provider.load_current_resource
expect(@provider.instance_eval("@is_resource_group")).to be_falsey
end
diff --git a/spec/unit/provider/service/freebsd_service_spec.rb b/spec/unit/provider/service/freebsd_service_spec.rb
index 5a55425d87..cfc28c94d5 100644
--- a/spec/unit/provider/service/freebsd_service_spec.rb
+++ b/spec/unit/provider/service/freebsd_service_spec.rb
@@ -189,18 +189,6 @@ PS_SAMPLE
expect(provider.status_load_success).to be_nil
end
- context "when ps command is nil" do
- before do
- node.automatic_attrs[:command] = {:ps => nil}
- end
-
- it "should set running to nil" do
- pending "superclass raises no conversion of nil to string which seems broken"
- provider.determine_current_status!
- expect(current_resource.running).to be_nil
- end
- end
-
context "when ps is empty string" do
before do
node.automatic_attrs[:command] = {:ps => ""}
diff --git a/spec/unit/provider/service/gentoo_service_spec.rb b/spec/unit/provider/service/gentoo_service_spec.rb
index c08982acc3..0aa7bf4529 100644
--- a/spec/unit/provider/service/gentoo_service_spec.rb
+++ b/spec/unit/provider/service/gentoo_service_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: Lee Jensen (<ljensen@engineyard.com>)
# Author:: AJ Christensen (<aj@opscode.com>)
-# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# Copyright:: Copyright (c) 2008-2015 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -108,17 +108,17 @@ describe Chef::Provider::Service::Gentoo do
it "should support the status command automatically" do
@provider.load_current_resource
- expect(@new_resource.supports[:status]).to be_truthy
+ expect(@provider.supports[:status]).to be true
end
it "should support the restart command automatically" do
@provider.load_current_resource
- expect(@new_resource.supports[:restart]).to be_truthy
+ expect(@provider.supports[:restart]).to be true
end
it "should not support the reload command automatically" do
@provider.load_current_resource
- expect(@new_resource.supports[:reload]).not_to be_truthy
+ expect(@provider.supports[:reload]).to be_falsey
end
end
diff --git a/spec/unit/provider/service/macosx_spec.rb b/spec/unit/provider/service/macosx_spec.rb
index fb751592df..54183bdc3d 100644
--- a/spec/unit/provider/service/macosx_spec.rb
+++ b/spec/unit/provider/service/macosx_spec.rb
@@ -22,17 +22,17 @@ describe Chef::Provider::Service::Macosx do
describe ".gather_plist_dirs" do
context "when HOME directory is set" do
before do
- allow(ENV).to receive(:[]).with('HOME').and_return("/User/someuser")
+ allow(Chef::Util::PathHelper).to receive(:home).with('Library', 'LaunchAgents').and_yield('/Users/someuser/Library/LaunchAgents')
end
it "includes users's LaunchAgents folder" do
- expect(described_class.gather_plist_dirs).to include("#{ENV['HOME']}/Library/LaunchAgents")
+ expect(described_class.gather_plist_dirs).to include("/Users/someuser/Library/LaunchAgents")
end
end
context "when HOME directory is not set" do
before do
- allow(ENV).to receive(:[]).with('HOME').and_return(nil)
+ allow(Chef::Util::PathHelper).to receive(:home).with('Library', 'LaunchAgents').and_return(nil)
end
it "doesn't include user's LaunchAgents folder" do
@@ -58,248 +58,275 @@ describe Chef::Provider::Service::Macosx do
</plist>
XML
- ["redis-server", "io.redis.redis-server"].each do |service_name|
- before do
- allow(Dir).to receive(:glob).and_return(["/Users/igor/Library/LaunchAgents/io.redis.redis-server.plist"], [])
- allow(provider).to receive(:shell_out!).
- with("launchctl list", {:group => 1001, :user => 101}).
- and_return(double("Status", :stdout => launchctl_stdout))
- allow(provider).to receive(:shell_out).
- with(/launchctl list /,
- {:group => nil, :user => nil}).
- and_return(double("Status",
- :stdout => launchctl_stdout, :exitstatus => 0))
- allow(provider).to receive(:shell_out!).
- with(/plutil -convert xml1 -o/).
- and_return(double("Status", :stdout => plutil_stdout))
-
- allow(File).to receive(:stat).and_return(double("stat", :gid => 1001, :uid => 101))
- end
-
- context "#{service_name}" do
- let(:new_resource) { Chef::Resource::Service.new(service_name) }
- let!(:current_resource) { Chef::Resource::Service.new(service_name) }
-
- describe "#load_current_resource" do
-
- # CHEF-5223 "you can't glob for a file that hasn't been converged
- # onto the node yet."
- context "when the plist doesn't exist" do
-
- def run_resource_setup_for_action(action)
- new_resource.action(action)
- provider.action = action
- provider.load_current_resource
- provider.define_resource_requirements
- provider.process_resource_requirements
- end
-
- before do
- allow(Dir).to receive(:glob).and_return([])
- allow(provider).to receive(:shell_out!).
- with(/plutil -convert xml1 -o/).
- and_raise(Mixlib::ShellOut::ShellCommandFailed)
- end
-
- it "works for action :nothing" do
- expect { run_resource_setup_for_action(:nothing) }.not_to raise_error
- end
-
- it "works for action :start" do
- expect { run_resource_setup_for_action(:start) }.not_to raise_error
- end
-
- it "errors if action is :enable" do
- expect { run_resource_setup_for_action(:enable) }.to raise_error(Chef::Exceptions::Service)
- end
-
- it "errors if action is :disable" do
- expect { run_resource_setup_for_action(:disable) }.to raise_error(Chef::Exceptions::Service)
+ ["Daemon", "Agent"].each do |service_type|
+ ["redis-server", "io.redis.redis-server"].each do |service_name|
+ ["10.9", "10.10", "10.11"].each do |platform_version|
+ let(:plist) {'/Library/LaunchDaemons/io.redis.redis-server.plist'}
+ let(:session) { StringIO.new }
+ if service_type == 'Agent'
+ let(:plist) {'/Library/LaunchAgents/io.redis.redis-server.plist'}
+ let(:session) {'-S Aqua '}
+ let(:su_cmd) {'su -l igor -c'}
+ if platform_version == "10.9"
+ let(:su_cmd) {'su igor -c'}
end
end
-
- context "when launchctl returns pid in service list" do
- let(:launchctl_stdout) { StringIO.new <<-SVC_LIST }
- 12761 - 0x100114220.old.machinit.thing
- 7777 - io.redis.redis-server
- - - com.lol.stopped-thing
- SVC_LIST
-
- before do
- provider.load_current_resource
- end
-
- it "sets resource running state to true" do
- expect(provider.current_resource.running).to be_truthy
- end
-
- it "sets resouce enabled state to true" do
- expect(provider.current_resource.enabled).to be_truthy
- end
+ let(:service_label) {'io.redis.redis-server'}
+ before do
+ allow(Dir).to receive(:glob).and_return([plist], [])
+ allow(Etc).to receive(:getlogin).and_return('igor')
+ allow(node).to receive(:[]).with("platform_version").and_return(platform_version)
+ cmd = "launchctl list #{service_label}"
+ allow(provider).to receive(:shell_out_with_systems_locale).
+ with(/(#{su_cmd} '#{cmd}'|#{cmd})/).
+ and_return(double("Status",
+ :stdout => launchctl_stdout, :exitstatus => 0))
+ allow(File).to receive(:exists?).and_return([true], [])
+ allow(provider).to receive(:shell_out_with_systems_locale!).
+ with(/plutil -convert xml1 -o/).
+ and_return(double("Status", :stdout => plutil_stdout))
end
- describe "running unsupported actions" do
- let(:launchctl_stdout) { StringIO.new <<-SVC_LIST }
-12761 - 0x100114220.old.machinit.thing
-7777 - io.redis.redis-server
-- - com.lol.stopped-thing
+ context "#{service_name} that is a #{service_type} running Osx #{platform_version}" do
+ let(:new_resource) { Chef::Resource::MacosxService.new(service_name) }
+ let!(:current_resource) { Chef::Resource::MacosxService.new(service_name) }
+
+ describe "#load_current_resource" do
+
+ # CHEF-5223 "you can't glob for a file that hasn't been converged
+ # onto the node yet."
+ context "when the plist doesn't exist" do
+
+ def run_resource_setup_for_action(action)
+ new_resource.action(action)
+ provider.action = action
+ provider.load_current_resource
+ provider.define_resource_requirements
+ provider.process_resource_requirements
+ end
+
+ before do
+ allow(Dir).to receive(:glob).and_return([])
+ allow(File).to receive(:exists?).and_return([true], [])
+ allow(provider).to receive(:shell_out!).
+ with(/plutil -convert xml1 -o/).
+ and_raise(Mixlib::ShellOut::ShellCommandFailed)
+ end
+
+ it "works for action :nothing" do
+ expect { run_resource_setup_for_action(:nothing) }.not_to raise_error
+ end
+
+ it "works for action :start" do
+ expect { run_resource_setup_for_action(:start) }.not_to raise_error
+ end
+
+ it "errors if action is :enable" do
+ expect { run_resource_setup_for_action(:enable) }.to raise_error(Chef::Exceptions::Service)
+ end
+
+ it "errors if action is :disable" do
+ expect { run_resource_setup_for_action(:disable) }.to raise_error(Chef::Exceptions::Service)
+ end
+ end
+
+ context "when launchctl returns pid in service list" do
+ let(:launchctl_stdout) { StringIO.new <<-SVC_LIST }
+{
+ "LimitLoadToSessionType" = "System";
+ "Label" = "io.redis.redis-server";
+ "TimeOut" = 30;
+ "OnDemand" = false;
+ "LastExitStatus" = 0;
+ "PID" = 62803;
+ "Program" = "do_some.sh";
+ "ProgramArguments" = (
+ "path/to/do_something.sh";
+ "-f";
+ );
+};
SVC_LIST
- before do
- allow(Dir).to receive(:glob).and_return(["/Users/igor/Library/LaunchAgents/io.redis.redis-server.plist"], [])
- end
- it "should throw an exception when reload action is attempted" do
- expect {provider.run_action(:reload)}.to raise_error(Chef::Exceptions::UnsupportedAction)
- end
- end
- context "when launchctl returns empty service pid" do
- let(:launchctl_stdout) { StringIO.new <<-SVC_LIST }
- 12761 - 0x100114220.old.machinit.thing
- - - io.redis.redis-server
- - - com.lol.stopped-thing
- SVC_LIST
-
- before do
- provider.load_current_resource
- end
+ before do
+ provider.load_current_resource
+ end
- it "sets resource running state to false" do
- expect(provider.current_resource.running).to be_falsey
- end
+ it "sets resource running state to true" do
+ expect(provider.current_resource.running).to be_truthy
+ end
- it "sets resouce enabled state to true" do
- expect(provider.current_resource.enabled).to be_truthy
- end
- end
+ it "sets resouce enabled state to true" do
+ expect(provider.current_resource.enabled).to be_truthy
+ end
+ end
- context "when launchctl doesn't return service entry at all" do
- let(:launchctl_stdout) { StringIO.new <<-SVC_LIST }
- 12761 - 0x100114220.old.machinit.thing
- - - com.lol.stopped-thing
- SVC_LIST
+ describe "running unsupported actions" do
+ before do
+ allow(Dir).to receive(:glob).and_return(["#{plist}"], [])
+ allow(File).to receive(:exists?).and_return([true], [])
+ end
+ it "should throw an exception when reload action is attempted" do
+ expect {provider.run_action(:reload)}.to raise_error(Chef::Exceptions::UnsupportedAction)
+ end
+ end
+ context "when launchctl returns empty service pid" do
+ let(:launchctl_stdout) { StringIO.new <<-SVC_LIST }
+{
+ "LimitLoadToSessionType" = "System";
+ "Label" = "io.redis.redis-server";
+ "TimeOut" = 30;
+ "OnDemand" = false;
+ "LastExitStatus" = 0;
+ "Program" = "do_some.sh";
+ "ProgramArguments" = (
+ "path/to/do_something.sh";
+ "-f";
+ );
+};
+SVC_LIST
- it "sets service running state to false" do
- provider.load_current_resource
- expect(provider.current_resource.running).to be_falsey
- end
+ before do
+ provider.load_current_resource
+ end
- context "and plist for service is not available" do
- before do
- allow(Dir).to receive(:glob).and_return([])
- provider.load_current_resource
- end
+ it "sets resource running state to false" do
+ expect(provider.current_resource.running).to be_falsey
+ end
- it "sets resouce enabled state to false" do
- expect(provider.current_resource.enabled).to be_falsey
+ it "sets resouce enabled state to true" do
+ expect(provider.current_resource.enabled).to be_truthy
+ end
end
- end
- context "and plist for service is available" do
- before do
- allow(Dir).to receive(:glob).and_return(["/Users/igor/Library/LaunchAgents/io.redis.redis-server.plist"], [])
- provider.load_current_resource
- end
+ context "when launchctl doesn't return service entry at all" do
+ let(:launchctl_stdout) { StringIO.new <<-SVC_LIST }
+Could not find service "io.redis.redis-server" in domain for system
+SVC_LIST
- it "sets resouce enabled state to true" do
- expect(provider.current_resource.enabled).to be_truthy
+ it "sets service running state to false" do
+ provider.load_current_resource
+ expect(provider.current_resource.running).to be_falsey
+ end
+
+ context "and plist for service is not available" do
+ before do
+ allow(Dir).to receive(:glob).and_return([])
+ provider.load_current_resource
+ end
+
+ it "sets resouce enabled state to false" do
+ expect(provider.current_resource.enabled).to be_falsey
+ end
+ end
+
+ context "and plist for service is available" do
+ before do
+ allow(Dir).to receive(:glob).and_return(["#{plist}"], [])
+ provider.load_current_resource
+ end
+
+ it "sets resouce enabled state to true" do
+ expect(provider.current_resource.enabled).to be_truthy
+ end
+ end
+
+ describe "and several plists match service name" do
+ it "throws exception" do
+ allow(Dir).to receive(:glob).and_return(["#{plist}",
+ "/Users/wtf/something.plist"])
+ provider.load_current_resource
+ provider.define_resource_requirements
+ expect { provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service)
+ end
+ end
end
end
-
- describe "and several plists match service name" do
- it "throws exception" do
- allow(Dir).to receive(:glob).and_return(["/Users/igor/Library/LaunchAgents/io.redis.redis-server.plist",
- "/Users/wtf/something.plist"])
+ describe "#start_service" do
+ before do
+ allow(Chef::Resource::MacosxService).to receive(:new).and_return(current_resource)
provider.load_current_resource
- provider.define_resource_requirements
- expect { provider.process_resource_requirements }.to raise_error(Chef::Exceptions::Service)
+ allow(current_resource).to receive(:running).and_return(false)
end
- end
- end
- end
- describe "#start_service" do
- before do
- allow(Chef::Resource::Service).to receive(:new).and_return(current_resource)
- provider.load_current_resource
- allow(current_resource).to receive(:running).and_return(false)
- end
- it "calls the start command if one is specified and service is not running" do
- allow(new_resource).to receive(:start_command).and_return("cowsay dirty")
+ it "calls the start command if one is specified and service is not running" do
+ allow(new_resource).to receive(:start_command).and_return("cowsay dirty")
- expect(provider).to receive(:shell_out_with_systems_locale!).with("cowsay dirty")
- provider.start_service
- end
+ expect(provider).to receive(:shell_out_with_systems_locale!).with("cowsay dirty")
+ provider.start_service
+ end
- it "shows warning message if service is already running" do
- allow(current_resource).to receive(:running).and_return(true)
- expect(Chef::Log).to receive(:debug).with("service[#{service_name}] already running, not starting")
+ it "shows warning message if service is already running" do
+ allow(current_resource).to receive(:running).and_return(true)
+ expect(Chef::Log).to receive(:debug).with("macosx_service[#{service_name}] already running, not starting")
- provider.start_service
- end
+ provider.start_service
+ end
- it "starts service via launchctl if service found" do
- expect(provider).to receive(:shell_out_with_systems_locale!).
- with("launchctl load -w '/Users/igor/Library/LaunchAgents/io.redis.redis-server.plist'",
- :group => 1001, :user => 101).
- and_return(0)
+ it "starts service via launchctl if service found" do
+ cmd = 'launchctl load -w ' + session + plist
+ expect(provider).to receive(:shell_out_with_systems_locale).
+ with(/(#{su_cmd} .#{cmd}.|#{cmd})/).
+ and_return(0)
- provider.start_service
- end
- end
+ provider.start_service
+ end
+ end
- describe "#stop_service" do
- before do
- allow(Chef::Resource::Service).to receive(:new).and_return(current_resource)
+ describe "#stop_service" do
+ before do
+ allow(Chef::Resource::MacosxService).to receive(:new).and_return(current_resource)
- provider.load_current_resource
- allow(current_resource).to receive(:running).and_return(true)
- end
+ provider.load_current_resource
+ allow(current_resource).to receive(:running).and_return(true)
+ end
- it "calls the stop command if one is specified and service is running" do
- allow(new_resource).to receive(:stop_command).and_return("kill -9 123")
+ it "calls the stop command if one is specified and service is running" do
+ allow(new_resource).to receive(:stop_command).and_return("kill -9 123")
- expect(provider).to receive(:shell_out_with_systems_locale!).with("kill -9 123")
- provider.stop_service
- end
+ expect(provider).to receive(:shell_out_with_systems_locale!).with("kill -9 123")
+ provider.stop_service
+ end
- it "shows warning message if service is not running" do
- allow(current_resource).to receive(:running).and_return(false)
- expect(Chef::Log).to receive(:debug).with("service[#{service_name}] not running, not stopping")
+ it "shows warning message if service is not running" do
+ allow(current_resource).to receive(:running).and_return(false)
+ expect(Chef::Log).to receive(:debug).with("macosx_service[#{service_name}] not running, not stopping")
- provider.stop_service
- end
+ provider.stop_service
+ end
- it "stops the service via launchctl if service found" do
- expect(provider).to receive(:shell_out_with_systems_locale!).
- with("launchctl unload '/Users/igor/Library/LaunchAgents/io.redis.redis-server.plist'",
- :group => 1001, :user => 101).
- and_return(0)
+ it "stops the service via launchctl if service found" do
+ cmd = 'launchctl unload -w '+ plist
+ expect(provider).to receive(:shell_out_with_systems_locale).
+ with(/(#{su_cmd} .#{cmd}.|#{cmd})/).
+ and_return(0)
- provider.stop_service
- end
- end
+ provider.stop_service
+ end
+ end
- describe "#restart_service" do
- before do
- allow(Chef::Resource::Service).to receive(:new).and_return(current_resource)
+ describe "#restart_service" do
+ before do
+ allow(Chef::Resource::Service).to receive(:new).and_return(current_resource)
- provider.load_current_resource
- allow(current_resource).to receive(:running).and_return(true)
- allow(provider).to receive(:sleep)
- end
+ provider.load_current_resource
+ allow(current_resource).to receive(:running).and_return(true)
+ allow(provider).to receive(:sleep)
+ end
- it "issues a command if given" do
- allow(new_resource).to receive(:restart_command).and_return("reload that thing")
+ it "issues a command if given" do
+ allow(new_resource).to receive(:restart_command).and_return("reload that thing")
- expect(provider).to receive(:shell_out_with_systems_locale!).with("reload that thing")
- provider.restart_service
- end
+ expect(provider).to receive(:shell_out_with_systems_locale!).with("reload that thing")
+ provider.restart_service
+ end
- it "stops and then starts service" do
- expect(provider).to receive(:stop_service)
- expect(provider).to receive(:start_service);
+ it "stops and then starts service" do
+ expect(provider).to receive(:unload_service)
+ expect(provider).to receive(:load_service);
- provider.restart_service
+ provider.restart_service
+ end
+ end
end
end
end
diff --git a/spec/unit/provider/service/openbsd_service_spec.rb b/spec/unit/provider/service/openbsd_service_spec.rb
index 1b5206470e..d3c150a14b 100644
--- a/spec/unit/provider/service/openbsd_service_spec.rb
+++ b/spec/unit/provider/service/openbsd_service_spec.rb
@@ -35,10 +35,12 @@ describe Chef::Provider::Service::Openbsd do
node
end
+ let(:supports) { {:status => false} }
+
let(:new_resource) do
new_resource = Chef::Resource::Service.new("sndiod")
new_resource.pattern("sndiod")
- new_resource.supports({:status => false})
+ new_resource.supports(supports)
new_resource
end
@@ -106,9 +108,7 @@ describe Chef::Provider::Service::Openbsd do
context "when the service supports status" do
let(:status) { double(:stdout => "", :exitstatus => 0) }
- before do
- new_resource.supports({:status => true})
- end
+ let(:supports) { { :status => true } }
it "should run '/etc/rc.d/service_name status'" do
expect(provider).to receive(:shell_out).with("/etc/rc.d/#{new_resource.service_name} check").and_return(status)
@@ -305,10 +305,12 @@ describe Chef::Provider::Service::Openbsd do
end
describe Chef::Provider::Service::Openbsd, "restart_service" do
- it "should call 'restart' on the service_name if the resource supports it" do
- new_resource.supports({:restart => true})
- expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{new_resource.service_name} restart")
- provider.restart_service()
+ context "when the new_resource supports restart" do
+ let(:supports) { { restart: true } }
+ it "should call 'restart' on the service_name if the resource supports it" do
+ expect(provider).to receive(:shell_out_with_systems_locale!).with("/etc/rc.d/#{new_resource.service_name} restart")
+ provider.restart_service()
+ end
end
it "should call the restart_command if one has been specified" do
diff --git a/spec/unit/provider/service/redhat_spec.rb b/spec/unit/provider/service/redhat_spec.rb
index 73cfec8a6f..5aaf54d9f5 100644
--- a/spec/unit/provider/service/redhat_spec.rb
+++ b/spec/unit/provider/service/redhat_spec.rb
@@ -64,24 +64,76 @@ describe "Chef::Provider::Service::Redhat" do
end
describe "load current resource" do
- it "sets the current enabled status to true if the service is enabled for any run level" do
+ before do
status = double("Status", :exitstatus => 0, :stdout => "" , :stderr => "")
- expect(@provider).to receive(:shell_out).with("/sbin/service chef status").and_return(status)
+ allow(@provider).to receive(:shell_out).with("/sbin/service chef status").and_return(status)
+ end
+
+ it "sets supports[:status] to true by default" do
chkconfig = double("Chkconfig", :exitstatus => 0, :stdout => "chef 0:off 1:off 2:off 3:off 4:off 5:on 6:off", :stderr => "")
expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0,1]).and_return(chkconfig)
- expect(@provider.instance_variable_get("@service_missing")).to be_falsey
+ expect(@provider.service_missing).to be false
@provider.load_current_resource
- expect(@current_resource.enabled).to be_truthy
+ expect(@provider.supports[:status]).to be true
+ end
+
+ it "lets the user override supports[:status] in the new_resource" do
+ @new_resource.supports( { status: false } )
+ @new_resource.pattern "myservice"
+ chkconfig = double("Chkconfig", :exitstatus => 0, :stdout => "chef 0:off 1:off 2:off 3:off 4:off 5:on 6:off", :stderr => "")
+ expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0,1]).and_return(chkconfig)
+ foo_out = double("ps_command", :exitstatus => 0, :stdout => "a line that matches myservice", :stderr => "")
+ expect(@provider).to receive(:shell_out!).with("foo").and_return(foo_out)
+ expect(@provider.service_missing).to be false
+ expect(@provider).not_to receive(:shell_out).with("/sbin/service chef status")
+ @provider.load_current_resource
+ expect(@provider.supports[:status]).to be false
+ end
+
+ it "sets the current enabled status to true if the service is enabled for any run level" do
+ chkconfig = double("Chkconfig", :exitstatus => 0, :stdout => "chef 0:off 1:off 2:off 3:off 4:off 5:on 6:off", :stderr => "")
+ expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0,1]).and_return(chkconfig)
+ expect(@provider.service_missing).to be false
+ @provider.load_current_resource
+ expect(@current_resource.enabled).to be true
end
it "sets the current enabled status to false if the regex does not match" do
- status = double("Status", :exitstatus => 0, :stdout => "" , :stderr => "")
- expect(@provider).to receive(:shell_out).with("/sbin/service chef status").and_return(status)
chkconfig = double("Chkconfig", :exitstatus => 0, :stdout => "chef 0:off 1:off 2:off 3:off 4:off 5:off 6:off", :stderr => "")
expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0,1]).and_return(chkconfig)
- expect(@provider.instance_variable_get("@service_missing")).to be_falsey
+ expect(@provider.service_missing).to be false
expect(@provider.load_current_resource).to eql(@current_resource)
- expect(@current_resource.enabled).to be_falsey
+ expect(@current_resource.enabled).to be false
+ end
+
+ it "sets the current enabled status to true if the service is enabled at specified run levels" do
+ @new_resource.run_levels([1, 2])
+ chkconfig = double("Chkconfig", :exitstatus => 0, :stdout => "chef 0:off 1:on 2:on 3:off 4:off 5:off 6:off", :stderr => "")
+ expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0,1]).and_return(chkconfig)
+ expect(@provider.service_missing).to be false
+ @provider.load_current_resource
+ expect(@current_resource.enabled).to be true
+ expect(@provider.current_run_levels).to eql([1, 2])
+ end
+
+ it "sets the current enabled status to false if the service is enabled at a run level it should not" do
+ @new_resource.run_levels([1, 2])
+ chkconfig = double("Chkconfig", :exitstatus => 0, :stdout => "chef 0:off 1:on 2:on 3:on 4:off 5:off 6:off", :stderr => "")
+ expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0,1]).and_return(chkconfig)
+ expect(@provider.service_missing).to be false
+ @provider.load_current_resource
+ expect(@current_resource.enabled).to be false
+ expect(@provider.current_run_levels).to eql([1, 2, 3])
+ end
+
+ it "sets the current enabled status to false if the service is not enabled at specified run levels" do
+ @new_resource.run_levels([ 2 ])
+ chkconfig = double("Chkconfig", :exitstatus => 0, :stdout => "chef 0:off 1:on 2:off 3:off 4:off 5:off 6:off", :stderr => "")
+ expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --list chef", :returns => [0,1]).and_return(chkconfig)
+ expect(@provider.service_missing).to be false
+ @provider.load_current_resource
+ expect(@current_resource.enabled).to be false
+ expect(@provider.current_run_levels).to eql([1])
end
end
@@ -144,6 +196,28 @@ describe "Chef::Provider::Service::Redhat" do
expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig #{@new_resource.service_name} on")
@provider.enable_service
end
+
+ it "should call chkconfig to add 'service_name' at specified run_levels" do
+ allow(@provider).to receive(:run_levels).and_return([1, 2])
+ expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --level 12 #{@new_resource.service_name} on")
+ @provider.enable_service
+ end
+
+ it "should call chkconfig to add 'service_name' at specified run_levels when run_levels do not match" do
+ allow(@provider).to receive(:run_levels).and_return([1, 2])
+ allow(@provider).to receive(:current_run_levels).and_return([1, 3])
+ expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --level 12 #{@new_resource.service_name} on")
+ expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --level 3 #{@new_resource.service_name} off")
+ @provider.enable_service
+ end
+
+ it "should call chkconfig to add 'service_name' at specified run_levels if there is an extra run_level" do
+ allow(@provider).to receive(:run_levels).and_return([1, 2])
+ allow(@provider).to receive(:current_run_levels).and_return([1, 2, 3])
+ expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --level 12 #{@new_resource.service_name} on")
+ expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --level 3 #{@new_resource.service_name} off")
+ @provider.enable_service
+ end
end
describe "disable_service" do
@@ -151,6 +225,12 @@ describe "Chef::Provider::Service::Redhat" do
expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig #{@new_resource.service_name} off")
@provider.disable_service
end
+
+ it "should call chkconfig to del 'service_name' at specified run_levels" do
+ allow(@provider).to receive(:run_levels).and_return([1, 2])
+ expect(@provider).to receive(:shell_out!).with("/sbin/chkconfig --level 12 #{@new_resource.service_name} off")
+ @provider.disable_service
+ end
end
end
diff --git a/spec/unit/provider/service/upstart_service_spec.rb b/spec/unit/provider/service/upstart_service_spec.rb
index ca7ce8f930..1c8e304cb7 100644
--- a/spec/unit/provider/service/upstart_service_spec.rb
+++ b/spec/unit/provider/service/upstart_service_spec.rb
@@ -19,6 +19,10 @@
require 'spec_helper'
describe Chef::Provider::Service::Upstart do
+ let(:shell_out_success) do
+ double('shell_out_with_systems_locale', :exitstatus => 0, :error? => false)
+ end
+
before(:each) do
@node =Chef::Node.new
@node.name('upstarter')
@@ -173,7 +177,7 @@ describe Chef::Provider::Service::Upstart do
end
it "should run the services status command if one has been specified" do
- allow(@provider).to receive(:shell_out!).with("/bin/chefhasmonkeypants status").and_return(0)
+ allow(@provider).to receive(:shell_out!).with("/bin/chefhasmonkeypants status").and_return(shell_out_success)
expect(@current_resource).to receive(:running).with(true)
@provider.load_current_resource
end
@@ -246,7 +250,7 @@ describe Chef::Provider::Service::Upstart do
end
it "should call '/sbin/start service_name' if no start command is specified" do
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/start #{@new_resource.service_name}").and_return(0)
+ expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/start #{@new_resource.service_name}").and_return(shell_out_success)
@provider.start_service()
end
@@ -261,7 +265,7 @@ describe Chef::Provider::Service::Upstart do
@new_resource.parameters({ "OSD_ID" => "2" })
@provider = Chef::Provider::Service::Upstart.new(@new_resource, @run_context)
@provider.current_resource = @current_resource
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/start rsyslog OSD_ID=2").and_return(0)
+ expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/start rsyslog OSD_ID=2").and_return(shell_out_success)
@provider.start_service()
end
@@ -274,13 +278,13 @@ describe Chef::Provider::Service::Upstart do
it "should call '/sbin/restart service_name' if no restart command is specified" do
allow(@current_resource).to receive(:running).and_return(true)
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/restart #{@new_resource.service_name}").and_return(0)
+ expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/restart #{@new_resource.service_name}").and_return(shell_out_success)
@provider.restart_service()
end
it "should call '/sbin/start service_name' if restart_service is called for a stopped service" do
allow(@current_resource).to receive(:running).and_return(false)
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/start #{@new_resource.service_name}").and_return(0)
+ expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/start #{@new_resource.service_name}").and_return(shell_out_success)
@provider.restart_service()
end
@@ -293,7 +297,7 @@ describe Chef::Provider::Service::Upstart do
it "should call '/sbin/reload service_name' if no reload command is specified" do
allow(@current_resource).to receive(:running).and_return(true)
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/reload #{@new_resource.service_name}").and_return(0)
+ expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/reload #{@new_resource.service_name}").and_return(shell_out_success)
@provider.reload_service()
end
@@ -306,7 +310,7 @@ describe Chef::Provider::Service::Upstart do
it "should call '/sbin/stop service_name' if no stop command is specified" do
allow(@current_resource).to receive(:running).and_return(true)
- expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/stop #{@new_resource.service_name}").and_return(0)
+ expect(@provider).to receive(:shell_out_with_systems_locale!).with("/sbin/stop #{@new_resource.service_name}").and_return(shell_out_success)
@provider.stop_service()
end
diff --git a/spec/unit/provider/template/content_spec.rb b/spec/unit/provider/template/content_spec.rb
index 4b88a3aea5..3d6e822c00 100644
--- a/spec/unit/provider/template/content_spec.rb
+++ b/spec/unit/provider/template/content_spec.rb
@@ -23,6 +23,10 @@ describe Chef::Provider::Template::Content do
let(:new_resource) do
double("Chef::Resource::Template (new)",
:cookbook_name => 'openldap',
+ :recipe_name => 'default',
+ :source_line => "/Users/lamont/solo/cookbooks/openldap/recipes/default.rb:2:in `from_file'",
+ :source_line_file => "/Users/lamont/solo/cookbooks/openldap/recipes/default.rb",
+ :source_line_number => "2",
:source => 'openldap_stuff.conf.erb',
:local => false,
:cookbook => nil,
@@ -75,4 +79,41 @@ describe Chef::Provider::Template::Content do
expect(IO.read(content.tempfile.path)).to eq("slappiness is a warm gun")
end
+ describe "when using location helpers" do
+ let(:new_resource) do
+ double("Chef::Resource::Template (new)",
+ :cookbook_name => 'openldap',
+ :recipe_name => 'default',
+ :source_line => CHEF_SPEC_DATA + "/cookbooks/openldap/recipes/default.rb:2:in `from_file'",
+ :source_line_file => CHEF_SPEC_DATA + "/cookbooks/openldap/recipes/default.rb",
+ :source_line_number => "2",
+ :source => 'helpers.erb',
+ :local => false,
+ :cookbook => nil,
+ :variables => {},
+ :inline_helper_blocks => {},
+ :inline_helper_modules => [],
+ :helper_modules => [])
+ end
+
+ it "creates the template with the rendered content" do
+ expect(IO.read(content.tempfile.path)).to eql <<EOF
+openldap
+default
+#{CHEF_SPEC_DATA}/cookbooks/openldap/recipes/default.rb:2:in `from_file'
+#{CHEF_SPEC_DATA}/cookbooks/openldap/recipes/default.rb
+2
+helpers.erb
+#{CHEF_SPEC_DATA}/cookbooks/openldap/templates/default/helpers.erb
+openldap
+default
+#{CHEF_SPEC_DATA}/cookbooks/openldap/recipes/default.rb:2:in `from_file'
+#{CHEF_SPEC_DATA}/cookbooks/openldap/recipes/default.rb
+2
+helpers.erb
+#{CHEF_SPEC_DATA}/cookbooks/openldap/templates/default/helpers.erb
+EOF
+ end
+
+ end
end
diff --git a/spec/unit/provider/user/dscl_spec.rb b/spec/unit/provider/user/dscl_spec.rb
index 5ea037d944..32d0812d8c 100644
--- a/spec/unit/provider/user/dscl_spec.rb
+++ b/spec/unit/provider/user/dscl_spec.rb
@@ -24,7 +24,7 @@ require 'mixlib/shellout'
describe Chef::Provider::User::Dscl do
before do
- allow(Chef::Platform).to receive(:windows?) { false }
+ allow(ChefConfig).to receive(:windows?) { false }
end
let(:node) {
node = Chef::Node.new
diff --git a/spec/unit/provider/user_spec.rb b/spec/unit/provider/user_spec.rb
index 381168647b..2345ce18fb 100644
--- a/spec/unit/provider/user_spec.rb
+++ b/spec/unit/provider/user_spec.rb
@@ -143,8 +143,8 @@ describe Chef::Provider::User do
begin
require 'rubygems'
require 'shadow'
- rescue LoadError => e
- pending "ruby-shadow gem not installed for dynamic load test"
+ rescue LoadError
+ skip "ruby-shadow gem not installed for dynamic load test"
true
else
false
@@ -161,7 +161,7 @@ describe Chef::Provider::User do
unless shadow_lib_unavail?
context "and we have the ruby-shadow gem" do
- pending "and we are not root (rerun this again as root)", :requires_unprivileged_user => true
+ skip "and we are not root (rerun this again as root)", :requires_unprivileged_user => true
context "and we are root", :requires_root => true do
it "should pass assertions when ruby-shadow can be loaded" do
diff --git a/spec/unit/provider_resolver_spec.rb b/spec/unit/provider_resolver_spec.rb
index a9fa08ebfd..88df4a20cc 100644
--- a/spec/unit/provider_resolver_spec.rb
+++ b/spec/unit/provider_resolver_spec.rb
@@ -18,32 +18,118 @@
require 'spec_helper'
require 'chef/mixin/convert_to_class_name'
+require 'chef/provider_resolver'
+require 'chef/platform/service_helpers'
include Chef::Mixin::ConvertToClassName
+# Open up Provider so we can write things down easier in here
+#module Chef::Provider
+
describe Chef::ProviderResolver do
+ let(:resource_name) { :service }
+ let(:provider) { nil }
+ let(:action) { :start }
+
let(:node) do
node = Chef::Node.new
- allow(node).to receive(:[]).with(:os).and_return(os)
- allow(node).to receive(:[]).with(:platform_family).and_return(platform_family)
- allow(node).to receive(:[]).with(:platform).and_return(platform)
- allow(node).to receive(:[]).with(:platform_version).and_return(platform_version)
- allow(node).to receive(:is_a?).and_return(Chef::Node)
+ node.automatic[:os] = os
+ node.automatic[:platform_family] = platform_family
+ node.automatic[:platform] = platform
+ node.automatic[:platform_version] = platform_version
+ node.automatic[:kernel] = { machine: 'i386' }
node
end
+ let(:run_context) { Chef::RunContext.new(node, nil, nil) }
let(:provider_resolver) { Chef::ProviderResolver.new(node, resource, action) }
+ let(:resolved_provider) do
+ begin
+ resource ? resource.provider_for_action(action).class : nil
+ rescue Chef::Exceptions::ProviderNotFound
+ nil
+ end
+ end
- let(:action) { :start }
+ let(:resource) do
+ resource_class = Chef::ResourceResolver.resolve(resource_name, node: node)
+ if resource_class
+ resource = resource_class.new('test', run_context)
+ resource.provider = provider if provider
+ end
+ resource
+ end
- let(:resolved_provider) { provider_resolver.resolve }
+ def self.on_platform(platform, *tags,
+ platform_version: '11.0.1',
+ platform_family: nil,
+ os: nil,
+ &block)
+ Array(platform).each do |platform|
+ Array(platform_version).each do |platform_version|
+ on_one_platform(platform, platform_version, platform_family || platform, os || platform_family || platform, *tags, &block)
+ end
+ end
+ end
- let(:provider) { nil }
+ def self.on_one_platform(platform, platform_version, platform_family, os, *tags, &block)
+ describe "on #{platform} #{platform_version}, platform_family: #{platform_family}, os: #{os}", *tags do
+ let(:os) { os }
+ let(:platform) { platform }
+ let(:platform_family) { platform_family }
+ let(:platform_version) { platform_version }
- let(:resource_name) { :service }
+ define_singleton_method(:os) { os }
+ define_singleton_method(:platform) { platform }
+ define_singleton_method(:platform_family) { platform_family }
+ define_singleton_method(:platform_version) { platform_version }
- let(:resource) { double(Chef::Resource, provider: provider, resource_name: resource_name) }
+ instance_eval(&block)
+ end
+ end
+
+ def self.expect_providers(**providers)
+ providers.each do |name, expected|
+ describe name.to_s do
+ let(:resource_name) { name }
+
+ tags = []
+ expected_provider = nil
+ expected_resource = nil
+ Array(expected).each do |p|
+ if p.is_a?(Class) && p <= Chef::Provider
+ expected_provider = p
+ elsif p.is_a?(Class) && p <= Chef::Resource
+ expected_resource = p
+ else
+ tags << p
+ end
+ end
+
+ if expected_resource && expected_provider
+ it "'#{name}' resolves to resource #{expected_resource} and provider #{expected_provider}", *tags do
+ expect(resource.class).to eql(expected_resource)
+ provider = double(expected_provider, class: expected_provider)
+ expect(provider).to receive(:action=).with(action)
+ expect(expected_provider).to receive(:new).with(resource, run_context).and_return(provider)
+ expect(resolved_provider).to eql(expected_provider)
+ end
+ elsif expected_provider
+ it "'#{name}' resolves to provider #{expected_provider}", *tags do
+ provider = double(expected_provider)
+ expect(provider).to receive(:action=).with(action)
+ expect(expected_provider).to receive(:new).with(resource, run_context).and_return(provider)
+ expect(resolved_provider).to eql(expected_provider)
+ end
+ else
+ it "'#{name}' fails to resolve (since #{name.inspect} is unsupported on #{platform} #{platform_version})", *tags do
+ expect(resolved_provider).to be_nil
+ end
+ end
+ end
+ end
+ end
describe "resolving service resource" do
def stub_service_providers(*services)
@@ -59,7 +145,6 @@ describe Chef::ProviderResolver do
end
before do
- expect(provider_resolver).not_to receive(:maybe_chef_platform_lookup)
allow(resource).to receive(:service_name).and_return("ntp")
end
@@ -296,257 +381,479 @@ describe Chef::ProviderResolver do
end
end
- describe "on Ubuntu 14.10" do
- let(:os) { "linux" }
- let(:platform) { "ubuntu" }
- let(:platform_family) { "debian" }
- let(:platform_version) { "14.04" }
-
+ on_platform "ubuntu", platform_version: "14.10", platform_family: "debian", os: "linux" do
it_behaves_like "an ubuntu platform with upstart, update-rc.d and systemd"
end
- describe "on Ubuntu 14.04" do
- let(:os) { "linux" }
- let(:platform) { "ubuntu" }
- let(:platform_family) { "debian" }
- let(:platform_version) { "14.04" }
-
+ on_platform "ubuntu", platform_version: "14.04", platform_family: "debian", os: "linux" do
it_behaves_like "an ubuntu platform with upstart and update-rc.d"
end
- describe "on Ubuntu 10.04" do
- let(:os) { "linux" }
- let(:platform) { "ubuntu" }
- let(:platform_family) { "debian" }
- let(:platform_version) { "10.04" }
-
+ on_platform "ubuntu", platform_version: "10.04", platform_family: "debian", os: "linux" do
it_behaves_like "an ubuntu platform with upstart and update-rc.d"
end
# old debian uses the Debian provider (does not have insserv or upstart, or update-rc.d???)
- describe "on Debian 4.0" do
- let(:os) { "linux" }
- let(:platform) { "debian" }
- let(:platform_family) { "debian" }
- let(:platform_version) { "4.0" }
-
+ on_platform "debian", platform_version: "4.0", os: "linux" do
#it_behaves_like "a debian platform using the debian provider"
end
# Debian replaced the debian provider with insserv in the FIXME:VERSION distro
- describe "on Debian 7.0" do
- let(:os) { "linux" }
- let(:platform) { "debian" }
- let(:platform_family) { "debian" }
- let(:platform_version) { "7.0" }
-
+ on_platform "debian", platform_version: "7.0", os: "linux" do
it_behaves_like "a debian platform using the insserv provider"
end
- %w{solaris2 openindiana opensolaris nexentacore omnios smartos}.each do |platform|
- describe "on #{platform}" do
- let(:os) { "solaris2" }
- let(:platform) { platform }
- let(:platform_family) { platform }
- let(:platform_version) { "5.11" }
-
- it "returns a Solaris provider" do
- stub_service_providers
- stub_service_configs
- expect(resolved_provider).to eql(Chef::Provider::Service::Solaris)
- end
+ on_platform %w{solaris2 openindiana opensolaris nexentacore omnios smartos}, os: "solaris2", platform_version: "5.11" do
+ it "returns a Solaris provider" do
+ stub_service_providers
+ stub_service_configs
+ expect(resolved_provider).to eql(Chef::Provider::Service::Solaris)
+ end
- it "always returns a Solaris provider" do
- # no matter what we stub on the next two lines we should get a Solaris provider
- stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd)
- stub_service_configs(:initd, :upstart, :xinetd, :user_local_etc_rcd, :systemd)
- expect(resolved_provider).to eql(Chef::Provider::Service::Solaris)
- end
+ it "always returns a Solaris provider" do
+ # no matter what we stub on the next two lines we should get a Solaris provider
+ stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd)
+ stub_service_configs(:initd, :upstart, :xinetd, :user_local_etc_rcd, :systemd)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Solaris)
end
end
- %w{mswin mingw32 windows}.each do |platform|
- describe "on #{platform}" do
- let(:os) { "windows" }
- let(:platform) { platform }
- let(:platform_family) { "windows" }
- let(:platform_version) { "5.11" }
-
- it "returns a Windows provider" do
- stub_service_providers
- stub_service_configs
- expect(resolved_provider).to eql(Chef::Provider::Service::Windows)
- end
+ on_platform %w{mswin mingw32 windows}, platform_family: "windows", platform_version: "5.11" do
+ it "returns a Windows provider" do
+ stub_service_providers
+ stub_service_configs
+ expect(resolved_provider).to eql(Chef::Provider::Service::Windows)
+ end
- it "always returns a Windows provider" do
- # no matter what we stub on the next two lines we should get a Windows provider
- stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd)
- stub_service_configs(:initd, :upstart, :xinetd, :user_local_etc_rcd, :systemd)
- expect(resolved_provider).to eql(Chef::Provider::Service::Windows)
- end
+ it "always returns a Windows provider" do
+ # no matter what we stub on the next two lines we should get a Windows provider
+ stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd)
+ stub_service_configs(:initd, :upstart, :xinetd, :user_local_etc_rcd, :systemd)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Windows)
end
end
- %w{mac_os_x mac_os_x_server}.each do |platform|
- describe "on #{platform}" do
- let(:os) { "darwin" }
- let(:platform) { platform }
- let(:platform_family) { "mac_os_x" }
- let(:platform_version) { "10.9.2" }
-
- it "returns a Macosx provider" do
- stub_service_providers
- stub_service_configs
- expect(resolved_provider).to eql(Chef::Provider::Service::Macosx)
- end
+ on_platform %w{mac_os_x mac_os_x_server}, os: "darwin", platform_family: "mac_os_x", platform_version: "10.9.2" do
+ it "returns a Macosx provider" do
+ stub_service_providers
+ stub_service_configs
+ expect(resolved_provider).to eql(Chef::Provider::Service::Macosx)
+ end
- it "always returns a Macosx provider" do
- # no matter what we stub on the next two lines we should get a Macosx provider
- stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd)
- stub_service_configs(:initd, :upstart, :xinetd, :user_local_etc_rcd, :systemd)
- expect(resolved_provider).to eql(Chef::Provider::Service::Macosx)
- end
+ it "always returns a Macosx provider" do
+ # no matter what we stub on the next two lines we should get a Macosx provider
+ stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd)
+ stub_service_configs(:initd, :upstart, :xinetd, :user_local_etc_rcd, :systemd)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Macosx)
end
end
- %w{freebsd netbsd}.each do |platform|
- describe "on #{platform}" do
- let(:os) { platform }
- let(:platform) { platform }
- let(:platform_family) { platform }
- let(:platform_version) { "10.0-RELEASE" }
-
- it "returns a Freebsd provider if it finds the /usr/local/etc/rc.d initscript" do
- stub_service_providers
- stub_service_configs(:usr_local_etc_rcd)
- expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
- end
+ on_platform %w(freebsd netbsd), platform_version: '3.1.4' do
+ it "returns a Freebsd provider if it finds the /usr/local/etc/rc.d initscript" do
+ stub_service_providers
+ stub_service_configs(:usr_local_etc_rcd)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
+ end
- it "returns a Freebsd provider if it finds the /etc/rc.d initscript" do
- stub_service_providers
- stub_service_configs(:etc_rcd)
- expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
- end
+ it "returns a Freebsd provider if it finds the /etc/rc.d initscript" do
+ stub_service_providers
+ stub_service_configs(:etc_rcd)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
+ end
- it "always returns a Freebsd provider if it finds the /usr/local/etc/rc.d initscript" do
- # should only care about :usr_local_etc_rcd stub in the service configs
- stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd)
- stub_service_configs(:usr_local_etc_rcd, :initd, :upstart, :xinetd, :systemd)
- expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
- end
+ it "always returns a Freebsd provider if it finds the /usr/local/etc/rc.d initscript" do
+ # should only care about :usr_local_etc_rcd stub in the service configs
+ stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd)
+ stub_service_configs(:usr_local_etc_rcd, :initd, :upstart, :xinetd, :systemd)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
+ end
- it "always returns a Freebsd provider if it finds the /usr/local/etc/rc.d initscript" do
- # should only care about :etc_rcd stub in the service configs
- stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd)
- stub_service_configs(:etc_rcd, :initd, :upstart, :xinetd, :systemd)
- expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
- end
+ it "always returns a Freebsd provider if it finds the /usr/local/etc/rc.d initscript" do
+ # should only care about :etc_rcd stub in the service configs
+ stub_service_providers(:debian, :invokercd, :insserv, :upstart, :redhat, :systemd)
+ stub_service_configs(:etc_rcd, :initd, :upstart, :xinetd, :systemd)
+ expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
+ end
- it "foo" do
- stub_service_providers
- stub_service_configs
- expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
- end
+ it "foo" do
+ stub_service_providers
+ stub_service_configs
+ expect(resolved_provider).to eql(Chef::Provider::Service::Freebsd)
end
end
end
- describe "resolving static providers" do
- def resource_class(resource)
- Chef::Resource.const_get(convert_to_class_name(resource.to_s))
- end
- static_mapping = {
- apt_package: Chef::Provider::Package::Apt,
- bash: Chef::Provider::Script,
- bff_package: Chef::Provider::Package::Aix,
- breakpoint: Chef::Provider::Breakpoint,
- chef_gem: Chef::Provider::Package::Rubygems,
- cookbook_file: Chef::Provider::CookbookFile,
- csh: Chef::Provider::Script,
- deploy: Chef::Provider::Deploy::Timestamped,
- deploy_revision: Chef::Provider::Deploy::Revision,
- directory: Chef::Provider::Directory,
- dpkg_package: Chef::Provider::Package::Dpkg,
- dsc_script: Chef::Provider::DscScript,
- easy_install_package: Chef::Provider::Package::EasyInstall,
- erl_call: Chef::Provider::ErlCall,
- execute: Chef::Provider::Execute,
- file: Chef::Provider::File,
- gem_package: Chef::Provider::Package::Rubygems,
- git: Chef::Provider::Git,
- homebrew_package: Chef::Provider::Package::Homebrew,
- http_request: Chef::Provider::HttpRequest,
- ips_package: Chef::Provider::Package::Ips,
- link: Chef::Provider::Link,
- log: Chef::Provider::Log::ChefLog,
- macports_package: Chef::Provider::Package::Macports,
- pacman_package: Chef::Provider::Package::Pacman,
- paludis_package: Chef::Provider::Package::Paludis,
- perl: Chef::Provider::Script,
- portage_package: Chef::Provider::Package::Portage,
- python: Chef::Provider::Script,
- remote_directory: Chef::Provider::RemoteDirectory,
- route: Chef::Provider::Route,
- rpm_package: Chef::Provider::Package::Rpm,
- ruby: Chef::Provider::Script,
- ruby_block: Chef::Provider::RubyBlock,
- script: Chef::Provider::Script,
- smartos_package: Chef::Provider::Package::SmartOS,
- solaris_package: Chef::Provider::Package::Solaris,
- subversion: Chef::Provider::Subversion,
- template: Chef::Provider::Template,
- timestamped_deploy: Chef::Provider::Deploy::Timestamped,
- whyrun_safe_ruby_block: Chef::Provider::WhyrunSafeRubyBlock,
- windows_package: Chef::Provider::Package::Windows,
- windows_service: Chef::Provider::Service::Windows,
- yum_package: Chef::Provider::Package::Yum,
+ PROVIDERS =
+ {
+ bash: [ Chef::Resource::Bash, Chef::Provider::Script ],
+ breakpoint: [ Chef::Resource::Breakpoint, Chef::Provider::Breakpoint ],
+ chef_gem: [ Chef::Resource::ChefGem, Chef::Provider::Package::Rubygems ],
+ cookbook_file: [ Chef::Resource::CookbookFile, Chef::Provider::CookbookFile ],
+ csh: [ Chef::Resource::Csh, Chef::Provider::Script ],
+ deploy: [ Chef::Resource::Deploy, Chef::Provider::Deploy::Timestamped ],
+ deploy_revision: [ Chef::Resource::DeployRevision, Chef::Provider::Deploy::Revision ],
+ directory: [ Chef::Resource::Directory, Chef::Provider::Directory ],
+ easy_install_package: [ Chef::Resource::EasyInstallPackage, Chef::Provider::Package::EasyInstall ],
+ erl_call: [ Chef::Resource::ErlCall, Chef::Provider::ErlCall ],
+ execute: [ Chef::Resource::Execute, Chef::Provider::Execute ],
+ file: [ Chef::Resource::File, Chef::Provider::File ],
+ gem_package: [ Chef::Resource::GemPackage, Chef::Provider::Package::Rubygems ],
+ git: [ Chef::Resource::Git, Chef::Provider::Git ],
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Gpasswd ],
+ homebrew_package: [ Chef::Resource::HomebrewPackage, Chef::Provider::Package::Homebrew ],
+ http_request: [ Chef::Resource::HttpRequest, Chef::Provider::HttpRequest ],
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+ link: [ Chef::Resource::Link, Chef::Provider::Link ],
+ log: [ Chef::Resource::Log, Chef::Provider::Log::ChefLog ],
+ macports_package: [ Chef::Resource::MacportsPackage, Chef::Provider::Package::Macports ],
+ mdadm: [ Chef::Resource::Mdadm, Chef::Provider::Mdadm ],
+ mount: [ Chef::Resource::Mount, Chef::Provider::Mount::Mount ],
+ perl: [ Chef::Resource::Perl, Chef::Provider::Script ],
+ portage_package: [ Chef::Resource::PortagePackage, Chef::Provider::Package::Portage ],
+ python: [ Chef::Resource::Python, Chef::Provider::Script ],
+ remote_directory: [ Chef::Resource::RemoteDirectory, Chef::Provider::RemoteDirectory ],
+ route: [ Chef::Resource::Route, Chef::Provider::Route ],
+ ruby: [ Chef::Resource::Ruby, Chef::Provider::Script ],
+ ruby_block: [ Chef::Resource::RubyBlock, Chef::Provider::RubyBlock ],
+ script: [ Chef::Resource::Script, Chef::Provider::Script ],
+ subversion: [ Chef::Resource::Subversion, Chef::Provider::Subversion ],
+ template: [ Chef::Resource::Template, Chef::Provider::Template ],
+ timestamped_deploy: [ Chef::Resource::TimestampedDeploy, Chef::Provider::Deploy::Timestamped ],
+ user: [ Chef::Resource::User, Chef::Provider::User::Useradd ],
+ whyrun_safe_ruby_block: [ Chef::Resource::WhyrunSafeRubyBlock, Chef::Provider::WhyrunSafeRubyBlock ],
+
+ # We want to check that these are unsupported:
+ apt_package: nil,
+ bff_package: nil,
+ dpkg_package: nil,
+ dsc_script: nil,
+ ips_package: nil,
+ pacman_package: nil,
+ paludis_package: nil,
+ rpm_package: nil,
+ smartos_package: nil,
+ solaris_package: nil,
+ yum_package: nil,
+ windows_package: nil,
+ windows_service: nil,
+
+ "linux" => {
+ apt_package: [ Chef::Resource::AptPackage, Chef::Provider::Package::Apt ],
+ dpkg_package: [ Chef::Resource::DpkgPackage, Chef::Provider::Package::Dpkg ],
+ pacman_package: [ Chef::Resource::PacmanPackage, Chef::Provider::Package::Pacman ],
+ paludis_package: [ Chef::Resource::PaludisPackage, Chef::Provider::Package::Paludis ],
+ rpm_package: [ Chef::Resource::RpmPackage, Chef::Provider::Package::Rpm ],
+ yum_package: [ Chef::Resource::YumPackage, Chef::Provider::Package::Yum ],
+
+ "debian" => {
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig::Debian ],
+ package: [ Chef::Resource::AptPackage, Chef::Provider::Package::Apt ],
+# service: [ Chef::Resource::DebianService, Chef::Provider::Service::Debian ],
+
+ "debian" => {
+ "7.0" => {
+ },
+ "6.0" => {
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+# service: [ Chef::Resource::InsservService, Chef::Provider::Service::Insserv ],
+ },
+ "5.0" => {
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+ },
+ },
+ "gcel" => {
+ "3.1.4" => {
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+ },
+ },
+ "linaro" => {
+ "3.1.4" => {
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+ },
+ },
+ "linuxmint" => {
+ "3.1.4" => {
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+# service: [ Chef::Resource::UpstartService, Chef::Provider::Service::Upstart ],
+ },
+ },
+ "raspbian" => {
+ "3.1.4" => {
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+ },
+ },
+ "ubuntu" => {
+ "11.10" => {
+ },
+ "10.04" => {
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+ },
+ },
+ },
+
+ "arch" => {
+ # TODO should be Chef::Resource::PacmanPackage
+ package: [ Chef::Resource::Package, Chef::Provider::Package::Pacman ],
+
+ "arch" => {
+ "3.1.4" => {
+ }
+ },
+ },
+
+ "freebsd" => {
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Pw ],
+ user: [ Chef::Resource::User, Chef::Provider::User::Pw ],
+
+ "freebsd" => {
+ "3.1.4" => {
+ },
+ },
+ },
+ "suse" => {
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Gpasswd ],
+ "suse" => {
+ "12.0" => {
+ },
+ %w(11.1 11.2 11.3) => {
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Suse ],
+ },
+ },
+ "opensuse" => {
+# service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
+ package: [ Chef::Resource::ZypperPackage, Chef::Provider::Package::Zypper ],
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Usermod ],
+ "12.3" => {
+ },
+ "12.2" => {
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Suse ],
+ },
+ },
+ },
+
+ "gentoo" => {
+ # TODO should be Chef::Resource::PortagePackage
+ package: [ Chef::Resource::Package, Chef::Provider::Package::Portage ],
+ portage_package: [ Chef::Resource::PortagePackage, Chef::Provider::Package::Portage ],
+# service: [ Chef::Resource::GentooService, Chef::Provider::Service::Gentoo ],
+
+ "gentoo" => {
+ "3.1.4" => {
+ },
+ },
+ },
+
+ "rhel" => {
+# service: [ Chef::Resource::SystemdService, Chef::Provider::Service::Systemd ],
+ package: [ Chef::Resource::YumPackage, Chef::Provider::Package::Yum ],
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig::Redhat ],
+
+ %w(amazon xcp xenserver ibm_powerkvm cloudlinux parallels) => {
+ "3.1.4" => {
+# service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
+ },
+ },
+ %w(redhat centos scientific oracle) => {
+ "7.0" => {
+ },
+ "6.0" => {
+# service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
+ },
+ },
+ "fedora" => {
+ "15.0" => {
+ },
+ "14.0" => {
+# service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
+ },
+ },
+ },
+
+ },
+
+ "darwin" => {
+ %w(mac_os_x mac_os_x_server) => {
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Dscl ],
+ package: [ Chef::Resource::HomebrewPackage, Chef::Provider::Package::Homebrew ],
+ user: [ Chef::Resource::User, Chef::Provider::User::Dscl ],
+
+ "mac_os_x" => {
+ "10.9.2" => {
+ },
+ },
+ },
+ },
+
+ "windows" => {
+ batch: [ Chef::Resource::Batch, Chef::Provider::Batch ],
+ dsc_script: [ Chef::Resource::DscScript, Chef::Provider::DscScript ],
+ env: [ Chef::Resource::Env, Chef::Provider::Env::Windows ],
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Windows ],
+ mount: [ Chef::Resource::Mount, Chef::Provider::Mount::Windows ],
+ package: [ Chef::Resource::WindowsPackage, Chef::Provider::Package::Windows ],
+ powershell_script: [ Chef::Resource::PowershellScript, Chef::Provider::PowershellScript ],
+ service: [ Chef::Resource::WindowsService, Chef::Provider::Service::Windows ],
+ user: [ Chef::Resource::User, Chef::Provider::User::Windows ],
+ windows_package: [ Chef::Resource::WindowsPackage, Chef::Provider::Package::Windows ],
+ windows_service: [ Chef::Resource::WindowsService, Chef::Provider::Service::Windows ],
+
+ "windows" => {
+ %w(mswin mingw32 windows) => {
+ "10.9.2" => {
+ },
+ },
+ },
+ },
+
+ "aix" => {
+ bff_package: [ Chef::Resource::BffPackage, Chef::Provider::Package::Aix ],
+ cron: [ Chef::Resource::Cron, Chef::Provider::Cron::Aix ],
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Aix ],
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig::Aix ],
+ mount: [ Chef::Resource::Mount, Chef::Provider::Mount::Aix ],
+ # TODO should be Chef::Resource::BffPackage
+ package: [ Chef::Resource::Package, Chef::Provider::Package::Aix ],
+ rpm_package: [ Chef::Resource::RpmPackage, Chef::Provider::Package::Rpm ],
+ user: [ Chef::Resource::User, Chef::Provider::User::Aix ],
+# service: [ Chef::Resource::AixService, Chef::Provider::Service::Aix ],
+
+ "aix" => {
+ "aix" => {
+ "5.6" => {
+ },
+ },
+ },
+ },
+
+ "hpux" => {
+ "hpux" => {
+ "hpux" => {
+ "3.1.4" => {
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Usermod ]
+ }
+ }
}
-
- describe "on Ubuntu 14.04" do
- let(:os) { "linux" }
- let(:platform) { "ubuntu" }
- let(:platform_family) { "debian" }
- let(:platform_version) { "14.04" }
-
- supported_providers = [
- :apt_package, :bash, :breakpoint, :chef_gem, :cookbook_file, :csh, :deploy,
- :deploy_revision, :directory, :dpkg_package, :easy_install_package, :erl_call,
- :execute, :file, :gem_package, :git, :homebrew_package, :http_request, :link,
- :log, :macports_package, :pacman_package, :paludis_package, :perl, :python,
- :remote_directory, :route, :rpm_package, :ruby, :ruby_block, :script, :subversion,
- :template, :timestamped_deploy, :whyrun_safe_ruby_block, :yum_package,
- ]
-
- supported_providers.each do |static_resource|
- static_provider = static_mapping[static_resource]
- context "when the resource is a #{static_resource}" do
- let(:resource) { double(Chef::Resource, provider: nil, resource_name: static_resource) }
- let(:action) { :start } # in reality this doesn't matter much
- it "should resolve to a #{static_provider} provider" do
- expect(provider_resolver).not_to receive(:maybe_chef_platform_lookup)
- expect(resolved_provider).to eql(static_provider)
+ },
+
+ "netbsd" => {
+ "netbsd" => {
+ "netbsd" => {
+ "3.1.4" => {
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Groupmod ],
+ },
+ },
+ },
+ },
+
+ "openbsd" => {
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Usermod ],
+ package: [ Chef::Resource::OpenbsdPackage, Chef::Provider::Package::Openbsd ],
+
+ "openbsd" => {
+ "openbsd" => {
+ "3.1.4" => {
+ },
+ },
+ },
+ },
+
+ "solaris2" => {
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Usermod ],
+ ips_package: [ Chef::Resource::IpsPackage, Chef::Provider::Package::Ips ],
+ package: [ Chef::Resource::SolarisPackage, Chef::Provider::Package::Solaris ],
+ mount: [ Chef::Resource::Mount, Chef::Provider::Mount::Solaris ],
+ solaris_package: [ Chef::Resource::SolarisPackage, Chef::Provider::Package::Solaris ],
+
+ "smartos" => {
+ smartos_package: [ Chef::Resource::SmartosPackage, Chef::Provider::Package::SmartOS ],
+ package: [ Chef::Resource::SmartosPackage, Chef::Provider::Package::SmartOS ],
+
+ "smartos" => {
+ "3.1.4" => {
+ },
+ },
+ },
+
+ "solaris2" => {
+ "nexentacore" => {
+ "3.1.4" => {
+ },
+ },
+ "omnios" => {
+ "3.1.4" => {
+ user: [ Chef::Resource::User, Chef::Provider::User::Solaris ],
+ }
+ },
+ "openindiana" => {
+ "3.1.4" => {
+ },
+ },
+ "opensolaris" => {
+ "3.1.4" => {
+ },
+ },
+ "solaris2" => {
+ user: [ Chef::Resource::User, Chef::Provider::User::Solaris ],
+ "5.11" => {
+ package: [ Chef::Resource::IpsPackage, Chef::Provider::Package::Ips ],
+ },
+ "5.9" => {
+ },
+ },
+ },
+
+ },
+
+ "solaris" => {
+ "solaris" => {
+ "solaris" => {
+ "3.1.4" => {
+ },
+ },
+ },
+ },
+
+ "exherbo" => {
+ "exherbo" => {
+ "exherbo" => {
+ "3.1.4" => {
+ # TODO should be Chef::Resource::PaludisPackage
+ package: [ Chef::Resource::Package, Chef::Provider::Package::Paludis ]
+ }
+ }
+ }
+ }
+ }
+
+ def self.create_provider_tests(providers, test, expected, filter)
+ expected = expected.merge(providers.select { |key, value| key.is_a?(Symbol) })
+ providers.each do |key, value|
+ if !key.is_a?(Symbol)
+ next_test = test.merge({ filter => key })
+ next_filter =
+ case filter
+ when :os
+ :platform_family
+ when :platform_family
+ :platform
+ when :platform
+ :platform_version
+ when :platform_version
+ nil
+ else
+ raise "Hash too deep; only os, platform_family, platform and platform_version supported"
end
- end
+ create_provider_tests(value, next_test, expected, next_filter)
end
-
- unsupported_providers = [
- :bff_package, :dsc_script, :ips_package, :smartos_package,
- :solaris_package, :windows_package, :windows_service,
- ]
-
- unsupported_providers.each do |static_resource|
- static_provider = static_mapping[static_resource]
- context "when the resource is a #{static_resource}" do
- let(:resource) { double(Chef::Resource, provider: nil, resource_name: static_resource) }
- let(:action) { :start } # in reality this doesn't matter much
- it "should fall back into the old provider mapper code and hooks" do
- retval = Object.new
- expect(provider_resolver).to receive(:maybe_chef_platform_lookup).and_return(retval)
- expect(resolved_provider).to equal(retval)
- end
- end
+ end
+ # If there is no filter, we're as deep as we need to go
+ if !filter
+ on_platform test.delete(:platform), test do
+ expect_providers(expected)
end
end
end
+
+ create_provider_tests(PROVIDERS, {}, {}, :os)
end
diff --git a/spec/unit/provider_spec.rb b/spec/unit/provider_spec.rb
index 5a21b094d0..97b88b1732 100644
--- a/spec/unit/provider_spec.rb
+++ b/spec/unit/provider_spec.rb
@@ -49,6 +49,13 @@ class ConvergeActionDemonstrator < Chef::Provider
end
end
+class CheckResourceSemanticsDemonstrator < ConvergeActionDemonstrator
+ def check_resource_semantics!
+ raise Chef::Exceptions::InvalidResourceSpecification.new("check_resource_semantics!")
+ end
+end
+
+
describe Chef::Provider do
before(:each) do
@cookbook_collection = Chef::CookbookCollection.new([])
@@ -89,6 +96,10 @@ describe Chef::Provider do
expect(@provider.send(:whyrun_supported?)).to eql(false)
end
+ it "should do nothing for check_resource_semantics! by default" do
+ expect { @provider.check_resource_semantics! }.not_to raise_error
+ end
+
it "should return true for action_nothing" do
expect(@provider.action_nothing).to eql(true)
end
@@ -103,9 +114,7 @@ describe Chef::Provider do
end
it "does not re-load recipes when creating the temporary run context" do
- # we actually want to test that RunContext#load is never called, but we
- # can't stub all instances of an object with rspec's mocks. :/
- allow(Chef::RunContext).to receive(:new).and_raise("not supposed to happen")
+ expect_any_instance_of(Chef::RunContext).not_to receive(:load)
snitch = Proc.new {temporary_collection = @run_context.resource_collection}
@provider.send(:recipe_eval, &snitch)
end
@@ -176,6 +185,15 @@ describe Chef::Provider do
expect(@resource).not_to be_updated_by_last_action
end
end
+
+ describe "and the resource is invalid" do
+ let(:provider) { CheckResourceSemanticsDemonstrator.new(@resource, @run_context) }
+
+ it "fails with InvalidResourceSpecification when run" do
+ expect { provider.run_action(:foo) }.to raise_error(Chef::Exceptions::InvalidResourceSpecification)
+ end
+
+ end
end
end
diff --git a/spec/unit/recipe_spec.rb b/spec/unit/recipe_spec.rb
index 8d0b1bcfd2..ea3ab44c16 100644
--- a/spec/unit/recipe_spec.rb
+++ b/spec/unit/recipe_spec.rb
@@ -20,6 +20,7 @@
#
require 'spec_helper'
+require 'chef/platform/resource_priority_map'
describe Chef::Recipe do
@@ -82,7 +83,7 @@ describe Chef::Recipe do
it "should require a name argument" do
expect {
recipe.cat
- }.to raise_error(ArgumentError, "You must supply a name when declaring a cat resource")
+ }.to raise_error(ArgumentError)
end
it "should allow regular errors (not NameErrors) to pass unchanged" do
@@ -120,7 +121,8 @@ describe Chef::Recipe do
it "locate resource for particular platform" do
ShaunTheSheep = Class.new(Chef::Resource)
- ShaunTheSheep.provides :laughter, :on_platforms => ["television"]
+ ShaunTheSheep.resource_name :shaun_the_sheep
+ ShaunTheSheep.provides :laughter, :platform => ["television"]
node.automatic[:platform] = "television"
node.automatic[:platform_version] = "123"
res = recipe.laughter "timmy"
@@ -130,12 +132,46 @@ describe Chef::Recipe do
it "locate a resource for all platforms" do
YourMom = Class.new(Chef::Resource)
+ YourMom.resource_name :your_mom
YourMom.provides :love_and_caring
res = recipe.love_and_caring "mommy"
expect(res.name).to eql("mommy")
res.kind_of?(YourMom)
end
+ describe "when there is more than one resource that resolves on a node" do
+ before do
+ node.automatic[:platform] = "nbc_sports"
+ Sounders = Class.new(Chef::Resource)
+ Sounders.resource_name :sounders
+ TottenhamHotspur = Class.new(Chef::Resource)
+ TottenhamHotspur.resource_name :tottenham_hotspur
+ end
+
+ after do
+ Object.send(:remove_const, :Sounders)
+ Object.send(:remove_const, :TottenhamHotspur)
+ end
+
+ it "selects the first one alphabetically" do
+ Sounders.provides :football, platform: "nbc_sports"
+ TottenhamHotspur.provides :football, platform: "nbc_sports"
+
+ res1 = recipe.football "club world cup"
+ expect(res1.name).to eql("club world cup")
+ expect(res1).to be_a_kind_of(Sounders)
+ end
+
+ it "selects the first one alphabetically even if the declaration order is reversed" do
+ TottenhamHotspur.provides :football2, platform: "nbc_sports"
+ Sounders.provides :football2, platform: "nbc_sports"
+
+ res1 = recipe.football2 "club world cup"
+ expect(res1.name).to eql("club world cup")
+ expect(res1).to be_a_kind_of(Sounders)
+ end
+ end
+
end
end
@@ -237,8 +273,17 @@ describe Chef::Recipe do
action :nothing
end
end
+
+ it "validating resources via build_resource" do
+ expect {recipe.build_resource(:remote_file, "klopp") do
+ source Chef::DelayedEvaluator.new {"http://chef.io"}
+ end}.to_not raise_error
+ end
+
end
+
+
describe "creating resources via declare_resource" do
let(:zm_resource) do
recipe.declare_resource(:zen_master, "klopp") do
@@ -361,7 +406,7 @@ describe Chef::Recipe do
it "does not copy the action from the first resource" do
expect(original_resource.action).to eq([:score])
- expect(duplicated_resource.action).to eq(:nothing)
+ expect(duplicated_resource.action).to eq([:nothing])
end
it "does not copy the source location of the first resource" do
@@ -529,6 +574,36 @@ describe Chef::Recipe do
expect(cookbook_collection[:openldap]).not_to receive(:load_recipe).with("default", run_context)
openldap_recipe.include_recipe "::default"
end
+
+ it "will not load a recipe twice when called first from an LWRP provider" do
+ openldap_recipe = Chef::Recipe.new("openldap", "test", run_context)
+ expect(node).to receive(:loaded_recipe).with(:openldap, "default").exactly(:once)
+ allow(run_context).to receive(:unreachable_cookbook?).with(:openldap).and_return(false)
+ expect(cookbook_collection[:openldap]).to receive(:load_recipe).with("default", run_context)
+ openldap_recipe.include_recipe "::default"
+ expect(cookbook_collection[:openldap]).not_to receive(:load_recipe).with("default", run_context)
+ openldap_recipe.openldap_includer("do it").run_action(:run)
+ end
+
+ it "will not load a recipe twice when called last from an LWRP provider" do
+ openldap_recipe = Chef::Recipe.new("openldap", "test", run_context)
+ expect(node).to receive(:loaded_recipe).with(:openldap, "default").exactly(:once)
+ allow(run_context).to receive(:unreachable_cookbook?).with(:openldap).and_return(false)
+ expect(cookbook_collection[:openldap]).to receive(:load_recipe).with("default", run_context)
+ openldap_recipe.openldap_includer("do it").run_action(:run)
+ expect(cookbook_collection[:openldap]).not_to receive(:load_recipe).with("default", run_context)
+ openldap_recipe.include_recipe "::default"
+ end
+
+ it "will not load a recipe twice when called both times from an LWRP provider" do
+ openldap_recipe = Chef::Recipe.new("openldap", "test", run_context)
+ expect(node).to receive(:loaded_recipe).with(:openldap, "default").exactly(:once)
+ allow(run_context).to receive(:unreachable_cookbook?).with(:openldap).and_return(false)
+ expect(cookbook_collection[:openldap]).to receive(:load_recipe).with("default", run_context)
+ openldap_recipe.openldap_includer("do it").run_action(:run)
+ expect(cookbook_collection[:openldap]).not_to receive(:load_recipe).with("default", run_context)
+ openldap_recipe.openldap_includer("do it").run_action(:run)
+ end
end
describe "tags" do
@@ -593,5 +668,9 @@ describe Chef::Recipe do
expect(recipe.singleton_class.included_modules).to include(Chef::DSL::Audit)
expect(recipe.respond_to?(:control_group)).to be true
end
+
+ it "should respond to :ps_credential from Chef::DSL::Powershell" do
+ expect(recipe.respond_to?(:ps_credential)).to be true
+ end
end
end
diff --git a/spec/unit/registry_helper_spec.rb b/spec/unit/registry_helper_spec.rb
index 036a0834db..b2d0b7b125 100644
--- a/spec/unit/registry_helper_spec.rb
+++ b/spec/unit/registry_helper_spec.rb
@@ -21,6 +21,7 @@ require 'spec_helper'
describe Chef::Provider::RegistryKey do
let(:value1) { { :name => "one", :type => :string, :data => "1" } }
+ let(:value1_upcase_name) { {:name => "ONE", :type => :string, :data => "1"} }
let(:key_path) { 'HKCU\Software\OpscodeNumbers' }
let(:key) { 'Software\OpscodeNumbers' }
let(:key_parent) { 'Software' }
@@ -71,7 +72,20 @@ describe Chef::Provider::RegistryKey do
expect(@registry).to receive(:data_exists?).with(key_path, value1).and_return(true)
@registry.set_value(key_path, value1)
end
-
+ it "does nothing if case insensitive key and hive and value exist" do
+ expect(@registry).to receive(:key_exists!).with(key_path.downcase).and_return(true)
+ expect(@registry).to receive(:get_hive_and_key).with(key_path.downcase).and_return([@hive_mock, key])
+ expect(@registry).to receive(:value_exists?).with(key_path.downcase, value1).and_return(true)
+ expect(@registry).to receive(:data_exists?).with(key_path.downcase, value1).and_return(true)
+ @registry.set_value(key_path.downcase, value1)
+ end
+ it "does nothing if key and hive and value with a case insensitive name exist" do
+ expect(@registry).to receive(:key_exists!).with(key_path.downcase).and_return(true)
+ expect(@registry).to receive(:get_hive_and_key).with(key_path.downcase).and_return([@hive_mock, key])
+ expect(@registry).to receive(:value_exists?).with(key_path.downcase, value1_upcase_name).and_return(true)
+ expect(@registry).to receive(:data_exists?).with(key_path.downcase, value1_upcase_name).and_return(true)
+ @registry.set_value(key_path.downcase, value1_upcase_name)
+ end
it "updates value if key and hive and value exist, but data is different" do
expect(@registry).to receive(:key_exists!).with(key_path).and_return(true)
expect(@registry).to receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
diff --git a/spec/unit/resource/batch_spec.rb b/spec/unit/resource/batch_spec.rb
index 4a056b8735..b8c2897f42 100644
--- a/spec/unit/resource/batch_spec.rb
+++ b/spec/unit/resource/batch_spec.rb
@@ -25,6 +25,7 @@ describe Chef::Resource::Batch do
node.default["kernel"] = Hash.new
node.default["kernel"][:machine] = :x86_64.to_s
+ node.automatic[:os] = 'windows'
run_context = Chef::RunContext.new(node, nil, nil)
diff --git a/spec/unit/resource/breakpoint_spec.rb b/spec/unit/resource/breakpoint_spec.rb
index ed1f3ebcd5..88ab34d568 100644
--- a/spec/unit/resource/breakpoint_spec.rb
+++ b/spec/unit/resource/breakpoint_spec.rb
@@ -37,7 +37,7 @@ describe Chef::Resource::Breakpoint do
end
it "defaults to the break action" do
- expect(@breakpoint.action).to eq("break")
+ expect(@breakpoint.action).to eq([:break])
end
it "names itself after the line number of the file where it's created" do
diff --git a/spec/unit/resource/cron_spec.rb b/spec/unit/resource/cron_spec.rb
index 743552c1de..0978be6930 100644
--- a/spec/unit/resource/cron_spec.rb
+++ b/spec/unit/resource/cron_spec.rb
@@ -35,7 +35,7 @@ describe Chef::Resource::Cron do
end
it "should have a default action of 'create'" do
- expect(@resource.action).to eql(:create)
+ expect(@resource.action).to eql([:create])
end
it "should accept create or delete for action" do
diff --git a/spec/unit/resource/deploy_spec.rb b/spec/unit/resource/deploy_spec.rb
index 07f5f973c0..5b6a452784 100644
--- a/spec/unit/resource/deploy_spec.rb
+++ b/spec/unit/resource/deploy_spec.rb
@@ -30,35 +30,12 @@ describe Chef::Resource::Deploy do
class << self
-
- def resource_has_a_hash_attribute(attr_name)
- it "has a Hash attribute for #{attr_name.to_s}" do
- @resource.send(attr_name, {foo: "bar"})
- expect(@resource.send(attr_name)).to eql({foo: "bar"})
- expect {@resource.send(attr_name, 8675309)}.to raise_error(ArgumentError)
- end
-
- it "the Hash attribute for #{attr_name.to_s} is nillable" do
- @resource.send(attr_name, {foo: "bar"})
- expect(@resource.send(attr_name)).to eql({foo: "bar"})
- @resource.send(attr_name, nil)
- expect(@resource.send(attr_name)).to eql(nil)
- end
- end
-
def resource_has_a_string_attribute(attr_name)
it "has a String attribute for #{attr_name.to_s}" do
@resource.send(attr_name, "this is a string")
expect(@resource.send(attr_name)).to eql("this is a string")
expect {@resource.send(attr_name, 8675309)}.to raise_error(ArgumentError)
end
-
- it "the String attribute for #{attr_name.to_s} is nillable" do
- @resource.send(attr_name, "this is a string")
- expect(@resource.send(attr_name)).to eql("this is a string")
- @resource.send(attr_name, nil)
- expect(@resource.send(attr_name)).to eql(nil)
- end
end
def resource_has_a_boolean_attribute(attr_name, opts={:defaults_to=>false})
@@ -171,10 +148,16 @@ describe Chef::Resource::Deploy do
expect(@resource.current_path).to eql("/my/deploy/dir/current")
end
+ it "allows depth to be set via integer" do
+ expect(@resource.depth).to be_nil
+ @resource.depth 1
+ expect(@resource.depth).to eql(1)
+ end
+
it "gives #depth as 5 if shallow clone is true, nil otherwise" do
expect(@resource.depth).to be_nil
@resource.shallow_clone true
- expect(@resource.depth).to eql("5")
+ expect(@resource.depth).to eql(5)
end
it "aliases repo as repository" do
@@ -212,10 +195,6 @@ describe Chef::Resource::Deploy do
expect(@resource.symlink_before_migrate).to eq({"wtf?" => "wtf is going on"})
end
- resource_has_a_hash_attribute :symlink_before_migrate
- resource_has_a_hash_attribute :symlinks
- resource_has_a_hash_attribute :additional_remotes
-
resource_has_a_callback_attribute :before_migrate
resource_has_a_callback_attribute :before_symlink
resource_has_a_callback_attribute :before_restart
diff --git a/spec/unit/resource/directory_spec.rb b/spec/unit/resource/directory_spec.rb
index c452b2a914..e9e80806db 100644
--- a/spec/unit/resource/directory_spec.rb
+++ b/spec/unit/resource/directory_spec.rb
@@ -35,7 +35,7 @@ describe Chef::Resource::Directory do
end
it "should have a default action of 'create'" do
- expect(@resource.action).to eql(:create)
+ expect(@resource.action).to eql([:create])
end
it "should accept create or delete for action" do
diff --git a/spec/unit/resource/dsc_resource_spec.rb b/spec/unit/resource/dsc_resource_spec.rb
new file mode 100644
index 0000000000..06769d86ce
--- /dev/null
+++ b/spec/unit/resource/dsc_resource_spec.rb
@@ -0,0 +1,85 @@
+#
+# Author:: Adam Edwards (<adamed@getchef.com>)
+# Copyright:: Copyright (c) 2014 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+describe Chef::Resource::DscResource do
+ let(:dsc_test_resource_name) { 'DSCTest' }
+ let(:dsc_test_property_name) { :DSCTestProperty }
+ let(:dsc_test_property_value) { 'DSCTestValue' }
+
+ context 'when Powershell supports Dsc' do
+ let(:dsc_test_run_context) {
+ node = Chef::Node.new
+ node.automatic[:languages][:powershell][:version] = '5.0.10018.0'
+ empty_events = Chef::EventDispatch::Dispatcher.new
+ Chef::RunContext.new(node, {}, empty_events)
+ }
+ let(:dsc_test_resource) {
+ Chef::Resource::DscResource.new(dsc_test_resource_name, dsc_test_run_context)
+ }
+
+ it "has a default action of `:run`" do
+ expect(dsc_test_resource.action).to eq([:run])
+ end
+
+ it "has an allowed_actions attribute with only the `:run` and `:nothing` attributes" do
+ expect(dsc_test_resource.allowed_actions.to_set).to eq([:run,:nothing].to_set)
+ end
+
+ it "allows the resource attribute to be set" do
+ dsc_test_resource.resource(dsc_test_resource_name)
+ expect(dsc_test_resource.resource).to eq(dsc_test_resource_name)
+ end
+
+ it "allows the module_name attribute to be set" do
+ dsc_test_resource.module_name(dsc_test_resource_name)
+ expect(dsc_test_resource.module_name).to eq(dsc_test_resource_name)
+ end
+
+ context "when setting a dsc property" do
+ it "allows setting a dsc property with a property name of type Symbol" do
+ dsc_test_resource.property(dsc_test_property_name, dsc_test_property_value)
+ expect(dsc_test_resource.property(dsc_test_property_name)).to eq(dsc_test_property_value)
+ expect(dsc_test_resource.properties[dsc_test_property_name]).to eq(dsc_test_property_value)
+ end
+
+ it "raises a TypeError if property_name is not a symbol" do
+ expect{
+ dsc_test_resource.property('Foo', dsc_test_property_value)
+ }.to raise_error(TypeError)
+ end
+
+ context "when using DelayedEvaluators" do
+ it "allows setting a dsc property with a property name of type Symbol" do
+ dsc_test_resource.property(dsc_test_property_name, Chef::DelayedEvaluator.new {
+ dsc_test_property_value
+ })
+ expect(dsc_test_resource.property(dsc_test_property_name)).to eq(dsc_test_property_value)
+ expect(dsc_test_resource.properties[dsc_test_property_name]).to eq(dsc_test_property_value)
+ end
+ end
+ end
+
+ context 'Powershell DSL methods' do
+ it "responds to :ps_credential" do
+ expect(dsc_test_resource.respond_to?(:ps_credential)).to be true
+ end
+ end
+ end
+end
diff --git a/spec/unit/resource/dsc_script_spec.rb b/spec/unit/resource/dsc_script_spec.rb
index 71103ea590..1fa865a2d5 100644
--- a/spec/unit/resource/dsc_script_spec.rb
+++ b/spec/unit/resource/dsc_script_spec.rb
@@ -29,7 +29,7 @@ describe Chef::Resource::DscScript do
Chef::RunContext.new(node, {}, empty_events)
}
let(:dsc_test_resource) {
- Chef::Resource::DscScript.new(dsc_test_resource_name, dsc_test_run_context)
+ Chef::Resource::DscScript.new(dsc_test_resource_name, dsc_test_run_context)
}
let(:configuration_code) {'echo "This is supposed to create a configuration document."'}
let(:configuration_path) {'c:/myconfigs/formatc.ps1'}
@@ -38,7 +38,7 @@ describe Chef::Resource::DscScript do
let(:configuration_data_script) { 'c:/myconfigs/data/safedata.psd1' }
it "has a default action of `:run`" do
- expect(dsc_test_resource.action).to eq(:run)
+ expect(dsc_test_resource.action).to eq([:run])
end
it "has an allowed_actions attribute with only the `:run` and `:nothing` attributes" do
@@ -70,6 +70,10 @@ describe Chef::Resource::DscScript do
expect(dsc_test_resource.configuration_data_script).to eq(configuration_data_script)
end
+ it "has the ps_credential helper method" do
+ expect(dsc_test_resource).to respond_to(:ps_credential)
+ end
+
context "when calling imports" do
let(:module_name) { 'FooModule' }
let(:module_name_b) { 'BarModule' }
diff --git a/spec/unit/resource/env_spec.rb b/spec/unit/resource/env_spec.rb
index 566827a27e..9bee07c593 100644
--- a/spec/unit/resource/env_spec.rb
+++ b/spec/unit/resource/env_spec.rb
@@ -35,7 +35,7 @@ describe Chef::Resource::Env do
end
it "should have a default action of 'create'" do
- expect(@resource.action).to eql(:create)
+ expect(@resource.action).to eql([:create])
end
{ :create => false, :delete => false, :modify => false, :flibber => true }.each do |action,bad_value|
diff --git a/spec/unit/resource/erl_call_spec.rb b/spec/unit/resource/erl_call_spec.rb
index 8ec182665f..9abf2e7812 100644
--- a/spec/unit/resource/erl_call_spec.rb
+++ b/spec/unit/resource/erl_call_spec.rb
@@ -35,7 +35,7 @@ describe Chef::Resource::ErlCall do
end
it "should have a default action of run" do
- expect(@resource.action).to eql("run")
+ expect(@resource.action).to eql([:run])
end
it "should accept run as an action" do
diff --git a/spec/unit/resource/file/verification_spec.rb b/spec/unit/resource/file/verification_spec.rb
index 3609d9d482..6b929789c8 100644
--- a/spec/unit/resource/file/verification_spec.rb
+++ b/spec/unit/resource/file/verification_spec.rb
@@ -69,12 +69,40 @@ describe Chef::Resource::File::Verification do
end
context "with a verification command(String)" do
+ before(:each) do
+ allow(Chef::Log).to receive(:deprecation).and_return(nil)
+ end
+
+ def platform_specific_verify_command(variable_name)
+ if windows?
+ "if \"#{temp_path}\" == \"%{#{variable_name}}\" (exit 0) else (exit 1)"
+ else
+ "test #{temp_path} = %{#{variable_name}}"
+ end
+ end
+
it "substitutes \%{file} with the path" do
- test_command = if windows?
- "if \"#{temp_path}\" == \"%{file}\" (exit 0) else (exit 1)"
- else
- "test #{temp_path} = %{file}"
- end
+ test_command = platform_specific_verify_command('file')
+ v = Chef::Resource::File::Verification.new(parent_resource, test_command, {})
+ expect(v.verify(temp_path)).to eq(true)
+ end
+
+ it "warns about deprecation when \%{file} is used" do
+ expect(Chef::Log).to receive(:deprecation).with(/%{file} is deprecated/, /verification_spec\.rb/)
+ test_command = platform_specific_verify_command('file')
+ Chef::Resource::File::Verification.new(parent_resource, test_command, {})
+ .verify(temp_path)
+ end
+
+ it "does not warn about deprecation when \%{file} is not used" do
+ expect(Chef::Log).to_not receive(:deprecation)
+ test_command = platform_specific_verify_command('path')
+ Chef::Resource::File::Verification.new(parent_resource, test_command, {})
+ .verify(temp_path)
+ end
+
+ it "substitutes \%{path} with the path" do
+ test_command = platform_specific_verify_command('path')
v = Chef::Resource::File::Verification.new(parent_resource, test_command, {})
expect(v.verify(temp_path)).to eq(true)
end
diff --git a/spec/unit/resource/file_spec.rb b/spec/unit/resource/file_spec.rb
index db52e35004..76beaf15e1 100644
--- a/spec/unit/resource/file_spec.rb
+++ b/spec/unit/resource/file_spec.rb
@@ -29,7 +29,7 @@ describe Chef::Resource::File do
end
it "should have a default action of 'create'" do
- expect(@resource.action).to eql("create")
+ expect(@resource.action).to eql([:create])
end
it "should have a default content of nil" do
diff --git a/spec/unit/resource/group_spec.rb b/spec/unit/resource/group_spec.rb
index bcf9205f7e..a4029fc911 100644
--- a/spec/unit/resource/group_spec.rb
+++ b/spec/unit/resource/group_spec.rb
@@ -50,7 +50,7 @@ describe Chef::Resource::Group, "initialize" do
end
it "should set action to :create" do
- expect(@resource.action).to eql(:create)
+ expect(@resource.action).to eql([:create])
end
%w{create remove modify manage}.each do |action|
diff --git a/spec/unit/resource/ifconfig_spec.rb b/spec/unit/resource/ifconfig_spec.rb
index ea5282acd5..e3e1f6daa2 100644
--- a/spec/unit/resource/ifconfig_spec.rb
+++ b/spec/unit/resource/ifconfig_spec.rb
@@ -47,21 +47,23 @@ describe Chef::Resource::Ifconfig do
end
end
- shared_examples "being a platform using the default ifconfig provider" do |platform, version|
+ shared_examples "being a platform based on an old Debian" do |platform, version|
before do
+ @node.automatic_attrs[:os] = 'linux'
+ @node.automatic_attrs[:platform_family] = 'debian'
@node.automatic_attrs[:platform] = platform
@node.automatic_attrs[:platform_version] = version
end
it "should use an ordinary Provider::Ifconfig as a provider for #{platform} #{version}" do
- expect(@resource.provider_for_action(:add)).to be_a_kind_of(Chef::Provider::Ifconfig)
- expect(@resource.provider_for_action(:add)).not_to be_a_kind_of(Chef::Provider::Ifconfig::Debian)
- expect(@resource.provider_for_action(:add)).not_to be_a_kind_of(Chef::Provider::Ifconfig::Redhat)
+ expect(@resource.provider_for_action(:add).class).to eq(Chef::Provider::Ifconfig)
end
end
shared_examples "being a platform based on RedHat" do |platform, version|
before do
+ @node.automatic_attrs[:os] = 'linux'
+ @node.automatic_attrs[:platform_family] = 'rhel'
@node.automatic_attrs[:platform] = platform
@node.automatic_attrs[:platform_version] = version
end
@@ -73,6 +75,8 @@ describe Chef::Resource::Ifconfig do
shared_examples "being a platform based on a recent Debian" do |platform, version|
before do
+ @node.automatic_attrs[:os] = 'linux'
+ @node.automatic_attrs[:platform_family] = 'debian'
@node.automatic_attrs[:platform] = platform
@node.automatic_attrs[:platform_version] = version
end
@@ -87,7 +91,7 @@ describe Chef::Resource::Ifconfig do
end
describe "when it is an old Debian platform" do
- it_should_behave_like "being a platform using the default ifconfig provider", "debian", "6.0"
+ it_should_behave_like "being a platform based on an old Debian", "debian", "6.0"
end
describe "when it is a new Debian platform" do
@@ -95,7 +99,7 @@ describe Chef::Resource::Ifconfig do
end
describe "when it is an old Ubuntu platform" do
- it_should_behave_like "being a platform using the default ifconfig provider", "ubuntu", "11.04"
+ it_should_behave_like "being a platform based on an old Debian", "ubuntu", "11.04"
end
describe "when it is a new Ubuntu platform" do
diff --git a/spec/unit/resource/link_spec.rb b/spec/unit/resource/link_spec.rb
index 3573a15f31..0246fcd13b 100644
--- a/spec/unit/resource/link_spec.rb
+++ b/spec/unit/resource/link_spec.rb
@@ -36,7 +36,7 @@ describe Chef::Resource::Link do
end
it "should have a default action of 'create'" do
- expect(@resource.action).to eql(:create)
+ expect(@resource.action).to eql([:create])
end
{ :create => false, :delete => false, :blues => true }.each do |action,bad_value|
@@ -53,6 +53,21 @@ describe Chef::Resource::Link do
expect(@resource.target_file).to eql("fakey_fakerton")
end
+ it "should accept a delayed evaluator as the target path" do
+ @resource.target_file Chef::DelayedEvaluator.new { "my_lazy_name" }
+ expect(@resource.target_file).to eql("my_lazy_name")
+ end
+
+ it "should accept a delayed evaluator when accessing via 'path'" do
+ @resource.target_file Chef::DelayedEvaluator.new { "my_lazy_name" }
+ expect(@resource.path).to eql("my_lazy_name")
+ end
+
+ it "should accept a delayed evaluator via 'to'" do
+ @resource.to Chef::DelayedEvaluator.new { "my_lazy_name" }
+ expect(@resource.to).to eql("my_lazy_name")
+ end
+
it "should accept a string as the link source via 'to'" do
expect { @resource.to "/tmp" }.not_to raise_error
end
diff --git a/spec/unit/resource/mdadm_spec.rb b/spec/unit/resource/mdadm_spec.rb
index 866309ec5b..6ca99c58e5 100644
--- a/spec/unit/resource/mdadm_spec.rb
+++ b/spec/unit/resource/mdadm_spec.rb
@@ -35,7 +35,7 @@ describe Chef::Resource::Mdadm do
end
it "should have a default action of create" do
- expect(@resource.action).to eql(:create)
+ expect(@resource.action).to eql([:create])
end
it "should accept create, assemble, stop as actions" do
diff --git a/spec/unit/resource/mount_spec.rb b/spec/unit/resource/mount_spec.rb
index ad95c06e04..acce26dcab 100644
--- a/spec/unit/resource/mount_spec.rb
+++ b/spec/unit/resource/mount_spec.rb
@@ -38,7 +38,7 @@ describe Chef::Resource::Mount do
end
it "should have a default action of mount" do
- expect(@resource.action).to eql(:mount)
+ expect(@resource.action).to eql([:mount])
end
it "should accept mount, umount and remount as actions" do
diff --git a/spec/unit/resource/ohai_spec.rb b/spec/unit/resource/ohai_spec.rb
index fe29755abf..3bc21a41d2 100644
--- a/spec/unit/resource/ohai_spec.rb
+++ b/spec/unit/resource/ohai_spec.rb
@@ -34,7 +34,7 @@ describe Chef::Resource::Ohai do
end
it "should have a default action of create" do
- expect(@resource.action).to eql(:reload)
+ expect(@resource.action).to eql([:reload])
end
it "should allow you to set the plugin attribute" do
diff --git a/spec/unit/resource/powershell_spec.rb b/spec/unit/resource/powershell_script_spec.rb
index c263172ae6..2505c4a3d7 100644
--- a/spec/unit/resource/powershell_spec.rb
+++ b/spec/unit/resource/powershell_script_spec.rb
@@ -25,6 +25,7 @@ describe Chef::Resource::PowershellScript do
node.default["kernel"] = Hash.new
node.default["kernel"][:machine] = :x86_64.to_s
+ node.automatic[:os] = 'windows'
run_context = Chef::RunContext.new(node, nil, nil)
diff --git a/spec/unit/resource/registry_key_spec.rb b/spec/unit/resource/registry_key_spec.rb
index e2a864d73a..2e2811d026 100644
--- a/spec/unit/resource/registry_key_spec.rb
+++ b/spec/unit/resource/registry_key_spec.rb
@@ -45,7 +45,7 @@ describe Chef::Resource::RegistryKey, "initialize" do
end
it "should set action to :create" do
- expect(@resource.action).to eql(:create)
+ expect(@resource.action).to eql([:create])
end
%w{create create_if_missing delete delete_key}.each do |action|
diff --git a/spec/unit/resource/remote_file_spec.rb b/spec/unit/resource/remote_file_spec.rb
index 3731d1aee2..0a379ff574 100644
--- a/spec/unit/resource/remote_file_spec.rb
+++ b/spec/unit/resource/remote_file_spec.rb
@@ -39,6 +39,11 @@ describe Chef::Resource::RemoteFile do
expect(Chef::Platform.find_provider(:noplatform, 'noversion', @resource)).to eq(Chef::Provider::RemoteFile)
end
+ it "says its provider is RemoteFile when the source is a network share" do
+ @resource.source("\\\\fakey\\fakerton\\fake.txt")
+ expect(@resource.provider).to eq(Chef::Provider::RemoteFile)
+ expect(Chef::Platform.find_provider(:noplatform, 'noversion', @resource)).to eq(Chef::Provider::RemoteFile)
+ end
describe "source" do
it "does not have a default value for 'source'" do
@@ -50,6 +55,16 @@ describe Chef::Resource::RemoteFile do
expect(@resource.source).to eql([ "http://opscode.com/" ])
end
+ it "should accept a windows network share source" do
+ @resource.source "\\\\fakey\\fakerton\\fake.txt"
+ expect(@resource.source).to eql([ "\\\\fakey\\fakerton\\fake.txt" ])
+ end
+
+ it 'should accept file URIs with spaces' do
+ @resource.source("file:///C:/foo bar")
+ expect(@resource.source).to eql(["file:///C:/foo bar"])
+ end
+
it "should accept a delayed evalutator (string) for the remote file source" do
@resource.source Chef::DelayedEvaluator.new {"http://opscode.com/"}
expect(@resource.source).to eql([ "http://opscode.com/" ])
diff --git a/spec/unit/resource/ruby_block_spec.rb b/spec/unit/resource/ruby_block_spec.rb
index 9f19fecd4f..8664564ac5 100644
--- a/spec/unit/resource/ruby_block_spec.rb
+++ b/spec/unit/resource/ruby_block_spec.rb
@@ -30,8 +30,8 @@ describe Chef::Resource::RubyBlock do
expect(@resource).to be_a_kind_of(Chef::Resource::RubyBlock)
end
- it "should have a default action of 'create'" do
- expect(@resource.action).to eql("run")
+ it "should have a default action of 'run'" do
+ expect(@resource.action).to eql([:run])
end
it "should have a resource name of :ruby_block" do
diff --git a/spec/unit/resource/service_spec.rb b/spec/unit/resource/service_spec.rb
index eb6f444e93..b9e3757255 100644
--- a/spec/unit/resource/service_spec.rb
+++ b/spec/unit/resource/service_spec.rb
@@ -1,7 +1,7 @@
#
# Author:: AJ Christensen (<aj@hjksolutions.com>)
# Author:: Tyler Cloke (<tyler@opscode.com>)
-# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# Copyright:: Copyright (c) 2008-2015 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -139,14 +139,14 @@ describe Chef::Resource::Service do
expect { @resource.send(attrib, "poop") }.to raise_error(ArgumentError)
end
- it "should default all the feature support to false" do
- support_hash = { :status => false, :restart => false, :reload=> false }
+ it "should default all the feature support to nil" do
+ support_hash = { :status => nil, :restart => nil, :reload=> nil }
expect(@resource.supports).to eq(support_hash)
end
it "should allow you to set what features this resource supports as a array" do
support_array = [ :status, :restart ]
- support_hash = { :status => true, :restart => true, :reload => false }
+ support_hash = { :status => true, :restart => true, :reload => nil }
@resource.supports(support_array)
expect(@resource.supports).to eq(support_hash)
end
diff --git a/spec/unit/resource/template_spec.rb b/spec/unit/resource/template_spec.rb
index df5ca94b8a..2fd951b72d 100644
--- a/spec/unit/resource/template_spec.rb
+++ b/spec/unit/resource/template_spec.rb
@@ -98,7 +98,7 @@ describe Chef::Resource::Template do
context "on windows", :windows_only do
# according to Chef::Resource::File, windows state attributes are rights + deny_rights
- pending "it describes its state"
+ skip "it describes its state"
end
it "returns the file path as its identity" do
diff --git a/spec/unit/resource/timestamped_deploy_spec.rb b/spec/unit/resource/timestamped_deploy_spec.rb
index eca6c570d4..4ebfdaf059 100644
--- a/spec/unit/resource/timestamped_deploy_spec.rb
+++ b/spec/unit/resource/timestamped_deploy_spec.rb
@@ -23,11 +23,10 @@ describe Chef::Resource::TimestampedDeploy, "initialize" do
static_provider_resolution(
resource: Chef::Resource::TimestampedDeploy,
provider: Chef::Provider::Deploy::Timestamped,
- name: :deploy,
+ name: :timestamped_deploy,
action: :deploy,
os: 'linux',
platform_family: 'rhel',
)
end
-
diff --git a/spec/unit/resource/user_spec.rb b/spec/unit/resource/user_spec.rb
index f05de94fe0..3bf7e6187b 100644
--- a/spec/unit/resource/user_spec.rb
+++ b/spec/unit/resource/user_spec.rb
@@ -43,7 +43,7 @@ describe Chef::Resource::User, "initialize" do
end
it "should set action to :create" do
- expect(@resource.action).to eql(:create)
+ expect(@resource.action).to eql([:create])
end
it "should set supports[:manage_home] to false" do
diff --git a/spec/unit/resource/windows_package_spec.rb b/spec/unit/resource/windows_package_spec.rb
index 1e02f2449b..6aa5d357ea 100644
--- a/spec/unit/resource/windows_package_spec.rb
+++ b/spec/unit/resource/windows_package_spec.rb
@@ -63,9 +63,9 @@ describe Chef::Resource::WindowsPackage, "initialize" do
end
it "coverts a source to an absolute path" do
- allow(::File).to receive(:absolute_path).and_return("c:\\Files\\frost.msi")
+ allow(::File).to receive(:absolute_path).and_return("c:\\files\\frost.msi")
resource.source("frost.msi")
- expect(resource.source).to eql "c:\\Files\\frost.msi"
+ expect(resource.source).to eql "c:\\files\\frost.msi"
end
it "converts slashes to backslashes in the source path" do
@@ -78,4 +78,18 @@ describe Chef::Resource::WindowsPackage, "initialize" do
# it's a little late to stub out File.absolute_path
expect(resource.source).to include("solitaire.msi")
end
+
+ it "supports the checksum attribute" do
+ resource.checksum('somechecksum')
+ expect(resource.checksum).to eq('somechecksum')
+ end
+
+ context 'when a URL is used' do
+ let(:resource_source) { 'https://foo.bar/solitare.msi' }
+ let(:resource) { Chef::Resource::WindowsPackage.new(resource_source) }
+
+ it "should return the source unmodified" do
+ expect(resource.source).to eq(resource_source)
+ end
+ end
end
diff --git a/spec/unit/resource/yum_package_spec.rb b/spec/unit/resource/yum_package_spec.rb
index e01b87c580..f24f1e3405 100644
--- a/spec/unit/resource/yum_package_spec.rb
+++ b/spec/unit/resource/yum_package_spec.rb
@@ -1,6 +1,6 @@
#
# Author:: AJ Christensen (<aj@opscode.com>)
-# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# Copyright:: Copyright (c) 2008-2015 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -78,3 +78,12 @@ describe Chef::Resource::YumPackage, "allow_downgrade" do
expect { @resource.allow_downgrade "monkey" }.to raise_error(ArgumentError)
end
end
+
+describe Chef::Resource::YumPackage, "yum_binary" do
+ let(:resource) { Chef::Resource::YumPackage.new("foo") }
+
+ it "should allow you to specify the yum_binary" do
+ resource.yum_binary "/usr/bin/yum-something"
+ expect(resource.yum_binary).to eql("/usr/bin/yum-something")
+ end
+end
diff --git a/spec/unit/resource_collection_spec.rb b/spec/unit/resource_collection_spec.rb
index b43b012dfc..d52e7e2c26 100644
--- a/spec/unit/resource_collection_spec.rb
+++ b/spec/unit/resource_collection_spec.rb
@@ -252,7 +252,7 @@ describe Chef::ResourceCollection do
expect(json).to match(/instance_vars/)
end
- include_examples "to_json equalivent to Chef::JSONCompat.to_json" do
+ include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
let(:jsonable) { rc }
end
end
diff --git a/spec/unit/resource_resolver_spec.rb b/spec/unit/resource_resolver_spec.rb
new file mode 100644
index 0000000000..b3bda9d945
--- /dev/null
+++ b/spec/unit/resource_resolver_spec.rb
@@ -0,0 +1,53 @@
+#
+# Author:: Ranjib Dey
+# Copyright:: Copyright (c) 2015 Ranjib Dey <ranjib@linux.com>.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'chef/resource_resolver'
+
+
+describe Chef::ResourceResolver do
+ it '#resolve' do
+ expect(described_class.resolve(:execute)).to eq(Chef::Resource::Execute)
+ end
+
+ it '#list' do
+ expect(described_class.list(:package)).to_not be_empty
+ end
+
+ context 'instance methods' do
+ let(:resolver) do
+ described_class.new(Chef::Node.new, 'execute')
+ end
+
+ it '#resolve' do
+ expect(resolver.resolve).to eq Chef::Resource::Execute
+ end
+
+ it '#list' do
+ expect(resolver.list).to eq [ Chef::Resource::Execute ]
+ end
+
+ it '#provided_by? returns true when resource class is in the list' do
+ expect(resolver.provided_by?(Chef::Resource::Execute)).to be_truthy
+ end
+
+ it '#provided_by? returns false when resource class is not in the list' do
+ expect(resolver.provided_by?(Chef::Resource::File)).to be_falsey
+ end
+ end
+end
diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb
index 8214021f65..b9ba80068b 100644
--- a/spec/unit/resource_spec.rb
+++ b/spec/unit/resource_spec.rb
@@ -21,10 +21,6 @@
require 'spec_helper'
-class ResourceTestHarness < Chef::Resource
- provider_base Chef::Provider::Package
-end
-
describe Chef::Resource do
before(:each) do
@cookbook_repo_path = File.join(CHEF_SPEC_DATA, 'cookbooks')
@@ -35,6 +31,18 @@ describe Chef::Resource do
@resource = Chef::Resource.new("funk", @run_context)
end
+ it "should mixin shell_out" do
+ expect(@resource.respond_to?(:shell_out)).to be true
+ end
+
+ it "should mixin shell_out!" do
+ expect(@resource.respond_to?(:shell_out!)).to be true
+ end
+
+ it "should mixin shell_out_with_systems_locale" do
+ expect(@resource.respond_to?(:shell_out_with_systems_locale)).to be true
+ end
+
describe "when inherited" do
it "adds an entry to a list of subclasses" do
@@ -51,8 +59,8 @@ describe Chef::Resource do
end
describe "when declaring the identity attribute" do
- it "has no identity attribute by default" do
- expect(Chef::Resource.identity_attr).to be_nil
+ it "has :name as identity attribute by default" do
+ expect(Chef::Resource.identity_attr).to eq(:name)
end
it "sets an identity attribute" do
@@ -324,6 +332,86 @@ describe Chef::Resource do
end
end
+ describe "self.resource_name" do
+ context "When resource_name is not set" do
+ it "and there are no provides lines, resource_name is nil" do
+ c = Class.new(Chef::Resource) do
+ end
+
+ r = c.new('hi')
+ r.declared_type = :d
+ expect(c.resource_name).to be_nil
+ expect(r.resource_name).to be_nil
+ expect(r.declared_type).to eq :d
+ end
+
+ it "and there are no provides lines, @resource_name is used" do
+ c = Class.new(Chef::Resource) do
+ def initialize(*args, &block)
+ @resource_name = :blah
+ super
+ end
+ end
+
+ r = c.new('hi')
+ r.declared_type = :d
+ expect(c.resource_name).to be_nil
+ expect(r.resource_name).to eq :blah
+ expect(r.declared_type).to eq :d
+ end
+
+ it "and the resource class gets a late-bound name, resource_name is nil" do
+ c = Class.new(Chef::Resource) do
+ def self.name
+ "ResourceSpecNameTest"
+ end
+ end
+
+ r = c.new('hi')
+ r.declared_type = :d
+ expect(c.resource_name).to be_nil
+ expect(r.resource_name).to be_nil
+ expect(r.declared_type).to eq :d
+ end
+ end
+
+ it "resource_name without provides is honored" do
+ c = Class.new(Chef::Resource) do
+ resource_name 'blah'
+ end
+
+ r = c.new('hi')
+ r.declared_type = :d
+ expect(c.resource_name).to eq :blah
+ expect(r.resource_name).to eq :blah
+ expect(r.declared_type).to eq :d
+ end
+ it "setting class.resource_name with 'resource_name = blah' overrides declared_type" do
+ c = Class.new(Chef::Resource) do
+ provides :self_resource_name_test_2
+ end
+ c.resource_name = :blah
+
+ r = c.new('hi')
+ r.declared_type = :d
+ expect(c.resource_name).to eq :blah
+ expect(r.resource_name).to eq :blah
+ expect(r.declared_type).to eq :d
+ end
+ it "setting class.resource_name with 'resource_name blah' overrides declared_type" do
+ c = Class.new(Chef::Resource) do
+ resource_name :blah
+ provides :self_resource_name_test_3
+ end
+
+ r = c.new('hi')
+ r.declared_type = :d
+ expect(c.resource_name).to eq :blah
+ expect(r.resource_name).to eq :blah
+ expect(r.declared_type).to eq :d
+ end
+ end
+
describe "is" do
it "should return the arguments passed with 'is'" do
zm = Chef::Resource::ZenMaster.new("coffee")
@@ -343,7 +431,7 @@ describe Chef::Resource do
expect(json).to match(/instance_vars/)
end
- include_examples "to_json equalivent to Chef::JSONCompat.to_json" do
+ include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
let(:jsonable) { @resource }
end
end
@@ -447,8 +535,21 @@ describe Chef::Resource do
expect(Chef::Resource.provider_base).to eq(Chef::Provider)
end
- it "allows the base provider to be overriden by a " do
- expect(ResourceTestHarness.provider_base).to eq(Chef::Provider::Package)
+ it "allows the base provider to be overridden" do
+ Chef::Config.treat_deprecation_warnings_as_errors(false)
+ class OverrideProviderBaseTest < Chef::Resource
+ provider_base Chef::Provider::Package
+ end
+
+ expect(OverrideProviderBaseTest.provider_base).to eq(Chef::Provider::Package)
+ end
+
+ it "warns when setting provider_base" do
+ expect {
+ class OverrideProviderBaseTest2 < Chef::Resource
+ provider_base Chef::Provider::Package
+ end
+ }.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
end
end
@@ -709,21 +810,21 @@ describe Chef::Resource do
end
it 'adds mappings for a single platform' do
- expect(Chef::Resource.node_map).to receive(:set).with(
+ expect(Chef.resource_handler_map).to receive(:set).with(
:dinobot, Chef::Resource::Klz, { platform: ['autobots'] }
)
klz.provides :dinobot, platform: ['autobots']
end
it 'adds mappings for multiple platforms' do
- expect(Chef::Resource.node_map).to receive(:set).with(
+ expect(Chef.resource_handler_map).to receive(:set).with(
:energy, Chef::Resource::Klz, { platform: ['autobots', 'decepticons']}
)
klz.provides :energy, platform: ['autobots', 'decepticons']
end
it 'adds mappings for all platforms' do
- expect(Chef::Resource.node_map).to receive(:set).with(
+ expect(Chef.resource_handler_map).to receive(:set).with(
:tape_deck, Chef::Resource::Klz, {}
)
klz.provides :tape_deck
@@ -731,35 +832,51 @@ describe Chef::Resource do
end
- describe "lookups from the platform map" do
- let(:klz1) { Class.new(Chef::Resource) }
- let(:klz2) { Class.new(Chef::Resource) }
+ describe "resource_for_node" do
+ describe "lookups from the platform map" do
+ let(:klz1) { Class.new(Chef::Resource) }
+
+ before(:each) do
+ Chef::Resource::Klz1 = klz1
+ @node = Chef::Node.new
+ @node.name("bumblebee")
+ @node.automatic[:platform] = "autobots"
+ @node.automatic[:platform_version] = "6.1"
+ Object.const_set('Soundwave', klz1)
+ klz1.provides :soundwave
+ end
- before(:each) do
- Chef::Resource::Klz1 = klz1
- Chef::Resource::Klz2 = klz2
- @node = Chef::Node.new
- @node.name("bumblebee")
- @node.automatic[:platform] = "autobots"
- @node.automatic[:platform_version] = "6.1"
- Object.const_set('Soundwave', klz1)
- klz2.provides :dinobot, :on_platforms => ['autobots']
- Object.const_set('Grimlock', klz2)
- end
+ after(:each) do
+ Object.send(:remove_const, :Soundwave)
+ Chef::Resource.send(:remove_const, :Klz1)
+ end
- after(:each) do
- Object.send(:remove_const, :Soundwave)
- Object.send(:remove_const, :Grimlock)
- Chef::Resource.send(:remove_const, :Klz1)
- Chef::Resource.send(:remove_const, :Klz2)
+ it "returns a resource by short_name if nothing else matches" do
+ expect(Chef::Resource.resource_for_node(:soundwave, @node)).to eql(klz1)
+ end
end
- describe "resource_for_node" do
- it "returns a resource by short_name and node" do
- expect(Chef::Resource.resource_for_node(:dinobot, @node)).to eql(Grimlock)
+ describe "lookups from the platform map" do
+ let(:klz2) { Class.new(Chef::Resource) }
+
+ before(:each) do
+ Chef::Resource::Klz2 = klz2
+ @node = Chef::Node.new
+ @node.name("bumblebee")
+ @node.automatic[:platform] = "autobots"
+ @node.automatic[:platform_version] = "6.1"
+ klz2.provides :dinobot, :platform => ['autobots']
+ Object.const_set('Grimlock', klz2)
+ klz2.provides :grimlock
end
- it "returns a resource by short_name if nothing else matches" do
- expect(Chef::Resource.resource_for_node(:soundwave, @node)).to eql(Soundwave)
+
+ after(:each) do
+ Object.send(:remove_const, :Grimlock)
+ Chef::Resource.send(:remove_const, :Klz2)
+ end
+
+ it "returns a resource by short_name and node" do
+ expect(Chef::Resource.resource_for_node(:dinobot, @node)).to eql(klz2)
end
end
@@ -860,4 +977,90 @@ describe Chef::Resource do
end
end
+
+ describe "#action" do
+ let(:resource_class) do
+ Class.new(described_class) do
+ allowed_actions(%i{one two})
+ end
+ end
+ let(:resource) { resource_class.new('test', nil) }
+ subject { resource.action }
+
+ context "with a no action" do
+ it { is_expected.to eq [:nothing] }
+ end
+
+ context "with a default action" do
+ let(:resource_class) do
+ Class.new(described_class) do
+ default_action(:one)
+ end
+ end
+ it { is_expected.to eq [:one] }
+ end
+
+ context "with a symbol action" do
+ before { resource.action(:one) }
+ it { is_expected.to eq [:one] }
+ end
+
+ context "with a string action" do
+ before { resource.action('two') }
+ it { is_expected.to eq [:two] }
+ end
+
+ context "with an array action" do
+ before { resource.action([:two, :one]) }
+ it { is_expected.to eq [:two, :one] }
+ end
+
+ context "with an assignment" do
+ before { resource.action = :one }
+ it { is_expected.to eq [:one] }
+ end
+
+ context "with an array assignment" do
+ before { resource.action = [:two, :one] }
+ it { is_expected.to eq [:two, :one] }
+ end
+
+ context "with an invalid action" do
+ it { expect { resource.action(:three) }.to raise_error Chef::Exceptions::ValidationFailed }
+ end
+
+ context "with an invalid assignment action" do
+ it { expect { resource.action = :three }.to raise_error Chef::Exceptions::ValidationFailed }
+ end
+ end
+
+ describe ".default_action" do
+ let(:default_action) { }
+ let(:resource_class) do
+ actions = default_action
+ Class.new(described_class) do
+ default_action(actions) if actions
+ end
+ end
+ subject { resource_class.default_action }
+
+ context "with no default actions" do
+ it { is_expected.to eq [:nothing] }
+ end
+
+ context "with a symbol default action" do
+ let(:default_action) { :one }
+ it { is_expected.to eq [:one] }
+ end
+
+ context "with a string default action" do
+ let(:default_action) { 'one' }
+ it { is_expected.to eq [:one] }
+ end
+
+ context "with an array default action" do
+ let(:default_action) { [:two, :one] }
+ it { is_expected.to eq [:two, :one] }
+ end
+ end
end
diff --git a/spec/unit/rest_spec.rb b/spec/unit/rest_spec.rb
index 1aa7ac84ee..3b04981610 100644
--- a/spec/unit/rest_spec.rb
+++ b/spec/unit/rest_spec.rb
@@ -69,8 +69,8 @@ describe Chef::REST do
rest
end
- let(:standard_read_headers) {{"Accept"=>"application/json", "Accept"=>"application/json", "Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "X-REMOTE-REQUEST-ID"=>request_id}}
- let(:standard_write_headers) {{"Accept"=>"application/json", "Content-Type"=>"application/json", "Accept"=>"application/json", "Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "X-REMOTE-REQUEST-ID"=>request_id}}
+ let(:standard_read_headers) {{"Accept"=>"application/json", "Accept"=>"application/json", "Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "X-REMOTE-REQUEST-ID"=>request_id, 'X-Ops-Server-API-Version' => Chef::HTTP::Authenticator::DEFAULT_SERVER_API_VERSION}}
+ let(:standard_write_headers) {{"Accept"=>"application/json", "Content-Type"=>"application/json", "Accept"=>"application/json", "Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "X-REMOTE-REQUEST-ID"=>request_id, 'X-Ops-Server-API-Version' => Chef::HTTP::Authenticator::DEFAULT_SERVER_API_VERSION}}
before(:each) do
Chef::Log.init(log_stringio)
@@ -92,6 +92,15 @@ describe Chef::REST do
Chef::REST.new(base_url, nil, nil, options)
end
+ context 'when created with a chef zero URL' do
+
+ let(:url) { "chefzero://localhost:1" }
+
+ it "does not load the signing key" do
+ expect { Chef::REST.new(url) }.to_not raise_error
+ end
+ end
+
describe "calling an HTTP verb on a path or absolute URL" do
it "adds a relative URL to the base url it was initialized with" do
expect(rest.create_url("foo/bar/baz")).to eq(URI.parse(base_url + "/foo/bar/baz"))
@@ -268,19 +277,6 @@ describe Chef::REST do
rest
end
- let(:base_headers) do
- {
- 'Accept' => 'application/json',
- 'X-Chef-Version' => Chef::VERSION,
- 'Accept-Encoding' => Chef::REST::RESTRequest::ENCODING_GZIP_DEFLATE,
- 'X-REMOTE-REQUEST-ID' => request_id
- }
- end
-
- let (:req_with_body_headers) do
- base_headers.merge("Content-Type" => "application/json", "Content-Length" => '13')
- end
-
before(:each) do
Chef::Config[:ssl_client_cert] = nil
Chef::Config[:ssl_client_key] = nil
@@ -295,7 +291,8 @@ describe Chef::REST do
'X-Chef-Version' => Chef::VERSION,
'Accept-Encoding' => Chef::REST::RESTRequest::ENCODING_GZIP_DEFLATE,
'Host' => host_header,
- 'X-REMOTE-REQUEST-ID' => request_id
+ 'X-REMOTE-REQUEST-ID' => request_id,
+ 'X-Ops-Server-API-Version' => Chef::HTTP::Authenticator::DEFAULT_SERVER_API_VERSION
}
end
@@ -539,7 +536,7 @@ describe Chef::REST do
end
end
end
- end
+ end # as JSON API requests
context "when streaming downloads to a tempfile" do
let!(:tempfile) { Tempfile.open("chef-rspec-rest_spec-line-@{__LINE__}--") }
@@ -577,7 +574,8 @@ describe Chef::REST do
'X-Chef-Version' => Chef::VERSION,
'Accept-Encoding' => Chef::REST::RESTRequest::ENCODING_GZIP_DEFLATE,
'Host' => host_header,
- 'X-REMOTE-REQUEST-ID'=> request_id
+ 'X-REMOTE-REQUEST-ID'=> request_id,
+ 'X-Ops-Server-API-Version' => Chef::HTTP::Authenticator::DEFAULT_SERVER_API_VERSION
}
expect(Net::HTTP::Get).to receive(:new).with("/?foo=bar", expected_headers).and_return(request_mock)
rest.streaming_request(url, {})
@@ -588,7 +586,8 @@ describe Chef::REST do
'X-Chef-Version' => Chef::VERSION,
'Accept-Encoding' => Chef::REST::RESTRequest::ENCODING_GZIP_DEFLATE,
'Host' => host_header,
- 'X-REMOTE-REQUEST-ID'=> request_id
+ 'X-REMOTE-REQUEST-ID'=> request_id,
+ 'X-Ops-Server-API-Version' => Chef::HTTP::Authenticator::DEFAULT_SERVER_API_VERSION
}
expect(Net::HTTP::Get).to receive(:new).with("/?foo=bar", expected_headers).and_return(request_mock)
rest.streaming_request(url, {})
@@ -686,7 +685,7 @@ describe Chef::REST do
expect(block_called).to be_truthy
end
end
- end
+ end # when making REST requests
context "when following redirects" do
let(:rest) do
diff --git a/spec/unit/role_spec.rb b/spec/unit/role_spec.rb
index 5421b5a7b3..ecc7945a08 100644
--- a/spec/unit/role_spec.rb
+++ b/spec/unit/role_spec.rb
@@ -21,7 +21,7 @@ require 'chef/role'
describe Chef::Role do
before(:each) do
- allow(Chef::Platform).to receive(:windows?) { false }
+ allow(ChefConfig).to receive(:windows?) { false }
@role = Chef::Role.new
@role.name("ops_master")
end
@@ -217,7 +217,7 @@ describe Chef::Role do
end
- include_examples "to_json equalivent to Chef::JSONCompat.to_json" do
+ include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
let(:jsonable) { @role }
end
end
diff --git a/spec/unit/run_context/child_run_context_spec.rb b/spec/unit/run_context/child_run_context_spec.rb
new file mode 100644
index 0000000000..63586e459c
--- /dev/null
+++ b/spec/unit/run_context/child_run_context_spec.rb
@@ -0,0 +1,133 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Tim Hinderliter (<tim@opscode.com>)
+# Author:: Christopher Walters (<cw@opscode.com>)
+# Copyright:: Copyright (c) 2008, 2010 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+require 'support/lib/library_load_order'
+
+describe Chef::RunContext::ChildRunContext do
+ context "with a run context with stuff in it" do
+ let(:chef_repo_path) { File.expand_path(File.join(CHEF_SPEC_DATA, "run_context", "cookbooks")) }
+ let(:cookbook_collection) {
+ cl = Chef::CookbookLoader.new(chef_repo_path)
+ cl.load_cookbooks
+ Chef::CookbookCollection.new(cl)
+ }
+ let(:node) {
+ node = Chef::Node.new
+ node.run_list << "test" << "test::one" << "test::two"
+ node
+ }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, cookbook_collection, events) }
+
+ context "and a child run context" do
+ let(:child) { run_context.create_child }
+
+ it "parent_run_context is set to the parent" do
+ expect(child.parent_run_context).to eq run_context
+ end
+
+ it "audits is not the same as the parent" do
+ expect(child.audits.object_id).not_to eq run_context.audits.object_id
+ child.audits['hi'] = 'lo'
+ expect(child.audits['hi']).to eq('lo')
+ expect(run_context.audits['hi']).not_to eq('lo')
+ end
+
+ it "resource_collection is not the same as the parent" do
+ expect(child.resource_collection.object_id).not_to eq run_context.resource_collection.object_id
+ f = Chef::Resource::File.new('hi', child)
+ child.resource_collection.insert(f)
+ expect(child.resource_collection).to include f
+ expect(run_context.resource_collection).not_to include f
+ end
+
+ it "immediate_notification_collection is not the same as the parent" do
+ expect(child.immediate_notification_collection.object_id).not_to eq run_context.immediate_notification_collection.object_id
+ src = Chef::Resource::File.new('hi', child)
+ dest = Chef::Resource::File.new('argh', child)
+ notification = Chef::Resource::Notification.new(dest, :create, src)
+ child.notifies_immediately(notification)
+ expect(child.immediate_notification_collection['file[hi]']).to eq([notification])
+ expect(run_context.immediate_notification_collection['file[hi]']).not_to eq([notification])
+ end
+
+ it "immediate_notifications is not the same as the parent" do
+ src = Chef::Resource::File.new('hi', child)
+ dest = Chef::Resource::File.new('argh', child)
+ notification = Chef::Resource::Notification.new(dest, :create, src)
+ child.notifies_immediately(notification)
+ expect(child.immediate_notifications(src)).to eq([notification])
+ expect(run_context.immediate_notifications(src)).not_to eq([notification])
+ end
+
+ it "delayed_notification_collection is not the same as the parent" do
+ expect(child.delayed_notification_collection.object_id).not_to eq run_context.delayed_notification_collection.object_id
+ src = Chef::Resource::File.new('hi', child)
+ dest = Chef::Resource::File.new('argh', child)
+ notification = Chef::Resource::Notification.new(dest, :create, src)
+ child.notifies_delayed(notification)
+ expect(child.delayed_notification_collection['file[hi]']).to eq([notification])
+ expect(run_context.delayed_notification_collection['file[hi]']).not_to eq([notification])
+ end
+
+ it "delayed_notifications is not the same as the parent" do
+ src = Chef::Resource::File.new('hi', child)
+ dest = Chef::Resource::File.new('argh', child)
+ notification = Chef::Resource::Notification.new(dest, :create, src)
+ child.notifies_delayed(notification)
+ expect(child.delayed_notifications(src)).to eq([notification])
+ expect(run_context.delayed_notifications(src)).not_to eq([notification])
+ end
+
+ it "create_child creates a child-of-child" do
+ c = child.create_child
+ expect(c.parent_run_context).to eq child
+ end
+
+ context "after load('include::default')" do
+ before do
+ run_list = Chef::RunList.new('include::default').expand('_default')
+ # TODO not sure why we had to do this to get everything to work ...
+ node.automatic_attrs[:recipes] = []
+ child.load(run_list)
+ end
+
+ it "load_recipe loads into the child" do
+ expect(child.resource_collection).to be_empty
+ child.load_recipe("include::includee")
+ expect(child.resource_collection).not_to be_empty
+ end
+
+ it "include_recipe loads into the child" do
+ expect(child.resource_collection).to be_empty
+ child.include_recipe("include::includee")
+ expect(child.resource_collection).not_to be_empty
+ end
+
+ it "load_recipe_file loads into the child" do
+ expect(child.resource_collection).to be_empty
+ child.load_recipe_file(File.expand_path("include/recipes/includee.rb", chef_repo_path))
+ expect(child.resource_collection).not_to be_empty
+ end
+ end
+ end
+ end
+end
diff --git a/spec/unit/run_context_spec.rb b/spec/unit/run_context_spec.rb
index d656111a7d..99801575ef 100644
--- a/spec/unit/run_context_spec.rb
+++ b/spec/unit/run_context_spec.rb
@@ -53,6 +53,44 @@ describe Chef::RunContext do
expect(run_context.node).to eq(node)
end
+ it "loads up node[:cookbooks]" do
+ expect(run_context.node[:cookbooks]).to eql(
+ {
+ "circular-dep1" => {
+ "version" => "0.0.0",
+ },
+ "circular-dep2" => {
+ "version" => "0.0.0",
+ },
+ "dependency1" => {
+ "version" => "0.0.0",
+ },
+ "dependency2" => {
+ "version" => "0.0.0",
+ },
+ "include" => {
+ "version" => "0.0.0",
+ },
+ "no-default-attr" => {
+ "version" => "0.0.0",
+ },
+ "test" => {
+ "version" => "0.0.0",
+ },
+ "test-with-circular-deps" => {
+ "version" => "0.0.0",
+ },
+ "test-with-deps" => {
+ "version" => "0.0.0",
+ },
+ }
+ )
+ end
+
+ it "has a nil parent_run_context" do
+ expect(run_context.parent_run_context).to be_nil
+ end
+
describe "loading cookbooks for a run list" do
before do
@@ -159,4 +197,45 @@ describe Chef::RunContext do
expect(run_context.reboot_requested?).to be_falsey
end
end
+
+ describe "notifications" do
+ let(:notification) { Chef::Resource::Notification.new(nil, nil, notifying_resource) }
+
+ shared_context "notifying resource is a Chef::Resource" do
+ let(:notifying_resource) { Chef::Resource.new("gerbil") }
+
+ it "should be keyed off the resource name" do
+ run_context.send(setter, notification)
+ expect(run_context.send(getter, notifying_resource)).to eq([notification])
+ end
+ end
+
+ shared_context "notifying resource is a subclass of Chef::Resource" do
+ let(:declared_type) { :alpaca }
+ let(:notifying_resource) {
+ r = Class.new(Chef::Resource).new("guinea pig")
+ r.declared_type = declared_type
+ r
+ }
+
+ it "should be keyed off the resource declared key" do
+ run_context.send(setter, notification)
+ expect(run_context.send(getter, notifying_resource)).to eq([notification])
+ end
+ end
+
+ describe "of the immediate kind" do
+ let(:setter) { :notifies_immediately }
+ let(:getter) { :immediate_notifications }
+ include_context "notifying resource is a Chef::Resource"
+ include_context "notifying resource is a subclass of Chef::Resource"
+ end
+
+ describe "of the delayed kind" do
+ let(:setter) { :notifies_delayed }
+ let(:getter) { :delayed_notifications }
+ include_context "notifying resource is a Chef::Resource"
+ include_context "notifying resource is a subclass of Chef::Resource"
+ end
+ end
end
diff --git a/spec/unit/run_list/versioned_recipe_list_spec.rb b/spec/unit/run_list/versioned_recipe_list_spec.rb
index 209ac37fc1..9c3ecaa0dd 100644
--- a/spec/unit/run_list/versioned_recipe_list_spec.rb
+++ b/spec/unit/run_list/versioned_recipe_list_spec.rb
@@ -26,98 +26,165 @@ describe Chef::RunList::VersionedRecipeList do
end
end
+ let(:list) { described_class.new }
+
+ let(:versioned_recipes) { [] }
+
+ let(:recipes) { [] }
+
+ before do
+ recipes.each { |r| list << r }
+ versioned_recipes.each {|r| list.add_recipe r[:name], r[:version]}
+ end
+
describe "add_recipe" do
- before(:each) do
- @list = Chef::RunList::VersionedRecipeList.new
- @list << "apt"
- @list << "god"
- @list << "apache2"
- end
+
+ let(:recipes) { %w[ apt god apache2 ] }
it "should append the recipe to the end of the list" do
- @list.add_recipe "rails"
- expect(@list).to eq(["apt", "god", "apache2", "rails"])
+ list.add_recipe "rails"
+ expect(list).to eq(["apt", "god", "apache2", "rails"])
end
it "should not duplicate entries" do
- @list.add_recipe "apt"
- expect(@list).to eq(["apt", "god", "apache2"])
+ list.add_recipe "apt"
+ expect(list).to eq(["apt", "god", "apache2"])
end
it "should allow you to specify a version" do
- @list.add_recipe "rails", "1.0.0"
- expect(@list).to eq(["apt", "god", "apache2", "rails"])
- expect(@list.with_versions).to include({:name => "rails", :version => "1.0.0"})
+ list.add_recipe "rails", "1.0.0"
+ expect(list).to eq(["apt", "god", "apache2", "rails"])
+ expect(list.with_versions).to include({:name => "rails", :version => "1.0.0"})
end
it "should allow you to specify a version for a recipe that already exists" do
- @list.add_recipe "apt", "1.2.3"
- expect(@list).to eq(["apt", "god", "apache2"])
- expect(@list.with_versions).to include({:name => "apt", :version => "1.2.3"})
+ list.add_recipe "apt", "1.2.3"
+ expect(list).to eq(["apt", "god", "apache2"])
+ expect(list.with_versions).to include({:name => "apt", :version => "1.2.3"})
end
it "should allow you to specify the same version of a recipe twice" do
- @list.add_recipe "rails", "1.0.0"
- @list.add_recipe "rails", "1.0.0"
- expect(@list.with_versions).to include({:name => "rails", :version => "1.0.0"})
+ list.add_recipe "rails", "1.0.0"
+ list.add_recipe "rails", "1.0.0"
+ expect(list.with_versions).to include({:name => "rails", :version => "1.0.0"})
end
it "should allow you to spcify no version, even when a version already exists" do
- @list.add_recipe "rails", "1.0.0"
- @list.add_recipe "rails"
- expect(@list.with_versions).to include({:name => "rails", :version => "1.0.0"})
+ list.add_recipe "rails", "1.0.0"
+ list.add_recipe "rails"
+ expect(list.with_versions).to include({:name => "rails", :version => "1.0.0"})
end
it "should not allow multiple versions of the same recipe" do
- @list.add_recipe "rails", "1.0.0"
- expect {@list.add_recipe "rails", "0.1.0"}.to raise_error Chef::Exceptions::CookbookVersionConflict
+ list.add_recipe "rails", "1.0.0"
+ expect {list.add_recipe "rails", "0.1.0"}.to raise_error Chef::Exceptions::CookbookVersionConflict
end
end
describe "with_versions" do
- before(:each) do
- @recipes = [
+
+ let(:versioned_recipes) do
+ [
{:name => "apt", :version => "1.0.0"},
{:name => "god", :version => nil},
{:name => "apache2", :version => "0.0.1"}
]
- @list = Chef::RunList::VersionedRecipeList.new
- @recipes.each {|i| @list.add_recipe i[:name], i[:version]}
end
-
it "should return an array of hashes with :name and :version" do
- expect(@list.with_versions).to eq(@recipes)
+ expect(list.with_versions).to eq(versioned_recipes)
end
it "should retain the same order as the version-less list" do
- with_versions = @list.with_versions
- @list.each_with_index do |item, index|
+ with_versions = list.with_versions
+ list.each_with_index do |item, index|
expect(with_versions[index][:name]).to eq(item)
end
end
end
describe "with_version_constraints" do
- before(:each) do
- @recipes = [
- {:name => "apt", :version => "~> 1.2.0"},
- {:name => "god", :version => nil},
- {:name => "apache2", :version => "0.0.1"}
- ]
- @list = Chef::RunList::VersionedRecipeList.new
- @recipes.each {|i| @list.add_recipe i[:name], i[:version]}
- @constraints = @recipes.map do |x|
- { :name => x[:name],
- :version_constraint => Chef::VersionConstraint.new(x[:version])
- }
- end
+
+ let(:versioned_recipes) do
+ [
+ {:name => "apt", :version => "~> 1.2.0"},
+ {:name => "god", :version => nil},
+ {:name => "apache2", :version => "0.0.1"}
+ ]
end
+
it "should return an array of hashes with :name and :version_constraint" do
- @list.with_version_constraints.each do |x|
- expect(x).to have_key :name
- expect(x[:version_constraint]).not_to be nil
+ list.with_version_constraints.each_with_index do |recipe_spec, i|
+
+ expected_recipe = versioned_recipes[i]
+
+ expect(recipe_spec[:name]).to eq(expected_recipe[:name])
+ expect(recipe_spec[:version_constraint]).to eq(Chef::VersionConstraint.new(expected_recipe[:version]))
end
end
end
+
+ describe "with_fully_qualified_names_and_version_constraints" do
+
+ let(:fq_names) { list.with_fully_qualified_names_and_version_constraints }
+
+ context "with bare cookbook names" do
+
+ let(:recipes) { %w[ apache2 ] }
+
+ it "gives $cookbook_name::default" do
+ expect(fq_names).to eq( %w[ apache2::default ] )
+ end
+
+ end
+
+ context "with qualified recipe names but no versions" do
+
+ let(:recipes) { %w[ mysql::server ] }
+
+ it "returns the qualified recipe names" do
+ expect(fq_names).to eq( %w[ mysql::server ] )
+ end
+
+ end
+
+ context "with unqualified names that have version constraints" do
+
+ let(:versioned_recipes) do
+ [
+ {:name => "apt", :version => "~> 1.2.0"},
+ ]
+ end
+
+ it "gives qualified names with their versions" do
+ expect(fq_names).to eq([ "apt::default@~> 1.2.0" ])
+ end
+
+ it "does not mutate the recipe name" do
+ expect(fq_names).to eq([ "apt::default@~> 1.2.0" ])
+ expect(list).to eq( [ "apt" ] )
+ end
+
+ end
+
+ context "with fully qualified names that have version constraints" do
+
+ let(:versioned_recipes) do
+ [
+ {:name => "apt::cacher", :version => "~> 1.2.0"},
+ ]
+ end
+
+ it "gives qualified names with their versions" do
+ expect(fq_names).to eq([ "apt::cacher@~> 1.2.0" ])
+ end
+
+ it "does not mutate the recipe name" do
+ expect(fq_names).to eq([ "apt::cacher@~> 1.2.0" ])
+ expect(list).to eq( [ "apt::cacher" ] )
+ end
+
+ end
+ end
+
end
diff --git a/spec/unit/run_list_spec.rb b/spec/unit/run_list_spec.rb
index bf996de8c1..e150579431 100644
--- a/spec/unit/run_list_spec.rb
+++ b/spec/unit/run_list_spec.rb
@@ -307,7 +307,7 @@ describe Chef::RunList do
expect(Chef::JSONCompat.to_json(@run_list)).to eq(Chef::JSONCompat.to_json(["recipe[nagios::client]", "role[production]", "recipe[apache2]"]))
end
- include_examples "to_json equalivent to Chef::JSONCompat.to_json" do
+ include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
let(:jsonable) { @run_list }
end
diff --git a/spec/unit/search/query_spec.rb b/spec/unit/search/query_spec.rb
index 2fb197b183..59ac80f228 100644
--- a/spec/unit/search/query_spec.rb
+++ b/spec/unit/search/query_spec.rb
@@ -81,6 +81,9 @@ describe Chef::Search::Query do
end
describe "search" do
+ let(:query_string) { "search/node?q=platform:rhel&sort=X_CHEF_id_CHEF_X%20asc&start=0" }
+ let(:query_string_continue) { "search/node?q=platform:rhel&sort=X_CHEF_id_CHEF_X%20asc&start=4" }
+
let(:response) { {
"rows" => [
{ "name" => "my-name-is-node",
@@ -140,6 +143,19 @@ describe Chef::Search::Query do
"total" => 4
} }
+ let(:big_response) {
+ r = response.dup
+ r["total"] = 8
+ r
+ }
+
+ let(:big_response_end) {
+ r = response.dup
+ r["start"] = 4
+ r["total"] = 8
+ r
+ }
+
it "accepts a type as the first argument" do
expect { query.search("node") }.not_to raise_error
expect { query.search(:node) }.not_to raise_error
@@ -195,6 +211,14 @@ describe Chef::Search::Query do
query.search(:node, "*:*", sort: nil, start: 0, rows: 1) { |r| @call_me.do(r) }
end
+ it "sends multiple API requests when the server indicates there is more data" do
+ expect(rest).to receive(:get_rest).with(query_string).and_return(big_response)
+ expect(rest).to receive(:get_rest).with(query_string_continue).and_return(big_response_end)
+ query.search(:node, "platform:rhel") do |r|
+ nil
+ end
+ end
+
context "when :filter_result is provided as a result" do
include_context "filtered search" do
let(:filter_key) { :filter_result }
diff --git a/spec/unit/shell_spec.rb b/spec/unit/shell_spec.rb
index 0e028f4359..379043a017 100644
--- a/spec/unit/shell_spec.rb
+++ b/spec/unit/shell_spec.rb
@@ -43,6 +43,8 @@ describe Shell do
before do
Shell.irb_conf = {}
allow(Shell::ShellSession.instance).to receive(:reset!)
+ allow(ChefConfig).to receive(:windows?).and_return(false)
+ allow(Chef::Util::PathHelper).to receive(:home).and_return('/home/foo')
end
describe "reporting its status" do
@@ -56,7 +58,7 @@ describe Shell do
describe "configuring IRB" do
it "configures irb history" do
Shell.configure_irb
- expect(Shell.irb_conf[:HISTORY_FILE]).to eq("~/.chef/chef_shell_history")
+ expect(Shell.irb_conf[:HISTORY_FILE]).to eq(Chef::Util::PathHelper.home('.chef', 'chef_shell_history'))
expect(Shell.irb_conf[:SAVE_HISTORY]).to eq(1000)
end
@@ -69,7 +71,7 @@ describe Shell do
Shell.irb_conf[:IRB_RC].call(conf)
expect(conf.prompt_c).to eq("chef > ")
expect(conf.return_format).to eq(" => %s \n")
- expect(conf.prompt_i).to eq("chef > ")
+ expect(conf.prompt_i).to eq("chef (#{Chef::VERSION})> ")
expect(conf.prompt_n).to eq("chef ?> ")
expect(conf.prompt_s).to eq("chef%l> ")
expect(conf.use_tracer).to eq(false)
@@ -83,7 +85,7 @@ describe Shell do
conf.main = Chef::Recipe.new(nil,nil,Chef::RunContext.new(Chef::Node.new, {}, events))
Shell.irb_conf[:IRB_RC].call(conf)
expect(conf.prompt_c).to eq("chef:recipe > ")
- expect(conf.prompt_i).to eq("chef:recipe > ")
+ expect(conf.prompt_i).to eq("chef:recipe (#{Chef::VERSION})> ")
expect(conf.prompt_n).to eq("chef:recipe ?> ")
expect(conf.prompt_s).to eq("chef:recipe%l> ")
end
@@ -95,7 +97,7 @@ describe Shell do
conf.main = Chef::Node.new
Shell.irb_conf[:IRB_RC].call(conf)
expect(conf.prompt_c).to eq("chef:attributes > ")
- expect(conf.prompt_i).to eq("chef:attributes > ")
+ expect(conf.prompt_i).to eq("chef:attributes (#{Chef::VERSION})> ")
expect(conf.prompt_n).to eq("chef:attributes ?> ")
expect(conf.prompt_s).to eq("chef:attributes%l> ")
end
diff --git a/spec/unit/user_spec.rb b/spec/unit/user_spec.rb
index d451531b16..97cc32eb3e 100644
--- a/spec/unit/user_spec.rb
+++ b/spec/unit/user_spec.rb
@@ -16,6 +16,11 @@
# limitations under the License.
#
+# DEPRECATION NOTE
+# This code only remains to support users still operating with
+# Open Source Chef Server 11 and should be removed once support
+# for OSC 11 ends. New development should occur in user_spec.rb.
+
require 'spec_helper'
require 'chef/user'
@@ -155,7 +160,7 @@ describe Chef::User do
expect(@json).not_to include("password")
end
- include_examples "to_json equalivent to Chef::JSONCompat.to_json" do
+ include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
let(:jsonable) { @user }
end
end
@@ -200,8 +205,8 @@ describe Chef::User do
before (:each) do
@user = Chef::User.new
@user.name "foobar"
- @http_client = double("Chef::REST mock")
- allow(Chef::REST).to receive(:new).and_return(@http_client)
+ @http_client = double("Chef::ServerAPI mock")
+ allow(Chef::ServerAPI).to receive(:new).and_return(@http_client)
end
describe "list" do
@@ -214,24 +219,24 @@ describe Chef::User do
end
it "lists all clients on an OSC server" do
- allow(@http_client).to receive(:get_rest).with("users").and_return(@osc_response)
+ allow(@http_client).to receive(:get).with("users").and_return(@osc_response)
expect(Chef::User.list).to eq(@osc_response)
end
it "inflate all clients on an OSC server" do
- allow(@http_client).to receive(:get_rest).with("users").and_return(@osc_response)
+ allow(@http_client).to receive(:get).with("users").and_return(@osc_response)
expect(Chef::User.list(true)).to eq(@osc_inflated_response)
end
it "lists all clients on an OHC/OPC server" do
- allow(@http_client).to receive(:get_rest).with("users").and_return(@ohc_response)
+ allow(@http_client).to receive(:get).with("users").and_return(@ohc_response)
# We expect that Chef::User.list will give a consistent response
# so OHC API responses should be transformed to OSC-style output.
expect(Chef::User.list).to eq(@osc_response)
end
it "inflate all clients on an OHC/OPC server" do
- allow(@http_client).to receive(:get_rest).with("users").and_return(@ohc_response)
+ allow(@http_client).to receive(:get).with("users").and_return(@ohc_response)
expect(Chef::User.list(true)).to eq(@osc_inflated_response)
end
end
@@ -239,14 +244,14 @@ describe Chef::User do
describe "create" do
it "creates a new user via the API" do
@user.password "password"
- expect(@http_client).to receive(:post_rest).with("users", {:name => "foobar", :admin => false, :password => "password"}).and_return({})
+ expect(@http_client).to receive(:post).with("users", {:name => "foobar", :admin => false, :password => "password"}).and_return({})
@user.create
end
end
describe "read" do
it "loads a named user from the API" do
- expect(@http_client).to receive(:get_rest).with("users/foobar").and_return({"name" => "foobar", "admin" => true, "public_key" => "pubkey"})
+ expect(@http_client).to receive(:get).with("users/foobar").and_return({"name" => "foobar", "admin" => true, "public_key" => "pubkey"})
user = Chef::User.load("foobar")
expect(user.name).to eq("foobar")
expect(user.admin).to eq(true)
@@ -256,14 +261,14 @@ describe Chef::User do
describe "update" do
it "updates an existing user on via the API" do
- expect(@http_client).to receive(:put_rest).with("users/foobar", {:name => "foobar", :admin => false}).and_return({})
+ expect(@http_client).to receive(:put).with("users/foobar", {:name => "foobar", :admin => false}).and_return({})
@user.update
end
end
describe "destroy" do
it "deletes the specified user via the API" do
- expect(@http_client).to receive(:delete_rest).with("users/foobar")
+ expect(@http_client).to receive(:delete).with("users/foobar")
@user.destroy
end
end
diff --git a/spec/unit/user_v1_spec.rb b/spec/unit/user_v1_spec.rb
new file mode 100644
index 0000000000..8fd370a010
--- /dev/null
+++ b/spec/unit/user_v1_spec.rb
@@ -0,0 +1,584 @@
+#
+# Author:: Steven Danna (steve@opscode.com)
+# Copyright:: Copyright (c) 2012 Opscode, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'spec_helper'
+
+require 'chef/user_v1'
+require 'tempfile'
+
+describe Chef::UserV1 do
+ before(:each) do
+ @user = Chef::UserV1.new
+ end
+
+ shared_examples_for "string fields with no contraints" do
+ it "should let you set the public key" do
+ expect(@user.send(method, "some_string")).to eq("some_string")
+ end
+
+ it "should return the current public key" do
+ @user.send(method, "some_string")
+ expect(@user.send(method)).to eq("some_string")
+ end
+
+ it "should throw an ArgumentError if you feed it something lame" do
+ expect { @user.send(method, Hash.new) }.to raise_error(ArgumentError)
+ end
+ end
+
+ shared_examples_for "boolean fields with no constraints" do
+ it "should let you set the field" do
+ expect(@user.send(method, true)).to eq(true)
+ end
+
+ it "should return the current field value" do
+ @user.send(method, true)
+ expect(@user.send(method)).to eq(true)
+ end
+
+ it "should return the false value when false" do
+ @user.send(method, false)
+ expect(@user.send(method)).to eq(false)
+ end
+
+ it "should throw an ArgumentError if you feed it anything but true or false" do
+ expect { @user.send(method, Hash.new) }.to raise_error(ArgumentError)
+ end
+ end
+
+ describe "initialize" do
+ it "should be a Chef::UserV1" do
+ expect(@user).to be_a_kind_of(Chef::UserV1)
+ end
+ end
+
+ describe "username" do
+ it "should let you set the username to a string" do
+ expect(@user.username("ops_master")).to eq("ops_master")
+ end
+
+ it "should return the current username" do
+ @user.username "ops_master"
+ expect(@user.username).to eq("ops_master")
+ end
+
+ # It is not feasible to check all invalid characters. Here are a few
+ # that we probably care about.
+ it "should not accept invalid characters" do
+ # capital letters
+ expect { @user.username "Bar" }.to raise_error(ArgumentError)
+ # slashes
+ expect { @user.username "foo/bar" }.to raise_error(ArgumentError)
+ # ?
+ expect { @user.username "foo?" }.to raise_error(ArgumentError)
+ # &
+ expect { @user.username "foo&" }.to raise_error(ArgumentError)
+ end
+
+
+ it "should not accept spaces" do
+ expect { @user.username "ops master" }.to raise_error(ArgumentError)
+ end
+
+ it "should throw an ArgumentError if you feed it anything but a string" do
+ expect { @user.username Hash.new }.to raise_error(ArgumentError)
+ end
+ end
+
+ describe "boolean fields" do
+ describe "create_key" do
+ it_should_behave_like "boolean fields with no constraints" do
+ let(:method) { :create_key }
+ end
+ end
+ end
+
+ describe "string fields" do
+ describe "public_key" do
+ it_should_behave_like "string fields with no contraints" do
+ let(:method) { :public_key }
+ end
+ end
+
+ describe "private_key" do
+ it_should_behave_like "string fields with no contraints" do
+ let(:method) { :private_key }
+ end
+ end
+
+ describe "display_name" do
+ it_should_behave_like "string fields with no contraints" do
+ let(:method) { :display_name }
+ end
+ end
+
+ describe "first_name" do
+ it_should_behave_like "string fields with no contraints" do
+ let(:method) { :first_name }
+ end
+ end
+
+ describe "middle_name" do
+ it_should_behave_like "string fields with no contraints" do
+ let(:method) { :middle_name }
+ end
+ end
+
+ describe "last_name" do
+ it_should_behave_like "string fields with no contraints" do
+ let(:method) { :last_name }
+ end
+ end
+
+ describe "email" do
+ it_should_behave_like "string fields with no contraints" do
+ let(:method) { :email }
+ end
+ end
+
+ describe "password" do
+ it_should_behave_like "string fields with no contraints" do
+ let(:method) { :password }
+ end
+ end
+ end
+
+ describe "when serializing to JSON" do
+ before(:each) do
+ @user.username("black")
+ @json = @user.to_json
+ end
+
+ it "serializes as a JSON object" do
+ expect(@json).to match(/^\{.+\}$/)
+ end
+
+ it "includes the username value" do
+ expect(@json).to include(%q{"username":"black"})
+ end
+
+ it "includes the display name when present" do
+ @user.display_name("get_displayed")
+ expect(@user.to_json).to include(%{"display_name":"get_displayed"})
+ end
+
+ it "does not include the display name if not present" do
+ expect(@json).not_to include("display_name")
+ end
+
+ it "includes the first name when present" do
+ @user.first_name("char")
+ expect(@user.to_json).to include(%{"first_name":"char"})
+ end
+
+ it "does not include the first name if not present" do
+ expect(@json).not_to include("first_name")
+ end
+
+ it "includes the middle name when present" do
+ @user.middle_name("man")
+ expect(@user.to_json).to include(%{"middle_name":"man"})
+ end
+
+ it "does not include the middle name if not present" do
+ expect(@json).not_to include("middle_name")
+ end
+
+ it "includes the last name when present" do
+ @user.last_name("der")
+ expect(@user.to_json).to include(%{"last_name":"der"})
+ end
+
+ it "does not include the last name if not present" do
+ expect(@json).not_to include("last_name")
+ end
+
+ it "includes the email when present" do
+ @user.email("charmander@pokemon.poke")
+ expect(@user.to_json).to include(%{"email":"charmander@pokemon.poke"})
+ end
+
+ it "does not include the email if not present" do
+ expect(@json).not_to include("email")
+ end
+
+ it "includes the public key when present" do
+ @user.public_key("crowes")
+ expect(@user.to_json).to include(%{"public_key":"crowes"})
+ end
+
+ it "does not include the public key if not present" do
+ expect(@json).not_to include("public_key")
+ end
+
+ it "includes the private key when present" do
+ @user.private_key("monkeypants")
+ expect(@user.to_json).to include(%q{"private_key":"monkeypants"})
+ end
+
+ it "does not include the private key if not present" do
+ expect(@json).not_to include("private_key")
+ end
+
+ it "includes the password if present" do
+ @user.password "password"
+ expect(@user.to_json).to include(%q{"password":"password"})
+ end
+
+ it "does not include the password if not present" do
+ expect(@json).not_to include("password")
+ end
+
+ include_examples "to_json equivalent to Chef::JSONCompat.to_json" do
+ let(:jsonable) { @user }
+ end
+ end
+
+ describe "when deserializing from JSON" do
+ before(:each) do
+ user = {
+ "username" => "mr_spinks",
+ "display_name" => "displayed",
+ "first_name" => "char",
+ "middle_name" => "man",
+ "last_name" => "der",
+ "email" => "charmander@pokemon.poke",
+ "password" => "password",
+ "public_key" => "turtles",
+ "private_key" => "pandas",
+ "create_key" => false
+ }
+ @user = Chef::UserV1.from_json(Chef::JSONCompat.to_json(user))
+ end
+
+ it "should deserialize to a Chef::UserV1 object" do
+ expect(@user).to be_a_kind_of(Chef::UserV1)
+ end
+
+ it "preserves the username" do
+ expect(@user.username).to eq("mr_spinks")
+ end
+
+ it "preserves the display name if present" do
+ expect(@user.display_name).to eq("displayed")
+ end
+
+ it "preserves the first name if present" do
+ expect(@user.first_name).to eq("char")
+ end
+
+ it "preserves the middle name if present" do
+ expect(@user.middle_name).to eq("man")
+ end
+
+ it "preserves the last name if present" do
+ expect(@user.last_name).to eq("der")
+ end
+
+ it "preserves the email if present" do
+ expect(@user.email).to eq("charmander@pokemon.poke")
+ end
+
+ it "includes the password if present" do
+ expect(@user.password).to eq("password")
+ end
+
+ it "preserves the public key if present" do
+ expect(@user.public_key).to eq("turtles")
+ end
+
+ it "includes the private key if present" do
+ expect(@user.private_key).to eq("pandas")
+ end
+
+ it "includes the create key status if not nil" do
+ expect(@user.create_key).to be_falsey
+ end
+ end
+
+ describe "Versioned API Interactions" do
+ let(:response_406) { OpenStruct.new(:code => '406') }
+ let(:exception_406) { Net::HTTPServerException.new("406 Not Acceptable", response_406) }
+
+ before (:each) do
+ @user = Chef::UserV1.new
+ allow(@user).to receive(:chef_root_rest_v0).and_return(double('chef rest root v0 object'))
+ allow(@user).to receive(:chef_root_rest_v1).and_return(double('chef rest root v1 object'))
+ end
+
+ describe "update" do
+ before do
+ # populate all fields that are valid between V0 and V1
+ @user.username "some_username"
+ @user.display_name "some_display_name"
+ @user.first_name "some_first_name"
+ @user.middle_name "some_middle_name"
+ @user.last_name "some_last_name"
+ @user.email "some_email"
+ @user.password "some_password"
+ end
+
+ let(:payload) {
+ {
+ :username => "some_username",
+ :display_name => "some_display_name",
+ :first_name => "some_first_name",
+ :middle_name => "some_middle_name",
+ :last_name => "some_last_name",
+ :email => "some_email",
+ :password => "some_password"
+ }
+ }
+
+ context "when server API V1 is valid on the Chef Server receiving the request" do
+ context "when the user submits valid data" do
+ it "properly updates the user" do
+ expect(@user.chef_root_rest_v1).to receive(:put).with("users/some_username", payload).and_return({})
+ @user.update
+ end
+ end
+ end
+
+ context "when server API V1 is not valid on the Chef Server receiving the request" do
+ let(:payload) {
+ {
+ :username => "some_username",
+ :display_name => "some_display_name",
+ :first_name => "some_first_name",
+ :middle_name => "some_middle_name",
+ :last_name => "some_last_name",
+ :email => "some_email",
+ :password => "some_password",
+ :public_key => "some_public_key"
+ }
+ }
+
+ before do
+ @user.public_key "some_public_key"
+ allow(@user.chef_root_rest_v1).to receive(:put)
+ end
+
+ context "when the server returns a 400" do
+ let(:response_400) { OpenStruct.new(:code => '400') }
+ let(:exception_400) { Net::HTTPServerException.new("400 Bad Request", response_400) }
+
+ context "when the 400 was due to public / private key fields no longer being supported" do
+ let(:response_body_400) { '{"error":["Since Server API v1, all keys must be updated via the keys endpoint. "]}' }
+
+ before do
+ allow(response_400).to receive(:body).and_return(response_body_400)
+ allow(@user.chef_root_rest_v1).to receive(:put).and_raise(exception_400)
+ end
+
+ it "proceeds with the V0 PUT since it can handle public / private key fields" do
+ expect(@user.chef_root_rest_v0).to receive(:put).with("users/some_username", payload).and_return({})
+ @user.update
+ end
+
+ it "does not call server_client_api_version_intersection, since we know to proceed with V0 in this case" do
+ expect(@user).to_not receive(:server_client_api_version_intersection)
+ allow(@user.chef_root_rest_v0).to receive(:put).and_return({})
+ @user.update
+ end
+ end # when the 400 was due to public / private key fields
+
+ context "when the 400 was NOT due to public / private key fields no longer being supported" do
+ let(:response_body_400) { '{"error":["Some other error. "]}' }
+
+ before do
+ allow(response_400).to receive(:body).and_return(response_body_400)
+ allow(@user.chef_root_rest_v1).to receive(:put).and_raise(exception_400)
+ end
+
+ it "will not proceed with the V0 PUT since the original bad request was not key related" do
+ expect(@user.chef_root_rest_v0).to_not receive(:put).with("users/some_username", payload)
+ expect { @user.update }.to raise_error(exception_400)
+ end
+
+ it "raises the original error" do
+ expect { @user.update }.to raise_error(exception_400)
+ end
+
+ end
+ end # when the server returns a 400
+
+ context "when the server returns a 406" do
+ # from spec/support/shared/unit/api_versioning.rb
+ it_should_behave_like "version handling" do
+ let(:object) { @user }
+ let(:method) { :update }
+ let(:http_verb) { :put }
+ let(:rest_v1) { @user.chef_root_rest_v1 }
+ end
+
+ context "when the server supports API V0" do
+ before do
+ allow(@user).to receive(:server_client_api_version_intersection).and_return([0])
+ allow(@user.chef_root_rest_v1).to receive(:put).and_raise(exception_406)
+ end
+
+ it "properly updates the user" do
+ expect(@user.chef_root_rest_v0).to receive(:put).with("users/some_username", payload).and_return({})
+ @user.update
+ end
+ end # when the server supports API V0
+ end # when the server returns a 406
+
+ end # when server API V1 is not valid on the Chef Server receiving the request
+ end # update
+
+ describe "create" do
+ let(:payload) {
+ {
+ :username => "some_username",
+ :display_name => "some_display_name",
+ :first_name => "some_first_name",
+ :last_name => "some_last_name",
+ :email => "some_email",
+ :password => "some_password"
+ }
+ }
+ before do
+ @user.username "some_username"
+ @user.display_name "some_display_name"
+ @user.first_name "some_first_name"
+ @user.last_name "some_last_name"
+ @user.email "some_email"
+ @user.password "some_password"
+ end
+
+ # from spec/support/shared/unit/user_and_client_shared.rb
+ it_should_behave_like "user or client create" do
+ let(:object) { @user }
+ let(:error) { Chef::Exceptions::InvalidUserAttribute }
+ let(:rest_v0) { @user.chef_root_rest_v0 }
+ let(:rest_v1) { @user.chef_root_rest_v1 }
+ let(:url) { "users" }
+ end
+
+ context "when handling API V1" do
+ it "creates a new user via the API with a middle_name when it exists" do
+ @user.middle_name "some_middle_name"
+ expect(@user.chef_root_rest_v1).to receive(:post).with("users", payload.merge({:middle_name => "some_middle_name"})).and_return({})
+ @user.create
+ end
+ end # when server API V1 is valid on the Chef Server receiving the request
+
+ context "when API V1 is not supported by the server" do
+ # from spec/support/shared/unit/api_versioning.rb
+ it_should_behave_like "version handling" do
+ let(:object) { @user }
+ let(:method) { :create }
+ let(:http_verb) { :post }
+ let(:rest_v1) { @user.chef_root_rest_v1 }
+ end
+ end
+
+ context "when handling API V0" do
+ before do
+ allow(@user).to receive(:server_client_api_version_intersection).and_return([0])
+ allow(@user.chef_root_rest_v1).to receive(:post).and_raise(exception_406)
+ end
+
+ it "creates a new user via the API with a middle_name when it exists" do
+ @user.middle_name "some_middle_name"
+ expect(@user.chef_root_rest_v0).to receive(:post).with("users", payload.merge({:middle_name => "some_middle_name"})).and_return({})
+ @user.create
+ end
+ end # when server API V1 is not valid on the Chef Server receiving the request
+
+ end # create
+
+ # DEPRECATION
+ # This can be removed after API V0 support is gone
+ describe "reregister" do
+ let(:payload) {
+ {
+ "username" => "some_username",
+ }
+ }
+
+ before do
+ @user.username "some_username"
+ end
+
+ context "when server API V0 is valid on the Chef Server receiving the request" do
+ it "creates a new object via the API" do
+ expect(@user.chef_root_rest_v0).to receive(:put).with("users/#{@user.username}", payload.merge({"private_key" => true})).and_return({})
+ @user.reregister
+ end
+ end # when server API V0 is valid on the Chef Server receiving the request
+
+ context "when server API V0 is not supported by the Chef Server" do
+ # from spec/support/shared/unit/api_versioning.rb
+ it_should_behave_like "user and client reregister" do
+ let(:object) { @user }
+ let(:rest_v0) { @user.chef_root_rest_v0 }
+ end
+ end # when server API V0 is not supported by the Chef Server
+ end # reregister
+
+ end # Versioned API Interactions
+
+ describe "API Interactions" do
+ before (:each) do
+ @user = Chef::UserV1.new
+ @user.username "foobar"
+ @http_client = double("Chef::REST mock")
+ allow(Chef::REST).to receive(:new).and_return(@http_client)
+ end
+
+ describe "list" do
+ before(:each) do
+ Chef::Config[:chef_server_url] = "http://www.example.com"
+ @osc_response = { "admin" => "http://www.example.com/users/admin"}
+ @ohc_response = [ { "user" => { "username" => "admin" }} ]
+ allow(Chef::UserV1).to receive(:load).with("admin").and_return(@user)
+ @osc_inflated_response = { "admin" => @user }
+ end
+
+ it "lists all clients on an OHC/OPC server" do
+ allow(@http_client).to receive(:get).with("users").and_return(@ohc_response)
+ # We expect that Chef::UserV1.list will give a consistent response
+ # so OHC API responses should be transformed to OSC-style output.
+ expect(Chef::UserV1.list).to eq(@osc_response)
+ end
+
+ it "inflate all clients on an OHC/OPC server" do
+ allow(@http_client).to receive(:get).with("users").and_return(@ohc_response)
+ expect(Chef::UserV1.list(true)).to eq(@osc_inflated_response)
+ end
+ end
+
+ describe "read" do
+ it "loads a named user from the API" do
+ expect(@http_client).to receive(:get).with("users/foobar").and_return({"username" => "foobar", "admin" => true, "public_key" => "pubkey"})
+ user = Chef::UserV1.load("foobar")
+ expect(user.username).to eq("foobar")
+ expect(user.public_key).to eq("pubkey")
+ end
+ end
+
+ describe "destroy" do
+ it "deletes the specified user via the API" do
+ expect(@http_client).to receive(:delete).with("users/foobar")
+ @user.destroy
+ end
+ end
+ end
+end
diff --git a/spec/unit/util/dsc/resource_store.rb b/spec/unit/util/dsc/resource_store.rb
new file mode 100644
index 0000000000..a89e73fcaa
--- /dev/null
+++ b/spec/unit/util/dsc/resource_store.rb
@@ -0,0 +1,76 @@
+#
+# Author:: Jay Mundrawala <jdm@chef.io>
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef'
+require 'chef/util/dsc/resource_store'
+
+describe Chef::Util::DSC::ResourceStore do
+ let(:resource_store) { Chef::Util::DSC::ResourceStore.new }
+ let(:resource_a) { {
+ 'ResourceType' => 'AFoo',
+ 'Name' => 'Foo',
+ 'Module' => {'Name' => 'ModuleA'}
+ }
+ }
+
+ let(:resource_b) { {
+ 'ResourceType' => 'BFoo',
+ 'Name' => 'Foo',
+ 'Module' => {'Name' => 'ModuleB'}
+ }
+ }
+
+ context 'when resources are not cached' do
+ context 'when calling #resources' do
+ it 'returns an empty array' do
+ expect(resource_store.resources).to eql([])
+ end
+ end
+
+ context 'when calling #find' do
+ it 'returns an empty list if it cannot find any matching resources' do
+ expect(resource_store).to receive(:query_resource).and_return([])
+ expect(resource_store.find('foo')).to eql([])
+ end
+
+ it 'returns the resource if it is found (comparisons are case insensitive)' do
+ expect(resource_store).to receive(:query_resource).and_return([resource_a])
+ expect(resource_store.find('foo')).to eql([resource_a])
+ end
+
+ it 'returns multiple resoures if they are found' do
+ expect(resource_store).to receive(:query_resource).and_return([resource_a, resource_b])
+ expect(resource_store.find('foo')).to include(resource_a, resource_b)
+ end
+
+ it 'deduplicates resources by ResourceName' do
+ expect(resource_store).to receive(:query_resource).and_return([resource_a, resource_a])
+ resource_store.find('foo')
+ expect(resource_store.resources).to eq([resource_a])
+ end
+ end
+ end
+
+ context 'when resources are cached' do
+ it 'recalls resources from the cache if present' do
+ expect(resource_store).not_to receive(:query_resource)
+ expect(resource_store).to receive(:resources).and_return([resource_a])
+ resource_store.find('foo')
+ end
+ end
+end
diff --git a/spec/unit/util/path_helper_spec.rb b/spec/unit/util/path_helper_spec.rb
deleted file mode 100644
index 5756c29b90..0000000000
--- a/spec/unit/util/path_helper_spec.rb
+++ /dev/null
@@ -1,233 +0,0 @@
-#
-# Author:: Bryan McLellan <btm@loftninjas.org>
-# Copyright:: Copyright (c) 2014 Chef Software, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require 'chef/util/path_helper'
-require 'spec_helper'
-
-describe Chef::Util::PathHelper do
- PathHelper = Chef::Util::PathHelper
-
- [ false, true ].each do |is_windows|
- context "on #{is_windows ? "windows" : "unix"}" do
- before(:each) do
- allow(Chef::Platform).to receive(:windows?).and_return(is_windows)
- end
-
- describe "join" do
- it "joins components when some end with separators" do
- expected = PathHelper.cleanpath("/foo/bar/baz")
- expected = "C:#{expected}" if is_windows
- expect(PathHelper.join(is_windows ? 'C:\\foo\\' : "/foo/", "bar", "baz")).to eq(expected)
- end
-
- it "joins components when some end and start with separators" do
- expected = PathHelper.cleanpath("/foo/bar/baz")
- expected = "C:#{expected}" if is_windows
- expect(PathHelper.join(is_windows ? 'C:\\foo\\' : "/foo/", "bar/", "/baz")).to eq(expected)
- end
-
- it "joins components that don't end in separators" do
- expected = PathHelper.cleanpath("/foo/bar/baz")
- expected = "C:#{expected}" if is_windows
- expect(PathHelper.join(is_windows ? 'C:\\foo' : "/foo", "bar", "baz")).to eq(expected)
- end
-
- it "joins starting with '' resolve to absolute paths" do
- expect(PathHelper.join('', 'a', 'b')).to eq("#{PathHelper.path_separator}a#{PathHelper.path_separator}b")
- end
-
- it "joins ending with '' add a / to the end" do
- expect(PathHelper.join('a', 'b', '')).to eq("a#{PathHelper.path_separator}b#{PathHelper.path_separator}")
- end
-
- if is_windows
- it "joins components on Windows when some end with unix separators" do
- expect(PathHelper.join('C:\\foo/', "bar", "baz")).to eq('C:\\foo\\bar\\baz')
- end
- end
- end
-
- if is_windows
- it "path_separator is \\" do
- expect(PathHelper.path_separator).to eq('\\')
- end
- else
- it "path_separator is /" do
- expect(PathHelper.path_separator).to eq('/')
- end
- end
-
- if is_windows
- it "cleanpath changes slashes into backslashes and leaves backslashes alone" do
- expect(PathHelper.cleanpath('/a/b\\c/d/')).to eq('\\a\\b\\c\\d')
- end
- it "cleanpath does not remove leading double backslash" do
- expect(PathHelper.cleanpath('\\\\a/b\\c/d/')).to eq('\\\\a\\b\\c\\d')
- end
- else
- it "cleanpath removes extra slashes alone" do
- expect(PathHelper.cleanpath('/a///b/c/d/')).to eq('/a/b/c/d')
- end
- end
-
- describe "dirname" do
- it "dirname('abc') is '.'" do
- expect(PathHelper.dirname('abc')).to eq('.')
- end
- it "dirname('/') is '/'" do
- expect(PathHelper.dirname(PathHelper.path_separator)).to eq(PathHelper.path_separator)
- end
- it "dirname('a/b/c') is 'a/b'" do
- expect(PathHelper.dirname(PathHelper.join('a', 'b', 'c'))).to eq(PathHelper.join('a', 'b'))
- end
- it "dirname('a/b/c/') is 'a/b'" do
- expect(PathHelper.dirname(PathHelper.join('a', 'b', 'c', ''))).to eq(PathHelper.join('a', 'b'))
- end
- it "dirname('/a/b/c') is '/a/b'" do
- expect(PathHelper.dirname(PathHelper.join('', 'a', 'b', 'c'))).to eq(PathHelper.join('', 'a', 'b'))
- end
- end
- end
- end
-
- describe "validate_path" do
- context "on windows" do
- before(:each) do
- # pass by default
- allow(Chef::Platform).to receive(:windows?).and_return(true)
- allow(PathHelper).to receive(:printable?).and_return(true)
- allow(PathHelper).to receive(:windows_max_length_exceeded?).and_return(false)
- end
-
- it "returns the path if the path passes the tests" do
- expect(PathHelper.validate_path("C:\\ThisIsRigged")).to eql("C:\\ThisIsRigged")
- end
-
- it "does not raise an error if everything looks great" do
- expect { PathHelper.validate_path("C:\\cool path\\dude.exe") }.not_to raise_error
- end
-
- it "raises an error if the path has invalid characters" do
- allow(PathHelper).to receive(:printable?).and_return(false)
- expect { PathHelper.validate_path("Newline!\n") }.to raise_error(Chef::Exceptions::ValidationFailed)
- end
-
- it "Adds the \\\\?\\ prefix if the path exceeds MAX_LENGTH and does not have it" do
- long_path = "C:\\" + "a" * 250 + "\\" + "b" * 250
- prefixed_long_path = "\\\\?\\" + long_path
- allow(PathHelper).to receive(:windows_max_length_exceeded?).and_return(true)
- expect(PathHelper.validate_path(long_path)).to eql(prefixed_long_path)
- end
- end
- end
-
- describe "windows_max_length_exceeded?" do
- it "returns true if the path is too long (259 + NUL) for the API" do
- expect(PathHelper.windows_max_length_exceeded?("C:\\" + "a" * 250 + "\\" + "b" * 6)).to be_truthy
- end
-
- it "returns false if the path is not too long (259 + NUL) for the standard API" do
- expect(PathHelper.windows_max_length_exceeded?("C:\\" + "a" * 250 + "\\" + "b" * 5)).to be_falsey
- end
-
- it "returns false if the path is over 259 characters but uses the \\\\?\\ prefix" do
- expect(PathHelper.windows_max_length_exceeded?("\\\\?\\C:\\" + "a" * 250 + "\\" + "b" * 250)).to be_falsey
- end
- end
-
- describe "printable?" do
- it "returns true if the string contains no non-printable characters" do
- expect(PathHelper.printable?("C:\\Program Files (x86)\\Microsoft Office\\Files.lst")).to be_truthy
- end
-
- it "returns true when given 'abc' in unicode" do
- expect(PathHelper.printable?("\u0061\u0062\u0063")).to be_truthy
- end
-
- it "returns true when given japanese unicode" do
- expect(PathHelper.printable?("\uff86\uff87\uff88")).to be_truthy
- end
-
- it "returns false if the string contains a non-printable character" do
- expect(PathHelper.printable?("\my files\work\notes.txt")).to be_falsey
- end
-
- # This isn't necessarily a requirement, but here to be explicit about functionality.
- it "returns false if the string contains a newline or tab" do
- expect(PathHelper.printable?("\tThere's no way,\n\t *no* way,\n\t that you came from my loins.\n")).to be_falsey
- end
- end
-
- describe "canonical_path" do
- context "on windows", :windows_only do
- it "returns an absolute path with backslashes instead of slashes" do
- expect(PathHelper.canonical_path("\\\\?\\C:/windows/win.ini")).to eq("\\\\?\\c:\\windows\\win.ini")
- end
-
- it "adds the \\\\?\\ prefix if it is missing" do
- expect(PathHelper.canonical_path("C:/windows/win.ini")).to eq("\\\\?\\c:\\windows\\win.ini")
- end
-
- it "returns a lowercase path" do
- expect(PathHelper.canonical_path("\\\\?\\C:\\CASE\\INSENSITIVE")).to eq("\\\\?\\c:\\case\\insensitive")
- end
- end
-
- context "not on windows", :unix_only do
- it "returns a canonical path" do
- expect(PathHelper.canonical_path("/etc//apache.d/sites-enabled/../sites-available/default")).to eq("/etc/apache.d/sites-available/default")
- end
- end
- end
-
- describe "paths_eql?" do
- it "returns true if the paths are the same" do
- allow(PathHelper).to receive(:canonical_path).with("bandit").and_return("c:/bandit/bandit")
- allow(PathHelper).to receive(:canonical_path).with("../bandit/bandit").and_return("c:/bandit/bandit")
- expect(PathHelper.paths_eql?("bandit", "../bandit/bandit")).to be_truthy
- end
-
- it "returns false if the paths are different" do
- allow(PathHelper).to receive(:canonical_path).with("bandit").and_return("c:/Bo/Bandit")
- allow(PathHelper).to receive(:canonical_path).with("../bandit/bandit").and_return("c:/bandit/bandit")
- expect(PathHelper.paths_eql?("bandit", "../bandit/bandit")).to be_falsey
- end
- end
-
- describe "escape_glob" do
- it "escapes characters reserved by glob" do
- path = "C:\\this\\*path\\[needs]\\escaping?"
- escaped_path = "C:\\\\this\\\\\\*path\\\\\\[needs\\]\\\\escaping\\?"
- expect(PathHelper.escape_glob(path)).to eq(escaped_path)
- end
-
- context "when given more than one argument" do
- it "joins, cleanpaths, and escapes characters reserved by glob" do
- args = ["this/*path", "[needs]", "escaping?"]
- escaped_path = if windows?
- "this\\\\\\*path\\\\\\[needs\\]\\\\escaping\\?"
- else
- "this/\\*path/\\[needs\\]/escaping\\?"
- end
- expect(PathHelper).to receive(:join).with(*args).and_call_original
- expect(PathHelper).to receive(:cleanpath).and_call_original
- expect(PathHelper.escape_glob(*args)).to eq(escaped_path)
- end
- end
- end
-end
diff --git a/spec/unit/util/powershell/ps_credential_spec.rb b/spec/unit/util/powershell/ps_credential_spec.rb
new file mode 100644
index 0000000000..bac58b02e5
--- /dev/null
+++ b/spec/unit/util/powershell/ps_credential_spec.rb
@@ -0,0 +1,37 @@
+#
+# Author:: Jay Mundrawala <jdm@chef.io>
+# Copyright:: Copyright (c) 2015 Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'chef'
+require 'chef/util/powershell/ps_credential'
+
+describe Chef::Util::Powershell::PSCredential do
+ let (:username) { 'foo' }
+ let (:password) { 'password' }
+
+ context 'when username and password are provided' do
+ let(:ps_credential) { Chef::Util::Powershell::PSCredential.new(username, password)}
+ context 'when calling to_psobject' do
+ it 'should create the script to create a PSCredential when calling' do
+ allow(ps_credential).to receive(:encrypt).with(password).and_return('encrypted')
+ expect(ps_credential.to_psobject).to eq(
+ "New-Object System.Management.Automation.PSCredential("\
+ "'#{username}',('encrypted' | ConvertTo-SecureString))")
+ end
+ end
+ end
+end
diff --git a/spec/unit/workstation_config_loader_spec.rb b/spec/unit/workstation_config_loader_spec.rb
deleted file mode 100644
index a865103188..0000000000
--- a/spec/unit/workstation_config_loader_spec.rb
+++ /dev/null
@@ -1,283 +0,0 @@
-#
-# Author:: Daniel DeLeo (<dan@getchef.com>)
-# Copyright:: Copyright (c) 2014 Chef Software, Inc.
-# License:: Apache License, Version 2.0
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-require 'spec_helper'
-require 'tempfile'
-require 'chef/workstation_config_loader'
-
-describe Chef::WorkstationConfigLoader do
-
- let(:explicit_config_location) { nil }
-
- let(:env) { {} }
-
- let(:config_loader) do
- described_class.new(explicit_config_location).tap do |c|
- allow(c).to receive(:env).and_return(env)
- end
- end
-
- # Test methods that do I/O or reference external state which are stubbed out
- # elsewhere.
- describe "external dependencies" do
- let(:config_loader) { described_class.new(nil) }
-
- it "delegates to ENV for env" do
- expect(config_loader.env).to equal(ENV)
- end
-
- it "tests a path's existence" do
- expect(config_loader.path_exists?('/nope/nope/nope/nope/frab/jab/nab')).to be(false)
- expect(config_loader.path_exists?(__FILE__)).to be(true)
- end
-
- end
-
- describe "locating the config file" do
- context "without an explicit config" do
-
- before do
- allow(config_loader).to receive(:path_exists?).with(an_instance_of(String)).and_return(false)
- end
-
- it "has no config if HOME is not set" do
- expect(config_loader.config_location).to be(nil)
- expect(config_loader.no_config_found?).to be(true)
- end
-
- context "when HOME is set and contains a knife.rb" do
-
- let(:home) { "/Users/example.user" }
-
- before do
- env["HOME"] = home
- allow(config_loader).to receive(:path_exists?).with("#{home}/.chef/knife.rb").and_return(true)
- end
-
- it "uses the config in HOME/.chef/knife.rb" do
- expect(config_loader.config_location).to eq("#{home}/.chef/knife.rb")
- end
-
- context "and has a config.rb" do
-
- before do
- allow(config_loader).to receive(:path_exists?).with("#{home}/.chef/config.rb").and_return(true)
- end
-
- it "uses the config in HOME/.chef/config.rb" do
- expect(config_loader.config_location).to eq("#{home}/.chef/config.rb")
- end
-
- context "and/or a parent dir contains a .chef dir" do
-
- let(:env_pwd) { "/path/to/cwd" }
-
- before do
- if Chef::Platform.windows?
- env["CD"] = env_pwd
- else
- env["PWD"] = env_pwd
- end
-
- allow(config_loader).to receive(:path_exists?).with("#{env_pwd}/.chef/knife.rb").and_return(true)
- allow(File).to receive(:exist?).with("#{env_pwd}/.chef").and_return(true)
- allow(File).to receive(:directory?).with("#{env_pwd}/.chef").and_return(true)
- end
-
- it "prefers the config from parent_dir/.chef" do
- expect(config_loader.config_location).to eq("#{env_pwd}/.chef/knife.rb")
- end
-
- context "and the parent dir's .chef dir has a config.rb" do
-
- before do
- allow(config_loader).to receive(:path_exists?).with("#{env_pwd}/.chef/config.rb").and_return(true)
- end
-
- it "prefers the config from parent_dir/.chef" do
- expect(config_loader.config_location).to eq("#{env_pwd}/.chef/config.rb")
- end
-
- context "and/or the current working directory contains a .chef dir" do
-
- let(:cwd) { Dir.pwd }
-
- before do
- allow(config_loader).to receive(:path_exists?).with("#{cwd}/knife.rb").and_return(true)
- end
-
- it "prefers a knife.rb located in the cwd" do
- expect(config_loader.config_location).to eq("#{cwd}/knife.rb")
- end
-
- context "and the CWD's .chef dir has a config.rb" do
-
- before do
- allow(config_loader).to receive(:path_exists?).with("#{cwd}/config.rb").and_return(true)
- end
-
- it "prefers a config located in the cwd" do
- expect(config_loader.config_location).to eq("#{cwd}/config.rb")
- end
-
-
- context "and/or KNIFE_HOME is set" do
-
- let(:knife_home) { "/path/to/knife/home" }
-
- before do
- env["KNIFE_HOME"] = knife_home
- allow(config_loader).to receive(:path_exists?).with("#{knife_home}/knife.rb").and_return(true)
- end
-
- it "prefers a knife located in KNIFE_HOME" do
- expect(config_loader.config_location).to eq("/path/to/knife/home/knife.rb")
- end
-
- context "and KNIFE_HOME contains a config.rb" do
-
- before do
- env["KNIFE_HOME"] = knife_home
- allow(config_loader).to receive(:path_exists?).with("#{knife_home}/config.rb").and_return(true)
- end
-
- it "prefers a config.rb located in KNIFE_HOME" do
- expect(config_loader.config_location).to eq("/path/to/knife/home/config.rb")
- end
-
- end
-
- end
- end
- end
- end
- end
- end
- end
-
- context "when the current working dir is inside a symlinked directory" do
- before do
- # pwd according to your shell is /home/someuser/prod/chef-repo, but
- # chef-repo is a symlink to /home/someuser/codes/chef-repo
- env["CD"] = "/home/someuser/prod/chef-repo" # windows
- env["PWD"] = "/home/someuser/prod/chef-repo" # unix
-
- allow(Dir).to receive(:pwd).and_return("/home/someuser/codes/chef-repo")
- end
-
- it "loads the config from the non-dereferenced directory path" do
- expect(File).to receive(:exist?).with("/home/someuser/prod/chef-repo/.chef").and_return(false)
- expect(File).to receive(:exist?).with("/home/someuser/prod/.chef").and_return(true)
- expect(File).to receive(:directory?).with("/home/someuser/prod/.chef").and_return(true)
-
- expect(config_loader).to receive(:path_exists?).with("/home/someuser/prod/.chef/knife.rb").and_return(true)
-
- expect(config_loader.config_location).to eq("/home/someuser/prod/.chef/knife.rb")
- end
- end
- end
-
- context "when given an explicit config to load" do
-
- let(:explicit_config_location) { "/path/to/explicit/config.rb" }
-
- it "prefers the explicit config" do
- expect(config_loader.config_location).to eq(explicit_config_location)
- end
-
- end
- end
-
-
- describe "loading the config file" do
-
- context "when no explicit config is specifed and no implicit config is found" do
-
- before do
- allow(config_loader).to receive(:path_exists?).with(an_instance_of(String)).and_return(false)
- end
-
- it "skips loading" do
- expect(config_loader.config_location).to be(nil)
- expect(config_loader.load).to be(false)
- end
-
- end
-
- context "when an explicit config is given but it doesn't exist" do
-
- let(:explicit_config_location) { "/nope/nope/nope/frab/jab/nab" }
-
- it "raises a configuration error" do
- expect { config_loader.load }.to raise_error(Chef::Exceptions::ConfigurationError)
- end
-
- end
-
- context "when the config file exists" do
-
- let(:config_content) { "" }
-
- let(:explicit_config_location) do
- # could use described_class, but remove all ':' from the path if so.
- t = Tempfile.new("Chef-WorkstationConfigLoader-rspec-test")
- t.print(config_content)
- t.close
- t.path
- end
-
- after { File.unlink(explicit_config_location) if File.exists?(explicit_config_location) }
-
- context "and is valid" do
-
- let(:config_content) { "config_file_evaluated(true)" }
-
- it "loads the config" do
- expect(config_loader.load).to be(true)
- expect(Chef::Config.config_file_evaluated).to be(true)
- end
-
- it "sets Chef::Config.config_file" do
- config_loader.load
- expect(Chef::Config.config_file).to eq(explicit_config_location)
- end
- end
-
- context "and has a syntax error" do
-
- let(:config_content) { "{{{{{:{{" }
-
- it "raises a ConfigurationError" do
- expect { config_loader.load }.to raise_error(Chef::Exceptions::ConfigurationError)
- end
- end
-
- context "and raises a ruby exception during evaluation" do
-
- let(:config_content) { ":foo\n:bar\nraise 'oops'\n:baz\n" }
-
- it "raises a ConfigurationError" do
- expect { config_loader.load }.to raise_error(Chef::Exceptions::ConfigurationError)
- end
- end
-
- end
-
- end
-
-end