chef-dk 0.2.1 → 0.3.0

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +2 -2
  3. data/README.md +25 -0
  4. data/lib/chef-dk/builtin_commands.rb +4 -0
  5. data/lib/chef-dk/cli.rb +46 -0
  6. data/lib/chef-dk/command/base.rb +4 -0
  7. data/lib/chef-dk/command/generator_commands/template.rb +2 -1
  8. data/lib/chef-dk/command/install.rb +105 -0
  9. data/lib/chef-dk/command/push.rb +123 -0
  10. data/lib/chef-dk/cookbook_profiler/identifiers.rb +5 -0
  11. data/lib/chef-dk/exceptions.rb +38 -0
  12. data/lib/chef-dk/generator.rb +16 -1
  13. data/lib/chef-dk/helpers.rb +1 -1
  14. data/lib/chef-dk/policyfile/cookbook_location_specification.rb +4 -0
  15. data/lib/chef-dk/policyfile/cookbook_locks.rb +73 -0
  16. data/lib/chef-dk/policyfile/reports/install.rb +70 -0
  17. data/lib/chef-dk/policyfile/reports/table_printer.rb +58 -0
  18. data/lib/chef-dk/policyfile/reports/upload.rb +70 -0
  19. data/lib/chef-dk/policyfile/solution_dependencies.rb +102 -8
  20. data/lib/chef-dk/policyfile/uploader.rb +37 -6
  21. data/lib/chef-dk/policyfile_compiler.rb +19 -5
  22. data/lib/chef-dk/policyfile_lock.rb +122 -9
  23. data/lib/chef-dk/policyfile_services/install.rb +131 -0
  24. data/lib/chef-dk/policyfile_services/push.rb +121 -0
  25. data/lib/chef-dk/skeletons/code_generator/recipes/cookbook.rb +6 -4
  26. data/lib/chef-dk/ui.rb +50 -0
  27. data/lib/chef-dk/version.rb +1 -1
  28. data/spec/shared/a_file_generator.rb +4 -1
  29. data/spec/test_helpers.rb +21 -0
  30. data/spec/unit/cli_spec.rb +100 -1
  31. data/spec/unit/command/base_spec.rb +23 -0
  32. data/spec/unit/command/exec_spec.rb +2 -2
  33. data/spec/unit/command/install_spec.rb +159 -0
  34. data/spec/unit/command/push_spec.rb +203 -0
  35. data/spec/unit/command/shell_init_spec.rb +1 -1
  36. data/spec/unit/policyfile/cookbook_location_specification_spec.rb +7 -0
  37. data/spec/unit/policyfile/cookbook_locks_spec.rb +13 -2
  38. data/spec/unit/policyfile/reports/install_spec.rb +115 -0
  39. data/spec/unit/policyfile/reports/upload_spec.rb +96 -0
  40. data/spec/unit/policyfile/solution_dependencies_spec.rb +1 -1
  41. data/spec/unit/policyfile/uploader_spec.rb +9 -12
  42. data/spec/unit/policyfile_lock_serialization_spec.rb +292 -0
  43. data/spec/unit/policyfile_services/install_spec.rb +170 -0
  44. data/spec/unit/policyfile_services/push_spec.rb +202 -0
  45. metadata +48 -6
@@ -20,7 +20,7 @@ require 'chef-dk/policyfile/solution_dependencies'
20
20
 
21
21
  describe ChefDK::Policyfile::SolutionDependencies do
22
22
 
23
- let(:dependency_data) { {} }
23
+ let(:dependency_data) { {"Policyfile" => [], "dependencies" => {}} }
24
24
 
25
25
  let(:solution_dependencies) do
26
26
  s = described_class.new
@@ -79,17 +79,6 @@ describe ChefDK::Policyfile::Uploader do
79
79
  expect(uploader.http_client).to eq(http_client)
80
80
  end
81
81
 
82
- context "when created without an HTTP client" do
83
-
84
- let(:http_client) { nil }
85
-
86
- it "creates an HTTP client with default config" do
87
- skip "TODO: determine correct behavior"
88
- end
89
-
90
- end
91
-
92
-
93
82
  describe "creating uploading documents in compat mode" do
