def test_put_error_without_transient_result(self):
        error = self._get_delete_error(3)
        delete_spy = test_utils.FailingFunction(search.Index.delete, error, 1)
        delete_docs_counter = test_utils.CallCounter(
            gae_search_services.delete_documents_from_index)
        delete_docs_ctx = self.swap(gae_search_services,
                                    'delete_documents_from_index',
                                    delete_docs_counter)
        delete_ctx = self.swap(search.Index, 'delete', delete_spy)
        assert_raises_ctx = self.assertRaises(
            gae_search_services.SearchFailureError)
        with delete_docs_ctx, delete_ctx, assert_raises_ctx as e:
            gae_search_services.delete_documents_from_index(['a', 'b', 'c'],
                                                            'my_index')

        # Assert that the method only gets called once, since the error is not
        # transient.
        self.assertEqual(delete_docs_counter.times_called, 1)
        self.assertEqual(e.exception.original_exception, error)
Beispiel #2
0
    def test_use_custom_number_of_retries(self):
        exception = self._get_delete_error(1, 0)
        failing_delete = test_utils.FailingFunction(search.Index.delete,
                                                    exception, 42)

        delete_docs_counter = test_utils.CallCounter(
            gae_search_services.delete_documents_from_index)

        delete_ctx = self.swap(search.Index, 'delete', failing_delete)
        delete_docs_ctx = self.swap(gae_search_services,
                                    'delete_documents_from_index',
                                    delete_docs_counter)
        assert_raises_ctx = self.assertRaises(
            gae_search_services.SearchFailureError)
        with delete_ctx, delete_docs_ctx, assert_raises_ctx as cm:
            gae_search_services.delete_documents_from_index(['id'], 'index',
                                                            42)

        self.assertEqual(delete_docs_counter.times_called, 42)
Beispiel #3
0
    def test_arguments_are_preserved_in_retries(self):
        doc = {'id': 'doc', 'prop': 'val'}
        exception = self._get_put_error(1, 0)
        failing_put = test_utils.FailingFunction(search.Index.put, exception,
                                                 3)

        add_docs_counter = test_utils.CallCounter(
            gae_search_services.add_documents_to_index)

        put_ctx = self.swap(search.Index, 'put', failing_put)
        add_docs_ctx = self.swap(gae_search_services, 'add_documents_to_index',
                                 add_docs_counter)

        with put_ctx, add_docs_ctx:
            gae_search_services.add_documents_to_index([doc], 'my_index', 4)

        self.assertEqual(add_docs_counter.times_called, 4)
        result = search.Index('my_index').get('doc')
        self.assertEqual(result.field('prop').value, 'val')
Beispiel #4
0
    def _is_string_classifier_called(self, answer):
        string_classifier_predict = (
            lda_string_classifier.LDAStringClassifier.predict)
        predict_counter = test_utils.CallCounter(string_classifier_predict)

        with self.swap(lda_string_classifier.LDAStringClassifier, 'predict',
                       predict_counter):

            response = reader.classify(self.exp_state, answer)

        answer_group_index = response['answer_group_index']
        rule_spec_index = response['rule_spec_index']
        answer_groups = self.exp_state.interaction.answer_groups
        if answer_group_index == len(answer_groups):
            return 'default'

        answer_group = answer_groups[answer_group_index]
        return (answer_group.get_classifier_rule_index() == rule_spec_index
                and predict_counter.times_called == 1)
    def test_use_default_num_retries(self):
        exception = search.TransientError('oops')
        failing_index_search = test_utils.FailingFunction(
            search.Index.search, exception,
            gae_search_services.DEFAULT_NUM_RETRIES)

        search_counter = test_utils.CallCounter(gae_search_services.search)

        search_ctx = self.swap(search.Index, 'search', failing_index_search)
        search_counter_ctx = self.swap(gae_search_services, 'search',
                                       search_counter)
        assert_raises_ctx = self.assertRaises(
            gae_search_services.SearchFailureError)
        with search_ctx, search_counter_ctx, assert_raises_ctx as context_mgr:
            gae_search_services.search('query', 'my_index')

        self.assertEqual(context_mgr.exception.original_exception, exception)

        self.assertEqual(search_counter.times_called,
                         gae_search_services.DEFAULT_NUM_RETRIES)
    def test_use_default_num_retries(self):
        exception = search.TransientError('oops')
        failing_index_search = test_utils.FailingFunction(
            search.Index.search, exception, 1)

        search_counter = test_utils.CallCounter(gae_search_services.search)

        search_ctx = self.swap(search.Index, 'search', failing_index_search)
        search_counter_ctx = self.swap(gae_search_services, 'search',
                                       search_counter)
        assert_raises_ctx = self.assertRaisesRegexp(
            gae_search_services.SearchFailureError,
            '<class \'google.appengine.api.search.search.TransientError\'>: '
            'oops')
        with search_ctx, search_counter_ctx, assert_raises_ctx as context_mgr:
            gae_search_services.search('query', 'my_index')

        self.assertEqual(context_mgr.exception.original_exception, exception)

        self.assertEqual(search_counter.times_called, 1)
