chef 15.3.14 → 15.4.45

Sign up to get free protection for your applications and to get access to all the features.
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