Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Test] Firsts moked tests #22

Merged
merged 5 commits into from
Jan 1, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
42 changes: 42 additions & 0 deletions src/blueprints/tests/services/test_search_formations_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from unittest.mock import patch
import pytest
from src.business_logic.formation.scrap.tests.unit.test_search_onisep_formations import (
MOKED_RESEARCH,
)
from src.business_logic.formation.scrap.types import FormationIsFavorite
from src.models.formation import Formation


@pytest.fixture
def mock_search_formations():
with patch(
"src.business_logic.formation.scrap.search_formation.get_raw_data"
) as mock_get_raw_data:
yield mock_get_raw_data


def test_no_authenticated_search_formation_api_should_return_formations(
client, mock_search_formations
):
# Arrange
mock_search_formations.return_value = MOKED_RESEARCH

# When
response = client.post(
"/api/v1/formations/search", json={"query": "STH", "limit": 1}
)

result = response.json
print(result)

# Then
formation_favorite = result["formations"][0]
formation_favorite_instance = FormationIsFavorite(**formation_favorite)

formation = formation_favorite["formation"]
formation_instance = Formation(**formation)

assert result["total"] == 5754
assert isinstance(formation_favorite_instance, FormationIsFavorite)
assert formation_favorite["is_favorite"] is False
assert isinstance(formation_instance, Formation)
15 changes: 5 additions & 10 deletions src/business_logic/formation/scrap/get_main_formation.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,13 @@
format_formations,
)
from src.business_logic.formation.scrap.types import FormationsWithTotal
from src.business_logic.formation.scrap.utils.get_onisep_data import get_onisep_data


def _get_raw_main_formations(limit: int = 10, offset: int = None) -> dict:
params = f"/search?&size={limit}"
if offset:
params += f"&from={offset}"
return get_onisep_data(params)
from src.business_logic.formation.scrap.utils.get_onisep_data import (
get_raw_data,
)


def get_main_formations(limit: int = 10, offset: int = None) -> FormationsWithTotal:
data = _get_raw_main_formations(limit, offset)
data = get_raw_data(limit, offset)

formated_formations = format_formations(data["results"])

Expand All @@ -24,7 +19,7 @@ def get_main_formations(limit: int = 10, offset: int = None) -> FormationsWithTo
def auth_get_main_formations(
user_id: int, limit: int = 10, offset: int = None
) -> FormationsWithTotal:
data = _get_raw_main_formations(limit, offset)
data = get_raw_data(limit, offset)

formated_formations = format_formation_with_is_favorite(user_id, data["results"])

Expand Down
15 changes: 5 additions & 10 deletions src/business_logic/formation/scrap/search_formation.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,15 @@
format_formations,
)
from src.business_logic.formation.scrap.types import FormationsWithTotal
from src.business_logic.formation.scrap.utils.get_onisep_data import get_onisep_data


def _get_raw_search_data(query: str, limit: int, offset: int = None) -> dict:
params = f"/search?q={query}&size={limit}"
if offset:
params += f"&from={offset}"
return get_onisep_data(params)
from src.business_logic.formation.scrap.utils.get_onisep_data import (
get_raw_data,
)


def search_formations(
query: str, limit: int, offset: int = None
) -> FormationsWithTotal:
data = _get_raw_search_data(query, limit, offset)
data = get_raw_data(query, limit, offset)

formated_formations = format_formations(data["results"])

