Esempio n. 1
0
    def test_volume_page_search(self, mocksolr_interface, mockpaginator,
                                mockrepo):
        mockobj = NonCallableMock()
        mockobj.pid = 'vol:1'
        mockobj.title = 'Lecoq, the detective'
        mockobj.date = ['1801']
        mockrepo.return_value.get_object.return_value = mockobj

        mocksolr = mocksolr_interface.return_value
        # simulate sunburnt's fluid interface
        mocksolr.query.return_value = mocksolr.query
        for method in [
                'query', 'facet_by', 'sort_by', 'field_limit', 'highlight',
                'exclude', 'filter', 'join', 'paginate', 'results_as'
        ]:
            getattr(mocksolr.query, method).return_value = mocksolr.query

        # set up mock results for collection query and facet counts
        solr_result = NonCallableMagicMock(
            spec_set=['__iter__', 'facet_counts'])

        # *only* mock iter, to avoid weirdness with django templates & callables
        solr_results = [
            SolrPage(
                **{
                    'pid': 'page:1',
                    'page_order': '1',
                    'score': 0.5,
                    'solr_highlights': {
                        'page_text': ['snippet with search term']
                    },
                    'identifier': ['http://testpid.co/ark:/1234/11/']
                }),
            SolrPage(
                **{
                    'pid': 'page:233',
                    'page_order': '123',
                    'score': 0.02,
                    'solr_highlights': {
                        'page_text': ['sample text result from content']
                    },
                    'identifier': ['http://testpid.co/ark:/1234/22/']
                }),
        ]
        solr_result.__iter__.return_value = solr_results
        mocksolr.query.__iter__.return_value = iter(solr_result)
        mocksolr.count.return_value = 2

        mockpage = NonCallableMock()
        mockpaginator.return_value.page.return_value = mockpage
        results = NonCallableMagicMock(
            spec=['__iter__', 'facet_counts', 'highlighting'])
        results.__iter__.return_value = iter(solr_result)

        mockpage.object_list = results
        mockpage.has_other_pages = False
        mockpage.paginator.count = 2
        mockpage.paginator.page_range = [1]
        # patch in highlighting - apparent change in sunburnt behavior
        results.highlighting = {
            'page:1': {
                'page_text': ['snippet with search term']
            },
            'page:233': {
                'page_text': ['sample text result from content']
            }
        }

        vol_url = reverse('books:volume', kwargs={'pid': mockobj.pid})
        response = self.client.get(vol_url, {'keyword': 'determine'})
        self.assertEqual(
            response.templates[0].name,
            views.VolumeDetail.search_template_name,
            'volume search template should be used for valid search submission'
        )
        for page in iter(solr_result):
            self.assertContains(
                response,
                reverse('books:page-image',
                        kwargs={
                            'vol_pid': mockobj.pid,
                            'pid': page.pid,
                            'mode': 'mini-thumbnail'
                        }),
                msg_prefix=
                'search results should include mini page thumbnail url')
            self.assertContains(
                response,
                "Page %(page_order)s" % page,
                msg_prefix='search results should include page number')
            self.assertContains(
                response,
                page['score'],
                msg_prefix='search results should display page relevance score'
            )
            self.assertContains(
                response,
                reverse('books:page',
                        kwargs={
                            'vol_pid': mockobj.pid,
                            'pid': page['pid']
                        }),
                msg_prefix='search results should link to full page view')
            self.assertContains(
                response,
                '... %s ...' % page['solr_highlights']['page_text'][0],
                msg_prefix='solr snippets should display when available')

        # ajax request
        with patch('readux.books.views.VolumeDetail.get_context_data'
                   ) as mock_ctx:
            results = NonCallableMagicMock(
                spec=['__iter__', 'facet_counts', 'highlighting'])
            results.__iter__.return_value = iter(solr_result)
            results.highlighting = {
                solr_results[0].pid: {
                    'page_text': 'sample highlighting snippet'
                },
                solr_results[1].pid: {
                    'page_text': 'another highlighting snippet'
                }
            }
            mockpage = NonCallableMagicMock(spec=['__iter__'])
            mockpage.object_list = results
            mock_ctx.return_value = {
                'pages': mockpage,
            }
            response = self.client.get(vol_url, {'keyword': 'determine'},
                                       HTTP_X_REQUESTED_WITH='XMLHttpRequest')
            self.assertEqual('application/json', response['content-type'])
            data = json.loads(response.content)
            for idx in range(len(data)):
                self.assertEqual(solr_results[idx].pid, data[idx]['pid'])
                self.assertEqual('p. %s' % solr_results[idx]['page_order'],
                                 data[idx]['label'])
                self.assertEqual(
                    reverse('books:page-image',
                            kwargs={
                                'vol_pid': mockobj.pid,
                                'pid': solr_results[idx].pid,
                                'mode': 'mini-thumbnail'
                            }), data[idx]['thumbnail'])
                self.assertEqual(
                    results.highlighting[solr_results[idx].pid]['page_text'],
                    data[idx]['highlights'])
