MocUpgradePrecheck.Tests.ps1

BeforeAll {
    Import-Module Pester
    ipmo .\Moc.psm1
    Import-Module $PSScriptRoot\MocUpgradePrecheckTestMocks.psm1
    $cluster = "s-cluster"
}

AfterAll {
    Remove-Module Moc
    Remove-Module MocUpgradePrecheckTestMocks
}

# Important note on mocks
# Module name param tells pester to inject the mock into the corresponding
# modules scope, so the internal functions of MocNodesHelper will use the mock
# instead of the regular version of the function

Describe "Verify MocNodesMatchFailoverClusterNodes" {
    BeforeAll {
        $activeStatus = @{
            "statuses" = @{
                "RunningState" = "Active"
            }
        }

        $inactiveStatus = @{
            "statuses" = @{
                "RunningState" = "Inactive"
                "Error" = "Precheck Error Message"
            }
        }
    }

    It "test fails when Get-MocConfig throws with [FAILURE][CRITICAL]" {
        Mock -ModuleName "Moc" Get-MocConfig {
            throw "Mock exception"
        }

        $result = Test-MocNodesMatchFailoverClusterNodes -Cluster $cluster
        $result.count | Should -Be 1
        $result.Severity | Should -Be "CRITICAL"
        $result.Status | Should -Be "FAILURE"
    }

    It "test fails when Get-MocConfig throws with [FAILURE][CRITICAL]" {
        Mock -ModuleName "Moc" Get-MocConfig {
            @{
                "cloudLocation" = "MocLocation"
            }
        }

        Mock -ModuleName "Moc" Get-MocNode {
            throw "Mock exception"
        }

        $result = Test-MocNodesMatchFailoverClusterNodes -Cluster $cluster
        $result.count | Should -Be 1
        $result.Severity | Should -Be "CRITICAL"
        $result.Status | Should -Be "FAILURE"
    }

    It "MocNodes test succeeds with [SUCCESS][INFORMATIONAL]" {

        Mock -ModuleName "Moc" Get-MocNode {
            @(
                @{
                    "name" = "ABC-D120301"
                    "properties" = $activeStatus
                },
                @{
                    "name" = "abc-d120302"
                    "properties" = $activeStatus
                }
            )
        } -ParameterFilter {$Location -ne $null}

        # MOC node can be exact case match, or full lowercase of the failover cluster name
        Mock -ModuleName "Moc" Get-ClusterNode {
            @(
                @{
                    "Name" = "ABC-D120301"
                    "State" = "Up"
                },
                @{
                    "Name" = "Abc-D120302"
                    "State" = "Up"
                }
            )
        }

        Mock -ModuleName "Moc" Get-MocConfig {
            @{
                "cloudLocation" = "MocLocation"
            }
        }

        $result = Test-MocNodesMatchFailoverClusterNodes -Cluster $cluster

        $result.count | Should -Be 5
        $result[0].Severity | Should -Be "INFORMATIONAL"
        $result[0].Status | Should -Be "SUCCESS"
        $result[0].TargetResourceName | Should -Be "s-cluster"

        $result[1].Severity | Should -Be "INFORMATIONAL"
        $result[1].Status | Should -Be "SUCCESS"
        $result[1].TargetResourceName | Should -Be "ABC-D120301"

        $result[2].Severity | Should -Be "INFORMATIONAL"
        $result[2].Status | Should -Be "SUCCESS"
        $result[2].TargetResourceName | Should -Be "Abc-D120302"

        $result[3].Severity | Should -Be "INFORMATIONAL"
        $result[3].Status | Should -Be "SUCCESS"
        $result[3].TargetResourceName | Should -Be "ABC-D120301"

        $result[4].Severity | Should -Be "INFORMATIONAL"
        $result[4].Status | Should -Be "SUCCESS"
        $result[4].TargetResourceName | Should -Be "abc-d120302"
    }

    It "MocNodes test succeeds fails when node state is inactive [FAILURE][CRITICAL]" {
        Mock -ModuleName "Moc" Get-MocNode {
            # Since there is a single element, add the comma to make sure it gets
            # returned as an array type, not a single object. Same applies to below
            ,@(
                @{
                    "name" = "ABC-D120301"
                    "properties" = $inactiveStatus
                }
            )
        } -ParameterFilter {$Location -ne $null}

        Mock -ModuleName "Moc" Get-ClusterNode {
            ,@(
                @{
                    "Name" = "ABC-D120301"
                    "State" = "Up"
                }
            )
        }

        Mock -ModuleName "Moc" Get-MocConfig {
            @{
                "cloudLocation" = "MocLocation"
            }
        }

        $result = Test-MocNodesMatchFailoverClusterNodes -Cluster $cluster

        $result.count | Should -Be 3
        $result[0].Severity | Should -Be "INFORMATIONAL"
        $result[0].Status | Should -Be "SUCCESS"
        $result[0].TargetResourceName | Should -Be "s-cluster"

        $result[1].Severity | Should -Be "INFORMATIONAL"
        $result[1].Status | Should -Be "SUCCESS"
        $result[1].TargetResourceName | Should -Be "ABC-D120301"

        $result[2].Severity | Should -Be "CRITICAL"
        $result[2].Status | Should -Be "FAILURE"
        $result[2].TargetResourceName | Should -Be "ABC-D120301"
        $result[2].Description -match "unexpected running state.*Precheck Error Message" | Should -BeTrue
    }

    It "MocNodes test fails due to differences in casing or name with [FAILURE][CRITICAL]" {
        Mock -ModuleName "Moc" Get-MocNode {
            @(
                @{
                    "name" = "ABC-D120301"
                    "properties" = $activeStatus
                },
                @{
                    "name" = "ABC-D120302"
                    "properties" = $activeStatus
                }
            )
        } -ParameterFilter {$Location -ne $null}

        # The MOC name needs to be either fully lowercase of the Failover cluster name
        # or an exact match, anything else should fail
        Mock -ModuleName "Moc" Get-ClusterNode {
            @(
                @{
                    "Name" = "Abc-d120301"
                    "State" = "Up"
                },
                @{
                    "Name" = "EFG-D120302"
                    "State" = "Up"
                }
            )
        }

        Mock -ModuleName "Moc" Get-MocConfig {
            @{
                "cloudLocation" = "MocLocation"
            }
        }

        $result = Test-MocNodesMatchFailoverClusterNodes -Cluster $cluster

        $result.count | Should -Be 5

        $result[0].Severity | Should -Be "INFORMATIONAL"
        $result[0].Status | Should -Be "SUCCESS"
        $result[0].TargetResourceName | Should -Be "s-cluster"

        $result[1].Severity | Should -Be "CRITICAL"
        $result[1].Status | Should -Be "FAILURE"
        $result[1].TargetResourceName | Should -Be "abc-d120301"

        $result[2].Severity | Should -Be "CRITICAL"
        $result[2].Status | Should -Be "FAILURE"
        $result[2].TargetResourceName | Should -Be "EFG-D120302"

        $result[3].Severity | Should -Be "INFORMATIONAL"
        $result[3].Status | Should -Be "SUCCESS"
        $result[3].TargetResourceName | Should -Be "Abc-d120301"

        $result[4].Severity | Should -Be "INFORMATIONAL"
        $result[4].Status | Should -Be "SUCCESS"
        $result[4].TargetResourceName | Should -Be "ABC-D120302"
    }

    It "MocNodes test fails due to additional MOC nodes [FAILURE][CRITICAL]" {
        Mock -ModuleName "Moc" Get-MocNode {
            @(
                @{
                    "name" = "ABC-D120301"
                    "properties" = $activeStatus
                },
                @{
                    "name" = "ABC-D120302"
                    "properties" = $activeStatus
                },
                @{
                    "name" = "ABC-D120303"
                    "properties" = $activeStatus
                }
            )
        } -ParameterFilter {$Location -ne $null}

        Mock -ModuleName "Moc" Get-ClusterNode {
            @(
                @{
                    "Name" = "ABC-D120301"
                    "State" = "Up"
                },
                @{
                    "Name" = "ABC-D120302"
                    "State" = "Up"
                }
            )
        }

        Mock -ModuleName "Moc" Get-MocConfig {
            @{
                "cloudLocation" = "MocLocation"
            }
        }

        $result = Test-MocNodesMatchFailoverClusterNodes -Cluster $cluster

        $result.count | Should -Be 6
        $result[0].Severity | Should -Be "CRITICAL"
        $result[0].Status | Should -Be "FAILURE"
        $result[0].TargetResourceName | Should -Be "s-cluster"

        $result[1].Severity | Should -Be "INFORMATIONAL"
        $result[1].Status | Should -Be "SUCCESS"
        $result[1].TargetResourceName | Should -Be "ABC-D120301"

        $result[2].Severity | Should -Be "INFORMATIONAL"
        $result[2].Status | Should -Be "SUCCESS"
        $result[2].TargetResourceName | Should -Be "ABC-D120302"

        $result[3].Severity | Should -Be "INFORMATIONAL"
        $result[3].Status | Should -Be "SUCCESS"
        $result[3].TargetResourceName | Should -Be "ABC-D120301"

        $result[4].Severity | Should -Be "INFORMATIONAL"
        $result[4].Status | Should -Be "SUCCESS"
        $result[4].TargetResourceName | Should -Be "ABC-D120302"

        $result[5].Severity | Should -Be "INFORMATIONAL"
        $result[5].Status | Should -Be "SUCCESS"
        $result[5].TargetResourceName | Should -Be "ABC-D120303"
    }

    It "MocNodes test fails due to additional cluster nodes and case change [FAILURE][CRITICAL]" {
        Mock -ModuleName "Moc" Get-MocNode {
            @(
                @{
                    "name" = "ABC-D120301"
                    "properties" = $activeStatus
                },
                @{
                    "name" = "ABC-D120302"
                    "properties" = $activeStatus
                }
            )
        } -ParameterFilter {$Location -ne $null}

        Mock -ModuleName "Moc" Get-ClusterNode {
            @(
                @{
                    "Name" = "ABC-D120301"
                    "State" = "Up"
                },
                @{
                    "Name" = "abc-d120302"
                    "State" = "Up"
                },
                @{
                    "Name" = "abc-d120303"
                    "State" = "Up"
                }
            )
        }

        Mock -ModuleName "Moc" Get-MocConfig {
            @{
                "cloudLocation" = "MocLocation"
            }
        }


        $result = Test-MocNodesMatchFailoverClusterNodes -Cluster $cluster

        $result.count | Should -Be 6
        $result[0].Severity | Should -Be "CRITICAL"
        $result[0].Status | Should -Be "FAILURE"
        $result[0].TargetResourceName | Should -Be "s-cluster"

        $result[1].Severity | Should -Be "INFORMATIONAL"
        $result[1].Status | Should -Be "SUCCESS"
        $result[1].TargetResourceName | Should -Be "ABC-D120301"

        $result[2].Severity | Should -Be "CRITICAL"
        $result[2].Status | Should -Be "FAILURE"
        $result[2].TargetResourceName | Should -Be "abc-d120302"

        $result[3].Severity | Should -Be "CRITICAL"
        $result[3].Status | Should -Be "FAILURE"
        $result[3].TargetResourceName | Should -Be "abc-d120303"

        $result[4].Severity | Should -Be "INFORMATIONAL"
        $result[4].Status | Should -Be "SUCCESS"
        $result[4].TargetResourceName | Should -Be "ABC-D120301"

        $result[5].Severity | Should -Be "INFORMATIONAL"
        $result[5].Status | Should -Be "SUCCESS"
        $result[5].TargetResourceName | Should -Be "ABC-D120302"
    }
}

