Esempio n. 1
0
    def test_volume_export(self, mockrepo):
        mockobj = NonCallableMock()
        mockobj.pid = 'vol:1'
        mockobj.title = 'Lecoq, the detective'
        mockobj.volume = 'V.1'
        mockobj.date = ['1801']
        mockrepo.return_value.get_object.return_value = mockobj
        # to support for last modified conditional
        mockobj.ocr.created = datetime.now()

        # anonymous
        export_url = reverse('books:webexport', kwargs={'pid': mockobj.pid})
        response = self.client.get(export_url)
        self.assertContains(
            response,
            '''<div class="alert alert-warning">Export functionality is only available
      to logged in users.</div>''',
            msg_prefix=
            'Anonymous user should see warning when viewing export page',
            html=True)

        # log in as a regular user
        self.client.login(**self.user_credentials['user'])
        response = self.client.get(export_url)
        self.assert_(
            'export_form' in response.context,
            'export form should be set in response context for logged in user')
        self.assertContains(
            response,
            'Export to GitHub requires a GitHub account.',
            msg_prefix='user should see a warning about github account')
Esempio n. 2
0
    def test_volume_export(self, mockrepo):
        mockobj = NonCallableMock()
        mockobj.pid = 'vol:1'
        mockobj.title = 'Lecoq, the detective'
        mockobj.volume = 'V.1'
        mockobj.date = ['1801']
        mockrepo.return_value.get_object.return_value = mockobj
        # to support for last modified conditional
        mockobj.ocr.created = datetime.now()

        # anonymous
        export_url = reverse('books:webexport', kwargs={'pid': mockobj.pid})
        response = self.client.get(export_url)
        self.assertContains(response,
            '''<div class="alert alert-warning">Export functionality is only available
      to logged in users.</div>''',
            msg_prefix='Anonymous user should see warning when viewing export page',
            html=True)

        # log in as a regular user
        self.client.login(**self.user_credentials['user'])
        response = self.client.get(export_url)
        self.assert_('export_form' in response.context,
            'export form should be set in response context for logged in user')
        self.assertContains(response, 'Export to GitHub requires a GitHub account.',
            msg_prefix='user should see a warning about github account')
Esempio n. 3
0
    def test_volume_pages(self, mockpaginator, mockrepo):
        mockvol = NonCallableMock(spec=Volume)
        mockvol.pid = 'vol:1'
        mockvol.title = 'Lecoq, the detective'
        mockvol.date = ['1801']
        # second object retrieved from fedora is page, for layout
        mockvol.width = 150
        mockvol.height = 200
        # volume url needed to identify annotations for pages in this volume
        mockvol.get_absolute_url.return_value = reverse(
            'books:volume', kwargs={'pid': mockvol.pid})
        mockrepo.return_value.get_object.return_value = mockvol
        mockvol.find_solr_pages = MagicMock()
        mockvol.find_solr_pages.return_value.count = 3
        mockvol.find_solr_pages.__len__.return_value = 3
        mockpage = Mock(width=640, height=400)
        mockvol.pages = [mockpage]

        vol_page_url = reverse('books:pages', kwargs={'pid': mockvol.pid})
        response = self.client.get(vol_page_url)
        # volume method should be used to find pages
        self.assert_(call() in mockvol.find_solr_pages.call_args_list)
        # volume should be set in context
        self.assert_(mockvol, response.context['vol'])
        # annotated pages should be empty for anonymous user
        self.assertEqual({}, response.context['annotated_pages'])

        # log in as a regular user
        self.client.login(**self.user_credentials['user'])
        testuser = get_user_model().objects.get(
            username=self.user_credentials['user']['username'])

        page1_url = reverse('books:page',
                            kwargs={
                                'vol_pid': mockvol.pid,
                                'pid': 'page:1'
                            })
        page2_url = reverse('books:page',
                            kwargs={
                                'vol_pid': mockvol.pid,
                                'pid': 'page:2'
                            })
        page3_url = reverse('books:page',
                            kwargs={
                                'vol_pid': mockvol.pid,
                                'pid': 'page:3'
                            })
        mockvol.page_annotation_count.return_value = {
            absolutize_url(page1_url): 5,
            absolutize_url(page2_url): 2,
            page3_url: 13
        }
        response = self.client.get(vol_page_url)
        mockvol.page_annotation_count.assert_called_with(testuser)
        annotated_pages = response.context['annotated_pages']
        # counts should be preserved; urls should be non-absolute
        # whether they started that way or not
        self.assertEqual(5, annotated_pages[absolutize_url(page1_url)])
        self.assertEqual(2, annotated_pages[absolutize_url(page2_url)])
        self.assertEqual(13, annotated_pages[page3_url])
