From f2232ecf3839609b2be6d30d1427dec82360e501 Mon Sep 17 00:00:00 2001 From: Pierre Raybaut <1311787+PierreRaybaut@users.noreply.github.com> Date: Sun, 25 Oct 2026 08:14:05 +0100 Subject: [PATCH] test(provenance): follow the contract versions of Sigima 1.4 Sigima 1.4 increases to version 2 the contracts of the operations whose result changed for some inputs (DataLab-Platform/Sigima#87), among which sigima.signal.difference, average and signals_to_image. Activities recorded with version 1 are now refused explicitly (unsupported_contract) instead of being replayed. The provenance tests no longer hard-code version 1: activities recorded in the session are expected at the local contract version and replayed exactly; the reference files of both editions, recorded with version 1, are expected to be refused explicitly, without computation, and a call recorded with version 1 is described but not applied again. Assisted-by: Claude Opus 5.5 --- .../common/provenance_capture_unit_test.py | 20 ++++++++++-- .../common/provenance_difference_unit_test.py | 32 ++++++++++++++----- .../common/provenance_processing_unit_test.py | 21 ++++++------ 3 files changed, 52 insertions(+), 21 deletions(-) diff --git a/datalab/tests/features/common/provenance_capture_unit_test.py b/datalab/tests/features/common/provenance_capture_unit_test.py index f6ce9b1d8..bf846753e 100644 --- a/datalab/tests/features/common/provenance_capture_unit_test.py +++ b/datalab/tests/features/common/provenance_capture_unit_test.py @@ -18,6 +18,7 @@ import sigima.proc.image as sipi import sigima.proc.signal as sips from sigima.objects import create_image, create_signal +from sigima.proc import contracts from sigima.tests.data import create_peak_image from datalab.env import execenv @@ -542,6 +543,11 @@ def test_replaced_analysis_result_is_not_attributed() -> None: # from Pyodide (Playwright, ``DLW_WRITE_PROVENANCE_FIXTURE=1``). HDF5_DIR = osp.join(osp.dirname(osp.dirname(__file__)), "hdf5") +#: Contract versions of the local Sigima, by operation identifier +LOCAL_VERSIONS = { + contract.operation_id: contract.contract_version + for contract in contracts.build_contract_registry().contracts +} CAPTURE_SHAPE = [ ("image", None, "geometry"), ("image", "image", None), @@ -709,6 +715,13 @@ def check_families_reference(win, edition: str) -> list[dict]: reports = [] for activity in activities: report = win.verify_provenance_activity(activity["activity_id"]) + operation = activity["call"]["operation"] + if operation["contract_version"] != LOCAL_VERSIONS[operation["id"]]: + # Recorded with another version of the contract (e.g. before Sigima + # 1.4): refused explicitly, never compared + assert report["eligibility"] == "unsupported_contract", report + reports.append(report) + continue accepted = ("exact",) if edition == "desktop" else ("exact", "within_tolerance") assert report["verdict"] in accepted, report reports.append(report) @@ -724,12 +737,15 @@ def test_families_reference_round_trip() -> None: win.save_h5_workspace(path) with app() as win: win.load_h5_workspace([path], reset_all=True) - check_families_reference(win, "desktop") + reports = check_families_reference(win, "desktop") + # Recorded with the local contracts: every activity is replayed + assert all(report["eligibility"] == "ready" for report in reports) @pytest.mark.parametrize("edition", ["desktop", "web"]) def test_families_reference_files(edition: str) -> None: - """Families files of both editions are replayed here under the declared rule.""" + """Families files of both editions are replayed here under the declared rule, + except the activities recorded with an earlier contract version (refused).""" path = osp.join(HDF5_DIR, f"provenance_{edition}_families.h5") with app() as win: win.load_h5_workspace([path], reset_all=True) diff --git a/datalab/tests/features/common/provenance_difference_unit_test.py b/datalab/tests/features/common/provenance_difference_unit_test.py index fd3ac9700..7afd3e045 100644 --- a/datalab/tests/features/common/provenance_difference_unit_test.py +++ b/datalab/tests/features/common/provenance_difference_unit_test.py @@ -12,6 +12,7 @@ import pytest import sigima.proc.signal as sips from sigima.objects import SignalObj, create_signal +from sigima.proc import contracts from datalab.env import execenv from datalab.objectmodel import get_uuid @@ -25,6 +26,8 @@ ) HDF5_DIR = osp.join(osp.dirname(osp.dirname(__file__)), "hdf5") +#: Version of the difference contract of the local Sigima +DIFFERENCE_VERSION = contracts.contract_for_function(sips.difference).contract_version def add(win, x, y, title: str, units=("s", "V")) -> SignalObj: @@ -89,8 +92,14 @@ def build_differences(win) -> tuple[SignalObj, ...]: return a, b, a_b, b_a -def check_differences(win, edition: str = "desktop") -> None: - """Check the two recorded differences of a workspace and replay them.""" +def check_differences( + win, edition: str = "desktop", contract_version: int = DIFFERENCE_VERSION +) -> None: + """Check the two recorded differences of a workspace and replay them. + + Activities recorded with another version of the difference contract (files + written before Sigima 1.4) are refused explicitly, without computation. + """ ledger = win.provenance.ledger ledger.validate() act_ab, act_ba = ledger.activities @@ -99,7 +108,7 @@ def check_differences(win, edition: str = "desktop") -> None: assert activity["edition"] == edition assert activity["call"]["operation"] == { "id": "sigima.signal.difference", - "contract_version": 1, + "contract_version": contract_version, } assert activity["call"]["parameters"] == {} assert activity["context"]["x_alignment"] == RULE @@ -111,11 +120,17 @@ def check_differences(win, edition: str = "desktop") -> None: result = win.find_object_by_uuid(state["object_uuid"]) assert np.array_equal(result.y, expected) computations = win.signalpanel.processor.execution.computations + replayable = contract_version == DIFFERENCE_VERSION for activity in (act_ab, act_ba): report = win.verify_provenance_activity(activity["activity_id"]) - assert report["verdict"] == "exact", report - assert report["context"]["x_alignment"] == RULE - assert win.signalpanel.processor.execution.computations == computations + 2 + if replayable: + assert report["verdict"] == "exact", report + assert report["context"]["x_alignment"] == RULE + else: + assert report["eligibility"] == "unsupported_contract", report + assert report["verdict"] == "not_verified", report + expected = computations + (2 if replayable else 0) + assert win.signalpanel.processor.execution.computations == expected def test_capture_and_verification() -> None: @@ -148,11 +163,12 @@ def test_desktop_round_trip() -> None: @pytest.mark.parametrize("edition", ["desktop", "web"]) def test_reference_files(edition: str) -> None: - """Reference files of both editions reopen and replay exactly on Desktop.""" + """Reference files of both editions reopen on Desktop; recorded with version 1 + of the difference contract (before Sigima 1.4), they are refused explicitly.""" path = osp.join(HDF5_DIR, f"provenance_{edition}_difference.h5") with app() as win: win.load_h5_workspace([path], reset_all=True) - check_differences(win, edition) + check_differences(win, edition, contract_version=1) def test_swapped_roles_give_a_different_result() -> None: diff --git a/datalab/tests/features/common/provenance_processing_unit_test.py b/datalab/tests/features/common/provenance_processing_unit_test.py index 74c93d26a..5e8431680 100644 --- a/datalab/tests/features/common/provenance_processing_unit_test.py +++ b/datalab/tests/features/common/provenance_processing_unit_test.py @@ -456,7 +456,8 @@ def test_one_output_of_a_grouped_command(method: str) -> None: def test_web_files_are_described_and_applied_again() -> None: - """Reference files written by DataLab-Web: consulted, restored, reapplied.""" + """Reference files written by DataLab-Web: consulted, restored, reapplied when + their contract version is still supported.""" hdf5 = osp.join(osp.dirname(osp.dirname(__file__)), "hdf5") with app() as win: win.load_h5_workspace( @@ -497,17 +498,15 @@ def test_web_files_are_described_and_applied_again() -> None: [osp.join(hdf5, "provenance_web_difference.h5")], reset_all=True ) act_ab, _act_ba = win.provenance.ledger.activities + # Recorded with version 1 of the difference contract (before Sigima 1.4): + # described, but not applied again with the current contract + assert act_ab["call"]["operation"]["contract_version"] == 1 a_b = win.find_object_by_uuid(output_uuid(win, act_ab)) call = resolve_processing_call(win, a_b) - assert call.pattern == "2-to-1" and call.can_reapply + assert call.pattern == "2-to-1" and not call.can_reapply + assert "sigima.signal.difference" in call.reason assert [(i.role, i.object_uuid) for i in call.inputs] == inputs_of(win, act_ab) - a_b.y[:] = 0.0 + count = len(win.provenance.ledger.activities) report = win.signalpanel.processor.recompute_processing(a_b, interactive=False) - assert report.success, report.message - assert np.array_equal(a_b.y, A_MINUS_B) - activity = win.provenance.ledger.activities[-1] - assert activity["context"]["x_alignment"] == RULE - assert ( - win.verify_provenance_activity(activity["activity_id"])["verdict"] - == "exact" - ) + assert not report.success + assert len(win.provenance.ledger.activities) == count