ey_cli 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/Gemfile +3 -0
  2. data/Gemfile.lock +57 -0
  3. data/LICENSE +22 -0
  4. data/README.md +104 -0
  5. data/Rakefile +129 -0
  6. data/bin/ey_cli +5 -0
  7. data/ey_cli.gemspec +117 -0
  8. data/history.md +3 -0
  9. data/lib/ey_cli/api.rb +96 -0
  10. data/lib/ey_cli/cli.rb +17 -0
  11. data/lib/ey_cli/command_manager.rb +29 -0
  12. data/lib/ey_cli/commands/accounts.rb +26 -0
  13. data/lib/ey_cli/commands/apps.rb +24 -0
  14. data/lib/ey_cli/commands/base.rb +16 -0
  15. data/lib/ey_cli/commands/console.rb +38 -0
  16. data/lib/ey_cli/commands/create_app.rb +76 -0
  17. data/lib/ey_cli/commands/create_env.rb +106 -0
  18. data/lib/ey_cli/commands/deploy.rb +23 -0
  19. data/lib/ey_cli/commands/help.rb +65 -0
  20. data/lib/ey_cli/commands/show.rb +65 -0
  21. data/lib/ey_cli/controllers/accounts.rb +23 -0
  22. data/lib/ey_cli/controllers/apps.rb +66 -0
  23. data/lib/ey_cli/controllers/environments.rb +78 -0
  24. data/lib/ey_cli/git_utils.rb +12 -0
  25. data/lib/ey_cli/models/account.rb +6 -0
  26. data/lib/ey_cli/models/app.rb +15 -0
  27. data/lib/ey_cli/models/base.rb +54 -0
  28. data/lib/ey_cli/models/environment.rb +32 -0
  29. data/lib/ey_cli/options_parser.rb +47 -0
  30. data/lib/ey_cli/smarty_parser.rb +29 -0
  31. data/lib/ey_cli/term.rb +58 -0
  32. data/lib/ey_cli.rb +46 -0
  33. data/spec/auth_helper.rb +28 -0
  34. data/spec/ey_cli/api_spec.rb +70 -0
  35. data/spec/ey_cli/command_manager_spec.rb +33 -0
  36. data/spec/ey_cli/commands/help_spec.rb +36 -0
  37. data/spec/ey_cli/controllers/accounts_spec.rb +40 -0
  38. data/spec/ey_cli/controllers/apps_spec.rb +97 -0
  39. data/spec/ey_cli/controllers/environments_spec.rb +101 -0
  40. data/spec/ey_cli/models/app_spec.rb +50 -0
  41. data/spec/ey_cli/models/base_spec.rb +98 -0
  42. data/spec/ey_cli/models/environment_spec.rb +51 -0
  43. data/spec/ey_cli/options_parser_spec.rb +19 -0
  44. data/spec/ey_cli/smarty_parser_spec.rb +19 -0
  45. data/spec/spec_helper.rb +29 -0
  46. metadata +288 -0
