chef 15.3.14 → 15.4.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/chef.gemspec +2 -2
  3. data/lib/chef/application/solo.rb +1 -1
  4. data/lib/chef/event_dispatch/dispatcher.rb +9 -2
  5. data/lib/chef/formatters/doc.rb +3 -3
  6. data/lib/chef/knife.rb +13 -3
  7. data/lib/chef/knife/bootstrap.rb +28 -4
  8. data/lib/chef/knife/bootstrap/templates/chef-full.erb +7 -8
  9. data/lib/chef/knife/data_bag_secret_options.rb +11 -4
  10. data/lib/chef/knife/download.rb +2 -2
  11. data/lib/chef/knife/exec.rb +9 -1
  12. data/lib/chef/knife/ssh.rb +1 -1
  13. data/lib/chef/knife/ssl_check.rb +1 -1
  14. data/lib/chef/knife/supermarket_list.rb +19 -7
  15. data/lib/chef/knife/supermarket_search.rb +3 -2
  16. data/lib/chef/node/attribute.rb +2 -0
  17. data/lib/chef/node/attribute_collections.rb +8 -0
  18. data/lib/chef/node/immutable_collections.rb +12 -0
  19. data/lib/chef/node/mixin/immutablize_array.rb +1 -0
  20. data/lib/chef/node/mixin/immutablize_hash.rb +1 -0
  21. data/lib/chef/provider.rb +14 -8
  22. data/lib/chef/provider/package/chocolatey.rb +11 -3
  23. data/lib/chef/provider/package/dnf/python_helper.rb +8 -3
  24. data/lib/chef/provider/package/windows/exe.rb +2 -2
  25. data/lib/chef/provider/package/windows/msi.rb +3 -3
  26. data/lib/chef/provider/package/yum/python_helper.rb +8 -3
  27. data/lib/chef/provider/service/windows.rb +1 -1
  28. data/lib/chef/resource/apt_repository.rb +19 -13
  29. data/lib/chef/resource/apt_update.rb +15 -1
  30. data/lib/chef/resource/archive_file.rb +10 -1
  31. data/lib/chef/resource/build_essential.rb +14 -1
  32. data/lib/chef/resource/chocolatey_config.rb +17 -1
  33. data/lib/chef/resource/chocolatey_feature.rb +15 -0
  34. data/lib/chef/resource/chocolatey_package.rb +31 -1
  35. data/lib/chef/resource/chocolatey_source.rb +17 -1
  36. data/lib/chef/resource/cookbook_file.rb +1 -1
  37. data/lib/chef/resource/cron_access.rb +22 -1
  38. data/lib/chef/resource/cron_d.rb +46 -1
  39. data/lib/chef/resource/dmg_package.rb +28 -0
  40. data/lib/chef/resource/kernel_module.rb +61 -0
  41. data/lib/chef/resource/sudo.rb +2 -2
  42. data/lib/chef/resource/windows_ad_join.rb +72 -3
  43. data/lib/chef/resource/windows_service.rb +1 -1
  44. data/lib/chef/resource/windows_share.rb +2 -1
  45. data/lib/chef/shell.rb +4 -4
  46. data/lib/chef/shell/ext.rb +2 -2
  47. data/lib/chef/train_transport.rb +1 -1
  48. data/lib/chef/version.rb +1 -1
  49. data/spec/functional/resource/ifconfig_spec.rb +0 -2
  50. data/spec/functional/resource/mount_spec.rb +0 -4
  51. data/spec/functional/util/powershell/cmdlet_spec.rb +2 -2
  52. data/spec/integration/knife/chef_repo_path_spec.rb +4 -2
  53. data/spec/integration/recipes/resource_converge_if_changed_spec.rb +19 -19
  54. data/spec/spec_helper.rb +2 -0
  55. data/spec/unit/formatters/doc_spec.rb +18 -0
  56. data/spec/unit/knife/bootstrap_spec.rb +46 -10
  57. data/spec/unit/knife/supermarket_list_spec.rb +70 -0
  58. data/spec/unit/knife/supermarket_search_spec.rb +85 -0
  59. data/spec/unit/node/attribute_spec.rb +22 -0
  60. data/spec/unit/node/immutable_collections_spec.rb +72 -144
  61. data/spec/unit/provider/package/chocolatey_spec.rb +50 -35
  62. data/spec/unit/provider/package/windows/exe_spec.rb +1 -1
  63. data/spec/unit/provider/service/windows_spec.rb +23 -3
  64. data/spec/unit/resource/chocolatey_package_spec.rb +17 -2
  65. data/spec/unit/resource/windows_ad_join_spec.rb +4 -0
  66. data/spec/unit/resource/windows_service_spec.rb +5 -0
  67. data/spec/unit/resource/windows_share_spec.rb +7 -0
  68. data/tasks/docs.rb +4 -1
  69. metadata +10 -8