94
83
 
95
84
  let(:cookbook_locks) { {} }
@@ -132,6 +121,8 @@ describe ChefDK::Policyfile::Uploader do
132
121
 
133
122
  lock = instance_double("ChefDK::Policyfile::CookbookLock",
134
123
  name: name,
124
+ version: "1.0.0",
125
+ identifier: "64b3e64306cff223206348e46af545b19032b170",
135
126
  dotted_decimal_identifier: dotted_decimal_id,
136
127
  cookbook_path: cache_path)
137
128
 
@@ -212,7 +203,13 @@ describe ChefDK::Policyfile::Uploader do
212
203
  it "lists the cookbooks in the lock as possibly needing to be uploaded" do
213
204
  expect(policyfile_lock).to receive(:validate_cookbooks!)
214
205
 
215
- expect(uploader.cookbook_versions_for_policy).to eq(cookbook_versions.values)
206
+ expected_versions_for_policy = cookbook_versions.keys.map do |cb_name|
207
+ cb = cookbook_versions[cb_name]
208
+ lock = cookbook_locks[cb_name]
209
+ ChefDK::Policyfile::Uploader::LockedCookbookForUpload.new(cb, lock)
210
+ end
211
+
212
+ expect(uploader.cookbook_versions_for_policy).to eq(expected_versions_for_policy)
216
213
  end
217
214
 
218
215
  it "lists all cookbooks in the lock as needing to be uploaded" do
