kopia lustrzana https://github.com/wagtail/wagtail
Rewrite backend tests
rodzic
208ca70b1f
commit
6a52ae0494
|
@ -215,7 +215,7 @@
|
|||
"title": "The Rust Programming Language",
|
||||
"authors": [11, 12],
|
||||
"publication_date": "2018-05-22",
|
||||
"number_of_pages": 488
|
||||
"number_of_pages": 440
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from datetime import date
|
||||
import time
|
||||
import unittest
|
||||
|
||||
|
@ -36,30 +37,401 @@ class BackendTests(WagtailTestUtils):
|
|||
# no conf entry found - skip tests for this backend
|
||||
raise unittest.SkipTest("No WAGTAILSEARCH_BACKENDS entry for the backend %s" % self.backend_path)
|
||||
|
||||
self.load_test_data()
|
||||
management.call_command('update_index', backend_name=self.backend_name, interactive=False, stdout=StringIO())
|
||||
|
||||
def reset_index(self):
|
||||
if self.backend.rebuilder_class:
|
||||
for index, indexed_models in group_models_by_index(self.backend, [models.Author, models.Book, models.Novel]).items():
|
||||
rebuilder = self.backend.rebuilder_class(index)
|
||||
index = rebuilder.start()
|
||||
for model in indexed_models:
|
||||
index.add_model(model)
|
||||
rebuilder.finish()
|
||||
# SEARCH TESTS
|
||||
|
||||
def refresh_index(self):
|
||||
index = self.backend.get_index_for_model(models.Author)
|
||||
if index:
|
||||
index.refresh()
|
||||
def test_search_simple(self):
|
||||
results = self.backend.search("JavaScript", models.Book)
|
||||
self.assertEqual(set(r.title for r in results), {
|
||||
"JavaScript: The good parts",
|
||||
"JavaScript: The Definitive Guide"
|
||||
})
|
||||
|
||||
def test_search_count(self):
|
||||
results = self.backend.search("JavaScript", models.Book)
|
||||
self.assertEqual(results.count(), 2)
|
||||
|
||||
def test_search_blank(self):
|
||||
# Blank searches should never return anything
|
||||
results = self.backend.search("", models.Book)
|
||||
self.assertEqual(set(results), set())
|
||||
|
||||
def test_search_all(self):
|
||||
# Searches on None should return everything in the index
|
||||
# TODO: we have to put [:100] on the end due to issue #3431
|
||||
results = self.backend.search(None, models.Book)[:100]
|
||||
self.assertEqual(set(results), set(models.Book.objects.all()))
|
||||
|
||||
def test_ranking(self):
|
||||
# Note: also tests the "or" operator
|
||||
results = list(self.backend.search("JavaScript Definitive", models.Book, operator='or'))
|
||||
self.assertEqual(set(r.title for r in results), {
|
||||
"JavaScript: The good parts",
|
||||
"JavaScript: The Definitive Guide"
|
||||
})
|
||||
|
||||
# "JavaScript: The Definitive Guide" should be first
|
||||
self.assertEqual(results[0].title, "JavaScript: The Definitive Guide")
|
||||
|
||||
def test_search_and_operator(self):
|
||||
# Should not return "JavaScript: The good parts" as it does not have "Definitive"
|
||||
results = self.backend.search("JavaScript Definitive", models.Book, operator='and')
|
||||
self.assertEqual(set(r.title for r in results), {
|
||||
"JavaScript: The Definitive Guide"
|
||||
})
|
||||
|
||||
def test_search_on_child_class(self):
|
||||
# Searches on a child class should only return results that have the child class as well
|
||||
# and all results should be instances of the child class
|
||||
results = self.backend.search(None, models.Novel)
|
||||
self.assertEqual(set(results), set(models.Novel.objects.all()))
|
||||
|
||||
self.assertIsInstance(results[0], models.Novel)
|
||||
|
||||
def test_search_child_class_field_from_parent(self):
|
||||
# Searches the Book model for content that exists in the Novel model
|
||||
# Note: "Westeros" only occurs in the Novel.setting field
|
||||
# All results should be instances of the parent class
|
||||
results = self.backend.search("Westeros", models.Book)
|
||||
|
||||
self.assertEqual(set(r.title for r in results), {
|
||||
"A Game of Thrones",
|
||||
"A Clash of Kings",
|
||||
"A Storm of Swords"
|
||||
})
|
||||
|
||||
self.assertIsInstance(results[0], models.Book)
|
||||
|
||||
def test_search_on_individual_field(self):
|
||||
# The following query shouldn't search the Novel.setting field so none
|
||||
# of the Novels set in "Westeros" should be returned
|
||||
results = self.backend.search("Westeros Hobbit", models.Book, fields=['title'], operator='or')
|
||||
|
||||
self.assertEqual(set(r.title for r in results), {
|
||||
"The Hobbit"
|
||||
})
|
||||
|
||||
def test_search_on_unknown_field(self):
|
||||
with self.assertRaises(FieldError):
|
||||
list(self.backend.search("Westeros Hobbit", models.Book, fields=['unknown'], operator='or'))
|
||||
|
||||
def test_search_on_non_searchable_field(self):
|
||||
with self.assertRaises(FieldError):
|
||||
list(self.backend.search("Westeros Hobbit", models.Book, fields=['number_of_pages'], operator='or'))
|
||||
|
||||
def test_search_on_related_fields(self):
|
||||
results = self.backend.search("Bilbo Baggins", models.Novel)
|
||||
|
||||
self.assertEqual(set(r.title for r in results), {
|
||||
"The Hobbit",
|
||||
"The Fellowship of the Ring",
|
||||
"The Two Towers",
|
||||
"The Return of the King"
|
||||
})
|
||||
|
||||
def test_search_boosting_on_related_fields(self):
|
||||
# Bilbo Baggins is the protagonist of "The Hobbit" but not any of the "Lord of the Rings" novels.
|
||||
# As the protagonist has more boost than other characters, "The Hobbit" should always be returned
|
||||
# first
|
||||
results = list(self.backend.search("Bilbo Baggins", models.Novel))
|
||||
|
||||
self.assertEqual(results[0].title, "The Hobbit")
|
||||
|
||||
# The remaining results should be scored equally so their rank is undefined
|
||||
self.assertEqual(set(r.title for r in results[1:]), {
|
||||
"The Fellowship of the Ring",
|
||||
"The Two Towers",
|
||||
"The Return of the King"
|
||||
})
|
||||
|
||||
def test_search_callable_field(self):
|
||||
# "Django Two scoops" only mentions "Python" in its "get_programming_language_display"
|
||||
# callable field
|
||||
results = self.backend.search("Python", models.Book)
|
||||
|
||||
self.assertEqual(set(r.title for r in results), {
|
||||
"Learning Python",
|
||||
"Two Scoops of Django 1.11"
|
||||
})
|
||||
|
||||
# FILTERING TESTS
|
||||
|
||||
def test_filter_exact_value(self):
|
||||
results = self.backend.search(None, models.Book.objects.filter(number_of_pages=440))
|
||||
|
||||
self.assertEqual(set(r.title for r in results), {
|
||||
"The Return of the King",
|
||||
"The Rust Programming Language"
|
||||
})
|
||||
|
||||
def test_filter_exact_value_on_parent_model_field(self):
|
||||
results = self.backend.search(None, models.Novel.objects.filter(number_of_pages=440))
|
||||
|
||||
self.assertEqual(set(r.title for r in results), {
|
||||
"The Return of the King"
|
||||
})
|
||||
|
||||
def test_filter_lt(self):
|
||||
results = self.backend.search(None, models.Book.objects.filter(number_of_pages__lt=440))
|
||||
|
||||
self.assertEqual(set(r.title for r in results), {
|
||||
"The Hobbit",
|
||||
"JavaScript: The good parts",
|
||||
"The Fellowship of the Ring",
|
||||
"Foundation",
|
||||
"The Two Towers"
|
||||
})
|
||||
|
||||
def test_filter_lte(self):
|
||||
results = self.backend.search(None, models.Book.objects.filter(number_of_pages__lte=440))
|
||||
|
||||
self.assertEqual(set(r.title for r in results), {
|
||||
"The Return of the King",
|
||||
"The Rust Programming Language",
|
||||
"The Hobbit",
|
||||
"JavaScript: The good parts",
|
||||
"The Fellowship of the Ring",
|
||||
"Foundation",
|
||||
"The Two Towers"
|
||||
})
|
||||
|
||||
def test_filter_gt(self):
|
||||
results = self.backend.search(None, models.Book.objects.filter(number_of_pages__gt=440))
|
||||
|
||||
self.assertEqual(set(r.title for r in results), {
|
||||
"JavaScript: The Definitive Guide",
|
||||
"Learning Python",
|
||||
"A Clash of Kings",
|
||||
"A Game of Thrones",
|
||||
"Two Scoops of Django 1.11",
|
||||
"A Storm of Swords"
|
||||
})
|
||||
|
||||
def test_filter_gte(self):
|
||||
results = self.backend.search(None, models.Book.objects.filter(number_of_pages__gte=440))
|
||||
|
||||
self.assertEqual(set(r.title for r in results), {
|
||||
"The Return of the King",
|
||||
"The Rust Programming Language",
|
||||
"JavaScript: The Definitive Guide",
|
||||
"Learning Python",
|
||||
"A Clash of Kings",
|
||||
"A Game of Thrones",
|
||||
"Two Scoops of Django 1.11",
|
||||
"A Storm of Swords"
|
||||
})
|
||||
|
||||
def test_filter_in_list(self):
|
||||
results = self.backend.search(None, models.Book.objects.filter(number_of_pages__in=[440, 1160]))
|
||||
|
||||
self.assertEqual(set(r.title for r in results), {
|
||||
"The Return of the King",
|
||||
"The Rust Programming Language",
|
||||
"Learning Python"
|
||||
})
|
||||
|
||||
def test_filter_in_iterable(self):
|
||||
results = self.backend.search(None, models.Book.objects.filter(number_of_pages__in=iter([440, 1160])))
|
||||
|
||||
self.assertEqual(set(r.title for r in results), {
|
||||
"The Return of the King",
|
||||
"The Rust Programming Language",
|
||||
"Learning Python"
|
||||
})
|
||||
|
||||
def test_filter_in_values_list_subquery(self):
|
||||
values = models.Book.objects.filter(number_of_pages__lt=440).values_list('number_of_pages', flat=True)
|
||||
results = self.backend.search(None, models.Book.objects.filter(number_of_pages__in=values))
|
||||
|
||||
self.assertEqual(set(r.title for r in results), {
|
||||
"The Hobbit",
|
||||
"JavaScript: The good parts",
|
||||
"The Fellowship of the Ring",
|
||||
"Foundation",
|
||||
"The Two Towers"
|
||||
})
|
||||
|
||||
def test_filter_isnull_true(self):
|
||||
# Note: We don't know the birth dates of any of the programming guide authors
|
||||
results = self.backend.search(None, models.Author.objects.filter(date_of_birth__isnull=True))
|
||||
|
||||
self.assertEqual(set(r.name for r in results), {
|
||||
"David Ascher",
|
||||
"Mark Lutz",
|
||||
"David Flanagan",
|
||||
"Douglas Crockford",
|
||||
"Daniel Roy Greenfeld",
|
||||
"Audrey Roy Greenfeld",
|
||||
"Carol Nichols",
|
||||
"Steve Klabnik"
|
||||
})
|
||||
|
||||
def test_filter_isnull_false(self):
|
||||
# Note: We know the birth dates of all of the novel authors
|
||||
results = self.backend.search(None, models.Author.objects.filter(date_of_birth__isnull=False))
|
||||
|
||||
self.assertEqual(set(r.name for r in results), {
|
||||
"Isaac Asimov",
|
||||
"George R.R. Martin",
|
||||
"J. R. R. Tolkien"
|
||||
})
|
||||
|
||||
def test_filter_prefix(self):
|
||||
results = self.backend.search(None, models.Book.objects.filter(title__startswith="Th"))
|
||||
|
||||
self.assertEqual(set(r.title for r in results), {
|
||||
"The Hobbit",
|
||||
"The Fellowship of the Ring",
|
||||
"The Two Towers",
|
||||
"The Return of the King",
|
||||
"The Rust Programming Language"
|
||||
})
|
||||
|
||||
def test_filter_and_operator(self):
|
||||
results = self.backend.search(
|
||||
None, models.Book.objects.filter(number_of_pages=440) & models.Book.objects.filter(publication_date=date(1955, 10, 20)))
|
||||
|
||||
self.assertEqual(set(r.title for r in results), {
|
||||
"The Return of the King"
|
||||
})
|
||||
|
||||
def test_filter_or_operator(self):
|
||||
results = self.backend.search(None, models.Book.objects.filter(number_of_pages=440) | models.Book.objects.filter(number_of_pages=1160))
|
||||
|
||||
self.assertEqual(set(r.title for r in results), {
|
||||
"Learning Python",
|
||||
"The Return of the King",
|
||||
"The Rust Programming Language"
|
||||
})
|
||||
|
||||
def test_filter_on_non_filterable_field(self):
|
||||
with self.assertRaises(FieldError):
|
||||
list(self.backend.search(None, models.Author.objects.filter(name__startswith="Issac")))
|
||||
|
||||
# ORDER BY RELEVANCE
|
||||
|
||||
def test_order_by_relevance(self):
|
||||
results = self.backend.search(None, models.Novel.objects.order_by('number_of_pages'), order_by_relevance=False)
|
||||
|
||||
# Ordering should be set to "number_of_pages"
|
||||
self.assertEqual(list(r.title for r in results), [
|
||||
"Foundation",
|
||||
"The Hobbit",
|
||||
"The Two Towers",
|
||||
"The Fellowship of the Ring",
|
||||
"The Return of the King",
|
||||
"A Game of Thrones",
|
||||
"A Clash of Kings",
|
||||
"A Storm of Swords"
|
||||
])
|
||||
|
||||
def test_order_by_non_filterable_field(self):
|
||||
with self.assertRaises(FieldError):
|
||||
list(self.backend.search(None, models.Author.objects.order_by('name'), order_by_relevance=False))
|
||||
|
||||
# SLICING TESTS
|
||||
|
||||
def test_single_result(self):
|
||||
# Note: different to test_ranking as that casts the results to the list before doing a key lookup
|
||||
# This test sends the key IDs back into Elasticsearch, performing two separate queries
|
||||
results = self.backend.search("JavaScript Definitive", models.Book, operator='or')
|
||||
self.assertEqual(set(r.title for r in results), {
|
||||
"JavaScript: The good parts",
|
||||
"JavaScript: The Definitive Guide"
|
||||
})
|
||||
|
||||
self.assertEqual(results[0].title, "JavaScript: The Definitive Guide")
|
||||
self.assertEqual(results[1].title, "JavaScript: The good parts")
|
||||
|
||||
def test_limit(self):
|
||||
# Note: we need consistant ordering for this test
|
||||
results = self.backend.search(None, models.Novel.objects.order_by('number_of_pages'), order_by_relevance=False)
|
||||
|
||||
# Limit the results
|
||||
results = results[:3]
|
||||
|
||||
self.assertEqual(list(r.title for r in results), [
|
||||
"Foundation",
|
||||
"The Hobbit",
|
||||
"The Two Towers"
|
||||
])
|
||||
|
||||
def test_offset(self):
|
||||
# Note: we need consistant ordering for this test
|
||||
results = self.backend.search(None, models.Novel.objects.order_by('number_of_pages'), order_by_relevance=False)
|
||||
|
||||
# Offset the results
|
||||
results = results[3:]
|
||||
|
||||
self.assertEqual(list(r.title for r in results), [
|
||||
"The Fellowship of the Ring",
|
||||
"The Return of the King",
|
||||
"A Game of Thrones",
|
||||
"A Clash of Kings",
|
||||
"A Storm of Swords"
|
||||
])
|
||||
|
||||
def test_offset_and_limit(self):
|
||||
# Note: we need consistant ordering for this test
|
||||
results = self.backend.search(None, models.Novel.objects.order_by('number_of_pages'), order_by_relevance=False)
|
||||
|
||||
# Offset the results
|
||||
results = results[3:6]
|
||||
|
||||
self.assertEqual(list(r.title for r in results), [
|
||||
"The Fellowship of the Ring",
|
||||
"The Return of the King",
|
||||
"A Game of Thrones"
|
||||
])
|
||||
|
||||
# MISC TESTS
|
||||
|
||||
def test_same_rank_pages(self):
|
||||
# Checks that results with a same ranking cannot be found multiple times
|
||||
# across pages (see issue #3729).
|
||||
same_rank_objects = set()
|
||||
|
||||
index = self.backend.get_index_for_model(models.Book)
|
||||
for i in range(10):
|
||||
obj = models.Book.objects.create(title='Rank %s' % i, publication_date=date(2017, 10, 18), number_of_pages=100)
|
||||
index.add_item(obj)
|
||||
same_rank_objects.add(obj)
|
||||
index.refresh()
|
||||
|
||||
results = self.backend.search('Rank', models.Book)
|
||||
results_across_pages = set()
|
||||
for i, obj in enumerate(same_rank_objects):
|
||||
results_across_pages.add(results[i:i + 1][0])
|
||||
self.assertSetEqual(results_across_pages, same_rank_objects)
|
||||
|
||||
def test_delete(self):
|
||||
# Delete foundation
|
||||
obj = models.Book.objects.filter(title="Foundation").delete()
|
||||
|
||||
# Refresh the index
|
||||
# Note: The delete signal handler should've removed the book, but we still need to refresh the index manually
|
||||
index = self.backend.get_index_for_model(models.Book)
|
||||
if index:
|
||||
index.refresh()
|
||||
|
||||
def load_test_data(self):
|
||||
self.reset_index()
|
||||
# To test that the book was deleted from the index as well, we will perform the slicing check from an earlier
|
||||
# test where "Foundation" was the first result. We need to test it this way so we can pick up the case where
|
||||
# the object still exists in the index but not in the database (in that case, just two objects would be returned
|
||||
# instead of three).
|
||||
|
||||
# Note: we need consistant ordering for this test
|
||||
results = self.backend.search(None, models.Novel.objects.order_by('number_of_pages'), order_by_relevance=False)
|
||||
|
||||
# Limit the results
|
||||
results = results[:3]
|
||||
|
||||
self.assertEqual(list(r.title for r in results), [
|
||||
"The Hobbit",
|
||||
"The Two Towers",
|
||||
"The Fellowship of the Ring"
|
||||
])
|
||||
|
||||
self.refresh_index()
|
||||
|
||||
@override_settings(
|
||||
WAGTAILSEARCH_BACKENDS={
|
||||
|
|
|
@ -12,29 +12,42 @@ from .test_backends import BackendTests
|
|||
class TestDBBackend(BackendTests, TestCase):
|
||||
backend_path = 'wagtail.wagtailsearch.backends.db'
|
||||
|
||||
# Doesn't support ranking
|
||||
@unittest.expectedFailure
|
||||
def test_callable_indexed_field(self):
|
||||
super(TestDBBackend, self).test_callable_indexed_field()
|
||||
def test_ranking(self):
|
||||
super(TestDBBackend, self).test_ranking()
|
||||
|
||||
# Doesn't support ranking
|
||||
@unittest.expectedFailure
|
||||
def test_related_objects_search(self):
|
||||
super(TestDBBackend, self).test_related_objects_search()
|
||||
def test_search_boosting_on_related_fields(self):
|
||||
super(TestDBBackend, self).test_search_boosting_on_related_fields()
|
||||
|
||||
# Doesn't support searching specific fields
|
||||
@unittest.expectedFailure
|
||||
def test_update_index_command(self):
|
||||
super(TestDBBackend, self).test_update_index_command()
|
||||
|
||||
def test_annotate_score(self):
|
||||
results = self.backend.search("Python", models.Book).annotate_score('_score')
|
||||
|
||||
for result in results:
|
||||
# DB backend doesn't do scoring, so annotate_score should just add None
|
||||
self.assertIsNone(result._score)
|
||||
def test_search_child_class_field_from_parent(self):
|
||||
super(TestDBBackend, self).test_search_child_class_field_from_parent()
|
||||
|
||||
# Doesn't support searching related fields
|
||||
@unittest.expectedFailure
|
||||
def test_boost(self):
|
||||
super(TestDBBackend, self).test_boost()
|
||||
def test_search_on_related_fields(self):
|
||||
super(TestDBBackend, self).test_search_on_related_fields()
|
||||
|
||||
# Doesn't support searching callable fields
|
||||
@unittest.expectedFailure
|
||||
def test_order_by_relevance(self):
|
||||
super(TestDBBackend, self).test_order_by_relevance()
|
||||
def test_search_callable_field(self):
|
||||
super(TestDBBackend, self).test_search_callable_field()
|
||||
|
||||
# Broken
|
||||
@unittest.expectedFailure
|
||||
def test_order_by_non_filterable_field(self):
|
||||
super(TestDBBackend, self).test_order_by_non_filterable_field()
|
||||
|
||||
# Broken
|
||||
@unittest.expectedFailure
|
||||
def test_single_result(self):
|
||||
super(TestDBBackend, self).test_single_result()
|
||||
|
||||
# Doesn't support the index API used in this test
|
||||
@unittest.expectedFailure
|
||||
def test_same_rank_pages(self):
|
||||
super(TestDBBackend, self).test_same_rank_pages()
|
||||
|
|
|
@ -25,6 +25,21 @@ from .test_backends import BackendTests
|
|||
class TestElasticsearch2SearchBackend(BackendTests, TestCase):
|
||||
backend_path = 'wagtail.wagtailsearch.backends.elasticsearch2'
|
||||
|
||||
# Broken
|
||||
@unittest.expectedFailure
|
||||
def test_filter_in_values_list_subquery(self):
|
||||
super(TestElasticsearch2SearchBackend, self).test_filter_in_values_list_subquery()
|
||||
|
||||
# Broken
|
||||
@unittest.expectedFailure
|
||||
def test_order_by_non_filterable_field(self):
|
||||
super(TestElasticsearch2SearchBackend, self).test_order_by_non_filterable_field()
|
||||
|
||||
# Broken
|
||||
@unittest.expectedFailure
|
||||
def test_delete(self):
|
||||
super(TestElasticsearch2SearchBackend, self).test_delete()
|
||||
|
||||
|
||||
class TestElasticsearch2SearchQuery(TestCase):
|
||||
def assertDictEqual(self, a, b):
|
||||
|
|
|
@ -24,6 +24,21 @@ from .test_backends import BackendTests
|
|||
class TestElasticsearch5SearchBackend(BackendTests, TestCase):
|
||||
backend_path = 'wagtail.wagtailsearch.backends.elasticsearch5'
|
||||
|
||||
# Broken
|
||||
@unittest.expectedFailure
|
||||
def test_filter_in_values_list_subquery(self):
|
||||
super(TestElasticsearch5SearchBackend, self).test_filter_in_values_list_subquery()
|
||||
|
||||
# Broken
|
||||
@unittest.expectedFailure
|
||||
def test_order_by_non_filterable_field(self):
|
||||
super(TestElasticsearch5SearchBackend, self).test_order_by_non_filterable_field()
|
||||
|
||||
# Broken
|
||||
@unittest.expectedFailure
|
||||
def test_delete(self):
|
||||
super(TestElasticsearch5SearchBackend, self).test_delete()
|
||||
|
||||
|
||||
class TestElasticsearch5SearchQuery(TestCase):
|
||||
def assertDictEqual(self, a, b):
|
||||
|
|
|
@ -24,6 +24,21 @@ from .test_backends import BackendTests
|
|||
class TestElasticsearchSearchBackend(BackendTests, TestCase):
|
||||
backend_path = 'wagtail.wagtailsearch.backends.elasticsearch'
|
||||
|
||||
# Broken
|
||||
@unittest.expectedFailure
|
||||
def test_filter_in_values_list_subquery(self):
|
||||
super(TestElasticsearchSearchBackend, self).test_filter_in_values_list_subquery()
|
||||
|
||||
# Broken
|
||||
@unittest.expectedFailure
|
||||
def test_order_by_non_filterable_field(self):
|
||||
super(TestElasticsearchSearchBackend, self).test_order_by_non_filterable_field()
|
||||
|
||||
# Broken
|
||||
@unittest.expectedFailure
|
||||
def test_delete(self):
|
||||
super(TestElasticsearchSearchBackend, self).test_delete()
|
||||
|
||||
|
||||
class TestElasticsearchSearchQuery(TestCase):
|
||||
def assertDictEqual(self, a, b):
|
||||
|
|
Ładowanie…
Reference in New Issue