Move the redirect tests into the tests directory and consolidate with the tests from mcom-tests

This commit is contained in:
Dave Hunt 2016-01-15 15:19:01 +00:00
Родитель 8bb9bd9856
Коммит 656ef73c60
20 изменённых файлов: 279 добавлений и 121 удалений

Просмотреть файл

@ -24,14 +24,14 @@ before_script:
script:
- grunt test
- python manage.py test
- py.test test_redirects
- py.test -r a -m smoke tests/redirects
before_install:
- travis_retry git submodule update --init --recursive
install:
- travis_retry pip install -r requirements/pip.txt
- travis_retry python bin/peep.py install -r requirements/dev.txt --no-use-wheel
- travis_retry python bin/peep.py install -r requirements/prod.txt --no-use-wheel
- travis_retry python bin/peep.py install -r requirements/test_redirects.txt --no-use-wheel
- travis_retry python bin/peep.py install -r requirements/test.txt --no-use-wheel
notifications:
irc:
channels:

Просмотреть файл

@ -143,3 +143,46 @@ of the header:
header_redirector('cookie', 'been-here', '/firefox/', '/firefox/new/'),
vary='cookie'),
]
Testing redirects
-----------------
A suite of tests exists for redirects, which is intended as a reference of the
redirects we expect to work on www.mozilla.org. This will become a base for
implementing these redirects in the bedrock app and allow us to test them
before release.
Installation
~~~~~~~~~~~~
First follow the :ref:`installation instructions for bedrock<install>`, which
will guide you through installing pip and setting up a virtual environment for
the tests. The additional requirements can then be installed by using the
following commands:
.. code-block:: bash
$ source venv/bin/activate
$ bin/peep.py install -r requirements/test.txt
Running the tests
~~~~~~~~~~~~~~~~~
.. code-block:: bash
$ py.test tests/redirect/
This will start a local instance of bedrock, run the tests, and then stop the
instance. If you wish to run the tests against another instance of the site
(e.g. www.mozilla.org) you can set the ``--base-url`` command line option:
.. code-block:: bash
$ py.test --base-url https://www.mozilla.org tests/redirect/
By default, tests will run one at a time. If you intend to run the suite
against a remote instance of the site (e.g. production) it will run a lot
quicker by running the tests in parallel. To do this, you can add ``-n auto``
to the command line. Replace ``auto`` with an integer if you want to set the
maximum number of concurrent processes.

Просмотреть файл

@ -60,12 +60,18 @@ To run the full functional test suite against your local bedrock instance:
.. code-block:: bash
$ py.test --driver Firefox --html tests/functional/results.html tests/functional/
$ py.test --base-url http://localhost:8000 --driver Firefox --html tests/functional/results.html tests/functional/
This will run all test suites found in the ``tests/functional`` directory and
assumes you have bedrock running at ``localhost`` on port ``8000``. Results will
be reported in ``tests/functional/results.html``.
.. Note::
If you omit the ``--base-url`` command line option then a local instance of
bedrock will be started, however the tests are not currently able to run
against bedrock in this way.
By default, tests will run one at a time. This is the safest way to ensure
predictable results, due to
`bug 1230105 <https://bugzilla.mozilla.org/show_bug.cgi?id=1230105>`_.
@ -85,14 +91,14 @@ e.g. ``tests/functional/test_newsletter.py``:
.. code-block:: bash
$ py.test --driver Firefox --html tests/functional/results.html -n auto tests/functional/test_newsletter.py
$ py.test --base-url http://localhost:8000 --driver Firefox --html tests/functional/results.html -n auto tests/functional/test_newsletter.py
To run a single test you can filter using the ``-k`` argument supplied with a keyword
e.g. ``-k test_successful_sign_up``:
.. code-block:: bash
$ py.test --driver Firefox --html tests/functional/results.html -n auto tests/functional/test_newsletter.py -k test_successful_sign_up
$ py.test --base-url http://localhost:8000 --driver Firefox --html tests/functional/results.html -n auto tests/functional/test_newsletter.py -k test_successful_sign_up
You can also easily run the tests against any bedrock environment by specifying the
``--base-url`` argument. For example, to run all functional tests against dev:

