cuboid 0.0.0 → 0.0.1alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (221) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +0 -0
  3. data/Gemfile +20 -5
  4. data/LICENSE.md +22 -0
  5. data/README.md +158 -19
  6. data/Rakefile +56 -3
  7. data/config/paths.yml +15 -0
  8. data/cuboid.gemspec +61 -23
  9. data/lib/cuboid.rb +96 -4
  10. data/lib/cuboid/application.rb +326 -0
  11. data/lib/cuboid/application/parts/data.rb +18 -0
  12. data/lib/cuboid/application/parts/report.rb +29 -0
  13. data/lib/cuboid/application/parts/state.rb +274 -0
  14. data/lib/cuboid/application/runtime.rb +25 -0
  15. data/lib/cuboid/banner.rb +13 -0
  16. data/lib/cuboid/data.rb +86 -0
  17. data/lib/cuboid/data/application.rb +52 -0
  18. data/lib/cuboid/error.rb +9 -0
  19. data/lib/cuboid/option_group.rb +129 -0
  20. data/lib/cuboid/option_groups.rb +8 -0
  21. data/lib/cuboid/option_groups/datastore.rb +23 -0
  22. data/lib/cuboid/option_groups/dispatcher.rb +38 -0
  23. data/lib/cuboid/option_groups/output.rb +14 -0
  24. data/lib/cuboid/option_groups/paths.rb +184 -0
  25. data/lib/cuboid/option_groups/report.rb +39 -0
  26. data/lib/cuboid/option_groups/rpc.rb +105 -0
  27. data/lib/cuboid/option_groups/scheduler.rb +27 -0
  28. data/lib/cuboid/option_groups/snapshot.rb +13 -0
  29. data/lib/cuboid/option_groups/system.rb +10 -0
  30. data/lib/cuboid/options.rb +254 -0
  31. data/lib/cuboid/processes.rb +13 -0
  32. data/lib/cuboid/processes/dispatchers.rb +140 -0
  33. data/lib/cuboid/processes/executables/base.rb +54 -0
  34. data/lib/cuboid/processes/executables/dispatcher.rb +5 -0
  35. data/lib/cuboid/processes/executables/instance.rb +12 -0
  36. data/lib/cuboid/processes/executables/rest_service.rb +13 -0
  37. data/lib/cuboid/processes/executables/scheduler.rb +5 -0
  38. data/lib/cuboid/processes/helpers.rb +4 -0
  39. data/lib/cuboid/processes/helpers/dispatchers.rb +23 -0
  40. data/lib/cuboid/processes/helpers/instances.rb +39 -0
  41. data/lib/cuboid/processes/helpers/processes.rb +23 -0
  42. data/lib/cuboid/processes/helpers/schedulers.rb +23 -0
  43. data/lib/cuboid/processes/instances.rb +203 -0
  44. data/lib/cuboid/processes/manager.rb +262 -0
  45. data/lib/cuboid/processes/schedulers.rb +128 -0
  46. data/lib/cuboid/report.rb +220 -0
  47. data/lib/cuboid/rest/server.rb +165 -0
  48. data/lib/cuboid/rest/server/instance_helpers.rb +99 -0
  49. data/lib/cuboid/rest/server/routes/dispatcher.rb +41 -0
  50. data/lib/cuboid/rest/server/routes/grid.rb +41 -0
  51. data/lib/cuboid/rest/server/routes/instances.rb +131 -0
  52. data/lib/cuboid/rest/server/routes/scheduler.rb +140 -0
  53. data/lib/cuboid/rpc/client.rb +3 -0
  54. data/lib/cuboid/rpc/client/base.rb +58 -0
  55. data/lib/cuboid/rpc/client/dispatcher.rb +58 -0
  56. data/lib/cuboid/rpc/client/instance.rb +100 -0
  57. data/lib/cuboid/rpc/client/instance/service.rb +37 -0
  58. data/lib/cuboid/rpc/client/scheduler.rb +46 -0
  59. data/lib/cuboid/rpc/serializer.rb +92 -0
  60. data/lib/cuboid/rpc/server/active_options.rb +38 -0
  61. data/lib/cuboid/rpc/server/application_wrapper.rb +138 -0
  62. data/lib/cuboid/rpc/server/base.rb +63 -0
  63. data/lib/cuboid/rpc/server/dispatcher.rb +317 -0
  64. data/lib/cuboid/rpc/server/dispatcher/node.rb +247 -0
  65. data/lib/cuboid/rpc/server/dispatcher/service.rb +145 -0
  66. data/lib/cuboid/rpc/server/instance.rb +338 -0
  67. data/lib/cuboid/rpc/server/output.rb +92 -0
  68. data/lib/cuboid/rpc/server/scheduler.rb +482 -0
  69. data/lib/cuboid/ruby.rb +4 -0
  70. data/lib/cuboid/ruby/array.rb +17 -0
  71. data/lib/cuboid/ruby/hash.rb +41 -0
  72. data/lib/cuboid/ruby/object.rb +32 -0
  73. data/lib/cuboid/snapshot.rb +186 -0
  74. data/lib/cuboid/state.rb +94 -0
  75. data/lib/cuboid/state/application.rb +309 -0
  76. data/lib/cuboid/state/options.rb +27 -0
  77. data/lib/cuboid/support.rb +11 -0
  78. data/lib/cuboid/support/buffer.rb +3 -0
  79. data/lib/cuboid/support/buffer/autoflush.rb +61 -0
  80. data/lib/cuboid/support/buffer/base.rb +91 -0
  81. data/lib/cuboid/support/cache.rb +7 -0
  82. data/lib/cuboid/support/cache/base.rb +226 -0
  83. data/lib/cuboid/support/cache/least_cost_replacement.rb +77 -0
  84. data/lib/cuboid/support/cache/least_recently_pushed.rb +21 -0
  85. data/lib/cuboid/support/cache/least_recently_used.rb +31 -0
  86. data/lib/cuboid/support/cache/preference.rb +31 -0
  87. data/lib/cuboid/support/cache/random_replacement.rb +20 -0
  88. data/lib/cuboid/support/crypto.rb +2 -0
  89. data/lib/cuboid/support/crypto/rsa_aes_cbc.rb +86 -0
  90. data/lib/cuboid/support/database.rb +5 -0
  91. data/lib/cuboid/support/database/base.rb +177 -0
  92. data/lib/cuboid/support/database/categorized_queue.rb +195 -0
  93. data/lib/cuboid/support/database/hash.rb +300 -0
  94. data/lib/cuboid/support/database/queue.rb +149 -0
  95. data/lib/cuboid/support/filter.rb +3 -0
  96. data/lib/cuboid/support/filter/base.rb +110 -0
  97. data/lib/cuboid/support/filter/set.rb +29 -0
  98. data/lib/cuboid/support/glob.rb +27 -0
  99. data/lib/cuboid/support/mixins.rb +8 -0
  100. data/lib/cuboid/support/mixins/observable.rb +99 -0
  101. data/lib/cuboid/support/mixins/parts.rb +20 -0
  102. data/lib/cuboid/support/mixins/profiler.rb +93 -0
  103. data/lib/cuboid/support/mixins/spec_instances.rb +65 -0
  104. data/lib/cuboid/support/mixins/terminal.rb +57 -0
  105. data/lib/cuboid/system.rb +119 -0
  106. data/lib/cuboid/system/platforms.rb +84 -0
  107. data/lib/cuboid/system/platforms/linux.rb +26 -0
  108. data/lib/cuboid/system/platforms/mixins/unix.rb +46 -0
  109. data/lib/cuboid/system/platforms/osx.rb +25 -0
  110. data/lib/cuboid/system/platforms/windows.rb +81 -0
  111. data/lib/cuboid/system/slots.rb +143 -0
  112. data/lib/cuboid/ui/output.rb +52 -0
  113. data/lib/cuboid/ui/output_interface.rb +43 -0
  114. data/lib/cuboid/ui/output_interface/abstract.rb +68 -0
  115. data/lib/cuboid/ui/output_interface/controls.rb +84 -0
  116. data/lib/cuboid/ui/output_interface/error_logging.rb +119 -0
  117. data/lib/cuboid/ui/output_interface/implemented.rb +58 -0
  118. data/lib/cuboid/ui/output_interface/personalization.rb +62 -0
  119. data/lib/cuboid/utilities.rb +155 -0
  120. data/lib/cuboid/version.rb +4 -3
  121. data/lib/version +1 -0
  122. data/logs/placeholder +0 -0
  123. data/spec/cuboid/application/parts/data_spec.rb +12 -0
  124. data/spec/cuboid/application/parts/report_spec.rb +6 -0
  125. data/spec/cuboid/application/parts/state_spec.rb +192 -0
  126. data/spec/cuboid/application/runtime_spec.rb +21 -0
  127. data/spec/cuboid/application_spec.rb +37 -0
  128. data/spec/cuboid/data/application_spec.rb +22 -0
  129. data/spec/cuboid/data_spec.rb +47 -0
  130. data/spec/cuboid/error_spec.rb +23 -0
  131. data/spec/cuboid/option_groups/datastore_spec.rb +54 -0
  132. data/spec/cuboid/option_groups/dispatcher_spec.rb +12 -0
  133. data/spec/cuboid/option_groups/output_spec.rb +11 -0
  134. data/spec/cuboid/option_groups/paths_spec.rb +184 -0
  135. data/spec/cuboid/option_groups/report_spec.rb +26 -0
  136. data/spec/cuboid/option_groups/rpc_spec.rb +53 -0
  137. data/spec/cuboid/option_groups/snapshot_spec.rb +26 -0
  138. data/spec/cuboid/option_groups/system.rb +12 -0
  139. data/spec/cuboid/options_spec.rb +218 -0
  140. data/spec/cuboid/report_spec.rb +221 -0
  141. data/spec/cuboid/rest/server_spec.rb +1205 -0
  142. data/spec/cuboid/rpc/client/base_spec.rb +151 -0
  143. data/spec/cuboid/rpc/client/dispatcher_spec.rb +13 -0
  144. data/spec/cuboid/rpc/client/instance_spec.rb +38 -0
  145. data/spec/cuboid/rpc/server/active_options_spec.rb +21 -0
  146. data/spec/cuboid/rpc/server/base_spec.rb +60 -0
  147. data/spec/cuboid/rpc/server/dispatcher/node_spec.rb +222 -0
  148. data/spec/cuboid/rpc/server/dispatcher/service_spec.rb +112 -0
  149. data/spec/cuboid/rpc/server/dispatcher_spec.rb +317 -0
  150. data/spec/cuboid/rpc/server/instance_spec.rb +307 -0
  151. data/spec/cuboid/rpc/server/output_spec.rb +32 -0
  152. data/spec/cuboid/rpc/server/scheduler_spec.rb +400 -0
  153. data/spec/cuboid/ruby/array_spec.rb +77 -0
  154. data/spec/cuboid/ruby/hash_spec.rb +63 -0
  155. data/spec/cuboid/ruby/object_spec.rb +22 -0
  156. data/spec/cuboid/snapshot_spec.rb +123 -0
  157. data/spec/cuboid/state/application_spec.rb +538 -0
  158. data/spec/cuboid/state/options_spec.rb +37 -0
  159. data/spec/cuboid/state_spec.rb +53 -0
  160. data/spec/cuboid/support/buffer/autoflush_spec.rb +78 -0
  161. data/spec/cuboid/support/buffer/base_spec.rb +193 -0
  162. data/spec/cuboid/support/cache/least_cost_replacement_spec.rb +61 -0
  163. data/spec/cuboid/support/cache/least_recently_pushed_spec.rb +90 -0
  164. data/spec/cuboid/support/cache/least_recently_used_spec.rb +80 -0
  165. data/spec/cuboid/support/cache/preference_spec.rb +37 -0
  166. data/spec/cuboid/support/cache/random_replacement_spec.rb +42 -0
  167. data/spec/cuboid/support/crypto/rsa_aes_cbc_spec.rb +28 -0
  168. data/spec/cuboid/support/database/categorized_queue_spec.rb +327 -0
  169. data/spec/cuboid/support/database/hash_spec.rb +204 -0
  170. data/spec/cuboid/support/database/scheduler_spec.rb +325 -0
  171. data/spec/cuboid/support/filter/set_spec.rb +19 -0
  172. data/spec/cuboid/support/glob_spec.rb +75 -0
  173. data/spec/cuboid/support/mixins/observable_spec.rb +95 -0
  174. data/spec/cuboid/system/platforms/linux_spec.rb +31 -0
  175. data/spec/cuboid/system/platforms/osx_spec.rb +32 -0
  176. data/spec/cuboid/system/platforms/windows_spec.rb +41 -0
  177. data/spec/cuboid/system/slots_spec.rb +202 -0
  178. data/spec/cuboid/system_spec.rb +105 -0
  179. data/spec/cuboid/utilities_spec.rb +131 -0
  180. data/spec/spec_helper.rb +46 -0
  181. data/spec/support/factories/placeholder +0 -0
  182. data/spec/support/factories/scan_report.rb +18 -0
  183. data/spec/support/fixtures/empty/placeholder +0 -0
  184. data/spec/support/fixtures/executables/node.rb +50 -0
  185. data/spec/support/fixtures/mock_app.rb +61 -0
  186. data/spec/support/fixtures/mock_app/test_service.rb +64 -0
  187. data/spec/support/fixtures/services/echo.rb +64 -0
  188. data/spec/support/helpers/framework.rb +3 -0
  189. data/spec/support/helpers/matchers.rb +5 -0
  190. data/spec/support/helpers/misc.rb +3 -0
  191. data/spec/support/helpers/paths.rb +15 -0
  192. data/spec/support/helpers/request_helpers.rb +38 -0
  193. data/spec/support/helpers/requires.rb +8 -0
  194. data/spec/support/helpers/resets.rb +52 -0
  195. data/spec/support/helpers/web_server.rb +15 -0
  196. data/spec/support/lib/factory.rb +107 -0
  197. data/spec/support/lib/web_server_client.rb +41 -0
  198. data/spec/support/lib/web_server_dispatcher.rb +25 -0
  199. data/spec/support/lib/web_server_manager.rb +118 -0
  200. data/spec/support/logs/placeholder +0 -0
  201. data/spec/support/pems/cacert.pem +37 -0
  202. data/spec/support/pems/client/cert.pem +37 -0
  203. data/spec/support/pems/client/foo-cert.pem +39 -0
  204. data/spec/support/pems/client/foo-key.pem +51 -0
  205. data/spec/support/pems/client/key.pem +51 -0
  206. data/spec/support/pems/server/cert.pem +37 -0
  207. data/spec/support/pems/server/key.pem +51 -0
  208. data/spec/support/reports/placeholder +0 -0
  209. data/spec/support/shared/application.rb +10 -0
  210. data/spec/support/shared/component.rb +31 -0
  211. data/spec/support/shared/component/options/base.rb +187 -0
  212. data/spec/support/shared/option_group.rb +98 -0
  213. data/spec/support/shared/support/cache.rb +419 -0
  214. data/spec/support/shared/support/filter.rb +143 -0
  215. data/spec/support/shared/system/platforms/base.rb +25 -0
  216. data/spec/support/shared/system/platforms/mixins/unix.rb +37 -0
  217. data/spec/support/snapshots/placeholder +0 -0
  218. metadata +566 -21
  219. data/.gitignore +0 -8
  220. data/bin/console +0 -15
  221. data/bin/setup +0 -8
