right_api_client 1.6.1 → 1.6.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,25 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
3
+
4
+ require 'pp'
5
+ require 'yaml'
6
+
7
+ $: << File.expand_path('../../lib', __FILE__)
8
+
9
+ require 'right_api_client'
10
+
11
+ RSpec.configure do |config|
12
+ #
13
+ # include helpers
14
+
15
+ $config = config
16
+
17
+ Dir[File.expand_path('../support/**/*.rb', __FILE__)].each do |path|
18
+ require(path)
19
+ end
20
+
21
+ #
22
+ # misc
23
+
24
+ config.mock_with :flexmock
25
+ end
@@ -0,0 +1,40 @@
1
+ module MockSpecHelper
2
+
3
+ def mock_rest_client
4
+ @api_version = RightApi::Client::API_VERSION
5
+ @test_account_id = '1'
6
+ @rest_client = RestClient::Resource.new(RightApi::Client::DEFAULT_API_URL)
7
+ flexmock(RestClient::Resource).should_receive(:new).and_return(@rest_client)
8
+ @session = flexmock(:cookies => {})
9
+ @header = {'X-Api-Version' => @api_version, 'X-Account' => @test_account_id, :cookies => {}, :accept => :json}
10
+ end
11
+
12
+ def given_user_facing_client
13
+ mock_rest_client
14
+ flexmock(@rest_client).should_receive(:post).with(
15
+ {'email' => 'email', 'password' => 'password', 'account_href' => '/api/accounts/1'},
16
+ {'X-Api-Version' => @api_version}, Proc).and_return(@session)
17
+ flexmock(@rest_client).should_receive(:get).with(@header, Proc).once.and_return(['', '{"links": [
18
+ {
19
+ "href": "/api/servers",
20
+ "rel": "servers"
21
+ }]}'])
22
+ @client = RightApi::Client.new(:email => 'email', :password => 'password', :account_id => @test_account_id)
23
+ end
24
+
25
+ def given_instance_facing_client
26
+ mock_rest_client
27
+ flexmock(@rest_client).should_receive(:post).with(
28
+ {'instance_token' => 'instance_token', 'account_href' => '/api/accounts/1'},
29
+ {'X-Api-Version' => @api_version}, Proc).and_return(@session)
30
+ flexmock(@rest_client).should_receive(:get).with(@header, Proc).once.and_return(['', '{"links": [
31
+ {
32
+ "href": "/api/clouds/1/instances/1",
33
+ "rel": "self"
34
+ }]}'])
35
+ @client = RightApi::Client.new(:instance_token => 'instance_token', :account_id => @test_account_id)
36
+ end
37
+ end
38
+
39
+ $config.include MockSpecHelper
40
+
@@ -0,0 +1,23 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ describe RightApi::Client, :unit=>true do
4
+ context 'when the RightScale API misbehaves by sending empty bodies with 200 response' do
5
+ before(:each) do
6
+ given_user_facing_client
7
+ @result = Net::HTTPOK.new('1.1', '200', 'OK')
8
+ @result.set_content_type('application/vnd.rightscale.server+json')
9
+ @request = RestClient::Request.new(:method => 'GET', :headers => {}, :url => '/api/servers/1')
10
+ @response = RestClient::Response.create('', @result, {}, @request)
11
+ flexmock(@rest_client).should_receive(:get).with(Hash, Proc).and_yield(@response, @request, @result)
12
+ flexmock(@rest_client).should_receive(:post).with(Hash, Hash, Proc).and_yield(@response, @request, @result)
13
+ end
14
+
15
+ it 'raises an empty body error for a GET' do
16
+ expect { @client.servers(:id => 1).show }.to raise_error(RightApi::EmptyBodyError)
17
+ end
18
+
19
+ it 'raises an empty body error for a POST' do
20
+ expect { @client.servers.create }.to raise_error(RightApi::EmptyBodyError)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,214 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+ include RightApi::Helper
3
+
4
+ describe RightApi::Helper, :unit=>true do
5
+
6
+ API_MEDIA_TYPES = %w{audit_entry ip_address process server}
7
+
8
+ context ".define_instance_method" do
9
+ it "defines method" do
10
+ flexmock(RightApi::Helper).should_receive(:define_method)
11
+ define_instance_method('show') {1}
12
+ end
13
+ end
14
+
15
+ context ".api_methods" do
16
+ it "returns api methods" do
17
+ api_methods.should == RightApi::Helper.methods(false)
18
+ end
19
+ end
20
+
21
+ context ".has_id" do
22
+ it "returns false when params have no :id" do
23
+ params = {}
24
+ has_id(params).should == false
25
+ end
26
+
27
+ it "returns true when params have no :id" do
28
+ params = {:id => 1}
29
+ has_id(params).should == true
30
+ end
31
+ end
32
+
33
+ context ".add_id_and_params_to_path" do
34
+ it "adds id to the path" do
35
+ path = "/api/hello"
36
+ params = {:id => 1}
37
+ result = "/api/hello/1"
38
+ add_id_and_params_to_path(path,params).should == result
39
+ end
40
+
41
+ it "adds params to the path" do
42
+ path = "/api/hello"
43
+ params = {:right => "scale"}
44
+ result = "/api/hello?right=scale"
45
+ add_id_and_params_to_path(path,params).should == result
46
+ end
47
+
48
+ it "adds filters to the path" do
49
+ path = "/api/hello"
50
+ params = {:filter => ["first_name==Right", "last_name==Scale"]}
51
+ result = "/api/hello?filter[]=first_name%3D%3DRight&filter[]=last_name%3D%3DScale"
52
+ add_id_and_params_to_path(path,params).should == result
53
+ end
54
+
55
+ it "adds id/params/filters to the path" do
56
+ path = "/api/hello"
57
+ params = {
58
+ :id => 1,
59
+ :param => "params",
60
+ :filter => ["first_name==Right", "last_name==Scale"]
61
+ }
62
+ result = "/api/hello/1?filter[]=first_name%3D%3DRight&filter[]=last_name%3D%3DScale&param=params"
63
+ add_id_and_params_to_path(path,params).should == result
64
+ end
65
+ end
66
+
67
+ context ".insert_in_path" do
68
+ it "inserts term in front of first ? in path" do
69
+ path = "aa?bb?cc?dd???"
70
+ term = "term"
71
+ result = "aa/term?bb?cc?dd???"
72
+ insert_in_path(path,term).should == result
73
+ end
74
+
75
+ it "appends term to the end of path" do
76
+ path = "helloThisIsAPath"
77
+ term = "term"
78
+ result = "helloThisIsAPath/term"
79
+ insert_in_path(path,term).should == result
80
+ end
81
+ end
82
+
83
+ context ".is_singular?" do
84
+ API_MEDIA_TYPES.each do |media_type|
85
+ it "identifies media type #{media_type} as singular" do
86
+ is_singular?(media_type).should == true
87
+ end
88
+ end
89
+ end
90
+
91
+ context ".get_href_from_links" do
92
+ it "returns nil for empty links" do
93
+ links = []
94
+ get_href_from_links(links).should == nil
95
+ end
96
+
97
+ it "returns href of self link" do
98
+ links = [
99
+ {"rel" => "network", "href" => "/api/networks/aaa"},
100
+ {"rel" => "self", "href" => "/api/self"}
101
+ ]
102
+ get_href_from_links(links).should == "/api/self"
103
+
104
+ links.should == [
105
+ {"rel" => "network", "href" => "/api/networks/aaa"},
106
+ {"rel" => "self", "href" => "/api/self"}
107
+ ]
108
+ end
109
+ end
110
+
111
+ context ".get_and_delete_href_from_links" do
112
+ it "returns nil for empty links " do
113
+ links = []
114
+ get_and_delete_href_from_links(links).should == nil
115
+ end
116
+
117
+ it "returns and delete href of self link from links" do
118
+ links = [
119
+ {"rel" => "network", "href" => "/api/networks/aaa"},
120
+ {"rel" => "self", "href" => "/api/self"}
121
+ ]
122
+ get_and_delete_href_from_links(links).should == "/api/self"
123
+
124
+ links.should == [{"rel" => "network", "href" => "/api/networks/aaa"}]
125
+ end
126
+ end
127
+
128
+ context ".simple_singularize" do
129
+ it "returns hardcoded values for special words" do
130
+ pair = {
131
+ "audit_entries" => "audit_entry",
132
+ "ip_addresses" => "ip_address",
133
+ "processes" => "process"
134
+ }
135
+ pair.keys.each do |key|
136
+ simple_singularize(key).should == pair[key]
137
+ end
138
+ end
139
+
140
+ it "returns choped word for general work" do
141
+ word = "RightScale"
142
+ simple_singularize(word).should == word.chop
143
+ end
144
+ end
145
+
146
+ context ".get_singular" do
147
+ it "returns downcased singular form of word" do
148
+ word = "RightScale"
149
+ get_singular(word).should == simple_singularize(word.to_s.downcase)
150
+ end
151
+ end
152
+
153
+ context ".fix_array_of_hashes" do
154
+ it "fixes all the keys that have the value as array of hashes" do
155
+ res = fix_array_of_hashes(
156
+ 'a' => '1',
157
+ 'b' => [1, 2, 3],
158
+ 'c' => {1 => 2, 3 => 4},
159
+ 'd' => [
160
+ {5 => 6, 7 => 8},
161
+ {9 => 10, 11 => 12}
162
+ ]
163
+ )
164
+
165
+ res.should == {
166
+ 'a' => '1',
167
+ 'b' => [1, 2, 3],
168
+ 'c' => {1 => 2, 3 => 4},
169
+ 'd[]' => [
170
+ {5 => 6, 7 => 8},
171
+ {9 => 10, 11 => 12}
172
+ ]
173
+ }
174
+ end
175
+
176
+ it "fixes key that have a top-level array of hashes as value" do
177
+ res = fix_array_of_hashes(
178
+ 'a' => [
179
+ {1 => 2},
180
+ {3 => 4}
181
+ ]
182
+ )
183
+
184
+ res.should == {
185
+ 'a[]' => [
186
+ {1 => 2},
187
+ {3 => 4}
188
+ ]
189
+ }
190
+ end
191
+
192
+ it "fixes key that have a nested array of hashes as value" do
193
+ res = fix_array_of_hashes(
194
+ 'a' => {
195
+ 'b' => [
196
+ {1 => 2},
197
+ {3 => 4}
198
+ ]
199
+ }
200
+ )
201
+
202
+ res.should == {
203
+ 'a' => {
204
+ 'b' => {
205
+ '' => [
206
+ {1 => 2},
207
+ {3 => 4}
208
+ ]
209
+ }
210
+ }
211
+ }
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,25 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ describe RightApi::Client, :unit=>true do
4
+ context "given an instance-facing logged in RightScale user" do
5
+ before(:each) do
6
+ given_instance_facing_client
7
+ end
8
+
9
+ it "has the required methods for the client" do
10
+ @client.api_methods.sort.collect{|s| s.to_s}.should == ["backups", "get_instance", "live_tasks", "tags","volume_attachments", "volume_snapshots", "volume_types", "volumes"]
11
+ end
12
+
13
+ it "returns an instance of the Resource class when user provides an id" do
14
+ @client.volumes(:id => 1).class.should == RightApi::Resource
15
+ @client.backups(:id => 1).class.should == RightApi::Resource
16
+ @client.live_tasks(:id => 1).class.should == RightApi::Resource
17
+ end
18
+
19
+ it "returns an instance of the Resources class when user does not provide an id" do
20
+ @client.volumes.class.should == RightApi::Resources
21
+ @client.backups.class.should == RightApi::Resources
22
+ @client.tags.class.should == RightApi::Resources
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,93 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ describe RightApi::ResourceDetail, :unit=>true do
4
+ before(:each) do
5
+ given_user_facing_client
6
+ end
7
+
8
+ subject {RightApi::ResourceDetail.new(@client, 'resource_detail', '/api/resource_detail', {})}
9
+
10
+ context "attributes" do
11
+ its(:client) {should eq @client}
12
+ its(:attributes) {should eq Set.new << :links}
13
+ its(:resource_type) {should eq 'resource_detail'}
14
+ its(:associations) {should eq Set.new}
15
+ its(:actions) {should eq Set.new}
16
+ its(:raw) {should eq Hash.new}
17
+ end
18
+
19
+ context ".inspect" do
20
+ let(:resource_detail){RightApi::ResourceDetail.new(@client, 'resource_detail', '/api/resource_detail', {})}
21
+ it "returns correct inspect infos" do
22
+ inspect_text = "#<#{resource_detail.class.name} resource_type=\"#{resource_detail.resource_type}\">"
23
+ resource_detail.inspect.should == inspect_text
24
+ end
25
+ end
26
+
27
+ context "given a logged in RightScale user" do
28
+
29
+ it "has the required methods for instances of the ResourceDetail class" do
30
+ resource = RightApi::ResourceDetail.new(@client, 'deployment', '/api/deployments/1', {})
31
+ resource.api_methods.sort.collect{|s| s.to_s}.should == ["destroy", "links", "show", "update"]
32
+ end
33
+
34
+ it "has resource-specific methods for instances of the ResourceDetail class" do
35
+ resource = RightApi::ResourceDetail.new(@client, 'deployment', '/api/deployments/1',
36
+ {:attribute1 => 'value1', :attribute2 => 'value2'})
37
+ resource.api_methods.sort.collect{|s| s.to_s}.should == ["attribute1", "attribute2", "destroy", "links", "show", "update"]
38
+ end
39
+
40
+ it "has the links for instances of the ResourceDetail class" do
41
+ resource = RightApi::ResourceDetail.new(@client, 'deployment', '/api/deployments/1',
42
+ {'links' => [{'rel' => 'link1', 'href' => 'link1_href'},
43
+ {'rel' => 'link2', 'href' => 'link2_href'}]})
44
+ resource.api_methods.sort.collect{|s| s.to_s}.should == ["destroy", "link1", "link2", "links", "show", "update"]
45
+ end
46
+
47
+ it "has the actions for instances of the ResourceDetail class" do
48
+ resource = RightApi::ResourceDetail.new(@client, 'deployment', '/api/deployments/1',
49
+ {'links' => [{'rel' => 'self', 'href' => 'self'}],
50
+ 'actions' => [{'rel' => 'action1'}, {'rel' => 'action2'}]})
51
+ resource.api_methods.sort.collect{|s| s.to_s}.should == ["action1", "action2", "destroy", "href", "links", "show", "update"]
52
+
53
+ flexmock(@rest_client).should_receive(:post).with({}, @header, Proc).and_return('ok')
54
+ resource.action1.should == 'ok'
55
+ end
56
+
57
+ it "has live_tasks for the 'instance' resource" do
58
+ resource = RightApi::ResourceDetail.new(@client, 'instance', '/api/instances/1', {})
59
+ resource.api_methods.sort.collect{|s| s.to_s}.should == ["destroy", "links", "live_tasks", "show", "update"]
60
+ flexmock(RightApi::Resource).should_receive(:process).with(@client, 'live_task', '/api/instances/1/live/tasks/1').and_return('ok')
61
+ resource.live_tasks(:id => '1').should == 'ok'
62
+ end
63
+
64
+ it "adds methods for child resources from detailed views" do
65
+ resource = RightApi::ResourceDetail.new(@client, 'server', '/api/servers/1', {
66
+ 'links' => [
67
+ {'href' => '/api/servers/1', 'rel' => 'self'},
68
+ {'href' => '/api/clouds/1/instances/1', 'rel' => 'current_instance'}],
69
+ 'current_instance' => {'links' => [{'href' => '/api/clouds/1/instances/1', 'rel' => 'self'}]}})
70
+ resource.api_methods.collect{|s| s.to_s}.sort.should == ["current_instance", "destroy", "href", "links", "show", "update"]
71
+ end
72
+ end
73
+
74
+ context ".[]" do
75
+ let(:resource) { RightApi::ResourceDetail.new(@client, 'deployment', '/api/deployments/1',
76
+ {'links' => [{'rel' => 'link1', 'href' => 'link1_href'}],
77
+ 'real_attribute'=>'hi mom',
78
+ 'link1' => 'sneaky'}) }
79
+
80
+ it 'reads attributes whose name overlaps with a link' do
81
+ resource['link1'].should == 'sneaky'
82
+ resource.link1.should_not == resource['link1']
83
+ end
84
+
85
+ it 'accepts String keys' do
86
+ resource['real_attribute'].should == 'hi mom'
87
+ end
88
+
89
+ it 'accepts Symbol keys' do
90
+ resource[:real_attribute].should == 'hi mom'
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,78 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ describe RightApi::Resource, :unit=>true do
4
+ before(:each) do
5
+ given_user_facing_client
6
+ end
7
+
8
+ subject {RightApi::Resource.new(@client, 'resource', '/api/resource')}
9
+
10
+ context "attributes" do
11
+ its(:client) {should eq @client}
12
+ its(:href) {should eq '/api/resource'}
13
+ its(:resource_type) {should eq 'resource'}
14
+ end
15
+
16
+ context "#process" do
17
+ it "creates resource_detail with data" do
18
+ flexmock(RightApi::ResourceDetail).should_receive(:new)
19
+ RightApi::Resource.process(@client, 'resource', '/api/resource', :right => ['scale'])
20
+ end
21
+
22
+ it "creates resource without data" do
23
+ flexmock(RightApi::Resource).should_receive(:new)
24
+ RightApi::Resource.process(@client, 'resource', '/api/resource')
25
+ end
26
+ end
27
+
28
+ context "#process_detailed" do
29
+ it "calls process with data array" do
30
+ flexmock(RightApi::Resource).should_receive(:process)
31
+ RightApi::Resource.process(@client, 'resource', '/api/resource', :right => ['scale'])
32
+ end
33
+
34
+ it "calls process with data array" do
35
+ flexmock(RightApi::Resource).should_receive(:process)
36
+ RightApi::Resource.process(@client, 'resource', '/api/resource')
37
+ end
38
+
39
+ it "creates new process detail with data links" do
40
+ flexmock(RightApi::ResourceDetail).should_receive(:new)
41
+ RightApi::Resource.process(@client, 'resource', '/api/resource', 'links' => 'scale')
42
+ end
43
+ end
44
+
45
+ context ".inspect" do
46
+ let(:resource) {RightApi::Resource.new(@client, 'resource', '/api/resource')}
47
+ it "returns correct inspect text" do
48
+ inspect_text = "#<#{resource.class.name} resource_type=\"#{resource.resource_type}\">"
49
+ resource.inspect.should == inspect_text
50
+ end
51
+ end
52
+
53
+ context ".method_missing" do
54
+ it "sends correct post request" do
55
+ client = flexmock(@client)
56
+ resource = RightApi::Resource.new(client, 'resource', '/api/resource')
57
+ client.should_receive(:send).with(:do_post, "#{resource.href}/method")
58
+ resource.method_missing('method')
59
+ end
60
+ end
61
+
62
+ context "given a logged in RightScale user" do
63
+ it "has the required methods for instances of the Resource class" do
64
+ resource = RightApi::Resource.process(@client, 'deployment', '/api/deployments/1')
65
+ resource.api_methods.sort.map(&:to_s).should == %w[ destroy show update ]
66
+ end
67
+
68
+ it "has destroy/show/update for all instances of the Resource class" do
69
+ resource = RightApi::Resource.process(@client, 'session', '/api/session')
70
+ resource.api_methods.sort.map(&:to_s).should == %w[ destroy show update ]
71
+ end
72
+
73
+ it "has an array of ResourceDetail instances for index calls" do
74
+ resources = RightApi::Resource.process(@client, 'deployment', '/api/deployments', [{}])
75
+ resources.first.class.should == RightApi::ResourceDetail
76
+ end
77
+ end
78
+ end