Beispiel #1
0
class QueryDuringRegistration(LibyaRapidTest):
    """
    Voter query should work any time.
    """

    def setUp(self):
        self.conn = self.lookup_connections(identities='111')[0]
        self.good_nid = get_random_number_string(length=constants.NID_LENGTH)
        self.fields = {'to_addr': settings.REGISTRATION_SHORT_CODE}
        self.msg = u"%s" % (self.good_nid)
        self.election = ElectionFactory(polling_start_time=PAST_DAY, polling_end_time=FUTURE_DAY)

    def test_query_before_reg_opens(self, mock_lookup):
        RegistrationPeriodFactory(start_time=FUTURE_DAY, end_time=FUTURE_DAY)
        self.receive(self.msg, self.conn, fields=self.fields)
        self.assertTrue(mock_lookup.called)

    def test_query_during_reg(self, mock_lookup):
        RegistrationPeriodFactory(start_time=PAST_DAY, end_time=FUTURE_DAY)
        mock_lookup.return_value = Result('1', constants.VOTER_QUERY_NOT_FOUND)
        self.receive(self.msg, self.conn, fields=self.fields)
        self.assertTrue(mock_lookup.called)

    def test_query_after_reg_closes(self, mock_lookup):
        # after reg closes but before polling is over
        self.election.polling_start_time = FUTURE_DAY
        self.election.save()
        RegistrationPeriodFactory(start_time=PAST_DAY, end_time=PAST_DAY)
        mock_lookup.return_value = Result('1', constants.VOTER_QUERY_NOT_FOUND)
        self.receive(self.msg, self.conn, fields=self.fields)
        self.assertTrue(mock_lookup.called)

    def test_query_after_polling(self, mock_lookup):
        # after polling is over, voters can still check their registrations
        RegistrationPeriodFactory(start_time=PAST_DAY, end_time=PAST_DAY)
        self.election.polling_end_time = PAST_DAY
        self.election.save()
        self.receive(self.msg, self.conn, fields=self.fields)
        self.assertTrue(mock_lookup.called)
Beispiel #2
0
class QueryDuringRegistration(LibyaRapidTest):
    """
    Voter query should work any time.
    """
    def setUp(self):
        self.conn = self.lookup_connections(identities='111')[0]
        self.good_nid = get_random_number_string(length=constants.NID_LENGTH)
        self.fields = {'to_addr': settings.REGISTRATION_SHORT_CODE}
        self.msg = u"%s" % (self.good_nid)
        self.election = ElectionFactory(polling_start_time=PAST_DAY,
                                        polling_end_time=FUTURE_DAY)

    def test_query_before_reg_opens(self, mock_lookup):
        RegistrationPeriodFactory(start_time=FUTURE_DAY, end_time=FUTURE_DAY)
        self.receive(self.msg, self.conn, fields=self.fields)
        self.assertTrue(mock_lookup.called)

    def test_query_during_reg(self, mock_lookup):
        RegistrationPeriodFactory(start_time=PAST_DAY, end_time=FUTURE_DAY)
        mock_lookup.return_value = Result('1', constants.VOTER_QUERY_NOT_FOUND)
        self.receive(self.msg, self.conn, fields=self.fields)
        self.assertTrue(mock_lookup.called)

    def test_query_after_reg_closes(self, mock_lookup):
        # after reg closes but before polling is over
        self.election.polling_start_time = FUTURE_DAY
        self.election.save()
        RegistrationPeriodFactory(start_time=PAST_DAY, end_time=PAST_DAY)
        mock_lookup.return_value = Result('1', constants.VOTER_QUERY_NOT_FOUND)
        self.receive(self.msg, self.conn, fields=self.fields)
        self.assertTrue(mock_lookup.called)

    def test_query_after_polling(self, mock_lookup):
        # after polling is over, voters can still check their registrations
        RegistrationPeriodFactory(start_time=PAST_DAY, end_time=PAST_DAY)
        self.election.polling_end_time = PAST_DAY
        self.election.save()
        self.receive(self.msg, self.conn, fields=self.fields)
        self.assertTrue(mock_lookup.called)
