add tests

This commit is contained in:
thomas girod 2024-07-22 19:12:03 +02:00
parent b9d19be183
commit cb1aa8bef0
13 changed files with 332 additions and 160 deletions

32
core/baker_recipes.py Normal file
View 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),
)

View File

@ -13,7 +13,7 @@ from pedagogy.schemas import SimpleUvSchema, UvFilterSchema, UvSchema
from pedagogy.utbm_api import find_uv
@api_controller("/uv", permissions=[IsSubscriber])
@api_controller("/uv")
class UvController(ControllerBase):
@route.get(
"/{year}/{code}",
@ -31,7 +31,10 @@ class UvController(ControllerBase):
return res
@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)
def fetch_uv_list(self, search: Query[UvFilterSchema]):

View File

@ -123,7 +123,7 @@ class UvFilterSchema(FilterSchema):
def filter_semester(self, value: set[str] | None) -> Q:
"""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.
"""
if not value:

View File

@ -125,8 +125,6 @@
function update_query_string(key, value) {
const url = new URL(window.location.href);
console.log(value)
console.log(!!value)
if (!value) {
url.searchParams.delete(key)
} else if (Array.isArray(value)) {

View File

165
pedagogy/tests/test_api.py Normal file
View 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")

View File

@ -20,7 +20,6 @@
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
import json
import pytest
from django.conf import settings
@ -555,157 +554,6 @@ class UVCommentUpdateTest(TestCase):
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):
"""Assert access rights and if the form works well."""

20
poetry.lock generated
View File

@ -1225,6 +1225,24 @@ files = [
griffe = ">=0.47"
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]]
name = "nodeenv"
version = "1.9.1"
@ -2454,4 +2472,4 @@ filelock = ">=3.4"
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
content-hash = "ee0b881719f6834880266d72272429708e781b3ccd34a0fbf3e8b4119dcb95fd"
content-hash = "f8e48947d004d61d63a345d36d7b42777030e1ac3687bb27f97b2c51318fcc8d"

View File

@ -67,6 +67,7 @@ freezegun = "^1.2.2" # used to test time-dependent code
pytest = "^8.2.2"
pytest-cov = "^5.0.0"
pytest-django = "^4.8.0"
model-bakery = "^1.18.2"
# deps used to work on the documentation
[tool.poetry.group.docs.dependencies]

View File

@ -33,8 +33,12 @@ class SasController(ControllerBase):
# 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
raise PermissionDenied
pictures = filters.filter(
Picture.objects.filter(is_moderated=True, asked_for_removal=False)
pictures = list(
filters.filter(
Picture.objects.filter(is_moderated=True, asked_for_removal=False)
)
.distinct()
.order_by("-date")
)
for picture in pictures:
picture.full_size_url = picture.get_download_url()

0
sas/tests/__init__.py Normal file
View File

103
sas/tests/test_api.py Normal file
View 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