engineyard 1.4.29 → 1.7.0.pre2

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 (68) hide show
  1. data/README.rdoc +139 -4
  2. data/bin/ey +1 -7
  3. data/lib/engineyard.rb +1 -22
  4. data/lib/engineyard/cli.rb +192 -94
  5. data/lib/engineyard/cli/#recipes.rb# +32 -0
  6. data/lib/engineyard/cli/api.rb +42 -28
  7. data/lib/engineyard/cli/recipes.rb +13 -6
  8. data/lib/engineyard/cli/ui.rb +103 -42
  9. data/lib/engineyard/cli/web.rb +16 -10
  10. data/lib/engineyard/config.rb +92 -18
  11. data/lib/engineyard/deploy_config.rb +66 -0
  12. data/lib/engineyard/deploy_config/migrate.rb +125 -0
  13. data/lib/engineyard/deploy_config/ref.rb +56 -0
  14. data/lib/engineyard/error.rb +38 -78
  15. data/lib/engineyard/repo.rb +75 -27
  16. data/lib/engineyard/serverside_runner.rb +133 -0
  17. data/lib/engineyard/thor.rb +110 -18
  18. data/lib/engineyard/version.rb +1 -1
  19. data/spec/engineyard/cli/api_spec.rb +10 -16
  20. data/spec/engineyard/cli_spec.rb +0 -11
  21. data/spec/engineyard/config_spec.rb +1 -8
  22. data/spec/engineyard/deploy_config_spec.rb +203 -0
  23. data/spec/engineyard/eyrc_spec.rb +2 -0
  24. data/spec/engineyard/repo_spec.rb +57 -34
  25. data/spec/ey/deploy_spec.rb +102 -52
  26. data/spec/ey/list_environments_spec.rb +69 -14
  27. data/spec/ey/login_spec.rb +11 -7
  28. data/spec/ey/logout_spec.rb +4 -4
  29. data/spec/ey/logs_spec.rb +6 -6
  30. data/spec/ey/recipes/apply_spec.rb +1 -1
  31. data/spec/ey/recipes/download_spec.rb +1 -1
  32. data/spec/ey/recipes/upload_spec.rb +6 -6
  33. data/spec/ey/rollback_spec.rb +3 -3
  34. data/spec/ey/ssh_spec.rb +9 -9
  35. data/spec/ey/status_spec.rb +2 -2
  36. data/spec/ey/whoami_spec.rb +9 -8
  37. data/spec/spec_helper.rb +18 -15
  38. data/spec/support/{fake_awsm.rb → git_repos.rb} +0 -14
  39. data/spec/support/helpers.rb +84 -28
  40. data/spec/support/matchers.rb +0 -16
  41. data/spec/support/shared_behavior.rb +83 -103
  42. metadata +65 -51
  43. data/lib/engineyard/api.rb +0 -117
  44. data/lib/engineyard/collection.rb +0 -7
  45. data/lib/engineyard/collection/abstract.rb +0 -71
  46. data/lib/engineyard/collection/apps.rb +0 -8
  47. data/lib/engineyard/collection/environments.rb +0 -8
  48. data/lib/engineyard/model.rb +0 -12
  49. data/lib/engineyard/model/account.rb +0 -8
  50. data/lib/engineyard/model/api_struct.rb +0 -33
  51. data/lib/engineyard/model/app.rb +0 -32
  52. data/lib/engineyard/model/deployment.rb +0 -90
  53. data/lib/engineyard/model/environment.rb +0 -194
  54. data/lib/engineyard/model/instance.rb +0 -166
  55. data/lib/engineyard/model/log.rb +0 -9
  56. data/lib/engineyard/model/user.rb +0 -6
  57. data/lib/engineyard/resolver.rb +0 -134
  58. data/lib/engineyard/rest_client_ext.rb +0 -9
  59. data/lib/engineyard/ruby_ext.rb +0 -9
  60. data/spec/engineyard/api_spec.rb +0 -39
  61. data/spec/engineyard/collection/apps_spec.rb +0 -16
  62. data/spec/engineyard/collection/environments_spec.rb +0 -16
  63. data/spec/engineyard/model/api_struct_spec.rb +0 -41
  64. data/spec/engineyard/model/environment_spec.rb +0 -198
  65. data/spec/engineyard/model/instance_spec.rb +0 -27
  66. data/spec/engineyard/resolver_spec.rb +0 -112
  67. data/spec/support/fake_awsm.ru +0 -245
  68. data/spec/support/scenarios.rb +0 -417