Expand All @@ -26,7 +21,7 @@ def search_formations(
def auth_search_formations(
user_id: int, query: str, limit: int, offset: int = None
) -> FormationsWithTotal:
data = _get_raw_search_data(query, limit, offset)
data = get_raw_data(query, limit, offset)
formated_formations = format_formation_with_is_favorite(user_id, data["results"])

return FormationsWithTotal(data["total"], formated_formations)
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from unittest.mock import MagicMock, patch

import pytest
from src.business_logic.formation.exceptions import NoOnisepAPIException
from src.business_logic.formation.scrap.utils.get_onisep_data import get_raw_data

from src.constants.http_status_codes import (
HTTP_200_OK,
HTTP_401_UNAUTHORIZED,
HTTP_500_INTERNAL_SERVER_ERROR,
)


@pytest.fixture
def mock_onisep_request():
with patch(
"src.business_logic.formation.scrap.utils.get_onisep_data.requests.get"
) as mock_requests_get:
yield mock_requests_get


def test_get_onisep_data_successful(mock_onisep_request):
# Arrange
mock_response = MagicMock()
mock_response.status_code = HTTP_200_OK
mock_response.json.return_value = {"total": "5173"}
mock_onisep_request.return_value = mock_response
# Act
result = get_raw_data("SHR")
# Assert
assert result == {"total": "5173"}


def test_get_onisep_data_retry_after_unauthorized(mock_onisep_request):
# Arrange
unauthorized_response = MagicMock()
unauthorized_response.status_code = HTTP_401_UNAUTHORIZED
authorized_response = MagicMock()
authorized_response.status_code = HTTP_200_OK
authorized_response.json.return_value = {"total": "5173"}

# Set up the responses for the two requests
mock_onisep_request.side_effect = [unauthorized_response, authorized_response]

# Act
result = get_raw_data("SHR")

# Assert
assert result == {"total": "5173"}


def test_get_onisep_data_raises_exception(mock_onisep_request):
# Arrange
mock_response = MagicMock()
mock_response.status_code = HTTP_500_INTERNAL_SERVER_ERROR
mock_onisep_request.return_value = mock_response

# Act and Assert
with pytest.raises(NoOnisepAPIException):
get_raw_data("SHR")
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
from unittest.mock import patch

import pytest
from src.business_logic.formation.scrap.search_formation import (
auth_search_formations,
search_formations,
)
from src.business_logic.formation.scrap.types import (
FormationsWithTotal,
)
from src.business_logic.formation.scrap.utils.format_formations import (
format_formation_with_is_favorite,
format_formations,
)
from src.models.user import User
from src.models.user_favori import UserFavori
from src.tests.factories.factories import (
UserFactory,
UserFavorisFactory,
)


MOKED_RESEARCH = {
"total": 5754,
"size": 1,
"results": [
{
"code_nsf": "334",
"sigle_type_formation": "",
"libelle_type_formation": "baccalauréat technologique",
"libelle_formation_principal": "bac techno STHR Sciences et technologies de l'hôtellerie et de la restauration",
"sigle_formation": "STHR",
"duree": "1 an",
"niveau_de_sortie_indicatif": "Bac ou équivalent",
"code_rncp": "",
"niveau_de_certification": "4",
"libelle_niveau_de_certification": "niveau 4 (bac ou équivalent)",
"tutelle": "Ministère chargé de l'Éducation nationale et de la Jeunesse",
"url_et_id_onisep": "http://www.onisep.fr/http/redirection/formation/slug/FOR.494",
"domainesous-domaine": "hôtellerie-restauration, tourisme/hôtellerie | hôtellerie-restauration, tourisme/restauration",
}
],
}


@pytest.fixture
def mock_search_formations():
with patch(
"src.business_logic.formation.scrap.search_formation.get_raw_data"
) as mock_get_raw_data:
yield mock_get_raw_data


def test_search_formations_should_return_formations_without_favorite(
mock_search_formations,
):
# Arrange
mock_search_formations.return_value = MOKED_RESEARCH

# Act
formations = search_formations("STHR", 1)

# Assert
moked_formation = MOKED_RESEARCH["results"]
waited_formations = format_formations(moked_formation)
waited_result = FormationsWithTotal(
total=MOKED_RESEARCH["total"], formations=waited_formations
)

assert formations == waited_result
mock_search_formations.assert_called_once_with("STHR", 1, None)


def test_authenticated_search_formations_should_return_formations_with_favorite(
mock_search_formations, db_session
):
# Arrange
mock_search_formations.return_value = MOKED_RESEARCH

# Given
user: User = UserFactory()
db_session.add(user)
db_session.flush()

user_favori: UserFavori = UserFavorisFactory(user_id=user.id)
db_session.add(user_favori)
db_session.commit()

# Act
formations = auth_search_formations(user.id, "STHR", 1)

# Assert
moked_formation = MOKED_RESEARCH["results"]
waited_formations = format_formation_with_is_favorite(user.id, moked_formation)
waited_result = FormationsWithTotal(
total=MOKED_RESEARCH["total"], formations=waited_formations
)

assert formations == waited_result
mock_search_formations.assert_called_once_with("STHR", 1, None)
22 changes: 21 additions & 1 deletion src/business_logic/formation/scrap/utils/get_onisep_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
from src.business_logic.formation import HEADERS, ONISEP_URL, HeaderKey
from src.business_logic.formation.exceptions import NoOnisepAPIException
from src.business_logic.formation.scrap.utils.get_onisep_token import get_token
from src.constants.http_status_codes import HTTP_200_OK, HTTP_401_UNAUTHORIZED
from src.constants.http_status_codes import (
HTTP_200_OK,
HTTP_401_UNAUTHORIZED,
)


DATASET = "5fa591127f501"
Expand All @@ -23,3 +26,20 @@ def get_onisep_data(params: str) -> dict:
raise NoOnisepAPIException(
f"\n status: {response.status_code} \n message : Onisep API is down. \n dataset : {DATASET} \n headers : {HEADERS} "
)


def get_raw_data(
query: str = None,
limit: int = 10,
offset: int = None,
is_main_formations: bool = True,
) -> dict:
if is_main_formations:
params = f"/search?&size={limit}"
else:
params = f"/search?q={query}&size={limit}"

if offset:
params += f"&from={offset}"

return get_onisep_data(params)
3 changes: 2 additions & 1 deletion src/business_logic/formation/scrap/utils/get_onisep_token.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import requests

from src.business_logic.formation.exceptions import NoOnisepAPIException
from src.constants.http_status_codes import HTTP_200_OK


URL = "https://api.opendata.onisep.fr/api/1.0/login"
Expand All @@ -20,7 +21,7 @@ def _get_form_data() -> dict[str, str]:

def get_token() -> BearerToken:
response = requests.post(URL, data=_get_form_data())
if response.status_code == 200:
if response.status_code == HTTP_200_OK:
data = response.json()
return f"Bearer {data.get('token')}"
raise NoOnisepAPIException(
Expand Down
17 changes: 10 additions & 7 deletions src/tests/factories/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,9 @@ class UserFactory(factory.Factory):
class Meta:
model = models.User

username = factory.Faker("uuidv4")
email = factory.Faker("uuidv4")


class UserFavorisFactory(factory.Factory):
class Meta:
model = models.UserFavori
username = factory.Faker("uuid4")
email = factory.Faker("uuid4")
password = factory.Faker("uuid4")


class FormationFactory(factory.Factory):
Expand All @@ -29,3 +25,10 @@ class Meta:
domain = "hôtellerie-restauration, tourisme/hôtellerie | hôtellerie-restauration, tourisme/restauration"
niveau_de_sortie = "Bac ou équivalent"
duree = "1 an"


class UserFavorisFactory(factory.Factory):
class Meta:
model = models.UserFavori

formation = factory.SubFactory(FormationFactory)
18 changes: 18 additions & 0 deletions src/tests/fixtures/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@
from sqlalchemy.orm import Session
from flask.testing import FlaskClient
from flask_sqlalchemy import SQLAlchemy
from flask_jwt_extended import (
create_access_token,
)

from src import create_app
from src import db as _db
from src.business_logic.formation.scrap.utils.get_onisep_token import BearerToken
from src.models.user import User


@pytest.fixture(autouse=True, scope="session")
Expand Down Expand Up @@ -34,3 +39,16 @@ def db_session(db: SQLAlchemy) -> Session:
def client(app: Flask, db: SQLAlchemy) -> Generator[FlaskClient, None, None]:
with app.test_client() as client:
yield client


@pytest.fixture(scope="module")
def authenticated_user(db_session: Session) -> tuple[User, dict[str, BearerToken]]:
from src.tests.factories import UserFactory

user: User = UserFactory()
db_session.add(user)
db_session.commit()

headers = {"Authorization": f"Bearer {create_access_token(identity=user.id)}"}

yield user, headers