Beispiel #3
0
class PeriodsTest(LibyaTest):
    """
    Match requirements in Phases google spreadsheet:
    https://docs.google.com/a/caktusgroup.com/spreadsheet/ccc?
    key=0ApHYnffwYN6edDJSVmZaeXBqWDZnVk9ReTh6RWEyRWc&usp=drive_web#gid=0
    """
    def setUp(self):
        # Default to polling in progress
        self.election = ElectionFactory(polling_start_time=PAST_DAY,
                                        polling_end_time=FUTURE_DAY)

    def test_before_registration_opens_period(self):
        self.election.polling_start_time = FUTURE_DAY
        self.election.save()
        RegistrationPeriodFactory(start_time=FUTURE_DAY, end_time=FUTURE_DAY)
        self.assertFalse(tool_1_enabled())
        self.assertFalse(polling_reports_enabled())

    def test_during_registration(self):
        self.election.polling_start_time = FUTURE_DAY
        self.election.save()
        RegistrationPeriodFactory(start_time=PAST_DAY, end_time=FUTURE_DAY)
        self.assertTrue(tool_1_enabled())
        self.assertFalse(polling_reports_enabled())

    def test_after_registration_before_polling(self):
        self.election.polling_start_time = FUTURE_DAY
        self.election.save()
        RegistrationPeriodFactory(start_time=PAST_DAY, end_time=PAST_DAY)
        self.assertFalse(tool_1_enabled())
        self.assertFalse(polling_reports_enabled())

    def test_polling_period(self):
        RegistrationPeriodFactory(start_time=PAST_DAY, end_time=PAST_DAY)
        self.assertFalse(tool_1_enabled())
        self.assertTrue(polling_reports_enabled())

    def test_post_polling_reporting_period(self):
        self.election.polling_end_time = FIFTEEN_HOURS_AGO
        self.election.save()
        RegistrationPeriodFactory(start_time=PAST_DAY, end_time=PAST_DAY)
        self.assertFalse(tool_1_enabled())
        self.assertTrue(polling_reports_enabled())

    def test_counting_period(self):
        self.election.polling_end_time = PAST_DAY
        self.election.save()
        RegistrationPeriodFactory(start_time=PAST_DAY, end_time=PAST_DAY)
        self.assertFalse(tool_1_enabled())
        self.assertFalse(polling_reports_enabled())

    def test_reopened_reg_period(self):
        self.election.polling_end_time = PAST_DAY
        self.election.save()
        RegistrationPeriodFactory(start_time=PAST_DAY, end_time=PAST_DAY)
        self.assertFalse(tool_1_enabled())
        self.assertFalse(polling_reports_enabled())

    def test_after_reopened_reg_period(self):
        self.election.polling_end_time = PAST_DAY
        self.election.save()
        RegistrationPeriodFactory(start_time=PAST_DAY, end_time=PAST_DAY)
        self.assertFalse(tool_1_enabled())
        self.assertFalse(polling_reports_enabled())
