forked from HAL9000/cleveragents-core
1878998b7a
Rename the TDD tag system from tdd_bug/tdd_bug_<N> to tdd_issue/tdd_issue_<N> across the entire codebase. The tdd_expected_fail tag is unchanged. The TDD expected-failure workflow is not limited to bug fixes — it applies equally to any issue type (features, tasks, refactors). The _bug suffix was misleading and narrowed the perceived scope. The new _issue suffix accurately reflects that the TDD tagging system applies to any Forgejo issue. Changes span 92 files: - features/environment.py: validate_tdd_tags(), should_invert_result(), and apply_tdd_inversion() updated — regex, variables, error messages - robot/tdd_expected_fail_listener.py: _validate_tdd_tags(), _should_invert_result(), start_test(), end_test() updated consistently - 33 Behave .feature files: all @tdd_bug/@tdd_bug_<N> tags renamed - 29 Robot .robot files: all tdd_bug/tdd_bug_<N> tags renamed - 3 Robot fixture files renamed (tdd_bug_alone, tdd_missing_tdd_bug, tdd_expected_fail_missing_bug_n) with content and references updated - Tag validation tests and helpers updated (function names, command dispatch keys, output strings, fixture references) - CONTRIBUTING.md: section renamed from 'TDD Bug Test Tags' to 'TDD Issue Test Tags', all tag references and examples updated - noxfile.py: comment references updated - Step definition files, mock helpers, and benchmark files: docstring references updated ISSUES CLOSED: #965
184 lines
6.7 KiB
Python
184 lines
6.7 KiB
Python
"""Step definitions for TDD Issue #822 — checkpoint rollback is simulated.
|
|
|
|
These steps exercise ``CheckpointService.rollback_to_checkpoint()`` against
|
|
a real git repository to prove that the method does **not** execute a
|
|
``git reset --hard`` and therefore leaves the filesystem unchanged.
|
|
|
|
On ``master`` (before the fix), ``rollback_to_checkpoint()`` constructs a
|
|
``RollbackResult`` with the checkpoint metadata but skips the actual git
|
|
operation. Files modified after the checkpoint remain modified after
|
|
rollback, which is the bug.
|
|
|
|
The feature is tagged ``@tdd_expected_fail`` so the assertion failure
|
|
(proving the bug) is inverted to a pass by the Behave environment hook.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
from behave import given, then, when
|
|
from behave.runner import Context
|
|
|
|
from cleveragents.application.services.checkpoint_service import CheckpointService
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Constants
|
|
# ---------------------------------------------------------------------------
|
|
|
|
_INITIAL_CONTENT = "initial content\n"
|
|
_MODIFIED_CONTENT = "modified after checkpoint\n"
|
|
_TRACKED_FILENAME = "tracked.txt"
|
|
_NEW_FILENAME = "extra.txt"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def _run_git(args: list[str], cwd: str) -> subprocess.CompletedProcess[str]:
|
|
"""Run a git command and return the completed process."""
|
|
return subprocess.run(
|
|
["git", *args],
|
|
cwd=cwd,
|
|
capture_output=True,
|
|
text=True,
|
|
check=True,
|
|
timeout=30,
|
|
)
|
|
|
|
|
|
def _get_head_sha(cwd: str) -> str:
|
|
"""Return the HEAD commit SHA of a git repository."""
|
|
result = _run_git(["rev-parse", "HEAD"], cwd=cwd)
|
|
return result.stdout.strip()
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Given steps
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given("a temporary git workspace with an initial committed file")
|
|
def step_create_workspace(context: Context) -> None:
|
|
"""Create a temp directory with a git repo containing a tracked file."""
|
|
tmpdir = tempfile.mkdtemp(prefix="tdd_checkpoint_822_")
|
|
context.workspace_dir = tmpdir
|
|
|
|
# Register cleanup early to avoid leaks if subsequent steps fail
|
|
def _cleanup() -> None:
|
|
shutil.rmtree(tmpdir, ignore_errors=True)
|
|
|
|
context.add_cleanup(_cleanup)
|
|
|
|
# Initialise git repo
|
|
_run_git(["init", "--initial-branch=main"], cwd=tmpdir)
|
|
_run_git(["config", "user.email", "test@example.com"], cwd=tmpdir)
|
|
_run_git(["config", "user.name", "Test"], cwd=tmpdir)
|
|
|
|
# Create and commit the tracked file
|
|
tracked_path = os.path.join(tmpdir, _TRACKED_FILENAME)
|
|
Path(tracked_path).write_text(_INITIAL_CONTENT)
|
|
_run_git(["add", _TRACKED_FILENAME], cwd=tmpdir)
|
|
_run_git(["commit", "-m", "Initial commit"], cwd=tmpdir)
|
|
|
|
context.tracked_file_path = tracked_path
|
|
context.initial_sha = _get_head_sha(tmpdir)
|
|
|
|
|
|
@given("a checkpoint is created from the current commit")
|
|
def step_create_checkpoint(context: Context) -> None:
|
|
"""Create a checkpoint referencing the current HEAD commit."""
|
|
plan_id_value = "01JBG822CHKPT000PXAN000000"
|
|
sandbox_ref = context.initial_sha
|
|
|
|
service = CheckpointService()
|
|
service.register_sandbox(plan_id_value, context.workspace_dir)
|
|
|
|
checkpoint = service.create_checkpoint(
|
|
plan_id=plan_id_value,
|
|
sandbox_ref=sandbox_ref,
|
|
reason="TDD checkpoint for bug #822",
|
|
checkpoint_type="manual",
|
|
)
|
|
|
|
context.checkpoint_service = service
|
|
context.checkpoint = checkpoint
|
|
context.plan_id = plan_id_value
|
|
|
|
|
|
@given("the tracked file is modified after the checkpoint")
|
|
def step_modify_tracked_file(context: Context) -> None:
|
|
"""Overwrite the tracked file and commit the change."""
|
|
Path(context.tracked_file_path).write_text(_MODIFIED_CONTENT)
|
|
_run_git(["add", _TRACKED_FILENAME], cwd=context.workspace_dir)
|
|
_run_git(["commit", "-m", "Modify tracked file"], cwd=context.workspace_dir)
|
|
|
|
|
|
@given("a new file is added and committed after the checkpoint")
|
|
def step_add_new_file(context: Context) -> None:
|
|
"""Create a new file and commit it after the checkpoint."""
|
|
new_path = os.path.join(context.workspace_dir, _NEW_FILENAME)
|
|
Path(new_path).write_text("new file content\n")
|
|
_run_git(["add", _NEW_FILENAME], cwd=context.workspace_dir)
|
|
_run_git(["commit", "-m", "Add new file"], cwd=context.workspace_dir)
|
|
context.new_file_path = new_path
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# When steps
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when("I invoke rollback_to_checkpoint targeting the checkpoint")
|
|
def step_invoke_rollback(context: Context) -> None:
|
|
"""Call rollback_to_checkpoint and store the result."""
|
|
service: CheckpointService = context.checkpoint_service
|
|
result = service.rollback_to_checkpoint(
|
|
plan_id=context.plan_id,
|
|
checkpoint_id=context.checkpoint.checkpoint_id,
|
|
)
|
|
context.rollback_result = result
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Then steps
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@then("the tracked file content should match the checkpoint state")
|
|
def step_assert_file_reverted(context: Context) -> None:
|
|
"""Assert that the tracked file has been reverted to its initial content.
|
|
|
|
This assertion FAILS on master because rollback_to_checkpoint() does
|
|
not execute git reset --hard — the file still contains the modified
|
|
content. The failure proves bug #822 exists.
|
|
"""
|
|
actual = Path(context.tracked_file_path).read_text()
|
|
assert actual == _INITIAL_CONTENT, (
|
|
f"Expected tracked file to be reverted to checkpoint content.\n"
|
|
f"Expected: {_INITIAL_CONTENT!r}\n"
|
|
f"Actual: {actual!r}\n"
|
|
f"Bug #822: rollback_to_checkpoint() did not execute git reset --hard."
|
|
)
|
|
|
|
|
|
@then("the new file should not exist in the workspace")
|
|
def step_assert_new_file_removed(context: Context) -> None:
|
|
"""Assert that the file added after the checkpoint no longer exists.
|
|
|
|
This assertion FAILS on master because rollback_to_checkpoint() does
|
|
not execute git reset --hard — the new file remains on disk. The
|
|
failure proves bug #822 exists.
|
|
"""
|
|
assert not os.path.exists(context.new_file_path), (
|
|
f"Expected new file to be removed after rollback.\n"
|
|
f"File still exists: {context.new_file_path}\n"
|
|
f"Bug #822: rollback_to_checkpoint() did not execute git reset --hard."
|
|
)
|