3eecb79003
CI / lint (pull_request) Successful in 16s
CI / quality (pull_request) Successful in 46s
CI / typecheck (pull_request) Successful in 56s
CI / security (pull_request) Successful in 56s
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 29s
CI / e2e_tests (pull_request) Successful in 2m7s
CI / unit_tests (pull_request) Successful in 3m8s
CI / docker (pull_request) Successful in 9s
CI / integration_tests (pull_request) Successful in 3m57s
CI / coverage (pull_request) Successful in 7m48s
CI / benchmark-regression (pull_request) Successful in 40m48s
TDD expected-fail tests proving bug #822 exists: CheckpointService.rollback_to_checkpoint() returns a successful RollbackResult but does not execute git reset --hard. Files modified after the checkpoint remain unchanged after rollback. Also fixes Robot Framework timeout robustness across the entire test suite: all Run Process calls now use on_timeout=kill (prevents SIGTERM-induced -15 exit codes under CI load) and timeouts increased to 120s (prevents premature kills during heavy parallel execution). ISSUES CLOSED: #839
211 lines
6.9 KiB
Python
211 lines
6.9 KiB
Python
"""Helper script for tdd_checkpoint_real_rollback.robot integration tests.
|
|
|
|
Each subcommand exercises CheckpointService.rollback_to_checkpoint() against
|
|
a real git repository to prove that the method does **not** execute
|
|
``git reset --hard``. Files modified after a checkpoint remain unchanged
|
|
after rollback, demonstrating bug #822.
|
|
|
|
The helper exits 0 with a sentinel when the rollback correctly restores
|
|
file state (bug fixed), and exits 1 when the bug is still present.
|
|
The ``tdd_expected_fail_listener`` on the Robot side handles pass/fail
|
|
inversion while the bug remains open.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
from collections.abc import Callable
|
|
from pathlib import Path
|
|
from typing import NoReturn
|
|
|
|
# Ensure local source tree AND robot/ directory are importable.
|
|
_ROOT = Path(__file__).resolve().parents[1]
|
|
_SRC = str(_ROOT / "src")
|
|
_ROBOT = str(_ROOT / "robot")
|
|
for _p in (_SRC, _ROBOT):
|
|
if _p not in sys.path:
|
|
sys.path.insert(0, _p)
|
|
|
|
from cleveragents.application.services.checkpoint_service import ( # noqa: E402
|
|
CheckpointService,
|
|
)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Constants
|
|
# ---------------------------------------------------------------------------
|
|
|
|
_INITIAL_CONTENT = "initial content\n"
|
|
_MODIFIED_CONTENT = "modified after checkpoint\n"
|
|
_TRACKED_FILENAME = "tracked.txt"
|
|
_NEW_FILENAME = "extra.txt"
|
|
_PLAN_ID = "01JBG822CHKPT000PXAN000000"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def _fail(msg: str) -> NoReturn:
|
|
"""Print failure message to stderr and exit with code 1."""
|
|
print(msg, file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
|
|
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()
|
|
|
|
|
|
def _create_workspace() -> str:
|
|
"""Create a temporary git workspace with an initial committed file.
|
|
|
|
Returns the path to the temporary directory.
|
|
"""
|
|
tmpdir = tempfile.mkdtemp(prefix="tdd_checkpoint_822_robot_")
|
|
try:
|
|
_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)
|
|
|
|
tracked_path = os.path.join(tmpdir, _TRACKED_FILENAME)
|
|
Path(tracked_path).write_text(_INITIAL_CONTENT, encoding="utf-8")
|
|
_run_git(["add", _TRACKED_FILENAME], cwd=tmpdir)
|
|
_run_git(["commit", "-m", "Initial commit"], cwd=tmpdir)
|
|
except Exception:
|
|
shutil.rmtree(tmpdir, ignore_errors=True)
|
|
raise
|
|
return tmpdir
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Subcommands
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def rollback_restores_content() -> None:
|
|
"""Verify that rollback reverts a modified tracked file.
|
|
|
|
Exits 0 with sentinel when the file is correctly reverted (bug fixed).
|
|
Exits 1 when the bug is still present (file remains modified).
|
|
"""
|
|
tmpdir = _create_workspace()
|
|
try:
|
|
tracked_path = os.path.join(tmpdir, _TRACKED_FILENAME)
|
|
initial_sha = _get_head_sha(tmpdir)
|
|
|
|
# Create checkpoint from current state
|
|
service = CheckpointService()
|
|
service.register_sandbox(_PLAN_ID, tmpdir)
|
|
checkpoint = service.create_checkpoint(
|
|
plan_id=_PLAN_ID,
|
|
sandbox_ref=initial_sha,
|
|
reason="TDD checkpoint for bug #822",
|
|
checkpoint_type="manual",
|
|
)
|
|
|
|
# Modify the tracked file and commit
|
|
Path(tracked_path).write_text(_MODIFIED_CONTENT)
|
|
_run_git(["add", _TRACKED_FILENAME], cwd=tmpdir)
|
|
_run_git(["commit", "-m", "Modify tracked file"], cwd=tmpdir)
|
|
|
|
# Invoke rollback
|
|
service.rollback_to_checkpoint(
|
|
plan_id=_PLAN_ID,
|
|
checkpoint_id=checkpoint.checkpoint_id,
|
|
)
|
|
|
|
# Assert file content matches checkpoint state
|
|
actual = Path(tracked_path).read_text()
|
|
if actual != _INITIAL_CONTENT:
|
|
_fail(
|
|
f"Tracked file not reverted after rollback. "
|
|
f"Expected: {_INITIAL_CONTENT!r}, Got: {actual!r}. "
|
|
f"Bug #822: rollback did not execute git reset --hard."
|
|
)
|
|
|
|
print("tdd-checkpoint-rollback-restores-content-ok")
|
|
finally:
|
|
shutil.rmtree(tmpdir, ignore_errors=True)
|
|
|
|
|
|
def rollback_removes_added_files() -> None:
|
|
"""Verify that rollback removes files added after the checkpoint.
|
|
|
|
Exits 0 with sentinel when the new file is removed (bug fixed).
|
|
Exits 1 when the bug is still present (new file still exists).
|
|
"""
|
|
tmpdir = _create_workspace()
|
|
try:
|
|
initial_sha = _get_head_sha(tmpdir)
|
|
|
|
# Create checkpoint from current state
|
|
service = CheckpointService()
|
|
service.register_sandbox(_PLAN_ID, tmpdir)
|
|
checkpoint = service.create_checkpoint(
|
|
plan_id=_PLAN_ID,
|
|
sandbox_ref=initial_sha,
|
|
reason="TDD checkpoint for bug #822",
|
|
checkpoint_type="manual",
|
|
)
|
|
|
|
# Add a new file and commit
|
|
new_path = os.path.join(tmpdir, _NEW_FILENAME)
|
|
Path(new_path).write_text("new file content\n")
|
|
_run_git(["add", _NEW_FILENAME], cwd=tmpdir)
|
|
_run_git(["commit", "-m", "Add new file"], cwd=tmpdir)
|
|
|
|
# Invoke rollback
|
|
service.rollback_to_checkpoint(
|
|
plan_id=_PLAN_ID,
|
|
checkpoint_id=checkpoint.checkpoint_id,
|
|
)
|
|
|
|
# Assert the new file no longer exists
|
|
if os.path.exists(new_path):
|
|
_fail(
|
|
f"New file still exists after rollback: {new_path}. "
|
|
f"Bug #822: rollback did not execute git reset --hard."
|
|
)
|
|
|
|
print("tdd-checkpoint-rollback-removes-added-files-ok")
|
|
finally:
|
|
shutil.rmtree(tmpdir, ignore_errors=True)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Dispatcher
|
|
# ---------------------------------------------------------------------------
|
|
|
|
_COMMANDS: dict[str, Callable[[], None]] = {
|
|
"rollback-restores-content": rollback_restores_content,
|
|
"rollback-removes-added-files": rollback_removes_added_files,
|
|
}
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) < 2 or sys.argv[1] not in _COMMANDS:
|
|
print(
|
|
f"Usage: {sys.argv[0]} <{'|'.join(_COMMANDS)}>",
|
|
file=sys.stderr,
|
|
)
|
|
sys.exit(1)
|
|
cmd = _COMMANDS[sys.argv[1]]
|
|
cmd()
|