|
7 | 7 | from urllib.parse import quote_plus
|
8 | 8 |
|
9 | 9 | import pytest
|
10 |
| -from fastapi import status |
| 10 | +from fastapi import HTTPException, status |
11 | 11 | from sqlalchemy import func
|
12 | 12 | from sqlalchemy.schema import Column as SAColumn
|
13 | 13 | from sqlmodel import select
|
14 | 14 |
|
| 15 | +from qualicharge.api.v1.routers.dynamic import get_pdc_id |
15 | 16 | from qualicharge.auth.factories import GroupFactory
|
16 | 17 | from qualicharge.auth.schemas import GroupOperationalUnit, ScopesEnum, User
|
17 | 18 | from qualicharge.conf import settings
|
|
37 | 38 | from qualicharge.schemas.utils import save_statique, save_statiques
|
38 | 39 |
|
39 | 40 |
|
| 41 | +def test_get_pdc_id(db_session): |
| 42 | + """Test the get_pdc_id utility.""" |
| 43 | + id_pdc_itinerance = "FRALLE0123456" |
| 44 | + with pytest.raises(HTTPException, match="Point of charge does not exist"): |
| 45 | + get_pdc_id(id_pdc_itinerance, db_session) |
| 46 | + |
| 47 | + n_pdc = 4 |
| 48 | + save_statiques(db_session, StatiqueFactory.batch(n_pdc)) |
| 49 | + pdcs = db_session.exec(select(PointDeCharge)).all() |
| 50 | + assert len(pdcs) == n_pdc |
| 51 | + |
| 52 | + for pdc in pdcs: |
| 53 | + assert pdc.id == get_pdc_id(pdc.id_pdc_itinerance, db_session) |
| 54 | + |
| 55 | + |
| 56 | +def test_get_pdc_id_cache(db_session): |
| 57 | + """Test the get_pdc_id utility cache.""" |
| 58 | + n_pdc = 4 |
| 59 | + save_statiques(db_session, StatiqueFactory.batch(n_pdc)) |
| 60 | + pdcs = db_session.exec(select(PointDeCharge)).all() |
| 61 | + assert len(pdcs) == n_pdc |
| 62 | + |
| 63 | + hits_by_pdc = 9 |
| 64 | + for pdc_index in range(n_pdc): |
| 65 | + pdc = pdcs[pdc_index] |
| 66 | + |
| 67 | + # First call: feed the cache |
| 68 | + with SAQueryCounter(db_session.connection()) as counter: |
| 69 | + pdc_id = get_pdc_id(pdc.id_pdc_itinerance, db_session) |
| 70 | + assert pdc_id == pdc.id |
| 71 | + cache_info = get_pdc_id.cache_info() # type: ignore[attr-defined] |
| 72 | + assert counter.count == 1 |
| 73 | + assert cache_info.hits == pdc_index * hits_by_pdc |
| 74 | + assert cache_info.currsize == pdc_index + 1 |
| 75 | + |
| 76 | + # Test cached entry |
| 77 | + for hit in range(1, hits_by_pdc + 1): |
| 78 | + with SAQueryCounter(db_session.connection()) as counter: |
| 79 | + pdc_id = get_pdc_id(pdc.id_pdc_itinerance, db_session) |
| 80 | + assert pdc_id == pdc.id |
| 81 | + cache_info = get_pdc_id.cache_info() # type: ignore[attr-defined] |
| 82 | + assert counter.count == 0 |
| 83 | + assert cache_info.hits == (pdc_index * hits_by_pdc) + hit |
| 84 | + assert cache_info.currsize == pdc_index + 1 |
| 85 | + |
| 86 | + |
40 | 87 | @pytest.mark.parametrize(
|
41 | 88 | "client_auth",
|
42 | 89 | (
|
@@ -331,7 +378,7 @@ def test_read_status_for_non_existing_point_of_charge(client_auth):
|
331 | 378 | """Test the /status/{id_pdc_itinerance} endpoint for unknown point of charge."""
|
332 | 379 | response = client_auth.get("/dynamique/status/FR911E1111ER1")
|
333 | 380 | assert response.status_code == status.HTTP_404_NOT_FOUND
|
334 |
| - assert response.json() == {"detail": "Selected point of charge does not exist"} |
| 381 | + assert response.json() == {"detail": "Point of charge does not exist"} |
335 | 382 |
|
336 | 383 |
|
337 | 384 | def test_read_status_for_non_existing_status(db_session, client_auth):
|
@@ -420,6 +467,50 @@ def test_read_status_for_superuser(db_session, client_auth):
|
420 | 467 | assert expected_status.etat_prise_type_ef == response_status.etat_prise_type_ef
|
421 | 468 |
|
422 | 469 |
|
| 470 | +def test_read_status_get_pdc_id_cache(db_session, client_auth): |
| 471 | + """Test the /status/{id_pdc_itinerance} endpoint's get_pdc_id cache usage.""" |
| 472 | + StatusFactory.__session__ = db_session |
| 473 | + |
| 474 | + # Create the PointDeCharge |
| 475 | + id_pdc_itinerance = "FR911E1111ER1" |
| 476 | + save_statique( |
| 477 | + db_session, StatiqueFactory.build(id_pdc_itinerance=id_pdc_itinerance) |
| 478 | + ) |
| 479 | + pdc = db_session.exec( |
| 480 | + select(PointDeCharge).where( |
| 481 | + PointDeCharge.id_pdc_itinerance == id_pdc_itinerance |
| 482 | + ) |
| 483 | + ).one() |
| 484 | + |
| 485 | + # Create 20 attached statuses |
| 486 | + n_statuses = 20 |
| 487 | + StatusFactory.create_batch_sync(n_statuses, point_de_charge_id=pdc.id) |
| 488 | + |
| 489 | + # Count queries while getting the latest status |
| 490 | + with SAQueryCounter(db_session.connection()) as counter: |
| 491 | + client_auth.get(f"/dynamique/status/{id_pdc_itinerance}") |
| 492 | + cache_info = get_pdc_id.cache_info() # type: ignore[attr-defined] |
| 493 | + assert cache_info.hits == 0 |
| 494 | + assert cache_info.currsize == 1 |
| 495 | + # We expect the following db request: |
| 496 | + # 1. User authentication |
| 497 | + # 2. get_user injection |
| 498 | + # 3. get_pdc_id |
| 499 | + # 4. latest db status (sub) queries |
| 500 | + # 5. get_pdc_id |
| 501 | + expected = 5 |
| 502 | + assert counter.count == expected |
| 503 | + |
| 504 | + for hit in range(1, 10): |
| 505 | + # Count queries while getting the latest status |
| 506 | + with SAQueryCounter(db_session.connection()) as counter: |
| 507 | + client_auth.get(f"/dynamique/status/{id_pdc_itinerance}") |
| 508 | + cache_info = get_pdc_id.cache_info() # type: ignore[attr-defined] |
| 509 | + assert cache_info.hits == hit |
| 510 | + assert cache_info.currsize == 1 |
| 511 | + assert counter.count == 1 |
| 512 | + |
| 513 | + |
423 | 514 | @pytest.mark.parametrize(
|
424 | 515 | "client_auth",
|
425 | 516 | (
|
@@ -489,7 +580,7 @@ def test_read_status_history_for_non_existing_point_of_charge(client_auth):
|
489 | 580 | """Test the /status/{id_pdc_itinerance}/history endpoint for unknown PDC."""
|
490 | 581 | response = client_auth.get("/dynamique/status/FR911E1111ER1/history")
|
491 | 582 | assert response.status_code == status.HTTP_404_NOT_FOUND
|
492 |
| - assert response.json() == {"detail": "Selected point of charge does not exist"} |
| 583 | + assert response.json() == {"detail": "Point of charge does not exist"} |
493 | 584 |
|
494 | 585 |
|
495 | 586 | def test_read_status_history_for_non_existing_status(db_session, client_auth):
|
@@ -709,7 +800,7 @@ def test_create_status_for_non_existing_point_of_charge(client_auth):
|
709 | 800 | "/dynamique/status/", json=json.loads(qc_status.model_dump_json())
|
710 | 801 | )
|
711 | 802 | assert response.status_code == status.HTTP_404_NOT_FOUND
|
712 |
| - assert response.json() == {"detail": "Attached point of charge does not exist"} |
| 803 | + assert response.json() == {"detail": "Point of charge does not exist"} |
713 | 804 |
|
714 | 805 |
|
715 | 806 | @pytest.mark.parametrize(
|
@@ -1180,7 +1271,7 @@ def test_create_session_for_non_existing_point_of_charge(client_auth):
|
1180 | 1271 | "/dynamique/session/", json=json.loads(qc_session.model_dump_json())
|
1181 | 1272 | )
|
1182 | 1273 | assert response.status_code == status.HTTP_404_NOT_FOUND
|
1183 |
| - assert response.json() == {"detail": "Attached point of charge does not exist"} |
| 1274 | + assert response.json() == {"detail": "Point of charge does not exist"} |
1184 | 1275 |
|
1185 | 1276 |
|
1186 | 1277 | @pytest.mark.parametrize(
|
|
0 commit comments