@@ -141,6 +141,8 @@ RSpec.configure do |config|
141
141
  config.filter_run_excluding skip_appveyor: true if ENV["APPVEYOR"]
142
142
  config.filter_run_excluding appveyor_only: true unless ENV["APPVEYOR"]
143
143
 
144
+ config.filter_run_excluding skip_buildkite: true if ENV["BUILDKITE"]
145
+
144
146
  config.filter_run_excluding windows_only: true unless windows?
145
147
  config.filter_run_excluding not_supported_on_windows: true if windows?
146
148
  config.filter_run_excluding not_supported_on_macos: true if mac_osx?
@@ -76,6 +76,24 @@ describe Chef::Formatters::Base do
76
76
  expect(formatter.pretty_elapsed_time).to include("10 hours 10 minutes 10 seconds")
77
77
  end
78
78
 
79
+ it "shows nothing if total is nil" do
80
+ res = Chef::Resource::RemoteFile.new("canteloupe")
81
+ formatter.resource_update_progress(res, 35, nil, 10)
82
+ expect(out.string).to eq("")
83
+ end
84
+
85
+ it "shows nothing if total is 0" do
86
+ res = Chef::Resource::RemoteFile.new("canteloupe")
87
+ formatter.resource_update_progress(res, 35, 0, 10)
88
+ expect(out.string).to eq("")
89
+ end
90
+
91
+ it "shows nothing if current and total are 0" do
92
+ res = Chef::Resource::RemoteFile.new("canteloupe")
93
+ formatter.resource_update_progress(res, 0, 0, 10)
94
+ expect(out.string).to eq("")
95
+ end
96
+
79
97
  it "shows the percentage completion of an action" do
80
98
  res = Chef::Resource::RemoteFile.new("canteloupe")
81
99
  formatter.resource_update_progress(res, 35, 50, 10)
@@ -1839,6 +1839,20 @@ describe Chef::Knife::Bootstrap do
1839
1839
  e
1840
1840
  end
1841
1841
 
1842
+ let(:expected_error_password_prompt) do
1843
+ e = Train::ClientError.new
1844
+ reason = :no_ssh_password_or_key_available
1845
+ allow(e).to receive(:reason).and_return(reason)
1846
+ e
1847
+ end
1848
+
1849
+ let(:expected_error_password_prompt_winrm) do
1850
+ e = RuntimeError.new
1851
+ message = "password is a required option"
1852
+ allow(e).to receive(:message).and_return(message)
1853
+ e
1854
+ end
1855
+
1842
1856
  context "and password auth was used" do
1843
1857
  before do
1844
1858
  allow(connection).to receive(:password_auth?).and_return true
@@ -1854,19 +1868,41 @@ describe Chef::Knife::Bootstrap do
1854
1868
  before do
1855
1869
  allow(connection).to receive(:password_auth?).and_return false
1856
1870
  allow(connection).to receive(:user).and_return "testuser"
1871
+ allow(knife).to receive(:connection_protocol).and_return connection_protocol
1857
1872
  end
1858
1873
 
