From 9ecca86e66e6c96eb1db41f6a53993ee03e7b9cb Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Fri, 22 Apr 2022 10:28:55 +0200 Subject: [PATCH] Added unit tests --- modoboa/admin/api/v2/serializers.py | 8 +- modoboa/admin/api/v2/tests.py | 125 ++++++++++++++++++++++++++++ modoboa/admin/api/v2/viewsets.py | 4 +- modoboa/admin/tests/test_export.py | 40 ++++----- 4 files changed, 155 insertions(+), 22 deletions(-) diff --git a/modoboa/admin/api/v2/serializers.py b/modoboa/admin/api/v2/serializers.py index c8035d64c..e520e71ff 100644 --- a/modoboa/admin/api/v2/serializers.py +++ b/modoboa/admin/api/v2/serializers.py @@ -551,8 +551,14 @@ class UserForwardSerializer(serializers.Serializer): class CSVImportSerializer(serializers.Serializer): - """Serializer for all CSV import endpoints.""" + """Base serializer for all CSV import endpoints.""" sourcefile = serializers.FileField() sepchar = serializers.CharField(required=False, default=";") continue_if_exists = serializers.BooleanField(default=False) + + +class CSVIdentityImportSerializer(CSVImportSerializer): + """Custom serializer for identity import.""" + + crypt_password = serializers.BooleanField() diff --git a/modoboa/admin/api/v2/tests.py b/modoboa/admin/api/v2/tests.py index 344f5f723..db3578d16 100644 --- a/modoboa/admin/api/v2/tests.py +++ b/modoboa/admin/api/v2/tests.py @@ -1,6 +1,8 @@ """API v2 tests.""" +from django.core.files.base import ContentFile from django.urls import reverse +from django.utils.encoding import force_text from rest_framework.authtoken.models import Token @@ -126,6 +128,46 @@ class DomainViewSetTestCase(ModoAPITestCase): resp = self.client.post(url, data, format="json") self.assertEqual(resp.status_code, 200) + def test_domains_import(self): + f = ContentFile(b"""domain; domain1.com; 1000; 100; True +domain; domain2.com; 1000; 200; False +domainalias; domalias1.com; domain1.com; True +""", name="domains.csv") + self.client.post( + reverse("v2:domain-import-from-csv"), { + "sourcefile": f + } + ) + admin = core_models.User.objects.get(username="admin") + dom = models.Domain.objects.get(name="domain1.com") + self.assertEqual(dom.quota, 1000) + self.assertEqual(dom.default_mailbox_quota, 100) + self.assertTrue(dom.enabled) + self.assertTrue(admin.is_owner(dom)) + domalias = models.DomainAlias.objects.get(name="domalias1.com") + self.assertEqual(domalias.target, dom) + self.assertTrue(dom.enabled) + self.assertTrue(admin.is_owner(domalias)) + dom = models.Domain.objects.get(name="domain2.com") + self.assertEqual(dom.default_mailbox_quota, 200) + self.assertFalse(dom.enabled) + self.assertTrue(admin.is_owner(dom)) + + def test_export_domains(self): + """Check domain export.""" + dom = models.Domain.objects.get(name="test.com") + factories.DomainAliasFactory(name="alias.test", target=dom) + response = self.client.get(reverse("v2:domain-export")) + expected_response = [ + "domain,test.com,50,10,True", + "domainalias,alias.test,test.com,True", + "domain,test2.com,0,0,True", + ] + self.assertListEqual( + expected_response, + force_text(response.content.strip()).split("\r\n") + ) + class AccountViewSetTestCase(ModoAPITestCase): @@ -269,6 +311,89 @@ class IdentityViewSetTestCase(ModoAPITestCase): self.assertEqual(resp.status_code, 200) self.assertEqual(len(resp.json()), 8) + def test_import(self): + f = ContentFile(""" +account; user1@test.com; toto; User; One; True; SimpleUsers; user1@test.com; 0 +account; Truc@test.com; toto; René; Truc; True; DomainAdmins; truc@test.com; 5; test.com +alias; alias1@test.com; True; user1@test.com +forward; alias2@test.com; True; user1+ext@test.com +forward; fwd1@test.com; True; user@extdomain.com +dlist; dlist@test.com; True; user1@test.com; user@extdomain.com +""", name="identities.csv") # NOQA:E501 + self.client.post( + reverse("v2:identities-import-from-csv"), + {"sourcefile": f, "crypt_password": True} + ) + admin = core_models.User.objects.get(username="admin") + u1 = core_models.User.objects.get(username="user1@test.com") + mb1 = u1.mailbox + self.assertTrue(admin.is_owner(u1)) + self.assertEqual(u1.email, "user1@test.com") + self.assertEqual(u1.first_name, "User") + self.assertEqual(u1.last_name, "One") + self.assertTrue(u1.is_active) + self.assertEqual(u1.role, "SimpleUsers") + self.assertTrue(mb1.use_domain_quota) + self.assertEqual(mb1.quota, 0) + self.assertTrue(admin.is_owner(mb1)) + self.assertEqual(mb1.full_address, "user1@test.com") + self.assertTrue( + self.client.login(username="user1@test.com", password="toto") + ) + + da = core_models.User.objects.get(username="truc@test.com") + damb = da.mailbox + self.assertEqual(da.first_name, u"René") + self.assertEqual(da.role, "DomainAdmins") + self.assertEqual(damb.quota, 5) + self.assertFalse(damb.use_domain_quota) + self.assertEqual(damb.full_address, "truc@test.com") + dom = models.Domain.objects.get(name="test.com") + self.assertIn(da, dom.admins) + u = core_models.User.objects.get(username="user@test.com") + self.assertTrue(da.can_access(u)) + + al = models.Alias.objects.get(address="alias1@test.com") + self.assertTrue( + al.aliasrecipient_set + .filter(r_mailbox=u1.mailbox).exists() + ) + self.assertTrue(admin.is_owner(al)) + + fwd = models.Alias.objects.get(address="fwd1@test.com") + self.assertTrue( + fwd.aliasrecipient_set + .filter( + address="user@extdomain.com", r_mailbox__isnull=True, + r_alias__isnull=True) + .exists() + ) + self.assertTrue(admin.is_owner(fwd)) + + dlist = models.Alias.objects.get(address="dlist@test.com") + self.assertTrue( + dlist.aliasrecipient_set + .filter(r_mailbox=u1.mailbox).exists() + ) + self.assertTrue( + dlist.aliasrecipient_set.filter(address="user@extdomain.com") + .exists() + ) + self.assertTrue(admin.is_owner(dlist)) + + def test_export(self): + response = self.client.get(reverse("v2:identities-export")) + expected_response = "account,admin,,,,True,SuperAdmins,,\r\naccount,admin@test.com,{PLAIN}toto,,,True,DomainAdmins,admin@test.com,10,test.com\r\naccount,admin@test2.com,{PLAIN}toto,,,True,DomainAdmins,admin@test2.com,10,test2.com\r\naccount,user@test.com,{PLAIN}toto,,,True,SimpleUsers,user@test.com,10\r\naccount,user@test2.com,{PLAIN}toto,,,True,SimpleUsers,user@test2.com,10\r\nalias,alias@test.com,True,user@test.com\r\nalias,forward@test.com,True,user@external.com\r\nalias,postmaster@test.com,True,test@truc.fr,toto@titi.com\r\n" # NOQA:E501 + received_content = force_text(response.content.strip()).split("\r\n") + # Empty admin password because it is hashed using SHA512-CRYPT + admin_row = received_content[0].split(",") + admin_row[2] = "" + received_content[0] = ",".join(admin_row) + self.assertCountEqual( + expected_response.strip().split("\r\n"), + received_content + ) + class AliasViewSetTestCase(ModoAPITestCase): diff --git a/modoboa/admin/api/v2/viewsets.py b/modoboa/admin/api/v2/viewsets.py index 8912fad5f..f4908a643 100644 --- a/modoboa/admin/api/v2/viewsets.py +++ b/modoboa/admin/api/v2/viewsets.py @@ -230,7 +230,7 @@ class IdentityViewSet(viewsets.ViewSet): return response.Response(result) @extend_schema( - request=serializers.CSVImportSerializer + request=serializers.CSVIdentityImportSerializer ) @action(methods=["post"], detail=False, @@ -238,7 +238,7 @@ class IdentityViewSet(viewsets.ViewSet): url_path="import") def import_from_csv(self, request, **kwargs): """Import accounts and aliases from CSV file.""" - serializer = serializers.CSVImportSerializer(data=request.data) + serializer = serializers.CSVIdentityImportSerializer(data=request.data) serializer.is_valid(raise_exception=True) status, msg = lib.import_data( request.user, diff --git a/modoboa/admin/tests/test_export.py b/modoboa/admin/tests/test_export.py index 830dd6eab..2aecc7a22 100644 --- a/modoboa/admin/tests/test_export.py +++ b/modoboa/admin/tests/test_export.py @@ -38,15 +38,6 @@ class ExportTestCase(ModoTestCase): {"filename": "test.csv"} ) - def assertListEqual(self, list1, list2): # NOQA:N802 - list1 = force_text(list1).split("\r\n") - list2 = force_text(list2).split("\r\n") - self.assertEqual(len(list1), len(list2)) - for entry in list1: - if not entry: - continue - self.assertIn(entry, list2) - def test_export_domains(self): """Check domain export.""" dom = models.Domain.objects.get(name="test.com") @@ -58,8 +49,8 @@ class ExportTestCase(ModoTestCase): "domain;test2.com;0;0;True", ] self.assertListEqual( - "\r\n".join(expected_response), - response.content.strip() + expected_response, + force_text(response.content.strip()).split("\r\n") ) # Test management command too. @@ -67,12 +58,21 @@ class ExportTestCase(ModoTestCase): call_command("modo", "export", "domains") response = sys.stdout.getvalue() sys.stdout = stdout_backup - self.assertListEqual("\r\n".join(expected_response), response.strip()) + self.assertListEqual( + expected_response, force_text(response.strip()).split("\r\n")) def test_export_identities(self): response = self.__export_identities() - expected_response = "account;admin@test.com;{PLAIN}toto;;;True;DomainAdmins;admin@test.com;10;test.com\r\naccount;admin@test2.com;{PLAIN}toto;;;True;DomainAdmins;admin@test2.com;10;test2.com\r\naccount;user@test.com;{PLAIN}toto;;;True;SimpleUsers;user@test.com;10\r\naccount;user@test2.com;{PLAIN}toto;;;True;SimpleUsers;user@test2.com;10\r\nalias;alias@test.com;True;user@test.com\r\nalias;forward@test.com;True;user@external.com\r\nalias;postmaster@test.com;True;test@truc.fr;toto@titi.com\r\n" # NOQA:E501 - self.assertListEqual(expected_response, response.content.strip()) + expected_response = "account;admin;;;;True;SuperAdmins;;\r\naccount;admin@test.com;{PLAIN}toto;;;True;DomainAdmins;admin@test.com;10;test.com\r\naccount;admin@test2.com;{PLAIN}toto;;;True;DomainAdmins;admin@test2.com;10;test2.com\r\naccount;user@test.com;{PLAIN}toto;;;True;SimpleUsers;user@test.com;10\r\naccount;user@test2.com;{PLAIN}toto;;;True;SimpleUsers;user@test2.com;10\r\nalias;alias@test.com;True;user@test.com\r\nalias;forward@test.com;True;user@external.com\r\nalias;postmaster@test.com;True;test@truc.fr;toto@titi.com\r\n" # NOQA:E501 + received_content = force_text(response.content.strip()).split("\r\n") + # Empty admin password because it is hashed using SHA512-CRYPT + admin_row = received_content[0].split(";") + admin_row[2] = "" + received_content[0] = ";".join(admin_row) + self.assertCountEqual( + expected_response.strip().split("\r\n"), + received_content + ) def test_export_simpleusers(self): factories.MailboxFactory( @@ -83,9 +83,10 @@ class ExportTestCase(ModoTestCase): response = self.__export_identities( idtfilter="account", grpfilter="SimpleUsers" ) - self.assertListEqual( - "account;user@test.com;{PLAIN}toto;;;True;SimpleUsers;user@test.com;10\r\naccount;user@test2.com;{PLAIN}toto;;;True;SimpleUsers;user@test2.com;10\r\naccount;toto@test.com;{PLAIN}toto;Léon;;True;SimpleUsers;toto@test.com;10", # NOQA:E501 - response.content.strip() + expected_response = "account;user@test.com;{PLAIN}toto;;;True;SimpleUsers;user@test.com;10\r\naccount;user@test2.com;{PLAIN}toto;;;True;SimpleUsers;user@test2.com;10\r\naccount;toto@test.com;{PLAIN}toto;Léon;;True;SimpleUsers;toto@test.com;10" # NOQA:E501 + self.assertCountEqual( + expected_response.split("\r\n"), + force_text(response.content.strip()).split("\r\n") ) def test_export_superadmins(self): @@ -107,9 +108,10 @@ class ExportTestCase(ModoTestCase): response = self.__export_identities( idtfilter="account", grpfilter="DomainAdmins" ) + expected_response = "account;admin@test.com;{PLAIN}toto;;;True;DomainAdmins;admin@test.com;10;test.com\r\naccount;admin@test2.com;{PLAIN}toto;;;True;DomainAdmins;admin@test2.com;10;test2.com" # NOQA:E501 self.assertListEqual( - "account;admin@test.com;{PLAIN}toto;;;True;DomainAdmins;admin@test.com;10;test.com\r\naccount;admin@test2.com;{PLAIN}toto;;;True;DomainAdmins;admin@test2.com;10;test2.com", # NOQA:E501 - response.content.strip() + expected_response.split("\r\n"), + force_text(response.content.strip()).split("\r\n") ) def test_export_aliases(self):