tests_views.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2013 The Distro Tracker Developers
  3. # See the COPYRIGHT file at the top-level directory of this distribution and
  4. # at http://deb.li/DTAuthors
  5. #
  6. # This file is part of Distro Tracker. It is subject to the license terms
  7. # in the LICENSE file found in the top-level directory of this
  8. # distribution and at http://deb.li/DTLicense. No part of Distro Tracker,
  9. # including this file, may be copied, modified, propagated, or distributed
  10. # except according to the terms contained in the LICENSE file.
  11. """
  12. Tests for the Distro Tracker core views.
  13. """
  14. from __future__ import unicode_literals
  15. from distro_tracker.test import TestCase, TemplateTestsMixin
  16. from distro_tracker.core.models import BinaryPackage, BinaryPackageName
  17. from distro_tracker.core.models import SourcePackageName, SourcePackage
  18. from distro_tracker.core.models import PackageName, PseudoPackageName
  19. from distro_tracker.core.models import News
  20. from distro_tracker.core.models import ActionItem, ActionItemType
  21. import json
  22. from django.core.urlresolvers import reverse
  23. from django.conf import settings
  24. class PackageViewTest(TestCase):
  25. """
  26. Tests for the package view.
  27. """
  28. def setUp(self):
  29. self.package = SourcePackageName.objects.create(name='dummy-package')
  30. self.binary_package = BinaryPackageName.objects.create(
  31. name='binary-package')
  32. self.pseudo_package = \
  33. PseudoPackageName.objects.create(name='pseudo-pkg')
  34. self.src_pkg = SourcePackage.objects.create(
  35. source_package_name=self.package, version='1.0.0')
  36. self.bin_pkg = BinaryPackage.objects.create(
  37. binary_package_name=self.binary_package,
  38. source_package=self.src_pkg,
  39. short_description='a useful package')
  40. self.src_pkg.binary_packages = [self.binary_package]
  41. self.src_pkg.save()
  42. self.bin_pkg.save()
  43. def get_package_url(self, package_name):
  44. """
  45. Helper method which returns the URL for the package with the given name
  46. """
  47. return reverse('dtracker-package-page', kwargs={
  48. 'package_name': package_name
  49. })
  50. def test_source_package_page(self):
  51. """
  52. Tests that when visiting the package page for an existing package, a
  53. response based on the correct template is returned.
  54. """
  55. url = self.get_package_url(self.package.name)
  56. response = self.client.get(url)
  57. self.assertTemplateUsed(response, 'core/package.html')
  58. def test_source_package_page_with_plus_it_its_name(self):
  59. """
  60. Tests that we can visit the page for a package which contains
  61. a plus its name (non-regression test for bug #754497).
  62. """
  63. pkg = SourcePackageName.objects.create(name='libti++')
  64. url = self.get_package_url(pkg.name)
  65. response = self.client.get(url)
  66. self.assertTemplateUsed(response, 'core/package.html')
  67. def test_binary_package_redirects_to_source(self):
  68. """
  69. Tests that when visited a binary package URL, the user is redirected
  70. to the corresponding source package page.
  71. """
  72. url = self.get_package_url(self.binary_package.name)
  73. response = self.client.get(url)
  74. self.assertRedirects(response, self.get_package_url(self.package.name))
  75. def test_pseudo_package_page(self):
  76. """
  77. Tests that when visiting a page for a pseudo package the correct
  78. template is used.
  79. """
  80. url = self.get_package_url(self.pseudo_package.name)
  81. response = self.client.get(url)
  82. self.assertTemplateUsed(response, 'core/package.html')
  83. def test_non_existent_package(self):
  84. """
  85. Tests that a 404 is returned when the given package does not exist.
  86. """
  87. url = self.get_package_url('no-exist')
  88. self.assertEqual(self.client.get(url).status_code, 404)
  89. def test_subscriptions_only_package(self):
  90. """
  91. Tests that a 404 is returned when the given package is a "subscriptions
  92. only" package.
  93. """
  94. package_name = 'sub-only-pkg'
  95. # Make sure the package actually exists.
  96. PackageName.objects.create(name=package_name)
  97. url = self.get_package_url(package_name)
  98. self.assertEqual(self.client.get(url).status_code, 404)
  99. def test_old_package_with_news(self):
  100. """
  101. Tests that when visiting the package page for an old package with news,
  102. a response based on the correct template is returned.
  103. """
  104. package_name = 'old-pkg-with-news'
  105. oldpackage = PackageName.objects.create(name=package_name)
  106. News.objects.create(package=oldpackage, title='sample-title',
  107. content='sample-content')
  108. url = self.get_package_url(package_name)
  109. response = self.client.get(url)
  110. self.assertTemplateUsed(response, 'core/package.html')
  111. def test_legacy_url_redirects(self):
  112. """
  113. Tests that the old PTS style package URLs are correctly redirected.
  114. """
  115. url_template = '/{hash}/{package}.html'
  116. # Redirects for packages that do not start with "lib"
  117. url = url_template.format(hash=self.package.name[0],
  118. package=self.package.name)
  119. response = self.client.get(url)
  120. self.assertRedirects(response, self.get_package_url(self.package.name),
  121. status_code=301)
  122. # No redirect when the hash does not match the package
  123. url = url_template.format(hash='q', package=self.package.name)
  124. self.assertEqual(self.client.get(url).status_code, 404)
  125. # Redirect when the package name starts with "lib"
  126. lib_package = 'libpackage'
  127. SourcePackageName.objects.create(name=lib_package)
  128. url = url_template.format(hash='libp', package=lib_package)
  129. self.assertRedirects(self.client.get(url),
  130. self.get_package_url(lib_package),
  131. status_code=301)
  132. def test_catchall_redirect(self):
  133. """
  134. Tests that requests made to the root domain are redirected to a package
  135. page when possible and when it does not conflict with another URL rule.
  136. """
  137. url = '/{}'.format(self.package.name)
  138. response = self.client.get(url, follow=True)
  139. # User redirected to the existing package page
  140. self.assertRedirects(response, self.get_package_url(self.package.name))
  141. # Trailing slash
  142. url = '/{}/'.format(self.package.name)
  143. response = self.client.get(url, follow=True)
  144. # User redirected to the existing package page
  145. self.assertRedirects(response, self.get_package_url(self.package.name))
  146. # Admin URLs have precedence to the catch all package redirect
  147. url = reverse('admin:index')
  148. response = self.client.get(url, follow=True)
  149. # No redirects to non-existing /pkg/admin, so no 404 either
  150. self.assertNotEqual(404, response.status_code)
  151. # Non existing package
  152. url = '/{}'.format('no-exist')
  153. response = self.client.get(url, follow=True)
  154. self.assertEqual(404, response.status_code)
  155. def test_short_description(self):
  156. """
  157. Tests that the short description is displayed.
  158. """
  159. url = self.get_package_url(self.package.name)
  160. response = self.client.get(url)
  161. response_content = response.content.decode('utf-8')
  162. self.assertIn('a useful package', response_content)
  163. def test_page_does_not_contain_None(self):
  164. """
  165. Ensure Python's None never ends up displayed on the web page.
  166. """
  167. url = self.get_package_url(self.package.name)
  168. response = self.client.get(url)
  169. response_content = response.content.decode('utf-8')
  170. self.assertNotIn('None', response_content)
  171. class PackageSearchViewTest(TestCase):
  172. def setUp(self):
  173. self.pseudo_package = \
  174. PseudoPackageName.objects.create(name='pseudo-package')
  175. self.source_package = \
  176. SourcePackageName.objects.create(name='dummy-package')
  177. self.binary_package = BinaryPackageName.objects.create(
  178. name='binary-package')
  179. src_pkg = SourcePackage.objects.create(
  180. source_package_name=self.source_package, version='1.0.0')
  181. src_pkg.binary_packages = [self.binary_package]
  182. src_pkg.save()
  183. def test_package_search_source_package(self):
  184. """
  185. Tests the package search when the given package is an existing source
  186. package.
  187. """
  188. response = self.client.get(reverse('dtracker-package-search'), {
  189. 'package_name': self.source_package.name
  190. })
  191. self.assertRedirects(response, self.source_package.get_absolute_url())
  192. def test_package_search_pseudo_package(self):
  193. """
  194. Tests the package search when the given package is an existing pseudo
  195. package.
  196. """
  197. response = self.client.get(reverse('dtracker-package-search'), {
  198. 'package_name': self.pseudo_package.name
  199. })
  200. self.assertRedirects(response, self.pseudo_package.get_absolute_url())
  201. def test_package_search_binary_package(self):
  202. """
  203. Tests the package search when the given package is an existing binary
  204. package.
  205. """
  206. response = self.client.get(reverse('dtracker-package-search'), {
  207. 'package_name': self.binary_package.name
  208. })
  209. self.assertRedirects(response, self.source_package.get_absolute_url())
  210. def test_package_does_not_exist(self):
  211. """
  212. Tests the package search when the given package does not exist.
  213. """
  214. response = self.client.get(reverse('dtracker-package-search'), {
  215. 'package_name': 'no-exist'
  216. })
  217. self.assertTemplateUsed('core/package_search.html')
  218. self.assertIn('package_name', response.context)
  219. self.assertEqual(response.context['package_name'], 'no-exist')
  220. def test_case_insensitive_package_search(self):
  221. """
  222. Tests that package search is case insensitive
  223. """
  224. response = self.client.get(reverse('dtracker-package-search'), {
  225. 'package_name': 'DuMmy-PACKAGE'
  226. })
  227. self.assertRedirects(response, self.source_package.get_absolute_url())
  228. class OpenSearchDescriptionTest(TestCase):
  229. """
  230. Tests for the :class:`distro_tracker.core.views.OpenSearchDescription`.
  231. """
  232. def test_html_head_contains_opensearch_link_entry(self):
  233. osd_uri = reverse('dtracker-opensearch-description')
  234. header = '<link type="application/opensearchdescription+xml" title="'
  235. header += "%s Package Tracker Search" % \
  236. settings.DISTRO_TRACKER_VENDOR_NAME
  237. header += '" rel="search" href="' + osd_uri + '"/>'
  238. response = self.client.get(reverse('dtracker-index'))
  239. self.assertContains(response, header, html=True)
  240. def test_opensearch_description_url(self):
  241. response = self.client.get(reverse('dtracker-opensearch-description'))
  242. self.assertTemplateUsed(response, 'core/opensearch-description.xml')
  243. def test_opensearch_description_contains_relevant_urls(self):
  244. response = self.client.get(reverse('dtracker-opensearch-description'))
  245. self.assertContains(response, reverse('dtracker-favicon'))
  246. self.assertContains(response, reverse('dtracker-package-search') +
  247. '?package_name={searchTerms}')
  248. class IndexViewTest(TestCase):
  249. def test_index(self):
  250. """
  251. Tests that the correct template is rendered when the index page is
  252. accessed.
  253. """
  254. response = self.client.get('/')
  255. self.assertTemplateUsed(response, 'core/index.html')
  256. class PackageAutocompleteViewTest(TestCase):
  257. def setUp(self):
  258. SourcePackageName.objects.create(name='dummy-package')
  259. SourcePackageName.objects.create(name='d-package')
  260. SourcePackageName.objects.create(name='package')
  261. PseudoPackageName.objects.create(name='pseudo-package')
  262. PseudoPackageName.objects.create(name='zzz')
  263. BinaryPackageName.objects.create(name='package-dev')
  264. BinaryPackageName.objects.create(name='libpackage')
  265. PackageName.objects.create(name='ppp')
  266. def test_source_package_autocomplete(self):
  267. """
  268. Tests the autocomplete functionality when the client asks for source
  269. packages.
  270. """
  271. response = self.client.get(reverse('dtracker-api-package-autocomplete'),
  272. {'package_type': 'source', 'q': 'd'})
  273. response = json.loads(response.content.decode('utf-8'))
  274. self.assertEqual(len(response), 2)
  275. self.assertEqual(response[0], 'd')
  276. self.assertEqual(len(response[1]), 2)
  277. self.assertIn('dummy-package', response[1])
  278. self.assertIn('d-package', response[1])
  279. # No packages given when there are no matching source packages
  280. response = self.client.get(reverse('dtracker-api-package-autocomplete'),
  281. {'package_type': 'source', 'q': 'z'})
  282. response = json.loads(response.content.decode('utf-8'))
  283. self.assertEqual(len(response), 2)
  284. self.assertEqual(response[0], 'z')
  285. self.assertEqual(len(response[1]), 0)
  286. def test_binary_package_autocomplete(self):
  287. """
  288. Tests the autocomplete functionality when the client asks for binary
  289. packages.
  290. """
  291. response = self.client.get(reverse('dtracker-api-package-autocomplete'),
  292. {'package_type': 'binary', 'q': 'p'})
  293. response = json.loads(response.content.decode('utf-8'))
  294. self.assertEqual(len(response), 2)
  295. self.assertEqual(response[0], 'p')
  296. self.assertEqual(len(response[1]), 2)
  297. self.assertIn('package-dev', response[1])
  298. self.assertIn('libpackage', response[1])
  299. # No packages given when there are no matching binary packages
  300. response = self.client.get(reverse('dtracker-api-package-autocomplete'),
  301. {'package_type': 'binary', 'q': 'z'})
  302. response = json.loads(response.content.decode('utf-8'))
  303. self.assertEqual(len(response), 2)
  304. self.assertEqual(response[0], 'z')
  305. self.assertEqual(len(response[1]), 0)
  306. def test_pseudo_package_autocomplete(self):
  307. """
  308. Tests the autocomplete functionality when the client asks for pseudo
  309. packages.
  310. """
  311. response = self.client.get(reverse('dtracker-api-package-autocomplete'),
  312. {'package_type': 'pseudo', 'q': 'p'})
  313. response = json.loads(response.content.decode('utf-8'))
  314. self.assertEqual(len(response), 2)
  315. self.assertEqual(response[0], 'p')
  316. self.assertEqual(len(response[1]), 1)
  317. self.assertIn('pseudo-package', response[1])
  318. # No packages given when there are no matching pseudo packages
  319. response = self.client.get(reverse('dtracker-api-package-autocomplete'),
  320. {'package_type': 'pseudo', 'q': 'y'})
  321. response = json.loads(response.content.decode('utf-8'))
  322. self.assertEqual(len(response), 2)
  323. self.assertEqual(response[0], 'y')
  324. self.assertEqual(len(response[1]), 0)
  325. def test_all_packages_autocomplete(self):
  326. """
  327. Tests the autocomplete functionality when the client does not specify
  328. the type of package. The result should only contain source and pseudo
  329. packages, no binary package.
  330. """
  331. response = self.client.get(reverse('dtracker-api-package-autocomplete'),
  332. {'q': 'p'})
  333. response = json.loads(response.content.decode('utf-8'))
  334. self.assertEqual(len(response), 2)
  335. self.assertEqual(response[0], 'p')
  336. self.assertEqual(len(response[1]), 4)
  337. self.assertIn('package', response[1])
  338. self.assertIn('pseudo-package', response[1])
  339. self.assertIn('d-package', response[1])
  340. self.assertIn('dummy-package', response[1])
  341. # No packages given when there are no matching packages
  342. response = self.client.get(reverse('dtracker-api-package-autocomplete'),
  343. {'q': '-dev'})
  344. response = json.loads(response.content.decode('utf-8'))
  345. self.assertEqual(len(response), 2)
  346. self.assertEqual(response[0], '-dev')
  347. self.assertEqual(len(response[1]), 0)
  348. def test_no_query_given(self):
  349. """
  350. Tests the autocomplete when there is no query parameter given.
  351. """
  352. response = self.client.get(reverse('dtracker-api-package-autocomplete'),
  353. {'package_type': 'source'})
  354. self.assertEqual(response.status_code, 404)
  355. class ActionItemJsonViewTest(TestCase):
  356. """
  357. Tests for the :class:`distro_tracker.core.views.ActionItemJsonView`.
  358. """
  359. def setUp(self):
  360. self.package = SourcePackageName.objects.create(name='dummy-package')
  361. self.action_type = ActionItemType.objects.create(
  362. type_name='test',
  363. full_description_template='action-item-test.html')
  364. self.add_test_template_dir()
  365. def test_item_exists(self):
  366. """
  367. Tests that the JSON response correctly returns an item's content.
  368. """
  369. expected_short_description = 'Short description of item'
  370. action_item = ActionItem.objects.create(
  371. package=self.package,
  372. item_type=self.action_type,
  373. short_description=expected_short_description)
  374. response = self.client.get(reverse('dtracker-api-action-item', kwargs={
  375. 'item_pk': action_item.pk,
  376. }))
  377. response = json.loads(response.content.decode('utf-8'))
  378. # Correct short description
  379. self.assertEqual(
  380. expected_short_description,
  381. response['short_description'])
  382. # Package name included
  383. self.assertEqual(
  384. 'dummy-package',
  385. response['package']['name'])
  386. # Full description from rendered template
  387. self.assertIn("Item's PK is", response['full_description'])
  388. # Template name NOT included
  389. self.assertNotIn('full_description_template', response)
  390. def test_item_does_not_exist(self):
  391. """
  392. Tests that the JSON ActionItem view returns 404 when the item does not
  393. exist.
  394. """
  395. does_not_exist = 100
  396. # Sanity check - the PK actually does not exist
  397. self.assertEqual(0,
  398. ActionItem.objects.filter(pk=does_not_exist).count())
  399. response = self.client.get(reverse('dtracker-api-action-item', kwargs={
  400. 'item_pk': does_not_exist,
  401. }))
  402. self.assertEqual(response.status_code, 404)
  403. class NewsViewTest(TestCase, TemplateTestsMixin):
  404. """
  405. Tests for the :class:`distro_tracker.core.views.PackageNews`.
  406. """
  407. NEWS_LIMIT = settings.DISTRO_TRACKER_NEWS_PANEL_LIMIT
  408. def setUp(self):
  409. self.package = SourcePackageName.objects.create(name='dummy-package')
  410. self.src_pkg = SourcePackage.objects.create(
  411. source_package_name=self.package, version='1.0.0')
  412. self.src_pkg.save()
  413. self.news_url = reverse('dtracker-package-news',
  414. kwargs={'package_name': self.package.name})
  415. # add some news
  416. for i in range(2 * self.NEWS_LIMIT + 1):
  417. self.package.news_set.create(title="News {}".format(i),
  418. created_by="Author {}".format(i))
  419. def get_package_news(self, page=None):
  420. if not page:
  421. return self.client.get(self.news_url)
  422. else:
  423. return self.client.get('%s?page=%s' % (self.news_url, page))
  424. def test_news_page_has_link_to_package_page(self):
  425. response = self.get_package_news()
  426. package_url = reverse('dtracker-package-page', kwargs={
  427. 'package_name': self.package.name,
  428. })
  429. self.assertLinkIsInResponse(response, package_url)
  430. def test_news_page_has_paginated_link_to_page_2(self):
  431. response = self.get_package_news()
  432. self.assertLinkIsInResponse(response, '?page=2')
  433. def test_news_page_has_no_invalid_paginated_link(self):
  434. response = self.get_package_news()
  435. self.assertLinkIsNotInResponse(response, '?page=4')
  436. def test_page_2_of_news_page_has_link_to_page_1(self):
  437. response = self.get_package_news(page=2)
  438. self.assertLinkIsInResponse(response, '?page=1')