cuboid 0.0.0 → 0.0.1alpha

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