2011-08-29 13:10:56 +00:00
|
|
|
"""
|
|
|
|
|
Tests for django-watson.
|
|
|
|
|
|
|
|
|
|
Fun fact: The MySQL full text search engine does not support indexing of words
|
|
|
|
|
that are 3 letters or fewer. Thus, the standard metasyntactic variables in
|
|
|
|
|
these tests have been amended to 'fooo' and 'baar'. Ho hum.
|
|
|
|
|
"""
|
2011-08-20 17:50:35 +00:00
|
|
|
|
2012-02-28 14:15:25 +00:00
|
|
|
from unittest import skipUnless
|
2011-08-29 10:18:53 +00:00
|
|
|
|
2011-08-21 14:15:15 +00:00
|
|
|
from django.db import models
|
|
|
|
|
from django.test import TestCase
|
2011-08-21 17:31:38 +00:00
|
|
|
from django.core.management import call_command
|
2011-08-29 10:36:34 +00:00
|
|
|
from django.conf.urls.defaults import *
|
2011-08-30 09:53:33 +00:00
|
|
|
from django.conf import settings
|
2011-08-29 13:37:19 +00:00
|
|
|
from django.contrib import admin
|
2011-08-29 13:57:55 +00:00
|
|
|
from django.contrib.auth.models import User
|
2011-09-08 15:53:07 +00:00
|
|
|
from django.http import HttpResponseNotFound, HttpResponseServerError
|
2011-08-30 10:22:39 +00:00
|
|
|
from django import template
|
2011-09-06 16:42:06 +00:00
|
|
|
from django.utils import simplejson as json
|
2011-08-21 14:15:15 +00:00
|
|
|
|
2011-08-23 16:12:35 +00:00
|
|
|
import watson
|
2011-08-29 10:51:40 +00:00
|
|
|
from watson.registration import RegistrationError, get_backend, SearchEngine
|
2011-08-21 20:12:28 +00:00
|
|
|
from watson.models import SearchEntry
|
2011-08-21 14:15:15 +00:00
|
|
|
|
|
|
|
|
|
2011-08-21 14:52:24 +00:00
|
|
|
class TestModelBase(models.Model):
|
|
|
|
|
|
|
|
|
|
title = models.CharField(
|
|
|
|
|
max_length = 200,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
content = models.TextField(
|
|
|
|
|
blank = True,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
description = models.TextField(
|
|
|
|
|
blank = True,
|
|
|
|
|
)
|
|
|
|
|
|
2011-08-21 20:25:10 +00:00
|
|
|
is_published = models.BooleanField(
|
|
|
|
|
default = True,
|
|
|
|
|
)
|
|
|
|
|
|
2011-08-21 14:52:24 +00:00
|
|
|
def __unicode__(self):
|
|
|
|
|
return self.title
|
2011-08-21 14:15:15 +00:00
|
|
|
|
|
|
|
|
class Meta:
|
2011-08-21 14:52:24 +00:00
|
|
|
abstract = True
|
2011-08-21 14:15:15 +00:00
|
|
|
app_label = "auth" # Hack: Cannot use an app_label that is under South control, due to http://south.aeracode.org/ticket/520
|
2011-08-21 14:52:24 +00:00
|
|
|
|
|
|
|
|
|
2011-09-15 11:22:37 +00:00
|
|
|
class WatsonTestModel1(TestModelBase):
|
2011-08-21 14:52:24 +00:00
|
|
|
|
|
|
|
|
pass
|
2011-08-21 17:00:49 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
str_pk_gen = 0;
|
|
|
|
|
|
|
|
|
|
def get_str_pk():
|
|
|
|
|
global str_pk_gen
|
|
|
|
|
str_pk_gen += 1;
|
|
|
|
|
return str(str_pk_gen)
|
2011-08-21 14:52:24 +00:00
|
|
|
|
|
|
|
|
|
2011-09-15 11:22:37 +00:00
|
|
|
class WatsonTestModel2(TestModelBase):
|
2011-08-21 14:52:24 +00:00
|
|
|
|
2011-08-21 17:00:49 +00:00
|
|
|
id = models.CharField(
|
|
|
|
|
primary_key = True,
|
|
|
|
|
max_length = 100,
|
|
|
|
|
default = get_str_pk
|
|
|
|
|
)
|
2011-08-21 14:15:15 +00:00
|
|
|
|
|
|
|
|
|
2011-08-23 16:12:35 +00:00
|
|
|
class RegistrationTest(TestCase):
|
2011-08-21 14:15:15 +00:00
|
|
|
|
|
|
|
|
def testRegistration(self):
|
|
|
|
|
# Register the model and test.
|
2011-09-15 11:22:37 +00:00
|
|
|
watson.register(WatsonTestModel1)
|
|
|
|
|
self.assertTrue(watson.is_registered(WatsonTestModel1))
|
|
|
|
|
self.assertRaises(RegistrationError, lambda: watson.register(WatsonTestModel1))
|
|
|
|
|
self.assertTrue(WatsonTestModel1 in watson.get_registered_models())
|
|
|
|
|
self.assertTrue(isinstance(watson.get_adapter(WatsonTestModel1), watson.SearchAdapter))
|
2011-08-21 14:15:15 +00:00
|
|
|
# Unregister the model and text.
|
2011-09-15 11:22:37 +00:00
|
|
|
watson.unregister(WatsonTestModel1)
|
|
|
|
|
self.assertFalse(watson.is_registered(WatsonTestModel1))
|
|
|
|
|
self.assertRaises(RegistrationError, lambda: watson.unregister(WatsonTestModel1))
|
|
|
|
|
self.assertTrue(WatsonTestModel1 not in watson.get_registered_models())
|
|
|
|
|
self.assertRaises(RegistrationError, lambda: isinstance(watson.get_adapter(WatsonTestModel1)))
|
2011-08-24 10:06:41 +00:00
|
|
|
|
|
|
|
|
|
2011-08-29 10:51:40 +00:00
|
|
|
complex_registration_search_engine = SearchEngine("restricted")
|
|
|
|
|
|
|
|
|
|
|
2011-09-11 20:09:01 +00:00
|
|
|
class InstallUninstallTestBase(TestCase):
|
|
|
|
|
|
|
|
|
|
def testUninstallAndInstall(self):
|
|
|
|
|
# Not too much to test here, as some backends don't require installation.
|
|
|
|
|
# Just make sure the commands don't error.
|
|
|
|
|
call_command("uninstallwatson", verbosity=0)
|
|
|
|
|
call_command("installwatson", verbosity=0)
|
|
|
|
|
|
|
|
|
|
@skipUnless(get_backend().requires_installation, "search backend does not require installation")
|
|
|
|
|
def testRealInstallAndUninstall(self):
|
|
|
|
|
backend = get_backend()
|
|
|
|
|
call_command("uninstallwatson", verbosity=0)
|
|
|
|
|
self.assertFalse(backend.is_installed())
|
|
|
|
|
call_command("installwatson", verbosity=0)
|
|
|
|
|
self.assertTrue(backend.is_installed())
|
|
|
|
|
|
|
|
|
|
|
2011-08-24 10:06:41 +00:00
|
|
|
class SearchTestBase(TestCase):
|
|
|
|
|
|
2011-09-15 11:22:37 +00:00
|
|
|
model1 = WatsonTestModel1
|
2011-08-29 15:47:40 +00:00
|
|
|
|
2011-09-15 11:22:37 +00:00
|
|
|
model2 = WatsonTestModel2
|
2011-08-24 10:06:41 +00:00
|
|
|
|
2011-09-05 11:06:57 +00:00
|
|
|
@watson.update_index()
|
2011-08-21 14:52:24 +00:00
|
|
|
def setUp(self):
|
2011-09-08 15:53:07 +00:00
|
|
|
# If migrations are off, then this is needed to get the indices installed. It has to
|
|
|
|
|
# be called in the setUp() method, but multiple invocations should be safe.
|
|
|
|
|
call_command("installwatson", verbosity=0)
|
2011-08-25 11:09:01 +00:00
|
|
|
# Remove all the current registered models.
|
|
|
|
|
self.registered_models = watson.get_registered_models()
|
|
|
|
|
for model in self.registered_models:
|
|
|
|
|
watson.unregister(model)
|
|
|
|
|
# Register the test models.
|
2011-08-29 15:47:40 +00:00
|
|
|
watson.register(self.model1)
|
|
|
|
|
watson.register(self.model2, exclude=("id",))
|
2011-09-15 11:22:37 +00:00
|
|
|
complex_registration_search_engine.register(WatsonTestModel1, exclude=("content", "description",), store=("is_published",))
|
|
|
|
|
complex_registration_search_engine.register(WatsonTestModel2, fields=("title",))
|
2011-08-21 15:55:37 +00:00
|
|
|
# Create some test models.
|
2011-09-15 11:22:37 +00:00
|
|
|
self.test11 = WatsonTestModel1.objects.create(
|
2011-08-27 17:38:44 +00:00
|
|
|
title = "title model1 instance11",
|
|
|
|
|
content = "content model1 instance11",
|
|
|
|
|
description = "description model1 instance11",
|
2011-08-21 15:55:37 +00:00
|
|
|
)
|
2011-09-15 11:22:37 +00:00
|
|
|
self.test12 = WatsonTestModel1.objects.create(
|
2011-08-27 17:38:44 +00:00
|
|
|
title = "title model1 instance12",
|
|
|
|
|
content = "content model1 instance12",
|
|
|
|
|
description = "description model1 instance12",
|
2011-08-21 15:55:37 +00:00
|
|
|
)
|
2011-09-15 11:22:37 +00:00
|
|
|
self.test21 = WatsonTestModel2.objects.create(
|
2011-08-27 17:38:44 +00:00
|
|
|
title = "title model2 instance21",
|
|
|
|
|
content = "content model2 instance21",
|
|
|
|
|
description = "description model2 instance21",
|
2011-08-21 15:55:37 +00:00
|
|
|
)
|
2011-09-15 11:22:37 +00:00
|
|
|
self.test22 = WatsonTestModel2.objects.create(
|
2011-08-27 17:38:44 +00:00
|
|
|
title = "title model2 instance22",
|
|
|
|
|
content = "content model2 instance22",
|
|
|
|
|
description = "description model2 instance22",
|
2011-08-21 15:55:37 +00:00
|
|
|
)
|
2011-08-24 10:06:41 +00:00
|
|
|
|
|
|
|
|
def tearDown(self):
|
2011-08-25 11:09:01 +00:00
|
|
|
# Re-register the old registered models.
|
|
|
|
|
for model in self.registered_models:
|
|
|
|
|
watson.register(model)
|
|
|
|
|
# Unregister the test models.
|
2011-08-29 15:47:40 +00:00
|
|
|
watson.unregister(self.model1)
|
|
|
|
|
watson.unregister(self.model2)
|
2011-09-15 11:22:37 +00:00
|
|
|
complex_registration_search_engine.unregister(WatsonTestModel1)
|
|
|
|
|
complex_registration_search_engine.unregister(WatsonTestModel2)
|
2011-08-24 10:06:41 +00:00
|
|
|
# Delete the test models.
|
2011-09-15 11:22:37 +00:00
|
|
|
WatsonTestModel1.objects.all().delete()
|
|
|
|
|
WatsonTestModel2.objects.all().delete()
|
2011-08-24 10:06:41 +00:00
|
|
|
del self.test11
|
|
|
|
|
del self.test12
|
|
|
|
|
del self.test21
|
|
|
|
|
del self.test22
|
|
|
|
|
# Delete the search index.
|
|
|
|
|
SearchEntry.objects.all().delete()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class InternalsTest(SearchTestBase):
|
|
|
|
|
|
2011-08-23 16:12:35 +00:00
|
|
|
def testSearchEntriesCreated(self):
|
2011-08-29 10:51:40 +00:00
|
|
|
self.assertEqual(SearchEntry.objects.filter(engine_slug="default").count(), 4)
|
2011-08-24 10:06:41 +00:00
|
|
|
|
2011-08-27 17:38:44 +00:00
|
|
|
def testBuildWatsonCommand(self):
|
2011-08-24 10:06:41 +00:00
|
|
|
# This update won't take affect, because no search context is active.
|
2011-08-29 13:08:16 +00:00
|
|
|
self.test11.title = "fooo"
|
2011-08-24 10:06:41 +00:00
|
|
|
self.test11.save()
|
|
|
|
|
# Test that no update has happened.
|
2011-08-29 13:08:16 +00:00
|
|
|
self.assertEqual(watson.search("fooo").count(), 0)
|
2011-08-24 10:06:41 +00:00
|
|
|
# Run the rebuild command.
|
2011-08-24 11:42:03 +00:00
|
|
|
call_command("buildwatson", verbosity=0)
|
2011-08-24 10:06:41 +00:00
|
|
|
# Test that the update is now applies.
|
2011-08-29 13:08:16 +00:00
|
|
|
self.assertEqual(watson.search("fooo").count(), 1)
|
2011-08-24 10:06:41 +00:00
|
|
|
|
2011-08-21 16:50:09 +00:00
|
|
|
def testUpdateSearchIndex(self):
|
|
|
|
|
# Update a model and make sure that the search results match.
|
2011-09-05 11:06:57 +00:00
|
|
|
with watson.update_index():
|
2011-08-29 13:08:16 +00:00
|
|
|
self.test11.title = "fooo"
|
2011-08-21 16:50:09 +00:00
|
|
|
self.test11.save()
|
|
|
|
|
# Test a search that should get one model.
|
2011-08-29 13:08:16 +00:00
|
|
|
exact_search = watson.search("fooo")
|
2011-08-21 16:50:09 +00:00
|
|
|
self.assertEqual(len(exact_search), 1)
|
2011-08-29 13:08:16 +00:00
|
|
|
self.assertEqual(exact_search[0].title, "fooo")
|
2011-08-23 16:20:28 +00:00
|
|
|
# Delete a model and make sure that the search results match.
|
2012-02-06 13:21:58 +00:00
|
|
|
with watson.update_index():
|
|
|
|
|
self.test11.delete()
|
2011-08-29 13:08:16 +00:00
|
|
|
self.assertEqual(watson.search("fooo").count(), 0)
|
2011-09-05 11:09:25 +00:00
|
|
|
|
|
|
|
|
def testSearchIndexUpdateAbandonedOnError(self):
|
|
|
|
|
try:
|
|
|
|
|
with watson.update_index():
|
|
|
|
|
self.test11.title = "fooo"
|
|
|
|
|
self.test11.save()
|
|
|
|
|
raise Exception("Foo")
|
|
|
|
|
except:
|
|
|
|
|
pass
|
|
|
|
|
# Test a search that should get not model.
|
|
|
|
|
self.assertEqual(watson.search("fooo").count(), 0)
|
2011-08-24 10:06:41 +00:00
|
|
|
|
|
|
|
|
def testFixesDuplicateSearchEntries(self):
|
2011-08-29 10:51:40 +00:00
|
|
|
search_entries = SearchEntry.objects.filter(engine_slug="default")
|
2011-08-24 10:06:41 +00:00
|
|
|
# Duplicate a couple of search entries.
|
2011-08-29 10:51:40 +00:00
|
|
|
for search_entry in search_entries.all()[:2]:
|
2011-08-24 10:06:41 +00:00
|
|
|
search_entry.id = None
|
|
|
|
|
search_entry.save()
|
|
|
|
|
# Make sure that we have six (including duplicates).
|
2011-08-29 10:51:40 +00:00
|
|
|
self.assertEqual(search_entries.all().count(), 6)
|
2011-08-24 10:06:41 +00:00
|
|
|
# Run the rebuild command.
|
2011-08-24 11:42:03 +00:00
|
|
|
call_command("buildwatson", verbosity=0)
|
2011-08-24 10:06:41 +00:00
|
|
|
# Make sure that we have four again (including duplicates).
|
2011-08-29 10:51:40 +00:00
|
|
|
self.assertEqual(search_entries.all().count(), 4)
|
2011-08-24 10:22:51 +00:00
|
|
|
|
2011-10-02 16:16:24 +00:00
|
|
|
def testEmptyFilterGivesAllResults(self):
|
2011-09-15 11:22:37 +00:00
|
|
|
for model in (WatsonTestModel1, WatsonTestModel2):
|
2011-10-02 16:16:24 +00:00
|
|
|
self.assertEqual(watson.filter(model, "").count(), 2)
|
|
|
|
|
self.assertEqual(watson.filter(model, " ").count(), 2)
|
2011-08-24 10:06:41 +00:00
|
|
|
|
2011-08-27 17:45:43 +00:00
|
|
|
def testFilter(self):
|
2011-09-15 11:22:37 +00:00
|
|
|
for model in (WatsonTestModel1, WatsonTestModel2):
|
2011-08-27 17:45:43 +00:00
|
|
|
# Test can find all.
|
|
|
|
|
self.assertEqual(watson.filter(model, "TITLE").count(), 2)
|
|
|
|
|
# Test can find a specific one.
|
2011-09-15 11:22:37 +00:00
|
|
|
obj = watson.filter(WatsonTestModel1, "INSTANCE12").get()
|
|
|
|
|
self.assertTrue(isinstance(obj, WatsonTestModel1))
|
2011-08-27 17:45:43 +00:00
|
|
|
self.assertEqual(obj.title, "title model1 instance12")
|
|
|
|
|
# Test can do filter on a queryset.
|
2011-09-15 11:22:37 +00:00
|
|
|
obj = watson.filter(WatsonTestModel1.objects.filter(title__icontains="TITLE"), "INSTANCE12").get()
|
|
|
|
|
self.assertTrue(isinstance(obj, WatsonTestModel1))
|
2011-08-27 17:45:43 +00:00
|
|
|
self.assertEqual(obj.title, "title model1 instance12")
|
|
|
|
|
|
2011-08-27 17:38:44 +00:00
|
|
|
|
2011-08-24 10:06:41 +00:00
|
|
|
class SearchTest(SearchTestBase):
|
|
|
|
|
|
2011-09-06 16:17:14 +00:00
|
|
|
def emptySearchTextGivesNoResults(self):
|
|
|
|
|
self.assertEqual(watson.search("").count(), 0)
|
|
|
|
|
self.assertEqual(watson.search(" ").count(), 0)
|
|
|
|
|
|
2011-08-24 10:06:41 +00:00
|
|
|
def testMultiTableSearch(self):
|
|
|
|
|
# Test a search that should get all models.
|
2011-08-27 17:38:44 +00:00
|
|
|
self.assertEqual(watson.search("TITLE").count(), 4)
|
|
|
|
|
self.assertEqual(watson.search("CONTENT").count(), 4)
|
|
|
|
|
self.assertEqual(watson.search("DESCRIPTION").count(), 4)
|
2011-08-28 12:42:47 +00:00
|
|
|
self.assertEqual(watson.search("TITLE CONTENT DESCRIPTION").count(), 4)
|
2011-08-24 10:06:41 +00:00
|
|
|
# Test a search that should get two models.
|
2011-08-27 17:38:44 +00:00
|
|
|
self.assertEqual(watson.search("MODEL1").count(), 2)
|
|
|
|
|
self.assertEqual(watson.search("MODEL2").count(), 2)
|
|
|
|
|
self.assertEqual(watson.search("TITLE MODEL1").count(), 2)
|
|
|
|
|
self.assertEqual(watson.search("TITLE MODEL2").count(), 2)
|
2011-08-24 10:06:41 +00:00
|
|
|
# Test a search that should get one model.
|
2011-08-27 17:38:44 +00:00
|
|
|
self.assertEqual(watson.search("INSTANCE11").count(), 1)
|
|
|
|
|
self.assertEqual(watson.search("INSTANCE21").count(), 1)
|
|
|
|
|
self.assertEqual(watson.search("TITLE INSTANCE11").count(), 1)
|
|
|
|
|
self.assertEqual(watson.search("TITLE INSTANCE21").count(), 1)
|
|
|
|
|
# Test a search that should get zero models.
|
2011-08-29 13:08:16 +00:00
|
|
|
self.assertEqual(watson.search("FOOO").count(), 0)
|
|
|
|
|
self.assertEqual(watson.search("FOOO INSTANCE11").count(), 0)
|
2011-08-27 17:38:44 +00:00
|
|
|
self.assertEqual(watson.search("MODEL2 INSTANCE11").count(), 0)
|
2011-08-21 16:58:41 +00:00
|
|
|
|
|
|
|
|
def testLimitedModelList(self):
|
|
|
|
|
# Test a search that should get all models.
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertEqual(watson.search("TITLE", models=(WatsonTestModel1, WatsonTestModel2)).count(), 4)
|
2011-08-27 17:38:44 +00:00
|
|
|
# Test a search that should get two models.
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertEqual(watson.search("MODEL1", models=(WatsonTestModel1, WatsonTestModel2)).count(), 2)
|
|
|
|
|
self.assertEqual(watson.search("MODEL1", models=(WatsonTestModel1,)).count(), 2)
|
|
|
|
|
self.assertEqual(watson.search("MODEL2", models=(WatsonTestModel1, WatsonTestModel2)).count(), 2)
|
|
|
|
|
self.assertEqual(watson.search("MODEL2", models=(WatsonTestModel2,)).count(), 2)
|
2011-08-24 10:57:16 +00:00
|
|
|
# Test a search that should get one model.
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertEqual(watson.search("INSTANCE11", models=(WatsonTestModel1, WatsonTestModel2)).count(), 1)
|
|
|
|
|
self.assertEqual(watson.search("INSTANCE11", models=(WatsonTestModel1,)).count(), 1)
|
|
|
|
|
self.assertEqual(watson.search("INSTANCE21", models=(WatsonTestModel1, WatsonTestModel2,)).count(), 1)
|
|
|
|
|
self.assertEqual(watson.search("INSTANCE21", models=(WatsonTestModel2,)).count(), 1)
|
2011-08-27 17:38:44 +00:00
|
|
|
# Test a search that should get zero models.
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertEqual(watson.search("MODEL1", models=(WatsonTestModel2,)).count(), 0)
|
|
|
|
|
self.assertEqual(watson.search("MODEL2", models=(WatsonTestModel1,)).count(), 0)
|
|
|
|
|
self.assertEqual(watson.search("INSTANCE21", models=(WatsonTestModel1,)).count(), 0)
|
|
|
|
|
self.assertEqual(watson.search("INSTANCE11", models=(WatsonTestModel2,)).count(), 0)
|
2011-08-21 16:58:41 +00:00
|
|
|
|
|
|
|
|
def testExcludedModelList(self):
|
|
|
|
|
# Test a search that should get all models.
|
2011-08-27 17:38:44 +00:00
|
|
|
self.assertEqual(watson.search("TITLE", exclude=()).count(), 4)
|
|
|
|
|
# Test a search that should get two models.
|
|
|
|
|
self.assertEqual(watson.search("MODEL1", exclude=()).count(), 2)
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertEqual(watson.search("MODEL1", exclude=(WatsonTestModel2,)).count(), 2)
|
2011-08-27 17:38:44 +00:00
|
|
|
self.assertEqual(watson.search("MODEL2", exclude=()).count(), 2)
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertEqual(watson.search("MODEL2", exclude=(WatsonTestModel1,)).count(), 2)
|
2011-08-21 16:58:41 +00:00
|
|
|
# Test a search that should get one model.
|
2011-08-27 17:38:44 +00:00
|
|
|
self.assertEqual(watson.search("INSTANCE11", exclude=()).count(), 1)
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertEqual(watson.search("INSTANCE11", exclude=(WatsonTestModel2,)).count(), 1)
|
2011-08-27 17:38:44 +00:00
|
|
|
self.assertEqual(watson.search("INSTANCE21", exclude=()).count(), 1)
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertEqual(watson.search("INSTANCE21", exclude=(WatsonTestModel1,)).count(), 1)
|
2011-08-27 17:38:44 +00:00
|
|
|
# Test a search that should get zero models.
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertEqual(watson.search("MODEL1", exclude=(WatsonTestModel1,)).count(), 0)
|
|
|
|
|
self.assertEqual(watson.search("MODEL2", exclude=(WatsonTestModel2,)).count(), 0)
|
|
|
|
|
self.assertEqual(watson.search("INSTANCE21", exclude=(WatsonTestModel2,)).count(), 0)
|
|
|
|
|
self.assertEqual(watson.search("INSTANCE11", exclude=(WatsonTestModel1,)).count(), 0)
|
2011-08-27 17:38:44 +00:00
|
|
|
|
|
|
|
|
def testLimitedModelQuerySet(self):
|
2011-08-25 11:09:01 +00:00
|
|
|
# Test a search that should get all models.
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertEqual(watson.search("TITLE", models=(WatsonTestModel1.objects.filter(title__icontains="TITLE"), WatsonTestModel2.objects.filter(title__icontains="TITLE"),)).count(), 4)
|
2011-08-27 17:38:44 +00:00
|
|
|
# Test a search that should get two models.
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertEqual(watson.search("MODEL1", models=(WatsonTestModel1.objects.filter(
|
2011-08-27 17:38:44 +00:00
|
|
|
title__icontains = "MODEL1",
|
|
|
|
|
description__icontains = "MODEL1",
|
|
|
|
|
),)).count(), 2)
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertEqual(watson.search("MODEL2", models=(WatsonTestModel2.objects.filter(
|
2011-08-27 17:38:44 +00:00
|
|
|
title__icontains = "MODEL2",
|
|
|
|
|
description__icontains = "MODEL2",
|
|
|
|
|
),)).count(), 2)
|
2011-08-25 11:09:01 +00:00
|
|
|
# Test a search that should get one model.
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertEqual(watson.search("INSTANCE11", models=(WatsonTestModel1.objects.filter(
|
2011-08-27 17:38:44 +00:00
|
|
|
title__icontains = "MODEL1",
|
|
|
|
|
),)).count(), 1)
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertEqual(watson.search("INSTANCE21", models=(WatsonTestModel2.objects.filter(
|
2011-08-27 17:38:44 +00:00
|
|
|
title__icontains = "MODEL2",
|
|
|
|
|
),)).count(), 1)
|
|
|
|
|
# Test a search that should get no models.
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertEqual(watson.search("INSTANCE11", models=(WatsonTestModel1.objects.filter(
|
2011-08-27 17:38:44 +00:00
|
|
|
title__icontains = "MODEL2",
|
|
|
|
|
),)).count(), 0)
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertEqual(watson.search("INSTANCE21", models=(WatsonTestModel2.objects.filter(
|
2011-08-27 17:38:44 +00:00
|
|
|
title__icontains = "MODEL1",
|
|
|
|
|
),)).count(), 0)
|
|
|
|
|
|
|
|
|
|
def testExcludedModelQuerySet(self):
|
|
|
|
|
# Test a search that should get all models.
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertEqual(watson.search("TITLE", exclude=(WatsonTestModel1.objects.filter(title__icontains="FOOO"), WatsonTestModel2.objects.filter(title__icontains="FOOO"),)).count(), 4)
|
2011-08-27 17:38:44 +00:00
|
|
|
# Test a search that should get two models.
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertEqual(watson.search("MODEL1", exclude=(WatsonTestModel1.objects.filter(
|
2011-08-27 17:38:44 +00:00
|
|
|
title__icontains = "INSTANCE21",
|
|
|
|
|
description__icontains = "INSTANCE22",
|
|
|
|
|
),)).count(), 2)
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertEqual(watson.search("MODEL2", exclude=(WatsonTestModel2.objects.filter(
|
2011-08-27 17:38:44 +00:00
|
|
|
title__icontains = "INSTANCE11",
|
|
|
|
|
description__icontains = "INSTANCE12",
|
|
|
|
|
),)).count(), 2)
|
2011-08-25 11:09:01 +00:00
|
|
|
# Test a search that should get one model.
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertEqual(watson.search("INSTANCE11", exclude=(WatsonTestModel1.objects.filter(
|
2011-08-27 17:38:44 +00:00
|
|
|
title__icontains = "MODEL2",
|
|
|
|
|
),)).count(), 1)
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertEqual(watson.search("INSTANCE21", exclude=(WatsonTestModel2.objects.filter(
|
2011-08-27 17:38:44 +00:00
|
|
|
title__icontains = "MODEL1",
|
|
|
|
|
),)).count(), 1)
|
2011-08-25 11:09:01 +00:00
|
|
|
# Test a search that should get no models.
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertEqual(watson.search("INSTANCE11", exclude=(WatsonTestModel1.objects.filter(
|
2011-08-27 17:38:44 +00:00
|
|
|
title__icontains = "MODEL1",
|
|
|
|
|
),)).count(), 0)
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertEqual(watson.search("INSTANCE21", exclude=(WatsonTestModel2.objects.filter(
|
2011-08-27 17:38:44 +00:00
|
|
|
title__icontains = "MODEL2",
|
|
|
|
|
),)).count(), 0)
|
|
|
|
|
|
2011-08-28 12:42:47 +00:00
|
|
|
def testKitchenSink(self):
|
|
|
|
|
"""For sanity, let's just test everything together in one giant search of doom!"""
|
2012-02-28 14:15:25 +00:00
|
|
|
self.assertEqual(watson.search(
|
2011-08-28 12:42:47 +00:00
|
|
|
"INSTANCE11",
|
|
|
|
|
models = (
|
2011-09-15 11:22:37 +00:00
|
|
|
WatsonTestModel1.objects.filter(title__icontains="INSTANCE11"),
|
|
|
|
|
WatsonTestModel2.objects.filter(title__icontains="TITLE"),
|
2011-08-28 12:42:47 +00:00
|
|
|
),
|
|
|
|
|
exclude = (
|
2011-09-15 11:22:37 +00:00
|
|
|
WatsonTestModel1.objects.filter(title__icontains="MODEL2"),
|
|
|
|
|
WatsonTestModel2.objects.filter(title__icontains="MODEL1"),
|
2011-08-28 12:42:47 +00:00
|
|
|
)
|
|
|
|
|
).get().title, "title model1 instance11")
|
|
|
|
|
|
2011-08-21 18:05:34 +00:00
|
|
|
|
|
|
|
|
class LiveFilterSearchTest(SearchTest):
|
2011-08-21 20:12:28 +00:00
|
|
|
|
2011-09-15 11:22:37 +00:00
|
|
|
model1 = WatsonTestModel1.objects.filter(is_published=True)
|
2011-08-29 15:47:40 +00:00
|
|
|
|
2011-09-15 11:22:37 +00:00
|
|
|
model2 = WatsonTestModel2.objects.filter(is_published=True)
|
2011-08-21 20:25:10 +00:00
|
|
|
|
|
|
|
|
def testUnpublishedModelsNotFound(self):
|
|
|
|
|
# Make sure that there are four to find!
|
2011-08-23 16:12:35 +00:00
|
|
|
self.assertEqual(watson.search("tItle Content Description").count(), 4)
|
2011-08-21 20:25:10 +00:00
|
|
|
# Unpublish two objects.
|
2011-09-05 11:06:57 +00:00
|
|
|
with watson.update_index():
|
2011-08-21 20:25:10 +00:00
|
|
|
self.test11.is_published = False
|
|
|
|
|
self.test11.save()
|
|
|
|
|
self.test21.is_published = False
|
|
|
|
|
self.test21.save()
|
|
|
|
|
# This should return 4, but two of them are unpublished.
|
2011-08-27 17:45:43 +00:00
|
|
|
self.assertEqual(watson.search("tItle Content Description").count(), 2)
|
|
|
|
|
|
|
|
|
|
def testCanOverridePublication(self):
|
|
|
|
|
# Unpublish two objects.
|
2011-09-05 11:06:57 +00:00
|
|
|
with watson.update_index():
|
2011-08-27 17:45:43 +00:00
|
|
|
self.test11.is_published = False
|
|
|
|
|
self.test11.save()
|
|
|
|
|
# This should still return 4, since we're overriding the publication.
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertEqual(watson.search("tItle Content Description", models=(WatsonTestModel2, WatsonTestModel1._base_manager.all(),)).count(), 4)
|
2011-08-29 10:10:09 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class RankingTest(SearchTestBase):
|
|
|
|
|
|
2011-09-05 11:06:57 +00:00
|
|
|
@watson.update_index()
|
2011-08-29 10:18:53 +00:00
|
|
|
def setUp(self):
|
|
|
|
|
super(RankingTest, self).setUp()
|
2011-08-29 13:08:16 +00:00
|
|
|
self.test11.title += " fooo baar fooo"
|
2011-08-29 10:18:53 +00:00
|
|
|
self.test11.save()
|
2011-08-29 18:18:27 +00:00
|
|
|
self.test12.content += " fooo baar"
|
2011-08-29 10:18:53 +00:00
|
|
|
self.test12.save()
|
|
|
|
|
|
2011-08-29 10:10:09 +00:00
|
|
|
def testRankingParamPresentOnSearch(self):
|
|
|
|
|
self.assertGreater(watson.search("TITLE")[0].watson_rank, 0)
|
|
|
|
|
|
|
|
|
|
def testRankingParamPresentOnFilter(self):
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertGreater(watson.filter(WatsonTestModel1, "TITLE")[0].watson_rank, 0)
|
2011-08-29 10:10:09 +00:00
|
|
|
|
|
|
|
|
def testRankingParamAbsentOnSearch(self):
|
|
|
|
|
self.assertRaises(AttributeError, lambda: watson.search("TITLE", ranking=False)[0].watson_rank)
|
|
|
|
|
|
|
|
|
|
def testRankingParamAbsentOnFilter(self):
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertRaises(AttributeError, lambda: watson.filter(WatsonTestModel1, "TITLE", ranking=False)[0].watson_rank)
|
2011-08-29 10:18:53 +00:00
|
|
|
|
|
|
|
|
@skipUnless(get_backend().supports_ranking, "search backend does not support ranking")
|
|
|
|
|
def testRankingWithSearch(self):
|
|
|
|
|
self.assertEqual(
|
2011-08-29 13:08:16 +00:00
|
|
|
[entry.title for entry in watson.search("FOOO")],
|
2011-08-29 18:18:27 +00:00
|
|
|
[u"title model1 instance11 fooo baar fooo", u"title model1 instance12"]
|
2011-08-29 10:18:53 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@skipUnless(get_backend().supports_ranking, "search backend does not support ranking")
|
|
|
|
|
def testRankingWithFilter(self):
|
|
|
|
|
self.assertEqual(
|
2011-09-15 11:22:37 +00:00
|
|
|
[entry.title for entry in watson.filter(WatsonTestModel1, "FOOO")],
|
2011-08-29 18:18:27 +00:00
|
|
|
[u"title model1 instance11 fooo baar fooo", u"title model1 instance12"]
|
2011-08-29 10:36:34 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2011-08-29 10:51:40 +00:00
|
|
|
class ComplexRegistrationTest(SearchTestBase):
|
|
|
|
|
|
|
|
|
|
def testMetaStored(self):
|
|
|
|
|
self.assertEqual(complex_registration_search_engine.search("instance11")[0].meta["is_published"], True)
|
|
|
|
|
|
|
|
|
|
def testMetaNotStored(self):
|
|
|
|
|
self.assertRaises(KeyError, lambda: complex_registration_search_engine.search("instance21")[0].meta["is_published"])
|
|
|
|
|
|
|
|
|
|
def testFieldsExcludedOnSearch(self):
|
|
|
|
|
self.assertEqual(complex_registration_search_engine.search("TITLE").count(), 4)
|
|
|
|
|
self.assertEqual(complex_registration_search_engine.search("CONTENT").count(), 0)
|
|
|
|
|
self.assertEqual(complex_registration_search_engine.search("DESCRIPTION").count(), 0)
|
|
|
|
|
|
|
|
|
|
def testFieldsExcludedOnFilter(self):
|
2011-09-15 11:22:37 +00:00
|
|
|
self.assertEqual(complex_registration_search_engine.filter(WatsonTestModel1, "TITLE").count(), 2)
|
|
|
|
|
self.assertEqual(complex_registration_search_engine.filter(WatsonTestModel1, "CONTENT").count(), 0)
|
|
|
|
|
self.assertEqual(complex_registration_search_engine.filter(WatsonTestModel1, "DESCRIPTION").count(), 0)
|
|
|
|
|
self.assertEqual(complex_registration_search_engine.filter(WatsonTestModel2, "TITLE").count(), 2)
|
|
|
|
|
self.assertEqual(complex_registration_search_engine.filter(WatsonTestModel2, "CONTENT").count(), 0)
|
|
|
|
|
self.assertEqual(complex_registration_search_engine.filter(WatsonTestModel2, "DESCRIPTION").count(), 0)
|
2011-08-29 10:51:40 +00:00
|
|
|
|
|
|
|
|
|
2011-09-15 11:22:37 +00:00
|
|
|
class WatsonTestModel1Admin(watson.SearchAdmin):
|
2011-08-29 13:37:19 +00:00
|
|
|
|
2011-08-29 13:57:55 +00:00
|
|
|
search_fields = ("title", "description", "content",)
|
2011-08-29 13:37:19 +00:00
|
|
|
|
|
|
|
|
list_display = ("title",)
|
|
|
|
|
|
|
|
|
|
|
2011-09-15 11:22:37 +00:00
|
|
|
admin.site.register(WatsonTestModel1, WatsonTestModel1Admin)
|
2011-08-29 13:37:19 +00:00
|
|
|
|
|
|
|
|
|
2011-09-06 16:42:06 +00:00
|
|
|
urlpatterns = patterns("",
|
2011-08-29 10:36:34 +00:00
|
|
|
|
2011-09-06 16:42:06 +00:00
|
|
|
url("^simple/", include("watson.urls")),
|
2011-08-29 10:36:34 +00:00
|
|
|
|
2011-09-06 16:42:06 +00:00
|
|
|
url("^custom/", include("watson.urls"), kwargs={
|
2011-08-29 13:08:16 +00:00
|
|
|
"query_param": "fooo",
|
2011-08-29 10:36:34 +00:00
|
|
|
"empty_query_redirect": "/simple/",
|
2011-08-30 09:53:33 +00:00
|
|
|
"extra_context": {
|
|
|
|
|
"foo": "bar",
|
|
|
|
|
"foo2": lambda: "bar2",
|
2011-08-30 10:22:39 +00:00
|
|
|
},
|
|
|
|
|
"paginate_by": 10,
|
2011-08-29 10:36:34 +00:00
|
|
|
}),
|
2011-08-29 13:37:19 +00:00
|
|
|
|
|
|
|
|
url("^admin/", include(admin.site.urls)),
|
2011-08-29 10:36:34 +00:00
|
|
|
|
|
|
|
|
)
|
2011-08-29 13:57:55 +00:00
|
|
|
|
|
|
|
|
|
2011-09-08 15:53:07 +00:00
|
|
|
def handler404(request):
|
|
|
|
|
return HttpResponseNotFound("Not found")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def handler500(request):
|
|
|
|
|
return HttpResponseServerError("Server error")
|
|
|
|
|
|
|
|
|
|
|
2011-08-29 13:57:55 +00:00
|
|
|
class AdminIntegrationTest(SearchTestBase):
|
|
|
|
|
|
|
|
|
|
urls = "watson.tests"
|
|
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
|
super(AdminIntegrationTest, self).setUp()
|
|
|
|
|
self.user = User(
|
|
|
|
|
username = "foo",
|
|
|
|
|
is_staff = True,
|
|
|
|
|
is_superuser = True,
|
|
|
|
|
)
|
|
|
|
|
self.user.set_password("bar")
|
|
|
|
|
self.user.save()
|
|
|
|
|
|
2011-08-30 09:53:33 +00:00
|
|
|
@skipUnless("django.contrib.admin" in settings.INSTALLED_APPS, "Django admin site not installed")
|
2011-08-29 13:57:55 +00:00
|
|
|
def testAdminIntegration(self):
|
|
|
|
|
self.client.login(username="foo", password="bar")
|
2011-09-08 15:56:31 +00:00
|
|
|
# Test a search with no query.
|
2011-09-15 11:22:37 +00:00
|
|
|
response = self.client.get("/admin/auth/watsontestmodel1/")
|
2011-09-08 15:56:31 +00:00
|
|
|
self.assertContains(response, "instance11")
|
|
|
|
|
self.assertContains(response, "instance12")
|
|
|
|
|
self.assertContains(response, "searchbar") # Ensure that the search bar renders.
|
2011-08-29 13:57:55 +00:00
|
|
|
# Test a search for all the instances.
|
2011-09-15 11:22:37 +00:00
|
|
|
response = self.client.get("/admin/auth/watsontestmodel1/?q=title content description")
|
2011-08-29 13:57:55 +00:00
|
|
|
self.assertContains(response, "instance11")
|
|
|
|
|
self.assertContains(response, "instance12")
|
|
|
|
|
# Test a search for half the instances.
|
2011-09-15 11:22:37 +00:00
|
|
|
response = self.client.get("/admin/auth/watsontestmodel1/?q=instance11")
|
2011-08-29 13:57:55 +00:00
|
|
|
self.assertContains(response, "instance11")
|
|
|
|
|
self.assertNotContains(response, "instance12")
|
|
|
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
|
super(AdminIntegrationTest, self).tearDown()
|
|
|
|
|
self.user.delete()
|
|
|
|
|
del self.user
|
2011-08-29 10:36:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class SiteSearchTest(SearchTestBase):
|
|
|
|
|
|
|
|
|
|
urls = "watson.tests"
|
|
|
|
|
|
|
|
|
|
def testSiteSearch(self):
|
|
|
|
|
# Test a search than should find everything.
|
|
|
|
|
response = self.client.get("/simple/?q=title")
|
|
|
|
|
self.assertContains(response, "instance11")
|
|
|
|
|
self.assertContains(response, "instance12")
|
|
|
|
|
self.assertContains(response, "instance21")
|
|
|
|
|
self.assertContains(response, "instance22")
|
2011-08-29 17:20:58 +00:00
|
|
|
self.assertTemplateUsed(response, "watson/search_results.html")
|
2011-08-29 10:36:34 +00:00
|
|
|
# Test a search that should find one thing.
|
|
|
|
|
response = self.client.get("/simple/?q=instance11")
|
|
|
|
|
self.assertContains(response, "instance11")
|
|
|
|
|
self.assertNotContains(response, "instance12")
|
|
|
|
|
self.assertNotContains(response, "instance21")
|
|
|
|
|
self.assertNotContains(response, "instance22")
|
|
|
|
|
# Test a search that should find nothing.
|
2011-08-29 13:08:16 +00:00
|
|
|
response = self.client.get("/simple/?q=fooo")
|
2011-08-29 10:36:34 +00:00
|
|
|
self.assertNotContains(response, "instance11")
|
|
|
|
|
self.assertNotContains(response, "instance12")
|
|
|
|
|
self.assertNotContains(response, "instance21")
|
|
|
|
|
self.assertNotContains(response, "instance22")
|
|
|
|
|
|
2011-09-06 16:42:06 +00:00
|
|
|
def testSiteSearchJSON(self):
|
|
|
|
|
# Test a search that should find everything.
|
|
|
|
|
response = self.client.get("/simple/json/?q=title")
|
|
|
|
|
self.assertEqual(response["Content-Type"], "application/json; charset=utf-8")
|
|
|
|
|
results = set(result["title"] for result in json.loads(response.content)["results"])
|
|
|
|
|
self.assertEqual(len(results), 4)
|
|
|
|
|
self.assertTrue("title model1 instance11" in results)
|
|
|
|
|
self.assertTrue("title model1 instance12" in results)
|
|
|
|
|
self.assertTrue("title model2 instance21" in results)
|
|
|
|
|
self.assertTrue("title model2 instance22" in results)
|
|
|
|
|
|
2011-08-29 10:36:34 +00:00
|
|
|
def testSiteSearchCustom(self):
|
|
|
|
|
# Test a search than should find everything.
|
2011-08-29 13:08:16 +00:00
|
|
|
response = self.client.get("/custom/?fooo=title")
|
2011-08-29 10:36:34 +00:00
|
|
|
self.assertContains(response, "instance11")
|
|
|
|
|
self.assertContains(response, "instance12")
|
|
|
|
|
self.assertContains(response, "instance21")
|
|
|
|
|
self.assertContains(response, "instance22")
|
2011-08-29 17:20:58 +00:00
|
|
|
self.assertTemplateUsed(response, "watson/search_results.html")
|
2011-08-30 09:53:33 +00:00
|
|
|
# Test that the extra context is included.
|
|
|
|
|
self.assertEqual(response.context["foo"], "bar")
|
|
|
|
|
self.assertEqual(response.context["foo2"], "bar2")
|
2011-08-30 10:22:39 +00:00
|
|
|
# Test that pagination is included.
|
|
|
|
|
self.assertEqual(response.context["paginator"].num_pages, 1)
|
|
|
|
|
self.assertEqual(response.context["page_obj"].number, 1)
|
|
|
|
|
self.assertEqual(response.context["search_results"], response.context["page_obj"].object_list)
|
|
|
|
|
# Test a request for an empty page.
|
|
|
|
|
try:
|
|
|
|
|
response = self.client.get("/custom/?fooo=title&page=10")
|
|
|
|
|
except template.TemplateDoesNotExist as ex:
|
|
|
|
|
# No 404 template defined.
|
2011-09-05 11:06:57 +00:00
|
|
|
self.assertEqual(ex.args[0], "404.html")
|
2011-08-30 10:22:39 +00:00
|
|
|
else:
|
|
|
|
|
self.assertEqual(response.status_code, 404)
|
|
|
|
|
# Test a requet for the last page.
|
|
|
|
|
response = self.client.get("/custom/?fooo=title&page=last")
|
|
|
|
|
self.assertEqual(response.context["paginator"].num_pages, 1)
|
|
|
|
|
self.assertEqual(response.context["page_obj"].number, 1)
|
|
|
|
|
self.assertEqual(response.context["search_results"], response.context["page_obj"].object_list)
|
2011-08-29 10:36:34 +00:00
|
|
|
# Test a search that should find nothing.
|
2011-08-29 13:08:16 +00:00
|
|
|
response = self.client.get("/custom/?q=fooo")
|
2011-09-06 16:42:06 +00:00
|
|
|
self.assertRedirects(response, "/simple/")
|
|
|
|
|
|
|
|
|
|
def testSiteSearchCustomJSON(self):
|
|
|
|
|
# Test a search that should find everything.
|
|
|
|
|
response = self.client.get("/custom/json/?fooo=title&page=last")
|
|
|
|
|
self.assertEqual(response["Content-Type"], "application/json; charset=utf-8")
|
|
|
|
|
results = set(result["title"] for result in json.loads(response.content)["results"])
|
|
|
|
|
self.assertEqual(len(results), 4)
|
|
|
|
|
self.assertTrue("title model1 instance11" in results)
|
|
|
|
|
self.assertTrue("title model1 instance12" in results)
|
|
|
|
|
self.assertTrue("title model2 instance21" in results)
|
|
|
|
|
self.assertTrue("title model2 instance22" in results)
|
|
|
|
|
# Test a search with an invalid page.
|
|
|
|
|
response = self.client.get("/custom/json/?fooo=title&page=200")
|
|
|
|
|
results = set(result["title"] for result in json.loads(response.content)["results"])
|
|
|
|
|
self.assertEqual(len(results), 0)
|