@@ -1,10 +1,5 @@
1
1
  module EY
2
2
  class << self
3
- def fake_awsm
4
- @fake_awsm ||= load_fake_awsm
5
- end
6
- alias_method :start_fake_awsm, :fake_awsm
7
-
8
3
  def define_git_repo(name, &setup)
9
4
  git_repo_setup[name] ||= setup
10
5
  end
@@ -31,15 +26,6 @@ module EY
31
26
 
32
27
  protected
33
28
 
34
- def load_fake_awsm
35
- config_ru = File.join(EY_ROOT, "spec/support/fake_awsm.ru")
36
- unless system("ruby -c '#{config_ru}' > /dev/null")
37
- raise SyntaxError, "There is a syntax error in fake_awsm.ru! fix it!"
38
- end
39
- @server = RealWeb.start_server_in_fork(config_ru)
40
- "http://localhost:#{@server.port}"
41
- end
42
-
43
29
  def git_repo_setup
44
30
  @git_repo_setup ||= {}
45
31
  end
@@ -24,19 +24,25 @@ module SpecHelpers
24
24
 
25
25
  module IntegrationHelpers
26
26
  def run_ey(command_options, ey_options={})
27
+
27
28
  if respond_to?(:extra_ey_options) # needed for ssh tests
28
29
  ey_options.merge!(extra_ey_options)
30
+ return ey(command_to_run(command_options), ey_options)
29
31
  end
30
32
 
31
- ey(command_to_run(command_options), ey_options)
33
+ if ey_options[:expect_failure]
34
+ fast_failing_ey(command_to_run(command_options))
35
+ else
36
+ fast_ey(command_to_run(command_options))
37
+ end
32
38
  end
33
39
 
34
- def make_scenario(hash)
40
+ def make_scenario(opts)
35
41
  # since nil will silently turn to empty string when interpolated,
36
42
  # and there's a lot of string matching involved in integration
37
43
  # testing, it would be nice to have early notification of typos.
38
44
  scenario = Hash.new { |h,k| raise "Tried to get key #{k.inspect}, but it's missing!" }
39
- scenario.merge!(hash)
45
+ scenario.merge!(opts)
40
46
  end
41
47
  end
42
48
 
@@ -69,21 +75,37 @@ module SpecHelpers
69
75
  ZeroExitStatus = Class.new(UnexpectedExit)
70
76
 
71
77
  def ey_api
72
- @api ||= EY::API.new('asdf')
78
+ @api ||= EY::CloudClient.new('asdf', EY::CLI::UI.new)
79
+ end
80
+
81
+ def ensure_eyrc
82
+ begin
83
+ unless (data = read_eyrc) and data['api_token']
84
+ raise ".eyrc has no token, specs will stall waiting for stdin authentication input"
85
+ end
86
+ rescue Errno::ENOENT => e
87
+ raise ".eyrc must be written before calling run_ey or specs will stall waiting for stdin authentication input"
88
+ end
73
89
  end
74
90
 
75
- def fast_ey(args)
76
- err, out = StringIO.new, StringIO.new
77
- capture_stderr_into(err) do
78
- capture_stdout_into(out) do
79
- with_env('DEBUG' => 'true') do
80
- EY::CLI.start(args)
91
+ def fast_ey(args, options = {})
92
+
93
+ ensure_eyrc
94
+
95
+ begin
96
+ err, out = StringIO.new, StringIO.new
97
+ debug = options[:debug] == false ? nil : 'true'
98
+ capture_stderr_into(err) do
99
+ capture_stdout_into(out) do
100
+ with_env('DEBUG' => debug) do
101
+ EY::CLI.start(args)
102
+ end
81
103
  end
