right_api_client 1.6.1 → 1.6.2

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.
@@ -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