Beispiel #4
0
class PeriodsTest(LibyaTest):
    """
    Match requirements in Phases google spreadsheet:
    https://docs.google.com/a/caktusgroup.com/spreadsheet/ccc?
    key=0ApHYnffwYN6edDJSVmZaeXBqWDZnVk9ReTh6RWEyRWc&usp=drive_web#gid=0
    """

    def setUp(self):
        # Default to polling in progress
        self.election = ElectionFactory(polling_start_time=PAST_DAY, polling_end_time=FUTURE_DAY)

    def test_before_registration_opens_period(self):
        self.election.polling_start_time = FUTURE_DAY
        self.election.save()
        RegistrationPeriodFactory(start_time=FUTURE_DAY, end_time=FUTURE_DAY)
        self.assertFalse(tool_1_enabled())
        self.assertFalse(polling_reports_enabled())

    def test_during_registration(self):
        self.election.polling_start_time = FUTURE_DAY
        self.election.save()
        RegistrationPeriodFactory(start_time=PAST_DAY, end_time=FUTURE_DAY)
        self.assertTrue(tool_1_enabled())
        self.assertFalse(polling_reports_enabled())

    def test_after_registration_before_polling(self):
        self.election.polling_start_time = FUTURE_DAY
        self.election.save()
        RegistrationPeriodFactory(start_time=PAST_DAY, end_time=PAST_DAY)
        self.assertFalse(tool_1_enabled())
        self.assertFalse(polling_reports_enabled())

    def test_polling_period(self):
        RegistrationPeriodFactory(start_time=PAST_DAY, end_time=PAST_DAY)
        self.assertFalse(tool_1_enabled())
        self.assertTrue(polling_reports_enabled())

    def test_post_polling_reporting_period(self):
        self.election.polling_end_time = FIFTEEN_HOURS_AGO
        self.election.save()
        RegistrationPeriodFactory(start_time=PAST_DAY, end_time=PAST_DAY)
        self.assertFalse(tool_1_enabled())
        self.assertTrue(polling_reports_enabled())

    def test_counting_period(self):
        self.election.polling_end_time = PAST_DAY
        self.election.save()
        RegistrationPeriodFactory(start_time=PAST_DAY, end_time=PAST_DAY)
        self.assertFalse(tool_1_enabled())
        self.assertFalse(polling_reports_enabled())

    def test_reopened_reg_period(self):
        self.election.polling_end_time = PAST_DAY
        self.election.save()
        RegistrationPeriodFactory(start_time=PAST_DAY, end_time=PAST_DAY)
        self.assertFalse(tool_1_enabled())
        self.assertFalse(polling_reports_enabled())

    def test_after_reopened_reg_period(self):
        self.election.polling_end_time = PAST_DAY
        self.election.save()
        RegistrationPeriodFactory(start_time=PAST_DAY, end_time=PAST_DAY)
        self.assertFalse(tool_1_enabled())
        self.assertFalse(polling_reports_enabled())