@@ -0,0 +1,292 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2014 Chef Software Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require 'spec_helper'
19
+ require 'chef-dk/policyfile_lock'
20
+
21
+ describe ChefDK::PolicyfileLock, "when reading a Policyfile.lock" do
22
+
23
+ let(:valid_lock_data) do
24
+ {
25
+ "name" => "example",
26
+ "run_list" => [ "recipe[cookbook::recipe_name]" ],
27
+ "cookbook_locks" => {
28
+ # TODO: add some valid locks
29
+ },
30
+ "solution_dependencies" => {
31
+ "Policyfile" => [],
32
+ "dependencies" => {}
33
+ }
34
+ }
35
+ end
36
+
37
+ let(:storage_config) { ChefDK::Policyfile::StorageConfig.new }
38
+
39
+ let(:lockfile) { ChefDK::PolicyfileLock.new(storage_config) }
40
+
41
+ describe "validating required fields" do
42
+
43
+ it "does not raise an error when all fields are valid" do
44
+ expect { lockfile.build_from_lock_data(valid_lock_data) }.to_not raise_error
45
+ end
46
+
47
+ it "requires the name to be present" do
48
+ missing_name = valid_lock_data.dup
49
+ missing_name.delete("name")
50
+
51
+ expect { lockfile.build_from_lock_data(missing_name) }.to raise_error(ChefDK::InvalidLockfile)
52
+
53
+ blank_name = valid_lock_data.dup
54
+ blank_name["name"] = ""
55
+ expect { lockfile.build_from_lock_data(blank_name) }.to raise_error(ChefDK::InvalidLockfile)
56
+
57
+ invalid_name = valid_lock_data.dup
58
+ invalid_name["name"] = {}
59
+ expect { lockfile.build_from_lock_data(invalid_name) }.to raise_error(ChefDK::InvalidLockfile)
60
+ end
61
+
62
+ it "requires the run_list to be present" do
63
+ no_run_list = valid_lock_data.dup
64
+ no_run_list.delete("run_list")
65
+
66
+ expect { lockfile.build_from_lock_data(no_run_list) }.to raise_error(ChefDK::InvalidLockfile)
67
+
68
+ bad_run_list = valid_lock_data.dup
69
+ bad_run_list["run_list"] = "bad data"
70
+ expect { lockfile.build_from_lock_data(bad_run_list) }.to raise_error(ChefDK::InvalidLockfile)
71
+ end
72
+
73
+ it "validates the format of run_list items" do
74
+ bad_run_list = valid_lock_data.dup
75
+ bad_run_list["run_list"] = [ "bad data" ]
76
+
77
+ expect { lockfile.build_from_lock_data(bad_run_list) }.to raise_error(ChefDK::InvalidLockfile)
78
+ end
79
+
80
+ it "requires the `cookbook_locks` section be present and its value is a Hash" do
81
+ missing_locks = valid_lock_data.dup
82
+ missing_locks.delete("cookbook_locks")
83
+
84
+ expect { lockfile.build_from_lock_data(missing_locks) }.to raise_error(ChefDK::InvalidLockfile)
85
+
86
+ invalid_locks = valid_lock_data.dup
87
+ invalid_locks["cookbook_locks"] = []
88
+ expect { lockfile.build_from_lock_data(invalid_locks) }.to raise_error(ChefDK::InvalidLockfile)
89
+ end
90
+
91
+ describe "validating solution_dependencies" do
92
+
93
+ it "requires the `solution_dependencies' section be present" do
94
+ missing_soln_deps = valid_lock_data.dup
95
+ missing_soln_deps.delete("solution_dependencies")
96
+
97
+ expect { lockfile.build_from_lock_data(missing_soln_deps) }.to raise_error(ChefDK::InvalidLockfile)
98
+ end
99
+
100
+ it "requires the solution_dependencies object be a Hash" do
101
+ invalid_soln_deps = valid_lock_data.dup
102
+ invalid_soln_deps["solution_dependencies"] = []
103
+ expect { lockfile.build_from_lock_data(invalid_soln_deps) }.to raise_error(ChefDK::InvalidLockfile)
104
+ end
105
+
106
+ it "requires the solution_dependencies object have a 'Policyfile' and 'dependencies' key" do
107
+ missing_keys_soln_deps = valid_lock_data.dup
108
+ missing_keys_soln_deps["solution_dependencies"] = {}
109
+ expect { lockfile.build_from_lock_data(missing_keys_soln_deps) }.to raise_error(ChefDK::InvalidLockfile)
110
+
111
+ missing_policyfile_key = valid_lock_data.dup
112
+ missing_policyfile_key["solution_dependencies"] = {"dependencies" => {} }
113
+ expect { lockfile.build_from_lock_data(missing_policyfile_key) }.to raise_error(ChefDK::InvalidLockfile)
114
+
115
+ missing_dependencies_key = valid_lock_data.dup
116
+ missing_dependencies_key["solution_dependencies"] = { "Policyfile" => [] }
117
+ expect { lockfile.build_from_lock_data(missing_dependencies_key) }.to raise_error(ChefDK::InvalidLockfile)
118
+ end
119
+
120
+ it "requires the Policyfile dependencies be an Array" do
121
+ invalid_policyfile_deps = valid_lock_data.dup
122
+ invalid_policyfile_deps["solution_dependencies"] = {"Policyfile" => 42, "dependencies" => {} }
123
+ expect { lockfile.build_from_lock_data(invalid_policyfile_deps) }.to raise_error(ChefDK::InvalidLockfile)
124
+ end
125
+
126
+ it %q(requires the Policyfile dependencies be formatted like [ "COOKBOOK_NAME", "CONSTRAINT" ]) do
127
+ invalid_policyfile_deps_content = valid_lock_data.dup
128
+ invalid_policyfile_deps_content["solution_dependencies"] = { "Policyfile" => [ "bad" ], "dependencies" => {} }
129
+ expect { lockfile.build_from_lock_data(invalid_policyfile_deps_content) }.to raise_error(ChefDK::InvalidLockfile)
130
+
131
+ invalid_policyfile_deps_content2 = valid_lock_data.dup
132
+ invalid_policyfile_deps_content2["solution_dependencies"] = { "Policyfile" => [ [42, "~> 2.0"] ], "dependencies" => {} }
133
+ expect { lockfile.build_from_lock_data(invalid_policyfile_deps_content2) }.to raise_error(ChefDK::InvalidLockfile)
134
+
135
+ invalid_policyfile_deps_content3 = valid_lock_data.dup
136
+ invalid_policyfile_deps_content3["solution_dependencies"] = { "Policyfile" => [ ["cookbook_name", "bad"] ], "dependencies" => {} }
137
+ expect { lockfile.build_from_lock_data(invalid_policyfile_deps_content3) }.to raise_error(ChefDK::InvalidLockfile)
138
+ end
139
+
140
+ it "requires the cookbook dependencies be a Hash" do
141
+ invalid_cookbook_deps = valid_lock_data.dup
142
+ invalid_cookbook_deps["solution_dependencies"] = { "Policyfile" => [], "dependencies" => 42 }
143
+ expect { lockfile.build_from_lock_data(invalid_cookbook_deps) }.to raise_error(ChefDK::InvalidLockfile)
144
+ end
145
+
146
+ it "requires the cookbook dependencies entries be in the correct format" do
147
+ invalid_cookbook_deps = valid_lock_data.dup
148
+ bad_deps = { 42 => 42 }
149
+ invalid_cookbook_deps["solution_dependencies"] = { "Policyfile" => [], "dependencies" => bad_deps }
150
+ expect { lockfile.build_from_lock_data(invalid_cookbook_deps) }.to raise_error(ChefDK::InvalidLockfile)
151
+
152
+ invalid_cookbook_deps2 = valid_lock_data.dup
153
+ bad_deps2 = { "bad-format" => [] }
154
+ invalid_cookbook_deps2["solution_dependencies"] = { "Policyfile" => [], "dependencies" => bad_deps2 }
155
+ expect { lockfile.build_from_lock_data(invalid_cookbook_deps2) }.to raise_error(ChefDK::InvalidLockfile)
156
+
157
+ invalid_cookbook_deps3 = valid_lock_data.dup
158
+ bad_deps3 = { "cookbook (1.0.0)" => 42 }
159
+ invalid_cookbook_deps3["solution_dependencies"] = { "Policyfile" => [], "dependencies" => bad_deps3 }
160
+ expect { lockfile.build_from_lock_data(invalid_cookbook_deps3) }.to raise_error(ChefDK::InvalidLockfile)
161
+
162
+ invalid_cookbook_deps4 = valid_lock_data.dup
163
+ bad_deps4 = { "cookbook (1.0.0)" => [ 42 ] }
164
+ invalid_cookbook_deps4["solution_dependencies"] = { "Policyfile" => [], "dependencies" => bad_deps4 }
165
+ expect { lockfile.build_from_lock_data(invalid_cookbook_deps4) }.to raise_error(ChefDK::InvalidLockfile)
166
+ end
167
+ end
168
+
169
+ describe "validating cookbook_locks entries" do
170
+
171
+ # TODO: also check non-cached cookbook
172
+ let(:valid_cookbook_lock) do
173
+ {
174
+ "version" => "1.0.0",
175
+ "identifier" => "68c13b136a49b4e66cfe9d8aa2b5a85167b5bf9b",
176
+ "dotted_decimal_identifier" => "111.222.333",
177
+ "cache_key" => "foo-1.0.0",
178
+ "source_options" => {}
179
+ }
180
+ end
181
+
182
+ it "requires that each cookbook lock be a Hash" do
183
+ invalid_cookbook_lock = valid_lock_data.dup
184
+ invalid_cookbook_lock["cookbook_locks"] = { "foo" => 42 }
185
+ expect { lockfile.build_from_lock_data(invalid_cookbook_lock) }.to raise_error(ChefDK::InvalidLockfile)
186
+ end
187
+
188
+ it "requires that cookbook locks not be empty" do
189
+ invalid_cookbook_lock = valid_lock_data.dup
190
+ invalid_cookbook_lock["cookbook_locks"] = { "foo" => {} }
191
+ expect { lockfile.build_from_lock_data(invalid_cookbook_lock) }.to raise_error(ChefDK::InvalidLockfile)
192
+ end
193
+
194
+ it "requires that each cookbook lock have a version" do
195
+ invalid_lockfile = valid_lock_data.dup
196
+ invalid_cookbook_lock = valid_cookbook_lock.dup
197
+ invalid_cookbook_lock.delete("version")
198
+ invalid_lockfile["cookbook_locks"] = { "foo" => invalid_cookbook_lock }
199
+ expect { lockfile.build_from_lock_data(invalid_lockfile) }.to raise_error(ChefDK::InvalidLockfile)
200
+ end
201
+
202
+ it "requires that the version be a string" do
203
+ invalid_lockfile = valid_lock_data.dup
204
+ invalid_cookbook_lock = valid_cookbook_lock.dup
205
+ invalid_cookbook_lock["version"] = 42
206
+ invalid_lockfile["cookbook_locks"] = { "foo" => invalid_cookbook_lock }
207
+ expect { lockfile.build_from_lock_data(invalid_lockfile) }.to raise_error(ChefDK::InvalidLockfile)
208
+ end
209
+
210
+ it "requires that each cookbook lock have an identifier" do
211
+ invalid_lockfile = valid_lock_data.dup
212
+ invalid_cookbook_lock = valid_cookbook_lock.dup
213
+ invalid_cookbook_lock.delete("identifier")
214
+ invalid_lockfile["cookbook_locks"] = { "foo" => invalid_cookbook_lock }
215
+ expect { lockfile.build_from_lock_data(invalid_lockfile) }.to raise_error(ChefDK::InvalidLockfile)
216
+ end
217
+
218
+ it "requires that the identifier be a string" do
219
+ invalid_lockfile = valid_lock_data.dup
220
+ invalid_cookbook_lock = valid_cookbook_lock.dup
221
+ invalid_cookbook_lock["identifier"] = 42
222
+ invalid_lockfile["cookbook_locks"] = { "foo" => invalid_cookbook_lock }
223
+ expect { lockfile.build_from_lock_data(invalid_lockfile) }.to raise_error(ChefDK::InvalidLockfile)
224
+ end
225
+
226
+ it "requires that a cookbook lock have a key named `cache_key'" do
227
+ invalid_lockfile = valid_lock_data.dup
228
+ invalid_cookbook_lock = valid_cookbook_lock.dup
229
+ invalid_cookbook_lock.delete("cache_key")
230
+ invalid_lockfile["cookbook_locks"] = { "foo" => invalid_cookbook_lock }
231
+ expect { lockfile.build_from_lock_data(invalid_lockfile) }.to raise_error(ChefDK::InvalidLockfile)
232
+ end
233
+
234
+ it "requires that the cache_key be a string or null" do
235
+ invalid_lockfile = valid_lock_data.dup
236
+ invalid_cookbook_lock = valid_cookbook_lock.dup
237
+ invalid_cookbook_lock["cache_key"] = 42
238
+ invalid_lockfile["cookbook_locks"] = { "foo" => invalid_cookbook_lock }
239
+ expect { lockfile.build_from_lock_data(invalid_lockfile) }.to raise_error(ChefDK::InvalidLockfile)
240
+ end
241
+
242
+ it "requires that a cookbook lock have a source_options attribute" do
243
+ invalid_lockfile = valid_lock_data.dup
244
+ invalid_cookbook_lock = valid_cookbook_lock.dup
245
+ invalid_cookbook_lock.delete("source_options")
246
+ invalid_lockfile["cookbook_locks"] = { "foo" => invalid_cookbook_lock }
247
+ expect { lockfile.build_from_lock_data(invalid_lockfile) }.to raise_error(ChefDK::InvalidLockfile)
248
+ end
249
+
250
+ it "requires that source options be a Hash" do
251
+ invalid_lockfile = valid_lock_data.dup
252
+ invalid_cookbook_lock = valid_cookbook_lock.dup
253
+ invalid_cookbook_lock["source_options"] = 42
254
+ invalid_lockfile["cookbook_locks"] = { "foo" => invalid_cookbook_lock }
255
+ expect { lockfile.build_from_lock_data(invalid_lockfile) }.to raise_error(ChefDK::InvalidLockfile)
256
+ end
257
+
258
+ it "requires that a cookbook lock be a valid local cookbook if `cache_key' is null/nil" do
259
+ valid_lock_with_local_cookbook = valid_lock_data.dup
260
+ valid_local_cookbook = valid_cookbook_lock.dup
261
+ valid_local_cookbook["cache_key"] = nil
262
+ valid_local_cookbook["source"] = "path/to/foo"
263
+ valid_lock_with_local_cookbook["cookbook_locks"] = { "foo" => valid_local_cookbook }
264
+ expect { lockfile.build_from_lock_data(valid_lock_with_local_cookbook) }.to_not raise_error
265
+
266
+ invalid_lock_with_local_cookbook = valid_lock_data.dup
267
+ invalid_local_cookbook = valid_cookbook_lock.dup
268
+ invalid_local_cookbook["cache_key"] = nil
269
+ invalid_local_cookbook["source"] = 42
270
+ invalid_lock_with_local_cookbook["cookbook_locks"] = { "foo" => invalid_local_cookbook }
271
+ expect { lockfile.build_from_lock_data(invalid_lock_with_local_cookbook) }.to raise_error(ChefDK::InvalidLockfile)
272
+ end
273
+
274
+ it "requires that a cookbook lock w/ a key named `cache_key' be a valid cached cookbook structure" do
275
+ valid_lock_with_cached_cookbook = valid_lock_data.dup
276
+ valid_cached_cookbook = valid_cookbook_lock.dup
277
+ valid_cached_cookbook["cache_key"] = nil
278
+ valid_cached_cookbook["source"] = "path/to/foo"
279
+ valid_lock_with_cached_cookbook["cookbook_locks"] = { "foo" => valid_cached_cookbook }
280
+ expect { lockfile.build_from_lock_data(valid_lock_with_cached_cookbook) }.to_not raise_error
281
+
282
+ invalid_lock_with_cached_cookbook = valid_lock_data.dup
283
+ invalid_cached_cookbook = valid_cookbook_lock.dup
284
+ invalid_cached_cookbook["cache_key"] = 42
285
+ invalid_lock_with_cached_cookbook["cookbook_locks"] = { "foo" => invalid_cached_cookbook }
286
+ expect { lockfile.build_from_lock_data(invalid_lock_with_cached_cookbook) }.to raise_error(ChefDK::InvalidLockfile)
287
+ end
288
+
289
+ end
290
+ end
291
+
292
+ end
@@ -0,0 +1,170 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2014 Chef Software Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require 'spec_helper'
19
+ require 'chef-dk/policyfile_services/install'
20
+
21
+ describe ChefDK::PolicyfileServices::Install do
22
+
23
+ let(:working_dir) do
24
+ path = File.join(tempdir, "policyfile_services_test_working_dir")
25
+ Dir.mkdir(path)
26
+ path
27
+ end
28
+
29
+ let(:policyfile_rb_explicit_name) { nil }
30
+
31
+ let(:policyfile_rb_name) { policyfile_rb_explicit_name || "Policyfile.rb" }
32
+
33
+ let(:policyfile_lock_name) { "Policyfile.lock.json" }
34
+
35
+ let(:policyfile_rb_path) { File.join(working_dir, policyfile_rb_name) }
36
+
37
+ let(:policyfile_lock_path) { File.join(working_dir, policyfile_lock_name) }
38
+
39
+ let(:local_cookbooks_root) do
40
+ File.join(fixtures_path, "local_path_cookbooks")
41
+ end
42
+
43
+ let(:policyfile_content) do
44
+ <<-E
45
+ name 'install-example'
46
+
47
+ run_list 'local-cookbook'
48
+
49
+ cookbook 'local-cookbook', path: '#{local_cookbooks_root}/local-cookbook'
50
+ E
51
+ end
52
+
53
+ let(:ui) { TestHelpers::TestUI.new }
54
+
55
+ let(:install_service) { described_class.new(policyfile: policyfile_rb_name, ui: ui, root_dir: working_dir) }
56
+
57
+ let(:storage_config) do
58
+ ChefDK::Policyfile::StorageConfig.new( cache_path: nil, relative_paths_root: local_cookbooks_root )
59
+ end
60
+
61
+ def result_policyfile_lock
62
+ expect(File).to exist(policyfile_lock_path)
63
+ content = IO.read(policyfile_lock_path)
64
+ lock_data = FFI_Yajl::Parser.parse(content)
65
+ ChefDK::PolicyfileLock.new(storage_config).build_from_lock_data(lock_data)
66
+ end
67
+
68
+ context "when no Policyfile is present or specified" do
69
+
70
+ it "errors out" do
71
+ expect { install_service.run }.to raise_error(ChefDK::PolicyfileNotFound, "Policyfile not found at path #{policyfile_rb_path}")
72
+ end
73
+
74
+ end
75
+
76
+ context "when a Policyfile exists" do
77
+
78
+ before do
79
+ File.open(policyfile_rb_path, "w+") { |f| f.print(policyfile_content) }
80
+ end
81
+
82
+ it "infers that the Policyfile.rb is located at $CWD/Policyfile.rb" do
83
+ expect(install_service.policyfile_path).to eq(policyfile_rb_path)
84
+ end
85
+
86
+ it "reads the policyfile from disk" do
87
+ expect(install_service.policyfile_content).to eq(policyfile_content)
88
+ end
89
+
90
+ context "and the policyfile has an error" do
91
+
92
+ let(:policyfile_content) { 'raise "borkbork"' }
93
+
94
+ it "errors out and creates no lockfile" do
95
+ expect { install_service.run }.to raise_error(ChefDK::PolicyfileInstallError)
96
+ expect(File).to_not exist(policyfile_lock_path)
97
+ end
98
+
99
+ end
100
+
101
+ context "and no lockfile exists" do
102
+
103
+ it "solves the Policyfile demands, installs cookbooks, emits a lockfile" do
104
+ install_service.run
105
+ generated_lock = result_policyfile_lock
106
+ expect(generated_lock.name).to eq('install-example')
107
+ expect(generated_lock.cookbook_locks).to have_key("local-cookbook")
108
+ end
109
+
110
+ end
111
+
112
+ context "and a lockfile exists" do
113
+
114
+ before do
115
+ install_service.dup.run
116
+ end
117
+
118
+ it "reads the policyfile lock from disk" do
119
+ lock = install_service.policyfile_lock
120
+ expect(lock).to be_an_instance_of(ChefDK::PolicyfileLock)
121
+ expect(lock.name).to eq('install-example')
122
+ expect(lock.cookbook_locks).to have_key("local-cookbook")
123
+ end
124
+
125
+ it "ensures that cookbooks are installed" do
126
+ expect(install_service.policyfile_lock).to receive(:install_cookbooks).and_call_original
127
+ install_service.run
128
+ end
129
+
130
+ describe "when an error occurs during the install" do
131
+
132
+ before do
133
+ expect(install_service.policyfile_lock).to receive(:install_cookbooks).and_raise("some error")
134
+ end
135
+
136
+ it "raises a PolicyfileInstallError" do
137
+ expect { install_service.run }.to raise_error(ChefDK::PolicyfileInstallError)
138
+ end
139
+
140
+
141
+ end
142
+
143
+ context "and the Policyfile has updated dependendencies" do
144
+
145
+ # For very first iteration, we won't tackle this case if it's hard
146
+ it "Conservatively updates deps, recomputes lock, and installs"
147
+
148
+ end
149
+
150
+ end
151
+
152
+ context "and an explicit Policyfile name is given" do
153
+
154
+ let(:policyfile_rb_explicit_name) { "MyPolicy.rb" }
155
+
156
+ let(:policyfile_lock_name) { "MyPolicy.lock.json" }
157
+
158
+ it "infers that the Policyfile.rb is located at $CWD/$POLICYFILE_NAME" do
159
+ expect(install_service.policyfile_path).to eq(policyfile_rb_path)
160
+ end
161
+
162
+ it "reads the policyfile from disk" do
163
+ expect(install_service.policyfile_content).to eq(policyfile_content)
164
+ end
165
+
166
+ end
167
+ end
168
+
169
+ end
170
+