feat: extend operational investigation workflows
Generic Container CI/CD / test-build-publish (push) Successful in 3m53s
Server CI/CD v2 / build-test-publish-and-deploy (push) Successful in 3m52s

This commit is contained in:
2026-10-30 17:30:19 +08:00
parent 63af998045
commit 05183aff93
82 changed files with 4644 additions and 330 deletions
+67
View File
@@ -137,3 +137,70 @@ def test_evidence_receipt_commits_in_root_operational_transaction(monkeypatch):
assert attached.evidence_id == 'saved'
assert events == ['commit-reads', 'begin-root-operational', 'commit-evidence']
receipt_repo.save_receipt.assert_awaited_once()
@pytest.mark.parametrize('job_budget', [3600, 7200])
def test_http_timeout_does_not_limit_queued_execution(monkeypatch, job_budget):
from unittest.mock import AsyncMock
from app.mcp import calculation_jobs as jobs, query_service
from app.mcp.catalog import TOOLS
tool = TOOLS['run_simulation']
args = tool.model.model_validate({
'context_handle': 'x' * 32, 'timeout_seconds': 1,
'start_time': '2026-10-08T12:00:00+08:00', 'duration': 3600,
'output_node_ids': ['J1'],
})
repo = SimpleNamespace(enqueue=AsyncMock(return_value={
'job_id': uuid4(), 'status': 'queued', 'tool': tool.name,
}))
monkeypatch.setattr(jobs, 'CalculationJobRepository', lambda *a: repo)
monkeypatch.setattr(query_service, 'authorize_query', AsyncMock())
scope = SimpleNamespace(connection=SimpleNamespace(commit=AsyncMock()),
project=SimpleNamespace(project_id=uuid4(), user_id=uuid4()))
result = asyncio.run(jobs.submit_calculation(
scope, binding(), tool, args, 'host-call', job_timeout_seconds=job_budget,
))
assert result.data['status'] == 'queued'
assert repo.enqueue.await_args.kwargs['timeout_seconds'] == job_budget
assert 'timeout_seconds' not in repo.enqueue.await_args.kwargs['parameters']
def test_expired_result_is_safe_explicit_error(monkeypatch):
from unittest.mock import AsyncMock
from app.mcp import calculation_jobs as jobs
from app.mcp.calculation_models import CalculationResultInput
original = binding()
row = {'job_id': uuid4(), 'binding': durable_binding(original), 'status': 'succeeded',
'result': None, 'result_expires_at': datetime(2020, 1, 1, tzinfo=timezone.utc)}
repo = SimpleNamespace(get=AsyncMock(return_value=row))
monkeypatch.setattr(jobs, 'CalculationJobRepository', lambda *a: repo)
monkeypatch.setattr(jobs, 'authorize_original', AsyncMock())
attach = AsyncMock()
monkeypatch.setattr(jobs, 'attach_calculation_evidence', attach)
scope = SimpleNamespace(connection=None, project=SimpleNamespace(project_id=original.project_id, user_id=uuid4()))
with pytest.raises(HTTPException) as error:
asyncio.run(jobs.read_calculation_job(scope, original, SimpleNamespace(name='get_calculation_result'),
CalculationResultInput(context_handle='x' * 32, job_id=row['job_id'])))
assert error.value.status_code == 410
assert jobs.public_job(row)['result_available'] is False
assert repo.get.await_args.kwargs['include_result'] is True
attach.assert_not_awaited()
def test_legacy_execution_timeout_does_not_block_owner_authorization(monkeypatch):
from unittest.mock import AsyncMock
from app.mcp import calculation_jobs as jobs, query_service
from app.mcp.catalog import TOOLS
original = binding()
owner = uuid4()
row = {'binding': durable_binding(original), 'tool': 'run_simulation',
'owner_user_id': owner, 'parameters': {'timeout_seconds': 3600,
'start_time': '2026-10-29T00:00:00Z', 'output_node_ids': ['1']}}
authorize = AsyncMock()
monkeypatch.setattr(query_service, 'authorize_query', authorize)
scope = SimpleNamespace(project=SimpleNamespace(project_id=original.project_id, user_id=owner))
_, tool, args = asyncio.run(jobs.authorize_original(scope, row))
assert tool is TOOLS['run_simulation']
assert args.timeout_seconds <= 240
assert row['parameters']['timeout_seconds'] == 3600
authorize.assert_awaited_once()