chef-dk 0.2.1 → 0.3.0

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