manifests-cf-plugin 0.7.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,365 @@
1
+ require "spec_helper"
2
+
3
+ require "manifests-cf-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
+ # cf 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(CF::UserError, /Could not find .+ in the manifest./)
242
+ end
243
+ end
244
+ end
245
+
246
+ # cf 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(CF::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
@@ -0,0 +1,321 @@
1
+ require 'spec_helper'
2
+ require 'manifests-cf-plugin'
3
+
4
+ describe CFManifests do
5
+ let(:inputs_hash) { {} }
6
+ let(:given_hash) { {} }
7
+ let(:global_hash) { {} }
8
+ let(:inputs) { Mothership::Inputs.new(nil, nil, inputs_hash, given_hash, global_hash) }
9
+
10
+ let(:cmd) do
11
+ manifest = CF::App::Push.new(nil, inputs)
12
+ manifest.extend CFManifests
13
+ stub(manifest).client { client }
14
+ manifest
15
+ end
16
+
17
+ let(:target_base) { "some-cloud.com" }
18
+
19
+ let(:foo) { fake(:app, :name => "foo") }
20
+ let(:bar) { fake(:app, :name => "bar") }
21
+ let(:baz) { fake(:app, :name => "baz") }
22
+ let(:xxx) { fake(:app, :name => "xxx") }
23
+ let(:yyy) { fake(:app, :name => "yyy") }
24
+
25
+ let(:client) do
26
+ fake_client :apps => [foo, bar, baz, xxx, yyy]
27
+ end
28
+
29
+ let(:manifest_file) { "/abc/manifest.yml" }
30
+
31
+ before do
32
+ stub(cmd).target_base { target_base }
33
+ stub(cmd).v2? { true }
34
+
35
+ stub(cmd).manifest { manifest }
36
+ stub(cmd).manifest_file { manifest_file }
37
+ end
38
+
39
+ describe '#find_apps' do
40
+ subject { cmd.find_apps(nil) }
41
+
42
+ context 'when there is no manifest file' do
43
+ before { stub(cmd).manifest { nil } }
44
+ it { should eq [] }
45
+ end
46
+ end
47
+
48
+ describe '#create_manifest_for' do
49
+ let(:app) {
50
+ fake :app,
51
+ :framework => fake(:framework),
52
+ :runtime => fake(:runtime),
53
+ :memory => 2048,
54
+ :total_instances => 2,
55
+ :command => "ruby main.rb",
56
+ :buildpack => "git://example.com/foo.git",
57
+ :routes => [
58
+ fake(:route,
59
+ :host => "some-app-name",
60
+ :domain => fake(:domain, :name => target_base))
61
+ ],
62
+ :service_bindings => [
63
+ fake(
64
+ :service_binding,
65
+ :service_instance =>
66
+ fake(
67
+ :service_instance,
68
+ :name => "service-1",
69
+ :service_plan =>
70
+ fake(
71
+ :service_plan,
72
+ :name => "P200",
73
+ :service => fake(:service))))
74
+ ]
75
+ }
76
+
77
+ subject { cmd.create_manifest_for(app, "some-path") }
78
+
79
+ its(["name"]) { should eq app.name }
80
+ its(["framework"]) { should eq app.framework.name }
81
+ its(["runtime"]) { should eq app.runtime.name }
82
+ its(["memory"]) { should eq "2G" }
83
+ its(["instances"]) { should eq 2 }
84
+ its(["path"]) { should eq "some-path" }
85
+ its(["url"]) { should eq "some-app-name.${target-base}" }
86
+ its(["command"]) { should eq "ruby main.rb" }
87
+ its(["buildpack"]) { should eq "git://example.com/foo.git" }
88
+
89
+ it "contains the service information" do
90
+ expect(subject["services"]).to be_a Hash
91
+
92
+ services = subject["services"]
93
+ app.service_bindings.each do |b|
94
+ service = b.service_instance
95
+
96
+ expect(services).to include service.name
97
+
98
+ info = services[service.name]
99
+
100
+ plan = service.service_plan
101
+ offering = plan.service
102
+
103
+ { "plan" => plan.name,
104
+ "label" => offering.label,
105
+ "provider" => offering.provider,
106
+ "version" => offering.version
107
+ }.each do |attr, val|
108
+ expect(info).to include attr
109
+ expect(info[attr]).to eq val
110
+ end
111
+ end
112
+ end
113
+
114
+ context 'when there is no url' do
115
+ let(:app) {
116
+ fake :app,
117
+ :framework => fake(:framework),
118
+ :runtime => fake(:runtime),
119
+ :memory => 2048,
120
+ :total_instances => 2
121
+ }
122
+
123
+ its(["url"]) { should eq "none" }
124
+ end
125
+
126
+ context 'when there is no command' do
127
+ let(:app) {
128
+ fake :app,
129
+ :framework => fake(:framework),
130
+ :runtime => fake(:runtime),
131
+ :memory => 2048,
132
+ :total_instances => 2
133
+ }
134
+
135
+ it { should_not include "command" }
136
+ end
137
+
138
+ context 'when there are no service bindings' do
139
+ let(:app) {
140
+ fake :app,
141
+ :framework => fake(:framework),
142
+ :runtime => fake(:runtime),
143
+ :memory => 2048,
144
+ :total_instances => 2
145
+ }
146
+
147
+ it { should_not include "services" }
148
+ end
149
+ end
150
+
151
+ describe "#setup_services" do
152
+ let(:service_bindings) { [] }
153
+ let(:app) { fake :app, :service_bindings => service_bindings }
154
+
155
+ before do
156
+ dont_allow_ask(anything, anything)
157
+ end
158
+
159
+ context "when services are defined in the manifest" do
160
+ let(:info) {
161
+ { :services => { "service-1" => { :label => "mysql", :plan => "100" } } }
162
+ }
163
+
164
+ let(:service_1) { fake(:service_instance, :name => "service-1") }
165
+
166
+ let(:plan_100) { fake :service_plan, :name => "100" }
167
+
168
+ let(:mysql) {
169
+ fake(
170
+ :service,
171
+ :label => "mysql",
172
+ :provider => "core",
173
+ :service_plans => [plan_100])
174
+ }
175
+
176
+ let(:service_instances) { [] }
177
+
178
+ let(:client) {
179
+ fake_client :services => [mysql], :service_instances => service_instances
180
+ }
181
+
182
+ context "and the services exist" do
183
+ let(:service_instances) { [service_1] }
184
+
185
+ context "and are already bound" do
186
+ let(:service_bindings) { [fake(:service_binding, :service_instance => service_1)] }
187
+
188
+ it "does neither create nor bind the service again" do
189
+ dont_allow(cmd).invoke :create_service, anything
190
+ dont_allow(cmd).invoke :bind_service, anything
191
+ cmd.send(:setup_services, app, info)
192
+ end
193
+ end
194
+
195
+ context "but are not bound" do
196
+ it "does not create the services" do
197
+ dont_allow(cmd).invoke :create_service, anything
198
+ stub(cmd).invoke :bind_service, anything
199
+ cmd.send(:setup_services, app, info)
200
+ end
201
+
202
+ it "binds the service" do
203
+ mock(cmd).invoke :bind_service, :app => app, :service => service_1
204
+ cmd.send(:setup_services, app, info)
205
+ end
206
+ end
207
+ end
208
+
209
+ context "and the services do not exist" do
210
+ it "creates the services" do
211
+ mock(cmd).invoke :create_service, :app => app,
212
+ :name => service_1.name, :offering => mysql, :plan => plan_100
213
+ dont_allow(cmd).invoke :bind_service, anything
214
+ cmd.send(:setup_services, app, info)
215
+ end
216
+ end
217
+ end
218
+
219
+ context "when there are no services defined" do
220
+ let(:info) { {} }
221
+
222
+ it "does not ask anything" do
223
+ cmd.send(:setup_services, app, info)
224
+ end
225
+ end
226
+ end
227
+
228
+ describe "#apps_in_manifest" do
229
+ let(:foo_hash) { { :name => "foo", :path => "/abc/foo" } }
230
+ let(:bar_hash) { { :name => "bar", :path => "/abc/bar" } }
231
+ let(:baz_hash) { { :name => "baz", :path => "/abc/baz" } }
232
+
233
+ let(:manifest) { { :applications => [foo_hash, bar_hash, baz_hash] } }
234
+
235
+ subject { cmd.apps_in_manifest(inputs) }
236
+
237
+ context "when no apps are passed" do
238
+ let(:given_hash) { {} }
239
+
240
+ its(:first) { should eq [] }
241
+ its(:last) { should eq [] }
242
+ end
243
+
244
+ context "when app names are passed" do
245
+ context "and all of them are in the manifest" do
246
+ let(:given_hash) { { :apps => ["foo", "bar"] } }
247
+
248
+ its(:first) { should eq [foo_hash, bar_hash] }
249
+ its(:last) { should eq [] }
250
+ end
251
+
252
+ context "and one of them is in the manifest" do
253
+ let(:given_hash) { { :apps => ["foo", "xxx"] } }
254
+
255
+ its(:first) { should eq [foo_hash] }
256
+ its(:last) { should eq ["xxx"] }
257
+ end
258
+
259
+ context "and none of them are in the manifest" do
260
+ let(:given_hash) { { :apps => ["xxx", "yyy"] } }
261
+
262
+ its(:first) { should eq [] }
263
+ its(:last) { should eq ["xxx", "yyy"] }
264
+ end
265
+ end
266
+
267
+ context "when apps are passed as paths" do
268
+ context "and the paths are in the manifest" do
269
+ let(:given_hash) { { :apps => ["/abc/foo"] } }
270
+
271
+ its(:first) { should eq [foo_hash] }
272
+ its(:last) { should eq [] }
273
+ end
274
+
275
+ context "and any path is not in the manifest" do
276
+ let(:given_hash) { { :apps => ["/abc/xxx"] } }
277
+
278
+ it "fails with a manifest-specific method (i.e. path not in manifest)" do
279
+ expect { subject }.to raise_error(CF::UserError, /Path .+ is not present in manifest/)
280
+ end
281
+ end
282
+ end
283
+ end
284
+
285
+ describe "#all_apps" do
286
+ let(:applications) do
287
+ [
288
+ {:name => "foo", :path => "/abc"},
289
+ {:name => "bar", :path => "/abc"},
290
+ {:name => "baz", :path => "/abc/baz"}
291
+ ]
292
+ end
293
+
294
+ let(:manifest) do
295
+ { :applications => applications }
296
+ end
297
+
298
+ subject { cmd.all_apps }
299
+
300
+ it "returns all of the apps described in the manifest, as hashes" do
301
+ expect(subject).to eq applications
302
+ end
303
+ end
304
+
305
+ describe "#current_apps" do
306
+ let(:manifest) do
307
+ {:applications => [
308
+ {:name => "foo", :path => "/abc"},
309
+ {:name => "bar", :path => "/abc"},
310
+ {:name => "baz", :path => "/abc/baz"}
311
+ ]}
312
+ end
313
+
314
+ subject { cmd.current_apps }
315
+
316
+ it "returns the applications with the cwd as their path" do
317
+ stub(Dir).pwd { "/abc" }
318
+ expect(subject).to eq [{ :name => "foo", :path => "/abc"}, { :name => "bar", :path => "/abc" }]
319
+ end
320
+ end
321
+ end