Esempio n. 2
0
    def test_view(self, mockpaginator, mockrepo, mocksolr_interface, mocksolr_iface):
        mocksolr = mocksolr_interface.return_value
        mocksolr_iface.return_value = mocksolr
        view_url = reverse('collection:view', kwargs={'pid': 'coll'})

        mocksolr.query.return_value = mocksolr.query
        for method in ['query', 'sort_by', 'results_as', 'field_limit',
                       'facet_query']:
            getattr(mocksolr.query, method).return_value = mocksolr.query

        # set no solr results for last-modified query
        mocksolr.query.count.return_value = 0

        # simulate 404
        mockcoll = NonCallableMock()
        mockrepo.return_value.get_object.return_value = mockcoll
        # - doesn't exist
        mockcoll.exists = False
        response = self.client.get(view_url)
        expected, got = 404, response.status_code
        self.assertEqual(expected, got,
            'expected status code %s but got %s for view collection when object doesn\'t exist' % \
            (expected, got))
        # - exists but is the wrong type
        mockcoll.exists = True
        mockcoll.has_requisite_content_models = False
        response = self.client.get(view_url)
        expected, got = 404, response.status_code
        self.assertEqual(expected, got,
            'expected status code %s but got %s for view collection when object has wrong cmodels' % \
            (expected, got))

        # simulate valid fedora object
        mockcoll.has_requisite_content_models = True
        mockcoll.short_label = 'Yellowbacks'
        mockcoll.pid = 'coll:1'
        mockcoll.dc.content.description = 'Cheap 19thc paperbacks, often bound in yellow.'
        # simulate sunburnt's fluid interface
        # set up mock results for display on template
        solr_result = [
            SolrVolume(**{'pid': 'vol:1', 'title': 'Asodecoan',
                'creator': ['Atlanta-Southern Dental College.; Emory University Archives.']}),
            SolrVolume(**{'pid': 'vol:2', 'title': 'Sugar Crop of Lousiana', 'label': 'ocm123_V.2',
                         'date': ['1831']}),
        ]
        results = NonCallableMagicMock(spec=['__iter__', 'facet_counts'])
        results.__iter__.return_value = iter(solr_result)
        results.facet_counts.facet_queries = []


        # - using a noncallable for the pagination result that is used in the template
        # because passing callables into django templates does weird things
        mockpage = NonCallableMock()
        mockpaginator.return_value.page.return_value = mockpage
        mockpage.object_list = results
        mockpage.has_other_pages = False
        mockpage.paginator.count = 2
        mockpage.paginator.page_range = [1]
        mockpaginator.return_value.count = 2
        mockpaginator.return_value.page_range = [1]

        response = self.client.get(view_url)
        # inspect solr query
        mocksolr.query.assert_called_with(content_model=Volume.VOLUME_CMODEL_PATTERN,
                                          collection_id=mockcoll.pid)
        mocksolr.query.sort_by.assert_any_call('title_exact')
        mocksolr.query.sort_by.assert_any_call('label')
        mocksolr.query.results_as.assert_called_with(SolrVolume)
        # inspect html result
        self.assertContains(response, mockcoll.short_label,
            msg_prefix='collection short label should be displayed')
        self.assertContains(response,
            '<title>%s Collection | Readux</title>' % mockcoll.short_label,
            html=True,
            msg_prefix='collection label should be included in html title')
        self.assertContains(response, mockcoll.dc.content.description,
            msg_prefix='collection dc:description should be displayed')
        self.assertContains(response, '2 volumes in this collection',
            msg_prefix='total count of volumes in the collection should be displayed')
        self.assertContains(response, solr_result[0]['title'],
            msg_prefix='volume title %(title)s should be displayed' % solr_result[0])
        self.assertContains(response, solr_result[1]['title'],
            msg_prefix='volume title %(title)s should be displayed' % solr_result[1])
        self.assertContains(response, '[V.2]',
            msg_prefix='volume number should be displayed when present')
        # date/creator
        self.assertContains(response, '(%s)' % solr_result[1]['date'][0],
            msg_prefix='volume date should be displayed when present')
        self.assertContains(response, '%s' % solr_result[0]['creator'][0],
            msg_prefix='volume author/creator should be displayed when present')

        # check that unapi / zotero harvest is enabled
        self.assertContains(response,
            '<link rel="unapi-server" type="application/xml" title="unAPI" href="%s" />' % \
            reverse('books:unapi'),
            html=True,
            msg_prefix='link to unAPI server URL should be specified in header')
        self.assertContains(response,
            '<abbr class="unapi-id" title="%s"></abbr>' % solr_result[0]['pid'],
            msg_prefix='unapi item id for %s should be included to allow zotero harvest' % \
                        solr_result[0]['pid'])
        self.assertContains(response,
            '<abbr class="unapi-id" title="%s"></abbr>' % solr_result[1]['pid'],
            msg_prefix='unapi item id for %s should be included to allow zotero harvest' % \
                       solr_result[1]['pid'])

        self.assertFalse(response.has_header('last-modified'),
            'last modified header should not be set when no results were found in solr')

        # last-modified
        mocksolr.query.count.return_value = 1
        # set a timestamp to be returned for last-modified conditional processing
        mocksolr.query.return_value.__getitem__.return_value = {'timestamp': datetime.now()}
        response = self.client.get(view_url)
        self.assertTrue(response.has_header('last-modified'),
            'last modified header should be set')

        ## annotation totals
        # empty annotation total in context for anonymous user
        self.assertEqual({}, response.context['annotated_volumes'])
        # check that annotation total is retrieved for ONLY logged in users
        with patch('readux.collection.views.Volume') as mockvolclass:

             self.client.get(view_url)
             mockvolclass.volume_annotation_count.assert_not_called()

             User = get_user_model()
             credentials = {'username': '******', 'password': '******'}
             testuser = User.objects.create_user(**credentials)
             self.client.login(**credentials)
             self.client.get(view_url)
             mockvolclass.volume_annotation_count.assert_called_with(testuser)
