action_test.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. import pytest
  2. import unittest.mock
  3. from unittest.mock import ANY
  4. import cipy
  5. import cipy.action
  6. class NoOp(cipy.action.Action):
  7. def run(self, context: cipy.Context) -> cipy.Status:
  8. return cipy.Status.NOT_RUN
  9. def mock_action(*, name: str = "Mock", id: str = "") -> unittest.mock.MagicMock:
  10. rval = unittest.mock.MagicMock(
  11. wraps=NoOp(name=name, id=id), spec=cipy.action.Action
  12. )
  13. rval.name = name
  14. rval.id = id
  15. rval.inputs = cipy.Inputs()
  16. rval.outputs = cipy.Outputs()
  17. return rval
  18. def test_action() -> None:
  19. action = NoOp(name="Stub")
  20. assert action.run(cipy.Context()) is cipy.Status.NOT_RUN
  21. def test_call_proxies_functions() -> None:
  22. action = mock_action()
  23. call = cipy.Call(action)
  24. call.is_enabled(cipy.Status.NOT_RUN, cipy.Context())
  25. action.is_enabled.assert_called_once()
  26. call.run(cipy.Context())
  27. action.run.assert_called_once()
  28. call.cleanup(cipy.Context())
  29. action.cleanup.assert_called_once()
  30. def test_call_can_pass_args() -> None:
  31. class Inputs(cipy.Inputs):
  32. foo: int = cipy.required()
  33. action = mock_action()
  34. action.inputs = Inputs()
  35. call = cipy.Call(action, foo=3)
  36. call.run(cipy.Context())
  37. action.run.assert_called_once()
  38. assert action.inputs.foo == 3
  39. def test_composite_runs_steps_in_order() -> None:
  40. run = unittest.mock.Mock()
  41. a1 = mock_action(name="A")
  42. a2 = mock_action(name="B")
  43. a1.run = run.a1
  44. a1.run.return_value = cipy.Status.SUCCESS
  45. a2.run = run.a2
  46. a2.run.return_value = cipy.Status.SUCCESS
  47. comp = cipy.Composite(a1, a2)
  48. comp.run(cipy.Context())
  49. run.assert_has_calls(
  50. [unittest.mock.call.a1(ANY), unittest.mock.call.a2(ANY)], any_order=False
  51. )
  52. def test_composite_checks_is_enabled_for_each() -> None:
  53. a1 = mock_action(name="A")
  54. a1.run.return_value = cipy.Status.FAILURE
  55. a2 = mock_action(name="B")
  56. a2.run.return_value = cipy.Status.SUCCESS
  57. comp = cipy.Composite(a1, a2)
  58. comp.run(cipy.Context())
  59. a1.run.assert_called_once()
  60. a2.is_enabled.assert_called_once()
  61. a2.run.assert_not_called()
  62. def test_composite_cleanup_goes_backwards() -> None:
  63. cleanup = unittest.mock.Mock()
  64. a1 = mock_action(name="A")
  65. a2 = mock_action(name="B")
  66. a1.run.return_value = cipy.Status.SUCCESS
  67. a2.run.return_value = cipy.Status.SUCCESS
  68. a1.cleanup = cleanup.a1
  69. a2.cleanup = cleanup.a2
  70. comp = cipy.Composite(a1, a2)
  71. comp.run(cipy.Context())
  72. comp.cleanup(cipy.Context())
  73. cleanup.assert_has_calls(
  74. [unittest.mock.call.a2(ANY), unittest.mock.call.a1(ANY)], any_order=False
  75. )
  76. def test_composite_cleanup_ignores_skipped() -> None:
  77. a1 = mock_action(name="A")
  78. a1.run.return_value = cipy.Status.SKIPPED
  79. a2 = mock_action(name="B")
  80. a2.run.return_value = cipy.Status.SUCCESS
  81. comp = cipy.Composite(a1, a2)
  82. comp.run(cipy.Context())
  83. comp.cleanup(cipy.Context())
  84. a2.cleanup.assert_called_once()
  85. a1.cleanup.assert_not_called()