X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/c72f1de32f688690d1161a1852e471e88919e057..6319bc1895aaf8d2a7f944e45e52c960f931a11e:/services/api/test/integration/websocket_test.rb diff --git a/services/api/test/integration/websocket_test.rb b/services/api/test/integration/websocket_test.rb index bafab66984..925d879906 100644 --- a/services/api/test/integration/websocket_test.rb +++ b/services/api/test/integration/websocket_test.rb @@ -1,10 +1,26 @@ require 'test_helper' require 'websocket_runner' require 'oj' +require 'database_cleaner' + +DatabaseCleaner.strategy = :truncation class WebsocketTest < ActionDispatch::IntegrationTest + self.use_transactional_fixtures = false + + setup do + DatabaseCleaner.start + end + + teardown do + DatabaseCleaner.clean + end + + def ws_helper (token = nil, timeout = true) + opened = false + close_status = nil + too_long = false - def ws_helper (token = nil) EM.run { if token ws = Faye::WebSocket::Client.new("ws://localhost:3002/websocket?api_token=#{api_client_authorizations(token).api_token}") @@ -12,28 +28,33 @@ class WebsocketTest < ActionDispatch::IntegrationTest ws = Faye::WebSocket::Client.new("ws://localhost:3002/websocket") end - ws.on :close do |event| - EM.stop_event_loop + ws.on :open do |event| + opened = true + if timeout + EM::Timer.new 4 do + too_long = true if close_status.nil? + EM.stop_event_loop + end + end end - EM::Timer.new 3 do - puts "\nTest took too long" + ws.on :close do |event| + close_status = [:close, event.code, event.reason] EM.stop_event_loop end yield ws } + + assert opened, "Should have opened web socket" + assert (not too_long), "Test took too long" + assert_equal 1000, close_status[1], "Connection closed unexpectedly (check log for errors)" end test "connect with no token" do - opened = false status = nil ws_helper do |ws| - ws.on :open do |event| - opened = true - end - ws.on :message do |event| d = Oj.load event.data status = d["status"] @@ -41,18 +62,15 @@ class WebsocketTest < ActionDispatch::IntegrationTest end end - assert opened, "Should have opened web socket" assert_equal 401, status end test "connect, subscribe and get response" do - opened = false status = nil ws_helper :admin do |ws| ws.on :open do |event| - opened = true ws.send ({method: 'subscribe'}.to_json) end @@ -63,47 +81,490 @@ class WebsocketTest < ActionDispatch::IntegrationTest end end - assert opened, "Should have opened web socket" assert_equal 200, status end + test "connect, subscribe, get event" do + state = 1 + spec = nil + ev_uuid = nil + + authorize_with :admin + + ws_helper :admin do |ws| + ws.on :open do |event| + ws.send ({method: 'subscribe'}.to_json) + end + + ws.on :message do |event| + d = Oj.load event.data + case state + when 1 + assert_equal 200, d["status"] + spec = Specimen.create + state = 2 + when 2 + ev_uuid = d["object_uuid"] + ws.close + end + end + + end + + assert_not_nil spec + assert_equal spec.uuid, ev_uuid + end + + test "connect, subscribe, get two events" do + state = 1 + spec = nil + human = nil + spec_ev_uuid = nil + human_ev_uuid = nil + + authorize_with :admin + + ws_helper :admin do |ws| + ws.on :open do |event| + ws.send ({method: 'subscribe'}.to_json) + end + + ws.on :message do |event| + d = Oj.load event.data + case state + when 1 + assert_equal 200, d["status"] + spec = Specimen.create + human = Human.create + state = 2 + when 2 + spec_ev_uuid = d["object_uuid"] + state = 3 + when 3 + human_ev_uuid = d["object_uuid"] + state = 4 + ws.close + when 4 + assert false, "Should not get any more events" + end + end + + end + + assert_not_nil spec + assert_not_nil human + assert_equal spec.uuid, spec_ev_uuid + assert_equal human.uuid, human_ev_uuid + end + + test "connect, subscribe, filter events" do + state = 1 + human = nil + human_ev_uuid = nil + + authorize_with :admin + + ws_helper :admin do |ws| + ws.on :open do |event| + ws.send ({method: 'subscribe', filters: [['object_uuid', 'is_a', 'arvados#human']]}.to_json) + end + + ws.on :message do |event| + d = Oj.load event.data + case state + when 1 + assert_equal 200, d["status"] + Specimen.create + human = Human.create + state = 2 + when 2 + human_ev_uuid = d["object_uuid"] + state = 3 + ws.close + when 3 + assert false, "Should not get any more events" + end + end + + end + + assert_not_nil human + assert_equal human.uuid, human_ev_uuid + end + + + test "connect, subscribe, multiple filters" do + state = 1 + spec = nil + human = nil + spec_ev_uuid = nil + human_ev_uuid = nil + + authorize_with :admin + + ws_helper :admin do |ws| + ws.on :open do |event| + ws.send ({method: 'subscribe', filters: [['object_uuid', 'is_a', 'arvados#human']]}.to_json) + ws.send ({method: 'subscribe', filters: [['object_uuid', 'is_a', 'arvados#specimen']]}.to_json) + end + + ws.on :message do |event| + d = Oj.load event.data + case state + when 1 + assert_equal 200, d["status"] + state = 2 + when 2 + assert_equal 200, d["status"] + spec = Specimen.create + Trait.create # not part of filters, should not be received + human = Human.create + state = 3 + when 3 + spec_ev_uuid = d["object_uuid"] + state = 4 + when 4 + human_ev_uuid = d["object_uuid"] + state = 5 + ws.close + when 5 + assert false, "Should not get any more events" + end + end + + end + + assert_not_nil spec + assert_not_nil human + assert_equal spec.uuid, spec_ev_uuid + assert_equal human.uuid, human_ev_uuid + end + + test "connect, subscribe, ask events starting at seq num" do + state = 1 + human = nil + human_ev_uuid = nil + + authorize_with :admin + + lastid = logs(:log3).id + l1 = nil + l2 = nil + + ws_helper :admin do |ws| + ws.on :open do |event| + ws.send ({method: 'subscribe', last_log_id: lastid}.to_json) + end + + ws.on :message do |event| + d = Oj.load event.data + case state + when 1 + assert_equal 200, d["status"] + state = 2 + when 2 + l1 = d["object_uuid"] + assert_not_nil l1, "Unexpected message: #{d}" + state = 3 + when 3 + l2 = d["object_uuid"] + assert_not_nil l2, "Unexpected message: #{d}" + state = 4 + ws.close + when 4 + assert false, "Should not get any more events" + end + end + + end + + assert_equal logs(:log4).object_uuid, l1 + assert_equal logs(:log5).object_uuid, l2 + end + + test "connect, subscribe, get event, unsubscribe" do + state = 1 + spec = nil + spec_ev_uuid = nil + filter_id = nil + + authorize_with :admin + + ws_helper :admin, false do |ws| + ws.on :open do |event| + ws.send ({method: 'subscribe'}.to_json) + EM::Timer.new 3 do + # Set a time limit on the test because after unsubscribing the server + # still has to process the next event (and then hopefully correctly + # decides not to send it because we unsubscribed.) + ws.close + end + end + + ws.on :message do |event| + d = Oj.load event.data + case state + when 1 + assert_equal 200, d["status"] + spec = Specimen.create + state = 2 + when 2 + spec_ev_uuid = d["object_uuid"] + ws.send ({method: 'unsubscribe'}.to_json) + + EM::Timer.new 1 do + Specimen.create + end + + state = 3 + when 3 + assert_equal 200, d["status"] + state = 4 + when 4 + assert false, "Should not get any more events" + end + end + + end + + assert_not_nil spec + assert_equal spec.uuid, spec_ev_uuid + end + + test "connect, subscribe, get event, unsubscribe with filter" do + state = 1 + spec = nil + spec_ev_uuid = nil + + authorize_with :admin + + ws_helper :admin, false do |ws| + ws.on :open do |event| + ws.send ({method: 'subscribe', filters: [['object_uuid', 'is_a', 'arvados#human']]}.to_json) + EM::Timer.new 3 do + # Set a time limit on the test because after unsubscribing the server + # still has to process the next event (and then hopefully correctly + # decides not to send it because we unsubscribed.) + ws.close + end + end + + ws.on :message do |event| + d = Oj.load event.data + case state + when 1 + assert_equal 200, d["status"] + spec = Human.create + state = 2 + when 2 + spec_ev_uuid = d["object_uuid"] + ws.send ({method: 'unsubscribe', filters: [['object_uuid', 'is_a', 'arvados#human']]}.to_json) + + EM::Timer.new 1 do + Human.create + end + + state = 3 + when 3 + assert_equal 200, d["status"] + state = 4 + when 4 + assert false, "Should not get any more events" + end + end + + end + + assert_not_nil spec + assert_equal spec.uuid, spec_ev_uuid + end + + + test "connect, subscribe, get event, try to unsubscribe with bogus filter" do + state = 1 + spec = nil + spec_ev_uuid = nil + human = nil + human_ev_uuid = nil + + authorize_with :admin + + ws_helper :admin do |ws| + ws.on :open do |event| + ws.send ({method: 'subscribe'}.to_json) + end + + ws.on :message do |event| + d = Oj.load event.data + case state + when 1 + assert_equal 200, d["status"] + spec = Specimen.create + state = 2 + when 2 + spec_ev_uuid = d["object_uuid"] + ws.send ({method: 'unsubscribe', filters: [['foo', 'bar', 'baz']]}.to_json) + + EM::Timer.new 1 do + human = Human.create + end + + state = 3 + when 3 + assert_equal 404, d["status"] + state = 4 + when 4 + human_ev_uuid = d["object_uuid"] + state = 5 + ws.close + when 5 + assert false, "Should not get any more events" + end + end + + end + + assert_not_nil spec + assert_not_nil human + assert_equal spec.uuid, spec_ev_uuid + assert_equal human.uuid, human_ev_uuid + end + + + + test "connected, not subscribed, no event" do + authorize_with :admin + + ws_helper :admin, false do |ws| + ws.on :open do |event| + EM::Timer.new 1 do + Specimen.create + end + + EM::Timer.new 3 do + ws.close + end + end + + ws.on :message do |event| + assert false, "Should not get any messages, message was #{event.data}" + end + end + end + + test "connected, not authorized to see event" do + state = 1 + + authorize_with :admin + + ws_helper :active, false do |ws| + ws.on :open do |event| + ws.send ({method: 'subscribe'}.to_json) + + EM::Timer.new 3 do + ws.close + end + end + + ws.on :message do |event| + d = Oj.load event.data + case state + when 1 + assert_equal 200, d["status"] + Specimen.create + state = 2 + when 2 + assert false, "Should not get any messages, message was #{event.data}" + end + end + + end + + end + + test "connect, try bogus method" do + status = nil + + ws_helper :admin do |ws| + ws.on :open do |event| + ws.send ({method: 'frobnabble'}.to_json) + end + + ws.on :message do |event| + d = Oj.load event.data + status = d["status"] + ws.close + end + end + + assert_equal 400, status + end + + test "connect, missing method" do + status = nil + + ws_helper :admin do |ws| + ws.on :open do |event| + ws.send ({fizzbuzz: 'frobnabble'}.to_json) + end + + ws.on :message do |event| + d = Oj.load event.data + status = d["status"] + ws.close + end + end + + assert_equal 400, status + end + + test "connect, send malformed request" do + status = nil + + ws_helper :admin do |ws| + ws.on :open do |event| + ws.send '' + end + + ws.on :message do |event| + d = Oj.load event.data + status = d["status"] + ws.close + end + end + + assert_equal 400, status + end + - # test "connect, subscribe, get event" do - # opened = false - # state = 1 - # spec_uuid = nil - # ev_uuid = nil - - # puts "user #{Thread.current[:user]}" - # authorize_with :admin - # puts "user #{Thread.current[:user]}" - - # ws_helper :admin do |ws| - # ws.on :open do |event| - # puts "XXX" - # opened = true - # ws.send ({method: 'subscribe'}.to_json) - # end - - # ws.on :message do |event| - # d = Oj.load event.data - # puts d - # case state - # when 1 - # assert_equal 200, d["status"] - # spec_uuid = Specimen.create.save.uuid - # state = 2 - # when 2 - # ev_uuid = d["uuid"] - # ws.close - # end - # end - - # end - - # assert opened, "Should have opened web socket" - # assert_not spec_uuid.nil? - # assert_equal spec_uuid, ev_uuid - # end + test "connect, try subscribe too many filters" do + state = 1 + + authorize_with :admin + + ws_helper :admin do |ws| + ws.on :open do |event| + (1..17).each do |i| + ws.send ({method: 'subscribe', filters: [['object_uuid', '=', i]]}.to_json) + end + end + + ws.on :message do |event| + d = Oj.load event.data + case state + when (1..EventBus::MAX_FILTERS) + assert_equal 200, d["status"] + state += 1 + when (EventBus::MAX_FILTERS+1) + assert_equal 403, d["status"] + ws.close + end + end + + end + + assert_equal 17, state + + end end