Esempio n. 3
0
    def test_search(self, mockqs_paginate, mocksolr_interface, mockpaginator):
        mockpage = NonCallableMock()
        search_url = reverse('books:search')

        # NOTE: pagination now happens in django's class-based view,
        # so must be mocked there
        mockqs_paginate.return_value = (mockpaginator.return_value, mockpage,
                                        [], False)

        # no search terms - invalid form
        response = self.client.get(search_url)
        self.assertContains(response, 'Please enter one or more search terms')

        mocksolr = mocksolr_interface.return_value
        # simulate sunburnt's fluid interface
        mocksolr.query.return_value = mocksolr.query
        for method in [
                'query', 'facet_by', 'sort_by', 'field_limit', 'exclude',
                'filter', 'join', 'paginate', 'results_as', 'facet_query'
        ]:
            getattr(mocksolr.query, method).return_value = mocksolr.query

        # set up mock results for collection query and facet counts
        solr_result = NonCallableMagicMock(
            spec_set=['__iter__', 'facet_counts'])
        # *only* mock iter, to avoid weirdness with django templates & callables
        solr_result.__iter__.return_value = [
            SolrVolume(**{
                'pid': 'vol:1',
                'title': 'Lecoq, the detective',
                'pdf_size': 1024
            }),
            SolrVolume(**{
                'pid': 'vol:2',
                'title': 'Mabel Meredith',
                'pdf_size': 34665
            }),
        ]
        mocksolr.query.__iter__.return_value = iter(solr_result)
        mocksolr.count.return_value = 2
        # mock facets
        # solr_result.facet_counts.facet_fields = {
        #     'collection_label_facet': [('Civil War Literature', 2), ('Yellowbacks', 4)]
        # }

        # use a noncallable for the pagination result that is used in the template
        # because passing callables into django templates does weird things

        mockpaginator.return_value.page.return_value = mockpage
        results = NonCallableMagicMock(
            spec=['__iter__', 'facet_counts', '__len__'])

        results.__iter__.return_value = iter(solr_result)
        results.facet_counts.facet_fields = {
            'collection_label_facet': [('Emory Yearbooks', 1),
                                       ('Yellowbacks', 4)]
        }
        results.__len__.return_value = 2

        mockpage.object_list = results
        mockpage.has_other_pages = False
        mockpage.paginator.count = 2
        mockpage.paginator.page_range = [1]
        mockpaginator.return_value.count = 2
        mockpaginator.return_value.page_range = [1]
        mockqs_paginate.return_value = (mockpaginator.return_value, mockpage,
                                        results, True)

        # query with search terms
        response = self.client.get(search_url, {'keyword': 'yellowbacks'})

        mocksolr.query.filter.assert_called_with(
            content_model=Volume.VOLUME_CMODEL_PATTERN)
        # because of creator/title search boosting, actual query is a little difficult to test
        mocksolr.Q.assert_any_call('yellowbacks')
        mocksolr.Q.assert_any_call(creator='yellowbacks')
        mocksolr.Q.assert_any_call(title='yellowbacks')
        # not sure how to test query on Q|Q**3|Q**3
        mocksolr.query.field_limit.assert_called_with(
            SolrVolume.necessary_fields, score=True)

        # check that unapi / zotero harvest is enabled
        self.assertContains(response,
            '<link rel="unapi-server" type="application/xml" title="unAPI" href="%s" />' % \
            reverse('books:unapi'),
            html=True,
            msg_prefix='link to unAPI server URL should be specified in header')

        # check that items are displayed
        for item in solr_result:
            self.assertContains(response,
                                item['title'],
                                msg_prefix='title should be displayed')
            self.assertContains(
                response,
                unquote(reverse('books:pdf', kwargs={'pid': item['pid']})),
                msg_prefix='link to pdf should be included in response')
            self.assertContains(response,
                '<abbr class="unapi-id" title="%s"></abbr>' % item['pid'],
                msg_prefix='unapi item id for %s should be included to allow zotero harvest' \
                           % item['pid'])
            # pdf size
            self.assertContains(
                response,
                filesizeformat(item['pdf_size']),
                msg_prefix=
                'PDF size should be displayed in human-readable format')

        # check that collection facets are displayed / linked
        for coll, count in results.facet_counts.facet_fields[
                'collection_label_facet']:
            self.assertContains(
                response,
                coll,
                msg_prefix=
                'collection facet label should be displayed on search results page'
            )
            # not a very definitive test, but at least check the number is displayed
            self.assertContains(
                response,
                count,
                msg_prefix=
                'collection facet count should be displayed on search results page'
            )
            self.assertContains(
                response,
                '?keyword=yellowbacks&amp;collection=%s' %
                coll.replace(' ', '%20'),
                msg_prefix=
                'response should include link to search filtered by collection facet'
            )

        # multiple terms and phrase
        response = self.client.get(
            search_url,
            {'keyword': 'yellowbacks "lecoq the detective" mystery'})
        for term in ['yellowbacks', 'lecoq the detective', 'mystery']:
            mocksolr.Q.assert_any_call(term)

        # filtered by collection
        response = self.client.get(search_url, {
            'keyword': 'lecoq',
            'collection': 'Yellowbacks'
        })
        mocksolr.query.query.assert_any_call(collection_label='"%s"' %
                                             'Yellowbacks')

        ## annotation totals
        # empty annotation total in context for anonymous user
        self.assertEqual({}, response.context['annotated_volumes'])
        # check that annotation total is retrieved for ONLY logged in users
        with patch('readux.books.views.Volume') as mockvolclass:
            response = self.client.get(search_url, {
                'keyword': 'lecoq',
                'collection': 'Yellowbacks'
            })
            mockvolclass.volume_annotation_count.assert_not_called()

            User = get_user_model()
            testuser = User.objects.get(
                username=self.user_credentials['user']['username'])
            self.client.login(**self.user_credentials['user'])
            response = self.client.get(search_url, {
                'keyword': 'lecoq',
                'collection': 'Yellowbacks'
            })
            mockvolclass.volume_annotation_count.assert_called_with(testuser)
