Merge branch 'master' into 2767-doc-updates
[arvados.git] / services / api / test / integration / websocket_test.rb
1 require 'test_helper'
2 require 'websocket_runner'
3 require 'oj'
4 require 'database_cleaner'
5
6 DatabaseCleaner.strategy = :truncation
7
8 class WebsocketTest < ActionDispatch::IntegrationTest
9   self.use_transactional_fixtures = false
10
11   setup do
12     DatabaseCleaner.start
13   end
14
15   teardown do
16     DatabaseCleaner.clean
17   end
18
19   def ws_helper (token = nil, timeout = true)
20     opened = false
21     close_status = nil
22     too_long = false
23
24     EM.run {
25       if token
26         ws = Faye::WebSocket::Client.new("ws://localhost:3002/websocket?api_token=#{api_client_authorizations(token).api_token}")
27       else
28         ws = Faye::WebSocket::Client.new("ws://localhost:3002/websocket")
29       end
30
31       ws.on :open do |event|
32         opened = true
33         if timeout
34           EM::Timer.new 3 do
35             too_long = true
36             EM.stop_event_loop
37           end
38         end
39       end
40
41       ws.on :close do |event|
42         close_status = [:close, event.code, event.reason]
43         EM.stop_event_loop
44       end
45
46       yield ws
47     }
48
49     assert opened, "Should have opened web socket"
50     assert (not too_long), "Test took too long"
51     assert_equal 1000, close_status[1], "Connection closed unexpectedly (check log for errors)"
52   end
53
54   test "connect with no token" do
55     status = nil
56
57     ws_helper do |ws|
58       ws.on :message do |event|
59         d = Oj.load event.data
60         status = d["status"]
61         ws.close
62       end
63     end
64
65     assert_equal 401, status
66   end
67
68
69   test "connect, subscribe and get response" do
70     status = nil
71
72     ws_helper :admin do |ws|
73       ws.on :open do |event|
74         ws.send ({method: 'subscribe'}.to_json)
75       end
76
77       ws.on :message do |event|
78         d = Oj.load event.data
79         status = d["status"]
80         ws.close
81       end
82     end
83
84     assert_equal 200, status
85   end
86
87   test "connect, subscribe, get event" do
88     state = 1
89     spec = nil
90     ev_uuid = nil
91
92     authorize_with :admin
93
94     ws_helper :admin do |ws|
95       ws.on :open do |event|
96         ws.send ({method: 'subscribe'}.to_json)
97       end
98
99       ws.on :message do |event|
100         d = Oj.load event.data
101         case state
102         when 1
103           assert_equal 200, d["status"]
104           spec = Specimen.create
105           state = 2
106         when 2
107           ev_uuid = d["object_uuid"]
108           ws.close
109         end
110       end
111
112     end
113
114     assert_not_nil spec
115     assert_equal spec.uuid, ev_uuid
116   end
117
118   test "connect, subscribe, get two events" do
119     state = 1
120     spec = nil
121     human = nil
122     spec_ev_uuid = nil
123     human_ev_uuid = nil
124
125     authorize_with :admin
126
127     ws_helper :admin do |ws|
128       ws.on :open do |event|
129         ws.send ({method: 'subscribe'}.to_json)
130       end
131
132       ws.on :message do |event|
133         d = Oj.load event.data
134         case state
135         when 1
136           assert_equal 200, d["status"]
137           spec = Specimen.create
138           human = Human.create
139           state = 2
140         when 2
141           spec_ev_uuid = d["object_uuid"]
142           state = 3
143         when 3
144           human_ev_uuid = d["object_uuid"]
145           state = 4
146           ws.close
147         when 4
148           assert false, "Should not get any more events"
149         end
150       end
151
152     end
153
154     assert_not_nil spec
155     assert_not_nil human
156     assert_equal spec.uuid, spec_ev_uuid
157     assert_equal human.uuid, human_ev_uuid
158   end
159
160   test "connect, subscribe, filter events" do
161     state = 1
162     human = nil
163     human_ev_uuid = nil
164
165     authorize_with :admin
166
167     ws_helper :admin do |ws|
168       ws.on :open do |event|
169         ws.send ({method: 'subscribe', filters: [['object_uuid', 'is_a', 'arvados#human']]}.to_json)
170       end
171
172       ws.on :message do |event|
173         d = Oj.load event.data
174         case state
175         when 1
176           assert_equal 200, d["status"]
177           Specimen.create
178           human = Human.create
179           state = 2
180         when 2
181           human_ev_uuid = d["object_uuid"]
182           state = 3
183           ws.close
184         when 3
185           assert false, "Should not get any more events"
186         end
187       end
188
189     end
190
191     assert_not_nil human
192     assert_equal human.uuid, human_ev_uuid
193   end
194
195
196   test "connect, subscribe, multiple filters" do
197     state = 1
198     spec = nil
199     human = nil
200     spec_ev_uuid = nil
201     human_ev_uuid = nil
202
203     authorize_with :admin
204
205     ws_helper :admin do |ws|
206       ws.on :open do |event|
207         ws.send ({method: 'subscribe', filters: [['object_uuid', 'is_a', 'arvados#human']]}.to_json)
208         ws.send ({method: 'subscribe', filters: [['object_uuid', 'is_a', 'arvados#specimen']]}.to_json)
209       end
210
211       ws.on :message do |event|
212         d = Oj.load event.data
213         case state
214         when 1
215           assert_equal 200, d["status"]
216           state = 2
217         when 2
218           assert_equal 200, d["status"]
219           spec = Specimen.create
220           Trait.create # not part of filters, should not be received
221           human = Human.create
222           state = 3
223         when 3
224           spec_ev_uuid = d["object_uuid"]
225           state = 4
226         when 4
227           human_ev_uuid = d["object_uuid"]
228           state = 5
229           ws.close
230         when 5
231           assert false, "Should not get any more events"
232         end
233       end
234
235     end
236
237     assert_not_nil spec
238     assert_not_nil human
239     assert_equal spec.uuid, spec_ev_uuid
240     assert_equal human.uuid, human_ev_uuid
241   end
242
243   test "connect, subscribe, ask events starting at seq num" do
244     state = 1
245     human = nil
246     human_ev_uuid = nil
247
248     authorize_with :admin
249
250     lastid = logs(:log3).id
251     l1 = nil
252     l2 = nil
253
254     ws_helper :admin do |ws|
255       ws.on :open do |event|
256         ws.send ({method: 'subscribe', last_log_id: lastid}.to_json)
257       end
258
259       ws.on :message do |event|
260         d = Oj.load event.data
261         case state
262         when 1
263           assert_equal 200, d["status"]
264           state = 2
265         when 2
266           l1 = d["object_uuid"]
267           assert_not_nil l1, "Unexpected message: #{d}"
268           state = 3
269         when 3
270           l2 = d["object_uuid"]
271           assert_not_nil l2, "Unexpected message: #{d}"
272           state = 4
273           ws.close
274         when 4
275           assert false, "Should not get any more events"
276         end
277       end
278
279     end
280
281     assert_equal logs(:log4).object_uuid, l1
282     assert_equal logs(:log5).object_uuid, l2
283   end
284
285   test "connect, subscribe, get event, unsubscribe" do
286     state = 1
287     spec = nil
288     spec_ev_uuid = nil
289     filter_id = nil
290
291     authorize_with :admin
292
293     ws_helper :admin, false do |ws|
294       ws.on :open do |event|
295         ws.send ({method: 'subscribe'}.to_json)
296         EM::Timer.new 3 do
297           # Set a time limit on the test because after unsubscribing the server
298           # still has to process the next event (and then hopefully correctly
299           # decides not to send it because we unsubscribed.)
300           ws.close
301         end
302       end
303
304       ws.on :message do |event|
305         d = Oj.load event.data
306         case state
307         when 1
308           assert_equal 200, d["status"]
309           spec = Specimen.create
310           state = 2
311         when 2
312           spec_ev_uuid = d["object_uuid"]
313           ws.send ({method: 'unsubscribe'}.to_json)
314
315           EM::Timer.new 1 do
316             Specimen.create
317           end
318
319           state = 3
320         when 3
321           assert_equal 200, d["status"]
322           state = 4
323         when 4
324           assert false, "Should not get any more events"
325         end
326       end
327
328     end
329
330     assert_not_nil spec
331     assert_equal spec.uuid, spec_ev_uuid
332   end
333
334   test "connect, subscribe, get event, unsubscribe with filter" do
335     state = 1
336     spec = nil
337     spec_ev_uuid = nil
338
339     authorize_with :admin
340
341     ws_helper :admin, false do |ws|
342       ws.on :open do |event|
343         ws.send ({method: 'subscribe', filters: [['object_uuid', 'is_a', 'arvados#human']]}.to_json)
344         EM::Timer.new 3 do
345           # Set a time limit on the test because after unsubscribing the server
346           # still has to process the next event (and then hopefully correctly
347           # decides not to send it because we unsubscribed.)
348           ws.close
349         end
350       end
351
352       ws.on :message do |event|
353         d = Oj.load event.data
354         case state
355         when 1
356           assert_equal 200, d["status"]
357           spec = Human.create
358           state = 2
359         when 2
360           spec_ev_uuid = d["object_uuid"]
361           ws.send ({method: 'unsubscribe', filters: [['object_uuid', 'is_a', 'arvados#human']]}.to_json)
362
363           EM::Timer.new 1 do
364             Human.create
365           end
366
367           state = 3
368         when 3
369           assert_equal 200, d["status"]
370           state = 4
371         when 4
372           assert false, "Should not get any more events"
373         end
374       end
375
376     end
377
378     assert_not_nil spec
379     assert_equal spec.uuid, spec_ev_uuid
380   end
381
382
383   test "connect, subscribe, get event, try to unsubscribe with bogus filter" do
384     state = 1
385     spec = nil
386     spec_ev_uuid = nil
387     human = nil
388     human_ev_uuid = nil
389
390     authorize_with :admin
391
392     ws_helper :admin do |ws|
393       ws.on :open do |event|
394         ws.send ({method: 'subscribe'}.to_json)
395       end
396
397       ws.on :message do |event|
398         d = Oj.load event.data
399         case state
400         when 1
401           assert_equal 200, d["status"]
402           spec = Specimen.create
403           state = 2
404         when 2
405           spec_ev_uuid = d["object_uuid"]
406           ws.send ({method: 'unsubscribe', filters: [['foo', 'bar', 'baz']]}.to_json)
407
408           EM::Timer.new 1 do
409             human = Human.create
410           end
411
412           state = 3
413         when 3
414           assert_equal 404, d["status"]
415           state = 4
416         when 4
417           human_ev_uuid = d["object_uuid"]
418           state = 5
419           ws.close
420         when 5
421           assert false, "Should not get any more events"
422         end
423       end
424
425     end
426
427     assert_not_nil spec
428     assert_not_nil human
429     assert_equal spec.uuid, spec_ev_uuid
430     assert_equal human.uuid, human_ev_uuid
431   end
432
433
434
435   test "connected, not subscribed, no event" do
436     authorize_with :admin
437
438     ws_helper :admin, false do |ws|
439       ws.on :open do |event|
440         EM::Timer.new 1 do
441           Specimen.create
442         end
443
444         EM::Timer.new 3 do
445           ws.close
446         end
447       end
448
449       ws.on :message do |event|
450         assert false, "Should not get any messages, message was #{event.data}"
451       end
452     end
453   end
454
455   test "connected, not authorized to see event" do
456     state = 1
457
458     authorize_with :admin
459
460     ws_helper :active, false do |ws|
461       ws.on :open do |event|
462         ws.send ({method: 'subscribe'}.to_json)
463
464         EM::Timer.new 3 do
465           ws.close
466         end
467       end
468
469       ws.on :message do |event|
470         d = Oj.load event.data
471         case state
472         when 1
473           assert_equal 200, d["status"]
474           Specimen.create
475           state = 2
476         when 2
477           assert false, "Should not get any messages, message was #{event.data}"
478         end
479       end
480
481     end
482
483   end
484
485   test "connect, try bogus method" do
486     status = nil
487
488     ws_helper :admin do |ws|
489       ws.on :open do |event|
490         ws.send ({method: 'frobnabble'}.to_json)
491       end
492
493       ws.on :message do |event|
494         d = Oj.load event.data
495         status = d["status"]
496         ws.close
497       end
498     end
499
500     assert_equal 400, status
501   end
502
503   test "connect, missing method" do
504     status = nil
505
506     ws_helper :admin do |ws|
507       ws.on :open do |event|
508         ws.send ({fizzbuzz: 'frobnabble'}.to_json)
509       end
510
511       ws.on :message do |event|
512         d = Oj.load event.data
513         status = d["status"]
514         ws.close
515       end
516     end
517
518     assert_equal 400, status
519   end
520
521   test "connect, send malformed request" do
522     status = nil
523
524     ws_helper :admin do |ws|
525       ws.on :open do |event|
526         ws.send '<XML4EVER></XML4EVER>'
527       end
528
529       ws.on :message do |event|
530         d = Oj.load event.data
531         status = d["status"]
532         ws.close
533       end
534     end
535
536     assert_equal 400, status
537   end
538
539
540   test "connect, try subscribe too many filters" do
541     state = 1
542
543     authorize_with :admin
544
545     ws_helper :admin do |ws|
546       ws.on :open do |event|
547         (1..17).each do |i|
548           ws.send ({method: 'subscribe', filters: [['object_uuid', '=', i]]}.to_json)
549         end
550       end
551
552       ws.on :message do |event|
553         d = Oj.load event.data
554         case state
555         when (1..EventBus::MAX_FILTERS)
556           assert_equal 200, d["status"]
557           state += 1
558         when (EventBus::MAX_FILTERS+1)
559           assert_equal 403, d["status"]
560           ws.close
561         end
562       end
563
564     end
565
566     assert_equal 17, state
567
568   end
569
570 end