Esempio n. 4
0
    def test_volume_pages(self, mockpaginator, mockrepo):
        mockvol = NonCallableMock(spec=Volume)
        mockvol.pid = 'vol:1'
        mockvol.title = 'Lecoq, the detective'
        mockvol.date = ['1801']
        # second object retrieved from fedora is page, for layout
        mockvol.width = 150
        mockvol.height = 200
        # volume url needed to identify annotations for pages in this volume
        mockvol.get_absolute_url.return_value = reverse('books:volume',
            kwargs={'pid': mockvol.pid})
        mockrepo.return_value.get_object.return_value = mockvol
        mockvol.find_solr_pages = MagicMock()
        mockvol.find_solr_pages.return_value.count = 3
        mockvol.find_solr_pages.__len__.return_value = 3
        mockpage = Mock(width=640, height=400)
        mockvol.pages = [mockpage]

        vol_page_url = reverse('books:pages', kwargs={'pid': mockvol.pid})
        response = self.client.get(vol_page_url)
        # volume method should be used to find pages
        self.assert_(call() in mockvol.find_solr_pages.call_args_list)
        # volume should be set in context
        self.assert_(mockvol, response.context['vol'])
        # annotated pages should be empty for anonymous user
        self.assertEqual({}, response.context['annotated_pages'])

        # log in as a regular user
        self.client.login(**self.user_credentials['user'])
        testuser = get_user_model().objects.get(username=self.user_credentials['user']['username'])

        page1_url = reverse('books:page', kwargs={'vol_pid': mockvol.pid, 'pid': 'page:1'})
        page2_url = reverse('books:page', kwargs={'vol_pid': mockvol.pid, 'pid': 'page:2'})
        page3_url = reverse('books:page', kwargs={'vol_pid': mockvol.pid, 'pid': 'page:3'})
        mockvol.page_annotation_count.return_value = {
          absolutize_url(page1_url): 5,
          absolutize_url(page2_url): 2,
          page3_url: 13
        }
        response = self.client.get(vol_page_url)
        mockvol.page_annotation_count.assert_called_with(testuser)
        annotated_pages = response.context['annotated_pages']
        # counts should be preserved; urls should be non-absolute
        # whether they started that way or not
        self.assertEqual(5, annotated_pages[absolutize_url(page1_url)])
        self.assertEqual(2, annotated_pages[absolutize_url(page2_url)])
        self.assertEqual(13, annotated_pages[page3_url])
