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 def test_custom_model_form_class_is_none_when_no_form_config(): crd = Mock(spec=ControlPlaneCRD) service_def = Mock() service_def.form_config = None crd.service_definition = service_def crd.django_model = Mock() if not ( crd.django_model and crd.service_definition and crd.service_definition.form_config and crd.service_definition.form_config.get("fieldsets") ): result = None else: result = generate_custom_form_class( crd.service_definition.form_config, crd.django_model ) assert result is None def test_custom_model_form_class_returns_class_when_form_config_exists(): crd = Mock(spec=ControlPlaneCRD) service_def = Mock() service_def.form_config = { "fieldsets": [ { "title": "General", "fields": [ { "type": "text", "label": "Name", "controlplane_field_mapping": "name", "required": True, } ], } ] } crd.service_definition = service_def class TestModel(models.Model): name = models.CharField(max_length=100) class Meta: app_label = "test" crd.django_model = TestModel if not ( crd.django_model and crd.service_definition and crd.service_definition.form_config and crd.service_definition.form_config.get("fieldsets") ): result = None else: result = generate_custom_form_class( crd.service_definition.form_config, crd.django_model ) 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) def test_choice_field_uses_custom_choices_from_form_config(): """Test that choice fields use custom choices when provided in form_config""" class TestModel(models.Model): name = models.CharField(max_length=100) environment = models.CharField( max_length=20, choices=[ ("dev", "Development"), ("staging", "Staging"), ("prod", "Production"), ("test", "Testing"), ], ) class Meta: app_label = "test" form_config = { "fieldsets": [ { "title": "General", "fields": [ { "type": "text", "label": "Name", "controlplane_field_mapping": "name", "required": True, }, { "type": "choice", "label": "Environment", "controlplane_field_mapping": "environment", "required": True, "choices": [["dev", "Development"], ["prod", "Production"]], }, ], } ] } form_class = generate_custom_form_class(form_config, TestModel) form = form_class() environment_field = form.fields["environment"] assert list(environment_field.choices) == [ ("dev", "Development"), ("prod", "Production"), ] assert hasattr(environment_field, "_controlplane_choices") assert len(environment_field._controlplane_choices) == 5 # 4 choices + empty choice def test_choice_field_uses_control_plane_choices_when_no_custom_choices(): class TestModel(models.Model): name = models.CharField(max_length=100) environment = models.CharField( max_length=20, choices=[ ("dev", "Development"), ("staging", "Staging"), ("prod", "Production"), ], ) class Meta: app_label = "test" form_config = { "fieldsets": [ { "title": "General", "fields": [ { "type": "text", "label": "Name", "controlplane_field_mapping": "name", "required": True, }, { "type": "choice", "label": "Environment", "controlplane_field_mapping": "environment", "required": True, }, ], } ] } form_class = generate_custom_form_class(form_config, TestModel) form = form_class() environment_field = form.fields["environment"] choices_list = list(environment_field.choices) assert len(choices_list) == 4 # 3 choices + empty choice assert ("dev", "Development") in choices_list def test_choice_field_validates_against_control_plane_choices(): class TestModel(models.Model): name = models.CharField(max_length=100) environment = models.CharField( max_length=20, choices=[ ("dev", "Development"), ("staging", "Staging"), ("prod", "Production"), ], ) class Meta: app_label = "test" form_config = { "fieldsets": [ { "title": "General", "fields": [ { "type": "text", "label": "Name", "controlplane_field_mapping": "name", "required": True, }, { "type": "choice", "label": "Environment", "controlplane_field_mapping": "environment", "required": True, "choices": [["dev", "Development"], ["prod", "Production"]], }, ], } ] } form_class = generate_custom_form_class(form_config, TestModel) form = form_class(data={"name": "test-service", "environment": "dev"}) form.fields["context"].required = False # Skip context validation assert form.is_valid(), f"Form should be valid but has errors: {form.errors}" form = form_class(data={"name": "test-service", "environment": "prod"}) form.fields["context"].required = False # Skip context validation assert form.is_valid(), f"Form should be valid but has errors: {form.errors}" form = form_class(data={"name": "test-service", "environment": "invalid"}) form.fields["context"].required = False # Skip context validation assert not form.is_valid() assert "environment" in form.errors def test_admin_form_validates_choice_values_against_schema(): from servala.core.forms import ServiceDefinitionAdminForm form = ServiceDefinitionAdminForm() mock_crd = Mock() mock_crd.resource_schema = { "properties": { "spec": { "properties": { "environment": { "type": "string", "enum": ["dev", "staging", "prod"], } } } } } valid_form_config = { "fieldsets": [ { "fields": [ { "type": "text", "label": "Name", "controlplane_field_mapping": "name", }, { "type": "choice", "label": "Environment", "controlplane_field_mapping": "spec.environment", "choices": [["dev", "Development"], ["prod", "Production"]], }, ] } ] } spec_schema = mock_crd.resource_schema["properties"]["spec"] errors = [] for field in valid_form_config["fieldsets"][0]["fields"]: if field.get("type") == "choice": form._validate_choice_field( field, field["controlplane_field_mapping"], spec_schema, "spec", errors ) assert len(errors) == 0, f"Expected no errors but got: {errors}" invalid_form_config = { "fieldsets": [ { "fields": [ { "type": "text", "label": "Name", "controlplane_field_mapping": "name", }, { "type": "choice", "label": "Environment", "controlplane_field_mapping": "spec.environment", "choices": [ ["dev", "Development"], ["invalid", "Invalid Environment"], ], }, ] } ] } errors = [] for field in invalid_form_config["fieldsets"][0]["fields"]: if field.get("type") == "choice": form._validate_choice_field( field, field["controlplane_field_mapping"], spec_schema, "spec", errors ) assert len(errors) > 0, "Expected validation errors but got none" error_message = str(errors[0]) assert "invalid" in error_message.lower() assert "Environment" in error_message