Rewrite backend tests

pull/3939/head
Karl Hobley 2017-10-19 11:18:05 +01:00
rodzic 208ca70b1f
commit 6a52ae0494
6 zmienionych plików z 464 dodań i 34 usunięć

Wyświetl plik

@ -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
}
},

Wyświetl plik

@ -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={

Wyświetl plik

@ -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()

Wyświetl plik

@ -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):

Wyświetl plik

@ -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):

Wyświetl plik

@ -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):