vmware-vra 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,21 @@
1
+ #
2
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
3
+ # Copyright:: Copyright (c) 2015 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ module Vra
20
+ VERSION = '1.0.0.rc1'
21
+ end
@@ -0,0 +1,98 @@
1
+ #
2
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
3
+ # Copyright:: Copyright (c) 2015 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'spec_helper'
20
+
21
+ describe Vra::CatalogRequest do
22
+ let(:client) do
23
+ Vra::Client.new(username: 'user@corp.local',
24
+ password: 'password',
25
+ tenant: 'tenant',
26
+ base_url: 'https://vra.corp.local')
27
+ end
28
+
29
+ let(:catalog_id) { '9e98042e-5443-4082-afd5-ab5a32939bbc' }
30
+
31
+ let(:catalog_item_payload) do
32
+ {
33
+ '@type' => 'CatalogItem',
34
+ 'id' => '9e98042e-5443-4082-afd5-ab5a32939bbc',
35
+ 'version' => 2,
36
+ 'name' => 'CentOS 6.6',
37
+ 'description' => 'Blueprint for deploying a CentOS Linux development server',
38
+ 'status' => 'PUBLISHED',
39
+ 'statusName' => 'Published',
40
+ 'organization' => {
41
+ 'tenantRef' => 'vsphere.local',
42
+ 'tenantLabel' => 'vsphere.local',
43
+ 'subtenantRef' => '962ab3f9-858c-4483-a49f-fa97392c314b',
44
+ 'subtenantLabel' => 'catalog_subtenant'
45
+ },
46
+ 'providerBinding' => {
47
+ 'bindingId' => '33af5413-4f20-4b3b-8268-32edad434dfb',
48
+ 'providerRef' => {
49
+ 'id' => 'c3b2bc30-47b0-454f-b57d-df02a7356fe6',
50
+ 'label' => 'iaas-service'
51
+ }
52
+ }
53
+ }
54
+ end
55
+
56
+ describe '#initialize' do
57
+ it 'raises an error if no ID or catalog item data have been provided' do
58
+ expect { Vra::CatalogItem.new }.to raise_error(ArgumentError)
59
+ end
60
+
61
+ it 'raises an error if an ID and catalog item data have both been provided' do
62
+ expect { Vra::CatalogItem.new(id: 123, data: 'foo') }.to raise_error(ArgumentError)
63
+ end
64
+
65
+ context 'when an ID is provided' do
66
+ it 'fetches the catalog_item record' do
67
+ catalog_item = Vra::CatalogItem.allocate
68
+ expect(catalog_item).to receive(:fetch_catalog_item)
69
+ catalog_item.send(:initialize, client, id: catalog_id)
70
+ end
71
+ end
72
+
73
+ context 'when catalog item data is provided' do
74
+ it 'populates the ID correctly' do
75
+ catalog_item = Vra::Resource.new(client, data: catalog_item_payload)
76
+ expect(catalog_item.id).to eq catalog_id
77
+ end
78
+ end
79
+ end
80
+
81
+ describe '#fetch_catalog_item' do
82
+ context 'when the catalog item exists' do
83
+ let(:response) { double('response', code: 200, body: catalog_item_payload.to_json) }
84
+
85
+ it 'calls http_get against the catalog_service' do
86
+ expect(client).to receive(:http_get).with('/catalog-service/api/consumer/catalogItems/catalog-12345').and_return(response)
87
+ Vra::CatalogItem.new(client, id: 'catalog-12345')
88
+ end
89
+ end
90
+
91
+ context 'when the catalog item does not exist' do
92
+ it 'raises an exception' do
93
+ allow(client).to receive(:http_get).with('/catalog-service/api/consumer/catalogItems/catalog-12345').and_raise(Vra::Exception::HTTPNotFound)
94
+ expect { Vra::CatalogItem.new(client, id: 'catalog-12345') }.to raise_error(Vra::Exception::NotFound)
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,124 @@
1
+ #
2
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
3
+ # Copyright:: Copyright (c) 2015 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'spec_helper'
20
+
21
+ describe Vra::CatalogRequest do
22
+ before(:each) do
23
+ catalog_item = double('catalog_item')
24
+ allow(catalog_item).to receive(:blueprint_id).and_return('catalog_blueprint')
25
+ allow(catalog_item).to receive(:tenant_id).and_return('catalog_tenant')
26
+ allow(catalog_item).to receive(:subtenant_id).and_return('catalog_subtenant')
27
+ allow(Vra::CatalogItem).to receive(:new).and_return(catalog_item)
28
+ end
29
+
30
+ let(:client) do
31
+ Vra::Client.new(username: 'user@corp.local',
32
+ password: 'password',
33
+ tenant: 'tenant',
34
+ base_url: 'https://vra.corp.local')
35
+ end
36
+
37
+ context 'when no subtenant ID is provided' do
38
+ let(:request) do
39
+ client.catalog.request('catalog-12345',
40
+ cpus: 2,
41
+ memory: 1024,
42
+ lease_days: 15,
43
+ requested_for: 'tester@corp.local',
44
+ notes: 'test notes')
45
+ end
46
+
47
+ it 'uses the subtenant ID from the catalog item' do
48
+ expect(request.subtenant_id).to eq 'catalog_subtenant'
49
+ end
50
+ end
51
+
52
+ context 'when subtenant is provided, and all shared tests' do
53
+ let(:request) do
54
+ client.catalog.request('catalog-12345',
55
+ cpus: 2,
56
+ memory: 1024,
57
+ lease_days: 15,
58
+ requested_for: 'tester@corp.local',
59
+ notes: 'test notes',
60
+ subtenant_id: 'user_subtenant')
61
+ end
62
+
63
+ describe '#initialize' do
64
+ it 'sets the appropriate instance vars' do
65
+ expect(request.catalog_id).to eq 'catalog-12345'
66
+ expect(request.cpus).to eq 2
67
+ expect(request.memory).to eq 1024
68
+ expect(request.lease_days).to eq 15
69
+ expect(request.requested_for).to eq 'tester@corp.local'
70
+ expect(request.notes).to eq 'test notes'
71
+ expect(request.subtenant_id).to eq 'user_subtenant'
72
+ end
73
+ end
74
+
75
+ describe '#validate_params!' do
76
+ context 'when all required params are provided' do
77
+ it 'does not raise an exception' do
78
+ expect { request.validate_params! }.to_not raise_error
79
+ end
80
+ end
81
+
82
+ context 'when a required parameter is not provided' do
83
+ it 'raises an exception' do
84
+ request.cpus = nil
85
+ expect { request.validate_params! }.to raise_error(ArgumentError)
86
+ end
87
+ end
88
+ end
89
+
90
+ describe '#request_payload' do
91
+ it 'properly handles additional parameters' do
92
+ request.set_parameter('param1', 'string', 'my string')
93
+ request.set_parameter('param2', 'integer', '2468')
94
+
95
+ payload = request.request_payload
96
+ param1 = payload['requestData']['entries'].find { |x| x['key'] == 'param1' }
97
+ param2 = payload['requestData']['entries'].find { |x| x['key'] == 'param2' }
98
+
99
+ expect(param1).to be_a(Hash)
100
+ expect(param2).to be_a(Hash)
101
+ expect(param1['value']['value']).to eq 'my string'
102
+ expect(param2['value']['value']).to eq 2468
103
+ end
104
+ end
105
+
106
+ describe '#submit' do
107
+ before do
108
+ allow(request).to receive(:request_payload).and_return({})
109
+ response = double('response', code: 200, headers: { location: '/requests/request-12345' })
110
+ allow(client).to receive(:http_post).with('/catalog-service/api/consumer/requests', '{}').and_return(response)
111
+ end
112
+
113
+ it 'calls http_post' do
114
+ expect(client).to receive(:http_post).with('/catalog-service/api/consumer/requests', '{}')
115
+
116
+ request.submit
117
+ end
118
+
119
+ it 'returns a Vra::Request object' do
120
+ expect(request.submit).to be_an_instance_of(Vra::Request)
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,124 @@
1
+ #
2
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
3
+ # Copyright:: Copyright (c) 2015 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'spec_helper'
20
+
21
+ describe Vra::Catalog do
22
+ let(:client) do
23
+ Vra::Client.new(username: 'user@corp.local',
24
+ password: 'password',
25
+ tenant: 'tenant',
26
+ base_url: 'https://vra.corp.local')
27
+ end
28
+
29
+ let(:catalog_item) do
30
+ {
31
+ '@type' => 'CatalogItem',
32
+ 'id' => 'a9cd6148-6e0b-4a80-ac47-f5255c52b43d',
33
+ 'version' => 2,
34
+ 'name' => 'CentOS 6.6',
35
+ 'description' => 'Blueprint for deploying a CentOS Linux development server',
36
+ 'status' => 'PUBLISHED',
37
+ 'statusName' => 'Published',
38
+ 'organization' => {
39
+ 'tenantRef' => 'vsphere.local',
40
+ 'tenantLabel' => 'vsphere.local',
41
+ 'subtenantRef' => nil,
42
+ 'subtenantLabel' => nil
43
+ },
44
+ 'providerBinding' => {
45
+ 'bindingId' => '33af5413-4f20-4b3b-8268-32edad434dfb',
46
+ 'providerRef' => {
47
+ 'id' => 'c3b2bc30-47b0-454f-b57d-df02a7356fe6',
48
+ 'label' => 'iaas-service'
49
+ }
50
+ }
51
+ }
52
+ end
53
+
54
+ let(:entitled_catalog_item) do
55
+ {
56
+ '@type' => 'ConsumerEntitledCatalogItem',
57
+ 'catalogItem' => {
58
+ 'id' => 'd29efd6b-3cd6-4f8d-b1d8-da4ddd4e52b1',
59
+ 'version' => 2,
60
+ 'name' => 'WindowsServer2012',
61
+ 'description' => 'Windows Server 2012 with the latest updates and patches.',
62
+ 'status' => 'PUBLISHED',
63
+ 'statusName' => 'Published',
64
+ 'organization' => {
65
+ 'tenantRef' => 'vsphere.local',
66
+ 'tenantLabel' => 'vsphere.local',
67
+ 'subtenantRef' => nil,
68
+ 'subtenantLabel' => nil
69
+ },
70
+ 'providerBinding' => {
71
+ 'bindingId' => '59fd02a1-acca-4918-9d3d-2298d310caef',
72
+ 'providerRef' => {
73
+ 'id' => 'c3b2bc30-47b0-454f-b57d-df02a7356fe6',
74
+ 'label' => 'iaas-service'
75
+ }
76
+ }
77
+ }
78
+ }
79
+ end
80
+
81
+ describe '#all_items' do
82
+ it 'calls the catalogItems endpoint' do
83
+ expect(client).to receive(:http_get_paginated_array!).with('/catalog-service/api/consumer/catalogItems')
84
+ .and_return([ catalog_item ])
85
+
86
+ client.catalog.all_items
87
+ end
88
+
89
+ it 'returns a Vra::CatalogItem object' do
90
+ allow(client).to receive(:http_get_paginated_array!).with('/catalog-service/api/consumer/catalogItems')
91
+ .and_return([ catalog_item ])
92
+
93
+ items = client.catalog.all_items
94
+
95
+ expect(items.first).to be_an_instance_of(Vra::CatalogItem)
96
+ end
97
+ end
98
+
99
+ describe '#entitled_items' do
100
+ it 'calls the entitledCatalogItems endpoint' do
101
+ expect(client).to receive(:http_get_paginated_array!).with('/catalog-service/api/consumer/entitledCatalogItems')
102
+ .and_return([ entitled_catalog_item ])
103
+
104
+ client.catalog.entitled_items
105
+ end
106
+
107
+ it 'returns a Vra::CatalogItem object' do
108
+ allow(client).to receive(:http_get_paginated_array!).with('/catalog-service/api/consumer/entitledCatalogItems')
109
+ .and_return([ entitled_catalog_item ])
110
+
111
+ items = client.catalog.entitled_items
112
+
113
+ expect(items.first).to be_an_instance_of(Vra::CatalogItem)
114
+ end
115
+ end
116
+
117
+ describe '#request' do
118
+ it 'returns a new Vra::CatalogRequest object' do
119
+ allow(Vra::CatalogItem).to receive(:new)
120
+ request = client.catalog.request('blueprint-1', cpus: 2)
121
+ expect(request).to be_an_instance_of(Vra::CatalogRequest)
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,466 @@
1
+ #
2
+ # Author:: Chef Partner Engineering (<partnereng@chef.io>)
3
+ # Copyright:: Copyright (c) 2015 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'spec_helper'
20
+
21
+ describe Vra::Client do
22
+ let(:client) do
23
+ Vra::Client.new(username: 'user@corp.local',
24
+ password: 'password',
25
+ tenant: 'tenant',
26
+ base_url: 'https://vra.corp.local')
27
+ end
28
+
29
+ describe '#initialize' do
30
+ it 'calls validate_client_options!' do
31
+ client = Vra::Client.allocate
32
+ expect(client).to receive(:validate_client_options!)
33
+ client.send(:initialize, username: 'user@corp.local',
34
+ password: 'password',
35
+ tenant: 'tenant',
36
+ base_url: 'https://vra.corp.local')
37
+ end
38
+ end
39
+
40
+ describe '#request_headers' do
41
+ context 'when bearer token exists' do
42
+ it 'has an Authorization header' do
43
+ client.bearer_token = '12345'
44
+ expect(client.request_headers.key?('Authorization')).to be true
45
+ end
46
+ end
47
+
48
+ context 'when bearer token does not exist' do
49
+ it 'has an Authorization header' do
50
+ expect(client.request_headers.key?('Authorization')).to be false
51
+ end
52
+ end
53
+ end
54
+
55
+ describe '#authorize!' do
56
+ context 'when a token is not authorized or no token exists' do
57
+ it 'generates a token successfully' do
58
+ allow(client).to receive(:authorized?).twice.and_return(false, true)
59
+ expect(client).to receive(:generate_bearer_token)
60
+
61
+ client.authorize!
62
+ end
63
+
64
+ it 'raises an exception if token generation fails' do
65
+ allow(client).to receive(:authorized?).and_return(false)
66
+ allow(client).to receive(:generate_bearer_token).and_raise(Vra::Exception::Unauthorized)
67
+
68
+ expect { client.authorize! }.to raise_error(Vra::Exception::Unauthorized)
69
+ end
70
+
71
+ it 'raises an exception if a generated token is unauthorized' do
72
+ allow(client).to receive(:authorized).twice.and_return(false, false)
73
+ allow(client).to receive(:generate_bearer_token)
74
+
75
+ expect { client.authorize! }.to raise_error(Vra::Exception::Unauthorized)
76
+ end
77
+ end
78
+
79
+ context 'when a token is authorized' do
80
+ it 'does not generate a new token' do
81
+ allow(client).to receive(:authorized?).and_return(true)
82
+ expect(client).to_not receive(:generate_bearer_token)
83
+
84
+ client.authorize!
85
+ end
86
+ end
87
+ end
88
+
89
+ describe '#authorized?' do
90
+ context 'when token does not exist' do
91
+ it 'returns false' do
92
+ expect(client.authorized?).to be false
93
+ end
94
+ end
95
+
96
+ context 'when token exists' do
97
+ before(:each) do
98
+ client.bearer_token = '12345'
99
+ end
100
+
101
+ url = '/identity/api/tokens/12345'
102
+
103
+ it 'returns true if the token validates successfully' do
104
+ response = double('response')
105
+ allow(response).to receive(:code).and_return(204)
106
+ allow(client).to receive(:http_head).with(url, :skip_auth).and_return(response)
107
+
108
+ expect(client.authorized?).to be true
109
+ end
110
+
111
+ it 'returns false if the token validates unsuccessfully' do
112
+ response = double('response')
113
+ allow(response).to receive(:code).and_return(500)
114
+ allow(client).to receive(:http_head).with(url, :skip_auth).and_return(response)
115
+
116
+ expect(client.authorized?).to be false
117
+ end
118
+ end
119
+ end
120
+
121
+ describe '#generate_bearer_token' do
122
+ payload = {
123
+ 'username' => 'user@corp.local',
124
+ 'password' => 'password',
125
+ 'tenant' => 'tenant'
126
+ }.to_json
127
+
128
+ it 'posts to the tokens API endpoint' do
129
+ response = double('response')
130
+ allow(response).to receive(:code).and_return(200)
131
+ allow(response).to receive(:body).and_return('{"id":"12345"}')
132
+ expect(client).to receive(:http_post).with('/identity/api/tokens',
133
+ payload,
134
+ :skip_auth).and_return(response)
135
+
136
+ client.generate_bearer_token
137
+ end
138
+
139
+ context 'when token is generated successfully' do
140
+ it 'sets the token' do
141
+ response = double('response')
142
+ allow(response).to receive(:code).and_return(200)
143
+ allow(response).to receive(:body).and_return('{"id":"12345"}')
144
+ allow(client).to receive(:http_post).with('/identity/api/tokens',
145
+ payload,
146
+ :skip_auth).and_return(response)
147
+
148
+ client.generate_bearer_token
149
+
150
+ expect(client.bearer_token).to eq '12345'
151
+ end
152
+ end
153
+
154
+ context 'when token is not generated successfully' do
155
+ it 'raises an exception' do
156
+ response = double('response')
157
+ allow(response).to receive(:code).and_return(500)
158
+ allow(response).to receive(:body).and_return('error string')
159
+ allow(client).to receive(:http_post).with('/identity/api/tokens',
160
+ payload,
161
+ :skip_auth)
162
+ .and_return(response)
163
+
164
+ expect { client.generate_bearer_token }.to raise_error(Vra::Exception::Unauthorized)
165
+ end
166
+ end
167
+ end
168
+
169
+ describe '#full_url' do
170
+ it 'returns a properly formatted url' do
171
+ expect(client.full_url('/mypath')).to eq 'https://vra.corp.local/mypath'
172
+ end
173
+ end
174
+
175
+ describe '#http_head' do
176
+ context 'when skip_auth is nil' do
177
+ it 'authorizes before proceeding' do
178
+ response = double('response')
179
+ allow(RestClient::Request).to receive(:execute).and_return(response)
180
+ expect(client).to receive(:authorize!)
181
+
182
+ client.http_head('/test')
183
+ end
184
+ end
185
+
186
+ context 'when skip_auth is not nil' do
187
+ it 'does not authorize before proceeding' do
188
+ response = double('response')
189
+ allow(RestClient::Request).to receive(:execute).and_return(response)
190
+ expect(client).to_not receive(:authorize!)
191
+
192
+ client.http_head('/test', :skip_auth)
193
+ end
194
+ end
195
+
196
+ it 'calls RestClient::Request#execute' do
197
+ response = double('response')
198
+ path = '/test'
199
+ full_url = 'https://vra.corp.local/test'
200
+ headers = { 'Accept' => 'application/json', 'Content-Type' => 'application/json' }
201
+ verify_ssl = true
202
+
203
+ allow(client).to receive(:authorize!)
204
+ expect(RestClient::Request).to receive(:execute).with(method: :head,
205
+ url: full_url,
206
+ headers: headers,
207
+ verify_ssl: verify_ssl)
208
+ .and_return(response)
209
+
210
+ client.http_head(path)
211
+ end
212
+ end
213
+
214
+ describe '#http_get' do
215
+ context 'when skip_auth is nil' do
216
+ it 'authorizes before proceeding' do
217
+ response = double('response')
218
+ allow(RestClient::Request).to receive(:execute).and_return(response)
219
+ expect(client).to receive(:authorize!)
220
+
221
+ client.http_get('/test')
222
+ end
223
+ end
224
+
225
+ context 'when skip_auth is not nil' do
226
+ it 'does not authorize before proceeding' do
227
+ response = double('response')
228
+ allow(RestClient::Request).to receive(:execute).and_return(response)
229
+ expect(client).to_not receive(:authorize!)
230
+
231
+ client.http_get('/test', :skip_auth)
232
+ end
233
+ end
234
+
235
+ it 'calls RestClient::Request#execute' do
236
+ response = double('response')
237
+ path = '/test'
238
+ full_url = 'https://vra.corp.local/test'
239
+ headers = { 'Accept' => 'application/json', 'Content-Type' => 'application/json' }
240
+ verify_ssl = true
241
+
242
+ allow(client).to receive(:authorize!)
243
+ expect(RestClient::Request).to receive(:execute).with(method: :get,
244
+ url: full_url,
245
+ headers: headers,
246
+ verify_ssl: verify_ssl)
247
+ .and_return(response)
248
+
249
+ client.http_get(path)
250
+ end
251
+
252
+ it 'calls raise_http_exception upon a RestClient error' do
253
+ allow(client).to receive(:authorize!)
254
+ allow(RestClient::Request).to receive(:execute).and_raise(RestClient::ResourceNotFound)
255
+ expect(client).to receive(:raise_http_exception)
256
+
257
+ client.http_get('/404')
258
+ end
259
+ end
260
+
261
+ describe '#http_get!' do
262
+ it 'returns the response body' do
263
+ response = double('response', body: 'body text')
264
+ allow(client).to receive(:http_get).with('/test').and_return(response)
265
+
266
+ expect(client.http_get!('/test')).to eq 'body text'
267
+ end
268
+ end
269
+
270
+ describe '#http_get_paginated_array!' do
271
+ it 'allows a limit override' do
272
+ expect(client).to receive(:http_get!)
273
+ .with('/test?limit=10&page=1')
274
+ .and_return({ 'content' => [], 'metadata' => { 'totalPages' => 1 } }.to_json)
275
+
276
+ client.http_get_paginated_array!('/test', 10)
277
+ end
278
+
279
+ it 'only calls http_get! once when total pages is 0 (no items)' do
280
+ expect(client).to receive(:http_get!)
281
+ .once
282
+ .with('/test?limit=20&page=1')
283
+ .and_return({ 'content' => [], 'metadata' => { 'totalPages' => 0 } }.to_json)
284
+
285
+ client.http_get_paginated_array!('/test')
286
+ end
287
+
288
+ it 'only calls http_get! once when total pages is 1' do
289
+ expect(client).to receive(:http_get!)
290
+ .once
291
+ .with('/test?limit=20&page=1')
292
+ .and_return({ 'content' => [], 'metadata' => { 'totalPages' => 1 } }.to_json)
293
+
294
+ client.http_get_paginated_array!('/test')
295
+ end
296
+
297
+ it 'calls http_get! 3 times if there are 3 pages of response' do
298
+ expect(client).to receive(:http_get!)
299
+ .with('/test?limit=20&page=1')
300
+ .and_return({ 'content' => [], 'metadata' => { 'totalPages' => 3 } }.to_json)
301
+ expect(client).to receive(:http_get!)
302
+ .with('/test?limit=20&page=2')
303
+ .and_return({ 'content' => [], 'metadata' => { 'totalPages' => 3 } }.to_json)
304
+ expect(client).to receive(:http_get!)
305
+ .with('/test?limit=20&page=3')
306
+ .and_return({ 'content' => [], 'metadata' => { 'totalPages' => 3 } }.to_json)
307
+
308
+ client.http_get_paginated_array!('/test')
309
+ end
310
+ end
311
+
312
+ describe '#http_post' do
313
+ context 'when skip_auth is nil' do
314
+ it 'authorizes before proceeding' do
315
+ response = double('response')
316
+ allow(RestClient::Request).to receive(:execute).and_return(response)
317
+ expect(client).to receive(:authorize!)
318
+
319
+ client.http_post('/test', 'some payload')
320
+ end
321
+ end
322
+
323
+ context 'when skip_auth is not nil' do
324
+ it 'does not authorize before proceeding' do
325
+ response = double('response')
326
+ allow(RestClient::Request).to receive(:execute).and_return(response)
327
+ expect(client).to_not receive(:authorize!)
328
+
329
+ client.http_post('/test', 'some payload', :skip_auth)
330
+ end
331
+ end
332
+
333
+ it 'calls RestClient::Request#execute' do
334
+ response = double('response')
335
+ path = '/test'
336
+ full_url = 'https://vra.corp.local/test'
337
+ headers = { 'Accept' => 'application/json', 'Content-Type' => 'application/json' }
338
+ payload = 'some payload'
339
+ verify_ssl = true
340
+
341
+ allow(client).to receive(:authorize!)
342
+ expect(RestClient::Request).to receive(:execute).with(method: :post,
343
+ url: full_url,
344
+ headers: headers,
345
+ payload: payload,
346
+ verify_ssl: verify_ssl)
347
+ .and_return(response)
348
+
349
+ client.http_post(path, payload)
350
+ end
351
+
352
+ it 'calls raise_http_exception upon a RestClient error' do
353
+ allow(client).to receive(:authorize!)
354
+ allow(RestClient::Request).to receive(:execute).and_raise(RestClient::ResourceNotFound)
355
+ expect(client).to receive(:raise_http_exception)
356
+
357
+ client.http_post('/404', 'test payload')
358
+ end
359
+ end
360
+
361
+ describe '#http_post!' do
362
+ it 'returns the response body' do
363
+ response = double('response', body: 'body text')
364
+ allow(client).to receive(:http_post).with('/test', 'test payload').and_return(response)
365
+
366
+ expect(client.http_post!('/test', 'test payload')).to eq 'body text'
367
+ end
368
+ end
369
+
370
+ describe '#raise_http_exception' do
371
+ context 'when a 404 is received' do
372
+ let(:exception) do
373
+ double('RestClient::ResourceNotFound',
374
+ http_code: 404,
375
+ message: 'Not Found',
376
+ response: '404 Not Found')
377
+ end
378
+
379
+ it 'raises a Vra::Exception::HTTPNotFound exception' do
380
+ expect { client.raise_http_exception(exception, '/test') }.to raise_error(Vra::Exception::HTTPNotFound)
381
+ end
382
+ end
383
+
384
+ context 'when an unspecified http error is received' do
385
+ let(:exception) do
386
+ double('RestClient::BadRequest',
387
+ http_code: 400,
388
+ message: 'Bad Request',
389
+ response: '400 Bad Request')
390
+ end
391
+
392
+ it 'raises a Vra::Exception::HTTPError exception' do
393
+ expect { client.raise_http_exception(exception, '/test') }.to raise_error(Vra::Exception::HTTPError)
394
+ end
395
+ end
396
+ end
397
+
398
+ describe '#validate_client_options!' do
399
+ context 'when all required options are supplied' do
400
+ it 'does not raise an exception' do
401
+ expect { client.validate_client_options! }.not_to raise_error(ArgumentError)
402
+ end
403
+ end
404
+
405
+ context 'when username is missing' do
406
+ let(:client) do
407
+ Vra::Client.new(password: 'password',
408
+ tenant: 'tenant',
409
+ base_url: 'https://vra.corp.local')
410
+ end
411
+
412
+ it 'raises an exception' do
413
+ expect { client.validate_client_options! }.to raise_error(ArgumentError)
414
+ end
415
+ end
416
+
417
+ context 'when password is missing' do
418
+ let(:client) do
419
+ Vra::Client.new(username: 'username',
420
+ tenant: 'tenant',
421
+ base_url: 'https://vra.corp.local')
422
+ end
423
+
424
+ it 'raises an exception' do
425
+ expect { client.validate_client_options! }.to raise_error(ArgumentError)
426
+ end
427
+ end
428
+
429
+ context 'when tenant is missing' do
430
+ let(:client) do
431
+ Vra::Client.new(username: 'username',
432
+ password: 'password',
433
+ base_url: 'https://vra.corp.local')
434
+ end
435
+
436
+ it 'raises an exception' do
437
+ expect { client.validate_client_options! }.to raise_error(ArgumentError)
438
+ end
439
+ end
440
+
441
+ context 'when base URL is missing' do
442
+ let(:client) do
443
+ Vra::Client.new(username: 'username',
444
+ password: 'password',
445
+ tenant: 'tenant')
446
+ end
447
+
448
+ it 'raises an exception' do
449
+ expect { client.validate_client_options! }.to raise_error(ArgumentError)
450
+ end
451
+ end
452
+
453
+ context 'when base URL is not a valid HTTP URL' do
454
+ let(:client) do
455
+ Vra::Client.new(username: 'username',
456
+ password: 'password',
457
+ tenant: 'tenant',
458
+ base_url: 'something-that-is-not-a-HTTP-URI')
459
+ end
460
+
461
+ it 'raises an exception' do
462
+ expect { client.validate_client_options! }.to raise_error(ArgumentError)
463
+ end
464
+ end
465
+ end
466
+ end