1859
- it "warns, prompts for password, then reconnects with a password-enabled configuration using the new password" do
1860
- question_mock = double("question")
1861
- expect(knife).to receive(:do_connect).and_raise(expected_error)
1862
- expect(knife.ui).to receive(:warn).with(/Failed to auth.*/)
1863
- expect(knife.ui).to receive(:ask).and_yield(question_mock).and_return("newpassword")
1864
- # Ensure that we set echo off to prevent showing password on the screen
1865
- expect(question_mock).to receive(:echo=).with false
1866
- expect(knife).to receive(:do_connect) do |opts|
1867
- expect(opts[:password]).to eq "newpassword"
1874
+ context "when using ssh" do
1875
+ let(:connection_protocol) { "ssh" }
1876
+
1877
+ it "warns, prompts for password, then reconnects with a password-enabled configuration using the new password" do
1878
+ question_mock = double("question")
1879
+ expect(knife).to receive(:do_connect).and_raise(expected_error_password_prompt)
1880
+ expect(knife.ui).to receive(:warn).with(/Failed to auth.*/)
1881
+ expect(knife.ui).to receive(:ask).and_yield(question_mock).and_return("newpassword")
1882
+ # Ensure that we set echo off to prevent showing password on the screen
1883
+ expect(question_mock).to receive(:echo=).with false
1884
+ expect(knife).to receive(:do_connect) do |opts|
1885
+ expect(opts[:password]).to eq "newpassword"
1886
+ end
1887
+ knife.connect!
1888
+ end
1889
+ end
1890
+
1891
+ context "when using winrm" do
1892
+ let(:connection_protocol) { "winrm" }
1893
+
1894
+ it "warns, prompts for password, then reconnects with a password-enabled configuration using the new password for" do
1895
+ question_mock = double("question")
1896
+ expect(knife).to receive(:do_connect).and_raise(expected_error_password_prompt_winrm)
1897
+ expect(knife.ui).to receive(:warn).with(/Failed to auth.*/)
1898
+ expect(knife.ui).to receive(:ask).and_yield(question_mock).and_return("newpassword")
1899
+ # Ensure that we set echo off to prevent showing password on the screen
1900
+ expect(question_mock).to receive(:echo=).with false
1901
+ expect(knife).to receive(:do_connect) do |opts|
1902
+ expect(opts[:password]).to eq "newpassword"
1903
+ end
1904
+ knife.connect!
1868
1905
  end
1869
- knife.connect!
1870
1906
  end
1871
1907
  end
1872
1908
  end
