Coverage for tests/test_documents.py: 100%

110 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-05 11:17 +0200

1"""Tests du sous-client documents (synchrone et asynchrone).""" 

2 

3from __future__ import annotations 

4 

5from typing import Any 

6 

7from unittest.mock import AsyncMock, MagicMock 

8 

9import pytest # type: ignore[import] 

10 

11from henrri_connect.connect import ( 

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

13) 

14from henrri_connect.models import Document, ValidateDocumentRequest, DocumentQuery 

15from henrri_connect.models.base import DocumentKind, DocumentState 

16from tests.conftest import DOCUMENT_JSON, PAGED_META, make_response 

17 

18 

19def _paged(elements: list[Any]) -> dict[str, Any]: 

20 return {"elements": elements, "meta": PAGED_META} 

21 

22 

23class TestSyncDocuments: 

24 """Tests du sous-client documents (synchrone).""" 

25 def test_list_retourne_documents( 

26 self, sync_client: SyncHenrriClient, mock_http: MagicMock 

27 ) -> None: 

28 """Test de la méthode list_documents().""" 

29 mock_http.request.return_value = make_response(_paged([DOCUMENT_JSON])) 

30 

31 result = sync_client.documents.list_documents(request=DocumentQuery()) 

32 

33 assert result.elements is not None 

34 assert len(result.elements) == 1 

35 assert result.elements[0].id == 100 

36 

37 def test_list_passe_les_filtres( 

38 self, sync_client: SyncHenrriClient, mock_http: MagicMock 

39 ) -> None: 

40 """Test de la méthode list_documents().""" 

41 mock_http.request.return_value = make_response(_paged([])) 

42 

43 sync_client.documents.list_documents( 

44 request=DocumentQuery( 

45 document_types=[DocumentKind.INVOICE], state=DocumentState.PENDING 

46 ), 

47 ) 

48 

49 _, kwargs = mock_http.request.call_args 

50 params = kwargs["params"] 

51 # ``DocumentQuery`` serializes ``document_types`` as ``documentTypes`` and ``state`` as string 

52 assert params.get("documentTypes") is not None 

53 assert params["documentTypes"][0] == DocumentKind.INVOICE 

54 assert params["state"] == "Pending" 

55 

56 def test_add_cree_document( 

57 self, sync_client: SyncHenrriClient, mock_http: MagicMock 

58 ) -> None: 

59 """Test de la méthode add_document().""" 

60 mock_http.request.return_value = make_response({**DOCUMENT_JSON, "id": 200}) 

61 doc = Document(document_type_id=1, customer_id=1) # type: ignore[call-arg] 

62 

63 result = sync_client.documents.add(doc) 

64 

65 assert result.id == 200 

66 assert mock_http.request.call_args.args[0] == "POST" 

67 

68 def test_get_retourne_document( 

69 self, sync_client: SyncHenrriClient, mock_http: MagicMock 

70 ) -> None: 

71 """Test de la méthode get_document().""" 

72 mock_http.request.return_value = make_response(DOCUMENT_JSON) 

73 

74 result = sync_client.documents.get(100) 

75 

76 assert result.id == 100 

77 assert "/v1/documents/100" in mock_http.request.call_args.args[1] 

78 

79 def test_modify_met_a_jour( 

80 self, sync_client: SyncHenrriClient, mock_http: MagicMock 

81 ) -> None: 

82 """Test de la méthode modify_document().""" 

83 updated: dict[str, Any] = {**DOCUMENT_JSON, "title": "Facture modifiée"} 

84 mock_http.request.return_value = make_response(updated) 

85 

86 result = sync_client.documents.modify( 

87 100, Document(document_type_id=1, customer_id=1) # type: ignore[call-arg] 

88 ) 

89 

90 assert result.title == "Facture modifiée" 

91 assert mock_http.request.call_args.args[0] == "PUT" 

92 

93 def test_delete( 

94 self, sync_client: SyncHenrriClient, mock_http: MagicMock 

95 ) -> None: 

96 """Test de la méthode delete_document().""" 

97 mock_http.request.return_value = make_response({}) 

98 

99 sync_client.documents.delete(100) 

100 

101 assert mock_http.request.call_args.args[0] == "DELETE" 

102 

103 def test_get_tax_details( 

104 self, sync_client: SyncHenrriClient, mock_http: MagicMock 

105 ) -> None: 

106 """Test de la méthode get_tax_details().""" 

107 mock_http.request.return_value = make_response( 

108 { 

109 "taxDetailArray": [ 

110 { 

111 "rate": 20.0, 

112 "priceBeforeTax": 100.0, 

113 "taxAmount": 20.0, 

114 "priceAfterTax": 120.0 

115 } 

116 ] 

117 } 

118 ) 

119 

120 result = sync_client.documents.get_tax_details(100) 

121 

122 assert result.tax_detail_array is not None 

123 assert len(result.tax_detail_array) == 1 

124 assert result.tax_detail_array[0].rate == pytest.approx(20.0) # type: ignore[misc] 

125 

126 def test_validate_document( 

127 self, sync_client: SyncHenrriClient, mock_http: MagicMock 

128 ) -> None: 

129 """Test de la méthode validate_document().""" 

130 mock_http.request.return_value = make_response({**DOCUMENT_JSON, "validated": True}) 

