From 7a1b9aae6aecb960a8e6c618530f766b185d16b4 Mon Sep 17 00:00:00 2001 From: krmax44 Date: Mon, 18 Nov 2024 18:06:57 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20improve=20test=20performan?= =?UTF-8?q?ce=20with=20pytest-xdist?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 2 +- Makefile | 8 +++++--- froide/foirequest/tests/test_api.py | 1 + froide/foirequest/tests/test_web.py | 8 ++++++++ froide/publicbody/tests.py | 4 ++++ froide/tests/live/test_request.py | 3 +++ pyproject.toml | 9 +++++++-- requirements-test.txt | 27 ++++++++++++--------------- requirements.txt | 10 +++------- 9 files changed, 44 insertions(+), 28 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4c08b1e59..79fc9ea77 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,7 +62,7 @@ jobs: run: pnpm run build - name: Run tests run: | - coverage run --branch -m pytest froide/ + make test coverage report --format=markdown >> $GITHUB_STEP_SUMMARY env: DATABASE_URL: postgis://postgres:postgres@localhost/froide diff --git a/Makefile b/Makefile index 4daf07d48..ba2e08866 100644 --- a/Makefile +++ b/Makefile @@ -2,10 +2,12 @@ export DJANGO_SETTINGS_MODULE=froide.settings export DJANGO_CONFIGURATION=Test export PYTHONWARNINGS=default +lint: + pre-commit run --all + test: - ruff check - coverage run --branch -m pytest froide/ - coverage report + pytest --cov froide -n auto -m "not elasticsearch" + pytest --cov froide --cov-append -m "elasticsearch" testui: coverage run --branch -m pytest --browser chromium froide/tests/live/ diff --git a/froide/foirequest/tests/test_api.py b/froide/foirequest/tests/test_api.py index 111628d6f..f74f9adde 100644 --- a/froide/foirequest/tests/test_api.py +++ b/froide/foirequest/tests/test_api.py @@ -110,6 +110,7 @@ def test_search(self): response = self.client.get("/api/v1/request/search/?q=Number") self.assertEqual(response.status_code, 200) + @pytest.mark.elasticsearch def test_search_similar(self): factories.delete_index() search_url = "/api/v1/request/search/" diff --git a/froide/foirequest/tests/test_web.py b/froide/foirequest/tests/test_web.py index 15273efe2..193eb9d17 100644 --- a/froide/foirequest/tests/test_web.py +++ b/froide/foirequest/tests/test_web.py @@ -94,6 +94,7 @@ def test_request_prefilled_redirect(world, client): @pytest.mark.django_db +@pytest.mark.elasticsearch def test_list_requests(world, client): factories.rebuild_index() response = client.get(reverse("foirequest-list")) @@ -117,6 +118,7 @@ def test_list_requests(world, client): assert response.status_code == 404 +@pytest.mark.elasticsearch @pytest.mark.django_db def test_list_jurisdiction_requests(world, client): factories.rebuild_index() @@ -151,6 +153,7 @@ def test_list_jurisdiction_requests(world, client): @pytest.mark.django_db +@pytest.mark.elasticsearch def test_tagged_requests(world, client): tag_slug = "awesome" req = FoiRequest.published.all()[0] @@ -170,6 +173,7 @@ def test_tagged_requests(world, client): @pytest.mark.django_db +@pytest.mark.elasticsearch def test_publicbody_requests(world, client): factories.rebuild_index() req = FoiRequest.published.all()[0] @@ -188,6 +192,7 @@ def test_publicbody_requests(world, client): @pytest.mark.django_db(transaction=True) +@pytest.mark.elasticsearch def test_list_no_identical(world, client): factories.FoiRequestFactory.create(site=world) factories.rebuild_index() @@ -277,6 +282,7 @@ def test_auth_links(world, client): @pytest.mark.django_db +@pytest.mark.elasticsearch def test_feed(world, client): factories.rebuild_index() @@ -673,6 +679,7 @@ def jurisdiction_with_many_requests_slug(request: pytest.FixtureRequest): @pytest.mark.django_db +@pytest.mark.elasticsearch @pytest.mark.parametrize( "filter_field,filter_value,filter_value_function", [["jurisdiction", None, jurisdiction_with_many_requests_slug], ["q", "*", None]], @@ -726,6 +733,7 @@ def dict_combinations_all_r(sequence): yield dict(parts) +@pytest.mark.elasticsearch @pytest.mark.django_db def test_request_list_path_filter( client: Client, diff --git a/froide/publicbody/tests.py b/froide/publicbody/tests.py index 7a436704c..818ae5be0 100644 --- a/froide/publicbody/tests.py +++ b/froide/publicbody/tests.py @@ -7,6 +7,8 @@ from django.test import TestCase from django.urls import reverse +import pytest + from froide.foirequest.tests.factories import make_world, rebuild_index from froide.georegion.models import GeoRegion from froide.helper.csv_utils import export_csv_bytes @@ -25,6 +27,7 @@ class PublicBodyTest(TestCase): def setUp(self): self.site = make_world() + @pytest.mark.elasticsearch def test_web_page(self): pb = PublicBody.objects.all()[0] category = CategoryFactory.create(is_topic=True) @@ -250,6 +253,7 @@ def test_search(self): response = self.client.get("/api/v1/publicbody/search/?format=json&q=Body") self.assertEqual(response.status_code, 200) + @pytest.mark.elasticsearch def test_autocomplete(self): pb = PublicBody.objects.all()[0] rebuild_index() diff --git a/froide/tests/live/test_request.py b/froide/tests/live/test_request.py index 0cc45f654..0ac709d79 100644 --- a/froide/tests/live/test_request.py +++ b/froide/tests/live/test_request.py @@ -42,6 +42,7 @@ def do_login(page, live_server, navigate=True): expect(page.locator("#navbaraccount-link")).to_have_count(1) +@pytest.mark.elasticsearch @pytest.mark.django_db def test_make_not_logged_in_request(page, live_server, public_body_with_index): pb = PublicBody.objects.all().first() @@ -120,6 +121,7 @@ def test_make_not_logged_in_request_to_public_body(page, live_server, world): assert req.status == FoiRequest.STATUS.AWAITING_USER_CONFIRMATION +@pytest.mark.elasticsearch @pytest.mark.django_db def test_make_logged_in_request(page, live_server, public_body_with_index, dummy_user): do_login(page, live_server) @@ -227,6 +229,7 @@ def test_collapsed_menu(page, live_server): @pytest.mark.django_db +@pytest.mark.elasticsearch @pytest.mark.parametrize( "from_resolution, to_resolution", [("", "successful"), ("successful", "refused")], diff --git a/pyproject.toml b/pyproject.toml index 81388401c..91e9d75ff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -80,9 +80,11 @@ test = [ "pre-commit", "pycodestyle", "pyflakes", + "pytest-cov", "pytest-django", "pytest-factoryboy", "pytest-playwright", + "pytest-xdist", "tblib", "text-unidecode", "time-machine", @@ -137,15 +139,18 @@ branch = true [tool.coverage.report] show_missing = true -skip_covered = true +skip_covered = false exclude_lines = ["pragma: no cover"] +[toolcoverage.files] +source = ["froide"] + [tool.pytest.ini_options] DJANGO_CONFIGURATION = "Test" DJANGO_SETTINGS_MODULE = "froide.settings" python_files = ["tests.py", "test_*.py", "*_tests.py"] addopts = ["--reuse-db"] -markers = ["no_delivery_mock"] +markers = ["no_delivery_mock", "elasticsearch"] [tool.mypy] plugins = ["mypy_django_plugin.main"] diff --git a/requirements-test.txt b/requirements-test.txt index 92ca8b547..55f3d27f1 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -13,8 +13,6 @@ asgiref==3.8.1 # channels # django # django-stubs -async-timeout==4.0.3 - # via aiohttp attrs==24.2.0 # via # aiohttp @@ -70,6 +68,7 @@ coverage==7.6.1 # via # froide (pyproject.toml) # django-coverage-plugin + # pytest-cov cron-descriptor==1.4.5 # via django-celery-beat cryptography==43.0.1 @@ -127,7 +126,7 @@ django-crossdomainmedia==0.0.4 # via froide (pyproject.toml) django-elasticsearch-dsl==8.0 # via froide (pyproject.toml) -django-filingcabinet @ git+https://github.com/okfde/django-filingcabinet.git@69cde01cf767ebf6447d057b1400b87c32d03803 +django-filingcabinet @ git+https://github.com/okfde/django-filingcabinet.git@3e7a8cc61b04cb77f7010d4c878112fc93af4337 # via froide (pyproject.toml) django-filter==24.3 # via @@ -191,8 +190,8 @@ elasticsearch-dsl==8.15.2 # via # froide (pyproject.toml) # django-elasticsearch-dsl -exceptiongroup==1.2.2 - # via pytest +execnet==2.1.1 + # via pytest-xdist factory-boy==3.3.1 # via # froide (pyproject.toml) @@ -312,7 +311,7 @@ prompt-toolkit==3.0.47 # via click-repl psycopg==3.2.3 # via froide (pyproject.toml) -psycopg-binary==3.2.1 +psycopg-binary==3.2.3 # via psycopg pycodestyle==2.12.1 # via froide (pyproject.toml) @@ -341,17 +340,23 @@ pyphen==0.16.0 pytest==8.3.3 # via # pytest-base-url + # pytest-cov # pytest-django # pytest-factoryboy # pytest-playwright + # pytest-xdist pytest-base-url==2.1.0 # via pytest-playwright +pytest-cov==6.0.0 + # via froide (pyproject.toml) pytest-django==4.9.0 # via froide (pyproject.toml) pytest-factoryboy==2.7.0 # via froide (pyproject.toml) pytest-playwright==0.5.2 # via froide (pyproject.toml) +pytest-xdist==3.6.1 + # via froide (pyproject.toml) python-crontab==3.2.0 # via django-celery-beat python-dateutil==2.9.0.post0 @@ -427,12 +432,6 @@ tinycss2==1.3.0 # via # cssselect2 # weasyprint -tomli==2.0.1 - # via - # coverage - # django-stubs - # mypy - # pytest types-markdown==3.7.0.20240822 # via froide (pyproject.toml) types-python-dateutil==2.9.0.20240906 @@ -443,22 +442,20 @@ types-requests==2.32.0.20240907 # via froide (pyproject.toml) typing-extensions==4.12.2 # via - # asgiref # dj-database-url # django-stubs # django-stubs-ext # elasticsearch-dsl # jwcrypto - # multidict # mypy # psycopg # pyee - # pypdf # pytest-factoryboy tzdata==2024.1 # via # celery # django-celery-beat + # kombu uritemplate==4.1.1 # via # coreapi diff --git a/requirements.txt b/requirements.txt index f8d2615f5..b4da36951 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,8 +12,6 @@ asgiref==3.8.1 # via # channels # django -async-timeout==4.0.3 - # via aiohttp attrs==24.2.0 # via # aiohttp @@ -112,7 +110,7 @@ django-crossdomainmedia==0.0.4 # via froide (pyproject.toml) django-elasticsearch-dsl==8.0 # via froide (pyproject.toml) -django-filingcabinet @ git+https://github.com/okfde/django-filingcabinet.git@69cde01cf767ebf6447d057b1400b87c32d03803 +django-filingcabinet @ git+https://github.com/okfde/django-filingcabinet.git@3e7a8cc61b04cb77f7010d4c878112fc93af4337 # via froide (pyproject.toml) django-filter==24.3 # via @@ -248,7 +246,7 @@ prompt-toolkit==3.0.47 # via click-repl psycopg==3.2.3 # via froide (pyproject.toml) -psycopg-binary==3.2.1 +psycopg-binary==3.2.3 # via psycopg pycparser==2.22 # via cffi @@ -329,17 +327,15 @@ tinycss2==1.3.0 # weasyprint typing-extensions==4.12.2 # via - # asgiref # dj-database-url # elasticsearch-dsl # jwcrypto - # multidict # psycopg - # pypdf tzdata==2024.1 # via # celery # django-celery-beat + # kombu uritemplate==4.1.1 # via # coreapi