Esempio n. 4
0
    def test_volume_page_search(self, mocksolr_interface, mockpaginator, mockrepo):
        mockobj = NonCallableMock()
        mockobj.pid = 'vol:1'
        mockobj.title = 'Lecoq, the detective'
        mockobj.date = ['1801']
        mockrepo.return_value.get_object.return_value = mockobj

        mocksolr = mocksolr_interface.return_value
        # simulate sunburnt's fluid interface
        mocksolr.query.return_value = mocksolr.query
        for method in ['query', 'facet_by', 'sort_by', 'field_limit', 'highlight',
                       'exclude', 'filter', 'join', 'paginate', 'results_as']:
            getattr(mocksolr.query, method).return_value = mocksolr.query

        # set up mock results for collection query and facet counts
        solr_result = NonCallableMagicMock(spec_set=['__iter__', 'facet_counts'])

        # *only* mock iter, to avoid weirdness with django templates & callables
        solr_results = [
            SolrPage(**{'pid': 'page:1', 'page_order': '1', 'score': 0.5,
             'solr_highlights': {'page_text': ['snippet with search term']},
                 'identifier': ['http://testpid.co/ark:/1234/11/']}),
            SolrPage(**{'pid': 'page:233', 'page_order': '123', 'score': 0.02,
             'solr_highlights': {'page_text': ['sample text result from content']},
              'identifier': ['http://testpid.co/ark:/1234/22/']}),
        ]
        solr_result.__iter__.return_value = solr_results
        mocksolr.query.__iter__.return_value = iter(solr_result)
        mocksolr.count.return_value = 2

        mockpage = NonCallableMock()
        mockpaginator.return_value.page.return_value = mockpage
        results = NonCallableMagicMock(spec=['__iter__', 'facet_counts', 'highlighting'])
        results.__iter__.return_value = iter(solr_result)

        mockpage.object_list = results
        mockpage.has_other_pages = False
        mockpage.paginator.count = 2
        mockpage.paginator.page_range = [1]
         # patch in highlighting - apparent change in sunburnt behavior
        results.highlighting = {
            'page:1': {'page_text': ['snippet with search term']},
            'page:233':  {'page_text': ['sample text result from content']}
        }

        vol_url = reverse('books:volume', kwargs={'pid': mockobj.pid})
        response = self.client.get(vol_url, {'keyword': 'determine'})
        self.assertEqual(response.templates[0].name,
            views.VolumeDetail.search_template_name,
            'volume search template should be used for valid search submission')
        for page in iter(solr_result):
            self.assertContains(response,
                reverse('books:page-image', kwargs={'vol_pid': mockobj.pid,
                    'pid': page.pid, 'mode': 'mini-thumbnail'}),
                msg_prefix='search results should include mini page thumbnail url')
            self.assertContains(response, "Page %(page_order)s" % page,
                msg_prefix='search results should include page number')
            self.assertContains(response, page['score'],
                msg_prefix='search results should display page relevance score')
            self.assertContains(response, reverse('books:page',
                kwargs={'vol_pid': mockobj.pid, 'pid': page['pid']}),
                msg_prefix='search results should link to full page view')
            self.assertContains(response, '... %s ...' % page['solr_highlights']['page_text'][0],
                msg_prefix='solr snippets should display when available')

        # ajax request
        with patch('readux.books.views.VolumeDetail.get_context_data') as mock_ctx:
            results = NonCallableMagicMock(spec=['__iter__', 'facet_counts', 'highlighting'])
            results.__iter__.return_value = iter(solr_result)
            results.highlighting = {
                solr_results[0].pid: {
                    'page_text': 'sample highlighting snippet'
                },
                solr_results[1].pid: {
                    'page_text': 'another highlighting snippet'
                }

            }
            mockpage = NonCallableMagicMock(spec=['__iter__'])
            mockpage.object_list = results
            mock_ctx.return_value = {
                 'pages': mockpage,
             }
            response = self.client.get(vol_url, {'keyword': 'determine'},
                HTTP_X_REQUESTED_WITH='XMLHttpRequest')
            self.assertEqual('application/json', response['content-type'])
            data = json.loads(response.content)
            for idx in range(len(data)):
                self.assertEqual(solr_results[idx].pid, data[idx]['pid'])
                self.assertEqual('p. %s' % solr_results[idx]['page_order'],
                    data[idx]['label'])
                self.assertEqual(reverse('books:page-image', kwargs={'vol_pid': mockobj.pid,
                    'pid': solr_results[idx].pid, 'mode': 'mini-thumbnail'}),
                    data[idx]['thumbnail'])
                self.assertEqual(results.highlighting[solr_results[idx].pid]['page_text'],
                    data[idx]['highlights'])