131 req = ValidateDocumentRequest( 

132 email="sign@example.com", 

133 first_name="Jean", 

134 last_name="Dupont", 

135 validation_date="2025-01-01", 

136 time_offset=60, 

137 ip="127.0.0.1", 

138 ) 

139 

140 result = sync_client.documents.validate(100, req) 

141 

142 assert result.validated is True 

143 assert mock_http.request.call_args.args[0] == "POST" 

144 assert "/v1/documents/100/validate" in mock_http.request.call_args.args[1] 

145 

146 def test_get_pdf_url( 

147 self, sync_client: SyncHenrriClient, mock_http: MagicMock 

148 ) -> None: 

149 """Test de la méthode get_pdf_url().""" 

150 mock_http.request.return_value = make_response( 

151 { 

152 "downloadUrl": "https://cdn.example.com/facture.pdf", 

153 "fileName": "facture.pdf" 

154 } 

155 ) 

156 

157 result = sync_client.documents.get_pdf_url(100) 

158 

159 assert result.download_url == "https://cdn.example.com/facture.pdf" 

160 

161 def test_get_pdf_bytes( 

162 self, sync_client: SyncHenrriClient, mock_http: MagicMock 

163 ) -> None: 

164 """Test de la méthode get_pdf_bytes().""" 

165 pdf_bytes = b"%PDF-1.4 test" 

166 mock_http.request.return_value = make_response({}, content=pdf_bytes) 

167 

168 result = sync_client.documents.get_pdf_bytes(100) 

169 

170 assert result == pdf_bytes 

171 

172 def test_finalize( 

173 self, sync_client: SyncHenrriClient, mock_http: MagicMock 

174 ) -> None: 

175 """Test de la méthode finalize().""" 

176 mock_http.request.return_value = make_response({**DOCUMENT_JSON, "finalized": True}) 

177 

178 result = sync_client.documents.finalize(100) 

179 

180 assert result.finalized is True 

181 assert "/v1/documents/100/finalize" in mock_http.request.call_args.args[1] 

182 

183 def test_transform_to_invoice( 

184 self, sync_client: SyncHenrriClient, mock_http: MagicMock 

185 ) -> None: 

186 """Test de la méthode transform_to_invoice().""" 

187 mock_http.request.return_value = make_response({**DOCUMENT_JSON, "id": 101}) 

188 

189 result = sync_client.documents.transform_to_invoice(100) 

190 

191 assert result.id == 101 

192 assert "/transform-to-invoice" in mock_http.request.call_args.args[1] 

193 

194 def test_get_payment_milestones( 

195 self, sync_client: SyncHenrriClient, mock_http: MagicMock 

196 ) -> None: 

197 """Test de la méthode get_payment_milestones().""" 

198 milestone: dict[str, Any] = { 

199 "id": 1, 

200 "dueDate": "2025-06-30T00:00:00", 

201 "isPaid": False, 

202 "documentId": 100, 

203 } 

204 mock_http.request.return_value = make_response({"elements": [milestone]}) 

205 

206 result = sync_client.documents.get_payment_milestones(100) 

207 

208 assert result.elements is not None 

209 assert len(result.elements) == 1 

210 assert result.elements[0].document_id == 100 

211 

212 def test_get_with_all( 

213 self, sync_client: SyncHenrriClient, mock_http: MagicMock 

214 ) -> None: 

215 """Test de la méthode get_with_all().""" 

216 mock_http.request.return_value = make_response(DOCUMENT_JSON) 

217 

218 result = sync_client.documents.get_with_all(100) 

219 

220 assert result.id == 100 

221 assert "/with-all" in mock_http.request.call_args.args[1] 

222 

223 

224class TestAsyncDocuments: 

225 """Test de la classe AsyncDocuments.""" 

226 async def test_list_retourne_documents( 

227 self, async_client: AsyncHenrriClient, mock_async_http: AsyncMock 

228 ) -> None: 

229 """Test de la méthode list_documents().""" 

230 mock_async_http.request.return_value = make_response(_paged([DOCUMENT_JSON])) 

231 

232 result = await async_client.documents.list_documents(request=DocumentQuery()) 

233 

234 assert result.elements is not None 

235 assert len(result.elements) == 1 

236 assert result.elements[0].id == 100 

237 

238 async def test_get_retourne_document( 

239 self, async_client: AsyncHenrriClient, mock_async_http: AsyncMock 

240 ) -> None: 

241 """Test de la méthode get_document().""" 

242 mock_async_http.request.return_value = make_response(DOCUMENT_JSON) 

243 

244 result = await async_client.documents.get(100) 

245 

246 assert result.id == 100 

247 

248 async def test_finalize( 

249 self, async_client: AsyncHenrriClient, mock_async_http: AsyncMock 

250 ) -> None: 

251 """Test de la méthode finalize().""" 

252 mock_async_http.request.return_value = make_response({**DOCUMENT_JSON, "finalized": True}) 

253 

254 result = await async_client.documents.finalize(100) 

255 

256 assert result.finalized is True 

257 

258 async def test_get_pdf_bytes( 

259 self, async_client: AsyncHenrriClient, mock_async_http: AsyncMock 

260 ) -> None: 

261 """Test de la méthode get_pdf_bytes().""" 

262 pdf_bytes = b"%PDF-1.4 async" 

263 mock_async_http.request.return_value = make_response({}, content=pdf_bytes) 

264 

265 result = await async_client.documents.get_pdf_bytes(100) 

266 

267 assert result == pdf_bytes