static 1.0.1 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ module VMCManifests
2
+ VERSION = "0.6.2".freeze
3
+ end
@@ -0,0 +1,176 @@
1
+ require "spec_helper"
2
+
3
+ require "manifests-vmc-plugin/loader"
4
+
5
+
6
+ describe VMCManifests::Normalizer do
7
+ let(:manifest) { {} }
8
+ let(:loader) { VMCManifests::Loader.new(nil, nil) }
9
+
10
+ describe '#normalize!' do
11
+ subject do
12
+ loader.normalize!(manifest)
13
+ manifest
14
+ end
15
+
16
+ context 'with a manifest where the applications have no path set' do
17
+ let(:manifest) { { "applications" => { "." => { "name" => "foo" } } } }
18
+
19
+ it "sets the path to their tag, assuming it's a path" do
20
+ expect(subject).to eq(
21
+ :applications => [{ :name => "foo", :path => "." }])
22
+ end
23
+ end
24
+
25
+ context 'with a manifest where the url is nil' do
26
+ let(:manifest) { { "applications" => { "." => { "url" => nil } } } }
27
+
28
+ it "sets it to none" do
29
+ expect(subject).to eq(
30
+ :applications => [{ :path => ".", :url => "none" }]
31
+ )
32
+ end
33
+ end
34
+
35
+ context 'with a manifest with a subdomain attribute' do
36
+ let(:manifest) { { "applications" => { "." => { "subdomain" => "use-this-for-host" } } } }
37
+
38
+ it "sets the subdomain key to be host" do
39
+ expect(subject).to eq(
40
+ :applications => [{ :path => ".", :host => "use-this-for-host" }]
41
+ )
42
+ end
43
+
44
+ context "when the host attribute is also set" do
45
+ let(:manifest) { { "applications" => { "." => { "subdomain" => "dont-use-this-for-host", "host" => "canonical-attribute" } } } }
46
+
47
+ it 'does not overwrite an explicit host attribute' do
48
+ expect(subject).to eq(
49
+ :applications => [{ :path => ".", :host => "canonical-attribute" }]
50
+ )
51
+ end
52
+ end
53
+ end
54
+
55
+ context 'with a manifest with toplevel attributes' do
56
+ context 'and properties' do
57
+ let(:manifest) {
58
+ { "name" => "foo", "properties" => { "fizz" => "buzz" } }
59
+ }
60
+
61
+ it 'keeps the properties at the toplevel' do
62
+ expect(subject).to eq(
63
+ :applications => [{ :name => "foo", :path => "." }],
64
+ :properties => { :fizz => "buzz" })
65
+ end
66
+ end
67
+
68
+ context 'and no applications' do
69
+ context 'and no path' do
70
+ let(:manifest) { { "name" => "foo" } }
71
+
72
+ it 'adds it as an application with path .' do
73
+ expect(subject).to eq(
74
+ :applications => [{ :name => "foo", :path => "." }])
75
+ end
76
+ end
77
+
78
+ context 'and a path' do
79
+ let(:manifest) { { "name" => "foo", "path" => "./foo" } }
80
+
81
+ it 'adds it as an application with the proper tag and path' do
82
+ expect(subject).to eq(
83
+ :applications => [{ :name => "foo", :path => "./foo" }])
84
+ end
85
+ end
86
+ end
87
+
88
+ context 'and applications' do
89
+ let(:manifest) {
90
+ { "runtime" => "ruby19",
91
+ "applications" => {
92
+ "./foo" => { "name" => "foo" },
93
+ "./bar" => { "name" => "bar" },
94
+ "./baz" => { "name" => "baz", "runtime" => "ruby18" }
95
+ }
96
+ }
97
+ }
98
+
99
+ it "merges the toplevel attributes into the applications" do
100
+ expect(subject[:applications]).to match_array [
101
+ { :name => "foo", :path => "./foo", :runtime => "ruby19" },
102
+ { :name => "bar", :path => "./bar", :runtime => "ruby19" },
103
+ { :name => "baz", :path => "./baz", :runtime => "ruby18" }
104
+ ]
105
+ end
106
+ end
107
+ end
108
+
109
+ context 'with a manifest where applications is a hash' do
110
+ let(:manifest) { { "applications" => { "foo" => { "name" => "foo" } } } }
111
+
112
+ it 'converts the array to a hash, with the path as the key' do
113
+ expect(subject).to eq(
114
+ :applications => [{ :name => "foo", :path => "foo" }])
115
+ end
116
+
117
+ context "and the applications had dependencies" do
118
+ let(:manifest) do
119
+ { "applications" => {
120
+ "bar" => { "name" => "bar", "depends-on" => "foo" },
121
+ "foo" => { "name" => "foo" }
122
+ }
123
+ }
124
+ end
125
+
126
+ it "converts using dependency order" do
127
+ expect(subject).to eq(
128
+ :applications => [{ :name => "foo", :path => "foo" }, { :name => "bar", :path => "bar" }])
129
+ end
130
+
131
+ context "and there's a circular dependency" do
132
+ let(:manifest) do
133
+ { "applications" => {
134
+ "bar" => { "name" => "bar", "depends-on" => "foo" },
135
+ "foo" => { "name" => "foo", "depends-on" => "bar" }
136
+ }
137
+ }
138
+ end
139
+
140
+ it "doesn't blow up" do
141
+ expect(subject).to be_true
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ describe '#normalize_app!' do
149
+ subject do
150
+ loader.send(:normalize_app!, manifest)
151
+ manifest
152
+ end
153
+
154
+ context 'with framework as a hash' do
155
+ let(:manifest) {
156
+ { "name" => "foo",
157
+ "framework" => { "name" => "ruby19", "mem" => "64M" }
158
+ }
159
+ }
160
+
161
+ it 'sets the framework to just the name' do
162
+ expect(subject).to eq(
163
+ "name" => "foo",
164
+ "framework" => "ruby19")
165
+ end
166
+ end
167
+
168
+ context 'with mem instead of memory' do
169
+ let(:manifest) { { "name" => "foo", "mem" => "128M" } }
170
+
171
+ it 'renames mem to memory' do
172
+ expect(subject).to eq("name" => "foo", "memory" => "128M")
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,365 @@
1
+ require "spec_helper"
2
+
3
+ require "manifests-vmc-plugin/plugin"
4
+
5
+
6
+ describe ManifestsPlugin do
7
+ let(:manifest) { {} }
8
+ let(:manifest_file) { nil }
9
+ let(:inputs_hash) { {} }
10
+ let(:given_hash) { {} }
11
+ let(:global_hash) { { :quiet => true } }
12
+ let(:command) { nil }
13
+ let(:inputs) { Mothership::Inputs.new(Mothership.commands[:push], nil, inputs_hash, given_hash, global_hash) }
14
+ let(:plugin) { ManifestsPlugin.new(command, inputs) }
15
+
16
+ let(:client) { fake_client }
17
+
18
+ before do
19
+ stub(plugin).manifest { manifest }
20
+ stub(plugin).manifest_file { manifest_file } if manifest_file
21
+ stub(plugin).client { client }
22
+ end
23
+
24
+ describe "#wrap_with_optional_name" do
25
+ let(:name_made_optional) { true }
26
+ let(:wrapped) { mock! }
27
+
28
+ subject { plugin.send(:wrap_with_optional_name, name_made_optional, wrapped, inputs) }
29
+
30
+ context "when --all is given" do
31
+ let(:inputs_hash) { { :all => true } }
32
+
33
+ it "skips all manifest-related logic, and invokes the command" do
34
+ mock(wrapped).call
35
+ dont_allow(plugin).show_manifest_usage
36
+ subject
37
+ end
38
+ end
39
+
40
+ context "when there is no manifest" do
41
+ let(:manifest) { nil }
42
+
43
+ context "and an app is given" do
44
+ let(:given_hash) { { :app => "foo" } }
45
+
46
+ it "passes through to the command" do
47
+ mock(wrapped).call
48
+ dont_allow(plugin).show_manifest_usage
49
+ subject
50
+ end
51
+ end
52
+
53
+ context "and an app is NOT given" do
54
+ let(:inputs_hash) { {} }
55
+
56
+ context "and we made it optional" do
57
+ it "fails manually" do
58
+ mock(plugin).no_apps
59
+ subject
60
+ end
61
+ end
62
+
63
+ context "and we did NOT make it optional" do
64
+ let(:name_made_optional) { false }
65
+
66
+ it "passes through to the command" do
67
+ mock(wrapped).call
68
+ dont_allow(plugin).show_manifest_usage
69
+ subject
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ context "when there is a manifest" do
76
+ let(:manifest_file) { "/abc/manifest.yml" }
77
+
78
+ before do
79
+ stub(plugin).show_manifest_usage
80
+ end
81
+
82
+ context "when no apps are given" do
83
+ context "and the user's working directory matches a particular app in the manifest" do
84
+ let(:manifest) { { :applications => [{ :name => "foo", :path => "/abc/foo" }] } }
85
+
86
+ it "calls the command for only that app" do
87
+ mock(wrapped).call(anything) do |inputs|
88
+ expect(inputs.given[:app]).to eq "foo"
89
+ end
90
+
91
+ stub(Dir).pwd { "/abc/foo" }
92
+
93
+ subject
94
+ end
95
+ end
96
+
97
+ context "and the user's working directory isn't in the manifest" do
98
+ let(:manifest) { { :applications => [{ :name => "foo" }, { :name => "bar" }] } }
99
+
100
+ it "calls the command for all apps in the manifest" do
101
+ uncalled_apps = ["foo", "bar"]
102
+ mock(wrapped).call(anything).twice do |inputs|
103
+ uncalled_apps.delete inputs.given[:app]
104
+ end
105
+
106
+ subject
107
+
108
+ expect(uncalled_apps).to be_empty
109
+ end
110
+ end
111
+ end
112
+
113
+ context "when any of the given apps are not in the manifest" do
114
+ let(:manifest) { { :applications => [{ :name => "a" }, { :name => "b" }] } }
115
+
116
+ context "and --apps is given" do
117
+ let(:given_hash) { { :apps => ["x", "a"] } }
118
+
119
+ it "passes through to the original command" do
120
+ mock(plugin).show_manifest_usage
121
+
122
+ uncalled_apps = ["a", "x"]
123
+ mock(wrapped).call(anything).twice do |inputs|
124
+ uncalled_apps.delete inputs.given[:app]
125
+ end
126
+
127
+ subject
128
+
129
+ expect(uncalled_apps).to be_empty
130
+ subject
131
+ end
132
+ end
133
+ end
134
+
135
+ context "when none of the given apps are in the manifest" do
136
+ let(:manifest) { { :applications => [{ :name => "a" }, { :name => "b" }] } }
137
+
138
+ context "and --apps is given" do
139
+ let(:given_hash) { { :apps => ["x", "y"] } }
140
+
141
+ it "passes through to the original command" do
142
+ dont_allow(plugin).show_manifest_usage
143
+ mock(wrapped).call
144
+ subject
145
+ end
146
+ end
147
+ end
148
+
149
+ context "when an app name that's in the manifest is given" do
150
+ let(:manifest) { { :applications => [{ :name => "foo" }] } }
151
+ let(:given_hash) { { :app => "foo" } }
152
+
153
+ it "calls the command with that app" do
154
+ mock(wrapped).call(anything) do |inputs|
155
+ expect(inputs.given[:app]).to eq "foo"
156
+ end
157
+
158
+ subject
159
+ end
160
+ end
161
+
162
+ context "when a path to an app that's in the manifest is given" do
163
+ let(:manifest) { { :applications => [{ :name => "foo", :path => "/abc/foo" }] } }
164
+ let(:given_hash) { { :app => "/abc/foo" } }
165
+
166
+ it "calls the command with that app" do
167
+ mock(wrapped).call(anything) do |inputs|
168
+ expect(inputs.given[:app]).to eq "foo"
169
+ end
170
+
171
+ subject
172
+ end
173
+ end
174
+ end
175
+ end
176
+
177
+ describe "#wrap_push" do
178
+ let(:wrapped) { mock! }
179
+ let(:command) { Mothership.commands[:push] }
180
+
181
+ subject { plugin.send(:wrap_push, wrapped, inputs) }
182
+
183
+ before do
184
+ stub(plugin).show_manifest_usage
185
+ end
186
+
187
+ context "with a manifest" do
188
+ let(:manifest_file) { "/abc/manifest.yml" }
189
+
190
+ let(:manifest) do
191
+ { :applications => [
192
+ { :name => "a",
193
+ :path => "/abc/a",
194
+ :instances => "200",
195
+ :memory => "128M"
196
+ }
197
+ ]
198
+ }
199
+ end
200
+
201
+ # vmc push foo
202
+ context "and a name is given" do
203
+ context "and the name is present in the manifest" do
204
+ let(:given_hash) { { :name => "a" } }
205
+
206
+ context "and the app exists" do
207
+ let(:app) { fake :app, :name => "a" }
208
+ let(:client) { fake_client :apps => [app] }
209
+
210
+ context "and --reset was given" do
211
+ let(:inputs_hash) { { :reset => true } }
212
+ let(:given_hash) { { :name => "a", :instances => "100" } }
213
+
214
+ it "rebases their inputs on the manifest's values" do
215
+ mock(wrapped).call(anything) do |inputs|
216
+ expect(inputs.given).to eq(
217
+ :name => "a", :path => "/abc/a", :instances => "100", :memory => "128M")
218
+ end
219
+
220
+ subject
221
+ end
222
+ end
223
+ end
224
+
225
+ context "and the app does NOT exist" do
226
+ it "pushes a new app with the inputs from the manifest" do
227
+ mock(wrapped).call(anything) do |inputs|
228
+ expect(inputs.given).to eq(
229
+ :name => "a", :path => "/abc/a", :instances => "200", :memory => "128M")
230
+ end
231
+
232
+ subject
233
+ end
234
+ end
235
+ end
236
+
237
+ context "and the name is NOT present in the manifest" do
238
+ let(:given_hash) { { :name => "x" } }
239
+
240
+ it "fails, saying that name was not found in the manifest" do
241
+ expect { subject }.to raise_error(VMC::UserError, /Could not find .+ in the manifest./)
242
+ end
243
+ end
244
+ end
245
+
246
+ # vmc push ./abc
247
+ context "and a path is given" do
248
+ context "and there are apps matching that path in the manifest" do
249
+ let(:manifest) do
250
+ { :applications => [
251
+ { :name => "a",
252
+ :path => "/abc/a",
253
+ :instances => "200",
254
+ :memory => "128M"
255
+ },
256
+ { :name => "b",
257
+ :path => "/abc/a",
258
+ :instances => "200",
259
+ :memory => "128M"
260
+ }
261
+ ]
262
+ }
263
+ end
264
+
265
+ let(:given_hash) { { :name => "/abc/a" } }
266
+
267
+ it "pushes the found apps" do
268
+ pushed_apps = []
269
+ mock(wrapped).call(anything).twice do |inputs|
270
+ pushed_apps << inputs[:name]
271
+ end
272
+
273
+ subject
274
+
275
+ expect(pushed_apps).to eq(["a", "b"])
276
+ end
277
+ end
278
+
279
+ context "and there are NOT apps matching that path in the manifest" do
280
+ let(:given_hash) { { :name => "/abc/x" } }
281
+
282
+ it "fails, saying that the path was not found in the manifest" do
283
+ expect { subject }.to raise_error(VMC::UserError, /Path .+ is not present in manifest/)
284
+ end
285
+ end
286
+ end
287
+ end
288
+
289
+ context "without a manifest" do
290
+ let(:app) { mock! }
291
+ let(:manifest) { nil }
292
+
293
+ it "asks to save the manifest when uploading the application" do
294
+ mock_ask("Save configuration?", :default => false)
295
+ stub(wrapped).call { plugin.filter(:push_app, app) }
296
+ subject
297
+ end
298
+ end
299
+ end
300
+
301
+ describe "#push_input_for" do
302
+ context "with an existing app" do
303
+ before do
304
+ stub(plugin).from_manifest { "PATH" }
305
+ app.changes.clear
306
+ end
307
+
308
+ let(:client) { fake_client(:apps => [app]) }
309
+ let(:manifest_memory) { "256M" }
310
+ let(:app) { fake :app, :name => "a", :memory => 256 }
311
+ let(:manifest) { { :name => "a", :memory => manifest_memory } }
312
+
313
+ subject { plugin.send(:push_input_for, manifest, inputs) }
314
+
315
+ context "with --reset" do
316
+ let(:inputs_hash) { { :reset => true } }
317
+
318
+ context "with changes" do
319
+ let(:manifest_memory) { "128M" }
320
+
321
+ it "applies the changes" do
322
+ subject[:memory].should == "128M"
323
+ end
324
+
325
+ it "does not ask to set --reset" do
326
+ dont_allow(plugin).warn_reset_changes
327
+ subject
328
+ end
329
+ end
330
+
331
+ context "without changes" do
332
+ it "does not ask to set --reset" do
333
+ dont_allow(plugin).warn_reset_changes
334
+ subject
335
+ end
336
+ end
337
+ end
338
+
339
+ context "without --reset" do
340
+ let(:inputs_hash) { {} }
341
+
342
+ context "with changes" do
343
+ let(:manifest_memory) { "128M" }
344
+
345
+ it "asks user to provide --reset" do
346
+ mock(plugin).warn_reset_changes
347
+ subject
348
+ end
349
+
350
+ it "does not apply changes" do
351
+ stub(plugin).warn_reset_changes
352
+ subject[:memory].should == nil
353
+ end
354
+ end
355
+
356
+ context "without changes" do
357
+ it "does not ask to set --reset" do
358
+ dont_allow(plugin).warn_reset_changes
359
+ subject
360
+ end
361
+ end
362
+ end
363
+ end
364
+ end
365
+ end