import pytest from netbox_services.models import Service, ServiceResourceBinding, ServiceTemplate @pytest.fixture def tenant(db): from tenancy.models import Tenant return Tenant.objects.create(name="test-tenant", slug="test-tenant") @pytest.fixture def service_template(db): return ServiceTemplate.objects.create( name="test-template", status="draft", description="A test service template", tree=[ { "name": "device", "object_type": "dcim.device", "quantity": 1, "selector": {"type": "netbox_filter", "filters": {}}, "scope": "service", "children": [], } ], ) @pytest.fixture def service(db, service_template, tenant): return Service.objects.create( name="test-service", service_template=service_template, tenant=tenant, status="planned", ) @pytest.fixture def admin_user(db): from users.models import User return User.objects.create_superuser("test-admin", "admin@test.local", "test") @pytest.fixture def admin_client(db, admin_user): from django.test import Client client = Client() client.force_login(admin_user) return client @pytest.fixture def api_client(db, admin_user): from rest_framework.test import APIClient client = APIClient() client.force_authenticate(user=admin_user) return client @pytest.mark.django_db class TestServiceTemplate: def test_str(self, service_template): assert str(service_template) == "test-template" def test_default_status(self, db): st = ServiceTemplate.objects.create(name="default-status") assert st.status == "draft" def test_default_tree(self, db): st = ServiceTemplate.objects.create(name="default-tree") assert st.tree == [] def test_is_locked_false(self, service_template): assert service_template.is_locked is False def test_is_locked_true(self, service_template, service): assert service_template.is_locked is True def test_ordering(self, db): ServiceTemplate.objects.create(name="z-last") ServiceTemplate.objects.create(name="a-first") names = list(ServiceTemplate.objects.values_list("name", flat=True)) assert names == ["a-first", "z-last"] def test_tree_validation_valid(self, db): st = ServiceTemplate( name="valid-tree", tree=[{ "name": "dev", "object_type": "dcim.device", "quantity": 1, "selector": {"type": "netbox_filter", "filters": {}}, "scope": "service", "children": [], }], ) st.clean() def test_tree_validation_missing_name(self, db): from django.core.exceptions import ValidationError st = ServiceTemplate( name="bad-tree", tree=[{"object_type": "dcim.device", "quantity": 1, "selector": {"type": "netbox_filter", "filters": {}}}], ) with pytest.raises(ValidationError): st.clean() def test_tree_validation_missing_object_type(self, db): from django.core.exceptions import ValidationError st = ServiceTemplate( name="bad-tree2", tree=[{"name": "dev", "quantity": 1, "selector": {"type": "netbox_filter", "filters": {}}}], ) with pytest.raises(ValidationError): st.clean() def test_tree_validation_no_selector_or_allocation(self, db): from django.core.exceptions import ValidationError st = ServiceTemplate( name="bad-tree3", tree=[{"name": "dev", "object_type": "dcim.device", "quantity": 1}], ) with pytest.raises(ValidationError): st.clean() @pytest.mark.django_db class TestService: def test_str(self, service): assert str(service) == "test-service" def test_default_status(self, db, service_template, tenant): s = Service.objects.create( name="status-test", service_template=service_template, tenant=tenant, ) assert s.status == "planned" def test_tenant_required(self, db, service_template): from django.db import IntegrityError with pytest.raises(IntegrityError): Service.objects.create( name="no-tenant", service_template=service_template, ) def test_protect_on_template_delete(self, service, service_template): from django.db.models import ProtectedError with pytest.raises(ProtectedError): service_template.delete() @pytest.mark.django_db class TestServiceResourceBinding: def test_str(self, service): from dcim.models import Site from django.contrib.contenttypes.models import ContentType site = Site.objects.create(name="bind-test-site", slug="bind-test-site") ct = ContentType.objects.get_for_model(Site) binding = ServiceResourceBinding.objects.create( service=service, content_type=ct, object_id=site.pk, ) assert "test-service" in str(binding) assert "bind-test-site" in str(binding) def test_private(self): assert ServiceResourceBinding._netbox_private is True