Files
sientia-dataops-model-manager/tests/utils/models/test_train_model_params.py

380 lines
16 KiB
Python

"""Unit tests for TrainModelParams with 100% coverage."""
import pytest
@pytest.fixture
def valid_train_params_dict():
"""Create a valid dictionary for TrainModelParams."""
return {
'variable_columns': ['var1', 'var2'],
'lag_train': {'var1': 5, 'var2': 5},
'lag_val': {'var1': 3, 'var2': 3},
'target_variable': 'target',
'rem_static_win': True,
'low_lim': {'var1': 0.0, 'var2': 1.0},
'upp_lim': {'var1': 10.0, 'var2': 20.0},
'window': 10,
'use_scaler': True,
'include_ar': False,
'bucket_name': 'test-bucket',
'file_name': 'test-file.csv',
'line_separator': ',',
'decimal_separator': '.',
'train_size': 80,
'shuffle': True,
'experiment_run_id': 1,
'experiment_name': 'test_experiment',
'removed_intervals': [],
'model_name': 'Linear Regression',
'degree': 1,
'interaction_only': False,
'nan_treatment': 'drop',
'start_date': None,
'end_date': None,
'scaler_name': 'Standard Scaler',
'support_filters': {},
}
def test_train_model_params_from_dict_success(valid_train_params_dict):
"""Test TrainModelParams.from_dict with valid data."""
from model_manager.utils.models.train_model_params import TrainModelParams
params = TrainModelParams.from_dict(valid_train_params_dict)
assert params.variable_columns == ['var1', 'var2']
assert params.lag_train == {'var1': 5, 'var2': 5}
assert params.lag_val == {'var1': 3, 'var2': 3}
assert params.target_variable == 'target'
assert params.rem_static_win is True
assert params.low_lim == {'var1': 0.0, 'var2': 1.0}
assert params.upp_lim == {'var1': 10.0, 'var2': 20.0}
assert params.window == 10
assert params.use_scaler is True
assert params.include_ar is False
assert params.bucket_name == 'test-bucket'
assert params.file_name == 'test-file.csv'
assert params.line_separator == ','
assert params.decimal_separator == '.'
assert params.train_size == 80
assert params.shuffle is True
assert params.experiment_run_id == 1
assert params.experiment_name == 'test_experiment'
assert params.removed_intervals == []
def test_train_model_params_check_none_raises_value_error():
"""Test _check_none raises ValueError when value is None."""
from model_manager.utils.models.train_model_params import TrainModelParams
with pytest.raises(ValueError, match='test_field is required and cannot be None'):
TrainModelParams._check_none(None, str, 'test_field')
def test_train_model_params_check_none_raises_type_error():
"""Test _check_none raises TypeError when type is incorrect."""
from model_manager.utils.models.train_model_params import TrainModelParams
with pytest.raises(TypeError, match='test_field must be of type str, but got int'):
TrainModelParams._check_none(123, str, 'test_field')
def test_train_model_params_check_none_success():
"""Test _check_none returns value when valid."""
from model_manager.utils.models.train_model_params import TrainModelParams
result = TrainModelParams._check_none('test_value', str, 'test_field')
assert result == 'test_value'
def test_train_model_params_check_type_raises_type_error():
"""Test _check_type raises TypeError when type is incorrect."""
from model_manager.utils.models.train_model_params import TrainModelParams
with pytest.raises(TypeError, match='test_field must be of type int, but got str'):
TrainModelParams._check_type('not_an_int', int, 'test_field')
def test_train_model_params_check_type_success():
"""Test _check_type returns value when valid."""
from model_manager.utils.models.train_model_params import TrainModelParams
result = TrainModelParams._check_type(42, int, 'test_field')
assert result == 42
def test_train_model_params_check_type_with_none():
"""Test _check_type allows None value."""
from model_manager.utils.models.train_model_params import TrainModelParams
result = TrainModelParams._check_type(None, str, 'test_field')
assert result is None
def test_train_model_params_from_dict_missing_field(valid_train_params_dict):
"""Test from_dict raises ValueError when required field is missing."""
from model_manager.utils.models.train_model_params import TrainModelParams
del valid_train_params_dict['variable_columns']
with pytest.raises(ValueError, match='variable_columns is required and cannot be None'):
TrainModelParams.from_dict(valid_train_params_dict)
def test_train_model_params_from_dict_wrong_type(valid_train_params_dict):
"""Test from_dict raises TypeError when field has wrong type."""
from model_manager.utils.models.train_model_params import TrainModelParams
valid_train_params_dict['lag_train'] = 'not_a_dict'
with pytest.raises(TypeError, match='lag_train must be of type dict, but got str'):
TrainModelParams.from_dict(valid_train_params_dict)
def test_train_model_params_from_dict_with_none_removed_intervals(valid_train_params_dict):
"""Test from_dict allows None for removed_intervals."""
from model_manager.utils.models.train_model_params import TrainModelParams
valid_train_params_dict['removed_intervals'] = None
params = TrainModelParams.from_dict(valid_train_params_dict)
assert params.removed_intervals is None
def test_validate_business_rules_success(valid_train_params_dict):
"""Test validate_business_rules with valid parameters."""
from model_manager.utils.models.train_model_params import TrainModelParams
params = TrainModelParams.from_dict(valid_train_params_dict)
params.validate_business_rules() # Should not raise
def test_validate_business_rules_train_size_too_low(valid_train_params_dict):
"""Test validate_business_rules raises error when train_size < 10."""
from model_manager.utils.models.train_model_params import TrainModelParams
valid_train_params_dict['train_size'] = 5
params = TrainModelParams.from_dict(valid_train_params_dict)
with pytest.raises(ValueError, match='train_size must be between 10 and 100, got 5'):
params.validate_business_rules()
def test_validate_business_rules_train_size_too_high(valid_train_params_dict):
"""Test validate_business_rules raises error when train_size > 100."""
from model_manager.utils.models.train_model_params import TrainModelParams
valid_train_params_dict['train_size'] = 101
params = TrainModelParams.from_dict(valid_train_params_dict)
with pytest.raises(ValueError, match='train_size must be between 10 and 100, got 101'):
params.validate_business_rules()
def test_validate_business_rules_empty_variable_columns(valid_train_params_dict):
"""Test validate_business_rules raises error when variable_columns is empty."""
from model_manager.utils.models.train_model_params import TrainModelParams
valid_train_params_dict['variable_columns'] = []
params = TrainModelParams.from_dict(valid_train_params_dict)
with pytest.raises(ValueError, match='variable_columns cannot be empty'):
params.validate_business_rules()
def test_validate_business_rules_negative_lag_train(valid_train_params_dict):
"""Test validate_business_rules raises error when lag_train is negative."""
from model_manager.utils.models.train_model_params import TrainModelParams
valid_train_params_dict['lag_train'] = {'var1': -1, 'var2': 5}
params = TrainModelParams.from_dict(valid_train_params_dict)
with pytest.raises(ValueError, match='lag_train for var1 must be non-negative, got -1'):
params.validate_business_rules()
def test_validate_business_rules_negative_lag_val(valid_train_params_dict):
"""Test validate_business_rules raises error when lag_val is negative."""
from model_manager.utils.models.train_model_params import TrainModelParams
valid_train_params_dict['lag_val'] = {'var1': 3, 'var2': -2}
params = TrainModelParams.from_dict(valid_train_params_dict)
with pytest.raises(ValueError, match='lag_val for var2 must be non-negative, got -2'):
params.validate_business_rules()
def test_validate_business_rules_negative_window(valid_train_params_dict):
"""Test validate_business_rules raises error when window is negative."""
from model_manager.utils.models.train_model_params import TrainModelParams
valid_train_params_dict['window'] = -5
params = TrainModelParams.from_dict(valid_train_params_dict)
with pytest.raises(ValueError, match='window must be non-negative, got -5'):
params.validate_business_rules()
def test_validate_business_rules_mismatched_limit_keys(valid_train_params_dict):
"""Test validate_business_rules raises error when low_lim and upp_lim keys don't match."""
from model_manager.utils.models.train_model_params import TrainModelParams
valid_train_params_dict['low_lim'] = {'var1': 0.0}
valid_train_params_dict['upp_lim'] = {'var1': 10.0, 'var2': 20.0}
params = TrainModelParams.from_dict(valid_train_params_dict)
with pytest.raises(ValueError, match='low_lim and upp_lim must have the same keys'):
params.validate_business_rules()
def test_validate_business_rules_low_lim_greater_than_upp_lim(valid_train_params_dict):
"""Test validate_business_rules raises error when low_lim >= upp_lim."""
from model_manager.utils.models.train_model_params import TrainModelParams
valid_train_params_dict['low_lim'] = {'var1': 15.0, 'var2': 1.0}
valid_train_params_dict['upp_lim'] = {'var1': 10.0, 'var2': 20.0}
params = TrainModelParams.from_dict(valid_train_params_dict)
with pytest.raises(ValueError, match='low_lim must be less than upp_lim for variable "var1"'):
params.validate_business_rules()
def test_validate_business_rules_low_lim_equal_to_upp_lim(valid_train_params_dict):
"""Test validate_business_rules raises error when low_lim == upp_lim."""
from model_manager.utils.models.train_model_params import TrainModelParams
valid_train_params_dict['low_lim'] = {'var1': 10.0, 'var2': 1.0}
valid_train_params_dict['upp_lim'] = {'var1': 10.0, 'var2': 20.0}
params = TrainModelParams.from_dict(valid_train_params_dict)
with pytest.raises(ValueError, match='low_lim must be less than upp_lim for variable "var1"'):
params.validate_business_rules()
def test_validate_business_rules_empty_bucket_name(valid_train_params_dict):
"""Test validate_business_rules raises error when bucket_name is empty."""
from model_manager.utils.models.train_model_params import TrainModelParams
valid_train_params_dict['bucket_name'] = ''
params = TrainModelParams.from_dict(valid_train_params_dict)
with pytest.raises(ValueError, match='bucket_name cannot be empty or whitespace'):
params.validate_business_rules()
def test_validate_business_rules_whitespace_bucket_name(valid_train_params_dict):
"""Test validate_business_rules raises error when bucket_name is whitespace."""
from model_manager.utils.models.train_model_params import TrainModelParams
valid_train_params_dict['bucket_name'] = ' '
params = TrainModelParams.from_dict(valid_train_params_dict)
with pytest.raises(ValueError, match='bucket_name cannot be empty or whitespace'):
params.validate_business_rules()
def test_validate_business_rules_empty_file_name(valid_train_params_dict):
"""Test validate_business_rules raises error when file_name is empty."""
from model_manager.utils.models.train_model_params import TrainModelParams
valid_train_params_dict['file_name'] = ''
params = TrainModelParams.from_dict(valid_train_params_dict)
with pytest.raises(ValueError, match='file_name cannot be empty or whitespace'):
params.validate_business_rules()
def test_validate_business_rules_whitespace_file_name(valid_train_params_dict):
"""Test validate_business_rules raises error when file_name is whitespace."""
from model_manager.utils.models.train_model_params import TrainModelParams
valid_train_params_dict['file_name'] = ' \t '
params = TrainModelParams.from_dict(valid_train_params_dict)
with pytest.raises(ValueError, match='file_name cannot be empty or whitespace'):
params.validate_business_rules()
def test_validate_business_rules_empty_experiment_name(valid_train_params_dict):
"""Test validate_business_rules raises error when experiment_name is empty."""
from model_manager.utils.models.train_model_params import TrainModelParams
valid_train_params_dict['experiment_name'] = ''
params = TrainModelParams.from_dict(valid_train_params_dict)
with pytest.raises(ValueError, match='experiment_name cannot be empty or whitespace'):
params.validate_business_rules()
def test_validate_business_rules_whitespace_experiment_name(valid_train_params_dict):
"""Test validate_business_rules raises error when experiment_name is whitespace."""
from model_manager.utils.models.train_model_params import TrainModelParams
valid_train_params_dict['experiment_name'] = ' \n '
params = TrainModelParams.from_dict(valid_train_params_dict)
with pytest.raises(ValueError, match='experiment_name cannot be empty or whitespace'):
params.validate_business_rules()
def test_validate_business_rules_train_size_boundary_10(valid_train_params_dict):
"""Test validate_business_rules accepts train_size = 10 (lower boundary)."""
from model_manager.utils.models.train_model_params import TrainModelParams
valid_train_params_dict['train_size'] = 10
params = TrainModelParams.from_dict(valid_train_params_dict)
params.validate_business_rules() # Should not raise
def test_validate_business_rules_train_size_boundary_100(valid_train_params_dict):
"""Test validate_business_rules accepts train_size = 100 (upper boundary)."""
from model_manager.utils.models.train_model_params import TrainModelParams
valid_train_params_dict['train_size'] = 100
params = TrainModelParams.from_dict(valid_train_params_dict)
params.validate_business_rules() # Should not raise
def test_validate_business_rules_zero_lag_train(valid_train_params_dict):
"""Test validate_business_rules accepts lag_train = 0."""
from model_manager.utils.models.train_model_params import TrainModelParams
valid_train_params_dict['lag_train'] = {'var1': 0, 'var2': 0}
params = TrainModelParams.from_dict(valid_train_params_dict)
params.validate_business_rules() # Should not raise
def test_validate_business_rules_zero_lag_val(valid_train_params_dict):
"""Test validate_business_rules accepts lag_val = 0."""
from model_manager.utils.models.train_model_params import TrainModelParams
valid_train_params_dict['lag_val'] = {'var1': 0, 'var2': 0}
params = TrainModelParams.from_dict(valid_train_params_dict)
params.validate_business_rules() # Should not raise
def test_validate_business_rules_zero_window(valid_train_params_dict):
"""Test validate_business_rules accepts window = 0."""
from model_manager.utils.models.train_model_params import TrainModelParams
valid_train_params_dict['window'] = 0
params = TrainModelParams.from_dict(valid_train_params_dict)
params.validate_business_rules() # Should not raise
def test_validate_business_rules_empty_limits(valid_train_params_dict):
"""Test validate_business_rules accepts empty low_lim and upp_lim."""
from model_manager.utils.models.train_model_params import TrainModelParams
valid_train_params_dict['low_lim'] = {}
valid_train_params_dict['upp_lim'] = {}
params = TrainModelParams.from_dict(valid_train_params_dict)
params.validate_business_rules() # Should not raise