def test_session_synchronization(): phone_number_a = _clean_up_number('15555555555') phone_number_b = _clean_up_number('15555555554') session_a_1 = FakeSession(session_id=str(uuid4()), phone_number=phone_number_a, connection_id='Alpha') session_a_2 = FakeSession(session_id=str(uuid4()), phone_number=phone_number_a, connection_id='Beta') session_b_1 = FakeSession(session_id=str(uuid4()), phone_number=phone_number_b, connection_id='Kappa') # Nothing set yet, so it can be claimed assert XFormsSessionSynchronization.channel_is_available_for_session( session_a_1) # And so can the other one assert XFormsSessionSynchronization.channel_is_available_for_session( session_a_2) # Claim succeeds assert XFormsSessionSynchronization.claim_channel_for_session(session_a_1) # Claim for same channel fails assert not XFormsSessionSynchronization.channel_is_available_for_session( session_a_2) assert not XFormsSessionSynchronization.claim_channel_for_session( session_a_2) # But same session can re-claim it assert XFormsSessionSynchronization.claim_channel_for_session(session_a_1) # And another session can claim another channel assert XFormsSessionSynchronization.claim_channel_for_session(session_b_1) # And if the first session releases the channel XFormsSessionSynchronization.release_channel_for_session(session_a_1) # Then the contact is still set assert XFormsSessionSynchronization.get_running_session_info_for_channel( SMSChannel(BACKEND_ID, phone_number_a)).contact_id == 'Alpha' # But the other session (that couldn't before) can claim it now assert XFormsSessionSynchronization.claim_channel_for_session(session_a_2) # Trying to clear the channel claim will fail because the session is still open assert not XFormsSessionSynchronization.clear_stale_channel_claim( SMSChannel(BACKEND_ID, phone_number_a)) # But if we close the session first session_a_2.close() # The session is now "stale" so we can clear that stale channel claim assert XFormsSessionSynchronization.clear_stale_channel_claim( SMSChannel(BACKEND_ID, phone_number_a)) # If we try to clear it again it'll be a no-op and return false, since it's already cleared assert not XFormsSessionSynchronization.clear_stale_channel_claim( SMSChannel(BACKEND_ID, phone_number_a))
def get_inbound_phone_entry(msg): if msg.backend_id: backend = SQLMobileBackend.load(msg.backend_id, is_couch_id=True) if toggles.INBOUND_SMS_LENIENCY.enabled(backend.domain): p = None if toggles.ONE_PHONE_NUMBER_MULTIPLE_CONTACTS.enabled( backend.domain): running_session_info = XFormsSessionSynchronization.get_running_session_info_for_channel( SMSChannel(backend_id=msg.backend_id, phone_number=msg.phone_number)) contact_id = running_session_info.contact_id if contact_id: p = PhoneNumber.get_phone_number_for_owner( contact_id, msg.phone_number) if p is not None: return (p, True) elif running_session_info.session_id: # This would be very unusual, as it would mean the supposedly running form session # is linked to a phone number, contact pair that doesn't exist in the PhoneNumber table notify_error( "Contact from running session has no match in PhoneNumber table. " "Only known way for this to happen is if you " "unregister a phone number for a contact " "while they are in an active session.", details={'running_session_info': running_session_info}) if not backend.is_global: p = PhoneNumber.get_two_way_number_with_domain_scope( msg.phone_number, backend.domains_with_access) return (p, p is not None) return (PhoneNumber.get_reserved_number(msg.phone_number), False)
def test_get_inbound_phone_entry__one_phone_number_multiple_contacts_on__session_no_contact( self): """Fall back to lenient query if we can't find the contact returned by the sticky session""" channel = SMSChannel(self.domain2_backend.couch_id, self.phone_number) info = RunningSessionInfo(None, "fake-owner-missing") XFormsSessionSynchronization._set_running_session_info_for_channel( channel, info, 60) self.addCleanup( XFormsSessionSynchronization. _release_running_session_info_for_channel, info, channel) phone_number, has_domain_two_way_scope = self._get_inbound_phone_entry__backend_domain_2( ) self.assertEqual(phone_number.owner_id, "fake-owner-3") self.assertTrue(has_domain_two_way_scope) self.assertTrue( self.domain2_backend.domain_is_authorized(phone_number.domain))
def test_get_inbound_phone_entry__one_phone_number_multiple_contacts_on__with_session( self): """Should return the phone number of the contact associated with the session""" channel = SMSChannel(self.domain2_backend.couch_id, self.phone_number) info = RunningSessionInfo(uuid.uuid4().hex, "fake-owner-4") XFormsSessionSynchronization._set_running_session_info_for_channel( channel, info, 60) self.addCleanup( XFormsSessionSynchronization. _release_running_session_info_for_channel, info, channel) phone_number, has_domain_two_way_scope = self._get_inbound_phone_entry__backend_domain_2( ) self.assertEqual(phone_number.owner_id, "fake-owner-4") self.assertTrue(has_domain_two_way_scope) self.assertTrue( self.domain2_backend.domain_is_authorized(phone_number.domain))
def test_get_inbound_phone_entry__one_phone_number_multiple_contacts_on__session_different_domain( self): """Phone number returned belongs to a domain which does not have access to this backend. TODO: This should probably be an error or it should fall back to 'lenient' query""" channel = SMSChannel(self.domain2_backend.couch_id, self.phone_number) info = RunningSessionInfo(uuid.uuid4().hex, "fake-owner-2") XFormsSessionSynchronization._set_running_session_info_for_channel( channel, info, 60) self.addCleanup( XFormsSessionSynchronization. _release_running_session_info_for_channel, info, channel) phone_number, has_domain_two_way_scope = self._get_inbound_phone_entry__backend_domain_2( ) self.assertEqual(phone_number.owner_id, "fake-owner-2") self.assertTrue(has_domain_two_way_scope) self.assertFalse( self.domain2_backend.domain_is_authorized( phone_number.domain)) # bad
def test_pickle_roundtrip(): assert pickle.loads(pickle.dumps(SMSChannel('abc', '123'))) == SMSChannel( 'abc', '123') assert pickle.loads(pickle.dumps(RunningSessionInfo( 'xxx', 'yyy'))) == RunningSessionInfo('xxx', 'yyy')
def _clean_up_number(phone_number): XFormsSessionSynchronization._set_running_session_info_for_channel( SMSChannel(BACKEND_ID, phone_number), RunningSessionInfo(None, None), expiry=10) return phone_number
def get_channel(self): return SMSChannel(BACKEND_ID, self.phone_number)