@@ -0,0 +1,70 @@
1
+ #
2
+ # Author:: Vivek Singh (<vivek.singh@msystechnologies.com>)
3
+ # Copyright:: Copyright 2018-2019, Chef Software Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require "chef/knife/supermarket_list"
20
+ require "spec_helper"
21
+
22
+ describe Chef::Knife::SupermarketList do
23
+ let(:knife) { described_class.new }
24
+ let(:noauth_rest) { double("no auth rest") }
25
+ let(:stdout) { StringIO.new }
26
+ let(:cookbooks_data) {
27
+ [
28
+ { "cookbook_name" => "1password", "cookbook_maintainer" => "jtimberman", "cookbook_description" => "Installs 1password", "cookbook" => "https://supermarket.chef.io/api/v1/cookbooks/1password" },
29
+ { "cookbook_name" => "301", "cookbook_maintainer" => "markhuge", "cookbook_description" => "Installs/Configures 301", "cookbook" => "https://supermarket.chef.io/api/v1/cookbooks/301" },
30
+ { "cookbook_name" => "3cx", "cookbook_maintainer" => "obay", "cookbook_description" => "Installs/Configures 3cx", "cookbook" => "https://supermarket.chef.io/api/v1/cookbooks/3cx" },
31
+ { "cookbook_name" => "7dtd", "cookbook_maintainer" => "gregf", "cookbook_description" => "Installs/Configures the 7 Days To Die server", "cookbook" => "https://supermarket.chef.io/api/v1/cookbooks/7dtd" },
32
+ { "cookbook_name" => "7-zip", "cookbook_maintainer" => "sneal", "cookbook_description" => "Installs/Configures the 7-zip file archiver", "cookbook" => "https://supermarket.chef.io/api/v1/cookbooks/7-zip" },
33
+ ]
34
+ }
35
+
36
+ let(:response_text) {
37
+ {
38
+ "start" => 0,
39
+ "total" => 5,
40
+ "items" => cookbooks_data,
41
+ }
42
+ }
43
+
44
+ describe "run" do
45
+ before do
46
+ allow(knife.ui).to receive(:stdout).and_return(stdout)
47
+ allow(knife).to receive(:noauth_rest).and_return(noauth_rest)
48
+ expect(noauth_rest).to receive(:get).and_return(response_text)
49
+ knife.configure_chef
50
+ end
51
+
52
+ it "should display all supermarket cookbooks" do
53
+ knife.run
54
+ cookbooks_data.each do |item|
55
+ expect(stdout.string).to match /#{item["cookbook_name"]}\s/
56
+ end
57
+ end
58
+
59
+ describe "with -w or --with-uri" do
60
+ it "should display the cookbook uris" do
61
+ knife.config[:with_uri] = true
62
+ knife.run
63
+ cookbooks_data.each do |item|
64
+ expect(stdout.string).to match /#{item["cookbook_name"]}\s/
65
+ expect(stdout.string).to match /#{item["cookbook"]}\s/
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,85 @@
1
+ #
2
+ # Author:: Vivek Singh (<vivek.singh@msystechnologies.com>)
3
+ # Copyright:: Copyright 2018-2019, Chef Software Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require "chef/knife/supermarket_search"
20
+ require "spec_helper"
21
+
22
+ describe Chef::Knife::SupermarketSearch do
23
+ let(:knife) { described_class.new }
24
+ let(:noauth_rest) { double("no auth rest") }
25
+ let(:stdout) { StringIO.new }
26
+ let(:cookbooks_data) {
27
+ [
28
+ { "cookbook_name" => "mysql", "cookbook_maintainer" => "sous-chefs", "cookbook_description" => "Provides mysql_service, mysql_config, and mysql_client resources", "cookbook" => "https://supermarket.chef.io/api/v1/cookbooks/mysql" },
29
+ { "cookbook_name" => "mw_mysql", "cookbook_maintainer" => "car", "cookbook_description" => "Installs/Configures mw_mysql", "cookbook" => "https://supermarket.chef.io/api/v1/cookbooks/mw_mysql" },
30
+ { "cookbook_name" => "L7-mysql", "cookbook_maintainer" => "szelcsanyi", "cookbook_description" => "Installs/Configures MySQL server", "cookbook" => "https://supermarket.chef.io/api/v1/cookbooks/l7-mysql" },
31
+ { "cookbook_name" => "mysql-sys", "cookbook_maintainer" => "ovaistariq", "cookbook_description" => "Installs the mysql-sys tool. Description of the tool is available here https://github.com/MarkLeith/mysql-sys", "cookbook" => "https://supermarket.chef.io/api/v1/cookbooks/mysql-sys" },
32
+ { "cookbook_name" => "cg_mysql", "cookbook_maintainer" => "phai", "cookbook_description" => "Installs/Configures mysql with master and slave", "cookbook" => "https://supermarket.chef.io/api/v1/cookbooks/cg_mysql" },
33
+ ]
34
+ }
35
+
36
+ let(:response_text) {
37
+ {
38
+ "start" => 0,
39
+ "total" => 5,
40
+ "items" => cookbooks_data,
41
+ }
42
+ }
43
+
44
+ let(:empty_response_text) {
45
+ {
46
+ "start" => 0,
47
+ "total" => 0,
48
+ "items" => [],
49
+ }
50
+ }
51
+
52
+ describe "run" do
53
+ before do
54
+ allow(knife.ui).to receive(:stdout).and_return(stdout)
55
+ allow(knife).to receive(:noauth_rest).and_return(noauth_rest)
56
+ knife.configure_chef
57
+ end
58
+
59
+ context "when name_args is present" do
60
+ before do
61
+ expect(noauth_rest).to receive(:get).and_return(response_text)
62
+ end
63
+
64
+ it "should display cookbooks with given name value" do
65
+ knife.name_args = ["mysql"]
66
+ knife.run
67
+ cookbooks_data.each do |item|
68
+ expect(stdout.string).to match /#{item["cookbook_name"]}\s/
69
+ end
70
+ end
71
+ end
72
+
73
+ context "when name_args is empty string" do
74
+ before do
75
+ expect(noauth_rest).to receive(:get).and_return(empty_response_text)
76
+ end
77
+
78
+ it "display nothing with name arg empty string" do
79
+ knife.name_args = [""]
80
+ knife.run
81
+ expect(stdout.string).to eq("\n")
82
+ end
83
+ end
84
+ end
85
+ end
@@ -1297,4 +1297,26 @@ describe Chef::Node::Attribute do
1297
1297
  expect(@attributes["foo"]).to be nil
1298
1298
  end
1299
1299
  end
1300
+
1301
+ describe "to_json" do
1302
+ it "should convert to a valid json string" do
1303
+ json = @attributes["hot"].to_json
1304
+ expect { JSON.parse(json) }.not_to raise_error
1305
+ end
1306
+
1307
+ it "should convert to a json based on current state" do
1308
+ expect(@attributes["hot"].to_json).to eq("{\"day\":\"sunday\"}")
1309
+ end
1310
+ end
1311
+
1312
+ describe "to_yaml" do
1313
+ it "should convert to a valid yaml format" do
1314
+ json = @attributes["hot"].to_yaml
1315
+ expect { YAML.parse(json) }.not_to raise_error
1316
+ end
1317
+
1318
+ it "should convert to a yaml based on current state" do
1319
+ expect(@attributes["hot"].to_yaml).to eq("---\nday: sunday\n")
1320
+ end
1321
+ end
1300
1322
  end
