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))
Example #2
0
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)