Beispiel #7
0
    def test_use_custom_number_of_retries(self):
        exception = search.TransientError('oops')
        failing_index_search = test_utils.FailingFunction(
            search.Index.search,
            exception,
            3)

        search_counter = test_utils.CallCounter(gae_search_services.search)

        index_ctx = self.swap(search.Index, 'search', failing_index_search)
        search_counter_ctx = self.swap(
            gae_search_services, 'search', search_counter)
        assert_raises_ctx = self.assertRaisesRegexp(
            gae_search_services.SearchFailureError,
            '<class \'google.appengine.api.search.search.TransientError\'>:'
            ' oops')
        with index_ctx, search_counter_ctx, assert_raises_ctx:
            gae_search_services.search('query', 'my_index', retries=3)

        self.assertEqual(search_counter.times_called, 3)
    def test_use_custom_number_of_retries(self):
        doc = {'id': 'doc', 'prop': 'val'}
        exception = self._get_put_error(1, transient=0)
        failing_put = test_utils.FailingFunction(search.Index.put, exception,
                                                 42)

        add_docs_counter = test_utils.CallCounter(
            gae_search_services.add_documents_to_index)

        put_ctx = self.swap(search.Index, 'put', failing_put)
        add_docs_ctx = self.swap(gae_search_services, 'add_documents_to_index',
                                 add_docs_counter)
        assert_raises_ctx = self.assertRaises(
            gae_search_services.SearchFailureError)
        with put_ctx, add_docs_ctx, assert_raises_ctx:
            gae_search_services.add_documents_to_index([doc],
                                                       'my_index',
                                                       retries=42)

        self.assertEqual(add_docs_counter.times_called, 42)
Beispiel #9
0
    def test_email_with_bad_content_is_not_sent(self):
        can_send_emails_ctx = self.swap(feconf, 'CAN_SEND_EMAILS_TO_USERS',
                                        True)

        config_services.set_property(
            self.ADMIN_ID, email_manager.SIGNUP_EMAIL_CONTENT.name, {
                'subject': 'New email subject',
                'html_body': 'New HTML body.<script>alert(3);</script>',
            })

        logged_errors = []

        def _log_error_for_tests(error_message):
            logged_errors.append(error_message)

        log_new_error_counter = test_utils.CallCounter(_log_error_for_tests)
        log_new_error_ctx = self.swap(email_manager, 'log_new_error',
                                      log_new_error_counter)

        with can_send_emails_ctx, log_new_error_ctx:
            self.assertEqual(log_new_error_counter.times_called, 0)

            self.login(self.EDITOR_EMAIL)
            response = self.testapp.get(feconf.SIGNUP_URL)
            csrf_token = self.get_csrf_token_from_response(response)

            # No user-facing error should surface.
            response_dict = self.post_json(feconf.SIGNUP_DATA_URL, {
                'agreed_to_terms': True,
                'username': self.EDITOR_USERNAME
            },
                                           csrf_token=csrf_token)

            # However, an error should be recorded in the logs.
            self.assertEqual(log_new_error_counter.times_called, 1)
            self.assertTrue(logged_errors[0].startswith(
                'Original email HTML body does not match cleaned HTML body'))

            # Check that no email was sent.
            messages = self.mail_stub.get_sent_messages(to=self.EDITOR_EMAIL)
            self.assertEqual(0, len(messages))
