Loading edge_cloud_management_api/controllers/federation_manager_controller.py +3 −1 Original line number Diff line number Diff line Loading @@ -27,7 +27,9 @@ def create_federation(): """POST /partner - Create federation with partner OP.""" body = request.get_json() token = requests.post(TOKEN_ENDPOINT, headers=token_headers, data=data).json().get('access_token') token = __get_token() if not token: return jsonify({"error": "Unable to obtain access token"}), 500 response, code = federation_client.post_partner(body, token) fed = {'_id': response.get('federationContextId'), 'token': token} if code==200: Loading edge_cloud_management_api/specification/openapi.yaml +71 −0 Original line number Diff line number Diff line Loading @@ -1255,6 +1255,10 @@ paths: responses: "200": description: Zone resource info retrieved content: application/json: schema: $ref: '#/components/schemas/ZoneResourceInfo' "404": description: Zone not found delete: Loading Loading @@ -1283,6 +1287,10 @@ paths: responses: "200": description: Zone reservation removed content: application/json: schema: $ref: '#/components/schemas/ZoneReservationRemoval' "404": description: Zone not found /fed-context-id: Loading Loading @@ -2326,6 +2334,69 @@ components: availZoneNotifLink: $ref: '#/components/schemas/Uri' ZoneResourceInfo: type: object properties: zoneId: $ref: '#/components/schemas/ZoneIdentifier' computeResourceQuotaLimits: type: array items: $ref: '#/components/schemas/ComputeResource' reservedComputeResources: type: array items: $ref: '#/components/schemas/ComputeResource' flavoursSupported: type: array items: $ref: '#/components/schemas/ZoneFlavour' ZoneReservationRemoval: type: object properties: status: type: string example: status: deleted ComputeResource: type: object properties: cpuArchType: $ref: '#/components/schemas/CPUArchType' numCPU: type: integer format: int32 memory: type: integer format: int32 hugepages: type: array items: type: string ZoneFlavour: type: object properties: flavourId: type: string cpuArchType: $ref: '#/components/schemas/CPUArchType' numCPU: type: integer format: int32 memorySize: type: integer format: int32 storageSize: type: integer format: int32 supportedOSTypes: type: array items: $ref: '#/components/schemas/OSType' FixedNetworkIds: minItems: 1 type: array Loading tests/unit/controllers/test_edge_cloud_controller.py +32 −7 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ import pytest from unittest.mock import MagicMock, patch from flask import Flask from edge_cloud_management_api.controllers.edge_cloud_controller import ( get_cached_zones, get_edge_cloud_zones, ) from edge_cloud_management_api.app import get_app_instance Loading @@ -24,9 +25,9 @@ def mock_zones(): @pytest.fixture def mock_get_all_cloud_zones(mock_zones): def mock_get_cached_zones(mock_zones): with patch( "edge_cloud_management_api.controllers.edge_cloud_controller.get_all_cloud_zones", "edge_cloud_management_api.controllers.edge_cloud_controller.get_cached_zones", return_value=mock_zones, ) as mock_function: yield mock_function Loading @@ -51,7 +52,7 @@ def test_get_edge_cloud_zones( status, expected_response_status, expected_count, mock_get_all_cloud_zones: MagicMock, mock_get_cached_zones: MagicMock, test_app: Flask, ): """ Loading @@ -62,12 +63,36 @@ def test_get_edge_cloud_zones( assert response_status == expected_response_status if expected_response_status == 400: assert response.json["code"] == "VALIDATION_ERROR" data = response.get_json() assert data is not None assert data["code"] == "VALIDATION_ERROR" elif expected_response_status == 200: # Since the function does not filter, always expect all mock zones assert isinstance(response.json, list) assert len(response.json) == expected_count mock_get_all_cloud_zones.assert_called_once() data = response.get_json() assert isinstance(data, list) assert len(data) == expected_count mock_get_cached_zones.assert_called_once() else: # Defensive: should not get here assert False, "Unexpected response status" @pytest.mark.unit def test_get_cached_zones_returns_cached(mock_zones): with patch( "edge_cloud_management_api.controllers.edge_cloud_controller.get_zones", return_value=mock_zones, ): assert get_cached_zones() == mock_zones @pytest.mark.unit def test_get_cached_zones_fallback_to_srm(mock_zones): with patch( "edge_cloud_management_api.controllers.edge_cloud_controller.get_zones", side_effect=Exception("db error"), ), patch( "edge_cloud_management_api.controllers.edge_cloud_controller.get_local_zones", return_value=mock_zones, ): assert get_cached_zones() == mock_zones tests/unit/controllers/test_federation_manager_controller.py +50 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ def test_create_federation(mock_factory_class, test_app: Flask): assert status == 200 data = response.get_json() assert data is not None assert "federationContextId" in data assert data["federationContextId"] == "abc" Loading Loading @@ -79,3 +80,52 @@ def test_get_federation_context_ids(mock_factory_class, test_app: Flask): assert status == 200 assert response.get_json() == {"FederationContextId": "ctx-123"} @pytest.mark.component @patch("edge_cloud_management_api.controllers.federation_manager_controller.__get_token", return_value="token") @patch("edge_cloud_management_api.controllers.federation_manager_controller.federation_client") def test_request_zone_synch(mock_federation_client, _mock_get_token, test_app: Flask): federation_context_id = "ctx-123" body = { "acceptedAvailabilityZones": ["zone-1"], "availZoneNotifLink": "http://callback.local" } mock_federation_client.request_zone_sync.return_value = ({"status": "ok"}, 200) with test_app.test_request_context(json=body): response, status = federation_manager_controller.request_zone_synch(federation_context_id) assert status == 200 assert response.get_json() == {"status": "ok"} mock_federation_client.request_zone_sync.assert_called_once() @pytest.mark.component @patch("edge_cloud_management_api.controllers.federation_manager_controller.__get_token", return_value="token") @patch("edge_cloud_management_api.controllers.federation_manager_controller.federation_client") def test_get_zone_resource_info(mock_federation_client, _mock_get_token, test_app: Flask): federation_context_id = "ctx-123" zone_id = "zone-1" mock_federation_client.get_zone_resource_info.return_value = {"zoneId": zone_id} with test_app.test_request_context(): response = federation_manager_controller.get_zone_resource_info(federation_context_id, zone_id) assert response.get_json() == {"zoneId": zone_id} mock_federation_client.get_zone_resource_info.assert_called_once() @pytest.mark.component @patch("edge_cloud_management_api.controllers.federation_manager_controller.__get_token", return_value="token") @patch("edge_cloud_management_api.controllers.federation_manager_controller.federation_client") def test_remove_zone_sync(mock_federation_client, _mock_get_token, test_app: Flask): federation_context_id = "ctx-123" zone_id = "zone-1" mock_federation_client.remove_zone_sync.return_value = {"status": "deleted"} with test_app.test_request_context(): response = federation_manager_controller.remove_zone_sync(federation_context_id, zone_id) assert response.get_json() == {"status": "deleted"} mock_federation_client.remove_zone_sync.assert_called_once() Loading
edge_cloud_management_api/controllers/federation_manager_controller.py +3 −1 Original line number Diff line number Diff line Loading @@ -27,7 +27,9 @@ def create_federation(): """POST /partner - Create federation with partner OP.""" body = request.get_json() token = requests.post(TOKEN_ENDPOINT, headers=token_headers, data=data).json().get('access_token') token = __get_token() if not token: return jsonify({"error": "Unable to obtain access token"}), 500 response, code = federation_client.post_partner(body, token) fed = {'_id': response.get('federationContextId'), 'token': token} if code==200: Loading
edge_cloud_management_api/specification/openapi.yaml +71 −0 Original line number Diff line number Diff line Loading @@ -1255,6 +1255,10 @@ paths: responses: "200": description: Zone resource info retrieved content: application/json: schema: $ref: '#/components/schemas/ZoneResourceInfo' "404": description: Zone not found delete: Loading Loading @@ -1283,6 +1287,10 @@ paths: responses: "200": description: Zone reservation removed content: application/json: schema: $ref: '#/components/schemas/ZoneReservationRemoval' "404": description: Zone not found /fed-context-id: Loading Loading @@ -2326,6 +2334,69 @@ components: availZoneNotifLink: $ref: '#/components/schemas/Uri' ZoneResourceInfo: type: object properties: zoneId: $ref: '#/components/schemas/ZoneIdentifier' computeResourceQuotaLimits: type: array items: $ref: '#/components/schemas/ComputeResource' reservedComputeResources: type: array items: $ref: '#/components/schemas/ComputeResource' flavoursSupported: type: array items: $ref: '#/components/schemas/ZoneFlavour' ZoneReservationRemoval: type: object properties: status: type: string example: status: deleted ComputeResource: type: object properties: cpuArchType: $ref: '#/components/schemas/CPUArchType' numCPU: type: integer format: int32 memory: type: integer format: int32 hugepages: type: array items: type: string ZoneFlavour: type: object properties: flavourId: type: string cpuArchType: $ref: '#/components/schemas/CPUArchType' numCPU: type: integer format: int32 memorySize: type: integer format: int32 storageSize: type: integer format: int32 supportedOSTypes: type: array items: $ref: '#/components/schemas/OSType' FixedNetworkIds: minItems: 1 type: array Loading
tests/unit/controllers/test_edge_cloud_controller.py +32 −7 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ import pytest from unittest.mock import MagicMock, patch from flask import Flask from edge_cloud_management_api.controllers.edge_cloud_controller import ( get_cached_zones, get_edge_cloud_zones, ) from edge_cloud_management_api.app import get_app_instance Loading @@ -24,9 +25,9 @@ def mock_zones(): @pytest.fixture def mock_get_all_cloud_zones(mock_zones): def mock_get_cached_zones(mock_zones): with patch( "edge_cloud_management_api.controllers.edge_cloud_controller.get_all_cloud_zones", "edge_cloud_management_api.controllers.edge_cloud_controller.get_cached_zones", return_value=mock_zones, ) as mock_function: yield mock_function Loading @@ -51,7 +52,7 @@ def test_get_edge_cloud_zones( status, expected_response_status, expected_count, mock_get_all_cloud_zones: MagicMock, mock_get_cached_zones: MagicMock, test_app: Flask, ): """ Loading @@ -62,12 +63,36 @@ def test_get_edge_cloud_zones( assert response_status == expected_response_status if expected_response_status == 400: assert response.json["code"] == "VALIDATION_ERROR" data = response.get_json() assert data is not None assert data["code"] == "VALIDATION_ERROR" elif expected_response_status == 200: # Since the function does not filter, always expect all mock zones assert isinstance(response.json, list) assert len(response.json) == expected_count mock_get_all_cloud_zones.assert_called_once() data = response.get_json() assert isinstance(data, list) assert len(data) == expected_count mock_get_cached_zones.assert_called_once() else: # Defensive: should not get here assert False, "Unexpected response status" @pytest.mark.unit def test_get_cached_zones_returns_cached(mock_zones): with patch( "edge_cloud_management_api.controllers.edge_cloud_controller.get_zones", return_value=mock_zones, ): assert get_cached_zones() == mock_zones @pytest.mark.unit def test_get_cached_zones_fallback_to_srm(mock_zones): with patch( "edge_cloud_management_api.controllers.edge_cloud_controller.get_zones", side_effect=Exception("db error"), ), patch( "edge_cloud_management_api.controllers.edge_cloud_controller.get_local_zones", return_value=mock_zones, ): assert get_cached_zones() == mock_zones
tests/unit/controllers/test_federation_manager_controller.py +50 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ def test_create_federation(mock_factory_class, test_app: Flask): assert status == 200 data = response.get_json() assert data is not None assert "federationContextId" in data assert data["federationContextId"] == "abc" Loading Loading @@ -79,3 +80,52 @@ def test_get_federation_context_ids(mock_factory_class, test_app: Flask): assert status == 200 assert response.get_json() == {"FederationContextId": "ctx-123"} @pytest.mark.component @patch("edge_cloud_management_api.controllers.federation_manager_controller.__get_token", return_value="token") @patch("edge_cloud_management_api.controllers.federation_manager_controller.federation_client") def test_request_zone_synch(mock_federation_client, _mock_get_token, test_app: Flask): federation_context_id = "ctx-123" body = { "acceptedAvailabilityZones": ["zone-1"], "availZoneNotifLink": "http://callback.local" } mock_federation_client.request_zone_sync.return_value = ({"status": "ok"}, 200) with test_app.test_request_context(json=body): response, status = federation_manager_controller.request_zone_synch(federation_context_id) assert status == 200 assert response.get_json() == {"status": "ok"} mock_federation_client.request_zone_sync.assert_called_once() @pytest.mark.component @patch("edge_cloud_management_api.controllers.federation_manager_controller.__get_token", return_value="token") @patch("edge_cloud_management_api.controllers.federation_manager_controller.federation_client") def test_get_zone_resource_info(mock_federation_client, _mock_get_token, test_app: Flask): federation_context_id = "ctx-123" zone_id = "zone-1" mock_federation_client.get_zone_resource_info.return_value = {"zoneId": zone_id} with test_app.test_request_context(): response = federation_manager_controller.get_zone_resource_info(federation_context_id, zone_id) assert response.get_json() == {"zoneId": zone_id} mock_federation_client.get_zone_resource_info.assert_called_once() @pytest.mark.component @patch("edge_cloud_management_api.controllers.federation_manager_controller.__get_token", return_value="token") @patch("edge_cloud_management_api.controllers.federation_manager_controller.federation_client") def test_remove_zone_sync(mock_federation_client, _mock_get_token, test_app: Flask): federation_context_id = "ctx-123" zone_id = "zone-1" mock_federation_client.remove_zone_sync.return_value = {"status": "deleted"} with test_app.test_request_context(): response = federation_manager_controller.remove_zone_sync(federation_context_id, zone_id) assert response.get_json() == {"status": "deleted"} mock_federation_client.remove_zone_sync.assert_called_once()