From 309c7a8ac6bfb7489db920b4216055815d13d3a8 Mon Sep 17 00:00:00 2001
From: Karel van Klink <karel.vanklink@geant.org>
Date: Tue, 29 Oct 2024 11:16:28 +0100
Subject: [PATCH] Add switch fixture and unit test for lan interconnect product

---
 gso/workflows/__init__.py                     |  5 ++
 test/conftest.py                              |  7 ++
 test/fixtures/__init__.py                     |  2 +
 test/fixtures/switch_fixtures.py              | 60 +++++++++++++++
 test/services/conftest.py                     |  3 +
 .../lan_switch_interconnect/__init__.py       |  0
 .../test_create_lan_switch_interconnect.py    | 75 +++++++++++++++++++
 7 files changed, 152 insertions(+)
 create mode 100644 test/fixtures/switch_fixtures.py
 create mode 100644 test/workflows/lan_switch_interconnect/__init__.py
 create mode 100644 test/workflows/lan_switch_interconnect/test_create_lan_switch_interconnect.py

diff --git a/gso/workflows/__init__.py b/gso/workflows/__init__.py
index ac978adb..0ab6a997 100644
--- a/gso/workflows/__init__.py
+++ b/gso/workflows/__init__.py
@@ -56,6 +56,11 @@ LazyWorkflowInstance("gso.workflows.switch.activate_switch", "activate_switch")
 LazyWorkflowInstance("gso.workflows.switch.terminate_switch", "terminate_switch")
 LazyWorkflowInstance("gso.workflows.switch.validate_switch", "validate_switch")
 
+#  LAN Switch Interconnect workflows
+LazyWorkflowInstance(
+    "gso.workflows.lan_switch_interconnect.create_lan_switch_interconnect", "create_lan_switch_interconnect"
+)
+
 #  Site workflows
 LazyWorkflowInstance("gso.workflows.site.create_site", "create_site")
 LazyWorkflowInstance("gso.workflows.site.modify_site", "modify_site")
diff --git a/test/conftest.py b/test/conftest.py
index 98176865..84e7433a 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -48,6 +48,7 @@ from test.fixtures import (  # noqa: F401
     service_binding_port_factory,
     site_subscription_factory,
     super_pop_switch_subscription_factory,
+    switch_subscription_factory,
 )
 
 logging.getLogger("faker.factory").setLevel(logging.WARNING)
@@ -110,6 +111,12 @@ class FakerProvider(BaseProvider):
     def network_interface(self) -> str:
         return self.generator.numerify("ge-@#/@#/@#")
 
+    def juniper_ae_interface_name(self) -> str:
+        return self.generator.numerify("ae@#")
+
+    def nokia_lag_interface_name(self) -> str:
+        return self.generator.numerify("lag-@#")
+
     def link_members_juniper(self) -> LAGMemberList[LAGMember]:
         iface_amount = self.generator.random_int(min=2, max=5)
         interface_names = [f"{prefix}{i}" for prefix in ["xe-1/0/", "ge-3/0/", "xe-2/1/"] for i in range(iface_amount)]
diff --git a/test/fixtures/__init__.py b/test/fixtures/__init__.py
index 0b69c5fb..b140d222 100644
--- a/test/fixtures/__init__.py
+++ b/test/fixtures/__init__.py
@@ -11,6 +11,7 @@ from test.fixtures.opengear_fixtures import opengear_subscription_factory
 from test.fixtures.router_fixtures import router_subscription_factory
 from test.fixtures.site_fixtures import site_subscription_factory
 from test.fixtures.super_pop_switch_fixtures import super_pop_switch_subscription_factory
