Commit 00d388ec authored by Dimitrios Gogos's avatar Dimitrios Gogos
Browse files

fix: ignore terminal states when checking app instance existence for registration

parent 425b0e97
Loading
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -5,9 +5,11 @@ from sqlalchemy.ext.asyncio import AsyncSession

from open_exposure_gateway.adapters.database.mappers import AppInstanceMapper
from open_exposure_gateway.adapters.database.sql import AppInstanceRow
from open_exposure_gateway.domain.models import AppInstance
from open_exposure_gateway.domain.models import AppInstance, AppInstanceState
from open_exposure_gateway.ports.database.instances import AppInstanceRepository

_TERMINAL_STATES = (AppInstanceState.FAILED, AppInstanceState.TERMINATED)


class SqlAppInstanceRepository(AppInstanceRepository):
    def __init__(self, session: AsyncSession) -> None:
@@ -25,7 +27,8 @@ class SqlAppInstanceRepository(AppInstanceRepository):

    async def exists_for_app_registration(self, app_registration_id: UUID) -> bool:
        stmt = select(AppInstanceRow.app_instance_id).where(
            AppInstanceRow.app_registration_id == app_registration_id
            AppInstanceRow.app_registration_id == app_registration_id,
            AppInstanceRow.state.notin_(_TERMINAL_STATES),
        )
        row = await self._session.scalar(stmt.limit(1))
        return row is not None
+20 −0
Original line number Diff line number Diff line
@@ -197,6 +197,26 @@ async def test_app_instance_repo_persists_and_loads(db_session: AsyncSession) ->
    assert reloaded.edge_cloud_zone_id == instance.edge_cloud_zone_id


@pytest.mark.parametrize("state", [AppInstanceState.FAILED, AppInstanceState.TERMINATED])
async def test_app_instance_repo_exists_for_app_registration_ignores_terminal_states(
    db_session: AsyncSession, state: AppInstanceState
) -> None:
    registration = await SqlAppRegistrationRepository(db_session).save(_app_registration())
    operation = await SqlOperationRepository(db_session).save(
        _operation(app_registration_id=registration.app_registration_id)
    )
    repo = SqlAppInstanceRepository(db_session)
    instance = _app_instance(
        operation_id=operation.operation_id,
        app_registration_id=registration.app_registration_id,
    )
    instance.state = state

    await repo.save(instance)

    assert await repo.exists_for_app_registration(registration.app_registration_id) is False


async def test_callback_registration_repo_persists_and_loads(db_session: AsyncSession) -> None:
    operation = await SqlOperationRepository(db_session).save(_operation())
    repo = SqlCallbackRegistrationRepository(db_session)
+6 −1
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ from open_exposure_gateway.domain.edge_application_management import (
)
from open_exposure_gateway.domain.models import (
    AppInstance,
    AppInstanceState,
    AppRegistration,
    CallbackDelivery,
    CallbackRegistration,
@@ -356,7 +357,11 @@ class FakeAppInstanceRepository(AppInstanceRepository):
        return None

    async def exists_for_app_registration(self, app_registration_id: UUID) -> bool:
        return any(row.app_registration_id == app_registration_id for row in self.rows.values())
        terminal_states = (AppInstanceState.FAILED, AppInstanceState.TERMINATED)
        return any(
            row.app_registration_id == app_registration_id and row.state not in terminal_states
            for row in self.rows.values()
        )

    async def save(self, app_instance: AppInstance) -> AppInstance:
        stored = app_instance.model_copy(deep=True)
+36 −0
Original line number Diff line number Diff line
@@ -548,6 +548,42 @@ class TestDeleteApp:
        srm_client.delete_app.assert_not_called()
        assert await app_registration_repo.get_by_app_id(APP_ID) is not None

    @pytest.mark.parametrize("state", [AppInstanceState.FAILED, AppInstanceState.TERMINATED])
    async def test_allows_delete_when_app_only_has_instances_in_a_terminal_state(
        self,
        state: AppInstanceState,
        service: EdgeApplicationManagementService,
        srm_client: AsyncMock,
        app_registration_repo: FakeAppRegistrationRepository,
        app_instance_repo: FakeAppInstanceRepository,
    ) -> None:
        app_registration_id = uuid4()
        await app_registration_repo.save(
            AppRegistration(
                app_registration_id=app_registration_id,
                app_id=APP_ID,
                tenant_id="tenant-1",
                name="myvideoapp",
                version="1.0.0",
                package_type=PackageType.HELM,
                status=AppRegistrationStatus.REGISTERED,
            )
        )
        await app_instance_repo.save(
            AppInstance(
                app_instance_id=INSTANCE_ID,
                operation_id=uuid4(),
                app_registration_id=app_registration_id,
                edge_cloud_zone_id=ZONE_ID,
                state=state,
            )
        )

        await service.delete_app(app_id=APP_ID, app_provider_id="VideoAppsCo")

        srm_client.delete_app.assert_called_once()
        assert await app_registration_repo.get_by_app_id(APP_ID) is None


class TestCreateAppInstance:
    def _make_request(self) -> CreateAppInstanceRequest: