require 'spec_helper' require 'thread' def time_it(cat, &blk) start = Time.now blk.call time = Time.now - start puts("TIME: #{cat}: #{time}") end def safety_net(&blk) begin blk.call rescue => e #Bugsnag.notify(e) @log.error("unhandled exception in EM Timer #{e}") puts "Error during processing: #{$!}" puts "Backtrace:\n\t#{e.backtrace.join("\n\t")}" end end LoginClient = Class.new do attr_accessor :onmsgblock, :onopenblock, :oncloseblock, :onerrorblock, :encode_json, :channel_id, :client_id, :user_id, :context, :trusted, :subscriptions def initialize(user) @subscriptions = Set.new @channel_id = user.id @encode_json = true end def connected? true end def onopen(&block) @onopenblock = Proc.new { |handshake| block } end def onmessage(&block) @onmsgblock= Proc.new { |data| block.call data } end def onclose(&block) @oncloseblock = Proc.new { || block.call } end def onerror(&block) @onerrorblock = Proc.new { |err| block.call err } end def close() end def send(msg) puts msg end def get_peername return "\x00\x02\x93\v\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00" # 37643, "localhost" end end class TestClient def initialize(user, router) @user = user @client = LoginClient.new(@user) @router = router end def connect @router.new_client(@client, false) end def login login_message = login_options(@user) @router.handle_login(@client, login_message) end def heartbeat(notification_id = nil, notification_seen_at = nil) heartbeat = Jampb::Heartbeat.new if notification_id heartbeat.notification_seen = notification_id else heartbeat.notification_seen = 'junk' end if notification_seen_at heartbeat.notification_seen_at = notification_seen_at.to_s end @router.handle_heartbeat(heartbeat, '1', @client) end def disconnect @client.oncloseblock.call end private def login_options(user) options = {} options["token"] = user.remember_token options["client_id"] = user.id options["client_type"] = "client" options["client_id_int"] = 1 options end end describe Router, :spec_timeout => 15 do def database_env (env) singleton = GenericState.new singleton.id = 'default' singleton.env = env singleton.save! end def rails_env (env) JamRuby::Environment.should_receive(:mode).any_number_of_times.and_return(env) end include EventedSpec::EMSpec default_timeout(1000000) em_before do puts "EM BEFORE ROUTER NEW" end subject { @router } em_after do puts "EM AFTER" end def sequence_1(user, special_heartbeat) client1 = TestClient.new(user, @router) client1.connect client1.login @router.client_lookup.count.should be 1 client1.heartbeat update_notification_at = Time.now.utc #client1.heartbeat(special_heartbeat.id.to_s, nil) client1.heartbeat(nil, update_notification_at) client1.disconnect sleep 6 user.reload user = User.find(user.id) @router.client_lookup.count.should be 0 last_notification_seen_at = user.notification_seen_at last_notification_seen_at.should_not be_nil last_notification_seen_at.to_i.should be update_notification_at.to_i end def sequence_2(user) before_count = Connection.count client1 = TestClient.new(user, @router) client1.connect client1.login @router.client_lookup.count.should be 1 after_count = Connection.count before_count.should be (after_count - 1) sleep 25 @router.client_lookup.count.should be 0 disc_count = Connection.count before_count.should be disc_count end def sequence_in_session(user) before_count = Connection.count client1 = TestClient.new(user, @router) client1.connect client1.login @router.client_lookup.count.should be 1 music_session = FactoryGirl.create(:active_music_session, :creator => user) connection = Connection.find_by_user_id(user.id) connection.music_session = music_session connection.save! client1.disconnect @router.client_lookup.count.should be 0 disc_count = Connection.count before_count.should be disc_count end def sequence_in_two_session(user1, user2) # create friendship between the two, to increase notifications FactoryGirl.create(:friendship, :user => user1, :friend => user2) FactoryGirl.create(:friendship, :user => user2, :friend => user1) before_count = Connection.count client1 = TestClient.new(user1, @router) client1.connect client1.login @router.client_lookup.count.should be 1 client1 = TestClient.new(user2, @router) client1.connect client1.login @router.client_lookup.count.should be 2 music_session = FactoryGirl.create(:active_music_session, :creator => user) connection = Connection.find_by_user_id(user1.id) connection.music_session = music_session connection.save! connection2 = Connection.find_by_user_id(user2.id) connection2.music_session = music_session connection2.save! client1.disconnect @router.client_lookup.count.should be 1 disc_count = Connection.count before_count.should be (disc_count - 1) end def delete_conn_from_under(user) before_count = Connection.count client1 = TestClient.new(user, @router) client1.connect client1.login @router.client_lookup.count.should be 1 after_count = Connection.count before_count.should be (after_count - 1) Connection.first.delete @router.periodical_check_clients @router.client_lookup.count.should be 0 disc_count = Connection.count before_count.should be disc_count end describe "stress" do before { database_env('production') rails_env('production') stub_const("ENV", {'BUILD_NUMBER' => 1}) } let(:user1) { FactoryGirl.create(:user) } let(:user2) { FactoryGirl.create(:user) } let(:user3) { FactoryGirl.create(:user) } let(:user4) { FactoryGirl.create(:user) } let(:user5) { FactoryGirl.create(:user) } let!(:other) { FactoryGirl.create(:user, last_jam_locidispid: 1) } let!(:msg1) {FactoryGirl.create(:notification_text_message, source_user: other, target_user: user1) } it "the test" do @router = Router.new() @router.connect_time_expire_client = 20 @router.connect_time_stale_client = 14 @router.heartbeat_interval_client = @router.connect_time_stale_client / 2 @router.connect_time_expire_browser = 20 @router.connect_time_stale_browser = 14 @router.max_connections_per_user = 10 @router.heartbeat_interval_browser = @router.connect_time_stale_browser / 2 @router.maximum_minutely_heartbeat_rate_browser = 10 @router.maximum_minutely_heartbeat_rate_client = 10 @router.amqp_connection_manager = AmqpConnectionManager.new(true, 4, host: 'localhost', port: 5672) @router.gateway_name = 'gateway1' @router.init em do EventMachine::PeriodicTimer.new(5) do time_it('stats_dump') { safety_net { @router.periodical_stats_dump } } end sequence_in_session(user3) sequence_in_two_session(user4, user5) delete_conn_from_under(user3) sequence_1(user1, msg1) sequence_1(user1, msg1) sequence_2(user2) done end end end end