tests.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799
  1. import json
  2. from datetime import date, datetime, timedelta
  3. from io import StringIO
  4. from django.core import management
  5. from django.test import TestCase
  6. from django.urls import reverse
  7. from wagtail.admin.admin_url_finder import AdminURLFinder
  8. from wagtail.contrib.search_promotions.models import (
  9. Query,
  10. QueryDailyHits,
  11. SearchPromotion,
  12. )
  13. from wagtail.contrib.search_promotions.templatetags.wagtailsearchpromotions_tags import (
  14. get_search_promotions,
  15. )
  16. from wagtail.test.utils import WagtailTestUtils
  17. class TestSearchPromotions(TestCase):
  18. def test_search_pick_create(self):
  19. # Create a search pick to the root page
  20. SearchPromotion.objects.create(
  21. query=Query.get("root page"),
  22. page_id=1,
  23. sort_order=0,
  24. description="First search promotion",
  25. )
  26. # Check
  27. self.assertEqual(Query.get("root page").editors_picks.count(), 1)
  28. self.assertEqual(Query.get("root page").editors_picks.first().page_id, 1)
  29. def test_search_pick_link_create(self):
  30. # Add 3 search picks in a different order to their sort_order values
  31. # They should be ordered by their sort order values and not their insertion order
  32. SearchPromotion.objects.create(
  33. query=Query.get("root page"),
  34. external_link_url="https://wagtail.org",
  35. sort_order=0,
  36. description="First search promotion",
  37. )
  38. # Check
  39. self.assertEqual(Query.get("root page").editors_picks.count(), 1)
  40. self.assertEqual(
  41. Query.get("root page").editors_picks.first().external_link_url,
  42. "https://wagtail.org",
  43. )
  44. def test_search_pick_ordering(self):
  45. SearchPromotion.objects.create(
  46. query=Query.get("root page"),
  47. page_id=1,
  48. sort_order=0,
  49. description="First search pick",
  50. )
  51. SearchPromotion.objects.create(
  52. query=Query.get("root page"),
  53. page_id=1,
  54. sort_order=2,
  55. description="Last search pick",
  56. )
  57. SearchPromotion.objects.create(
  58. query=Query.get("root page"),
  59. external_link_url="https://wagtail.org",
  60. sort_order=1,
  61. description="Middle search pick",
  62. )
  63. # Check
  64. self.assertEqual(Query.get("root page").editors_picks.count(), 3)
  65. self.assertEqual(
  66. Query.get("root page").editors_picks.first().description,
  67. "First search pick",
  68. )
  69. self.assertEqual(
  70. Query.get("root page").editors_picks.last().description, "Last search pick"
  71. )
  72. def test_get_most_popular(self):
  73. popularQuery = Query.get("popular")
  74. for i in range(5):
  75. popularQuery.add_hit()
  76. SearchPromotion.objects.create(
  77. query=Query.get("popular"),
  78. page_id=2,
  79. sort_order=0,
  80. description="Popular search pick",
  81. )
  82. SearchPromotion.objects.create(
  83. query=Query.get("uninteresting"),
  84. page_id=1,
  85. sort_order=2,
  86. description="Uninteresting search pick",
  87. )
  88. # Check
  89. self.assertEqual(Query.get_most_popular().count(), 1)
  90. popular_picks = Query.get("popular").editors_picks.first()
  91. self.assertEqual(
  92. popular_picks.description,
  93. "Popular search pick",
  94. )
  95. self.assertEqual(popular_picks.query.hits, 5)
  96. def test_get_most_popular_since(self):
  97. TODAY = date.today()
  98. TWO_DAYS_AGO = TODAY - timedelta(days=2)
  99. FIVE_DAYS_AGO = TODAY - timedelta(days=5)
  100. popularQuery = Query.get("popular")
  101. for i in range(5):
  102. popularQuery.add_hit(date=FIVE_DAYS_AGO)
  103. surpriseQuery = Query.get("surprise")
  104. surpriseQuery.add_hit(date=TODAY)
  105. surpriseQuery.add_hit(date=TWO_DAYS_AGO)
  106. surpriseQuery.add_hit(date=FIVE_DAYS_AGO)
  107. SearchPromotion.objects.create(
  108. query=Query.get("popular"),
  109. page_id=2,
  110. sort_order=0,
  111. description="Popular search pick",
  112. )
  113. SearchPromotion.objects.create(
  114. query=Query.get("surprise"),
  115. page_id=2,
  116. sort_order=2,
  117. description="Surprising search pick",
  118. )
  119. # Check
  120. most_popular_queries = Query.get_most_popular(date_since=TWO_DAYS_AGO)
  121. self.assertEqual(most_popular_queries.count(), 1)
  122. editors_picks = Query.get("surprise").editors_picks
  123. surprise_picks = editors_picks.first()
  124. self.assertEqual(
  125. surprise_picks.description,
  126. "Surprising search pick",
  127. )
  128. self.assertEqual(surprise_picks.query.hits, 3)
  129. class TestGetSearchPromotionsTemplateTag(TestCase):
  130. def test_get_search_promotions_template_tag(self):
  131. # Create a search pick to the root page
  132. pick = SearchPromotion.objects.create(
  133. query=Query.get("root page"),
  134. page_id=1,
  135. sort_order=0,
  136. description="First search pick",
  137. )
  138. # Create another search pick against a different query
  139. SearchPromotion.objects.create(
  140. query=Query.get("root page again"),
  141. page_id=1,
  142. sort_order=0,
  143. description="Second search pick",
  144. )
  145. # Check
  146. search_picks = list(get_search_promotions("root page"))
  147. self.assertEqual(search_picks, [pick])
  148. def test_get_search_promotions_with_none_query_string(self):
  149. search_picks = list(get_search_promotions(None))
  150. self.assertEqual(search_picks, [])
  151. class TestSearchPromotionsIndexView(WagtailTestUtils, TestCase):
  152. def setUp(self):
  153. self.login()
  154. def test_simple(self):
  155. response = self.client.get(reverse("wagtailsearchpromotions:index"))
  156. self.assertEqual(response.status_code, 200)
  157. self.assertTemplateUsed(response, "wagtailsearchpromotions/index.html")
  158. def test_search(self):
  159. response = self.client.get(
  160. reverse("wagtailsearchpromotions:index"), {"q": "Hello"}
  161. )
  162. self.assertEqual(response.status_code, 200)
  163. self.assertEqual(response.context["query_string"], "Hello")
  164. self.assertContains(
  165. response,
  166. 'Sorry, no promoted results match "<em>Hello</em>"',
  167. )
  168. def test_search_with_results(self):
  169. SearchPromotion.objects.create(
  170. query=Query.get("search promotion query"),
  171. page_id=1,
  172. )
  173. SearchPromotion.objects.create(
  174. query=Query.get("search promotion query"),
  175. external_link_url="https://wagtail.org",
  176. )
  177. response = self.client.get(
  178. reverse("wagtailsearchpromotions:index"), {"q": "search promotion query"}
  179. )
  180. self.assertEqual(response.status_code, 200)
  181. self.assertEqual(response.context["query_string"], "search promotion query")
  182. self.assertContains(response, '<a href="/admin/pages/1/edit/" class="nolink">')
  183. self.assertContains(
  184. response,
  185. '<a href="https://wagtail.org" class="nolink" target="_blank" rel="noreferrer">',
  186. )
  187. def make_search_picks(self):
  188. for i in range(50):
  189. SearchPromotion.objects.create(
  190. query=Query.get("query " + str(i)),
  191. page_id=1,
  192. sort_order=0,
  193. description="First search pick",
  194. )
  195. def test_pagination(self):
  196. self.make_search_picks()
  197. response = self.client.get(reverse("wagtailsearchpromotions:index"), {"p": 2})
  198. # Check response
  199. self.assertEqual(response.status_code, 200)
  200. self.assertTemplateUsed(response, "wagtailsearchpromotions/index.html")
  201. # Check that we got the correct page
  202. self.assertEqual(response.context["queries"].number, 2)
  203. def test_pagination_invalid(self):
  204. self.make_search_picks()
  205. response = self.client.get(
  206. reverse("wagtailsearchpromotions:index"), {"p": "Hello World!"}
  207. )
  208. # Check response
  209. self.assertEqual(response.status_code, 404)
  210. def test_pagination_out_of_range(self):
  211. self.make_search_picks()
  212. response = self.client.get(
  213. reverse("wagtailsearchpromotions:index"), {"p": 99999}
  214. )
  215. # Check response
  216. self.assertEqual(response.status_code, 404)
  217. def test_results_are_ordered_alphabetically(self):
  218. self.make_search_picks()
  219. SearchPromotion.objects.create(
  220. query=Query.get("aaargh snake"),
  221. page_id=1,
  222. sort_order=0,
  223. description="ooh, it's a snake",
  224. )
  225. response = self.client.get(reverse("wagtailsearchpromotions:index"))
  226. # Check response
  227. self.assertEqual(response.status_code, 200)
  228. self.assertTemplateUsed(response, "wagtailsearchpromotions/index.html")
  229. # "aargh snake" should be the first result alphabetically
  230. self.assertEqual(response.context["queries"][0].query_string, "aaargh snake")
  231. def test_results_ordering(self):
  232. self.make_search_picks()
  233. url = reverse("wagtailsearchpromotions:index")
  234. SearchPromotion.objects.create(
  235. query=Query.get("zyzzyvas"),
  236. page_id=1,
  237. sort_order=0,
  238. description="no definition found, this is a valid scrabble word though",
  239. )
  240. SearchPromotion.objects.create(
  241. query=Query.get("aardwolf"),
  242. page_id=1,
  243. sort_order=22,
  244. description="Striped hyena of southeast Africa that feeds chiefly on insects",
  245. )
  246. popularQuery = Query.get("optimal")
  247. for i in range(50):
  248. popularQuery.add_hit()
  249. SearchPromotion.objects.create(
  250. query=popularQuery,
  251. page_id=1,
  252. sort_order=15,
  253. description="An oddly popular search term?",
  254. )
  255. popularQuery = Query.get("suboptimal")
  256. for i in range(25):
  257. popularQuery.add_hit()
  258. SearchPromotion.objects.create(
  259. query=popularQuery,
  260. page_id=1,
  261. sort_order=15,
  262. description="Not as popular",
  263. )
  264. # ordered by querystring (reversed)
  265. response = self.client.get(url + "?ordering=-query_string")
  266. self.assertEqual(response.status_code, 200)
  267. self.assertEqual(response.context["queries"][0].query_string, "zyzzyvas")
  268. # last page, still ordered by query string (reversed)
  269. response = self.client.get(url + "?ordering=-query_string&p=3")
  270. self.assertEqual(response.status_code, 200)
  271. self.assertEqual(response.context["queries"][-1].query_string, "aardwolf")
  272. # ordered by querystring (not reversed)
  273. response = self.client.get(url + "?ordering=query_string")
  274. self.assertEqual(response.status_code, 200)
  275. self.assertEqual(response.context["queries"][0].query_string, "aardwolf")
  276. # ordered by sum of daily hits (reversed)
  277. response = self.client.get(url + "?ordering=-views")
  278. self.assertEqual(response.status_code, 200)
  279. self.assertEqual(response.context["queries"][0].query_string, "optimal")
  280. self.assertEqual(response.context["queries"][1].query_string, "suboptimal")
  281. # ordered by sum of daily hits, last page (not reversed)
  282. response = self.client.get(url + "?ordering=views&p=3")
  283. self.assertEqual(response.status_code, 200)
  284. self.assertEqual(response.context["queries"][-1].query_string, "optimal")
  285. self.assertEqual(response.context["queries"][-2].query_string, "suboptimal")
  286. class TestSearchPromotionsAddView(WagtailTestUtils, TestCase):
  287. def setUp(self):
  288. self.login()
  289. def test_simple(self):
  290. response = self.client.get(reverse("wagtailsearchpromotions:add"))
  291. self.assertEqual(response.status_code, 200)
  292. self.assertTemplateUsed(response, "wagtailsearchpromotions/add.html")
  293. def test_post(self):
  294. # Submit
  295. post_data = {
  296. "query_string": "test",
  297. "editors_picks-TOTAL_FORMS": 1,
  298. "editors_picks-INITIAL_FORMS": 0,
  299. "editors_picks-MAX_NUM_FORMS": 1000,
  300. "editors_picks-0-DELETE": "",
  301. "editors_picks-0-ORDER": 0,
  302. "editors_picks-0-page": 1,
  303. "editors_picks-0-description": "Hello",
  304. }
  305. response = self.client.post(reverse("wagtailsearchpromotions:add"), post_data)
  306. # User should be redirected back to the index
  307. self.assertRedirects(response, reverse("wagtailsearchpromotions:index"))
  308. # Check that the search pick was created
  309. self.assertTrue(Query.get("test").editors_picks.filter(page_id=1).exists())
  310. def test_post_with_external_link(self):
  311. # Submit
  312. post_data = {
  313. "query_string": "test",
  314. "editors_picks-TOTAL_FORMS": 1,
  315. "editors_picks-INITIAL_FORMS": 0,
  316. "editors_picks-MAX_NUM_FORMS": 1000,
  317. "editors_picks-0-DELETE": "",
  318. "editors_picks-0-ORDER": 0,
  319. "editors_picks-0-external_link_url": "https://wagtail.org",
  320. "editors_picks-0-external_link_text": "Wagtail",
  321. "editors_picks-0-description": "Hello",
  322. }
  323. response = self.client.post(reverse("wagtailsearchpromotions:add"), post_data)
  324. # User should be redirected back to the index
  325. self.assertRedirects(response, reverse("wagtailsearchpromotions:index"))
  326. # Check that the search pick was created
  327. self.assertTrue(
  328. Query.get("test")
  329. .editors_picks.filter(external_link_url="https://wagtail.org")
  330. .exists()
  331. )
  332. def test_post_without_recommendations(self):
  333. # Submit
  334. post_data = {
  335. "query_string": "test",
  336. "editors_picks-TOTAL_FORMS": 0,
  337. "editors_picks-INITIAL_FORMS": 0,
  338. "editors_picks-MAX_NUM_FORMS": 1000,
  339. }
  340. response = self.client.post(reverse("wagtailsearchpromotions:add"), post_data)
  341. # User should be given an error
  342. self.assertEqual(response.status_code, 200)
  343. self.assertFormsetError(
  344. response,
  345. "searchpicks_formset",
  346. None,
  347. None,
  348. "Please specify at least one recommendation for this search term.",
  349. )
  350. def test_post_with_page_and_external_link(self):
  351. post_data = {
  352. "query_string": "test",
  353. "editors_picks-TOTAL_FORMS": 1,
  354. "editors_picks-INITIAL_FORMS": 0,
  355. "editors_picks-MAX_NUM_FORMS": 1000,
  356. "editors_picks-0-DELETE": "",
  357. "editors_picks-0-ORDER": 0,
  358. "editors_picks-0-page": 1,
  359. "editors_picks-0-external_link_url": "https://wagtail.org",
  360. "editors_picks-0-external_link_text": "Wagtail",
  361. "editors_picks-0-description": "Hello",
  362. }
  363. response = self.client.post(reverse("wagtailsearchpromotions:add"), post_data)
  364. # User should be given an error
  365. self.assertEqual(response.status_code, 200)
  366. self.assertFormsetError(
  367. response,
  368. "searchpicks_formset",
  369. None,
  370. None,
  371. "Please only select a page OR enter an external link.",
  372. )
  373. def test_post_missing_recommendation(self):
  374. post_data = {
  375. "query_string": "test",
  376. "editors_picks-TOTAL_FORMS": 1,
  377. "editors_picks-INITIAL_FORMS": 0,
  378. "editors_picks-MAX_NUM_FORMS": 1000,
  379. "editors_picks-0-DELETE": "",
  380. "editors_picks-0-ORDER": 0,
  381. "editors_picks-0-description": "Hello",
  382. }
  383. response = self.client.post(reverse("wagtailsearchpromotions:add"), post_data)
  384. # User should be given an error
  385. self.assertEqual(response.status_code, 200)
  386. self.assertFormsetError(
  387. response,
  388. "searchpicks_formset",
  389. None,
  390. None,
  391. "You must recommend a page OR an external link.",
  392. )
  393. def test_post_missing_external_text(self):
  394. post_data = {
  395. "query_string": "test",
  396. "editors_picks-TOTAL_FORMS": 1,
  397. "editors_picks-INITIAL_FORMS": 0,
  398. "editors_picks-MAX_NUM_FORMS": 1000,
  399. "editors_picks-0-DELETE": "",
  400. "editors_picks-0-ORDER": 0,
  401. "editors_picks-0-external_link_url": "https://wagtail.org",
  402. }
  403. response = self.client.post(reverse("wagtailsearchpromotions:add"), post_data)
  404. # User should be given an error
  405. self.assertEqual(response.status_code, 200)
  406. self.assertFormsetError(
  407. response,
  408. "searchpicks_formset",
  409. None,
  410. None,
  411. "You must enter an external link text if you enter an external link URL.",
  412. )
  413. class TestSearchPromotionsEditView(WagtailTestUtils, TestCase):
  414. def setUp(self):
  415. self.user = self.login()
  416. # Create an search pick to edit
  417. self.query = Query.get("Hello")
  418. self.search_pick = self.query.editors_picks.create(
  419. page_id=1, sort_order=0, description="Root page"
  420. )
  421. self.search_pick_2 = self.query.editors_picks.create(
  422. page_id=2, sort_order=1, description="Homepage"
  423. )
  424. def test_simple(self):
  425. response = self.client.get(
  426. reverse("wagtailsearchpromotions:edit", args=(self.query.id,))
  427. )
  428. self.assertEqual(response.status_code, 200)
  429. self.assertTemplateUsed(response, "wagtailsearchpromotions/edit.html")
  430. url_finder = AdminURLFinder(self.user)
  431. expected_url = "/admin/searchpicks/%d/" % self.query.id
  432. self.assertEqual(url_finder.get_edit_url(self.search_pick), expected_url)
  433. def test_post(self):
  434. # Submit
  435. post_data = {
  436. "query_string": "Hello",
  437. "editors_picks-TOTAL_FORMS": 2,
  438. "editors_picks-INITIAL_FORMS": 2,
  439. "editors_picks-MAX_NUM_FORMS": 1000,
  440. "editors_picks-0-id": self.search_pick.id,
  441. "editors_picks-0-DELETE": "",
  442. "editors_picks-0-ORDER": 0,
  443. "editors_picks-0-page": 1,
  444. "editors_picks-0-description": "Description has changed", # Change
  445. "editors_picks-1-id": self.search_pick_2.id,
  446. "editors_picks-1-DELETE": "",
  447. "editors_picks-1-ORDER": 1,
  448. "editors_picks-1-page": 2,
  449. "editors_picks-1-description": "Homepage",
  450. }
  451. response = self.client.post(
  452. reverse("wagtailsearchpromotions:edit", args=(self.query.id,)), post_data
  453. )
  454. # User should be redirected back to the index
  455. self.assertRedirects(response, reverse("wagtailsearchpromotions:index"))
  456. # Check that the search pick description was edited
  457. self.assertEqual(
  458. SearchPromotion.objects.get(id=self.search_pick.id).description,
  459. "Description has changed",
  460. )
  461. def test_post_reorder(self):
  462. # Check order before reordering
  463. self.assertEqual(Query.get("Hello").editors_picks.all()[0], self.search_pick)
  464. self.assertEqual(Query.get("Hello").editors_picks.all()[1], self.search_pick_2)
  465. # Submit
  466. post_data = {
  467. "query_string": "Hello",
  468. "editors_picks-TOTAL_FORMS": 2,
  469. "editors_picks-INITIAL_FORMS": 2,
  470. "editors_picks-MAX_NUM_FORMS": 1000,
  471. "editors_picks-0-id": self.search_pick.id,
  472. "editors_picks-0-DELETE": "",
  473. "editors_picks-0-ORDER": 1, # Change
  474. "editors_picks-0-page": 1,
  475. "editors_picks-0-description": "Root page",
  476. "editors_picks-1-id": self.search_pick_2.id,
  477. "editors_picks-1-DELETE": "",
  478. "editors_picks-1-ORDER": 0, # Change
  479. "editors_picks-1-page": 2,
  480. "editors_picks-1-description": "Homepage",
  481. }
  482. response = self.client.post(
  483. reverse("wagtailsearchpromotions:edit", args=(self.query.id,)), post_data
  484. )
  485. # User should be redirected back to the index
  486. self.assertRedirects(response, reverse("wagtailsearchpromotions:index"))
  487. # Check that the ordering has been saved correctly
  488. self.assertEqual(
  489. SearchPromotion.objects.get(id=self.search_pick.id).sort_order, 1
  490. )
  491. self.assertEqual(
  492. SearchPromotion.objects.get(id=self.search_pick_2.id).sort_order, 0
  493. )
  494. # Check that the recommendations were reordered
  495. self.assertEqual(Query.get("Hello").editors_picks.all()[0], self.search_pick_2)
  496. self.assertEqual(Query.get("Hello").editors_picks.all()[1], self.search_pick)
  497. def test_post_delete_recommendation(self):
  498. # Submit
  499. post_data = {
  500. "query_string": "Hello",
  501. "editors_picks-TOTAL_FORMS": 2,
  502. "editors_picks-INITIAL_FORMS": 2,
  503. "editors_picks-MAX_NUM_FORMS": 1000,
  504. "editors_picks-0-id": self.search_pick.id,
  505. "editors_picks-0-DELETE": "",
  506. "editors_picks-0-ORDER": 0,
  507. "editors_picks-0-page": 1,
  508. "editors_picks-0-description": "Root page",
  509. "editors_picks-1-id": self.search_pick_2.id,
  510. "editors_picks-1-DELETE": 1,
  511. "editors_picks-1-ORDER": 1,
  512. "editors_picks-1-page": 2,
  513. "editors_picks-1-description": "Homepage",
  514. }
  515. response = self.client.post(
  516. reverse("wagtailsearchpromotions:edit", args=(self.query.id,)), post_data
  517. )
  518. # User should be redirected back to the index
  519. self.assertRedirects(response, reverse("wagtailsearchpromotions:index"))
  520. # Check that the recommendation was deleted
  521. self.assertFalse(
  522. SearchPromotion.objects.filter(id=self.search_pick_2.id).exists()
  523. )
  524. # The other recommendation should still exist
  525. self.assertTrue(SearchPromotion.objects.filter(id=self.search_pick.id).exists())
  526. def test_post_without_recommendations(self):
  527. # Submit
  528. post_data = {
  529. "query_string": "Hello",
  530. "editors_picks-TOTAL_FORMS": 2,
  531. "editors_picks-INITIAL_FORMS": 2,
  532. "editors_picks-MAX_NUM_FORMS": 1000,
  533. "editors_picks-0-id": self.search_pick.id,
  534. "editors_picks-0-DELETE": 1,
  535. "editors_picks-0-ORDER": 0,
  536. "editors_picks-0-page": 1,
  537. "editors_picks-0-description": "Description has changed", # Change
  538. "editors_picks-1-id": self.search_pick_2.id,
  539. "editors_picks-1-DELETE": 1,
  540. "editors_picks-1-ORDER": 1,
  541. "editors_picks-1-page": 2,
  542. "editors_picks-1-description": "Homepage",
  543. }
  544. response = self.client.post(
  545. reverse("wagtailsearchpromotions:edit", args=(self.query.id,)), post_data
  546. )
  547. # User should be given an error
  548. self.assertEqual(response.status_code, 200)
  549. self.assertFormsetError(
  550. response,
  551. "searchpicks_formset",
  552. None,
  553. None,
  554. "Please specify at least one recommendation for this search term.",
  555. )
  556. class TestSearchPromotionsDeleteView(WagtailTestUtils, TestCase):
  557. def setUp(self):
  558. self.login()
  559. # Create an search pick to delete
  560. self.query = Query.get("Hello")
  561. self.search_pick = self.query.editors_picks.create(
  562. page_id=1, description="Root page"
  563. )
  564. self.search_pick_2 = self.query.editors_picks.create(
  565. page_id=2, description="Homepage"
  566. )
  567. def test_simple(self):
  568. response = self.client.get(
  569. reverse("wagtailsearchpromotions:delete", args=(self.query.id,))
  570. )
  571. self.assertEqual(response.status_code, 200)
  572. self.assertTemplateUsed(response, "wagtailsearchpromotions/confirm_delete.html")
  573. def test_post(self):
  574. # Submit
  575. response = self.client.post(
  576. reverse("wagtailsearchpromotions:delete", args=(self.query.id,))
  577. )
  578. # User should be redirected back to the index
  579. self.assertRedirects(response, reverse("wagtailsearchpromotions:index"))
  580. # Check that both recommendations were deleted
  581. self.assertFalse(
  582. SearchPromotion.objects.filter(id=self.search_pick_2.id).exists()
  583. )
  584. # The other recommendation should still exist
  585. self.assertFalse(
  586. SearchPromotion.objects.filter(id=self.search_pick.id).exists()
  587. )
  588. class TestGarbageCollectManagementCommand(TestCase):
  589. def test_garbage_collect_command(self):
  590. nowdt = datetime.now()
  591. old_hit_date = (nowdt - timedelta(days=14)).date()
  592. recent_hit_date = (nowdt - timedelta(days=1)).date()
  593. # Add 10 hits that are more than one week old. The related queries and the daily hits
  594. # should be deleted by the search_garbage_collect command.
  595. query_ids_to_be_deleted = []
  596. for i in range(10):
  597. q = Query.get(f"Hello {i}")
  598. q.add_hit(date=old_hit_date)
  599. query_ids_to_be_deleted.append(q.id)
  600. # Add 10 hits that are less than one week old. These ones should not be deleted.
  601. recent_query_ids = []
  602. for i in range(10):
  603. q = Query.get(f"World {i}")
  604. q.add_hit(date=recent_hit_date)
  605. recent_query_ids.append(q.id)
  606. # Add 10 queries that are promoted. These ones should not be deleted.
  607. promoted_query_ids = []
  608. for i in range(10):
  609. q = Query.get(f"Foo bar {i}")
  610. q.add_hit(date=old_hit_date)
  611. SearchPromotion.objects.create(
  612. query=q, page_id=1, sort_order=0, description="Test"
  613. )
  614. promoted_query_ids.append(q.id)
  615. management.call_command("searchpromotions_garbage_collect", stdout=StringIO())
  616. self.assertFalse(Query.objects.filter(id__in=query_ids_to_be_deleted).exists())
  617. self.assertFalse(
  618. QueryDailyHits.objects.filter(
  619. date=old_hit_date, query_id__in=query_ids_to_be_deleted
  620. ).exists()
  621. )
  622. self.assertEqual(Query.objects.filter(id__in=recent_query_ids).count(), 10)
  623. self.assertEqual(
  624. QueryDailyHits.objects.filter(
  625. date=recent_hit_date, query_id__in=recent_query_ids
  626. ).count(),
  627. 10,
  628. )
  629. self.assertEqual(Query.objects.filter(id__in=promoted_query_ids).count(), 10)
  630. self.assertEqual(
  631. QueryDailyHits.objects.filter(
  632. date=recent_hit_date, query_id__in=promoted_query_ids
  633. ).count(),
  634. 0,
  635. )
  636. class TestCopyDailyHitsFromWagtailSearchManagementCommand(TestCase):
  637. def run_command(self, **options):
  638. output = StringIO()
  639. management.call_command(
  640. "copy_daily_hits_from_wagtailsearch", stdout=output, **options
  641. )
  642. output.seek(0)
  643. return output
  644. def test_copy(self):
  645. # Create some daily hits in the wagtailsearch.{Query,QueryDailyHits} models
  646. from wagtail.search.models import Query as WSQuery
  647. query = WSQuery.get("test query")
  648. query.add_hit(date(2021, 8, 24))
  649. query.add_hit(date(2021, 8, 24))
  650. query.add_hit(date(2021, 7, 1))
  651. # Check that nothing magically got inserted into the new query model
  652. self.assertFalse(Query.objects.exists())
  653. # Run the management command
  654. self.run_command()
  655. # Check that the query now exists in the new model
  656. new_query = Query.objects.get()
  657. self.assertEqual(new_query.query_string, "test query")
  658. # Check daily hits
  659. self.assertEqual(new_query.hits, 3)
  660. class TestQueryChooserView(WagtailTestUtils, TestCase):
  661. def setUp(self):
  662. self.login()
  663. def get(self, params={}):
  664. return self.client.get("/admin/searchpicks/queries/chooser/", params)
  665. def test_simple(self):
  666. response = self.get()
  667. self.assertEqual(response.status_code, 200)
  668. self.assertTemplateUsed(
  669. response, "wagtailsearchpromotions/queries/chooser/chooser.html"
  670. )
  671. response_json = json.loads(response.content.decode())
  672. self.assertEqual(response_json["step"], "chooser")
  673. def test_search(self):
  674. response = self.get({"q": "Hello"})
  675. self.assertEqual(response.status_code, 200)
  676. def test_pagination(self):
  677. # page numbers in range should be accepted
  678. response = self.get({"p": 1})
  679. self.assertEqual(response.status_code, 200)
  680. # page numbers out of range should return 404
  681. response = self.get({"p": 9999})
  682. self.assertEqual(response.status_code, 404)