Commit 9e530fc6 authored by Dimitrios Gogos's avatar Dimitrios Gogos
Browse files

fix: rename terminal states to teardown states for clarity in traffic influence service

parent f470b4ef
Loading
Loading
Loading
Loading
Loading
+6 −8
Original line number Diff line number Diff line
@@ -68,14 +68,12 @@ _OPERATION_COMPLETION_STATUS_MAP: dict[str, OperationStatus] = {
# CallbackRegistration.api_family discriminator for Traffic Influence's subscriptionRequest.
_API_FAMILY = "traffic_influence"

# Blocks re-deleting an already-terminal resource (same purpose as QoD's _TERMINAL_STATES
# guard on delete_session). GET intentionally does NOT use this -- TI's own CAMARA spec
# documents 'deletion in progress'/'deleted' as visible GET states, unlike QoD.
_TERMINAL_STATES = frozenset(
# Teardown has been requested or has completed: the resource is on its way out, so DELETE
# is a 404 and no late activate/status signal may revive it.
_TEARDOWN_STATES = frozenset(
    {
        RecordTrafficInfluenceState.DELETION_IN_PROGRESS,
        RecordTrafficInfluenceState.DELETED,
        RecordTrafficInfluenceState.ERROR,
    }
)

@@ -398,7 +396,7 @@ class TrafficInfluenceService:
                    operation_id=event.operation_id,
                )
                return
            if traffic_influence.state in _TERMINAL_STATES:
            if traffic_influence.state in _TEARDOWN_STATES:
                logger.info(
                    "activate_completion_ignored_for_terminal_traffic_influence",
                    operation_id=event.operation_id,
@@ -477,7 +475,7 @@ class TrafficInfluenceService:
            )
            return

        if traffic_influence.state in _TERMINAL_STATES:
        if traffic_influence.state in _TEARDOWN_STATES:
            logger.info(
                "status_change_ignored_for_terminal_traffic_influence",
                operation_id=event.operation_id,
@@ -615,7 +613,7 @@ class TrafficInfluenceService:
        traffic_influence = await self._traffic_influence_repo.get_by_id(
            self._parse_traffic_influence_id(traffic_influence_id)
        )
        if traffic_influence is None or traffic_influence.state in _TERMINAL_STATES:
        if traffic_influence is None or traffic_influence.state in _TEARDOWN_STATES:
            raise NotFoundException(message=f"Traffic influence {traffic_influence_id} not found")

        operation_id, correlation_id, requested_at = self._new_operation_metadata(x_correlator)
+55 −0
Original line number Diff line number Diff line
@@ -660,6 +660,61 @@ class TestTrafficInfluenceDeleteFlow:
        deactivates = [p for s, p in fake_bus.published if s == Subject.TASK_DEACTIVATE]
        assert len(deactivates) == 1

    async def test_errored_policy_can_still_be_deleted(
        self,
        api_client: TestClient,
        live_ti: None,
        fake_bus: FakeDataBus,
        traffic_influence_repo: FakeTrafficInfluenceRepository,
    ) -> None:
        """'error' means the policy exists but something went wrong -- not that it is
        gone. Treating it as terminal 404s the DELETE, which means the deactivate is
        never published and the policy leaks at SRM with no API route to remove it.
        CAMARA puts no state precondition on DELETE (only on PATCH)."""
        response = api_client.post(f"{TI_BASE}/traffic-influences", json=TI_BODY)
        traffic_influence_id = UUID(response.json()["trafficInfluenceID"])
        operation_id = traffic_influence_repo.rows[traffic_influence_id].operation_id

        await fake_bus.publish(
            str(Subject.OPERATION_STATUS),
            operation_status_payload(str(operation_id), metadata={"qos_status": "UNAVAILABLE"}),
        )
        assert (
            traffic_influence_repo.rows[traffic_influence_id].state == TrafficInfluenceState.ERROR
        )

        delete_response = api_client.delete(f"{TI_BASE}/traffic-influences/{traffic_influence_id}")

        assert delete_response.status_code == 202
        assert len([p for s, p in fake_bus.published if s == Subject.TASK_DEACTIVATE]) == 1
        assert (
            traffic_influence_repo.rows[traffic_influence_id].state
            == TrafficInfluenceState.DELETION_IN_PROGRESS
        )

    async def test_errored_policy_recovers_when_srm_reports_it_live_again(
        self,
        api_client: TestClient,
        live_ti: None,
        fake_bus: FakeDataBus,
        traffic_influence_repo: FakeTrafficInfluenceRepository,
    ) -> None:
        """SRM is the authority on whether the capability is live, so a transient
        UNAVAILABLE must not permanently pin the policy to 'error'."""
        response = api_client.post(f"{TI_BASE}/traffic-influences", json=TI_BODY)
        traffic_influence_id = UUID(response.json()["trafficInfluenceID"])
        operation_id = traffic_influence_repo.rows[traffic_influence_id].operation_id

        for qos_status in ("UNAVAILABLE", "AVAILABLE"):
            await fake_bus.publish(
                str(Subject.OPERATION_STATUS),
                operation_status_payload(str(operation_id), metadata={"qos_status": qos_status}),
            )

        assert (
            traffic_influence_repo.rows[traffic_influence_id].state == TrafficInfluenceState.ACTIVE
        )

    async def test_delete_publishes_deactivate_command_once_available(
        self,
        api_client: TestClient,