Artifex/tests/test_worktree_mutation_tools.py
2026-08-15 16:03:01 +07:00

115 lines
4.5 KiB
Python

from __future__ import annotations
import subprocess
from pathlib import Path
from tools.capabilities import Capability
from tools.runtime import WorktreeTools
def init_repo(path: Path) -> WorktreeTools:
path.mkdir()
subprocess.run(["git", "init", "-b", "main"], cwd=path, check=True, capture_output=True, text=True)
return WorktreeTools(path, {Capability.READ_REPOSITORY, Capability.INVESTIGATE_WORKTREE, Capability.WRITE_WORKTREE})
def commit_baseline(path: Path) -> None:
subprocess.run(["git", "-c", "user.name=T", "-c", "user.email=t@example.invalid", "add", "."], cwd=path, check=True)
subprocess.run(["git", "-c", "user.name=T", "-c", "user.email=t@example.invalid", "commit", "-m", "baseline"], cwd=path, check=True, capture_output=True, text=True)
def test_apply_patch_single_hunk_and_git_diff(tmp_path: Path) -> None:
tools = init_repo(tmp_path / "repo")
(tmp_path / "repo" / "file.txt").write_text("one\ntwo\nthree\n", encoding="utf-8")
commit_baseline(tmp_path / "repo")
result = tools.apply_patch("file.txt", "@@ -1,3 +1,3 @@\n one\n-two\n+TWO\n three\n")
assert result.success
assert result.hunks_applied == 1
assert "TWO" in (tmp_path / "repo" / "file.txt").read_text(encoding="utf-8")
assert "+TWO" in tools.diff()
def test_apply_patch_multiple_hunks(tmp_path: Path) -> None:
tools = init_repo(tmp_path / "repo")
(tmp_path / "repo" / "file.txt").write_text("a\nb\nc\nd\ne\n", encoding="utf-8")
commit_baseline(tmp_path / "repo")
result = tools.apply_patch("file.txt", "@@ -1,2 +1,2 @@\n-a\n+A\n b\n@@ -4,2 +4,2 @@\n-d\n+D\n e\n")
assert result.success
assert result.hunks_applied == 2
assert (tmp_path / "repo" / "file.txt").read_text(encoding="utf-8") == "A\nb\nc\nD\ne\n"
def test_apply_patch_relocates_matching_hunk_when_line_number_is_stale(tmp_path: Path) -> None:
tools = init_repo(tmp_path / "repo")
(tmp_path / "repo" / "file.txt").write_text("header\nalpha\nbeta\ngamma\n", encoding="utf-8")
commit_baseline(tmp_path / "repo")
result = tools.apply_patch("file.txt", "@@ -1,3 +1,3 @@\n alpha\n-beta\n+BETA\n gamma\n")
assert result.success
assert (tmp_path / "repo" / "file.txt").read_text(encoding="utf-8") == "header\nalpha\nBETA\ngamma\n"
def test_apply_patch_context_mismatch_is_not_partial(tmp_path: Path) -> None:
tools = init_repo(tmp_path / "repo")
path = tmp_path / "repo" / "file.txt"
path.write_text("a\nb\nc\n", encoding="utf-8")
commit_baseline(tmp_path / "repo")
result = tools.apply_patch("file.txt", "@@ -1,2 +1,2 @@\n-wrong\n+W\n b\n")
assert not result.success
assert path.read_text(encoding="utf-8") == "a\nb\nc\n"
def test_apply_patch_rejects_path_traversal(tmp_path: Path) -> None:
tools = init_repo(tmp_path / "repo")
result = tools.apply_patch("../outside.txt", "@@ -1 +1 @@\n-a\n+b\n")
assert not result.success
def test_delete_file_behaviour(tmp_path: Path) -> None:
tools = init_repo(tmp_path / "repo")
(tmp_path / "repo" / "file.txt").write_text("x\n", encoding="utf-8")
(tmp_path / "repo" / "dir").mkdir()
commit_baseline(tmp_path / "repo")
assert tools.delete_file("file.txt").success
assert "D file.txt" in tools.status()
assert not tools.delete_file("missing.txt").success
assert not tools.delete_file("dir").success
assert not tools.delete_file("../outside.txt").success
def test_move_file_behaviour(tmp_path: Path) -> None:
tools = init_repo(tmp_path / "repo")
repo = tmp_path / "repo"
(repo / "source.txt").write_text("content", encoding="utf-8")
(repo / "existing.txt").write_text("existing", encoding="utf-8")
commit_baseline(repo)
assert tools.move_file("source.txt", "nested/dest.txt").success
assert (repo / "nested" / "dest.txt").read_text(encoding="utf-8") == "content"
assert not tools.move_file("missing.txt", "x.txt").success
assert not tools.move_file("nested/dest.txt", "existing.txt").success
assert not tools.move_file("nested/dest.txt", "../outside.txt").success
assert "source.txt" in tools.status()
assert "nested/" in tools.status()
def test_create_directory_behaviour(tmp_path: Path) -> None:
tools = init_repo(tmp_path / "repo")
repo = tmp_path / "repo"
(repo / "file.txt").write_text("x", encoding="utf-8")
assert tools.create_directory("a").success
assert tools.create_directory("a/b/c").success
assert tools.create_directory("a/b/c").success
assert not tools.create_directory("file.txt").success
assert not tools.create_directory("../outside").success