@@ -0,0 +1,1205 @@
1
+ require 'spec_helper'
2
+ require "#{Cuboid::Options.paths.lib}/rest/server"
3
+
4
+ describe Cuboid::Rest::Server do
5
+ include RequestHelpers
6
+
7
+ before(:each) do
8
+ app.reset
9
+ Cuboid::Options.system.max_slots = 10
10
+ Cuboid::Options.paths.application = "#{fixtures_path}/mock_app.rb"
11
+ end
12
+
13
+ let(:options) {{}}
14
+ let(:url) { tpl_url % id }
15
+ let(:id) { @id }
16
+ let(:non_existent_id) { 'stuff' }
17
+
18
+ let(:dispatcher) { Cuboid::Processes::Dispatchers.spawn }
19
+ let(:scheduler) { Cuboid::Processes::Schedulers.spawn }
20
+
21
+ def create_instance
22
+ post '/instances', options
23
+ response_data['id']
24
+ end
25
+
26
+ context 'supports compressing as' do
27
+ ['gzip'].each do |compression_method|
28
+
29
+ it compression_method do
30
+ get '/', {}, { 'HTTP_ACCEPT_ENCODING' => compression_method }
31
+ expect( response.headers['Content-Encoding'] ).to eq compression_method.split( ',' ).first
32
+ end
33
+
34
+ end
35
+ end
36
+
37
+ context 'when the client does not support compression' do
38
+ it 'does not compress the response' do
39
+ get '/'
40
+ expect(response.headers['Content-Encoding']).to be_nil
41
+ end
42
+ end
43
+
44
+ context 'when authentication' do
45
+ let(:username) { nil }
46
+ let(:password) { nil }
47
+ let(:userpwd) { "#{username}:#{password}" }
48
+ let(:url) { "http://localhost:#{Cuboid::Options.rpc.server_port}/instances" }
49
+
50
+ before do
51
+ Cuboid::Options.datastore['username'] = username
52
+ Cuboid::Options.datastore['password'] = password
53
+
54
+ Cuboid::Options.rpc.server_port = Cuboid::Utilities.available_port
55
+ Cuboid::Processes::Manager.spawn( :rest_service )
56
+
57
+ sleep 0.1 while Typhoeus.get( url ).code == 0
58
+ end
59
+
60
+ after do
61
+ Cuboid::Processes::Manager.killall
62
+ end
63
+
64
+ context 'username' do
65
+ let(:username) { 'username' }
66
+
67
+ context 'is configured' do
68
+ it 'requires authentication' do
69
+ expect(Typhoeus.get( url ).code).to eq 401
70
+ expect(Typhoeus.get( url, userpwd: userpwd ).code).to eq 200
71
+ end
72
+ end
73
+ end
74
+
75
+ context 'password' do
76
+ let(:password) { 'password' }
77
+
78
+ context 'is configured' do
79
+ it 'requires authentication' do
80
+ expect(Typhoeus.get( url ).code).to eq 401
81
+ expect(Typhoeus.get( url, userpwd: userpwd ).code).to eq 200
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ describe 'SSL options', if: !Cuboid.windows? do
88
+ let(:ssl_key) { nil }
89
+ let(:ssl_cert) { nil }
90
+ let(:ssl_ca) { nil }
91
+ let(:url) { "http://localhost:#{Cuboid::Options.rpc.server_port}/instances" }
92
+ let(:https_url) { "https://localhost:#{Cuboid::Options.rpc.server_port}/instances" }
93
+
94
+ before do
95
+ Cuboid::Options.rpc.ssl_ca = ssl_ca
96
+ Cuboid::Options.rpc.server_ssl_private_key = ssl_key
97
+ Cuboid::Options.rpc.server_ssl_certificate = ssl_cert
98
+
99
+ Cuboid::Options.rpc.server_port = Cuboid::Utilities.available_port
100
+ Cuboid::Processes::Manager.spawn( :rest_service )
101
+
102
+ sleep 0.1 while Typhoeus.get( url ).return_code == :couldnt_connect
103
+ end
104
+
105
+ after do
106
+ Cuboid::Processes::Manager.killall
107
+ end
108
+
109
+ describe 'when key and certificate is given' do
110
+ let(:ssl_key) { "#{support_path}/pems/server/key.pem" }
111
+ let(:ssl_cert) { "#{support_path}/pems/server/cert.pem" }
112
+
113
+ describe 'when no CA is given' do
114
+ it 'disables peer verification' do
115
+ expect(Typhoeus.get( https_url, ssl_verifypeer: false ).code).to eq 200
116
+ end
117
+ end
118
+
119
+ describe 'a CA is given' do
120
+ let(:ssl_ca) { "#{support_path}/pems/cacert.pem" }
121
+
122
+ it 'enables peer verification' do
123
+ expect(Typhoeus.get( https_url, ssl_verifypeer: false ).code).to eq 0
124
+
125
+ expect(Typhoeus.get(
126
+ https_url,
127
+ ssl_verifypeer: true,
128
+ sslcert: "#{support_path}/pems/client/cert.pem",
129
+ sslkey: "#{support_path}/pems/client/key.pem",
130
+ cainfo: ssl_ca
131
+ ).code).to eq 200
132
+ end
133
+ end
134
+ end
135
+
136
+ describe 'when only key is given' do
137
+ let(:ssl_key) { "#{support_path}/pems/server/key.pem" }
138
+
139
+ it 'does not enable SSL' do
140
+ expect(Typhoeus.get( url ).code).to eq 200
141
+ end
142
+ end
143
+
144
+ describe 'when only cert is given' do
145
+ let(:ssl_cert) { "#{support_path}/pems/server/cert.pem" }
146
+
147
+ it 'does not enable SSL' do
148
+ expect(Typhoeus.get( url ).code).to eq 200
149
+ end
150
+ end
151
+ end
152
+
153
+ describe 'GET /instances' do
154
+ let(:tpl_url) { '/instances' }
155
+
156
+ it 'lists ids for all instances' do
157
+ ids = []
158
+ 2.times do
159
+ ids << create_instance
160
+ end
161
+
162
+ get url
163
+
164
+ ids.each do |id|
165
+ expect(response_data[id]).to eq({})
166
+ end
167
+ end
168
+
169
+ context 'when there is a Scheduler' do
170
+ before do
171
+ put '/scheduler/url', scheduler.url
172
+ end
173
+
174
+ it 'includes its running instances' do
175
+ id = scheduler.push( options )
176
+ sleep 0.1 while scheduler.running.empty?
177
+
178
+ get url
179
+ expect(response_data).to include id
180
+ end
181
+
182
+ context 'when a running instance completes' do
183
+ it 'is removed' do
184
+ scheduler.push( options )
185
+ sleep 0.1 while scheduler.completed.empty?
186
+
187
+ get url
188
+ expect(response_data).to be_empty
189
+ end
190
+ end
191
+ end
192
+ end
193
+
194
+ describe 'POST /instances' do
195
+ let(:tpl_url) { '/instances' }
196
+
197
+ it 'creates an instance' do
198
+ post url, options
199
+ expect(response_code).to eq 200
200
+ end
201
+
202
+ context 'when given invalid options' do
203
+ it 'returns a 500' do
204
+ post url, invalid: 'blah'
205
+
206
+ expect(response_code).to eq 500
207
+ expect(response_data['error']).to eq 'Arachni::RPC::Exceptions::RemoteException'
208
+ expect(response_data).to include 'backtrace'
209
+ end
210
+
211
+ it 'does not list the instance on the index' do
212
+ get '/instances'
213
+ ids = response_data.keys
214
+
215
+ post url, invalid: 'blah'
216
+
217
+ get '/instances'
218
+ expect(response_data.keys - ids).to be_empty
219
+ end
220
+ end
221
+
222
+ context 'when the system is at max utilization' do
223
+ it 'returns a 503' do
224
+ Cuboid::Options.system.max_slots = 1
225
+
226
+ post url, options
227
+ expect(response_code).to eq 200
228
+
229
+ sleep 1
230
+
231
+ post url, options
232
+ expect(response_code).to eq 503
233
+ expect(response_data['error']).to eq 'Service unavailable: System is at maximum ' +
234
+ 'utilization, slot limit reached.'
235
+ end
236
+ end
237
+
238
+ context 'when a Dispatcher has been set' do
239
+
240
+ it 'uses it' do
241
+ put '/dispatcher/url', dispatcher.url
242
+
243
+ get "/grid/#{dispatcher.url}"
244
+ expect(response_data['running_instances']).to be_empty
245
+
246
+ create_instance
247
+
248
+ get "/grid/#{dispatcher.url}"
249
+ expect(response_data['running_instances'].size).to eq 1
250
+ end
251
+ end
252
+ end
253
+
254
+ describe 'GET /instances/:instance' do
255
+ let(:tpl_url) { '/instances/%s' }
256
+
257
+ before do
258
+ @id = create_instance
259
+ end
260
+
261
+ it 'gets progress info' do
262
+ loop do
263
+ get url
264
+ break if !response_data['busy']
265
+ sleep 0.5
266
+ end
267
+
268
+ %w(errors status busy messages statistics).each do |key|
269
+ expect(response_data).to include key
270
+ end
271
+
272
+ %w(statistics).each do |key|
273
+ expect(response_data.any?).to be_truthy
274
+ end
275
+ end
276
+
277
+ context 'when a session is maintained' do
278
+ it 'only returns new errors'
279
+ end
280
+
281
+ context 'when a session is not maintained' do
282
+ it 'always returns all errors'
283
+ end
284
+
285
+ context 'when passed a non-existent id' do
286
+ let(:id) { non_existent_id }
287
+
288
+ it 'returns 404' do
289
+ get url
290
+ expect(response_code).to eq 404
291
+ end
292
+ end
293
+
294
+ context 'when the instance is from the Scheduler' do
295
+ before do
296
+ put '/scheduler/url', scheduler.url
297
+ end
298
+
299
+ it 'includes it' do
300
+ @id = scheduler.push( options )
301
+ sleep 0.1 while scheduler.running.empty?
302
+
303
+ get url
304
+ expect(response_data).to include 'busy'
305
+ end
306
+
307
+ context 'when the instance completes' do
308
+ it 'is removed' do
309
+ @id = scheduler.push( options )
310
+ sleep 0.1 while scheduler.completed.empty?
311
+
312
+ get url
313
+ expect(response_code).to be 404
314
+ end
315
+ end
316
+ end
317
+ end
318
+
319
+ describe 'PUT /instances/:instance/scheduler' do
320
+ let(:tpl_url) { '/instances/%s/scheduler' }
321
+
322
+ before do
323
+ @id = create_instance
324
+ end
325
+
326
+ context 'when there is a Scheduler' do
327
+ before do
328
+ put '/scheduler/url', scheduler.url
329
+ end
330
+
331
+ it 'moves the instance to the Scheduler' do
332
+ expect(scheduler.running).to be_empty
333
+
334
+ put url
335
+ expect(response_code).to be 200
336
+ expect(scheduler.running).to include @id
337
+ end
338
+
339
+ context 'but the instance could not be found' do
340
+ it 'returns 404' do
341
+ @id = 'ss'
342
+
343
+ put url
344
+ expect(response_code).to be 404
345
+ end
346
+ end
347
+ end
348
+
349
+ context 'when there is no Scheduler' do
350
+ it 'returns 501' do
351
+ put url
352
+ expect(response_code).to be 501
353
+ end
354
+ end
355
+ end
356
+
357
+ describe 'GET /instances/:instance/summary' do
358
+ let(:tpl_url) { '/instances/%s/summary' }
359
+
360
+ before do
361
+ @id = create_instance
362
+ end
363
+
364
+ context 'when passed a non-existent id' do
365
+ let(:id) { non_existent_id }
366
+
367
+ it 'returns 404' do
368
+ get url
369
+ expect(response_code).to eq 404
370
+ end
371
+ end
372
+
373
+ context 'when the instance is from the Scheduler' do
374
+ before do
375
+ put '/scheduler/url', scheduler.url
376
+ end
377
+
378
+ it 'includes it' do
379
+ @id = scheduler.push( options )
380
+ sleep 0.1 while scheduler.running.empty?
381
+
382
+ get url
383
+ expect(response_data).to include 'busy'
384
+ end
385
+
386
+ context 'when the instance completes' do
387
+ it 'is removed' do
388
+ @id = scheduler.push( options )
389
+ sleep 0.1 while scheduler.completed.empty?
390
+
391
+ get url
392
+ expect(response_code).to be 404
393
+ end
394
+ end
395
+ end
396
+ end
397
+
398
+ describe 'GET /instances/:instance/report.crf' do
399
+ let(:tpl_url) { "/instances/%s/report.crf" }
400
+
401
+ before do
402
+ @id = create_instance
403
+ end
404
+
405
+ it 'returns instance report' do
406
+ get url
407
+
408
+ file = Tempfile.new( "#{Dir.tmpdir}/report-#{Process.pid}.crf" )
409
+ file.write last_response.body
410
+ file.close
411
+
412
+ expect(Cuboid::Report.load( file.path ).data).to eq 'My results.'
413
+ end
414
+
415
+ it 'has content-type application/octet-stream' do
416
+ get url
417
+ expect(last_response.headers['content-type']).to eq 'application/octet-stream'
418
+ end
419
+
420
+ context 'when passed a non-existent id' do
421
+ let(:id) { non_existent_id }
422
+
423
+ it 'returns 404' do
424
+ get url
425
+ expect(response_code).to eq 404
426
+ end
427
+ end
428
+ end
429
+
430
+ describe 'GET /instances/:instance/report.json' do
431
+ let(:tpl_url) { "/instances/%s/report.json" }
432
+
433
+ before do
434
+ @id = create_instance
435
+ end
436
+
437
+ it 'returns instance report' do
438
+ get url
439
+
440
+ report = JSON.load( last_response.body )
441
+ expect(MockApp.serializer.load report['data']).to eq 'My results.'
442
+ end
443
+
444
+ it 'has content-type application/json' do
445
+ get url
446
+ expect(last_response.headers['content-type']).to eq 'application/json'
447
+ end
448
+
449
+ context 'when passed a non-existent id' do
450
+ let(:id) { non_existent_id }
451
+
452
+ it 'returns 404' do
453
+ get url
454
+ expect(response_code).to eq 404
455
+ end
456
+ end
457
+ end
458
+
459
+ describe 'PUT /instances/:instance/pause' do
460
+ let(:tpl_url) { '/instances/%s/pause' }
461
+
462
+ before do
463
+ @id = create_instance
464
+ end
465
+
466
+ it 'pauses the instance' do
467
+ put url
468
+ expect(response_code).to eq 200
469
+
470
+ get "/instances/#{id}"
471
+ expect(['pausing', 'paused']).to include response_data['status']
472
+ end
473
+
474
+ context 'when passed a non-existent id' do
475
+ let(:id) { non_existent_id }
476
+
477
+ it 'returns 404' do
478
+ put url
479
+ expect(response_code).to eq 404
480
+ end
481
+ end
482
+
483
+ context 'when the instance is from the Scheduler' do
484
+ before do
485
+ put '/scheduler/url', scheduler.url
486
+ end
487
+
488
+ it 'includes it' do
489
+ @id = scheduler.push( options )
490
+ sleep 0.1 while scheduler.running.empty?
491
+
492
+ put url
493
+ expect(response_code).to eq 200
494
+
495
+ get "/instances/#{id}"
496
+ expect(['pausing', 'paused']).to include response_data['status']
497
+ end
498
+
499
+ context 'when the instance completes' do
500
+ it 'is removed' do
501
+ @id = scheduler.push( options )
502
+ sleep 0.1 while scheduler.completed.empty?
503
+
504
+ put url
505
+ expect(response_code).to be 404
506
+ end
507
+ end
508
+ end
509
+ end
510
+
511
+ describe 'PUT /instances/:instance/resume' do
512
+ let(:tpl_url) { '/instances/%s/resume' }
513
+
514
+ before do
515
+ @id = create_instance
516
+ end
517
+
518
+ it 'resumes the instance' do
519
+ put "/instances/#{id}/pause"
520
+ get "/instances/#{id}"
521
+
522
+ expect(['pausing', 'paused']).to include response_data['status']
523
+
524
+ put url
525
+ get "/instances/#{id}"
526
+
527
+ expect(['pausing', 'paused']).to_not include response_data['status']
528
+ end
529
+
530
+ context 'when passed a non-existent id' do
531
+ let(:id) { non_existent_id }
532
+
533
+ it 'returns 404' do
534
+ put url
535
+ expect(response_code).to eq 404
536
+ end
537
+ end
538
+
539
+ context 'when the instance is from the Scheduler' do
540
+ before do
541
+ put '/scheduler/url', scheduler.url
542
+ end
543
+
544
+ it 'includes it' do
545
+ @id = scheduler.push( options )
546
+ sleep 0.1 while scheduler.running.empty?
547
+
548
+ put "/instances/#{id}/pause"
549
+ get "/instances/#{id}"
550
+
551
+ expect(['pausing', 'paused']).to include response_data['status']
552
+
553
+ put url
554
+ get "/instances/#{id}"
555
+
556
+ expect(['running', 'done']).to include response_data['status']
557
+ end
558
+
559
+ context 'when the instance completes' do
560
+ it 'is removed' do
561
+ @id = scheduler.push( options )
562
+ sleep 0.1 while scheduler.completed.empty?
563
+
564
+ put url
565
+ expect(response_code).to be 404
566
+ end
567
+ end
568
+ end
569
+ end
570
+
571
+ describe 'DELETE /instances/:instance' do
572
+ let(:tpl_url) { '/instances/%s' }
573
+
574
+ before do
575
+ @id = create_instance
576
+ end
577
+
578
+ it 'aborts the instance' do
579
+ get url
580
+ expect(response_code).to eq 200
581
+
582
+ delete url
583
+
584
+ get "/instances/#{id}"
585
+ expect(response_code).to eq 404
586
+ end
587
+
588
+ context 'when passed a non-existent id' do
589
+ let(:id) { non_existent_id }
590
+
591
+ it 'returns 404' do
592
+ delete url
593
+ expect(response_code).to eq 404
594
+ end
595
+ end
596
+
597
+ context 'when the instance is from the Scheduler' do
598
+ before do
599
+ put '/scheduler/url', scheduler.url
600
+ end
601
+
602
+ it 'includes it' do
603
+ @id = scheduler.push( options )
604
+ sleep 0.1 while scheduler.running.empty?
605
+
606
+ delete url
607
+ expect(response_code).to eq 200
608
+
609
+ sleep 0.1 while scheduler.failed.empty?
610
+
611
+ expect(scheduler.failed).to include @id
612
+ end
613
+
614
+ context 'when the instance completes' do
615
+ it 'is removed' do
616
+ @id = scheduler.push( options )
617
+ sleep 0.1 while scheduler.completed.empty?
618
+
619
+ delete url
620
+ expect(response_code).to be 404
621
+ end
622
+ end
623
+ end
624
+ end
625
+
626
+ describe 'GET /dispatcher/url' do
627
+ let(:tpl_url) { '/dispatcher/url' }
628
+
629
+ it 'returns the Dispatcher' do
630
+ put url, dispatcher.url
631
+ expect(response_code).to eq 200
632
+
633
+ get url
634
+ expect(response_code).to eq 200
635
+ expect(response_data).to eq dispatcher.url
636
+ end
637
+
638
+ context 'when no Dispatcher has been set' do
639
+ it 'returns 501' do
640
+ get url
641
+ expect(response_code).to eq 501
642
+ expect(response_data).to eq 'No Dispatcher has been set.'
643
+ end
644
+ end
645
+ end
646
+
647
+ describe 'PUT /dispatcher/url' do
648
+ let(:tpl_url) { '/dispatcher/url' }
649
+
650
+ it 'sets the Dispatcher' do
651
+ put url, dispatcher.url
652
+ expect(response_code).to eq 200
653
+ end
654
+
655
+ context 'when passed a non-existent URL' do
656
+ let(:id) { non_existent_id }
657
+
658
+ it 'returns 500' do
659
+ put url, 'localhost:383838'
660
+ expect(response_code).to eq 500
661
+ expect(response_data['error']).to eq 'Arachni::RPC::Exceptions::ConnectionError'
662
+ end
663
+ end
664
+ end
665
+
666
+ describe 'DELETE /dispatcher/url' do
667
+ let(:tpl_url) { '/dispatcher/url' }
668
+
669
+ it 'removes the the Dispatcher' do
670
+ put url, dispatcher.url
671
+ expect(response_code).to eq 200
672
+
673
+ delete url
674
+ expect(response_code).to eq 200
675
+
676
+ get url, dispatcher.url
677
+ expect(response_code).to eq 501
678
+ end
679
+
680
+ context 'when no Dispatcher has been set' do
681
+ it 'returns 501' do
682
+ delete url
683
+ expect(response_code).to eq 501
684
+ expect(response_data).to eq 'No Dispatcher has been set.'
685
+ end
686
+ end
687
+ end
688
+
689
+ describe 'GET /grid' do
690
+ let(:dispatcher) { Cuboid::Processes::Dispatchers.grid_spawn }
691
+ let(:tpl_url) { '/grid' }
692
+
693
+ it 'returns Grid info' do
694
+ put '/dispatcher/url', dispatcher.url
695
+ expect(response_code).to eq 200
696
+
697
+ get url
698
+ expect(response_code).to eq 200
699
+ expect(response_data.sort).to eq ([dispatcher.url] + dispatcher.node.neighbours).sort
700
+ end
701
+
702
+ context 'when no Dispatcher has been set' do
703
+ it 'returns 501' do
704
+ get url
705
+ expect(response_code).to eq 501
706
+ expect(response_data).to eq 'No Dispatcher has been set.'
707
+ end
708
+ end
709
+ end
710
+
711
+ describe 'GET /grid/:dispatcher' do
712
+ let(:dispatcher) { Cuboid::Processes::Dispatchers.grid_spawn }
713
+ let(:tpl_url) { '/grid/%s' }
714
+
715
+ it 'returns Dispatcher info' do
716
+ put '/dispatcher/url', dispatcher.url
717
+ expect(response_code).to eq 200
718
+
719
+ @id = dispatcher.url
720
+
721
+ get url
722
+ expect(response_code).to eq 200
723
+ expect(response_data).to eq dispatcher.statistics
724
+ end
725
+
726
+ context 'when no Dispatcher has been set' do
727
+ it 'returns 501' do
728
+ @id = 'localhost:2222'
729
+
730
+ get url
731
+ expect(response_code).to eq 501
732
+ expect(response_data).to eq 'No Dispatcher has been set.'
733
+ end
734
+ end
735
+ end
736
+
737
+ describe 'DELETE /grid/:dispatcher' do
738
+ let(:dispatcher) { Cuboid::Processes::Dispatchers.grid_spawn }
739
+ let(:tpl_url) { '/grid/%s' }
740
+
741
+ it 'unplugs the Dispatcher from the Grid' do
742
+ put '/dispatcher/url', dispatcher.url
743
+ expect(response_code).to eq 200
744
+
745
+ @id = dispatcher.url
746
+
747
+ expect(dispatcher.node.grid_member?).to be_truthy
748
+
749
+ delete url
750
+ expect(response_code).to eq 200
751
+ expect(dispatcher.node.grid_member?).to be_falsey
752
+ end
753
+
754
+ context 'when no Dispatcher has been set' do
755
+ it 'returns 501' do
756
+ @id = 'localhost:2222'
757
+
758
+ delete url
759
+ expect(response_code).to eq 501
760
+ expect(response_data).to eq 'No Dispatcher has been set.'
761
+ end
762
+ end
763
+ end
764
+
765
+ describe 'GET /scheduler' do
766
+ let(:tpl_url) { '/scheduler' }
767
+
768
+ context 'when a Scheduler has been set' do
769
+ before do
770
+ put '/scheduler/url', scheduler.url
771
+ end
772
+
773
+ it 'lists schedulerd instances grouped by priority' do
774
+ low = scheduler.push( options, priority: -1 )
775
+ high = scheduler.push( options, priority: 1 )
776
+ medium = scheduler.push( options, priority: 0 )
777
+
778
+ get url
779
+ expect(response_code).to eq 200
780
+ expect(response_data.to_a).to eq({
781
+ '1' => [high],
782
+ '0' => [medium],
783
+ '-1' => [low]
784
+ }.to_a)
785
+ end
786
+ end
787
+
788
+ context 'when no Scheduler has been set' do
789
+ it 'returns 501' do
790
+ get url
791
+
792
+ expect(response_code).to eq 501
793
+ expect(response_data).to eq 'No Scheduler has been set.'
794
+ end
795
+ end
796
+ end
797
+
798
+ describe 'POST /scheduler' do
799
+ let(:tpl_url) { '/scheduler' }
800
+
801
+ context 'when a Scheduler has been set' do
802
+ before do
803
+ put '/scheduler/url', scheduler.url
804
+ end
805
+
806
+ it 'pushes the instance to the Scheduler' do
807
+ post url, [options, priority: 9]
808
+
809
+ expect(response_code).to eq 200
810
+
811
+ id = response_data['id']
812
+
813
+ expect(scheduler.get(id)).to eq(
814
+ 'options' => options,
815
+ 'priority' => 9
816
+ )
817
+ end
818
+
819
+ context 'when given invalid options' do
820
+ it 'returns a 500' do
821
+ post url, invalid: 'blah'
822
+
823
+ expect(response_code).to eq 500
824
+ expect(response_data['error']).to eq 'Arachni::RPC::Exceptions::RemoteException'
825
+ expect(response_data).to include 'backtrace'
826
+ end
827
+ end
828
+ end
829
+
830
+ context 'when no Scheduler has been set' do
831
+ it 'returns 501' do
832
+ get url
833
+
834
+ expect(response_code).to eq 501
835
+ expect(response_data).to eq 'No Scheduler has been set.'
836
+ end
837
+ end
838
+ end
839
+
840
+ describe 'GET /scheduler/url' do
841
+ let(:tpl_url) { '/scheduler/url' }
842
+
843
+ context 'when a Scheduler has been set' do
844
+ before do
845
+ put '/scheduler/url', scheduler.url
846
+ end
847
+
848
+ it 'returns its URL' do
849
+ get url
850
+ expect(response_code).to eq 200
851
+ expect(response_data).to eq scheduler.url
852
+ end
853
+ end
854
+
855
+ context 'when no Scheduler has been set' do
856
+ it 'returns 501' do
857
+ get url
858
+
859
+ expect(response_code).to eq 501
860
+ expect(response_data).to eq 'No Scheduler has been set.'
861
+ end
862
+ end
863
+ end
864
+
865
+ describe 'PUT /scheduler/url' do
866
+ let(:tpl_url) { '/scheduler/url' }
867
+
868
+
869
+ it 'sets the Scheduler URL' do
870
+ put url, scheduler.url
871
+ expect(response_code).to eq 200
872
+ end
873
+
874
+ context 'when given an invalid URL' do
875
+ it 'returns 500' do
876
+ put url, 'localhost:393939'
877
+
878
+ expect(response_code).to eq 500
879
+ expect(response_data['error']).to eq 'Arachni::RPC::Exceptions::ConnectionError'
880
+ expect(response_data['description']).to include 'Connection closed'
881
+ end
882
+ end
883
+ end
884
+
885
+ describe 'DELETE /scheduler/url' do
886
+ let(:tpl_url) { '/scheduler/url' }
887
+
888
+ context 'when a Scheduler has been set' do
889
+ before do
890
+ put '/scheduler/url', scheduler.url
891
+ end
892
+
893
+ it 'removes it' do
894
+ delete url
895
+ expect(response_code).to eq 200
896
+
897
+ get '/scheduler/url'
898
+ expect(response_code).to eq 501
899
+ end
900
+ end
901
+
902
+ context 'when no Scheduler has been set' do
903
+ it 'returns 501' do
904
+ get url
905
+
906
+ expect(response_code).to eq 501
907
+ expect(response_data).to eq 'No Scheduler has been set.'
908
+ end
909
+ end
910
+ end
911
+
912
+ describe 'GET /scheduler/running' do
913
+ let(:tpl_url) { '/scheduler/running' }
914
+
915
+ context 'when a Scheduler has been set' do
916
+ before do
917
+ put '/scheduler/url', scheduler.url
918
+ end
919
+
920
+ it 'returns running instances' do
921
+ get url
922
+ expect(response_data.empty?).to be_truthy
923
+
924
+ @id = scheduler.push( options )
925
+ sleep 0.1 while scheduler.running.empty?
926
+
927
+ get url
928
+ expect(response_data.size).to be 1
929
+ expect(response_data[@id]).to include 'url'
930
+ expect(response_data[@id]).to include 'token'
931
+ expect(response_data[@id]).to include 'pid'
932
+ end
933
+ end
934
+
935
+ context 'when no Scheduler has been set' do
936
+ it 'returns 501' do
937
+ get url
938
+
939
+ expect(response_code).to eq 501
940
+ expect(response_data).to eq 'No Scheduler has been set.'
941
+ end
942
+ end
943
+ end
944
+
945
+ describe 'GET /scheduler/completed' do
946
+ let(:tpl_url) { '/scheduler/completed' }
947
+
948
+ context 'when a Scheduler has been set' do
949
+ before do
950
+ put '/scheduler/url', scheduler.url
951
+ end
952
+
953
+ it 'returns completed instances' do
954
+ get url
955
+ expect(response_data.empty?).to be_truthy
956
+
957
+ @id = scheduler.push( options )
958
+ sleep 0.1 while scheduler.completed.empty?
959
+
960
+ get url
961
+ expect(response_data.size).to be 1
962
+ expect(File.exists? response_data[@id]).to be true
963
+ end
964
+ end
965
+
966
+ context 'when no Scheduler has been set' do
967
+ it 'returns 501' do
968
+ get url
969
+
970
+ expect(response_code).to eq 501
971
+ expect(response_data).to eq 'No Scheduler has been set.'
972
+ end
973
+ end
974
+ end
975
+
976
+ describe 'GET /scheduler/failed' do
977
+ let(:tpl_url) { '/scheduler/failed' }
978
+
979
+ context 'when a Scheduler has been set' do
980
+ before do
981
+ put '/scheduler/url', scheduler.url
982
+ end
983
+
984
+ it 'returns failed instances' do
985
+ get url
986
+ expect(response_data.empty?).to be_truthy
987
+
988
+ @id = scheduler.push( options )
989
+ sleep 0.1 while scheduler.running.empty?
990
+ Cuboid::Processes::Manager.kill scheduler.running.values.first['pid']
991
+ sleep 0.1 while scheduler.failed.empty?
992
+
993
+ get url
994
+ expect(response_data.size).to be 1
995
+ expect(response_data[@id]['error']).to eq 'Arachni::RPC::Exceptions::ConnectionError'
996
+ expect(response_data[@id]['description']).to include 'Connection closed [Connection refused - connect(2) for'
997
+ end
998
+ end
999
+
1000
+ context 'when no Scheduler has been set' do
1001
+ it 'returns 501' do
1002
+ get url
1003
+
1004
+ expect(response_code).to eq 501
1005
+ expect(response_data).to eq 'No Scheduler has been set.'
1006
+ end
1007
+ end
1008
+ end
1009
+
1010
+ describe 'GET /scheduler/size' do
1011
+ let(:tpl_url) { '/scheduler/size' }
1012
+
1013
+ context 'when a Scheduler has been set' do
1014
+ before do
1015
+ put '/scheduler/url', scheduler.url
1016
+ end
1017
+
1018
+ it 'returns the scheduler size' do
1019
+ get url
1020
+ expect(response_data).to eq 0
1021
+
1022
+ 10.times do
1023
+ scheduler.push( options )
1024
+ end
1025
+
1026
+ get url
1027
+ expect(response_data).to be 10
1028
+ end
1029
+ end
1030
+
1031
+ context 'when no Scheduler has been set' do
1032
+ it 'returns 501' do
1033
+ get url
1034
+
1035
+ expect(response_code).to eq 501
1036
+ expect(response_data).to eq 'No Scheduler has been set.'
1037
+ end
1038
+ end
1039
+ end
1040
+
1041
+ describe 'DELETE /scheduler' do
1042
+ let(:tpl_url) { '/scheduler' }
1043
+
1044
+ context 'when a Scheduler has been set' do
1045
+ before do
1046
+ put '/scheduler/url', scheduler.url
1047
+ end
1048
+
1049
+ it 'empties the scheduler' do
1050
+ expect(scheduler.empty?).to be_truthy
1051
+
1052
+ 10.times do
1053
+ scheduler.push( options )
1054
+ end
1055
+
1056
+ expect(scheduler.any?).to be_truthy
1057
+
1058
+ delete url
1059
+ expect(scheduler.empty?).to be_truthy
1060
+ end
1061
+ end
1062
+
1063
+ context 'when no Scheduler has been set' do
1064
+ it 'returns 501' do
1065
+ get url
1066
+
1067
+ expect(response_code).to eq 501
1068
+ expect(response_data).to eq 'No Scheduler has been set.'
1069
+ end
1070
+ end
1071
+ end
1072
+
1073
+ describe 'GET /scheduler/:instance' do
1074
+ let(:tpl_url) { '/scheduler/%s' }
1075
+
1076
+ context 'when a Scheduler has been set' do
1077
+ before do
1078
+ put '/scheduler/url', scheduler.url
1079
+ end
1080
+
1081
+ it 'returns info for the Queued instance' do
1082
+ @id = scheduler.push( options )
1083
+
1084
+ get url
1085
+ expect(response_code).to be 200
1086
+ expect(response_data).to eq({
1087
+ 'options' => options,
1088
+ 'priority' => 0
1089
+ })
1090
+ end
1091
+
1092
+ context 'when the instance could not be found' do
1093
+ let(:id) { non_existent_id }
1094
+
1095
+ it 'returns 404' do
1096
+ get url
1097
+
1098
+ expect(response_code).to eq 404
1099
+ expect(response_data).to eq 'Instance not in Scheduler.'
1100
+ end
1101
+ end
1102
+ end
1103
+
1104
+ context 'when no Scheduler has been set' do
1105
+ let(:id) { non_existent_id }
1106
+
1107
+ it 'returns 501' do
1108
+ get url
1109
+
1110
+ expect(response_code).to eq 501
1111
+ expect(response_data).to eq 'No Scheduler has been set.'
1112
+ end
1113
+ end
1114
+ end
1115
+
1116
+ describe 'PUT /scheduler/:instance/detach' do
1117
+ let(:tpl_url) { '/scheduler/%s/detach' }
1118
+
1119
+ context 'when a Scheduler has been set' do
1120
+ before do
1121
+ put '/scheduler/url', scheduler.url
1122
+ end
1123
+
1124
+ it 'detaches the instance from the Scheduler' do
1125
+ @id = scheduler.push( options )
1126
+ sleep 0.1 while scheduler.running.empty?
1127
+
1128
+ put url
1129
+ expect(response_code).to be 200
1130
+ expect(scheduler.running).to be_empty
1131
+ expect(scheduler.completed).to be_empty
1132
+ expect(scheduler.failed).to be_empty
1133
+
1134
+ get '/instances'
1135
+ expect(response_code).to be 200
1136
+ expect(response_data.keys).to eq [@id]
1137
+ end
1138
+
1139
+ context 'when the instance could not be found' do
1140
+ let(:id) { non_existent_id }
1141
+
1142
+ it 'returns 404' do
1143
+ put url
1144
+
1145
+ expect(response_code).to eq 404
1146
+ expect(response_data).to eq 'Instance not in Scheduler.'
1147
+ end
1148
+ end
1149
+ end
1150
+
1151
+ context 'when no Scheduler has been set' do
1152
+ let(:id) { non_existent_id }
1153
+
1154
+ it 'returns 501' do
1155
+ put url
1156
+
1157
+ expect(response_code).to eq 501
1158
+ expect(response_data).to eq 'No Scheduler has been set.'
1159
+ end
1160
+ end
1161
+ end
1162
+
1163
+ describe 'DELETE /scheduler/:instance' do
1164
+ let(:tpl_url) { '/scheduler/%s' }
1165
+
1166
+ context 'when a Scheduler has been set' do
1167
+ before do
1168
+ put '/scheduler/url', scheduler.url
1169
+ end
1170
+
1171
+ it 'removes the instance from the Scheduler' do
1172
+ @id = scheduler.push( options )
1173
+
1174
+ expect(scheduler.any?).to be_truthy
1175
+
1176
+ delete url
1177
+
1178
+ expect(response_code).to be 200
1179
+ expect(scheduler.empty?).to be_truthy
1180
+ end
1181
+
1182
+ context 'when the instance could not be found' do
1183
+ let(:id) { non_existent_id }
1184
+
1185
+ it 'returns 404' do
1186
+ delete url
1187
+
1188
+ expect(response_code).to eq 404
1189
+ expect(response_data).to eq 'Instance not in Scheduler.'
1190
+ end
1191
+ end
1192
+ end
1193
+
1194
+ context 'when no Scheduler has been set' do
1195
+ let(:id) { non_existent_id }
1196
+
1197
+ it 'returns 501' do
1198
+ delete url
1199
+
1200
+ expect(response_code).to eq 501
1201
+ expect(response_data).to eq 'No Scheduler has been set.'
1202
+ end
1203
+ end
1204
+ end
1205
+ end