Coverage for tests/conftest.py: 100%

60 statements  

« 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.""" 

2 

3from __future__ import annotations 

4 

5from typing import Any 

6from unittest.mock import AsyncMock, MagicMock 

7 

8import httpx 

9import pytest 

10 

11from henrri_connect.connect import ( 

12 AsyncHenrriClient, SyncHenrriClient, # type: ignore[import] 

13 ) 

14 

15 

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 

30 

31 

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 

43 

44 

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 

56 

57 

58@pytest.fixture 

59def mock_http() -> MagicMock: 

60 """Mock httpx.Client.""" 

61 return MagicMock(spec=httpx.Client) 

62 

63 

64@pytest.fixture 

65def mock_async_http() -> AsyncMock: 

66 """Mock httpx.AsyncClient.""" 

67 return AsyncMock(spec=httpx.AsyncClient) 

68 

69 

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) 

74 

75 

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) 

80 

81 

82# ── Helpers de données de test ──────────────────────────────────────────────── 

83 

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} 

95 

96COMPANY_JSON: dict[str, Any] = { 

97 "id": 42, 

98 "name": "Ma Société", 

99 "isSelfEmployed": False, 

100} 

101 

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} 

110 

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} 

122 

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} 

136 

137UNIT_JSON: dict[str, Any] = { 

138 "id": 3, 

139 "name": "Heure", 

140 "unitKind": "Hourly", 

141} 

142 

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} 

152 

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} 

164 

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} 

175 

176DOCUMENT_LINE_TYPE_JSON: dict[str, Any] = { 

177 "id": 1, 

178 "label": "Article", 

179 "type": "Item", 

180} 

181 

182TOKEN_JSON: dict[str, Any] = { 

183 "accessToken": "new_access_token", 

184 "refreshToken": "new_refresh_token", 

185 "expiresIn": 3600, 

186 "isError": False, 

187} 

188 

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} 

196 

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} 

205 

206PAGED_META: dict[str, Any] = { 

207 "page": 1, 

208 "limit": 50, 

209 "totalCount": 1, 

210 "totalPages": 1, 

211 "hasNext": False, 

212}