From 1f6697d0af0db8605fe3f48ae4fab1a28b7f4c53 Mon Sep 17 00:00:00 2001 From: "sdkv2-dri-agent[bot]" <306176125+sdkv2-dri-agent[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 17:01:12 +0000 Subject: [PATCH 1/2] Add regression test for IcM #TEST-12345 --- .../unittests/test_defect_icm_TEST-12345.py | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 sdk/ml/azure-ai-ml/tests/unittests/test_defect_icm_TEST-12345.py diff --git a/sdk/ml/azure-ai-ml/tests/unittests/test_defect_icm_TEST-12345.py b/sdk/ml/azure-ai-ml/tests/unittests/test_defect_icm_TEST-12345.py new file mode 100644 index 000000000000..bb641ebecc5d --- /dev/null +++ b/sdk/ml/azure-ai-ml/tests/unittests/test_defect_icm_TEST-12345.py @@ -0,0 +1,62 @@ +# --------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# --------------------------------------------------------- + +import pytest +from contextlib import nullcontext as does_not_raise + +from azure.ai.ml import command +from azure.ai.ml.exceptions import ValidationException + + +@pytest.mark.unittest +@pytest.mark.defect_regression +class TestDefectIcmTEST_12345: + """Tests for defect reported in IcM #TEST-12345. + + Root cause: CommandJob serialization called .lower() on compute without validating None, + and _validate() did not enforce compute for non-local runs, leading to AttributeError instead + of a clear ValidationException. + """ + + @pytest.mark.parametrize( + "compute_value, expected", + [ + (None, pytest.raises(ValidationException)), + ("", pytest.raises(ValidationException)), + ("local", does_not_raise()), + ("cpu-cluster", does_not_raise()), + ], + ids=["compute_is_none", "compute_is_empty", "compute_local", "compute_valid_cluster"], + ) + def test_command_job_requires_compute_or_local(self, compute_value, expected): + """Regression test: ensure CommandJob with missing compute fails validation with a clear error, + and valid compute/local values succeed without raising. + + This would have caught the AttributeError('NoneType' has no attribute 'lower') thrown during + _to_rest_object() when compute was None. + """ + job = command( + command="echo hello", + environment="AzureML-sklearn-1.0-ubuntu20.04-py38-cpu:1", + ) + job.compute = compute_value + + with expected: + # _to_rest_object triggers _validate() and serialization + job._to_rest_object() + + def test_error_message_is_clear_when_compute_is_none(self): + """Ensure the SDK raises a friendly ValidationException that mentions compute is required.""" + job = command( + command="echo hello", + environment="AzureML-sklearn-1.0-ubuntu20.04-py38-cpu:1", + ) + job.compute = None + + with pytest.raises(ValidationException) as ex: + job._to_rest_object() + + # Message should clearly indicate compute is required (non-local) + assert "compute" in str(ex.value).lower() + assert "required" in str(ex.value).lower() From 4c72dd0d8631badf677f6121f52d972538011854 Mon Sep 17 00:00:00 2001 From: "sdkv2-dri-agent[bot]" <306176125+sdkv2-dri-agent[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 17:01:15 +0000 Subject: [PATCH 2/2] Add similar defect test for IcM #TEST-12345 --- .../test_similar_defect_sweep_TEST-12345.py | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 sdk/ml/azure-ai-ml/tests/unittests/test_similar_defect_sweep_TEST-12345.py diff --git a/sdk/ml/azure-ai-ml/tests/unittests/test_similar_defect_sweep_TEST-12345.py b/sdk/ml/azure-ai-ml/tests/unittests/test_similar_defect_sweep_TEST-12345.py new file mode 100644 index 000000000000..249219fb51b5 --- /dev/null +++ b/sdk/ml/azure-ai-ml/tests/unittests/test_similar_defect_sweep_TEST-12345.py @@ -0,0 +1,63 @@ +# --------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# --------------------------------------------------------- + +import pytest +from contextlib import nullcontext as does_not_raise + +from azure.ai.ml import command +from azure.ai.ml.exceptions import ValidationException +from azure.ai.ml.sweep import SweepJob, Objective + + +@pytest.mark.unittest +@pytest.mark.defect_regression +class TestSimilarDefectSweepTEST_12345: + """Similar defect test: SweepJob.compute=None should raise ValidationException instead of AttributeError. + + Root cause pattern: unsafe .lower() normalization on compute and missing validation for non-local runs. + """ + + @pytest.mark.parametrize( + "compute_value, expected", + [ + (None, pytest.raises(ValidationException)), + ("", pytest.raises(ValidationException)), + ("cpu-cluster", does_not_raise()), + ], + ids=["compute_none", "compute_empty", "compute_valid"], + ) + def test_sweep_job_requires_compute(self, compute_value, expected): + # Minimal trial command job used by sweep + trial_cmd = command( + command="echo trial", + environment="AzureML-sklearn-1.0-ubuntu20.04-py38-cpu:1", + ) + + sweep = SweepJob( + trial=trial_cmd, + sampling_algorithm="random", + objective=Objective(goal="maximize", primary_metric="accuracy"), + compute=compute_value, + ) + + with expected: + sweep._to_rest_object() + + def test_sweep_job_message_is_clear_when_compute_missing(self): + trial_cmd = command( + command="echo trial", + environment="AzureML-sklearn-1.0-ubuntu20.04-py38-cpu:1", + ) + sweep = SweepJob( + trial=trial_cmd, + sampling_algorithm="random", + objective=Objective(goal="maximize", primary_metric="accuracy"), + compute=None, + ) + + with pytest.raises(ValidationException) as ex: + sweep._to_rest_object() + + assert "compute" in str(ex.value).lower() + assert "required" in str(ex.value).lower()