Просмотреть файл

@ -1,3 +1,5 @@
-r dev.txt
# sha256: Ljg5nb6EKJH-hTkmAaq49AqPTMWpBTwybeNaHMApesY
# sha256: ZdKqaLKOfTEjO7K6jrMc2kDkZx-KwtayQeNYyWUqdLk
apipkg==1.4
@ -7,6 +9,9 @@ apipkg==1.4
# sha256: h9QBPQYl1HiaT1a415oE1c5tsRUrtl8dOXRPdwmjZrQ
beautifulsoup4==4.4.1
# sha256: -WfKOb25jhYpmmnEWpRMXUNFOTYV7WRwux5iyjUGv0E
braceexpand==0.1.1
# sha256: 9m3Up1GXJaG34UrZrn09-OCbLaiAYjhuCOlByvwO8-Y
# sha256: 0rkJx5RYMuHBnPrNlueNpova3GVkQM_H3-WbdmdE64w
execnet==1.4.1
@ -19,6 +24,10 @@ py==1.4.31
# sha256: RLsy-zkltaKEzu4a9V4KY9JUNuxBUjIIlAPu06NHZn4
pytest==2.8.5
# sha256: 0UWsncelV6cZq3l3C-CUEATh4DjhN8NFkZGdnfKnkLE
# sha256: 12-TTnf6Bz9IzFIZRaSZAKhZ5hD6Ap3YgNHYuZe3fCM
pytest-django==2.8.0
# sha256: KieLOTNuHzPoNbDJ2WvbzMMjR4LHNi3Lh9mHhnTlmeU
# sha256: 5j2bkYqs99tlgR6MsCop0xRR65TE4x5eVYKHqkAuC6o
pytest-html==1.7

Просмотреть файл

@ -1,9 +0,0 @@
-r dev.txt
-r test.txt
# sha256: -WfKOb25jhYpmmnEWpRMXUNFOTYV7WRwux5iyjUGv0E
braceexpand==0.1.1
# sha256: 0UWsncelV6cZq3l3C-CUEATh4DjhN8NFkZGdnfKnkLE
# sha256: 12-TTnf6Bz9IzFIZRaSZAKhZ5hD6Ap3YgNHYuZe3fCM
pytest-django==2.8.0

Просмотреть файл

@ -6,11 +6,10 @@ max-line-length=150
with-id=1
logging-clear-handlers=1
verbosity=3
exclude-dir=test_redirects
exclude-dir=tests/redirects
tests/functional
[pytest]
addopts = --showlocals -r a --ignore=node_modules
DJANGO_SETTINGS_MODULE = bedrock.settings
base_url = http://127.0.0.1:8000
sensitive_url = mozilla\.(com|org)

Просмотреть файл

