feat: expose cleanup and reporting outcomes for gateway runs
Some checks failed
ci / validate (push) Has been cancelled
Some checks failed
ci / validate (push) Has been cancelled
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a0726e-5232-73f2-aaca-2c05ceb62efb
This commit is contained in:
parent
f76cb7d955
commit
356e34992a
6 changed files with 177 additions and 7 deletions
|
|
@ -201,6 +201,8 @@ def test_owner_session_failure_still_destroys_sandbox(failure):
|
|||
rein=ReinAharness(), manager=manager)
|
||||
assert not result.ok
|
||||
assert result.evidence.failure_stage == "session_start"
|
||||
assert result.evidence.session_cleanup == "not_attempted"
|
||||
assert result.evidence.sandbox_destroy == "succeeded"
|
||||
manager.destroy.assert_called_once_with("sbx1")
|
||||
host_run.assert_not_called()
|
||||
|
||||
|
|
@ -415,3 +417,105 @@ def test_manager_initialization_failure_returns_redacted_evidence():
|
|||
assert result.tool_error == "private manager configuration"
|
||||
assert "private manager configuration" not in str(post.call_args)
|
||||
post.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('cleanup_fails,destroy_fails', [(True, False), (False, True), (True, True)])
|
||||
def test_teardown_outcomes_survive_original_execution_failure(cleanup_fails, destroy_fails):
|
||||
manager = MagicMock()
|
||||
manager.create.return_value = _fake_status()
|
||||
if destroy_fails:
|
||||
manager.destroy.side_effect = RuntimeError('private destroy failure')
|
||||
|
||||
class FailingRein(_FakeRein):
|
||||
def dispatch_tool(self, session, tool_call):
|
||||
raise RuntimeError('original execution failure')
|
||||
|
||||
def cleanup_session(self, session):
|
||||
if cleanup_fails:
|
||||
raise RuntimeError('private cleanup failure')
|
||||
|
||||
with patch('glas_harness.gateway.hub.post_progress_event', return_value=True) as post:
|
||||
result = run_execution(_request(report_to_hub=True), catalog=_catalog_with_readiness(),
|
||||
rein=FailingRein(), manager=manager)
|
||||
assert not result.ok
|
||||
assert result.evidence.failure_stage == 'execution'
|
||||
assert result.tool_error == 'original execution failure'
|
||||
assert result.evidence.session_cleanup == ('failed' if cleanup_fails else 'succeeded')
|
||||
assert result.evidence.sandbox_destroy == ('failed' if destroy_fails else 'succeeded')
|
||||
detail = post.call_args.kwargs['detail']
|
||||
assert detail['session_cleanup'] == result.evidence.session_cleanup
|
||||
assert detail['sandbox_destroy'] == result.evidence.sandbox_destroy
|
||||
assert 'private' not in str(detail)
|
||||
manager.destroy.assert_called_once_with('sbx1')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('report,accepted,expected', [
|
||||
(False, True, 'not_requested'), (True, True, 'accepted'), (True, False, 'failed')
|
||||
])
|
||||
def test_reporting_outcome_does_not_change_execution(report, accepted, expected):
|
||||
manager = MagicMock()
|
||||
manager.create.return_value = _fake_status()
|
||||
rein = _FakeRein()
|
||||
with patch('glas_harness.gateway.hub.post_progress_event', return_value=accepted) as post:
|
||||
result = run_execution(_request(report_to_hub=report), catalog=_catalog_with_readiness(),
|
||||
rein=rein, manager=manager)
|
||||
assert result.ok
|
||||
assert result.hub_report_status == expected
|
||||
assert result.evidence.session_cleanup == 'succeeded'
|
||||
assert result.evidence.sandbox_destroy == 'succeeded'
|
||||
assert post.call_count == int(report)
|
||||
assert rein.calls.count('dispatch_tool') == 1
|
||||
|
||||
|
||||
def test_refusal_reports_no_teardown_attempt_and_reporting_failure():
|
||||
with patch('glas_harness.gateway.hub.post_progress_event', return_value=False):
|
||||
result = run_execution(_request(report_to_hub=True))
|
||||
assert result.evidence.outcome == 'refused'
|
||||
assert result.evidence.session_cleanup == 'not_attempted'
|
||||
assert result.evidence.sandbox_destroy == 'not_attempted'
|
||||
assert result.hub_report_status == 'failed'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('http_failure', ['timeout', 'status'])
|
||||
def test_http_reporting_failure_preserves_success(http_failure):
|
||||
import httpx
|
||||
manager = MagicMock()
|
||||
manager.create.return_value = _fake_status()
|
||||
with patch('glas_harness.hub.httpx.post') as post:
|
||||
if http_failure == 'timeout':
|
||||
post.side_effect = httpx.ConnectTimeout('private transport detail')
|
||||
else:
|
||||
post.return_value = httpx.Response(503, request=httpx.Request('POST', 'http://hub/progress/'))
|
||||
result = run_execution(_request(report_to_hub=True), catalog=_catalog_with_readiness(),
|
||||
rein=_FakeRein(), manager=manager)
|
||||
assert result.ok
|
||||
assert result.hub_report_status == 'failed'
|
||||
assert 'private transport detail' not in result.model_dump_json()
|
||||
post.assert_called_once()
|
||||
manager.destroy.assert_called_once_with('sbx1')
|
||||
|
||||
|
||||
def test_cleanup_failure_after_success_still_destroys_sandbox():
|
||||
manager = MagicMock()
|
||||
manager.create.return_value = _fake_status()
|
||||
rein = _FakeRein()
|
||||
rein.cleanup_session = MagicMock(side_effect=RuntimeError('cleanup failed'))
|
||||
result = run_execution(_request(), catalog=_catalog_with_readiness(),
|
||||
rein=rein, manager=manager)
|
||||
assert not result.ok
|
||||
assert result.evidence.failure_stage == 'teardown'
|
||||
assert result.evidence.session_cleanup == 'failed'
|
||||
assert result.evidence.sandbox_destroy == 'succeeded'
|
||||
manager.destroy.assert_called_once_with('sbx1')
|
||||
|
||||
|
||||
def test_old_result_records_have_unknown_outcomes():
|
||||
from glas_harness.contract import GatewayResult
|
||||
old_record = run_execution(_request()).model_dump()
|
||||
old_record.pop('hub_report_status')
|
||||
old_record['evidence'].pop('session_cleanup')
|
||||
old_record['evidence'].pop('sandbox_destroy')
|
||||
restored = GatewayResult.model_validate(old_record)
|
||||
assert restored.hub_report_status == 'unknown'
|
||||
assert restored.evidence.session_cleanup == 'unknown'
|
||||
assert restored.evidence.sandbox_destroy == 'unknown'
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue