1
1
"""Tests for NetAppClient abstraction layer."""
2
2
3
+ import ipaddress
3
4
from unittest .mock import MagicMock
4
5
from unittest .mock import Mock
5
6
from unittest .mock import patch
21
22
from understack_workflows .netapp .value_objects import NodeResult
22
23
from understack_workflows .netapp .value_objects import PortResult
23
24
from understack_workflows .netapp .value_objects import PortSpec
25
+ from understack_workflows .netapp .value_objects import RouteSpec
24
26
from understack_workflows .netapp .value_objects import SvmResult
25
27
from understack_workflows .netapp .value_objects import SvmSpec
26
28
from understack_workflows .netapp .value_objects import VolumeResult
@@ -521,6 +523,185 @@ def test_get_namespaces_failure(
521
523
with pytest .raises (NetAppManagerError ):
522
524
netapp_client .get_namespaces (namespace_spec )
523
525
526
+ @patch ("understack_workflows.netapp.client.NetworkRoute" )
527
+ def test_create_route_success (self , mock_route_class , netapp_client ):
528
+ """Test successful route creation."""
529
+ mock_route_instance = MagicMock ()
530
+ mock_route_instance .uuid = "route-uuid-123"
531
+ mock_route_class .return_value = mock_route_instance
532
+
533
+ route_spec = RouteSpec (
534
+ svm_name = "os-test-project" ,
535
+ gateway = "100.127.0.17" ,
536
+ destination = ipaddress .IPv4Network ("100.126.0.0/17" ),
537
+ )
538
+
539
+ result = netapp_client .create_route (route_spec )
540
+
541
+ # Verify route configuration
542
+ assert mock_route_instance .svm == {"name" : "os-test-project" }
543
+ assert mock_route_instance .gateway == "100.127.0.17"
544
+ assert mock_route_instance .destination == {
545
+ "address" : "100.126.0.0" ,
546
+ "netmask" : "255.255.128.0" ,
547
+ }
548
+ mock_route_instance .post .assert_called_once_with (hydrate = True )
549
+
550
+ # Verify result
551
+ assert result .uuid == "route-uuid-123"
552
+ assert result .gateway == "100.127.0.17"
553
+ assert result .destination == ipaddress .IPv4Network ("100.126.0.0/17" )
554
+ assert result .svm_name == "os-test-project"
555
+
556
+ @patch ("understack_workflows.netapp.client.NetworkRoute" )
557
+ def test_create_route_netapp_rest_error (self , mock_route_class , netapp_client ):
558
+ """Test route creation with NetAppRestError."""
559
+ mock_route_instance = MagicMock ()
560
+ mock_route_instance .post .side_effect = NetAppRestError ("SVM not found" )
561
+ mock_route_class .return_value = mock_route_instance
562
+
563
+ # Configure error handler to raise NetworkOperationError
564
+ netapp_client ._error_handler .handle_netapp_error .side_effect = (
565
+ NetworkOperationError (
566
+ "Route creation failed: SVM not found" ,
567
+ interface_name = "test-route" ,
568
+ context = {"svm_name" : "os-nonexistent-project" },
569
+ )
570
+ )
571
+
572
+ route_spec = RouteSpec (
573
+ svm_name = "os-nonexistent-project" ,
574
+ gateway = "100.127.0.17" ,
575
+ destination = ipaddress .IPv4Network ("100.126.0.0/17" ),
576
+ )
577
+
578
+ with pytest .raises (NetworkOperationError ):
579
+ netapp_client .create_route (route_spec )
580
+
581
+ # Verify error handler was called with correct context
582
+ netapp_client ._error_handler .handle_netapp_error .assert_called_once ()
583
+ call_args = netapp_client ._error_handler .handle_netapp_error .call_args
584
+ assert isinstance (call_args [0 ][0 ], NetAppRestError )
585
+ assert call_args [0 ][1 ] == "Route creation"
586
+ assert call_args [0 ][2 ]["svm_name" ] == "os-nonexistent-project"
587
+ assert call_args [0 ][2 ]["gateway" ] == "100.127.0.17"
588
+ assert call_args [0 ][2 ]["destination" ] == ipaddress .IPv4Network ("100.126.0.0/17" )
589
+
590
+ @patch ("understack_workflows.netapp.client.NetworkRoute" )
591
+ def test_create_route_invalid_svm_error (self , mock_route_class , netapp_client ):
592
+ """Test route creation with invalid SVM name."""
593
+ mock_route_instance = MagicMock ()
594
+ mock_route_instance .post .side_effect = NetAppRestError (
595
+ "SVM 'os-invalid-svm' does not exist"
596
+ )
597
+ mock_route_class .return_value = mock_route_instance
598
+
599
+ # Configure error handler to raise NetworkOperationError
600
+ netapp_client ._error_handler .handle_netapp_error .side_effect = (
601
+ NetworkOperationError (
602
+ "Route creation failed: SVM does not exist" ,
603
+ interface_name = "test-route" ,
604
+ context = {"svm_name" : "os-invalid-svm" },
605
+ )
606
+ )
607
+
608
+ route_spec = RouteSpec (
609
+ svm_name = "os-invalid-svm" ,
610
+ gateway = "100.127.0.17" ,
611
+ destination = ipaddress .IPv4Network ("100.126.0.0/17" ),
612
+ )
613
+
614
+ with pytest .raises (NetworkOperationError ):
615
+ netapp_client .create_route (route_spec )
616
+
617
+ # Verify error context includes SVM information
618
+ call_args = netapp_client ._error_handler .handle_netapp_error .call_args
619
+ assert call_args [0 ][2 ]["svm_name" ] == "os-invalid-svm"
620
+
621
+ @patch ("understack_workflows.netapp.client.NetworkRoute" )
622
+ def test_create_route_gateway_unreachable_error (
623
+ self , mock_route_class , netapp_client
624
+ ):
625
+ """Test route creation with unreachable gateway."""
626
+ mock_route_instance = MagicMock ()
627
+ mock_route_instance .post .side_effect = NetAppRestError (
628
+ "Gateway 192.168.1.1 is not reachable from SVM network"
629
+ )
630
+ mock_route_class .return_value = mock_route_instance
631
+
632
+ # Configure error handler to raise NetworkOperationError
633
+ netapp_client ._error_handler .handle_netapp_error .side_effect = (
634
+ NetworkOperationError (
635
+ "Route creation failed: Gateway unreachable" ,
636
+ interface_name = "test-route" ,
637
+ context = {"gateway" : "192.168.1.1" },
638
+ )
639
+ )
640
+
641
+ route_spec = RouteSpec (
642
+ svm_name = "os-test-project" ,
643
+ gateway = "192.168.1.1" , # Invalid gateway for this network
644
+ destination = ipaddress .IPv4Network ("100.126.0.0/17" ),
645
+ )
646
+
647
+ with pytest .raises (NetworkOperationError ):
648
+ netapp_client .create_route (route_spec )
649
+
650
+ # Verify error context includes gateway information
651
+ call_args = netapp_client ._error_handler .handle_netapp_error .call_args
652
+ assert call_args [0 ][2 ]["gateway" ] == "192.168.1.1"
653
+ assert call_args [0 ][2 ]["destination" ] == ipaddress .IPv4Network ("100.126.0.0/17" )
654
+
655
+ @patch ("understack_workflows.netapp.client.NetworkRoute" )
656
+ def test_create_route_logging_behavior (self , mock_route_class , netapp_client ):
657
+ """Test route creation logging behavior."""
658
+ mock_route_instance = MagicMock ()
659
+ mock_route_instance .uuid = "route-uuid-456"
660
+ mock_route_class .return_value = mock_route_instance
661
+
662
+ route_spec = RouteSpec (
663
+ svm_name = "os-logging-test" ,
664
+ gateway = "100.127.128.17" ,
665
+ destination = ipaddress .IPv4Network ("100.126.128.0/17" ),
666
+ )
667
+
668
+ netapp_client .create_route (route_spec )
669
+
670
+ # Verify logging calls
671
+ error_handler = netapp_client ._error_handler
672
+ log_info_calls = error_handler .log_info .call_args_list
673
+ log_debug_calls = error_handler .log_debug .call_args_list
674
+
675
+ # Should have info logs for start and completion
676
+ assert len (log_info_calls ) >= 2
677
+
678
+ # Should have debug log for route creation
679
+ assert len (log_debug_calls ) >= 1
680
+
681
+ # Find the route-specific log messages
682
+ route_start_logs = [
683
+ call for call in log_info_calls if "Creating route:" in call [0 ][0 ]
684
+ ]
685
+ route_completion_logs = [
686
+ call
687
+ for call in log_info_calls
688
+ if "Route created successfully:" in call [0 ][0 ]
689
+ ]
690
+
691
+ # Verify route start log
692
+ assert len (route_start_logs ) == 1
693
+ start_log = route_start_logs [0 ]
694
+ assert start_log [0 ][1 ]["destination" ] == ipaddress .IPv4Network (
695
+ "100.126.128.0/17"
696
+ )
697
+ assert start_log [0 ][1 ]["gateway" ] == "100.127.128.17"
698
+ assert start_log [0 ][1 ]["svm_name" ] == "os-logging-test"
699
+
700
+ # Verify route completion log
701
+ assert len (route_completion_logs ) == 1
702
+ completion_log = route_completion_logs [0 ]
703
+ assert completion_log [0 ][1 ]["uuid" ] == "route-uuid-456"
704
+
524
705
525
706
class TestNetAppClientInterface :
526
707
"""Test cases for NetAppClientInterface abstract class."""
@@ -544,5 +725,6 @@ def test_interface_methods_are_abstract(self):
544
725
"create_port" ,
545
726
"get_nodes" ,
546
727
"get_namespaces" ,
728
+ "create_route" ,
547
729
}
548
730
assert abstract_methods == expected_methods
0 commit comments