Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 185 additions & 0 deletions integration_tests/qfdmo/test_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
from urllib.parse import urlencode

import pytest
from django.contrib.gis.geos import Point
from django.core.management import call_command

from qfdmo.models.acteur import ActeurService, ActeurStatus, DisplayedActeur
from qfdmo.models.action import GroupeAction
from qfdmo.models.categorie_objet import SousCategorieObjet
from unit_tests.qfdmo.acteur_factory import (
DisplayedActeurFactory,
DisplayedPropositionServiceFactory,
)
from unit_tests.qfdmo.sscatobj_factory import SousCategorieObjetFactory

BASE_URL = "http://localhost:8000/api/qfdmo"


# Fixtures
# --------
@pytest.fixture
def sous_categorie():
sous_categorie = SousCategorieObjetFactory()
return sous_categorie


@pytest.fixture
def displayed_acteur():
DisplayedActeurFactory(
pk="UN-ACTEUR", location=Point(2.3, 48.86), statut=ActeurStatus.ACTIF
)


@pytest.fixture(scope="session")
def django_db_setup(django_db_setup, django_db_blocker):
with django_db_blocker.unblock():
call_command(
"loaddata",
"categories",
"actions",
"acteur_services",
"acteur_types",
)


# Tests
# -----
@pytest.mark.django_db
def test_get_actions(client):
"""Test the /actions endpoint"""
response = client.get(f"{BASE_URL}/actions")
assert response.status_code == 200
assert isinstance(response.json(), list)


@pytest.mark.django_db
def test_get_groupe_actions(client):
"""Test the /actions/groupes endpoint"""
response = client.get(f"{BASE_URL}/actions/groupes")
assert response.status_code == 200
assert isinstance(response.json(), list)
assert len(response.json()) == GroupeAction.objects.filter(afficher=True).count()


@pytest.mark.django_db
def test_get_acteurs(client, displayed_acteur):
"""Test the /acteurs endpoint with filters"""
params = {
"latitude": 48.86,
"longitude": 2.3,
"rayon": 5,
}
response = client.get(f"{BASE_URL}/acteurs?{urlencode(params)}")
assert response.status_code == 200
data = response.json()
assert isinstance(data["items"], list)
assert data["count"] > 0

returned_acteur = data["items"][0]
assert "nom" in returned_acteur
assert "adresse" in returned_acteur
assert returned_acteur["identifiant_unique"] == "UN-ACTEUR"


@pytest.mark.django_db
def test_get_acteurs_sous_categorie_filter(client, displayed_acteur):
"""Test the /acteurs endpoint with sous categorie filter"""
acteur = DisplayedActeurFactory(
pk="UN-AUTRE-ACTEUR", location=Point(2.3, 48.86), statut=ActeurStatus.ACTIF
)
sous_categorie = SousCategorieObjetFactory(id=666)
proposition_service = DisplayedPropositionServiceFactory(acteur=acteur)
proposition_service.sous_categories.add(sous_categorie)

params = {"latitude": 48.86, "longitude": 2.3, "rayon": 5, "sous_categories": 666}
response = client.get(f"{BASE_URL}/acteurs?{urlencode(params)}")
data = response.json()

assert SousCategorieObjet.objects.filter(id=666).exists()
assert DisplayedActeur.objects.filter(statut=ActeurStatus.ACTIF).count() == 2
assert (
DisplayedActeur.objects.filter(
statut=ActeurStatus.ACTIF,
proposition_services__sous_categories__id__in=[666],
).count()
== 1
)
assert data["count"] == 1


@pytest.mark.django_db
def test_get_acteurs_types(client):
"""Test the /acteurs/types endpoint"""
response = client.get(f"{BASE_URL}/acteurs/types")
assert response.status_code == 200
data = response.json()
assert isinstance(data, list)
assert len(data) > 0
assert "code" in data[0]
assert "id" in data[0]
assert "libelle" in data[0]


@pytest.mark.django_db
def test_get_acteurs_services(client):
"""Test the /acteurs/services endpoint"""
response = client.get(f"{BASE_URL}/acteurs/services")
assert response.status_code == 200
data = response.json()
assert isinstance(data, list)
assert len(data) == ActeurService.objects.all().count()
assert "code" in data[0]
assert "id" in data[0]
assert "libelle" in data[0]