Beispiel #5
0
class HandlerTest(LibyaRapidTest):
    def setUp(self):
        # lookup_connections takes any identity and creates connections.
        # It's better than create_connection because it uses 'mockbackend'
        # which keeps track of sent messages.
        self.conn = self.lookup_connections(identities=['111'])[0]
        self.good_nid = get_random_number_string(length=constants.NID_LENGTH)
        self.good_center_id = get_random_number_string(length=constants.CENTER_ID_LENGTH)
        self.fields = {'to_addr': settings.REGISTRATION_SHORT_CODE}
        self.reg_period = RegistrationPeriodFactory(start_time=PAST_DAY, end_time=FUTURE_DAY)
        self.election = ElectionFactory(polling_start_time=PAST_DAY, polling_end_time=FUTURE_DAY)

    def test_new_logic_rename_me(self):
        phone1 = get_random_number_string(length=10)
        phone2 = get_random_number_string(length=10)
        nid1 = self.good_nid
        ppc1 = self.good_center_id
        ppc2 = get_random_number_string(length=constants.CENTER_ID_LENGTH)
        ppc3 = get_random_number_string(length=constants.CENTER_ID_LENGTH)
        ppc4 = get_random_number_string(length=constants.CENTER_ID_LENGTH)
        ppc5 = get_random_number_string(length=constants.CENTER_ID_LENGTH)
        CitizenFactory(national_id=nid1)
        RegistrationCenterFactory(center_id=ppc1)
        RegistrationCenterFactory(center_id=ppc2)
        RegistrationCenterFactory(center_id=ppc3)
        RegistrationCenterFactory(center_id=ppc4)
        RegistrationCenterFactory(center_id=ppc5)
        # Each item in the test_data array is one test.
        # Each test contains one or more steps.
        # Each step contains the phone we receive a message from, the
        # registration center code in that message, the expected response,
        # and the expected registration state after that message.
        # All messages are for the same NID.
        # After each test item, the registrations are reset (deleted).

        # MSG1: you are registered, use same phone to change
        # MSG4: only one more time
        # MSG5: that was your last time

        test_data = [
            # Repeat the same registration from the same phone
            [
                (phone1, ppc1, constants.MESSAGE_1, ppc1),
                (phone1, ppc1, constants.MESSAGE_1, ppc1),
                (phone1, ppc1, constants.MESSAGE_1, ppc1),
                (phone1, ppc1, constants.MESSAGE_1, ppc1),
            ],
            # Keep trying to change your registration from the same phone
            [
                (phone1, ppc1, constants.MESSAGE_1, ppc1),
                (phone1, ppc2, constants.MESSAGE_1, ppc2),  # one change is okay
                (phone1, ppc3, constants.MESSAGE_4, ppc3),  # twice is okay but only one left
                (phone1, ppc4, constants.MESSAGE_5, ppc4),  # three is okay but last time
                (phone1, ppc3, constants.MESSAGE_6, ppc4),  # too many, sorry
                (phone1, ppc5, constants.MESSAGE_6, ppc4),  # still too many
            ],
            # Repeat the same registration from a different phone
            [
                (phone1, ppc1, constants.MESSAGE_1, ppc1),
                (phone2, ppc1, constants.MESSAGE_7, ppc1),
                (phone1, ppc1, constants.MESSAGE_1, ppc1),
                (phone2, ppc1, constants.MESSAGE_7, ppc1),
                (phone1, ppc1, constants.MESSAGE_1, ppc1),
                (phone2, ppc1, constants.MESSAGE_7, ppc1),
            ],
            # Try to change registration from a different phone
            [
                (phone1, ppc1, constants.MESSAGE_1, ppc1),
                (phone2, ppc2, constants.MESSAGE_2, ppc1),  # sorry charlie
                # then from the original phone
                (phone1, ppc2, constants.MESSAGE_1, ppc2),  # that's ok - change 1
                # again from another phone
                (phone2, ppc3, constants.MESSAGE_2, ppc2),
                # back to original phone
                (phone1, ppc3, constants.MESSAGE_4, ppc3),  # change 2 - 1 left
                # try other phone again
                (phone2, ppc4, constants.MESSAGE_2, ppc3),
                # original phone again
                (phone1, ppc2, constants.MESSAGE_5, ppc2),  # 3rd change is the last
                (phone1, ppc4, constants.MESSAGE_6, ppc2),  # too many, sorry
                (phone1, ppc5, constants.MESSAGE_6, ppc2),  # still too many
                # Once you've used up your changes, even sending your current
                # registration results in message 6
                (phone1, ppc2, constants.MESSAGE_6, ppc2),
            ],
            # After changing the registration, the repeat count is reset
            [
                (phone1, ppc1, constants.MESSAGE_1, ppc1),
                (phone1, ppc1, constants.MESSAGE_1, ppc1),  # 2nd time - msg 1
                (phone1, ppc1, constants.MESSAGE_1, ppc1),  # 3rd time - msg 1 (enhanced)
                (phone1, ppc2, constants.MESSAGE_1, ppc2),   # change #1 reg - reset counter
                (phone1, ppc2, constants.MESSAGE_1, ppc2),  # 2nd time, same data as #1 change
                (phone1, ppc2, constants.MESSAGE_1, ppc2),  # 3rd time, same data as #1 change
                (phone1, ppc3, constants.MESSAGE_4, ppc3),   # change reg - reset counter
                (phone1, ppc2, constants.MESSAGE_5, ppc2),  # final change
                (phone1, ppc2, constants.MESSAGE_6, ppc2),   # no more changes, always 6
            ],
        ]

        for one_test in test_data:
            # Reset state
            Registration.objects.unfiltered().delete()
            for from_phone, center_id, expected_response, expected_center_id in one_test:
                text = "%s*%s" % (nid1, center_id)
                conn = self.lookup_connections(identities=[from_phone])[0]
                self.receive(text, conn, fields=self.fields)

                # Check the response
                self.assertEqual(expected_response, self.get_last_response_code())
                # Check the state
                reg = Registration.objects.get(citizen__national_id=nid1)
                self.assertEqual(int(expected_center_id), reg.registration_center.center_id)

    def test_input_triggers_proper_response(self):
        short_center_id = u'%s*%s' % (
            self.good_nid, get_random_number_string(length=constants.CENTER_ID_LENGTH - 1))
        long_center_id = u'%s*%s' % (
            self.good_nid, get_random_number_string(length=constants.CENTER_ID_LENGTH + 1))
        short_nid = u"%s*%s" % (
            get_random_number_string(length=constants.NID_LENGTH - 1), self.good_center_id)
        long_nid = u"%s*%s" % (
            get_random_number_string(length=constants.NID_LENGTH + 1), self.good_center_id)
        three_ids = u"%s*%s*123" % (self.good_nid, self.good_center_id)
        io_table = [
            (u"garbage", constants.MESSAGE_INCORRECT),
            (get_random_number_string(), constants.VOTER_QUERY_NID_WRONG_LENGTH),
            (short_center_id, constants.RESPONSE_CENTER_ID_WRONG_LENGTH),
            (long_center_id, constants.RESPONSE_CENTER_ID_WRONG_LENGTH),
            (short_nid, constants.RESPONSE_NID_WRONG_LENGTH),
            (long_nid, constants.RESPONSE_NID_WRONG_LENGTH),
            (three_ids, constants.MESSAGE_INCORRECT),
        ]
        # send the messages
        for (input, _) in io_table:
            self.receive(input, self.conn, fields=self.fields)
        # strip out our split messages
        outputs = [o.fields['message_code'] for o in self.outbound if not o.fields.get('split')]
        for (i, (_, expected_output)) in enumerate(io_table):
            self.assertEqual(outputs[i], expected_output)

    def test_unexpected_error(self):
        with patch.object(PatternHandler, 'dispatch') as dispatch:
            dispatch.side_effect = ValueError
            self.receive("Anything", self.conn, fields=self.fields)
        self.assertEqual(self.outbound[0].fields['message_code'],
                         constants.RESPONSE_SERVER_ERROR)

    @patch('register.handlers.process_registration_request', autospec=True)
    def test_valid_nid(self, mock_prr):
        center_name = u"A Random Center"
        person_name = u"A Random Name"
        msg = u"%s*%s" % (self.good_nid, self.good_center_id)
        mock_prr.return_value = Result('', constants.RESPONSE_VALID_REGISTRATION,
                                       dict(person=person_name,
                                            centre=center_name,
                                            code=int(self.good_center_id)))
        self.receive(msg, self.conn, fields=self.fields)
        self.assertEqual(self.outbound[0].fields['message_code'],
                         constants.RESPONSE_VALID_REGISTRATION)
        # message was saved
        sms = SMS.objects.get(message=msg)
        self.assertEqual(SMS.REGISTRATION, sms.msg_type)
        # response was saved
        sms = SMS.objects.filter(to_number=self.conn.identity)
        self.assertNotEqual(sms.count(), 0)

    @patch('register.processors.process_registration_request', autospec=True)
    def test_invalid_center_id(self, mock_prr):
        msg = u"%s*%s" % (self.good_nid, self.good_center_id)
        mock_prr.return_value = Result('', constants.RESPONSE_CENTER_ID_INVALID)
        self.receive(msg, self.conn, fields=self.fields)
        self.assertEqual(self.get_last_response_code(), constants.RESPONSE_CENTER_ID_INVALID)

    @patch('register.handlers.registration_allowed')
    def test_outside_registration_period(self, mock_is_open):
        mock_is_open.return_value = False
        RegistrationCenterFactory(center_id=self.good_center_id)
        msg = u"%s*%s" % (self.good_nid, self.good_center_id)
        self.receive(msg, self.conn, fields=self.fields)
        self.assertEqual(self.get_last_response_code(), constants.REGISTRATION_NOT_OPEN)

    def test_not_started(self):
        self.reg_period.start_time = FUTURE_DAY
        self.reg_period.save()
        RegistrationCenterFactory(center_id=self.good_center_id)
        msg = u"%s*%s" % (self.good_nid, self.good_center_id)
        self.receive(msg, self.conn, fields=self.fields)
        self.assertEqual(self.get_last_response_code(), constants.REGISTRATION_NOT_OPEN)

    def test_has_ended(self):
        self.reg_period.end_time = PAST_DAY
        self.reg_period.save()
        self.election.polling_end_time = PAST_DAY
        self.election.save()
        RegistrationCenterFactory(center_id=self.good_center_id)
        msg = u"%s*%s" % (self.good_nid, self.good_center_id)
        self.receive(msg, self.conn, fields=self.fields)
        self.assertEqual(self.get_last_response_code(), constants.REGISTRATION_NOT_OPEN)

    def test_we_only_handle_our_shortcode(self):
        msg = u"garbage"
        not_our_shortcode = '11111'
        self.receive(msg, self.conn, fields={'to_addr': not_our_shortcode})
        # We do send a default response, but we test that more below.
        self.assertEqual(1, len(self.full_outbound()))

    def test_that_we_save_incoming_sms_when_closed(self):
        self.reg_period.start_time = FUTURE_DAY
        self.reg_period.save()
        self.election.polling_start_time = FUTURE_DAY
        self.election.save()
        msg = u"garbage"
        for shortcode in set((settings.REGISTRATION_SHORT_CODE,
                              settings.VOTER_QUERY_SHORT_CODE, settings.REPORTS_SHORT_CODE)):
            self.receive(msg, self.conn, fields={'to_addr': shortcode})
            # test that we save the incoming SMS
            incoming = SMS.objects.filter(to_number=shortcode)
            self.assertEqual(len(incoming), 1)
