mirror of
				https://github.com/ae-utbm/sith.git
				synced 2025-10-31 00:53:08 +00:00 
			
		
		
		
	
							
								
								
									
										6
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							| @@ -21,6 +21,10 @@ jobs: | |||||||
|   tests: |   tests: | ||||||
|     name: Run tests and generate coverage report |     name: Run tests and generate coverage report | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|  |     strategy: | ||||||
|  |       fail-fast: false  # don't interrupt the other test processes | ||||||
|  |       matrix: | ||||||
|  |         pytest-mark: [slow, not slow] | ||||||
|     steps: |     steps: | ||||||
|       - name: Check out repository |       - name: Check out repository | ||||||
|         uses: actions/checkout@v4 |         uses: actions/checkout@v4 | ||||||
| @@ -28,7 +32,7 @@ jobs: | |||||||
|       - uses: ./.github/actions/setup_xapian |       - uses: ./.github/actions/setup_xapian | ||||||
|       - uses: ./.github/actions/compile_messages |       - uses: ./.github/actions/compile_messages | ||||||
|       - name: Run tests |       - name: Run tests | ||||||
|         run: poetry run coverage run -m pytest |         run: poetry run coverage run -m pytest -m "${{ matrix.pytest-mark }}" | ||||||
|       - name: Generate coverage report |       - name: Generate coverage report | ||||||
|         run: | |         run: | | ||||||
|           poetry run coverage report |           poetry run coverage report | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ from django.utils.translation import activate | |||||||
| @pytest.fixture(scope="session") | @pytest.fixture(scope="session") | ||||||
| def django_db_setup(django_db_setup, django_db_blocker): | def django_db_setup(django_db_setup, django_db_blocker): | ||||||
|     with django_db_blocker.unblock(): |     with django_db_blocker.unblock(): | ||||||
|         call_command("setup") |         call_command("populate") | ||||||
|  |  | ||||||
|  |  | ||||||
| @pytest.fixture(scope="session", autouse=True) | @pytest.fixture(scope="session", autouse=True) | ||||||
|   | |||||||
| @@ -1098,8 +1098,10 @@ Welcome to the wiki page! | |||||||
|             n = News( |             n = News( | ||||||
|                 title="Repas barman", |                 title="Repas barman", | ||||||
|                 summary="Enjoy la fin du semestre!", |                 summary="Enjoy la fin du semestre!", | ||||||
|                 content="Viens donc t'enjailler avec les autres barmans aux " |                 content=( | ||||||
|                 "frais du BdF! \o/", |                     "Viens donc t'enjailler avec les autres barmans aux " | ||||||
|  |                     "frais du BdF! \\o/" | ||||||
|  |                 ), | ||||||
|                 type="EVENT", |                 type="EVENT", | ||||||
|                 club=bar_club, |                 club=bar_club, | ||||||
|                 author=subscriber, |                 author=subscriber, | ||||||
|   | |||||||
| @@ -19,7 +19,8 @@ import base64 | |||||||
| import os | import os | ||||||
| import random | import random | ||||||
| import string | import string | ||||||
| from datetime import date, datetime, timedelta, timezone | from datetime import date, datetime, timedelta | ||||||
|  | from datetime import timezone as tz | ||||||
| from typing import Tuple | from typing import Tuple | ||||||
|  |  | ||||||
| from dict2xml import dict2xml | from dict2xml import dict2xml | ||||||
| @@ -549,7 +550,7 @@ class Counter(models.Model): | |||||||
|         if since is None: |         if since is None: | ||||||
|             since = get_start_of_semester() |             since = get_start_of_semester() | ||||||
|         if isinstance(since, date): |         if isinstance(since, date): | ||||||
|             since = datetime(since.year, since.month, since.day, tzinfo=timezone.utc) |             since = datetime(since.year, since.month, since.day, tzinfo=tz.utc) | ||||||
|         return ( |         return ( | ||||||
|             self.sellings.filter(date__gte=since) |             self.sellings.filter(date__gte=since) | ||||||
|             .annotate( |             .annotate( | ||||||
| @@ -583,7 +584,7 @@ class Counter(models.Model): | |||||||
|         if since is None: |         if since is None: | ||||||
|             since = get_start_of_semester() |             since = get_start_of_semester() | ||||||
|         if isinstance(since, date): |         if isinstance(since, date): | ||||||
|             since = datetime(since.year, since.month, since.day, tzinfo=timezone.utc) |             since = datetime(since.year, since.month, since.day, tzinfo=tz.utc) | ||||||
|         total = self.sellings.filter(date__gte=since).aggregate( |         total = self.sellings.filter(date__gte=since).aggregate( | ||||||
|             total=Sum(F("quantity") * F("unit_price"), output_field=CurrencyField()) |             total=Sum(F("quantity") * F("unit_price"), output_field=CurrencyField()) | ||||||
|         )["total"] |         )["total"] | ||||||
|   | |||||||
| @@ -207,6 +207,29 @@ Pour lancer les tests il suffit d'utiliser la commande intégrée à django. | |||||||
|     # Lancer une méthode en particulier de cette même classe |     # Lancer une méthode en particulier de cette même classe | ||||||
|     pytest core.tests.UserRegistrationTest.test_register_user_form_ok |     pytest core.tests.UserRegistrationTest.test_register_user_form_ok | ||||||
|  |  | ||||||
|  | .. note:: | ||||||
|  |  | ||||||
|  |     Certains tests sont un peu longs à tourner. | ||||||
|  |     Pour ne faire tourner que les tests les plus rapides, | ||||||
|  |     vous pouvez exécutez pytest ainsi : | ||||||
|  |  | ||||||
|  |     .. code-block:: bash | ||||||
|  |  | ||||||
|  |         pytest -m "not slow" | ||||||
|  |  | ||||||
|  |         # vous pouvez toujours faire comme au-dessus | ||||||
|  |         pytest core -m "not slow" | ||||||
|  |  | ||||||
|  |     A l'inverse, vous pouvez ne faire tourner que les tests | ||||||
|  |     lents en remplaçant `-m "not slow"` par `-m slow`. | ||||||
|  |  | ||||||
|  |     De cette manière, votre processus de développement | ||||||
|  |     devrait être un peu plus fluide. | ||||||
|  |     Cependant, n'oubliez pas de bien faire tourner | ||||||
|  |     tous les tests avant de push un commit. | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| Vérifier les dépendances Javascript | Vérifier les dépendances Javascript | ||||||
| ----------------------------------- | ----------------------------------- | ||||||
|  |  | ||||||
|   | |||||||
| @@ -220,10 +220,11 @@ class EbouticTest(TestCase): | |||||||
|         self.client.get(reverse("eboutic:command")) |         self.client.get(reverse("eboutic:command")) | ||||||
|         response = self.client.get(et_answer_url) |         response = self.client.get(et_answer_url) | ||||||
|         assert response.status_code == 500 |         assert response.status_code == 500 | ||||||
|         assert ( |         msg = ( | ||||||
|             "Basket processing failed with error: SuspiciousOperation('Basket total and amount do not match'" |             "Basket processing failed with error: " | ||||||
|             in response.content.decode("utf-8"), |             "SuspiciousOperation('Basket total and amount do not match'" | ||||||
|         ) |         ) | ||||||
|  |         assert msg in response.content.decode("utf-8") | ||||||
|  |  | ||||||
|     def test_buy_simple_product_with_credit_card(self): |     def test_buy_simple_product_with_credit_card(self): | ||||||
|         self.client.force_login(self.subscriber) |         self.client.force_login(self.subscriber) | ||||||
|   | |||||||
| @@ -128,7 +128,7 @@ class Command(BaseCommand): | |||||||
|             self.clubs.append(Club(unix_name=f"galaxy-club-{i}", name=f"club-{i}")) |             self.clubs.append(Club(unix_name=f"galaxy-club-{i}", name=f"club-{i}")) | ||||||
|         # We don't need to create corresponding groups here, as the Galaxy doesn't care about them |         # We don't need to create corresponding groups here, as the Galaxy doesn't care about them | ||||||
|         Club.objects.bulk_create(self.clubs) |         Club.objects.bulk_create(self.clubs) | ||||||
|         self.clubs = Club.objects.filter(unix_name__startswith="galaxy-").all() |         self.clubs = list(Club.objects.filter(unix_name__startswith="galaxy-").all()) | ||||||
|  |  | ||||||
|     def make_users(self): |     def make_users(self): | ||||||
|         """ |         """ | ||||||
| @@ -147,20 +147,20 @@ class Command(BaseCommand): | |||||||
|             self.logger.info(f"Creating {u}") |             self.logger.info(f"Creating {u}") | ||||||
|             self.users.append(u) |             self.users.append(u) | ||||||
|         User.objects.bulk_create(self.users) |         User.objects.bulk_create(self.users) | ||||||
|         self.users = User.objects.filter(username__startswith="galaxy-").all() |         self.users = list(User.objects.filter(username__startswith="galaxy-").all()) | ||||||
|  |  | ||||||
|         # now that users are created, create their subscription |         # now that users are created, create their subscription | ||||||
|         subs = [] |         subs = [] | ||||||
|         for i in range(self.NB_USERS): |         end = Subscription.compute_end(duration=2) | ||||||
|             u = self.users[i] |         for i, user in enumerate(self.users): | ||||||
|             self.logger.info(f"Registering {u}") |             self.logger.info(f"Registering {user}") | ||||||
|             subs.append( |             subs.append( | ||||||
|                 Subscription( |                 Subscription( | ||||||
|                     member=u, |                     member=user, | ||||||
|                     subscription_start=Subscription.compute_start( |                     subscription_start=Subscription.compute_start( | ||||||
|                         self.now - timedelta(days=self.NB_USERS - i) |                         self.now - timedelta(days=self.NB_USERS - i) | ||||||
|                     ), |                     ), | ||||||
|                     subscription_end=Subscription.compute_end(duration=2), |                     subscription_end=end, | ||||||
|                 ) |                 ) | ||||||
|             ) |             ) | ||||||
|         Subscription.objects.bulk_create(subs) |         Subscription.objects.bulk_create(subs) | ||||||
| @@ -175,20 +175,22 @@ class Command(BaseCommand): | |||||||
|         Then it will take 14 other citizen among the previous 200 |         Then it will take 14 other citizen among the previous 200 | ||||||
|         (godfathers are usually older), and apply another |         (godfathers are usually older), and apply another | ||||||
|         heuristic to determine whether they should have a family link |         heuristic to determine whether they should have a family link | ||||||
|  |         It will result in approximately 40% of users having at least one godchild | ||||||
|  |         and 70% having at least one godfather. | ||||||
|         """ |         """ | ||||||
|         if self.users is None: |         if self.users is None: | ||||||
|             raise RuntimeError( |             raise RuntimeError( | ||||||
|                 "The `make_users()` method must be called before `make_families()`" |                 "The `make_users()` method must be called before `make_families()`" | ||||||
|             ) |             ) | ||||||
|  |         godfathers = [] | ||||||
|         for i in range(200, self.NB_USERS): |         for i in range(200, self.NB_USERS): | ||||||
|             godfathers = [] |  | ||||||
|             for j in range(i - 200, i, 14):  # this will loop 14 times (14² = 196) |             for j in range(i - 200, i, 14):  # this will loop 14 times (14² = 196) | ||||||
|                 if (i / 10) % 10 == (i + j) % 10: |                 if (i // 10) % 10 == (i + j) % 10: | ||||||
|                     u1 = self.users[i] |                     u1 = self.users[i] | ||||||
|                     u2 = self.users[j] |                     u2 = self.users[j] | ||||||
|  |                     godfathers.append(User.godfathers.through(from_user=u1, to_user=u2)) | ||||||
|                     self.logger.info(f"Making {u2} the godfather of {u1}") |                     self.logger.info(f"Making {u2} the godfather of {u1}") | ||||||
|                     godfathers.append(u2) |         User.godfathers.through.objects.bulk_create(godfathers) | ||||||
|                 u1.godfathers.set(godfathers) |  | ||||||
|  |  | ||||||
|     def make_club_memberships(self): |     def make_club_memberships(self): | ||||||
|         """ |         """ | ||||||
| @@ -295,7 +297,7 @@ class Command(BaseCommand): | |||||||
|             self.picts[i].compressed.name = self.picts[i].name |             self.picts[i].compressed.name = self.picts[i].name | ||||||
|             self.picts[i].thumbnail.name = self.picts[i].name |             self.picts[i].thumbnail.name = self.picts[i].name | ||||||
|         Picture.objects.bulk_create(self.picts) |         Picture.objects.bulk_create(self.picts) | ||||||
|         self.picts = Picture.objects.filter(name__startswith="galaxy-").all() |         self.picts = list(Picture.objects.filter(name__startswith="galaxy-").all()) | ||||||
|  |  | ||||||
|     def make_pictures_memberships(self): |     def make_pictures_memberships(self): | ||||||
|         """ |         """ | ||||||
| @@ -377,32 +379,24 @@ class Command(BaseCommand): | |||||||
|         u1 = self.users[uid] |         u1 = self.users[uid] | ||||||
|         u2 = self.users[uid - 100] |         u2 = self.users[uid - 100] | ||||||
|         u3 = self.users[uid + 100] |         u3 = self.users[uid + 100] | ||||||
|         u1.godfathers.add(u2) |         User.godfathers.through.objects.bulk_create( | ||||||
|         u1.godchildren.add(u3) |             [ | ||||||
|  |                 User.godfathers.through(from_user=u1, to_user=u2), | ||||||
|  |                 User.godfathers.through(from_user=u3, to_user=u2), | ||||||
|  |             ], | ||||||
|  |             ignore_conflicts=True,  # in case a relationship has already been created | ||||||
|  |         ) | ||||||
|         self.logger.info(f"{u1} will be important and close to {u2} and {u3}") |         self.logger.info(f"{u1} will be important and close to {u2} and {u3}") | ||||||
|         pictures_tags = [] |         pictures_tags = [] | ||||||
|         for p in range(  # Mix them with other citizen for more chaos |         for p in range(uid - 400, uid - 200): | ||||||
|             uid - 400, uid - 200 |             # Mix them with other citizen for more chaos | ||||||
|         ): |             pictures_tags += [ | ||||||
|             # users may already be on the pictures |                 PeoplePictureRelation(user=u1, picture=self.picts[p]), | ||||||
|             if not self.picts[p].people.filter(user=u1).exists(): |                 PeoplePictureRelation(user=u2, picture=self.picts[p]), | ||||||
|                 pictures_tags.append( |                 PeoplePictureRelation(user=u1, picture=self.picts[p + self.NB_USERS]), | ||||||
|                     PeoplePictureRelation(user=u1, picture=self.picts[p]) |                 PeoplePictureRelation(user=u2, picture=self.picts[p + self.NB_USERS]), | ||||||
|                 ) |             ] | ||||||
|             if not self.picts[p].people.filter(user=u2).exists(): |         # users may already be on the pictures. | ||||||
|                 pictures_tags.append( |         # In this case the conflict will just be ignored | ||||||
|                     PeoplePictureRelation(user=u2, picture=self.picts[p]) |         # and nothing will happen for this entry | ||||||
|                 ) |         PeoplePictureRelation.objects.bulk_create(pictures_tags, ignore_conflicts=True) | ||||||
|             if not self.picts[p + self.NB_USERS].people.filter(user=u1).exists(): |  | ||||||
|                 pictures_tags.append( |  | ||||||
|                     PeoplePictureRelation( |  | ||||||
|                         user=u1, picture=self.picts[p + self.NB_USERS] |  | ||||||
|                     ) |  | ||||||
|                 ) |  | ||||||
|             if not self.picts[p + self.NB_USERS].people.filter(user=u2).exists(): |  | ||||||
|                 pictures_tags.append( |  | ||||||
|                     PeoplePictureRelation( |  | ||||||
|                         user=u2, picture=self.picts[p + self.NB_USERS] |  | ||||||
|                     ) |  | ||||||
|                 ) |  | ||||||
|         PeoplePictureRelation.objects.bulk_create(pictures_tags) |  | ||||||
|   | |||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| @@ -25,6 +25,7 @@ | |||||||
| import json | import json | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
|  |  | ||||||
|  | import pytest | ||||||
| from django.core.management import call_command | from django.core.management import call_command | ||||||
| from django.test import TestCase | from django.test import TestCase | ||||||
| from django.urls import reverse | from django.urls import reverse | ||||||
| @@ -147,6 +148,7 @@ class GalaxyTestModel(TestCase): | |||||||
|             galaxy.rule(0)  # We want everybody here |             galaxy.rule(0)  # We want everybody here | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @pytest.mark.slow | ||||||
| class GalaxyTestView(TestCase): | class GalaxyTestView(TestCase): | ||||||
|     @classmethod |     @classmethod | ||||||
|     def setUpTestData(cls): |     def setUpTestData(cls): | ||||||
| @@ -196,6 +198,4 @@ class GalaxyTestView(TestCase): | |||||||
|         # Dump computed state, either for easier debugging, or to copy as new reference if changes are legit |         # Dump computed state, either for easier debugging, or to copy as new reference if changes are legit | ||||||
|         (galaxy_dir / "test_galaxy_state.json").write_text(json.dumps(state)) |         (galaxy_dir / "test_galaxy_state.json").write_text(json.dumps(state)) | ||||||
|  |  | ||||||
|         assert ( |         assert state == json.loads((galaxy_dir / "ref_galaxy_state.json").read_text()) | ||||||
|             state == json.loads((galaxy_dir / "ref_galaxy_state.json").read_text()), |  | ||||||
|         ) |  | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							| @@ -573,17 +573,17 @@ test = ["testfixtures"] | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "djangorestframework" | name = "djangorestframework" | ||||||
| version = "3.15.1" | version = "3.15.2" | ||||||
| description = "Web APIs for Django, made easy." | description = "Web APIs for Django, made easy." | ||||||
| optional = false | optional = false | ||||||
| python-versions = ">=3.6" | python-versions = ">=3.8" | ||||||
| files = [ | files = [ | ||||||
|     {file = "djangorestframework-3.15.1-py3-none-any.whl", hash = "sha256:3ccc0475bce968608cf30d07fb17d8e52d1d7fc8bfe779c905463200750cbca6"}, |     {file = "djangorestframework-3.15.2-py3-none-any.whl", hash = "sha256:2b8871b062ba1aefc2de01f773875441a961fefbf79f5eed1e32b2f096944b20"}, | ||||||
|     {file = "djangorestframework-3.15.1.tar.gz", hash = "sha256:f88fad74183dfc7144b2756d0d2ac716ea5b4c7c9840995ac3bfd8ec034333c1"}, |     {file = "djangorestframework-3.15.2.tar.gz", hash = "sha256:36fe88cd2d6c6bec23dca9804bab2ba5517a8bb9d8f47ebc68981b56840107ad"}, | ||||||
| ] | ] | ||||||
|  |  | ||||||
| [package.dependencies] | [package.dependencies] | ||||||
| django = ">=3.0" | django = ">=4.2" | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "docutils" | name = "docutils" | ||||||
|   | |||||||
| @@ -76,6 +76,7 @@ select = ["I", "F401"] | |||||||
| [tool.pytest.ini_options] | [tool.pytest.ini_options] | ||||||
| DJANGO_SETTINGS_MODULE = "sith.settings" | DJANGO_SETTINGS_MODULE = "sith.settings" | ||||||
| python_files = ["tests.py", "test_*.py", "*_tests.py"] | python_files = ["tests.py", "test_*.py", "*_tests.py"] | ||||||
|  | markers = ["slow"] | ||||||
|  |  | ||||||
| [build-system] | [build-system] | ||||||
| requires = ["poetry-core>=1.0.0"] | requires = ["poetry-core>=1.0.0"] | ||||||
|   | |||||||
| @@ -72,7 +72,7 @@ class MergeUserTest(TestCase): | |||||||
|         assert "B'ian" == self.to_keep.nick_name |         assert "B'ian" == self.to_keep.nick_name | ||||||
|         assert "Jerusalem" == self.to_keep.address |         assert "Jerusalem" == self.to_keep.address | ||||||
|         assert "Rome" == self.to_keep.parent_address |         assert "Rome" == self.to_keep.parent_address | ||||||
|         assert (3, self.to_keep.groups.count()) |         assert self.to_keep.groups.count() == 3 | ||||||
|         groups = sorted(self.to_keep.groups.all(), key=lambda i: i.id) |         groups = sorted(self.to_keep.groups.all(), key=lambda i: i.id) | ||||||
|         expected = sorted([subscribers, mde_admin, sas_admin], key=lambda i: i.id) |         expected = sorted([subscribers, mde_admin, sas_admin], key=lambda i: i.id) | ||||||
|         assert groups == expected |         assert groups == expected | ||||||
|   | |||||||
| @@ -696,6 +696,17 @@ if DEBUG: | |||||||
|  |  | ||||||
| if TESTING: | if TESTING: | ||||||
|     CAPTCHA_TEST_MODE = True |     CAPTCHA_TEST_MODE = True | ||||||
|  |     PASSWORD_HASHERS = [  # not secure, but faster password hasher | ||||||
|  |         "django.contrib.auth.hashers.MD5PasswordHasher", | ||||||
|  |     ] | ||||||
|  |     STORAGES = {  # store files in memory rather than using the hard drive | ||||||
|  |         "default": { | ||||||
|  |             "BACKEND": "django.core.files.storage.InMemoryStorage", | ||||||
|  |         }, | ||||||
|  |         "staticfiles": { | ||||||
|  |             "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", | ||||||
|  |         }, | ||||||
|  |     } | ||||||
|  |  | ||||||
| if SENTRY_DSN: | if SENTRY_DSN: | ||||||
|     # Connection to sentry |     # Connection to sentry | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user