Loading ansible/tests/r2_e2e/provider_observer.py +9 −0 Changes for ansible/tests/r2_e2e/provider_observer.py: 9 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -29,3 +29,12 @@ def read_subscriptions(qod, http): if response.status_code == 200: subscriptions.append(response.json()) return subscriptions def ti_collection(response): """This EURECOM endpoint serializes an existing empty AF collection as null.""" assert response.status_code == 200, 'TI collection observation failed' rows = response.json() assert rows is None or isinstance(rows, list), 'Unexpected TI collection shape' assert rows is None or all(isinstance(row, dict) for row in rows), 'Invalid TI collection entry' return rows or [] ansible/tests/r2_e2e/test_deploy_to_ti.py +12 −5 Changes for ansible/tests/r2_e2e/test_deploy_to_ti.py: 12 added lines, 5 removed lines. Original line number Diff line number Diff line Loading @@ -11,7 +11,7 @@ from uuid import uuid4 import pytest from checkpoints import EURECOM_QOS_PROFILES from provider_observer import read_subscriptions from provider_observer import read_subscriptions, ti_collection from qod_scenario import QodScenario, preflight as preflight_network from conftest import EAM_SCOPES, INSTANCE_SCOPES, KeycloakAdmin, Token, http from sdk_scenario import Scenario, check_endpoint_metadata, check_reachability, check_requested_resources, poll, preflight Loading Loading @@ -256,7 +256,7 @@ def test_device_ti_uses_the_deployed_application_endpoint(progressive_scenario,p op,profile,token=progressive_scenario.operator,progressive_scenario.profile,progressive_scenario.token qod=profile['qod'] base=qod['base_url']+'/3gpp-traffic-influence/v1/af/subscriptions' identity=None identity,backend_resource=None,None try: response=http('POST',op.oeg_url+'/traffic-influence/v0.10/traffic-influence-devices',token,json={ 'apiConsumerId':progressive_scenario.name,'appId':progressive_scenario.app_id, Loading @@ -274,9 +274,11 @@ def test_device_ti_uses_the_deployed_application_endpoint(progressive_scenario,p poll(active,profile['timeout_seconds'],'OEG device TI active') response=http('GET',base) assert response.status_code==200 policies=[row for row in response.json() if row.get('afAppId')==progressive_scenario.app_id] policies=[row for row in ti_collection(response) if row.get('afAppId')==progressive_scenario.app_id] assert len(policies)==1 policy=policies[0] backend_resource=base+'/'+policy['self'].rstrip('/').rsplit('/',1)[-1] assert http('GET',backend_resource).status_code==200, 'Observed TI identity is not readable' device_ip=qod['session_request']['device']['ipv4Address']['publicAddress'] assert policy['ipv4Addr']==device_ip and not policy.get('anyUeInd') assert policy['notificationDestination']==qod['notification_url'] Loading @@ -296,7 +298,12 @@ def test_device_ti_uses_the_deployed_application_endpoint(progressive_scenario,p public=http('GET',op.oeg_url+TI+'/'+identity,token) assert public.status_code in (200,404) backend=http('GET',base) assert backend.status_code==200 rows=ti_collection(backend) if backend_resource: individual=http('GET',backend_resource) assert individual.status_code in (200,404) if individual.status_code!=404: return False return (public.status_code==404 or public.json()['state']=='deleted') and not any( row.get('afAppId')==progressive_scenario.app_id for row in backend.json()) row.get('afAppId')==progressive_scenario.app_id for row in rows) poll(gone,profile['timeout_seconds'],'public TI and provider policy removal') ansible/tests/unit/test_eurecom_lab.py +12 −1 Changes for ansible/tests/unit/test_eurecom_lab.py: 12 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -12,7 +12,7 @@ ROOT=Path(__file__).resolve().parents[3] sys.path.insert(0,str(ROOT/'ci')) sys.path.insert(0,str(ROOT/'ansible/tests/r2_e2e')) from eurecom_lab import SERVICES, composition from provider_observer import read_subscriptions from provider_observer import read_subscriptions, ti_collection import provider_observer Loading Loading @@ -69,3 +69,14 @@ def test_created_resource_without_valid_identity_cannot_look_like_an_empty_colle with pytest.raises(AssertionError): read_subscriptions({'base_url':'http://nef','subscriptions_url':'http://nef'+prefix, 'observer':{'container':'owned-ingress'}},None) @pytest.mark.parametrize('body',[None,[],[{'self':'owned-policy'}]]) def test_ti_collection_preserves_real_provider_empty_and_nonempty_shapes(body): assert ti_collection(SimpleNamespace(status_code=200,json=lambda:body)) == (body or []) @pytest.mark.parametrize('status,body',[(404,None),(200,{}),(200,[None])]) def test_ti_collection_error_or_malformed_body_cannot_prove_absence(status,body): with pytest.raises(AssertionError): ti_collection(SimpleNamespace(status_code=status,json=lambda:body)) ci/lab/README.md +3 −1 Changes for ci/lab/README.md: 3 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -164,7 +164,9 @@ transparent ingress log records only method, URI, status and upstream Location; the observer follows actual successful creation identities and verifies their individual deletion. Missing identities fail observation. No API payloads or authorization headers are recorded. An initial TI collection GET may return404 before its AF exists. The lab callback route returns204, providing a reachable before its AF exists, and an existing empty TI collection serializes as JSON null. Cleanup requires 404 for the observed individual TI resource as well as public removal; accepting an empty list alone cannot prove deletion. The lab callback route returns 204, providing a reachable technical destination without implementing notifications in SRM or TF-SDK. This compatibility tier qualifies API translation, selected provider behavior Loading Loading
ansible/tests/r2_e2e/provider_observer.py +9 −0 Changes for ansible/tests/r2_e2e/provider_observer.py: 9 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -29,3 +29,12 @@ def read_subscriptions(qod, http): if response.status_code == 200: subscriptions.append(response.json()) return subscriptions def ti_collection(response): """This EURECOM endpoint serializes an existing empty AF collection as null.""" assert response.status_code == 200, 'TI collection observation failed' rows = response.json() assert rows is None or isinstance(rows, list), 'Unexpected TI collection shape' assert rows is None or all(isinstance(row, dict) for row in rows), 'Invalid TI collection entry' return rows or []
ansible/tests/r2_e2e/test_deploy_to_ti.py +12 −5 Changes for ansible/tests/r2_e2e/test_deploy_to_ti.py: 12 added lines, 5 removed lines. Original line number Diff line number Diff line Loading @@ -11,7 +11,7 @@ from uuid import uuid4 import pytest from checkpoints import EURECOM_QOS_PROFILES from provider_observer import read_subscriptions from provider_observer import read_subscriptions, ti_collection from qod_scenario import QodScenario, preflight as preflight_network from conftest import EAM_SCOPES, INSTANCE_SCOPES, KeycloakAdmin, Token, http from sdk_scenario import Scenario, check_endpoint_metadata, check_reachability, check_requested_resources, poll, preflight Loading Loading @@ -256,7 +256,7 @@ def test_device_ti_uses_the_deployed_application_endpoint(progressive_scenario,p op,profile,token=progressive_scenario.operator,progressive_scenario.profile,progressive_scenario.token qod=profile['qod'] base=qod['base_url']+'/3gpp-traffic-influence/v1/af/subscriptions' identity=None identity,backend_resource=None,None try: response=http('POST',op.oeg_url+'/traffic-influence/v0.10/traffic-influence-devices',token,json={ 'apiConsumerId':progressive_scenario.name,'appId':progressive_scenario.app_id, Loading @@ -274,9 +274,11 @@ def test_device_ti_uses_the_deployed_application_endpoint(progressive_scenario,p poll(active,profile['timeout_seconds'],'OEG device TI active') response=http('GET',base) assert response.status_code==200 policies=[row for row in response.json() if row.get('afAppId')==progressive_scenario.app_id] policies=[row for row in ti_collection(response) if row.get('afAppId')==progressive_scenario.app_id] assert len(policies)==1 policy=policies[0] backend_resource=base+'/'+policy['self'].rstrip('/').rsplit('/',1)[-1] assert http('GET',backend_resource).status_code==200, 'Observed TI identity is not readable' device_ip=qod['session_request']['device']['ipv4Address']['publicAddress'] assert policy['ipv4Addr']==device_ip and not policy.get('anyUeInd') assert policy['notificationDestination']==qod['notification_url'] Loading @@ -296,7 +298,12 @@ def test_device_ti_uses_the_deployed_application_endpoint(progressive_scenario,p public=http('GET',op.oeg_url+TI+'/'+identity,token) assert public.status_code in (200,404) backend=http('GET',base) assert backend.status_code==200 rows=ti_collection(backend) if backend_resource: individual=http('GET',backend_resource) assert individual.status_code in (200,404) if individual.status_code!=404: return False return (public.status_code==404 or public.json()['state']=='deleted') and not any( row.get('afAppId')==progressive_scenario.app_id for row in backend.json()) row.get('afAppId')==progressive_scenario.app_id for row in rows) poll(gone,profile['timeout_seconds'],'public TI and provider policy removal')
ansible/tests/unit/test_eurecom_lab.py +12 −1 Changes for ansible/tests/unit/test_eurecom_lab.py: 12 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -12,7 +12,7 @@ ROOT=Path(__file__).resolve().parents[3] sys.path.insert(0,str(ROOT/'ci')) sys.path.insert(0,str(ROOT/'ansible/tests/r2_e2e')) from eurecom_lab import SERVICES, composition from provider_observer import read_subscriptions from provider_observer import read_subscriptions, ti_collection import provider_observer Loading Loading @@ -69,3 +69,14 @@ def test_created_resource_without_valid_identity_cannot_look_like_an_empty_colle with pytest.raises(AssertionError): read_subscriptions({'base_url':'http://nef','subscriptions_url':'http://nef'+prefix, 'observer':{'container':'owned-ingress'}},None) @pytest.mark.parametrize('body',[None,[],[{'self':'owned-policy'}]]) def test_ti_collection_preserves_real_provider_empty_and_nonempty_shapes(body): assert ti_collection(SimpleNamespace(status_code=200,json=lambda:body)) == (body or []) @pytest.mark.parametrize('status,body',[(404,None),(200,{}),(200,[None])]) def test_ti_collection_error_or_malformed_body_cannot_prove_absence(status,body): with pytest.raises(AssertionError): ti_collection(SimpleNamespace(status_code=status,json=lambda:body))
ci/lab/README.md +3 −1 Changes for ci/lab/README.md: 3 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -164,7 +164,9 @@ transparent ingress log records only method, URI, status and upstream Location; the observer follows actual successful creation identities and verifies their individual deletion. Missing identities fail observation. No API payloads or authorization headers are recorded. An initial TI collection GET may return404 before its AF exists. The lab callback route returns204, providing a reachable before its AF exists, and an existing empty TI collection serializes as JSON null. Cleanup requires 404 for the observed individual TI resource as well as public removal; accepting an empty list alone cannot prove deletion. The lab callback route returns 204, providing a reachable technical destination without implementing notifications in SRM or TF-SDK. This compatibility tier qualifies API translation, selected provider behavior Loading