script_test.py 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. # mypy: disable-error-code="attr-defined"
  2. import io
  3. import pytest
  4. import subprocess
  5. import typing
  6. import unittest.mock
  7. from pathlib import Path
  8. import cipy
  9. import cipy.script
  10. from matchers import HasAttributes
  11. def mock_run(
  12. args: typing.Sequence[str],
  13. *,
  14. stdout: int | None = None,
  15. stderr: int | None = None,
  16. check: bool, # Force an error if any of our mocked runs doesnt specify check
  17. **kwargs: typing.Any,
  18. ):
  19. return subprocess.CompletedProcess(
  20. args=args,
  21. stdout=None if stdout is None else "",
  22. stderr=None if stderr is None else "",
  23. returncode=0,
  24. )
  25. @pytest.fixture(scope="function", autouse=True)
  26. def disable_subprocess() -> typing.Iterator[None]:
  27. mock = unittest.mock.Mock()
  28. mock.side_effect = mock_run
  29. with unittest.mock.patch("subprocess.run", mock):
  30. yield
  31. def test_node_run_runs_main() -> None:
  32. action = cipy.script.NodeScript(name="example", main=Path("/dev/null"))
  33. action.run(cipy.Context())
  34. assert subprocess.run.call_args_list[0].args == (["node", "/dev/null"],)
  35. def test_node_cleanup_noop_without_post() -> None:
  36. action = cipy.script.NodeScript(name="example", main=Path("/dev/null"))
  37. action.cleanup(cipy.Context())
  38. subprocess.run.assert_not_called()
  39. def test_node_cleanup_runs_post() -> None:
  40. action = cipy.script.NodeScript(
  41. name="example", main=Path("/dev/null"), post=Path("/dev/zero")
  42. )
  43. action.cleanup(cipy.Context())
  44. assert subprocess.run.call_args_list[0].args == (["node", "/dev/zero"],)
  45. def test_script_writes_to_a_file() -> None:
  46. action = cipy.script.Script(script="""
  47. echo Lorem Ipsum
  48. """)
  49. def check(args, **kwargs):
  50. assert Path(args[-1]).is_file()
  51. with open(args[-1], "r", encoding="utf8") as file:
  52. assert file.read() == action.script
  53. return mock_run(args, **kwargs)
  54. subprocess.run.side_effect = check
  55. action.run(cipy.Context())
  56. def test_script_performs_mustache_substitution() -> None:
  57. action = cipy.script.Script(script="""
  58. echo {{ foo }}
  59. """)
  60. def check(args, **kwargs):
  61. assert Path(args[-1]).is_file()
  62. with open(args[-1], "r", encoding="utf8") as file:
  63. assert file.read() == "echo 1"
  64. return mock_run(args, **kwargs)
  65. subprocess.run.side_effect = check
  66. action.run(cipy.Context(foo=1))
  67. def test_script_propogates_mustache_error(ci_logger) -> None:
  68. action = cipy.script.Script(script="""
  69. echo {{ foo }}
  70. """)
  71. subprocess.run.side_effect = RuntimeError("Should not be reached")
  72. assert action.run(cipy.Context()) is cipy.Status.FAILURE
  73. ci_logger.format.assert_called_with(
  74. HasAttributes(name="Script", message="Could not find key 'foo'\n")
  75. )