Describe "Verify Test-HCIClusterAndNodeRegistration" {
    It "Succeeds when cluster is connected and registered, and all nodes are active [SUCCESS][INFORMATIONAL]" {
        Mock -ModuleName "Moc" Get-AzureStackHCI {
            @{ 
                "RegistrationStatus" = "Registered"
                "ConnectionStatus" = "Connected"
            }
        }

        Mock -ModuleName "Moc" Get-ClusterNode {
            @(
                @{
                    "Name" = "abc-d120301"
                    "State" = "Up"
                },
                @{
                    "Name" = "abc-d120302"
                    "State" = "Up"
                }
            )
        }

        Mock -ModuleName "Moc" Get-AzureStackHCISubscriptionStatus {
            @(
                @{
                    "SubscriptionName" = "Azure Stack HCI"
                    "Status" = "Active"
                }
            )
        }
        # this is to ensure the Test-HCIClusterAndNodeRegistration is called in completely irrespecive of the test machine osbuild
        Mock -ModuleName "Moc" Get-CimInstance {
            @{
                "BuildNumber" = 26000
            }
        }


        $result = Test-HCIClusterAndNodeRegistration -Cluster $cluster

        $result.count | Should -Be 3
        $result[0].Severity | Should -Be "INFORMATIONAL"
        $result[0].Status | Should -Be "SUCCESS"
        $result[0].TargetResourceName | Should -Be "s-cluster"

        $result[1].Severity | Should -Be "INFORMATIONAL"
        $result[1].Status | Should -Be "SUCCESS"
        $result[1].TargetResourceName | Should -Be "abc-d120301"

        $result[2].Severity | Should -Be "INFORMATIONAL"
        $result[2].Status | Should -Be "SUCCESS"
        $result[2].TargetResourceName | Should -Be "abc-d120302"
    }

    It "Succeeds when cluster is connected and registered, and all nodes are active with Preview Subscription Name [SUCCESS][INFORMATIONAL]" {
        Mock -ModuleName "Moc" Get-AzureStackHCI {
            @{
                "RegistrationStatus" = "Registered"
                "ConnectionStatus" = "Connected"
            }
        }

        Mock -ModuleName "Moc" Get-ClusterNode {
            @(
                @{
                    "Name" = "abc-d120301"
                    "State" = "Up"
                },
                @{
                    "Name" = "abc-d120302"
                    "State" = "Up"
                }
            )
        }

        Mock -ModuleName "Moc" Get-AzureStackHCISubscriptionStatus {
            @(
                @{
                    "SubscriptionName" = "Azure Stack HCI Preview"
                    "Status" = "Active"
                }
            )
        }

        # this is to ensure the Test-HCIClusterAndNodeRegistration is called in completely irrespecive of the test machine osbuild
        Mock -ModuleName "Moc" Get-CimInstance {
            @{
                "BuildNumber" = 26000
            }
        }
        $result = Test-HCIClusterAndNodeRegistration -Cluster $cluster

        $result.count | Should -Be 3
        $result[0].Severity | Should -Be "INFORMATIONAL"
        $result[0].Status | Should -Be "SUCCESS"
        $result[0].TargetResourceName | Should -Be "s-cluster"

        $result[1].Severity | Should -Be "INFORMATIONAL"
        $result[1].Status | Should -Be "SUCCESS"
        $result[1].TargetResourceName | Should -Be "abc-d120301"

        $result[2].Severity | Should -Be "INFORMATIONAL"
        $result[2].Status | Should -Be "SUCCESS"
        $result[2].TargetResourceName | Should -Be "abc-d120302"
    }

    It "Fails when cluster is connected and registered, but nodes do not have valid subscription name [FAILURE][CRITICAL]" {
        Mock -ModuleName "Moc" Get-AzureStackHCI {
            @{
                "RegistrationStatus" = "Registered"
                "ConnectionStatus" = "Connected"
            }
        }

        Mock -ModuleName "Moc" Get-ClusterNode {
            @(
                @{
                    "Name" = "abc-d120301"
                    "State" = "Up"
                },
                @{
                    "Name" = "abc-d120302"
                    "State" = "Up"
                }
            )
        }

        Mock -ModuleName "Moc" Get-AzureStackHCISubscriptionStatus {
            @(
                @{
                    "SubscriptionName" = "Azure Stack Invalid"
                    "Status" = "Active"
                }
            )
        }

        # this is to ensure the Test-HCIClusterAndNodeRegistration is called in completely irrespecive of the test machine osbuild
        Mock -ModuleName "Moc" Get-CimInstance {
            @{
                "BuildNumber" = 26000
            }
        }
        $result = Test-HCIClusterAndNodeRegistration -Cluster $cluster

        $result.count | Should -Be 3
        $result[0].Severity | Should -Be "INFORMATIONAL"
        $result[0].Status | Should -Be "SUCCESS"
        $result[0].TargetResourceName | Should -Be "s-cluster"

        $result[1].Severity | Should -Be "CRITICAL"
        $result[1].Status | Should -Be "FAILURE"
        $result[1].TargetResourceName | Should -Be "abc-d120301"

        $result[2].Severity | Should -Be "CRITICAL"
        $result[2].Status | Should -Be "FAILURE"
        $result[2].TargetResourceName | Should -Be "abc-d120302"
    }

    It "Failed when cluster is not registered, and all nodes are inactive [FAILURE][CRITICAL]" {
        Mock -ModuleName "Moc" Get-AzureStackHCI {
            @{ 
                "RegistrationStatus" = "Unregistered"
                "ConnectionStatus" = "Disconnected"
            }
        }

        Mock -ModuleName "Moc" Get-ClusterNode {
            @(
                @{
                    "Name" = "abc-d120301"
                    "State" = "Up"
                },
                @{
                    "Name" = "abc-d120302"
                    "State" = "Up"
                }
            )
        }

        Mock -ModuleName "Moc" Get-AzureStackHCISubscriptionStatus {
            @(
                @{
                    "SubscriptionName" = "Azure Stack HCI"
                    "Status" = "Inactive"
                }
            )
        }

        # this is to ensure the Test-HCIClusterAndNodeRegistration is called in completely irrespecive of the test machine osbuild
        Mock -ModuleName "Moc" Get-CimInstance {
            @{
                "BuildNumber" = 26000
            }
        }
        
        $result = Test-HCIClusterAndNodeRegistration -Cluster $cluster

        $result.count | Should -Be 3
        $result[0].Severity | Should -Be "CRITICAL"
        $result[0].Status | Should -Be "FAILURE"
        $result[0].TargetResourceName | Should -Be "s-cluster"
        $result[0].Description -match "Registration Status" | Should -BeTrue

        $result[1].Severity | Should -Be "CRITICAL"
        $result[1].Status | Should -Be "FAILURE"
        $result[1].TargetResourceName | Should -Be "abc-d120301"

        $result[2].Severity | Should -Be "CRITICAL"
        $result[2].Status | Should -Be "FAILURE"
        $result[2].TargetResourceName | Should -Be "abc-d120302"
    }
}