@@ -19,6 +19,64 @@
19
19
  require "spec_helper"
20
20
  require "chef/node/immutable_collections"
21
21
 
22
+ shared_examples_for "ImmutableMash module" do |param|
23
+ let(:copy) { @immutable_mash.send(param) }
24
+
25
+ it "converts an immutable mash to a new mutable hash" do
26
+ expect(copy).to be_is_a(Hash)
27
+ end
28
+
29
+ it "converts an immutable nested mash to a new mutable hash" do
30
+ expect(copy["top_level_4"]["level2"]).to be_is_a(Hash)
31
+ end
32
+
33
+ it "converts an immutable nested array to a new mutable array" do
34
+ expect(copy["top_level_2"]).to be_instance_of(Array)
35
+ end
36
+
37
+ it "should create a mash with the same content" do
38
+ expect(copy).to eq(@immutable_mash)
39
+ end
40
+
41
+ it "should allow mutation" do
42
+ expect { copy["m"] = "m" }.not_to raise_error
43
+ end
44
+ end
45
+
46
+ shared_examples_for "ImmutableArray module" do |param|
47
+ let(:copy) { @immutable_nested_array.send(param) }
48
+
49
+ it "converts an immutable array to a new mutable array" do
50
+ expect(copy).to be_instance_of(Array)
51
+ end
52
+
53
+ it "converts an immutable nested array to a new mutable array" do
54
+ expect(copy[1]).to be_instance_of(Array)
55
+ end
56
+
57
+ it "converts an immutable nested mash to a new mutable hash" do
58
+ expect(copy[2]).to be_is_a(Hash)
59
+ end
60
+
61
+ it "should create an array with the same content" do
62
+ expect(copy).to eq(@immutable_nested_array)
63
+ end
64
+
65
+ it "should allow mutation" do
66
+ expect { copy << "m" }.not_to raise_error
67
+ end
68
+ end
69
+
70
+ shared_examples_for "Immutable#to_yaml" do
71
+ it "converts an immutable array to a new valid YAML mutable string" do
72
+ expect { YAML.parse(copy) }.not_to raise_error
73
+ end
74
+
75
+ it "should create a YAML string with content" do
76
+ expect(copy).to eq(parsed_yaml)
77
+ end
78
+ end
79
+
22
80
  describe Chef::Node::ImmutableMash do
23
81
  before do