Beispiel #10
0
    def test_use_custom_number_of_retries(self):
        exception = self._get_delete_error(1, transient=0)
        failing_delete = test_utils.FailingFunction(
            search.Index.delete, exception, 42)

        delete_docs_counter = test_utils.CallCounter(
            gae_search_services.delete_documents_from_index)

        delete_ctx = self.swap(search.Index, 'delete', failing_delete)
        delete_docs_ctx = self.swap(
            gae_search_services,
            'delete_documents_from_index',
            delete_docs_counter)
        assert_raises_ctx = self.assertRaisesRegexp(
            gae_search_services.SearchFailureError,
            '<class \'google.appengine.api.search.search.DeleteError\'>: lol')
        with delete_ctx, delete_docs_ctx, assert_raises_ctx:
            gae_search_services.delete_documents_from_index(
                ['id'], 'index', retries=42)

        self.assertEqual(delete_docs_counter.times_called, 42)
Beispiel #11
0
    def test_fetch_gravatar_failure_exception(self):
        user_email = '*****@*****.**'
        error_messages = []
        def log_mock(message):
            error_messages.append(message)

        gravatar_url = user_services.get_gravatar_url(user_email)
        expected_error_message = (
            'Failed to fetch Gravatar from %s' % gravatar_url)
        logging_error_mock = test_utils.CallCounter(log_mock)
        urlfetch_fail_mock = test_utils.FailingFunction(
            urlfetch.fetch, urlfetch.InvalidURLError,
            test_utils.FailingFunction.INFINITY)
        log_swap_ctx = self.swap(logging, 'error', logging_error_mock)
        fetch_swap_ctx = self.swap(urlfetch, 'fetch', urlfetch_fail_mock)
        with log_swap_ctx, fetch_swap_ctx:
            profile_picture = user_services.fetch_gravatar(user_email)
            self.assertEqual(logging_error_mock.times_called, 1)
            self.assertEqual(expected_error_message, error_messages[0])
            self.assertEqual(
                profile_picture, user_services.DEFAULT_IDENTICON_DATA_URL)
Beispiel #12
0
    def _get_classiying_rule_type(self, answer):
        string_classifier_predict = (
            classifier_services.StringClassifier.predict_label_for_doc)
        predict_counter = test_utils.CallCounter(string_classifier_predict)

        with self.swap(classifier_services.StringClassifier,
                       'predict_label_for_doc', predict_counter):

            response = reader.classify(self.exp_id, self.exp_state, answer,
                                       {'answer': answer})

        answer_group_index = response['answer_group_index']
        rule_spec_index = response['rule_spec_index']
        answer_groups = self.exp_state.interaction.answer_groups
        if answer_group_index == len(answer_groups):
            return 'default'

        answer_group = answer_groups[answer_group_index]
        if answer_group.get_fuzzy_rule_index() == rule_spec_index:
            return ('soft'
                    if predict_counter.times_called == 0 else 'classifier')
        return 'hard'
    def test_use_default_num_retries(self):
        exception = self._get_delete_error(1, transient=0)
        failing_delete = test_utils.FailingFunction(search.Index.delete,
                                                    exception, 1)

        delete_docs_counter = test_utils.CallCounter(
            gae_search_services.delete_documents_from_index)

        delete_ctx = self.swap(search.Index, 'delete', failing_delete)
        delete_docs_ctx = self.swap(gae_search_services,
                                    'delete_documents_from_index',
                                    delete_docs_counter)
        assert_raises_ctx = self.assertRaisesRegexp(
            gae_search_services.SearchFailureError,
            '<class \'google.appengine.api.search.search.DeleteError\'>: lol')
        with delete_ctx, delete_docs_ctx, assert_raises_ctx as context_mgr:
            gae_search_services.delete_documents_from_index(['doc'],
                                                            'my_index')

        self.assertEqual(context_mgr.exception.original_exception, exception)

        self.assertEqual(delete_docs_counter.times_called, 1)