@ -1,34 +0,0 @@
# mozilla.org redirection tests
A test suite intended as a reference of the redirects we expect to work
on www.mozilla.org. This will become a base for implementing these
redirects in the [bedrock][] app and allow us to test them there before
release.
## Setup
1. [Install bedrock](http://bedrock.readthedocs.org/en/latest/install.html)
2. Activate or switch to your bedrock [virtualenv][] environment
3. Install the redirect test dependencies: `./bin/peep.py install -r requirements/test_redirects.txt`
## Running the tests
Run the tests from within your bedrock [virtualenv][] environment.
```bash
$ py.test test_redirects
```
By default the suite is run against your local copy of bedrock. If you wish to run
it against another instance of the site (e.g. www.mozilla.org) you can set the
`--mozorg-url` command line switch.
```bash
$ py.test test_redirects --mozorg-url=https://www.mozilla.org
```
**Note**: If you intend to run the suite against a remote instance of the site (e.g. production) it will run a lot quicker if you install [pytest-xdist][] and have it use multiple processes (e.g. `py.test test_redirects -n auto`).
[bedrock]: https://github.com/mozilla/bedrock/
[virtualenv]: https://virtualenv.pypa.io/
[pytest-xdist]: http://pytest.org/latest/xdist.html

Просмотреть файл

@ -1,23 +0,0 @@
import pytest
from pytest_django.fixtures import live_server
def pytest_addoption(parser):
group = parser.getgroup('django')
group._addoption('--mozorg-url',
action='store', type='string', dest='mozorg_url', default='local',
help='URL of the mozorg server to test (default: local)')
def pytest_configure(config):
url = config.getvalue('mozorg_url')
config.mozorg_url = url.rstrip('/')
@pytest.fixture(scope='session')
def live_or_remote_server(request):
url = request.config.getvalue('mozorg_url').rstrip('/')
if url == 'local':
return live_server(request).url
return url

Просмотреть файл

@ -1,29 +0,0 @@
"""Test redirects from the global.conf file."""
from __future__ import absolute_import
from operator import itemgetter
import pytest
from .base import assert_valid_url
from .map_410 import URLS_410
from .map_htaccess import URLS as HTA_URLS
from .map_globalconf import URLS as GLOBAL_URLS
@pytest.mark.parametrize('url', GLOBAL_URLS, ids=itemgetter('url'))
def test_global_conf_url(url, live_or_remote_server):
# live_or_remote_server defined in conftest.py
url['base_url'] = live_or_remote_server
assert_valid_url(**url)
@pytest.mark.parametrize('url', HTA_URLS, ids=itemgetter('url'))
def test_htaccess_url(url, live_or_remote_server):
# live_or_remote_server defined in conftest.py
url['base_url'] = live_or_remote_server
assert_valid_url(**url)
@pytest.mark.parametrize('url', URLS_410)
def test_410_url(url, live_or_remote_server):
assert_valid_url(url, status_code=410, base_url=live_or_remote_server)

10
tests/conftest.py Normal file
Просмотреть файл

@ -0,0 +1,10 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import pytest
@pytest.fixture(scope='session')
def base_url(base_url, request):
return base_url or request.getfuncargvalue('live_server').url

Просмотреть файл

@ -8,12 +8,19 @@ import requests
def pytest_generate_tests(metafunc):
if 'not headless' in metafunc.config.option.markexpr:
return # test deslected by mark expression
base_url = metafunc.config.option.base_url
if not base_url:
pytest.skip(
'This test requires a base URL to be specified on the command '
'line or in a configuration file.')
paths = (
'/firefox/new/',
'/thunderbird/')
argvalues = []
for path in paths:
r = requests.get(metafunc.config.option.base_url + path)
r = requests.get(base_url + path)
soup = BeautifulSoup(r.content, 'html.parser')
urls = [a['href'] for a in soup.find('ul', class_='download-list').find_all('a')]
assert len(urls) > 0

Просмотреть файл

@ -8,6 +8,13 @@ import requests
def pytest_generate_tests(metafunc):
if 'not headless' in metafunc.config.option.markexpr:
return # test deslected by mark expression
base_url = metafunc.config.option.base_url
if not base_url:
pytest.skip(
'This test requires a base URL to be specified on the command '
'line or in a configuration file.')
paths = (
'/firefox/all/',
'/firefox/beta/all/',
@ -15,7 +22,7 @@ def pytest_generate_tests(metafunc):
'/firefox/organizations/all/')
argvalues = []
for path in paths:
r = requests.get(metafunc.config.option.base_url + path)
r = requests.get(base_url + path)
soup = BeautifulSoup(r.content, 'html.parser')
table = soup.find('table', class_='build-table')
urls = [a['href'] for a in table.find_all('a')]

Просмотреть файл

Просмотреть файл

@ -1,18 +1,24 @@
import re
from urlparse import urlparse, parse_qs
from braceexpand import braceexpand
import requests
def get_abs_url(url, base_url):
if url.startswith('/'):
# urljoin messes with query strings too much
return ''.join([base_url, url])
try:
if url.pattern.startswith('/'):
# url is a compiled regular expression pattern
return re.compile(''.join([re.escape(base_url), url.pattern]))
except AttributeError:
if url.startswith('/'):
# urljoin messes with query strings too much
return ''.join([base_url, url])
return url
def url_test(url, location=None, status_code=301, req_headers=None, req_kwargs=None,
resp_headers=None, query=None):
def url_test(url, location=None, status_code=requests.codes.moved_permanently,
req_headers=None, req_kwargs=None, resp_headers=None, query=None):
"""
Function for producing a config dict for the redirect test.
@ -23,13 +29,18 @@ def url_test(url, location=None, status_code=301, req_headers=None, req_kwargs=N
If you use brace expansion this function will return a list of dicts instead of a dict.
You must use the `flatten` function provided to prepare your test fixture if you do this.
If you combine brace expansion with a compiled regular expression pattern you must
escape any backslashes as this is the escape character for brace expansion.
example:
url_test('/about/drivers{/,.html}', 'https://wiki.mozilla.org/Firefox/Drivers'),
url_test('/projects/index.{de,fr,hr,sq}.html', '/{de,fr,hr,sq}/firefox/products/'),
url_test('/firefox/notes/', re.compile(r'\/firefox\/[\d\.]+\/releasenotes\/'),
url_test('/firefox/android/{,beta/}notes/', re.compile(r'\\/firefox\\/android\\/[\\d\\.]+{,beta}\\/releasenotes\\/'
:param url: The URL in question (absolute or relative).
:param location: If a redirect, the expected value of the "Location" header.
:param location: If a redirect, either the expected value or a compiled regular expression to match the "Location" header.
:param status_code: Expected status code from the request.
:param req_headers: Extra headers to send with the request.
:param req_kwargs: Extra arguments to pass to requests.get()
@ -51,6 +62,13 @@ def url_test(url, location=None, status_code=301, req_headers=None, req_kwargs=N
if num_urls == 1:
return test_data
try:
# location is a compiled regular expression pattern
location_pattern = location.pattern
test_data['location'] = location_pattern
except AttributeError:
location_pattern = None
new_urls = []
if location:
expanded_locations = list(braceexpand(test_data['location']))
@ -60,18 +78,23 @@ def url_test(url, location=None, status_code=301, req_headers=None, req_kwargs=N
data = test_data.copy()
data['url'] = url
if location and num_urls == num_locations:
data['location'] = expanded_locations[i]
if location_pattern is not None:
# recompile the pattern after expansion
data['location'] = re.compile(expanded_locations[i])
else:
data['location'] = expanded_locations[i]
new_urls.append(data)
return new_urls
def assert_valid_url(url, location=None, status_code=301, req_headers=None, req_kwargs=None,
resp_headers=None, query=None, base_url=None):
def assert_valid_url(url, location=None, status_code=requests.codes.moved_permanently,
req_headers=None, req_kwargs=None, resp_headers=None,
query=None, base_url=None):
"""
Define a test of a URL's response.
:param url: The URL in question (absolute or relative).
:param location: If a redirect, the expected value of the "Location" header.
:param location: If a redirect, either the expected value or a compiled regular expression to match the "Location" header.
:param status_code: Expected status code from the request.
:param req_headers: Extra headers to send with the request.
:param req_kwargs: Extra arguments to pass to requests.get()
@ -103,7 +126,12 @@ def assert_valid_url(url, location=None, status_code=301, req_headers=None, req_
# strip off query for further comparison
resp_location = resp_location.split('?')[0]
assert resp_location == get_abs_url(location, base_url)
abs_location = get_abs_url(location, base_url)
try:
# location is a compiled regular expression pattern
assert abs_location.match(resp_location) is not None
except AttributeError:
assert abs_location == resp_location
if resp_headers:
for name, value in resp_headers.items():

Просмотреть файл

Просмотреть файл

@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
import requests
from .base import flatten, url_test
URLS = flatten((
url_test('https://www.mozilla.com/', 'https://www.mozilla.org/firefox/'),
url_test('https://aurora.mozilla.org/', 'https://www.mozilla.org/firefox/aurora/', status_code=requests.codes.found),
url_test('https://beta.mozilla.org/', 'https://www.mozilla.org/en-US/firefox/channel/#beta'),
))

Просмотреть файл

@ -1,6 +1,10 @@
# -*- coding: utf-8 -*-
# flake8: noqa
from __future__ import absolute_import
import re
import requests
from .base import flatten, url_test
@ -224,8 +228,8 @@ URLS = flatten((
url_test('/projects/calendar/holidays.html', '/projects/calendar/holidays/'),
url_test('/en-US/projects/calendar/random/stuff/', '/projects/calendar/'),
# redirects don't catch real urls
url_test('/en-US/projects/calendar/', status_code=200),
url_test('/en-US/projects/calendar/holidays/', status_code=200),
url_test('/en-US/projects/calendar/', status_code=requests.codes.ok),
url_test('/en-US/projects/calendar/holidays/', status_code=requests.codes.ok),
# bug 1124038
url_test('/thunderbird/organizations/{all-esr.html,faq/}', '/thunderbird/organizations/'),
@ -797,7 +801,7 @@ URLS = flatten((
'https://developer.mozilla.org/docs/Mozilla/Projects/Rhino/Download_Rhino'),
url_test('/rhino/doc.html',
'https://developer.mozilla.org/docs/Mozilla/Projects/Rhino/Documentation'),
url_test('/rhino/random/stuff/', 'https://developer.mozilla.org/docs/Mozilla/Projects/Rhino'),
url_test('/rhino/{,random/stuff}', 'https://developer.mozilla.org/docs/Mozilla/Projects/Rhino'),
# Bug 730488 deprecate /firefox/all-older.html
url_test('/firefox/all-older.html', '/firefox/new/'),
@ -915,12 +919,12 @@ URLS = flatten((
# Bug 1186373
url_test('/firefox/hello/npssurvey/',
'https://www.surveygizmo.com/s3/2227372/Firefox-Hello-Product-Survey',
status_code=302),
status_code=requests.codes.found),
# Bug 1221739
url_test('/firefox/hello/feedbacksurvey/',
'https://www.surveygizmo.com/s3/2319863/d2b7dc4b5687',
status_code=302),
status_code=requests.codes.found),
# bug 1224060
url_test('/ja/firefox/ios/1.0/{releasenotes,system-requirements}/',
@ -951,4 +955,25 @@ URLS = flatten((
# bug 1239960
url_test('/firefox/partners/', '/firefox/os/'),
url_test('/b2g/', '/firefox/os/'),
# from mcom-tests
url_test('/firefox/', '/en-US/firefox/'),
url_test('/en-US/firefox/', '/firefox/new/'),
url_test('/firefox/new/', '/en-US/firefox/new/'),
url_test('/firefox/mobile/', '/firefox/android/'),
url_test('/mobile/37.0{,beta,a2}/releasenotes', '/firefox/android/37.0{,beta,a2}/releasenotes/'),
url_test('/projects/firefox/3.6.13/whatsnew/', '/firefox/3.6.13/whatsnew/'),
url_test('/en-US/firefox/3.6.13/{firstrun,whatsnew}/', '/en-US/firefox/new/'),
url_test('/apps/', 'https://marketplace.firefox.com/'),
url_test('/dnt/', '/firefox/dnt/'),
url_test('/metrofirefox/', '/firefox/'),
url_test('/en-US/firefox/android/{,beta/}notes/',
re.compile(r'/en-US/firefox/android/[\\d\\.]+{,beta}/releasenotes/'),
status_code=requests.codes.found),
url_test('/en-US/firefox/android/aurora/notes/',
re.compile(r'/en-US/firefox/android/[\d\.a-zA-Z]+/auroranotes/'),
status_code=requests.codes.found),
url_test('/en-US/firefox/notes/',
re.compile(r'/en-US/firefox/[\d\.]+/releasenotes/'),
status_code=requests.codes.found),
))

Просмотреть файл

Просмотреть файл

@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from .base import flatten, url_test
# supported locales
LOCALES = [
'ar', 'ast', 'bg', 'bn-IN', 'ca', 'cs', 'de', 'dsb', 'el', 'en-GB',
'en-US', 'es-CL', 'es-ES', 'fr', 'fy-NL', 'gd', 'it', 'ja', 'ko', 'nl',
'pt-BR', 'pt-PT', 'ru', 'son', 'sv-SE', 'ta', 'tr', 'uk', 'zh-CN', 'zh-TW']
# List of some locale name variants including unsupported short names and
# obsolete ab-CD-style names, which could be included in the visitors'
# Accept-Language HTTP header and should be redirected to the respective
# canonical locales
LOCALE_VARIANTS = {
'en-US': ['en', 'en-CA'],
'es-ES': ['es', 'es-419'],
'fr': ['fr-FR'],
'ja': ['ja-JP-mac'],
'pt-BR': ['pt'],
'ta': ['ta-LK']}
_urls = []
for locale in LOCALES:
variants = [locale]
variants.extend(LOCALE_VARIANTS.get(locale, []))
for variant in variants:
# check the landing page redirects for each locale variant
_urls.append(url_test('/', '/{locale}/'.format(locale=locale),
req_headers={'Accept-Language': variant}))
URLS = flatten(_urls)

Просмотреть файл

@ -0,0 +1,72 @@
"""Test redirects from the global.conf file."""
from __future__ import absolute_import
from operator import itemgetter
import pytest
import requests
from .base import assert_valid_url
from .map_410 import URLS_410
from .map_htaccess import URLS as HTA_URLS
from .map_globalconf import URLS as GLOBAL_URLS
from .map_external import URLS as EXTERNAL_URLS
from .map_locales import URLS as LOCALE_URLS
@pytest.mark.smoke
@pytest.mark.headless
@pytest.mark.nondestructive
@pytest.mark.parametrize('url', GLOBAL_URLS, ids=itemgetter('url'))
def test_global_conf_url(url, base_url):
url['base_url'] = base_url
assert_valid_url(**url)
@pytest.mark.smoke
@pytest.mark.headless
@pytest.mark.nondestructive
@pytest.mark.parametrize('url', HTA_URLS, ids=itemgetter('url'))
def test_htaccess_url(url, base_url):
url['base_url'] = base_url
assert_valid_url(**url)
@pytest.mark.smoke
@pytest.mark.headless
@pytest.mark.nondestructive
@pytest.mark.parametrize('url', LOCALE_URLS)
def test_locale_url(url, base_url):
url['base_url'] = base_url
assert_valid_url(**url)
@pytest.mark.headless
@pytest.mark.nondestructive
@pytest.mark.parametrize('url', EXTERNAL_URLS, ids=itemgetter('url'))
def test_external_url(url):
assert_valid_url(**url)
@pytest.mark.smoke
@pytest.mark.headless
@pytest.mark.nondestructive
@pytest.mark.parametrize('url', URLS_410)
def test_410_url(url, base_url):
assert_valid_url(url, status_code=requests.codes.gone, base_url=base_url)
@pytest.mark.smoke
@pytest.mark.headless
@pytest.mark.nondestructive
def test_404_url(base_url):
assert_valid_url(
'/en-US/abck',
status_code=requests.codes.not_found,
base_url=base_url)
@pytest.mark.smoke
@pytest.mark.headless
@pytest.mark.nondestructive
def test_x_robots_tag(base_url):
assert_valid_url(base_url, resp_headers={'x-robots-tag': 'noodp'})