@@ -72,6 +72,7 @@ def test_sign_pdf_with_pfx_credentials(monkeypatch: pytest.MonkeyPatch) -> None:
7272 pfx_file = make_pfx_file (str (PdfRestFileID .generate ()))
7373 passphrase_file = make_passphrase_file (str (PdfRestFileID .generate ()))
7474 output_id = str (PdfRestFileID .generate ())
75+ seen = {"post" : 0 , "get" : 0 }
7576
7677 signature_configuration = {
7778 "type" : "new" ,
@@ -90,6 +91,7 @@ def test_sign_pdf_with_pfx_credentials(monkeypatch: pytest.MonkeyPatch) -> None:
9091
9192 def handler (request : httpx .Request ) -> httpx .Response :
9293 if request .method == "POST" and request .url .path == "/signed-pdf" :
94+ seen ["post" ] += 1
9395 payload = json .loads (request .content .decode ("utf-8" ))
9496 assert payload == payload_dump
9597 return httpx .Response (
@@ -100,6 +102,7 @@ def handler(request: httpx.Request) -> httpx.Response:
100102 },
101103 )
102104 if request .method == "GET" and request .url .path == f"/resource/{ output_id } " :
105+ seen ["get" ] += 1
103106 assert request .url .params ["format" ] == "info"
104107 return httpx .Response (
105108 200 ,
@@ -124,6 +127,7 @@ def handler(request: httpx.Request) -> httpx.Response:
124127 assert isinstance (response , PdfRestFileBasedResponse )
125128 assert response .output_file .name == "signed-pdf.pdf"
126129 assert str (input_file .id ) in {str (item ) for item in response .input_ids }
130+ assert seen == {"post" : 1 , "get" : 1 }
127131
128132
129133def test_sign_pdf_with_certificate_credentials_and_logo (
@@ -135,6 +139,7 @@ def test_sign_pdf_with_certificate_credentials_and_logo(
135139 private_key_file = make_private_key_file (str (PdfRestFileID .generate ()))
136140 logo_file = make_logo_file (str (PdfRestFileID .generate ()))
137141 output_id = str (PdfRestFileID .generate ())
142+ seen = {"post" : 0 , "get" : 0 }
138143
139144 signature_configuration = {
140145 "type" : "new" ,
@@ -159,6 +164,7 @@ def test_sign_pdf_with_certificate_credentials_and_logo(
159164
160165 def handler (request : httpx .Request ) -> httpx .Response :
161166 if request .method == "POST" and request .url .path == "/signed-pdf" :
167+ seen ["post" ] += 1
162168 payload = json .loads (request .content .decode ("utf-8" ))
163169 assert payload == payload_dump
164170 return httpx .Response (
@@ -174,6 +180,7 @@ def handler(request: httpx.Request) -> httpx.Response:
174180 },
175181 )
176182 if request .method == "GET" and request .url .path == f"/resource/{ output_id } " :
183+ seen ["get" ] += 1
177184 return httpx .Response (
178185 200 ,
179186 json = build_file_info_payload (
@@ -198,6 +205,7 @@ def handler(request: httpx.Request) -> httpx.Response:
198205 assert isinstance (response , PdfRestFileBasedResponse )
199206 assert response .output_file .name == "signed.pdf"
200207 assert logo_file .id in response .input_ids
208+ assert seen == {"post" : 1 , "get" : 1 }
201209
202210
203211def test_sign_pdf_requires_credential_pair (
@@ -266,6 +274,86 @@ def test_sign_pdf_requires_location_for_new_signature_type(
266274 )
267275
268276
277+ def test_sign_pdf_rejects_multiple_input_files (
278+ monkeypatch : pytest .MonkeyPatch ,
279+ ) -> None :
280+ monkeypatch .delenv ("PDFREST_API_KEY" , raising = False )
281+ input_file_a = make_pdf_file (PdfRestFileID .generate ())
282+ input_file_b = make_pdf_file (PdfRestFileID .generate ())
283+ pfx_file = make_pfx_file (str (PdfRestFileID .generate ()))
284+ passphrase_file = make_passphrase_file (str (PdfRestFileID .generate ()))
285+ transport = httpx .MockTransport (lambda request : (_ for _ in ()).throw (RuntimeError ))
286+
287+ with (
288+ PdfRestClient (api_key = VALID_API_KEY , transport = transport ) as client ,
289+ pytest .raises (ValidationError , match = r"files|at most 1 item" ),
290+ ):
291+ client .sign_pdf (
292+ [input_file_a , input_file_b ],
293+ signature_configuration = {
294+ "type" : "new" ,
295+ "location" : make_signature_location (),
296+ },
297+ credentials = {"pfx" : pfx_file , "passphrase" : passphrase_file },
298+ )
299+
300+
301+ def test_sign_pdf_rejects_multiple_logo_files (
302+ monkeypatch : pytest .MonkeyPatch ,
303+ ) -> None :
304+ monkeypatch .delenv ("PDFREST_API_KEY" , raising = False )
305+ input_file = make_pdf_file (PdfRestFileID .generate ())
306+ certificate_file = make_certificate_file (str (PdfRestFileID .generate ()))
307+ private_key_file = make_private_key_file (str (PdfRestFileID .generate ()))
308+ logo_file_a = make_logo_file (str (PdfRestFileID .generate ()))
309+ logo_file_b = make_logo_file (str (PdfRestFileID .generate ()))
310+ transport = httpx .MockTransport (lambda request : (_ for _ in ()).throw (RuntimeError ))
311+
312+ with (
313+ PdfRestClient (api_key = VALID_API_KEY , transport = transport ) as client ,
314+ pytest .raises (ValidationError , match = r"logo|at most 1 item" ),
315+ ):
316+ client .sign_pdf (
317+ input_file ,
318+ signature_configuration = {
319+ "type" : "new" ,
320+ "location" : make_signature_location (),
321+ },
322+ credentials = {
323+ "certificate" : certificate_file ,
324+ "private_key" : private_key_file ,
325+ },
326+ logo = [logo_file_a , logo_file_b ],
327+ )
328+
329+
330+ def test_sign_pdf_rejects_multiple_pfx_credential_files (
331+ monkeypatch : pytest .MonkeyPatch ,
332+ ) -> None :
333+ monkeypatch .delenv ("PDFREST_API_KEY" , raising = False )
334+ input_file = make_pdf_file (PdfRestFileID .generate ())
335+ pfx_file_a = make_pfx_file (str (PdfRestFileID .generate ()))
336+ pfx_file_b = make_pfx_file (str (PdfRestFileID .generate ()))
337+ passphrase_file = make_passphrase_file (str (PdfRestFileID .generate ()))
338+ transport = httpx .MockTransport (lambda request : (_ for _ in ()).throw (RuntimeError ))
339+
340+ with (
341+ PdfRestClient (api_key = VALID_API_KEY , transport = transport ) as client ,
342+ pytest .raises (ValidationError , match = r"pfx|at most 1 item" ),
343+ ):
344+ client .sign_pdf (
345+ input_file ,
346+ signature_configuration = {
347+ "type" : "new" ,
348+ "location" : make_signature_location (),
349+ },
350+ credentials = {
351+ "pfx" : [pfx_file_a , pfx_file_b ], # type: ignore[list-item]
352+ "passphrase" : passphrase_file ,
353+ },
354+ )
355+
356+
269357@pytest .mark .asyncio
270358async def test_async_sign_pdf_requires_credential_pair (
271359 monkeypatch : pytest .MonkeyPatch ,
@@ -329,6 +417,83 @@ async def test_async_sign_pdf_requires_location_for_new_signature_type(
329417 )
330418
331419
420+ @pytest .mark .asyncio
421+ async def test_async_sign_pdf_rejects_multiple_input_files (
422+ monkeypatch : pytest .MonkeyPatch ,
423+ ) -> None :
424+ monkeypatch .delenv ("PDFREST_API_KEY" , raising = False )
425+ input_file_a = make_pdf_file (PdfRestFileID .generate ())
426+ input_file_b = make_pdf_file (PdfRestFileID .generate ())
427+ pfx_file = make_pfx_file (str (PdfRestFileID .generate ()))
428+ passphrase_file = make_passphrase_file (str (PdfRestFileID .generate ()))
429+ transport = httpx .MockTransport (lambda request : (_ for _ in ()).throw (RuntimeError ))
430+
431+ async with AsyncPdfRestClient (api_key = ASYNC_API_KEY , transport = transport ) as client :
432+ with pytest .raises (ValidationError , match = r"files|at most 1 item" ):
433+ await client .sign_pdf (
434+ [input_file_a , input_file_b ],
435+ signature_configuration = {
436+ "type" : "new" ,
437+ "location" : make_signature_location (),
438+ },
439+ credentials = {"pfx" : pfx_file , "passphrase" : passphrase_file },
440+ )
441+
442+
443+ @pytest .mark .asyncio
444+ async def test_async_sign_pdf_rejects_multiple_logo_files (
445+ monkeypatch : pytest .MonkeyPatch ,
446+ ) -> None :
447+ monkeypatch .delenv ("PDFREST_API_KEY" , raising = False )
448+ input_file = make_pdf_file (PdfRestFileID .generate ())
449+ certificate_file = make_certificate_file (str (PdfRestFileID .generate ()))
450+ private_key_file = make_private_key_file (str (PdfRestFileID .generate ()))
451+ logo_file_a = make_logo_file (str (PdfRestFileID .generate ()))
452+ logo_file_b = make_logo_file (str (PdfRestFileID .generate ()))
453+ transport = httpx .MockTransport (lambda request : (_ for _ in ()).throw (RuntimeError ))
454+
455+ async with AsyncPdfRestClient (api_key = ASYNC_API_KEY , transport = transport ) as client :
456+ with pytest .raises (ValidationError , match = r"logo|at most 1 item" ):
457+ await client .sign_pdf (
458+ input_file ,
459+ signature_configuration = {
460+ "type" : "new" ,
461+ "location" : make_signature_location (),
462+ },
463+ credentials = {
464+ "certificate" : certificate_file ,
465+ "private_key" : private_key_file ,
466+ },
467+ logo = [logo_file_a , logo_file_b ],
468+ )
469+
470+
471+ @pytest .mark .asyncio
472+ async def test_async_sign_pdf_rejects_multiple_pfx_credential_files (
473+ monkeypatch : pytest .MonkeyPatch ,
474+ ) -> None :
475+ monkeypatch .delenv ("PDFREST_API_KEY" , raising = False )
476+ input_file = make_pdf_file (PdfRestFileID .generate ())
477+ pfx_file_a = make_pfx_file (str (PdfRestFileID .generate ()))
478+ pfx_file_b = make_pfx_file (str (PdfRestFileID .generate ()))
479+ passphrase_file = make_passphrase_file (str (PdfRestFileID .generate ()))
480+ transport = httpx .MockTransport (lambda request : (_ for _ in ()).throw (RuntimeError ))
481+
482+ async with AsyncPdfRestClient (api_key = ASYNC_API_KEY , transport = transport ) as client :
483+ with pytest .raises (ValidationError , match = r"pfx|at most 1 item" ):
484+ await client .sign_pdf (
485+ input_file ,
486+ signature_configuration = {
487+ "type" : "new" ,
488+ "location" : make_signature_location (),
489+ },
490+ credentials = {
491+ "pfx" : [pfx_file_a , pfx_file_b ], # type: ignore[list-item]
492+ "passphrase" : passphrase_file ,
493+ },
494+ )
495+
496+
332497@pytest .mark .parametrize (
333498 "signature_type" ,
334499 [
@@ -437,9 +602,11 @@ def test_sign_pdf_request_customization(
437602 private_key_file = make_private_key_file (str (PdfRestFileID .generate ()))
438603 output_id = str (PdfRestFileID .generate ())
439604 captured_timeout : dict [str , float | dict [str , float ] | None ] = {}
605+ seen = {"post" : 0 , "get" : 0 }
440606
441607 def handler (request : httpx .Request ) -> httpx .Response :
442608 if request .method == "POST" and request .url .path == "/signed-pdf" :
609+ seen ["post" ] += 1
443610 assert request .url .params ["trace" ] == "sync"
444611 assert request .headers ["X-Debug" ] == "sync-sign"
445612 captured_timeout ["value" ] = request .extensions .get ("timeout" )
@@ -454,6 +621,7 @@ def handler(request: httpx.Request) -> httpx.Response:
454621 json = {"inputId" : [input_file .id ], "outputId" : [output_id ]},
455622 )
456623 if request .method == "GET" and request .url .path == f"/resource/{ output_id } " :
624+ seen ["get" ] += 1
457625 assert request .url .params ["trace" ] == "sync"
458626 assert request .headers ["X-Debug" ] == "sync-sign"
459627 return httpx .Response (
@@ -494,6 +662,7 @@ def handler(request: httpx.Request) -> httpx.Response:
494662 assert all (pytest .approx (0.5 ) == value for value in timeout_value .values ())
495663 else :
496664 assert timeout_value == pytest .approx (0.5 )
665+ assert seen == {"post" : 1 , "get" : 1 }
497666
498667
499668@pytest .mark .asyncio
@@ -506,9 +675,11 @@ async def test_async_sign_pdf_request_customization(
506675 private_key_file = make_private_key_file (str (PdfRestFileID .generate ()))
507676 output_id = str (PdfRestFileID .generate ())
508677 captured_timeout : dict [str , float | dict [str , float ] | None ] = {}
678+ seen = {"post" : 0 , "get" : 0 }
509679
510680 def handler (request : httpx .Request ) -> httpx .Response :
511681 if request .method == "POST" and request .url .path == "/signed-pdf" :
682+ seen ["post" ] += 1
512683 assert request .url .params ["trace" ] == "async"
513684 assert request .headers ["X-Debug" ] == "async-sign"
514685 captured_timeout ["value" ] = request .extensions .get ("timeout" )
@@ -523,6 +694,7 @@ def handler(request: httpx.Request) -> httpx.Response:
523694 json = {"inputId" : [input_file .id ], "outputId" : [output_id ]},
524695 )
525696 if request .method == "GET" and request .url .path == f"/resource/{ output_id } " :
697+ seen ["get" ] += 1
526698 assert request .url .params ["trace" ] == "async"
527699 assert request .headers ["X-Debug" ] == "async-sign"
528700 return httpx .Response (
@@ -566,6 +738,7 @@ def handler(request: httpx.Request) -> httpx.Response:
566738 assert all (pytest .approx (0.5 ) == value for value in timeout_value .values ())
567739 else :
568740 assert timeout_value == pytest .approx (0.5 )
741+ assert seen == {"post" : 1 , "get" : 1 }
569742
570743
571744def test_sign_payload_requires_location_when_type_new () -> None :
0 commit comments