Coverage for tests/test_customers.py: 100%
105 statements
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-09 19:37 +0200
« prev ^ index » next coverage.py v7.14.1, created at 2026-06-09 19:37 +0200
1"""Tests du sous-client customers (synchrone et asynchrone)."""
3from __future__ import annotations
5from typing import Any
7from unittest.mock import AsyncMock, MagicMock
9import pytest # type: ignore[import] # pylint: disable=W0611
11from henrri_connect.connect import (
12 AsyncHenrriClient, SyncHenrriClient, # type: ignore[import]
13)
14from henrri_connect.models import Contact, Customer, CustomerRequest
15from henrri_connect.models.base import CustomerType
16from tests.conftest import CUSTOMER_JSON, PAGED_META, make_response
19def _paged(elements: list[Any]) -> dict[str, Any]:
20 return {"elements": elements, "meta": PAGED_META}
23class TestSyncCustomers:
24 """Tests du sous-client customers (synchrone)."""
25 def test_list_retourne_clients(
26 self, sync_client: SyncHenrriClient, mock_http: MagicMock
27 ) -> None:
28 """Test de la méthode list_customers()."""
29 mock_http.request.return_value = make_response(_paged([CUSTOMER_JSON]))
31 result = sync_client.customers.list_customers(
32 request=CustomerRequest(
33 page=1,
34 limit=50,
35 search="",
36 from_date="2020-01-01",
37 to_date="2027-01-01",
38 ),
39 with_selected_fields=False,
40 )
42 assert len(result.elements or []) == 1
43 assert (result.elements[0].name if result.elements else None) == "Acme Corp"
44 assert (result.meta.total_count if result.meta else 0) == 1
46 def test_list_passe_les_filtres(
47 self, sync_client: SyncHenrriClient, mock_http: MagicMock
48 ) -> None:
49 """Test de la méthode list_customers()."""
50 mock_http.request.return_value = make_response(_paged([]))
52 sync_client.customers.list_customers(
53 request=CustomerRequest(search="acme", page=2, limit=10, from_date="", to_date=""),
54 with_selected_fields=False,
55 )
57 _, kwargs = mock_http.request.call_args
58 params = kwargs["params"]
59 assert params["search"] == "acme"
60 assert params["page"] == 2
61 assert params["limit"] == 10
63 def test_list_exclut_params_none(
64 self, sync_client: SyncHenrriClient, mock_http: MagicMock
65 ) -> None:
66 """Test de la méthode list_customers()."""
67 mock_http.request.return_value = make_response(_paged([]))
69 sync_client.customers.list_customers(
70 request=CustomerRequest(search="", from_date="", to_date=""),
71 with_selected_fields=False,
72 )
74 _, kwargs = mock_http.request.call_args
75 params = kwargs["params"]
76 # ``search`` is required on CustomerRequest in current models;
77 # it will be present as empty string
78 assert params["search"] == ""
79 assert "sortBy" not in params
81 def test_add_cree_client(
82 self, sync_client: SyncHenrriClient, mock_http: MagicMock
83 ) -> None:
84 """Test de la méthode add_customer()."""
85 mock_http.request.return_value = make_response({**CUSTOMER_JSON, "id": 99})
86 customer = Customer(name="Nouveau", type=CustomerType.COMPANY)
88 result = sync_client.customers.add(customer)
90 assert result.id == 99
91 args = mock_http.request.call_args
92 assert args.args[0] == "POST"
94 def test_get_retourne_client(
95 self, sync_client: SyncHenrriClient, mock_http: MagicMock
96 ) -> None:
97 """Test de la méthode get_customer()."""
98 mock_http.request.return_value = make_response(CUSTOMER_JSON)
100 result = sync_client.customers.get(1)
102 assert result.id == 1
103 assert "/v1/customers/1" in mock_http.request.call_args.args[1]
105 def test_modify_met_a_jour(
106 self, sync_client: SyncHenrriClient, mock_http: MagicMock
107 ) -> None:
108 """Test de la méthode modify_customer()."""
109 updated = {**CUSTOMER_JSON, "name": "Acme Updated"}
110 mock_http.request.return_value = make_response(updated)
111 customer = Customer(name="Acme Updated", type=CustomerType.COMPANY)
113 result = sync_client.customers.modify(1, customer)
115 assert result.name == "Acme Updated"
116 assert mock_http.request.call_args.args[0] == "PUT"
118 def test_delete_envoie_delete(
119 self, sync_client: SyncHenrriClient, mock_http: MagicMock
120 ) -> None:
121 """Test de la méthode delete_customer()."""
122 mock_http.request.return_value = make_response({})
124 sync_client.customers.delete(1)
126 assert mock_http.request.call_args.args[0] == "DELETE"
127 assert "/v1/customers/1" in mock_http.request.call_args.args[1]
129 def test_get_best_sales(
130 self, sync_client: SyncHenrriClient, mock_http: MagicMock
131 ) -> None:
132 """Test de la méthode get_best_sales()."""
133 mock_http.request.return_value = make_response(_paged([CUSTOMER_JSON]))
135 result = sync_client.customers.get_best_sales(year=2025)
137 assert len(result.elements or []) == 1
138 assert "/customers/best-sales" in mock_http.request.call_args.args[1]
140 def test_get_address(
141 self, sync_client: SyncHenrriClient, mock_http: MagicMock
142 ) -> None:
143 """Test de la méthode get_address()."""
144 mock_http.request.return_value = make_response(
145 {"id": 5, "city": "Paris", "isPostCodeShared": False}
146 )
148 address = sync_client.customers.get_address(1)
150 assert address.city == "Paris"
151 assert "/v1/customers/1/address" in mock_http.request.call_args.args[1]
153 def test_list_contacts(
154 self, sync_client: SyncHenrriClient, mock_http: MagicMock
155 ) -> None:
156 """Test de la méthode list_contacts()."""
157 contact_json = {"id": 1, "firstName": "Alice", "isPrimary": True, "showOnDocument": False}
158 mock_http.request.return_value = make_response([contact_json])
160 contacts = sync_client.customers.list_contacts(1)
162 assert len(contacts) == 1
163 assert contacts[0].first_name == "Alice"
165 def test_add_contact(
166 self, sync_client: SyncHenrriClient, mock_http: MagicMock
167 ) -> None:
168 """Test de la méthode add_contact()."""
169 contact_json = {"id": 2, "firstName": "Bob", "isPrimary": False, "showOnDocument": False}
170 mock_http.request.return_value = make_response(contact_json)
171 contact = Contact()
173 result = sync_client.customers.add_contact(1, contact)
175 assert result.id == 2
176 assert mock_http.request.call_args.args[0] == "POST"
178 def test_delete_contact(
179 self, sync_client: SyncHenrriClient, mock_http: MagicMock
180 ) -> None:
181 """Test de la méthode delete_contact()."""
182 mock_http.request.return_value = make_response({})
184 sync_client.customers.delete_contact(1, 2)
186 assert mock_http.request.call_args.args[0] == "DELETE"
187 assert "/contacts/2" in mock_http.request.call_args.args[1]
190class TestAsyncCustomers:
191 """Test de la classe AsyncCustomers."""
192 async def test_list_retourne_clients(
193 self, async_client: AsyncHenrriClient, mock_async_http: AsyncMock
194 ) -> None:
195 """Test de la méthode list_customers()."""
196 mock_async_http.request.return_value = make_response(_paged([CUSTOMER_JSON]))
198 result = await async_client.customers.list_customers(
199 request=CustomerRequest(search="", from_date="", to_date=""),
200 with_selected_fields=False,
201 )
202 assert result is not None
203 assert result.elements is not None
204 assert len(result.elements) == 1
205 assert result.elements[0].name == "Acme Corp"
207 async def test_get_retourne_client(
208 self, async_client: AsyncHenrriClient, mock_async_http: AsyncMock
209 ) -> None:
210 """Test de la méthode get_customer()."""
211 mock_async_http.request.return_value = make_response(CUSTOMER_JSON)
213 result = await async_client.customers.get(1)
215 assert result.id == 1
217 async def test_add_cree_client(
218 self, async_client: AsyncHenrriClient, mock_async_http: AsyncMock
219 ) -> None:
220 """Test de la méthode add_customer()."""
221 mock_async_http.request.return_value = make_response({**CUSTOMER_JSON, "id": 88})
222 customer = Customer(name="Async Corp", type=CustomerType.COMPANY)
224 result = await async_client.customers.add(customer)
226 assert result.id == 88
228 async def test_delete_envoie_delete(
229 self, async_client: AsyncHenrriClient, mock_async_http: AsyncMock
230 ) -> None:
231 """Test de la méthode delete_customer()."""
232 mock_async_http.request.return_value = make_response({})
234 await async_client.customers.delete(1)
236 assert mock_async_http.request.call_args.args[0] == "DELETE"