+from test.fixtures.switch_fixtures import switch_subscription_factory
 
 __all__ = [
     "bgp_session_subscription_factory",
@@ -25,4 +26,5 @@ __all__ = [
     "service_binding_port_factory",
     "site_subscription_factory",
     "super_pop_switch_subscription_factory",
+    "switch_subscription_factory",
 ]
diff --git a/test/fixtures/switch_fixtures.py b/test/fixtures/switch_fixtures.py
new file mode 100644
index 00000000..9a1918f2
--- /dev/null
+++ b/test/fixtures/switch_fixtures.py
@@ -0,0 +1,60 @@
+import pytest
+from orchestrator.db import db
+from orchestrator.domain import SubscriptionModel
+from orchestrator.types import SubscriptionLifecycle
+from pydantic_forms.types import UUIDstr
+
+from gso.products import ProductName
+from gso.products.product_blocks.switch import SwitchModel
+from gso.products.product_types.site import Site
+from gso.products.product_types.switch import SwitchInactive
+from gso.services.subscriptions import get_product_id_by_name
+from gso.utils.shared_enums import Vendor
+from gso.utils.types.ip_address import PortNumber
+
+
+@pytest.fixture()
+def switch_subscription_factory(faker, geant_partner, site_subscription_factory):
+    def subscription_create(
+        partner: dict | None = None,
+        description: str | None = None,
+        start_date: str | None = "2024-01-01T10:20:30+01:02",
+        fqdn: str | None = None,
+        ts_port: PortNumber | None = None,
+        site: UUIDstr | None = None,
+        switch_vendor: Vendor | None = None,
+        switch_model: SwitchModel | None = None,
+        status: SubscriptionLifecycle | None = None,
+        *,
+        is_imported: bool = True,
+    ) -> UUIDstr:
+        if partner is None:
+            partner = geant_partner
+        if is_imported:
+            product_id = get_product_id_by_name(ProductName.SWITCH)
+            switch_subscription = SwitchInactive.from_product_id(product_id, partner["partner_id"])
+        else:
+            product_id = get_product_id_by_name(ProductName.IMPORTED_SWITCH)
+            raise NotImplemented
+            switch_subscription = ImportedSwitchInactive.from_product_id(product_id, partner["partner_id"])
+
+        switch_subscription.switch.fqdn = fqdn or faker.domain_name(levels=4)
+        switch_subscription.switch.ts_port = ts_port or faker.port_number(is_user=True)
+        switch_subscription.switch.site = site or Site.from_subscription(site_subscription_factory()).site
+        switch_subscription.switch.switch_vendor = switch_vendor or Vendor.JUNIPER
+        switch_subscription.switch.switch_model = switch_model or SwitchModel.EX3400
+
+        switch_subscription = SubscriptionModel.from_other_lifecycle(switch_subscription, SubscriptionLifecycle.ACTIVE)
+        switch_subscription.insync = True
+        switch_subscription.description = description or faker.sentence()
+        switch_subscription.start_date = start_date
+
+        if status:
+            switch_subscription.status = status
+
+        switch_subscription.save()
+        db.session.commit()
+
+        return str(switch_subscription.subscription_id)
+
+    return subscription_create
diff --git a/test/services/conftest.py b/test/services/conftest.py
index 3467d545..046d0ca4 100644
--- a/test/services/conftest.py
+++ b/test/services/conftest.py
@@ -64,6 +64,9 @@ class MockedNetboxClient:
     def delete_interface():
         return None
 
+    def get_available_lags_in_range(self):
+        return self.get_available_lags()
+
 
 class MockedSharePointClient:
     class BaseMockObject:
diff --git a/test/workflows/lan_switch_interconnect/__init__.py b/test/workflows/lan_switch_interconnect/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/test/workflows/lan_switch_interconnect/test_create_lan_switch_interconnect.py b/test/workflows/lan_switch_interconnect/test_create_lan_switch_interconnect.py
new file mode 100644
index 00000000..f42251ea
--- /dev/null
+++ b/test/workflows/lan_switch_interconnect/test_create_lan_switch_interconnect.py
@@ -0,0 +1,75 @@
+from unittest.mock import patch
+
+import pytest
+from orchestrator.types import SubscriptionLifecycle
+
+from gso.products import ProductName
+from gso.products.product_blocks.lan_switch_interconnect import LanSwitchInterconnectAddressSpace
+from gso.products.product_types.lan_switch_interconnect import LanSwitchInterconnect
+from gso.services.subscriptions import get_product_id_by_name
+from test.services.conftest import MockedNetboxClient
+from test.workflows import assert_complete, extract_state, run_workflow
+
+
+@pytest.fixture()
+def _netbox_client_mock():
+    # Mock NetboxClient methods
+    with (
+        patch("gso.services.netbox_client.NetboxClient.get_device_by_name") as mock_get_device_by_name,
+        patch("gso.services.netbox_client.NetboxClient.get_available_interfaces") as mock_get_available_interfaces,
+        patch("gso.services.netbox_client.NetboxClient.get_available_lags_in_range") as mock_available_lags_in_range,
+    ):
+        mock_get_device_by_name.return_value = MockedNetboxClient().get_device_by_name()
+        mock_get_available_interfaces.return_value = MockedNetboxClient().get_available_interfaces()
+        mock_available_lags_in_range.return_value = MockedNetboxClient().get_available_lags_in_range()
+
+        yield
+
+
+@pytest.fixture()
+def input_form_data(faker, router_subscription_factory, switch_subscription_factory):
+    def _generate_form_data(address_space: LanSwitchInterconnectAddressSpace):
+        return [
+            {
+                "product": get_product_id_by_name(ProductName.LAN_SWITCH_INTERCONNECT),
+            },
+            {
+                "tt_number": faker.tt_number(),
+                "router_side": router_subscription_factory(),
+                "switch_side": switch_subscription_factory(),
+                "address_space": address_space,
+                "description": faker.sentence(),
+                "minimum_link_count": 2,
+                "vlan_id": 111,  # VLAN ID for new interconnections is always 111
+            },
+            {
+                "router_side_iface": "lag-4",
+                "router_side_ae_members": faker.link_members_nokia()[:2],
+            },
+            {
+                "switch_side_iface": "ae9",
+                "switch_side_ae_members": faker.link_members_juniper()[:2],
+            },
+        ]
+
+    return _generate_form_data
+
+
+@pytest.mark.parametrize(
+    "address_space", [LanSwitchInterconnectAddressSpace.PRIVATE, LanSwitchInterconnectAddressSpace.PUBLIC]
+)
+@pytest.mark.workflow()
+def test_create_lan_switch_interconnect_success(
+    address_space,
+    input_form_data,
+    _netbox_client_mock,  # noqa: PT019
+):
+    initial_data = input_form_data(address_space)
+
+    result, _, _ = run_workflow("create_lan_switch_interconnect", initial_data)
+
+    assert_complete(result)
+    state = extract_state(result)
+    subscription_id = state["subscription_id"]
+    subscription = LanSwitchInterconnect.from_subscription(subscription_id)
+    assert subscription.status == SubscriptionLifecycle.ACTIVE
-- 
GitLab