vmware-vra 1.0.0.rc1

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