2024-08-26 19:11:14 -04:00
|
|
|
import logging
|
2024-05-04 16:38:51 -04:00
|
|
|
from collections.abc import Callable
|
2024-02-17 15:55:56 -05:00
|
|
|
from itertools import chain
|
2024-05-04 16:38:51 -04:00
|
|
|
from typing import TypedDict
|
2024-02-17 15:55:56 -05:00
|
|
|
|
2024-02-16 11:12:04 -05:00
|
|
|
from django.contrib.auth import get_user_model
|
2024-05-04 16:38:51 -04:00
|
|
|
from django.contrib.auth.models import AbstractBaseUser, Permission
|
2024-02-16 11:12:04 -05:00
|
|
|
from django.contrib.contenttypes.models import ContentType
|
2024-05-04 16:38:51 -04:00
|
|
|
from django.db import models
|
2024-02-16 11:12:04 -05:00
|
|
|
from django.test import Client
|
|
|
|
|
|
|
|
from hypothesis import given
|
|
|
|
from hypothesis import strategies as st
|
|
|
|
from hypothesis.extra.django import TestCase, from_model
|
|
|
|
|
2024-02-16 12:47:24 -05:00
|
|
|
from paperwork.models import (
|
|
|
|
Certification,
|
|
|
|
CertificationDefinition,
|
|
|
|
CertificationVersion,
|
|
|
|
Department,
|
|
|
|
InstructorOrVendor,
|
|
|
|
Waiver,
|
|
|
|
)
|
2024-02-16 11:12:04 -05:00
|
|
|
|
|
|
|
|
2024-05-04 16:38:51 -04:00
|
|
|
class PermissionLookup(TypedDict):
|
|
|
|
codename: str
|
|
|
|
model: type[models.Model]
|
|
|
|
|
|
|
|
|
2024-02-16 11:12:04 -05:00
|
|
|
class PermissionRequiredViewTestCaseMixin:
|
2024-05-04 16:38:51 -04:00
|
|
|
permissions: list[PermissionLookup] = []
|
|
|
|
path: str
|
|
|
|
|
|
|
|
client: Client
|
|
|
|
user_with_permission: AbstractBaseUser
|
|
|
|
user_without_permission: AbstractBaseUser
|
|
|
|
|
|
|
|
assertEqual: Callable
|
2024-02-16 11:12:04 -05:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def setUpTestData(cls):
|
|
|
|
User = get_user_model()
|
|
|
|
cls.client = Client()
|
|
|
|
|
|
|
|
cls.user_without_permission = User.objects.create_user(
|
|
|
|
username="user_without_permission"
|
|
|
|
)
|
|
|
|
cls.user_with_permission = User.objects.create_user(
|
|
|
|
username="user_with_permission"
|
|
|
|
)
|
|
|
|
|
|
|
|
resolved_permissions = [
|
|
|
|
Permission.objects.get(
|
|
|
|
content_type=ContentType.objects.get_for_model(permission["model"]),
|
|
|
|
codename=permission["codename"],
|
|
|
|
)
|
|
|
|
for permission in cls.permissions
|
|
|
|
]
|
|
|
|
|
|
|
|
cls.user_with_permission.user_permissions.add(*resolved_permissions)
|
|
|
|
|
|
|
|
def test_missing_permission(self) -> None:
|
2024-08-26 19:11:14 -04:00
|
|
|
# suppress PermissionDenied messages
|
|
|
|
logger = logging.getLogger("django.request")
|
|
|
|
previous_log_level = logger.getEffectiveLevel()
|
|
|
|
logger.setLevel(logging.ERROR)
|
|
|
|
|
2024-02-16 11:12:04 -05:00
|
|
|
self.client.force_login(self.user_without_permission)
|
|
|
|
response = self.client.get(self.path)
|
|
|
|
self.assertEqual(response.status_code, 403)
|
|
|
|
|
2024-08-26 19:11:14 -04:00
|
|
|
logger.setLevel(previous_log_level)
|
|
|
|
|
2024-02-16 11:12:04 -05:00
|
|
|
|
|
|
|
class WaiverReportTestCase(PermissionRequiredViewTestCaseMixin, TestCase):
|
|
|
|
permissions = [{"model": Waiver, "codename": "view_waiver"}]
|
|
|
|
path = "/paperwork/waivers"
|
|
|
|
|
|
|
|
@given(waivers=st.lists(from_model(Waiver, number=st.none())))
|
|
|
|
def test_waiver_report(self, waivers: list[Waiver]) -> None:
|
|
|
|
self.client.force_login(self.user_with_permission)
|
|
|
|
response = self.client.get(self.path)
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
|
|
|
|
class InstructorOrVendorReportTestCase(PermissionRequiredViewTestCaseMixin, TestCase):
|
|
|
|
permissions = [{"model": InstructorOrVendor, "codename": "view_instructororvendor"}]
|
|
|
|
path = "/paperwork/instructors-and-vendors"
|
|
|
|
|
|
|
|
@given(
|
|
|
|
instructors_or_vendors=st.lists(
|
|
|
|
from_model(InstructorOrVendor, serial=st.none())
|
|
|
|
)
|
|
|
|
)
|
|
|
|
def test_waiver_report(
|
|
|
|
self, instructors_or_vendors: list[InstructorOrVendor]
|
|
|
|
) -> None:
|
|
|
|
self.client.force_login(self.user_with_permission)
|
|
|
|
response = self.client.get(self.path)
|
|
|
|
self.assertEqual(response.status_code, 200)
|
2024-02-16 12:47:24 -05:00
|
|
|
|
|
|
|
|
|
|
|
@st.composite
|
2024-02-17 15:55:56 -05:00
|
|
|
def random_certifications(
|
|
|
|
draw,
|
|
|
|
) -> list[Certification]:
|
|
|
|
def certifications(version: CertificationVersion):
|
|
|
|
return st.lists(
|
2024-02-16 12:47:24 -05:00
|
|
|
from_model(
|
2024-02-17 15:55:56 -05:00
|
|
|
Certification,
|
|
|
|
number=st.none(),
|
|
|
|
certification_version=st.just(version),
|
2024-02-16 12:47:24 -05:00
|
|
|
),
|
2024-02-17 15:55:56 -05:00
|
|
|
max_size=10,
|
2024-02-16 12:47:24 -05:00
|
|
|
)
|
2024-02-17 15:55:56 -05:00
|
|
|
|
|
|
|
def versions_with_certifications(definition: CertificationDefinition):
|
|
|
|
return st.lists(
|
|
|
|
from_model(CertificationVersion, definition=st.just(definition)).flatmap(
|
|
|
|
certifications
|
|
|
|
),
|
|
|
|
max_size=2,
|
|
|
|
)
|
|
|
|
|
|
|
|
def definitions_with_versions(department: Department):
|
|
|
|
return st.lists(
|
|
|
|
from_model(CertificationDefinition, department=st.just(department)).flatmap(
|
|
|
|
versions_with_certifications
|
|
|
|
),
|
|
|
|
max_size=2,
|
2024-02-16 12:47:24 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
return draw(
|
|
|
|
st.lists(
|
2024-02-17 15:55:56 -05:00
|
|
|
from_model(Department).flatmap(definitions_with_versions),
|
|
|
|
max_size=2,
|
|
|
|
).map(
|
|
|
|
lambda x: list(
|
|
|
|
chain.from_iterable(chain.from_iterable(chain.from_iterable(x)))
|
2024-02-16 12:47:24 -05:00
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class CertifiersReportTestCase(PermissionRequiredViewTestCaseMixin, TestCase):
|
|
|
|
permissions = [{"model": Certification, "codename": "view_certification"}]
|
|
|
|
path = "/paperwork/certifiers"
|
|
|
|
|
|
|
|
@given(certifications=random_certifications())
|
2024-02-17 15:55:56 -05:00
|
|
|
def test_certifiers_report(self, certifications: list[Certification]) -> None:
|
2024-02-16 12:47:24 -05:00
|
|
|
self.client.force_login(self.user_with_permission)
|
|
|
|
response = self.client.get(self.path)
|
|
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
|
|
|
|
|
|
|
|
class CertificationCountReportTestCase(PermissionRequiredViewTestCaseMixin, TestCase):
|
|
|
|
permissions = [{"model": Certification, "codename": "view_certification"}]
|
|
|
|
path = "/paperwork/certification-count"
|
|
|
|
|
|
|
|
@given(certifications=random_certifications())
|
|
|
|
def test_certification_count_report(
|
|
|
|
self, certifications: list[Certification]
|
|
|
|
) -> None:
|
|
|
|
self.client.force_login(self.user_with_permission)
|
|
|
|
response = self.client.get(self.path)
|
|
|
|
self.assertEqual(response.status_code, 200)
|