From 6ad86af56815f47a7005b9cb474905d210b258f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Gremaud?= Date: Sat, 20 Jun 2026 08:32:12 +0200 Subject: [PATCH] feat(tests): move to pytest --- highscore/scores/test_models.py | 42 +++++++++++++++++++++++++++ highscore/scores/tests.py | 38 ------------------------- pyproject.toml | 2 ++ pytest.ini | 5 ++++ uv.lock | 50 +++++++++++++++++++++++++++++++++ 5 files changed, 99 insertions(+), 38 deletions(-) create mode 100644 highscore/scores/test_models.py delete mode 100644 highscore/scores/tests.py create mode 100644 pytest.ini diff --git a/highscore/scores/test_models.py b/highscore/scores/test_models.py new file mode 100644 index 0000000..cf1c1dd --- /dev/null +++ b/highscore/scores/test_models.py @@ -0,0 +1,42 @@ +import pytest + +from scores.models import Score, Version + + +@pytest.fixture +def version(db) -> Version: + version = Version.objects.create(name="default") + other = Version.objects.create(name="other") + + Score.objects.create(username="legrems", points=120, version=version) + Score.objects.create(username="Legrems", points=119, version=version) + Score.objects.create(username="legrems", points=50, version=version) + Score.objects.create(username="alice", points=200, version=version) + # Same user, different version: must not collide across versions. + Score.objects.create(username="legrems", points=999, version=other) + + return version + + +def best(version: Version) -> list[Score]: + return list(Score.objects.filter(version=version).best_per_username().with_rank()) + + +def test_keeps_only_highest_per_case_insensitive_username(version: Version) -> None: + results = best(version) + + assert len(results) == 2 + by_name = {s.username.lower(): s for s in results} + assert by_name["legrems"].points == 120 + assert by_name["alice"].points == 200 + + +def test_ranks_recomputed_over_deduplicated_set(version: Version) -> None: + ranks = {s.username.lower(): s.rank for s in best(version)} + + assert ranks["alice"] == 1 + assert ranks["legrems"] == 2 + + +def test_lower_scores_remain_in_db(version: Version) -> None: + assert Score.objects.filter(version=version).count() == 4 diff --git a/highscore/scores/tests.py b/highscore/scores/tests.py deleted file mode 100644 index 03438e7..0000000 --- a/highscore/scores/tests.py +++ /dev/null @@ -1,38 +0,0 @@ -from django.test import TestCase - -from scores.models import Score, Version - - -class BestPerUsernameTests(TestCase): - @classmethod - def setUpTestData(cls) -> None: - cls.version = Version.objects.create(name="default") - cls.other = Version.objects.create(name="other") - - Score.objects.create(username="legrems", points=120, version=cls.version) - Score.objects.create(username="Legrems", points=119, version=cls.version) - Score.objects.create(username="legrems", points=50, version=cls.version) - Score.objects.create(username="alice", points=200, version=cls.version) - # Same user, different version: must not collide across versions. - Score.objects.create(username="legrems", points=999, version=cls.other) - - def _best(self) -> list[Score]: - return list(Score.objects.filter(version=self.version).best_per_username().with_rank()) - - def test_keeps_only_highest_per_case_insensitive_username(self) -> None: - results = self._best() - - self.assertEqual(len(results), 2) - by_name = {s.username.lower(): s for s in results} - self.assertEqual(by_name["legrems"].points, 120) - self.assertEqual(by_name["alice"].points, 200) - - def test_ranks_recomputed_over_deduplicated_set(self) -> None: - results = self._best() - ranks = {s.username.lower(): s.rank for s in results} - - self.assertEqual(ranks["alice"], 1) - self.assertEqual(ranks["legrems"], 2) - - def test_lower_scores_remain_in_db(self) -> None: - self.assertEqual(Score.objects.filter(version=self.version).count(), 4) diff --git a/pyproject.toml b/pyproject.toml index 873f3e0..e28e03a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,8 @@ dev = [ "django-extensions>=4.1", "django-stubs>=6.0.5", "ipython>=9.14.1", + "pytest>=9.1.1", + "pytest-django>=4.12.0", "ruff>=0.15.17", ] diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..3712690 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,5 @@ +[pytest] +DJANGO_SETTINGS_MODULE = app.settings +pythonpath = highscore +python_files = test_*.py tests.py +testpaths = highscore diff --git a/uv.lock b/uv.lock index 87ea97c..fe5a089 100644 --- a/uv.lock +++ b/uv.lock @@ -365,6 +365,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1d/55/0f4df2a44053867ea9cbea73fc588b03c55605cd695cee0a3d86f0029cb2/incremental-24.11.0-py3-none-any.whl", hash = "sha256:a34450716b1c4341fe6676a0598e88a39e04189f4dce5dc96f656e040baa10b3", size = 21109, upload-time = "2025-11-28T02:30:16.442Z" }, ] +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + [[package]] name = "ipython" version = "9.14.1" @@ -510,6 +519,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, ] +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + [[package]] name = "prompt-toolkit" version = "3.0.52" @@ -701,6 +719,34 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/73/b8/a0e2790ae249d6f38c9f66de7a211621a7ab2650217bcd04e1262f578a56/pyopenssl-26.2.0-py3-none-any.whl", hash = "sha256:4f9d971bc5298b8bc1fab282803da04bf000c755d4ad9d99b52de2569ca19a70", size = 55823, upload-time = "2026-05-04T23:06:08.395Z" }, ] +[[package]] +name = "pytest" +version = "9.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, +] + +[[package]] +name = "pytest-django" +version = "4.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/13/2b/db9a193df89e5660137f5428063bcc2ced7ad790003b26974adf5c5ceb3b/pytest_django-4.12.0.tar.gz", hash = "sha256:df94ec819a83c8979c8f6de13d9cdfbe76e8c21d39473cfe2b40c9fc9be3c758", size = 91156, upload-time = "2026-02-14T18:40:49.235Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/a5/41d091f697c09609e7ef1d5d61925494e0454ebf51de7de05f0f0a728f1d/pytest_django-4.12.0-py3-none-any.whl", hash = "sha256:3ff300c49f8350ba2953b90297d23bf5f589db69545f56f1ec5f8cff5da83e85", size = 26123, upload-time = "2026-02-14T18:40:47.381Z" }, +] + [[package]] name = "python-dotenv" version = "1.2.2" @@ -753,6 +799,8 @@ dev = [ { name = "django-extensions" }, { name = "django-stubs" }, { name = "ipython" }, + { name = "pytest" }, + { name = "pytest-django" }, { name = "ruff" }, ] @@ -771,6 +819,8 @@ dev = [ { name = "django-extensions", specifier = ">=4.1" }, { name = "django-stubs", specifier = ">=6.0.5" }, { name = "ipython", specifier = ">=9.14.1" }, + { name = "pytest", specifier = ">=9.1.1" }, + { name = "pytest-django", specifier = ">=4.12.0" }, { name = "ruff", specifier = ">=0.15.17" }, ]