From 6209b28d48ccb831fc73855cf67695b6f9558a48 Mon Sep 17 00:00:00 2001 From: Mehdi Ben Hamida Date: Tue, 15 Sep 2026 14:57:38 +0200 Subject: [PATCH 1/9] :test_tube: add webhook test to Python integration tests --- tests/v2/test_client_integration.py | 58 ++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/tests/v2/test_client_integration.py b/tests/v2/test_client_integration.py index fd477ba5..f8808603 100644 --- a/tests/v2/test_client_integration.py +++ b/tests/v2/test_client_integration.py @@ -1,5 +1,6 @@ import os from pathlib import Path +from datetime import datetime import httpx import pytest @@ -13,7 +14,9 @@ ) from mindee.v2.parsing import InferenceActiveOptions from mindee.v2.product.extraction.extraction_response import ExtractionResponse -from tests.utils import FILE_TYPES_PATH, V2_PRODUCT_PATH +from mindee.v2.product.split.params.split_parameters import SplitParameters +from mindee.v2.product.split.split_response import SplitResponse +from tests.utils import FILE_TYPES_DIR, V2_PRODUCT_PATH @pytest.fixture(scope="session") @@ -166,6 +169,59 @@ def test_parse_file_filled_single_page_must_succeed( assert len(supplier_name.locations) == 0 +def _assert_webhook_job_success(response, webhook_ids: list[str]) -> None: + assert response.inference is not None + assert response.inference.result is not None + + assert response.job is not None + assert response.job.status == "Processed" + assert isinstance(response.job.completed_at, datetime) + assert response.job.error is None + assert len(response.job.webhooks) == 2 + assert all(webhook.status in {"Completed", "Failed"} for webhook in response.job.webhooks) + assert {webhook.id for webhook in response.job.webhooks} == set(webhook_ids) + + +@pytest.mark.integration +@pytest.mark.v2 +def test_extraction_with_two_webhooks_must_complete_and_succeed( + v2_client: Client, findoc_model_id: str +) -> None: + webhook_ids = [ + "-aa11-aa11-bdc5-2f8496c5641aa2286ed9", + "b2286ed9-aa11-aa11-bdc5-2f8496c5641a", + ] + + input_source = PathInput( + V2_PRODUCT_DATA_DIR / "extraction" / "financial_document" / "default_sample.jpg" + ) + params = ExtractionParameters(model_id=findoc_model_id, webhook_ids=webhook_ids) + + response = v2_client.enqueue_and_get_result(ExtractionResponse, input_source, params) + + _assert_webhook_job_success(response, webhook_ids) + assert response.inference.result.fields["supplier_name"].value == "John Smith" + + +@pytest.mark.integration +@pytest.mark.v2 +def test_split_with_two_webhooks_must_complete_and_succeed( + v2_client: Client, split_model_id: str +) -> None: + webhook_ids = [ + "a2286ed9-aa11-aa11-bdc5-2f8496c5641a", + "b2286ed9-aa11-aa11-bdc5-2f8496c5641a", + ] + + input_source = PathInput(V2_PRODUCT_DATA_DIR / "split" / "default_sample.pdf") + params = SplitParameters(model_id=split_model_id, webhook_ids=webhook_ids) + + response = v2_client.enqueue_and_get_result(SplitResponse, input_source, params) + + _assert_webhook_job_success(response, webhook_ids) + assert len(response.inference.result.splits) == 2 + + @pytest.mark.integration @pytest.mark.v2 def test_invalid_uuid_must_throw_error(v2_client: Client) -> None: From a4ca949c0ccab36ad536ddbfe9bd14053f166472 Mon Sep 17 00:00:00 2001 From: Mehdi Ben Hamida Date: Wed, 16 Sep 2026 10:38:24 +0200 Subject: [PATCH 2/9] :art: refactor code style in integration tests for readability --- tests/v2/test_client_integration.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/v2/test_client_integration.py b/tests/v2/test_client_integration.py index f8808603..c8a47453 100644 --- a/tests/v2/test_client_integration.py +++ b/tests/v2/test_client_integration.py @@ -1,6 +1,6 @@ import os -from pathlib import Path from datetime import datetime +from pathlib import Path import httpx import pytest @@ -178,7 +178,9 @@ def _assert_webhook_job_success(response, webhook_ids: list[str]) -> None: assert isinstance(response.job.completed_at, datetime) assert response.job.error is None assert len(response.job.webhooks) == 2 - assert all(webhook.status in {"Completed", "Failed"} for webhook in response.job.webhooks) + assert all( + webhook.status in {"Completed", "Failed"} for webhook in response.job.webhooks + ) assert {webhook.id for webhook in response.job.webhooks} == set(webhook_ids) @@ -197,7 +199,9 @@ def test_extraction_with_two_webhooks_must_complete_and_succeed( ) params = ExtractionParameters(model_id=findoc_model_id, webhook_ids=webhook_ids) - response = v2_client.enqueue_and_get_result(ExtractionResponse, input_source, params) + response = v2_client.enqueue_and_get_result( + ExtractionResponse, input_source, params + ) _assert_webhook_job_success(response, webhook_ids) assert response.inference.result.fields["supplier_name"].value == "John Smith" From b65bf692682670ecfa1e0a20cb4b7866a945e3a9 Mon Sep 17 00:00:00 2001 From: Mehdi Ben Hamida Date: Wed, 16 Sep 2026 15:59:21 +0200 Subject: [PATCH 3/9] :test_tube: add split model ID fixture and update webhook test data --- tests/conftest.py | 6 ++++++ tests/v2/test_client_integration.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index b5cc6b4a..0a67507f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -14,3 +14,9 @@ def force_gc(): def findoc_model_id() -> str: """Identifier of the Financial Document model, supplied through an env var.""" return os.getenv("MINDEE_V2_SE_TESTS_FINDOC_MODEL_ID", "") + + +@pytest.fixture(scope="session") +def split_model_id() -> str: + """Identifier of the Split model, supplied through an env var.""" + return os.getenv("MINDEE_V2_SE_TESTS_SPLIT_MODEL_ID", "") \ No newline at end of file diff --git a/tests/v2/test_client_integration.py b/tests/v2/test_client_integration.py index c8a47453..0ac42892 100644 --- a/tests/v2/test_client_integration.py +++ b/tests/v2/test_client_integration.py @@ -190,7 +190,7 @@ def test_extraction_with_two_webhooks_must_complete_and_succeed( v2_client: Client, findoc_model_id: str ) -> None: webhook_ids = [ - "-aa11-aa11-bdc5-2f8496c5641aa2286ed9", + "a2286ed9-aa11-aa11-bdc5-2f8496c5641a", "b2286ed9-aa11-aa11-bdc5-2f8496c5641a", ] From 3da56123ca16d3ec97654449d90b6397ccf43598 Mon Sep 17 00:00:00 2001 From: Mehdi Ben Hamida Date: Wed, 16 Sep 2026 16:06:25 +0200 Subject: [PATCH 4/9] :test_tube: add split model ID fixture and update webhook test data --- tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 0a67507f..c44074e4 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -19,4 +19,4 @@ def findoc_model_id() -> str: @pytest.fixture(scope="session") def split_model_id() -> str: """Identifier of the Split model, supplied through an env var.""" - return os.getenv("MINDEE_V2_SE_TESTS_SPLIT_MODEL_ID", "") \ No newline at end of file + return os.getenv("MINDEE_V2_SE_TESTS_SPLIT_MODEL_ID", "") From b780d329452d1008d3a49ade70ebee15c2b4d424 Mon Sep 17 00:00:00 2001 From: Mehdi Ben Hamida Date: Tue, 22 Sep 2026 14:35:04 +0200 Subject: [PATCH 5/9] :test_tube: update input source paths in integration tests for consistency --- tests/v2/test_client_integration.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/v2/test_client_integration.py b/tests/v2/test_client_integration.py index 0ac42892..462cd702 100644 --- a/tests/v2/test_client_integration.py +++ b/tests/v2/test_client_integration.py @@ -195,7 +195,7 @@ def test_extraction_with_two_webhooks_must_complete_and_succeed( ] input_source = PathInput( - V2_PRODUCT_DATA_DIR / "extraction" / "financial_document" / "default_sample.jpg" + V2_PRODUCT_PATH / "extraction" / "financial_document" / "default_sample.jpg" ) params = ExtractionParameters(model_id=findoc_model_id, webhook_ids=webhook_ids) @@ -217,7 +217,7 @@ def test_split_with_two_webhooks_must_complete_and_succeed( "b2286ed9-aa11-aa11-bdc5-2f8496c5641a", ] - input_source = PathInput(V2_PRODUCT_DATA_DIR / "split" / "default_sample.pdf") + input_source = PathInput(V2_PRODUCT_PATH / "split" / "default_sample.pdf") params = SplitParameters(model_id=split_model_id, webhook_ids=webhook_ids) response = v2_client.enqueue_and_get_result(SplitResponse, input_source, params) From 48c23351377c1d67ffa86e3d87bb743912d8fd35 Mon Sep 17 00:00:00 2001 From: Mehdi Ben Hamida Date: Thu, 24 Sep 2026 10:59:40 +0200 Subject: [PATCH 6/9] :test_tube: update import path for file types in integration tests --- tests/v2/test_client_integration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/v2/test_client_integration.py b/tests/v2/test_client_integration.py index 462cd702..09b6a298 100644 --- a/tests/v2/test_client_integration.py +++ b/tests/v2/test_client_integration.py @@ -16,7 +16,7 @@ from mindee.v2.product.extraction.extraction_response import ExtractionResponse from mindee.v2.product.split.params.split_parameters import SplitParameters from mindee.v2.product.split.split_response import SplitResponse -from tests.utils import FILE_TYPES_DIR, V2_PRODUCT_PATH +from tests.utils import FILE_TYPES_PATH, V2_PRODUCT_PATH @pytest.fixture(scope="session") From fe2a69cb49d962043a25a74846b7dd50655c4ee9 Mon Sep 17 00:00:00 2001 From: Mehdi Ben Hamida Date: Thu, 24 Sep 2026 14:30:53 +0200 Subject: [PATCH 7/9] :test_tube: update webhook IDs in integration tests for accuracy --- tests/v2/test_client_integration.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/v2/test_client_integration.py b/tests/v2/test_client_integration.py index 09b6a298..23ed7033 100644 --- a/tests/v2/test_client_integration.py +++ b/tests/v2/test_client_integration.py @@ -190,8 +190,8 @@ def test_extraction_with_two_webhooks_must_complete_and_succeed( v2_client: Client, findoc_model_id: str ) -> None: webhook_ids = [ - "a2286ed9-aa11-aa11-bdc5-2f8496c5641a", - "b2286ed9-aa11-aa11-bdc5-2f8496c5641a", + "9a0d88be-6913-484d-a019-9d2e16e2d3b9", + "32286ed9-fe40-4f42-bdc5-2f8496c5641a", ] input_source = PathInput( @@ -213,8 +213,8 @@ def test_split_with_two_webhooks_must_complete_and_succeed( v2_client: Client, split_model_id: str ) -> None: webhook_ids = [ - "a2286ed9-aa11-aa11-bdc5-2f8496c5641a", - "b2286ed9-aa11-aa11-bdc5-2f8496c5641a", + "b8fdfea3-24b6-438a-a6ca-7cd8c87a8875", + "d5bf36a9-1301-42c7-95be-03dc20d8f10e", ] input_source = PathInput(V2_PRODUCT_PATH / "split" / "default_sample.pdf") From ec7f0a37186461b14776e706b2e05491f013bd9d Mon Sep 17 00:00:00 2001 From: Mehdi Ben Hamida Date: Thu, 24 Sep 2026 14:55:20 +0200 Subject: [PATCH 8/9] :test_tube: refactor webhook job handling in integration tests for improved clarity and reliability --- tests/v2/test_client_integration.py | 47 ++++++++++++++++++----------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/tests/v2/test_client_integration.py b/tests/v2/test_client_integration.py index 23ed7033..15004da0 100644 --- a/tests/v2/test_client_integration.py +++ b/tests/v2/test_client_integration.py @@ -1,6 +1,7 @@ import os from datetime import datetime from pathlib import Path +from time import sleep import httpx import pytest @@ -13,6 +14,7 @@ MindeeHTTPErrorV2, ) from mindee.v2.parsing import InferenceActiveOptions +from mindee.v2.parsing.job.job import Job from mindee.v2.product.extraction.extraction_response import ExtractionResponse from mindee.v2.product.split.params.split_parameters import SplitParameters from mindee.v2.product.split.split_response import SplitResponse @@ -169,19 +171,24 @@ def test_parse_file_filled_single_page_must_succeed( assert len(supplier_name.locations) == 0 -def _assert_webhook_job_success(response, webhook_ids: list[str]) -> None: - assert response.inference is not None - assert response.inference.result is not None +def _enqueue_and_poll_job(v2_client: Client, input_source, params) -> Job: + """Enqueue a document and poll until the job reaches a final status.""" + job = v2_client.enqueue(input_source, params).job + for _ in range(60): + sleep(2) + job = v2_client.get_job(job.id).job + if job.status in {"Processed", "Failed"}: + break + return job - assert response.job is not None - assert response.job.status == "Processed" - assert isinstance(response.job.completed_at, datetime) - assert response.job.error is None - assert len(response.job.webhooks) == 2 - assert all( - webhook.status in {"Completed", "Failed"} for webhook in response.job.webhooks - ) - assert {webhook.id for webhook in response.job.webhooks} == set(webhook_ids) + +def _assert_webhook_job_success(job: Job, webhook_ids: list) -> None: + assert job.status == "Processed" + assert isinstance(job.completed_at, datetime) + assert job.error is None + assert len(job.webhooks) == len(webhook_ids) + assert all(webhook.status in {"Completed", "Failed"} for webhook in job.webhooks) + assert {webhook.id for webhook in job.webhooks} == set(webhook_ids) @pytest.mark.integration @@ -199,11 +206,12 @@ def test_extraction_with_two_webhooks_must_complete_and_succeed( ) params = ExtractionParameters(model_id=findoc_model_id, webhook_ids=webhook_ids) - response = v2_client.enqueue_and_get_result( - ExtractionResponse, input_source, params - ) + job = _enqueue_and_poll_job(v2_client, input_source, params) + _assert_webhook_job_success(job, webhook_ids) - _assert_webhook_job_success(response, webhook_ids) + response = v2_client.get_result_from_url(ExtractionResponse, job.result_url) + assert response.inference is not None + assert response.inference.result is not None assert response.inference.result.fields["supplier_name"].value == "John Smith" @@ -220,9 +228,12 @@ def test_split_with_two_webhooks_must_complete_and_succeed( input_source = PathInput(V2_PRODUCT_PATH / "split" / "default_sample.pdf") params = SplitParameters(model_id=split_model_id, webhook_ids=webhook_ids) - response = v2_client.enqueue_and_get_result(SplitResponse, input_source, params) + job = _enqueue_and_poll_job(v2_client, input_source, params) + _assert_webhook_job_success(job, webhook_ids) - _assert_webhook_job_success(response, webhook_ids) + response = v2_client.get_result_from_url(SplitResponse, job.result_url) + assert response.inference is not None + assert response.inference.result is not None assert len(response.inference.result.splits) == 2 From 2c3ca3eb4e0a0b87b3c40e3c4f65c4434984e1da Mon Sep 17 00:00:00 2001 From: Mehdi Ben Hamida Date: Thu, 24 Sep 2026 15:06:56 +0200 Subject: [PATCH 9/9] :test_tube: enhance job finality check in integration tests for webhook status consistency --- tests/v2/test_client_integration.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/v2/test_client_integration.py b/tests/v2/test_client_integration.py index 15004da0..15694340 100644 --- a/tests/v2/test_client_integration.py +++ b/tests/v2/test_client_integration.py @@ -171,13 +171,23 @@ def test_parse_file_filled_single_page_must_succeed( assert len(supplier_name.locations) == 0 +def _job_is_final(job: Job) -> bool: + if job.status == "Failed": + return True + if job.status != "Processed": + return False + # Webhook deliveries happen asynchronously after processing: + # wait until every webhook has reached a final status as well. + return all(webhook.status in {"Completed", "Failed"} for webhook in job.webhooks) + + def _enqueue_and_poll_job(v2_client: Client, input_source, params) -> Job: - """Enqueue a document and poll until the job reaches a final status.""" + """Enqueue a document and poll until the job and its webhooks reach a final status.""" job = v2_client.enqueue(input_source, params).job for _ in range(60): sleep(2) job = v2_client.get_job(job.id).job - if job.status in {"Processed", "Failed"}: + if _job_is_final(job): break return job