summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-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/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/provider/whyrun_safe_ruby_block_spec.rb2
-rw-r--r--spec/functional/rebooter_spec.rb2
-rw-r--r--spec/functional/resource/deploy_revision_spec.rb2
-rw-r--r--spec/functional/resource/dsc_script_spec.rb93
-rw-r--r--spec/functional/resource/group_spec.rb111
-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)48
-rw-r--r--spec/functional/win32/registry_helper_spec.rb12
-rw-r--r--spec/functional/win32/service_manager_spec.rb4
-rw-r--r--spec/integration/client/client_spec.rb53
-rw-r--r--spec/integration/knife/chef_repo_path_spec.rb24
-rw-r--r--spec/integration/recipes/lwrp_spec.rb8
-rw-r--r--spec/integration/recipes/recipe_dsl_spec.rb1055
-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/spec_helper.rb37
-rw-r--r--spec/support/platform_helpers.rb8
-rw-r--r--spec/support/shared/functional/win32_service.rb3
-rw-r--r--spec/support/shared/shared_examples.rb2
-rw-r--r--spec/support/shared/unit/api_versioning.rb4
-rw-r--r--spec/unit/api_client_spec.rb223
-rw-r--r--spec/unit/api_client_v1_spec.rb457
-rw-r--r--spec/unit/application/solo_spec.rb7
-rw-r--r--spec/unit/chef_class_spec.rb27
-rw-r--r--spec/unit/chef_fs/path_util_spec.rb108
-rw-r--r--spec/unit/client_spec.rb54
-rw-r--r--spec/unit/config_spec.rb31
-rw-r--r--spec/unit/cookbook/metadata_spec.rb26
-rw-r--r--spec/unit/cookbook/syntax_check_spec.rb1
-rw-r--r--spec/unit/cookbook_version_spec.rb2
-rw-r--r--spec/unit/data_bag_item_spec.rb2
-rw-r--r--spec/unit/data_bag_spec.rb2
-rw-r--r--spec/unit/deprecation_spec.rb9
-rw-r--r--spec/unit/dsl/reboot_pending_spec.rb2
-rw-r--r--spec/unit/environment_spec.rb2
-rw-r--r--spec/unit/event_dispatch/dispatcher_spec.rb23
-rw-r--r--spec/unit/event_dispatch/dsl_spec.rb83
-rw-r--r--spec/unit/exceptions_spec.rb2
-rw-r--r--spec/unit/formatters/doc_spec.rb6
-rw-r--r--spec/unit/formatters/error_inspectors/compile_error_inspector_spec.rb26
-rw-r--r--spec/unit/json_compat_spec.rb9
-rw-r--r--spec/unit/knife/bootstrap/client_builder_spec.rb16
-rw-r--r--spec/unit/knife/client_bulk_delete_spec.rb8
-rw-r--r--spec/unit/knife/client_create_spec.rb2
-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/bootstrap_context_spec.rb7
-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/node_run_list_remove_spec.rb17
-rw-r--r--spec/unit/knife/osc_user_create_spec.rb10
-rw-r--r--spec/unit/knife/osc_user_delete_spec.rb2
-rw-r--r--spec/unit/knife/osc_user_edit_spec.rb2
-rw-r--r--spec/unit/knife/osc_user_list_spec.rb2
-rw-r--r--spec/unit/knife/osc_user_reregister_spec.rb2
-rw-r--r--spec/unit/knife/osc_user_show_spec.rb2
-rw-r--r--spec/unit/knife/ssl_check_spec.rb4
-rw-r--r--spec/unit/knife/user_create_spec.rb2
-rw-r--r--spec/unit/knife/user_delete_spec.rb4
-rw-r--r--spec/unit/knife/user_edit_spec.rb4
-rw-r--r--spec/unit/knife/user_list_spec.rb2
-rw-r--r--spec/unit/knife/user_reregister_spec.rb2
-rw-r--r--spec/unit/knife/user_show_spec.rb4
-rw-r--r--spec/unit/lwrp_spec.rb327
-rw-r--r--spec/unit/mixin/params_validate_spec.rb6
-rw-r--r--spec/unit/mixin/windows_architecture_helper_spec.rb21
-rw-r--r--spec/unit/node_map_spec.rb12
-rw-r--r--spec/unit/node_spec.rb9
-rw-r--r--spec/unit/osc_user_spec.rb276
-rw-r--r--spec/unit/platform_spec.rb2
-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_spec.rb12
-rw-r--r--spec/unit/provider/directory_spec.rb35
-rw-r--r--spec/unit/provider/dsc_resource_spec.rb6
-rw-r--r--spec/unit/provider/dsc_script_spec.rb4
-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/dpkg_spec.rb9
-rw-r--r--spec/unit/provider/package/rpm_spec.rb459
-rw-r--r--spec/unit/provider/package/rubygems_spec.rb18
-rw-r--r--spec/unit/provider/package/yum_spec.rb121
-rw-r--r--spec/unit/provider/powershell_script_spec.rb (renamed from spec/unit/provider/powershell_spec.rb)0
-rw-r--r--spec/unit/provider/registry_key_spec.rb12
-rw-r--r--spec/unit/provider/service/aix_service_spec.rb6
-rw-r--r--spec/unit/provider/service/gentoo_service_spec.rb8
-rw-r--r--spec/unit/provider/service/macosx_spec.rb8
-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_resolver_spec.rb306
-rw-r--r--spec/unit/provider_spec.rb4
-rw-r--r--spec/unit/recipe_spec.rb26
-rw-r--r--spec/unit/registry_helper_spec.rb16
-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.rb8
-rw-r--r--spec/unit/resource/directory_spec.rb2
-rw-r--r--spec/unit/resource/dsc_resource_spec.rb2
-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/link_spec.rb2
-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)0
-rw-r--r--spec/unit/resource/registry_key_spec.rb2
-rw-r--r--spec/unit/resource/route_spec.rb2
-rw-r--r--spec/unit/resource/ruby_block_spec.rb6
-rw-r--r--spec/unit/resource/service_spec.rb8
-rw-r--r--spec/unit/resource/user_spec.rb2
-rw-r--r--spec/unit/resource/windows_service_spec.rb2
-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.rb113
-rw-r--r--spec/unit/role_spec.rb2
-rw-r--r--spec/unit/run_context/child_run_context_spec.rb133
-rw-r--r--spec/unit/run_context_spec.rb7
-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/runner_spec.rb4
-rw-r--r--spec/unit/user_spec.rb504
-rw-r--r--spec/unit/user_v1_spec.rb584
-rw-r--r--spec/unit/workstation_config_loader_spec.rb283
145 files changed, 8077 insertions, 2343 deletions
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/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/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 485e98f247..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
diff --git a/spec/functional/resource/deploy_revision_spec.rb b/spec/functional/resource/deploy_revision_spec.rb
index e5f5341fcd..4bce309a51 100644
--- a/spec/functional/resource/deploy_revision_spec.rb
+++ b/spec/functional/resource/deploy_revision_spec.rb
@@ -819,7 +819,7 @@ describe Chef::Resource::DeployRevision, :unix_only => true do
end
before do
- expect { deploy_that_fails.run_action(:deploy) }.to raise_error(Chef::Exceptions::Exec)
+ expect { deploy_that_fails.run_action(:deploy) }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
deploy_to_latest_with_callback_tracking.run_action(:deploy)
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/group_spec.rb b/spec/functional/resource/group_spec.rb
index 529af52d4e..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,44 +135,75 @@ 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]] }
-
- before do
- create_user(spec_members[1])
- create_user(spec_members[0])
- add_members_to_group([spec_members[0]])
- end
+ # 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) { [] }
- 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
@@ -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
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 17ae8cbd2a..be744e748b 100644
--- a/spec/functional/resource/powershell_spec.rb
+++ b/spec/functional/resource/powershell_script_spec.rb
@@ -125,16 +125,16 @@ describe Chef::Resource::WindowsScript::PowershellScript, :windows_only do
expect { resource.run_action(:run) }.not_to raise_error
end
- it "raises an error if the script is not syntactically correct and returns is not set to 1" do
+ 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 "returns 1 if the script provided to the code attribute is not syntactically correct" do
+ 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) }.not_to raise_error
+ expect { resource.run_action(:run) }.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
end
# This somewhat ambiguous case, two failures of different types,
@@ -227,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)
@@ -240,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/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/integration/client/client_spec.rb b/spec/integration/client/client_spec.rb
index 8c72048965..1a030c130b 100644
--- a/spec/integration/client/client_spec.rb
+++ b/spec/integration/client/client_spec.rb
@@ -303,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
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/recipes/lwrp_spec.rb b/spec/integration/recipes/lwrp_spec.rb
index e93763fddc..7ecdfc7c3a 100644
--- a/spec/integration/recipes/lwrp_spec.rb
+++ b/spec/integration/recipes/lwrp_spec.rb
@@ -45,12 +45,8 @@ 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)
- actual = result.stdout.lines.map { |l| l.chomp }.join("\n")
- expected = <<EOM
- * l_w_r_p_foo[me] action create (up to date)
-EOM
- expected = expected.lines.map { |l| l.chomp }.join("\n")
- expect(actual).to include(expected)
+ 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
diff --git a/spec/integration/recipes/recipe_dsl_spec.rb b/spec/integration/recipes/recipe_dsl_spec.rb
index 3f4bf9fd5f..52bca87c99 100644
--- a/spec/integration/recipes/recipe_dsl_spec.rb
+++ b/spec/integration/recipes/recipe_dsl_spec.rb
@@ -11,19 +11,15 @@ describe "Recipe DSL methods" do
before(:all) { Namer.current_index = 1 }
before { Namer.current_index += 1 }
- context "With resource 'base_thingy' declared as BaseThingy" do
+ context "with resource 'base_thingy' declared as BaseThingy" do
before(:context) {
class BaseThingy < Chef::Resource
- def initialize(*args, &block)
- super
- @allowed_actions = [ :create ]
- @action = :create
- end
-
resource_name 'base_thingy'
+ default_action :create
class<<self
+ attr_accessor :created_name
attr_accessor :created_resource
attr_accessor :created_provider
end
@@ -35,6 +31,7 @@ describe "Recipe DSL methods" do
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
@@ -52,20 +49,42 @@ describe "Recipe DSL methods" do
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
+ context "with a resource 'backcompat_thingy' declared in Chef::Resource and Chef::Provider" do
before(:context) {
class Chef::Resource::BackcompatThingy < Chef::Resource
- def initialize(*args, &block)
- super
- @allowed_actions = [ :create ]
- @action = :create
- end
+ default_action :create
end
class Chef::Provider::BackcompatThingy < Chef::Provider
def load_current_resource
@@ -100,13 +119,13 @@ describe "Recipe DSL methods" do
recipe = converge {
backcompat_thingy 'blah' do; end
}
- expect(recipe.logged_warnings).to eq ''
+ 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
+ context "with a resource named RecipeDSLSpecNamespace::Bar::BarThingy" do
before(:context) {
class RecipeDSLSpecNamespace::Bar::BarThingy < BaseThingy
@@ -114,19 +133,17 @@ describe "Recipe DSL methods" do
}
- it "bar_thingy works" do
- recipe = converge {
+ it "bar_thingy does not work" do
+ expect_converge {
bar_thingy 'blah' do; end
- }
- expect(recipe.logged_warnings).to eq ''
- expect(BaseThingy.created_resource).to eq(RecipeDSLSpecNamespace::Bar::BarThingy)
+ }.to raise_error(NoMethodError)
end
end
- context "With a resource named NoNameThingy with resource_name nil" do
+ context "with a resource named Chef::Resource::NoNameThingy with resource_name nil" do
before(:context) {
- class NoNameThingy < BaseThingy
+ class Chef::Resource::NoNameThingy < BaseThingy
resource_name nil
end
@@ -134,12 +151,12 @@ describe "Recipe DSL methods" do
it "no_name_thingy does not work" do
expect_converge {
- thingy 'blah' do; end
+ 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
+ context "with a resource named AnotherNoNameThingy with resource_name :another_thingy_name" do
before(:context) {
class AnotherNoNameThingy < BaseThingy
@@ -163,7 +180,7 @@ describe "Recipe DSL methods" do
end
end
- context "With a resource named AnotherNoNameThingy2 with resource_name :another_thingy_name2; resource_name :another_thingy_name3" do
+ context "with a resource named AnotherNoNameThingy2 with resource_name :another_thingy_name2; resource_name :another_thingy_name3" do
before(:context) {
class AnotherNoNameThingy2 < BaseThingy
@@ -195,10 +212,11 @@ describe "Recipe DSL methods" do
end
context "provides overriding resource_name" do
- context "With a resource named AnotherNoNameThingy3 with provides :another_no_name_thingy3, os: 'blarghle'" 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
@@ -223,10 +241,11 @@ describe "Recipe DSL methods" do
end
end
- context "With a resource named AnotherNoNameThingy4 with two provides" do
+ 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
@@ -262,7 +281,7 @@ describe "Recipe DSL methods" do
end
end
- context "With a resource named AnotherNoNameThingy5, a different resource_name, and a provides with the original resource_name" do
+ context "with a resource named AnotherNoNameThingy5, a different resource_name, and a provides with the original resource_name" do
before(:context) {
class AnotherNoNameThingy5 < BaseThingy
@@ -299,7 +318,7 @@ describe "Recipe DSL methods" do
end
end
- context "With a resource named AnotherNoNameThingy6, a provides with the original resource name, and a different resource_name" do
+ context "with a resource named AnotherNoNameThingy6, a provides with the original resource name, and a different resource_name" do
before(:context) {
class AnotherNoNameThingy6 < BaseThingy
@@ -336,7 +355,7 @@ describe "Recipe DSL methods" do
end
end
- context "With a resource named AnotherNoNameThingy7, a new resource_name, and provides with that new resource name" do
+ context "with a resource named AnotherNoNameThingy7, a new resource_name, and provides with that new resource name" do
before(:context) {
class AnotherNoNameThingy7 < BaseThingy
@@ -374,7 +393,7 @@ describe "Recipe DSL methods" do
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
+ 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
@@ -410,116 +429,6 @@ describe "Recipe DSL methods" do
}.to raise_error(NoMethodError)
end
end
-
- context "With a resource TwoClassesOneDsl" do
- let(:class_name) { "TwoClassesOneDsl#{Namer.current_index}" }
- let(:dsl_method) { :"two_classes_one_dsl#{Namer.current_index}" }
-
- before {
- eval <<-EOM, nil, __FILE__, __LINE__+1
- class #{class_name} < BaseThingy
- end
- EOM
- }
- context "and resource BlahModule::TwoClassesOneDsl" do
- before {
- eval <<-EOM, nil, __FILE__, __LINE__+1
- module BlahModule
- class #{class_name} < BaseThingy
- end
- end
- EOM
- }
- it "two_classes_one_dsl resolves to BlahModule::TwoClassesOneDsl (last declared)" do
- dsl_method = self.dsl_method
- recipe = converge {
- instance_eval("#{dsl_method} 'blah' do; end")
- }
- expect(recipe.logged_warnings).to eq ''
- expect(BaseThingy.created_resource).to eq eval("BlahModule::#{class_name}")
- end
- it "resource_matching_short_name returns BlahModule::TwoClassesOneDsl" do
- expect(Chef::Resource.resource_matching_short_name(dsl_method)).to eq eval("BlahModule::#{class_name}")
- end
- end
- context "and resource BlahModule::TwoClassesOneDsl with resource_name nil" do
- before {
- eval <<-EOM, nil, __FILE__, __LINE__+1
- module BlahModule
- class BlahModule::#{class_name} < BaseThingy
- resource_name nil
- end
- end
- EOM
- }
- it "two_classes_one_dsl resolves to ::TwoClassesOneDsl" do
- dsl_method = self.dsl_method
- recipe = converge {
- instance_eval("#{dsl_method} 'blah' do; end")
- }
- expect(recipe.logged_warnings).to eq ''
- expect(BaseThingy.created_resource).to eq eval("::#{class_name}")
- end
- it "resource_matching_short_name returns ::TwoClassesOneDsl" do
- expect(Chef::Resource.resource_matching_short_name(dsl_method)).to eq eval("::#{class_name}")
- end
- end
- context "and resource BlahModule::TwoClassesOneDsl with resource_name :argh" do
- before {
- eval <<-EOM, nil, __FILE__, __LINE__+1
- module BlahModule
- class BlahModule::#{class_name} < BaseThingy
- resource_name :argh
- end
- end
- EOM
- }
- it "two_classes_one_dsl resolves to ::TwoClassesOneDsl" do
- dsl_method = self.dsl_method
- recipe = converge {
- instance_eval("#{dsl_method} 'blah' do; end")
- }
- expect(recipe.logged_warnings).to eq ''
- expect(BaseThingy.created_resource).to eq eval("::#{class_name}")
- end
- it "resource_matching_short_name returns ::TwoClassesOneDsl" do
- expect(Chef::Resource.resource_matching_short_name(dsl_method)).to eq eval("::#{class_name}")
- end
- end
- context "and resource BlahModule::TwoClassesOneDsl with provides :two_classes_one_dsl, os: 'blarghle'" do
- before {
- eval <<-EOM, nil, __FILE__, __LINE__+1
- module BlahModule
- class BlahModule::#{class_name} < BaseThingy
- provides #{dsl_method.inspect}, os: 'blarghle'
- end
- end
- EOM
- }
-
- it "and os = blarghle, two_classes_one_dsl resolves to BlahModule::TwoClassesOneDsl" do
- dsl_method = self.dsl_method
- recipe = converge {
- # this is an ugly way to test, make Cheffish expose node attrs
- run_context.node.automatic[:os] = 'blarghle'
- instance_eval("#{dsl_method} 'blah' do; end")
- }
- expect(recipe.logged_warnings).to eq ''
- expect(BaseThingy.created_resource).to eq eval("BlahModule::#{class_name}")
- end
-
- it "and os = linux, two_classes_one_dsl resolves to ::TwoClassesOneDsl" do
- dsl_method = self.dsl_method
- recipe = converge {
- # this is an ugly way to test, make Cheffish expose node attrs
- run_context.node.automatic[:os] = 'linux'
- instance_eval("#{dsl_method} 'blah' do; end")
- }
- expect(recipe.logged_warnings).to eq ''
- expect(BaseThingy.created_resource).to eq eval("::#{class_name}")
- end
- end
- end
end
end
@@ -572,11 +481,11 @@ describe "Recipe DSL methods" do
}
- it "thingy3 works in a recipe and yields Foo::Thingy4 (the explicit one)" do
+ 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::Thingy4
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy3
end
it "thingy4 does not work in a recipe" do
@@ -586,16 +495,19 @@ describe "Recipe DSL methods" do
end
it "resource_matching_short_name returns Thingy4" do
- expect(Chef::Resource.resource_matching_short_name(:thingy3)).to eq RecipeDSLSpecNamespace::Thingy4
+ expect(Chef::Resource.resource_matching_short_name(:thingy3)).to eq RecipeDSLSpecNamespace::Thingy3
end
end
end
- context "when Thingy5 has resource_name :thingy5" do
+ 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
}
@@ -611,6 +523,7 @@ describe "Recipe DSL methods" do
before(:context) {
class RecipeDSLSpecNamespace::Thingy6 < BaseThingy
+ resource_name :thingy6
provides :thingy5
end
@@ -623,23 +536,151 @@ describe "Recipe DSL methods" do
expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy6
end
- it "thingy5 works in a recipe and yields Foo::Thingy6 (the later one)" do
+ 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::Thingy6
+ 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
@@ -661,11 +702,11 @@ describe "Recipe DSL methods" do
expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy7
end
- it "thingy8 works in a recipe and yields Thingy8 (the later one)" do
+ it "thingy8 works in a recipe and yields Thingy7 (alphabetical)" do
recipe = converge {
thingy8 'blah' do; end
}
- expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy8
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy7
end
it "resource_matching_short_name returns Thingy8" do
@@ -674,40 +715,40 @@ describe "Recipe DSL methods" do
end
end
- context "when Thingy5 provides :thingy5, :twizzle and :twizzle2" do
+ context "when Thingy12 provides :thingy12, :twizzle and :twizzle2" do
before(:context) {
- class RecipeDSLSpecNamespace::Thingy5 < BaseThingy
- resource_name :thingy5
+ class RecipeDSLSpecNamespace::Thingy12 < BaseThingy
+ resource_name :thingy12
provides :twizzle
provides :twizzle2
end
}
- it "thingy5 works in a recipe and yields Thingy5" do
+ it "thingy12 works in a recipe and yields Thingy12" do
expect_recipe {
- thingy5 'blah' do; end
+ thingy12 'blah' do; end
}.to emit_no_warnings_or_errors
- expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy5
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy12
end
- it "twizzle works in a recipe and yields Thingy5" do
+ 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::Thingy5
+ expect(BaseThingy.created_resource).to eq RecipeDSLSpecNamespace::Thingy12
end
- it "twizzle2 works in a recipe and yields Thingy5" do
+ 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::Thingy5
+ 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
+ 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
@@ -752,6 +793,700 @@ describe "Recipe DSL methods" do
}.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/spec_helper.rb b/spec/spec_helper.rb
index dcf244c3cc..aadf55f64b 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -87,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)
@@ -116,6 +118,7 @@ RSpec.configure do |config|
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"]
config.filter_run_excluding :windows_only => true unless windows?
config.filter_run_excluding :not_supported_on_mac_osx_106 => true if mac_osx_106?
@@ -127,6 +130,8 @@ RSpec.configure do |config|
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?
@@ -162,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
}
@@ -176,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/platform_helpers.rb b/spec/support/platform_helpers.rb
index da0313758b..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
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/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
index a4f353de60..05a4117f8e 100644
--- a/spec/support/shared/unit/api_versioning.rb
+++ b/spec/support/shared/unit/api_versioning.rb
@@ -26,7 +26,7 @@ shared_examples_for "version handling" 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::User supports" do
+ 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
@@ -34,7 +34,7 @@ shared_examples_for "version handling" do
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::User supports
+ 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
diff --git a/spec/unit/api_client_spec.rb b/spec/unit/api_client_spec.rb
index ba0eca3284..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
@@ -53,20 +58,6 @@ describe Chef::ApiClient do
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
@@ -129,12 +120,6 @@ describe Chef::ApiClient 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"})
@@ -143,15 +128,11 @@ 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
let(:client_string) do
- "{\"name\":\"black\",\"public_key\":\"crowes\",\"private_key\":\"monkeypants\",\"admin\":true,\"validator\":true,\"create_key\":true}"
+ "{\"name\":\"black\",\"public_key\":\"crowes\",\"private_key\":\"monkeypants\",\"admin\":true,\"validator\":true}"
end
let(:client) do
@@ -178,10 +159,6 @@ describe Chef::ApiClient 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
@@ -199,7 +176,6 @@ describe Chef::ApiClient do
"private_key" => "monkeypants",
"admin" => true,
"validator" => true,
- "create_key" => true,
"json_class" => "Chef::ApiClient"
}
end
@@ -224,10 +200,6 @@ describe Chef::ApiClient 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
@@ -243,18 +215,16 @@ describe Chef::ApiClient do
before(:each) do
client = {
- "name" => "black",
- "clientname" => "black",
- "public_key" => "crowes",
- "private_key" => "monkeypants",
- "admin" => true,
- "create_key" => true,
- "validator" => true,
- "json_class" => "Chef::ApiClient"
+ "name" => "black",
+ "clientname" => "black",
+ "public_key" => "crowes",
+ "private_key" => "monkeypants",
+ "admin" => true,
+ "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
@@ -275,10 +245,6 @@ describe Chef::ApiClient 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
@@ -304,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
@@ -332,34 +293,24 @@ describe Chef::ApiClient do
end
context "and the client exists" do
- let(:chef_rest_v0_mock) { double('chef rest root v0 object') }
- let(:payload) {
- {:name => "lost-my-key", :admin => false, :validator => false, :private_key => true}
- }
-
before do
@api_client_without_key = Chef::ApiClient.new
@api_client_without_key.name("lost-my-key")
- allow(@api_client_without_key).to receive(:chef_rest_v0).and_return(chef_rest_v0_mock)
- #allow(@api_client_with_key).to receive(:http_api).and_return(_api_mock)
-
- allow(chef_rest_v0_mock).to receive(:put).with("clients/lost-my-key", payload).and_return(@api_client_with_key)
- allow(chef_rest_v0_mock).to receive(:get).with("clients/lost-my-key").and_return(@api_client_without_key)
- allow(@http_client).to receive(:get).with("clients/lost-my-key").and_return(@api_client_without_key)
+ expect(@http_client).to receive(:get).with("clients/lost-my-key").and_return(@api_client_without_key)
end
+
context "and the client exists on a Chef 11-like server" do
before do
@api_client_with_key = Chef::ApiClient.new
@api_client_with_key.name("lost-my-key")
@api_client_with_key.private_key("the new private key")
- allow(@api_client_with_key).to receive(:chef_rest_v0).and_return(chef_rest_v0_mock)
+ expect(@http_client).to receive(:put).
+ with("clients/lost-my-key", :name => "lost-my-key", :admin => false, :validator => false, :private_key => true).
+ and_return(@api_client_with_key)
end
it "returns an ApiClient with a private key" do
- expect(chef_rest_v0_mock).to receive(:put).with("clients/lost-my-key", payload).
- and_return(@api_client_with_key)
-
response = Chef::ApiClient.reregister("lost-my-key")
# no sane == method for ApiClient :'(
expect(response).to eq(@api_client_without_key)
@@ -372,7 +323,7 @@ describe Chef::ApiClient do
context "and the client exists on a Chef 10-like server" do
before do
@api_client_with_key = {"name" => "lost-my-key", "private_key" => "the new private key"}
- expect(chef_rest_v0_mock).to receive(:put).
+ expect(@http_client).to receive(:put).
with("clients/lost-my-key", :name => "lost-my-key", :admin => false, :validator => false, :private_key => true).
and_return(@api_client_with_key)
end
@@ -390,134 +341,4 @@ describe Chef::ApiClient do
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::ApiClient.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)
- @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"})
- @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/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/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/chef_class_spec.rb b/spec/unit/chef_class_spec.rb
index 2528246be6..f1b877520c 100644
--- a/spec/unit/chef_class_spec.rb
+++ b/spec/unit/chef_class_spec.rb
@@ -46,10 +46,6 @@ describe "Chef class" do
Chef.set_provider_priority_map(provider_priority_map)
end
- after do
- Chef.reset!
- end
-
context "priority maps" do
context "#get_provider_priority_array" do
it "should use the current node to get the right priority_map" do
@@ -88,4 +84,27 @@ describe "Chef class" 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/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 1e4bbb5c56..8146774764 100644
--- a/spec/unit/client_spec.rb
+++ b/spec/unit/client_spec.rb
@@ -238,23 +238,24 @@ describe Chef::Client do
describe "when converge completes successfully" do
include_context "a client run"
include_context "converge completed"
-
- 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] }
+ 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
- describe "when audit phase completed" do
- include_context "audit phase completed"
- include_examples "a completed run"
- end
+ describe "when audit phase completed" do
+ include_context "audit phase completed"
+ include_examples "a completed run"
+ end
- 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] }
+ 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
@@ -512,11 +513,26 @@ describe Chef::Client do
allow_any_instance_of(Chef::RunLock).to receive(:save_pid).and_raise(NoMethodError)
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)
+ 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
diff --git a/spec/unit/config_spec.rb b/spec/unit/config_spec.rb
new file mode 100644
index 0000000000..8d155c61ab
--- /dev/null
+++ b/spec/unit/config_spec.rb
@@ -0,0 +1,31 @@
+
+require 'spec_helper'
+
+require 'chef/config'
+
+RSpec.describe Chef::Config do
+
+ 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 ":log_level" do
+ include_examples "deprecated by ohai but not deprecated" do
+ let(:option) { :log_level }
+ let(:value) { :debug }
+ 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
+
+end
diff --git a/spec/unit/cookbook/metadata_spec.rb b/spec/unit/cookbook/metadata_spec.rb
index d2954726e8..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
@@ -360,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
@@ -401,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'])
@@ -699,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))
@@ -734,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 318c2a7e85..efdb5b7926 100644
--- a/spec/unit/cookbook/syntax_check_spec.rb
+++ b/spec/unit/cookbook/syntax_check_spec.rb
@@ -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
nested_openldap_partials.erb
nested_partial.erb
diff --git a/spec/unit/cookbook_version_spec.rb b/spec/unit/cookbook_version_spec.rb
index 4990aef004..2bccddcaec 100644
--- a/spec/unit/cookbook_version_spec.rb
+++ b/spec/unit/cookbook_version_spec.rb
@@ -336,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 bd9a99a1de..13b835d120 100644
--- a/spec/unit/data_bag_spec.rb
+++ b/spec/unit/data_bag_spec.rb
@@ -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 2e1f3c39f3..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
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/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
index 7e43b1933f..1014feea89 100644
--- a/spec/unit/event_dispatch/dispatcher_spec.rb
+++ b/spec/unit/event_dispatch/dispatcher_spec.rb
@@ -47,14 +47,33 @@ describe Chef::EventDispatch::Dispatcher do
expect(event_sink).to receive(:run_start).with("12.4.0")
dispatcher.run_start("12.4.0")
- expect(event_sink).to receive(:synchronized_cookbook).with("apache2")
- dispatcher.synchronized_cookbook("apache2")
+ 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 fd90aeab71..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
diff --git a/spec/unit/formatters/doc_spec.rb b/spec/unit/formatters/doc_spec.rb
index d018207f49..eb98f5abd3 100644
--- a/spec/unit/formatters/doc_spec.rb
+++ b/spec/unit/formatters/doc_spec.rb
@@ -43,4 +43,10 @@ describe Chef::Formatters::Base do
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/compile_error_inspector_spec.rb b/spec/unit/formatters/error_inspectors/compile_error_inspector_spec.rb
index 5f95beb259..3c8d5dfa29 100644
--- a/spec/unit/formatters/error_inspectors/compile_error_inspector_spec.rb
+++ b/spec/unit/formatters/error_inspectors/compile_error_inspector_spec.rb
@@ -110,6 +110,32 @@ describe Chef::Formatters::ErrorInspectors::CompileErrorInspector do
end
end
+ 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
+
context "when the error does not contain any lines from cookbooks" do
let(:trace) do
diff --git a/spec/unit/json_compat_spec.rb b/spec/unit/json_compat_spec.rb
index 7482ba8a28..fd6469c146 100644
--- a/spec/unit/json_compat_spec.rb
+++ b/spec/unit/json_compat_spec.rb
@@ -67,13 +67,11 @@ 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
- # On FreeBSD 10.1 i386 rspec fails with a SystemStackError loading the expect line with more that 252 entries
- # https://github.com/chef/chef/issues/3101
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) }
@@ -84,7 +82,10 @@ describe Chef::JSONCompat do
end
it "should has 'test' as a 252 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']).to eq('test')
+ v = 252.times.inject(hash) do |memo, _|
+ memo['key']
+ end
+ expect(v).to eq('test')
end
end
end
diff --git a/spec/unit/knife/bootstrap/client_builder_spec.rb b/spec/unit/knife/bootstrap/client_builder_spec.rb
index e6aa307c7e..930ae8c9d3 100644
--- a/spec/unit/knife/bootstrap/client_builder_spec.rb
+++ b/spec/unit/knife/bootstrap/client_builder_spec.rb
@@ -149,6 +149,22 @@ describe Chef::Knife::Bootstrap::ClientBuilder do
client_builder.run
end
+ it "does not add tags by default" do
+ allow(node).to receive(:run_list).with([])
+ expect(node).to_not receive(:tags)
+ client_builder.run
+ end
+
+ it "adds tags to the node when given" do
+ tag_receiver = []
+
+ knife_config[:tags] = %w[foo bar]
+ allow(node).to receive(:run_list).with([])
+ allow(node).to receive(:tags).and_return(tag_receiver)
+ client_builder.run
+ expect(tag_receiver).to eq %w[foo bar]
+ end
+
it "builds a node when the run_list is a string" do
knife_config[:run_list] = "role[base],role[app]"
expect(node).to receive(:run_list).with(["role[base]", "role[app]"])
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 8fecfc885f..a1dcc564e2 100644
--- a/spec/unit/knife/client_create_spec.rb
+++ b/spec/unit/knife/client_create_spec.rb
@@ -34,7 +34,7 @@ describe Chef::Knife::ClientCreate do
end
let(:client) do
- Chef::ApiClient.new
+ Chef::ApiClientV1.new
end
let(:knife) do
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/bootstrap_context_spec.rb b/spec/unit/knife/core/bootstrap_context_spec.rb
index 3718cb228c..268b61c756 100644
--- a/spec/unit/knife/core/bootstrap_context_spec.rb
+++ b/spec/unit/knife/core/bootstrap_context_spec.rb
@@ -97,6 +97,13 @@ EXPECTED
end
end
+ describe "when tags are given" do
+ let(:config) { {:tags => [ "unicorn" ] } }
+ it "adds the attributes to first_boot" do
+ expect(Chef::JSONCompat.to_json(bootstrap_context.first_boot)).to eq(Chef::JSONCompat.to_json({:run_list => run_list, :tags => ["unicorn"]}))
+ end
+ end
+
describe "when JSON attributes are given" do
let(:config) { {:first_boot_attributes => {:baz => :quux}} }
it "adds the attributes to first_boot" do
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 219a1f2906..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");
@@ -32,209 +31,34 @@ describe Chef::Knife::SubcommandLoader 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.find_subcommands_via_rubygems.values.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",
+ let(:config_dir) { File.join(CHEF_SPEC_DATA, 'knife-site-subcommands') }
- # 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(:home) { "/home/alice" }
- let(:manifest_path) { home + "/.chef/plugin_manifest.json" }
-
- 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(Chef::Util::PathHelper).to receive(: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/node_run_list_remove_spec.rb b/spec/unit/knife/node_run_list_remove_spec.rb
index ceceef7178..a279a59635 100644
--- a/spec/unit/knife/node_run_list_remove_spec.rb
+++ b/spec/unit/knife/node_run_list_remove_spec.rb
@@ -84,6 +84,23 @@ describe Chef::Knife::NodeRunListRemove do
expect(@node.run_list).not_to include('role[monkey]')
expect(@node.run_list).not_to include('recipe[duck::type]')
end
+
+ it "should warn when the thing to remove is not in the runlist" do
+ @node.run_list << 'role[blah]'
+ @node.run_list << 'recipe[duck::type]'
+ @knife.name_args = [ 'adam', 'role[blork]' ]
+ expect(@knife.ui).to receive(:warn).with("role[blork] is not in the run list")
+ @knife.run
+ end
+
+ it "should warn even more when the thing to remove is not in the runlist and unqualified" do
+ @node.run_list << 'role[blah]'
+ @node.run_list << 'recipe[duck::type]'
+ @knife.name_args = [ 'adam', 'blork' ]
+ expect(@knife.ui).to receive(:warn).with("blork is not in the run list")
+ expect(@knife.ui).to receive(:warn).with(/did you forget recipe\[\] or role\[\]/)
+ @knife.run
+ end
end
end
end
diff --git a/spec/unit/knife/osc_user_create_spec.rb b/spec/unit/knife/osc_user_create_spec.rb
index 1b17d0d22f..e4ed78fe2b 100644
--- a/spec/unit/knife/osc_user_create_spec.rb
+++ b/spec/unit/knife/osc_user_create_spec.rb
@@ -36,19 +36,19 @@ describe Chef::Knife::OscUserCreate do
@knife.name_args = [ 'a_user' ]
@knife.config[:user_password] = "foobar"
- @user = Chef::OscUser.new
+ @user = Chef::User.new
@user.name "a_user"
- @user_with_private_key = Chef::OscUser.new
+ @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::OscUser).to receive(:new).and_return(@user)
- allow(Chef::OscUser).to receive(:from_hash).and_return(@user)
+ 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::OscUser).to receive(:new).and_return(@user)
+ 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
diff --git a/spec/unit/knife/osc_user_delete_spec.rb b/spec/unit/knife/osc_user_delete_spec.rb
index 0e16393ffe..4a3ec4228f 100644
--- a/spec/unit/knife/osc_user_delete_spec.rb
+++ b/spec/unit/knife/osc_user_delete_spec.rb
@@ -31,7 +31,7 @@ describe Chef::Knife::OscUserDelete do
end
it 'deletes the user' do
- expect(@knife).to receive(:delete_object).with(Chef::OscUser, 'my_user')
+ expect(@knife).to receive(:delete_object).with(Chef::User, 'my_user')
@knife.run
end
diff --git a/spec/unit/knife/osc_user_edit_spec.rb b/spec/unit/knife/osc_user_edit_spec.rb
index 71a9192389..279f2e30ef 100644
--- a/spec/unit/knife/osc_user_edit_spec.rb
+++ b/spec/unit/knife/osc_user_edit_spec.rb
@@ -38,7 +38,7 @@ describe Chef::Knife::OscUserEdit do
it 'loads and edits the user' do
data = { :name => "my_user" }
- allow(Chef::OscUser).to receive(:load).with("my_user").and_return(data)
+ 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
diff --git a/spec/unit/knife/osc_user_list_spec.rb b/spec/unit/knife/osc_user_list_spec.rb
index 59a15be058..f496a414b8 100644
--- a/spec/unit/knife/osc_user_list_spec.rb
+++ b/spec/unit/knife/osc_user_list_spec.rb
@@ -30,7 +30,7 @@ describe Chef::Knife::OscUserList do
end
it 'lists the users' do
- expect(Chef::OscUser).to receive(:list)
+ expect(Chef::User).to receive(:list)
expect(@knife).to receive(:format_list_for_display)
@knife.run
end
diff --git a/spec/unit/knife/osc_user_reregister_spec.rb b/spec/unit/knife/osc_user_reregister_spec.rb
index 406bbf1f3e..989eb180f1 100644
--- a/spec/unit/knife/osc_user_reregister_spec.rb
+++ b/spec/unit/knife/osc_user_reregister_spec.rb
@@ -29,7 +29,7 @@ describe Chef::Knife::OscUserReregister do
@knife = Chef::Knife::OscUserReregister.new
@knife.name_args = [ 'a_user' ]
@user_mock = double('user_mock', :private_key => "private_key")
- allow(Chef::OscUser).to receive(:load).and_return(@user_mock)
+ allow(Chef::User).to receive(:load).and_return(@user_mock)
@stdout = StringIO.new
allow(@knife.ui).to receive(:stdout).and_return(@stdout)
end
diff --git a/spec/unit/knife/osc_user_show_spec.rb b/spec/unit/knife/osc_user_show_spec.rb
index 67b9b45809..18d2086099 100644
--- a/spec/unit/knife/osc_user_show_spec.rb
+++ b/spec/unit/knife/osc_user_show_spec.rb
@@ -32,7 +32,7 @@ describe Chef::Knife::OscUserShow do
end
it 'loads and displays the user' do
- expect(Chef::OscUser).to receive(:load).with('my_user').and_return(@user_mock)
+ 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
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/user_create_spec.rb b/spec/unit/knife/user_create_spec.rb
index 49d62cc2d7..fa5c8324b4 100644
--- a/spec/unit/knife/user_create_spec.rb
+++ b/spec/unit/knife/user_create_spec.rb
@@ -186,7 +186,7 @@ describe Chef::Knife::UserCreate do
context "when a private_key is returned" do
before do
- allow(knife).to receive(:create_user_from_hash).and_return(Chef::User.from_hash(knife.user.to_hash.merge({"private_key" => "some_private_key"})))
+ 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
diff --git a/spec/unit/knife/user_delete_spec.rb b/spec/unit/knife/user_delete_spec.rb
index e49c781358..a24160624a 100644
--- a/spec/unit/knife/user_delete_spec.rb
+++ b/spec/unit/knife/user_delete_spec.rb
@@ -26,7 +26,7 @@ describe Chef::Knife::UserDelete do
before(:each) do
Chef::Knife::UserDelete.load_deps
knife.name_args = [ 'my_user' ]
- allow(Chef::User).to receive(:load).and_return(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)
@@ -51,7 +51,7 @@ describe Chef::Knife::UserDelete do
end
it 'deletes the user' do
- #expect(knife).to receive(:delete_object).with(Chef::User, 'my_user')
+ #expect(knife).to receive(:delete_object).with(Chef::UserV1, 'my_user')
expect(knife).to receive(:delete_object).with('my_user')
knife.run
end
diff --git a/spec/unit/knife/user_edit_spec.rb b/spec/unit/knife/user_edit_spec.rb
index 15a7726b20..a21d982d29 100644
--- a/spec/unit/knife/user_edit_spec.rb
+++ b/spec/unit/knife/user_edit_spec.rb
@@ -36,7 +36,7 @@ describe Chef::Knife::UserEdit do
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::User).to receive(:load).and_return({"username" => nil})
+ allow(Chef::UserV1).to receive(:load).and_return({"username" => nil})
end
it "displays the osc warning" do
@@ -52,7 +52,7 @@ describe Chef::Knife::UserEdit do
it 'loads and edits the user' do
data = { "username" => "my_user" }
- allow(Chef::User).to receive(:load).with("my_user").and_return(data)
+ 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
diff --git a/spec/unit/knife/user_list_spec.rb b/spec/unit/knife/user_list_spec.rb
index 9990cc802d..fa2bac426e 100644
--- a/spec/unit/knife/user_list_spec.rb
+++ b/spec/unit/knife/user_list_spec.rb
@@ -29,7 +29,7 @@ describe Chef::Knife::UserList do
end
it 'lists the users' do
- expect(Chef::User).to receive(:list)
+ expect(Chef::UserV1).to receive(:list)
expect(knife).to receive(:format_list_for_display)
knife.run
end
diff --git a/spec/unit/knife/user_reregister_spec.rb b/spec/unit/knife/user_reregister_spec.rb
index 412a6ec374..89aa6726cd 100644
--- a/spec/unit/knife/user_reregister_spec.rb
+++ b/spec/unit/knife/user_reregister_spec.rb
@@ -26,7 +26,7 @@ describe Chef::Knife::UserReregister do
before do
Chef::Knife::UserReregister.load_deps
knife.name_args = [ 'a_user' ]
- allow(Chef::User).to receive(:load).and_return(user_mock)
+ 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')
diff --git a/spec/unit/knife/user_show_spec.rb b/spec/unit/knife/user_show_spec.rb
index 43392a3a5c..7c39e428c0 100644
--- a/spec/unit/knife/user_show_spec.rb
+++ b/spec/unit/knife/user_show_spec.rb
@@ -35,7 +35,7 @@ describe Chef::Knife::UserShow do
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::User).to receive(:load).with('my_user').and_return(user_mock)
+ allow(Chef::UserV1).to receive(:load).with('my_user').and_return(user_mock)
allow(user_mock).to receive(:username).and_return(nil)
end
@@ -51,7 +51,7 @@ describe Chef::Knife::UserShow do
end
it 'loads and displays the user' do
- expect(Chef::User).to receive(:load).with('my_user').and_return(user_mock)
+ 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
diff --git a/spec/unit/lwrp_spec.rb b/spec/unit/lwrp_spec.rb
index 34c6f6f1c5..bcb64cb21e 100644
--- a/spec/unit/lwrp_spec.rb
+++ b/spec/unit/lwrp_spec.rb
@@ -163,7 +163,7 @@ describe "LWRP" do
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(resource.default_action).to eq([:pass_buck])
expect(Chef.method_defined?(:method_created_by_override_lwrp_foo)).to be_falsey
end
end
@@ -177,10 +177,6 @@ describe "LWRP" do
end
end
- it "should load the resource into a properly-named class and emit a warning when it is initialized" do
- expect { Chef::Resource::LwrpFoo.new('hi') }.to raise_error(Chef::Exceptions::DeprecatedFeatureError)
- end
-
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
@@ -202,7 +198,7 @@ describe "LWRP" do
end
it "should set the specified action as the default action" do
- expect(get_lwrp(:lwrp_foo).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
@@ -228,127 +224,6 @@ describe "LWRP" do
expect(cls.node[:penguin_name]).to eql("jackass")
end
- context "resource 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
- after do
- 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::LwrpFoo).to be_kind_of(Class)
- expect(Chef::Resource::LwrpFoo <= Chef::Resource::LWRPBase).to be_truthy
- end
-
- it "get_lwrp(:lwrp_foo).new is a Chef::Resource::LwrpFoo" do
- lwrp = get_lwrp(:lwrp_foo).new('hi')
- expect(lwrp.kind_of?(Chef::Resource::LwrpFoo)).to be_truthy
- expect(lwrp.is_a?(Chef::Resource::LwrpFoo)).to be_truthy
- expect(get_lwrp(:lwrp_foo) === lwrp).to be_truthy
- expect(Chef::Resource::LwrpFoo === lwrp).to be_truthy
- end
-
- it "Chef::Resource::LwrpFoo.new is a get_lwrp(:lwrp_foo)" do
- lwrp = Chef::Resource::LwrpFoo.new('hi')
- expect(lwrp.kind_of?(get_lwrp(:lwrp_foo))).to be_truthy
- expect(lwrp.is_a?(get_lwrp(:lwrp_foo))).to be_truthy
- expect(get_lwrp(:lwrp_foo) === lwrp).to be_truthy
- expect(Chef::Resource::LwrpFoo === lwrp).to be_truthy
- end
-
- it "works even if LwrpFoo exists in the top level" do
- module ::LwrpFoo
- end
- expect(Chef::Resource::LwrpFoo).not_to eq(::LwrpFoo)
- end
-
- context "with a subclass of get_lwrp(:lwrp_foo)" do
- let(:subclass) do
- Class.new(get_lwrp(:lwrp_foo))
- 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::LwrpFoo" do
- lwrp = subclass.new('hi')
- expect(lwrp.kind_of?(Chef::Resource::LwrpFoo)).to be_truthy
- expect(lwrp.is_a?(Chef::Resource::LwrpFoo)).to be_truthy
- expect(Chef::Resource::LwrpFoo === lwrp).to be_truthy
- expect(lwrp.class === Chef::Resource::LwrpFoo)
- end
- it "subclass.new is a get_lwrp(:lwrp_foo)" do
- lwrp = subclass.new('hi')
- expect(lwrp.kind_of?(get_lwrp(:lwrp_foo))).to be_truthy
- expect(lwrp.is_a?(get_lwrp(:lwrp_foo))).to be_truthy
- expect(get_lwrp(:lwrp_foo) === lwrp).to be_truthy
- expect(lwrp.class === get_lwrp(:lwrp_foo))
- end
- it "Chef::Resource::LwrpFoo.new is *not* a subclass" do
- lwrp = Chef::Resource::LwrpFoo.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::LwrpFoo).to be_falsey
- end
- it "get_lwrp(:lwrp_foo).new is *not* a subclass" do
- lwrp = get_lwrp(:lwrp_foo).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_foo)).to be_falsey
- end
- end
-
- context "with a subclass of Chef::Resource::LwrpFoo" do
- let(:subclass) do
- Class.new(Chef::Resource::LwrpFoo)
- 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::LwrpFoo" do
- lwrp = subclass.new('hi')
- expect(lwrp.kind_of?(Chef::Resource::LwrpFoo)).to be_truthy
- expect(lwrp.is_a?(Chef::Resource::LwrpFoo)).to be_truthy
- expect(Chef::Resource::LwrpFoo === lwrp).to be_truthy
- expect(lwrp.class === Chef::Resource::LwrpFoo)
- end
- it "subclass.new is a get_lwrp(:lwrp_foo)" do
- lwrp = subclass.new('hi')
- expect(lwrp.kind_of?(get_lwrp(:lwrp_foo))).to be_truthy
- expect(lwrp.is_a?(get_lwrp(:lwrp_foo))).to be_truthy
- expect(get_lwrp(:lwrp_foo) === lwrp).to be_truthy
- expect(lwrp.class === get_lwrp(:lwrp_foo))
- end
- it "Chef::Resource::LwrpFoo.new is *not* a subclass" do
- lwrp = Chef::Resource::LwrpFoo.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::LwrpFoo).to be_falsey
- end
- it "get_lwrp(:lwrp_foo).new is *not* a subclass" do
- lwrp = get_lwrp(:lwrp_foo).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_foo)).to be_falsey
- end
- end
- end
-
context "resource_name" do
let(:klass) { Class.new(Chef::Resource::LWRPBase) }
@@ -409,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
@@ -432,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
@@ -457,11 +332,50 @@ 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
@@ -525,7 +439,7 @@ describe "LWRP" do
end
it "sets itself as a provider for a resource of the same name" do
- found_providers = Chef::Platform::ProviderPriorityMap.instance.list_handlers(node, :lwrp_buck_passer)
+ 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.
@@ -537,7 +451,7 @@ describe "LWRP" 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::ProviderPriorityMap.instance.list_handlers(node, :l_w_r_p_buck_passer)
+ 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
@@ -548,10 +462,10 @@ describe "LWRP" 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::ProviderPriorityMap.instance.list_handlers(node, :'l-w-r-p_buck_passer')
+ incorrect_providers = Chef::Platform::ProviderHandlerMap.instance.list(node, :'l-w-r-p_buck_passer')
expect(incorrect_providers).to eq([])
- found_providers = Chef::Platform::ProviderPriorityMap.instance.list_handlers(node, :l_w_r_p_buck_passer)
+ 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
@@ -665,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/params_validate_spec.rb b/spec/unit/mixin/params_validate_spec.rb
index 85e1c1abab..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,11 +333,11 @@ 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
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 9b5ff5e8c6..7b37ea59f4 100644
--- a/spec/unit/node_map_spec.rb
+++ b/spec/unit/node_map_spec.rb
@@ -131,6 +131,18 @@ 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
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/osc_user_spec.rb b/spec/unit/osc_user_spec.rb
deleted file mode 100644
index 678486a16d..0000000000
--- a/spec/unit/osc_user_spec.rb
+++ /dev/null
@@ -1,276 +0,0 @@
-#
-# 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.
-#
-
-# 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/osc_user'
-require 'tempfile'
-
-describe Chef::OscUser do
- before(:each) do
- @user = Chef::OscUser.new
- end
-
- describe "initialize" do
- it "should be a Chef::OscUser" do
- expect(@user).to be_a_kind_of(Chef::OscUser)
- end
- end
-
- describe "name" do
- it "should let you set the name to a string" do
- expect(@user.name("ops_master")).to eq("ops_master")
- end
-
- it "should return the current name" do
- @user.name "ops_master"
- expect(@user.name).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.name "Bar" }.to raise_error(ArgumentError)
- # slashes
- expect { @user.name "foo/bar" }.to raise_error(ArgumentError)
- # ?
- expect { @user.name "foo?" }.to raise_error(ArgumentError)
- # &
- expect { @user.name "foo&" }.to raise_error(ArgumentError)
- end
-
-
- it "should not accept spaces" do
- expect { @user.name "ops master" }.to raise_error(ArgumentError)
- end
-
- it "should throw an ArgumentError if you feed it anything but a string" do
- expect { @user.name Hash.new }.to raise_error(ArgumentError)
- end
- end
-
- describe "admin" do
- it "should let you set the admin bit" do
- expect(@user.admin(true)).to eq(true)
- end
-
- it "should return the current admin value" do
- @user.admin true
- expect(@user.admin).to eq(true)
- end
-
- it "should default to false" do
- expect(@user.admin).to eq(false)
- end
-
- it "should throw an ArgumentError if you feed it anything but true or false" do
- expect { @user.name Hash.new }.to raise_error(ArgumentError)
- end
- end
-
- describe "public_key" do
- it "should let you set the public key" do
- expect(@user.public_key("super public")).to eq("super public")
- end
-
- it "should return the current public key" do
- @user.public_key("super public")
- expect(@user.public_key).to eq("super public")
- end
-
- it "should throw an ArgumentError if you feed it something lame" do
- expect { @user.public_key Hash.new }.to raise_error(ArgumentError)
- end
- end
-
- describe "private_key" do
- it "should let you set the private key" do
- expect(@user.private_key("super private")).to eq("super private")
- end
-
- it "should return the private key" do
- @user.private_key("super private")
- expect(@user.private_key).to eq("super private")
- end
-
- it "should throw an ArgumentError if you feed it something lame" do
- expect { @user.private_key Hash.new }.to raise_error(ArgumentError)
- end
- end
-
- describe "when serializing to JSON" do
- before(:each) do
- @user.name("black")
- @user.public_key("crowes")
- @json = @user.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 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 equalivent to Chef::JSONCompat.to_json" do
- let(:jsonable) { @user }
- end
- end
-
- describe "when deserializing from JSON" do
- before(:each) do
- user = { "name" => "mr_spinks",
- "public_key" => "turtles",
- "private_key" => "pandas",
- "password" => "password",
- "admin" => true }
- @user = Chef::OscUser.from_json(Chef::JSONCompat.to_json(user))
- end
-
- it "should deserialize to a Chef::OscUser object" do
- expect(@user).to be_a_kind_of(Chef::OscUser)
- end
-
- it "preserves the name" do
- expect(@user.name).to eq("mr_spinks")
- end
-
- it "preserves the public key" do
- expect(@user.public_key).to eq("turtles")
- end
-
- it "preserves the admin status" do
- expect(@user.admin).to be_truthy
- end
-
- it "includes the private key if present" do
- expect(@user.private_key).to eq("pandas")
- end
-
- it "includes the password if present" do
- expect(@user.password).to eq("password")
- end
-
- end
-
- describe "API Interactions" do
- before (:each) do
- @user = Chef::OscUser.new
- @user.name "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::OscUser).to receive(:load).with("admin").and_return(@user)
- @osc_inflated_response = { "admin" => @user }
- end
-
- it "lists all clients on an OSC server" do
- allow(@http_client).to receive(:get_rest).with("users").and_return(@osc_response)
- expect(Chef::OscUser.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)
- expect(Chef::OscUser.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)
- # We expect that Chef::OscUser.list will give a consistent response
- # so OHC API responses should be transformed to OSC-style output.
- expect(Chef::OscUser.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)
- expect(Chef::OscUser.list(true)).to eq(@osc_inflated_response)
- end
- end
-
- 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({})
- @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"})
- user = Chef::OscUser.load("foobar")
- expect(user.name).to eq("foobar")
- expect(user.admin).to eq(true)
- expect(user.public_key).to eq("pubkey")
- end
- end
-
- 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({})
- @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")
- @user.destroy
- end
- end
- end
-end
diff --git a/spec/unit/platform_spec.rb b/spec/unit/platform_spec.rb
index 36325d5411..34b46f657f 100644
--- a/spec/unit/platform_spec.rb
+++ b/spec/unit/platform_spec.rb
@@ -103,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
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_spec.rb b/spec/unit/provider/deploy_spec.rb
index 63658ac601..e6a7125e32 100644
--- a/spec/unit/provider/deploy_spec.rb
+++ b/spec/unit/provider/deploy_spec.rb
@@ -362,7 +362,7 @@ describe Chef::Provider::Deploy do
it "skips the migration when resource.migrate => false but runs symlinks before migration" do
@resource.migrate false
- expect(@provider).not_to receive :run_command
+ expect(@provider).not_to receive :shell_out!
expect(@provider).to receive :run_symlinks_before_migrate
@provider.migrate
end
@@ -378,7 +378,7 @@ describe Chef::Provider::Deploy do
allow(STDOUT).to receive(:tty?).and_return(true)
allow(Chef::Log).to receive(:info?).and_return(true)
- expect(@provider).to receive(:run_command).with(:command => "migration_foo", :cwd => @expected_release_dir,
+ expect(@provider).to receive(:shell_out!).with("migration_foo",:cwd => @expected_release_dir,
:user => "deployNinja", :group => "deployNinjas",
:log_level => :info, :live_stream => STDOUT,
:log_tag => "deploy[/my/deploy/dir]",
@@ -445,13 +445,13 @@ describe Chef::Provider::Deploy do
end
it "does nothing for restart if restart_command is empty" do
- expect(@provider).not_to receive(:run_command)
+ expect(@provider).not_to receive(:shell_out!)
@provider.restart
end
it "runs the restart command in the current application dir when the resource has a restart_command" do
@resource.restart_command "restartcmd"
- expect(@provider).to receive(:run_command).with(:command => "restartcmd", :cwd => "/my/deploy/dir/current", :log_tag => "deploy[/my/deploy/dir]", :log_level => :debug)
+ expect(@provider).to receive(:shell_out!).with("restartcmd", :cwd => "/my/deploy/dir/current", :log_tag => "deploy[/my/deploy/dir]", :log_level => :debug)
@provider.restart
end
@@ -509,7 +509,7 @@ describe Chef::Provider::Deploy do
it "shouldn't give a no method error on migrate if the environment is nil" do
allow(@provider).to receive(:enforce_ownership)
allow(@provider).to receive(:run_symlinks_before_migrate)
- allow(@provider).to receive(:run_command)
+ allow(@provider).to receive(:shell_out!)
@provider.migrate
end
@@ -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 38d6db8320..4fad8c8906 100644
--- a/spec/unit/provider/directory_spec.rb
+++ b/spec/unit/provider/directory_spec.rb
@@ -197,6 +197,41 @@ describe Chef::Provider::Directory do
expect { directory.run_action(:create) }.to raise_error(Chef::Exceptions::EnclosingDirectoryDoesNotExist)
end
end
+
+ 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
+
+ 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
+
+ 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 "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
describe "#run_action(:create)" do
diff --git a/spec/unit/provider/dsc_resource_spec.rb b/spec/unit/provider/dsc_resource_spec.rb
index 0a6c22bdcf..65c1c019f0 100644
--- a/spec/unit/provider/dsc_resource_spec.rb
+++ b/spec/unit/provider/dsc_resource_spec.rb
@@ -35,10 +35,10 @@ describe Chef::Provider::DscResource do
node
}
- it 'raises a NoProviderAvailable exception' do
+ it 'raises a ProviderNotFound exception' do
expect(provider).not_to receive(:meta_configuration)
expect{provider.run_action(:run)}.to raise_error(
- Chef::Exceptions::NoProviderAvailable, /5\.0\.10018\.0/)
+ Chef::Exceptions::ProviderNotFound, /5\.0\.10018\.0/)
end
end
@@ -56,7 +56,7 @@ describe Chef::Provider::DscResource do
expect(provider).to receive(:meta_configuration).and_return(
meta_configuration)
expect { provider.run_action(:run) }.to raise_error(
- Chef::Exceptions::NoProviderAvailable, /Disabled/)
+ Chef::Exceptions::ProviderNotFound, /Disabled/)
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/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/dpkg_spec.rb b/spec/unit/provider/package/dpkg_spec.rb
index 4974cff934..b868128147 100644
--- a/spec/unit/provider/package/dpkg_spec.rb
+++ b/spec/unit/provider/package/dpkg_spec.rb
@@ -54,7 +54,6 @@ describe Chef::Provider::Package::Dpkg do
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
diff --git a/spec/unit/provider/package/rpm_spec.rb b/spec/unit/provider/package/rpm_spec.rb
index 48164b145c..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", timeout: 900).and_return(status)
- expect(provider).to receive(:shell_out).with("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' ImageMagick-c++", timeout: 900).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", timeout: 900).and_return(status)
- expect(provider).to receive(:shell_out).with("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' ImageMagick-c++", timeout: 900).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", timeout: 900).and_return(status)
- expect(provider).to receive(:shell_out).with("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' openssh-askpass", timeout: 900).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", timeout: 900)
- 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", timeout: 900)
- 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", timeout: 900)
- 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", timeout: 900)
- 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", 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")
+ 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", 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")
+ 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", timeout: 900)
- 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", timeout: 900)
- 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 67ffb7bb9e..f790bdb1ce 100644
--- a/spec/unit/provider/package/rubygems_spec.rb
+++ b/spec/unit/provider/package/rubygems_spec.rb
@@ -369,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")
diff --git a/spec/unit/provider/package/yum_spec.rb b/spec/unit/provider/package/yum_spec.rb
index e878b92621..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");
@@ -24,7 +24,7 @@ describe Chef::Provider::Package::Yum 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',
@@ -39,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
@@ -88,6 +89,46 @@ describe Chef::Provider::Package::Yum do
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')
@@ -109,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")
@@ -163,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
@@ -194,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")
@@ -229,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")
@@ -281,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
@@ -352,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)
@@ -373,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')
@@ -395,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
@@ -419,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)
@@ -438,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)
@@ -462,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
@@ -474,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
@@ -482,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
@@ -493,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
@@ -503,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
@@ -514,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
@@ -531,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
@@ -549,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})
@@ -568,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
@@ -591,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
@@ -604,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")
@@ -615,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")
@@ -628,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
@@ -639,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
@@ -657,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})
@@ -706,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
@@ -714,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
@@ -723,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
@@ -737,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
@@ -747,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
@@ -759,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
@@ -771,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
@@ -1737,6 +1807,7 @@ EOF
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|
@@ -2053,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
@@ -2134,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
@@ -2144,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
@@ -2155,7 +2228,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 --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'])
@@ -2181,7 +2254,7 @@ describe "Chef::Provider::Package::Yum - Multi" do
@provider.load_current_resource
expect(@provider).to receive(:yum_command).with(
- "yum -d0 -e0 -y install cups-1.2.4-11.19.el5.x86_64 vim-1.0"
+ "-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
diff --git a/spec/unit/provider/powershell_spec.rb b/spec/unit/provider/powershell_script_spec.rb
index 855c18af9b..855c18af9b 100644
--- a/spec/unit/provider/powershell_spec.rb
+++ b/spec/unit/provider/powershell_script_spec.rb
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/service/aix_service_spec.rb b/spec/unit/provider/service/aix_service_spec.rb
index a0c8bb3407..5cca7d6f0a 100644
--- a/spec/unit/provider/service/aix_service_spec.rb
+++ b/spec/unit/provider/service/aix_service_spec.rb
@@ -94,7 +94,7 @@ describe Chef::Provider::Service::Aix do
end
it "service is a group" do
- expect(@provider).to receive(:shell_out!).with("lssrc -g 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
@@ -106,7 +106,7 @@ describe Chef::Provider::Service::Aix do
end
it "service is a group" do
- expect(@provider).to receive(:shell_out!).with("lssrc -g 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
@@ -119,7 +119,7 @@ describe Chef::Provider::Service::Aix do
end
it "service is a subsystem" do
- expect(@provider).to receive(:shell_out!).with("lssrc -g chef").and_return(@group_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
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 597845a558..54183bdc3d 100644
--- a/spec/unit/provider/service/macosx_spec.rb
+++ b/spec/unit/provider/service/macosx_spec.rb
@@ -60,15 +60,15 @@ XML
["Daemon", "Agent"].each do |service_type|
["redis-server", "io.redis.redis-server"].each do |service_name|
- ["10.9", "10.10"].each do |platform_version|
+ ["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 igor -c'}
- if platform_version != "10.10"
- let(:su_cmd) {'su -l igor -c'}
+ let(:su_cmd) {'su -l igor -c'}
+ if platform_version == "10.9"
+ let(:su_cmd) {'su igor -c'}
end
end
let(:service_label) {'io.redis.redis-server'}
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_resolver_spec.rb b/spec/unit/provider_resolver_spec.rb
index e18d69bc19..88df4a20cc 100644
--- a/spec/unit/provider_resolver_spec.rb
+++ b/spec/unit/provider_resolver_spec.rb
@@ -28,30 +28,37 @@ include Chef::Mixin::ConvertToClassName
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(:resolved_provider) { provider_resolver.resolve }
-
- let(:provider) { nil }
-
- let(:resource_name) { :service }
-
- let(:resource) { double(Chef::Resource, provider: provider, resource_name: resource_name) }
-
- before do
- allow(resource).to receive(:is_a?).with(Chef::Resource).and_return(true)
+ 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
def self.on_platform(platform, *tags,
@@ -83,16 +90,41 @@ describe Chef::ProviderResolver do
end
def self.expect_providers(**providers)
- providers.each do |name, provider|
+ providers.each do |name, expected|
describe name.to_s do
let(:resource_name) { name }
- if provider
- it "resolves to a #{provider}" do
- expect(resolved_provider).to eql(provider)
+
+ 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 "Fails to resolve (since #{name.inspect} is unsupported on #{platform} #{platform_version})" do
- expect { resolved_provider }.to raise_error /Cannot find a provider/
+ 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
@@ -454,49 +486,48 @@ describe Chef::ProviderResolver do
PROVIDERS =
{
- bash: Chef::Provider::Script,
- 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,
- 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,
- group: Chef::Provider::Group::Gpasswd,
- homebrew_package: Chef::Provider::Package::Homebrew,
- http_request: Chef::Provider::HttpRequest,
- ifconfig: Chef::Provider::Ifconfig,
- link: Chef::Provider::Link,
- log: Chef::Provider::Log::ChefLog,
- macports_package: Chef::Provider::Package::Macports,
- mdadm: Chef::Provider::Mdadm,
- mount: Chef::Provider::Mount::Mount,
- 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,
- subversion: Chef::Provider::Subversion,
- template: Chef::Provider::Template,
- timestamped_deploy: Chef::Provider::Deploy::Timestamped,
- user: Chef::Provider::User::Useradd,
- whyrun_safe_ruby_block: Chef::Provider::WhyrunSafeRubyBlock,
+ 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,
- dsc_script: nil,
dpkg_package: nil,
+ dsc_script: nil,
ips_package: nil,
pacman_package: nil,
paludis_package: nil,
@@ -508,61 +539,62 @@ describe Chef::ProviderResolver do
windows_service: nil,
"linux" => {
- apt_package: Chef::Provider::Package::Apt,
- dpkg_package: Chef::Provider::Package::Dpkg,
- pacman_package: Chef::Provider::Package::Pacman,
- paludis_package: Chef::Provider::Package::Paludis,
- rpm_package: Chef::Provider::Package::Rpm,
- yum_package: Chef::Provider::Package::Yum,
+ 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::Provider::Ifconfig::Debian,
- package: Chef::Provider::Package::Apt,
-# service: Chef::Provider::Service::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::Provider::Ifconfig,
-# service: Chef::Provider::Service::Insserv,
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+# service: [ Chef::Resource::InsservService, Chef::Provider::Service::Insserv ],
},
"5.0" => {
- ifconfig: Chef::Provider::Ifconfig,
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
},
},
"gcel" => {
"3.1.4" => {
- ifconfig: Chef::Provider::Ifconfig,
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
},
},
"linaro" => {
"3.1.4" => {
- ifconfig: Chef::Provider::Ifconfig,
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
},
},
"linuxmint" => {
"3.1.4" => {
- ifconfig: Chef::Provider::Ifconfig,
-# service: Chef::Provider::Service::Upstart,
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
+# service: [ Chef::Resource::UpstartService, Chef::Provider::Service::Upstart ],
},
},
"raspbian" => {
"3.1.4" => {
- ifconfig: Chef::Provider::Ifconfig,
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
},
},
"ubuntu" => {
"11.10" => {
},
"10.04" => {
- ifconfig: Chef::Provider::Ifconfig,
+ ifconfig: [ Chef::Resource::Ifconfig, Chef::Provider::Ifconfig ],
},
},
},
"arch" => {
- package: Chef::Provider::Package::Pacman,
+ # TODO should be Chef::Resource::PacmanPackage
+ package: [ Chef::Resource::Package, Chef::Provider::Package::Pacman ],
"arch" => {
"3.1.4" => {
@@ -571,8 +603,8 @@ describe Chef::ProviderResolver do
},
"freebsd" => {
- group: Chef::Provider::Group::Pw,
- user: Chef::Provider::User::Pw,
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Pw ],
+ user: [ Chef::Resource::User, Chef::Provider::User::Pw ],
"freebsd" => {
"3.1.4" => {
@@ -580,30 +612,31 @@ describe Chef::ProviderResolver do
},
},
"suse" => {
- group: Chef::Provider::Group::Gpasswd,
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Gpasswd ],
"suse" => {
"12.0" => {
},
%w(11.1 11.2 11.3) => {
- group: Chef::Provider::Group::Suse,
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Suse ],
},
},
"opensuse" => {
-# service: Chef::Provider::Service::Redhat,
- package: Chef::Provider::Package::Zypper,
- group: Chef::Provider::Group::Usermod,
+# 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::Provider::Group::Suse,
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Suse ],
},
},
},
"gentoo" => {
- package: Chef::Provider::Package::Portage,
- portage_package: Chef::Provider::Package::Portage,
-# service: Chef::Provider::Service::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" => {
@@ -612,27 +645,27 @@ describe Chef::ProviderResolver do
},
"rhel" => {
-# service: Chef::Provider::Service::Systemd,
- package: Chef::Provider::Package::Yum,
- ifconfig: Chef::Provider::Ifconfig::Redhat,
+# 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::Provider::Service::Redhat,
+# service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
},
},
%w(redhat centos scientific oracle) => {
"7.0" => {
},
"6.0" => {
-# service: Chef::Provider::Service::Redhat,
+# service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
},
},
"fedora" => {
"15.0" => {
},
"14.0" => {
-# service: Chef::Provider::Service::Redhat,
+# service: [ Chef::Resource::RedhatService, Chef::Provider::Service::Redhat ],
},
},
},
@@ -641,9 +674,9 @@ describe Chef::ProviderResolver do
"darwin" => {
%w(mac_os_x mac_os_x_server) => {
- group: Chef::Provider::Group::Dscl,
- package: Chef::Provider::Package::Homebrew,
- user: Chef::Provider::User::Dscl,
+ 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" => {
@@ -653,17 +686,17 @@ describe Chef::ProviderResolver do
},
"windows" => {
- batch: Chef::Provider::Batch,
- dsc_script: Chef::Provider::DscScript,
- env: Chef::Provider::Env::Windows,
- group: Chef::Provider::Group::Windows,
- mount: Chef::Provider::Mount::Windows,
- package: Chef::Provider::Package::Windows,
- powershell_script: Chef::Provider::PowershellScript,
- service: Chef::Provider::Service::Windows,
- user: Chef::Provider::User::Windows,
- windows_package: Chef::Provider::Package::Windows,
- windows_service: Chef::Provider::Service::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) => {
@@ -674,15 +707,16 @@ describe Chef::ProviderResolver do
},
"aix" => {
- bff_package: Chef::Provider::Package::Aix,
- cron: Chef::Provider::Cron::Aix,
- group: Chef::Provider::Group::Aix,
- ifconfig: Chef::Provider::Ifconfig::Aix,
- mount: Chef::Provider::Mount::Aix,
- package: Chef::Provider::Package::Aix,
- rpm_package: Chef::Provider::Package::Rpm,
- user: Chef::Provider::User::Aix,
-# service: Chef::Provider::Service::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" => {
@@ -696,7 +730,7 @@ describe Chef::ProviderResolver do
"hpux" => {
"hpux" => {
"3.1.4" => {
- group: Chef::Provider::Group::Usermod
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Usermod ]
}
}
}
@@ -706,15 +740,15 @@ describe Chef::ProviderResolver do
"netbsd" => {
"netbsd" => {
"3.1.4" => {
- group: Chef::Provider::Group::Groupmod,
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Groupmod ],
},
},
},
},
"openbsd" => {
- group: Chef::Provider::Group::Usermod,
- package: Chef::Provider::Package::Openbsd,
+ group: [ Chef::Resource::Group, Chef::Provider::Group::Usermod ],
+ package: [ Chef::Resource::OpenbsdPackage, Chef::Provider::Package::Openbsd ],
"openbsd" => {
"openbsd" => {
@@ -725,15 +759,15 @@ describe Chef::ProviderResolver do
},
"solaris2" => {
- group: Chef::Provider::Group::Usermod,
- ips_package: Chef::Provider::Package::Ips,
- package: Chef::Provider::Package::Ips,
- mount: Chef::Provider::Mount::Solaris,
- solaris_package: Chef::Provider::Package::Solaris,
+ 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::Provider::Package::SmartOS,
- package: Chef::Provider::Package::SmartOS,
+ smartos_package: [ Chef::Resource::SmartosPackage, Chef::Provider::Package::SmartOS ],
+ package: [ Chef::Resource::SmartosPackage, Chef::Provider::Package::SmartOS ],
"smartos" => {
"3.1.4" => {
@@ -744,12 +778,11 @@ describe Chef::ProviderResolver do
"solaris2" => {
"nexentacore" => {
"3.1.4" => {
- package: Chef::Provider::Package::Solaris,
},
},
"omnios" => {
"3.1.4" => {
- user: Chef::Provider::User::Solaris,
+ user: [ Chef::Resource::User, Chef::Provider::User::Solaris ],
}
},
"openindiana" => {
@@ -761,11 +794,11 @@ describe Chef::ProviderResolver do
},
},
"solaris2" => {
- user: Chef::Provider::User::Solaris,
+ user: [ Chef::Resource::User, Chef::Provider::User::Solaris ],
"5.11" => {
+ package: [ Chef::Resource::IpsPackage, Chef::Provider::Package::Ips ],
},
"5.9" => {
- package: Chef::Provider::Package::Solaris,
},
},
},
@@ -785,7 +818,8 @@ describe Chef::ProviderResolver do
"exherbo" => {
"exherbo" => {
"3.1.4" => {
- package: Chef::Provider::Package::Paludis
+ # TODO should be Chef::Resource::PaludisPackage
+ package: [ Chef::Resource::Package, Chef::Provider::Package::Paludis ]
}
}
}
diff --git a/spec/unit/provider_spec.rb b/spec/unit/provider_spec.rb
index d7a34bc21b..97b88b1732 100644
--- a/spec/unit/provider_spec.rb
+++ b/spec/unit/provider_spec.rb
@@ -114,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
diff --git a/spec/unit/recipe_spec.rb b/spec/unit/recipe_spec.rb
index ee98e63c1f..ea3ab44c16 100644
--- a/spec/unit/recipe_spec.rb
+++ b/spec/unit/recipe_spec.rb
@@ -121,6 +121,7 @@ describe Chef::Recipe do
it "locate resource for particular platform" do
ShaunTheSheep = Class.new(Chef::Resource)
+ ShaunTheSheep.resource_name :shaun_the_sheep
ShaunTheSheep.provides :laughter, :platform => ["television"]
node.automatic[:platform] = "television"
node.automatic[:platform_version] = "123"
@@ -131,6 +132,7 @@ 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")
@@ -141,7 +143,9 @@ describe Chef::Recipe 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
@@ -149,24 +153,20 @@ describe Chef::Recipe do
Object.send(:remove_const, :TottenhamHotspur)
end
- it "selects one if it is the last declared" do
- expect(Chef::Log).not_to receive(:warn)
-
+ 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(TottenhamHotspur)
+ expect(res1).to be_a_kind_of(Sounders)
end
- it "selects the other one if it is given priority" do
- expect(Chef::Log).not_to receive(:warn)
-
- TottenhamHotspur.provides :football, platform: "nbc_sports"
- Sounders.provides :football, platform: "nbc_sports"
+ 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.football "club world cup"
+ res1 = recipe.football2 "club world cup"
expect(res1.name).to eql("club world cup")
expect(res1).to be_a_kind_of(Sounders)
end
@@ -405,8 +405,8 @@ describe Chef::Recipe do
end
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(original_resource.action).to eq([:score])
+ expect(duplicated_resource.action).to eq([:nothing])
end
it "does not copy the source location of the first resource" do
@@ -501,7 +501,7 @@ describe Chef::Recipe do
recipe.from_file(File.join(CHEF_SPEC_DATA, "recipes", "test.rb"))
res = recipe.resources(:file => "/etc/nsswitch.conf")
expect(res.name).to eql("/etc/nsswitch.conf")
- expect(res.action).to eql(:create)
+ expect(res.action).to eql([:create])
expect(res.owner).to eql("root")
expect(res.group).to eql("root")
expect(res.mode).to eql(0644)
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/breakpoint_spec.rb b/spec/unit/resource/breakpoint_spec.rb
index 9c867ebcc7..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 0403a7ba6b..5b6a452784 100644
--- a/spec/unit/resource/deploy_spec.rb
+++ b/spec/unit/resource/deploy_spec.rb
@@ -148,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
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
index ae15f56eaf..06769d86ce 100644
--- a/spec/unit/resource/dsc_resource_spec.rb
+++ b/spec/unit/resource/dsc_resource_spec.rb
@@ -35,7 +35,7 @@ describe Chef::Resource::DscResource do
}
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
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 008d27372a..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 dd20f5f03a..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/link_spec.rb b/spec/unit/resource/link_spec.rb
index 51221e0472..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|
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 2505c4a3d7..2505c4a3d7 100644
--- a/spec/unit/resource/powershell_spec.rb
+++ b/spec/unit/resource/powershell_script_spec.rb
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/route_spec.rb b/spec/unit/resource/route_spec.rb
index ffb9304511..ec1d369932 100644
--- a/spec/unit/resource/route_spec.rb
+++ b/spec/unit/resource/route_spec.rb
@@ -35,7 +35,7 @@ describe Chef::Resource::Route do
end
it "should have a default action of 'add'" do
- expect(@resource.action).to eql(:add)
+ expect(@resource.action).to eql([:add])
end
it "should accept add or delete for action" do
diff --git a/spec/unit/resource/ruby_block_spec.rb b/spec/unit/resource/ruby_block_spec.rb
index 5d83f7e367..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
@@ -46,7 +46,7 @@ describe Chef::Resource::RubyBlock do
it "allows the action to be 'create'" do
@resource.action :create
- expect(@resource.action).to eq(:create)
+ expect(@resource.action).to eq([:create])
end
describe "when it has been initialized with block code" 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/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_service_spec.rb b/spec/unit/resource/windows_service_spec.rb
index 8866cad1bf..45a295c24e 100644
--- a/spec/unit/resource/windows_service_spec.rb
+++ b/spec/unit/resource/windows_service_spec.rb
@@ -44,6 +44,6 @@ describe Chef::Resource::WindowsService, "initialize" do
it "allows the action to be 'configure_startup'" do
resource.action :configure_startup
- expect(resource.action).to eq(:configure_startup)
+ expect(resource.action).to eq([:configure_startup])
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 8ba45d9350..b9ba80068b 100644
--- a/spec/unit/resource_spec.rb
+++ b/spec/unit/resource_spec.rb
@@ -59,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
@@ -344,6 +344,7 @@ describe Chef::Resource do
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)
@@ -358,6 +359,20 @@ describe Chef::Resource do
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
@@ -416,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
@@ -795,21 +810,21 @@ describe Chef::Resource do
end
it 'adds mappings for a single platform' do
- expect(Chef).to receive(:set_resource_priority_array).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).to receive(:set_resource_priority_array).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).to receive(:set_resource_priority_array).with(
+ expect(Chef.resource_handler_map).to receive(:set).with(
:tape_deck, Chef::Resource::Klz, {}
)
klz.provides :tape_deck
@@ -962,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/role_spec.rb b/spec/unit/role_spec.rb
index f120ca6da6..ecc7945a08 100644
--- a/spec/unit/role_spec.rb
+++ b/spec/unit/role_spec.rb
@@ -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 e20ba63b72..99801575ef 100644
--- a/spec/unit/run_context_spec.rb
+++ b/spec/unit/run_context_spec.rb
@@ -68,6 +68,9 @@ describe Chef::RunContext do
"dependency2" => {
"version" => "0.0.0",
},
+ "include" => {
+ "version" => "0.0.0",
+ },
"no-default-attr" => {
"version" => "0.0.0",
},
@@ -84,6 +87,10 @@ describe Chef::RunContext do
)
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
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/runner_spec.rb b/spec/unit/runner_spec.rb
index 82e57e068c..b30f818da1 100644
--- a/spec/unit/runner_spec.rb
+++ b/spec/unit/runner_spec.rb
@@ -273,8 +273,8 @@ describe Chef::Runner do
expected_message =<<-E
Multiple failures occurred:
-* FailureProvider::ChefClientFail occurred in delayed notification: failure_resource[explode] (dynamically defined) had an error: FailureProvider::ChefClientFail: chef had an error of some sort
-* FailureProvider::ChefClientFail occurred in delayed notification: failure_resource[explode again] (dynamically defined) had an error: FailureProvider::ChefClientFail: chef had an error of some sort
+* FailureProvider::ChefClientFail occurred in delayed notification: [explode] (dynamically defined) had an error: FailureProvider::ChefClientFail: chef had an error of some sort
+* FailureProvider::ChefClientFail occurred in delayed notification: [explode again] (dynamically defined) had an error: FailureProvider::ChefClientFail: chef had an error of some sort
E
expect(exception.message).to eq(expected_message)
diff --git a/spec/unit/user_spec.rb b/spec/unit/user_spec.rb
index 57822df7e3..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'
@@ -26,141 +31,98 @@ describe Chef::User do
@user = Chef::User.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::User" do
expect(@user).to be_a_kind_of(Chef::User)
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")
+ describe "name" do
+ it "should let you set the name to a string" do
+ expect(@user.name("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")
+ it "should return the current name" do
+ @user.name "ops_master"
+ expect(@user.name).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)
+ expect { @user.name "Bar" }.to raise_error(ArgumentError)
# slashes
- expect { @user.username "foo/bar" }.to raise_error(ArgumentError)
+ expect { @user.name "foo/bar" }.to raise_error(ArgumentError)
# ?
- expect { @user.username "foo?" }.to raise_error(ArgumentError)
+ expect { @user.name "foo?" }.to raise_error(ArgumentError)
# &
- expect { @user.username "foo&" }.to raise_error(ArgumentError)
+ expect { @user.name "foo&" }.to raise_error(ArgumentError)
end
it "should not accept spaces" do
- expect { @user.username "ops master" }.to raise_error(ArgumentError)
+ expect { @user.name "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)
+ expect { @user.name 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
+ describe "admin" do
+ it "should let you set the admin bit" do
+ expect(@user.admin(true)).to eq(true)
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
+ it "should return the current admin value" do
+ @user.admin true
+ expect(@user.admin).to eq(true)
end
- describe "private_key" do
- it_should_behave_like "string fields with no contraints" do
- let(:method) { :private_key }
- end
+ it "should default to false" do
+ expect(@user.admin).to eq(false)
end
- describe "display_name" do
- it_should_behave_like "string fields with no contraints" do
- let(:method) { :display_name }
- end
+ it "should throw an ArgumentError if you feed it anything but true or false" do
+ expect { @user.name Hash.new }.to raise_error(ArgumentError)
end
+ end
- describe "first_name" do
- it_should_behave_like "string fields with no contraints" do
- let(:method) { :first_name }
- end
+ describe "public_key" do
+ it "should let you set the public key" do
+ expect(@user.public_key("super public")).to eq("super public")
end
- describe "middle_name" do
- it_should_behave_like "string fields with no contraints" do
- let(:method) { :middle_name }
- end
+ it "should return the current public key" do
+ @user.public_key("super public")
+ expect(@user.public_key).to eq("super public")
end
- describe "last_name" do
- it_should_behave_like "string fields with no contraints" do
- let(:method) { :last_name }
- end
+ it "should throw an ArgumentError if you feed it something lame" do
+ expect { @user.public_key Hash.new }.to raise_error(ArgumentError)
end
+ end
- describe "email" do
- it_should_behave_like "string fields with no contraints" do
- let(:method) { :email }
- end
+ describe "private_key" do
+ it "should let you set the private key" do
+ expect(@user.private_key("super private")).to eq("super private")
end
- describe "password" do
- it_should_behave_like "string fields with no contraints" do
- let(:method) { :password }
- end
+ it "should return the private key" do
+ @user.private_key("super private")
+ expect(@user.private_key).to eq("super private")
+ end
+
+ it "should throw an ArgumentError if you feed it something lame" do
+ expect { @user.private_key Hash.new }.to raise_error(ArgumentError)
end
end
describe "when serializing to JSON" do
before(:each) do
- @user.username("black")
+ @user.name("black")
+ @user.public_key("crowes")
@json = @user.to_json
end
@@ -168,62 +130,16 @@ describe Chef::User 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")
+ it "includes the name value" do
+ expect(@json).to include(%q{"name":"black"})
end
- it "includes the public key when present" do
- @user.public_key("crowes")
- expect(@user.to_json).to include(%{"public_key":"crowes"})
+ it "includes the public key value" do
+ expect(@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")
+ it "includes the 'admin' flag" do
+ expect(@json).to include(%q{"admin":false})
end
it "includes the private key when present" do
@@ -244,25 +160,18 @@ 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
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",
+ user = { "name" => "mr_spinks",
"public_key" => "turtles",
"private_key" => "pandas",
- "create_key" => false
- }
+ "password" => "password",
+ "admin" => true }
@user = Chef::User.from_json(Chef::JSONCompat.to_json(user))
end
@@ -270,277 +179,34 @@ describe Chef::User do
expect(@user).to be_a_kind_of(Chef::User)
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")
+ it "preserves the name" do
+ expect(@user.name).to eq("mr_spinks")
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")
+ it "preserves the public key" do
+ expect(@user.public_key).to eq("turtles")
end
- it "preserves the last name if present" do
- expect(@user.last_name).to eq("der")
+ it "preserves the admin status" do
+ expect(@user.admin).to be_truthy
end
- it "preserves the email if present" do
- expect(@user.email).to eq("charmander@pokemon.poke")
+ it "includes the private key if present" do
+ expect(@user.private_key).to eq("pandas")
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::User.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::User.new
- @user.username "foobar"
- @http_client = double("Chef::REST mock")
- allow(Chef::REST).to receive(:new).and_return(@http_client)
+ @user.name "foobar"
+ @http_client = double("Chef::ServerAPI mock")
+ allow(Chef::ServerAPI).to receive(:new).and_return(@http_client)
end
describe "list" do
@@ -552,6 +218,16 @@ describe Chef::User do
@osc_inflated_response = { "admin" => @user }
end
+ it "lists all clients on an OSC server" do
+ 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).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).with("users").and_return(@ohc_response)
# We expect that Chef::User.list will give a consistent response
@@ -565,15 +241,31 @@ describe Chef::User do
end
end
+ describe "create" do
+ it "creates a new user via the API" do
+ @user.password "password"
+ 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).with("users/foobar").and_return({"username" => "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.username).to eq("foobar")
+ expect(user.name).to eq("foobar")
+ expect(user.admin).to eq(true)
expect(user.public_key).to eq("pubkey")
end
end
+ describe "update" do
+ it "updates an existing user on via the API" do
+ 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).with("users/foobar")
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/workstation_config_loader_spec.rb b/spec/unit/workstation_config_loader_spec.rb
deleted file mode 100644
index 72631f3dfa..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
- allow(Chef::Util::PathHelper).to receive(:home).with('.chef').and_yield(File.join(home, '.chef'))
- 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