From 6b777754e523bfc6bd501476e8f19b4b89b6e4cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kervella?= Date: Wed, 23 May 2018 15:26:41 +0000 Subject: [PATCH] API: Add tests --- api/tests.py | 330 ++++++++++++++++++++++++++++++++- re2o/settings_local.example.py | 4 + 2 files changed, 332 insertions(+), 2 deletions(-) diff --git a/api/tests.py b/api/tests.py index bfcda28f..aa4747eb 100644 --- a/api/tests.py +++ b/api/tests.py @@ -23,6 +23,332 @@ The tests for the API module. """ -# from django.test import TestCase +import json +from rest_framework.test import APITestCase +from requests import codes + +#import cotisations.models as cotisations +#import machines.models as machines +#import topologie.models as topologie +import users.models as users + + +class APIEndpointsTestCase(APITestCase): + # URLs that don't require to be authenticated + no_auth_endpoints = [ + '/api/' + ] + # URLs that require to be authenticated and have no special permissions + auth_no_perm_endpoints = [] + # URLs that require to be authenticated and have special permissions + auth_perm_endpoints = [ + '/api/cotisations/articles/', +# '/api/cotisations/articles//', + '/api/cotisations/banques/', +# '/api/cotisations/banques//', + '/api/cotisations/cotisations/', +# '/api/cotisations/cotisations//', + '/api/cotisations/factures/', +# '/api/cotisations/factures//', + '/api/cotisations/paiements/', +# '/api/cotisations/paiements//', + '/api/cotisations/ventes/', +# '/api/cotisations/ventes//', + '/api/machines/domains/', +# '/api/machines/domains//', + '/api/machines/extensions/', +# '/api/machines/extensions//', + '/api/machines/interfaces/', +# '/api/machines/interfaces//', + '/api/machines/iplists/', +# '/api/machines/iplists//', + '/api/machines/iptypes/', +# '/api/machines/iptypes//', + '/api/machines/ipv6lists/', +# '/api/machines/ipv6lists//', + '/api/machines/machines/', +# '/api/machines/machines//', + '/api/machines/machinetypes/', +# '/api/machines/machinetypes//', + '/api/machines/mx/', +# '/api/machines/mx//', + '/api/machines/nas/', +# '/api/machines/nas//', + '/api/machines/ns/', +# '/api/machines/ns//', + '/api/machines/ouvertureportlists/', +# '/api/machines/ouvertureportlists//', + '/api/machines/ouvertureports/', +# '/api/machines/ouvertureports//', + '/api/machines/servicelinks/', +# '/api/machines/servicelinks//', + '/api/machines/services/', +# '/api/machines/services//', + '/api/machines/soa/', +# '/api/machines/soa//', + '/api/machines/srv/', +# '/api/machines/srv//', + '/api/machines/txt/', +# '/api/machines/txt//', + '/api/machines/vlans/', +# '/api/machines/vlans//', + '/api/topologie/acesspoint/', +# '/api/topologie/acesspoint//', + '/api/topologie/building/', +# '/api/topologie/building//', + '/api/topologie/constructorswitch/', +# '/api/topologie/constructorswitch//', + '/api/topologie/modelswitch/', +# '/api/topologie/modelswitch//', + '/api/topologie/room/', +# '/api/topologie/room//', + '/api/topologie/stack/', +# '/api/topologie/stack//', + '/api/topologie/switch/', +# '/api/topologie/switch//', + '/api/topologie/switchbay/', +# '/api/topologie/switchbay//', + '/api/topologie/switchport/', +# '/api/topologie/switchport//', + '/api/users/adherents/', +# '/api/users/adherents//', + '/api/users/bans/', +# '/api/users/bans//', + '/api/users/clubs/', +# '/api/users/clubs//', + '/api/users/listrights/', +# '/api/users/listrights//', + '/api/users/schools/', +# '/api/users/schools//', + '/api/users/serviceusers/', +# '/api/users/serviceusers//', + '/api/users/shells/', +# '/api/users/shells//', + '/api/users/users/', +# '/api/users/users//', + '/api/users/whitelists/', +# '/api/users/whitelists//', + ] + + stduser = None + superuser = None + + @classmethod + def setUpTestData(cls): + # A user with no rights + cls.stduser = users.User.objects.create_user( + "apistduser", + "apistduser", + "apistduser@example.net", + "apistduser" + ) + # A user with all the rights + cls.superuser = users.User.objects.create_superuser( + "apisuperuser", + "apisuperuser", + "apisuperuser@example.net", + "apisuperuser" + ) + + # TODO : + # Create 1 object of every model so there is an exisiting object + # when quering for pk=1 + + @classmethod + def tearDownClass(cls): + cls.stduser.delete() + cls.superuser.delete() + super().tearDownClass() + + def check_responses_code(self, urls, expected_code, formats=[None], + assert_more=None): + """ + Utility function to test if a list of urls answer an expected code + + :param urls: (list) The list of urls to test + :param expected_code: (int) The HTTP return code expected + :param formats: (list) The list of formats to use for the request + (Default: [None]) + :param assert_more: (func) A function to assert more specific data + in the same test. It is evaluated with the responsem object, the + url and the format used. + """ + for url in urls: + for format in formats: + with self.subTest(url=url, format=format): + response = self.client.get(url, format=format) + assert response.status_code == expected_code + if assert_more: + assert_more(response, url, format) + + def test_no_auth_endpoints_with_no_auth(self): + """ + Test that every endpoint that does not require to be authenticated, + returns a Ok (200) response when not authenticated. + """ + urls = [endpoint.replace('', '1') + for endpoint in self.no_auth_endpoints] + self.check_responses_code(urls, codes.ok) + + def test_auth_endpoints_with_no_auth(self): + """ + Test that every endpoint that does require to be authenticated, + returns a Unauthorized (401) response when not authenticated. + """ + urls = [endpoint.replace('', '1') for endpoint in \ + self.auth_no_perm_endpoints + self.auth_perm_endpoints] + self.check_responses_code(urls, codes.unauthorized) + + def test_no_auth_endpoints_with_auth(self): + """ + Test that every endpoint that does not require to be authenticated, + returns a Ok (200) response when authenticated. + """ + self.client.force_authenticate(user=self.stduser) + urls = [endpoint.replace('', '1') + for endpoint in self.no_auth_endpoints] + self.check_responses_code(urls, codes.ok) + + def test_auth_no_perm_endpoints_with_auth_and_no_perm(self): + """ + Test that every endpoint that does require to be authenticated and + no special permissions, returns a Ok (200) response when + authenticated but without permissions. + """ + self.client.force_authenticate(user=self.stduser) + urls = [endpoint.replace('', '1') + for endpoint in self.auth_no_perm_endpoints] + self.check_responses_code(urls, codes.ok) + + def test_auth_perm_endpoints_with_auth_and_no_perm(self): + """ + Test that every endpoint that does require to be authenticated and + special permissions, returns a Forbidden (403) response when + authenticated but without permissions. + """ + self.client.force_authenticate(user=self.stduser) + urls = [endpoint.replace('', '1') + for endpoint in self.auth_perm_endpoints] + self.check_responses_code(urls, codes.forbidden) + + def test_auth_endpoints_with_auth_and_perm(self): + """ + Test that every endpoint that does require to be authenticated, + returns a Ok (200) response when authenticated with all permissions + """ + self.client.force_authenticate(user=self.superuser) + urls = [endpoint.replace('', '1') for endpoint \ + in self.auth_no_perm_endpoints + self.auth_perm_endpoints] + self.check_responses_code(urls, codes.ok) + + def test_endpoints_not_found(self): + """ + Test that every endpoint that uses a primary key parameter, + returns a Not Found (404) response when queried with non-existing + primary key + """ + self.client.force_authenticate(user=self.superuser) + # Select only the URLs with '' and replace it with '42' + urls = [endpoint.replace('', '42') for endpoint in \ + self.no_auth_endpoints + self.auth_no_perm_endpoints + \ + self.auth_perm_endpoints if '' in endpoint] + self.check_responses_code(urls, codes.not_found) + + def test_formats(self): + """ + Test that every endpoint returns a Ok (200) response when using + different formats. Also checks that 'json' format returns a valid json + """ + self.client.force_authenticate(user=self.superuser) + + urls = [endpoint.replace('', '1') for endpoint in \ + self.no_auth_endpoints + self.auth_no_perm_endpoints + \ + self.auth_perm_endpoints] + + def assert_more(response, url, format): + """Assert the response is valid json when format is json""" + if format is 'json': + json.loads(response.content.decode()) + + self.check_responses_code(urls, codes.ok, + formats=[None, 'json', 'api'], + assert_more=assert_more) + +class APIPaginationTestCase(APITestCase): + endpoints = [ + '/api/cotisations/articles/', + '/api/cotisations/banques/', + '/api/cotisations/cotisations/', + '/api/cotisations/factures/', + '/api/cotisations/paiements/', + '/api/cotisations/ventes/', + '/api/machines/domains/', + '/api/machines/extensions/', + '/api/machines/interfaces/', + '/api/machines/iplists/', + '/api/machines/iptypes/', + '/api/machines/ipv6lists/', + '/api/machines/machines/', + '/api/machines/machinetypes/', + '/api/machines/mx/', + '/api/machines/nas/', + '/api/machines/ns/', + '/api/machines/ouvertureportlists/', + '/api/machines/ouvertureports/', + '/api/machines/servicelinks/', + '/api/machines/services/', + '/api/machines/soa/', + '/api/machines/srv/', + '/api/machines/txt/', + '/api/machines/vlans/', + '/api/topologie/acesspoint/', + '/api/topologie/building/', + '/api/topologie/constructorswitch/', + '/api/topologie/modelswitch/', + '/api/topologie/room/', + '/api/topologie/stack/', + '/api/topologie/switch/', + '/api/topologie/switchbay/', + '/api/topologie/switchport/', + '/api/users/adherents/', + '/api/users/bans/', + '/api/users/clubs/', + '/api/users/listrights/', + '/api/users/schools/', + '/api/users/serviceusers/', + '/api/users/shells/', + '/api/users/users/', + '/api/users/whitelists/', + ] + superuser = None + + @classmethod + def setUpTestData(cls): + # A user with all the rights + cls.superuser = users.User.objects.create_superuser( + "apisuperuser", + "apisuperuser", + "apisuperuser@example.net", + "apisuperuser" + ) + + @classmethod + def tearDownClass(cls): + cls.superuser.delete() + super().tearDownClass() + + def test_pagination(self): + """ + Test that every endpoint is using the pagination correctly + """ + self.client.force_authenticate(self.superuser) + for url in self.endpoints: + with self.subTest(url=url): + response = self.client.get(url, format='json') + res_json = json.loads(response.content.decode()) + assert 'count' in res_json.keys() + assert 'next' in res_json.keys() + assert 'previous' in res_json.keys() + assert 'results' in res_json.keys() + assert not len('results') > 100 -# Create your tests here. diff --git a/re2o/settings_local.example.py b/re2o/settings_local.example.py index e15455df..662c1447 100644 --- a/re2o/settings_local.example.py +++ b/re2o/settings_local.example.py @@ -56,6 +56,10 @@ DATABASES = { 'USER': 'db_user_value', 'PASSWORD': DB_PASSWORD, 'HOST': 'db_host_value', + 'TEST': { + 'CHARSET': 'utf8', + 'COLLATION': 'utf8_general_ci' + } }, 'ldap': { # The LDAP 'ENGINE': 'ldapdb.backends.ldap',