24
82
  @data_in = { "top" => { "second_level" => "some value" },
@@ -67,82 +125,17 @@ describe Chef::Node::ImmutableMash do
67
125
  expect(@mash["test2"]).to eql("bar")
68
126
  end
69
127
 
70
- describe "to_hash" do
71
- before do
72
- @copy = @immutable_mash.to_hash
73
- end
74
-
75
- it "converts an immutable mash to a new mutable hash" do
76
- expect(@copy).to be_instance_of(Hash)
77
- end
78
-
79
- it "converts an immutable nested mash to a new mutable hash" do
80
- expect(@copy["top_level_4"]["level2"]).to be_instance_of(Hash)
81
- end
82
-
83
- it "converts an immutable nested array to a new mutable array" do
84
- expect(@copy["top_level_2"]).to be_instance_of(Array)
85
- end
86
-
87
- it "should create a mash with the same content" do
88
- expect(@copy).to eq(@immutable_mash)
89
- end
90
-
91
- it "should allow mutation" do
92
- expect { @copy["m"] = "m" }.not_to raise_error
128
+ %w{to_h to_hash dup}.each do |immutable_meth|
129
+ describe "#{immutable_meth}" do
130
+ include_examples "ImmutableMash module", description
93
131
  end
94
132
  end
95
133
 
96
- describe "dup" do
97
- before do
98
- @copy = @immutable_mash.dup
99
- end
100
-
101
- it "converts an immutable mash to a new mutable hash" do
102
- expect(@copy).to be_instance_of(Mash)
103
- end
104
-
105
- it "converts an immutable nested mash to a new mutable hash" do
106
- expect(@copy["top_level_4"]["level2"]).to be_instance_of(Mash)
107
- end
108
-
109
- it "converts an immutable nested array to a new mutable array" do
110
- expect(@copy["top_level_2"]).to be_instance_of(Array)
111
- end
112
-
113
- it "should create a mash with the same content" do
114
- expect(@copy).to eq(@immutable_mash)
115
- end
134
+ describe "to_yaml" do
135
+ let(:copy) { @immutable_mash.to_yaml }
136
+ let(:parsed_yaml) { "---\ntop:\n second_level: some value\ntop_level_2:\n- array\n- of\n- values\ntop_level_3:\n- hash_array: 1\n hash_array_b: 2\ntop_level_4:\n level2:\n key: value\n" }
116
137
 
117
- it "should allow mutation" do
118
- expect { @copy["m"] = "m" }.not_to raise_error
119
- end
120
- end
121
-
122
- describe "to_h" do
123
- before do
124
- @copy = @immutable_mash.to_h
125
- end
126
-
127
- it "converts an immutable mash to a new mutable hash" do
128
- expect(@copy).to be_instance_of(Hash)
129
- end
130
-
131
- it "converts an immutable nested mash to a new mutable hash" do
132
- expect(@copy["top_level_4"]["level2"]).to be_instance_of(Hash)
133
- end
134
-
135
- it "converts an immutable nested array to a new mutable array" do
136
- expect(@copy["top_level_2"]).to be_instance_of(Array)
137
- end
138
-
139
- it "should create a mash with the same content" do
140
- expect(@copy).to eq(@immutable_mash)
141
- end
142
-
143
- it "should allow mutation" do
144
- expect { @copy["m"] = "m" }.not_to raise_error
145
- end
138
+ include_examples "Immutable#to_yaml"
146
139
  end
147
140
 
148
141
  %i{
@@ -240,82 +233,17 @@ describe Chef::Node::ImmutableArray do
240
233
  expect(mutable[0]).to eq(:value)
241
234
  end
242
235
 
243
- describe "to_a" do
244
- before do
245
- @copy = @immutable_nested_array.to_a
246
- end
247
-
248
- it "converts an immutable array to a new mutable array" do
249
- expect(@copy).to be_instance_of(Array)
250
- end
251
-
252
- it "converts an immutable nested array to a new mutable array" do
253
- expect(@copy[1]).to be_instance_of(Array)
254
- end
255
-
256
- it "converts an immutable nested mash to a new mutable hash" do
257
- expect(@copy[2]).to be_instance_of(Hash)
258
- end
259
-
260
- it "should create an array with the same content" do
261
- expect(@copy).to eq(@immutable_nested_array)
262
- end
263
-
264
- it "should allow mutation" do
265
- expect { @copy << "m" }.not_to raise_error
236
+ %w{to_a to_array dup}.each do |immutable_meth|
237
+ describe "#{immutable_meth}" do
238
+ include_examples "ImmutableArray module", description
266
239
  end
267
240
  end
268
241
 
269
- describe "dup" do
270
- before do
271
- @copy = @immutable_nested_array.dup
272
- end
273
-
274
- it "converts an immutable array to a new mutable array" do
275
- expect(@copy).to be_instance_of(Array)
276
- end
277
-
278
- it "converts an immutable nested array to a new mutable array" do
279
- expect(@copy[1]).to be_instance_of(Array)
280
- end
281
-
282
- it "converts an immutable nested mash to a new mutable hash" do
283
- expect(@copy[2]).to be_instance_of(Mash)
284
- end
285
-
286
- it "should create an array with the same content" do
287
- expect(@copy).to eq(@immutable_nested_array)
288
- end
289
-
290
- it "should allow mutation" do
291
- expect { @copy << "m" }.not_to raise_error
292
- end
293
- end
242
+ describe "to_yaml" do
243
+ let(:copy) { @immutable_nested_array.to_yaml }
244
+ let(:parsed_yaml) { "---\n- level1\n- - foo\n - bar\n - baz\n - 1\n - 2\n - 3\n - \n - true\n - false\n - - el\n - 0\n - \n- m: m\n" }
294
245
 
295
- describe "to_array" do
296
- before do
297
- @copy = @immutable_nested_array.to_array
298
- end
299
-
300
- it "converts an immutable array to a new mutable array" do
301
- expect(@copy).to be_instance_of(Array)
302
- end
303
-
304
- it "converts an immutable nested array to a new mutable array" do
305
- expect(@copy[1]).to be_instance_of(Array)
306
- end
307
-
308
- it "converts an immutable nested mash to a new mutable hash" do
309
- expect(@copy[2]).to be_instance_of(Hash)
310
- end
311
-
312
- it "should create an array with the same content" do
313
- expect(@copy).to eq(@immutable_nested_array)
314
- end
315
-
316
- it "should allow mutation" do
317
- expect { @copy << "m" }.not_to raise_error
318
- end
246
+ include_examples "Immutable#to_yaml"
319
247
  end
320
248
 
321
249
  describe "#[]" do