fix(agent): isolate closure receipt id binds
This commit is contained in:
@@ -2194,12 +2194,13 @@ async def _read_verified_apply_closure_prerequisites(
|
||||
LEFT JOIN automation_operation_log candidate
|
||||
ON candidate.op_id = check_mode.parent_op_id
|
||||
AND candidate.operation_type = 'ansible_candidate_matched'
|
||||
WHERE apply.op_id = CAST(:apply_op_id AS uuid)
|
||||
WHERE apply.op_id = CAST(:apply_op_id_uuid AS uuid)
|
||||
AND apply.operation_type = 'ansible_apply_executed'
|
||||
LIMIT 1
|
||||
"""),
|
||||
{
|
||||
"apply_op_id": apply_op_id,
|
||||
"apply_op_id_uuid": apply_op_id,
|
||||
"incident_id": claim.incident_id,
|
||||
"project_id": project_id,
|
||||
"automation_run_id": automation_run_id,
|
||||
@@ -2298,7 +2299,7 @@ async def _read_incident_closure_readback(
|
||||
) AS resolved_lifecycle
|
||||
FROM incidents incident
|
||||
JOIN automation_operation_log apply
|
||||
ON apply.op_id = CAST(:apply_op_id AS uuid)
|
||||
ON apply.op_id = CAST(:apply_op_id_uuid AS uuid)
|
||||
WHERE incident.incident_id = :incident_id
|
||||
AND incident.project_id = :project_id
|
||||
LIMIT 1
|
||||
@@ -2307,6 +2308,7 @@ async def _read_incident_closure_readback(
|
||||
"incident_id": claim.incident_id,
|
||||
"project_id": project_id,
|
||||
"apply_op_id": apply_op_id,
|
||||
"apply_op_id_uuid": apply_op_id,
|
||||
"automation_run_id": automation_run_id,
|
||||
},
|
||||
)
|
||||
@@ -2434,7 +2436,7 @@ async def _commit_verified_incident_closure(
|
||||
WITH target_apply AS (
|
||||
SELECT apply.op_id
|
||||
FROM automation_operation_log apply
|
||||
WHERE apply.op_id = CAST(:apply_op_id AS uuid)
|
||||
WHERE apply.op_id = CAST(:apply_op_id_uuid AS uuid)
|
||||
AND apply.operation_type = 'ansible_apply_executed'
|
||||
AND apply.status = 'success'
|
||||
AND coalesce(
|
||||
@@ -2596,6 +2598,7 @@ async def _commit_verified_incident_closure(
|
||||
"""),
|
||||
{
|
||||
"apply_op_id": apply_op_id,
|
||||
"apply_op_id_uuid": apply_op_id,
|
||||
"incident_id": claim.incident_id,
|
||||
"project_id": project_id,
|
||||
"automation_run_id": automation_run_id,
|
||||
@@ -4581,13 +4584,13 @@ async def _verify_retry_terminal_projection_readback(
|
||||
) AS projection_verified
|
||||
FROM incidents incident
|
||||
JOIN automation_operation_log apply
|
||||
ON apply.op_id = CAST(:apply_op_id AS uuid)
|
||||
ON apply.op_id = CAST(:apply_op_id_uuid AS uuid)
|
||||
AND apply.operation_type = 'ansible_apply_executed'
|
||||
AND apply.status = 'failed'
|
||||
AND apply.input ->> 'automation_run_id'
|
||||
= :automation_run_id
|
||||
JOIN automation_operation_log replay
|
||||
ON replay.op_id = CAST(:replay_op_id AS uuid)
|
||||
ON replay.op_id = CAST(:replay_op_id_uuid AS uuid)
|
||||
AND replay.parent_op_id = apply.op_id
|
||||
AND replay.operation_type = 'ansible_execution_skipped'
|
||||
AND replay.status IN ('success', 'failed')
|
||||
@@ -4608,7 +4611,9 @@ async def _verify_retry_terminal_projection_readback(
|
||||
{
|
||||
"automation_run_id": automation_run_id,
|
||||
"apply_op_id": apply_op_id,
|
||||
"apply_op_id_uuid": apply_op_id,
|
||||
"replay_op_id": replay_op_id,
|
||||
"replay_op_id_uuid": replay_op_id,
|
||||
"terminal_type": terminal_type,
|
||||
"incident_id": claim.incident_id,
|
||||
"project_id": project_id,
|
||||
|
||||
@@ -288,6 +288,29 @@ async def test_atomic_closure_rolls_back_when_bundle_readback_is_missing(
|
||||
assert "UPDATE incidents incident" in atomic_statement
|
||||
assert "WITH target_apply AS" in atomic_statement
|
||||
assert "WHEN upper(incident.status::text) = 'CLOSED'" in atomic_statement
|
||||
assert db.parameters[1]["apply_op_id_uuid"] == db.parameters[1]["apply_op_id"]
|
||||
|
||||
|
||||
def test_uuid_lookup_and_json_text_receipt_binds_are_type_isolated() -> None:
|
||||
mixed_apply_id_queries = (
|
||||
service._read_verified_apply_closure_prerequisites,
|
||||
service._read_incident_closure_readback,
|
||||
service._commit_verified_incident_closure,
|
||||
service._verify_retry_terminal_projection_readback,
|
||||
)
|
||||
|
||||
for query_owner in mixed_apply_id_queries:
|
||||
source = inspect.getsource(query_owner)
|
||||
assert "CAST(:apply_op_id_uuid AS uuid)" in source
|
||||
assert "CAST(:apply_op_id AS uuid)" not in source
|
||||
assert '"apply_op_id_uuid": apply_op_id' in source
|
||||
|
||||
retry_source = inspect.getsource(
|
||||
service._verify_retry_terminal_projection_readback
|
||||
)
|
||||
assert "CAST(:replay_op_id_uuid AS uuid)" in retry_source
|
||||
assert "CAST(:replay_op_id AS uuid)" not in retry_source
|
||||
assert '"replay_op_id_uuid": replay_op_id' in retry_source
|
||||
|
||||
|
||||
def test_terminal_candidate_contract_requires_verified_four_node_chain() -> None:
|
||||
|
||||
Reference in New Issue
Block a user