Procházet zdrojové kódy

test: cover cipy.action.Composite

Sam Jaffe před 3 týdny
rodič
revize
543b0063a8
2 změnil soubory, kde provedl 80 přidání a 13 odebrání
  1. 10 13
      src/cipy/action.py
  2. 70 0
      tests/action_test.py

+ 10 - 13
src/cipy/action.py

@@ -108,29 +108,28 @@ class Composite(Action):
 
     name: str = ""
     steps: list[Action] = Field(frozen=True)
-    _counter: int = PrivateAttr(default=0)
-    _results: Results = PrivateAttr(default_factory=Results)
+    _results: list[Results.Item] = PrivateAttr()
 
     def __init__(self, *steps: Action, **kwargs: Any) -> None:
         super().__init__(steps=steps, **kwargs)  # type: ignore
+        self._results = [Results.Item() for i in range(0, len(self.steps))]
 
     @final
     def run(self, context: Context) -> Status:
         status = Status.NOT_RUN
 
-        with context.extend(steps=self._results) as outctx:
-            for step in self.steps:
+        with context.extend(steps=Results()) as outctx:
+            for index, step in enumerate(self.steps):
                 context.fabricate(step, "inputs")
-                self._counter += 1
 
                 if step.is_enabled(status, context):
                     stat = step.run(context)
-                    outctx.steps[step.id] = Results.Item(stat, step.outputs.validated())
+                    self._results[index] = Results.Item(stat, step.outputs.validated())
                 else:
-                    stat = Status.SKIPPED
-                    outctx.steps[step.id] = Results.Item(stat)
+                    self._results[index] = Results.Item(Status.SKIPPED)
 
-                status |= stat
+                outctx.steps[step.id] = self._results[index]
+                status |= self._results[index].conclusion
 
             if status is Status.SUCCESS:
                 outctx.fabricate(self, "outputs")
@@ -139,8 +138,6 @@ class Composite(Action):
 
     @final
     def cleanup(self, context: Context) -> None:
-        for step in reversed(self.steps):
-            if step.id not in self._results:
-                continue
-            if self._results[step.id].conclusion == Status.SUCCESS:
+        for step, result in zip(reversed(self.steps), reversed(self._results)):
+            if result.conclusion is Status.SUCCESS:
                 step.cleanup(context)

+ 70 - 0
tests/action_test.py

@@ -1,6 +1,8 @@
 import pytest
 import unittest.mock
 
+from unittest.mock import ANY
+
 import cipy
 import cipy.action
 
@@ -52,3 +54,71 @@ def test_call_can_pass_args() -> None:
 
     action.run.assert_called_once()
     assert action.inputs.foo == 3
+
+
+def test_composite_runs_steps_in_order() -> None:
+    run = unittest.mock.Mock()
+
+    a1 = mock_action(name="A")
+    a2 = mock_action(name="B")
+
+    a1.run = run.a1
+    a1.run.return_value = cipy.Status.SUCCESS
+    a2.run = run.a2
+    a2.run.return_value = cipy.Status.SUCCESS
+
+    comp = cipy.Composite(a1, a2)
+    comp.run(cipy.Context())
+
+    run.assert_has_calls(
+        [unittest.mock.call.a1(ANY), unittest.mock.call.a2(ANY)], any_order=False
+    )
+
+
+def test_composite_checks_is_enabled_for_each() -> None:
+    a1 = mock_action(name="A")
+    a1.run.return_value = cipy.Status.FAILURE
+    a2 = mock_action(name="B")
+    a2.run.return_value = cipy.Status.SUCCESS
+
+    comp = cipy.Composite(a1, a2)
+    comp.run(cipy.Context())
+
+    a1.run.assert_called_once()
+    a2.is_enabled.assert_called_once()
+    a2.run.assert_not_called()
+
+
+def test_composite_cleanup_goes_backwards() -> None:
+    cleanup = unittest.mock.Mock()
+
+    a1 = mock_action(name="A")
+    a2 = mock_action(name="B")
+
+    a1.run.return_value = cipy.Status.SUCCESS
+    a2.run.return_value = cipy.Status.SUCCESS
+
+    a1.cleanup = cleanup.a1
+    a2.cleanup = cleanup.a2
+
+    comp = cipy.Composite(a1, a2)
+    comp.run(cipy.Context())
+    comp.cleanup(cipy.Context())
+
+    cleanup.assert_has_calls(
+        [unittest.mock.call.a2(ANY), unittest.mock.call.a1(ANY)], any_order=False
+    )
+
+
+def test_composite_cleanup_ignores_skipped() -> None:
+    a1 = mock_action(name="A")
+    a1.run.return_value = cipy.Status.SKIPPED
+    a2 = mock_action(name="B")
+    a2.run.return_value = cipy.Status.SUCCESS
+
+    comp = cipy.Composite(a1, a2)
+    comp.run(cipy.Context())
+    comp.cleanup(cipy.Context())
+
+    a2.cleanup.assert_called_once()
+    a1.cleanup.assert_not_called()