Esempio n. 5
0
    def test_search(self, mockqs_paginate, mocksolr_interface, mockpaginator):
        mockpage = NonCallableMock()
        search_url = reverse('books:search')

        # NOTE: pagination now happens in django's class-based view,
        # so must be mocked there
        mockqs_paginate.return_value = (mockpaginator.return_value,
            mockpage, [], False)

        # no search terms - invalid form
        response = self.client.get(search_url)
        self.assertContains(response, 'Please enter one or more search terms')

        mocksolr = mocksolr_interface.return_value
        # simulate sunburnt's fluid interface
        mocksolr.query.return_value = mocksolr.query
        for method in ['query', 'facet_by', 'sort_by', 'field_limit',
                       'exclude', 'filter', 'join', 'paginate', 'results_as',
                       'facet_query']:
            getattr(mocksolr.query, method).return_value = mocksolr.query

        # set up mock results for collection query and facet counts
        solr_result = NonCallableMagicMock(spec_set=['__iter__', 'facet_counts'])
        # *only* mock iter, to avoid weirdness with django templates & callables
        solr_result.__iter__.return_value = [
            SolrVolume(**{'pid': 'vol:1', 'title': 'Lecoq, the detective', 'pdf_size': 1024}),
            SolrVolume(**{'pid': 'vol:2', 'title': 'Mabel Meredith', 'pdf_size': 34665}),
        ]
        mocksolr.query.__iter__.return_value = iter(solr_result)
        mocksolr.count.return_value = 2
        # mock facets
        # solr_result.facet_counts.facet_fields = {
        #     'collection_label_facet': [('Civil War Literature', 2), ('Yellowbacks', 4)]
        # }

        # use a noncallable for the pagination result that is used in the template
        # because passing callables into django templates does weird things

        mockpaginator.return_value.page.return_value = mockpage
        results = NonCallableMagicMock(spec=['__iter__', 'facet_counts', '__len__'])

        results.__iter__.return_value = iter(solr_result)
        results.facet_counts.facet_fields = {
            'collection_label_facet': [('Emory Yearbooks', 1), ('Yellowbacks', 4)]
        }
        results.__len__.return_value = 2

        mockpage.object_list = results
        mockpage.has_other_pages = False
        mockpage.paginator.count = 2
        mockpage.paginator.page_range = [1]
        mockpaginator.return_value.count = 2
        mockpaginator.return_value.page_range = [1]
        mockqs_paginate.return_value = (mockpaginator.return_value, mockpage, results, True)

        # query with search terms
        response = self.client.get(search_url, {'keyword': 'yellowbacks'})

        mocksolr.query.filter.assert_called_with(content_model=Volume.VOLUME_CMODEL_PATTERN)
        # because of creator/title search boosting, actual query is a little difficult to test
        mocksolr.Q.assert_any_call('yellowbacks')
        mocksolr.Q.assert_any_call(creator='yellowbacks')
        mocksolr.Q.assert_any_call(title='yellowbacks')
        # not sure how to test query on Q|Q**3|Q**3
        mocksolr.query.field_limit.assert_called_with(SolrVolume.necessary_fields,
            score=True)

        # check that unapi / zotero harvest is enabled
        self.assertContains(response,
            '<link rel="unapi-server" type="application/xml" title="unAPI" href="%s" />' % \
            reverse('books:unapi'),
            html=True,
            msg_prefix='link to unAPI server URL should be specified in header')

        # check that items are displayed
        for item in solr_result:
            self.assertContains(response, item['title'],
                msg_prefix='title should be displayed')
            self.assertContains(response, unquote(reverse('books:pdf', kwargs={'pid': item['pid']})),
                    msg_prefix='link to pdf should be included in response')
            self.assertContains(response,
                '<abbr class="unapi-id" title="%s"></abbr>' % item['pid'],
                msg_prefix='unapi item id for %s should be included to allow zotero harvest' \
                           % item['pid'])
            # pdf size
            self.assertContains(response, filesizeformat(item['pdf_size']),
                msg_prefix='PDF size should be displayed in human-readable format')

        # check that collection facets are displayed / linked
        for coll, count in results.facet_counts.facet_fields['collection_label_facet']:
            self.assertContains(response, coll,
                msg_prefix='collection facet label should be displayed on search results page')
            # not a very definitive test, but at least check the number is displayed
            self.assertContains(response, count,
                msg_prefix='collection facet count should be displayed on search results page')
            self.assertContains(response,
                '?keyword=yellowbacks&amp;collection=%s' % coll.replace(' ', '%20'),
                msg_prefix='response should include link to search filtered by collection facet')

        # multiple terms and phrase
        response = self.client.get(search_url, {'keyword': 'yellowbacks "lecoq the detective" mystery'})
        for term in ['yellowbacks', 'lecoq the detective', 'mystery']:
            mocksolr.Q.assert_any_call(term)

        # filtered by collection
        response = self.client.get(search_url, {'keyword': 'lecoq', 'collection': 'Yellowbacks'})
        mocksolr.query.query.assert_any_call(collection_label='"%s"' % 'Yellowbacks')

        ## annotation totals
        # empty annotation total in context for anonymous user
        self.assertEqual({}, response.context['annotated_volumes'])
        # check that annotation total is retrieved for ONLY logged in users
        with patch('readux.books.views.Volume') as mockvolclass:
            response = self.client.get(search_url, {'keyword': 'lecoq', 'collection': 'Yellowbacks'})
            mockvolclass.volume_annotation_count.assert_not_called()

            User = get_user_model()
            testuser = User.objects.get(username=self.user_credentials['user']['username'])
            self.client.login(**self.user_credentials['user'])
            response = self.client.get(search_url, {'keyword': 'lecoq', 'collection': 'Yellowbacks'})
            mockvolclass.volume_annotation_count.assert_called_with(testuser)