@@ -0,0 +1,29 @@
1
+ module EYCli
2
+ module SmartyParser
3
+ def parse(body, klass = self)
4
+ case body
5
+ when Hash
6
+ klass.new smarty(body)
7
+ when Array
8
+ body.map { |item| parse(item, klass) }
9
+ else
10
+ body
11
+ end
12
+ end
13
+
14
+ def smarty(hash)
15
+ hash.each do |key, value|
16
+ case key
17
+ when /accounts?/
18
+ hash[key] = parse(value, EYCli::Model::Account)
19
+ when /apps?/
20
+ hash[key] = parse(value, EYCli::Model::App)
21
+ when /environments?/
22
+ hash[key] = parse(value, EYCli::Model::Environment)
23
+ else
24
+ hash[key] = parse(value, Hashie::Mash)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,58 @@
1
+ module EYCli
2
+ class Term
3
+ require 'highline'
4
+
5
+ attr_reader :terminal
6
+
7
+ def initialize(input = $stdin, output = $stdout)
8
+ HighLine.color_scheme = HighLine::SampleColorScheme.new
9
+ @terminal = HighLine.new(input, output)
10
+ end
11
+
12
+ def choose_resource(collection, message, prompt)
13
+ say(message)
14
+
15
+ terminal.choose do |menu|
16
+ menu.index = :number
17
+ menu.index_suffix = ' ~> '
18
+ menu.prompt = prompt
19
+
20
+ collection.each do |resource|
21
+ menu.choice resource.name
22
+ end
23
+ end
24
+ end
25
+
26
+ def say(message)
27
+ terminal.say(message)
28
+ end
29
+
30
+ def error(message)
31
+ terminal.say(%Q{<%= color("#{message}", :error)%>})
32
+ end
33
+
34
+ def warning(message)
35
+ terminal.say(%Q{<%= color("#{message}", :warning)%>})
36
+ end
37
+
38
+ def success(message)
39
+ terminal.say(%Q{<%= color("#{message}", :debug)%>})
40
+ end
41
+
42
+ def print_errors(errors, message)
43
+ error(message)
44
+ errors.each do |key, value|
45
+ alert = value.respond_to?(:join) ? value.join(',') : value.inspect
46
+ error("\t- #{key}: #{alert}")
47
+ end
48
+ end
49
+
50
+ def ask(prompt, protected_filed = false)
51
+ if protected_filed
52
+ terminal.ask(prompt) {|q| q.echo = '*'}
53
+ else
54
+ terminal.ask(prompt) {|q| q.readline = true}
55
+ end
56
+ end
57
+ end
58
+ end
data/lib/ey_cli.rb ADDED
@@ -0,0 +1,46 @@
1
+ module EYCli
2
+ VERSION = '0.1.0'
3
+
4
+ require 'hashie/mash'
5
+ require 'json'
6
+ require 'multi_json'
7
+
8
+ require 'ey_cli/api'
9
+ require 'ey_cli/cli'
10
+ require 'ey_cli/git_utils'
11
+ require 'ey_cli/command_manager'
12
+ require 'ey_cli/options_parser'
13
+ require 'ey_cli/smarty_parser'
14
+ require 'ey_cli/term'
15
+
16
+ require 'ey_cli/models/base'
17
+ require 'ey_cli/models/account'
18
+ require 'ey_cli/models/app'
19
+ require 'ey_cli/models/environment'
20
+
21
+ require 'ey_cli/controllers/accounts'
22
+ require 'ey_cli/controllers/apps'
23
+ require 'ey_cli/controllers/environments'
24
+
25
+ require 'ey_cli/commands/base'
26
+ require 'ey_cli/commands/accounts'
27
+ require 'ey_cli/commands/apps'
28
+ require 'ey_cli/commands/console'
29
+ require 'ey_cli/commands/create_app'
30
+ require 'ey_cli/commands/create_env'
31
+ require 'ey_cli/commands/deploy'
32
+ require 'ey_cli/commands/help'
33
+ require 'ey_cli/commands/show'
34
+
35
+ def self.api(endpoint = nil)
36
+ @api ||= Api.new(endpoint)
37
+ end
38
+
39
+ def self.term(input = $stdin, output = $stdout)
40
+ @ui ||= Term.new(input, output)
41
+ end
42
+
43
+ def self.command_manager
44
+ @command_manager ||= EYCli::CommandManager.new
45
+ end
46
+ end
@@ -0,0 +1,28 @@
1
+ module EYCli
2
+ module AuthHelper
3
+ def self.extended(example_group)
4
+ example_group.use_credentials(example_group)
5
+ end
6
+
7
+ def self.included(example_group)
8
+ example_group.extend self
9
+ end
10
+
11
+ def mock_credentials
12
+ rc = File.expand_path('../fake_rc', __FILE__)
13
+
14
+ File.open(rc, 'w') do |f| f.write <<-EOF
15
+ ---
16
+ api_token: fake_token
17
+ EOF
18
+ end
19
+ ENV['EYRC'] = rc
20
+ end
21
+
22
+ def use_credentials(describe_block)
23
+ describe_block.before :each do
24
+ mock_credentials
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,70 @@
1
+ require File.expand_path('../spec_helper', File.dirname(__FILE__))
2
+
3
+ describe EYCli::Api do
4
+ subject { EYCli::Api.new('http://example.com') }
5
+
6
+ context "reading the auth token from the file" do
7
+ it "returns nil if the file doesn't exist" do
8
+ subject.read_token('fake_file').should be_nil
9
+ end
10
+
11
+ it "returns nil if the file is empty" do
12
+ File.open('fake_file', 'w')
13
+ subject.read_token('fake_file').should be_nil
14
+ end
15
+
16
+ it "returns the token in the root if there is no token for the endpoint" do
17
+ File.open('fake_file', 'w') do |f|
18
+ f.write <<-EOF
19
+ ---
20
+ api_token: root_api_token
21
+ EOF
22
+ end
23
+
24
+ subject.read_token('fake_file').should == 'root_api_token'
25
+ end
26
+
27
+ it "returns the token for the endpoint if exists" do
28
+ File.open('fake_file', 'w') do |f|
29
+ f.write <<-EOF
30
+ ---
31
+ api_token: root_api_token
32
+ http://example.com:
33
+ api_token: example_com_api_token
34
+ EOF
35
+ end
36
+
37
+ subject.read_token('fake_file').should == 'example_com_api_token'
38
+ end
39
+ end
40
+
41
+ context "saving the auth token" do
42
+ def fake_rc_file
43
+ File.read('fake_file').gsub(/\s*/m, '')
44
+ end
45
+
46
+ it "stores the token under the enpoint" do
47
+ subject.save_token('new_token', 'fake_file')
48
+ fake_rc_file.should == '---http://example.com:api_token:new_token'
49
+ end
50
+
51
+ it "adds it to the file if already existed" do
52
+ File.open('fake_file', 'w') {|f| f.write("---\n api_token: root_token") }
53
+ subject.save_token('new_token', 'fake_file')
54
+ fake_rc_file.should == '---api_token:root_tokenhttp://example.com:api_token:new_token'
55
+ end
56
+ end
57
+
58
+ context "fetching the token from the terminal" do
59
+ it "sends the user credentials to the api" do
60
+ EYCli.term.should_receive(:ask).with('Email: ').and_return('foo@foo.com')
61
+ EYCli.term.should_receive(:ask).with('Password: ', true).and_return('fooooo')
62
+
63
+ stub_request(:post, "http://example.com/authenticate?email=foo@foo.com&password=fooooo").
64
+ to_return(:status => 200, :body => to_json(:api_token => 'foo_token'))
65
+
66
+ ENV['EYRC'] = 'fake_file'
67
+ subject.fetch_token.should == 'foo_token'
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,33 @@
1
+ require File.expand_path('../spec_helper', File.dirname(__FILE__))
2
+
3
+ module EYCli::Command
4
+ class MockCommand; end
5
+ end
6
+
7
+ describe EYCli::CommandManager do
8
+ before { subject.register_command(:mock_command) }
9
+
10
+ it "registers commands" do
11
+ subject.commands.keys.should include(:mock_command)
12
+ end
13
+
14
+ it "returns nil if the command class is not registered" do
15
+ subject[:bar].should be_nil
16
+ end
17
+
18
+ it "returns an instance of the command class if it's registered" do
19
+ subject[:mock_command].should be_instance_of(EYCli::Command::MockCommand)
20
+ end
21
+
22
+ it "returns nil if the command class is not defined" do
23
+ subject.register_command(:bar)
24
+ subject[:bar].should be_nil
25
+ end
26
+
27
+ it "only loads the command class once" do
28
+ subject.should_receive(:load_command).
29
+ with(:mock_command).
30
+ once.and_return(EYCli::Command::MockCommand.new)
31
+ subject[:mock_command] || subject[:mock_command]
32
+ end
33
+ end
@@ -0,0 +1,36 @@
1
+ require File.expand_path('../../spec_helper', File.dirname(__FILE__))
2
+
3
+ class EYCli::Command::WithoutHelp; def help; end end
4
+ class EYCli::Command::WithHelp; def help; 'run with_help' end end
5
+
6
+ describe EYCli::Command::Help do
7
+ before do
8
+ EYCli.command_manager.register_command :without_help
9
+ EYCli.command_manager.register_command :with_help
10
+ end
11
+
12
+ it "prints the global help when the command name is not provided" do
13
+ subject.run([])
14
+ $stdout_test.string.should =~ /ey_cli help commands/
15
+ end
16
+
17
+ it "says that the help is not available when the command does not exist" do
18
+ subject.run(['fake_command'])
19
+ $stdout_test.string.should =~ /help not available/
20
+ end
21
+
22
+ it "says that the help is not available when the command does not have any help message" do
23
+ subject.run(['without_help'])
24
+ $stdout_test.string.should =~ /help not available for command: 'without_help'/
25
+ end
26
+
27
+ it "shows the available commands" do
28
+ subject.run(['commands'])
29
+ $stdout_test.string.should =~ /available commands/
30
+ end
31
+
32
+ it "shows the help message for the specified command" do
33
+ subject.run(['with_help'])
34
+ $stdout_test.string.should =~ /run with_help/
35
+ end
36
+ end
@@ -0,0 +1,40 @@
1
+ require File.expand_path('../../spec_helper', File.dirname(__FILE__))
2
+
3
+ describe EYCli::Controller::Accounts do
4
+ it "returns the first account if we don't provide a name and the user has only one account" do
5
+ expected = EYCli::Model::Account.new({:id => 1, :name => 'foo'})
6
+ stub_request(:get, 'http://example.com/accounts').
7
+ to_return(:status => 200, :body => to_json({'accounts' => [expected]}))
8
+
9
+ account = subject.fetch_account
10
+ account.should == expected
11
+ end
12
+
13
+ it "returns nil if we don't find the account by the name provided" do
14
+ expected = EYCli::Model::Account.new({:id => 1, :name => 'foo'})
15
+ stub_request(:get, 'http://example.com/accounts').
16
+ to_return(:status => 200, :body => to_json({'accounts' => [expected]}))
17
+
18
+ subject.fetch_account('bar').should be_nil
19
+ end
20
+
21
+ it "returns nil if the user hasn't created an account yet" do
22
+ stub_request(:get, 'http://example.com/accounts').
23
+ to_return(:status => 200, :body => to_json({'accounts' => []}))
24
+
25
+ subject.fetch_account.should be_nil
26
+ end
27
+
28
+ it "returns the account selected by the user" do
29
+ account1 = EYCli::Model::Account.new({:id => 1, :name => 'foo'})
30
+ account2 = EYCli::Model::Account.new({:id => 2, :name => 'bar'})
31
+ account3 = EYCli::Model::Account.new({:id => 3, :name => 'baz'})
32
+ stub_request(:get, 'http://example.com/accounts').
33
+ to_return(:status => 200, :body => to_json({'accounts' => [account1, account2, account3]}))
34
+
35
+ EYCli.term.terminal.should_receive(:choose).and_return('bar')
36
+
37
+ account = subject.fetch_account
38
+ account.should == account2
39
+ end
40
+ end
@@ -0,0 +1,97 @@
1
+ require File.expand_path('../../spec_helper', File.dirname(__FILE__))
2
+
3
+ describe EYCli::Controller::Apps do
4
+ before { Dir.mkdir('fake_app') }
5
+ let(:account) { EYCli::Model::Account.new(:id => 1) }
6
+
7
+ it "shows an error message when the directory is not a git repository" do
8
+ EYCli.term.should_receive(:error).with('Not a git repository: fake_app')
9
+ subject.create(account, 'fake_app')
10
+ end
11
+
12
+ context 'trying to figure out the application type' do
13
+ it "uses rails 3 as default" do
14
+ subject.fetch_type('fake_app').should == :rails3
15
+ end
16
+
17
+ it 'is a rails 3 app if the directory has the environment and rackup files' do
18
+ Dir.mkdir('fake_app/config')
19
+ ['fake_app/config.ru', 'fake_app/config/environment.rb'].each {|f| File.open(f, 'w')}
20
+
21
+ subject.fetch_type('fake_app').should == :rails3
22
+ end
23
+
24
+ it 'is a rails 2 app if the directory only includes the environment file' do
25
+ Dir.mkdir('fake_app/config')
26
+ File.open('fake_app/config/environment.rb', 'w')
27
+ subject.fetch_type('fake_app').should == :rails2
28
+ end
29
+
30
+ it 'is a rack app if the directory only includes the rackup file' do
31
+ File.open('fake_app/config.ru', 'w')
32
+ subject.fetch_type('fake_app').should == :rack
33
+ end
34
+ end
35
+
36
+ context 'under a git repository' do
37
+ before { Dir.mkdir('fake_app/.git') }
38
+ let(:success_params) do
39
+ {
40
+ :account => account,
41
+ 'app[name]' => 'fake_app',
42
+ 'app[app_type_id]' => 'rails3',
43
+ 'app[repository_uri]' => 'git@git.com:foo/bar.git'
44
+ }
45
+ end
46
+
47
+ it 'shows an error if the model cannot create the application' do
48
+ app_with_errors = EYCli::Model::App.new({:errors => {:name => 'bad_name'}})
49
+ EYCli.term.should_receive(:print_errors).with(app_with_errors.errors, 'App creation failed:')
50
+ EYCli::Model::App.should_receive(:create).and_return(app_with_errors)
51
+
52
+ subject.create(account, 'fake_app')
53
+ end
54
+
55
+ it "shows a success message when the model creates the app" do
56
+ app = EYCli::Model::App.new({:id => 1, :name => 'fake_app'})
57
+ EYCli::Model::App.should_receive(:create).with(success_params).and_return(app)
58
+ EYCli.term.should_receive(:success)
59
+ subject.should_receive(:fetch_repository).and_return('git@git.com:foo/bar.git')
60
+
61
+ subject.create(account, 'fake_app')
62
+ end
63
+ end
64
+
65
+ context '#fetch_app' do
66
+ let(:app1) { EYCli::Model::App.new(:id => 1, :repository_uri => 'git@foo.com', :name => 'foo_app') }
67
+ let(:app2) { EYCli::Model::App.new(:id => 2, :repository_uri => 'git@bar.com', :name => 'bar_app') }
68
+
69
+ it 'returns the first app if the user only has one' do
70
+ stub_request(:get, 'http://example.com/apps').
71
+ to_return(:status => 200, :body => to_json(:apps => [app1]))
72
+ subject.fetch_app.should == app1
73
+ end
74
+
75
+ it 'returns the first app that matches the name passed as an option' do
76
+ stub_request(:get, 'http://example.com/apps').
77
+ to_return(:status => 200, :body => to_json(:apps => [app1, app2]))
78
+ subject.fetch_app(nil, {:app_name => 'bar_app'}).should == app2
79
+ end
80
+
81
+ it 'returns the first app that matches the git repository uri of the base path' do
82
+ stub_request(:get, 'http://example.com/apps').
83
+ to_return(:status => 200, :body => to_json(:apps => [app1, app2]))
84
+ subject.should_receive(:git_repository?).and_return(true)
85
+ subject.should_receive(:fetch_repository).and_return('git@bar.com')
86
+
87
+ subject.fetch_app.should == app2
88
+ end
89
+
90
+ it 'returns the app according to the option the user chooses' do
91
+ stub_request(:get, 'http://example.com/apps').
92
+ to_return(:status => 200, :body => to_json(:apps => [app1, app2]))
93
+ EYCli.term.should_receive(:choose_resource).and_return('bar_app')
94
+ subject.fetch_app.should == app2
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,101 @@
1
+ require File.expand_path('../../spec_helper', File.dirname(__FILE__))
2
+
3
+ describe EYCli::Controller::Environments do
4
+ let(:app) { EYCli::Model::App.new(:id => 1, :name => 'fake_app') }
5
+
6
+ context 'creating a new environment' do
7
+ let(:success_params) do
8
+ {
9
+ :app => app,
10
+ 'environment[name]' => "#{app.name}_production",
11
+ 'environment[framework_env]' => 'production',
12
+ }
13
+ end
14
+
15
+ it "shows an error message if the model cannot create the environment" do
16
+ env_with_errors = EYCli::Model::Environment.new({:errors => {:name => 'bad_name'}})
17
+ EYCli.term.should_receive(:print_errors).with(env_with_errors.errors, 'Environment creation failed:')
18
+ EYCli::Model::Environment.should_receive(:create).and_return(env_with_errors)
19
+
20
+ subject.create(app, {})
21
+ end
22
+
23
+ it "shows a success message when the model creates the app" do
24
+ env = EYCli::Model::Environment.new({:id => 1, :name => 'fake_app_production', :framework_env => 'production'})
25
+ EYCli::Model::Environment.should_receive(:create).with(success_params).and_return(env)
26
+ EYCli.term.should_receive(:success)
27
+
28
+ response = subject.create(app, {})
29
+ response.should == env
30
+ end
31
+
32
+ it "allows to specify the framework environment as a parameter" do
33
+ params = success_params.merge({'environment[name]' => 'fake_app_staging', 'environment[framework_env]' => 'staging'})
34
+ env = EYCli::Model::Environment.new({:id => 1, :name => 'fake_app_staging', :framework_env => 'staging'})
35
+ EYCli::Model::Environment.should_receive(:create).with(params).and_return(env)
36
+ EYCli.term.should_receive(:success)
37
+
38
+ response = subject.create(app, {:framework_env => 'staging'})
39
+ response.should == env
40
+ end
41
+
42
+ it "allows to specify the environment name" do
43
+ params = success_params.merge({'environment[name]' => 'fake_environment_name', 'environment[framework_env]' => 'production'})
44
+ env = EYCli::Model::Environment.new({:id => 1, :name => 'fake_environment_name', :framework_env => 'production'})
45
+ EYCli::Model::Environment.should_receive(:create).with(params).and_return(env)
46
+ EYCli.term.should_receive(:success)
47
+
48
+ response = subject.create(app, {:name => 'fake_environment_name'})
49
+ response.should == env
50
+ end
51
+
52
+ context "with cluster options" do
53
+ it "is pending"
54
+ end
55
+ end
56
+
57
+ context "deploying an environment" do
58
+ it "shows an error if the application doesn't have any environment" do
59
+ EYCli.term.should_receive(:error)
60
+ subject.deploy(app)
61
+ end
62
+
63
+ context "when the app has only one environment" do
64
+ before do
65
+ @env = mock
66
+ app[:environments] = [@env]
67
+ end
68
+
69
+ it "uses the environment without asking" do
70
+ @env.should_receive(:deploy).and_return(Hashie::Mash.new)
71
+ EYCli.term.should_not_receive(:choose_resource)
72
+ subject.deploy(app)
73
+ end
74
+
75
+ it "shows the error list when the deploy fails" do
76
+ expected = Hashie::Mash.new({:errors => {:provision => 'Amazon cannot provision the instance'}})
77
+ @env.should_receive(:deploy).and_return(expected)
78
+ EYCli.term.should_receive(:print_errors).with(expected.errors, "Application deployment failed:")
79
+ subject.deploy(app)
80
+ end
81
+
82
+ it "shows the success message if it doesn't return any error" do
83
+ @env.should_receive(:deploy).and_return(Hashie::Mash.new)
84
+ EYCli.term.should_receive(:success)
85
+ subject.deploy(app)
86
+ end
87
+ end
88
+
89
+ context "when the app has more than one environment" do
90
+ it "shows the environments' list to let the user choose" do
91
+ env1 = EYCli::Model::Environment.new(:name => 'mock1')
92
+ env2 = mock
93
+ env2.should_receive(:name).and_return('mock2')
94
+ env2.should_receive(:deploy).and_return(Hashie::Mash.new)
95
+ app[:environments] = [env1, env2]
96
+ EYCli.term.should_receive(:choose_resource).and_return('mock2')
97
+ subject.deploy(app)
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,50 @@
1
+ require File.expand_path('../../spec_helper', File.dirname(__FILE__))
2
+
3
+ describe EYCli::Model::App do
4
+ context ".create" do
5
+ it "return an error if it doesn't find the account" do
6
+ app = EYCli::Model::App.create({})
7
+ app.errors?.should be_true
8
+ app.errors.keys.should include('account')
9
+ end
10
+
11
+ it "return an error if a model validation fails" do
12
+ account = EYCli::Model::Account.new({:id => 1})
13
+ stub_request(:post, 'http://example.com/accounts/1/apps?app[name]=foo').
14
+ to_return(:status => 422, :body => to_json({:errors => {:name => ['App name already exists']}}))
15
+
16
+ app = EYCli::Model::App.create({:account => account, 'app[name]' => 'foo'})
17
+ app.errors?.should be_true
18
+ app.errors.keys.should include('name')
19
+ end
20
+
21
+ it "returns the new app when it's successful" do
22
+ account = EYCli::Model::Account.new({:id => 1})
23
+ expected = EYCli::Model::App.new({:id => 1, :name => 'foo'})
24
+ stub_request(:post, 'http://example.com/accounts/1/apps?app[name]=foo').
25
+ to_return(:status => 201, :body => to_json(:app => expected))
26
+
27
+ app = EYCli::Model::App.create({:account => account, 'app[name]' => 'foo'})
28
+ app.should == expected
29
+ end
30
+
31
+ context '.find_by_repository_uri' do
32
+ let(:app1) { EYCli::Model::App.new(:id => 1, :repository_uri => 'git@foo.com') }
33
+ let(:app2) { EYCli::Model::App.new(:id => 2, :repository_uri => 'git@bar.com') }
34
+ before do
35
+ stub_request(:get, 'http://example.com/apps').
36
+ to_return(:status => 200, :body => to_json(:apps => [app1, app2]))
37
+ end
38
+
39
+ it "returns an instance of App if it finds it" do
40
+ app = EYCli::Model::App.find_by_repository_uri('git@bar.com')
41
+ app.should == app2
42
+ end
43
+
44
+ it "returns nil when it cannot find the app" do
45
+ app = EYCli::Model::App.find_by_repository_uri('foo@bar.com')
46
+ app.should be_nil
47
+ end
48
+ end
49
+ end
50
+ end