Commit b9581603 authored by Paris Stentoumis's avatar Paris Stentoumis
Browse files

Merge branch 'fix/retire-spec-with-orders' into 'refactor/tf-sdk-wiring'

fix(catalog): retire a specification that only orders reference

See merge request !40
parents 639a462f 942ab240
Loading
Loading
Loading
Loading
Loading
+12 −0
Changes for src/srm/adapters/database/repos/catalog.py: 12 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ from srm.adapters.database.sql import (
    ServiceCapabilityRequirementRow,
    ServiceDeploymentUnitRow,
    ServiceInstanceRow,
    ServiceOrderRow,
    ServiceSpecificationRow,
)
from srm.adapters.errors import DuplicateServiceSpecificationError, ServiceSpecificationInUseError
@@ -119,6 +120,17 @@ class SqlServiceSpecificationRepository(ServiceSpecificationRepository):
            .limit(1)
        )
        if any_instance_id is None:
            # An instance a partner hosts leaves no instance row here, only its order, and that
            # order still points at the specification: keep it as retired history instead.
            any_order_id = await self._session.scalar(
                select(ServiceOrderRow.id)
                .where(ServiceOrderRow.service_specification_id == id)
                .limit(1)
            )
            if any_order_id is not None:
                row.state = ServiceSpecificationState.RETIRED
                await self._session.flush()
                return True
            await self._session.execute(
                delete(ServiceCapabilityRequirementRow).where(
                    ServiceCapabilityRequirementRow.service_specification_id == id
+25 −0
Changes for tests/integration/test_postgres.py: 25 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -344,6 +344,31 @@ async def test_service_specification_delete_retires_when_only_terminal_instances
    assert reloaded.state == ServiceSpecificationState.RETIRED


async def test_service_specification_delete_retires_when_only_an_order_references_it(
    db_session: AsyncSession,
) -> None:
    # a partner-hosted deploy leaves its order but no instance row
    specification_repo = SqlServiceSpecificationRepository(db_session)
    zone_repo = SqlZoneRepository(db_session)
    order_repo = SqlServiceOrderRepository(db_session)
    specification = await specification_repo.create(_service_specification())
    zone = await zone_repo.create(_zone())
    await order_repo.create(
        _service_order(
            app_provider_id=specification.app_provider_id,
            specification_id=specification.id,
            zone_id=zone.id,
        )
    )

    deleted = await specification_repo.delete_or_retire(specification.id)

    assert deleted is True
    reloaded = await specification_repo.get_by_id(specification.id)
    assert reloaded is not None
    assert reloaded.state == ServiceSpecificationState.RETIRED


async def test_zone_repo_duplicate_id_is_conflict_without_overwrite(
    db_engine: AsyncEngine,
) -> None:
+17 −1
Changes for tests/unit/test_service_specification_repo.py: 17 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -221,11 +221,27 @@ async def test_delete_or_retire_hard_deletes_when_no_instances_exist() -> None:
    result = await repo.delete_or_retire(row.id)

    assert result is True
    session.scalar.assert_awaited_once()
    assert session.scalar.await_count == 2  # no instance, no order
    assert session.execute.await_count == 3
    session.flush.assert_awaited_once()


async def test_delete_or_retire_retires_when_only_orders_reference_it() -> None:
    # a partner-hosted deploy: SRM kept its order but never an instance row
    row = _service_specification_row()
    session = AsyncMock(spec=AsyncSession)
    session.get.return_value = row
    session.scalar.side_effect = [None, uuid4()]
    repo = SqlServiceSpecificationRepository(session)

    result = await repo.delete_or_retire(row.id)

    assert result is True
    assert row.state == ServiceSpecificationState.RETIRED
    session.execute.assert_not_awaited()
    session.flush.assert_awaited_once()


async def test_delete_or_retire_sets_state_to_retired_when_only_terminal_instances_exist() -> None:
    row = _service_specification_row()
    session = AsyncMock(spec=AsyncSession)