import pytest from netbox_services.engine.allocators import ( CreateAllocator, FirstFreeIPAddressAllocator, SpecificAllocator, ) @pytest.mark.django_db class TestFirstFreeIPAddressAllocator: def test_allocate_creates_ip(self, db): from ipam.models import IPAddress, Prefix from netbox_services.engine.context import Context Prefix.objects.create(prefix="10.99.0.0/24") allocator = FirstFreeIPAddressAllocator({ "strategy": "first_free", "prefix": {"type": "prefix", "filters": {}}, }) ip = allocator.allocate(Context(service=None)) assert ip is not None assert "10.99.0." in str(ip.address) def test_no_prefix_raises(self, db): from netbox_services.engine.context import Context allocator = FirstFreeIPAddressAllocator({ "strategy": "first_free", "prefix": {"type": "prefix", "filters": {"description": "no-such-prefix"}}, }) with pytest.raises(ValueError): allocator.allocate(Context(service=None)) @pytest.mark.django_db class TestSpecificAllocator: def test_specific_address(self, db): from netbox_services.engine.context import Context allocator = SpecificAllocator({"strategy": "specific", "address": "10.50.0.1/24"}) ip = allocator.allocate(Context(service=None)) assert str(ip.address) == "10.50.0.1/24" def test_specific_requires_address_or_path(self, db): from netbox_services.engine.context import Context allocator = SpecificAllocator({"strategy": "specific"}) with pytest.raises(ValueError): allocator.allocate(Context(service=None)) @pytest.mark.django_db class TestCreateAllocator: def test_create_returns_unsaved_instance(self, db): from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site from netbox_services.engine.context import Context mfr = Manufacturer.objects.create(name="alloc-mfr", slug="alloc-mfr") dt = DeviceType.objects.create(manufacturer=mfr, model="alloc-model", slug="alloc-model") role = DeviceRole.objects.create(name="alloc-role", slug="alloc-role") site = Site.objects.create(name="alloc-site", slug="alloc-site") allocator = CreateAllocator({ "strategy": "create", "attrs": { "name": "test-device", "device_type": dt.pk, "role": role.pk, "site": site.pk, "status": "active", }, }) ctx = Context(service=None) obj = allocator.allocate(ctx, model=Device) assert obj.pk is None assert obj.name == "test-device" assert obj.device_type == dt assert obj.role == role assert obj.site == site assert obj.status == "active" def test_create_with_jinja2_attrs(self, db): from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Site from tenancy.models import Tenant from netbox_services.engine.context import Context mfr = Manufacturer.objects.create(name="jinja-mfr", slug="jinja-mfr") dt = DeviceType.objects.create(manufacturer=mfr, model="jinja-model", slug="jinja-model") role = DeviceRole.objects.create(name="jinja-role", slug="jinja-role") site = Site.objects.create(name="jinja-site", slug="jinja-site") tenant = Tenant.objects.create(name="jinja-tenant", slug="jinja-tenant") service = type("FakeService", (), {"tenant": tenant})() parent = Device(name="parent-dev", device_type=dt, role=role, site=site) allocator = CreateAllocator({ "strategy": "create", "attrs": { "name": "rtr-{{ service.tenant.slug }}-{{ index + 1 }}", "device_type": dt.pk, "role": role.pk, "site": "{{ ..site.pk }}", "status": "active", }, }) ctx = Context(service=service, parent_object=parent) obj = allocator.allocate(ctx, model=Device, index=0, ancestors=[parent]) assert obj.name == "rtr-jinja-tenant-1" assert obj.site == site def test_create_requires_model(self, db): from netbox_services.engine.context import Context allocator = CreateAllocator({"strategy": "create", "attrs": {}}) with pytest.raises(ValueError): allocator.allocate(Context(service=None))