@pytest.mark.django_db
def test_get_acteur_by_identifiant(client):
"""Test the /acteur endpoint"""
identifiant_unique = "ACT12345"
DisplayedActeurFactory(pk=identifiant_unique, statut=ActeurStatus.ACTIF)
response = client.get(
f"{BASE_URL}/acteur", query_params={"identifiant_unique": identifiant_unique}
)
assert response.status_code == 200
data = response.json()
assert "nom" in data
assert "nom_commercial" in data
assert "siret" in data
assert "adresse" in data
assert data.get("identifiant_unique") == identifiant_unique


@pytest.mark.django_db
def test_get_acteur_inacteur_returns_404(client):
identifiant_unique = "INACTIF_ACT12345"
DisplayedActeurFactory(pk=identifiant_unique, statut=ActeurStatus.INACTIF)
response = client.get(
f"{BASE_URL}/acteur", query_params={"identifiant_unique": identifiant_unique}
)
assert response.status_code == 404


@pytest.mark.django_db
def test_autocomplete_epci(client, mocker):
"""Test the /autocomplete/configurateur endpoint"""
# Mock setup
mock_formatted_epcis_list = mocker.patch("qfdmo.geo_api.formatted_epcis_list")
mock_formatted_epcis_list.return_value = [
"200043123 - CC Auray Quiberon Terre Atlantique",
"200043156 - CC du Pays Rethélois",
]

# Simulate a successful response
response = client.get(
f"{BASE_URL}/autocomplete/configurateur", query_params={"query": "Quiberon"}
)

# Assertions
assert response.status_code == 200
data = response.json()
assert isinstance(data, list)
assert len(data) == 2
if data:
assert "Quiberon" in data[0]
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ filterwarnings = [
"ignore::django.utils.deprecation.RemovedInDjango60Warning",
]

[tool.pyright]
include = ["unit_tests", "code", "qfdmo", "qfdmd", "integration_tests"]
venvPath = "."
venv = ".venv"

[tool.ruff]
# Exclude a variety of commonly ignored directories.
exclude = [
Expand Down
30 changes: 28 additions & 2 deletions qfdmo/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
DisplayedActeur,
GroupeAction,
Source,
SousCategorieObjet,
)

router = Router()
Expand Down Expand Up @@ -45,7 +46,7 @@ class Meta:


class ActionSchema(ModelSchema):
services: str = Field(..., alias="primary")
couleur: str = Field(..., alias="primary")

class Meta:
model = Action
Expand All @@ -71,6 +72,12 @@ class Meta:
fields = ["id", "code", "libelle", "url"]


class SousCategorieObjetSchema(ModelSchema):
class Meta:
model = SousCategorieObjet
fields = ["id", "code", "libelle"]


class ActeurSchema(ModelSchema):
latitude: float
longitude: float
Expand Down Expand Up @@ -112,6 +119,10 @@ class ActeurFilterSchema(FilterSchema):
types: Optional[List[int]] = Field(None, q="acteur_type__in")
services: Optional[List[int]] = Field(None, q="acteur_services__in")
actions: Optional[List[int]] = Field(None, q="proposition_services__action_id__in")
sous_categories: Optional[List[int]] = Field(
None,
q="proposition_services__sous_categories__id__in",
)


@router.get("/sources", response=List[SourceSchema], summary="Liste des sources")
Expand All @@ -123,6 +134,19 @@ def sources(request):
return qs


@router.get(
"/sous-categories",
response=List[SousCategorieObjetSchema],
summary="Liste des catégories d'objets",
)
def sous_categories(request):
"""
Liste l'ensemble des <i>sous-catégories d'objet</i> possibles pour un acteur.
""" # noqa
qs = SousCategorieObjet.objects.filter(afficher=True)
return qs


@router.get(
"/actions", response=List[ActionSchema], summary="Liste des actions possibles"
)
Expand Down Expand Up @@ -218,7 +242,9 @@ def services(request):
summary="Retrouver un acteur actif",
)
def acteur(request, identifiant_unique: str):
return get_object_or_404(DisplayedActeur, pk=id, statut=ActeurStatus.ACTIF)
return get_object_or_404(
DisplayedActeur, pk=identifiant_unique, statut=ActeurStatus.ACTIF
)


@router.get("/autocomplete/configurateur")
Expand Down
Loading