Esempio n. 6
0
    def test_view(self, mockpaginator, mockrepo, mocksolr_interface,
                  mocksolr_iface):
        mocksolr = mocksolr_interface.return_value
        mocksolr_iface.return_value = mocksolr
        view_url = reverse('collection:view', kwargs={'pid': 'coll'})

        mocksolr.query.return_value = mocksolr.query
        for method in [
                'query', 'sort_by', 'results_as', 'field_limit', 'facet_query'
        ]:
            getattr(mocksolr.query, method).return_value = mocksolr.query

        # set no solr results for last-modified query
        mocksolr.query.count.return_value = 0

        # simulate 404
        mockcoll = NonCallableMock()
        mockrepo.return_value.get_object.return_value = mockcoll
        # - doesn't exist
        mockcoll.exists = False
        response = self.client.get(view_url)
        expected, got = 404, response.status_code
        self.assertEqual(expected, got,
            'expected status code %s but got %s for view collection when object doesn\'t exist' % \
            (expected, got))
        # - exists but is the wrong type
        mockcoll.exists = True
        mockcoll.has_requisite_content_models = False
        response = self.client.get(view_url)
        expected, got = 404, response.status_code
        self.assertEqual(expected, got,
            'expected status code %s but got %s for view collection when object has wrong cmodels' % \
            (expected, got))

        # simulate valid fedora object
        mockcoll.has_requisite_content_models = True
        mockcoll.short_label = 'Yellowbacks'
        mockcoll.pid = 'coll:1'
        mockcoll.dc.content.description = 'Cheap 19thc paperbacks, often bound in yellow.'
        # simulate sunburnt's fluid interface
        # set up mock results for display on template
        solr_result = [
            SolrVolume(
                **{
                    'pid':
                    'vol:1',
                    'title':
                    'Asodecoan',
                    'creator': [
                        'Atlanta-Southern Dental College.; Emory University Archives.'
                    ]
                }),
            SolrVolume(
                **{
                    'pid': 'vol:2',
                    'title': 'Sugar Crop of Lousiana',
                    'label': 'ocm123_V.2',
                    'date': ['1831']
                }),
        ]
        results = NonCallableMagicMock(spec=['__iter__', 'facet_counts'])
        results.__iter__.return_value = iter(solr_result)
        results.facet_counts.facet_queries = []

        # - using a noncallable for the pagination result that is used in the template
        # because passing callables into django templates does weird things
        mockpage = NonCallableMock()
        mockpaginator.return_value.page.return_value = mockpage
        mockpage.object_list = results
        mockpage.has_other_pages = False
        mockpage.paginator.count = 2
        mockpage.paginator.page_range = [1]
        mockpaginator.return_value.count = 2
        mockpaginator.return_value.page_range = [1]

        response = self.client.get(view_url)
        # inspect solr query
        mocksolr.query.assert_called_with(
            content_model=Volume.VOLUME_CMODEL_PATTERN,
            collection_id=mockcoll.pid)
        mocksolr.query.sort_by.assert_any_call('title_exact')
        mocksolr.query.sort_by.assert_any_call('label')
        mocksolr.query.results_as.assert_called_with(SolrVolume)
        # inspect html result
        self.assertContains(
            response,
            mockcoll.short_label,
            msg_prefix='collection short label should be displayed')
        self.assertContains(
            response,
            '<title>%s Collection | Readux</title>' % mockcoll.short_label,
            html=True,
            msg_prefix='collection label should be included in html title')
        self.assertContains(
            response,
            mockcoll.dc.content.description,
            msg_prefix='collection dc:description should be displayed')
        self.assertContains(
            response,
            '2 volumes in this collection',
            msg_prefix=
            'total count of volumes in the collection should be displayed')
        self.assertContains(
            response,
            solr_result[0]['title'],
            msg_prefix='volume title %(title)s should be displayed' %
            solr_result[0])
        self.assertContains(
            response,
            solr_result[1]['title'],
            msg_prefix='volume title %(title)s should be displayed' %
            solr_result[1])
        self.assertContains(
            response,
            '[V.2]',
            msg_prefix='volume number should be displayed when present')
        # date/creator
        self.assertContains(
            response,
            '(%s)' % solr_result[1]['date'][0],
            msg_prefix='volume date should be displayed when present')
        self.assertContains(
            response,
            '%s' % solr_result[0]['creator'][0],
            msg_prefix='volume author/creator should be displayed when present'
        )

        # check that unapi / zotero harvest is enabled
        self.assertContains(response,
            '<link rel="unapi-server" type="application/xml" title="unAPI" href="%s" />' % \
            reverse('books:unapi'),
            html=True,
            msg_prefix='link to unAPI server URL should be specified in header')
        self.assertContains(response,
            '<abbr class="unapi-id" title="%s"></abbr>' % solr_result[0]['pid'],
            msg_prefix='unapi item id for %s should be included to allow zotero harvest' % \
                        solr_result[0]['pid'])
        self.assertContains(response,
            '<abbr class="unapi-id" title="%s"></abbr>' % solr_result[1]['pid'],
            msg_prefix='unapi item id for %s should be included to allow zotero harvest' % \
                       solr_result[1]['pid'])

        self.assertFalse(
            response.has_header('last-modified'),
            'last modified header should not be set when no results were found in solr'
        )

        # last-modified
        mocksolr.query.count.return_value = 1
        # set a timestamp to be returned for last-modified conditional processing
        mocksolr.query.return_value.__getitem__.return_value = {
            'timestamp': datetime.now()
        }
        response = self.client.get(view_url)
        self.assertTrue(response.has_header('last-modified'),
                        'last modified header should be set')

        ## annotation totals
        # empty annotation total in context for anonymous user
        self.assertEqual({}, response.context['annotated_volumes'])
        # check that annotation total is retrieved for ONLY logged in users
        with patch('readux.collection.views.Volume') as mockvolclass:

            self.client.get(view_url)
            mockvolclass.volume_annotation_count.assert_not_called()

            User = get_user_model()
            credentials = {'username': '******', 'password': '******'}
            testuser = User.objects.create_user(**credentials)
            self.client.login(**credentials)
            self.client.get(view_url)
            mockvolclass.volume_annotation_count.assert_called_with(testuser)