Describe "Verify Test-MocNodeConnectivity" {
    It "Should fail when a network connection between cloudagent and nodeagent is unhealthy" {
        Mock -ModuleName "Moc" Get-ClusterNode {
            @(
                @{
                    "Name" = "abc-d120301"
                    "State" = "Up"
                },
                @{
                    "Name" = "abc-d120302"
                    "State" = "Up"
                }
            )
        }

        Mock -ModuleName "Moc" Get-MocConfig {
          @{
            "clusterRoleName" = "mockName"
          }
        }

        Mock -ModuleName "Moc" Get-FailoverCluster {
            @{
                "domain" = "contoso.com"
            }
        }

        Mock -ModuleName "Moc" Get-FailoverClusterGroup {
            @{
                "ownernode" = @{
                    "name" = "abc-d120302"
                }
            }
        }

        Mock -ModuleName "Moc" Invoke-Command {
            @{
                "TcpTestSucceeded" = $false
            }
        }

        Mock -ModuleName "Moc" Get-CloudFqdn {
            "abc-d120302.contoso.com"
        }

        $result = Test-MocNodeConnectivity -Cluster $cluster

        $result.count | Should -Be 4
        $result[0].Severity | Should -Be "CRITICAL"
        $result[0].Status | Should -Be "FAILURE"
        $result[0].TargetResourceName | Should -Be "abc-d120301"
        $result[0].Description |  Should -Be "TCP test from CloudAgent Node abc-d120302 to nodeagent abc-d120301.contoso.com failed"

        $result[1].Severity | Should -Be "CRITICAL"
        $result[1].Status | Should -Be "FAILURE"
        $result[1].TargetResourceName | Should -Be "abc-d120302"
        $result[1].Description |  Should -Be "TCP test from CloudAgent Node abc-d120302 to nodeagent abc-d120302.contoso.com failed"

        $result[2].Severity | Should -Be "CRITICAL"
        $result[2].Status | Should -Be "FAILURE"
        $result[2].TargetResourceName | Should -Be "abc-d120301"
        $result[2].Description |  Should -Be "TCP test from cluster node abc-d120301 to CloudAgent fqdn abc-d120302.contoso.com failed"

        $result[3].Severity | Should -Be "CRITICAL"
        $result[3].Status | Should -Be "FAILURE"
        $result[3].TargetResourceName | Should -Be "abc-d120302"
        $result[3].Description |  Should -Be "TCP test from cluster node abc-d120302 to CloudAgent fqdn abc-d120302.contoso.com failed"
    }

    It "Should succeed when all agents are connected" {
        Mock -ModuleName "Moc" Get-ClusterNode {
            @(
                @{
                    "Name" = "abc-d120301"
                    "State" = "Up"
                },
                @{
                    "Name" = "abc-d120302"
                    "State" = "Up"
                }
            )
        }

        Mock -ModuleName "Moc" Get-MocConfig {
          @{
            "clusterRoleName" = "mockName"
          }
        }

        Mock -ModuleName "Moc" Get-FailoverCluster {
            @{
                "domain" = "contoso.com"
            }
        }

        Mock -ModuleName "Moc" Get-FailoverClusterGroup {
            @{
                "ownernode" = @{
                    "name" = "abc-d120302"
                }
            }
        }

        Mock -ModuleName "Moc" Invoke-Command {
            @{
                "TcpTestSucceeded" = $true
            }
        }

        Mock -ModuleName "Moc" Get-CloudFqdn {
            "abc-d120302.contoso.com"
        }

        $result = Test-MocNodeConnectivity -Cluster $cluster

        $result.count | Should -Be 4
        $result[0].Severity | Should -Be "INFORMATIONAL"
        $result[0].Status | Should -Be "SUCCESS"
        $result[0].TargetResourceName | Should -Be "abc-d120301"
        $result[0].Description |  Should -Be "TCP test from CloudAgent Node abc-d120302 to nodeagent abc-d120301.contoso.com succeeded"

        $result[1].Severity | Should -Be "INFORMATIONAL"
        $result[1].Status | Should -Be "SUCCESS"
        $result[1].TargetResourceName | Should -Be "abc-d120302"
        $result[1].Description |  Should -Be "TCP test from CloudAgent Node abc-d120302 to nodeagent abc-d120302.contoso.com succeeded"

        $result[2].Severity | Should -Be "INFORMATIONAL"
        $result[2].Status | Should -Be "SUCCESS"
        $result[2].TargetResourceName | Should -Be "abc-d120301"
        $result[2].Description |  Should -Be "TCP test from cluster node abc-d120301 to CloudAgent fqdn abc-d120302.contoso.com succeeded"

        $result[3].Severity | Should -Be "INFORMATIONAL"
        $result[3].Status | Should -Be "SUCCESS"
        $result[3].TargetResourceName | Should -Be "abc-d120302"
        $result[3].Description |  Should -Be "TCP test from cluster node abc-d120302 to CloudAgent fqdn abc-d120302.contoso.com succeeded"
    }

    It "Should fail when Invoke-Command throws an exception" {
        Mock -ModuleName "Moc" Get-ClusterNode {
            @(
                @{
                    "Name" = "abc-d120301"
                    "State" = "Up"
                },
                @{
                    "Name" = "abc-d120302"
                    "State" = "Up"
                }
            )
        }
        
        Mock -ModuleName "Moc" Get-MocConfig {
          @{
            "clusterRoleName" = "mockName"
          }
        }

        Mock -ModuleName "Moc" Get-FailoverCluster {
            @{
                "domain" = "contoso.com"
            }
        }

        Mock -ModuleName "Moc" Get-FailoverClusterGroup {
            @{
                "ownernode" = @{
                    "name" = "abc-d120302"
                }
            }
        }

        Mock -ModuleName "Moc" Invoke-Command {
            throw "Mock exception"
        }

        Mock -ModuleName "Moc" Get-CloudFqdn {
            "abc-d120302.contoso.com"
        }

        $result = Test-MocNodeConnectivity -Cluster $cluster

        $result.count | Should -Be 4
        $result[0].Severity | Should -Be "CRITICAL"
        $result[0].Status | Should -Be "ERROR"
        $result[0].TargetResourceName | Should -Be "abc-d120301"
        $result[0].Description |  Should -Be "Unable to check CloudAgent Node to Nodeagent connection due to exception: Mock exception"

        $result[1].Severity | Should -Be "CRITICAL"
        $result[1].Status | Should -Be "ERROR"
        $result[1].TargetResourceName | Should -Be "abc-d120302"
        $result[1].Description |  Should -Be "Unable to check CloudAgent Node to Nodeagent connection due to exception: Mock exception"

        $result[2].Severity | Should -Be "CRITICAL"
        $result[2].Status | Should -Be "ERROR"
        $result[2].TargetResourceName | Should -Be "abc-d120301"
        $result[2].Description |  Should -Be "Unable to check Nodeagent Node to Cloudagent connection due to exception: Mock exception"

        $result[3].Severity | Should -Be "CRITICAL"
        $result[3].Status | Should -Be "ERROR"
        $result[3].TargetResourceName | Should -Be "abc-d120302"
        $result[3].Description |  Should -Be "Unable to check Nodeagent Node to Cloudagent connection due to exception: Mock exception"
    }
}


