♻️(plugins) rewrite plugin system as django app
This allow more flexibility around the installed plugins, this will allow to add models in plugins if needed.
This commit is contained in:
committed by
Sabrina Demagny
parent
4ced342062
commit
28fdee868d
1
src/backend/plugins/la_suite/tests/__init__.py
Normal file
1
src/backend/plugins/la_suite/tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Tests for the plugins module."""
|
||||
1
src/backend/plugins/la_suite/tests/hooks/__init__.py
Normal file
1
src/backend/plugins/la_suite/tests/hooks/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Test for the Organization plugins module."""
|
||||
@@ -0,0 +1,231 @@
|
||||
"""Tests for the CommuneCreation plugin."""
|
||||
|
||||
from django.conf import settings
|
||||
from django.test.utils import override_settings
|
||||
|
||||
import pytest
|
||||
import responses
|
||||
|
||||
from plugins.la_suite.hooks_utils.communes import ApiCall, CommuneCreation
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_extract_name_from_org_data_when_commune():
|
||||
"""Test the name is extracted correctly for a French commune."""
|
||||
data = {
|
||||
"results": [
|
||||
{
|
||||
"nom_complet": "COMMUNE DE VARZY",
|
||||
"nom_raison_sociale": "COMMUNE DE VARZY",
|
||||
"siege": {
|
||||
"libelle_commune": "VARZY",
|
||||
"liste_enseignes": ["MAIRIE"],
|
||||
"siret": "21580304000017",
|
||||
},
|
||||
"nature_juridique": "7210",
|
||||
"matching_etablissements": [
|
||||
{
|
||||
"siret": "21580304000017",
|
||||
"libelle_commune": "VARZY",
|
||||
"liste_enseignes": ["MAIRIE"],
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
plugin = CommuneCreation()
|
||||
name = plugin.get_organization_name_from_results(data, "21580304000017")
|
||||
assert name == "Varzy"
|
||||
|
||||
|
||||
def test_api_call_execution():
|
||||
"""Test that calling execute() faithfully executes the API call"""
|
||||
task = ApiCall()
|
||||
task.method = "POST"
|
||||
task.base = "https://some_host"
|
||||
task.url = "some_url"
|
||||
task.params = {"some_key": "some_value"}
|
||||
task.headers = {"Some-Header": "Some-Header-Value"}
|
||||
|
||||
with responses.RequestsMock() as rsps:
|
||||
rsps.add(
|
||||
rsps.POST,
|
||||
url="https://some_host/some_url",
|
||||
body='{"some_key": "some_value"}',
|
||||
content_type="application/json",
|
||||
headers={"Some-Header": "Some-Header-Value"},
|
||||
)
|
||||
|
||||
task.execute()
|
||||
|
||||
|
||||
@override_settings(DNS_PROVISIONING_TARGET_ZONE="collectivite.fr")
|
||||
def test_tasks_on_commune_creation_include_zone_creation():
|
||||
"""Test the first task in commune creation: creating the DNS sub-zone"""
|
||||
plugin = CommuneCreation()
|
||||
name = "Varzy"
|
||||
|
||||
tasks = plugin.complete_commune_creation(name)
|
||||
|
||||
assert tasks[0].base == "https://api.scaleway.com"
|
||||
assert tasks[0].url == "/domain/v2beta1/dns-zones"
|
||||
assert tasks[0].method == "POST"
|
||||
assert tasks[0].params == {
|
||||
"project_id": settings.DNS_PROVISIONING_RESOURCE_ID,
|
||||
"domain": "collectivite.fr",
|
||||
"subdomain": "varzy",
|
||||
}
|
||||
assert tasks[0].headers["X-Auth-Token"] == settings.DNS_PROVISIONING_API_CREDENTIALS
|
||||
|
||||
|
||||
@override_settings(DNS_PROVISIONING_TARGET_ZONE="collectivite.fr")
|
||||
def test_tasks_on_commune_creation_include_dimail_domain_creation():
|
||||
"""Test the second task in commune creation: creating the domain in Dimail"""
|
||||
plugin = CommuneCreation()
|
||||
name = "Merlaut"
|
||||
|
||||
tasks = plugin.complete_commune_creation(name)
|
||||
|
||||
assert tasks[1].base == settings.MAIL_PROVISIONING_API_URL
|
||||
assert tasks[1].url == "/domains/"
|
||||
assert tasks[1].method == "POST"
|
||||
assert tasks[1].params == {
|
||||
"name": "merlaut.collectivite.fr",
|
||||
"delivery": "virtual",
|
||||
"features": ["webmail", "mailbox"],
|
||||
"context_name": "merlaut.collectivite.fr",
|
||||
}
|
||||
assert (
|
||||
tasks[1].headers["Authorization"]
|
||||
== f"Basic {settings.MAIL_PROVISIONING_API_CREDENTIALS}"
|
||||
)
|
||||
|
||||
|
||||
@override_settings(DNS_PROVISIONING_TARGET_ZONE="collectivite.fr")
|
||||
def test_tasks_on_commune_creation_include_fetching_spec():
|
||||
"""Test the third task in commune creation: asking Dimail for the spec"""
|
||||
plugin = CommuneCreation()
|
||||
name = "Loc-Eguiner"
|
||||
|
||||
tasks = plugin.complete_commune_creation(name)
|
||||
|
||||
assert tasks[2].base == settings.MAIL_PROVISIONING_API_URL
|
||||
assert tasks[2].url == "/domains/loc-eguiner.collectivite.fr/spec"
|
||||
assert tasks[2].method == "GET"
|
||||
assert (
|
||||
tasks[2].headers["Authorization"]
|
||||
== f"Basic {settings.MAIL_PROVISIONING_API_CREDENTIALS}"
|
||||
)
|
||||
|
||||
|
||||
@override_settings(DNS_PROVISIONING_TARGET_ZONE="collectivite.fr")
|
||||
def test_tasks_on_commune_creation_include_dns_records():
|
||||
"""Test the next several tasks in commune creation: creating records"""
|
||||
plugin = CommuneCreation()
|
||||
name = "Abidos"
|
||||
|
||||
spec_response = [
|
||||
{"target": "", "type": "mx", "value": "mx.dev.ox.numerique.gouv.fr."},
|
||||
{
|
||||
"target": "dimail._domainkey",
|
||||
"type": "txt",
|
||||
"value": "v=DKIM1; h=sha256; k=rsa; p=MIICIjANB<truncated>AAQ==",
|
||||
},
|
||||
{"target": "imap", "type": "cname", "value": "imap.dev.ox.numerique.gouv.fr."},
|
||||
{"target": "smtp", "type": "cname", "value": "smtp.dev.ox.numerique.gouv.fr."},
|
||||
{
|
||||
"target": "",
|
||||
"type": "txt",
|
||||
"value": "v=spf1 include:_spf.dev.ox.numerique.gouv.fr -all",
|
||||
},
|
||||
{
|
||||
"target": "webmail",
|
||||
"type": "cname",
|
||||
"value": "webmail.dev.ox.numerique.gouv.fr.",
|
||||
},
|
||||
]
|
||||
|
||||
tasks = plugin.complete_commune_creation(name)
|
||||
tasks[2].response_data = spec_response
|
||||
|
||||
expected = {
|
||||
"changes": [
|
||||
{
|
||||
"add": {
|
||||
"records": [
|
||||
{
|
||||
"name": item["target"],
|
||||
"type": item["type"].upper(),
|
||||
"data": item["value"],
|
||||
"ttl": 3600,
|
||||
}
|
||||
for item in spec_response
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
zone_call = plugin.complete_zone_creation(tasks[2])
|
||||
assert zone_call.params == expected
|
||||
assert zone_call.url == "/domain/v2beta1/dns-zones/abidos.collectivite.fr/records"
|
||||
assert (
|
||||
zone_call.headers["X-Auth-Token"] == settings.DNS_PROVISIONING_API_CREDENTIALS
|
||||
)
|
||||
|
||||
|
||||
@override_settings(DNS_PROVISIONING_TARGET_ZONE="collectivite.fr")
|
||||
def test_tasks_on_grant_access():
|
||||
"""Test the final tasks after making user admin of an org"""
|
||||
plugin = CommuneCreation()
|
||||
|
||||
tasks = plugin.complete_grant_access("some-sub", "mezos.collectivite.fr")
|
||||
|
||||
assert tasks[0].base == settings.MAIL_PROVISIONING_API_URL
|
||||
assert tasks[0].url == "/users/"
|
||||
assert tasks[0].method == "POST"
|
||||
assert tasks[0].params == {
|
||||
"name": "some-sub",
|
||||
"password": "no",
|
||||
"is_admin": False,
|
||||
"perms": [],
|
||||
}
|
||||
assert (
|
||||
tasks[0].headers["Authorization"]
|
||||
== f"Basic {settings.MAIL_PROVISIONING_API_CREDENTIALS}"
|
||||
)
|
||||
|
||||
assert tasks[1].base == settings.MAIL_PROVISIONING_API_URL
|
||||
assert tasks[1].url == "/allows/"
|
||||
assert tasks[1].method == "POST"
|
||||
assert tasks[1].params == {
|
||||
"user": "some-sub",
|
||||
"domain": "mezos.collectivite.fr",
|
||||
}
|
||||
assert (
|
||||
tasks[1].headers["Authorization"]
|
||||
== f"Basic {settings.MAIL_PROVISIONING_API_CREDENTIALS}"
|
||||
)
|
||||
|
||||
|
||||
def test_normalize_name():
|
||||
"""Test name normalization"""
|
||||
plugin = CommuneCreation()
|
||||
assert plugin.normalize_name("Asnières-sur-Saône") == "asnieres-sur-saone"
|
||||
assert plugin.normalize_name("Bâgé-le-Châtel") == "bage-le-chatel"
|
||||
assert plugin.normalize_name("Courçais") == "courcais"
|
||||
assert plugin.normalize_name("Moÿ-de-l'Aisne") == "moy-de-l-aisne"
|
||||
assert plugin.normalize_name("Salouël") == "salouel"
|
||||
assert (
|
||||
plugin.normalize_name("Bors (Canton de Tude-et-Lavalette)")
|
||||
== "bors-canton-de-tude-et-lavalette"
|
||||
)
|
||||
|
||||
|
||||
@override_settings(DNS_PROVISIONING_TARGET_ZONE="collectivite.fr")
|
||||
def test_zone_name():
|
||||
"""Test transforming a commune name to a sub-zone of collectivite.fr"""
|
||||
plugin = CommuneCreation()
|
||||
assert plugin.zone_name("Bâgé-le-Châtel") == "bage-le-chatel.collectivite.fr"
|
||||
@@ -0,0 +1,211 @@
|
||||
"""Tests for the NameFromSiretOrganizationPlugin plugin."""
|
||||
|
||||
import pytest
|
||||
import responses
|
||||
|
||||
from core.models import Organization, get_organization_metadata_schema
|
||||
from core.plugins.registry import registry
|
||||
|
||||
from plugins.la_suite.hooks_utils.all_organizations import (
|
||||
get_organization_name_and_metadata_from_siret,
|
||||
)
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
# disable unused-argument for because organization_plugins_settings
|
||||
# is used to set the settings not to be used in the test
|
||||
# pylint: disable=unused-argument
|
||||
|
||||
|
||||
@pytest.fixture(name="hook_settings")
|
||||
def hook_settings_fixture(settings):
|
||||
"""
|
||||
Fixture to set the organization plugins settings and
|
||||
leave the initial state after the test.
|
||||
"""
|
||||
_original_hooks = dict(registry._hooks.items()) # pylint: disable=protected-access
|
||||
registry.register_hook(
|
||||
"organization_created", get_organization_name_and_metadata_from_siret
|
||||
)
|
||||
|
||||
settings.ORGANIZATION_METADATA_SCHEMA = "fr/organization_metadata.json"
|
||||
|
||||
# Reset the model validation cache
|
||||
get_organization_metadata_schema.cache_clear()
|
||||
get_organization_metadata_schema()
|
||||
|
||||
yield
|
||||
|
||||
# reset the hooks
|
||||
registry._hooks = _original_hooks # pylint: disable=protected-access
|
||||
|
||||
settings.ORGANIZATION_METADATA_SCHEMA = None
|
||||
|
||||
# Reset the model validation cache
|
||||
get_organization_metadata_schema.cache_clear()
|
||||
get_organization_metadata_schema()
|
||||
|
||||
|
||||
@responses.activate
|
||||
@pytest.mark.parametrize(
|
||||
"nature_juridique,is_commune,is_public_service",
|
||||
[
|
||||
("123", False, False),
|
||||
("7210", True, False),
|
||||
("123", False, True),
|
||||
("7210", True, True),
|
||||
],
|
||||
)
|
||||
def test_organization_plugins_run_after_create(
|
||||
hook_settings, nature_juridique, is_commune, is_public_service
|
||||
):
|
||||
"""Test the run_after_create method of the organization plugins for nominal case."""
|
||||
responses.add(
|
||||
responses.GET,
|
||||
"https://recherche-entreprises.api.gouv.fr/search?q=12345678901234",
|
||||
json={
|
||||
"results": [
|
||||
{
|
||||
# skipping some fields
|
||||
"matching_etablissements": [
|
||||
# skipping some fields
|
||||
{
|
||||
"liste_enseignes": ["AMAZING ORGANIZATION"],
|
||||
"siret": "12345678901234",
|
||||
}
|
||||
],
|
||||
"nature_juridique": nature_juridique,
|
||||
"complements": {
|
||||
"est_service_public": is_public_service,
|
||||
},
|
||||
}
|
||||
],
|
||||
"total_results": 1,
|
||||
"page": 1,
|
||||
"per_page": 10,
|
||||
"total_pages": 1,
|
||||
},
|
||||
status=200,
|
||||
)
|
||||
|
||||
organization = Organization.objects.create(
|
||||
name="12345678901234", registration_id_list=["12345678901234"]
|
||||
)
|
||||
assert organization.name == "Amazing Organization"
|
||||
assert organization.metadata["is_commune"] == is_commune
|
||||
assert organization.metadata["is_public_service"] == is_public_service
|
||||
|
||||
# Check that the organization has been updated in the database also
|
||||
organization.refresh_from_db()
|
||||
assert organization.name == "Amazing Organization"
|
||||
assert organization.metadata["is_commune"] == is_commune
|
||||
assert organization.metadata["is_public_service"] == is_public_service
|
||||
|
||||
|
||||
@responses.activate
|
||||
def test_organization_plugins_run_after_create_api_fail(hook_settings):
|
||||
"""Test the plugin when the API call fails."""
|
||||
responses.add(
|
||||
responses.GET,
|
||||
"https://recherche-entreprises.api.gouv.fr/search?q=12345678901234",
|
||||
json={"error": "Internal Server Error"},
|
||||
status=500,
|
||||
)
|
||||
|
||||
organization = Organization.objects.create(
|
||||
name="12345678901234", registration_id_list=["12345678901234"]
|
||||
)
|
||||
assert organization.name == "12345678901234"
|
||||
|
||||
|
||||
@responses.activate
|
||||
@pytest.mark.parametrize(
|
||||
"results",
|
||||
[
|
||||
{"results": []},
|
||||
{"results": [{"matching_etablissements": []}]},
|
||||
{"results": [{"matching_etablissements": [{"siret": "12345678901234"}]}]},
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"matching_etablissements": [
|
||||
{"siret": "12345678901234", "liste_enseignes": []}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
)
|
||||
def test_organization_plugins_run_after_create_missing_data(hook_settings, results):
|
||||
"""Test the plugin when the API call returns missing data."""
|
||||
responses.add(
|
||||
responses.GET,
|
||||
"https://recherche-entreprises.api.gouv.fr/search?q=12345678901234",
|
||||
json=results,
|
||||
status=200,
|
||||
)
|
||||
|
||||
organization = Organization.objects.create(
|
||||
name="12345678901234", registration_id_list=["12345678901234"]
|
||||
)
|
||||
assert organization.name == "12345678901234"
|
||||
|
||||
|
||||
@responses.activate
|
||||
def test_organization_plugins_run_after_create_name_already_set(
|
||||
hook_settings,
|
||||
):
|
||||
"""Test the plugin does nothing when the name already differs from the registration ID."""
|
||||
organization = Organization.objects.create(
|
||||
name="Magic WOW", registration_id_list=["12345678901234"]
|
||||
)
|
||||
assert organization.name == "Magic WOW"
|
||||
|
||||
|
||||
@responses.activate
|
||||
def test_organization_plugins_run_after_create_no_list_enseignes(
|
||||
hook_settings,
|
||||
):
|
||||
"""Test the run_after_create method of the organization plugins for nominal case."""
|
||||
responses.add(
|
||||
responses.GET,
|
||||
"https://recherche-entreprises.api.gouv.fr/search?q=12345678901234",
|
||||
json={
|
||||
"results": [
|
||||
{
|
||||
"nom_raison_sociale": "AMAZING ORGANIZATION",
|
||||
# skipping some fields
|
||||
"matching_etablissements": [
|
||||
# skipping some fields
|
||||
{
|
||||
"liste_enseignes": None,
|
||||
"siret": "12345678901234",
|
||||
}
|
||||
],
|
||||
"nature_juridique": "123",
|
||||
"complements": {
|
||||
"est_service_public": True,
|
||||
},
|
||||
}
|
||||
],
|
||||
"total_results": 1,
|
||||
"page": 1,
|
||||
"per_page": 10,
|
||||
"total_pages": 1,
|
||||
},
|
||||
status=200,
|
||||
)
|
||||
|
||||
organization = Organization.objects.create(
|
||||
name="12345678901234", registration_id_list=["12345678901234"]
|
||||
)
|
||||
assert organization.name == "Amazing Organization"
|
||||
assert organization.metadata["is_commune"] is False
|
||||
assert organization.metadata["is_public_service"] is True
|
||||
|
||||
# Check that the organization has been updated in the database also
|
||||
organization.refresh_from_db()
|
||||
assert organization.name == "Amazing Organization"
|
||||
assert organization.metadata["is_commune"] is False
|
||||
assert organization.metadata["is_public_service"] is True
|
||||
@@ -0,0 +1,42 @@
|
||||
"""Test module to check all application hooks are loaded."""
|
||||
|
||||
from core.plugins.registry import registry
|
||||
|
||||
from plugins.la_suite.apps import LaSuitePluginConfig
|
||||
|
||||
|
||||
def test_hooks_loaded():
|
||||
"""Test to check all application hooks are loaded."""
|
||||
_original_hooks = dict(registry._hooks.items()) # pylint: disable=protected-access
|
||||
_original_registered_apps = set(registry._registered_apps) # pylint: disable=protected-access
|
||||
|
||||
registry.reset()
|
||||
|
||||
assert registry.get_callbacks("organization_created") == []
|
||||
assert registry.get_callbacks("organization_access_granted") == []
|
||||
|
||||
# Force the application to run "ready" method
|
||||
LaSuitePluginConfig(
|
||||
app_name="plugins.la_suite", app_module=__import__("plugins.la_suite")
|
||||
).ready()
|
||||
|
||||
# Check that the hooks are loaded
|
||||
organization_created_hook_names = [
|
||||
callback.__name__ for callback in registry.get_callbacks("organization_created")
|
||||
]
|
||||
assert organization_created_hook_names == [
|
||||
"get_organization_name_and_metadata_from_siret_hook",
|
||||
"commune_organization_created",
|
||||
]
|
||||
|
||||
organization_access_granted_hook_names = [
|
||||
callback.__name__
|
||||
for callback in registry.get_callbacks("organization_access_granted")
|
||||
]
|
||||
assert organization_access_granted_hook_names == [
|
||||
"commune_organization_access_granted"
|
||||
]
|
||||
|
||||
# cleanup the hooks
|
||||
registry._hooks = _original_hooks # pylint: disable=protected-access
|
||||
registry._registered_apps = _original_registered_apps # pylint: disable=protected-access
|
||||
Reference in New Issue
Block a user