Coverage for tests/conftest.py: 100%
60 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"""Fixtures partagées pour l'ensemble des tests henrri-connect."""
3from __future__ import annotations
5from typing import Any
6from unittest.mock import AsyncMock, MagicMock
8import httpx
9import pytest
11from henrri_connect.connect import (
12 AsyncHenrriClient, SyncHenrriClient, # type: ignore[import]
13 )
16def make_response(
17 data: dict[str, Any] | list[Any],
18 status_code: int = 200,
19 content: bytes | None = None,
20 text: str = "",
21) -> MagicMock:
22 """Construit une réponse httpx mockée."""
23 mock = MagicMock(spec=httpx.Response)
24 mock.status_code = status_code
25 mock.is_success = status_code < 400
26 mock.json.return_value = data
27 mock.content = content or b""
28 mock.text = text
29 return mock
32def _build_sync_client(mock_http: MagicMock) -> SyncHenrriClient: # pylint: disable=W0621
33 """Crée un client synchrone avec HTTP mocké (sans __init__)."""
34 client: SyncHenrriClient = SyncHenrriClient.__new__(SyncHenrriClient)
35 client._client_id = "test_id" # pylint: disable=W0212
36 client._client_secret = "test_secret" # pylint: disable=W0212
37 client._base_url = "https://api-sandbox.henrri.io" # pylint: disable=W0212
38 client._access_token = "fake_access_token" # pylint: disable=W0212
39 client._refresh_token_str = "fake_refresh_token" # pylint: disable=W0212
40 client._http = mock_http # pylint: disable=W0212
41 client._init_subclients() # pylint: disable=W0212
42 return client
45def _build_async_client(mock_http: AsyncMock) -> AsyncHenrriClient: # pylint: disable=W0621
46 """Crée un client asynchrone avec HTTP mocké (sans __init__)."""
47 client: AsyncHenrriClient = AsyncHenrriClient.__new__(AsyncHenrriClient)
48 client._client_id = "test_id" # pylint: disable=W0212
49 client._client_secret = "test_secret" # pylint: disable=W0212
50 client._base_url = "https://api-sandbox.henrri.io" # pylint: disable=W0212
51 client._access_token = "fake_access_token" # pylint: disable=W0212
52 client._refresh_token_str = "fake_refresh_token" # pylint: disable=W0212
53 client._http = mock_http # pylint: disable=W0212
54 client._init_subclients() # type: ignore[union-attr] # pylint: disable=W0212
55 return client # pylint: disable=W0212
58@pytest.fixture
59def mock_http() -> MagicMock:
60 """Mock httpx.Client."""
61 return MagicMock(spec=httpx.Client)
64@pytest.fixture
65def mock_async_http() -> AsyncMock:
66 """Mock httpx.AsyncClient."""
67 return AsyncMock(spec=httpx.AsyncClient)
70@pytest.fixture
71def sync_client(mock_http: MagicMock) -> SyncHenrriClient: # pylint: disable=W0621
72 """Client synchrone prêt à l'emploi avec HTTP mocké."""
73 return _build_sync_client(mock_http)
76@pytest.fixture
77async def async_client(mock_async_http: AsyncMock) -> AsyncHenrriClient: # pylint: disable=W0621
78 """Client asynchrone prêt à l'emploi avec HTTP mocké."""
79 return _build_async_client(mock_async_http)
82# ── Helpers de données de test ────────────────────────────────────────────────
84CUSTOMER_JSON: dict[str, Any] = {
85 "id": 1,
86 "name": "Acme Corp",
87 "type": "professional",
88 "accountingNumber": "411000",
89 "isDeleted": False,
90 "isSupplier": False,
91 "serviceDiscountPercentage": 0.0,
92 "productDiscountPercentage": 0.0,
93 "customerTypeAlertEnabled": False,
94}
96COMPANY_JSON: dict[str, Any] = {
97 "id": 42,
98 "name": "Ma Société",
99 "isSelfEmployed": False,
100}
102USER_JSON: dict[str, Any] = {
103 "id": 7,
104 "email": "user@example.com",
105 "firstName": "Jean",
106 "lastName": "Dupont",
107 "isEnabled": True,
108 "twoFactorEnabled": False,
109}
111DOCUMENT_JSON: dict[str, Any] = {
112 "id": 100,
113 "documentTypeId": 1,
114 "customerId": 1,
115 "finalized": False,
116 "priceBeforeTax": 100.0,
117 "taxAmount": 20.0,
118 "priceAfterTax": 120.0,
119 "validated": False,
120 "userCanValidate": False,
121}
123DOCUMENT_LINE_JSON: dict[str, Any] = {
124 "id": 10,
125 "documentId": 100,
126 "typeId": 2,
127 "quantity": 1.0,
128 "purchasingPriceWithoutTax": 0.0,
129 "isATaxIncluded": False,
130 "areElementsOfGroupShown": False,
131 "isAGroup": False,
132 "doesGroupOwnDifferentVat": False,
133 "isMemberOfAGroup": False,
134 "isAdjustmentOfGroup": False,
135}
137UNIT_JSON: dict[str, Any] = {
138 "id": 3,
139 "name": "Heure",
140 "unitKind": "Hourly",
141}
143ITEM_JSON: dict[str, Any] = {
144 "id": 5,
145 "description": "Prestation de conseil",
146 "vatPercent": 20.0,
147 "creationDate": "2025-01-01T00:00:00",
148 "isATaxIncluded": False,
149 "purchasePrice": 0.0,
150 "isAGroup": False,
151}
153ITEM_CATEGORY_JSON: dict[str, Any] = {
154 "id": 2,
155 "label": "Services",
156 "marginPercent": 0.0,
157 "hourlyRate": 0.0,
158 "vat": 20.0,
159 "isAddedToRevenue": True,
160 "isDefault": False,
161 "isDeleted": False,
162 "itemCategoryKind": "Service",
163}
165DOCUMENT_TYPE_JSON: dict[str, Any] = {
166 "id": 1,
167 "label": "Facture",
168 "shortLabel": "FA",
169 "documentKind": "Invoice",
170 "isAccounting": True,
171 "isManaged": True,
172 "isMandatory": False,
173 "isVisible": True,
174}
176DOCUMENT_LINE_TYPE_JSON: dict[str, Any] = {
177 "id": 1,
178 "label": "Article",
179 "type": "Item",
180}
182TOKEN_JSON: dict[str, Any] = {
183 "accessToken": "new_access_token",
184 "refreshToken": "new_refresh_token",
185 "expiresIn": 3600,
186 "isError": False,
187}
189REVENUE_JSON: dict[str, Any] = {
190 "year": 2025,
191 "totalServicesRevenue": 50000.0,
192 "totalProductsRevenue": 10000.0,
193 "totalProductsMargin": 3000.0,
194 "totalServiceHours": 500.0,
195}
197MONTHLY_REVENUE_JSON: dict[str, Any] = {
198 "month": 1,
199 "year": 2025,
200 "totalServicesRevenue": 4000.0,
201 "totalProductsRevenue": 800.0,
202 "totalProductsMargin": 250.0,
203 "totalServiceHours": 40.0,
204}
206PAGED_META: dict[str, Any] = {
207 "page": 1,
208 "limit": 50,
209 "totalCount": 1,
210 "totalPages": 1,
211 "hasNext": False,
212}