This commit is contained in:
2022-11-08 21:19:51 +01:00
commit 4c456eafc3
160 changed files with 21472 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
from .initial import *
from .models import *
from .utils import *
from .views import *
from .droits import *

View File

@@ -0,0 +1 @@
from .socle import *

View File

@@ -0,0 +1,590 @@
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APIClient, APITestCase
from ...models import (Administre, CustomUser, Decision, DecisionChoices,
Domaine, Filiere, FormationEmploi, Garnison, Poste,
RefGest, RefOrg, RefSvFil, SousVivier)
class TestSocleVerifDroits(APITestCase):
""" Classe permettant de tester la vérification des droits de lecture et d'écriture d'un gestionnaire """
@classmethod
def setUpClass(cls):
super().setUpClass()
# Création des gestionnaires
cls.gest1 = CustomUser.objects.create(username='gest1', password='password', id=1)
cls.gest2 = CustomUser.objects.create(username='gest2', password='password', id=2)
cls.gest3 = CustomUser.objects.create(username='gest3', password='password', id=3)
cls.gest4 = CustomUser.objects.create(username='gest4', password='password', id=4)
cls.gest5 = CustomUser.objects.create(username='gest5', password='password', id=5)
cls.gest6 = CustomUser.objects.create(username='gest6', password='password', id=6)
cls.gest7 = CustomUser.objects.create(username='gest7', password='password', id=7)
cls.gest8 = CustomUser.objects.create(username='gest8', password='password', id=8)
cls.gest9 = CustomUser.objects.create(username='gest9', password='password', id=9)
cls.gest10 = CustomUser.objects.create(username='gest10', password='password', id=10)
# Création des objets du référentiel organique
cls.ref_org30 = RefOrg.objects.create(
ref_org_code='org30',
ref_org_code_niv_org3='org30',
ref_org_ref_fe=True,
ref_org_ref_sv_fil=True,
ref_org_itd=True,
ref_org_droit_lect=True,
ref_org_droit_ecr=True
) # Référentiel organique avec les droits de lecture et d'écriture (attribué aux gestionnaires 1)
cls.ref_org40a = RefOrg.objects.create(
ref_org_code='org40a',
ref_org_code_niv_org3='org30',
ref_org_code_niv_org4='org40a',
ref_org_ref_fe=True,
ref_org_ref_sv_fil=True,
ref_org_itd=True,
ref_org_droit_lect=True,
ref_org_droit_ecr=True
)
cls.ref_org40b = RefOrg.objects.create(
ref_org_code='org40b',
ref_org_code_niv_org3='org30',
ref_org_code_niv_org4='org40b',
ref_org_ref_fe=True,
ref_org_ref_sv_fil=True,
ref_org_itd=True,
ref_org_droit_lect=True,
ref_org_droit_ecr=True
)
cls.ref_org41 = RefOrg.objects.create(
ref_org_code='org41',
ref_org_code_niv_org4='org41',
ref_org_ref_fe=True,
ref_org_ref_sv_fil=True
) # Référentiel organique sans les droits de lecture et d'écriture (attribué aux gestionnaires 3)
cls.ref_org42 = RefOrg.objects.create(
ref_org_code='org42',
ref_org_code_niv_org4='org42',
ref_org_droit_lect=True
) # Référentiel organique avec les droits de lecture mais ni pcp ni fil ni itd (attribué aux gestionnaires 4)
cls.ref_org43 = RefOrg.objects.create(
ref_org_code='org43',
ref_org_code_niv_org4='org43',
ref_org_ref_fe=True,
ref_org_ref_sv_fil=True,
ref_org_droit_lect=True,
ref_org_droit_ecr=True
)
cls.ref_org44 = RefOrg.objects.create(
ref_org_code='org44',
ref_org_code_niv_org4='org44',
ref_org_ref_fe=True,
ref_org_ref_sv_fil=True,
ref_org_droit_lect=True
) # Référentiel organique sans les droits d'écriture (attribué aux gestionnaires 6)
cls.ref_org45 = RefOrg.objects.create(
ref_org_code='org45',
ref_org_code_niv_org4='org45',
ref_org_ref_fe=True,
ref_org_ref_sv_fil=True,
ref_org_droit_lect=True,
ref_org_droit_ecr=True
) # Référentiel organique sans FE ni FIL (attribué aux gestionnaires 7)
cls.ref_org36 = RefOrg.objects.create(
ref_org_code='org36',
ref_org_code_niv_org3='org36',
ref_org_ref_fe=True,
ref_org_ref_sv_fil=True,
ref_org_droit_lect=True,
ref_org_droit_ecr=True
) # Référentiel organique ne possedant aucune instances dans les ref FE et FIL (attribué aux gestionnaires 8)
cls.ref_org37 = RefOrg.objects.create(
ref_org_code='org37',
ref_org_code_niv_org3='org37',
ref_org_ref_fe=True,
ref_org_ref_sv_fil=True,
ref_org_droit_lect=True,
ref_org_droit_ecr=True
)
cls.ref_org47a = RefOrg.objects.create(
ref_org_code='org47a',
ref_org_code_niv_org3='org37',
ref_org_code_niv_org4='org47a',
ref_org_ref_fe=True,
ref_org_ref_sv_fil=True,
ref_org_droit_lect=True,
ref_org_droit_ecr=True
)
cls.ref_org47b = RefOrg.objects.create(
ref_org_code='org47b',
ref_org_code_niv_org3='org37',
ref_org_code_niv_org4='org47b',
ref_org_ref_fe=True,
ref_org_ref_sv_fil=True,
ref_org_droit_lect=True,
ref_org_droit_ecr=True
)
cls.ref_org48 = RefOrg.objects.create(
ref_org_code='org48',
ref_org_code_niv_org4='org48',
ref_org_itd=True,
ref_org_droit_lect=True,
ref_org_droit_ecr=True
) # Référentiel organique avec tous les droits mais seulement pour un gestionnaire ITD (attribué au gestionnaire 10)
# Création des objets du référentiel gestionnaire
cls.ref_gest1 = RefGest.objects.create(ref_gest_sap=cls.gest1.id, ref_gest_org=cls.ref_org30)
cls.ref_gest2 = RefGest.objects.create(ref_gest_sap=cls.gest2.id) # Gestionnaire ne possedant pas de cod_niv_org
cls.ref_gest3 = RefGest.objects.create(ref_gest_sap=cls.gest3.id, ref_gest_org=cls.ref_org41)
cls.ref_gest4 = RefGest.objects.create(ref_gest_sap=cls.gest4.id, ref_gest_org=cls.ref_org42)
cls.ref_gest5 = RefGest.objects.create(ref_gest_sap=cls.gest5.id, ref_gest_org=cls.ref_org43)
cls.ref_gest6 = RefGest.objects.create(ref_gest_sap=cls.gest6.id, ref_gest_org=cls.ref_org44)
cls.ref_gest7 = RefGest.objects.create(ref_gest_sap=cls.gest7.id, ref_gest_org=cls.ref_org45)
cls.ref_gest8 = RefGest.objects.create(ref_gest_sap=cls.gest8.id, ref_gest_org=cls.ref_org36)
cls.ref_gest9 = RefGest.objects.create(ref_gest_sap=cls.gest9.id, ref_gest_org=cls.ref_org37)
cls.ref_gest10 = RefGest.objects.create(ref_gest_sap=cls.gest10.id, ref_gest_org=cls.ref_org48)
# Création du référentiel FE et des DOM/FIL
cls.gar1 = Garnison.objects.create(gar_id='Gar1', gar_lieu='lieu1')
cls.fe1 = FormationEmploi.objects.create(fe_code='Fe1', fe_code_niv_org4='org40a', fe_garnison_lieu=cls.gar1.gar_lieu)
cls.fe2 = FormationEmploi.objects.create(fe_code='Fe2', fe_code_niv_org4='org43', fe_garnison_lieu=cls.gar1.gar_lieu)
cls.fe3 = FormationEmploi.objects.create(fe_code='Fe3', fe_code_niv_org4='org47a', fe_garnison_lieu=cls.gar1.gar_lieu)
cls.dom1 = Domaine.objects.create(d_code='Dom1')
cls.fil1 = Filiere.objects.create(f_code='Fil1')
# Création des administrés, postes et décisions
cls.adm1 = Administre.objects.create(a_id_sap=1, formation_emploi=cls.fe1, a_domaine=cls.dom1, a_filiere=cls.fil1, a_categorie='Cat1', a_nom='nom', a_prenom='prénom', a_sexe='M', a_eip='eip1') # Administré du fe1
cls.adm2 = Administre.objects.create(a_id_sap=2, formation_emploi=cls.fe1, a_domaine=cls.dom1, a_filiere=cls.fil1, a_categorie='Cat1', a_nom='nom', a_prenom='prénom', a_sexe='M', a_eip='eip2') # Idem
cls.adm3 = Administre.objects.create(a_id_sap=3, formation_emploi=cls.fe2, a_domaine=cls.dom1, a_filiere=cls.fil1, a_categorie='Cat1', a_nom='nom', a_prenom='prénom', a_sexe='M', a_eip='eip3') # Administré du fe2
cls.adm4 = Administre.objects.create(a_id_sap=4, formation_emploi=cls.fe2, a_domaine=cls.dom1, a_filiere=cls.fil1, a_categorie='Cat1', a_nom='nom', a_prenom='prénom', a_sexe='M', a_eip='eip4') # Idem
cls.poste1 = Poste.objects.create(p_id='Poste1', formation_emploi=cls.fe1, p_domaine=cls.dom1, p_filiere=cls.fil1, p_categorie='Cat1', p_eip='eip1')
cls.poste2 = Poste.objects.create(p_id='Poste2', formation_emploi=cls.fe1, p_domaine=cls.dom1, p_filiere=cls.fil1, p_categorie='Cat1', p_eip='eip2')
cls.poste3 = Poste.objects.create(p_id='Poste3', formation_emploi=cls.fe2, p_domaine=cls.dom1, p_filiere=cls.fil1, p_categorie='Cat1', p_eip='eip3')
cls.poste4 = Poste.objects.create(p_id='Poste4', formation_emploi=cls.fe2, p_domaine=cls.dom1, p_filiere=cls.fil1, p_categorie='Cat1', p_eip='eip4')
cls.poste5 = Poste.objects.create(p_id='Poste5', p_specifique='ITD', p_eip='eip5')
cls.dec1 = Decision.objects.create(administre=cls.adm1, poste=cls.poste1, de_decision=DecisionChoices.PROPOSITION_FE)
cls.dec2 = Decision.objects.create(administre=cls.adm3, poste=cls.poste3, de_decision=DecisionChoices.PROPOSITION_FE)
# Création du référentiel sous-vivier filiere
cls.ref_sv_fil1 = RefSvFil.objects.create(ref_sv_fil_code='org40b', ref_sv_fil_dom='Dom1', ref_sv_fil_fil='Fil1', ref_sv_fil_cat='Cat1')
# Création des clients de test
cls.c0 = APIClient() # Client de test qui ne sera pas authentifié
cls.c1 = APIClient()
cls.c2 = APIClient()
cls.c3 = APIClient()
cls.c4 = APIClient()
cls.c5 = APIClient()
cls.c6 = APIClient()
cls.c7 = APIClient()
cls.c8 = APIClient()
cls.c9 = APIClient()
cls.c10 = APIClient()
# Authentification des clients de test
cls.c1.force_authenticate(cls.gest1)
cls.c2.force_authenticate(cls.gest2)
cls.c3.force_authenticate(cls.gest3)
cls.c4.force_authenticate(cls.gest4)
cls.c5.force_authenticate(cls.gest5)
cls.c6.force_authenticate(cls.gest6)
cls.c7.force_authenticate(cls.gest7)
cls.c8.force_authenticate(cls.gest8)
cls.c9.force_authenticate(cls.gest9)
cls.c10.force_authenticate(cls.gest10)
cls.list_clients = [cls.c1, cls.c2, cls.c3, cls.c4, cls.c5, cls.c6, cls.c7, cls.c8, cls.c9, cls.c10]
# Création des données utilsées pour les requêtes
cls.patch_datas = {
'data_p1': {
'p_id': 'Poste1',
'p_eip': 'eip2'
},
'data_a1': {
'a_id_sap': 1,
'a_eip': 'eip2'
},
'data_d1': {
'pk': cls.adm1.pk,
'de_decision': DecisionChoices.POSITIONNE
},
'data_p3': {
'p_id': 'Poste3',
'p_eip': 'eip4'
},
'data_a3': {
'a_id_sap': 3,
'a_eip': 'eip4'
},
'data_d3': {
'pk': cls.adm3.pk,
'de_decision': DecisionChoices.POSITIONNE
},
'data_p5': {
'p_id': 'Poste5',
'p_eip': 'eip6'
}
}
cls.put_datas = {
'data_p1': [
{'p_id': 'Poste1',
'p_eip': 'eip2'
},
{'p_id': 'Poste2',
'p_eip': 'eip3'
}
],
'data_a1': [
{'a_id_sap': 1,
'a_eip': 'eip2'
},
{'a_id_sap': 2,
'a_eip': 'eip3'
},
],
'data_p3': [
{'p_id': 'Poste3',
'p_eip': 'eip4'
},
{'p_id': 'Poste4',
'p_eip': 'eip5'
}
],
'data_a3': [
{'a_id_sap': 3,
'a_eip': 'eip4'
},
{'a_id_sap': 4,
'a_eip': 'eip5'
},
]
}
# Définition des liens sur lesquels les requêtes seront effectuées
cls.urls = {
'list_p': reverse('Poste-list'),
'list_a': reverse('Administre-list'),
'list_d': reverse('Decision-list'),
'instance_p1': reverse('Poste-list') + 'Poste1/',
'instance_a1': reverse('Administre-list') + '1/',
'instance_d1': reverse('Decision-list') + '1/',
'instance_p3': reverse('Poste-list') + 'Poste3/',
'instance_a3': reverse('Administre-list') + '3/',
'instance_d3': reverse('Decision-list') + '3/',
'instance_p5': reverse('Poste-list') + 'Poste5/'
}
@classmethod
def tearDownClass(cls):
super().tearDownClass()
for client in cls.list_clients:
client.logout()
def test_get_not_authenticated(self):
"""
Teste la requete GET sur les vues PosteView, AdministreView et DecisionView sachant que le gestionnaire n'est pas authentifié
"""
# Le test passe mais il y a également interdiction car c5 n'a pas de 'code_niv_org'...
get_resp_p = self.c0.get(path=self.urls['list_p'], follow=True)
get_resp_a = self.c0.get(path=self.urls['list_a'], follow=True)
get_resp_d = self.c0.get(path=self.urls['list_d'], follow=True)
self.assertEqual(get_resp_p.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(get_resp_a.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(get_resp_d.status_code, status.HTTP_403_FORBIDDEN)
def test_get_ok(self):
"""
Teste la requete GET sur les vues PosteView, AdministreView et DecisionView sachant que :
- Le gestionnaire est authentifié,
- Qu'il a les droits de lecture
"""
get_resp_p = self.c1.get(path=self.urls['list_p'], follow=True)
get_resp_a = self.c1.get(path=self.urls['list_a'], follow=True)
get_resp_d = self.c1.get(path=self.urls['list_d'], follow=True)
self.assertEqual(get_resp_p.status_code, status.HTTP_200_OK)
self.assertEqual(get_resp_a.status_code, status.HTTP_200_OK)
self.assertEqual(get_resp_d.status_code, status.HTTP_200_OK)
def test_get_no_code_niv_org(self):
"""
Teste la requete GET sur les vues PosteView, AdministreView et DecisionView sachant que le gestionnaire n'a pas d'attribut code_niv_org
"""
get_resp_p = self.c2.get(path=self.urls['list_p'], follow=True)
get_resp_a = self.c2.get(path=self.urls['list_a'], follow=True)
get_resp_d = self.c2.get(path=self.urls['list_d'], follow=True)
self.assertEqual(get_resp_p.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(get_resp_a.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(get_resp_d.status_code, status.HTTP_403_FORBIDDEN)
def test_get_no_reading_rights(self):
"""
Teste la requete GET sur les vues PosteView, AdministreView et DecisionView sachant que le gestionnaire n'a pas les droits de lecture
"""
get_resp_p = self.c3.get(path=self.urls['list_p'], follow=True)
get_resp_a = self.c3.get(path=self.urls['list_a'], follow=True)
get_resp_d = self.c3.get(path=self.urls['list_d'], follow=True)
self.assertEqual(get_resp_p.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(get_resp_a.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(get_resp_d.status_code, status.HTTP_403_FORBIDDEN)
def test_get_not_pcp_not_fil(self):
"""
Teste la requete GET sur les vues PosteView, AdministreView et DecisionView sachant que le gestionnaire est ni pcp ni filiere
"""
get_resp_p = self.c4.get(path=self.urls['list_p'], follow=True)
get_resp_a = self.c4.get(path=self.urls['list_a'], follow=True)
get_resp_d = self.c4.get(path=self.urls['list_d'], follow=True)
self.assertEqual(get_resp_p.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(get_resp_a.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(get_resp_d.status_code, status.HTTP_403_FORBIDDEN)
def test_put_ok_fe_fil(self):
"""
Teste la requete PUT sur des postes et des administrés sachant que :
- Le gestionnaire est authentifié,
- Qu'il a les droits de lecture et d'écriture,
- Qu'il est PCP et FIL,
- Qu'il est associé à deux code_niv_org4,
- Les instances qu'il édite sont dans son FE et dans sa filiere
"""
# Pas de test put pour les décisions car la modification de plusieurs décisions n'est pas possible dans Ogure
put_resp_p = self.c1.put(path=self.urls['list_p'], data=self.put_datas['data_p1'], follow=True)
put_resp_a = self.c1.put(path=self.urls['list_a'], data=self.put_datas['data_a1'], follow=True)
self.assertEqual(put_resp_p.status_code, status.HTTP_200_OK)
self.assertEqual(put_resp_a.status_code, status.HTTP_200_OK)
def test_put_ok_fe(self):
"""
Teste la requete PUT sur des postes et des administrés sachant que :
- Le gestionnaire est authentifié,
- Qu'il a les droits de lecture et d'écriture,
- Qu'il est PCP et FIL,
- Qu'il est associé à un code_niv_org4,
- Les instances qu'il édite sont dans son FE MAIS PAS dans sa FIL
"""
# Pas de test put pour les décisions car la modification de plusieurs décisions n'est pas possible dans Ogure
put_resp_p = self.c5.put(path=self.urls['list_p'], data=self.put_datas['data_p3'], follow=True)
put_resp_a = self.c5.put(path=self.urls['list_a'], data=self.put_datas['data_a3'], follow=True)
self.assertEqual(put_resp_p.status_code, status.HTTP_200_OK)
self.assertEqual(put_resp_a.status_code, status.HTTP_200_OK)
def test_put_no_writing_rights(self):
"""
Teste la requete PUT sur des postes et des administrés sachant qu'il n'a pas les droits d'écriture'
"""
put_resp_p = self.c6.put(path=self.urls['list_p'], data=self.put_datas['data_p1'], follow=True)
put_resp_a = self.c6.put(path=self.urls['list_a'], data=self.put_datas['data_a1'], follow=True)
self.assertEqual(put_resp_p.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(put_resp_a.status_code, status.HTTP_403_FORBIDDEN)
def test_put_no_fe_no_fil(self):
"""
Teste la requete PUT sur des postes et des administrés sachant qu'il n'a pas de FE ni de FIL associé'
"""
put_resp_p = self.c7.put(path=self.urls['list_p'], data=self.put_datas['data_p1'], follow=True)
put_resp_a = self.c7.put(path=self.urls['list_a'], data=self.put_datas['data_a1'], follow=True)
self.assertEqual(put_resp_p.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(put_resp_a.status_code, status.HTTP_403_FORBIDDEN)
def test_put_no_instances_ok(self):
"""
Teste la requete PUT sur des postes et des administrés sachant qu'il n'a pas d'instances dans son FE ou dans sa FIL
"""
put_resp_p = self.c8.put(path=self.urls['list_p'], data=self.put_datas['data_p1'], follow=True)
put_resp_a = self.c8.put(path=self.urls['list_a'], data=self.put_datas['data_a1'], follow=True)
self.assertEqual(put_resp_p.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(put_resp_a.status_code, status.HTTP_403_FORBIDDEN)
def test_put_not_ok(self):
"""
Teste la requete PUT sur des postes et des administrés sachant que les instances modifiées ne sont ni dans le FE ni dans la FIL du gestionnaire
"""
put_resp_p = self.c9.put(path=self.urls['list_p'], data=self.put_datas['data_p1'], follow=True)
put_resp_a = self.c9.put(path=self.urls['list_a'], data=self.put_datas['data_a1'], follow=True)
self.assertEqual(put_resp_p.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(put_resp_a.status_code, status.HTTP_403_FORBIDDEN)
def test_patch_no_code_niv_org(self):
"""
Teste la requete PATCH sur les vues PosteView, AdministreView et DecisionView sachant que le gestionnaire n'a pas d'attribut code_niv_org
"""
patch_resp_p = self.c2.patch(path=self.urls['instance_p1'], data=self.patch_datas['data_p1'], follow=True)
patch_resp_a = self.c2.patch(path=self.urls['instance_a1'], data=self.patch_datas['data_a1'], follow=True)
patch_resp_d = self.c2.patch(path=self.urls['instance_d1'], data=self.patch_datas['data_d1'], follow=True)
self.assertEqual(patch_resp_p.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(patch_resp_a.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(patch_resp_d.status_code, status.HTTP_403_FORBIDDEN)
def test_patch_no_reading_rights(self):
"""
Teste la requete PATCH sur les vues PosteView, AdministreView et DecisionView sachant que le gestionnaire n'a pas les droits de lecture
"""
patch_resp_p = self.c3.patch(path=self.urls['instance_p1'], data=self.patch_datas['data_p1'], follow=True)
patch_resp_a = self.c3.patch(path=self.urls['instance_a1'], data=self.patch_datas['data_a1'], follow=True)
patch_resp_d = self.c3.patch(path=self.urls['instance_d1'], data=self.patch_datas['data_d1'], follow=True)
self.assertEqual(patch_resp_p.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(patch_resp_a.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(patch_resp_d.status_code, status.HTTP_403_FORBIDDEN)
def test_patch_not_pcp_not_fil_not_itd(self):
"""
Teste la requete PATCH sur les vues PosteView, AdministreView et DecisionView sachant que le gestionnaire est ni pcp ni filiere
"""
patch_resp_p = self.c4.patch(path=self.urls['instance_p1'], data=self.patch_datas['data_p1'], follow=True)
patch_resp_a = self.c4.patch(path=self.urls['instance_a1'], data=self.patch_datas['data_a1'], follow=True)
patch_resp_d = self.c4.patch(path=self.urls['instance_d1'], data=self.patch_datas['data_d1'], follow=True)
self.assertEqual(patch_resp_p.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(patch_resp_a.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(patch_resp_d.status_code, status.HTTP_403_FORBIDDEN)
def test_patch_ok_fe_fil(self):
"""
Teste la requete PATCH sur un poste, un administré et une décision sachant que :
- Le gestionnaire est authentifié,
- Qu'il a les droits de lecture et d'écriture,
- Qu'il est PCP et FIL,
- Qu'il est associé à deux code_niv_org4,
- Les instances qu'il édite sont dans son FE et dans sa filiere
"""
patch_resp_p = self.c1.patch(path=self.urls['instance_p1'], data=self.patch_datas['data_p1'], follow=True)
patch_resp_a = self.c1.patch(path=self.urls['instance_a1'], data=self.patch_datas['data_a1'], follow=True)
patch_resp_d = self.c1.patch(path=self.urls['instance_d1'], data=self.patch_datas['data_d1'], follow=True)
self.assertEqual(patch_resp_p.status_code, status.HTTP_200_OK)
self.assertEqual(patch_resp_a.status_code, status.HTTP_200_OK)
self.assertEqual(patch_resp_d.status_code, status.HTTP_200_OK)
def test_patch_ok_fe(self):
"""
Teste la requete PATCH sur un poste, un administré et une décision sachant que :
- Le gestionnaire est authentifié,
- Qu'il a les droits de lecture et d'écriture,
- Qu'il est PCP et FIL,
- Qu'il est associé à un code_niv_org4,
- Les instances qu'il édite sont dans son FE MAIS PAS dans sa FIL
"""
patch_resp_p = self.c5.patch(path=self.urls['instance_p3'], data=self.patch_datas['data_p3'], follow=True)
patch_resp_a = self.c5.patch(path=self.urls['instance_a3'], data=self.patch_datas['data_a3'], follow=True)
patch_resp_d = self.c5.patch(path=self.urls['instance_d3'], data=self.patch_datas['data_d3'], follow=True)
self.assertEqual(patch_resp_p.status_code, status.HTTP_200_OK)
self.assertEqual(patch_resp_a.status_code, status.HTTP_200_OK)
self.assertEqual(patch_resp_d.status_code, status.HTTP_200_OK)
def test_patch_ok_itd(self):
"""
Teste la requete PATCH sur un poste sachant que :
- Le gestionnaire est authentifié,
- Qu'il a les droits de lecture et d'écriture,
- Qu'il est ITD,
- Qu'il est associé à un code_niv_org4,
- Le poste édité est marqué ITD
"""
patch_resp_p = self.c10.patch(path=self.urls['instance_p5'], data=self.patch_datas['data_p5'], follow=True)
self.assertEqual(patch_resp_p.status_code, status.HTTP_200_OK)
def test_patch_no_writing_rights(self):
"""
Teste la requete PATCH sur des postes et des administrés sachant qu'il n'a pas les droits d'écriture'
"""
patch_resp_p = self.c6.patch(path=self.urls['instance_p1'], data=self.patch_datas['data_p1'], follow=True)
patch_resp_a = self.c6.patch(path=self.urls['instance_a1'], data=self.patch_datas['data_a1'], follow=True)
patch_resp_d = self.c6.patch(path=self.urls['instance_d1'], data=self.patch_datas['data_d1'], follow=True)
self.assertEqual(patch_resp_p.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(patch_resp_a.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(patch_resp_d.status_code, status.HTTP_403_FORBIDDEN)
def test_patch_no_niv_org(self):
"""
Teste la requete PATCH sur des postes et des administrés sachant qu'il n'a pas d'attribut niv_org'
"""
patch_resp_p = self.c7.patch(path=self.urls['instance_p1'], data=self.patch_datas['data_p1'], follow=True)
patch_resp_a = self.c7.patch(path=self.urls['instance_a1'], data=self.patch_datas['data_a1'], follow=True)
patch_resp_d = self.c7.patch(path=self.urls['instance_d1'], data=self.patch_datas['data_d1'], follow=True)
self.assertEqual(patch_resp_p.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(patch_resp_a.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(patch_resp_d.status_code, status.HTTP_403_FORBIDDEN)
def test_patch_no_instances_ok(self):
"""
Teste la requete PATCH sur des postes et des administrés sachant qu'il n'a pas d'instances dans son FE ou dans sa FIL
"""
patch_resp_p = self.c8.patch(path=self.urls['instance_p1'], data=self.patch_datas['data_p1'], follow=True)
patch_resp_a = self.c8.patch(path=self.urls['instance_a1'], data=self.patch_datas['data_a1'], follow=True)
patch_resp_d = self.c8.patch(path=self.urls['instance_d1'], data=self.patch_datas['data_d1'], follow=True)
self.assertEqual(patch_resp_p.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(patch_resp_a.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(patch_resp_d.status_code, status.HTTP_403_FORBIDDEN)
def test_patch_not_ok_fe_fil(self):
"""
Teste la requete PATCH sur un poste, un administré et une décision sachant que les instances modifiées ne sont ni dans le FE ni dans la FIL du gestionnaire
"""
patch_resp_p = self.c9.patch(path=self.urls['instance_p1'], data=self.patch_datas['data_p1'], follow=True)
patch_resp_a = self.c9.patch(path=self.urls['instance_a1'], data=self.patch_datas['data_a1'], follow=True)
patch_resp_d = self.c9.patch(path=self.urls['instance_d1'], data=self.patch_datas['data_d1'], follow=True)
self.assertEqual(patch_resp_p.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(patch_resp_a.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(patch_resp_d.status_code, status.HTTP_403_FORBIDDEN)
def test_patch_not_ok_itd(self):
"""
Teste la requete PATCH sur un poste sachant que l'instance modifiée n'est pas marquée ITD
"""
patch_resp_p = self.c10.patch(path=self.urls['instance_p1'], data=self.patch_datas['data_p1'], follow=True)
self.assertEqual(patch_resp_p.status_code, status.HTTP_403_FORBIDDEN)
def test_delete_ok(self):
delete_resp_p = self.c1.delete(path=self.urls['instance_p1'], follow=True)
delete_resp_a = self.c1.delete(path=self.urls['instance_a1'], follow=True)
self.assertEqual(delete_resp_p.status_code, status.HTTP_204_NO_CONTENT)
self.assertEqual(delete_resp_a.status_code, status.HTTP_204_NO_CONTENT)
def test_delete_decision_ok(self):
# On teste la suppression de décision séparément des autres suppressions sans quoi instance_d ferait référence à un administré supprimé
delete_resp_d = self.c1.delete(path=self.urls['instance_d1'], follow=True)
self.assertEqual(delete_resp_d.status_code, status.HTTP_204_NO_CONTENT)
def test_delete_not_ok(self):
# delete_resp_p = self.c9.delete(path=self.urls['instance_p1'], follow=True)
# delete_resp_a = self.c9.delete(path=self.urls['instance_a1'], follow=True)
delete_resp_d = self.c9.delete(path=self.urls['instance_d1'], follow=True)
# self.assertEqual(delete_resp_p.status_code, status.HTTP_403_FORBIDDEN)
# self.assertEqual(delete_resp_a.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(delete_resp_d.status_code, status.HTTP_403_FORBIDDEN)
def test_head_ok(self):
head_resp_p = self.c1.head(path=self.urls['list_p'], follow=True)
head_resp_a = self.c1.head(path=self.urls['list_a'], follow=True)
head_resp_d = self.c1.head(path=self.urls['list_d'], follow=True)
self.assertEqual(head_resp_p.status_code, status.HTTP_200_OK)
self.assertEqual(head_resp_a.status_code, status.HTTP_200_OK)
self.assertEqual(head_resp_d.status_code, status.HTTP_200_OK)
def test_head_no_reading_rights(self):
head_resp_p = self.c3.head(path=self.urls['list_p'], follow=True)
head_resp_a = self.c3.head(path=self.urls['list_a'], follow=True)
head_resp_d = self.c3.head(path=self.urls['list_d'], follow=True)
self.assertEqual(head_resp_p.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(head_resp_a.status_code, status.HTTP_403_FORBIDDEN)
self.assertEqual(head_resp_d.status_code, status.HTTP_403_FORBIDDEN)

View File

@@ -0,0 +1,140 @@
"""
Ce fichier n'est utilisé que pour les tests.
"""
# from ..utils_scoring import notations_test
# from django.test import TestCase
# from numpy import fliplr
# from numpy.core.fromnumeric import shape
# import pandas as pd
# from .utils_scoring import encoding,notePonderee,notations_test
# Create your tests here.
# class InsertModelTest(TestCase):
# # Fonction pour la table Garnisons
# def test_scoring(self):
# """Pour tester la fonction de scoring
# """
# mut_test = notations_test()
# data_test = mut_test.to_dict('records')
# data_source = [{
# 'mu_id': 0,
# 'poste_id': 1,
# 'administres_id_sap': 9984,
# 'pam_id': 1,
# 'mu_date_execution': '14/06/2021',
# 'mu_note_militaire': 0.5,
# 'mu_flag_cple_ideal': 0,
# 'mu_decision': '',
# 'mu_date_decision': '',
# 'mu_notes_gestionnaire': '',
# 'mu_notes_partagees': ''},
# {
# 'mu_id': 0,
# 'poste_id': 1,
# 'administres_id_sap': 56754,
# 'pam_id': 1,
# 'mu_date_execution': '14/06/2021',
# 'mu_note_militaire': 0.0,
# 'mu_flag_cple_ideal': 0,
# 'mu_decision': '',
# 'mu_date_decision': '',
# 'mu_notes_gestionnaire': '',
# 'mu_notes_partagees': ''},
# {
# 'mu_id': 0,
# 'poste_id': 1,
# 'administres_id_sap': 78905,
# 'pam_id': 1,
# 'mu_date_execution': '14/06/2021',
# 'mu_note_militaire': 0.0,
# 'mu_flag_cple_ideal': 0,
# 'mu_decision': '',
# 'mu_date_decision': '',
# 'mu_notes_gestionnaire': '',
# 'mu_notes_partagees': ''
# },
# {
# 'mu_id': 0,
# 'poste_id': 2,
# 'administres_id_sap': 9984,
# 'pam_id': 1,
# 'mu_date_execution': '14/06/2021',
# 'mu_note_militaire': 0.0,
# 'mu_flag_cple_ideal': 0,
# 'mu_decision': '',
# 'mu_date_decision': '',
# 'mu_notes_gestionnaire': '',
# 'mu_notes_partagees': ''
# },
# {
# 'mu_id': 0,
# 'poste_id': 2,
# 'administres_id_sap': 56754,
# 'pam_id': 1,
# 'mu_date_execution': '14/06/2021',
# 'mu_note_militaire': 0.5,
# 'mu_flag_cple_ideal': 0,
# 'mu_decision': '',
# 'mu_date_decision': '',
# 'mu_notes_gestionnaire': '',
# 'mu_notes_partagees': ''
# },
# {
# 'mu_id': 0,
# 'poste_id': 2,
# 'administres_id_sap': 78905,
# 'pam_id': 1,
# 'mu_date_execution': '14/06/2021',
# 'mu_note_militaire': 0.0,
# 'mu_flag_cple_ideal': 0,
# 'mu_decision': '',
# 'mu_date_decision': '',
# 'mu_notes_gestionnaire': '',
# 'mu_notes_partagees': ''
# },
# {
# 'mu_id': 0,
# 'poste_id': 3,
# 'administres_id_sap': 9984,
# 'pam_id': 1,
# 'mu_date_execution': '14/06/2021',
# 'mu_note_militaire': 0.0,
# 'mu_flag_cple_ideal': 0,
# 'mu_decision': '',
# 'mu_date_decision': '',
# 'mu_notes_gestionnaire': '',
# 'mu_notes_partagees': ''
# },
# {
# 'mu_id': 0,
# 'poste_id': 3,
# 'administres_id_sap': 56754,
# 'pam_id': 1,
# 'mu_date_execution': '14/06/2021',
# 'mu_note_militaire': 0.0,
# 'mu_flag_cple_ideal': 0,
# 'mu_decision': '',
# 'mu_date_decision': '',
# 'mu_notes_gestionnaire': '',
# 'mu_notes_partagees': ''
# },
# {
# 'mu_id': 0,
# 'poste_id': 3,
# 'administres_id_sap': 78905,
# 'pam_id': 1,
# 'mu_date_execution': '14/06/2021',
# 'mu_note_militaire': 0.5,
# 'mu_flag_cple_ideal': 0,
# 'mu_decision': '',
# 'mu_date_decision': '',
# 'mu_notes_gestionnaire': '',
# 'mu_notes_partagees': ''
# }]
# if data_source == data_test :
# test_value = True
# else :
# test_value = False
# self.assertIs(test_value,True)

View File

@@ -0,0 +1,3 @@
from .administre import *
from .calcul import *
from .poste import *

View File

@@ -0,0 +1,39 @@
from ...models import Administre, StatutPamChoices
from django.db.utils import IntegrityError
from django.test import TestCase
class AdministreTestCase(TestCase):
def test_attributes_statut_pam(self):
""" vérifie que le comportement reste correct avec l'ajout de nouveaux attributs """
attr_calc = 'calc_enabled'
attr_dec = 'dec_enabled'
for choice in StatutPamChoices:
self.assertIsInstance(choice.label, str, f"{choice} : le libellé n'a pas le bon type")
self.assertIsInstance(choice.value, str, f"{choice} : la valeur n'a pas le bon type")
self.assertEqual(choice.value, choice, f"{choice} : n'est pas égal à sa valeur")
self.assertIsInstance(getattr(choice, attr_calc), bool, f"{choice} : l'attribut {attr_calc} n'a pas le bon type")
self.assertIsInstance(getattr(choice, attr_dec), bool, f"{choice} : l'attribut {attr_dec} n'a pas le bon type")
def test_constraint_statut_pam(self):
""" vérifie qu'il n'est pas possible de stocker n'importe quoi en base """
valides = StatutPamChoices.values
invalide = 'autre'
def given():
self.assertTrue(valides, 'il devrait exister des choix valides')
self.assertNotIn(invalide, valides, 'le test nécessite une valeur invalide')
given()
id_sap = 1
# valide : création OK
for valide in valides:
Administre.objects.create(pk=id_sap, a_statut_pam=valide)
id_sap = id_sap + 1
# invalide : création KO
with self.assertRaises(IntegrityError):
Administre.objects.create(pk=id_sap, a_statut_pam=invalide)

View File

@@ -0,0 +1,34 @@
from django.db.utils import IntegrityError
from django.test import TestCase
from django.utils import timezone
from ...models.calcul import Calcul, SousVivier
from ...models.calcul import StatutCalculChoices as StatutCalcul
class CalculTestCase(TestCase):
@classmethod
def setUpTestData(cls):
for i in range(1, 15):
SousVivier.objects.create(pk=str(i))
def test_constraint_statut(self):
valides = StatutCalcul.values
invalide = 'autre'
def given():
self.assertTrue(valides, 'il devrait exister des choix valides')
self.assertNotIn(invalide, valides, 'le test nécessite une valeur invalide')
given()
i = 1
# valide : création OK
for valide in valides:
Calcul.objects.create(pk=str(i), ca_date_debut=timezone.now(), ca_statut=valide, ca_statut_pourcentage=0)
i = i + 1
# invalide : création KO
with self.assertRaises(IntegrityError):
Calcul.objects.create(pk=str(i), ca_date_debut=timezone.now(), ca_statut=invalide, ca_statut_pourcentage=0)

View File

@@ -0,0 +1,34 @@
from ...models import AvisPosteChoices as AvisPoste, Poste, PropositionsArmementChoices as PropositionsArmement
from django.db.utils import IntegrityError
from django.test import TestCase
class PosteTestCase(TestCase):
def test_attributes_avis(self):
""" vérifie que le comportement reste correct avec l'ajout de nouveaux attributs """
attr_calc = 'calc_enabled'
attr_dec = 'dec_enabled'
for choice in AvisPoste:
self.assertIsInstance(choice.label, str, f"{choice} : le libellé n'a pas le bon type")
self.assertIsInstance(choice.value, str, f"{choice} : la valeur n'a pas le bon type")
self.assertEqual(choice.value, choice, f"{choice} : n'est pas égal à sa valeur")
self.assertIsInstance(getattr(choice, attr_calc), bool, f"{choice} : l'attribut {attr_calc} n'a pas le bon type")
self.assertIsInstance(getattr(choice, attr_dec), bool, f"{choice} : l'attribut {attr_dec} n'a pas le bon type")
def test_constraint_propositions_armement(self):
valides = PropositionsArmement.values
invalide = 'autre'
def given():
self.assertTrue(valides, 'il devrait exister des choix valides')
self.assertNotIn(invalide, valides, 'le test nécessite une valeur invalide')
given()
# valide : création OK
for valide in valides:
Poste.objects.create(pk=valide, propositions_armement=valide)
# invalide : création KO
with self.assertRaises(IntegrityError):
Poste.objects.create(pk=invalide, propositions_armement=invalide)

View File

@@ -0,0 +1,75 @@
from django.test import TestCase
from django.urls import reverse
from rest_framework import status
import pandas as pd
from rest_framework.test import APIClient, APITestCase
from ...utils_extraction import to_table_pam
from ...models import (Administre, CustomUser, Decision, DecisionChoices,
Domaine, Filiere, FormationEmploi, Garnison, Poste,
RefGest, RefOrg, RefSvFil, SousVivier, Administres_Pams, Postes_Pams, PAM)
import datetime
class PamTestCase(TestCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.date = datetime.date(2023,1,1)
cls.pam_2022 = PAM.objects.create(pam_id='2022',pam_date=datetime.date(2022,7,15), pam_libelle= "PAM de l'année 2022", pam_statut='PAM en cours')
cls.pam_2023 = PAM.objects.create(pam_id='2023',pam_date=datetime.date(2022,7,15), pam_libelle= "PAM de l'année 2023", pam_statut='PAM A+1')
def test_extraction_pam(self):
"""
Construit la table PAM qui servira à récuperer l'année du PAM et piloter l'affichage des données en fonction du bon pam pour chaque tables
Cloture le PAM en le sauvegardant dans un fichier excel
"""
annee_pam = str(self.date.year)
annee_pam_suivant = str(self.date.year + 1)
pam=pd.DataFrame(columns = ['pam_id','pam_date', 'pam_libelle','pam_statut'])
pam_id = annee_pam
pam_id_suivant = annee_pam_suivant
pam_libelle = f"PAM de l'année {annee_pam}"
pam_libelle_suivant = f"PAM de l'année {annee_pam_suivant}"
pam_date = self.date
pam_date_suivant = pam_date
pam_statut = "PAM en cours"
pam_statut_suivant = "PAM A+1"
sorg_id = "SORG"
sorg_libelle = "SORG"
sorg_statut = "SORG"
pam['pam_id'] = [pam_id,pam_id_suivant, sorg_id]
pam['pam_date'] = [pam_date,pam_date_suivant, pam_date]
pam['pam_libelle'] = [pam_libelle,pam_libelle_suivant,sorg_libelle]
pam['pam_statut'] = [pam_statut,pam_statut_suivant,sorg_statut]
return pam
def test_insertion_pam(self):
self.liste_create = []
self.liste_update = []
self.update_header = ['pam_date','pam_libelle','pam_statut']
self.df = self.test_extraction_pam()
for i in range(self.df.shape[0]):
self.pams = PAM.objects.filter(pam_id=self.df.at[i,'pam_id'])
self.pam = PAM(pam_id=self.df.at[i, 'pam_id'],pam_date=self.df.at[i, 'pam_date'], pam_libelle=self.df.at[i, 'pam_libelle'],
pam_statut=self.df.at[i, 'pam_statut'])
if self.pam.pam_id in self.pams.values_list('pam_id', flat = True):
self.liste_update.append(self.pam)
else:
cloture=PAM.objects.filter(pam_statut="PAM en cours")
cloture.update(pam_statut ="PAM clôturé")
self.liste_create.append(self.pam)
if self.liste_create:
PAM.objects.bulk_create(self.liste_create)
if self.liste_update:
PAM.objects.bulk_update(self.liste_update, fields=self.update_header)

View File

@@ -0,0 +1,6 @@
from .alimentation import *
from .decisions import *
from .decorators import *
from .functions import *
from .logging import *
from .predicates import *

View File

@@ -0,0 +1,56 @@
from django.test import SimpleTestCase
from ...utils.alimentation import BOCols as Cols
class BOColsTestCase(SimpleTestCase):
""" tests pour BOCols """
def test_attributes(self):
""" vérifie que les nouveaux attributs sont renseignés correctement """
for col in Cols:
self.assertIsInstance(col.value, str, f"{col} : la valeur n'a pas le bon type")
def test_columns(self):
""" vérifie les colonnes renvoyées """
members = list(Cols)
col_names = [col.value for col in members]
self.assertCountEqual(col_names, Cols.columns(), "les noms de colonnes (sans argument) ne sont pas les bons")
col = members[0]
col_names = [col.value]
self.assertCountEqual(col_names, Cols.columns(col), "les noms de colonnes (un argument) ne sont pas les bons")
col_list = members[0:2]
col_names = [col.value for col in col_list]
self.assertCountEqual(col_names, Cols.columns(*col_list), "les noms de colonnes (plusieurs arguments) ne sont pas les bons")
def test_col_mapping(self):
""" vérifie les correspondances renvoyés """
members = list(Cols)
std_mapping = {'a': 1, 'b': 'test'}
enum_mapping = {members[0]: (2, 3), members[1]: members[1].name}
mapping_before = {**enum_mapping, **std_mapping}
mapping_after = Cols.col_mapping(mapping_before)
self.assertTrue(all(k in mapping_after for k in std_mapping.keys()), "toutes les clés standard doivent faire partie du mapping final")
self.assertTrue(all(v == mapping_after.get(k) for k, v in std_mapping.items()), "les valeurs de clés standard ne doivent pas changer")
self.assertTrue(all(k.value in mapping_after for k in enum_mapping.keys()), f"tous les noms de colonnes de clés de type {Cols.__name__} doivent faire partie du mapping final")
self.assertTrue(all(v == mapping_after.get(k.value) for k, v in enum_mapping.items()), f"les valeurs de clés de type {Cols.__name__} ne doivent pas changer")
def test_converters(self):
""" vérifie les convertisseurs renvoyés """
members = list(Cols)
col_names = [col.value for col in members if col.converter]
self.assertCountEqual(col_names, Cols.converters().keys(), "les convertisseurs (sans argument) ne sont pas les bons")
col = members[0]
col_names = [col.value]
self.assertCountEqual(col_names, Cols.converters(col).keys(), "les convertisseurs (un argument) ne sont pas les bons")
col_list = members[0:2]
col_names = [col.value for col in col_list]
self.assertCountEqual(col_names, Cols.converters(*col_list).keys(), "les convertisseurs (plusieurs arguments) ne sont pas les bons")

View File

@@ -0,0 +1,283 @@
from typing import Callable, List, Optional, Tuple, Union
from unittest import mock
from django.test import TestCase
from ...models import Administre
from ...models import AvisPosteChoices as AvisPoste
from ...models import CustomUser, Decision, DecisionChoices, Poste, SousVivier
from ...models import StatutPamChoices as StatutPam
from ...utils.decisions import (DECISIONS, KEY_CHOICES, KEY_CREATE,
KEY_PROFILES, KEY_UPDATE, ExtraDecisions,
get_all_decisions, get_available_decisions)
from ...utils.permissions import (KEY_READ, KEY_WRITE, Profiles,
get_profiles_by_adm)
class DecisionsTest(TestCase):
def setUp(self):
""" vérifie qu'il n'existe aucun administré ou poste avant le test """
self.assertEqual(Administre.objects.exists(), False, "pas d'administré avant un test")
self.assertEqual(Poste.objects.exists(), False, "pas de poste avant un test")
def tearDown(self):
""" supprime tous les administrés, les postes, les décisions en fin de test """
Decision.objects.all().delete()
Poste.objects.all().delete()
Administre.objects.all().delete()
def __load_administres(self) -> List[Administre]:
""" charge les administrés de la base à partir des IDs """
self.assertTrue(self.adm_ids, "il est nécessaire de renseigner des IDs d'administrés pour le chargement")
return list(Administre.objects.filter(pk__in=self.adm_ids or ()))
def __setup_decisions(self, decision: Optional[Union[DecisionChoices, ExtraDecisions]]) -> None:
""" supprime et recrée (si nécessaire) les postes et les décisions des administrés """
Decision.objects.all().delete()
Poste.objects.all().delete()
if decision and decision != ExtraDecisions.EMPTY:
for adm_id in self.adm_ids:
poste = Poste.objects.create(pk=str(adm_id), p_avis=AvisPoste.P1)
Decision.objects.create(administre_id=adm_id, poste=poste, de_decision=decision)
def __do_for_profiles(self, action: Callable[[Profiles], None], profiles: Tuple[Profiles], decisions: Tuple[Union[DecisionChoices, ExtraDecisions]] = ()) -> None:
"""
exécute une action (un test) avec un sous-test :
- soit pour chaque profil (utilise un mock)
- soit pour chaque profil et chaque statut de décision (utilise un mock)
implicite : self.adm_ids
"""
def run_subTest(profile: Profiles, decision: Optional[Union[DecisionChoices, ExtraDecisions]] = None) -> None:
with self.subTest(profile=profile.name, **({'decision': decision.name} if decision else {})):
@mock.patch(
f'{get_available_decisions.__module__}.{get_profiles_by_adm.__name__}',
return_value={adm_id: {KEY_READ: (), KEY_WRITE: (profile,)} for adm_id in self.adm_ids}
)
def do_with_profile(mock):
action(profile, decision)
do_with_profile()
for p in profiles:
if decisions:
for decision in decisions:
self.__setup_decisions(decision)
run_subTest(p, decision)
else:
run_subTest(p)
def __assert_common(self, decisions_by_adm):
"""
assertions communes à propos des décisions renvoyées pour les administrés
implicite : self.adm_ids
"""
self.assertIsInstance(decisions_by_adm, dict, "le résultat n'est jamais None")
for adm_id in self.adm_ids:
self.assertTrue(adm_id in decisions_by_adm, "tous les administrés doivent être présents dans le résultat")
decisions = decisions_by_adm.get(adm_id)
self.assertIsInstance(decisions, dict, "le format des décisions est un dictionnaire")
for key in (KEY_CREATE, KEY_UPDATE):
self.assertTrue(key in decisions, f"les décisions doivent contenir la clé {key}")
self.assertIsInstance(decisions.get(key), tuple, f"la valeur de {key} des décisions n'a pas le type attendu")
def test_tree_attribute(self):
""" vérifie que le comportement reste correct avec l'ajout de nouveaux attributs """
attr_trees = 'trees'
for choice in DecisionChoices:
self.assertIsInstance(choice.label, str, f"{choice} : le libellé n'a pas le bon type")
self.assertIsInstance(choice.value, str, f"{choice} : la valeur n'a pas le bon type")
self.assertEqual(choice.value, choice, f"{choice} : n'est pas égal à sa valeur")
self.assertIsInstance(getattr(choice, attr_trees), tuple, f"{choice} : l'attribut {attr_trees} n'a pas le bon type")
def test_champ_decisions(self):
""" vérifie la structure de DECISIONS """
to_test = DECISIONS
self.assertIsInstance(to_test, dict, "la structure n'a pas le type attendu")
self.assertTrue(to_test, "la structure ne doit pas être vide")
for key, value in to_test.items():
self.assertTrue(key is None or isinstance(key, DecisionChoices), f"la décision {key} n'a pas le type attendu")
self.assertIsInstance(value, dict, f"la valeur pour la décision {key} n'a pas le type attendu")
profiles = value.get(KEY_PROFILES)
self.assertTrue(profiles is None or isinstance(profiles, tuple), f"la valeur de {KEY_PROFILES} de la valeur pour la décision {key} n'a pas le type attendu")
for p in profiles:
self.assertIsInstance(p, Profiles, f"un élément de la clé {KEY_PROFILES} de la valeur pour la décision {key} n'a pas le type attendu")
choices = value.get(KEY_CHOICES)
self.assertTrue(choices is None or isinstance(choices, tuple), f"la valeur de {KEY_CHOICES} de la valeur pour la décision {key} n'a pas le type attendu")
for c in choices:
self.assertIsInstance(c, (DecisionChoices, ExtraDecisions), f"un élément de la clé {KEY_CHOICES} de la valeur pour la décision {key} n'a pas le type attendu")
def test_get_all_decisions(self):
""" vérifie les valeurs de get_all_decisions """
to_test = get_all_decisions()
self.assertIsInstance(to_test, tuple, "le résultat n'a pas le type attendu")
self.assertTrue(to_test, "le résultat ne doit pas être vide")
for value in to_test:
self.assertIsInstance(value, DecisionChoices, f"la valeur {value} n'a pas le type attendu")
def test_pam_wihout_choice(self):
""" pour ces statuts PAM il n'y a aucun choix """
user = CustomUser()
self.adm_ids = tuple(a.a_id_sap for a in (
Administre.objects.create(pk=i + 1, a_statut_pam=status) for i, status in enumerate(StatutPam) if not status.dec_enabled
))
def action(_p, _d):
decisions_by_adm = get_available_decisions(self.__load_administres(), user=user)
self.__assert_common(decisions_by_adm)
for adm_id in self.adm_ids:
decisions = decisions_by_adm.get(adm_id)
self.assertFalse(decisions.get(KEY_CREATE))
self.assertFalse(decisions.get(KEY_UPDATE))
self.__do_for_profiles(action, profiles=tuple(Profiles))
def test_pam_with_choices(self):
""" pour ces statuts PAM c'est l'arbre de décision qui est utilisé """
user = CustomUser()
self.adm_ids = tuple(a.a_id_sap for a in (
Administre.objects.create(pk=i + 1, a_statut_pam=status) for i, status in enumerate(StatutPam) if status.dec_enabled
))
D = DecisionChoices
EX = ExtraDecisions
def action(p, d):
decisions_by_adm = get_available_decisions(self.__load_administres(), user=user)
self.__assert_common(decisions_by_adm)
for adm_id in self.adm_ids:
decisions = decisions_by_adm.get(adm_id)
# create
decisions_create = decisions.get(KEY_CREATE)
if p == Profiles.FILIERE:
self.assertCountEqual(decisions_create, (D.PROPOSITION_FE, D.HME_PROPOSITION_VIVIER))
else:
self.assertFalse(decisions_create)
# update
decisions_update = decisions.get(KEY_UPDATE)
if not d and p == Profiles.FILIERE:
self.assertCountEqual(decisions_update, (D.PROPOSITION_FE, D.HME_PROPOSITION_VIVIER))
elif d == D.PROPOSITION_FE and p in (Profiles.PCP, Profiles.PCP_ACTUEL):
self.assertCountEqual(decisions_update, (D.DIALOGUE_EN_COURS, D.FOREMP_EN_COURS))
elif d == D.DIALOGUE_EN_COURS and p in (Profiles.PCP, Profiles.PCP_ACTUEL):
self.assertCountEqual(decisions_update, (D.DIALOGUE_TERMINE, D.DIALOGUE_INFRUCTUEUX))
elif d == D.DIALOGUE_TERMINE and p in (Profiles.PCP, Profiles.PCP_ACTUEL):
self.assertCountEqual(decisions_update, (D.FOREMP_EN_COURS,))
elif d == D.DIALOGUE_INFRUCTUEUX and p in (Profiles.PCP, Profiles.PCP_ACTUEL):
self.assertCountEqual(decisions_update, (D.FOREMP_EN_COURS, D.REMIS_A_DISPOSITION))
elif d == D.FOREMP_EN_COURS and p in (Profiles.PCP, Profiles.PCP_ACTUEL):
self.assertCountEqual(decisions_update, (D.FOREMP_TERMINE,))
elif d == D.FOREMP_TERMINE and p in (Profiles.PCP, Profiles.PCP_ACTUEL):
self.assertCountEqual(decisions_update, (D.PREPOSITIONNE, D.REMIS_A_DISPOSITION))
elif d == D.PREPOSITIONNE and p in (Profiles.PCP, Profiles.PCP_FUTUR):
self.assertCountEqual(decisions_update, (D.POSITIONNE, D.REMIS_A_DISPOSITION))
elif d == D.POSITIONNE and p in (Profiles.PCP, Profiles.PCP_FUTUR):
self.assertCountEqual(decisions_update, (D.OMIP_EN_COURS, D.OMI_EN_COURS, D.REMIS_A_DISPOSITION))
elif d == D.OMIP_EN_COURS and p in (Profiles.PCP, Profiles.PCP_FUTUR):
self.assertCountEqual(decisions_update, (D.OMIP_TERMINE, D.REMIS_A_DISPOSITION))
elif d == D.OMIP_TERMINE and p in (Profiles.PCP, Profiles.PCP_FUTUR):
self.assertCountEqual(decisions_update, (D.ATTENTE_AVIONAGE, D.OMI_EN_COURS, D.REMIS_A_DISPOSITION))
elif d == D.ATTENTE_AVIONAGE and p in (Profiles.PCP, Profiles.PCP_FUTUR):
self.assertCountEqual(decisions_update, (D.OMI_EN_COURS,))
elif d == D.OMI_EN_COURS and p in (Profiles.PCP, Profiles.PCP_FUTUR):
self.assertCountEqual(decisions_update, (D.OMI_ACTIVE, D.REMIS_A_DISPOSITION))
elif d == D.OMI_ACTIVE and p in (Profiles.PCP, Profiles.PCP_FUTUR):
self.assertCountEqual(decisions_update, (D.OMI_ANNULE,))
elif d == D.OMI_ANNULE and p in (Profiles.PCP, Profiles.PCP_FUTUR):
self.assertCountEqual(decisions_update, (D.REMIS_A_DISPOSITION,))
elif d == D.REMIS_A_DISPOSITION and p == Profiles.FILIERE:
self.assertCountEqual(decisions_update, (EX.EMPTY,))
elif d == D.HME_PROPOSITION_VIVIER and p in (Profiles.PCP, Profiles.PCP_ACTUEL, Profiles.PCP_FUTUR):
self.assertCountEqual(decisions_update, (D.HME_DIALOGUE_INITIE,))
elif d == D.HME_ETUDE_DESISTEMENT and p in (Profiles.PCP, Profiles.PCP_ACTUEL, Profiles.PCP_FUTUR):
self.assertCountEqual(decisions_update, (D.HME_DESISTEMENT,))
elif d == D.HME_DESISTEMENT and p in (Profiles.PCP, Profiles.PCP_ACTUEL, Profiles.PCP_FUTUR):
self.assertCountEqual(decisions_update, (D.REMIS_A_DISPOSITION,))
elif d == D.HME_DIALOGUE_INITIE and p in (Profiles.PCP, Profiles.PCP_ACTUEL, Profiles.PCP_FUTUR):
self.assertCountEqual(decisions_update, (D.HME_DIALOGUE_EN_COURS,))
elif d == D.HME_DIALOGUE_EN_COURS and p in (Profiles.PCP, Profiles.PCP_ACTUEL, Profiles.PCP_FUTUR):
self.assertCountEqual(decisions_update, (D.HME_DIALOGUE_TERMINE, D.HME_DIALOGUE_INFRUCTUEUX))
elif d == D.HME_DIALOGUE_TERMINE and p in (Profiles.PCP, Profiles.PCP_ACTUEL, Profiles.PCP_FUTUR):
self.assertCountEqual(decisions_update, (D.HME_PREPOSITIONNE,))
elif d == D.HME_DIALOGUE_INFRUCTUEUX and p in (Profiles.PCP, Profiles.PCP_ACTUEL, Profiles.PCP_FUTUR):
self.assertCountEqual(decisions_update, (D.HME_ETUDE_DESISTEMENT, D.HME_PREPOSITIONNE, D.REMIS_A_DISPOSITION))
elif d == D.HME_FOREMP_EN_COURS and p in (Profiles.PCP, Profiles.PCP_ACTUEL, Profiles.PCP_FUTUR):
self.assertCountEqual(decisions_update, (D.HME_FOREMP_TERMINE, D.HME_OMI_EN_COURS))
elif d == D.HME_FOREMP_TERMINE and p in (Profiles.PCP, Profiles.PCP_ACTUEL, Profiles.PCP_FUTUR):
self.assertCountEqual(decisions_update, (D.HME_OMIP_EN_COURS, D.HME_OMI_EN_COURS))
elif d == D.HME_PREPOSITIONNE and p == Profiles.HME:
self.assertCountEqual(decisions_update, (D.HME_VALIDATION_EXPERT, D.HME_REFUS_EXPERT))
elif d == D.HME_POSITIONNE and p in (Profiles.PCP, Profiles.PCP_ACTUEL, Profiles.PCP_FUTUR):
self.assertCountEqual(decisions_update, (D.HME_OMIP_EN_COURS, D.HME_OMI_EN_COURS, D.HME_FOREMP_EN_COURS))
elif d == D.HME_VALIDATION_EXPERT and p in (Profiles.PCP, Profiles.PCP_ACTUEL, Profiles.PCP_FUTUR):
self.assertCountEqual(decisions_update, (D.HME_POSITIONNE,))
elif d == D.HME_REFUS_EXPERT and p in (Profiles.PCP, Profiles.PCP_ACTUEL, Profiles.PCP_FUTUR):
self.assertCountEqual(decisions_update, (D.HME_PREPOSITIONNE, D.REMIS_A_DISPOSITION))
elif d == D.HME_OMIP_EN_COURS and p in (Profiles.PCP, Profiles.PCP_ACTUEL, Profiles.PCP_FUTUR):
self.assertCountEqual(decisions_update, (D.HME_OMIP_TERMINE,))
elif d == D.HME_OMIP_TERMINE and p in (Profiles.PCP, Profiles.PCP_ACTUEL, Profiles.PCP_FUTUR):
self.assertCountEqual(decisions_update, (D.HME_ATTENTE_AVIONAGE, D.HME_OMI_EN_COURS))
elif d == D.HME_ATTENTE_AVIONAGE and p in (Profiles.PCP, Profiles.PCP_ACTUEL, Profiles.PCP_FUTUR):
self.assertCountEqual(decisions_update, (D.HME_OMI_EN_COURS,))
elif d == D.HME_OMI_EN_COURS and p in (Profiles.PCP, Profiles.PCP_ACTUEL, Profiles.PCP_FUTUR):
self.assertCountEqual(decisions_update, (D.HME_OMI_ACTIVE,))
elif d == D.HME_OMI_ACTIVE and p in (Profiles.PCP, Profiles.PCP_ACTUEL, Profiles.PCP_FUTUR):
self.assertCountEqual(decisions_update, ())
else:
self.assertFalse(decisions_update)
self.__do_for_profiles(action, profiles=tuple(Profiles), decisions=(EX.EMPTY, *get_all_decisions()))

View File

@@ -0,0 +1,85 @@
from .samples import MyClass
from ...utils.decorators import class_logger, decorate_functions, CLASS_ATTR_LOGGER
from ...utils.functions import find_class
from ...utils.logging import get_logger_name
from ...utils.predicates import func_name_is_in
from django.test import SimpleTestCase
from typing import Tuple
import functools
def double_result(func):
""" décorateur pour doubler les résultats """
@functools.wraps(func)
def inner(*args, **kwargs):
return 2 * func(*args, **kwargs)
return inner
class DecoratorTest(SimpleTestCase):
def test_decorate_functions(self):
num = 5
a = 1
b = 2
res = a + b
res_x2 = res * 2
def given():
msg = 'le résultat initial est bien celui attendu'
self.assertEqual(MyClass.class_met(a, b), res, msg)
self.assertEqual(MyClass.static_met(a, b), res, msg)
self.assertEqual(MyClass(num).std_met(a, b), res, msg)
given()
# tout est décoré
Type = decorate_functions(double_result, lambda func: True)(MyClass)
self.assertEqual(Type.class_met(a, b), res_x2)
self.assertEqual(Type.static_met(a, b), res_x2)
self.assertEqual(Type(num).std_met(a, b), res_x2)
# une seule méthode est décorée (1ère)
Type = decorate_functions(double_result, func_name_is_in(MyClass.class_met.__name__,))(MyClass)
self.assertEqual(Type.class_met(a, b), res_x2)
self.assertEqual(Type.static_met(a, b), res)
self.assertEqual(Type(num).std_met(a, b), res)
# une seule méthode est décorée (2ème)
Type = decorate_functions(double_result, func_name_is_in(MyClass.static_met.__name__,))(MyClass)
self.assertEqual(Type.class_met(a, b), res)
self.assertEqual(Type.static_met(a, b), res_x2)
self.assertEqual(Type(num).std_met(a, b), res)
# une seule méthode est décorée (3ème)
Type = decorate_functions(double_result, func_name_is_in(MyClass.std_met.__name__,))(MyClass)
self.assertEqual(Type.class_met(a, b), res)
self.assertEqual(Type.static_met(a, b), res)
self.assertEqual(Type(num).std_met(a, b), res_x2)
# une seule méthode est décorée deux fois (3ème)
Type = decorate_functions(double_result, func_name_is_in(MyClass.std_met.__name__,))(Type)
self.assertEqual(Type.class_met(a, b), res)
self.assertEqual(Type.static_met(a, b), res)
self.assertEqual(Type(num).std_met(a, b), 2 * res_x2)
# pas de changement au niveau de la classe d'origine
self.assertEqual(MyClass.class_met(a, b), res)
self.assertEqual(MyClass.static_met(a, b), res)
self.assertEqual(MyClass(num).std_met(a, b), res)
def test_class_logger(self):
def given():
self.assertEqual(hasattr(MyClass, CLASS_ATTR_LOGGER), False, "la classe initiale n'a pas de logger")
given()
Type = class_logger(MyClass)
self.assertEqual(hasattr(Type, CLASS_ATTR_LOGGER), True)
self.assertEqual(getattr(Type, CLASS_ATTR_LOGGER).name, get_logger_name(MyClass))
# 2ème décoration
Type = class_logger(Type)
self.assertEqual(hasattr(Type, CLASS_ATTR_LOGGER), True)
self.assertEqual(getattr(Type, CLASS_ATTR_LOGGER).name, get_logger_name(MyClass))
# pas de changement au niveau de la classe d'origine
self.assertEqual(hasattr(MyClass, CLASS_ATTR_LOGGER), False)

View File

@@ -0,0 +1,30 @@
from .samples import my_func, MyClass
from ...utils.functions import find_class
from django.test import SimpleTestCase
class FunctionsTest(SimpleTestCase):
def test_find_class(self):
MainType = MyClass
main_type = MainType(11)
SubType = MyClass.MySubClass
sub_type = SubType(13)
def local_func(a: int, b: int):
return a + b
# fonction native : None
self.assertIsNone(find_class(str.join))
# fonctions : None
self.assertIsNone(find_class(local_func))
self.assertIsNone(find_class(my_func))
# méthodes
self.assertEqual(find_class(MainType.class_met), MainType)
self.assertEqual(find_class(MainType.static_met), MainType)
self.assertEqual(find_class(main_type.std_met), MainType)
self.assertEqual(find_class(SubType.sub_class_met), SubType)
self.assertEqual(find_class(SubType.sub_static_met), SubType)
self.assertEqual(find_class(sub_type.sub_std_met), SubType)

View File

@@ -0,0 +1,104 @@
import random
import string
from unittest import mock
from django.test import SimpleTestCase
from ...utils.logging import (TAG_DATA_FEED, TAG_PERF, get_logger,
get_logger_name)
from .samples import MyClass, my_func
class LoggingTest(SimpleTestCase):
def test_get_logger_name(self):
module_name = __name__
ext_module_name = my_func.__module__
MainType = MyClass
main_type = MainType(11)
main_type_logger = f'{ext_module_name}.{MainType.__qualname__}'
SubType = MyClass.MySubClass
sub_type = SubType(13)
sub_type_logger = f'{ext_module_name}.{SubType.__qualname__}'
def local_func(a: int, b: int):
return a + b
# None
self.assertIsNone(get_logger_name())
self.assertIsNone(get_logger_name(None))
# classes natives
self.assertIsNone(get_logger_name(str))
self.assertIsNone(get_logger_name(int))
self.assertIsNone(get_logger_name(dict))
self.assertIsNone(get_logger_name(list))
self.assertIsNone(get_logger_name(tuple))
# instances de classes natives (mais pas str)
self.assertIsNone(get_logger_name(1))
self.assertIsNone(get_logger_name({'a': 'b'}))
self.assertIsNone(get_logger_name(['c']))
self.assertIsNone(get_logger_name(('d',)))
# fonctions natives
self.assertIsNone(get_logger_name(str.join))
# str
self.assertEqual(get_logger_name('test'), 'test')
self.assertEqual(get_logger_name(''), None)
# classes
self.assertEqual(get_logger_name(MainType), main_type_logger)
self.assertEqual(get_logger_name(SubType), sub_type_logger)
# instances de classes
self.assertEqual(get_logger_name(main_type), main_type_logger)
self.assertEqual(get_logger_name(sub_type), sub_type_logger)
# fonctions et méthodes
self.assertEqual(get_logger_name(local_func), module_name)
self.assertEqual(get_logger_name(my_func), ext_module_name)
self.assertEqual(get_logger_name(MainType.class_met), main_type_logger)
self.assertEqual(get_logger_name(MainType.static_met), main_type_logger)
self.assertEqual(get_logger_name(main_type.std_met), main_type_logger)
self.assertEqual(get_logger_name(SubType.sub_class_met), sub_type_logger)
self.assertEqual(get_logger_name(SubType.sub_static_met), sub_type_logger)
self.assertEqual(get_logger_name(sub_type.sub_std_met), sub_type_logger)
def test_get_logger_without_tags(self):
func_1 = MyClass.class_met
func_2 = MyClass.static_met
def given():
self.assertEqual(get_logger_name(func_1), get_logger_name(func_2), 'le nom de logger doit être le même')
given()
msg = "l'instance doit être la même"
logger_name = ''.join(random.choice(string.ascii_lowercase) for i in range(10))
self.assertIs(get_logger(logger_name), get_logger(logger_name), msg)
self.assertIs(get_logger(func_1), get_logger(func_2), msg)
def test_get_logger_with_tags(self):
func_1 = MyClass.class_met
func_2 = MyClass.static_met
def given():
self.assertEqual(get_logger_name(func_1), get_logger_name(func_2), 'le nom de logger doit être le même')
given()
msg = "l'instance doit être la même"
for tags in [TAG_PERF, TAG_DATA_FEED, ('1', '2'), ['3', '4'], set(['5', '6'])]:
with self.subTest(tags=tags):
logger_name = ''.join(random.choice(string.ascii_lowercase) for i in range(10))
self.assertIs(get_logger(logger_name, tags), get_logger(logger_name, tags), msg)
self.assertIs(get_logger(func_1, tags), get_logger(func_2, tags), msg)
prev_logger = None
logger_name = ''.join(random.choice(string.ascii_lowercase) for i in range(10))
for i, tags in enumerate([('1', '2', '3', '4'), ['3', '4', '1', '2'], set(['2', '1', '3', '4', '1'])]):
curr_logger = get_logger(logger_name, tags)
if i > 0:
self.assertIs(curr_logger, prev_logger, msg)
prev_logger = get_logger(logger_name, tags)

View File

@@ -0,0 +1,127 @@
from .samples import my_func, _my_private_func, MyClass
from ...utils.predicates import func_class_name_is_in, func_class_name_is_not_in, func_name_is_in, func_name_is_not_in, func_is_public
from django.test import SimpleTestCase
class PredicatesTest(SimpleTestCase):
def __test_func_class_name_is_in(self, negation: bool = False):
""" teste func_class_name_is_in et func_class_name_is_not_in """
func = func_class_name_is_not_in if negation else func_class_name_is_in
result = negation is not True
own_name = self.__class__.__name__
MainType = MyClass
main_type = MainType(11)
main_type_name = MainType.__name__
SubType = MyClass.MySubClass
sub_type = SubType(13)
sub_type_name = SubType.__name__
def local_func(a: int, b: int):
return a + b
# fonction native : None
self.assertEqual(func(str.__name__)(str.join), False)
# fonction locale : None
self.assertEqual(func(own_name)(local_func), False)
# méthodes
predicate = func(main_type_name)
self.assertEqual(predicate(MainType.class_met), result)
self.assertEqual(predicate(MainType.static_met), result)
self.assertEqual(predicate(main_type.std_met), result)
self.assertEqual(predicate(SubType.sub_class_met), not result)
self.assertEqual(predicate(SubType.sub_static_met), not result)
self.assertEqual(predicate(sub_type.sub_std_met), not result)
predicate = func(sub_type_name)
self.assertEqual(predicate(MainType.class_met), not result)
self.assertEqual(predicate(MainType.static_met), not result)
self.assertEqual(predicate(main_type.std_met), not result)
self.assertEqual(predicate(SubType.sub_class_met), result)
self.assertEqual(predicate(SubType.sub_static_met), result)
self.assertEqual(predicate(sub_type.sub_std_met), result)
def __test_func_name_is_in(self, negation: bool = False):
""" teste func_name_is_in et func_name_is_not_in """
func = func_name_is_not_in if negation else func_name_is_in
result = negation is not True
MainType = MyClass
main_type = MainType(11)
SubType = MyClass.MySubClass
sub_type = SubType(13)
def local_func(a: int, b: int):
return a + b
# fonction native
self.assertEqual(func('join')(str.join), result)
self.assertEqual(func('other', 'join')(str.join), result)
# autre fonction, fonction locale
self.assertEqual(func('local_func')(local_func), result)
self.assertEqual(func('other', 'local_func')(local_func), result)
self.assertEqual(func('my_func')(my_func), result)
self.assertEqual(func('other', 'my_func')(my_func), result)
# méthodes
predicate = func('class_met', 'std_met', 'sub_static_met')
self.assertEqual(predicate(MainType.class_met), result)
self.assertEqual(predicate(MainType.static_met), not result)
self.assertEqual(predicate(main_type.std_met), result)
self.assertEqual(predicate(SubType.sub_class_met), not result)
self.assertEqual(predicate(SubType.sub_static_met), result)
self.assertEqual(predicate(sub_type.sub_std_met), not result)
predicate = func('static_met', 'sub_class_met', 'sub_std_met')
self.assertEqual(predicate(MainType.class_met), not result)
self.assertEqual(predicate(MainType.static_met), result)
self.assertEqual(predicate(main_type.std_met), not result)
self.assertEqual(predicate(SubType.sub_class_met), result)
self.assertEqual(predicate(SubType.sub_static_met), not result)
self.assertEqual(predicate(sub_type.sub_std_met), result)
def test_func_class_name_is_in(self):
self.__test_func_class_name_is_in()
def test_func_class_name_is_not_in(self):
self.__test_func_class_name_is_in(negation=True)
def test_func_name_is_in(self):
self.__test_func_name_is_in()
def test_func_name_is_not_in(self):
self.__test_func_name_is_in(negation=True)
def test_func_is_public(self):
MainType = MyClass
main_type = MainType(11)
SubType = MyClass.MySubClass
sub_type = SubType(13)
def local_func(a: int, b: int):
return a + b
# fonction native
self.assertEqual(func_is_public(str.join), True)
# autre fonction, fonction locale
self.assertEqual(func_is_public(local_func), True)
self.assertEqual(func_is_public(my_func), True)
self.assertEqual(func_is_public(_my_private_func), False)
# méthodes
self.assertEqual(func_is_public(MainType._protected_met), False)
self.assertEqual(func_is_public(MainType.class_met), True)
self.assertEqual(func_is_public(MainType.static_met), True)
self.assertEqual(func_is_public(main_type.std_met), True)
self.assertEqual(func_is_public(SubType.sub_class_met), True)
self.assertEqual(func_is_public(SubType.sub_static_met), True)
self.assertEqual(func_is_public(sub_type.sub_std_met), True)

View File

@@ -0,0 +1,58 @@
# quelques définitions pour les tests ==>
def my_func(a: int, b: int):
""" fonction sans classe """
return a + b
def _my_private_func(a: int, b: int):
""" fonction sans classe (non importée par défaut avec import *) """
return a + b
class MyClass():
""" classe pour tests """
num = None
def __init__(self, num):
""" la classe est paramétrée """
self.num = num
def _protected_met(self, a: int, b: int):
""" méthode standard (protected par convention) """
return a + b
class MySubClass():
sub_num = None
def __init__(self, num):
""" la classe est paramétrée """
self.sub_num = num
@classmethod
def sub_class_met(cls):
""" méthode de classe """
return True
@staticmethod
def sub_static_met():
""" méthode statique """
return True
def sub_std_met():
""" méthode standard """
return True
@classmethod
def class_met(cls, a: int, b: int):
""" méthode de classe """
return a + b
@staticmethod
def static_met(a: int, b: int):
""" méthode statique """
return a + b
def std_met(self, a: int, b: int):
""" méthode standard """
return a + b

View File

@@ -0,0 +1,2 @@
from .decision import *
from .notation import *

View File

@@ -0,0 +1,2 @@
USERNAME = 'test'
PASSWORD = 'test'

View File

@@ -0,0 +1,147 @@
from unittest import mock
from backend.models import Administre, Decision, DecisionChoices, Poste
from backend.models import StatutPamChoices as StatutPam
from backend.utils.decisions import (KEY_CREATE, KEY_UPDATE,
get_available_decisions)
from backend.utils.permissions import (KEY_READ, KEY_WRITE, Profiles,
get_profiles_by_adm)
from backend.views import DecisionView
from django.contrib.auth import get_user_model
from rest_framework import status
from rest_framework.test import APITestCase
from .constants import PASSWORD, USERNAME
from .test_utils import TestUtilsMixin, disable_gestionnaire_permission
PK_SV_1 = 'SV1'
VIEW_TYPE = DecisionView
class DecisionViewTest(APITestCase, TestUtilsMixin):
basename = 'Decision'
@classmethod
def setUpTestData(cls):
user = get_user_model().objects.create(id=1, username=USERNAME, is_superuser=True)
user.set_password(PASSWORD)
user.save()
def setUp(self):
""" vérifie qu'il n'existe aucun administré ou poste avant le test """
self.assertEqual(Administre.objects.exists(), False, "pas d'administré avant un test")
self.assertEqual(Poste.objects.exists(), False, "pas de poste avant un test")
logged_in = self.client.login(username=USERNAME, password=PASSWORD)
self.assertTrue(logged_in, "l'utilisateur devrait être connecté")
def tearDown(self):
""" supprime tous les administrés, les postes, les décisions en fin de test """
self.client.logout()
Decision.objects.all().delete()
Poste.objects.all().delete()
Administre.objects.all().delete()
def test_list_anonymous(self):
""" vérifie que l'accès anonyme est interdit """
self.client.logout()
url = self._api_url()
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
@disable_gestionnaire_permission(VIEW_TYPE)
def test_list_authenticated(self):
""" vérifie que l'utilisateur authentifié peut récupérer des décisions """
url = self._api_url()
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
@disable_gestionnaire_permission(VIEW_TYPE)
def test_crud(self):
""" test de création, MAJ, suppression """
try:
statut_pam = next(x for x in StatutPam if x.dec_enabled)
self.assertIsNotNone(statut_pam, 'il devrait exister un statut permettant les décisions')
self.user = get_user_model().objects.first()
self.adm = Administre.objects.create(pk=1, a_statut_pam=statut_pam)
@mock.patch(
f'{get_available_decisions.__module__}.{get_profiles_by_adm.__name__}',
return_value={self.adm.pk: {KEY_READ: (), KEY_WRITE: (Profiles.FILIERE, Profiles.PCP)}}
)
def do_with_mock_profiles(mock):
user = self.user
adm = self.adm
decisions = get_available_decisions((adm,), user=user).get(adm.pk)
self.assertTrue(decisions.get(KEY_CREATE), 'il devrait exister des décisions possibles (création)')
def create(poste_id, de_decision, adm_id=adm.pk, delete_former=None):
return self.client.post(
self._api_url(),
{'administre_id': adm_id, 'poste_id': poste_id, 'de_decision': de_decision, **({'delete_former': delete_former} if isinstance(delete_former, bool) else {})}
)
postes = (Poste.objects.create(pk='11'), Poste.objects.create(pk='13'))
poste_1 = postes[0].pk
poste_2 = postes[1].pk
dec_status_1 = decisions.get(KEY_CREATE)[0]
# création initiale
response = create(poste_1, dec_status_1)
qs_decision1 = Decision.objects.filter(pk=adm.pk, poste_id=poste_1, de_decision=dec_status_1)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(1, Decision.objects.count(), "il doit exister une seule décision")
self.assertTrue(qs_decision1.exists(), "la décision n'a pas les bonnes données")
# création bis, pas possible sans forcer
response = create(poste_2, dec_status_1)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(1, Decision.objects.count(), "il doit exister une seule décision")
self.assertTrue(qs_decision1.exists(), "la première décision doit encore exister")
# création bis en forçant
notes = 'notes'
qs_decision1.update(de_notes_gestionnaire='notes')
self.assertEqual(notes, qs_decision1.first().de_notes_gestionnaire, "les notes doivent être sauvegardées pour le prochain test")
response = create(poste_2, dec_status_1, delete_former=True)
qs_decision2 = Decision.objects.filter(pk=adm.pk, poste_id=poste_2, de_decision=dec_status_1)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(1, Decision.objects.count(), "il doit exister une seule décision")
self.assertFalse(qs_decision1.exists(), "la première décision ne doit plus exister")
self.assertTrue(qs_decision2.exists(), "la deuxième décision n'a pas les bonnes données")
self.assertIsNone(qs_decision2.first().de_notes_gestionnaire, "il ne doit plus exister de notes dans la deuxième décision")
# MAJ
adm = Administre.objects.filter(pk=adm.pk).first()
decisions = get_available_decisions((adm,), user=user).get(adm.pk)
self.assertTrue(decisions.get(KEY_UPDATE), 'il devrait exister des décisions possibles (MAJ) pour le prochain test')
dec_status_2 = decisions.get(KEY_UPDATE)[0]
response = self.client.patch(self._api_url(adm.pk), data={'de_decision': dec_status_2})
qs_decision3 = Decision.objects.filter(pk=adm.pk, poste_id=poste_2, de_decision=dec_status_2)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(1, Decision.objects.count(), "il doit exister une seule décision")
self.assertTrue(qs_decision3.exists(), "la deuxième décision doit changer de statut")
# suppression
response = self.client.delete(self._api_url(adm.pk))
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.assertEqual(0, Decision.objects.count(), "il ne doit plus exister de décision")
do_with_mock_profiles()
finally:
self.user = None
self.adm = None

View File

@@ -0,0 +1,46 @@
from backend.views import NotationView
from django.contrib.auth import get_user_model
from rest_framework import status
from rest_framework.test import APITestCase
from .constants import PASSWORD, USERNAME
from .test_utils import TestUtilsMixin, disable_gestionnaire_permission
VIEW_TYPE = NotationView
class NotationViewTest(APITestCase, TestUtilsMixin):
basename = 'Notation'
@classmethod
def setUpTestData(cls):
user = get_user_model().objects.create(id=1, username=USERNAME)
user.set_password(PASSWORD)
user.save()
def setUp(self):
logged_in = self.client.login(username=USERNAME, password=PASSWORD)
self.assertTrue(logged_in, "l'utilisateur devrait être connecté")
def tearDown(self):
self.client.logout()
def test_list_anonymous(self):
""" vérifie que l'accès anonyme est interdit """
self.client.logout()
url = self._api_url()
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
@disable_gestionnaire_permission(VIEW_TYPE)
def test_list_authenticated(self):
""" vérifie que l'utilisateur authentifié peut récupérer des décisions """
url = self._api_url()
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)

View File

@@ -0,0 +1,44 @@
from django.urls import reverse
from typing import Any
from unittest import mock
def disable_gestionnaire_permission(type):
"""
Utilise "mock" pour ignorer le gestionnaire de permissions dans la vue.
TODO trouver un mécanisme de mock plus précis
:param type: le type de la vue
:type type: class:`ModelViewSet` par exemple
:return: résultat du mock
:rtype: voir mock
"""
return mock.patch.object(type, 'permission_classes', [p for p in type.permission_classes if p.__name__ != 'GestionnairePermission'])
def api_url(basename: str, pk: Any = None) -> str:
"""
renvoie l'URL selon qu'on a besoin d'un ID ou non
nécessite l'utilisation de SimpleRouter ou DefaultRouter (voir urls.py)
:param basename: valeur de "basename"
:type basename: str
:param pk: valeur qui permet de savoir s'il s'agit d'une URL "list" ou "detail" (voir https://www.django-rest-framework.org/api-guide/routers/#simplerouter)
:type pk: Any
:return: URL
:rtype: str
"""
return reverse(f'{basename}-list') if pk is None else reverse(f'{basename}-detail', args=[pk])
class TestUtilsMixin:
"""
mixin à ajouter pour les tests
"""
def _api_url(self, pk: Any = None) -> str:
""" voir fonction "api_url", nécessite un champ "basename" pour remplir le paramètre """
return api_url(self.basename, pk)