Loading src/open_exposure_gateway/api/camara/edge_application_management/v0_1_0_alpha_1/schemas.py +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. Loading Loading @@ -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 Loading tests/unit/test_eam_flows.py +53 −0 Changes for tests/unit/test_eam_flows.py: 53 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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, Loading tests/unit/test_eam_service.py +1 −1 Changes for tests/unit/test_eam_service.py: 1 added line, 1 removed line. Original line number Diff line number Diff line Loading @@ -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, ), Loading Loading
src/open_exposure_gateway/api/camara/edge_application_management/v0_1_0_alpha_1/schemas.py +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. Loading Loading @@ -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 Loading
tests/unit/test_eam_flows.py +53 −0 Changes for tests/unit/test_eam_flows.py: 53 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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, Loading
tests/unit/test_eam_service.py +1 −1 Changes for tests/unit/test_eam_service.py: 1 added line, 1 removed line. Original line number Diff line number Diff line Loading @@ -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, ), Loading