From a5631c5a33f8f4ccc1bacfa0f3050d5ff9d7f34e Mon Sep 17 00:00:00 2001 From: Ryan Johnson Date: Wed, 9 Oct 2019 10:27:37 -0700 Subject: [PATCH] Launch beta domain (#5639) * bug 1525719: launch beta as main domain * bug 1525719: fix integration tests * bug 1525719: remove selenium and perf tests * bug 1525719: fix linting issues * bug 1525719: prettier fixes * bug 1525719: wiki unit test fix * bug 1525719: fix scrape unit test * bug 1525719: fix test and standardize compose tests * bug 1525719: add jest tests to jenkins * bug 1525719: remove api_settings usage * bug 1525719: improve endpoint headless tests * bug 1525719: fix test_lookups_require_login unit test * bug 1525719: fix jest config * bug 1525719: fix assertRedirects failures * adjust test_search_validation_problems --- Jenkinsfiles/default.groovy | 16 +- Jenkinsfiles/integration-tests.groovy | 45 +- Jenkinsfiles/prod-integration-tests.yml | 3 - Jenkinsfiles/stage-integration-tests.yml | 3 - Jenkinsfiles/utils.groovy | 30 +- Makefile | 5 +- docker-compose.selenium.yml | 31 -- docker-compose.test.yml | 29 -- docs/development.rst | 6 +- docs/index.rst | 3 +- docs/tests-functional.rst | 170 +++++++ docs/tests-performance.rst | 52 -- docs/tests-ui.rst | 289 ----------- jest.config.js | 3 +- kuma/api/conftest.py | 7 - kuma/api/v1/tests/test_views.py | 47 +- kuma/attachments/tests/test_templates.py | 8 +- kuma/attachments/tests/test_views.py | 29 +- kuma/authkeys/tests/test_views.py | 39 +- kuma/conftest.py | 11 - kuma/core/tests/__init__.py | 5 + kuma/core/utils.py | 5 +- kuma/dashboards/tests/test_views.py | 100 ++-- kuma/dashboards/views.py | 2 + kuma/health/tests/test_views.py | 1 - kuma/health/views.py | 1 - kuma/landing/tests/test_templates.py | 5 +- kuma/landing/tests/test_views.py | 24 +- kuma/payments/tests/test_views.py | 70 ++- .../tests/test_source_document_history.py | 10 +- kuma/scrape/tests/test_source_links.py | 7 +- kuma/scrape/tests/test_source_revision.py | 17 +- kuma/search/tests/test_views.py | 13 +- kuma/search/views.py | 2 + kuma/settings/common.py | 7 +- kuma/users/tests/test_templates.py | 109 +++-- kuma/users/tests/test_views.py | 84 ++-- kuma/wiki/tests/test_templates.py | 201 +++++--- kuma/wiki/tests/test_views.py | 407 ++++++++++------ kuma/wiki/tests/test_views_akismet.py | 15 +- kuma/wiki/tests/test_views_code.py | 3 +- kuma/wiki/tests/test_views_create.py | 20 +- kuma/wiki/tests/test_views_delete.py | 26 +- kuma/wiki/tests/test_views_document.py | 92 ++-- kuma/wiki/tests/test_views_edit.py | 5 +- kuma/wiki/tests/test_views_list.py | 41 +- kuma/wiki/tests/test_views_misc.py | 6 +- kuma/wiki/tests/test_views_revision.py | 13 +- kuma/wiki/tests/test_views_translate.py | 7 +- package-lock.json | 152 ++++++ package.json | 2 + renovate.json | 1 - requirements/constraints.txt | 9 +- requirements/test.txt | 17 - scripts/run_functional_tests.sh | 252 ---------- tests/conftest.py | 24 +- tests/functional/__init__.py | 5 - tests/functional/test_article.py | 108 ----- tests/functional/test_article_edit.py | 144 ------ tests/functional/test_article_new.py | 49 -- tests/functional/test_article_revision.py | 49 -- tests/functional/test_article_translate.py | 36 -- tests/functional/test_content_experiment.py | 77 --- tests/functional/test_dashboard.py | 204 -------- tests/functional/test_feedback.py | 43 -- tests/functional/test_home.py | 94 ---- tests/functional/test_language_selector.py | 29 -- .../test_maintenance_mode_redirects.py | 112 ----- tests/functional/test_notfound.py | 43 -- tests/functional/test_profiles.py | 17 - tests/functional/test_report.py | 37 -- tests/functional/test_search.py | 133 ------ tests/headless/__init__.py | 12 + tests/headless/test_cdn.py | 452 +++--------------- tests/headless/test_endpoints.py | 279 +++++++++-- tests/headless/test_robots.py | 2 +- tests/pages/__init__.py | 0 tests/pages/admin.py | 46 -- tests/pages/article.py | 166 ------- tests/pages/article_edit.py | 92 ---- tests/pages/article_new.py | 45 -- tests/pages/base.py | 314 ------------ tests/pages/content_experiment.py | 34 -- tests/pages/dashboard.py | 205 -------- tests/pages/home.py | 57 --- tests/pages/notfound.py | 29 -- tests/pages/regions/__init__.py | 0 tests/pages/regions/ckeditor.py | 144 ------ tests/pages/regions/column_container.py | 38 -- tests/pages/regions/notification.py | 10 - tests/pages/search.py | 93 ---- tests/performance/Dockerfile | 5 - tests/performance/README.md | 3 - tests/performance/smoke.py | 56 --- tests/utils/decorators.py | 13 - 95 files changed, 1565 insertions(+), 4291 deletions(-) delete mode 100644 docker-compose.selenium.yml delete mode 100644 docker-compose.test.yml create mode 100644 docs/tests-functional.rst delete mode 100644 docs/tests-performance.rst delete mode 100644 docs/tests-ui.rst delete mode 100755 scripts/run_functional_tests.sh delete mode 100644 tests/functional/__init__.py delete mode 100644 tests/functional/test_article.py delete mode 100644 tests/functional/test_article_edit.py delete mode 100644 tests/functional/test_article_new.py delete mode 100644 tests/functional/test_article_revision.py delete mode 100644 tests/functional/test_article_translate.py delete mode 100644 tests/functional/test_content_experiment.py delete mode 100644 tests/functional/test_dashboard.py delete mode 100644 tests/functional/test_feedback.py delete mode 100644 tests/functional/test_home.py delete mode 100644 tests/functional/test_language_selector.py delete mode 100644 tests/functional/test_maintenance_mode_redirects.py delete mode 100644 tests/functional/test_notfound.py delete mode 100644 tests/functional/test_profiles.py delete mode 100644 tests/functional/test_report.py delete mode 100644 tests/functional/test_search.py delete mode 100644 tests/pages/__init__.py delete mode 100644 tests/pages/admin.py delete mode 100644 tests/pages/article.py delete mode 100644 tests/pages/article_edit.py delete mode 100644 tests/pages/article_new.py delete mode 100644 tests/pages/base.py delete mode 100644 tests/pages/content_experiment.py delete mode 100644 tests/pages/dashboard.py delete mode 100644 tests/pages/home.py delete mode 100644 tests/pages/notfound.py delete mode 100644 tests/pages/regions/__init__.py delete mode 100644 tests/pages/regions/ckeditor.py delete mode 100644 tests/pages/regions/column_container.py delete mode 100644 tests/pages/regions/notification.py delete mode 100644 tests/pages/search.py delete mode 100644 tests/performance/Dockerfile delete mode 100644 tests/performance/README.md delete mode 100644 tests/performance/smoke.py delete mode 100644 tests/utils/decorators.py diff --git a/Jenkinsfiles/default.groovy b/Jenkinsfiles/default.groovy index 5f6497d31aa..9343edb02ae 100644 --- a/Jenkinsfiles/default.groovy +++ b/Jenkinsfiles/default.groovy @@ -1,15 +1,15 @@ -stage('Test') { - utils.compose_test() -} - -stage('Build & push kuma_base image') { +stage('Build base') { utils.sh_with_notify( - 'make build-base push-base', - 'Build and push of commit-tagged Kuma base image' + 'make build-base VERSION=latest', + 'Build of latest-tagged Kuma base image' ) } -stage('Build & push kuma image') { +stage('Test') { + utils.compose_test() +} + +stage('Build & push images') { utils.sh_with_notify( 'make build-kuma push-kuma', "Build & push of commit-tagged Kuma image" diff --git a/Jenkinsfiles/integration-tests.groovy b/Jenkinsfiles/integration-tests.groovy index 09124310e14..56d3aced90e 100644 --- a/Jenkinsfiles/integration-tests.groovy +++ b/Jenkinsfiles/integration-tests.groovy @@ -8,41 +8,6 @@ stage('Build') { } } -def functional_test(browser, base_dir) { - // Define a parallel build that runs stand-alone Selenium - // for the given browser. - return { - node { - // Setup the extra arguments for the pytest command. - def pytest_opts = "" - if (config.job && config.job.tests) { - pytest_opts += "-m '${config.job.tests}'" - } - if (config.job && config.job.maintenance_mode) { - pytest_opts += " --maintenance-mode" - } - // Timeout at 7 minutes for stalled nodes. - withEnv(["TIMEOUT=7m", - "BROWSER=${browser}", - "PYTEST_OPTS=${pytest_opts}", - "BASE_URL=${config.job.base_url}", - "TEST_IMAGE_TAG=${GIT_COMMIT_SHORT}", - "SELENIUM_IMAGE_TAG=${config.job.selenium}", - "COMPOSE_PROJECT_NAME=${browser}-${BUILD_TAG}", - "COMPOSE_FILE=${base_dir}/docker-compose.selenium.yml"]) - { - sh 'docker-compose pull selenium' - sh 'docker-compose up -d selenium' - try { - sh 'docker-compose run tests' - } finally { - sh 'docker-compose down --volumes --remove-orphans' - } - } - } - } -} - def headless_test(base_dir) { return { node { @@ -59,18 +24,12 @@ def headless_test(base_dir) { } stage('Test') { - // Setup parallel tests - def allTests = [:] def base_dir = pwd() - allTests['chrome'] = functional_test('chrome', base_dir) - allTests['firefox'] = functional_test('firefox', base_dir) - allTests['headless'] = headless_test(base_dir) - def nick = "ci-bot" try { - // Run the tests in parallel - parallel allTests + // Run the headless tests + headless_test(base_dir) // Notify on success utils.notify_irc([irc_nick: nick, stage: 'Test', status: 'success']) utils.notify_slack([stage: 'Test', status: 'success']) diff --git a/Jenkinsfiles/prod-integration-tests.yml b/Jenkinsfiles/prod-integration-tests.yml index 273bbb7eb27..aecc09a8d49 100644 --- a/Jenkinsfiles/prod-integration-tests.yml +++ b/Jenkinsfiles/prod-integration-tests.yml @@ -3,7 +3,4 @@ pipeline: script: "integration-tests" job: dockerfile: "docker/images/integration-tests/Dockerfile" - selenium: "3.141.5" base_url: 'https://developer.mozilla.org' - tests: "not login" - maintenance_mode: false diff --git a/Jenkinsfiles/stage-integration-tests.yml b/Jenkinsfiles/stage-integration-tests.yml index 232531e96cb..916751fb6bf 100644 --- a/Jenkinsfiles/stage-integration-tests.yml +++ b/Jenkinsfiles/stage-integration-tests.yml @@ -3,7 +3,4 @@ pipeline: script: "integration-tests" job: dockerfile: "docker/images/integration-tests/Dockerfile" - selenium: "3.141.5" base_url: 'https://developer.allizom.org' - tests: "not login" - maintenance_mode: false diff --git a/Jenkinsfiles/utils.groovy b/Jenkinsfiles/utils.groovy index d3b7f4981c9..5fc2aa2778e 100644 --- a/Jenkinsfiles/utils.groovy +++ b/Jenkinsfiles/utils.groovy @@ -236,19 +236,27 @@ def announce_push() { } def compose_test() { - def dc = 'docker-compose -f docker-compose.yml -f docker-compose.test.yml' + def dc = 'docker-compose' + def dc_exec = "${dc} exec -T web" def dc_down = "${dc} down --volumes --remove-orphans" - // Pre-test tear down to ensure we're starting with a clean slate. + def es_url = 'http://elasticsearch:9200' + def mysql_url = 'mysql://root:kuma@mysql:3306/developer_mozilla_org' + def junit_results = '--junit-xml=/app/test_results/django.xml' + sh_with_notify("${dc} --version", 'Check the version') sh_with_notify(dc_down, 'Pre-test tear-down') - // Run the "smoke" tests with no external dependencies. - sh_with_notify("${dc} run noext", 'Smoke tests') - // Build the static assets required for many tests. - sh_with_notify("${dc} run noext make localecompile build-static", - 'Compile locales and build static assets') - // Run the Kuma tests, building the mysql image before starting. - sh_with_notify("${dc} build mysql", 'Build mysql') - sh_with_notify("${dc} run test", 'Kuma tests') - // Tear everything down. + sh_with_notify("${dc} up -d", 'Start the services detached') + sh_with_notify("${dc_exec} urlwait ${mysql_url} 30", 'Wait for MySQL') + sh_with_notify("${dc_exec} urlwait ${es_url} 30", 'Wait for Elasticsearch') + sh_with_notify("${dc_exec} make clean", 'Start with fresh directories') + sh_with_notify("${dc_exec} make localecompile", 'Compile locales') + sh_with_notify("${dc_exec} make build-static", 'Build static assets') + sh_with_notify("${dc_exec} ./manage.py migrate", 'Run migrations') + sh_with_notify("${dc_exec} pytest ${junit_results} kuma", 'Run tests') + sh_with_notify("${dc_exec} npm run test-ci", 'Run jest tests') + sh_with_notify( + "${dc_exec} ./manage.py makemigrations --check --dry-run", + 'Ensure that no DB migrations were forgotten' + ) sh_with_notify(dc_down, 'Post-test tear-down') } diff --git a/Makefile b/Makefile index 8444b7339cc..1c0a5a495a5 100644 --- a/Makefile +++ b/Makefile @@ -42,9 +42,6 @@ coveragetest: clean coveragetesthtml: coveragetest coverage html -locust: - locust -f tests/performance/smoke.py --host=https://developer.allizom.org - webpack: @ echo "## Running webpack ##" @ npm run webpack:prod @@ -178,4 +175,4 @@ npmrefresh: npm install # Those tasks don't have file targets -.PHONY: test coveragetest locust clean locale install compilejsi18n collectstatic localetest localeextract localecompile localerefresh npmrefresh webpack compile-react-i18n +.PHONY: test coveragetest clean locale install compilejsi18n collectstatic localetest localeextract localecompile localerefresh npmrefresh webpack compile-react-i18n diff --git a/docker-compose.selenium.yml b/docker-compose.selenium.yml deleted file mode 100644 index 87c944ff8e6..00000000000 --- a/docker-compose.selenium.yml +++ /dev/null @@ -1,31 +0,0 @@ -version: '3.5' -services: - selenium: - image: selenium/standalone-${BROWSER:-chrome}:${SELENIUM_IMAGE_TAG:-latest} - shm_size: "2g" - expose: - - "4444" - - tests: - image: kuma-integration-tests:${TEST_IMAGE_TAG:-latest} - volumes: - - ./:/app:z - depends_on: - - selenium - user: ${UID:-1000} - command: > - sh -c "urlwait && - timeout --preserve-status ${TIMEOUT:-10m} - py.test tests/functional - --driver Remote - --host selenium - --capability browserName ${BROWSER:-chrome} - --base-url=${BASE_URL:-https://developer.allizom.org} - --junit-prefix=${BROWSER:-chrome} - --junit-xml=/app/test_results/functional-${BROWSER:-chrome}.xml - --reruns=2 - -vv ${PYTEST_OPTS:-}" - environment: - - URLWAIT_TIMEOUT=300 - - URLWAIT_VARNAME=SELENIUM_STATUS_URL - - SELENIUM_STATUS_URL=http://selenium:4444/wd/hub/status diff --git a/docker-compose.test.yml b/docker-compose.test.yml deleted file mode 100644 index 2207dead3ef..00000000000 --- a/docker-compose.test.yml +++ /dev/null @@ -1,29 +0,0 @@ -version: '2.1' -services: - noext: - #run tests with no external dependencies - image: mdnwebdocs/kuma_base - user: ${UID:-0} - volumes: - - ./:/app - command: py.test --nomigrations --junit-xml=/app/test_results/smoke.xml kuma/humans kuma/health - environment: - - DATABASE_URL=sqlite:/// - - PYTHONDONTWRITEBYTECODE=1 - - test: - image: mdnwebdocs/kuma_base - user: ${UID:-0} - volumes: - - ./:/app - command: sh -c "urlwait && py.test --nomigrations --junit-xml=/app/test_results/django.xml kuma" - depends_on: - - redis - - mysql - - elasticsearch - environment: - - DATABASE_URL=mysql://root:kuma@mysql:3306/developer_mozilla_org - - ES_URLS=elasticsearch:9200 - - REDIS_CACHE_SERVER=redis://redis:6379/3 - - PYTHONDONTWRITEBYTECODE=1 - - URLWAIT_TIMEOUT=300 diff --git a/docs/development.rst b/docs/development.rst index d6e08409432..859ae749f4d 100644 --- a/docs/development.rst +++ b/docs/development.rst @@ -43,10 +43,10 @@ Run the Django test suite:: For more information, see the :doc:`test documentation `. -Front-end tests +Functional tests --------------- -To run the front-end (selenium) tests, see -:doc:`Client-side Testing `. +To run the functional tests, see +:doc:`Client-side Testing `. Kumascript tests ---------------- diff --git a/docs/index.rst b/docs/index.rst index 67fa153436e..eff92c8d120 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -15,8 +15,7 @@ Contents: development troubleshooting tests - tests-ui - tests-performance + tests-functional feature-toggles celery diff --git a/docs/tests-functional.rst b/docs/tests-functional.rst new file mode 100644 index 00000000000..5a2e9261004 --- /dev/null +++ b/docs/tests-functional.rst @@ -0,0 +1,170 @@ +=================== +Client-side testing +=================== + +Kuma has a suite of functional tests using `pytest`_. All these test suites live +in the /tests/ directory. + +The tests directory comprises of: + +* ``/headless`` contains tests that don't require a browser +* ``/utils`` contains helper functions. + +.. _`pytest`: http://pytest.org/latest/ + +Setting up test virtual environment +=================================== + +#. In the terminal go to the directory where you have downloaded kuma. + +#. Create a virtual environment for the tests to run in (the example uses the + name mdntests):: + + $ virtualenv mdntests + +#. Activate the virtual environment:: + + $ source mdntests/bin/activate + +#. Make sure the version of pip in the virtual environment is high enough to support hashes:: + + $ pip install "pip>=8" + +#. Install the requirements the tests need to run:: + + $ pip install -r requirements/test.txt + +You may want to add your virtual environment folder to your local .gitignore +file. + +Running the tests +================= + +The minimum amount of information necessary to run the tests against the staging +server is the directory the tests are in. Currently, all of the tests should run +successfully against the staging server. + +In the virtual environment run:: + + $ pytest tests + +This basic command can be modified to run the tests against a local environment, +or to run tests concurrently. + +Only running tests in one file +------------------------------ + +Add the name of the file to the test location:: + + $ pytest tests/headless/test_cdn.py + +Run the tests against a different url +------------------------------------- + +By default the tests will run against the staging server. If you'd like to run +the tests against a different URL (e.g., your local environment) pass the +desired URL to the command with ``--base-url``:: + + $ pytest tests --base-url http://mdn.localhost:8000 + +Run the tests in parallel +------------------------- + +By default the tests will run one after the other but you can run several at +the same time by specifying a value for ``-n``:: + + $ pytest tests -n auto + +Using Alternate Environments +============================ + +Run tests on MDN's Continuous Integration (CI) infrastructure +------------------------------------------------------------- + +If you have commit rights on the `mozilla/kuma GitHub repository`_ you can +run the functional tests using the `MDN CI Infrastructure`_. Just force push +to `mozilla/kuma@stage-integration-tests`_ to run the tests +against https://developer.allizom.org. + +You can check the status, progress, and logs of the +test runs at `MDN's Jenkins-based multi-branch pipeline`_. + +.. _`mozilla/kuma GitHub repository`: https://github.com/mozilla/kuma +.. _`mozilla/kuma@stage-integration-tests`: https://github.com/mozilla/kuma/tree/stage-integration-tests +.. _`MDN's Jenkins-based multi-branch pipeline`: https://ci.us-west-2.mdn.mozit.cloud/blue/organizations/jenkins/kuma/branches/ + +MDN CI Infrastructure +===================== + +The MDN CI infrastructure is a Jenkins-based, multi-branch pipeline. The +pipelines for all branches are defined by the `Jenkinsfile`_ and the files +under the `Jenkinsfiles directory`_. The basic idea is that every branch may +have its own custom pipeline steps and configuration. + +Jenkins will auto-discover the steps and configuration by checking within the +`Jenkinsfiles directory`_ for a Groovy (``.groovy``) and/or YAML (``.yml``) +file with the same name as the branch. For example, the +"stage-integration-tests" branch has a +`Jenkinsfiles/stage-integration-tests.yml`_ file which will be +loaded as configuration and used to determine what to do next (load and +run the Groovy script specified by its ``pipeline.script`` setting - +`Jenkinsfiles/integration-tests.groovy`_ - and the script, in turn, will use +the dictionary of values provided by the ``job`` setting defined within the +configuration). + +Note that the YAML files for the integration-test branches provide settings +for configuring things like the Dockerfile to use to build the container, +and the URL to test against. + +The integration-test Groovy files use a number of global Jenkins functions +that were developed to make the building, running and pushing of +Docker containers seamless (as well as other cool stuff, see +`mozmar/jenkins-pipeline`_). They allow us to better handle situations that +have been painful in the past, like the stopping of background Docker +containers. + +The "prod-integration-tests" branch also has its own +`Jenkinsfiles/prod-integration-tests.yml`_ file. It's identical to the YAML +file for the "stage-integration-tests" branch except that it specifies that +the tests should be run against the production server rather than the staging +server (via its ``job.base_url`` setting). + +Similarly, the "master" branch has it's own pipeline, but instead of being +configured by a YAML file, the entire pipeline is defined within its +`Jenkinsfiles/master.groovy`_ file. + +The pipeline for any other branch which does not provide its own Groovy and/or +YAML file will follow that defined by the `Jenkinsfiles/default.groovy`_ file. + +You can check the status, progress, and logs of any pipeline runs via +`MDN's Jenkins-based multi-branch pipeline`_. + +.. _`mozmar/jenkins-pipeline`: https://github.com/mozmar/jenkins-pipeline +.. _`Jenkinsfile`: https://github.com/mozilla/kuma/blob/master/Jenkinsfile +.. _`Jenkinsfiles directory`: https://github.com/mozilla/kuma/tree/master/Jenkinsfiles +.. _`Jenkinsfiles/master.groovy`: https://github.com/mozilla/kuma/blob/master/Jenkinsfiles/master.groovy +.. _`Jenkinsfiles/default.groovy`: https://github.com/mozilla/kuma/blob/master/Jenkinsfiles/default.groovy +.. _`Jenkinsfiles/integration-tests.groovy`: https://github.com/mozilla/kuma/blob/master/Jenkinsfiles/integration-tests.groovy +.. _`Jenkinsfiles/prod-integration-tests.yml` : https://github.com/mozilla/kuma/blob/master/Jenkinsfiles/prod-integration-tests.yml +.. _`Jenkinsfiles/stage-integration-tests.yml` : https://github.com/mozilla/kuma/blob/master/Jenkinsfiles/stage-integration-tests.yml + +Markers +======= + +* ``nondestructive`` + + Tests are considered destructive unless otherwise indicated. Tests that + create, modify, or delete data are considered destructive and should not be + run in production. + +* ``smoke`` + + These tests should be the critical baseline functional tests. + +Guidelines for writing tests +============================ + +See `Bedrock`_ and the `Web QA Style Guide`_. + +.. _`Bedrock`: http://bedrock.readthedocs.io/en/latest/testing.html#guidelines-for-writing-functional-tests +.. _`Web QA Style Guide`: https://wiki.mozilla.org/QA/Execution/Web_Testing/Docs/Automation/StyleGuide diff --git a/docs/tests-performance.rst b/docs/tests-performance.rst deleted file mode 100644 index 8a12a72bfd6..00000000000 --- a/docs/tests-performance.rst +++ /dev/null @@ -1,52 +0,0 @@ -Performance testing -=================== - -In 2015, as part of a "measure and improve performance" initiative, MDN staff -configured Locust for performance testing of MDN. The locust test files -simulate read traffic to MDN, fetching the top pages in roughly the same ratios -as they were fetched on MDN, and can be run against test environments to -measure changes in code, caching and settings. - -These tests are not maintained or run during the current development process. -Some of the common URLs are not included in the sample database. - -Running tests -------------- -.. note:: **DO NOT RUN LOCUST TESTS AGAINST PRODUCTION** - -#. Create a file ``docker-compose.locust.yml`` in the root folder (same folder - as ``docker-compose.yml``) with the contents:: - - version: "2.1" - services: - locust: - build: ./tests/performance - depends_on: - - web - environment: - - LOCUST_MODE=standalone - - TARGET_URL=http://web:8000 - volumes: - - ./tests/performance/:/tests - ports: - - "8089:8089" - -#. Edit ``.env`` to update docker-compose:: - - COMPOSE_FILE=docker-compose.yml:docker-compose.locust.yml - -#. Restart with ``make up`` - -#. Load the Locust UI at http://localhost:8089, and select: - - * number of users to simulate - * users spawned per second - -See `Start locust -`_ for more. - -Adding a test suite -------------------- - -See `Writing a locustfile -`_ for more. diff --git a/docs/tests-ui.rst b/docs/tests-ui.rst deleted file mode 100644 index fd809fff67a..00000000000 --- a/docs/tests-ui.rst +++ /dev/null @@ -1,289 +0,0 @@ -=================== -Client-side testing -=================== - -Kuma has a suite of functional tests using `Selenium`_ and `pytest`_. This allows us -to emulate users interacting with a real browser. All these test suites live in -the /tests/ directory. - -The tests directory comprises of: - -* ``/functional`` contains pytest tests. -* ``/pages`` contains Python `PyPOM`_ page objects. -* ``/utils`` contains helper functions. -* ``/headless`` contains tests that don't require a browser - -.. _`Selenium`: http://docs.seleniumhq.org/ -.. _`pytest`: http://pytest.org/latest/ -.. _`PyPOM`: https://pypom.readthedocs.io/en/latest/ - -Setting up test virtual environment -=================================== - -#. In the terminal go to the directory where you have downloaded kuma. - -#. Create a virtual environment for the tests to run in (the example uses the - name mdntests):: - - $ virtualenv mdntests - -#. Activate the virtual environment:: - - $ source mdntests/bin/activate - -#. Make sure the version of pip in the virtual environment is high enough to support hashes:: - - $ pip install "pip>=8" - -#. Install the requirements the tests need to run:: - - $ pip install -r requirements/test.txt - -You may want to add your virtual environment folder to your local .gitignore -file. - -Running the tests -================= - -Before running the tests you will need to download the driver for the browser -you want to test in. Drivers are listed and linked to in the `pytest-selenium`_ -documentation. - -The minimum amount of information necessary to run the tests against the staging -server is the directory the tests are in, which driver to use, and the -subset of tests to run (by specifying a marker or marker expression like -``-m "not login"``, for more information see `Markers`_). Currently, all of the -tests except the tests marked "login" should run successfully against the staging -server. - -In the virtual environment run:: - - $ pytest -m "not login" tests --driver Chrome --driver-path /path/to/chromedriver - -You will be prompted "Do you want the application 'python' to accept incoming -network connections?" The tests seem to run fine no matter how you answer. - -This basic command can be modified to use different browsers, to run the tests -against a local environment, or to run tests concurrently. - -.. _`pytest-selenium`: http://pytest-selenium.readthedocs.io/en/latest/user_guide.html#specifying-a-browser - -Only running tests in one file ------------------------------- - -Add the name of the file to the test location:: - - $ pytest -m "not login" tests/functional/test_search.py --driver Chrome --driver-path /path/to/chromedriver - - -Run the tests against a different url -------------------------------------- - -By default the tests will run against the staging server. If you'd like to run -the tests against a different URL (e.g., your local environment) pass the -desired URL to the command with ``--base-url``:: - - $ pytest -m "not login" tests --base-url http://localhost:8000 --driver Chrome --driver-path /path/to/chromedriver - -Only running headless tests ---------------------------- - -Headless tests do not require Selenium or markers:: - - $ pytest tests/headless --base-url http://localhost:8000 - -Run the tests in parallel -------------------------- - -By default the tests will run one after the other but you can run several at -the same time by specifying a value for ``-n``:: - - $ pytest -m "not login" tests -n auto --driver Chrome --driver-path /path/to/chromedriver - -Run the tests against a server configured in maintenance mode -------------------------------------------------------------- - -By default the tests will run against a server assumed to be configured -normally. If you'd like to run them against a server configured in -maintenance mode, simply add ``--maintenance-mode`` to the ``pytest`` command -line. For example, if you've configured your local environment to run in -maintenance mode:: - - $ pytest --maintenance-mode -m "not search" tests --base-url http://localhost:8000 --driver Chrome --driver-path /path/to/chromedriver - -Note that the tests marked "search" were excluded assuming you've loaded the -sample database. If you've loaded a full copy of the production database, you -can drop the ``-m "not search"``. - -The ``--maintenance-mode`` command-line switch does two things. It will skip -any tests that don't make sense in maintenance mode (e.g., making sure the -signin link works), and include the tests that only make sense in maintenance -mode (e.g., making sure that endpoints related to editing redirect to the -maintenance-mode page). - -Using Alternate Environments -============================ - -Run tests on SauceLabs ----------------------- - -Running the tests on SauceLabs will allow you to test browsers not on your host -machine. - -#. `Signup for an account`_. - -#. Log in and obtain your Remote Access Key from user settings. - -#. Run a test specifying ``SauceLabs`` as your driver, and pass your credentials - and the browser to test:: - - $ SAUCELABS_USERNAME=thedude SAUCELABS_API_KEY=123456789 pytest -m "not login" tests/functional/ --driver SauceLabs --capability browsername MicrosoftEdge - -Alternatively you can save your credentials `in a configuration file`_ so you -don't have to type them each time. - -.. _`Signup for an account`: https://saucelabs.com/opensauce/ -.. _`in a configuration file`: http://pytest-selenium.readthedocs.io/en/latest/user_guide.html#sauce-labs - -Run tests on MDN's Continuous Integration (CI) infrastructure -------------------------------------------------------------- - -If you have commit rights on the `mozilla/kuma GitHub repository`_ -you can run the UI tests using the `MDN CI Infrastructure`_. Just force push -to `mozilla/kuma@stage-integration-tests`_ to run the tests -against https://developer.allizom.org. - -You can check the status, progress, and logs of the -test runs at `MDN's Jenkins-based multi-branch pipeline`_. - -.. _`mozilla/kuma GitHub repository`: https://github.com/mozilla/kuma -.. _`mozilla/kuma@stage-integration-tests`: https://github.com/mozilla/kuma/tree/stage-integration-tests -.. _`MDN's Jenkins-based multi-branch pipeline`: https://ci.us-west-2.mdn.mozit.cloud/blue/organizations/jenkins/kuma/branches/ - -Run tests locally using Selenium Docker images ----------------------------------------------- -Running Selenium locally will take over your browser, and may make it -difficult to do other things with your development system. One option is to -use Selenium's Docker images, which contain browsers, drivers, and the -selenium agent. - -.. note:: This feature is in development, and some tests will fail against - the local environment. - -To run dockerized Selenium against your local development environment:: - - $ scripts/run_functional_tests.sh - -This uses the "standalone" Docker images, and runs the tests in Chrome and -Firefox. It runs ``pytest`` with the options -``tests/functional -m "not login" -vv --reruns=1`` (run functional tests, -skip those requiring a login, be very verbose, and try failing tests again). -It places test results in a subdirectory of ``test_results``, which -includes logs, screenshots, and other details. - -To run with Selenium Grid, like the MDN CI Infrastructure, use:: - - $ SELENIUM_HUB=1 scripts/run_functional_tests.sh - -You can replace the default ``pytest`` options by passing arguments:: - - $ scripts/run_functional_tests.sh -m "not login" tests/functional/test_article_edit.py - -You can test staging by setting a new base URL as a ``pytest`` argument:: - - $ scripts/run_functional_tests.sh --base-url https://developer.allizom.org -m "not login" tests/functional/test_article_edit.py - -You can also use an environment variable and get the default ``pytest`` arguments:: - - $ BASE_URL=https://developer.allizom.org scripts/run_functional_tests.sh - -See ``scripts/run_functional_tests.sh`` for the all the configuration options. - -MDN CI Infrastructure -===================== - -The MDN CI infrastructure is a Jenkins-based, multi-branch pipeline. The -pipelines for all branches are defined by the `Jenkinsfile`_ and the files -under the `Jenkinsfiles directory`_. The basic idea is that every branch may -have its own custom pipeline steps and configuration. - -Jenkins will auto-discover the steps and configuration by checking within the -`Jenkinsfiles directory`_ for a Groovy (``.groovy``) and/or YAML (``.yml``) -file with the same name as the branch. For example, the -"stage-integration-tests" branch has a -`Jenkinsfiles/stage-integration-tests.yml`_ file which will be -loaded as configuration and used to determine what to do next (load and -run the Groovy script specified by its ``pipeline.script`` setting - -`Jenkinsfiles/integration-tests.groovy`_ - and the script, in turn, will use -the dictionary of values provided by the ``job`` setting defined within the -configuration). - -Note that the YAML files for the integration-test branches provide settings -for configuring things like the version of Selenium to use, the number of -Selenium nodes to spin-up, the Dockerfile to use to build the container, -the URL to test against, and which subset of integration tests to run -(via a pytest marker expression, see `Markers`_). - -The integration-test Groovy files use a number of global Jenkins functions -that were developed to make the building, running and pushing of -Docker containers seamless (as well as other cool stuff, see -`mozmar/jenkins-pipeline`_). They allow us to better handle situations that -have been painful in the past, like the stopping of background Docker -containers. - -The "prod-integration-tests" branch also has its own -`Jenkinsfiles/prod-integration-tests.yml`_ file. It's identical to the YAML -file for the "stage-integration-tests" branch except that it specifies that -the tests should be run against the production server rather than the staging -server (via its ``job.base_url`` setting). - -Similarly, the "master" branch has it's own pipeline, but instead of being -configured by a YAML file, the entire pipeline is defined within its -`Jenkinsfiles/master.groovy`_ file. - -The pipeline for any other branch which does not provide its own Groovy and/or -YAML file will follow that defined by the `Jenkinsfiles/default.groovy`_ file. - -You can check the status, progress, and logs of any pipeline runs via -`MDN's Jenkins-based multi-branch pipeline`_. - -.. _`mozmar/jenkins-pipeline`: https://github.com/mozmar/jenkins-pipeline -.. _`Jenkinsfile`: https://github.com/mozilla/kuma/blob/master/Jenkinsfile -.. _`Jenkinsfiles directory`: https://github.com/mozilla/kuma/tree/master/Jenkinsfiles -.. _`Jenkinsfiles/master.groovy`: https://github.com/mozilla/kuma/blob/master/Jenkinsfiles/master.groovy -.. _`Jenkinsfiles/default.groovy`: https://github.com/mozilla/kuma/blob/master/Jenkinsfiles/default.groovy -.. _`Jenkinsfiles/integration-tests.groovy`: https://github.com/mozilla/kuma/blob/master/Jenkinsfiles/integration-tests.groovy -.. _`Jenkinsfiles/prod-integration-tests.yml` : https://github.com/mozilla/kuma/blob/master/Jenkinsfiles/prod-integration-tests.yml -.. _`Jenkinsfiles/stage-integration-tests.yml` : https://github.com/mozilla/kuma/blob/master/Jenkinsfiles/stage-integration-tests.yml - -Markers -======= - -* ``nondestructive`` - - Tests are considered destructive unless otherwise indicated. Tests that - create, modify, or delete data are considered destructive and should not be - run in production. - -* ``smoke`` - - These tests should be the critical baseline functional tests. - -* ``nodata`` - - New instances of kuma have empty databases so only a subset of tests can be - run against them. These tests are marked with ``nodata``. - -* ``login`` - - These tests require the testing accounts to exist on the target site. For - security reasons these accounts will not be on production. Exclude these tests - with ``-m "not login"`` - -Guidelines for writing tests -============================ - -See `Bedrock`_ and the `Web QA Style Guide`_. - -.. _`Bedrock`: http://bedrock.readthedocs.io/en/latest/testing.html#guidelines-for-writing-functional-tests -.. _`Web QA Style Guide`: https://wiki.mozilla.org/QA/Execution/Web_Testing/Docs/Automation/StyleGuide diff --git a/jest.config.js b/jest.config.js index 684b253a67b..25dd5599651 100644 --- a/jest.config.js +++ b/jest.config.js @@ -76,7 +76,7 @@ module.exports = { }, // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader - modulePathIgnorePatterns: ['kumascript'] + modulePathIgnorePatterns: ['kumascript'], // Activates notifications for test results // notify: false, @@ -92,6 +92,7 @@ module.exports = { // Use this configuration option to add custom reporters to Jest // reporters: undefined, + reporters: ['default', 'jest-junit'] // Automatically reset mock state between every test // resetMocks: false, diff --git a/kuma/api/conftest.py b/kuma/api/conftest.py index e74c94daef6..1bb2fbe31e7 100644 --- a/kuma/api/conftest.py +++ b/kuma/api/conftest.py @@ -7,13 +7,6 @@ from kuma.wiki.models import Document, Revision -@pytest.fixture -def api_settings(settings): - settings.BETA_HOST = 'beta.mdn.dev' - settings.ALLOWED_HOSTS.append(settings.BETA_HOST) - return settings - - @pytest.fixture def redirect_to_self(wiki_user): """ diff --git a/kuma/api/v1/tests/test_views.py b/kuma/api/v1/tests/test_views.py index b92063678e7..1ef807f86a7 100644 --- a/kuma/api/v1/tests/test_views.py +++ b/kuma/api/v1/tests/test_views.py @@ -53,27 +53,26 @@ def test_get_content_based_redirect(root_doc, redirect_doc, redirect_to_self, @pytest.mark.parametrize( 'http_method', ['put', 'post', 'delete', 'options', 'head']) -def test_doc_api_disallowed_methods(client, api_settings, http_method): +def test_doc_api_disallowed_methods(client, http_method): """HTTP methods other than GET are not allowed.""" url = reverse('api.v1.doc', args=['en-US', 'Web/CSS']) - response = getattr(client, http_method)(url, - HTTP_HOST=api_settings.BETA_HOST) + response = getattr(client, http_method)(url) assert response.status_code == 405 assert_no_cache_header(response) -def test_doc_api_404(client, api_settings, root_doc): +def test_doc_api_404(client, root_doc): """We get a 404 if we ask for a document that does not exist.""" url = reverse('api.v1.doc', args=['en-US', 'NoSuchPage']) - response = client.get(url, HTTP_HOST=api_settings.BETA_HOST) + response = client.get(url) assert response.status_code == 404 assert_no_cache_header(response) -def test_doc_api(client, api_settings, trans_doc, cleared_cacheback_cache): +def test_doc_api(client, trans_doc, cleared_cacheback_cache): """On success we get document details in a JSON response.""" url = reverse('api.v1.doc', args=[trans_doc.locale, trans_doc.slug]) - response = client.get(url, HTTP_HOST=api_settings.BETA_HOST) + response = client.get(url) assert response.status_code == 200 assert_no_cache_header(response) @@ -105,14 +104,14 @@ def test_doc_api(client, api_settings, trans_doc, cleared_cacheback_cache): assert doc_data['lastModified'] == '2017-04-14T12:20:00' -def test_doc_api_for_redirect_to_doc(client, api_settings, root_doc, - redirect_doc, cleared_cacheback_cache): +def test_doc_api_for_redirect_to_doc(client, root_doc, redirect_doc, + cleared_cacheback_cache): """ Test the document API when we're requesting data for a document that redirects to another document. """ url = reverse('api.v1.doc', args=[redirect_doc.locale, redirect_doc.slug]) - response = client.get(url, HTTP_HOST=api_settings.BETA_HOST, follow=True) + response = client.get(url, follow=True) assert response.status_code == 200 assert_no_cache_header(response) @@ -145,7 +144,7 @@ def test_doc_api_for_redirect_to_doc(client, api_settings, root_doc, @pytest.mark.parametrize('case', ('redirect-to-home', 'redirect-to-other')) -def test_doc_api_for_redirect_to_non_doc(client, api_settings, redirect_to_home, +def test_doc_api_for_redirect_to_non_doc(client, redirect_to_home, redirect_to_macros, case): """ Test the document API when we're requesting data for a document that @@ -159,7 +158,7 @@ def test_doc_api_for_redirect_to_non_doc(client, api_settings, redirect_to_home, expected_redirect_url = absolutify('/en-US/dashboards/macros', for_wiki_site=True) url = reverse('api.v1.doc', args=[doc.locale, doc.slug]) - response = client.get(url, HTTP_HOST=api_settings.BETA_HOST) + response = client.get(url) assert response.status_code == 200 assert_no_cache_header(response) @@ -174,18 +173,17 @@ def test_doc_api_for_redirect_to_non_doc(client, api_settings, redirect_to_home, @pytest.mark.parametrize( 'http_method', ['put', 'post', 'delete', 'options', 'head']) -def test_whoami_disallowed_methods(client, api_settings, http_method): +def test_whoami_disallowed_methods(client, http_method): """HTTP methods other than GET are not allowed.""" url = reverse('api.v1.whoami') - response = getattr(client, http_method)(url, - HTTP_HOST=api_settings.BETA_HOST) + response = getattr(client, http_method)(url) assert response.status_code == 405 assert_no_cache_header(response) @pytest.mark.django_db @pytest.mark.parametrize('timezone', ('US/Eastern', 'US/Pacific')) -def test_whoami_anonymous(client, api_settings, timezone): +def test_whoami_anonymous(client, settings, timezone): """Test response for anonymous users.""" # Create some fake waffle objects Flag.objects.create(name='section_edit', authenticated=True) @@ -196,9 +194,9 @@ def test_whoami_anonymous(client, api_settings, timezone): Sample.objects.create(name="sample_never", percent=0) Sample.objects.create(name="sample_always", percent=100) - api_settings.TIME_ZONE = timezone + settings.TIME_ZONE = timezone url = reverse('api.v1.whoami') - response = client.get(url, HTTP_HOST=api_settings.BETA_HOST) + response = client.get(url) assert response.status_code == 200 assert response['content-type'] == 'application/json' assert response.json() == { @@ -234,7 +232,7 @@ def test_whoami_anonymous(client, api_settings, timezone): [('US/Eastern', False, False, False), ('US/Pacific', True, True, True)], ids=('muggle', 'wizard')) -def test_whoami(user_client, api_settings, wiki_user, wiki_user_github_account, +def test_whoami(user_client, wiki_user, wiki_user_github_account, beta_testers_group, timezone, is_staff, is_superuser, is_beta_tester): """Test responses for logged-in users.""" @@ -255,7 +253,7 @@ def test_whoami(user_client, api_settings, wiki_user, wiki_user_github_account, wiki_user.groups.add(beta_testers_group) wiki_user.save() url = reverse('api.v1.whoami') - response = user_client.get(url, HTTP_HOST=api_settings.BETA_HOST) + response = user_client.get(url) assert response.status_code == 200 assert response['content-type'] == 'application/json' assert response.json() == { @@ -286,22 +284,21 @@ def test_whoami(user_client, api_settings, wiki_user, wiki_user_github_account, @pytest.mark.django_db -def test_search_validation_problems(user_client, api_settings): +def test_search_validation_problems(user_client): url = reverse('api.v1.search', args=['en-US']) # 'q' not present - response = user_client.get(url, HTTP_HOST=api_settings.BETA_HOST) + response = user_client.get(url) assert response.status_code == 400 assert response.json()['error'] == "Search term 'q' must be set" # 'q' present but falsy - response = user_client.get(url, {'q': ''}, HTTP_HOST=api_settings.BETA_HOST) + response = user_client.get(url, {'q': ''}) assert response.status_code == 400 assert response.json()['error'] == "Search term 'q' must be set" # 'q' present but locale invalid - response = user_client.get( - url, {'q': 'x', 'locale': 'xxx'}, HTTP_HOST=api_settings.BETA_HOST) + response = user_client.get(url, {'q': 'x', 'locale': 'xxx'}) assert response.status_code == 400 assert response.json()['error'] == 'Not a valid locale code' diff --git a/kuma/attachments/tests/test_templates.py b/kuma/attachments/tests/test_templates.py index 92281421839..f200a7aae3b 100644 --- a/kuma/attachments/tests/test_templates.py +++ b/kuma/attachments/tests/test_templates.py @@ -11,7 +11,7 @@ @pytest.mark.security def test_xss_file_attachment_title(admin_client, constance_config, root_doc, - wiki_user, editor_client): + wiki_user, editor_client, settings): constance_config.WIKI_ATTACHMENT_ALLOWED_TYPES = 'text/plain' # use view to create new attachment @@ -25,7 +25,8 @@ def test_xss_file_attachment_title(admin_client, constance_config, root_doc, 'comment': 'xss', 'file': file_for_upload, } - response = admin_client.post(files_url, data=post_data) + response = admin_client.post(files_url, data=post_data, + HTTP_HOST=settings.WIKI_HOST) assert response.status_code == 302 # now stick it in/on a document @@ -35,7 +36,8 @@ def test_xss_file_attachment_title(admin_client, constance_config, root_doc, document=root_doc, creator=wiki_user, content=content) # view it and verify markup is escaped - response = editor_client.get(root_doc.get_edit_url()) + response = editor_client.get(root_doc.get_edit_url(), + HTTP_HOST=settings.WIKI_HOST) assert response.status_code == 200 doc = pq(response.content) text = doc('.page-attachments-table .attachment-name-cell').text() diff --git a/kuma/attachments/tests/test_views.py b/kuma/attachments/tests/test_views.py index f1dcb99c852..108f4950c7a 100644 --- a/kuma/attachments/tests/test_views.py +++ b/kuma/attachments/tests/test_views.py @@ -3,12 +3,14 @@ import pytest from constance.test import override_config +from django.conf import settings from django.core.files.base import ContentFile from django.db import transaction from django.utils.six.moves.urllib.parse import urlparse from pyquery import PyQuery as pq -from kuma.core.tests import assert_no_cache_header, assert_shared_cache_header +from kuma.core.tests import (assert_no_cache_header, assert_redirect_to_wiki, + assert_shared_cache_header) from kuma.core.urlresolvers import reverse from kuma.core.utils import to_html from kuma.users.tests import UserTestCase @@ -41,14 +43,15 @@ def _post_attachment(self): 'comment': 'Initial upload', 'file': file_for_upload, } - response = self.client.post(self.files_url, - data=post_data) + response = self.client.post(self.files_url, data=post_data, + HTTP_HOST=settings.WIKI_HOST) return response def test_edit_attachment(self): response = self._post_attachment() assert_no_cache_header(response) - self.assertRedirects(response, self.document.get_edit_url()) + assert response.status_code == 302 + assert response['Location'] == self.document.get_edit_url() attachment = Attachment.objects.get(title='Test uploaded file') rev = attachment.current_revision @@ -115,7 +118,8 @@ def test_mime_type_filtering(self): 'comment': 'Initial upload', 'file': _file, } - response = self.client.post(self.files_url, data=post_data) + response = self.client.post(self.files_url, data=post_data, + HTTP_HOST=settings.WIKI_HOST) assert response.status_code == 200 assert_no_cache_header(response) self.assertContains(response, 'Files of this type are not permitted.') @@ -143,7 +147,8 @@ def test_intermediate(self): } files_url = reverse('attachments.edit_attachment', kwargs={'document_path': doc.slug}) - response = self.client.post(files_url, data=post_data) + response = self.client.post(files_url, data=post_data, + HTTP_HOST=settings.WIKI_HOST) assert response.status_code == 302 assert_no_cache_header(response) @@ -204,7 +209,7 @@ def test_edit_attachment_get(admin_client, root_doc): url = reverse( 'attachments.edit_attachment', kwargs={'document_path': root_doc.slug}) - response = admin_client.get(url) + response = admin_client.get(url, HTTP_HOST=settings.WIKI_HOST) assert response.status_code == 302 assert_no_cache_header(response) assert urlparse(response['Location']).path == root_doc.get_edit_url() @@ -229,7 +234,8 @@ def test_edit_attachment_post_with_vacant_file(admin_client, root_doc, tmpdir, url = reverse('attachments.edit_attachment', kwargs={'document_path': root_doc.slug}) - response = admin_client.post(url, data=post_data) + response = admin_client.post(url, data=post_data, + HTTP_HOST=settings.WIKI_HOST) assert response.status_code == 200 doc = pq(response.content) assert to_html(doc('ul.errorlist a[href="#id_file"]')) == expected @@ -275,3 +281,10 @@ def test_raw_file_if_modified_since(client, settings, file_attachment): assert response['Last-Modified'] == convert_to_http_date(created) assert 'public' in response['Cache-Control'] assert 'max-age=900' in response['Cache-Control'] + + +def test_edit_attachment_redirect(client, root_doc): + url = reverse('attachments.edit_attachment', + kwargs={'document_path': root_doc.slug}) + response = client.get(url) + assert_redirect_to_wiki(response, url) diff --git a/kuma/authkeys/tests/test_views.py b/kuma/authkeys/tests/test_views.py index 5837470a7aa..4e43b076592 100644 --- a/kuma/authkeys/tests/test_views.py +++ b/kuma/authkeys/tests/test_views.py @@ -1,9 +1,11 @@ +import pytest +from django.conf import settings from django.contrib.auth import get_user_model from django.contrib.auth.models import Permission from django.test import TestCase from pyquery import PyQuery as pq -from kuma.core.tests import assert_no_cache_header +from kuma.core.tests import assert_no_cache_header, assert_redirect_to_wiki from kuma.core.urlresolvers import reverse from kuma.users.tests import user @@ -57,7 +59,7 @@ def test_new_key(self): url = reverse('authkeys.new') # Check out the creation page, look for the form. - resp = self.client.get(url) + resp = self.client.get(url, HTTP_HOST=settings.WIKI_HOST) assert resp.status_code == 200 assert_no_cache_header(resp) page = pq(resp.content) @@ -68,7 +70,8 @@ def test_new_key(self): assert keys.count() == 0 # Okay, create it. - resp = self.client.post(url, data, follow=False) + resp = self.client.post(url, data, follow=False, + HTTP_HOST=settings.WIKI_HOST) assert resp.status_code == 200 assert_no_cache_header(resp) @@ -92,7 +95,7 @@ def test_new_key(self): def test_list_key(self): """The current user's keys should be shown, but only that user's""" url = reverse('authkeys.list') - resp = self.client.get(url) + resp = self.client.get(url, HTTP_HOST=settings.WIKI_HOST) assert resp.status_code == 200 assert_no_cache_header(resp) page = pq(resp.content) @@ -120,7 +123,7 @@ def test_key_history(self): for qs, offset in (('', 0), ('?page=2', ITEMS_PER_PAGE)): url = '%s%s' % (reverse('authkeys.history', args=(self.key1.pk,)), qs) - resp = self.client.get(url) + resp = self.client.get(url, HTTP_HOST=settings.WIKI_HOST) assert resp.status_code == 200 assert_no_cache_header(resp) page = pq(resp.content) @@ -139,23 +142,23 @@ def test_key_history(self): def test_delete_key(self): """User should be able to delete own keys, but no one else's""" url = reverse('authkeys.delete', args=(self.key3.pk,)) - resp = self.client.get(url) + resp = self.client.get(url, HTTP_HOST=settings.WIKI_HOST) assert resp.status_code == 403 assert_no_cache_header(resp) - resp = self.client.post(url, follow=False) + resp = self.client.post(url, follow=False, HTTP_HOST=settings.WIKI_HOST) assert resp.status_code == 403 assert_no_cache_header(resp) url = reverse('authkeys.delete', args=(self.key1.pk,)) - resp = self.client.get(url) + resp = self.client.get(url, HTTP_HOST=settings.WIKI_HOST) assert resp.status_code == 200 assert_no_cache_header(resp) page = pq(resp.content) assert page.find('.description').text() == self.key1.description - resp = self.client.post(url, follow=False) + resp = self.client.post(url, follow=False, HTTP_HOST=settings.WIKI_HOST) assert resp.status_code == 302 assert_no_cache_header(resp) @@ -175,7 +178,7 @@ def setUp(self): def test_new_key_requires_permission(self): url = reverse('authkeys.new') - resp = self.client.get(url) + resp = self.client.get(url, HTTP_HOST=settings.WIKI_HOST) assert resp.status_code == 403 assert_no_cache_header(resp) @@ -183,7 +186,7 @@ def test_new_key_requires_permission(self): self.user.user_permissions.add(perm) self._cache_bust_user_perms() - resp = self.client.get(url) + resp = self.client.get(url, HTTP_HOST=settings.WIKI_HOST) assert resp.status_code == 200 assert_no_cache_header(resp) @@ -192,12 +195,12 @@ def test_delete_key_requires_separate_permission(self): self.key1.save() url = reverse('authkeys.delete', args=(self.key1.pk,)) - resp = self.client.get(url) + resp = self.client.get(url, HTTP_HOST=settings.WIKI_HOST) assert resp.status_code == 403 assert_no_cache_header(resp) self._cache_bust_user_perms() - resp = self.client.get(url) + resp = self.client.get(url, HTTP_HOST=settings.WIKI_HOST) assert resp.status_code == 403 assert_no_cache_header(resp) @@ -205,6 +208,14 @@ def test_delete_key_requires_separate_permission(self): self.user.user_permissions.add(perm) self._cache_bust_user_perms() - resp = self.client.get(url) + resp = self.client.get(url, HTTP_HOST=settings.WIKI_HOST) assert resp.status_code == 200 assert_no_cache_header(resp) + + +@pytest.mark.parametrize('endpoint', ['new', 'list', 'history', 'delete']) +def test_redirect(client, endpoint): + url = reverse('authkeys.{}'.format(endpoint), + args=(1,) if endpoint in ('history', 'delete') else ()) + response = client.get(url) + assert_redirect_to_wiki(response, url) diff --git a/kuma/conftest.py b/kuma/conftest.py index b272743f8eb..00ebcec84a3 100644 --- a/kuma/conftest.py +++ b/kuma/conftest.py @@ -15,17 +15,6 @@ from kuma.wiki.models import Document, Revision -@pytest.fixture -def host_settings(settings): - settings.DOMAIN = 'mdn.dev' - settings.BETA_HOST = 'beta.mdn.dev' - settings.WIKI_HOST = 'wiki.mdn.dev' - settings.ALLOWED_HOSTS.append(settings.DOMAIN) - settings.ALLOWED_HOSTS.append(settings.BETA_HOST) - settings.ALLOWED_HOSTS.append(settings.WIKI_HOST) - return settings - - @pytest.fixture(autouse=True) def set_default_language(): activate('en-US') diff --git a/kuma/core/tests/__init__.py b/kuma/core/tests/__init__.py index f4e004b8aaf..f4988904aa5 100644 --- a/kuma/core/tests/__init__.py +++ b/kuma/core/tests/__init__.py @@ -12,6 +12,11 @@ from django.utils.translation import trans_real +def assert_redirect_to_wiki(response, url): + assert response.status_code == 301 + assert response['Location'].endswith(settings.WIKI_HOST + url) + + def assert_no_cache_header(response): assert 'max-age=0' in response['Cache-Control'] assert 'no-cache' in response['Cache-Control'] diff --git a/kuma/core/utils.py b/kuma/core/utils.py index b8550c94cf6..ab2bb99676b 100644 --- a/kuma/core/utils.py +++ b/kuma/core/utils.py @@ -43,10 +43,7 @@ def to_html(pq): def is_wiki(request): - return request.get_host() not in ( - settings.BETA_ORIGIN, - settings.BETA_HOST, - ) + return request.get_host() == settings.WIKI_HOST def redirect_to_wiki(request, permanent=True): diff --git a/kuma/dashboards/tests/test_views.py b/kuma/dashboards/tests/test_views.py index d3450eaf89d..d455a89897d 100644 --- a/kuma/dashboards/tests/test_views.py +++ b/kuma/dashboards/tests/test_views.py @@ -6,6 +6,7 @@ import mock import pytest +from django.conf import settings from django.contrib.auth.models import Group, Permission from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ImproperlyConfigured @@ -14,6 +15,7 @@ from waffle.testutils import override_switch from kuma.core.tests import (assert_no_cache_header, + assert_redirect_to_wiki, assert_shared_cache_header) from kuma.core.urlresolvers import reverse from kuma.core.utils import to_html, urlparams @@ -77,7 +79,7 @@ def known_author(wiki_user): def test_revisions_not_logged_in(root_doc, client): """A user who is not logged in can't see the revisions dashboard.""" url = reverse('dashboards.revisions') - response = client.get(url) + response = client.get(url, HTTP_HOST=settings.WIKI_HOST) assert response.status_code == 302 assert response['Location'] == '/en-US/users/signin?next={}'.format(url) assert_no_cache_header(response) @@ -85,7 +87,8 @@ def test_revisions_not_logged_in(root_doc, client): def test_revisions(root_doc, user_client): """The revision dashboard works for logged in users.""" - response = user_client.get(reverse('dashboards.revisions')) + response = user_client.get(reverse('dashboards.revisions'), + HTTP_HOST=settings.WIKI_HOST) assert response.status_code == 200 assert 'Cache-Control' in response assert_no_cache_header(response) @@ -97,6 +100,7 @@ def test_revisions(root_doc, user_client): def test_revisions_list_via_AJAX(dashboard_revisions, user_client): """The full list of revisions can be returned via AJAX.""" response = user_client.get(reverse('dashboards.revisions'), + HTTP_HOST=settings.WIKI_HOST, HTTP_X_REQUESTED_WITH='XMLHttpRequest') assert response.status_code == 200 page = pq(response.content) @@ -115,7 +119,8 @@ def test_revisions_show_ips_button(switch, is_admin, root_doc, user_client, """Toggle IPs button appears for admins when the switch is active.""" client = admin_client if is_admin else user_client with override_switch('store_revision_ips', active=switch): - response = client.get(reverse('dashboards.revisions')) + response = client.get(reverse('dashboards.revisions'), + HTTP_HOST=settings.WIKI_HOST) assert response.status_code == 200 page = pq(response.content) ip_button = page.find('button#show_ips_btn') @@ -134,7 +139,8 @@ def test_revisions_show_spam_submission_button(has_perm, root_doc, wiki_user, content_type=content_type) wiki_user.user_permissions.add(perm) - response = user_client.get(reverse('dashboards.revisions')) + response = user_client.get(reverse('dashboards.revisions'), + HTTP_HOST=settings.WIKI_HOST) assert response.status_code == 200 page = pq(response.content) spam_report_button = page.find('.spam-ham-button') @@ -145,7 +151,8 @@ def test_revisions_locale_filter(dashboard_revisions, user_client): """Revisions can be filtered by locale.""" url = urlparams(reverse('dashboards.revisions', locale='fr'), locale='fr') - response = user_client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + response = user_client.get(url, HTTP_HOST=settings.WIKI_HOST, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') assert response.status_code == 200 page = pq(response.content) @@ -158,7 +165,8 @@ def test_revisions_locale_filter(dashboard_revisions, user_client): def test_revisions_creator_filter(dashboard_revisions, user_client): """Revisions can be filtered by a username.""" url = urlparams(reverse('dashboards.revisions'), user='wiki_user_2') - response = user_client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + response = user_client.get(url, HTTP_HOST=settings.WIKI_HOST, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') assert response.status_code == 200 page = pq(response.content) @@ -173,7 +181,8 @@ def test_revisions_creator_filter(dashboard_revisions, user_client): def test_revisions_topic_filter(dashboard_revisions, user_client): """Revisions can be filtered by topic (the document slug).""" url = urlparams(reverse('dashboards.revisions'), topic='wiki_user_2-doc') - response = user_client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + response = user_client.get(url, HTTP_HOST=settings.WIKI_HOST, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') assert response.status_code == 200 page = pq(response.content) @@ -192,7 +201,8 @@ def test_revisions_known_authors_filter(authors, dashboard_revisions, user_client, known_author): """Revisions can be filtered by the Known Authors group.""" url = urlparams(reverse('dashboards.revisions'), authors=authors) - response = user_client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + response = user_client.get(url, HTTP_HOST=settings.WIKI_HOST, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') assert response.status_code == 200 page = pq(response.content) @@ -220,7 +230,8 @@ def test_revisions_creator_overrides_known_authors_filter( url = urlparams(reverse('dashboards.revisions'), user='wiki_user_3', authors=RevisionDashboardForm.KNOWN_AUTHORS) - response = user_client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + response = user_client.get(url, HTTP_HOST=settings.WIKI_HOST, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') assert response.status_code == 200 page = pq(response.content) revisions = page.find('.dashboard-row') @@ -241,7 +252,8 @@ def test_revisions_deleted_document(dashboard_revisions, user_client, reason='Testing deleted docs.') del_doc.delete() - response = user_client.get(reverse('dashboards.revisions')) + response = user_client.get(reverse('dashboards.revisions'), + HTTP_HOST=settings.WIKI_HOST) assert response.status_code == 200 page = pq(response.content) rev_rows = page.find('.dashboard-row') @@ -258,7 +270,8 @@ class SpamDashTest(SampleRevisionsMixin, UserTestCase): def test_not_logged_in(self, mock_analytics_upageviews): """A user who is not logged in is not able to see the dashboard.""" - response = self.client.get(reverse('dashboards.spam')) + response = self.client.get(reverse('dashboards.spam'), + HTTP_HOST=settings.WIKI_HOST) assert response.status_code == 302 assert_no_cache_header(response) @@ -266,25 +279,29 @@ def test_permissions(self, mock_analytics_upageviews): """A user with correct permissions is able to see the dashboard.""" self.client.login(username='testuser', password='testpass') # Attempt to see spam dashboard as a logged-in user without permissions - response = self.client.get(reverse('dashboards.spam')) + response = self.client.get(reverse('dashboards.spam'), + HTTP_HOST=settings.WIKI_HOST) assert response.status_code == 403 # Give testuser wiki.add_revisionakismetsubmission permission perm_akismet = Permission.objects.get(codename='add_revisionakismetsubmission') self.testuser.user_permissions.add(perm_akismet) - response = self.client.get(reverse('dashboards.spam')) + response = self.client.get(reverse('dashboards.spam'), + HTTP_HOST=settings.WIKI_HOST) assert response.status_code == 403 # Give testuser wiki.add_documentspamattempt permission perm_spam = Permission.objects.get(codename='add_documentspamattempt') self.testuser.user_permissions.add(perm_spam) - response = self.client.get(reverse('dashboards.spam')) + response = self.client.get(reverse('dashboards.spam'), + HTTP_HOST=settings.WIKI_HOST) assert response.status_code == 403 # Give testuser wiki.add_userban permission perm_ban = Permission.objects.get(codename='add_userban') self.testuser.user_permissions.add(perm_ban) - response = self.client.get(reverse('dashboards.spam')) + response = self.client.get(reverse('dashboards.spam'), + HTTP_HOST=settings.WIKI_HOST) # With all correct permissions testuser is able to see the dashboard assert response.status_code == 200 assert_no_cache_header(response) @@ -311,11 +328,13 @@ def test_misconfigured_google_analytics_does_not_block(self, mock_analytics_upag self.client.login(username='admin', password='testpass') # The first response will say that the report is being processed - response = self.client.get(reverse('dashboards.spam')) + response = self.client.get(reverse('dashboards.spam'), + HTTP_HOST=settings.WIKI_HOST) self.assertContains( response, "The report is being processed", status_code=200) - response2 = self.client.get(reverse('dashboards.spam')) + response2 = self.client.get(reverse('dashboards.spam'), + HTTP_HOST=settings.WIKI_HOST) self.assertContains(response2, "Oops!", status_code=200) page = pq(response2.content) @@ -355,10 +374,12 @@ def test_recent_spam_revisions_show(self, mock_analytics_upageviews): self.client.login(username='admin', password='testpass') # The first response will say that the report is being processed - response = self.client.get(reverse('dashboards.spam')) + response = self.client.get(reverse('dashboards.spam'), + HTTP_HOST=settings.WIKI_HOST) assert 200 == response.status_code - response2 = self.client.get(reverse('dashboards.spam')) + response2 = self.client.get(reverse('dashboards.spam'), + HTTP_HOST=settings.WIKI_HOST) page = pq(response2.content) table_rows = page.find('.spam-events-table tbody tr') table_row_text = '' @@ -377,10 +398,12 @@ def test_spam_trends_show(self, mock_analytics_upageviews): """The spam trends table shows up.""" self.client.login(username='admin', password='testpass') # The first response will say that the report is being processed - response = self.client.get(reverse('dashboards.spam')) + response = self.client.get(reverse('dashboards.spam'), + HTTP_HOST=settings.WIKI_HOST) assert 200 == response.status_code - response2 = self.client.get(reverse('dashboards.spam')) + response2 = self.client.get(reverse('dashboards.spam'), + HTTP_HOST=settings.WIKI_HOST) page = pq(response2.content) spam_trends_table = page.find('.spam-trends-table') assert 1 == len(spam_trends_table) @@ -480,10 +503,12 @@ def test_spam_trends_stats(self, mock_analytics_upageviews): self.client.login(username='admin', password='testpass') # The first response will say that the report is being processed - response = self.client.get(reverse('dashboards.spam')) + response = self.client.get(reverse('dashboards.spam'), + HTTP_HOST=settings.WIKI_HOST) assert 200 == response.status_code - response2 = self.client.get(reverse('dashboards.spam')) + response2 = self.client.get(reverse('dashboards.spam'), + HTTP_HOST=settings.WIKI_HOST) page = pq(response2.content) row_daily = page.find('.spam-trends-table tbody tr')[0].text_content().replace(' ', '').strip('\n').split('\n') @@ -595,7 +620,7 @@ def test_spam_trends_stats(self, mock_analytics_upageviews): def test_disallowed_methods(db, client, http_method, endpoint): """HTTP methods other than GET & HEAD are not allowed.""" url = reverse('dashboards.{}'.format(endpoint)) - response = getattr(client, http_method)(url) + response = getattr(client, http_method)(url, HTTP_HOST=settings.WIKI_HOST) assert response.status_code == 405 if endpoint in ('spam', 'revisions'): assert_no_cache_header(response) @@ -608,7 +633,7 @@ def test_disallowed_methods(db, client, http_method, endpoint): @pytest.mark.parametrize('endpoint', ['user_lookup', 'topic_lookup']) def test_lookups_require_login(root_doc, client, endpoint): - qs, headers = '', {} + qs, headers = '', {'HTTP_HOST': settings.WIKI_HOST} headers.update(HTTP_X_REQUESTED_WITH='XMLHttpRequest') if endpoint == 'topic_lookup': qs = '?topic=root' @@ -623,7 +648,7 @@ def test_lookups_require_login(root_doc, client, endpoint): @pytest.mark.parametrize('mode', ['ajax', 'non-ajax']) @pytest.mark.parametrize('endpoint', ['user_lookup', 'topic_lookup']) def test_lookup(root_doc, wiki_user_2, wiki_user_3, user_client, mode, endpoint): - qs, headers = '', {} + qs, headers = '', {'HTTP_HOST': settings.WIKI_HOST} if mode == 'ajax': if endpoint == 'topic_lookup': qs = '?topic=root' @@ -656,7 +681,8 @@ def test_macros(mock_usage, client, db): } } - response = client.get(reverse('dashboards.macros')) + response = client.get(reverse('dashboards.macros'), + HTTP_HOST=settings.WIKI_HOST) assert response.status_code == 200 assert 'Cookie' in response['Vary'] assert_shared_cache_header(response) @@ -682,7 +708,8 @@ def test_macros_no_counts(mock_usage, client, db): } } - response = client.get(reverse('dashboards.macros')) + response = client.get(reverse('dashboards.macros'), + HTTP_HOST=settings.WIKI_HOST) assert response.status_code == 200 assert "Found 2 active macros." in response.content.decode('utf8') page = pq(response.content) @@ -692,7 +719,8 @@ def test_macros_no_counts(mock_usage, client, db): def test_index(client, db): """The dashboard index can be loaded.""" - response = client.get(reverse('dashboards.index')) + response = client.get(reverse('dashboards.index'), + HTTP_HOST=settings.WIKI_HOST) assert response.status_code == 200 content = response.content.decode(response.charset) assert reverse('dashboards.macros') in content @@ -705,7 +733,8 @@ def test_index(client, db): def test_index_non_english_sees_translations(client, db): """Non-English users see the in-progress translations dashboard.""" with override('fr'): - response = client.get(reverse('dashboards.index')) + response = client.get(reverse('dashboards.index'), + HTTP_HOST=settings.WIKI_HOST) assert response.status_code == 200 content = response.content.decode(response.charset) assert reverse('dashboards.macros') in content @@ -717,7 +746,8 @@ def test_index_non_english_sees_translations(client, db): def test_index_admin_sees_spam_dashboard(admin_client): """A moderator can see the spam dashboard in the list.""" - response = admin_client.get(reverse('dashboards.index')) + response = admin_client.get(reverse('dashboards.index'), + HTTP_HOST=settings.WIKI_HOST) assert response.status_code == 200 content = response.content.decode(response.charset) assert reverse('dashboards.macros') in content @@ -725,3 +755,11 @@ def test_index_admin_sees_spam_dashboard(admin_client): l10n_url = reverse('wiki.list_with_localization_tag', kwargs={'tag': 'inprogress'}) assert l10n_url not in content + + +@pytest.mark.parametrize('endpoint', ['index', 'revisions', 'spam', 'macros']) +def test_redirect(client, endpoint): + """Redirect to the wiki domain if not already.""" + url = reverse('dashboards.{}'.format(endpoint)) + response = client.get(url) + assert_redirect_to_wiki(response, url) diff --git a/kuma/dashboards/views.py b/kuma/dashboards/views.py index c227590fcb1..9da46320493 100644 --- a/kuma/dashboards/views.py +++ b/kuma/dashboards/views.py @@ -143,6 +143,7 @@ def revisions(request): return render(request, template, context) +@ensure_wiki_domain @shared_cache_control @vary_on_headers('X-Requested-With') @require_GET @@ -164,6 +165,7 @@ def user_lookup(request): return HttpResponse(data, content_type='application/json; charset=utf-8') +@ensure_wiki_domain @shared_cache_control @vary_on_headers('X-Requested-With') @require_GET diff --git a/kuma/health/tests/test_views.py b/kuma/health/tests/test_views.py index fd2ed41641e..10758f6fc67 100644 --- a/kuma/health/tests/test_views.py +++ b/kuma/health/tests/test_views.py @@ -111,7 +111,6 @@ def test_status(client, settings, mock_status_externals): 'PROTOCOL': 'http://', 'REVISION_HASH': '3f45719d45f15da73ccc15747c28b80ccc8dfee5', 'SITE_URL': 'http://mdn.localhost:8000', - 'BETA_SITE_URL': 'http://beta.mdn.localhost:8000', 'WIKI_SITE_URL': 'http://wiki.mdn.localhost:8000', 'STATIC_URL': '/static/', } diff --git a/kuma/health/views.py b/kuma/health/views.py index 60421fcbee1..268020f3f43 100644 --- a/kuma/health/views.py +++ b/kuma/health/views.py @@ -83,7 +83,6 @@ def status(request): 'ALLOWED_HOSTS': settings.ALLOWED_HOSTS, 'ATTACHMENT_HOST': settings.ATTACHMENT_HOST, 'ATTACHMENT_ORIGIN': settings.ATTACHMENT_ORIGIN, - 'BETA_SITE_URL': settings.BETA_SITE_URL, 'DEBUG': settings.DEBUG, 'INTERACTIVE_EXAMPLES_BASE': settings.INTERACTIVE_EXAMPLES_BASE, 'MAINTENANCE_MODE': settings.MAINTENANCE_MODE, diff --git a/kuma/landing/tests/test_templates.py b/kuma/landing/tests/test_templates.py index 228e04eb3c9..eb1541c22c3 100644 --- a/kuma/landing/tests/test_templates.py +++ b/kuma/landing/tests/test_templates.py @@ -20,13 +20,14 @@ def test_google_analytics_enabled(db, settings, client): assert b"ga('create" in response.content -def test_default_search_filters(db, client): +def test_default_search_filters(db, settings, client): group = FilterGroup.objects.create(name='Topic', slug='topic') for name in ['CSS', 'HTML', 'JavaScript']: Filter.objects.create(group=group, name=name, slug=name.lower(), default=True) - response = client.get(reverse('home'), follow=True) + response = client.get(reverse('home'), follow=True, + HTTP_HOST=settings.WIKI_HOST) page = pq(response.content) filters = page.find('#home-search-form input[type=hidden]') diff --git a/kuma/landing/tests/test_views.py b/kuma/landing/tests/test_views.py index bef56da4c67..119999e86b0 100644 --- a/kuma/landing/tests/test_views.py +++ b/kuma/landing/tests/test_views.py @@ -6,7 +6,8 @@ from django.utils.six.moves.urllib.parse import urlparse from ratelimit.exceptions import Ratelimited -from kuma.core.tests import assert_no_cache_header, assert_shared_cache_header +from kuma.core.tests import (assert_no_cache_header, assert_redirect_to_wiki, + assert_shared_cache_header) from kuma.core.urlresolvers import reverse @@ -24,13 +25,13 @@ def test_contribute_json(client, db): assert response['Content-Type'].startswith('application/json') -@pytest.mark.parametrize('case', ('DOMAIN', 'BETA_HOST', 'WIKI_HOST')) -def test_home(client, db, host_settings, case): - host = getattr(host_settings, case) - response = client.get(reverse('home', locale='en-US'), HTTP_HOST=host) +@pytest.mark.parametrize('case', ('DOMAIN', 'WIKI_HOST')) +def test_home(client, db, settings, case): + response = client.get(reverse('home', locale='en-US'), + HTTP_HOST=getattr(settings, case)) assert response.status_code == 200 assert_shared_cache_header(response) - if case in ('DOMAIN', 'WIKI_HOST'): + if case == 'WIKI_HOST': expected_template = 'landing/homepage.html' else: expected_template = 'landing/react_homepage.html' @@ -52,7 +53,7 @@ def test_home_when_rate_limited(mock_render, client, db): def test_maintenance_mode(db, client, settings, mode): url = reverse('maintenance_mode') settings.MAINTENANCE_MODE = (mode == 'maintenance') - response = client.get(url) + response = client.get(url, HTTP_HOST=settings.WIKI_HOST) if settings.MAINTENANCE_MODE: assert response.status_code == 200 assert ('landing/maintenance-mode.html' in @@ -116,3 +117,12 @@ def test_favicon_ico(client, settings): assert response.status_code == 302 assert_shared_cache_header(response) assert response['Location'] == '/static/img/favicon32-local.png' + + +@pytest.mark.parametrize( + 'endpoint', ['maintenance_mode', 'promote', 'promote_buttons']) +def test_redirect(client, endpoint): + """Redirect to the wiki domain if not already.""" + url = reverse(endpoint) + response = client.get(url) + assert_redirect_to_wiki(response, url) diff --git a/kuma/payments/tests/test_views.py b/kuma/payments/tests/test_views.py index 5fec0b842ff..44c26371e51 100644 --- a/kuma/payments/tests/test_views.py +++ b/kuma/payments/tests/test_views.py @@ -3,8 +3,9 @@ import mock import pytest import stripe +from django.conf import settings -from kuma.core.tests import assert_no_cache_header +from kuma.core.tests import assert_no_cache_header, assert_redirect_to_wiki from kuma.core.urlresolvers import reverse @@ -17,18 +18,19 @@ def stripe_user(wiki_user): @pytest.mark.django_db @mock.patch('kuma.payments.views.enabled', return_value=True) -def test_payments_view(mock_enabled, client, settings): +def test_payments_view(mock_enabled, client): """The one-time payment page renders.""" - response = client.get(reverse('payments')) + response = client.get(reverse('payments'), HTTP_HOST=settings.WIKI_HOST) assert_no_cache_header(response) assert response.status_code == 200 @pytest.mark.django_db @mock.patch('kuma.payments.views.enabled', return_value=True) -def test_payment_terms_view(mock_enabled, client, settings): +def test_payment_terms_view(mock_enabled, client): """The payment terms page renders.""" - response = client.get(reverse('payment_terms')) + response = client.get(reverse('payment_terms'), + HTTP_HOST=settings.WIKI_HOST) assert_no_cache_header(response) assert response.status_code == 200 @@ -36,9 +38,11 @@ def test_payment_terms_view(mock_enabled, client, settings): @pytest.mark.django_db @mock.patch('kuma.payments.views.enabled', return_value=True) @mock.patch('kuma.payments.views.get_stripe_customer_data', return_value=True) -def test_recurring_payment_management_no_customer_id(enabled_, get, user_client): +def test_recurring_payment_management_no_customer_id(enabled_, get, + user_client): """The recurring payments page shows there are no active subscriptions.""" - response = user_client.get(reverse('recurring_payment_management')) + response = user_client.get(reverse('recurring_payment_management'), + HTTP_HOST=settings.WIKI_HOST) assert response.status_code == 200 content = response.content.decode(response.charset) assert ('