Esempio n. 5
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. 6
0
    def test_volume(self, mockrepo):
        mockobj = NonCallableMock()
        mockobj.pid = 'vol:1'
        mockobj.title = 'Lecoq, the detective'
        mockobj.volume = 'V.1'
        mockobj.date = ['1801']
        mockobj.creator = ['Gaboriau, Emile']
        mockobj.book.dc.content.description_list = [
            'Translation of: Monsieur Lecoq.',
            'Victorian yellowbacks + paperbacks, 1849-1905'
        ]
        mockobj.book.dc.content.publisher = 'London : Vizetelly'
        mockobj.book.volume_set = [mockobj, NonCallableMock(pid='vol:2')]
        mockobj.pdf_size = 1024
        mockobj.has_pages = False
        mockobj.is_a_volume = True
        mockrepo.return_value.get_object.return_value = mockobj
        # to support for last modified conditional
        mockobj.ocr.created = datetime.now()

        vol_url = reverse('books:volume', kwargs={'pid': mockobj.pid})
        response = self.client.get(vol_url)
        self.assertContains(response,
                            mockobj.title,
                            msg_prefix='response should include title')
        self.assertContains(response,
                            mockobj.volume,
                            msg_prefix='response should include volume label')
        self.assertContains(response,
                            mockobj.date[0],
                            msg_prefix='response should include date')
        self.assertContains(response,
                            mockobj.creator[0],
                            msg_prefix='response should include creator')
        for desc in mockobj.book.dc.content.description_list:
            self.assertContains(
                response,
                desc,
                msg_prefix='response should include dc:description')
        self.assertContains(response,
                            mockobj.book.dc.content.publisher,
                            msg_prefix='response should include publisher')
        self.assertContains(response,
                            reverse('books:pdf', kwargs={'pid': mockobj.pid}),
                            msg_prefix='response should include link to pdf')
        # related volumes
        self.assertContains(
            response,
            'Related volumes',
            msg_prefix='response should include related volumes when present')
        self.assertContains(
            response,
            reverse('books:volume',
                    kwargs={'pid': mockobj.book.volume_set[0].pid}),
            msg_prefix='response should link to related volumes')
        # pdf size
        self.assertContains(
            response,
            filesizeformat(mockobj.pdf_size),
            msg_prefix='PDF size should be displayed in human-readable format')
        # no pages loaded, should not include volume search or read online
        self.assertNotContains(
            response,
            'Read online',
            msg_prefix=
            'volume without pages loaded should not display read online option'
        )
        # NOTE: href needed to differentiate from cover url, which starts the same
        self.assertNotContains(
            response,
            'href="%s"' % reverse('books:pages', kwargs={'pid': mockobj.pid}),
            msg_prefix=
            'volume without pages loaded should not have link to read online')
        self.assertNotContains(
            response,
            '<form id="volume-search" ',
            msg_prefix=
            'volume without pages loaded should not have volume search')

        # annotation total passed to context
        self.assert_(
            'annotated_volumes' not in response.context,
            'annotation count should not be set for volumes without pages')

        # simulate volume with pages loaded
        mockobj.has_pages = True
        # to test annotation count
        mockobj.get_absolute_url.return_value = '/books/vol:1/'
        mockobj.annotation_count.return_value = 5

        response = self.client.get(vol_url)
        # *should* include volume search and read online
        self.assertContains(
            response,
            'Read online',
            msg_prefix=
            'volume with pages loaded should display read online option')
        self.assertContains(
            response,
            reverse('books:pages', kwargs={'pid': mockobj.pid}),
            msg_prefix=
            'volume with pages loaded should have link to read online')
        self.assertContains(
            response,
            '<form id="volume-search" ',
            msg_prefix='volume without pages loaded should have volume search')
        # annotation total passed to context
        self.assertEqual(
            {mockobj.get_absolute_url(): 5},
            response.context['annotated_volumes'],
            'annotation count should be set for volumes with pages')

        mockobj.annotation_count.return_value = 0
        response = self.client.get(vol_url)
        self.assert_(
            'annotated_volumes' not in response.context,
            'annotation count should not be set in context when it is zero')

        # non-existent should 404
        mockobj.exists = False
        response = self.client.get(vol_url)
        expected, got = 404, response.status_code
        self.assertEqual(expected, got,
            'expected %s for %s when object does not exist, got %s' % \
            (expected, vol_url, got))
        # exists but isn't a volume - should also 404
        mockobj.exists = True
        mockobj.is_a_volume = False
        response = self.client.get(vol_url)
        expected, got = 404, response.status_code
        self.assertEqual(expected, got,
            'expected %s for %s when object is not a volume, got %s' % \
            (expected, vol_url, got))
