fix(telegram): pin durable provider destination proof
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 3m15s
CD Pipeline / build-and-deploy (push) Successful in 15m54s
CD Pipeline / post-deploy-checks (push) Successful in 2m17s
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 3m15s
CD Pipeline / build-and-deploy (push) Successful in 15m54s
CD Pipeline / post-deploy-checks (push) Successful in 2m17s
This commit is contained in:
@@ -21,6 +21,28 @@ from src.models.agent99_completion import Agent99TelegramLifecycleRequest
|
||||
from src.services import agent99_telegram_lifecycle as lifecycle_service
|
||||
from src.services import telegram_gateway as gateway_service
|
||||
|
||||
DESTINATION_BINDING = "a" * 64
|
||||
PROVIDER_DESTINATION_BINDING = "b" * 64
|
||||
PROVIDER_DESTINATION_VERIFICATION_METHOD = (
|
||||
"requested_chat_id_matches_provider_chat_id"
|
||||
)
|
||||
|
||||
|
||||
def provider_destination_context(
|
||||
*,
|
||||
destination_binding: str = DESTINATION_BINDING,
|
||||
provider_destination_binding: str = PROVIDER_DESTINATION_BINDING,
|
||||
) -> dict[str, object]:
|
||||
return {
|
||||
"destination_binding": destination_binding,
|
||||
"provider_destination_binding": provider_destination_binding,
|
||||
"provider_destination_verification_method": (
|
||||
PROVIDER_DESTINATION_VERIFICATION_METHOD
|
||||
),
|
||||
"provider_destination_identity_verified": True,
|
||||
"destination_binding_verified": True,
|
||||
}
|
||||
|
||||
|
||||
def payload() -> dict:
|
||||
return {
|
||||
@@ -356,7 +378,7 @@ def test_lifecycle_endpoint_202_retry_is_db_only_and_returns_200(
|
||||
monkeypatch,
|
||||
) -> None:
|
||||
monkeypatch.setattr(settings, "AGENT99_SRE_ALERT_RELAY_TOKEN", "expected")
|
||||
destination_binding = "a" * 64
|
||||
destination_binding = DESTINATION_BINDING
|
||||
calls = {"deliver": 0, "reconcile": 0}
|
||||
|
||||
async def pending_delivery(request):
|
||||
@@ -397,6 +419,10 @@ def test_lifecycle_endpoint_202_retry_is_db_only_and_returns_200(
|
||||
"delivery_status": "sent_reused",
|
||||
"provider_message_id": "8124",
|
||||
"destination_binding": destination_binding,
|
||||
"provider_destination_binding": PROVIDER_DESTINATION_BINDING,
|
||||
"provider_destination_verification_method": (
|
||||
PROVIDER_DESTINATION_VERIFICATION_METHOD
|
||||
),
|
||||
"durable_outbound_acknowledged": True,
|
||||
"destination_binding_verified": True,
|
||||
"provider_send_performed": False,
|
||||
@@ -421,6 +447,14 @@ def test_lifecycle_endpoint_202_retry_is_db_only_and_returns_200(
|
||||
|
||||
assert first.status_code == 202
|
||||
assert first.headers["retry-after"] == "2"
|
||||
assert set(first.json()["reconcile_receipt"]) == {
|
||||
"schema_version",
|
||||
"delivery_id",
|
||||
"incident_id",
|
||||
"state_key",
|
||||
"provider_message_id",
|
||||
"destination_binding",
|
||||
}
|
||||
retry_payload = {
|
||||
**payload(),
|
||||
"reconcile_receipt": first.json()["reconcile_receipt"],
|
||||
@@ -508,12 +542,17 @@ async def test_lifecycle_durable_readback_binds_exact_identity_without_egress(
|
||||
monkeypatch,
|
||||
) -> None:
|
||||
gateway = object.__new__(gateway_service.TelegramGateway)
|
||||
destination_binding = "a" * 64
|
||||
destination_binding = DESTINATION_BINDING
|
||||
reconcile = AsyncMock(
|
||||
return_value={
|
||||
"status": "sent",
|
||||
"provider_message_id": "8124",
|
||||
"destination_binding": destination_binding,
|
||||
"provider_destination_binding": PROVIDER_DESTINATION_BINDING,
|
||||
"provider_destination_verification_method": (
|
||||
PROVIDER_DESTINATION_VERIFICATION_METHOD
|
||||
),
|
||||
"provider_destination_identity_verified": True,
|
||||
"visual_requested": True,
|
||||
}
|
||||
)
|
||||
@@ -529,6 +568,10 @@ async def test_lifecycle_durable_readback_binds_exact_identity_without_egress(
|
||||
state_key=payload()["state_key"],
|
||||
expected_provider_message_id="8124",
|
||||
expected_destination_binding=destination_binding,
|
||||
expected_provider_destination_binding=PROVIDER_DESTINATION_BINDING,
|
||||
expected_provider_destination_verification_method=(
|
||||
PROVIDER_DESTINATION_VERIFICATION_METHOD
|
||||
),
|
||||
)
|
||||
|
||||
assert result["ok"] is True
|
||||
@@ -537,6 +580,11 @@ async def test_lifecycle_durable_readback_binds_exact_identity_without_egress(
|
||||
assert result["_awooop_durable_readback_performed"] is True
|
||||
assert result["_awooop_delivery_context"] == {
|
||||
"destination_binding": destination_binding,
|
||||
"provider_destination_binding": PROVIDER_DESTINATION_BINDING,
|
||||
"provider_destination_verification_method": (
|
||||
PROVIDER_DESTINATION_VERIFICATION_METHOD
|
||||
),
|
||||
"provider_destination_identity_verified": True,
|
||||
"destination_binding_verified": True,
|
||||
"durable_exact_row_readback": True,
|
||||
}
|
||||
@@ -550,6 +598,10 @@ async def test_lifecycle_durable_readback_binds_exact_identity_without_egress(
|
||||
},
|
||||
expected_provider_message_id="8124",
|
||||
expected_destination_binding=destination_binding,
|
||||
expected_provider_destination_binding=PROVIDER_DESTINATION_BINDING,
|
||||
expected_provider_destination_verification_method=(
|
||||
PROVIDER_DESTINATION_VERIFICATION_METHOD
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -588,6 +640,10 @@ async def test_lifecycle_reconcile_destination_mismatch_never_finalizes(
|
||||
},
|
||||
expected_provider_message_id="8124",
|
||||
expected_destination_binding="b" * 64,
|
||||
expected_provider_destination_binding=PROVIDER_DESTINATION_BINDING,
|
||||
expected_provider_destination_verification_method=(
|
||||
PROVIDER_DESTINATION_VERIFICATION_METHOD
|
||||
),
|
||||
)
|
||||
|
||||
assert result["status"] == "destination_binding_mismatch"
|
||||
@@ -598,7 +654,7 @@ async def test_lifecycle_reconcile_destination_mismatch_never_finalizes(
|
||||
async def test_lifecycle_reconcile_service_never_enters_provider_sender(
|
||||
monkeypatch,
|
||||
) -> None:
|
||||
destination_binding = "a" * 64
|
||||
destination_binding = DESTINATION_BINDING
|
||||
calls: list[dict] = []
|
||||
|
||||
class Gateway:
|
||||
@@ -614,8 +670,9 @@ async def test_lifecycle_reconcile_service_never_enters_provider_sender(
|
||||
"_awooop_delivery_status": "sent_reused",
|
||||
"_awooop_provider_send_performed": False,
|
||||
"_awooop_delivery_context": {
|
||||
"destination_binding": destination_binding,
|
||||
"destination_binding_verified": True,
|
||||
**provider_destination_context(
|
||||
destination_binding=destination_binding
|
||||
),
|
||||
"durable_exact_row_readback": True,
|
||||
},
|
||||
}
|
||||
@@ -672,9 +729,7 @@ async def test_lifecycle_service_uses_durable_canonical_sender(monkeypatch) -> N
|
||||
"_awooop_outbound_mirror_acknowledged": True,
|
||||
"_awooop_delivery_status": "sent",
|
||||
"_awooop_provider_send_performed": True,
|
||||
"_awooop_delivery_context": {
|
||||
"destination_binding_verified": True,
|
||||
},
|
||||
"_awooop_delivery_context": provider_destination_context(),
|
||||
}
|
||||
|
||||
monkeypatch.setattr(
|
||||
@@ -696,6 +751,49 @@ async def test_lifecycle_service_uses_durable_canonical_sender(monkeypatch) -> N
|
||||
assert "raw_log" not in calls[0]["text"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_lifecycle_repeat_after_success_reuses_provider_pinned_receipt(
|
||||
monkeypatch,
|
||||
) -> None:
|
||||
calls = 0
|
||||
|
||||
class Gateway:
|
||||
async def send_agent99_lifecycle_receipt(self, **_kwargs):
|
||||
nonlocal calls
|
||||
calls += 1
|
||||
return {
|
||||
"ok": True,
|
||||
"result": {"message_id": 9123},
|
||||
"_awooop_outbound_mirror_acknowledged": True,
|
||||
"_awooop_delivery_status": (
|
||||
"sent" if calls == 1 else "sent_reused"
|
||||
),
|
||||
"_awooop_provider_send_performed": calls == 1,
|
||||
"_awooop_delivery_context": provider_destination_context(),
|
||||
}
|
||||
|
||||
monkeypatch.setattr(
|
||||
lifecycle_service,
|
||||
"get_telegram_gateway",
|
||||
lambda: Gateway(),
|
||||
)
|
||||
request = Agent99TelegramLifecycleRequest.model_validate(payload())
|
||||
|
||||
first = await lifecycle_service.deliver_agent99_telegram_lifecycle(request)
|
||||
repeated = await lifecycle_service.deliver_agent99_telegram_lifecycle(
|
||||
request
|
||||
)
|
||||
|
||||
assert first["ok"] is True
|
||||
assert first["provider_send_performed"] is True
|
||||
assert repeated["ok"] is True
|
||||
assert repeated["delivery_status"] == "sent_reused"
|
||||
assert repeated["provider_send_performed"] is False
|
||||
assert repeated["provider_destination_binding"] == (
|
||||
PROVIDER_DESTINATION_BINDING
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_lifecycle_reconciles_pending_send_without_duplicate_provider_message(
|
||||
monkeypatch,
|
||||
@@ -710,10 +808,7 @@ async def test_lifecycle_reconciles_pending_send_without_duplicate_provider_mess
|
||||
"_awooop_delivery_status": "pending_unknown",
|
||||
"_awooop_provider_send_performed": True,
|
||||
"_awooop_visual_delivery_verified": True,
|
||||
"_awooop_delivery_context": {
|
||||
"destination_binding_verified": True,
|
||||
"destination_binding": "a" * 64,
|
||||
},
|
||||
"_awooop_delivery_context": provider_destination_context(),
|
||||
}
|
||||
reconciled_response = {
|
||||
"ok": True,
|
||||
@@ -722,6 +817,7 @@ async def test_lifecycle_reconciles_pending_send_without_duplicate_provider_mess
|
||||
"_awooop_delivery_status": "sent_reused",
|
||||
"_awooop_provider_send_performed": False,
|
||||
"_awooop_visual_delivery_verified": True,
|
||||
"_awooop_delivery_context": provider_destination_context(),
|
||||
}
|
||||
|
||||
class Gateway:
|
||||
@@ -765,7 +861,13 @@ async def test_lifecycle_reconciles_pending_send_without_duplicate_provider_mess
|
||||
"state_key": payload()["state_key"],
|
||||
"project_id": "awoooi",
|
||||
"expected_provider_message_id": "9126",
|
||||
"expected_destination_binding": "a" * 64,
|
||||
"expected_destination_binding": DESTINATION_BINDING,
|
||||
"expected_provider_destination_binding": (
|
||||
PROVIDER_DESTINATION_BINDING
|
||||
),
|
||||
"expected_provider_destination_verification_method": (
|
||||
PROVIDER_DESTINATION_VERIFICATION_METHOD
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@@ -787,10 +889,7 @@ async def test_lifecycle_reconciles_provider_sent_pending_ack_without_resend(
|
||||
"provider_sent_durable_receipt_pending"
|
||||
),
|
||||
"_awooop_provider_send_performed": True,
|
||||
"_awooop_delivery_context": {
|
||||
"destination_binding_verified": True,
|
||||
"destination_binding": "a" * 64,
|
||||
},
|
||||
"_awooop_delivery_context": provider_destination_context(),
|
||||
}
|
||||
|
||||
async def reconcile_agent99_lifecycle_receipt(self, **kwargs):
|
||||
@@ -801,9 +900,7 @@ async def test_lifecycle_reconciles_provider_sent_pending_ack_without_resend(
|
||||
"_awooop_outbound_mirror_acknowledged": True,
|
||||
"_awooop_delivery_status": "sent_reused",
|
||||
"_awooop_provider_send_performed": False,
|
||||
"_awooop_delivery_context": {
|
||||
"destination_binding_verified": True,
|
||||
},
|
||||
"_awooop_delivery_context": provider_destination_context(),
|
||||
}
|
||||
|
||||
async def no_sleep(_delay: float) -> None:
|
||||
@@ -827,7 +924,20 @@ async def test_lifecycle_reconciles_provider_sent_pending_ack_without_resend(
|
||||
assert len(send_calls) == 1
|
||||
assert len(reconcile_calls) == 1
|
||||
assert reconcile_calls[0]["expected_provider_message_id"] == "9129"
|
||||
assert reconcile_calls[0]["expected_destination_binding"] == "a" * 64
|
||||
assert (
|
||||
reconcile_calls[0]["expected_destination_binding"]
|
||||
== DESTINATION_BINDING
|
||||
)
|
||||
assert (
|
||||
reconcile_calls[0]["expected_provider_destination_binding"]
|
||||
== PROVIDER_DESTINATION_BINDING
|
||||
)
|
||||
assert (
|
||||
reconcile_calls[0][
|
||||
"expected_provider_destination_verification_method"
|
||||
]
|
||||
== PROVIDER_DESTINATION_VERIFICATION_METHOD
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -856,7 +966,7 @@ async def test_lifecycle_follower_only_reads_inflight_durable_delivery(
|
||||
"_awooop_delivery_status": "sent_reused",
|
||||
"_awooop_provider_send_performed": False,
|
||||
"_awooop_delivery_context": {
|
||||
"destination_binding_verified": True,
|
||||
**provider_destination_context(),
|
||||
"durable_exact_row_readback": True,
|
||||
},
|
||||
}
|
||||
@@ -882,6 +992,15 @@ async def test_lifecycle_follower_only_reads_inflight_durable_delivery(
|
||||
assert len(reconcile_calls) == 1
|
||||
assert reconcile_calls[0]["expected_provider_message_id"] is None
|
||||
assert reconcile_calls[0]["expected_destination_binding"] is None
|
||||
assert (
|
||||
reconcile_calls[0]["expected_provider_destination_binding"] is None
|
||||
)
|
||||
assert (
|
||||
reconcile_calls[0][
|
||||
"expected_provider_destination_verification_method"
|
||||
]
|
||||
is None
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -901,10 +1020,7 @@ async def test_lifecycle_reconciliation_fails_closed_after_bounded_readbacks(
|
||||
"_awooop_outbound_mirror_acknowledged": False,
|
||||
"_awooop_delivery_status": "pending_unknown",
|
||||
"_awooop_provider_send_performed": True,
|
||||
"_awooop_delivery_context": {
|
||||
"destination_binding_verified": True,
|
||||
"destination_binding": "a" * 64,
|
||||
},
|
||||
"_awooop_delivery_context": provider_destination_context(),
|
||||
}
|
||||
|
||||
async def reconcile_agent99_lifecycle_receipt(self, **kwargs):
|
||||
@@ -946,7 +1062,17 @@ async def test_lifecycle_reconciliation_fails_closed_after_bounded_readbacks(
|
||||
for call in reconcile_calls
|
||||
)
|
||||
assert all(
|
||||
call["expected_destination_binding"] == "a" * 64
|
||||
call["expected_destination_binding"] == DESTINATION_BINDING
|
||||
for call in reconcile_calls
|
||||
)
|
||||
assert all(
|
||||
call["expected_provider_destination_binding"]
|
||||
== PROVIDER_DESTINATION_BINDING
|
||||
for call in reconcile_calls
|
||||
)
|
||||
assert all(
|
||||
call["expected_provider_destination_verification_method"]
|
||||
== PROVIDER_DESTINATION_VERIFICATION_METHOD
|
||||
for call in reconcile_calls
|
||||
)
|
||||
|
||||
@@ -965,9 +1091,7 @@ async def test_visual_lifecycle_uses_one_durable_send_photo_receipt(monkeypatch)
|
||||
"_awooop_delivery_status": "sent",
|
||||
"_awooop_provider_send_performed": True,
|
||||
"_awooop_visual_delivery_verified": True,
|
||||
"_awooop_delivery_context": {
|
||||
"destination_binding_verified": True,
|
||||
},
|
||||
"_awooop_delivery_context": provider_destination_context(),
|
||||
}
|
||||
|
||||
monkeypatch.setattr(
|
||||
@@ -1016,7 +1140,7 @@ async def test_reused_legacy_text_receipt_does_not_claim_visual_delivery(
|
||||
|
||||
receipt = await lifecycle_service.deliver_agent99_telegram_lifecycle(request)
|
||||
|
||||
assert receipt["ok"] is True
|
||||
assert receipt["ok"] is False
|
||||
assert receipt["visual_requested"] is True
|
||||
assert receipt["visual_sent"] is False
|
||||
assert receipt["visual_delivery_status"] == "sent_reused"
|
||||
|
||||
Reference in New Issue
Block a user