Reorganized, renamed, and improved docstrings of TestCases.

This commit is contained in:
Jorge C. Leitão 2014-05-20 07:35:29 +02:00
parent b4a41565a5
commit 229752da87
3 changed files with 246 additions and 213 deletions

View file

@ -6,18 +6,15 @@ import xapian
import subprocess
import os
from django.conf import settings
from django.db import models
from django.test import TestCase
from haystack import connections, reset_search_queries
from haystack import connections
from haystack import indexes
from haystack.backends.xapian_backend import InvalidIndexError, _term_to_xapian_value
from haystack.models import SearchResult
from haystack.query import SearchQuerySet, SQ
from haystack.utils.loading import UnifiedIndex
from core.models import MockTag, MockModel, AnotherMockModel, AFourthMockModel
from core.models import MockTag, MockModel, AnotherMockModel
from core.tests.mocks import MockSearchResult
@ -158,8 +155,8 @@ class XapianSimpleMockIndex(indexes.SearchIndex):
class HaystackBackendTestCase(object):
"""
An abstract TestCase that implements a hack to ensure connections
has the mock index
Abstract TestCase that implements an hack to ensure `connections`
has the right index
It has a method get_index() that returns a SearchIndex
that must be overwritten.
@ -183,13 +180,19 @@ class HaystackBackendTestCase(object):
connections['default']._index = self.old_ui
class XapianBackendTestCase(HaystackBackendTestCase, TestCase):
class BackendIndexationTestCase(HaystackBackendTestCase, TestCase):
"""
Tests indexation behavior.
Tests related to how the backend indexes terms,
values, and others go here.
"""
def get_index(self):
return XapianSimpleMockIndex()
def setUp(self):
super(XapianBackendTestCase, self).setUp()
super(BackendIndexationTestCase, self).setUp()
mock = XapianMockModel()
mock.id = 1
self.backend.update(self.index, [mock])
@ -298,13 +301,19 @@ class XapianBackendTestCase(HaystackBackendTestCase, TestCase):
self.assertIn('corrup\xe7\xe3o', terms)
class XapianSearchBackendTestCase(HaystackBackendTestCase, TestCase):
class BackendFeaturesTestCase(HaystackBackendTestCase, TestCase):
"""
Tests supported features on the backend side.
Tests to features implemented on the backend
go here.
"""
def get_index(self):
return XapianMockSearchIndex()
def setUp(self):
super(XapianSearchBackendTestCase, self).setUp()
super(BackendFeaturesTestCase, self).setUp()
self.sample_objs = []
@ -589,10 +598,16 @@ class XapianSearchBackendTestCase(HaystackBackendTestCase, TestCase):
if xapian.minor_version() >= 2:
self.assertEqual(str(self.backend.parse_query('name:da*')),
'Xapian::Query((XNAMEdavid1:(pos=1) SYNONYM XNAMEdavid2:(pos=1) SYNONYM XNAMEdavid3:(pos=1)))')
'Xapian::Query(('
'XNAMEdavid1:(pos=1) SYNONYM '
'XNAMEdavid2:(pos=1) SYNONYM '
'XNAMEdavid3:(pos=1)))')
else:
self.assertEqual(str(self.backend.parse_query('name:da*')),
'Xapian::Query((XNAMEdavid1:(pos=1) OR XNAMEdavid2:(pos=1) OR XNAMEdavid3:(pos=1)))')
'Xapian::Query(('
'XNAMEdavid1:(pos=1) OR '
'XNAMEdavid2:(pos=1) OR '
'XNAMEdavid3:(pos=1)))')
self.assertEqual(str(self.backend.parse_query('name:david1..david2')),
'Xapian::Query(VALUE_RANGE 7 david1 david2)')
@ -648,192 +663,3 @@ class XapianSearchBackendTestCase(HaystackBackendTestCase, TestCase):
self.backend.silently_fail = False
self.assertRaises(InvalidIndexError, self.backend.more_like_this, mock)
class LiveXapianMockSearchIndex(indexes.SearchIndex):
text = indexes.CharField(document=True, use_template=True)
name = indexes.CharField(model_attr='author', faceted=True)
pub_date = indexes.DateTimeField(model_attr='pub_date')
title = indexes.CharField()
def get_model(self):
return MockModel
class LiveXapianSearchQueryTestCase(HaystackBackendTestCase, TestCase):
"""
SearchQuery specific tests
"""
fixtures = ['initial_data.json']
def get_index(self):
return LiveXapianMockSearchIndex()
def setUp(self):
super(LiveXapianSearchQueryTestCase, self).setUp()
self.backend.update(self.index, MockModel.objects.all())
self.sq = connections['default'].get_query()
def test_get_spelling(self):
self.sq.add_filter(SQ(content='indxd'))
self.assertEqual(self.sq.get_spelling_suggestion(), 'indexed')
self.assertEqual(self.sq.get_spelling_suggestion('indxd'), 'indexed')
def test_startswith(self):
self.sq.add_filter(SQ(name__startswith='da'))
self.assertEqual([result.pk for result in self.sq.get_results()], [1, 2, 3])
def test_build_query_gt(self):
self.sq.add_filter(SQ(name__gt='m'))
self.assertEqual(str(self.sq.build_query()),
'Xapian::Query((<alldocuments> AND_NOT VALUE_RANGE 3 a m))')
def test_build_query_gte(self):
self.sq.add_filter(SQ(name__gte='m'))
self.assertEqual(str(self.sq.build_query()),
'Xapian::Query(VALUE_RANGE 3 m zzzzzzzzzzzzzzzzzzzzzzzzzzzz'
'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'
'zzzzzzzzzzzzzz)')
def test_build_query_lt(self):
self.sq.add_filter(SQ(name__lt='m'))
self.assertEqual(str(self.sq.build_query()),
'Xapian::Query((<alldocuments> AND_NOT '
'VALUE_RANGE 3 m zzzzzzzzzzzzzzzzzzzzzz'
'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'
'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz))')
def test_build_query_lte(self):
self.sq.add_filter(SQ(name__lte='m'))
self.assertEqual(str(self.sq.build_query()), 'Xapian::Query(VALUE_RANGE 3 a m)')
def test_build_query_multiple_filter_types(self):
self.sq.add_filter(SQ(content='why'))
self.sq.add_filter(SQ(pub_date__lte=datetime.datetime(2009, 2, 10, 1, 59, 0)))
self.sq.add_filter(SQ(name__gt='david'))
self.sq.add_filter(SQ(title__gte='B'))
self.sq.add_filter(SQ(django_id__in=[1, 2, 3]))
self.assertEqual(str(self.sq.build_query()),
'Xapian::Query(((Zwhi OR why) AND '
'VALUE_RANGE 5 00010101000000 20090210015900 AND '
'(<alldocuments> AND_NOT VALUE_RANGE 3 a david) AND '
'VALUE_RANGE 7 b zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'
'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz AND '
'(QQ000000000001 OR QQ000000000002 OR QQ000000000003)))')
def test_log_query(self):
reset_search_queries()
self.assertEqual(len(connections['default'].queries), 0)
# Stow.
old_debug = settings.DEBUG
settings.DEBUG = False
len(self.sq.get_results())
self.assertEqual(len(connections['default'].queries), 0)
settings.DEBUG = True
# Redefine it to clear out the cached results.
self.sq = connections['default'].get_query()
self.sq.add_filter(SQ(name='bar'))
len(self.sq.get_results())
self.assertEqual(len(connections['default'].queries), 1)
self.assertEqual(str(connections['default'].queries[0]['query_string']), 'Xapian::Query((ZXNAMEbar OR XNAMEbar))')
# And again, for good measure.
self.sq = connections['default'].get_query()
self.sq.add_filter(SQ(name='bar'))
self.sq.add_filter(SQ(text='moof'))
len(self.sq.get_results())
self.assertEqual(len(connections['default'].queries), 2)
self.assertEqual(str(connections['default'].queries[0]['query_string']), 'Xapian::Query((ZXNAMEbar OR XNAMEbar))')
self.assertEqual(str(connections['default'].queries[1]['query_string']), 'Xapian::Query(((ZXNAMEbar OR XNAMEbar) AND (ZXTEXTmoof OR XTEXTmoof)))')
# Restore.
settings.DEBUG = old_debug
class LiveXapianSearchQuerySetTestCase(HaystackBackendTestCase, TestCase):
"""
SearchQuerySet specific tests
"""
fixtures = ['initial_data.json']
def get_index(self):
return LiveXapianMockSearchIndex()
def setUp(self):
super(LiveXapianSearchQuerySetTestCase, self).setUp()
self.backend.update(self.index, MockModel.objects.all())
self.sq = connections['default'].get_query()
self.sqs = SearchQuerySet()
def test_result_class(self):
# Assert that we're defaulting to ``SearchResult``.
sqs = self.sqs.all()
self.assertTrue(isinstance(sqs[0], SearchResult))
# Custom class.
sqs = self.sqs.result_class(MockSearchResult).all()
self.assertTrue(isinstance(sqs[0], MockSearchResult))
# Reset to default.
sqs = self.sqs.result_class(None).all()
self.assertTrue(isinstance(sqs[0], SearchResult))
def test_facet(self):
self.assertEqual(len(self.sqs.facet('name').facet_counts()['fields']['name']), 3)
class XapianBoostMockSearchIndex(indexes.SearchIndex):
text = indexes.CharField(
document=True, use_template=True,
template_name='search/indexes/core/mockmodel_template.txt'
)
author = indexes.CharField(model_attr='author', weight=2.0)
editor = indexes.CharField(model_attr='editor')
pub_date = indexes.DateField(model_attr='pub_date')
def get_model(self):
return AFourthMockModel
class XapianBoostBackendTestCase(HaystackBackendTestCase, TestCase):
def get_index(self):
return XapianBoostMockSearchIndex()
def setUp(self):
super(XapianBoostBackendTestCase, self).setUp()
self.sample_objs = []
for i in range(1, 5):
mock = AFourthMockModel()
mock.id = i
if i % 2:
mock.author = 'daniel'
mock.editor = 'david'
else:
mock.author = 'david'
mock.editor = 'daniel'
mock.pub_date = datetime.date(2009, 2, 25) - datetime.timedelta(days=i)
self.sample_objs.append(mock)
self.backend.update(self.index, self.sample_objs)
def test_boost(self):
sqs = SearchQuerySet()
self.assertEqual(len(sqs.all()), 4)
results = sqs.filter(SQ(author='daniel') | SQ(editor='daniel'))
self.assertEqual([result.id for result in results], [
'core.afourthmockmodel.1',
'core.afourthmockmodel.3',
'core.afourthmockmodel.2',
'core.afourthmockmodel.4'
])

View file

@ -9,16 +9,19 @@ from haystack.query import SearchQuerySet
from xapian_tests.models import Document
from xapian_tests.search_indexes import DocumentIndex
from xapian_tests.tests.test_xapian_backend import get_terms
from xapian_tests.tests.test_backend import pks
def pks(results):
return [result.pk for result in results]
class InterfaceTestCase(TestCase):
"""
Tests the interface of Xapian-Haystack.
class LiveXapianTestCase(TestCase):
Tests related to usability and expected behavior
go here.
"""
def setUp(self):
super(InterfaceTestCase, self).setUp()
types_names = ['book', 'magazine', 'article']
texts = ['This is a huge text',
@ -53,6 +56,7 @@ class LiveXapianTestCase(TestCase):
def tearDown(self):
Document.objects.all().delete()
self.backend.clear()
super(InterfaceTestCase, self).tearDown()
def test_count(self):
self.assertEqual(self.queryset.count(), Document.objects.count())

View file

@ -2,17 +2,20 @@ from __future__ import unicode_literals
import datetime
from django.conf import settings
from django.test import TestCase
from haystack import indexes
from haystack import connections
from haystack.query import SQ
from haystack import connections, reset_search_queries
from haystack.models import SearchResult
from haystack.query import SearchQuerySet, SQ
from core.models import MockModel, AnotherMockModel
from xapian_tests.tests.test_xapian_backend import HaystackBackendTestCase
from core.models import MockModel, AnotherMockModel, AFourthMockModel
from core.tests.mocks import MockSearchResult
from xapian_tests.tests.test_backend import HaystackBackendTestCase
class XapianMockQueryIndex(indexes.SearchIndex):
class MockQueryIndex(indexes.SearchIndex):
text = indexes.CharField(document=True)
pub_date = indexes.DateTimeField()
title = indexes.CharField()
@ -24,7 +27,7 @@ class XapianMockQueryIndex(indexes.SearchIndex):
class XapianSearchQueryTestCase(HaystackBackendTestCase, TestCase):
def get_index(self):
return XapianMockQueryIndex()
return MockQueryIndex()
def setUp(self):
super(XapianSearchQueryTestCase, self).setUp()
@ -231,3 +234,203 @@ class XapianSearchQueryTestCase(HaystackBackendTestCase, TestCase):
self.assertEqual(str(self.sq.build_query()),
'Xapian::Query(((Zwhi OR why) AND '
'("XTITLE1" OR "XTITLE2" OR "XTITLE3")))')
class MockSearchIndex(indexes.SearchIndex):
text = indexes.CharField(document=True, use_template=True)
name = indexes.CharField(model_attr='author', faceted=True)
pub_date = indexes.DateTimeField(model_attr='pub_date')
title = indexes.CharField()
def get_model(self):
return MockModel
class SearchQueryTestCase(HaystackBackendTestCase, TestCase):
"""
Tests expected behavior of
SearchQuery.
"""
fixtures = ['initial_data.json']
def get_index(self):
return MockSearchIndex()
def setUp(self):
super(SearchQueryTestCase, self).setUp()
self.backend.update(self.index, MockModel.objects.all())
self.sq = connections['default'].get_query()
def test_get_spelling(self):
self.sq.add_filter(SQ(content='indxd'))
self.assertEqual(self.sq.get_spelling_suggestion(), 'indexed')
self.assertEqual(self.sq.get_spelling_suggestion('indxd'), 'indexed')
def test_startswith(self):
self.sq.add_filter(SQ(name__startswith='da'))
self.assertEqual([result.pk for result in self.sq.get_results()], [1, 2, 3])
def test_build_query_gt(self):
self.sq.add_filter(SQ(name__gt='m'))
self.assertEqual(str(self.sq.build_query()),
'Xapian::Query((<alldocuments> AND_NOT VALUE_RANGE 3 a m))')
def test_build_query_gte(self):
self.sq.add_filter(SQ(name__gte='m'))
self.assertEqual(str(self.sq.build_query()),
'Xapian::Query(VALUE_RANGE 3 m zzzzzzzzzzzzzzzzzzzzzzzzzzzz'
'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'
'zzzzzzzzzzzzzz)')
def test_build_query_lt(self):
self.sq.add_filter(SQ(name__lt='m'))
self.assertEqual(str(self.sq.build_query()),
'Xapian::Query((<alldocuments> AND_NOT '
'VALUE_RANGE 3 m zzzzzzzzzzzzzzzzzzzzzz'
'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'
'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz))')
def test_build_query_lte(self):
self.sq.add_filter(SQ(name__lte='m'))
self.assertEqual(str(self.sq.build_query()), 'Xapian::Query(VALUE_RANGE 3 a m)')
def test_build_query_multiple_filter_types(self):
self.sq.add_filter(SQ(content='why'))
self.sq.add_filter(SQ(pub_date__lte=datetime.datetime(2009, 2, 10, 1, 59, 0)))
self.sq.add_filter(SQ(name__gt='david'))
self.sq.add_filter(SQ(title__gte='B'))
self.sq.add_filter(SQ(django_id__in=[1, 2, 3]))
self.assertEqual(str(self.sq.build_query()),
'Xapian::Query(((Zwhi OR why) AND '
'VALUE_RANGE 5 00010101000000 20090210015900 AND '
'(<alldocuments> AND_NOT VALUE_RANGE 3 a david) AND '
'VALUE_RANGE 7 b zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'
'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz AND '
'(QQ000000000001 OR QQ000000000002 OR QQ000000000003)))')
def test_log_query(self):
reset_search_queries()
self.assertEqual(len(connections['default'].queries), 0)
# Stow.
old_debug = settings.DEBUG
settings.DEBUG = False
len(self.sq.get_results())
self.assertEqual(len(connections['default'].queries), 0)
settings.DEBUG = True
# Redefine it to clear out the cached results.
self.sq = connections['default'].get_query()
self.sq.add_filter(SQ(name='bar'))
len(self.sq.get_results())
self.assertEqual(len(connections['default'].queries), 1)
self.assertEqual(str(connections['default'].queries[0]['query_string']),
'Xapian::Query((ZXNAMEbar OR XNAMEbar))')
# And again, for good measure.
self.sq = connections['default'].get_query()
self.sq.add_filter(SQ(name='bar'))
self.sq.add_filter(SQ(text='moof'))
len(self.sq.get_results())
self.assertEqual(len(connections['default'].queries), 2)
self.assertEqual(str(connections['default'].queries[0]['query_string']),
'Xapian::Query(('
'ZXNAMEbar OR '
'XNAMEbar))')
self.assertEqual(str(connections['default'].queries[1]['query_string']),
'Xapian::Query(('
'(ZXNAMEbar OR XNAMEbar) AND '
'(ZXTEXTmoof OR XTEXTmoof)))')
# Restore.
settings.DEBUG = old_debug
class LiveSearchQuerySetTestCase(HaystackBackendTestCase, TestCase):
"""
SearchQuerySet specific tests
"""
fixtures = ['initial_data.json']
def get_index(self):
return MockSearchIndex()
def setUp(self):
super(LiveSearchQuerySetTestCase, self).setUp()
self.backend.update(self.index, MockModel.objects.all())
self.sq = connections['default'].get_query()
self.sqs = SearchQuerySet()
def test_result_class(self):
# Assert that we're defaulting to ``SearchResult``.
sqs = self.sqs.all()
self.assertTrue(isinstance(sqs[0], SearchResult))
# Custom class.
sqs = self.sqs.result_class(MockSearchResult).all()
self.assertTrue(isinstance(sqs[0], MockSearchResult))
# Reset to default.
sqs = self.sqs.result_class(None).all()
self.assertTrue(isinstance(sqs[0], SearchResult))
def test_facet(self):
self.assertEqual(len(self.sqs.facet('name').facet_counts()['fields']['name']), 3)
class BoostMockSearchIndex(indexes.SearchIndex):
text = indexes.CharField(
document=True, use_template=True,
template_name='search/indexes/core/mockmodel_template.txt'
)
author = indexes.CharField(model_attr='author', weight=2.0)
editor = indexes.CharField(model_attr='editor')
pub_date = indexes.DateField(model_attr='pub_date')
def get_model(self):
return AFourthMockModel
class BoostFieldTestCase(HaystackBackendTestCase, TestCase):
"""
Tests boosted fields.
"""
def get_index(self):
return BoostMockSearchIndex()
def setUp(self):
super(BoostFieldTestCase, self).setUp()
self.sample_objs = []
for i in range(1, 5):
mock = AFourthMockModel()
mock.id = i
if i % 2:
mock.author = 'daniel'
mock.editor = 'david'
else:
mock.author = 'david'
mock.editor = 'daniel'
mock.pub_date = datetime.date(2009, 2, 25) - datetime.timedelta(days=i)
self.sample_objs.append(mock)
self.backend.update(self.index, self.sample_objs)
def test_boost(self):
sqs = SearchQuerySet()
self.assertEqual(len(sqs.all()), 4)
results = sqs.filter(SQ(author='daniel') | SQ(editor='daniel'))
self.assertEqual([result.id for result in results], [
'core.afourthmockmodel.1',
'core.afourthmockmodel.3',
'core.afourthmockmodel.2',
'core.afourthmockmodel.4'
])