Esempio n. 7
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. 8
0
    def test_volume(self, mockrepo):
        mockobj = NonCallableMock()
        mockobj.pid = 'vol:1'
        mockobj.title = 'Lecoq, the detective'
        mockobj.volume = 'V.1'
        mockobj.date = ['1801']
        mockobj.creator = ['Gaboriau, Emile']
        mockobj.book.dc.content.description_list = [
           'Translation of: Monsieur Lecoq.',
           'Victorian yellowbacks + paperbacks, 1849-1905'
        ]
        mockobj.book.dc.content.publisher = 'London : Vizetelly'
        mockobj.book.volume_set = [mockobj, NonCallableMock(pid='vol:2')]
        mockobj.pdf_size = 1024
        mockobj.has_pages = False
        mockobj.is_a_volume = True
        mockrepo.return_value.get_object.return_value = mockobj
        # to support for last modified conditional
        mockobj.ocr.created = datetime.now()

        vol_url = reverse('books:volume', kwargs={'pid': mockobj.pid})
        response = self.client.get(vol_url)
        self.assertContains(response, mockobj.title,
            msg_prefix='response should include title')
        self.assertContains(response, mockobj.volume,
            msg_prefix='response should include volume label')
        self.assertContains(response, mockobj.date[0],
            msg_prefix='response should include date')
        self.assertContains(response, mockobj.creator[0],
            msg_prefix='response should include creator')
        for desc in mockobj.book.dc.content.description_list:
            self.assertContains(response, desc,
                msg_prefix='response should include dc:description')
        self.assertContains(response, mockobj.book.dc.content.publisher,
            msg_prefix='response should include publisher')
        self.assertContains(response, reverse('books:pdf', kwargs={'pid': mockobj.pid}),
            msg_prefix='response should include link to pdf')
        # related volumes
        self.assertContains(response, 'Related volumes',
            msg_prefix='response should include related volumes when present')
        self.assertContains(response,
            reverse('books:volume', kwargs={'pid': mockobj.book.volume_set[0].pid}),
            msg_prefix='response should link to related volumes')
        # pdf size
        self.assertContains(response, filesizeformat(mockobj.pdf_size),
            msg_prefix='PDF size should be displayed in human-readable format')
        # no pages loaded, should not include volume search or read online
        self.assertNotContains(response, 'Read online',
            msg_prefix='volume without pages loaded should not display read online option')
        # NOTE: href needed to differentiate from cover url, which starts the same
        self.assertNotContains(response, 'href="%s"' % reverse('books:pages', kwargs={'pid': mockobj.pid}),
            msg_prefix='volume without pages loaded should not have link to read online')
        self.assertNotContains(response, '<form id="volume-search" ',
            msg_prefix='volume without pages loaded should not have volume search')

        # annotation total passed to context
        self.assert_('annotated_volumes' not in response.context,
            'annotation count should not be set for volumes without pages')

        # simulate volume with pages loaded
        mockobj.has_pages = True
        # to test annotation count
        mockobj.get_absolute_url.return_value = '/books/vol:1/'
        mockobj.annotation_count.return_value = 5

        response = self.client.get(vol_url)
        # *should* include volume search and read online
        self.assertContains(response, 'Read online',
            msg_prefix='volume with pages loaded should display read online option')
        self.assertContains(response, reverse('books:pages', kwargs={'pid': mockobj.pid}),
            msg_prefix='volume with pages loaded should have link to read online')
        self.assertContains(response, '<form id="volume-search" ',
            msg_prefix='volume without pages loaded should have volume search')
        # annotation total passed to context
        self.assertEqual({mockobj.get_absolute_url(): 5},
            response.context['annotated_volumes'],
            'annotation count should be set for volumes with pages')

        mockobj.annotation_count.return_value = 0
        response = self.client.get(vol_url)
        self.assert_('annotated_volumes' not in response.context,
            'annotation count should not be set in context when it is zero')

        # non-existent should 404
        mockobj.exists = False
        response = self.client.get(vol_url)
        expected, got = 404, response.status_code
        self.assertEqual(expected, got,
            'expected %s for %s when object does not exist, got %s' % \
            (expected, vol_url, got))
        # exists but isn't a volume - should also 404
        mockobj.exists = True
        mockobj.is_a_volume = False
        response = self.client.get(vol_url)
        expected, got = 404, response.status_code
        self.assertEqual(expected, got,
            'expected %s for %s when object is not a volume, got %s' % \
            (expected, vol_url, got))