From 802e1a0d42dff511f310683f7736939dff547676 Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah <22363102+codeforester@users.noreply.github.com> Date: Wed, 19 Aug 2026 18:27:20 +0530 Subject: [PATCH] test: migrate remaining manifest fixtures --- .../base_pr_policy/tests/test_engine.py | 9 +- cli/python/base_release/tests/test_engine.py | 99 +++++-------------- conftest.py | 96 ++++++++++++++++++ tests/test_shared_fixtures.py | 17 ++++ 4 files changed, 137 insertions(+), 84 deletions(-) diff --git a/cli/python/base_pr_policy/tests/test_engine.py b/cli/python/base_pr_policy/tests/test_engine.py index 287b708a..d319e3cd 100644 --- a/cli/python/base_pr_policy/tests/test_engine.py +++ b/cli/python/base_pr_policy/tests/test_engine.py @@ -10,14 +10,9 @@ from base_setup.github_manifest import GithubPrConfig, GithubPrRequiredSectionsConfig -def test_explicit_manifest_populates_history_project_metadata(tmp_path) -> None: +def test_explicit_manifest_populates_history_project_metadata(tmp_path, manifest_factory) -> None: project_root = tmp_path / "demo" - project_root.mkdir() - manifest_path = project_root / "base_manifest.yaml" - manifest_path.write_text( - "project:\n name: demo\ngithub:\n pr:\n required_sections:\n default: [Summary]\n", - encoding="utf-8", - ) + manifest_path = manifest_factory.write_pr_policy(project_root) outside = tmp_path / "outside" outside.mkdir() captured = [] diff --git a/cli/python/base_release/tests/test_engine.py b/cli/python/base_release/tests/test_engine.py index 5e4e0f09..6058d5c2 100644 --- a/cli/python/base_release/tests/test_engine.py +++ b/cli/python/base_release/tests/test_engine.py @@ -10,6 +10,7 @@ from contextlib import redirect_stderr from contextlib import redirect_stdout from pathlib import Path +from typing import Any from unittest import mock from base_cli_adapters.history import build_finished_record @@ -65,63 +66,6 @@ def isatty(self) -> bool: return True -def write_release_project( - root: Path, - *, - version_file_content: str = "1.2.3\n", - changelog: str | None = None, - homebrew: bool = True, -) -> Path: - changelog_content = changelog or "\n".join( - [ - "# Changelog", - "", - "## [Unreleased]", - "", - "## [1.2.3] - 2026-06-09", - "", - "- Added the release assistant.", - "", - "## [1.2.2] - 2026-06-01", - "", - "- Previous release.", - ] - ) - root.joinpath("VERSION").write_text(version_file_content, encoding="utf-8") - root.joinpath("CHANGELOG.md").write_text(changelog_content, encoding="utf-8") - manifest_lines = [ - "project:", - " name: demo", - "", - "release:", - " version_file: VERSION", - " changelog: CHANGELOG.md", - " tag_prefix: v", - " github:", - " repository: codeforester/demo", - " release_title: \"Demo v{version}\"", - ] - if homebrew: - manifest_lines.extend( - [ - " homebrew:", - " required: true", - " tap_repository: codeforester/homebrew-demo", - " formula_path: Formula/demo.rb", - " package: codeforester/demo/demo", - ] - ) - manifest_lines.extend(["", "artifacts: []"]) - manifest_path = root / "base_manifest.yaml" - manifest_path.write_text("\n".join(manifest_lines), encoding="utf-8") - subprocess.run(["git", "init"], cwd=root, check=True, stdout=subprocess.DEVNULL) - subprocess.run(["git", "config", "user.email", "base@example.com"], cwd=root, check=True) - subprocess.run(["git", "config", "user.name", "Base Tests"], cwd=root, check=True) - subprocess.run(["git", "add", "."], cwd=root, check=True) - subprocess.run(["git", "commit", "-m", "initial"], cwd=root, check=True, stdout=subprocess.DEVNULL) - return manifest_path - - def add_origin(root: Path) -> None: remote_path = root.parent / "remote.git" subprocess.run(["git", "init", "--bare", str(remote_path)], check=True, stdout=subprocess.DEVNULL) @@ -199,13 +143,14 @@ def test_delegated_missing_required_option_usage_uses_basectl_release(self) -> N class ReleaseEngineTests(unittest.TestCase): # pylint: disable=too-many-public-methods + manifest_factory: Any def test_explicit_manifest_populates_history_project_metadata(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) project_root = root / "demo" project_root.mkdir() - manifest_path = write_release_project(project_root) + manifest_path = self.manifest_factory.write_release(project_root) outside = root / "outside" outside.mkdir() captured: list[tuple[object, ...]] = [] @@ -233,7 +178,7 @@ def test_explicit_manifest_populates_history_project_metadata(self) -> None: def test_check_json_reports_ready_findings_with_stable_envelope(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) - manifest_path = write_release_project(root) + manifest_path = self.manifest_factory.write_release(root) with mock.patch("base_release.engine.release_findings", return_value=READY_FINDINGS): status, stdout, stderr = run_engine( @@ -271,7 +216,7 @@ def test_check_json_finding_is_error_with_null_execution_error(self) -> None: ) with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) - manifest_path = write_release_project(root) + manifest_path = self.manifest_factory.write_release(root) with mock.patch("base_release.engine.release_findings", return_value=findings): status, stdout, stderr = run_engine( @@ -289,7 +234,7 @@ def test_check_json_finding_is_error_with_null_execution_error(self) -> None: def test_check_json_warning_and_empty_findings_preserve_success_exit(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) - manifest_path = write_release_project(root) + manifest_path = self.manifest_factory.write_release(root) with mock.patch( "base_release.engine.release_findings", @@ -334,7 +279,7 @@ def test_check_json_controlled_manifest_failure_has_error_object(self) -> None: def test_notes_prints_changelog_section_for_version(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) - manifest_path = write_release_project(root) + manifest_path = self.manifest_factory.write_release(root) status, stdout, stderr = run_engine( ["notes", "--version", "1.2.3", "--manifest", str(manifest_path)], @@ -349,7 +294,7 @@ def test_notes_prints_changelog_section_for_version(self) -> None: def test_plan_prints_github_and_homebrew_handoff(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) - manifest_path = write_release_project(root) + manifest_path = self.manifest_factory.write_release(root) status, stdout, stderr = run_engine( ["plan", "--version", "1.2.3", "--manifest", str(manifest_path)], @@ -387,7 +332,7 @@ def test_plan_prints_1_0_homebrew_upgrade_reminder_without_issue_number(self) -> "- Stable release.", ] ) - manifest_path = write_release_project( + manifest_path = self.manifest_factory.write_release( root, version_file_content="1.0.0\n", changelog=changelog, @@ -406,7 +351,7 @@ def test_plan_prints_1_0_homebrew_upgrade_reminder_without_issue_number(self) -> def test_plan_prints_no_homebrew_handoff_for_github_only_project(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) - manifest_path = write_release_project(root, homebrew=False) + manifest_path = self.manifest_factory.write_release(root, homebrew=False) status, stdout, stderr = run_engine( ["plan", "--version", "1.2.3", "--manifest", str(manifest_path)], @@ -421,7 +366,7 @@ def test_plan_prints_no_homebrew_handoff_for_github_only_project(self) -> None: def test_publish_dry_run_prints_planned_actions_without_running_commands(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) - manifest_path = write_release_project(root) + manifest_path = self.manifest_factory.write_release(root) with mock.patch("base_release.engine.release_findings", return_value=READY_FINDINGS), mock.patch( "base_release.engine.github_release_finding", @@ -444,7 +389,7 @@ def test_publish_dry_run_prints_planned_actions_without_running_commands(self) - def test_publish_requires_yes_when_stdin_is_not_interactive(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) - manifest_path = write_release_project(root) + manifest_path = self.manifest_factory.write_release(root) with mock.patch("base_release.engine.release_findings", return_value=READY_FINDINGS), mock.patch( "base_release.engine.github_release_finding", @@ -464,7 +409,7 @@ def test_publish_requires_yes_when_stdin_is_not_interactive(self) -> None: def test_publish_yes_creates_annotated_tag_pushes_and_creates_github_release(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) - manifest_path = write_release_project(root) + manifest_path = self.manifest_factory.write_release(root) commands: list[tuple[list[str], Path | None]] = [] def fake_run_release_step(command: list[str], *, cwd: Path | None = None) -> None: @@ -508,7 +453,7 @@ def fake_run_release_step(command: list[str], *, cwd: Path | None = None) -> Non def test_publish_yes_reports_recovery_when_github_release_create_fails_after_tag_push(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) - manifest_path = write_release_project(root) + manifest_path = self.manifest_factory.write_release(root) commands: list[tuple[list[str], Path | None]] = [] def fake_run_release_step(command: list[str], *, cwd: Path | None = None) -> None: @@ -549,7 +494,7 @@ def fake_run_release_step(command: list[str], *, cwd: Path | None = None) -> Non def test_publish_fails_when_readiness_has_errors(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) - manifest_path = write_release_project(root) + manifest_path = self.manifest_factory.write_release(root) with mock.patch( "base_release.engine.release_findings", @@ -570,7 +515,7 @@ def test_publish_fails_when_readiness_has_errors(self) -> None: def test_publish_fails_when_github_release_already_exists(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) - manifest_path = write_release_project(root) + manifest_path = self.manifest_factory.write_release(root) with mock.patch("base_release.engine.release_findings", return_value=READY_FINDINGS), mock.patch( "base_release.engine.github_release_finding", @@ -596,7 +541,7 @@ def test_publish_fails_when_github_release_already_exists(self) -> None: def test_check_fails_when_version_file_does_not_match(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) - manifest_path = write_release_project(root, version_file_content="1.2.2\n") + manifest_path = self.manifest_factory.write_release(root, version_file_content="1.2.2\n") status, stdout, stderr = run_engine( ["check", "--version", "1.2.3", "--manifest", str(manifest_path)], @@ -611,7 +556,7 @@ def test_check_fails_when_version_file_does_not_match(self) -> None: def test_check_fails_when_changelog_section_is_missing(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) - manifest_path = write_release_project( + manifest_path = self.manifest_factory.write_release( root, changelog="# Changelog\n\n## [1.2.2] - 2026-06-01\n\n- Previous release.\n", ) @@ -630,7 +575,7 @@ def test_check_passes_for_clean_release_ready_project(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) / "project" root.mkdir() - manifest_path = write_release_project(root) + manifest_path = self.manifest_factory.write_release(root) add_origin(root) with mock.patch( @@ -653,7 +598,7 @@ def test_check_fails_when_worktree_is_dirty(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) / "project" root.mkdir() - manifest_path = write_release_project(root) + manifest_path = self.manifest_factory.write_release(root) add_origin(root) root.joinpath("scratch.txt").write_text("dirty\n", encoding="utf-8") @@ -675,7 +620,7 @@ def test_check_fails_when_local_tag_already_exists(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) / "project" root.mkdir() - manifest_path = write_release_project(root) + manifest_path = self.manifest_factory.write_release(root) add_origin(root) subprocess.run(["git", "tag", "v1.2.3"], cwd=root, check=True) @@ -697,7 +642,7 @@ def test_check_fails_when_remote_tag_already_exists(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: root = Path(tmpdir) / "project" root.mkdir() - manifest_path = write_release_project(root) + manifest_path = self.manifest_factory.write_release(root) add_origin_with_remote_tag(root, "v1.2.3") with mock.patch( diff --git a/conftest.py b/conftest.py index 6f931300..c2f1c392 100644 --- a/conftest.py +++ b/conftest.py @@ -2,6 +2,7 @@ from __future__ import annotations +import subprocess from pathlib import Path import pytest @@ -90,6 +91,101 @@ def write_command_surfaces(self, root: Path, name: str = "demo") -> Path: ], ) + def write_pr_policy(self, root: Path, name: str = "demo") -> Path: + """Write a manifest with the default pull-request policy sections.""" + + return self._write( + root, + [ + "project:", + f" name: {name}", + "github:", + " pr:", + " required_sections:", + " default: [Summary]", + ], + ) + + def write_release( + self, + root: Path, + *, + version_file_content: str = "1.2.3\n", + changelog: str | None = None, + homebrew: bool = True, + ) -> Path: + """Write a release-ready project with an initial Git commit.""" + + changelog_content = changelog or "\n".join( + [ + "# Changelog", + "", + "## [Unreleased]", + "", + "## [1.2.3] - 2026-06-09", + "", + "- Added the release assistant.", + "", + "## [1.2.2] - 2026-06-01", + "", + "- Previous release.", + ] + ) + root.mkdir(parents=True, exist_ok=True) + root.joinpath("VERSION").write_text(version_file_content, encoding="utf-8") + root.joinpath("CHANGELOG.md").write_text(changelog_content, encoding="utf-8") + manifest_lines = [ + "project:", + " name: demo", + "", + "release:", + " version_file: VERSION", + " changelog: CHANGELOG.md", + " tag_prefix: v", + " github:", + " repository: codeforester/demo", + " release_title: \"Demo v{version}\"", + ] + if homebrew: + manifest_lines.extend( + [ + " homebrew:", + " required: true", + " tap_repository: codeforester/homebrew-demo", + " formula_path: Formula/demo.rb", + " package: codeforester/demo/demo", + ] + ) + manifest_path = self._write(root, manifest_lines + ["", "artifacts: []"]) + self._initialize_git(root) + return manifest_path + + @staticmethod + def _initialize_git(root: Path) -> None: + subprocess.run(["git", "init"], cwd=root, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + subprocess.run( + ["git", "config", "user.email", "base@example.com"], + cwd=root, + check=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + subprocess.run( + ["git", "config", "user.name", "Base Tests"], + cwd=root, + check=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + subprocess.run(["git", "add", "."], cwd=root, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + subprocess.run( + ["git", "commit", "-m", "initial"], + cwd=root, + check=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + @staticmethod def write_ready_python_bin(python_bin: Path) -> None: python_bin.parent.mkdir(parents=True, exist_ok=True) diff --git a/tests/test_shared_fixtures.py b/tests/test_shared_fixtures.py index 6cd61f56..0d50ac59 100644 --- a/tests/test_shared_fixtures.py +++ b/tests/test_shared_fixtures.py @@ -23,3 +23,20 @@ def test_manifest_factory_writes_command_surfaces(project_root: Path, manifest_f assert "build:" in manifest assert "demo:" in manifest assert "activate:" in manifest + + +def test_manifest_factory_writes_pull_request_policy(project_root: Path, manifest_factory) -> None: + manifest_path = manifest_factory.write_pr_policy(project_root) + + assert manifest_path.read_text(encoding="utf-8") == ( + "project:\n name: demo\ngithub:\n pr:\n required_sections:\n default: [Summary]\n" + ) + + +def test_manifest_factory_writes_release_project(project_root: Path, manifest_factory) -> None: + manifest_path = manifest_factory.write_release(project_root) + + assert manifest_path == project_root / "base_manifest.yaml" + assert (project_root / "VERSION").read_text(encoding="utf-8") == "1.2.3\n" + assert (project_root / "CHANGELOG.md").is_file() + assert (project_root / ".git").is_dir()