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'])
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)
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&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)
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'])
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&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)
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)
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()