Commit ef3b6502 authored by George Papathanail's avatar George Papathanail
Browse files

fix: align EAM SubscriptionConfig with the CAMARA spec

parent 42aa2049
Loading
Loading
Loading
Loading
+12 −5
Changes for src/open_exposure_gateway/api/camara/edge_application_management/v0_1_0_alpha_1/schemas.py: 12 added lines, 5 removed lines.
Original line number Diff line number Diff line
import re
from datetime import datetime
from enum import StrEnum
from typing import Annotated, Any, Literal, Optional
from uuid import UUID

from pydantic import BaseModel, BeforeValidator, ConfigDict, Field, field_validator
from pydantic import (
    AwareDatetime,
    BaseModel,
    BeforeValidator,
    ConfigDict,
    Field,
    field_validator,
)

# Pattern shared by AppId, AppInstanceId, AppDeploymentId, EdgeCloudZoneId and
# KubernetesClusterRef in the spec: lowercase, hyphenated, version 1-5, RFC variant.
@@ -276,9 +282,10 @@ class AppInstanceInfo(BaseModel):
class SubscriptionConfig(BaseModel):
    model_config = ConfigDict(extra="forbid")

    subscriptionDetail: Optional[dict[str, Any]] = None
    subscriptionExpireTime: Optional[datetime] = None
    subscriptionMaxEvents: Optional[int] = None
    subscriptionDetail: dict[str, Any]
    # The spec requires a time zone, so naive timestamps are rejected.
    subscriptionExpireTime: Optional[AwareDatetime] = None
    subscriptionMaxEvents: Optional[int] = Field(default=None, ge=1, le=2147483647)
    initialEvent: Optional[bool] = None


+53 −0
Changes for tests/unit/test_eam_flows.py: 53 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ real hole in the flow, not a broken test.
"""

from collections.abc import Callable
from datetime import datetime, timezone
from typing import Any
from unittest.mock import AsyncMock
from uuid import UUID, uuid4
@@ -174,6 +175,58 @@ class TestCreateAppInstanceFlow:
        (callback,) = list(callback_registration_repo.rows.values())
        assert callback.sink == "https://client.example.com/callback"

    def test_valid_subscription_config_is_accepted_and_expiry_stays_tz_aware(
        self,
        api_client: TestClient,
        live_srm: FakeSRMClient,
        callback_registration_repo: FakeCallbackRegistrationRepository,
    ) -> None:
        body = {
            **CREATE_INSTANCE_BODY,
            "subscriptionRequest": {
                "sink": "https://client.example.com/callback",
                "config": {
                    "subscriptionDetail": {},
                    "subscriptionExpireTime": "2030-01-01T00:00:00Z",
                },
            },
        }
        response = api_client.post(f"{EAM_BASE}/appinstances", json=body)
        assert response.status_code == 202

        (callback,) = list(callback_registration_repo.rows.values())
        assert callback.expires_at == datetime(2030, 1, 1, tzinfo=timezone.utc)
        assert callback.expires_at.tzinfo is not None

    @pytest.mark.parametrize(
        "config",
        [
            pytest.param({"subscriptionExpireTime": "2030-01-01T00:00:00Z"}, id="no-detail"),
            pytest.param(
                {"subscriptionDetail": {}, "subscriptionExpireTime": "2030-01-01T00:00:00"},
                id="naive-expire-time",
            ),
            pytest.param({"subscriptionDetail": {}, "subscriptionMaxEvents": 0}, id="max-events-0"),
            pytest.param(
                {"subscriptionDetail": {}, "subscriptionMaxEvents": 2147483648},
                id="max-events-over-int32",
            ),
        ],
    )
    def test_invalid_subscription_config_is_rejected(
        self, api_client: TestClient, config: dict[str, Any]
    ) -> None:
        body = {
            **CREATE_INSTANCE_BODY,
            "subscriptionRequest": {
                "sink": "https://client.example.com/callback",
                "config": config,
            },
        }
        response = api_client.post(f"{EAM_BASE}/appinstances", json=body)
        assert response.status_code == 400
        assert response.json()["code"] == "INVALID_ARGUMENT"

    def test_delivers_callback_after_srm_completes(
        self,
        api_client: TestClient,
+1 −1
Changes for tests/unit/test_eam_service.py: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -1001,7 +1001,7 @@ class TestCreateAppInstance:
                types=[
                    "org.camaraproject.edge-application-management.v0.app-instance-status-change"
                ],
                config=SubscriptionConfig(subscriptionExpireTime=expires_at)
                config=SubscriptionConfig(subscriptionDetail={}, subscriptionExpireTime=expires_at)
                if expires_at
                else None,
            ),