2018-08-21 18:13:25 +03:00
|
|
|
"""
|
|
|
|
pytest hooks and fixtures used for our unittests.
|
|
|
|
|
|
|
|
Please note that there should not be any Django/Olympia related imports
|
|
|
|
on module-level, they should instead be added to hooks or fixtures directly.
|
|
|
|
"""
|
2024-03-18 14:28:28 +03:00
|
|
|
|
2018-10-15 18:30:02 +03:00
|
|
|
import os
|
2018-07-27 14:39:35 +03:00
|
|
|
import uuid
|
2015-08-26 20:33:42 +03:00
|
|
|
|
2014-11-19 19:36:40 +03:00
|
|
|
import pytest
|
2019-01-08 13:48:42 +03:00
|
|
|
import responses
|
Default to 'and' operator for match queries. Remove slug search, prioritize exact matches more. (#7303)
* Default to 'and' operator for match queries. Remove slug search, prioritize exact matches more.
References many "component: search" issues. What I tested with a
database of all public add-ons:
Example searches:
tab center redux - should find "Tab Center Redux" while "Tab Mix Plus" is probably second and "Redux DevTools" 4th or so
Open Image in New Tab -> should find "Open Image in New Tab" while "Open Bookmarks in New Tab" should be 2nd or 3rd
CoinHive -> Finds "Coinhive Blocker", "CoinBlock" (prefix search) and "NoMiners" (description)
Privacy -> Finds "Privacy Badger", "Privacy Pass", "Privacy Settings", "Google Privacy" (probably 4th or so) and "Blur" (summary + description + many users). Scores "Ghostery" on the first page but ranks it in the middle
firebu -> Finds "Firebug", "Firebug Autocompleter", "Firefinder for Firebug"
fireb -> Scores "Fire Drag" first, puts "Firebug" approximately 3rd or so
Menu Wizzard -> Finds "Menu Wizard" (fuzzy, typo) first, then "Add-ons Manager Context Menu" apparently because it matches good in the title and has many users
Frame Demolition -> Finds "Frame Demolition"
Demolition -> Finds only "Frame Demolition", same for "Demolation" (typo)
reStyle -> Finds "reStyle" and scores a few add-ons that match on "restore" next since the term is similar
MegaUpload DownloadHelper -> finds "MegaUpload DownloadHelper" first, scores "Video DownloadHelper" and "RadpidShare DownloadHelper" next. Doesn't find "Popup Blocker" anymore as currently happening on -prod
MegaUpload -> only finds "MegaUpload DownloadHelper" and nothing else
No Flash -> Scores "No Flash" first, then depending on users "Download Flash and Video", "YouTube Flash Video Player" and "YouTube Flash Player" (not necessarily in that order)
Disable Hello, Pocket & Reader+ -> finds "Disable Hello, Pocket & Reader+" first (yeay!), then scores "Reader", "Disable WebRTC" and "In My Pocket" next similarly to what's happening on -prod currently
Not working yet:
privacybadger -> "Privacy Badger" -> will probably need some kind of ngram filtering and analyzing (#591)
eyes -> 'decentraleyes' -> Not sure this should actually work, will probably need some more analyzing too (#591)
Not sure if it's specifically only because of these changes but #3248 is fixed.
This potentially fixes #7244, #6891, #6837, #6417, mozilla/addons#359.
Not sure if this fixes #mozilla/addons#567 but the results look much more promising and the amount of results doesn't explode here. I only have 2.8k add-ons for testing though so I'm not too sure.
And might be relevant to #6137.
This is a big step towards #2661, I doubt we can call this fixed though.
* Fix name tests
* Speed up ES tests, make scoring results more predictable by using only one shard and one replica
* Add tests
* Only test on new apiv3 based search.
* Fixup 'get_results'
* Remove debug print
* Isort imports
* Fix test settings, I'm blind.
* Adapt number of shards to what we define in settings_test
* Test search for grapple
* Remove property filtering in legacy api search, fix tests, fix flake8
* Add comment explaining shard config
* Fix tests again, add default platform, fix total counts again.
* Try to do some fixture cleanup, let's see...
* More test refactoring, make use of dfs-query-then-fetch during tests, allow us to test this later too via a waffle flag.
* Don't use waffle flag for legacy search, only for apiv3 searches.
* Fix unused import, fix usage of 'params'
* Use query-then-fetch in a regular addons-view tool, fix a few more tests to be able to handle the waffle-flag
* Create the dfs query then fetch flag only in ESTestCase, delete it properly. More serializer fixes
* precache the waffle flag for autocomplete tests too
* Minor cleanups
* Add docs, fix code style
* Add a todo
* Fix codestyle
2018-01-30 08:26:30 +03:00
|
|
|
|
2014-11-19 19:36:40 +03:00
|
|
|
|
2015-08-31 10:22:37 +03:00
|
|
|
@pytest.fixture(autouse=True)
|
|
|
|
def unpin_db(request):
|
|
|
|
"""Unpin the database from master in the current DB.
|
|
|
|
|
|
|
|
The `multidb` middleware pins the current thread to master for 15 seconds
|
|
|
|
after any POST request, which can lead to unexpected results for tests
|
|
|
|
of DB slave functionality."""
|
2018-08-21 18:13:25 +03:00
|
|
|
from multidb import pinning
|
2015-08-31 10:22:37 +03:00
|
|
|
|
|
|
|
request.addfinalizer(pinning.unpin_this_thread)
|
|
|
|
|
|
|
|
|
2020-05-28 21:47:07 +03:00
|
|
|
@pytest.fixture(autouse=True, scope='class')
|
2017-02-06 20:13:18 +03:00
|
|
|
def mock_elasticsearch():
|
|
|
|
"""Mock ElasticSearch in tests by default.
|
|
|
|
|
|
|
|
Tests that do need ES should inherit from ESTestCase, which will stop the
|
|
|
|
mock at setup time."""
|
2018-08-21 18:13:25 +03:00
|
|
|
from olympia.amo.tests import start_es_mocks, stop_es_mocks
|
|
|
|
|
2017-02-06 20:13:18 +03:00
|
|
|
start_es_mocks()
|
|
|
|
|
|
|
|
yield
|
|
|
|
|
|
|
|
stop_es_mocks()
|
|
|
|
|
|
|
|
|
2019-01-16 14:26:45 +03:00
|
|
|
@pytest.fixture(autouse=True)
|
|
|
|
def start_responses_mocking(request):
|
|
|
|
"""Enable ``responses`` this enforcing us to explicitly mark tests
|
|
|
|
that require internet usage.
|
|
|
|
"""
|
|
|
|
marker = request.node.get_closest_marker('allow_external_http_requests')
|
|
|
|
|
|
|
|
if not marker:
|
|
|
|
responses.start()
|
|
|
|
|
|
|
|
yield
|
|
|
|
|
|
|
|
try:
|
|
|
|
if not marker:
|
|
|
|
responses.stop()
|
|
|
|
responses.reset()
|
|
|
|
except RuntimeError:
|
|
|
|
# responses patcher was already uninstalled
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2018-05-09 18:53:35 +03:00
|
|
|
@pytest.fixture(autouse=True)
|
|
|
|
def mock_basket(settings):
|
|
|
|
"""Mock Basket in tests by default.
|
|
|
|
|
|
|
|
Tests that do need basket to work should disable `responses`
|
|
|
|
and add a passthrough.
|
|
|
|
"""
|
2023-11-02 17:35:10 +03:00
|
|
|
USER_TOKEN = '13f64f64-1de7-42f6-8c7f-a19e2fae5021'
|
2018-05-09 18:53:35 +03:00
|
|
|
responses.add(
|
|
|
|
responses.GET,
|
|
|
|
settings.BASKET_URL + '/news/lookup-user/',
|
2023-11-02 17:35:10 +03:00
|
|
|
json={'status': 'ok', 'newsletters': [], 'token': USER_TOKEN},
|
|
|
|
)
|
2018-05-09 18:53:35 +03:00
|
|
|
responses.add(
|
|
|
|
responses.POST,
|
|
|
|
settings.BASKET_URL + '/news/subscribe/',
|
2023-11-02 17:35:10 +03:00
|
|
|
json={'status': 'ok', 'token': USER_TOKEN},
|
|
|
|
)
|
2018-05-09 18:53:35 +03:00
|
|
|
responses.add(
|
|
|
|
responses.POST,
|
|
|
|
settings.BASKET_URL + '/news/unsubscribe/{}/'.format(USER_TOKEN),
|
2023-11-02 17:35:10 +03:00
|
|
|
json={'status': 'ok', 'token': USER_TOKEN},
|
|
|
|
)
|
2018-05-09 18:53:35 +03:00
|
|
|
|
|
|
|
|
2015-08-23 12:14:51 +03:00
|
|
|
def pytest_configure(config):
|
2018-08-21 18:13:25 +03:00
|
|
|
import django
|
2023-11-02 17:35:10 +03:00
|
|
|
|
2018-08-21 18:13:25 +03:00
|
|
|
# Forcefully call `django.setup`, pytest-django tries to be very lazy
|
|
|
|
# and doesn't call it if it has already been setup.
|
|
|
|
# That is problematic for us since we overwrite our logging config
|
|
|
|
# in settings_test and it can happen that django get's initialized
|
|
|
|
# with the wrong configuration. So let's forcefully re-initialize
|
|
|
|
# to setup the correct logging config since at this point
|
|
|
|
# DJANGO_SETTINGS_MODULE should be `settings_test` every time.
|
|
|
|
django.setup()
|
|
|
|
|
2017-05-30 08:30:46 +03:00
|
|
|
from olympia.amo.tests import prefix_indexes
|
2023-11-02 17:35:10 +03:00
|
|
|
|
2015-08-23 12:14:51 +03:00
|
|
|
prefix_indexes(config)
|
2015-08-26 20:33:42 +03:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(autouse=True, scope='session')
|
|
|
|
def instrument_jinja():
|
|
|
|
"""Make sure the "templates" list in a response is properly updated, even
|
|
|
|
though we're using Jinja2 and not the default django template engine."""
|
2018-08-21 18:13:25 +03:00
|
|
|
from django import test
|
|
|
|
|
2023-05-23 17:36:16 +03:00
|
|
|
import jinja2
|
|
|
|
|
2015-08-26 20:33:42 +03:00
|
|
|
old_render = jinja2.Template.render
|
|
|
|
|
|
|
|
def instrumented_render(self, *args, **kwargs):
|
|
|
|
context = dict(*args, **kwargs)
|
2023-11-02 17:35:10 +03:00
|
|
|
test.signals.template_rendered.send(sender=self, template=self, context=context)
|
2015-08-26 20:33:42 +03:00
|
|
|
return old_render(self, *args, **kwargs)
|
|
|
|
|
|
|
|
jinja2.Template.render = instrumented_render
|
|
|
|
|
|
|
|
|
2018-02-02 14:47:50 +03:00
|
|
|
def default_prefixer(settings):
|
2015-08-26 20:33:42 +03:00
|
|
|
"""Make sure each test starts with a default URL prefixer."""
|
2018-08-21 18:13:25 +03:00
|
|
|
from django import http
|
2023-05-23 17:36:16 +03:00
|
|
|
|
2018-08-21 18:13:25 +03:00
|
|
|
from olympia import amo
|
|
|
|
|
2015-08-26 20:33:42 +03:00
|
|
|
request = http.HttpRequest()
|
|
|
|
request.META['SCRIPT_NAME'] = ''
|
|
|
|
prefixer = amo.urlresolvers.Prefixer(request)
|
|
|
|
prefixer.app = settings.DEFAULT_APP
|
|
|
|
prefixer.locale = settings.LANGUAGE_CODE
|
2021-03-04 13:58:25 +03:00
|
|
|
amo.reverse.set_url_prefix(prefixer)
|
2015-08-26 20:33:42 +03:00
|
|
|
|
|
|
|
|
Upgrade Celery to 4.3 (#12073)
Upgrade Celery to 4.3.0
This PR upgrades to Celery 4.3, introduces a small hack because of a regression in celery.
This PR does not introduce any helpers to make use of celery workers inside the tests, we delayed that to a later point to unblock the upgrade.
This PR does fix some inconsistencies with elasticsearch mocks along the way as they were making some tests fail (see comment logs), the mocks are now applied and rolled back much less than before which makes them easier to reason about and to debug.
Notes:
* acks_late now doesn't acknowledge in case of failures or timeouts (which is the case for index_addons). We could use task_acks_on_failure_or_timeout but I figured the new behavior should be fine.
* any CELERY_ALWAYS_EAGER setting in local_settings.py must be replaced by CELERY_TASK_ALWAYS_EAGER
commit log:
* Implement proper wait_for_tasks helper, make TestTaskQueued work with celery worker
* Revert changes removing threading code from es reindex tests
* Fix es reindex test, correct always eager usage.
* Small cleanups, fix docs, don't reload session worker.
* Ensure docker-compose celery result doesn't use the same database as any potential tests
* Monkeypatch for celery regression
* Actually use our own app where all tasks are already bound to. This also enables logging
* Experiment: Group celery worker tests together with elasticsearch and dont run them multitasked
* Remove envlist as it doesn't add anything to our specific setup
* Fix es-and-celery marker selection
* Remove completely redundant tests
* Fix hanging tasks, let's see what travis thinks... I'll follow up with cleanups and much more docs once tests are actually succeeding
* Define celery.ping in amo.celery, use redis result backend
* Cleanup celery worker implementation a bit.
Mimic our usage of elasticsearch specific tests, this avoids various
necessary workarounds and environment cleanups because django handles
things like settings properly in the first place.
* Untangle es mocks a bit, raise error in case of not properly stopped patches.
* Re-implement state test by using celery manager to wait for results
* Explicitly re-reinitialize the workers event loop and dont reuse anything existing
* Remove celery worker thread specific code.
* Reset default celery result backend setting
2019-09-18 18:36:01 +03:00
|
|
|
@pytest.fixture(autouse=True)
|
2018-02-02 14:47:50 +03:00
|
|
|
def test_pre_setup(request, tmpdir, settings):
|
2018-08-21 18:13:25 +03:00
|
|
|
from django.core.cache import caches
|
|
|
|
from django.utils import translation
|
2023-05-23 17:36:16 +03:00
|
|
|
|
2018-08-21 18:13:25 +03:00
|
|
|
from olympia import amo, core
|
|
|
|
from olympia.translations.hold import clean_translations
|
2018-07-27 14:39:35 +03:00
|
|
|
from waffle import models as waffle_models
|
2023-05-23 17:36:16 +03:00
|
|
|
from waffle.utils import get_cache as waffle_get_cache
|
2018-07-27 14:39:35 +03:00
|
|
|
|
|
|
|
# Clear all cache-instances. They'll be re-initialized by Django
|
|
|
|
# This will make sure that our random `KEY_PREFIX` is applied
|
|
|
|
# appropriately.
|
|
|
|
# This is done by Django too whenever `settings` is changed
|
|
|
|
# directly but because we're using the `settings` fixture
|
|
|
|
# here this is not detected correctly.
|
2021-02-03 22:01:02 +03:00
|
|
|
if hasattr(caches, '_caches'):
|
|
|
|
# django 2.2
|
|
|
|
caches._caches.caches = {}
|
|
|
|
else:
|
|
|
|
# django 3.2
|
|
|
|
for cache in caches:
|
|
|
|
try:
|
|
|
|
del caches[cache]
|
|
|
|
except AttributeError:
|
|
|
|
pass
|
2018-07-27 14:39:35 +03:00
|
|
|
|
|
|
|
# Randomize the cache key prefix to keep
|
|
|
|
# tests isolated from each other.
|
|
|
|
prefix = uuid.uuid4().hex
|
|
|
|
settings.CACHES['default']['KEY_PREFIX'] = 'amo:{0}:'.format(prefix)
|
|
|
|
|
|
|
|
# Reset global django-waffle cache instance to make sure it's properly
|
|
|
|
# using our new key prefix
|
|
|
|
waffle_models.cache = waffle_get_cache()
|
2015-08-26 20:33:42 +03:00
|
|
|
|
|
|
|
translation.trans_real.deactivate()
|
|
|
|
# Django fails to clear this cache.
|
|
|
|
translation.trans_real._translations = {}
|
|
|
|
translation.trans_real.activate(settings.LANGUAGE_CODE)
|
|
|
|
|
2018-10-15 18:30:02 +03:00
|
|
|
def _path(*args):
|
|
|
|
path = str(os.path.join(*args))
|
|
|
|
if not os.path.exists(path):
|
|
|
|
os.makedirs(path)
|
|
|
|
return path
|
|
|
|
|
2019-01-16 14:09:35 +03:00
|
|
|
settings.STORAGE_ROOT = storage_root = _path(str(tmpdir.mkdir('storage')))
|
2023-11-02 17:35:10 +03:00
|
|
|
settings.SHARED_STORAGE = shared_storage = _path(storage_root, 'shared_storage')
|
2018-10-15 18:30:02 +03:00
|
|
|
|
|
|
|
settings.ADDONS_PATH = _path(storage_root, 'files')
|
|
|
|
settings.GUARDED_ADDONS_PATH = _path(storage_root, 'guarded-addons')
|
|
|
|
settings.GIT_FILE_STORAGE_PATH = _path(storage_root, 'git-storage')
|
2020-04-06 14:01:35 +03:00
|
|
|
settings.MLBF_STORAGE_PATH = _path(storage_root, 'mlbf')
|
2018-10-15 18:30:02 +03:00
|
|
|
settings.MEDIA_ROOT = _path(shared_storage, 'uploads')
|
2021-04-13 13:04:48 +03:00
|
|
|
settings.SITEMAP_STORAGE_PATH = _path(storage_root, 'sitemaps')
|
2018-10-15 18:30:02 +03:00
|
|
|
settings.TMP_PATH = _path(shared_storage, 'tmp')
|
2018-01-04 12:12:56 +03:00
|
|
|
|
2018-02-02 14:47:50 +03:00
|
|
|
# Reset the prefixer and urlconf after updating media root
|
|
|
|
default_prefixer(settings)
|
|
|
|
|
2018-09-17 10:24:14 +03:00
|
|
|
from django.urls import clear_url_caches, set_urlconf
|
2018-02-02 14:47:50 +03:00
|
|
|
|
|
|
|
def _clear_urlconf():
|
|
|
|
clear_url_caches()
|
|
|
|
set_urlconf(None)
|
|
|
|
|
|
|
|
_clear_urlconf()
|
|
|
|
|
|
|
|
request.addfinalizer(_clear_urlconf)
|
|
|
|
|
|
|
|
yield
|
2015-08-26 20:33:42 +03:00
|
|
|
|
2017-03-06 19:19:34 +03:00
|
|
|
core.set_user(None)
|
2015-08-26 20:33:42 +03:00
|
|
|
clean_translations(None) # Make sure queued translations are removed.
|
|
|
|
|
|
|
|
# Make sure we revert everything we might have changed to prefixers.
|
2021-03-04 13:58:25 +03:00
|
|
|
amo.reverse.clean_url_prefixes()
|
2015-09-10 13:32:58 +03:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def admin_group(db):
|
|
|
|
"""Create the Admins group."""
|
2018-08-21 18:13:25 +03:00
|
|
|
from olympia.access.models import Group
|
2023-11-02 17:35:10 +03:00
|
|
|
|
2015-09-10 13:32:58 +03:00
|
|
|
return Group.objects.create(name='Admins', rules='*:*')
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
2018-02-02 14:47:50 +03:00
|
|
|
def mozilla_user(admin_group, settings):
|
2015-09-10 13:32:58 +03:00
|
|
|
"""Create a "Mozilla User"."""
|
2018-08-21 18:13:25 +03:00
|
|
|
from olympia.access.models import GroupUser
|
|
|
|
from olympia.users.models import UserProfile
|
|
|
|
|
2023-11-02 17:35:10 +03:00
|
|
|
user = UserProfile.objects.create(
|
|
|
|
pk=settings.TASK_USER_ID, email='admin@mozilla.com', username='admin'
|
|
|
|
)
|
2015-09-10 13:32:58 +03:00
|
|
|
user.save()
|
|
|
|
GroupUser.objects.create(user=user, group=admin_group)
|
|
|
|
return user
|