diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a81a1b..4a794df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to ### Added +- ✨(domains) allow creation of "pending" mailboxes - ✨(teams) allow team management for team admins/owners #509 ## [1.5.0] - 2024-11-14 diff --git a/docs/interoperability/dimail.md b/docs/interoperability/dimail.md index 702eb56..e526278 100644 --- a/docs/interoperability/dimail.md +++ b/docs/interoperability/dimail.md @@ -9,21 +9,29 @@ API and its documentation can be found [here](https://api.dev.ox.numerique.gouv. ## Architectural links of dimail -As dimail's primary goal is to act as an interface between People and OX, its architecture is similar to that of People. A series of requests are sent from People to dimail upon creating domains, users and accesses. +As dimail's primary goal is to act as an interface between People and OX, its architecture is similar to that of People. A series of requests are sent from People to dimail upon creating domains, users, permissions and mailboxes. ### Domains -Upon creating a domain on People, the same domain is created on dimail and will undergo a series of checks. When all checks have passed, the domain is considered valid and mailboxes can be created on it. +Upon creating a domain on People, the same domain is created on dimail and will undergo a series of checks. When all checks have passed, the domain is considered enabled. Domains in OX have a field called "context". Context is a shared space between domains, allowing users to discover users not only on their domain but on their entire context. +### Mailboxes + +Mailboxes can be created by a domain owners or administrators in People's domain tab. + +On enabled domains, mailboxes are created at the same time on dimail (and a confirmation email is sent to the secondary email). +On pending/failed domains, mailboxes are only created locally with "pending" status and are sent to dimail upon domain's activation. +On disabled domains, mailboxes creation is not allowed. + ### Users The ones issuing requests. Dimail users will reflect domains owners and administrators on People, for logging purposes and to allow direct use of dimail, if desired. User reconciliation is made on user uuid provided by ProConnect. -### Accesses +### Permissions -As for People, an access - a permissions (or "allows" in dimail) - grants an user permission to create objects on a domain. +As for People, an access - a permissions (or an "allow" in dimail) - grants an user permission to create objects on a domain. Permissions requests are sent automatically upon : - dimail database initialisation: diff --git a/src/backend/locale/fr_FR/LC_MESSAGES/django.po b/src/backend/locale/fr_FR/LC_MESSAGES/django.po index e444e96..3d38ea9 100644 --- a/src/backend/locale/fr_FR/LC_MESSAGES/django.po +++ b/src/backend/locale/fr_FR/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: lasuite-people\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-10-15 10:52+0000\n" +"POT-Creation-Date: 2024-11-18 23:02+0000\n" "PO-Revision-Date: 2024-01-03 23:15\n" "Last-Translator: \n" "Language-Team: French\n" @@ -17,225 +17,261 @@ msgstr "" "X-Crowdin-File: backend.pot\n" "X-Crowdin-File-ID: 2\n" -#: core/admin.py:45 +#: core/admin.py:55 msgid "Personal info" msgstr "" -#: core/admin.py:47 +#: core/admin.py:57 msgid "Permissions" msgstr "" -#: core/admin.py:59 +#: core/admin.py:69 msgid "Important dates" msgstr "" -#: core/admin.py:97 +#: core/admin.py:108 msgid "User" msgstr "" -#: core/authentication/backends.py:82 +#: core/authentication/backends.py:89 msgid "User info contained no recognizable user identification" msgstr "" -#: core/authentication/backends.py:90 +#: core/authentication/backends.py:111 msgid "User account is disabled" msgstr "" -#: core/authentication/backends.py:102 +#: core/authentication/backends.py:157 msgid "Claims contained no recognizable user identification" msgstr "" +#: core/authentication/backends.py:176 +msgid "Claims contained no recognizable organization identification" +msgstr "" + #: core/enums.py:23 msgid "Failure" msgstr "" -#: core/enums.py:24 mailbox_manager/enums.py:20 +#: core/enums.py:24 mailbox_manager/enums.py:21 mailbox_manager/enums.py:30 msgid "Pending" -msgstr "" +msgstr "En attente" #: core/enums.py:25 msgid "Success" msgstr "" -#: core/models.py:42 +#: core/models.py:46 msgid "Member" msgstr "" -#: core/models.py:43 mailbox_manager/enums.py:13 +#: core/models.py:47 core/models.py:59 mailbox_manager/enums.py:14 msgid "Administrator" msgstr "" -#: core/models.py:44 mailbox_manager/enums.py:14 +#: core/models.py:48 mailbox_manager/enums.py:15 msgid "Owner" msgstr "" -#: core/models.py:56 +#: core/models.py:71 msgid "id" msgstr "" -#: core/models.py:57 +#: core/models.py:72 msgid "primary key for the record as UUID" msgstr "" -#: core/models.py:63 +#: core/models.py:78 msgid "created at" msgstr "" -#: core/models.py:64 +#: core/models.py:79 msgid "date and time at which a record was created" msgstr "" -#: core/models.py:69 +#: core/models.py:84 msgid "updated at" msgstr "" -#: core/models.py:70 +#: core/models.py:85 msgid "date and time at which a record was last updated" msgstr "" -#: core/models.py:101 +#: core/models.py:116 msgid "full name" msgstr "" -#: core/models.py:102 +#: core/models.py:117 msgid "short name" msgstr "" -#: core/models.py:107 +#: core/models.py:122 msgid "contact information" msgstr "" -#: core/models.py:108 +#: core/models.py:123 msgid "A JSON object containing the contact information" msgstr "" -#: core/models.py:122 +#: core/models.py:137 msgid "contact" msgstr "" -#: core/models.py:123 +#: core/models.py:138 msgid "contacts" msgstr "" -#: core/models.py:167 +#: core/models.py:262 core/models.py:331 mailbox_manager/models.py:24 +msgid "name" +msgstr "" + +#: core/models.py:270 +msgid "registration ID list" +msgstr "" + +#: core/models.py:279 +msgid "domain list" +msgstr "" + +#: core/models.py:289 +msgid "organization" +msgstr "" + +#: core/models.py:290 +msgid "organizations" +msgstr "" + +#: core/models.py:297 +msgid "An organization must have at least a registration ID or a domain." +msgstr "" + +#: core/models.py:316 msgid "" "Enter a valid sub. This value may contain only letters, numbers, and @/./+/-/" "_ characters." msgstr "" -#: core/models.py:173 +#: core/models.py:322 msgid "sub" msgstr "" -#: core/models.py:175 +#: core/models.py:324 msgid "" "Required. 255 characters or fewer. Letters, numbers, and @/./+/-/_ " "characters only." msgstr "" -#: core/models.py:181 core/models.py:489 +#: core/models.py:330 core/models.py:737 msgid "email address" msgstr "" -#: core/models.py:182 mailbox_manager/models.py:20 -msgid "name" -msgstr "" - -#: core/models.py:194 +#: core/models.py:343 msgid "language" msgstr "" -#: core/models.py:195 +#: core/models.py:344 msgid "The language in which the user wants to see the interface." msgstr "" -#: core/models.py:201 +#: core/models.py:350 msgid "The timezone in which the user wants to see times." msgstr "" -#: core/models.py:204 +#: core/models.py:353 msgid "device" msgstr "" -#: core/models.py:206 +#: core/models.py:355 msgid "Whether the user is a device or a real user." msgstr "" -#: core/models.py:209 +#: core/models.py:358 msgid "staff status" msgstr "" -#: core/models.py:211 +#: core/models.py:360 msgid "Whether the user can log into this admin site." msgstr "" -#: core/models.py:214 +#: core/models.py:363 msgid "active" msgstr "" -#: core/models.py:217 +#: core/models.py:366 msgid "" "Whether this user should be treated as active. Unselect this instead of " "deleting accounts." msgstr "" -#: core/models.py:229 +#: core/models.py:385 msgid "user" msgstr "" -#: core/models.py:230 +#: core/models.py:386 msgid "users" msgstr "" -#: core/models.py:306 +#: core/models.py:525 +msgid "Organization/user relation" +msgstr "" + +#: core/models.py:526 +msgid "Organization/user relations" +msgstr "" + +#: core/models.py:531 +msgid "This user is already in this organization." +msgstr "" + +#: core/models.py:563 msgid "Team" msgstr "" -#: core/models.py:307 +#: core/models.py:564 msgid "Teams" msgstr "" -#: core/models.py:367 +#: core/models.py:615 msgid "Team/user relation" msgstr "" -#: core/models.py:368 +#: core/models.py:616 msgid "Team/user relations" msgstr "" -#: core/models.py:373 +#: core/models.py:621 msgid "This user is already in this team." msgstr "" -#: core/models.py:462 +#: core/models.py:710 msgid "url" msgstr "" -#: core/models.py:463 +#: core/models.py:711 msgid "secret" msgstr "" -#: core/models.py:472 +#: core/models.py:720 msgid "Team webhook" msgstr "" -#: core/models.py:473 +#: core/models.py:721 msgid "Team webhooks" msgstr "" -#: core/models.py:506 +#: core/models.py:754 msgid "Team invitation" msgstr "" -#: core/models.py:507 +#: core/models.py:755 msgid "Team invitations" msgstr "" -#: core/models.py:532 +#: core/models.py:780 msgid "This email is already associated to a registered user." msgstr "" -#: core/models.py:574 core/models.py:580 +#: core/models.py:822 core/models.py:828 msgid "Invitation to join Desk!" msgstr "" @@ -420,65 +456,80 @@ msgstr "L'équipe de La Suite" msgid "This mail has been sent to %(email)s by %(name)s [%(href)s]" msgstr "" -#: mailbox_manager/enums.py:12 +#: mailbox_manager/admin.py:12 +msgid "Synchronise from dimail" +msgstr "" + +#: mailbox_manager/admin.py:23 +#, python-brace-format +msgid "Synchronisation failed for {domain.name} with message: [{err}]" +msgstr "" + +#: mailbox_manager/admin.py:29 +#, python-brace-format +msgid "Synchronisation succeed for {domain.name}. " +msgstr "" + +#: mailbox_manager/enums.py:13 msgid "Viewer" msgstr "" -#: mailbox_manager/enums.py:21 +#: mailbox_manager/enums.py:22 mailbox_manager/enums.py:31 msgid "Enabled" -msgstr "" +msgstr "Actif" -#: mailbox_manager/enums.py:22 +#: mailbox_manager/enums.py:23 mailbox_manager/enums.py:32 msgid "Failed" -msgstr "" +msgstr "En échec" -#: mailbox_manager/enums.py:23 +#: mailbox_manager/enums.py:24 mailbox_manager/enums.py:33 msgid "Disabled" -msgstr "" +msgstr "Désactivé" -#: mailbox_manager/models.py:31 +#: mailbox_manager/models.py:35 msgid "Mail domain" msgstr "" -#: mailbox_manager/models.py:32 +#: mailbox_manager/models.py:36 msgid "Mail domains" msgstr "" -#: mailbox_manager/models.py:98 +#: mailbox_manager/models.py:102 msgid "User/mail domain relation" msgstr "" -#: mailbox_manager/models.py:99 +#: mailbox_manager/models.py:103 msgid "User/mail domain relations" msgstr "" -#: mailbox_manager/models.py:171 +#: mailbox_manager/models.py:175 msgid "local_part" msgstr "" -#: mailbox_manager/models.py:185 +#: mailbox_manager/models.py:189 msgid "secondary email address" msgstr "" -#: mailbox_manager/models.py:190 +#: mailbox_manager/models.py:199 msgid "Mailbox" msgstr "" -#: mailbox_manager/models.py:191 +#: mailbox_manager/models.py:200 msgid "Mailboxes" msgstr "" -#: mailbox_manager/utils/dimail.py:137 +#: mailbox_manager/models.py:224 +msgid "You can't create a mailbox for a disabled domain." +msgstr "Vous ne pouvez pas créer de boîte mail pour un domain désactivé." + +#: mailbox_manager/utils/dimail.py:183 msgid "Your new mailbox information" msgstr "Informations concernant votre nouvelle boîte mail" -#: people/settings.py:134 +#: people/settings.py:135 msgid "English" msgstr "" -#: people/settings.py:135 +#: people/settings.py:136 msgid "French" msgstr "" - -#~ msgid "Regards," -#~ msgstr "Cordialement," diff --git a/src/backend/mailbox_manager/api/serializers.py b/src/backend/mailbox_manager/api/serializers.py index f5871dc..d8e87d3 100644 --- a/src/backend/mailbox_manager/api/serializers.py +++ b/src/backend/mailbox_manager/api/serializers.py @@ -16,32 +16,45 @@ class MailboxSerializer(serializers.ModelSerializer): class Meta: model = models.Mailbox - fields = ["id", "first_name", "last_name", "local_part", "secondary_email"] + fields = [ + "id", + "first_name", + "last_name", + "local_part", + "secondary_email", + "status", + ] # everything is actually read-only as we do not allow update for now - read_only_fields = ["id"] + read_only_fields = ["id", "status"] def create(self, validated_data): """ Override create function to fire a request on mailbox creation. """ - # send new mailbox request to dimail - client = DimailAPIClient() - response = client.send_mailbox_request( - validated_data, self.context["request"].user.sub - ) + mailbox_status = enums.MailDomainStatusChoices.PENDING - # fix format to have actual json, and remove uuid - mailbox_data = json.loads(response.content.decode("utf-8").replace("'", '"')) - del mailbox_data["uuid"] + if validated_data["domain"].status == enums.MailDomainStatusChoices.ENABLED: + client = DimailAPIClient() + # send new mailbox request to dimail + response = client.send_mailbox_request( + validated_data, self.context["request"].user.sub + ) + + # fix format to have actual json, and remove uuid + mailbox_data = json.loads( + response.content.decode("utf-8").replace("'", '"') + ) + del mailbox_data["uuid"] + + mailbox_status = enums.MailDomainStatusChoices.ENABLED + + # send confirmation email + client.send_new_mailbox_notification( + recipient=validated_data["secondary_email"], mailbox_data=mailbox_data + ) # actually save mailbox on our database - instance = models.Mailbox.objects.create(**validated_data) - - # send confirmation email - client.send_new_mailbox_notification( - recipient=validated_data["secondary_email"], mailbox_data=mailbox_data - ) - return instance + return models.Mailbox.objects.create(**validated_data, status=mailbox_status) class MailDomainSerializer(serializers.ModelSerializer): diff --git a/src/backend/mailbox_manager/enums.py b/src/backend/mailbox_manager/enums.py index 76218e6..9fca937 100644 --- a/src/backend/mailbox_manager/enums.py +++ b/src/backend/mailbox_manager/enums.py @@ -1,3 +1,4 @@ +# pylint: disable=too-many-ancestors """ Application enums declaration """ @@ -21,3 +22,12 @@ class MailDomainStatusChoices(models.TextChoices): ENABLED = "enabled", _("Enabled") FAILED = "failed", _("Failed") DISABLED = "disabled", _("Disabled") + + +class MailboxStatusChoices(models.TextChoices): + """Lists the possible statuses in which a mailbox can be.""" + + PENDING = "pending", _("Pending") + ENABLED = "enabled", _("Enabled") + FAILED = "failed", _("Failed") + DISABLED = "disabled", _("Disabled") diff --git a/src/backend/mailbox_manager/factories.py b/src/backend/mailbox_manager/factories.py index 48ababb..51f7eda 100644 --- a/src/backend/mailbox_manager/factories.py +++ b/src/backend/mailbox_manager/factories.py @@ -75,3 +75,9 @@ class MailboxFactory(factory.django.DjangoModelFactory): ) domain = factory.SubFactory(MailDomainEnabledFactory) secondary_email = factory.Faker("email") + + +class MailboxEnabledFactory(MailboxFactory): + """A factory to create mailbox enabled.""" + + status = enums.MailboxStatusChoices.ENABLED diff --git a/src/backend/mailbox_manager/migrations/0014_mailbox_status.py b/src/backend/mailbox_manager/migrations/0014_mailbox_status.py new file mode 100644 index 0000000..abf99f3 --- /dev/null +++ b/src/backend/mailbox_manager/migrations/0014_mailbox_status.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.3 on 2024-11-18 14:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mailbox_manager', '0013_remove_maildomain_secret'), + ] + + operations = [ + migrations.AddField( + model_name='mailbox', + name='status', + field=models.CharField(choices=[('pending', 'Pending'), ('enabled', 'Enabled'), ('failed', 'Failed'), ('disabled', 'Disabled')], default='pending', max_length=20), + ), + ] diff --git a/src/backend/mailbox_manager/models.py b/src/backend/mailbox_manager/models.py index 53e7403..3985912 100644 --- a/src/backend/mailbox_manager/models.py +++ b/src/backend/mailbox_manager/models.py @@ -10,7 +10,11 @@ from django.utils.translation import gettext_lazy as _ from core.models import BaseModel -from mailbox_manager.enums import MailDomainRoleChoices, MailDomainStatusChoices +from mailbox_manager.enums import ( + MailboxStatusChoices, + MailDomainRoleChoices, + MailDomainStatusChoices, +) class MailDomain(BaseModel): @@ -184,6 +188,11 @@ class Mailbox(BaseModel): secondary_email = models.EmailField( _("secondary email address"), null=False, blank=False ) + status = models.CharField( + max_length=20, + choices=MailboxStatusChoices.choices, + default=MailboxStatusChoices.PENDING, + ) class Meta: db_table = "people_mail_box" @@ -196,14 +205,8 @@ class Mailbox(BaseModel): def clean(self): """ - Mailboxes can only be created on enabled domains. - Also, mail-provisioning API credentials must be set for dimail to allow auth. + Mail-provisioning API credentials must be set for dimail to allow auth. """ - if self.domain.status != MailDomainStatusChoices.ENABLED: - raise exceptions.ValidationError( - "You can create mailbox only for a domain enabled" - ) - # Won't be able to query user token if MAIL_PROVISIONING_API_CREDENTIALS are not set if not settings.MAIL_PROVISIONING_API_CREDENTIALS: raise exceptions.ValidationError( @@ -216,6 +219,11 @@ class Mailbox(BaseModel): """ self.full_clean() + if self.domain.status == MailDomainStatusChoices.DISABLED: + raise exceptions.ValidationError( + _("You can't create a mailbox for a disabled domain.") + ) + if self._state.adding: return super().save(*args, **kwargs) diff --git a/src/backend/mailbox_manager/tests/api/mailboxes/test_api_mailboxes_create.py b/src/backend/mailbox_manager/tests/api/mailboxes/test_api_mailboxes_create.py index 2097e65..41896a8 100644 --- a/src/backend/mailbox_manager/tests/api/mailboxes/test_api_mailboxes_create.py +++ b/src/backend/mailbox_manager/tests/api/mailboxes/test_api_mailboxes_create.py @@ -137,6 +137,7 @@ def test_api_mailboxes__create_roles_success(role): "last_name": str(mailbox.last_name), "local_part": str(mailbox.local_part), "secondary_email": str(mailbox.secondary_email), + "status": enums.MailboxStatusChoices.ENABLED, } @@ -194,6 +195,7 @@ def test_api_mailboxes__create_with_accent_success(role): "last_name": str(mailbox.last_name), "local_part": str(mailbox.local_part), "secondary_email": str(mailbox.secondary_email), + "status": enums.MailboxStatusChoices.ENABLED, } @@ -236,6 +238,73 @@ def test_api_mailboxes__create_administrator_missing_fields(): assert response.json() == {"secondary_email": ["This field is required."]} +@pytest.mark.parametrize( + "role", + [ + enums.MailDomainRoleChoices.OWNER, + enums.MailDomainRoleChoices.ADMIN, + ], +) +def test_api_mailboxes__cannot_create_on_disabled_domain(role): + """Admin and owner users should not be able to create mailboxes for a disabled domain""" + mail_domain = factories.MailDomainFactory( + status=enums.MailDomainStatusChoices.DISABLED + ) + access = factories.MailDomainAccessFactory(role=role, domain=mail_domain) + + client = APIClient() + client.force_login(access.user) + + mailbox_values = serializers.MailboxSerializer( + factories.MailboxFactory.build() + ).data + response = client.post( + f"/api/v1.0/mail-domains/{mail_domain.slug}/mailboxes/", + mailbox_values, + format="json", + ) + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert not models.Mailbox.objects.exists() + assert response.json() == ["You can't create a mailbox for a disabled domain."] + + +@pytest.mark.parametrize( + "domain_status", + [ + enums.MailDomainStatusChoices.PENDING, + enums.MailDomainStatusChoices.FAILED, + ], +) +def test_api_mailboxes__create_pending_mailboxes(domain_status): + """ + Admin and owner users should be able to create mailboxes, including on pending and failed + domains. + Mailboxes created on pending and failed domains should have the "pending" status + """ + mail_domain = factories.MailDomainFactory(status=domain_status) + access = factories.MailDomainAccessFactory( + role=enums.MailDomainRoleChoices.ADMIN, domain=mail_domain + ) + + client = APIClient() + client.force_login(access.user) + + mailbox_values = serializers.MailboxSerializer( + factories.MailboxFactory.build() + ).data + with responses.RequestsMock(): + # We add no response in RequestsMock + # because we expect no outside calls to be made + response = client.post( + f"/api/v1.0/mail-domains/{mail_domain.slug}/mailboxes/", + mailbox_values, + format="json", + ) + assert response.status_code == status.HTTP_201_CREATED + mailbox = models.Mailbox.objects.get() + assert mailbox.status == "pending" + + ### REACTING TO DIMAIL-API ### We mock dimail's responses to avoid testing dimail's container too @@ -411,6 +480,7 @@ def test_api_mailboxes__domain_owner_or_admin_successful_creation_and_provisioni "last_name": str(mailbox_data["last_name"]), "local_part": str(mailbox_data["local_part"]), "secondary_email": str(mailbox_data["secondary_email"]), + "status": enums.MailboxStatusChoices.ENABLED, } assert mailbox.first_name == mailbox_data["first_name"] assert mailbox.last_name == mailbox_data["last_name"] diff --git a/src/backend/mailbox_manager/tests/api/mailboxes/test_api_mailboxes_list.py b/src/backend/mailbox_manager/tests/api/mailboxes/test_api_mailboxes_list.py index edd7871..b17207b 100644 --- a/src/backend/mailbox_manager/tests/api/mailboxes/test_api_mailboxes_list.py +++ b/src/backend/mailbox_manager/tests/api/mailboxes/test_api_mailboxes_list.py @@ -53,8 +53,8 @@ def test_api_mailboxes__list_authenticated(): def test_api_mailboxes__list_roles(role): """Owner, admin and viewer users should be able to list mailboxes""" mail_domain = factories.MailDomainEnabledFactory() - mailbox1 = factories.MailboxFactory(domain=mail_domain) - mailbox2 = factories.MailboxFactory(domain=mail_domain) + mailbox1 = factories.MailboxEnabledFactory(domain=mail_domain) + mailbox2 = factories.MailboxEnabledFactory(domain=mail_domain) access = factories.MailDomainAccessFactory(role=role, domain=mail_domain) client = APIClient() @@ -69,6 +69,7 @@ def test_api_mailboxes__list_roles(role): "last_name": str(mailbox2.last_name), "local_part": str(mailbox2.local_part), "secondary_email": str(mailbox2.secondary_email), + "status": enums.MailboxStatusChoices.ENABLED, }, { "id": str(mailbox1.id), @@ -76,6 +77,7 @@ def test_api_mailboxes__list_roles(role): "last_name": str(mailbox1.last_name), "local_part": str(mailbox1.local_part), "secondary_email": str(mailbox1.secondary_email), + "status": enums.MailboxStatusChoices.ENABLED, }, ] diff --git a/src/backend/mailbox_manager/tests/test_models_mailboxes.py b/src/backend/mailbox_manager/tests/test_models_mailboxes.py index 91ece88..92be194 100644 --- a/src/backend/mailbox_manager/tests/test_models_mailboxes.py +++ b/src/backend/mailbox_manager/tests/test_models_mailboxes.py @@ -74,7 +74,7 @@ def test_models_mailboxes__domain_must_be_a_maildomain_instance(): def test_models_mailboxes__domain_cannot_be_null(): """The "domain" field should not be null.""" - with pytest.raises(models.MailDomain.DoesNotExist, match="Mailbox has no domain."): + with pytest.raises(exceptions.ValidationError, match="This field cannot be null"): factories.MailboxFactory(domain=None) @@ -93,44 +93,37 @@ def test_models_mailboxes__secondary_email_cannot_be_null(): factories.MailboxFactory(secondary_email=None) -def test_models_mailboxes__cannot_be_created_for_disabled_maildomain(): - """Mailbox creation is allowed only for a domain enabled. +@pytest.mark.parametrize( + "domain_status", + [ + enums.MailDomainStatusChoices.PENDING, + enums.MailDomainStatusChoices.FAILED, + ], +) +def test_models_mailboxes__can_create_pending_mailboxes_on_non_enabled_domain( + domain_status, +): + """Mailbox creation is allowed for a domain pending and failed. + A pending mailbox is created.""" + mailbox = factories.MailboxFactory( + domain=factories.MailDomainFactory(status=domain_status) + ) + assert mailbox.status == enums.MailboxStatusChoices.PENDING + + +def test_models_mailboxes__cannot_create_mailboxes_on_disabled_domain(): + """Mailbox creation is not allowed for a domain disabled. A disabled status for the mail domain raises an error.""" with pytest.raises( exceptions.ValidationError, - match="You can create mailbox only for a domain enabled", + match="You can't create a mailbox for a disabled domain.", ): factories.MailboxFactory( domain=factories.MailDomainFactory( status=enums.MailDomainStatusChoices.DISABLED ) ) - - -def test_models_mailboxes__cannot_be_created_for_failed_maildomain(): - """Mailbox creation is allowed only for a domain enabled. - A failed status for the mail domain raises an error.""" - with pytest.raises( - exceptions.ValidationError, - match="You can create mailbox only for a domain enabled", - ): - factories.MailboxFactory( - domain=factories.MailDomainFactory( - status=enums.MailDomainStatusChoices.FAILED - ) - ) - - -def test_models_mailboxes__cannot_be_created_for_pending_maildomain(): - """Mailbox creation is allowed only for a domain enabled. - A pending status for the mail domain raises an error.""" - with pytest.raises( - exceptions.ValidationError, - match="You can create mailbox only for a domain enabled", - ): - # MailDomainFactory initializes a mail domain with default values, - # so mail domain status is pending! - factories.MailboxFactory(domain=factories.MailDomainFactory()) + assert not models.Mailbox.objects.exist() ### REACTING TO DIMAIL-API diff --git a/src/backend/mailbox_manager/tests/test_models_maildomain.py b/src/backend/mailbox_manager/tests/test_models_maildomain.py index f143e69..e2a1d98 100644 --- a/src/backend/mailbox_manager/tests/test_models_maildomain.py +++ b/src/backend/mailbox_manager/tests/test_models_maildomain.py @@ -37,6 +37,21 @@ def test_models_mail_domain__slug_inferred_from_name(): assert domain.slug == slugify(name) +# "STATUS" FIELD + + +def test_models_mail_domain__status_should_not_be_empty(): + """Status field should not be empty.""" + with pytest.raises(ValidationError, match="This field cannot be blank"): + factories.MailDomainFactory(status="") + + +def test_models_mail_domain__status_should_not_be_null(): + """Status field should not be null.""" + with pytest.raises(ValidationError, match="This field cannot be null."): + factories.MailDomainFactory(status=None) + + # get_abilities