Beispiel #14
0
    def test_email_not_sent_if_content_config_is_not_modified(self):
        can_send_emails_ctx = self.swap(feconf, 'CAN_SEND_EMAILS_TO_USERS',
                                        True)

        logged_errors = []

        def _log_error_for_tests(error_message):
            logged_errors.append(error_message)

        log_new_error_counter = test_utils.CallCounter(_log_error_for_tests)
        log_new_error_ctx = self.swap(email_manager, 'log_new_error',
                                      log_new_error_counter)

        with can_send_emails_ctx, log_new_error_ctx:
            self.assertEqual(log_new_error_counter.times_called, 0)

            self.login(self.EDITOR_EMAIL)
            response = self.testapp.get(feconf.SIGNUP_URL)
            csrf_token = self.get_csrf_token_from_response(response)

            # No user-facing error should surface.
            response_dict = self.post_json(feconf.SIGNUP_DATA_URL, {
                'agreed_to_terms': True,
                'username': self.EDITOR_USERNAME
            },
                                           csrf_token=csrf_token)

            # However, an error should be recorded in the logs.
            self.assertEqual(log_new_error_counter.times_called, 1)
            self.assertEqual(
                logged_errors[0],
                'Please ensure that the value for the admin config property '
                'SIGNUP_EMAIL_CONTENT is set, before allowing post-signup '
                'emails to be sent.')

            # Check that no email was sent.
            messages = self.mail_stub.get_sent_messages(to=self.EDITOR_EMAIL)
            self.assertEqual(0, len(messages))
Beispiel #15
0
    def test_patch_collection_search_document(self):
        def mock_get_doc(doc_id, index):
            self.assertEqual(doc_id, self.COLLECTION_ID)
            self.assertEqual(index, search_services.SEARCH_INDEX_COLLECTIONS)
            return {'a': 'b', 'c': 'd'}

        def mock_add_docs(docs, index):
            self.assertEqual(index, search_services.SEARCH_INDEX_COLLECTIONS)
            self.assertEqual(docs, [{'a': 'b', 'c': 'e', 'f': 'g'}])

        get_doc_swap = self.swap(gae_search_services,
                                 'get_document_from_index', mock_get_doc)

        add_docs_counter = test_utils.CallCounter(mock_add_docs)
        add_docs_swap = self.swap(gae_search_services,
                                  'add_documents_to_index', add_docs_counter)

        with get_doc_swap, add_docs_swap:
            patch = {'c': 'e', 'f': 'g'}
            search_services.patch_collection_search_document(
                self.COLLECTION_ID, patch)

        self.assertEqual(add_docs_counter.times_called, 1)
    def test_use_default_num_retries(self):
        exception = self._get_delete_error(1, 0)
        failing_delete = test_utils.FailingFunction(
            search.Index.delete, exception,
            gae_search_services.DEFAULT_NUM_RETRIES)

        delete_docs_counter = test_utils.CallCounter(
            gae_search_services.delete_documents_from_index)

        delete_ctx = self.swap(search.Index, 'delete', failing_delete)
        delete_docs_ctx = self.swap(gae_search_services,
                                    'delete_documents_from_index',
                                    delete_docs_counter)
        assert_raises_ctx = self.assertRaises(
            gae_search_services.SearchFailureError)
        with delete_ctx, delete_docs_ctx, assert_raises_ctx as context_mgr:
            gae_search_services.delete_documents_from_index(['doc'],
                                                            'my_index')

        self.assertEqual(context_mgr.exception.original_exception, exception)

        self.assertEqual(delete_docs_counter.times_called,
                         gae_search_services.DEFAULT_NUM_RETRIES)
