SIENTIAPDE-1231

Enhance validation script and refactor code in various modules

- Updated the validation script to include automatic code formatting and linting fixes using Ruff.
- Removed the `clean_tmp_files` method from the Gates class to streamline functionality.
- Simplified conditional checks in the OpcRepository for better clarity and error handling.
- Added model ID to the minimal retrain workflow for improved tracking.
- Introduced new test cases for error handling in MLFlow and storage operations, ensuring robustness in repository interactions.
This commit is contained in:
vitor-aignosi
2025-10-15 16:50:38 -03:00
parent ac795c7c53
commit f0fb9b854e
14 changed files with 261 additions and 36 deletions

View File

@@ -16,6 +16,13 @@ def test_filter_specific_variables_null_values():
)
def test_filter_specific_variables_null_values_with_empty_data():
assert (
filter_specific_variables_null_values(DataFrame(), config={'variables': ['variable2']})
is False
)
def test_filter_specific_variables_null_values_with_null_values():
assert (
filter_specific_variables_null_values(

View File

@@ -61,6 +61,11 @@ def minio_repository(mock_boto3, mock_config):
)
def test_close(minio_repository):
minio_repository.close()
minio_repository.s3_client.close.assert_called_once()
def test_ensure_bucket_exists_bucket_exists(minio_repository):
assert minio_repository.ensure_bucket_exists({}) is True

View File

@@ -173,6 +173,11 @@ def test_get_experiment_none_not_create(mlflow, mlflow_repository):
assert str(e) == 'Experiment test not found'
def test_get_model_params(mlflow, mlflow_repository):
output = mlflow_repository.get_model_params('test')
assert output == mlflow.get_run.return_value.data.params
@patch('laborious.utils.repository.model_repository.path')
@patch('laborious.utils.repository.model_repository.rmtree')
@patch('laborious.utils.repository.model_repository.makedirs')
@@ -202,6 +207,35 @@ def test_download_artifacts_success(makedirs, rmtree, path, mlflow_repository):
assert output == mlflow_repository.client.download_artifacts.return_value
@patch('laborious.utils.repository.model_repository.path')
@patch('laborious.utils.repository.model_repository.rmtree')
@patch('laborious.utils.repository.model_repository.makedirs')
def test_download_artifacts_success_path_false(makedirs, rmtree, path, mlflow_repository):
mlflow_repository.get_model_run_id = MagicMock(return_value='test')
path.exists.return_value = False
output = mlflow_repository.dowload_artifacts('test', 'path')
mlflow_repository.get_model_run_id.assert_called_once_with(
model_name='test', stage='Production'
)
path.join.assert_called_once_with('./tmp/artifacts/test', 'path')
path.exists.assert_called_once_with(path.join.return_value)
rmtree.assert_not_called()
makedirs.assert_called_once_with('./tmp/artifacts/test', exist_ok=True)
mlflow_repository.client.download_artifacts.assert_called_once_with(
mlflow_repository.get_model_run_id.return_value, 'path', './tmp/artifacts/test'
)
assert output == mlflow_repository.client.download_artifacts.return_value
def test_get_experiment_error(mlflow, mlflow_repository):
mlflow.get_experiment_by_name.return_value = None
@@ -383,6 +417,15 @@ valid_cases = [
},
['2025-01-01 12:00:00+0000', '2025-01-02 12:00:00+0000'],
),
(
{
'value': {
datetime(2025, 1, 1, 12, 0, 0, tzinfo=None): 1,
datetime(2025, 1, 2, 12, 0, 0, tzinfo=None): 2,
}
},
['2025-01-01 12:00:00+0000', '2025-01-02 12:00:00+0000'],
),
(
{
'value': {
@@ -987,6 +1030,20 @@ def test_retrain_model(mlflow_repository):
}
def test_retrain_model_error(mlflow_repository):
data = MagicMock()
model_name = 'test'
model_config = {'target': 'target', 'transform_flavor': 'sklearn', 'predict_flavor': 'pyfunc'}
mlflow_repository.get_model_run_id = MagicMock(side_effect=Exception('error'))
output = mlflow_repository.retrain_model(data, model_name, model_config, metadata['metadata'])
assert output == {
'success': False,
'experiment': None,
'message': 'Error retraining model test: error',
'traceback': ANY,
}
def test_update_production_model(mlflow_repository):
experiment = {'run_id': '0', 'experiment_id': '0'}
model_name = 'test'

View File

@@ -86,6 +86,15 @@ async def test_set_security_missing_certificates(opc_repository):
assert str(e) == 'Certificate and private key paths must be provided for secure connection.'
@pytest.mark.asyncio
async def test_set_security_missing_client(opc_repository):
opc_repository.client = None
try:
await opc_repository.set_security()
except ValueError as e:
assert str(e) == 'Client must be initialized before setting security'
@pytest.mark.asyncio
async def test_connect_with_security(opc_repository, mock_client):
opc_repository.try_connect = AsyncMock(return_value=(True, {}))
@@ -137,6 +146,21 @@ async def test_try_connect_fail(opc_repository):
assert error_data['attachment_content'] is not None
@pytest.mark.asyncio
async def test_try_connect_no_client(opc_repository):
opc_repository.client = None
result = await opc_repository.try_connect()
assert result == (
False,
{
'notification_id': f'OPC_CONNECTION_ERROR_{opc_repository.id}',
'message': 'Client is not initialized',
'block': 'opc_repository',
'level': NotificationLevel.ERROR,
},
)
@pytest.mark.asyncio
async def test_disconnect(opc_repository, mock_client):
opc_repository.client = mock_client

View File

@@ -1,6 +1,7 @@
from os import environ
from laborious.utils.connectors_config import (
build_minio_config,
build_mlflow_config,
build_mongodb_config,
build_opc_config,
@@ -162,3 +163,33 @@ def test_build_mongo_db_config_with_defaults():
'database_name': 'sientia',
'ttl_index_seconds': 3600,
}
def test_build_minio_config_with_env_vars():
environ['MINIO_ENDPOINT_URL'] = 'http://test-host'
environ['MINIO_ACCESS_KEY'] = 'test-key'
environ['MINIO_SECRET_KEY'] = 'test-secret'
environ['MINIO_REGION_NAME'] = 'test-region'
environ['MINIO_DEFAULT_BUCKET'] = 'test-bucket'
assert build_minio_config() == {
'endpoint_url': 'http://test-host',
'access_key': 'test-key',
'secret_key': 'test-secret',
'region_name': 'test-region',
'default_bucket': 'test-bucket',
}
def test_build_minio_config_with_defaults():
environ.pop('MINIO_ENDPOINT_URL', None)
environ.pop('MINIO_ACCESS_KEY', None)
environ.pop('MINIO_SECRET_KEY', None)
environ.pop('MINIO_REGION_NAME', None)
environ.pop('MINIO_DEFAULT_BUCKET', None)
assert build_minio_config() == {
'endpoint_url': 'http://localhost:9000',
'access_key': 'minioadmin',
'secret_key': 'minioadmin',
'region_name': 'us-east-1',
'default_bucket': 'laborious',
}