Files
govoplan/tests/test_devkit_cancellation.py
T
zemion 2ffdb23f69
Dependency Audit / dependency-audit (push) Successful in 1m45s
Deployment Installer / deployment-installer (push) Successful in 6s
Security Audit / security-audit (push) Successful in 11m30s
feat(devkit): add resumable workspace automation and UI review tooling
Verified with the coordinated workspace changes by devkit full run
2026-09-08T225814-186389-0000-3e3ed7cd (all seven phases passed).
This shared UI pass does not mark the individual module reviews complete.
2026-09-09 02:03:17 +02:00

89 lines
3.1 KiB
Python
Executable File

"""Signals during snapshot probes must not become passing run evidence."""
import os
import signal
import pytest
import test_devkit_runner as fixtures
from govoplan_devkit import runner
from govoplan_devkit.checkpoints import Checkpoints
example = fixtures.example
@pytest.mark.parametrize("interruption", [signal.SIGINT, signal.SIGTERM])
@pytest.mark.parametrize("probe", ["source", "environment"])
def test_interrupt_during_final_snapshot_is_not_a_passing_run(
example, monkeypatch, interruption, probe
):
args, repo = example
calls = {"source": 0, "environment": 0}
events, interruptions = [], []
args.on_progress = events.append
original = Checkpoints.source
def reached(kind):
calls[kind] += 1
if kind == probe and events and events[-1]["phase"] == "finalizing":
# Intermediate checkpoint probes are also real verification. Target
# finalization explicitly, while the runner owns signal handlers.
interruptions.append(kind)
os.kill(os.getpid(), interruption)
def source(self, *values, **kwargs):
reached("source")
return original(self, *values, **kwargs)
def environment(*_args, **_kwargs):
reached("environment")
return "fixture-environment"
monkeypatch.setattr(Checkpoints, "source", source)
monkeypatch.setattr(runner, "environment_fingerprint", environment)
result = runner.run_checks(args, [fixtures.stage(repo)])
assert interruptions == [probe]
assert calls[probe] >= 2
assert result["stages"][0]["status"] == "passed"
assert result["status"] == "interrupted"
assert result["_exit_code"] != 0
receipt = runner.read_receipt(args.workspace_root, args.state_dir, result["run_id"])
assert receipt["status"] == "interrupted"
@pytest.mark.parametrize("interruption", [signal.SIGINT, signal.SIGTERM])
@pytest.mark.parametrize("probe", ["source", "environment"])
def test_preparation_interrupt_stops_at_probe_boundary_without_running_checks(
example, monkeypatch, interruption, probe
):
args, repo = example
calls = []
original = Checkpoints.source
def reached(kind):
calls.append(kind)
if kind == probe:
os.kill(os.getpid(), interruption)
def source(self, *values, **kwargs):
reached("source")
return original(self, *values, **kwargs)
def environment(*_args, **_kwargs):
reached("environment")
return "fixture-environment"
def must_not_execute(*args, **kwargs):
pytest.fail("A cancelled preparation must not start a check")
monkeypatch.setattr(Checkpoints, "source", source)
monkeypatch.setattr(runner, "environment_fingerprint", environment)
monkeypatch.setattr(runner, "execute_stage", must_not_execute)
result = runner.run_checks(args, [fixtures.stage(repo)])
assert calls == (["source"] if probe == "source" else ["source", "environment"])
assert result["status"] == "interrupted"
assert result["snapshot_verified"] is False
assert result["_exit_code"] != 0
receipt = runner.read_receipt(args.workspace_root, args.state_dir, result["run_id"])
assert receipt["status"] == "interrupted"