test_base_commands.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. """Focused regression tests for module base commands."""
  2. from __future__ import annotations
  3. from types import SimpleNamespace
  4. from cli.core.module.base_commands import GenerationConfig, apply_output_name, generate_template, list_templates
  5. def _noop(*_args, **_kwargs) -> None:
  6. return None
  7. def _raise_destination_prompt(slug: str):
  8. raise AssertionError(f"prompt_generation_destination called for {slug}")
  9. def _raise_output_check(*_args, **_kwargs):
  10. raise AssertionError("check_output_directory should not run")
  11. class _DisplayCapture:
  12. def __init__(self) -> None:
  13. self.lines: list[str] = []
  14. self.templates = SimpleNamespace(
  15. render_template_header=_noop,
  16. render_file_tree=_noop,
  17. )
  18. self.variables = SimpleNamespace(render_variables_table=_noop)
  19. def text(self, value: str, style: str | None = None) -> None:
  20. del style
  21. self.lines.append(value)
  22. def success(self, value: str, *args, **kwargs) -> None:
  23. del args, kwargs
  24. self.lines.append(value)
  25. def warning(self, value: str, *args, **kwargs) -> None:
  26. del args, kwargs
  27. self.lines.append(value)
  28. def error(self, value: str, *args, **kwargs) -> None:
  29. del args, kwargs
  30. self.lines.append(value)
  31. def data_table(self, *args, **kwargs) -> None:
  32. del args, kwargs
  33. raise AssertionError("data_table should not be used for raw output")
  34. def info(self, *args, **kwargs) -> None:
  35. del args, kwargs
  36. raise AssertionError("info should not be used when templates exist")
  37. def test_list_templates_raw_outputs_tab_separated_rows() -> None:
  38. """Raw listing should emit one tab-separated row per template."""
  39. template = SimpleNamespace(
  40. id="whoami",
  41. metadata=SimpleNamespace(
  42. name="Whoami",
  43. tags=["docker", "test"],
  44. version=SimpleNamespace(name="1.0.0"),
  45. library="default",
  46. library_type="git",
  47. ),
  48. )
  49. display = _DisplayCapture()
  50. module_instance = SimpleNamespace(
  51. name="compose",
  52. display=display,
  53. _load_all_templates=lambda: [template],
  54. )
  55. returned_templates = list_templates(module_instance, raw=True)
  56. assert returned_templates == [template]
  57. assert display.lines == ["whoami\tWhoami\tdocker,test\t1.0.0\tdefault"]
  58. def test_apply_output_name_renames_top_level_paths_only() -> None:
  59. """Named generation should rename top-level outputs while preserving nested names."""
  60. rendered_files = {
  61. "files/test.txt": "nested",
  62. "main.tf": "main",
  63. "dns.tf": "dns",
  64. }
  65. assert apply_output_name(rendered_files, "servertest1") == {
  66. "servertest1_files/test.txt": "nested",
  67. "servertest1.tf": "main",
  68. "servertest1_dns.tf": "dns",
  69. }
  70. def test_generate_template_applies_output_name_before_writing(monkeypatch, tmp_path) -> None:
  71. """Generate should write renamed paths when --name is provided."""
  72. display = _DisplayCapture()
  73. template = SimpleNamespace(id="terraform", slug="terraform")
  74. module_instance = SimpleNamespace(name="terraform", display=display)
  75. written: dict[str, object] = {}
  76. monkeypatch.setattr("cli.core.module.base_commands._prepare_template", lambda *_args, **_kwargs: template)
  77. monkeypatch.setattr(
  78. "cli.core.module.base_commands._render_template",
  79. lambda *_args, **_kwargs: ({"files/test.txt": "nested", "main.tf": "main", "dns.tf": "dns"}, {}),
  80. )
  81. monkeypatch.setattr("cli.core.module.base_commands.check_output_directory", lambda *_args, **_kwargs: [])
  82. def capture_write(output_dir, rendered_files):
  83. written["output_dir"] = output_dir
  84. written["rendered_files"] = rendered_files
  85. monkeypatch.setattr("cli.core.module.base_commands.write_rendered_files", capture_write)
  86. generate_template(
  87. module_instance,
  88. GenerationConfig(
  89. id="terraform",
  90. output=str(tmp_path),
  91. interactive=False,
  92. name="servertest1",
  93. ),
  94. )
  95. assert written["output_dir"] == tmp_path
  96. assert written["rendered_files"] == {
  97. "servertest1_files/test.txt": "nested",
  98. "servertest1.tf": "main",
  99. "servertest1_dns.tf": "dns",
  100. }
  101. def test_generate_template_dry_run_skips_destination_prompt_and_overwrite_check(
  102. monkeypatch,
  103. ) -> None:
  104. """Dry runs without explicit destinations should not ask where to write or confirm overwrites."""
  105. display = _DisplayCapture()
  106. template = SimpleNamespace(id="whoami", slug="whoami")
  107. module_instance = SimpleNamespace(name="compose", display=display)
  108. monkeypatch.setattr("cli.core.module.base_commands._prepare_template", lambda *_args, **_kwargs: template)
  109. monkeypatch.setattr(
  110. "cli.core.module.base_commands._render_template",
  111. lambda *_args, **_kwargs: ({"compose.yaml": "services:\n"}, {}),
  112. )
  113. monkeypatch.setattr("cli.core.module.base_commands.prompt_generation_destination", _raise_destination_prompt)
  114. monkeypatch.setattr(
  115. "cli.core.module.base_commands.check_output_directory",
  116. _raise_output_check,
  117. )
  118. generate_template(
  119. module_instance,
  120. GenerationConfig(
  121. id="whoami",
  122. interactive=True,
  123. dry_run=True,
  124. ),
  125. )
  126. assert any("boilerplate rendered successfully" in line for line in display.lines)
  127. assert any("preview only" in line for line in display.lines)