From 68dce4c5fb9a6097eb7a558183f083dc4d43179e Mon Sep 17 00:00:00 2001 From: Tobias Kunze Date: Thu, 6 Nov 2025 15:06:10 +0100 Subject: [PATCH] Make form_config integers optional --- .../core/schemas/form_config_schema.json | 12 +- src/tests/test_form_config.py | 145 ++++++++++++++++++ 2 files changed, 151 insertions(+), 6 deletions(-) diff --git a/src/servala/core/schemas/form_config_schema.json b/src/servala/core/schemas/form_config_schema.json index 15f79df..62fa91b 100644 --- a/src/servala/core/schemas/form_config_schema.json +++ b/src/servala/core/schemas/form_config_schema.json @@ -48,21 +48,21 @@ "description": "Dot-notation path mapping to Kubernetes spec field (e.g., 'spec.parameters.service.fqdn')" }, "max_length": { - "type": "integer", + "type": ["integer", "null"], "description": "Maximum length for text/textarea fields", "minimum": 1 }, "rows": { - "type": "integer", + "type": ["integer", "null"], "description": "Number of rows for textarea fields", "minimum": 1 }, "min_value": { - "type": "number", + "type": ["number", "null"], "description": "Minimum value for number fields" }, "max_value": { - "type": "number", + "type": ["number", "null"], "description": "Maximum value for number fields" }, "choices": { @@ -76,12 +76,12 @@ } }, "min_values": { - "type": "integer", + "type": ["integer", "null"], "description": "Minimum number of values for array fields", "minimum": 0 }, "max_values": { - "type": "integer", + "type": ["integer", "null"], "description": "Maximum number of values for array fields", "minimum": 1 }, diff --git a/src/tests/test_form_config.py b/src/tests/test_form_config.py index 40c5230..913eb59 100644 --- a/src/tests/test_form_config.py +++ b/src/tests/test_form_config.py @@ -1,8 +1,10 @@ from unittest.mock import Mock +import jsonschema from django.db import models from servala.core.crd import generate_custom_form_class +from servala.core.forms import ServiceDefinitionAdminForm from servala.core.models import ControlPlaneCRD @@ -71,3 +73,146 @@ def test_custom_model_form_class_returns_class_when_form_config_exists(): assert result is not None assert hasattr(result, "form_config") + + +def test_form_config_schema_validates_minimal_config(): + form = ServiceDefinitionAdminForm() + schema = form.form_config_schema + + minimal_config = { + "fieldsets": [ + { + "fields": [ + { + "type": "text", + "label": "Service Name", + "controlplane_field_mapping": "spec.serviceName", + } + ] + } + ] + } + + jsonschema.validate(instance=minimal_config, schema=schema) + + +def test_form_config_schema_validates_config_with_null_integers(): + form = ServiceDefinitionAdminForm() + schema = form.form_config_schema + + config_with_nulls = { + "fieldsets": [ + { + "fields": [ + { + "type": "text", + "label": "Service Name", + "controlplane_field_mapping": "spec.serviceName", + "max_length": None, + "required": False, + }, + { + "type": "textarea", + "label": "Description", + "controlplane_field_mapping": "spec.description", + "rows": None, + "max_length": None, + }, + { + "type": "number", + "label": "Port", + "controlplane_field_mapping": "spec.port", + "min_value": None, + "max_value": None, + }, + { + "type": "array", + "label": "Tags", + "controlplane_field_mapping": "spec.tags", + "min_values": None, + "max_values": None, + }, + ] + } + ] + } + + jsonschema.validate(instance=config_with_nulls, schema=schema) + + +def test_form_config_schema_validates_full_config(): + form = ServiceDefinitionAdminForm() + schema = form.form_config_schema + + full_config = { + "fieldsets": [ + { + "title": "Service Configuration", + "fields": [ + { + "type": "text", + "label": "Service Name", + "controlplane_field_mapping": "spec.serviceName", + "help_text": "Enter a unique service name", + "required": True, + "max_length": 100, + }, + { + "type": "email", + "label": "Admin Email", + "controlplane_field_mapping": "spec.adminEmail", + "help_text": "Contact email for service administrator", + "required": True, + "max_length": 255, + }, + { + "type": "textarea", + "label": "Description", + "controlplane_field_mapping": "spec.description", + "help_text": "Describe the service purpose", + "required": False, + "rows": 5, + "max_length": 500, + }, + { + "type": "number", + "label": "Port", + "controlplane_field_mapping": "spec.port", + "help_text": "Service port number", + "required": True, + "min_value": 1, + "max_value": 65535, + }, + { + "type": "choice", + "label": "Environment", + "controlplane_field_mapping": "spec.environment", + "help_text": "Deployment environment", + "required": True, + "choices": [ + ["dev", "Development"], + ["staging", "Staging"], + ["prod", "Production"], + ], + }, + { + "type": "checkbox", + "label": "Enable Monitoring", + "controlplane_field_mapping": "spec.monitoring.enabled", + "help_text": "Enable service monitoring", + "required": False, + }, + { + "type": "array", + "label": "Tags", + "controlplane_field_mapping": "spec.tags", + "help_text": "Service tags for organization", + "required": False, + "min_values": 0, + "max_values": 10, + }, + ], + } + ] + } + jsonschema.validate(instance=full_config, schema=schema)