""" End-to-end tests for TrainModel workflow – main workflow scenarios. Covers: 1.1.x – Happy-path training (various scenarios from docs/test-scenarios/) 1.2.x – Error paths (MinIO failure, missing DB row) """ import pytest from temporalio.testing import WorkflowEnvironment from temporalio.worker import Worker from e2e.helpers import ( assert_experiment_error, assert_experiment_run_name_set, assert_experiment_status, assert_no_experiment_row, insert_experiment_run, load_scenario, make_workflow_id, start_and_await_workflow, ) from model_manager.workflows.train_model import TrainModel # --------------------------------------------------------------------------- # 1.1 – Happy paths # --------------------------------------------------------------------------- @pytest.mark.asyncio @pytest.mark.integration async def test_scenario_1_1_1_linear_regression_basic( temporal_test_env: WorkflowEnvironment, temporal_worker: Worker, postgres_engine, ): """Scenario 1.1.1 – Linear Regression Basic (cenário 01). Validates the complete training pipeline end-to-end: load_model_metadata → validate_train_params → train_model → update_experiment_run (TRAINING_SUCCESS). """ scenario = load_scenario('01-linear-regression-basic.json') experiment_run_id = scenario['experiment_run_id'] insert_experiment_run(postgres_engine, experiment_run_id) await start_and_await_workflow( temporal_test_env.client, TrainModel.run, scenario, make_workflow_id('test-s1-1-1'), ) assert_experiment_status(postgres_engine, experiment_run_id, 'TRAINING_SUCCESS') assert_experiment_run_name_set(postgres_engine, experiment_run_id) @pytest.mark.asyncio @pytest.mark.integration async def test_scenario_1_1_2_linear_regression_with_scaler( temporal_test_env: WorkflowEnvironment, temporal_worker: Worker, postgres_engine, ): """Scenario 1.1.2 – Linear regression with Standard Scaler (cenário 02).""" scenario = load_scenario('02-linear-regression-with-scaler.json') experiment_run_id = scenario['experiment_run_id'] insert_experiment_run(postgres_engine, experiment_run_id) await start_and_await_workflow( temporal_test_env.client, TrainModel.run, scenario, make_workflow_id('test-s1-1-2'), ) assert_experiment_status(postgres_engine, experiment_run_id, 'TRAINING_SUCCESS') assert_experiment_run_name_set(postgres_engine, experiment_run_id) @pytest.mark.asyncio @pytest.mark.integration async def test_scenario_1_1_3_polynomial_regression_degree2_with_scaler( temporal_test_env: WorkflowEnvironment, temporal_worker: Worker, postgres_engine, ): """Scenario 1.1.3 – Polynomial Regression Degree 2 with Standard Scaler (cenário 03).""" scenario = load_scenario('03-polynomial-regression-degree2.json') experiment_run_id = scenario['experiment_run_id'] insert_experiment_run(postgres_engine, experiment_run_id) await start_and_await_workflow( temporal_test_env.client, TrainModel.run, scenario, make_workflow_id('test-s1-1-3'), ) assert_experiment_status(postgres_engine, experiment_run_id, 'TRAINING_SUCCESS') assert_experiment_run_name_set(postgres_engine, experiment_run_id) @pytest.mark.asyncio @pytest.mark.integration async def test_scenario_1_1_4_polynomial_regression_degree3_with_scaler( temporal_test_env: WorkflowEnvironment, temporal_worker: Worker, postgres_engine, ): """Scenario 1.1.4 – Polynomial regression degree 3 with Standard Scaler (cenário 04).""" scenario = load_scenario('04-polynomial-regression-degree3.json') experiment_run_id = scenario['experiment_run_id'] insert_experiment_run(postgres_engine, experiment_run_id) await start_and_await_workflow( temporal_test_env.client, TrainModel.run, scenario, make_workflow_id('test-s1-1-4'), ) assert_experiment_status(postgres_engine, experiment_run_id, 'TRAINING_SUCCESS') assert_experiment_run_name_set(postgres_engine, experiment_run_id) @pytest.mark.asyncio @pytest.mark.integration async def test_scenario_1_1_5_linear_regression_with_lags( temporal_test_env: WorkflowEnvironment, temporal_worker: Worker, postgres_engine, ): """Scenario 1.1.5 – Linear Regression with lag_train/lag_val per variable (cenário 05).""" scenario = load_scenario('05-linear-regression-with-lags.json') experiment_run_id = scenario['experiment_run_id'] insert_experiment_run(postgres_engine, experiment_run_id) await start_and_await_workflow( temporal_test_env.client, TrainModel.run, scenario, make_workflow_id('test-s1-1-5'), ) assert_experiment_status(postgres_engine, experiment_run_id, 'TRAINING_SUCCESS') assert_experiment_run_name_set(postgres_engine, experiment_run_id) @pytest.mark.asyncio @pytest.mark.integration async def test_scenario_1_1_6_linear_regression_nan_interpolation( temporal_test_env: WorkflowEnvironment, temporal_worker: Worker, postgres_engine, ): """Scenario 1.1.6 – nan_treatment='linear interpolation' (cenário 06).""" scenario = load_scenario('06-linear-regression-nan-interpolation.json') experiment_run_id = scenario['experiment_run_id'] insert_experiment_run(postgres_engine, experiment_run_id) await start_and_await_workflow( temporal_test_env.client, TrainModel.run, scenario, make_workflow_id('test-s1-1-6'), ) assert_experiment_status(postgres_engine, experiment_run_id, 'TRAINING_SUCCESS') @pytest.mark.asyncio @pytest.mark.integration async def test_scenario_1_1_7_linear_regression_static_window_removal( temporal_test_env: WorkflowEnvironment, temporal_worker: Worker, postgres_engine, ): """Scenario 1.1.7 – rem_static_win=true with default static_threshold (cenário 07).""" scenario = load_scenario('07-linear-regression-static-window-removal.json') experiment_run_id = scenario['experiment_run_id'] insert_experiment_run(postgres_engine, experiment_run_id) await start_and_await_workflow( temporal_test_env.client, TrainModel.run, scenario, make_workflow_id('test-s1-1-7'), ) assert_experiment_status(postgres_engine, experiment_run_id, 'TRAINING_SUCCESS') @pytest.mark.asyncio @pytest.mark.integration async def test_scenario_1_1_8_linear_regression_with_limits( temporal_test_env: WorkflowEnvironment, temporal_worker: Worker, postgres_engine, ): """Scenario 1.1.8 – support_filters with min/max limits per variable (cenário 08).""" scenario = load_scenario('08-linear-regression-with-limits.json') experiment_run_id = scenario['experiment_run_id'] insert_experiment_run(postgres_engine, experiment_run_id) await start_and_await_workflow( temporal_test_env.client, TrainModel.run, scenario, make_workflow_id('test-s1-1-8'), ) assert_experiment_status(postgres_engine, experiment_run_id, 'TRAINING_SUCCESS') @pytest.mark.asyncio @pytest.mark.integration async def test_scenario_1_1_9_polynomial_degree2_scaler_and_lags( temporal_test_env: WorkflowEnvironment, temporal_worker: Worker, postgres_engine, ): """Scenario 1.1.9 – Polynomial degree 2, Standard Scaler and lags (cenário 09).""" scenario = load_scenario('09-polynomial-degree2-with-scaler-and-lags.json') experiment_run_id = scenario['experiment_run_id'] insert_experiment_run(postgres_engine, experiment_run_id) await start_and_await_workflow( temporal_test_env.client, TrainModel.run, scenario, make_workflow_id('test-s1-1-9'), ) assert_experiment_status(postgres_engine, experiment_run_id, 'TRAINING_SUCCESS') assert_experiment_run_name_set(postgres_engine, experiment_run_id) @pytest.mark.asyncio @pytest.mark.integration async def test_scenario_1_1_10_linear_regression_with_ar_opt_params( temporal_test_env: WorkflowEnvironment, temporal_worker: Worker, postgres_engine, ): """Scenario 1.1.10 – Linear regression with include_ar in opt_params (cenário 10).""" scenario = load_scenario('10-linear-regression-with-ar.json') experiment_run_id = scenario['experiment_run_id'] insert_experiment_run(postgres_engine, experiment_run_id) await start_and_await_workflow( temporal_test_env.client, TrainModel.run, scenario, make_workflow_id('test-s1-1-10'), ) assert_experiment_status(postgres_engine, experiment_run_id, 'TRAINING_SUCCESS') assert_experiment_run_name_set(postgres_engine, experiment_run_id) @pytest.mark.asyncio @pytest.mark.integration async def test_scenario_1_1_11_linear_regression_static_threshold_custom( temporal_test_env: WorkflowEnvironment, temporal_worker: Worker, postgres_engine, ): """Scenario 1.1.11 – rem_static_win=true with custom static_threshold (cenário 11).""" scenario = load_scenario('11-linear-regression-static-threshold-custom.json') experiment_run_id = scenario['experiment_run_id'] insert_experiment_run(postgres_engine, experiment_run_id) await start_and_await_workflow( temporal_test_env.client, TrainModel.run, scenario, make_workflow_id('test-s1-1-11'), ) assert_experiment_status(postgres_engine, experiment_run_id, 'TRAINING_SUCCESS') @pytest.mark.asyncio @pytest.mark.integration async def test_scenario_1_1_12_alternate_date_format_dd_mm_yyyy( temporal_test_env: WorkflowEnvironment, temporal_worker: Worker, postgres_engine, ): """Scenario 1.1.12 – DATA column and dd/MM/yyyy format CSV (cenário 12).""" scenario = load_scenario('12-angular-test-date-format.json') experiment_run_id = scenario['experiment_run_id'] insert_experiment_run( postgres_engine, experiment_run_id, file_name='training_data_dd_mm_yyyy.csv', ) await start_and_await_workflow( temporal_test_env.client, TrainModel.run, scenario, make_workflow_id('test-s1-1-12'), ) assert_experiment_status(postgres_engine, experiment_run_id, 'TRAINING_SUCCESS') assert_experiment_run_name_set(postgres_engine, experiment_run_id) @pytest.mark.asyncio @pytest.mark.integration async def test_scenario_1_1_13_alternate_csv_narrow_date_window( temporal_test_env: WorkflowEnvironment, temporal_worker: Worker, postgres_engine, ): """Scenario 1.1.13 – Same alternate CSV as 12 with date window (cenário 13).""" scenario = load_scenario('13-angular-test-double-date-column.json') experiment_run_id = scenario['experiment_run_id'] insert_experiment_run( postgres_engine, experiment_run_id, file_name='training_data_dd_mm_yyyy.csv', ) await start_and_await_workflow( temporal_test_env.client, TrainModel.run, scenario, make_workflow_id('test-s1-1-13'), ) assert_experiment_status(postgres_engine, experiment_run_id, 'TRAINING_SUCCESS') @pytest.mark.asyncio @pytest.mark.integration async def test_scenario_1_1_14_polynomial_with_support_filters( temporal_test_env: WorkflowEnvironment, temporal_worker: Worker, postgres_engine, ): """Scenario 1.1.14 – Polynomial degree 4, Standard Scaler, line support filters (cenário 14).""" scenario = load_scenario('14-angular-test-polynomial-support-filters.json') experiment_run_id = scenario['experiment_run_id'] insert_experiment_run(postgres_engine, experiment_run_id) await start_and_await_workflow( temporal_test_env.client, TrainModel.run, scenario, make_workflow_id('test-s1-1-14'), ) assert_experiment_status(postgres_engine, experiment_run_id, 'TRAINING_SUCCESS') @pytest.mark.asyncio @pytest.mark.integration async def test_scenario_1_1_15_linear_regression_custom_target_column_name( temporal_test_env: WorkflowEnvironment, temporal_worker: Worker, postgres_engine, ): """Scenario 1.1.15 – Target column is not named ``target``; report path uses target_variable.""" scenario = load_scenario('15-linear-regression-custom-target-column.json') experiment_run_id = scenario['experiment_run_id'] insert_experiment_run( postgres_engine, experiment_run_id, file_name='training_data_custom_target.csv', ) await start_and_await_workflow( temporal_test_env.client, TrainModel.run, scenario, make_workflow_id('test-s1-1-15'), ) assert_experiment_status(postgres_engine, experiment_run_id, 'TRAINING_SUCCESS') assert_experiment_run_name_set(postgres_engine, experiment_run_id) @pytest.mark.asyncio @pytest.mark.integration async def test_scenario_1_1_16_naive_timestamp_header_column( temporal_test_env: WorkflowEnvironment, temporal_worker: Worker, postgres_engine, ): """Scenario 1.1.16 – CSV uses ``Timestamp`` header; snake_case date_column/date_format.""" scenario = load_scenario('16-linear-regression-naive-timestamp-header.json') experiment_run_id = scenario['experiment_run_id'] insert_experiment_run( postgres_engine, experiment_run_id, file_name='training_data_timestamp_naive.csv', ) await start_and_await_workflow( temporal_test_env.client, TrainModel.run, scenario, make_workflow_id('test-s1-1-16'), ) assert_experiment_status(postgres_engine, experiment_run_id, 'TRAINING_SUCCESS') assert_experiment_run_name_set(postgres_engine, experiment_run_id) @pytest.mark.asyncio @pytest.mark.integration async def test_scenario_1_1_17_linear_regression_blank_timestamp_row_dropped( temporal_test_env: WorkflowEnvironment, temporal_worker: Worker, postgres_engine, ): """Scenario 1.1.17 – CSV has one empty timestamp cell; row is dropped and training succeeds.""" scenario = load_scenario('17-linear-regression-blank-timestamp-row.json') experiment_run_id = scenario['experiment_run_id'] insert_experiment_run( postgres_engine, experiment_run_id, file_name='training_data_blank_timestamp_row.csv', ) await start_and_await_workflow( temporal_test_env.client, TrainModel.run, scenario, make_workflow_id('test-s1-1-17'), ) assert_experiment_status(postgres_engine, experiment_run_id, 'TRAINING_SUCCESS') assert_experiment_run_name_set(postgres_engine, experiment_run_id) # --------------------------------------------------------------------------- # 1.2 – Error paths # --------------------------------------------------------------------------- @pytest.mark.asyncio @pytest.mark.integration async def test_scenario_1_2_1_minio_file_not_found( temporal_test_env: WorkflowEnvironment, temporal_worker: Worker, postgres_engine, ): """Scenario 1.2.1 – Training file does not exist in MinIO → TRAINING_ERROR.""" scenario = load_scenario('01-linear-regression-basic.json') scenario = {**scenario, 'experiment_run_id': 2001, 'file_name': 'does_not_exist.csv'} experiment_run_id = 2001 insert_experiment_run(postgres_engine, experiment_run_id) with pytest.raises(Exception): await start_and_await_workflow( temporal_test_env.client, TrainModel.run, scenario, make_workflow_id('test-s1-2-1'), ) assert_experiment_error( postgres_engine, experiment_run_id, expected_status='TRAINING_ERROR', error_substr='does_not_exist', ) @pytest.mark.asyncio @pytest.mark.integration async def test_scenario_1_2_2_experiment_run_id_not_in_db( temporal_test_env: WorkflowEnvironment, temporal_worker: Worker, postgres_engine, ): """Scenario 1.2.2 – experiment_run_id row absent → update_experiment_run raises.""" scenario = load_scenario('01-linear-regression-basic.json') scenario = {**scenario, 'experiment_run_id': 9999} # Intentionally NOT inserting the row with pytest.raises(Exception): await start_and_await_workflow( temporal_test_env.client, TrainModel.run, scenario, make_workflow_id('test-s1-2-2'), ) assert_no_experiment_row(postgres_engine, 9999)