Spaces:
Running
test(audit): éliminer tous les pytest.raises(Exception) résiduels
Browse filesRatchet ``BROAD_RAISES_BASELINE`` Phase 8 : **24 → 0**.
Les 24 sites résiduels acceptés par la baseline temporaire ont
tous été remplacés par leur classe d'exception précise :
**13 sites Pydantic ``ValidationError``** (frozen ConfigDict +
validateurs métier) :
- ``tests/pipeline/test_sprint_a14_s6_spec.py:116`` — PipelineSpec.frozen
- ``tests/domain/test_sprint_a14_s4_artifacts.py:118,146,150``
- ``tests/domain/test_sprint_a14_s4_corpus.py:48,55``
- ``tests/domain/test_sprint_a14_s4_documents.py:85``
- ``tests/domain/test_sprint_a14_s4_provenance_errors.py:43``
- ``tests/domain/test_sprint_a14_s5_evaluation_specs.py:53,66,115,207,259``
- ``tests/app/schemas/test_sprint_a14_s39_run_spec_extended.py`` :
7 sites ``pytest.raises(Exception, match=...)`` — les
``ValueError`` levés dans les validateurs Pydantic sont
re-emballés en ``ValidationError``.
**2 sites ``FrozenInstanceError``** (dataclass frozen) :
- ``tests/adapters/storage/test_sprint_a14_s29_artifact_store.py:108,551``
— ArtifactKey et ArtifactRecord (dataclass frozen=True).
**2 sites ``PicaronesError``** dans pipeline :
- ``tests/pipeline/test_sprint_a14_s28_planner.py:560`` —
``PipelineExecutor.run_plan`` rejette un argument qui n'est pas
un ``ExecutionPlan``.
- ``tests/pipeline/test_sprint_a14_s28_planner.py:626`` —
``PipelineExecutor.__init__`` rejette un ``planner`` qui n'est
pas un ``PipelinePlanner``.
**Test ratchet abaissé à 0**
``BROAD_RAISES_BASELINE = 24 → 0``. Tout nouveau
``pytest.raises(Exception)`` introduit dans une PR future fait
échouer ``test_broad_pytest_raises_below_baseline`` —
**verrouillage strict** désormais en place.
**Bilan**
Suite : 4 789 passed, 16 skipped, 8 deselected, 2 xfailed. Ruff
propre. Chaque test attrape désormais exactement la classe
d'exception attendue — les régressions où une exception
différente serait levée font échouer le test.
- tests/adapters/storage/test_sprint_a14_s29_artifact_store.py +3 -2
- tests/app/schemas/test_sprint_a14_s39_run_spec_extended.py +8 -7
- tests/architecture/test_no_broad_pytest_raises.py +1 -1
- tests/domain/test_sprint_a14_s4_artifacts.py +4 -3
- tests/domain/test_sprint_a14_s4_corpus.py +3 -2
- tests/domain/test_sprint_a14_s4_documents.py +3 -1
- tests/domain/test_sprint_a14_s4_provenance_errors.py +3 -1
- tests/domain/test_sprint_a14_s5_evaluation_specs.py +6 -5
- tests/pipeline/test_sprint_a14_s28_planner.py +6 -2
- tests/pipeline/test_sprint_a14_s6_spec.py +3 -1
|
@@ -35,6 +35,7 @@ from __future__ import annotations
|
|
| 35 |
|
| 36 |
import json
|
| 37 |
import threading
|
|
|
|
| 38 |
from pathlib import Path
|
| 39 |
|
| 40 |
import pytest
|
|
@@ -105,7 +106,7 @@ class TestArtifactKeyDataclass:
|
|
| 105 |
|
| 106 |
def test_frozen(self) -> None:
|
| 107 |
k = _basic_key()
|
| 108 |
-
with pytest.raises(
|
| 109 |
k.adapter_name = "different" # type: ignore[misc]
|
| 110 |
|
| 111 |
|
|
@@ -548,5 +549,5 @@ class TestStoredArtifactDataclass:
|
|
| 548 |
sa = StoredArtifact(
|
| 549 |
key="k", artifact=_make_artifact(), payload=b"x",
|
| 550 |
)
|
| 551 |
-
with pytest.raises(
|
| 552 |
sa.payload = b"y" # type: ignore[misc]
|
|
|
|
| 35 |
|
| 36 |
import json
|
| 37 |
import threading
|
| 38 |
+
from dataclasses import FrozenInstanceError
|
| 39 |
from pathlib import Path
|
| 40 |
|
| 41 |
import pytest
|
|
|
|
| 106 |
|
| 107 |
def test_frozen(self) -> None:
|
| 108 |
k = _basic_key()
|
| 109 |
+
with pytest.raises(FrozenInstanceError):
|
| 110 |
k.adapter_name = "different" # type: ignore[misc]
|
| 111 |
|
| 112 |
|
|
|
|
| 549 |
sa = StoredArtifact(
|
| 550 |
key="k", artifact=_make_artifact(), payload=b"x",
|
| 551 |
)
|
| 552 |
+
with pytest.raises(FrozenInstanceError):
|
| 553 |
sa.payload = b"y" # type: ignore[misc]
|
|
@@ -14,6 +14,7 @@ purement additive.
|
|
| 14 |
from __future__ import annotations
|
| 15 |
|
| 16 |
import pytest
|
|
|
|
| 17 |
|
| 18 |
from picarones.app.schemas.run_spec import (
|
| 19 |
PipelineSpecYaml,
|
|
@@ -94,7 +95,7 @@ class TestPreferredTextOutput:
|
|
| 94 |
assert pipe.preferred_text_output == "corrector.corrected_text"
|
| 95 |
|
| 96 |
def test_rejects_missing_dot(self) -> None:
|
| 97 |
-
with pytest.raises(
|
| 98 |
PipelineSpecYaml(
|
| 99 |
name="bad",
|
| 100 |
initial_inputs=(ArtifactType.IMAGE,),
|
|
@@ -108,7 +109,7 @@ class TestPreferredTextOutput:
|
|
| 108 |
)
|
| 109 |
|
| 110 |
def test_rejects_unknown_step(self) -> None:
|
| 111 |
-
with pytest.raises(
|
| 112 |
PipelineSpecYaml(
|
| 113 |
name="bad",
|
| 114 |
initial_inputs=(ArtifactType.IMAGE,),
|
|
@@ -122,7 +123,7 @@ class TestPreferredTextOutput:
|
|
| 122 |
)
|
| 123 |
|
| 124 |
def test_rejects_step_not_producing_type(self) -> None:
|
| 125 |
-
with pytest.raises(
|
| 126 |
PipelineSpecYaml(
|
| 127 |
name="bad",
|
| 128 |
initial_inputs=(ArtifactType.IMAGE,),
|
|
@@ -137,7 +138,7 @@ class TestPreferredTextOutput:
|
|
| 137 |
)
|
| 138 |
|
| 139 |
def test_rejects_unknown_artifact_type(self) -> None:
|
| 140 |
-
with pytest.raises(
|
| 141 |
PipelineSpecYaml(
|
| 142 |
name="bad",
|
| 143 |
initial_inputs=(ArtifactType.IMAGE,),
|
|
@@ -173,7 +174,7 @@ class TestInputsFromValidation:
|
|
| 173 |
|
| 174 |
def test_initial_step_id_rejects_unknown_initial_input(self) -> None:
|
| 175 |
# `__initial__` mais le type n'est pas dans initial_inputs → erreur.
|
| 176 |
-
with pytest.raises(
|
| 177 |
PipelineSpecYaml(
|
| 178 |
name="bad",
|
| 179 |
initial_inputs=(ArtifactType.IMAGE,),
|
|
@@ -218,7 +219,7 @@ class TestInputsFromValidation:
|
|
| 218 |
|
| 219 |
def test_rejects_forward_reference(self) -> None:
|
| 220 |
# Un step ne peut pas référencer un step en aval de lui.
|
| 221 |
-
with pytest.raises(
|
| 222 |
PipelineSpecYaml(
|
| 223 |
name="bad",
|
| 224 |
initial_inputs=(ArtifactType.IMAGE,),
|
|
@@ -241,7 +242,7 @@ class TestInputsFromValidation:
|
|
| 241 |
)
|
| 242 |
|
| 243 |
def test_rejects_step_not_producing_referenced_type(self) -> None:
|
| 244 |
-
with pytest.raises(
|
| 245 |
PipelineSpecYaml(
|
| 246 |
name="bad",
|
| 247 |
initial_inputs=(ArtifactType.IMAGE,),
|
|
|
|
| 14 |
from __future__ import annotations
|
| 15 |
|
| 16 |
import pytest
|
| 17 |
+
from pydantic import ValidationError
|
| 18 |
|
| 19 |
from picarones.app.schemas.run_spec import (
|
| 20 |
PipelineSpecYaml,
|
|
|
|
| 95 |
assert pipe.preferred_text_output == "corrector.corrected_text"
|
| 96 |
|
| 97 |
def test_rejects_missing_dot(self) -> None:
|
| 98 |
+
with pytest.raises(ValidationError, match="format"):
|
| 99 |
PipelineSpecYaml(
|
| 100 |
name="bad",
|
| 101 |
initial_inputs=(ArtifactType.IMAGE,),
|
|
|
|
| 109 |
)
|
| 110 |
|
| 111 |
def test_rejects_unknown_step(self) -> None:
|
| 112 |
+
with pytest.raises(ValidationError, match="introuvable"):
|
| 113 |
PipelineSpecYaml(
|
| 114 |
name="bad",
|
| 115 |
initial_inputs=(ArtifactType.IMAGE,),
|
|
|
|
| 123 |
)
|
| 124 |
|
| 125 |
def test_rejects_step_not_producing_type(self) -> None:
|
| 126 |
+
with pytest.raises(ValidationError, match="ne produit pas"):
|
| 127 |
PipelineSpecYaml(
|
| 128 |
name="bad",
|
| 129 |
initial_inputs=(ArtifactType.IMAGE,),
|
|
|
|
| 138 |
)
|
| 139 |
|
| 140 |
def test_rejects_unknown_artifact_type(self) -> None:
|
| 141 |
+
with pytest.raises(ValidationError, match="output_type"):
|
| 142 |
PipelineSpecYaml(
|
| 143 |
name="bad",
|
| 144 |
initial_inputs=(ArtifactType.IMAGE,),
|
|
|
|
| 174 |
|
| 175 |
def test_initial_step_id_rejects_unknown_initial_input(self) -> None:
|
| 176 |
# `__initial__` mais le type n'est pas dans initial_inputs → erreur.
|
| 177 |
+
with pytest.raises(ValidationError, match="initial_inputs"):
|
| 178 |
PipelineSpecYaml(
|
| 179 |
name="bad",
|
| 180 |
initial_inputs=(ArtifactType.IMAGE,),
|
|
|
|
| 219 |
|
| 220 |
def test_rejects_forward_reference(self) -> None:
|
| 221 |
# Un step ne peut pas référencer un step en aval de lui.
|
| 222 |
+
with pytest.raises(ValidationError, match="antérieure"):
|
| 223 |
PipelineSpecYaml(
|
| 224 |
name="bad",
|
| 225 |
initial_inputs=(ArtifactType.IMAGE,),
|
|
|
|
| 242 |
)
|
| 243 |
|
| 244 |
def test_rejects_step_not_producing_referenced_type(self) -> None:
|
| 245 |
+
with pytest.raises(ValidationError, match="ne produit pas"):
|
| 246 |
PipelineSpecYaml(
|
| 247 |
name="bad",
|
| 248 |
initial_inputs=(ArtifactType.IMAGE,),
|
|
@@ -72,7 +72,7 @@ def _scan_broad_raises() -> list[tuple[Path, int]]:
|
|
| 72 |
#:
|
| 73 |
#: 1. Remplacer ``pytest.raises(Exception)`` par la classe précise.
|
| 74 |
#: 2. Baisser :data:`BROAD_RAISES_BASELINE` du même montant.
|
| 75 |
-
BROAD_RAISES_BASELINE =
|
| 76 |
|
| 77 |
|
| 78 |
def test_broad_pytest_raises_below_baseline() -> None:
|
|
|
|
| 72 |
#:
|
| 73 |
#: 1. Remplacer ``pytest.raises(Exception)`` par la classe précise.
|
| 74 |
#: 2. Baisser :data:`BROAD_RAISES_BASELINE` du même montant.
|
| 75 |
+
BROAD_RAISES_BASELINE = 0
|
| 76 |
|
| 77 |
|
| 78 |
def test_broad_pytest_raises_below_baseline() -> None:
|
|
@@ -13,6 +13,7 @@ from __future__ import annotations
|
|
| 13 |
import hashlib
|
| 14 |
|
| 15 |
import pytest
|
|
|
|
| 16 |
|
| 17 |
from picarones.domain import (
|
| 18 |
Artifact,
|
|
@@ -115,7 +116,7 @@ class TestArtifactCreation:
|
|
| 115 |
|
| 116 |
def test_content_hash_must_be_64_hex(self) -> None:
|
| 117 |
# Trop court
|
| 118 |
-
with pytest.raises(
|
| 119 |
Artifact(
|
| 120 |
id="x", document_id="d1", type=ArtifactType.RAW_TEXT,
|
| 121 |
content_hash="abc",
|
|
@@ -143,11 +144,11 @@ class TestArtifactCreation:
|
|
| 143 |
class TestArtifactImmutability:
|
| 144 |
def test_frozen_blocks_attribute_mutation(self) -> None:
|
| 145 |
a = Artifact(id="x", document_id="d1", type=ArtifactType.RAW_TEXT)
|
| 146 |
-
with pytest.raises(
|
| 147 |
a.id = "y" # type: ignore[misc]
|
| 148 |
|
| 149 |
def test_extra_fields_rejected(self) -> None:
|
| 150 |
-
with pytest.raises(
|
| 151 |
Artifact( # type: ignore[call-arg]
|
| 152 |
id="x", document_id="d1", type=ArtifactType.RAW_TEXT,
|
| 153 |
bogus_field="oops",
|
|
|
|
| 13 |
import hashlib
|
| 14 |
|
| 15 |
import pytest
|
| 16 |
+
from pydantic import ValidationError
|
| 17 |
|
| 18 |
from picarones.domain import (
|
| 19 |
Artifact,
|
|
|
|
| 116 |
|
| 117 |
def test_content_hash_must_be_64_hex(self) -> None:
|
| 118 |
# Trop court
|
| 119 |
+
with pytest.raises(ValidationError):
|
| 120 |
Artifact(
|
| 121 |
id="x", document_id="d1", type=ArtifactType.RAW_TEXT,
|
| 122 |
content_hash="abc",
|
|
|
|
| 144 |
class TestArtifactImmutability:
|
| 145 |
def test_frozen_blocks_attribute_mutation(self) -> None:
|
| 146 |
a = Artifact(id="x", document_id="d1", type=ArtifactType.RAW_TEXT)
|
| 147 |
+
with pytest.raises(ValidationError):
|
| 148 |
a.id = "y" # type: ignore[misc]
|
| 149 |
|
| 150 |
def test_extra_fields_rejected(self) -> None:
|
| 151 |
+
with pytest.raises(ValidationError):
|
| 152 |
Artifact( # type: ignore[call-arg]
|
| 153 |
id="x", document_id="d1", type=ArtifactType.RAW_TEXT,
|
| 154 |
bogus_field="oops",
|
|
@@ -3,6 +3,7 @@
|
|
| 3 |
from __future__ import annotations
|
| 4 |
|
| 5 |
import pytest
|
|
|
|
| 6 |
|
| 7 |
from picarones.domain import ArtifactType, CorpusSpec, CorpusSpecError, DocumentRef, GroundTruthRef
|
| 8 |
|
|
@@ -45,14 +46,14 @@ class TestCorpusSpec:
|
|
| 45 |
assert c.metadata["language"] == "fr"
|
| 46 |
|
| 47 |
def test_name_validation(self) -> None:
|
| 48 |
-
with pytest.raises(
|
| 49 |
CorpusSpec(name="") # min_length=1
|
| 50 |
|
| 51 |
|
| 52 |
class TestCorpusSpecImmutability:
|
| 53 |
def test_frozen_blocks_mutation(self) -> None:
|
| 54 |
c = CorpusSpec(name="x")
|
| 55 |
-
with pytest.raises(
|
| 56 |
c.name = "y" # type: ignore[misc]
|
| 57 |
|
| 58 |
def test_json_roundtrip_with_multilevel_gt(self) -> None:
|
|
|
|
| 3 |
from __future__ import annotations
|
| 4 |
|
| 5 |
import pytest
|
| 6 |
+
from pydantic import ValidationError
|
| 7 |
|
| 8 |
from picarones.domain import ArtifactType, CorpusSpec, CorpusSpecError, DocumentRef, GroundTruthRef
|
| 9 |
|
|
|
|
| 46 |
assert c.metadata["language"] == "fr"
|
| 47 |
|
| 48 |
def test_name_validation(self) -> None:
|
| 49 |
+
with pytest.raises(ValidationError):
|
| 50 |
CorpusSpec(name="") # min_length=1
|
| 51 |
|
| 52 |
|
| 53 |
class TestCorpusSpecImmutability:
|
| 54 |
def test_frozen_blocks_mutation(self) -> None:
|
| 55 |
c = CorpusSpec(name="x")
|
| 56 |
+
with pytest.raises(ValidationError):
|
| 57 |
c.name = "y" # type: ignore[misc]
|
| 58 |
|
| 59 |
def test_json_roundtrip_with_multilevel_gt(self) -> None:
|
|
@@ -81,8 +81,10 @@ class TestMultiLevelGT:
|
|
| 81 |
|
| 82 |
class TestDocumentRefImmutability:
|
| 83 |
def test_frozen_blocks_mutation(self) -> None:
|
|
|
|
|
|
|
| 84 |
d = DocumentRef(id="x")
|
| 85 |
-
with pytest.raises(
|
| 86 |
d.id = "y" # type: ignore[misc]
|
| 87 |
|
| 88 |
def test_json_roundtrip(self) -> None:
|
|
|
|
| 81 |
|
| 82 |
class TestDocumentRefImmutability:
|
| 83 |
def test_frozen_blocks_mutation(self) -> None:
|
| 84 |
+
from pydantic import ValidationError
|
| 85 |
+
|
| 86 |
d = DocumentRef(id="x")
|
| 87 |
+
with pytest.raises(ValidationError):
|
| 88 |
d.id = "y" # type: ignore[misc]
|
| 89 |
|
| 90 |
def test_json_roundtrip(self) -> None:
|
|
@@ -39,8 +39,10 @@ class TestProvenanceRecord:
|
|
| 39 |
assert not p1.is_compatible_with(p4) # parameters_hash diffère
|
| 40 |
|
| 41 |
def test_frozen(self) -> None:
|
|
|
|
|
|
|
| 42 |
p = ProvenanceRecord(code_version="1.0.0")
|
| 43 |
-
with pytest.raises(
|
| 44 |
p.code_version = "1.0.1" # type: ignore[misc]
|
| 45 |
|
| 46 |
def test_json_roundtrip(self) -> None:
|
|
|
|
| 39 |
assert not p1.is_compatible_with(p4) # parameters_hash diffère
|
| 40 |
|
| 41 |
def test_frozen(self) -> None:
|
| 42 |
+
from pydantic import ValidationError
|
| 43 |
+
|
| 44 |
p = ProvenanceRecord(code_version="1.0.0")
|
| 45 |
+
with pytest.raises(ValidationError):
|
| 46 |
p.code_version = "1.0.1" # type: ignore[misc]
|
| 47 |
|
| 48 |
def test_json_roundtrip(self) -> None:
|
|
@@ -8,6 +8,7 @@ des dataclasses pydantic.
|
|
| 8 |
from __future__ import annotations
|
| 9 |
|
| 10 |
import pytest
|
|
|
|
| 11 |
|
| 12 |
from picarones.domain import (
|
| 13 |
ArtifactType,
|
|
@@ -50,7 +51,7 @@ class TestMetricSpec:
|
|
| 50 |
name="cer",
|
| 51 |
input_types=(ArtifactType.RAW_TEXT, ArtifactType.RAW_TEXT),
|
| 52 |
)
|
| 53 |
-
with pytest.raises(
|
| 54 |
spec.name = "wer" # type: ignore[misc]
|
| 55 |
|
| 56 |
def test_no_callable_field(self) -> None:
|
|
@@ -63,7 +64,7 @@ class TestMetricSpec:
|
|
| 63 |
assert not hasattr(spec, "func")
|
| 64 |
|
| 65 |
def test_extra_field_rejected(self) -> None:
|
| 66 |
-
with pytest.raises(
|
| 67 |
MetricSpec( # type: ignore[call-arg]
|
| 68 |
name="cer",
|
| 69 |
input_types=(ArtifactType.RAW_TEXT, ArtifactType.RAW_TEXT),
|
|
@@ -112,7 +113,7 @@ class TestProjectionSpec:
|
|
| 112 |
target_type=ArtifactType.RAW_TEXT,
|
| 113 |
projector_name="alto_to_text",
|
| 114 |
)
|
| 115 |
-
with pytest.raises(
|
| 116 |
p.projector_name = "other" # type: ignore[misc]
|
| 117 |
|
| 118 |
def test_json_roundtrip(self) -> None:
|
|
@@ -204,7 +205,7 @@ class TestEvaluationView:
|
|
| 204 |
name="x",
|
| 205 |
candidate_types=frozenset({ArtifactType.RAW_TEXT}),
|
| 206 |
)
|
| 207 |
-
with pytest.raises(
|
| 208 |
view.name = "y" # type: ignore[misc]
|
| 209 |
|
| 210 |
def test_json_roundtrip(self) -> None:
|
|
@@ -256,5 +257,5 @@ class TestEvaluationSpec:
|
|
| 256 |
|
| 257 |
def test_frozen(self) -> None:
|
| 258 |
s = EvaluationSpec()
|
| 259 |
-
with pytest.raises(
|
| 260 |
s.views = () # type: ignore[misc]
|
|
|
|
| 8 |
from __future__ import annotations
|
| 9 |
|
| 10 |
import pytest
|
| 11 |
+
from pydantic import ValidationError
|
| 12 |
|
| 13 |
from picarones.domain import (
|
| 14 |
ArtifactType,
|
|
|
|
| 51 |
name="cer",
|
| 52 |
input_types=(ArtifactType.RAW_TEXT, ArtifactType.RAW_TEXT),
|
| 53 |
)
|
| 54 |
+
with pytest.raises(ValidationError):
|
| 55 |
spec.name = "wer" # type: ignore[misc]
|
| 56 |
|
| 57 |
def test_no_callable_field(self) -> None:
|
|
|
|
| 64 |
assert not hasattr(spec, "func")
|
| 65 |
|
| 66 |
def test_extra_field_rejected(self) -> None:
|
| 67 |
+
with pytest.raises(ValidationError):
|
| 68 |
MetricSpec( # type: ignore[call-arg]
|
| 69 |
name="cer",
|
| 70 |
input_types=(ArtifactType.RAW_TEXT, ArtifactType.RAW_TEXT),
|
|
|
|
| 113 |
target_type=ArtifactType.RAW_TEXT,
|
| 114 |
projector_name="alto_to_text",
|
| 115 |
)
|
| 116 |
+
with pytest.raises(ValidationError):
|
| 117 |
p.projector_name = "other" # type: ignore[misc]
|
| 118 |
|
| 119 |
def test_json_roundtrip(self) -> None:
|
|
|
|
| 205 |
name="x",
|
| 206 |
candidate_types=frozenset({ArtifactType.RAW_TEXT}),
|
| 207 |
)
|
| 208 |
+
with pytest.raises(ValidationError):
|
| 209 |
view.name = "y" # type: ignore[misc]
|
| 210 |
|
| 211 |
def test_json_roundtrip(self) -> None:
|
|
|
|
| 257 |
|
| 258 |
def test_frozen(self) -> None:
|
| 259 |
s = EvaluationSpec()
|
| 260 |
+
with pytest.raises(ValidationError):
|
| 261 |
s.views = () # type: ignore[misc]
|
|
@@ -557,7 +557,10 @@ class TestPipelineExecutorWithPlanner:
|
|
| 557 |
executor = PipelineExecutor(
|
| 558 |
adapter_resolver=lambda n: _OCRStub(),
|
| 559 |
)
|
| 560 |
-
|
|
|
|
|
|
|
|
|
|
| 561 |
executor.run_plan(
|
| 562 |
plan="not a plan", # type: ignore[arg-type]
|
| 563 |
document=DocumentRef(id="d1"),
|
|
@@ -623,7 +626,8 @@ class TestPipelineExecutorWithPlanner:
|
|
| 623 |
assert plan.metric_junctions # non vide grâce au registry injecté
|
| 624 |
|
| 625 |
def test_planner_must_be_pipeline_planner(self) -> None:
|
| 626 |
-
|
|
|
|
| 627 |
PipelineExecutor(
|
| 628 |
adapter_resolver=lambda n: _OCRStub(),
|
| 629 |
planner="not a planner", # type: ignore[arg-type]
|
|
|
|
| 557 |
executor = PipelineExecutor(
|
| 558 |
adapter_resolver=lambda n: _OCRStub(),
|
| 559 |
)
|
| 560 |
+
# ``PipelineExecutor.run_plan`` lève ``PicaronesError`` quand
|
| 561 |
+
# l'argument ``plan`` n'est pas un ``ExecutionPlan``.
|
| 562 |
+
from picarones.domain.errors import PicaronesError
|
| 563 |
+
with pytest.raises(PicaronesError, match="ExecutionPlan"):
|
| 564 |
executor.run_plan(
|
| 565 |
plan="not a plan", # type: ignore[arg-type]
|
| 566 |
document=DocumentRef(id="d1"),
|
|
|
|
| 626 |
assert plan.metric_junctions # non vide grâce au registry injecté
|
| 627 |
|
| 628 |
def test_planner_must_be_pipeline_planner(self) -> None:
|
| 629 |
+
from picarones.domain.errors import PicaronesError
|
| 630 |
+
with pytest.raises(PicaronesError, match="PipelinePlanner"):
|
| 631 |
PipelineExecutor(
|
| 632 |
adapter_resolver=lambda n: _OCRStub(),
|
| 633 |
planner="not a planner", # type: ignore[arg-type]
|
|
@@ -112,6 +112,8 @@ class TestPipelineSpec:
|
|
| 112 |
assert s.step_by_id("missing") is None
|
| 113 |
|
| 114 |
def test_frozen(self) -> None:
|
|
|
|
|
|
|
| 115 |
s = PipelineSpec(name="x")
|
| 116 |
-
with pytest.raises(
|
| 117 |
s.name = "y" # type: ignore[misc]
|
|
|
|
| 112 |
assert s.step_by_id("missing") is None
|
| 113 |
|
| 114 |
def test_frozen(self) -> None:
|
| 115 |
+
from pydantic import ValidationError
|
| 116 |
+
|
| 117 |
s = PipelineSpec(name="x")
|
| 118 |
+
with pytest.raises(ValidationError):
|
| 119 |
s.name = "y" # type: ignore[misc]
|