Describe "Verify Test-MOCUpdateReadinessInternal" {
    It "Should return null if not a failover cluster" {
        Mock -ModuleName "Moc" Get-FailoverCluster {
            $null
        }

        $result = Test-MOCUpdateReadinessInternal

        $result | Should -BeNullOrEmpty
    }

    It "Should skip HCI test in case of non-HCI setup" {
        Mock -ModuleName "Moc" Get-FailoverCluster {
            $null
        }

        # return something not matching the HCI SKU (406)
        Mock -ModuleName "Moc" Get-CimInstance {
            @{
                "OperatingSystemSKU" = 8
            }
        }

        Mock -ModuleName "Moc" Test-MocNodesMatchFailoverClusterNodes {}
        Mock -ModuleName "Moc" Test-ProxyEnvironmentVariables {}
        Mock -ModuleName "Moc" Test-MocNodeConnectivity {}
        Mock -ModuleName "Moc" Test-HCIClusterAndNodeRegistration {}

        $result = Test-MOCUpdateReadinessInternal -Cluster $cluster
        Should -Invoke Test-HCIClusterAndNodeRegistration -ModuleName "Moc" -Exactly 0
        Should -Invoke Test-ProxyEnvironmentVariables -ModuleName "Moc" -Exactly 1
        # If we fix and add back Test-EventLogServiceOnNodes, we should either mock it
        # or increment the count here, since it calls Get-FailoverCluster
        Should -Invoke Get-FailoverCluster -ModuleName "Moc" -Exactly 0
    }
}

