ey_cli 0.1.0

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 (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