cloudstack_client 1.6.0 → 2.0.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.
@@ -0,0 +1,85 @@
1
+ require "json"
2
+
3
+ # Helpers for stubbing CloudStack HTTP responses.
4
+ #
5
+ # Every CloudStack request carries a generated `signature` query parameter, so
6
+ # stubs match on the `command` parameter with `hash_including` rather than on a
7
+ # full URL.
8
+ module ApiStubs
9
+ API_ENDPOINT = "https://cloudstack.test/client/api".freeze
10
+
11
+ # WebMock matches a bare URL only against requests with no query string, and
12
+ # every CloudStack request carries one. Match on the endpoint prefix instead.
13
+ ANY_REQUEST = %r{\Ahttps://cloudstack\.test/client/api}.freeze
14
+
15
+ # Stub a CloudStack command with an already-shaped response body.
16
+ #
17
+ # stub_command("listZones", "listzonesresponse" => { "count" => 1, ... })
18
+ def stub_command(command, body, status: 200)
19
+ stub_request(:get, API_ENDPOINT)
20
+ .with(query: hash_including("command" => command))
21
+ .to_return(
22
+ status: status,
23
+ body: JSON.generate(body),
24
+ headers: { "Content-Type" => "application/json" }
25
+ )
26
+ end
27
+
28
+ # Stub a command using CloudStack's usual envelope:
29
+ # { "<command>response" => { "count" => n, "<entity>" => [...] } }
30
+ def stub_list(command, entity, items, count: nil, extra: {})
31
+ inner = { "count" => count || items.size, entity => items }.merge(extra)
32
+ stub_command(command, { "#{command.downcase}response" => inner })
33
+ end
34
+
35
+ # Stub the initial async command, then the queryAsyncJobResult polls.
36
+ #
37
+ # `polls` is an array of jobstatus payloads returned in order.
38
+ def stub_async(command, job_id: "job-1", polls: [])
39
+ stub_command(command, { "#{command.downcase}response" => { "jobid" => job_id } })
40
+
41
+ responses = polls.map do |poll|
42
+ {
43
+ status: 200,
44
+ body: JSON.generate("queryasyncjobresultresponse" => poll),
45
+ headers: { "Content-Type" => "application/json" }
46
+ }
47
+ end
48
+
49
+ stub_request(:get, API_ENDPOINT)
50
+ .with(query: hash_including("command" => "queryAsyncJobResult"))
51
+ .to_return(*responses)
52
+ end
53
+
54
+ # A completed async job payload.
55
+ def job_success(result)
56
+ { "jobstatus" => 1, "jobresult" => result }
57
+ end
58
+
59
+ # A failed async job payload.
60
+ def job_failure(errortext, code: 530)
61
+ {
62
+ "jobstatus" => 2,
63
+ "jobresultcode" => code,
64
+ "jobresult" => { "errortext" => errortext }
65
+ }
66
+ end
67
+
68
+ # An in-flight async job payload.
69
+ def job_pending
70
+ { "jobstatus" => 0 }
71
+ end
72
+
73
+ # Parsed query parameters of the most recent request, for asserting on how
74
+ # a request was built rather than only on what came back.
75
+ def last_request_params
76
+ signature = WebMock::RequestRegistry.instance.requested_signatures.hash.keys.last
77
+ raise "no request was made" if signature.nil?
78
+
79
+ # CGI.parse was removed in Ruby 4.0; only CGI.escape/unescape remain.
80
+ URI.decode_www_form(signature.uri.query.to_s)
81
+ .each_with_object(Hash.new { |hash, key| hash[key] = [] }) do |(key, value), acc|
82
+ acc[key] << value
83
+ end
84
+ end
85
+ end
data/test/test_helper.rb CHANGED
@@ -2,4 +2,41 @@ require "cloudstack_client"
2
2
 
3
3
  require "minitest/spec"
4
4
  require "minitest/autorun"
5
- require "minitest/pride"
5
+ # Minitest 6 extracted Object#stub into the minitest-mock gem.
6
+ require "minitest/mock"
7
+ require "minitest/reporters"
8
+
9
+ require "webmock/minitest"
10
+
11
+ # No test may reach the network. An accidental live request should fail loudly
12
+ # rather than turn into a slow or flaky test.
13
+ WebMock.disable_net_connect!(allow_localhost: false)
14
+
15
+ Minitest::Reporters.use!(
16
+ ENV["CI"] ? Minitest::Reporters::ProgressReporter.new : Minitest::Reporters::SpecReporter.new
17
+ )
18
+
19
+ require_relative "support/api_stubs"
20
+
21
+ module TestHelpers
22
+ TEST_URL = "https://cloudstack.test/client/api".freeze
23
+ TEST_KEY = "test-key".freeze
24
+ TEST_SECRET = "test-secret".freeze
25
+
26
+ def fixture_path(*parts)
27
+ File.join(File.expand_path("data", __dir__), *parts)
28
+ end
29
+
30
+ # A client with no dynamically defined API methods: useful when the test
31
+ # targets Connection behaviour rather than the generated command methods.
32
+ def bare_client(options = {})
33
+ CloudstackClient::Client.new(
34
+ TEST_URL, TEST_KEY, TEST_SECRET, { no_api_methods: true }.merge(options)
35
+ )
36
+ end
37
+ end
38
+
39
+ class Minitest::Spec
40
+ include TestHelpers
41
+ include ApiStubs
42
+ end
@@ -0,0 +1,62 @@
1
+ require "test_helper"
2
+
3
+ describe CloudstackClient::Utils do
4
+ let(:utils) { Object.new.extend(CloudstackClient::Utils) }
5
+
6
+ describe "camel_case_to_underscore" do
7
+ it "converts a simple camel case command" do
8
+ _(utils.camel_case_to_underscore("listVirtualMachines"))
9
+ .must_equal "list_virtual_machines"
10
+ end
11
+
12
+ it "splits a leading acronym from the following word" do
13
+ _(utils.camel_case_to_underscore("listVPCOfferings"))
14
+ .must_equal "list_vpc_offerings"
15
+ end
16
+
17
+ it "handles a trailing acronym" do
18
+ _(utils.camel_case_to_underscore("listOsTypes")).must_equal "list_os_types"
19
+ end
20
+
21
+ it "handles consecutive capitals followed by a word" do
22
+ _(utils.camel_case_to_underscore("createSSHKeyPair"))
23
+ .must_equal "create_ssh_key_pair"
24
+ end
25
+
26
+ it "converts hyphens to underscores" do
27
+ _(utils.camel_case_to_underscore("some-command")).must_equal "some_command"
28
+ end
29
+
30
+ it "leaves an already underscored name unchanged" do
31
+ _(utils.camel_case_to_underscore("list_apis")).must_equal "list_apis"
32
+ end
33
+ end
34
+
35
+ describe "underscore_to_camel_case" do
36
+ it "converts an underscored name" do
37
+ _(utils.underscore_to_camel_case("list_virtual_machines"))
38
+ .must_equal "listVirtualMachines"
39
+ end
40
+
41
+ it "returns a name without underscores unchanged" do
42
+ _(utils.underscore_to_camel_case("listApis")).must_equal "listApis"
43
+ end
44
+
45
+ it "cannot restore acronym casing, because the conversion is lossy" do
46
+ # Documents a known limitation: "ssh" carries no record of "SSH".
47
+ # Api resolves underscored names through an index instead of relying
48
+ # on this method being a true inverse.
49
+ _(utils.underscore_to_camel_case("create_ssh_key_pair"))
50
+ .must_equal "createSshKeyPair"
51
+ end
52
+ end
53
+
54
+ describe "round tripping real command names" do
55
+ it "round trips names without acronyms" do
56
+ %w[listVirtualMachines deployVirtualMachine listOsTypes listApis].each do |name|
57
+ underscored = utils.camel_case_to_underscore(name)
58
+ _(utils.underscore_to_camel_case(underscored)).must_equal name
59
+ end
60
+ end
61
+ end
62
+ end
metadata CHANGED
@@ -1,15 +1,28 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloudstack_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nik Wolfgramm
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2026-04-16 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: base64
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0.1'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0.1'
13
26
  - !ruby/object:Gem::Dependency
14
27
  name: rake
15
28
  requirement: !ruby/object:Gem::Requirement
@@ -56,17 +69,108 @@ dependencies:
56
69
  name: minitest
57
70
  requirement: !ruby/object:Gem::Requirement
58
71
  requirements:
59
- - - "~>"
72
+ - - ">="
60
73
  - !ruby/object:Gem::Version
61
74
  version: '5.14'
75
+ - - "<"
76
+ - !ruby/object:Gem::Version
77
+ version: '7'
62
78
  type: :development
63
79
  prerelease: false
64
80
  version_requirements: !ruby/object:Gem::Requirement
65
81
  requirements:
66
- - - "~>"
82
+ - - ">="
67
83
  - !ruby/object:Gem::Version
68
84
  version: '5.14'
69
- description: CloudStack API client written in Ruby
85
+ - - "<"
86
+ - !ruby/object:Gem::Version
87
+ version: '7'
88
+ - !ruby/object:Gem::Dependency
89
+ name: minitest-reporters
90
+ requirement: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '1.8'
95
+ type: :development
96
+ prerelease: false
97
+ version_requirements: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '1.8'
102
+ - !ruby/object:Gem::Dependency
103
+ name: webmock
104
+ requirement: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: '3.26'
109
+ type: :development
110
+ prerelease: false
111
+ version_requirements: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - "~>"
114
+ - !ruby/object:Gem::Version
115
+ version: '3.26'
116
+ - !ruby/object:Gem::Dependency
117
+ name: benchmark
118
+ requirement: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0.3'
123
+ type: :development
124
+ prerelease: false
125
+ version_requirements: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0.3'
130
+ - !ruby/object:Gem::Dependency
131
+ name: rubocop
132
+ requirement: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - "~>"
135
+ - !ruby/object:Gem::Version
136
+ version: '1.90'
137
+ type: :development
138
+ prerelease: false
139
+ version_requirements: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - "~>"
142
+ - !ruby/object:Gem::Version
143
+ version: '1.90'
144
+ - !ruby/object:Gem::Dependency
145
+ name: rubocop-minitest
146
+ requirement: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - "~>"
149
+ - !ruby/object:Gem::Version
150
+ version: '0.40'
151
+ type: :development
152
+ prerelease: false
153
+ version_requirements: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - "~>"
156
+ - !ruby/object:Gem::Version
157
+ version: '0.40'
158
+ - !ruby/object:Gem::Dependency
159
+ name: rubocop-rake
160
+ requirement: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - "~>"
163
+ - !ruby/object:Gem::Version
164
+ version: '0.7'
165
+ type: :development
166
+ prerelease: false
167
+ version_requirements: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - "~>"
170
+ - !ruby/object:Gem::Version
171
+ version: '0.7'
172
+ description: A Ruby client for the Apache CloudStack API that builds its methods dynamically
173
+ from the CloudStack API definition, including an interactive console.
70
174
  email:
71
175
  - nik.wolfgramm@gmail.com
72
176
  executables:
@@ -74,12 +178,14 @@ executables:
74
178
  extensions: []
75
179
  extra_rdoc_files: []
76
180
  files:
181
+ - ".github/dependabot.yml"
77
182
  - ".github/workflows/ci.yml"
78
183
  - ".github/workflows/release.yml"
79
184
  - ".gitignore"
80
- - ".travis.yml"
185
+ - ".rubocop.yml"
186
+ - ".rubocop_todo.yml"
187
+ - CHANGELOG.md
81
188
  - Gemfile
82
- - Gemfile.lock
83
189
  - LICENSE.txt
84
190
  - README.md
85
191
  - Rakefile
@@ -93,22 +199,33 @@ files:
93
199
  - lib/cloudstack_client/configuration.rb
94
200
  - lib/cloudstack_client/connection.rb
95
201
  - lib/cloudstack_client/error.rb
202
+ - lib/cloudstack_client/request_handling.rb
96
203
  - lib/cloudstack_client/utils.rb
97
204
  - lib/cloudstack_client/version.rb
98
205
  - test/api_test.rb
99
206
  - test/benchmark.rb
100
207
  - test/client_test.rb
101
208
  - test/configuration_test.rb
209
+ - test/connection_test.rb
102
210
  - test/data/0.42.json
103
211
  - test/data/0.42.json.gz
104
212
  - test/data/cloudstack-1.yml
105
213
  - test/data/cloudstack-2.yml
214
+ - test/data/cloudstack-empty.yml
215
+ - test/data/cloudstack-incomplete.yml
216
+ - test/data/cloudstack-malformed.yml
217
+ - test/data/cloudstack-unsafe.yml
218
+ - test/support/api_stubs.rb
106
219
  - test/test_helper.rb
220
+ - test/utils_test.rb
107
221
  homepage: https://github.com/niwo/cloudstack_client
108
222
  licenses:
109
223
  - MIT
110
- metadata: {}
111
- post_install_message:
224
+ metadata:
225
+ homepage_uri: https://github.com/niwo/cloudstack_client
226
+ bug_tracker_uri: https://github.com/niwo/cloudstack_client/issues
227
+ changelog_uri: https://github.com/niwo/cloudstack_client/releases
228
+ rubygems_mfa_required: 'true'
112
229
  rdoc_options:
113
230
  - "--line-numbers"
114
231
  - "--inline-source"
@@ -118,24 +235,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
118
235
  requirements:
119
236
  - - ">="
120
237
  - !ruby/object:Gem::Version
121
- version: 1.9.3
238
+ version: '3.0'
122
239
  required_rubygems_version: !ruby/object:Gem::Requirement
123
240
  requirements:
124
241
  - - ">="
125
242
  - !ruby/object:Gem::Version
126
243
  version: '0'
127
244
  requirements: []
128
- rubygems_version: 3.4.19
129
- signing_key:
245
+ rubygems_version: 4.0.18
130
246
  specification_version: 4
131
247
  summary: CloudStack API client written in Ruby
132
- test_files:
133
- - test/api_test.rb
134
- - test/benchmark.rb
135
- - test/client_test.rb
136
- - test/configuration_test.rb
137
- - test/data/0.42.json
138
- - test/data/0.42.json.gz
139
- - test/data/cloudstack-1.yml
140
- - test/data/cloudstack-2.yml
141
- - test/test_helper.rb
248
+ test_files: []
data/.travis.yml DELETED
@@ -1,8 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 3.2
4
- - 3.4
5
- - 4.0
6
- before_install:
7
- - gem update --system
8
- - gem install bundler
data/Gemfile.lock DELETED
@@ -1,27 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- cloudstack_client (1.6.0)
5
-
6
- GEM
7
- remote: https://rubygems.org/
8
- specs:
9
- bond (0.5.1)
10
- minitest (5.27.0)
11
- rake (13.4.1)
12
- ripl (0.7.1)
13
- bond (~> 0.5.1)
14
- thor (1.5.0)
15
-
16
- PLATFORMS
17
- x86_64-linux
18
-
19
- DEPENDENCIES
20
- cloudstack_client!
21
- minitest (~> 5.14)
22
- rake (~> 13.0)
23
- ripl (~> 0.7)
24
- thor (~> 1.1)
25
-
26
- BUNDLED WITH
27
- 2.6.9