Esempio n. 7
0
    def test_search(self, mockpaginator, mocksolr_interface, mocksearch_libs):
        search_url = reverse('search:keyword')
        mocksolr = mocksolr_interface.return_value

        mocksolr.query.return_value = mocksolr.query
        for method in [
                'query', 'facet_by', 'sort_by', 'field_limit', 'exclude',
                'filter'
        ]:
            getattr(mocksolr.query, method).return_value = mocksolr.query

        # testing guest access - should be denied
        response = self.client.get(search_url, {'keyword': '*invalid'})
        expected, got = 302, response.status_code
        self.assertEqual(expected, got,
            'Expected status code %s when accessing %s as guest, got %s' % \
            (expected, got,search_url))
        self.assert_(
            reverse('accounts:login') in response['Location'],
            'guest access to search should redirect to login page')

        # create researcher IP for localhost so anonymous access will be
        # treated as anonymous researcher
        researchip = ResearcherIP(name='test client', ip_address='127.0.0.1')
        researchip.save()

        # invalid search term (leading wildcard)
        response = self.client.get(search_url, {'keyword': '*invalid'})
        self.assertContains(response,
                            'Search terms may not begin with wildcards')

        # search all items
        response = self.client.get(search_url)
        # check solr query args
        # - query should be called with no search terms (find all)
        mocksolr.query.assert_called_with()
        # - sort by title when no search terms for relevance to be meaningful
        mocksolr.query.sort_by.assert_called_with('title_exact')
        # check context params ?

        mocksolr.query.filter.assert_called_with(has_access_copy=True,
                                                 researcher_access=True)

        # NOTE: template logic tested separately to avoid
        # complications with callable Mock objects
        testresult = {
            'object_type': 'audio',
            'pid': 'testobj:1',
            'title': 'Something Interesting',
            'collection_id': 'testcoll:123',
            'collection_source_id': '123',
            'collection_label': 'Papers of Somebody SoandSo',
            'date_created': '1990',
            'date_issued': '1991',
            'part': 'Side 1',
            'duration': 65,
            'ark_uri': 'http://pid.co/ark:/1234/53bs4',
        }

        # use a noncallable for the pagination result that is used in the template
        # because passing callables into django templates does weird things
        mockpage = NonCallableMock()
        mockpaginator.return_value.page.return_value = mockpage
        mockpage.object_list = [testresult]
        mockpage.has_other_pages = False
        mockpage.paginator.count = 1
        mockpage.paginator.page_range = [1]

        # search with search terms
        response = self.client.get(search_url,
                                   {'keyword': 'fantabulous expurgation'})
        # check solr query args
        # - query should be called with tokenized search terms
        mocksolr.query.query.assert_called_with('fantabulous', 'expurgation')
        # - sort by relevance score when there are search terms
        mocksolr.query.sort_by.assert_called_with('-score')
        # - include relevance score in return values
        # NOTE: not testing via assert_called_with because now sunburnt
        # seems to require a full list of all fields to return
        args, kwargs = mocksolr.query.field_limit.call_args
        self.assertTrue(kwargs['score'],
                        'relevance score should be returned from solr')
        # mocksolr.query.field_limit.assert_called_with(score=True)

        # test object info displayed on search result page from mock result
        self.assertContains(
            response,
            reverse('audio:view', args=[testresult['pid']]),
            msg_prefix='search result links to audio view page')
        self.assertContains(
            response,
            testresult['title'],
            msg_prefix='search result displays audio object title')
        # add expected url to test result dict to simplify testing
        testresult['url'] = reverse(
            'collection:view', kwargs={'pid': testresult['collection_id']})
        self.assertContains(response,
            '<h4><a class="text-muted" href="%(url)s">%(collection_source_id)s : %(collection_label)s</a></h4>' % \
            testresult,
            html=True,
            msg_prefix='collection number and label should both be displayed')
        self.assertContains(
            response,
            '<li><h4>Issued</h4><span>%(date_issued)s</span></li>' %
            testresult,
            html=True,
            msg_prefix='date issued and should be displayed when present')
        self.assertContains(
            response,
            '<li><h4>Created</h4><span>%(date_created)s</span></li>' %
            testresult,
            html=True,
            msg_prefix='date issued and should be displayed when present')
        self.assertContains(
            response,
            testresult['part'],
            msg_prefix='part note should be displayed when present')
        self.assertContains(
            response,
            '1 min, 5 secs',
            msg_prefix=
            'duration should be displayed in abbreviated human-readable form')
        self.assertContains(
            response,
            testresult['ark_uri'].split('/')[-1],
            msg_prefix='ARK NOID should be displayed when present')

        # TODO: test edit link is only present if user has permission to edit

        researchip.delete()