82
104
  end
105
+ ensure
106
+ @err, @out = err.string, out.string
107
+ @raw_ssh_commands, @ssh_commands = extract_ssh_commands(@out)
83
108
  end
84
- ensure
85
- @err, @out = err.string, out.string
86
- @raw_ssh_commands, @ssh_commands = extract_ssh_commands(@out)
87
109
  end
88
110
 
89
111
  def fast_failing_ey(*args)
@@ -94,17 +116,8 @@ module SpecHelpers
94
116
  # SystemExit typically indicates a bogus command, which we
95
117
  # here in expected-to-fail land are entirely happy with.
96
118
  nil
97
- rescue EY::Error => e
98
- more_err, more_out = StringIO.new, StringIO.new
99
-
100
- capture_stderr_into(more_err) do
101
- capture_stdout_into(more_out) do
102
- EY.ui.print_exception(e)
103
- end
104
- end
105
-
106
- @err << more_err.string
107
- @out << more_out.string
119
+ rescue EY::Error, EY::CloudClient::Error => e
120
+ nil
108
121
  end
109
122
  end
110
123
 
@@ -123,7 +136,12 @@ module SpecHelpers
123
136
  end
124
137
 
125
138
  def ey(args = [], options = {}, &block)
139
+ if respond_to?(:extra_ey_options) # needed for ssh tests
140
+ options.merge!(extra_ey_options)
141
+ end
142
+
126
143
  hide_err = options.has_key?(:hide_err) ? options[:hide_err] : options[:expect_failure]
144
+
127
145
  path_prepends = options[:prepend_to_path]
128
146
 