Describe "Verify Confirm-MocAgentReadiness" {
    It "Should return null when there is no failover cluster" {
        Mock -ModuleName "Moc" Get-FailoverCluster {
            $null
        }
        Mock -ModuleName "Moc" Get-ClusterGroup {}

        $result = Confirm-MocAgentReadiness

        $result | Should -BeNullOrEmpty
        # ensure we never get to the point of calling Get-ClusterGroup
        Should -Invoke Get-ClusterGroup -ModuleName "Moc" -Exactly 0
    }

    It "Should succeed when all agents are already online" {
        # just return anything non-null
        Mock -ModuleName "Moc" Get-FailoverCluster {
            @{
                "Name" = $cluster
            }
        }

        Mock -ModuleName "Moc" Get-MocConfig {
            @{
                "clusterRoleName" = "test"
            }
        }

        Mock -ModuleName "Moc" Get-ClusterGroup {
            @{
                "State" = "Online"
            }
        }

        Mock -ModuleName "Moc" Get-ClusterNode {
            @(
                @{
                    "Name" = "abc-d120301"
                    "State" = "Up"
                },
                @{
                    "Name" = "abc-d120302"
                    "State" = "Up"
                }
            )
        }

        Mock -ModuleName "Moc" Invoke-Command {
            $false
        } -ParameterFilter { $ComputerName -eq  "abc-d120301"}

        Mock -ModuleName "Moc" Invoke-Command {
            $false
        } -ParameterFilter { $ComputerName -eq  "abc-d120302"}

        Mock -ModuleName "Moc" Start-ClusterGroup {}
        Mock -ModuleName "Moc" Wait-ForActiveNodes {}

        Confirm-MocAgentReadiness

        # make sure this is never called, indicating we took no action
        Should -Invoke Start-ClusterGroup -ModuleName "Moc" -Exactly 0
        Should -Invoke Wait-ForActiveNodes -ModuleName "Moc" -Exactly 0
    }

    It "Should exit early but not throw when cloudagent does not become ready" {
        # just return anything non-null
        Mock -ModuleName "Moc" Get-FailoverCluster {
            @{
                "Name" = $cluster
            }
        }
        Mock -ModuleName "Moc" Get-MocConfig {
            @{
                "clusterRoleName" = "test"
            }
        }
        Mock -ModuleName "Moc" Get-ClusterGroup {
            @{
                "State" = "Offline"
            }
        }

        Mock -ModuleName "Moc" Get-ClusterNode {}
        Mock -ModuleName "Moc" Start-ClusterGroup {}
        Mock -ModuleName "Moc" Wait-ForCloudAgentEndpoint {
            throw "Test exception"
        }

        { Confirm-MocAgentReadiness } | Should -Not -Throw

        Should -Invoke Start-ClusterGroup -ModuleName "Moc" -Exactly 1
        # make sure this is never called, indicating we exited prior to this
        Should -Invoke Get-ClusterNode -ModuleName "Moc" -Exactly 0
    }

    It "Should wait for cloudagent and nodeagent if they are offline" {
        # just return anything non-null
        Mock -ModuleName "Moc" Get-FailoverCluster {
            @{
                "Name" = $cluster
            }
        }
        Mock -ModuleName "Moc" Get-ClusterGroup {
            @{
                "State" = "Offline"
            }
        }

        Mock -ModuleName "Moc" Get-MocConfig {
            @{
                "clusterRoleName" = "test"
            }
        }

        Mock -ModuleName "Moc" Start-ClusterGroup {}
        Mock -ModuleName "Moc" Wait-ForCloudAgentEndpoint {
            $null
        }

        Mock -ModuleName "Moc" Get-ClusterNode {
            @(
                @{
                    "Name" = "abc-d120301"
                    "State" = "Up"
                },
                @{
                    "Name" = "abc-d120302"
                    "State" = "Up"
                }
            )
        }

        Mock -ModuleName "Moc" Invoke-Command {
            $false
        } -ParameterFilter { $ComputerName -eq  "abc-d120301"}

        Mock -ModuleName "Moc" Invoke-Command {
            $true
        } -ParameterFilter { $ComputerName -eq  "abc-d120302"}

        Mock -ModuleName "Moc" Wait-ForActiveNodes {
            $true
        }

        { Confirm-MocAgentReadiness } | Should -Not -Throw

        Should -Invoke Start-ClusterGroup -ModuleName "Moc" -Exactly 1
        Should -Invoke Wait-ForCloudAgentEndpoint -ModuleName "Moc" -Exactly 1
        Should -Invoke Wait-ForActiveNodes -ModuleName "Moc" -Exactly 1
    }

    It "Should not throw even if we have an unexpected exception" {
        # just return anything non-null
        Mock -ModuleName "Moc" Get-FailoverCluster {
            @{
                "Name" = $cluster
            }
        }
        Mock -ModuleName "Moc" Get-MocConfig {
            @{
                "clusterRoleName" = "test"
            }
        }
        Mock -ModuleName "Moc" Get-ClusterGroup {
            @{
                "State" = "Offline"
            }
        }

        Mock -ModuleName "Moc" Start-ClusterGroup {}
        Mock -ModuleName "Moc" Wait-ForCloudAgentEndpoint {
            $null
        }

        Mock -ModuleName "Moc" Get-ClusterNode {
            @(
                @{
                    "Name" = "abc-d120301"
                    "State" = "Up"
                },
                @{
                    "Name" = "abc-d120302"
                    "State" = "Up"
                }
            )
        }

        Mock -ModuleName "Moc" Invoke-Command {
            $false
        } -ParameterFilter { $ComputerName -eq  "abc-d120301"}

        Mock -ModuleName "Moc" Invoke-Command {
            $true
        } -ParameterFilter { $ComputerName -eq  "abc-d120302"}

        Mock -ModuleName "Moc" Wait-ForActiveNodes {
            throw "unexpected failure"
        }

        { Confirm-MocAgentReadiness } | Should -Not -Throw

        Should -Invoke Start-ClusterGroup -ModuleName "Moc" -Exactly 1
        Should -Invoke Wait-ForCloudAgentEndpoint -ModuleName "Moc" -Exactly 1
        Should -Invoke Wait-ForActiveNodes -ModuleName "Moc" -Exactly 1
    }
}
# SIG # Begin signature block
# MIIoVAYJKoZIhvcNAQcCoIIoRTCCKEECAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDlnO6PHWNmcw/Y
# O9ysN9XMYZf+HSn5mgbDgmF7M2FXwaCCDYUwggYDMIID66ADAgECAhMzAAAEhJji
# EuB4ozFdAAAAAASEMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjUwNjE5MTgyMTM1WhcNMjYwNjE3MTgyMTM1WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDtekqMKDnzfsyc1T1QpHfFtr+rkir8ldzLPKmMXbRDouVXAsvBfd6E82tPj4Yz
# aSluGDQoX3NpMKooKeVFjjNRq37yyT/h1QTLMB8dpmsZ/70UM+U/sYxvt1PWWxLj
# MNIXqzB8PjG6i7H2YFgk4YOhfGSekvnzW13dLAtfjD0wiwREPvCNlilRz7XoFde5
# KO01eFiWeteh48qUOqUaAkIznC4XB3sFd1LWUmupXHK05QfJSmnei9qZJBYTt8Zh
# ArGDh7nQn+Y1jOA3oBiCUJ4n1CMaWdDhrgdMuu026oWAbfC3prqkUn8LWp28H+2S
# LetNG5KQZZwvy3Zcn7+PQGl5AgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUBN/0b6Fh6nMdE4FAxYG9kWCpbYUw
# VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh
# dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzUwNTM2MjAfBgNVHSMEGDAW
# gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v
# d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw
# MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx
# XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB
# AGLQps1XU4RTcoDIDLP6QG3NnRE3p/WSMp61Cs8Z+JUv3xJWGtBzYmCINmHVFv6i
# 8pYF/e79FNK6P1oKjduxqHSicBdg8Mj0k8kDFA/0eU26bPBRQUIaiWrhsDOrXWdL
# m7Zmu516oQoUWcINs4jBfjDEVV4bmgQYfe+4/MUJwQJ9h6mfE+kcCP4HlP4ChIQB
# UHoSymakcTBvZw+Qst7sbdt5KnQKkSEN01CzPG1awClCI6zLKf/vKIwnqHw/+Wvc
# Ar7gwKlWNmLwTNi807r9rWsXQep1Q8YMkIuGmZ0a1qCd3GuOkSRznz2/0ojeZVYh
# ZyohCQi1Bs+xfRkv/fy0HfV3mNyO22dFUvHzBZgqE5FbGjmUnrSr1x8lCrK+s4A+
# bOGp2IejOphWoZEPGOco/HEznZ5Lk6w6W+E2Jy3PHoFE0Y8TtkSE4/80Y2lBJhLj
# 27d8ueJ8IdQhSpL/WzTjjnuYH7Dx5o9pWdIGSaFNYuSqOYxrVW7N4AEQVRDZeqDc
# fqPG3O6r5SNsxXbd71DCIQURtUKss53ON+vrlV0rjiKBIdwvMNLQ9zK0jy77owDy
# XXoYkQxakN2uFIBO1UNAvCYXjs4rw3SRmBX9qiZ5ENxcn/pLMkiyb68QdwHUXz+1
# fI6ea3/jjpNPz6Dlc/RMcXIWeMMkhup/XEbwu73U+uz/MIIHejCCBWKgAwIBAgIK
# YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm
# aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw
# OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD
# VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG
# 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la
# UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc
# 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D
# dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+
# lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk
# kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6
# A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd
# X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL
# 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd
# sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3
# T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS
# 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI
# bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL
# BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD
# uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv
# c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
# MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
# MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF
# BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h
# cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA
# YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn
# 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7
# v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b
# pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/
# KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy
# CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp
# mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi
# hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb
# BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS
# oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL
# gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX
# cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCGiUwghohAgEBMIGVMH4x
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p
# Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAASEmOIS4HijMV0AAAAA
# BIQwDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw
# HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEILPG
# gMaaFBtNmwAskUPgYPyZNG4nRrGulRASht8z2hfTMEIGCisGAQQBgjcCAQwxNDAy
# oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20wDQYJKoZIhvcNAQEBBQAEggEAew25dBpQ6zt7TImIip+QgIbtFbZCyo/DZGTS
# YIU5LIHiXhEJJIW5WAh6GfKabgjB77aLn0eecp40ZL9s17u+lFSRXUvgZDSOOUyN
# Ip5mjM53dpyVsql47+3SNhKBGQEHzRqseOppL21v+dx33uB0VBUdj6ZlWAeOcJ0h
# RUROcWaaPfevJQhNev8BsF2bQWbau9CvHMuM5cgPIS2/yOaCm+HU6X/t5bfMFrvK
# ahYeFn2cQyN0/pGqj122MbjKvpZDcWfCCRe7bp1L4VB9nd/WKfRjIKYpaT8rTus7
# 2f5i+esVjnV5bbuX11OKQ6GK42Av/P7wQV9KTmXR5aeR09d+wKGCF68wgherBgor
# BgEEAYI3AwMBMYIXmzCCF5cGCSqGSIb3DQEHAqCCF4gwgheEAgEDMQ8wDQYJYIZI
# AWUDBAIBBQAwggFaBgsqhkiG9w0BCRABBKCCAUkEggFFMIIBQQIBAQYKKwYBBAGE
# WQoDATAxMA0GCWCGSAFlAwQCAQUABCATNIJPsnirQH1+Eecw5cleqZL5gEAhOko7
# xNlsbrSs7gIGaSc4+Uu5GBMyMDI1MTIxNTE5MzA1MS44MjNaMASAAgH0oIHZpIHW
# MIHTMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQL
# EyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsT
# Hm5TaGllbGQgVFNTIEVTTjo0MzFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9z
# b2Z0IFRpbWUtU3RhbXAgU2VydmljZaCCEf0wggcoMIIFEKADAgECAhMzAAACHUvA
# koc4hX45AAEAAAIdMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
# IFBDQSAyMDEwMB4XDTI1MDgxNDE4NDgzM1oXDTI2MTExMzE4NDgzM1owgdMxCzAJ
# BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k
# MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jv
# c29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEnMCUGA1UECxMeblNoaWVs
# ZCBUU1MgRVNOOjQzMUEtMDVFMC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGlt
# ZS1TdGFtcCBTZXJ2aWNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA
# orSgaAA8oOl4ph574zw29egUN8DDepRHLX8FM1zHNJmXG6KrSqUKwzcKafopuYdP
# TETTCvb9aJfESuAU0iGNUFI/D6R0kvdfpe2oPX+E3sbTQvGi4JPH5qdIYUaJ45V/
# 4bqe8eNvbWzpC+ZKjH193DeiI1XAI918JoQmBhlEXo/Ton1721luZJgincsf5LjM
# Y3jX84WyXUSX3dsS7h/7xVI+w1yjg7pa+0y3o/me2Tsv6UJUdSTQap5ORGSfCncl
# nP1z3IiiWIWr3Vo7aIPWsgJzq3m5GxpxUHCQk8qzUhk50y/uB+LGE3WIK2C77iy9
# iFsSfSLUnyMEzGRDW9mXHT4PH7Ozz6CHqQEiNvwcHqlvlCh1pHQh1NXQSAqOoVBs
# 5mi6easf6yxWTfe5DrR79503r8pU6VqC2Y9XMRU4wH9QbYXYsIUZ33Jmndy22W1L
# BDAbxBPQHCBlncGDU3BgdhVUVLe80mggFO98FdkWho67w4kPdCTRkvdvkY8PrQYE
# /nQjHXCa0g7LcMttZb6ejMHfQ+tUWXv6+nZ4Ynkr2OkaxclFCw4RIYNMWD26AWbQ
# j/WEdzga18fKtw66L5gzXPza6jFBfPJeKE3H8QAuwpirmH4ms+5nUjNNQOmNgqJn
# 0U1+3Yn7ClswD79YN0r3fdbYBMDApBZJpNlK7q7HXRsCAwEAAaOCAUkwggFFMB0G
# A1UdDgQWBBSEWfBxNEamZtXm8gl92Yq80jfxXTAfBgNVHSMEGDAWgBSfpxVdAF5i
# XYP05dJlpxtTNRnpcjBfBgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vd3d3Lm1pY3Jv
# c29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENB
# JTIwMjAxMCgxKS5jcmwwbAYIKwYBBQUHAQEEYDBeMFwGCCsGAQUFBzAChlBodHRw
# Oi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMFRp
# bWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNydDAMBgNVHRMBAf8EAjAAMBYGA1Ud
# JQEB/wQMMAoGCCsGAQUFBwMIMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsF
# AAOCAgEAkdweB4yxvLspLKq0D+miyD4Q0EcxVFpNZuJxiR54gWRkeTDDuymNeB03
# JhlsBpbwSYJ5uZSgDBCvwHED2VL8lJpFlOprJzxsXWC2NTfA+O+PO5Fk5jw6LHh6
# jeBADDEdQAx3Hqi7Zm0JwvQ93z5f6dtxkm29WqOcHYXRXfAQwy1hSrLXyfeblqR6
# 6jpP/9n0fCkWU4ggsUjQpQ2Ngj1DV09J4Y3y7p9Nd81+Xs6qYo++7RKm8qiB/5ND
# eigOLjlAeFgiEXIRUJW+mJyqpQw+OORlaqcFjR8Hu0G+/7bMdek68YX+kPpDBk7U
# e+I/xgiYJ1xcDRBn/vczLtN72+RIlD4UgXYLuBSCk//pDEPX5z39Cr+rkc6E4Y28
# FPk4BhloAyvp628P4xfElQY8TcxraUbZShypocE6ny95D1K1BkltZmrHVKCxmgln
# uOlM15NKIrXFlXCzdqpCtIwQ417wNAVF/QDPvzzbumPdTi6fb0tLbScYobV6zvbB
# sMsKEME4Tj1b9oIXC8dybJq4nbboEXYpRwi1QAbpSNrn+PxGW9uf1q63FnMJu4gm
# 3Oh63njW/iVf723quzyHrSijWMgY0HiRiHQi0Jyu0h8MdhRUp7mxbmLQckPiOFwA
# lIaUN/k725y/aLWpkRU6fqmLlEOyH5WpyLd23AYy9r8v+Qoba6swggdxMIIFWaAD
# AgECAhMzAAAAFcXna54Cm0mZAAAAAAAVMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYD
# VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe
# MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3Nv
# ZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0yMTA5MzAxODIy
# MjVaFw0zMDA5MzAxODMyMjVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
# cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw
# MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5OGmTOe0ciELeaLL1yR5
# vQ7VgtP97pwHB9KpbE51yMo1V/YBf2xK4OK9uT4XYDP/XE/HZveVU3Fa4n5KWv64
# NmeFRiMMtY0Tz3cywBAY6GB9alKDRLemjkZrBxTzxXb1hlDcwUTIcVxRMTegCjhu
# je3XD9gmU3w5YQJ6xKr9cmmvHaus9ja+NSZk2pg7uhp7M62AW36MEBydUv626GIl
# 3GoPz130/o5Tz9bshVZN7928jaTjkY+yOSxRnOlwaQ3KNi1wjjHINSi947SHJMPg
# yY9+tVSP3PoFVZhtaDuaRr3tpK56KTesy+uDRedGbsoy1cCGMFxPLOJiss254o2I
# 5JasAUq7vnGpF1tnYN74kpEeHT39IM9zfUGaRnXNxF803RKJ1v2lIH1+/NmeRd+2
# ci/bfV+AutuqfjbsNkz2K26oElHovwUDo9Fzpk03dJQcNIIP8BDyt0cY7afomXw/
# TNuvXsLz1dhzPUNOwTM5TI4CvEJoLhDqhFFG4tG9ahhaYQFzymeiXtcodgLiMxhy
# 16cg8ML6EgrXY28MyTZki1ugpoMhXV8wdJGUlNi5UPkLiWHzNgY1GIRH29wb0f2y
# 1BzFa/ZcUlFdEtsluq9QBXpsxREdcu+N+VLEhReTwDwV2xo3xwgVGD94q0W29R6H
# XtqPnhZyacaue7e3PmriLq0CAwEAAaOCAd0wggHZMBIGCSsGAQQBgjcVAQQFAgMB
# AAEwIwYJKwYBBAGCNxUCBBYEFCqnUv5kxJq+gpE8RjUpzxD/LwTuMB0GA1UdDgQW
# BBSfpxVdAF5iXYP05dJlpxtTNRnpcjBcBgNVHSAEVTBTMFEGDCsGAQQBgjdMg30B
# ATBBMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3Bz
# L0RvY3MvUmVwb3NpdG9yeS5odG0wEwYDVR0lBAwwCgYIKwYBBQUHAwgwGQYJKwYB
# BAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMB
# Af8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186aGMQwVgYDVR0fBE8wTTBL
# oEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMv
# TWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggr
# BgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNS
# b29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwDQYJKoZIhvcNAQELBQADggIBAJ1Vffwq
# reEsH2cBMSRb4Z5yS/ypb+pcFLY+TkdkeLEGk5c9MTO1OdfCcTY/2mRsfNB1OW27
# DzHkwo/7bNGhlBgi7ulmZzpTTd2YurYeeNg2LpypglYAA7AFvonoaeC6Ce5732pv
# vinLbtg/SHUB2RjebYIM9W0jVOR4U3UkV7ndn/OOPcbzaN9l9qRWqveVtihVJ9Ak
# vUCgvxm2EhIRXT0n4ECWOKz3+SmJw7wXsFSFQrP8DJ6LGYnn8AtqgcKBGUIZUnWK
# NsIdw2FzLixre24/LAl4FOmRsqlb30mjdAy87JGA0j3mSj5mO0+7hvoyGtmW9I/2
# kQH2zsZ0/fZMcm8Qq3UwxTSwethQ/gpY3UA8x1RtnWN0SCyxTkctwRQEcb9k+SS+
# c23Kjgm9swFXSVRk2XPXfx5bRAGOWhmRaw2fpCjcZxkoJLo4S5pu+yFUa2pFEUep
# 8beuyOiJXk+d0tBMdrVXVAmxaQFEfnyhYWxz/gq77EFmPWn9y8FBSX5+k77L+Dvk
# txW/tM4+pTFRhLy/AsGConsXHRWJjXD+57XQKBqJC4822rpM+Zv/Cuk0+CQ1Zyvg
# DbjmjJnW4SLq8CdCPSWU5nR0W2rRnj7tfqAxM328y+l7vzhwRNGQ8cirOoo6CGJ/
# 2XBjU02N7oJtpQUQwXEGahC0HVUzWLOhcGbyoYIDWDCCAkACAQEwggEBoYHZpIHW
# MIHTMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQL
# EyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsT
# Hm5TaGllbGQgVFNTIEVTTjo0MzFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9z
# b2Z0IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIaAxUAuoO+BKbfXzqy
# fi9GLEdWHkCLeT+ggYMwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
# aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv
# cnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAx
# MDANBgkqhkiG9w0BAQsFAAIFAOzqwtcwIhgPMjAyNTEyMTUxNzI0MzlaGA8yMDI1
# MTIxNjE3MjQzOVowdjA8BgorBgEEAYRZCgQBMS4wLDAKAgUA7OrC1wIBADAJAgEA
# AgEjAgH/MAcCAQACAhCnMAoCBQDs7BRXAgEAMDYGCisGAQQBhFkKBAIxKDAmMAwG
# CisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQEL
# BQADggEBAJ/fzjLsyvLhZr5XNx4v62W9xLgP9RNzdkKrxOZqCQn8OOfI9jW/bgk3
# 274wXw5t/zP1Q8/z6enUm4ftg116ZQZCz4yW6ncKLhYd49FBJ9CBCEN+aSwwis48
# S0F/TL3s3G6paw1nVyGbnHMQLqmSxwgPG/naQWr3weiz0B/A3o6l3hDERTqSuySb
# 1PVxq15p40rF+Pbyskr3DHO33jQ6foNdJybOcTLmdhHjGv6crDRwkn5OFLte14qL
# tHjU1gP3KcXZr7gTYQMlcQ6SOx3W1Y6PBz39/HM6GdYSF/aLoeLNyV5mzty8pdAo
# zFtXrYYmsLIaZQOTaoBl7uzjHOTc48UxggQNMIIECQIBATCBkzB8MQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQg
# VGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAh1LwJKHOIV+OQABAAACHTANBglghkgB
# ZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3
# DQEJBDEiBCAWwocPvapUdwnE+8Hwhm3FVQ44gd8e55c213qhSgiPLjCB+gYLKoZI
# hvcNAQkQAi8xgeowgecwgeQwgb0EILG2lcxcSIsnOuozvt6nitM3Csw6PqClY32F
# m+mPlAVRMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0
# b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh
# dGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMA
# AAIdS8CShziFfjkAAQAAAh0wIgQg08RI1BNyVbTPDdLNrUFqySDezSmmdNvAV+XT
# eo12+2kwDQYJKoZIhvcNAQELBQAEggIAZADgEO54yMe7zWoiJW+dGa++MklMyGMW
# zTlRLtsQo8Woue6So7/YEfjqiBZ1wcI25Dd/T1yPAap8J7N83S60f4nSxRB4lvWI
# oGJbrDqaq9zP9Z0QEYw8cx/HcMoJTHnLpZiggWFYU9cJbLZMQXFn0nVjxcH6A2dn
# +XCDCs7BMQ+mvDe/uPQ44jVo52VqBHEmybjGg0aUG4DizRG0K0rUI2hsC8246rXK
# 7oMjbHwh35PGjL0cKSCRWrqPA19TMbzGRmD/4mdy++O7Fm2LNyS2fwoVY6R610WU
# RqyhkNRyyZ7yASYm9ojZIz0dEX7/CNvvTKsDpeZHyCwB7Q7lboT8xcGVSpHegH3c
# MUIhxJa5WMu9glsZCIRv2LNixt8UG1aD6+6Qwdo2AM1oZG2E/+0yRUB0BaLED7Lr
# Zqt39tV0qJyLtycvVGAjjsDVPlA8JqSiefhD46t8ViJZF+J5uLau68CwkdVtZ0w4
# ++oAKC10EwZXB8p8AVoKBekmzXR8r1os81mbF3VYgcCAXNrquAmdHQxzGxz60swL
# QZoHXk+KiNifYaodplR1Wpjg5/puoUSZVnWZWFxAjFAl8e17K/6x9CFqdHJp+z1O
# 1vr0Fk5ZLD/ZIqo8jhpoISQCDWxanbEXS36LB/z6jpaj/9NsKB1LyxBBkw+zTrvs
# f9oCzmcAbuo=
# SIG # End signature block