This is an automated email from the ASF dual-hosted git repository. yasithdev pushed a commit to branch feat/generic-experiment-launcher in repository https://gitbox.apache.org/repos/asf/airavata-portals.git
commit 237b4fe7aaa72a1b578953bf27f0117ebcdfc598 Author: yasithdev <[email protected]> AuthorDate: Fri Apr 24 21:29:11 2026 -0400 fix(launcher): tighten ExperimentDraftSerializer.validate_inputs + add reject tests --- .../django_airavata/apps/api/launcher_serializers.py | 6 +++++- .../apps/api/tests/test_launcher_serializers.py | 15 ++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/airavata-django-portal/django_airavata/apps/api/launcher_serializers.py b/airavata-django-portal/django_airavata/apps/api/launcher_serializers.py index af186fc4d..268e17e55 100644 --- a/airavata-django-portal/django_airavata/apps/api/launcher_serializers.py +++ b/airavata-django-portal/django_airavata/apps/api/launcher_serializers.py @@ -35,10 +35,14 @@ class ExperimentDraftSerializer(serializers.Serializer): runtime = RuntimeSerializer() def validate_inputs(self, value): - # Each input is either a scalar (str/int/float/bool) or a {storage_id, path} object. + scalar_types = (str, int, float, bool) for name, v in value.items(): if isinstance(v, dict): StorageRefSerializer(data=v).is_valid(raise_exception=True) + elif not isinstance(v, scalar_types): + raise serializers.ValidationError( + {name: "input must be a scalar (str/int/float/bool) or a {storage_id, path} object"} + ) return value diff --git a/airavata-django-portal/django_airavata/apps/api/tests/test_launcher_serializers.py b/airavata-django-portal/django_airavata/apps/api/tests/test_launcher_serializers.py index b1101d277..d7a616a2a 100644 --- a/airavata-django-portal/django_airavata/apps/api/tests/test_launcher_serializers.py +++ b/airavata-django-portal/django_airavata/apps/api/tests/test_launcher_serializers.py @@ -3,11 +3,12 @@ from pathlib import Path from unittest import TestCase import jsonschema +from django.conf import settings from django_airavata.apps.api import launcher_serializers -CONTRACTS = Path(__file__).resolve().parents[4] / "tests" / "contracts" +CONTRACTS = Path(settings.BASE_DIR) / "tests" / "contracts" class ExperimentDraftSerializerTest(TestCase): @@ -52,6 +53,18 @@ class ExperimentDraftSerializerTest(TestCase): serializer = launcher_serializers.ExperimentDraftSerializer(data=draft) self.assertFalse(serializer.is_valid()) + def test_serializer_rejects_malformed_file_input(self): + draft = self._valid_draft() + draft["inputs"]["sim_dir"] = {"storage_id": "my-home"} # missing 'path' + serializer = launcher_serializers.ExperimentDraftSerializer(data=draft) + self.assertFalse(serializer.is_valid()) + + def test_serializer_rejects_non_scalar_non_dict_input(self): + draft = self._valid_draft() + draft["inputs"]["steps"] = [1, 2, 3] # neither scalar nor {storage_id, path} + serializer = launcher_serializers.ExperimentDraftSerializer(data=draft) + self.assertFalse(serializer.is_valid()) + class PreviewResponseSchemaTest(TestCase): def test_valid_response(self):