129
147
  ey_env = {
@@ -154,6 +172,7 @@ module SpecHelpers
154
172
  with_env(ey_env) do
155
173
  exit_status = Open4::open4("#{eybin} #{Escape.shell_command(args)}") do |pid, stdin, stdout, stderr|
156
174
  block.call(stdin) if block
175
+ stdin.close
157
176
  @out = stdout.read
158
177
  @err = stderr.read
159
178
  end
@@ -172,7 +191,7 @@ module SpecHelpers
172
191
  end
173
192
 
174
193
  def extract_ssh_commands(output)
175
- raw_ssh_commands = @out.split(/\n/).find_all do |line|
194
+ raw_ssh_commands = [@out,@err].join("\n").split(/\n/).find_all do |line|
176
195
  line =~ /^bash -lc/ || line =~ /^ssh/
177
196
  end
178
197
 
@@ -198,9 +217,46 @@ module SpecHelpers
198
217
  [raw_ssh_commands, ssh_commands]
199
218
  end
200
219
 
201
- def api_scenario(scenario, remote = "user@git.host:path/to/repo.git")
202
- response = ::RestClient.put(EY.fake_awsm + '/scenario', {"scenario" => scenario, "remote" => remote}, {})
203
- raise "Setting scenario failed: #{response.inspect}" unless response.code == 200
220
+ DEPRECATED_SCENARIOS = {
221
+ "empty" => "User Name",
222
+ "one app without environment" => "App Without Env",
223
+ "one app, one environment, not linked" => "Unlinked App",
224
+ "two apps" => "Two Apps",
225
+ "one app, one environment" => "Linked App",
226
+ "two accounts, two apps, two environments, ambiguous" => "Multiple Ambiguous Accounts",
227
+ "one app, one environment, no instances" => "Linked App Not Running",
228
+ "one app, one environment, app master red" => "Linked App Red Master",
229
+ "one app, many environments" => "One App Many Envs",
230
+ "one app, many similarly-named environments" => "One App Similarly Named Envs",
231
+ "two apps, same git uri" => "Two Apps Same Git URI",
232
+ }
233
+
234
+ def api_scenario(old_name)
235
+ clean_eyrc # switching scenarios, always clean up
236
+ name = DEPRECATED_SCENARIOS[old_name]
237
+ @scenario = EY::CloudClient::Test::Scenario[name]
238
+ @scenario_email = @scenario.email
239
+ @scenario_password = @scenario.password
240
+ @scenario_api_token = @scenario.api_token
241
+ @scenario
242
+ end
243
+
244
+ def login_scenario(scenario_name)
245
+ scen = api_scenario(scenario_name)
246
+ write_eyrc('api_token' => scenario_api_token)
247
+ scen
248
+ end
249
+
250
+ def scenario_email
251
+ @scenario_email
252
+ end
253
+
254
+ def scenario_password
255
+ @scenario_password
256
+ end
257
+
258
+ def scenario_api_token
259
+ @scenario_api_token
204
260
  end
205
261
 
206
262
  def read_yaml(file)
@@ -26,19 +26,3 @@ RSpec::Matchers.define :have_app_code do
26
26
  "Expected #has_app_code? to be false on instance: #{instance.inspect}"
27
27
  end
28
28
  end
29
-
30
- RSpec::Matchers.define :resolve_to do |expected|
31
- match do |pair|
32
- app, env = *pair
33
- app.name == expected[:app_name] && env.name == expected[:environment_name]
34
- end
35
-
36
- failure_message_for_should do |pair|
37
- app, env = *pair
38
- "Expected: #{expected[:app_name]}, #{expected[:environment_name]}; Got: #{app.name}, #{env.name}"
39
- end
40
-
41
- failure_message_for_should_not do |pair|
42
- "Expected to not match: #{expected[:app_name]}, #{expected[:environment_name]}"
43
- end
44
- end
@@ -9,7 +9,7 @@ shared_examples_for "it has an ambiguous git repo" do
9
9
  use_git_repo('dup test')
10
10
 
11
11
  before(:all) do
12
- api_scenario "two apps, same git uri"
12
+ login_scenario "two apps, same git uri"
13
13
  end
14
14
  end
15
15
 
@@ -18,10 +18,9 @@ shared_examples_for "it requires an unambiguous git repo" do
18
18
 
19
19
  it "lists disambiguating environments to choose from" do
20
20
  run_ey({}, {:expect_failure => true})
21
- @err.should =~ /ambiguous/
22
- @err.should =~ /specify one of the following environments/
23
- @err.should =~ /giblets \(main\)/
24
- @err.should =~ /keycollector_production \(main\)/
21
+ @err.should include('Multiple environments possible, please be more specific')
22
+ @err.should =~ /giblets/
23
+ @err.should =~ /keycollector_production/
25
24
  end
26
25
  end
27
26
 
@@ -29,16 +28,28 @@ shared_examples_for "it takes an environment name and an app name and an account
29
28
  include_examples "it takes an app name"
30
29
  include_examples "it takes an environment name"
31
30
 
31
+ it "complains when you send --account without a value" do
32
+ login_scenario "empty"
33
+ fast_failing_ey command_to_run({}) << '--account'
34
+ @err.should include("No value provided for option '--account'")
35
+ fast_failing_ey command_to_run({}) << '-c'
36
+ @err.should include("No value provided for option '--account'")
37
+ end
38
+
32
39
  context "when multiple accounts with collaboration" do
33
40
  before :all do
34
- api_scenario "two accounts, two apps, two environments, ambiguous"
41
+ login_scenario "two accounts, two apps, two environments, ambiguous"
35
42
  end
36
43
 
37
44
  it "fails when the app and environment are ambiguous across accounts" do
38
- run_ey({:environment => "giblets", :app => "rails232app", :ref => 'master'}, {:expect_failure => true})
39
- @err.should match(/Multiple app deployments possible/i)
40
- @err.should match(/ey \S+ --environment='giblets' --app='rails232app' --account='account_2'/i)
41
- @err.should match(/ey \S+ --environment='giblets' --app='rails232app' --account='main'/i)
45
+ run_ey({:environment => "giblets", :app => "rails232app", :ref => 'master'}, {:expect_failure => !@succeeds_on_multiple_matches})
46
+ if @succeeds_on_multiple_matches
47
+ @err.should_not match(/multiple/i)
48
+ else
49
+ @err.should match(/Multiple application environments possible/i)
50
+ @err.should match(/ey \S+ --account='account_2' --app='rails232app' --environment='giblets'/i)
51
+ @err.should match(/ey \S+ --account='main' --app='rails232app' --environment='giblets'/i)
52
+ end
42
53
  end
43
54
 
44
55
  it "runs when specifying the account disambiguates the app to deploy" do
@@ -56,9 +67,17 @@ end
56
67
  shared_examples_for "it takes an environment name and an account name" do
57
68
  include_examples "it takes an environment name"
58
69
 
70
+ it "complains when you send --account without a value" do
71
+ login_scenario "empty"
72
+ fast_failing_ey command_to_run({}) << '--account'
73
+ @err.should include("No value provided for option '--account'")
74
+ fast_failing_ey command_to_run({}) << '-c'
75
+ @err.should include("No value provided for option '--account'")
76
+ end
77
+
59
78
  context "when multiple accounts with collaboration" do
60
79
  before :all do
61
- api_scenario "two accounts, two apps, two environments, ambiguous"
80
+ login_scenario "two accounts, two apps, two environments, ambiguous"
62
81
  end
63
82
 
64
83
  it "fails when the app and environment are ambiguous across accounts" do
@@ -86,9 +105,8 @@ shared_examples_for "it takes an environment name and an account name" do
86
105
  end
87
106
 
88
107
  it "returns the error message to the user" do
89
- lambda do
90
- fast_ey(command_to_run({:environment => "giblets", :account => "main"}))
91
- end.should raise_error(EY::Error, /400.*Important infos/)
108
+ fast_failing_ey(command_to_run({:environment => "giblets", :account => "main"}))
109
+ @err.should match(/400.*Important infos/)
92
110
  end
93
111
  end
94
112
 
@@ -97,8 +115,8 @@ end
97
115
 
98
116
  shared_examples_for "it takes an environment name" do
99
117
  it "operates on the current environment by default" do
100
- api_scenario "one app, one environment"
101
- run_ey({:environment => nil}, {:debug => true})
118
+ login_scenario "one app, one environment"
119
+ run_ey(:environment => nil)
102
120
  verify_ran(make_scenario({
103
121
  :environment => 'giblets',
104
122
  :application => 'rails232app',
@@ -108,9 +126,18 @@ shared_examples_for "it takes an environment name" do
108
126
  end
109
127
 
110
128
  it "complains when you specify a nonexistent environment" do
111
- api_scenario "one app, one environment"
112
- run_ey({:environment => 'typo-happens-here'}, {:expect_failure => true})
113
- @err.should match(/no environment named 'typo-happens-here'/i)
129
+ login_scenario "one app, one environment"
130
+ # This test must shell out (not sure why, plz FIXME)
131
+ ey command_to_run(:environment => 'typo-happens-here'), {:expect_failure => true}
132
+ @err.should match(/No environment found matching .*typo-happens-here/i)
133
+ end
134
+
135
+ it "complains when you send --environment without a value" do
136
+ login_scenario "empty"
137
+ fast_failing_ey command_to_run({}) << '--environment'
138
+ @err.should include("No value provided for option '--environment'")
139
+ fast_failing_ey command_to_run({}) << '-e'
140
+ @err.should include("No value provided for option '--environment'")
114
141
  end
115
142
 
116
143
  context "outside a git repo" do
@@ -126,7 +153,7 @@ shared_examples_for "it takes an environment name" do
126
153
  use_git_repo("not actually a git repo")
127
154
 
128
155
  before :all do
129
- api_scenario "one app, one environment"
156
+ login_scenario "one app, one environment"
130
157
  end
131
158
 
132
159
  it "works (and does not complain about git remotes)" do
@@ -137,44 +164,57 @@ shared_examples_for "it takes an environment name" do
137
164
 
138
165
  context "given a piece of the environment name" do
139
166
  before(:all) do
140
- api_scenario "one app, many similarly-named environments"
167
+ login_scenario "one app, many similarly-named environments"
141
168
  end
142
169
 
143
170
  it "complains when the substring is ambiguous" do
144
- run_ey({:environment => 'staging'}, {:expect_failure => true})
145
- if @takes_app_name
146
- @err.should match(/multiple app deployments possible/i)
171
+ run_ey({:environment => 'staging'}, {:expect_failure => !@succeeds_on_multiple_matches})
172
+
173
+ if @succeeds_on_multiple_matches
174
+ @err.should_not match(/multiple .* possible/i)
147
175
  else
148
- @err.should match(/multiple environments possible/i)
176
+ if @takes_app_name
177
+ @err.should match(/multiple application environments possible/i)
178
+ else
179
+ @err.should match(/multiple environments possible/i)
180
+ end
149
181
  end
150
182
  end
151
183
 
152
184
  it "works when the substring is unambiguous" do
153
- api_scenario "one app, many similarly-named environments"
154
- run_ey({:environment => 'prod'}, {:debug => true})
185
+ login_scenario "one app, many similarly-named environments"
186
+ run_ey({:environment => 'prod', :migrate => true}, {:debug => true})
155
187
  verify_ran(make_scenario({
156
- :environment => 'railsapp_production',
157
- :application => 'rails232app',
158
- :master_hostname => 'app_master_hostname.compute-1.amazonaws.com',
159
- :ssh_username => 'turkey',
160
- }))
188
+ :environment => 'railsapp_production',
189
+ :application => 'rails232app',
190
+ :master_hostname => 'app_master_hostname.compute-1.amazonaws.com',
191
+ :ssh_username => 'turkey',
192
+ }))
161
193
  end
162
194
  end
163
195
 
164
196
  it "complains when it can't guess the environment and its name isn't specified" do
165
- api_scenario "one app, one environment, not linked"
197
+ login_scenario "one app without environment"
166
198
  run_ey({:environment => nil}, {:expect_failure => true})
167
- @err.should match(/there is no application configured/i)
199
+ @err.should match(/No environment found for applications matching remotes:/i)
168
200
  end
169
201
  end
170
202
 
171
203
  shared_examples_for "it takes an app name" do
172
204
  before { @takes_app_name = true }
173
205
 
206
+ it "complains when you send --app without a value" do
207
+ login_scenario "empty"
208
+ fast_failing_ey command_to_run({}) << '--app'
209
+ @err.should include("No value provided for option '--app'")
210
+ fast_failing_ey command_to_run({}) << '-a'
211
+ @err.should include("No value provided for option '--app'")
212
+ end
213
+
174
214
  it "allows you to specify a valid app" do
175
- api_scenario "one app, one environment"
215
+ login_scenario "one app, one environment"
176
216
  Dir.chdir(Dir.tmpdir) do
177
- run_ey({:environment => 'giblets', :app => 'rails232app', :ref => 'master'}, {})
217
+ run_ey({:environment => 'giblets', :app => 'rails232app', :ref => 'master', :migrate => nil}, {})
178
218
  verify_ran(make_scenario({
179
219
  :environment => 'giblets',
180
220
  :application => 'rails232app',
@@ -185,9 +225,9 @@ shared_examples_for "it takes an app name" do
185
225
  end
186
226
 
187
227
  it "can guess the environment from the app" do
188
- api_scenario "two apps"
228
+ login_scenario "two apps"
189
229
  Dir.chdir(Dir.tmpdir) do
190
- run_ey({:app => 'rails232app', :ref => 'master'}, {})
230
+ run_ey({:app => 'rails232app', :ref => 'master', :migrate => true}, {})
191
231
  verify_ran(make_scenario({
192
232
  :environment => 'giblets',
193
233
  :application => 'rails232app',
@@ -198,10 +238,10 @@ shared_examples_for "it takes an app name" do
198
238
  end
199
239
 
200
240
  it "complains when you specify a nonexistant app" do
201
- api_scenario "one app, one environment"
241
+ login_scenario "one app, one environment"
202
242
  run_ey({:environment => 'giblets', :app => 'P-time-SAT-solver', :ref => 'master'},
203
243
  {:expect_failure => true})
204
- @err.should =~ /no app.*P-time-SAT-solver/i
244
+ @err.should =~ /No app.*P-time-SAT-solver/i
205
245
  end
206
246
 
207
247
  end
@@ -209,7 +249,7 @@ end
209
249
  shared_examples_for "it invokes engineyard-serverside" do
210
250
  context "with arguments" do
211
251
  before(:all) do
212
- api_scenario "one app, one environment"
252
+ login_scenario "one app, one environment"
213
253
  run_ey({:environment => 'giblets', :verbose => true})
214
254
  end
215
255
 
@@ -239,8 +279,8 @@ shared_examples_for "it invokes engineyard-serverside" do
239
279
 
240
280
  context "when no instances have names" do
241
281
  before(:each) do
242
- api_scenario "two apps"
243
- run_ey({:env => 'giblets', :app => 'rails232app', :ref => 'master', :verbose => true})
282
+ login_scenario "two apps"
283
+ run_ey({:env => 'giblets', :app => 'rails232app', :ref => 'master', :migrate => true, :verbose => true})
244
284
  end
245
285
 
246
286
  it "omits the --instance-names parameter" do
@@ -248,63 +288,3 @@ shared_examples_for "it invokes engineyard-serverside" do
248
288
  end
249
289
  end
250
290
  end
251
-
252
- shared_examples_for "model collections" do
253
- describe "#match_one" do
254
- it "works when given an unambiguous substring" do
255
- @collection.match_one("prod").name.should == "app_production"
256
- end
257
-
258
- it "raises an error when given an ambiguous substring" do
259
- lambda {
260
- @collection.match_one("staging")
261
- }.should raise_error(@collection_class.ambiguous_error)
262
- end
263
-
264
- it "returns an exact match if one exists" do
265
- @collection.match_one("app_staging").name.should == "app_staging"
266
- end
267
-
268
- it "returns nil when it can't find anything" do
269
- @collection.match_one("dev-and-production").should be_nil
270
- end
271
- end
272
-
273
- describe "#match_one!" do
274
- it "works when given an unambiguous substring" do
275
- @collection.match_one!("prod").name.should == "app_production"
276
- end
277
-
278
- it "raises an error when given an ambiguous substring" do
279
- lambda {
280
- @collection.match_one!("staging")
281
- }.should raise_error(@collection_class.ambiguous_error)
282
- end
283
-
284
- it "returns an exact match if one exists" do
285
- @collection.match_one!("app_staging").name.should == "app_staging"
286
- end
287
-
288
- it "raises an error when given an ambiguous exact string" do
289
- lambda {
290
- @collection.match_one!("app_duplicate")
291
- }.should raise_error(@collection_class.ambiguous_error)
292
- end
293
-
294
- it "raises an error when it can't find anything" do
295
- lambda {
296
- @collection.match_one!("dev-and-production")
297
- }.should raise_error(@collection_class.invalid_error)
298
- end
299
- end
300
-
301
- describe "#named" do
302
- it "finds matching by name" do
303
- @collection.named("app_staging").name.should == "app_staging"
304
- end
305
-
306
- it "returns nil when no name matches" do
307
- @collection.named("something else").should be_nil
308
- end
309
- end
310
- end