Beispiel #17
0
    def test_update_private_exploration_status_in_search(self):
        def mock_delete_docs(ids, index):
            self.assertEqual(ids, [self.EXP_ID])
            self.assertEqual(index, search_services.SEARCH_INDEX_EXPLORATIONS)

        def mock_get_rights(unused_exp_id):
            return rights_manager.ActivityRights(
                self.EXP_ID, [self.owner_id], [self.editor_id],
                [self.viewer_id],
                status=rights_manager.ACTIVITY_STATUS_PRIVATE)

        delete_docs_counter = test_utils.CallCounter(mock_delete_docs)

        delete_docs_swap = self.swap(gae_search_services,
                                     'delete_documents_from_index',
                                     delete_docs_counter)
        get_rights_swap = self.swap(rights_manager, 'get_exploration_rights',
                                    mock_get_rights)

        with get_rights_swap, delete_docs_swap:
            search_services.update_exploration_status_in_search(self.EXP_ID)

        self.assertEqual(delete_docs_counter.times_called, 1)
    def test_put_error_without_transient_result(self):
        docs = [{'id': 'doc1', 'prop': 'val1'},
                {'id': 'doc2', 'prop': 'val2'},
                {'id': 'doc3', 'prop': 'val3'}]

        error = self._get_put_error(3)
        failing_put = test_utils.FailingFunction(search.Index.put, error, 1)

        add_docs_counter = test_utils.CallCounter(
            gae_search_services.add_documents_to_index)
        add_docs_ctx = self.swap(
            gae_search_services,
            'add_documents_to_index',
            add_docs_counter)
        put_ctx = self.swap(search.Index, 'put', failing_put)
        assert_raises_ctx = self.assertRaises(
            gae_search_services.SearchFailureError)
        with add_docs_ctx, put_ctx, assert_raises_ctx as e:
            gae_search_services.add_documents_to_index(docs, 'my_index')

        # assert that the method only gets called once, since the error is not
        # transient.
        self.assertEqual(add_docs_counter.times_called, 1)
        self.assertEqual(e.exception.original_exception, error)
    def test_arguments_are_preserved_in_retries(self):
        index = search.Index('index')
        index.put([search.Document(doc_id='doc', fields=[
            search.TextField(name='prop', value='val')
        ])])
        exception = self._get_delete_error(1, 0)
        failing_delete = test_utils.FailingFunction(
            search.Index.delete, exception, 3)

        delete_docs_counter = test_utils.CallCounter(
            gae_search_services.delete_documents_from_index)

        index_ctx = self.swap(search.Index, 'delete', failing_delete)
        delete_docs_ctx = self.swap(
            gae_search_services,
            'delete_documents_from_index',
            delete_docs_counter)
        with index_ctx, delete_docs_ctx:
            gae_search_services.delete_documents_from_index(
                ['doc'], 'index', 4)

        self.assertEqual(delete_docs_counter.times_called, 4)
        result = search.Index('my_index').get('doc')
        self.assertIsNone(result)
    def test_use_default_num_retries(self):
        doc = {'id': 'doc', 'prop': 'val'}
        exception = self._get_put_error(1, 0)
        failing_put = test_utils.FailingFunction(
            search.Index.put,
            exception,
            gae_search_services.DEFAULT_NUM_RETRIES,
        )

        add_docs_counter = test_utils.CallCounter(
            gae_search_services.add_documents_to_index)

        put_ctx = self.swap(search.Index, 'put', failing_put)
        add_docs_ctx = self.swap(gae_search_services, 'add_documents_to_index',
                                 add_docs_counter)
        assert_raises_ctx = self.assertRaises(
            gae_search_services.SearchFailureError)
        with put_ctx, add_docs_ctx, assert_raises_ctx as context_mgr:
            gae_search_services.add_documents_to_index([doc], 'my_index')

        self.assertEqual(context_mgr.exception.original_exception, exception)

        self.assertEqual(add_docs_counter.times_called,
                         gae_search_services.DEFAULT_NUM_RETRIES)