зеркало из https://github.com/microsoft/AzureTRE.git
Aligning airlock processor unittests to use pytest (#3086)
* aligning airlock processor unittests to use pytest * update changelog * update version * Update airlock_processor/tests/shared_code/test_blob_operations.py Co-authored-by: Tamir Kamara <26870601+tamirkamara@users.noreply.github.com> * Update airlock_processor/tests/test_status_change_queue_trigger.py Co-authored-by: Tamir Kamara <26870601+tamirkamara@users.noreply.github.com> * adding mock to the req dev file * fix Co-authored-by: Tamir Kamara <26870601+tamirkamara@users.noreply.github.com>
This commit is contained in:
Родитель
76ea0efa7a
Коммит
44d3b91069
|
@ -37,6 +37,7 @@ ENHANCEMENTS:
|
|||
* Upgrade Guacamole dependencies ([#3053](https://github.com/microsoft/AzureTRE/pull/3053))
|
||||
* Lint TRE cost tags per entity type (workspace, shared service, etc.) ([#3061](https://github.com/microsoft/AzureTRE/pull/3061))
|
||||
* Validate required secrets have value ([#3073](https://github.com/microsoft/AzureTRE/pull/3073))
|
||||
* Airlock processor unittests uses pytest ([#3026](https://github.com/microsoft/AzureTRE/pull/3026))
|
||||
|
||||
|
||||
BUG FIXES:
|
||||
|
|
|
@ -1 +1 @@
|
|||
__version__ = "0.4.12"
|
||||
__version__ = "0.4.13"
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
# Dev requirements
|
||||
pytest==7.2.0
|
||||
mock==5.0.0
|
||||
|
|
|
@ -1,47 +1,49 @@
|
|||
from collections import namedtuple
|
||||
import json
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock, patch
|
||||
import pytest
|
||||
from mock import MagicMock, patch
|
||||
|
||||
from shared_code.blob_operations import get_blob_info_from_topic_and_subject, get_blob_info_from_blob_url, copy_data, get_blob_url
|
||||
from exceptions import TooManyFilesInRequestException, NoFilesInRequestException
|
||||
|
||||
|
||||
TestBlob = namedtuple("Blob", "name")
|
||||
def get_test_blob():
|
||||
return namedtuple("Blob", "name")
|
||||
|
||||
|
||||
class TestBlobOperations(TestCase):
|
||||
class TestBlobOperations():
|
||||
|
||||
def test_get_blob_info_from_topic_and_subject(self):
|
||||
topic = "/subscriptions/SUB_ID/resourceGroups/RG_NAME/providers/Microsoft.Storage/storageAccounts/ST_ACC_NAME"
|
||||
subject = "/blobServices/default/containers/c144728c-3c69-4a58-afec-48c2ec8bfd45/blobs/BLOB"
|
||||
|
||||
storage_account_name, container_name, blob_name = get_blob_info_from_topic_and_subject(topic=topic, subject=subject)
|
||||
|
||||
self.assertEqual(storage_account_name, "ST_ACC_NAME")
|
||||
self.assertEqual(container_name, "c144728c-3c69-4a58-afec-48c2ec8bfd45")
|
||||
self.assertEqual(blob_name, "BLOB")
|
||||
assert storage_account_name == "ST_ACC_NAME"
|
||||
assert container_name == "c144728c-3c69-4a58-afec-48c2ec8bfd45"
|
||||
assert blob_name == "BLOB"
|
||||
|
||||
def test_get_blob_info_from_url(self):
|
||||
url = "https://stalimextest.blob.core.windows.net/c144728c-3c69-4a58-afec-48c2ec8bfd45/test_dataset.txt"
|
||||
|
||||
storage_account_name, container_name, blob_name = get_blob_info_from_blob_url(blob_url=url)
|
||||
|
||||
self.assertEqual(storage_account_name, "stalimextest")
|
||||
self.assertEqual(container_name, "c144728c-3c69-4a58-afec-48c2ec8bfd45")
|
||||
self.assertEqual(blob_name, "test_dataset.txt")
|
||||
assert storage_account_name == "stalimextest"
|
||||
assert container_name == "c144728c-3c69-4a58-afec-48c2ec8bfd45"
|
||||
assert blob_name == "test_dataset.txt"
|
||||
|
||||
@patch("shared_code.blob_operations.BlobServiceClient")
|
||||
def test_copy_data_fails_if_too_many_blobs_to_copy(self, mock_blob_service_client):
|
||||
mock_blob_service_client().get_container_client().list_blobs = MagicMock(return_value=[TestBlob("a"), TestBlob("b")])
|
||||
mock_blob_service_client().get_container_client().list_blobs = MagicMock(return_value=[get_test_blob()("a"), get_test_blob()("b")])
|
||||
|
||||
with self.assertRaises(TooManyFilesInRequestException):
|
||||
with pytest.raises(TooManyFilesInRequestException):
|
||||
copy_data("source_acc", "dest_acc", "req_id")
|
||||
|
||||
@patch("shared_code.blob_operations.BlobServiceClient")
|
||||
def test_copy_data_fails_if_no_blobs_to_copy(self, mock_blob_service_client):
|
||||
mock_blob_service_client().get_container_client().list_blobs = MagicMock(return_value=[])
|
||||
|
||||
with self.assertRaises(NoFilesInRequestException):
|
||||
with pytest.raises(NoFilesInRequestException):
|
||||
copy_data("source_acc", "dest_acc", "req_id")
|
||||
|
||||
@patch("shared_code.blob_operations.BlobServiceClient")
|
||||
|
@ -69,7 +71,7 @@ class TestBlobOperations(TestCase):
|
|||
|
||||
# Any additional mocks for the copy_data method to work
|
||||
mock_blob_service_client().get_user_delegation_key = MagicMock(return_value="key")
|
||||
mock_blob_service_client().get_container_client().list_blobs = MagicMock(return_value=[TestBlob("a")])
|
||||
mock_blob_service_client().get_container_client().list_blobs = MagicMock(return_value=[get_test_blob()("a")])
|
||||
|
||||
copy_data("source_acc", "dest_acc", "req_id")
|
||||
|
||||
|
@ -82,11 +84,11 @@ class TestBlobOperations(TestCase):
|
|||
blob_name = "blob"
|
||||
|
||||
blob_url = get_blob_url(account_name, container_name, blob_name)
|
||||
self.assertEqual(blob_url, f"https://{account_name}.blob.core.windows.net/{container_name}/{blob_name}")
|
||||
assert blob_url == f"https://{account_name}.blob.core.windows.net/{container_name}/{blob_name}"
|
||||
|
||||
def test_get_blob_url_without_blob_name_should_return_container_url(self):
|
||||
account_name = "account"
|
||||
container_name = "container"
|
||||
|
||||
blob_url = get_blob_url(account_name, container_name)
|
||||
self.assertEqual(blob_url, f"https://{account_name}.blob.core.windows.net/{container_name}/")
|
||||
assert blob_url == f"https://{account_name}.blob.core.windows.net/{container_name}/"
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock, patch
|
||||
from mock import patch, MagicMock
|
||||
|
||||
from DataDeletionTrigger import delete_blob_and_container_if_last_blob
|
||||
|
||||
|
||||
class TestDataDeletionTrigger(TestCase):
|
||||
class TestDataDeletionTrigger():
|
||||
@patch("DataDeletionTrigger.BlobServiceClient")
|
||||
def test_delete_blob_and_container_if_last_blob_deletes_container(self, mock_blob_service_client):
|
||||
blob_url = "https://stalimextest.blob.core.windows.net/c144728c-3c69-4a58-afec-48c2ec8bfd45/test_dataset.txt"
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
from json import JSONDecodeError
|
||||
import os
|
||||
import unittest
|
||||
from unittest import mock
|
||||
from unittest.mock import MagicMock, patch
|
||||
import pytest
|
||||
from mock import MagicMock, patch
|
||||
|
||||
from pydantic import ValidationError
|
||||
from StatusChangedQueueTrigger import get_request_files, main, extract_properties, get_source_dest_for_copy, is_require_data_copy
|
||||
|
@ -10,74 +9,74 @@ from azure.functions.servicebus import ServiceBusMessage
|
|||
from shared_code import constants
|
||||
|
||||
|
||||
class TestPropertiesExtraction(unittest.TestCase):
|
||||
class TestPropertiesExtraction():
|
||||
def test_extract_prop_valid_body_return_all_values(self):
|
||||
message_body = "{ \"data\": { \"request_id\":\"123\",\"new_status\":\"456\" ,\"previous_status\":\"789\" , \"type\":\"101112\", \"workspace_id\":\"ws1\" }}"
|
||||
message = _mock_service_bus_message(body=message_body)
|
||||
req_prop = extract_properties(message)
|
||||
self.assertEqual(req_prop.request_id, "123")
|
||||
self.assertEqual(req_prop.new_status, "456")
|
||||
self.assertEqual(req_prop.previous_status, "789")
|
||||
self.assertEqual(req_prop.type, "101112")
|
||||
self.assertEqual(req_prop.workspace_id, "ws1")
|
||||
assert req_prop.request_id == "123"
|
||||
assert req_prop.new_status == "456"
|
||||
assert req_prop.previous_status == "789"
|
||||
assert req_prop.type == "101112"
|
||||
assert req_prop.workspace_id == "ws1"
|
||||
|
||||
def test_extract_prop_missing_arg_throws(self):
|
||||
message_body = "{ \"data\": { \"status\":\"456\" , \"type\":\"789\", \"workspace_id\":\"ws1\" }}"
|
||||
message = _mock_service_bus_message(body=message_body)
|
||||
self.assertRaises(ValidationError, extract_properties, message)
|
||||
pytest.raises(ValidationError, extract_properties, message)
|
||||
|
||||
message_body = "{ \"data\": { \"request_id\":\"123\", \"type\":\"789\", \"workspace_id\":\"ws1\" }}"
|
||||
message = _mock_service_bus_message(body=message_body)
|
||||
self.assertRaises(ValidationError, extract_properties, message)
|
||||
pytest.raises(ValidationError, extract_properties, message)
|
||||
|
||||
message_body = "{ \"data\": { \"request_id\":\"123\",\"status\":\"456\" , \"workspace_id\":\"ws1\" }}"
|
||||
message = _mock_service_bus_message(body=message_body)
|
||||
self.assertRaises(ValidationError, extract_properties, message)
|
||||
pytest.raises(ValidationError, extract_properties, message)
|
||||
|
||||
message_body = "{ \"data\": { \"request_id\":\"123\",\"status\":\"456\" , \"type\":\"789\" }}"
|
||||
message = _mock_service_bus_message(body=message_body)
|
||||
self.assertRaises(ValidationError, extract_properties, message)
|
||||
pytest.raises(ValidationError, extract_properties, message)
|
||||
|
||||
def test_extract_prop_invalid_json_throws(self):
|
||||
message_body = "Hi"
|
||||
message = _mock_service_bus_message(body=message_body)
|
||||
self.assertRaises(JSONDecodeError, extract_properties, message)
|
||||
pytest.raises(JSONDecodeError, extract_properties, message)
|
||||
|
||||
|
||||
class TestDataCopyProperties(unittest.TestCase):
|
||||
class TestDataCopyProperties():
|
||||
def test_only_specific_status_are_triggering_copy(self):
|
||||
self.assertEqual(is_require_data_copy("Mitzi"), False)
|
||||
self.assertEqual(is_require_data_copy(""), False)
|
||||
self.assertEqual(is_require_data_copy("submit"), False)
|
||||
self.assertEqual(is_require_data_copy("approved"), False)
|
||||
self.assertEqual(is_require_data_copy("REJected"), False)
|
||||
self.assertEqual(is_require_data_copy("blocked"), False)
|
||||
assert not is_require_data_copy("Mitzi")
|
||||
assert not is_require_data_copy("")
|
||||
assert not is_require_data_copy("submit")
|
||||
assert not is_require_data_copy("approved")
|
||||
assert not is_require_data_copy("REJected")
|
||||
assert not is_require_data_copy("blocked")
|
||||
|
||||
# Testing all values that should return true
|
||||
self.assertEqual(is_require_data_copy("submITted"), True)
|
||||
self.assertEqual(is_require_data_copy("submitted"), True)
|
||||
self.assertEqual(is_require_data_copy("approval_in_progress"), True)
|
||||
self.assertEqual(is_require_data_copy("rejection_in_progress"), True)
|
||||
self.assertEqual(is_require_data_copy("blocking_in_progress"), True)
|
||||
assert is_require_data_copy("submITted")
|
||||
assert is_require_data_copy("submitted")
|
||||
assert is_require_data_copy("approval_in_progress")
|
||||
assert is_require_data_copy("rejection_in_progress")
|
||||
assert is_require_data_copy("blocking_in_progress")
|
||||
|
||||
def test_wrong_status_raises_when_getting_storage_account_properties(self):
|
||||
self.assertRaises(Exception, get_source_dest_for_copy, "Miaow", "import")
|
||||
pytest.raises(Exception, get_source_dest_for_copy, "Miaow", "import")
|
||||
|
||||
def test_wrong_type_raises_when_getting_storage_account_properties(self):
|
||||
self.assertRaises(Exception, get_source_dest_for_copy, "accepted", "somethingelse")
|
||||
pytest.raises(Exception, get_source_dest_for_copy, "accepted", "somethingelse")
|
||||
|
||||
|
||||
class TestFileEnumeration(unittest.TestCase):
|
||||
class TestFileEnumeration():
|
||||
@patch("StatusChangedQueueTrigger.set_output_event_to_report_request_files")
|
||||
@patch("StatusChangedQueueTrigger.get_request_files")
|
||||
@patch("StatusChangedQueueTrigger.is_require_data_copy", return_value=False)
|
||||
@mock.patch.dict(os.environ, {"TRE_ID": "tre-id"}, clear=True)
|
||||
@patch.dict(os.environ, {"TRE_ID": "tre-id"}, clear=True)
|
||||
def test_get_request_files_should_be_called_on_submit_stage(self, _, mock_get_request_files, mock_set_output_event_to_report_request_files):
|
||||
message_body = "{ \"data\": { \"request_id\":\"123\",\"new_status\":\"submitted\" ,\"previous_status\":\"draft\" , \"type\":\"export\", \"workspace_id\":\"ws1\" }}"
|
||||
message = _mock_service_bus_message(body=message_body)
|
||||
main(msg=message, stepResultEvent=MagicMock(), dataDeletionEvent=MagicMock())
|
||||
self.assertTrue(mock_get_request_files.called)
|
||||
self.assertTrue(mock_set_output_event_to_report_request_files.called)
|
||||
assert mock_get_request_files.called
|
||||
assert mock_set_output_event_to_report_request_files.called
|
||||
|
||||
@patch("StatusChangedQueueTrigger.set_output_event_to_report_failure")
|
||||
@patch("StatusChangedQueueTrigger.get_request_files")
|
||||
|
@ -86,8 +85,8 @@ class TestFileEnumeration(unittest.TestCase):
|
|||
message_body = "{ \"data\": { \"request_id\":\"123\",\"new_status\":\"fake-status\" ,\"previous_status\":\"None\" , \"type\":\"export\", \"workspace_id\":\"ws1\" }}"
|
||||
message = _mock_service_bus_message(body=message_body)
|
||||
main(msg=message, stepResultEvent=MagicMock(), dataDeletionEvent=MagicMock())
|
||||
self.assertFalse(mock_get_request_files.called)
|
||||
self.assertFalse(mock_set_output_event_to_report_failure.called)
|
||||
assert not mock_get_request_files.called
|
||||
assert not mock_set_output_event_to_report_failure.called
|
||||
|
||||
@patch("StatusChangedQueueTrigger.set_output_event_to_report_failure")
|
||||
@patch("StatusChangedQueueTrigger.get_request_files")
|
||||
|
@ -96,11 +95,11 @@ class TestFileEnumeration(unittest.TestCase):
|
|||
message_body = "{ \"data\": { \"request_id\":\"123\",\"new_status\":\"submitted\" ,\"previous_status\":\"draft\" , \"type\":\"export\", \"workspace_id\":\"ws1\" }}"
|
||||
message = _mock_service_bus_message(body=message_body)
|
||||
main(msg=message, stepResultEvent=MagicMock(), dataDeletionEvent=MagicMock())
|
||||
self.assertTrue(mock_get_request_files.called)
|
||||
self.assertTrue(mock_set_output_event_to_report_failure.called)
|
||||
assert mock_get_request_files.called
|
||||
assert mock_set_output_event_to_report_failure.called
|
||||
|
||||
@patch("StatusChangedQueueTrigger.blob_operations.get_request_files")
|
||||
@mock.patch.dict(os.environ, {"TRE_ID": "tre-id"}, clear=True)
|
||||
@patch.dict(os.environ, {"TRE_ID": "tre-id"}, clear=True)
|
||||
def test_get_request_files_called_with_correct_storage_account(self, mock_get_request_files):
|
||||
source_storage_account_for_submitted_stage = constants.STORAGE_ACCOUNT_NAME_EXPORT_INTERNAL + 'ws1'
|
||||
message_body = "{ \"data\": { \"request_id\":\"123\",\"new_status\":\"submitted\" ,\"previous_status\":\"draft\" , \"type\":\"export\", \"workspace_id\":\"ws1\" }}"
|
||||
|
@ -110,14 +109,14 @@ class TestFileEnumeration(unittest.TestCase):
|
|||
mock_get_request_files.assert_called_with(account_name=source_storage_account_for_submitted_stage, request_id=request_properties.request_id)
|
||||
|
||||
|
||||
class TestFilesDeletion(unittest.TestCase):
|
||||
class TestFilesDeletion():
|
||||
@patch("StatusChangedQueueTrigger.set_output_event_to_trigger_container_deletion")
|
||||
@mock.patch.dict(os.environ, {"TRE_ID": "tre-id"}, clear=True)
|
||||
@patch.dict(os.environ, {"TRE_ID": "tre-id"}, clear=True)
|
||||
def test_delete_request_files_should_be_called_on_cancel_stage(self, mock_set_output_event_to_trigger_container_deletion):
|
||||
message_body = "{ \"data\": { \"request_id\":\"123\",\"new_status\":\"cancelled\" ,\"previous_status\":\"draft\" , \"type\":\"export\", \"workspace_id\":\"ws1\" }}"
|
||||
message = _mock_service_bus_message(body=message_body)
|
||||
main(msg=message, stepResultEvent=MagicMock(), dataDeletionEvent=MagicMock())
|
||||
self.assertTrue(mock_set_output_event_to_trigger_container_deletion.called)
|
||||
assert mock_set_output_event_to_trigger_container_deletion.called
|
||||
|
||||
|
||||
def _mock_service_bus_message(body: str):
|
||||
|
|
Загрузка…
Ссылка в новой задаче