Loading .gitlab-ci.yml +18 −6 Changes for .gitlab-ci.yml: 18 added lines, 6 removed lines. Original line number Diff line number Diff line workflow: rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: '$CI_PIPELINE_SOURCE == "push" && ($CI_COMMIT_BRANCH == "master" || $CI_COMMIT_BRANCH == "main")' - if: '$CI_PIPELINE_SOURCE == "push" && ($CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "develop")' - if: '$CI_PIPELINE_SOURCE == "push" && $CI_OPEN_MERGE_REQUESTS' when: never - when: always Loading Loading @@ -156,7 +156,10 @@ platform-lab: DOCKER_TLS_VERIFY: "1" DOCKER_CERT_PATH: /certs/client rules: - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH == "test/platform-application-checkpoints" || $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME == "test/platform-application-checkpoints"' - if: &release_lab_ref >- $CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "develop" || ($CI_MERGE_REQUEST_SOURCE_PROJECT_ID == $CI_PROJECT_ID && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main")) when: on_success allow_failure: false variables: Loading @@ -180,6 +183,9 @@ platform-lab: - $OOP_LAB_REPORT_DIR/candidate.json - $OOP_LAB_REPORT_DIR/feature-matrix.json - $OOP_LAB_REPORT_DIR/feature-matrix.md - $OOP_LAB_REPORT_DIR/index.html - $OOP_LAB_REPORT_DIR/summary.txt - $OOP_LAB_REPORT_DIR/acceptance-junit.xml - $OOP_LAB_REPORT_DIR/wrapper-junit.xml - $OOP_LAB_REPORT_DIR/pods.txt - $OOP_LAB_REPORT_DIR/events.txt Loading @@ -187,9 +193,11 @@ platform-lab: - $OOP_LAB_REPORT_DIR/health/junit.xml - $OOP_LAB_REPORT_DIR/health/acceptance/ reports: annotations: $OOP_LAB_REPORT_DIR/annotations.json junit: - $OOP_LAB_REPORT_DIR/junit.xml - $OOP_LAB_REPORT_DIR/wrapper-junit.xml - $OOP_LAB_REPORT_DIR/acceptance-junit.xml expire_in: 90 days # Same lab and lifecycle, using a chart archive instead of a container manifest. Loading @@ -199,7 +207,7 @@ platform-lab-helm: OOP_LAB_PACKAGE_TYPE: HELM OOP_LAB_REPORT_DIR: .cache/platform-lab/helm rules: - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH == "test/platform-application-checkpoints" || $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME == "test/platform-application-checkpoints"' - if: *release_lab_ref variables: OOP_LAB_PROFILE: ci/lab/profile.release-2.0.json OOP_LAB_REFRESH_CANDIDATE: "1" Loading @@ -213,7 +221,7 @@ platform-lab-network: OOP_LAB_NETWORK_PROVIDER: EURECOM OOP_LAB_REPORT_DIR: .cache/platform-lab/network rules: - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH == "test/platform-application-checkpoints" || $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME == "test/platform-application-checkpoints"' - if: *release_lab_ref variables: OOP_LAB_PROFILE: ci/lab/profile.release-2.0.json OOP_LAB_REFRESH_CANDIDATE: "1" Loading @@ -231,16 +239,20 @@ release-feature-report: - {job: platform-lab-helm, artifacts: true, optional: true} - {job: platform-lab-network, artifacts: true, optional: true} rules: - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH == "test/platform-application-checkpoints" || $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME == "test/platform-application-checkpoints"' - if: *release_lab_ref - when: never script: - python ci/merge_feature_reports.py .cache/platform-lab .cache/release-features artifacts: when: always expose_as: Observed release features expose_as: E2E results and verified features paths: - .cache/release-features/feature-matrix.md - .cache/release-features/feature-matrix.json - .cache/release-features/index.html - .cache/release-features/summary.txt reports: annotations: .cache/release-features/annotations.json expire_in: 90 days # Explicit live mode leaves all default component tests and deployment jobs alone. Loading README.md +16 −0 Changes for README.md: 16 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -4,6 +4,22 @@ Ansible for the ETSI Open Operator Platform (OOP) Release 2. Each operator in th inventory gets its own platform on its own Kubernetes cluster; the playbooks then federate the operators and run end-to-end tests across them. ## Understand the E2E lab and its results Start with [OpenOP E2E tests, explained](ci/lab/EXPLAINED.md): a short overview with diagrams for readers new to OpenOP. The [check catalogue](ci/lab/CHECKS.md) explains individual tests. The [operator guide](ci/lab/README.md) covers running the same isolated lab locally and in GitLab CI. In a pipeline, open **release-feature-report** for the readable feature matrix and its report/documentation links. Open **Tests** for individual acceptance cases and setup/cleanup checkpoints. HTML/JSON artifacts retain exact-run evidence; a job's success covers only its stated checks and limits. New work targets develop. The Release 2.0.0 MR promotes develop to main after the module leader and release owner review; the release owner creates the final tags and GitLab Release. Historical master commits remain intact. - `playbooks/infrastructure/k3s/`: k3s on plain Ubuntu VMs, when you have no cluster yet - `playbooks/scenarios/r2_existing_cluster/`: deploy the `oop-platform` Helm chart per operator, federate the operators, run the e2e tests, undeploy Loading ansible/tests/r2_e2e/test_apps.py +2 −0 Changes for ansible/tests/r2_e2e/test_apps.py: 2 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ def registered_app(operators, callers, request): @pytest.mark.e2e_suite("onboarding") def test_app_provider_registers_reads_and_deletes_an_app(operators, callers, registered_app): """Onboard an application through OEG, read its stored manifest and list entry, then verify catalogue removal.""" for op in operators: url, manifest = registered_app[op.host] token = callers[op.host]["provider"] Loading @@ -78,6 +79,7 @@ def test_app_provider_registers_reads_and_deletes_an_app(operators, callers, reg @pytest.mark.e2e_suite("onboarding") def test_app_provider_lists_its_apps(operators, callers, registered_app): """Verify the application registered by this test client appears in its catalogue list.""" for op in operators: r = http("GET", op.oeg_url + APPS, callers[op.host]["provider"]) assert r.status_code == 200, r.text Loading ansible/tests/r2_e2e/test_auth.py +3 −0 Changes for ansible/tests/r2_e2e/test_auth.py: 3 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ def _partners(op): def test_oeg_refuses_calls_without_a_valid_token(operators): """Require HTTP 401 for missing or invalid bearer tokens on application and federation routes.""" for op in operators: for url in (op.oeg_url + APPS, _partners(op)): r = http("GET", url) Loading @@ -27,12 +28,14 @@ def test_oeg_refuses_another_operators_token(pair, callers): def test_oeg_keeps_federation_to_operators(operators, callers): """Require HTTP 403 when an App Provider calls an operator-only federation route.""" for op in operators: r = http("GET", _partners(op), callers[op.host]["provider"]) assert r.status_code == 403, r.text def test_oeg_keeps_app_apis_to_app_providers(operators, callers): """Reject the operator role on application routes and let an App Provider reach the application lookup.""" for op in operators: app = f"{op.oeg_url}{APPS}/{uuid.uuid4()}" r = http("GET", app, callers[op.host]["operator"]) Loading ansible/tests/r2_e2e/test_deploy_to_ti.py +13 −0 Changes for ansible/tests/r2_e2e/test_deploy_to_ti.py: 13 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -94,6 +94,7 @@ def public_endpoint(progressive_scenario, deployed_workload, public_instance): @pytest.mark.platform_acceptance def test_cloud_zones_match_configured_workload_targets(request, operators, callers): """Compare public zone names and unique identities with the workload zones configured in this lab.""" profile = request.config.sdk_profile expected = set(profile.get("expected_zones", [profile.get("zone_name")])) assert expected and None not in expected, "Declare expected workload zone names in the profile" Loading @@ -111,11 +112,13 @@ def test_cloud_zones_match_configured_workload_targets(request, operators, calle @pytest.mark.platform_acceptance def test_progressive_oeg_request_creates_a_ready_workload(deployed_workload): """Instantiate through OEG and independently require the owned Kubernetes Deployment, ready Pod and NodePort Service.""" assert deployed_workload["pod_uid"] and deployed_workload["service_uid"] @pytest.mark.platform_acceptance def test_progressive_workload_applies_requested_resources(progressive_scenario, deployed_workload): """Compare requested CPU and decimal-MB memory with the actual Kubernetes Deployment and Pod resource requests.""" check_requested_resources(deployed_workload, progressive_scenario.evidence["requested_resources"]) progressive_scenario.evidence.update(resources_verified=True, last_verified_phase="resources_verified") Loading @@ -123,16 +126,19 @@ def test_progressive_workload_applies_requested_resources(progressive_scenario, @pytest.mark.platform_acceptance def test_progressive_oeg_returns_the_application_instance(public_instance): """Poll the public instance until ready and verify its application and zone identities.""" assert public_instance["status"] == "ready" @pytest.mark.platform_acceptance def test_progressive_oeg_returns_the_observed_endpoint(public_endpoint): """Compare the public application endpoint with the actual owned Kubernetes Service and cluster node.""" assert public_endpoint["port"] and public_endpoint["host"] @pytest.mark.platform_acceptance def test_progressive_endpoint_reaches_the_deployed_application(progressive_scenario, public_endpoint): """Call the returned HTTP endpoint and verify the response identifies the deployed application Pod.""" check_reachability(public_endpoint, progressive_scenario.profile, http) progressive_scenario.evidence.update(endpoint_reachable=True, last_verified_phase="reachability_verified") Loading @@ -140,6 +146,7 @@ def test_progressive_endpoint_reaches_the_deployed_application(progressive_scena @pytest.mark.platform_acceptance def test_progressive_oeg_termination_removes_the_workload(progressive_scenario, deployed_workload): """Terminate through OEG and verify the public instance and owned Deployment, Pod and Service disappear.""" progressive_scenario.terminate() Loading Loading @@ -209,21 +216,25 @@ def qod_case(network_caller): @pytest.mark.parametrize('profile_name',EURECOM_QOS_PROFILES) @pytest.mark.platform_acceptance def test_qod_profile_lifecycle(qod_case,profile_name): """Create and read QoD through OEG, inspect the real NEF resource and flow, then verify public and provider deletion.""" qod_case.verify_supported(profile_name) @pytest.mark.platform_acceptance def test_qod_unsupported_profile_is_rejected(qod_case): """Reject a profile outside this control path configuration and prove no provider subscription remains.""" qod_case.verify_refused(qod_case.profile['qod']['unsupported_profile']) @pytest.mark.platform_acceptance def test_qod_backend_refusal_retains_diagnostics(qod_case): """Use a locally allowed profile refused by the NEF and require useful backend diagnostics and cleanup.""" qod_case.verify_refused(qod_case.profile['qod']['backend_refusal_profile'],backend=True) @pytest.mark.platform_acceptance def test_known_device_returns_geographic_location(network_caller): """Retrieve the simulated device location through OEG and verify geographic coordinates and freshness.""" op,profile,token=network_caller device=profile['qod']['session_request']['device'] response=http('POST',op.oeg_url+'/location-retrieval/v0.5/retrieve',token, Loading @@ -245,6 +256,7 @@ def test_known_device_returns_geographic_location(network_caller): @pytest.mark.platform_acceptance def test_location_without_device_is_rejected(network_caller): """Require the public missing-identifier error when location retrieval has no device.""" op,_,token=network_caller response=http('POST',op.oeg_url+'/location-retrieval/v0.5/retrieve',token,json={}) assert response.status_code==422 and response.json()['code']=='MISSING_IDENTIFIER' Loading @@ -253,6 +265,7 @@ def test_location_without_device_is_rejected(network_caller): @pytest.mark.platform_acceptance def test_device_ti_uses_the_deployed_application_endpoint(progressive_scenario,public_endpoint): # The application owner requests TI; a different test client is another tenant. """Activate device TI through OEG, verify real device/application IPs and notification URL in the NEF, then verify deletion.""" 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' Loading Loading
.gitlab-ci.yml +18 −6 Changes for .gitlab-ci.yml: 18 added lines, 6 removed lines. Original line number Diff line number Diff line workflow: rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: '$CI_PIPELINE_SOURCE == "push" && ($CI_COMMIT_BRANCH == "master" || $CI_COMMIT_BRANCH == "main")' - if: '$CI_PIPELINE_SOURCE == "push" && ($CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "develop")' - if: '$CI_PIPELINE_SOURCE == "push" && $CI_OPEN_MERGE_REQUESTS' when: never - when: always Loading Loading @@ -156,7 +156,10 @@ platform-lab: DOCKER_TLS_VERIFY: "1" DOCKER_CERT_PATH: /certs/client rules: - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH == "test/platform-application-checkpoints" || $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME == "test/platform-application-checkpoints"' - if: &release_lab_ref >- $CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "develop" || ($CI_MERGE_REQUEST_SOURCE_PROJECT_ID == $CI_PROJECT_ID && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main")) when: on_success allow_failure: false variables: Loading @@ -180,6 +183,9 @@ platform-lab: - $OOP_LAB_REPORT_DIR/candidate.json - $OOP_LAB_REPORT_DIR/feature-matrix.json - $OOP_LAB_REPORT_DIR/feature-matrix.md - $OOP_LAB_REPORT_DIR/index.html - $OOP_LAB_REPORT_DIR/summary.txt - $OOP_LAB_REPORT_DIR/acceptance-junit.xml - $OOP_LAB_REPORT_DIR/wrapper-junit.xml - $OOP_LAB_REPORT_DIR/pods.txt - $OOP_LAB_REPORT_DIR/events.txt Loading @@ -187,9 +193,11 @@ platform-lab: - $OOP_LAB_REPORT_DIR/health/junit.xml - $OOP_LAB_REPORT_DIR/health/acceptance/ reports: annotations: $OOP_LAB_REPORT_DIR/annotations.json junit: - $OOP_LAB_REPORT_DIR/junit.xml - $OOP_LAB_REPORT_DIR/wrapper-junit.xml - $OOP_LAB_REPORT_DIR/acceptance-junit.xml expire_in: 90 days # Same lab and lifecycle, using a chart archive instead of a container manifest. Loading @@ -199,7 +207,7 @@ platform-lab-helm: OOP_LAB_PACKAGE_TYPE: HELM OOP_LAB_REPORT_DIR: .cache/platform-lab/helm rules: - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH == "test/platform-application-checkpoints" || $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME == "test/platform-application-checkpoints"' - if: *release_lab_ref variables: OOP_LAB_PROFILE: ci/lab/profile.release-2.0.json OOP_LAB_REFRESH_CANDIDATE: "1" Loading @@ -213,7 +221,7 @@ platform-lab-network: OOP_LAB_NETWORK_PROVIDER: EURECOM OOP_LAB_REPORT_DIR: .cache/platform-lab/network rules: - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH == "test/platform-application-checkpoints" || $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME == "test/platform-application-checkpoints"' - if: *release_lab_ref variables: OOP_LAB_PROFILE: ci/lab/profile.release-2.0.json OOP_LAB_REFRESH_CANDIDATE: "1" Loading @@ -231,16 +239,20 @@ release-feature-report: - {job: platform-lab-helm, artifacts: true, optional: true} - {job: platform-lab-network, artifacts: true, optional: true} rules: - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH == "test/platform-application-checkpoints" || $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME == "test/platform-application-checkpoints"' - if: *release_lab_ref - when: never script: - python ci/merge_feature_reports.py .cache/platform-lab .cache/release-features artifacts: when: always expose_as: Observed release features expose_as: E2E results and verified features paths: - .cache/release-features/feature-matrix.md - .cache/release-features/feature-matrix.json - .cache/release-features/index.html - .cache/release-features/summary.txt reports: annotations: .cache/release-features/annotations.json expire_in: 90 days # Explicit live mode leaves all default component tests and deployment jobs alone. Loading
README.md +16 −0 Changes for README.md: 16 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -4,6 +4,22 @@ Ansible for the ETSI Open Operator Platform (OOP) Release 2. Each operator in th inventory gets its own platform on its own Kubernetes cluster; the playbooks then federate the operators and run end-to-end tests across them. ## Understand the E2E lab and its results Start with [OpenOP E2E tests, explained](ci/lab/EXPLAINED.md): a short overview with diagrams for readers new to OpenOP. The [check catalogue](ci/lab/CHECKS.md) explains individual tests. The [operator guide](ci/lab/README.md) covers running the same isolated lab locally and in GitLab CI. In a pipeline, open **release-feature-report** for the readable feature matrix and its report/documentation links. Open **Tests** for individual acceptance cases and setup/cleanup checkpoints. HTML/JSON artifacts retain exact-run evidence; a job's success covers only its stated checks and limits. New work targets develop. The Release 2.0.0 MR promotes develop to main after the module leader and release owner review; the release owner creates the final tags and GitLab Release. Historical master commits remain intact. - `playbooks/infrastructure/k3s/`: k3s on plain Ubuntu VMs, when you have no cluster yet - `playbooks/scenarios/r2_existing_cluster/`: deploy the `oop-platform` Helm chart per operator, federate the operators, run the e2e tests, undeploy Loading
ansible/tests/r2_e2e/test_apps.py +2 −0 Changes for ansible/tests/r2_e2e/test_apps.py: 2 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ def registered_app(operators, callers, request): @pytest.mark.e2e_suite("onboarding") def test_app_provider_registers_reads_and_deletes_an_app(operators, callers, registered_app): """Onboard an application through OEG, read its stored manifest and list entry, then verify catalogue removal.""" for op in operators: url, manifest = registered_app[op.host] token = callers[op.host]["provider"] Loading @@ -78,6 +79,7 @@ def test_app_provider_registers_reads_and_deletes_an_app(operators, callers, reg @pytest.mark.e2e_suite("onboarding") def test_app_provider_lists_its_apps(operators, callers, registered_app): """Verify the application registered by this test client appears in its catalogue list.""" for op in operators: r = http("GET", op.oeg_url + APPS, callers[op.host]["provider"]) assert r.status_code == 200, r.text Loading
ansible/tests/r2_e2e/test_auth.py +3 −0 Changes for ansible/tests/r2_e2e/test_auth.py: 3 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ def _partners(op): def test_oeg_refuses_calls_without_a_valid_token(operators): """Require HTTP 401 for missing or invalid bearer tokens on application and federation routes.""" for op in operators: for url in (op.oeg_url + APPS, _partners(op)): r = http("GET", url) Loading @@ -27,12 +28,14 @@ def test_oeg_refuses_another_operators_token(pair, callers): def test_oeg_keeps_federation_to_operators(operators, callers): """Require HTTP 403 when an App Provider calls an operator-only federation route.""" for op in operators: r = http("GET", _partners(op), callers[op.host]["provider"]) assert r.status_code == 403, r.text def test_oeg_keeps_app_apis_to_app_providers(operators, callers): """Reject the operator role on application routes and let an App Provider reach the application lookup.""" for op in operators: app = f"{op.oeg_url}{APPS}/{uuid.uuid4()}" r = http("GET", app, callers[op.host]["operator"]) Loading
ansible/tests/r2_e2e/test_deploy_to_ti.py +13 −0 Changes for ansible/tests/r2_e2e/test_deploy_to_ti.py: 13 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -94,6 +94,7 @@ def public_endpoint(progressive_scenario, deployed_workload, public_instance): @pytest.mark.platform_acceptance def test_cloud_zones_match_configured_workload_targets(request, operators, callers): """Compare public zone names and unique identities with the workload zones configured in this lab.""" profile = request.config.sdk_profile expected = set(profile.get("expected_zones", [profile.get("zone_name")])) assert expected and None not in expected, "Declare expected workload zone names in the profile" Loading @@ -111,11 +112,13 @@ def test_cloud_zones_match_configured_workload_targets(request, operators, calle @pytest.mark.platform_acceptance def test_progressive_oeg_request_creates_a_ready_workload(deployed_workload): """Instantiate through OEG and independently require the owned Kubernetes Deployment, ready Pod and NodePort Service.""" assert deployed_workload["pod_uid"] and deployed_workload["service_uid"] @pytest.mark.platform_acceptance def test_progressive_workload_applies_requested_resources(progressive_scenario, deployed_workload): """Compare requested CPU and decimal-MB memory with the actual Kubernetes Deployment and Pod resource requests.""" check_requested_resources(deployed_workload, progressive_scenario.evidence["requested_resources"]) progressive_scenario.evidence.update(resources_verified=True, last_verified_phase="resources_verified") Loading @@ -123,16 +126,19 @@ def test_progressive_workload_applies_requested_resources(progressive_scenario, @pytest.mark.platform_acceptance def test_progressive_oeg_returns_the_application_instance(public_instance): """Poll the public instance until ready and verify its application and zone identities.""" assert public_instance["status"] == "ready" @pytest.mark.platform_acceptance def test_progressive_oeg_returns_the_observed_endpoint(public_endpoint): """Compare the public application endpoint with the actual owned Kubernetes Service and cluster node.""" assert public_endpoint["port"] and public_endpoint["host"] @pytest.mark.platform_acceptance def test_progressive_endpoint_reaches_the_deployed_application(progressive_scenario, public_endpoint): """Call the returned HTTP endpoint and verify the response identifies the deployed application Pod.""" check_reachability(public_endpoint, progressive_scenario.profile, http) progressive_scenario.evidence.update(endpoint_reachable=True, last_verified_phase="reachability_verified") Loading @@ -140,6 +146,7 @@ def test_progressive_endpoint_reaches_the_deployed_application(progressive_scena @pytest.mark.platform_acceptance def test_progressive_oeg_termination_removes_the_workload(progressive_scenario, deployed_workload): """Terminate through OEG and verify the public instance and owned Deployment, Pod and Service disappear.""" progressive_scenario.terminate() Loading Loading @@ -209,21 +216,25 @@ def qod_case(network_caller): @pytest.mark.parametrize('profile_name',EURECOM_QOS_PROFILES) @pytest.mark.platform_acceptance def test_qod_profile_lifecycle(qod_case,profile_name): """Create and read QoD through OEG, inspect the real NEF resource and flow, then verify public and provider deletion.""" qod_case.verify_supported(profile_name) @pytest.mark.platform_acceptance def test_qod_unsupported_profile_is_rejected(qod_case): """Reject a profile outside this control path configuration and prove no provider subscription remains.""" qod_case.verify_refused(qod_case.profile['qod']['unsupported_profile']) @pytest.mark.platform_acceptance def test_qod_backend_refusal_retains_diagnostics(qod_case): """Use a locally allowed profile refused by the NEF and require useful backend diagnostics and cleanup.""" qod_case.verify_refused(qod_case.profile['qod']['backend_refusal_profile'],backend=True) @pytest.mark.platform_acceptance def test_known_device_returns_geographic_location(network_caller): """Retrieve the simulated device location through OEG and verify geographic coordinates and freshness.""" op,profile,token=network_caller device=profile['qod']['session_request']['device'] response=http('POST',op.oeg_url+'/location-retrieval/v0.5/retrieve',token, Loading @@ -245,6 +256,7 @@ def test_known_device_returns_geographic_location(network_caller): @pytest.mark.platform_acceptance def test_location_without_device_is_rejected(network_caller): """Require the public missing-identifier error when location retrieval has no device.""" op,_,token=network_caller response=http('POST',op.oeg_url+'/location-retrieval/v0.5/retrieve',token,json={}) assert response.status_code==422 and response.json()['code']=='MISSING_IDENTIFIER' Loading @@ -253,6 +265,7 @@ def test_location_without_device_is_rejected(network_caller): @pytest.mark.platform_acceptance def test_device_ti_uses_the_deployed_application_endpoint(progressive_scenario,public_endpoint): # The application owner requests TI; a different test client is another tenant. """Activate device TI through OEG, verify real device/application IPs and notification URL in the NEF, then verify deletion.""" 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' Loading