Beispiel #6
0
class HandlerTest(LibyaRapidTest):
    def setUp(self):
        # lookup_connections takes any identity and creates connections.
        # It's better than create_connection because it uses 'mockbackend'
        # which keeps track of sent messages.
        self.conn = self.lookup_connections(identities=['111'])[0]
        self.good_nid = get_random_number_string(length=constants.NID_LENGTH)
        self.good_center_id = get_random_number_string(
            length=constants.CENTER_ID_LENGTH)
        self.fields = {'to_addr': settings.REGISTRATION_SHORT_CODE}
        self.reg_period = RegistrationPeriodFactory(start_time=PAST_DAY,
                                                    end_time=FUTURE_DAY)
        self.election = ElectionFactory(polling_start_time=PAST_DAY,
                                        polling_end_time=FUTURE_DAY)

    def test_new_logic_rename_me(self):
        phone1 = get_random_number_string(length=10)
        phone2 = get_random_number_string(length=10)
        nid1 = self.good_nid
        ppc1 = self.good_center_id
        ppc2 = get_random_number_string(length=constants.CENTER_ID_LENGTH)
        ppc3 = get_random_number_string(length=constants.CENTER_ID_LENGTH)
        ppc4 = get_random_number_string(length=constants.CENTER_ID_LENGTH)
        ppc5 = get_random_number_string(length=constants.CENTER_ID_LENGTH)
        CitizenFactory(national_id=nid1)
        RegistrationCenterFactory(center_id=ppc1)
        RegistrationCenterFactory(center_id=ppc2)
        RegistrationCenterFactory(center_id=ppc3)
        RegistrationCenterFactory(center_id=ppc4)
        RegistrationCenterFactory(center_id=ppc5)
        # Each item in the test_data array is one test.
        # Each test contains one or more steps.
        # Each step contains the phone we receive a message from, the
        # registration center code in that message, the expected response,
        # and the expected registration state after that message.
        # All messages are for the same NID.
        # After each test item, the registrations are reset (deleted).

        # MSG1: you are registered, use same phone to change
        # MSG4: only one more time
        # MSG5: that was your last time

        test_data = [
            # Repeat the same registration from the same phone
            [
                (phone1, ppc1, constants.MESSAGE_1, ppc1),
                (phone1, ppc1, constants.MESSAGE_1, ppc1),
                (phone1, ppc1, constants.MESSAGE_1, ppc1),
                (phone1, ppc1, constants.MESSAGE_1, ppc1),
            ],
            # Keep trying to change your registration from the same phone
            [
                (phone1, ppc1, constants.MESSAGE_1, ppc1),
                (phone1, ppc2, constants.MESSAGE_1,
                 ppc2),  # one change is okay
                (phone1, ppc3, constants.MESSAGE_4,
                 ppc3),  # twice is okay but only one left
                (phone1, ppc4, constants.MESSAGE_5,
                 ppc4),  # three is okay but last time
                (phone1, ppc3, constants.MESSAGE_6, ppc4),  # too many, sorry
                (phone1, ppc5, constants.MESSAGE_6, ppc4),  # still too many
            ],
            # Repeat the same registration from a different phone
            [
                (phone1, ppc1, constants.MESSAGE_1, ppc1),
                (phone2, ppc1, constants.MESSAGE_7, ppc1),
                (phone1, ppc1, constants.MESSAGE_1, ppc1),
                (phone2, ppc1, constants.MESSAGE_7, ppc1),
                (phone1, ppc1, constants.MESSAGE_1, ppc1),
                (phone2, ppc1, constants.MESSAGE_7, ppc1),
            ],
            # Try to change registration from a different phone
            [
                (phone1, ppc1, constants.MESSAGE_1, ppc1),
                (phone2, ppc2, constants.MESSAGE_2, ppc1),  # sorry charlie
                # then from the original phone
                (phone1, ppc2, constants.MESSAGE_1, ppc2
                 ),  # that's ok - change 1
                # again from another phone
                (phone2, ppc3, constants.MESSAGE_2, ppc2),
                # back to original phone
                (phone1, ppc3, constants.MESSAGE_4, ppc3),  # change 2 - 1 left
                # try other phone again
                (phone2, ppc4, constants.MESSAGE_2, ppc3),
                # original phone again
                (phone1, ppc2, constants.MESSAGE_5, ppc2
                 ),  # 3rd change is the last
                (phone1, ppc4, constants.MESSAGE_6, ppc2),  # too many, sorry
                (phone1, ppc5, constants.MESSAGE_6, ppc2),  # still too many
                # Once you've used up your changes, even sending your current
                # registration results in message 6
                (phone1, ppc2, constants.MESSAGE_6, ppc2),
            ],
            # After changing the registration, the repeat count is reset
            [
                (phone1, ppc1, constants.MESSAGE_1, ppc1),
                (phone1, ppc1, constants.MESSAGE_1, ppc1),  # 2nd time - msg 1
                (phone1, ppc1, constants.MESSAGE_1,
                 ppc1),  # 3rd time - msg 1 (enhanced)
                (phone1, ppc2, constants.MESSAGE_1,
                 ppc2),  # change #1 reg - reset counter
                (phone1, ppc2, constants.MESSAGE_1,
                 ppc2),  # 2nd time, same data as #1 change
                (phone1, ppc2, constants.MESSAGE_1,
                 ppc2),  # 3rd time, same data as #1 change
                (phone1, ppc3, constants.MESSAGE_4,
                 ppc3),  # change reg - reset counter
                (phone1, ppc2, constants.MESSAGE_5, ppc2),  # final change
                (phone1, ppc2, constants.MESSAGE_6,
                 ppc2),  # no more changes, always 6
            ],
        ]

        for one_test in test_data:
            # Reset state
            Registration.objects.unfiltered().delete()
            for from_phone, center_id, expected_response, expected_center_id in one_test:
                text = "%s*%s" % (nid1, center_id)
                conn = self.lookup_connections(identities=[from_phone])[0]
                self.receive(text, conn, fields=self.fields)

                # Check the response
                self.assertEqual(expected_response,
                                 self.get_last_response_code())
                # Check the state
                reg = Registration.objects.get(citizen__national_id=nid1)
                self.assertEqual(int(expected_center_id),
                                 reg.registration_center.center_id)

    def test_input_triggers_proper_response(self):
        short_center_id = u'%s*%s' % (
            self.good_nid,
            get_random_number_string(length=constants.CENTER_ID_LENGTH - 1))
        long_center_id = u'%s*%s' % (
            self.good_nid,
            get_random_number_string(length=constants.CENTER_ID_LENGTH + 1))
        short_nid = u"%s*%s" % (get_random_number_string(
            length=constants.NID_LENGTH - 1), self.good_center_id)
        long_nid = u"%s*%s" % (get_random_number_string(
            length=constants.NID_LENGTH + 1), self.good_center_id)
        three_ids = u"%s*%s*123" % (self.good_nid, self.good_center_id)
        io_table = [
            (u"garbage", constants.MESSAGE_INCORRECT),
            (get_random_number_string(),
             constants.VOTER_QUERY_NID_WRONG_LENGTH),
            (short_center_id, constants.RESPONSE_CENTER_ID_WRONG_LENGTH),
            (long_center_id, constants.RESPONSE_CENTER_ID_WRONG_LENGTH),
            (short_nid, constants.RESPONSE_NID_WRONG_LENGTH),
            (long_nid, constants.RESPONSE_NID_WRONG_LENGTH),
            (three_ids, constants.MESSAGE_INCORRECT),
        ]
        # send the messages
        for (input, _) in io_table:
            self.receive(input, self.conn, fields=self.fields)
        # strip out our split messages
        outputs = [
            o.fields['message_code'] for o in self.outbound
            if not o.fields.get('split')
        ]
        for (i, (_, expected_output)) in enumerate(io_table):
            self.assertEqual(outputs[i], expected_output)

    def test_unexpected_error(self):
        with patch.object(PatternHandler, 'dispatch') as dispatch:
            dispatch.side_effect = ValueError
            self.receive("Anything", self.conn, fields=self.fields)
        self.assertEqual(self.outbound[0].fields['message_code'],
                         constants.RESPONSE_SERVER_ERROR)

    @patch('register.handlers.process_registration_request', autospec=True)
    def test_valid_nid(self, mock_prr):
        center_name = u"A Random Center"
        person_name = u"A Random Name"
        msg = u"%s*%s" % (self.good_nid, self.good_center_id)
        mock_prr.return_value = Result(
            '', constants.RESPONSE_VALID_REGISTRATION,
            dict(person=person_name,
                 centre=center_name,
                 code=int(self.good_center_id)))
        self.receive(msg, self.conn, fields=self.fields)
        self.assertEqual(self.outbound[0].fields['message_code'],
                         constants.RESPONSE_VALID_REGISTRATION)
        # message was saved
        sms = SMS.objects.get(message=msg)
        self.assertEqual(SMS.REGISTRATION, sms.msg_type)
        # response was saved
        sms = SMS.objects.filter(to_number=self.conn.identity)
        self.assertNotEqual(sms.count(), 0)

    @patch('register.processors.process_registration_request', autospec=True)
    def test_invalid_center_id(self, mock_prr):
        msg = u"%s*%s" % (self.good_nid, self.good_center_id)
        mock_prr.return_value = Result('',
                                       constants.RESPONSE_CENTER_ID_INVALID)
        self.receive(msg, self.conn, fields=self.fields)
        self.assertEqual(self.get_last_response_code(),
                         constants.RESPONSE_CENTER_ID_INVALID)

    @patch('register.handlers.registration_allowed')
    def test_outside_registration_period(self, mock_is_open):
        mock_is_open.return_value = False
        RegistrationCenterFactory(center_id=self.good_center_id)
        msg = u"%s*%s" % (self.good_nid, self.good_center_id)
        self.receive(msg, self.conn, fields=self.fields)
        self.assertEqual(self.get_last_response_code(),
                         constants.REGISTRATION_NOT_OPEN)

    def test_not_started(self):
        self.reg_period.start_time = FUTURE_DAY
        self.reg_period.save()
        RegistrationCenterFactory(center_id=self.good_center_id)
        msg = u"%s*%s" % (self.good_nid, self.good_center_id)
        self.receive(msg, self.conn, fields=self.fields)
        self.assertEqual(self.get_last_response_code(),
                         constants.REGISTRATION_NOT_OPEN)

    def test_has_ended(self):
        self.reg_period.end_time = PAST_DAY
        self.reg_period.save()
        self.election.polling_end_time = PAST_DAY
        self.election.save()
        RegistrationCenterFactory(center_id=self.good_center_id)
        msg = u"%s*%s" % (self.good_nid, self.good_center_id)
        self.receive(msg, self.conn, fields=self.fields)
        self.assertEqual(self.get_last_response_code(),
                         constants.REGISTRATION_NOT_OPEN)

    def test_we_only_handle_our_shortcode(self):
        msg = u"garbage"
        not_our_shortcode = '11111'
        self.receive(msg, self.conn, fields={'to_addr': not_our_shortcode})
        # We do send a default response, but we test that more below.
        self.assertEqual(1, len(self.full_outbound()))

    def test_that_we_save_incoming_sms_when_closed(self):
        self.reg_period.start_time = FUTURE_DAY
        self.reg_period.save()
        self.election.polling_start_time = FUTURE_DAY
        self.election.save()
        msg = u"garbage"
        for shortcode in set(
            (settings.REGISTRATION_SHORT_CODE, settings.VOTER_QUERY_SHORT_CODE,
             settings.REPORTS_SHORT_CODE)):
            self.receive(msg, self.conn, fields={'to_addr': shortcode})
            # test that we save the incoming SMS
            incoming = SMS.objects.filter(to_number=shortcode)
            self.assertEqual(len(incoming), 1)