mirror of
				https://github.com/ae-utbm/sith.git
				synced 2025-10-31 17:13:08 +00:00 
			
		
		
		
	add tests
This commit is contained in:
		
							
								
								
									
										32
									
								
								core/baker_recipes.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								core/baker_recipes.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | from datetime import timedelta | ||||||
|  |  | ||||||
|  | from django.utils.timezone import now | ||||||
|  | from model_bakery import seq | ||||||
|  | from model_bakery.recipe import Recipe, related | ||||||
|  |  | ||||||
|  | from core.models import User | ||||||
|  | from subscription.models import Subscription | ||||||
|  |  | ||||||
|  | active_subscription = Recipe( | ||||||
|  |     Subscription, | ||||||
|  |     subscription_start=now() - timedelta(days=30), | ||||||
|  |     subscription_end=now() + timedelta(days=30), | ||||||
|  | ) | ||||||
|  | ended_subscription = Recipe( | ||||||
|  |     Subscription, | ||||||
|  |     subscription_start=now() - timedelta(days=60), | ||||||
|  |     subscription_end=now() - timedelta(days=30), | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | subscriber_user = Recipe( | ||||||
|  |     User, | ||||||
|  |     first_name="subscriber", | ||||||
|  |     last_name=seq("user "), | ||||||
|  |     subscriptions=related(active_subscription), | ||||||
|  | ) | ||||||
|  | old_subscriber_user = Recipe( | ||||||
|  |     User, | ||||||
|  |     first_name="old subscriber", | ||||||
|  |     last_name=seq("user "), | ||||||
|  |     subscriptions=related(ended_subscription), | ||||||
|  | ) | ||||||
| @@ -13,7 +13,7 @@ from pedagogy.schemas import SimpleUvSchema, UvFilterSchema, UvSchema | |||||||
| from pedagogy.utbm_api import find_uv | from pedagogy.utbm_api import find_uv | ||||||
|  |  | ||||||
|  |  | ||||||
| @api_controller("/uv", permissions=[IsSubscriber]) | @api_controller("/uv") | ||||||
| class UvController(ControllerBase): | class UvController(ControllerBase): | ||||||
|     @route.get( |     @route.get( | ||||||
|         "/{year}/{code}", |         "/{year}/{code}", | ||||||
| @@ -31,7 +31,10 @@ class UvController(ControllerBase): | |||||||
|         return res |         return res | ||||||
|  |  | ||||||
|     @route.get( |     @route.get( | ||||||
|         "", response=PaginatedResponseSchema[SimpleUvSchema], url_name="fetch_uvs" |         "", | ||||||
|  |         response=PaginatedResponseSchema[SimpleUvSchema], | ||||||
|  |         url_name="fetch_uvs", | ||||||
|  |         permissions=[IsSubscriber | IsInGroup(settings.SITH_GROUP_PEDAGOGY_ADMIN_ID)], | ||||||
|     ) |     ) | ||||||
|     @paginate(PageNumberPaginationExtra, page_size=100) |     @paginate(PageNumberPaginationExtra, page_size=100) | ||||||
|     def fetch_uv_list(self, search: Query[UvFilterSchema]): |     def fetch_uv_list(self, search: Query[UvFilterSchema]): | ||||||
|   | |||||||
| @@ -123,7 +123,7 @@ class UvFilterSchema(FilterSchema): | |||||||
|     def filter_semester(self, value: set[str] | None) -> Q: |     def filter_semester(self, value: set[str] | None) -> Q: | ||||||
|         """Special filter for the semester. |         """Special filter for the semester. | ||||||
|  |  | ||||||
|         If both "SPRING" and "AUTUMN" are given, UV that are available |         If either "SPRING" or "AUTUMN" is given, UV that are available | ||||||
|         during "AUTUMN_AND_SPRING" will be filtered. |         during "AUTUMN_AND_SPRING" will be filtered. | ||||||
|         """ |         """ | ||||||
|         if not value: |         if not value: | ||||||
|   | |||||||
| @@ -125,8 +125,6 @@ | |||||||
|  |  | ||||||
|     function update_query_string(key, value) { |     function update_query_string(key, value) { | ||||||
|         const url = new URL(window.location.href); |         const url = new URL(window.location.href); | ||||||
|         console.log(value) |  | ||||||
|         console.log(!!value) |  | ||||||
|         if (!value) { |         if (!value) { | ||||||
|             url.searchParams.delete(key) |             url.searchParams.delete(key) | ||||||
|         } else if (Array.isArray(value)) { |         } else if (Array.isArray(value)) { | ||||||
|   | |||||||
							
								
								
									
										0
									
								
								pedagogy/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								pedagogy/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										165
									
								
								pedagogy/tests/test_api.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								pedagogy/tests/test_api.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,165 @@ | |||||||
|  | import json | ||||||
|  |  | ||||||
|  | from django.conf import settings | ||||||
|  | from django.test import TestCase | ||||||
|  | from django.urls import reverse | ||||||
|  | from model_bakery import baker | ||||||
|  | from model_bakery.recipe import Recipe | ||||||
|  |  | ||||||
|  | from core.baker_recipes import subscriber_user | ||||||
|  | from core.models import RealGroup, User | ||||||
|  | from pedagogy.models import UV | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class UVSearchTest(TestCase): | ||||||
|  |     """Test UV guide rights for view and API.""" | ||||||
|  |  | ||||||
|  |     @classmethod | ||||||
|  |     def setUpTestData(cls): | ||||||
|  |         cls.root = User.objects.get(username="root") | ||||||
|  |         cls.url = reverse("api:fetch_uvs") | ||||||
|  |         uv_recipe = Recipe(UV, author=cls.root) | ||||||
|  |         uvs = [ | ||||||
|  |             uv_recipe.prepare( | ||||||
|  |                 code="AP4A", credit_type="CS", semester="AUTUMN", department="GI" | ||||||
|  |             ), | ||||||
|  |             uv_recipe.prepare( | ||||||
|  |                 code="MT01", credit_type="CS", semester="AUTUMN", department="TC" | ||||||
|  |             ), | ||||||
|  |             uv_recipe.prepare( | ||||||
|  |                 code="PHYS11", credit_type="CS", semester="AUTUMN", department="TC" | ||||||
|  |             ), | ||||||
|  |             uv_recipe.prepare( | ||||||
|  |                 code="TNEV", credit_type="TM", semester="SPRING", department="TC" | ||||||
|  |             ), | ||||||
|  |             uv_recipe.prepare( | ||||||
|  |                 code="MT10", credit_type="TM", semester="AUTUMN", department="IMSI" | ||||||
|  |             ), | ||||||
|  |             uv_recipe.prepare( | ||||||
|  |                 code="DA50", | ||||||
|  |                 credit_type="TM", | ||||||
|  |                 semester="AUTUMN_AND_SPRING", | ||||||
|  |                 department="GI", | ||||||
|  |             ), | ||||||
|  |         ] | ||||||
|  |         UV.objects.bulk_create(uvs) | ||||||
|  |  | ||||||
|  |     def test_permissions(self): | ||||||
|  |         # Test with anonymous user | ||||||
|  |         response = self.client.get(self.url) | ||||||
|  |         assert response.status_code == 403 | ||||||
|  |  | ||||||
|  |         # Test with not subscribed user | ||||||
|  |         self.client.force_login(baker.make(User)) | ||||||
|  |         response = self.client.get(self.url) | ||||||
|  |         assert response.status_code == 403 | ||||||
|  |  | ||||||
|  |         for user in ( | ||||||
|  |             self.root, | ||||||
|  |             subscriber_user.make(), | ||||||
|  |             baker.make( | ||||||
|  |                 User, | ||||||
|  |                 groups=[ | ||||||
|  |                     RealGroup.objects.get(pk=settings.SITH_GROUP_PEDAGOGY_ADMIN_ID) | ||||||
|  |                 ], | ||||||
|  |             ), | ||||||
|  |         ): | ||||||
|  |             # users that have right | ||||||
|  |             with self.subTest(): | ||||||
|  |                 self.client.force_login(user) | ||||||
|  |                 response = self.client.get(self.url) | ||||||
|  |                 assert response.status_code == 200 | ||||||
|  |  | ||||||
|  |     def test_format(self): | ||||||
|  |         """Test that the return data format is correct""" | ||||||
|  |         self.client.force_login(self.root) | ||||||
|  |         res = self.client.get(self.url + "?search=PA00") | ||||||
|  |         uv = UV.objects.get(code="PA00") | ||||||
|  |         assert res.status_code == 200 | ||||||
|  |         assert json.loads(res.content) == { | ||||||
|  |             "count": 1, | ||||||
|  |             "next": None, | ||||||
|  |             "previous": None, | ||||||
|  |             "results": [ | ||||||
|  |                 { | ||||||
|  |                     "id": uv.id, | ||||||
|  |                     "title": uv.title, | ||||||
|  |                     "code": uv.code, | ||||||
|  |                     "credit_type": uv.credit_type, | ||||||
|  |                     "semester": uv.semester, | ||||||
|  |                     "department": uv.department, | ||||||
|  |                 } | ||||||
|  |             ], | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     def test_search_by_code(self): | ||||||
|  |         self.client.force_login(self.root) | ||||||
|  |         res = self.client.get(self.url + "?search=MT") | ||||||
|  |         assert res.status_code == 200 | ||||||
|  |         assert {uv["code"] for uv in json.loads(res.content)["results"]} == { | ||||||
|  |             "MT01", | ||||||
|  |             "MT10", | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     def test_search_by_credit_type(self): | ||||||
|  |         self.client.force_login(self.root) | ||||||
|  |         res = self.client.get(self.url + "?credit_type=CS") | ||||||
|  |         assert res.status_code == 200 | ||||||
|  |         codes = [uv["code"] for uv in json.loads(res.content)["results"]] | ||||||
|  |         assert codes == ["AP4A", "MT01", "PHYS11"] | ||||||
|  |         res = self.client.get(self.url + "?credit_type=CS&credit_type=OM") | ||||||
|  |         assert res.status_code == 200 | ||||||
|  |         codes = {uv["code"] for uv in json.loads(res.content)["results"]} | ||||||
|  |         assert codes == {"AP4A", "MT01", "PHYS11", "PA00"} | ||||||
|  |  | ||||||
|  |     def test_search_by_semester(self): | ||||||
|  |         self.client.force_login(self.root) | ||||||
|  |         res = self.client.get(self.url + "?semester=SPRING") | ||||||
|  |         assert res.status_code == 200 | ||||||
|  |         codes = {uv["code"] for uv in json.loads(res.content)["results"]} | ||||||
|  |         assert codes == {"DA50", "TNEV", "PA00"} | ||||||
|  |  | ||||||
|  |     def test_search_multiple_filters(self): | ||||||
|  |         self.client.force_login(self.root) | ||||||
|  |         res = self.client.get( | ||||||
|  |             self.url + "?semester=AUTUMN&credit_type=CS&department=TC" | ||||||
|  |         ) | ||||||
|  |         assert res.status_code == 200 | ||||||
|  |         codes = {uv["code"] for uv in json.loads(res.content)["results"]} | ||||||
|  |         assert codes == {"MT01", "PHYS11"} | ||||||
|  |  | ||||||
|  |     def test_search_fails(self): | ||||||
|  |         self.client.force_login(self.root) | ||||||
|  |         res = self.client.get(self.url + "?credit_type=CS&search=DA") | ||||||
|  |         assert res.status_code == 200 | ||||||
|  |         assert json.loads(res.content)["results"] == [] | ||||||
|  |  | ||||||
|  |     def test_search_pa00_fail(self): | ||||||
|  |         self.client.force_login(self.root) | ||||||
|  |         # Search with UV code | ||||||
|  |         response = self.client.get(reverse("pedagogy:guide"), {"search": "IFC"}) | ||||||
|  |         self.assertNotContains(response, text="PA00") | ||||||
|  |  | ||||||
|  |         # Search with first letter of UV code | ||||||
|  |         response = self.client.get(reverse("pedagogy:guide"), {"search": "I"}) | ||||||
|  |         self.assertNotContains(response, text="PA00") | ||||||
|  |  | ||||||
|  |         # Search with UV manager | ||||||
|  |         response = self.client.get(reverse("pedagogy:guide"), {"search": "GILLES"}) | ||||||
|  |         self.assertNotContains(response, text="PA00") | ||||||
|  |  | ||||||
|  |         # Search with department | ||||||
|  |         response = self.client.get(reverse("pedagogy:guide"), {"department": "TC"}) | ||||||
|  |         self.assertNotContains(response, text="PA00") | ||||||
|  |  | ||||||
|  |         # Search with semester | ||||||
|  |         response = self.client.get(reverse("pedagogy:guide"), {"semester": "CLOSED"}) | ||||||
|  |         self.assertNotContains(response, text="PA00") | ||||||
|  |  | ||||||
|  |         # Search with language | ||||||
|  |         response = self.client.get(reverse("pedagogy:guide"), {"language": "EN"}) | ||||||
|  |         self.assertNotContains(response, text="PA00") | ||||||
|  |  | ||||||
|  |         # Search with credit type | ||||||
|  |         response = self.client.get(reverse("pedagogy:guide"), {"credit_type": "TM"}) | ||||||
|  |         self.assertNotContains(response, text="PA00") | ||||||
| @@ -20,7 +20,6 @@ | |||||||
| # Place - Suite 330, Boston, MA 02111-1307, USA. | # Place - Suite 330, Boston, MA 02111-1307, USA. | ||||||
| # | # | ||||||
| # | # | ||||||
| import json |  | ||||||
| 
 | 
 | ||||||
| import pytest | import pytest | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| @@ -555,157 +554,6 @@ class UVCommentUpdateTest(TestCase): | |||||||
|         self.assertEqual(self.comment.author, self.krophil) |         self.assertEqual(self.comment.author, self.krophil) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class UVSearchTest(TestCase): |  | ||||||
|     """Test UV guide rights for view and API.""" |  | ||||||
| 
 |  | ||||||
|     @classmethod |  | ||||||
|     def setUpTestData(cls): |  | ||||||
|         cls.bibou = User.objects.get(username="root") |  | ||||||
|         cls.tutu = User.objects.get(username="tutu") |  | ||||||
|         cls.sli = User.objects.get(username="sli") |  | ||||||
|         cls.guy = User.objects.get(username="guy") |  | ||||||
|         cls.url = reverse("api:fetch_uvs") |  | ||||||
|         uvs = [ |  | ||||||
|             UV(code="AP4A", credit_type="CS", semester="AUTUMN", department="GI"), |  | ||||||
|             UV(code="MT01", credit_type="CS", semester="AUTUMN", department="TC"), |  | ||||||
|             UV(code="PHYS11", credit_type="CS", semester="AUTUMN", department="TC"), |  | ||||||
|             UV(code="TNEV", credit_type="TM", semester="SPRING", department="TC"), |  | ||||||
|             UV(code="MT10", credit_type="TM", semester="AUTUMN", department="IMSI"), |  | ||||||
|             UV( |  | ||||||
|                 code="DA50", |  | ||||||
|                 credit_type="TM", |  | ||||||
|                 semester="AUTUMN_AND_SPRING", |  | ||||||
|                 department="GI", |  | ||||||
|             ), |  | ||||||
|         ] |  | ||||||
|         for uv in uvs: |  | ||||||
|             uv.author = cls.bibou |  | ||||||
|             uv.title = "" |  | ||||||
|             uv.manager = "" |  | ||||||
|             uv.language = "FR" |  | ||||||
|             uv.objectives = "" |  | ||||||
|             uv.program = "" |  | ||||||
|             uv.skills = "" |  | ||||||
|             uv.key_concepts = "" |  | ||||||
|             uv.credits = 6 |  | ||||||
|         UV.objects.bulk_create(uvs) |  | ||||||
| 
 |  | ||||||
|     def fetch_uvs(self, **kwargs): |  | ||||||
|         params = "&".join(f"{key}={val}" for key, val in kwargs.items()) |  | ||||||
|         return json.loads(f"{self.url}?{params}") |  | ||||||
| 
 |  | ||||||
|     def test_permissions(self): |  | ||||||
|         # Test with anonymous user |  | ||||||
|         response = self.client.get(self.url) |  | ||||||
|         assert response.status_code == 403 |  | ||||||
| 
 |  | ||||||
|         # Test with not subscribed user |  | ||||||
|         self.client.force_login(self.guy) |  | ||||||
|         response = self.client.get(self.url) |  | ||||||
|         assert response.status_code == 403 |  | ||||||
| 
 |  | ||||||
|         for user in self.bibou, self.tutu, self.sli: |  | ||||||
|             # users that have right |  | ||||||
|             with self.subTest(): |  | ||||||
|                 self.client.force_login(user) |  | ||||||
|                 response = self.client.get(self.url) |  | ||||||
|                 assert response.status_code == 200 |  | ||||||
| 
 |  | ||||||
|     def test_format(self): |  | ||||||
|         """Test that the return data format is correct""" |  | ||||||
|         self.client.force_login(self.bibou) |  | ||||||
|         res = self.client.get(self.url + "?search=PA00") |  | ||||||
|         uv = UV.objects.get(code="PA00") |  | ||||||
|         assert res.status_code == 200 |  | ||||||
|         assert json.loads(res.content) == { |  | ||||||
|             "count": 1, |  | ||||||
|             "next": None, |  | ||||||
|             "previous": None, |  | ||||||
|             "results": [ |  | ||||||
|                 { |  | ||||||
|                     "id": uv.id, |  | ||||||
|                     "title": uv.title, |  | ||||||
|                     "code": uv.code, |  | ||||||
|                     "credit_type": uv.credit_type, |  | ||||||
|                     "semester": uv.semester, |  | ||||||
|                     "department": uv.department, |  | ||||||
|                 } |  | ||||||
|             ], |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     def test_search_by_code(self): |  | ||||||
|         self.client.force_login(self.bibou) |  | ||||||
|         res = self.client.get(self.url + "?search=MT") |  | ||||||
|         assert res.status_code == 200 |  | ||||||
|         assert {uv["code"] for uv in json.loads(res.content)["results"]} == { |  | ||||||
|             "MT01", |  | ||||||
|             "MT10", |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     def test_search_by_credit_type(self): |  | ||||||
|         self.client.force_login(self.bibou) |  | ||||||
|         res = self.client.get(self.url + "?credit_type=CS") |  | ||||||
|         assert res.status_code == 200 |  | ||||||
|         codes = [uv["code"] for uv in json.loads(res.content)["results"]] |  | ||||||
|         assert codes == ["AP4A", "MT01", "PHYS11"] |  | ||||||
|         res = self.client.get(self.url + "?credit_type=CS&credit_type=OM") |  | ||||||
|         assert res.status_code == 200 |  | ||||||
|         codes = {uv["code"] for uv in json.loads(res.content)["results"]} |  | ||||||
|         assert codes == {"AP4A", "MT01", "PHYS11", "PA00"} |  | ||||||
| 
 |  | ||||||
|     def test_search_by_semester(self): |  | ||||||
|         self.client.force_login(self.bibou) |  | ||||||
|         res = self.client.get(self.url + "?semester=SPRING") |  | ||||||
|         assert res.status_code == 200 |  | ||||||
|         codes = {uv["code"] for uv in json.loads(res.content)["results"]} |  | ||||||
|         assert codes == {"DA50", "TNEV", "PA00"} |  | ||||||
| 
 |  | ||||||
|     def test_search_multiple_filters(self): |  | ||||||
|         self.client.force_login(self.bibou) |  | ||||||
|         res = self.client.get( |  | ||||||
|             self.url + "?semester=AUTUMN&credit_type=CS&department=TC" |  | ||||||
|         ) |  | ||||||
|         assert res.status_code == 200 |  | ||||||
|         codes = {uv["code"] for uv in json.loads(res.content)["results"]} |  | ||||||
|         assert codes == {"MT01", "PHYS11"} |  | ||||||
| 
 |  | ||||||
|     def test_search_fails(self): |  | ||||||
|         self.client.force_login(self.bibou) |  | ||||||
|         res = self.client.get(self.url + "?credit_type=CS&search=DA") |  | ||||||
|         assert res.status_code == 200 |  | ||||||
|         assert json.loads(res.content)["results"] == [] |  | ||||||
| 
 |  | ||||||
|     def test_search_pa00_fail(self): |  | ||||||
|         self.client.force_login(self.bibou) |  | ||||||
|         # Search with UV code |  | ||||||
|         response = self.client.get(reverse("pedagogy:guide"), {"search": "IFC"}) |  | ||||||
|         self.assertNotContains(response, text="PA00") |  | ||||||
| 
 |  | ||||||
|         # Search with first letter of UV code |  | ||||||
|         response = self.client.get(reverse("pedagogy:guide"), {"search": "I"}) |  | ||||||
|         self.assertNotContains(response, text="PA00") |  | ||||||
| 
 |  | ||||||
|         # Search with UV manager |  | ||||||
|         response = self.client.get(reverse("pedagogy:guide"), {"search": "GILLES"}) |  | ||||||
|         self.assertNotContains(response, text="PA00") |  | ||||||
| 
 |  | ||||||
|         # Search with department |  | ||||||
|         response = self.client.get(reverse("pedagogy:guide"), {"department": "TC"}) |  | ||||||
|         self.assertNotContains(response, text="PA00") |  | ||||||
| 
 |  | ||||||
|         # Search with semester |  | ||||||
|         response = self.client.get(reverse("pedagogy:guide"), {"semester": "CLOSED"}) |  | ||||||
|         self.assertNotContains(response, text="PA00") |  | ||||||
| 
 |  | ||||||
|         # Search with language |  | ||||||
|         response = self.client.get(reverse("pedagogy:guide"), {"language": "EN"}) |  | ||||||
|         self.assertNotContains(response, text="PA00") |  | ||||||
| 
 |  | ||||||
|         # Search with credit type |  | ||||||
|         response = self.client.get(reverse("pedagogy:guide"), {"credit_type": "TM"}) |  | ||||||
|         self.assertNotContains(response, text="PA00") |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class UVModerationFormTest(TestCase): | class UVModerationFormTest(TestCase): | ||||||
|     """Assert access rights and if the form works well.""" |     """Assert access rights and if the form works well.""" | ||||||
| 
 | 
 | ||||||
							
								
								
									
										20
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										20
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							| @@ -1225,6 +1225,24 @@ files = [ | |||||||
| griffe = ">=0.47" | griffe = ">=0.47" | ||||||
| mkdocstrings = ">=0.25" | mkdocstrings = ">=0.25" | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "model-bakery" | ||||||
|  | version = "1.18.2" | ||||||
|  | description = "Smart object creation facility for Django." | ||||||
|  | optional = false | ||||||
|  | python-versions = ">=3.8" | ||||||
|  | files = [ | ||||||
|  |     {file = "model_bakery-1.18.2-py3-none-any.whl", hash = "sha256:fd13a251d20db78b790d80f75350a73af5d199e5151227b5dd35cb76f2f08fe8"}, | ||||||
|  |     {file = "model_bakery-1.18.2.tar.gz", hash = "sha256:8f8ab4ba26a206ed848da9b1740b5006b5eeca8a67389efb28dbff37b362e802"}, | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | [package.dependencies] | ||||||
|  | django = ">=4.2" | ||||||
|  |  | ||||||
|  | [package.extras] | ||||||
|  | docs = ["myst-parser", "sphinx", "sphinx-rtd-theme"] | ||||||
|  | test = ["black", "coverage", "mypy", "pillow", "pytest", "pytest-django", "ruff"] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "nodeenv" | name = "nodeenv" | ||||||
| version = "1.9.1" | version = "1.9.1" | ||||||
| @@ -2454,4 +2472,4 @@ filelock = ">=3.4" | |||||||
| [metadata] | [metadata] | ||||||
| lock-version = "2.0" | lock-version = "2.0" | ||||||
| python-versions = "^3.10" | python-versions = "^3.10" | ||||||
| content-hash = "ee0b881719f6834880266d72272429708e781b3ccd34a0fbf3e8b4119dcb95fd" | content-hash = "f8e48947d004d61d63a345d36d7b42777030e1ac3687bb27f97b2c51318fcc8d" | ||||||
|   | |||||||
| @@ -67,6 +67,7 @@ freezegun = "^1.2.2"  # used to test time-dependent code | |||||||
| pytest = "^8.2.2" | pytest = "^8.2.2" | ||||||
| pytest-cov = "^5.0.0" | pytest-cov = "^5.0.0" | ||||||
| pytest-django = "^4.8.0" | pytest-django = "^4.8.0" | ||||||
|  | model-bakery = "^1.18.2" | ||||||
|  |  | ||||||
| # deps used to work on the documentation | # deps used to work on the documentation | ||||||
| [tool.poetry.group.docs.dependencies] | [tool.poetry.group.docs.dependencies] | ||||||
|   | |||||||
| @@ -33,9 +33,13 @@ class SasController(ControllerBase): | |||||||
|             # User can view any moderated picture if he/she is subscribed. |             # User can view any moderated picture if he/she is subscribed. | ||||||
|             # If not, he/she can view only the one he/she has been identified on |             # If not, he/she can view only the one he/she has been identified on | ||||||
|             raise PermissionDenied |             raise PermissionDenied | ||||||
|         pictures = filters.filter( |         pictures = list( | ||||||
|  |             filters.filter( | ||||||
|                 Picture.objects.filter(is_moderated=True, asked_for_removal=False) |                 Picture.objects.filter(is_moderated=True, asked_for_removal=False) | ||||||
|             ) |             ) | ||||||
|  |             .distinct() | ||||||
|  |             .order_by("-date") | ||||||
|  |         ) | ||||||
|         for picture in pictures: |         for picture in pictures: | ||||||
|             picture.full_size_url = picture.get_download_url() |             picture.full_size_url = picture.get_download_url() | ||||||
|             picture.compressed_url = picture.get_download_compressed_url() |             picture.compressed_url = picture.get_download_compressed_url() | ||||||
|   | |||||||
							
								
								
									
										0
									
								
								sas/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								sas/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										103
									
								
								sas/tests/test_api.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								sas/tests/test_api.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | |||||||
|  | from django.test import TestCase | ||||||
|  | from django.urls import reverse | ||||||
|  | from model_bakery import baker | ||||||
|  | from model_bakery.recipe import Recipe | ||||||
|  |  | ||||||
|  | from core.baker_recipes import old_subscriber_user, subscriber_user | ||||||
|  | from core.models import User | ||||||
|  | from sas.models import Album, PeoplePictureRelation, Picture | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class SasTest(TestCase): | ||||||
|  |     @classmethod | ||||||
|  |     def setUpTestData(cls): | ||||||
|  |         Picture.objects.all().delete() | ||||||
|  |         owner = User.objects.get(username="root") | ||||||
|  |  | ||||||
|  |         cls.user_a = old_subscriber_user.make() | ||||||
|  |         cls.user_b, cls.user_c = subscriber_user.make(_quantity=2) | ||||||
|  |  | ||||||
|  |         picture_recipe = Recipe( | ||||||
|  |             Picture, is_in_sas=True, is_folder=False, owner=owner, is_moderated=True | ||||||
|  |         ) | ||||||
|  |         cls.album_a = baker.make(Album, is_in_sas=True) | ||||||
|  |         cls.album_b = baker.make(Album, is_in_sas=True) | ||||||
|  |         for album in cls.album_a, cls.album_b: | ||||||
|  |             pictures = picture_recipe.make(parent=album, _quantity=5, _bulk_create=True) | ||||||
|  |             baker.make(PeoplePictureRelation, picture=pictures[1], user=cls.user_a) | ||||||
|  |             baker.make(PeoplePictureRelation, picture=pictures[2], user=cls.user_a) | ||||||
|  |             baker.make(PeoplePictureRelation, picture=pictures[2], user=cls.user_b) | ||||||
|  |             baker.make(PeoplePictureRelation, picture=pictures[3], user=cls.user_b) | ||||||
|  |             baker.make(PeoplePictureRelation, picture=pictures[4], user=cls.user_a) | ||||||
|  |             baker.make(PeoplePictureRelation, picture=pictures[4], user=cls.user_b) | ||||||
|  |             baker.make(PeoplePictureRelation, picture=pictures[4], user=cls.user_c) | ||||||
|  |  | ||||||
|  |     def test_anonymous_user_forbidden(self): | ||||||
|  |         res = self.client.get(reverse("api:pictures")) | ||||||
|  |         assert res.status_code == 403 | ||||||
|  |  | ||||||
|  |     def test_filter_by_album(self): | ||||||
|  |         self.client.force_login(self.user_b) | ||||||
|  |         res = self.client.get(reverse("api:pictures") + f"?album_id={self.album_a.id}") | ||||||
|  |         assert res.status_code == 200 | ||||||
|  |         expected = list( | ||||||
|  |             self.album_a.children_pictures.order_by("-date").values_list( | ||||||
|  |                 "id", flat=True | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |         assert [i["id"] for i in res.json()] == expected | ||||||
|  |  | ||||||
|  |     def test_filter_by_user(self): | ||||||
|  |         self.client.force_login(self.user_b) | ||||||
|  |         res = self.client.get( | ||||||
|  |             reverse("api:pictures") + f"?users_identified={self.user_a.id}" | ||||||
|  |         ) | ||||||
|  |         assert res.status_code == 200 | ||||||
|  |         expected = list( | ||||||
|  |             self.user_a.pictures.order_by("-picture__date").values_list( | ||||||
|  |                 "picture_id", flat=True | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |         assert [i["id"] for i in res.json()] == expected | ||||||
|  |  | ||||||
|  |     def test_filter_by_multiple_user(self): | ||||||
|  |         self.client.force_login(self.user_b) | ||||||
|  |         res = self.client.get( | ||||||
|  |             reverse("api:pictures") | ||||||
|  |             + f"?users_identified={self.user_a.id}&users_identified={self.user_b.id}" | ||||||
|  |         ) | ||||||
|  |         assert res.status_code == 200 | ||||||
|  |         expected = list( | ||||||
|  |             self.user_a.pictures.union(self.user_b.pictures.all()) | ||||||
|  |             .order_by("-picture__date") | ||||||
|  |             .values_list("picture_id", flat=True) | ||||||
|  |         ) | ||||||
|  |         assert [i["id"] for i in res.json()] == expected | ||||||
|  |  | ||||||
|  |     def test_not_subscribed_user(self): | ||||||
|  |         """Test that a user that is not subscribed can only its own pictures.""" | ||||||
|  |         self.client.force_login(self.user_a) | ||||||
|  |         res = self.client.get( | ||||||
|  |             reverse("api:pictures") + f"?users_identified={self.user_a.id}" | ||||||
|  |         ) | ||||||
|  |         assert res.status_code == 200 | ||||||
|  |         expected = list( | ||||||
|  |             self.user_a.pictures.order_by("-picture__date").values_list( | ||||||
|  |                 "picture_id", flat=True | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |         assert [i["id"] for i in res.json()] == expected | ||||||
|  |  | ||||||
|  |         # trying to access the pictures of someone else | ||||||
|  |         res = self.client.get( | ||||||
|  |             reverse("api:pictures") + f"?users_identified={self.user_b.id}" | ||||||
|  |         ) | ||||||
|  |         assert res.status_code == 403 | ||||||
|  |  | ||||||
|  |         # trying to access the pictures of someone else shouldn't success, | ||||||
|  |         # even if mixed with owned pictures | ||||||
|  |         res = self.client.get( | ||||||
|  |             reverse("api:pictures") | ||||||
|  |             + f"?users_identified={self.user_a.id}&users_identified={self.user_b.id}" | ||||||
|  |         ) | ||||||
|  |         assert res.status_code == 403 | ||||||
		Reference in New Issue
	
	Block a user