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"
                }
            )
        }
        $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"
                }
            )
        }
        $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"
                }
            )
        }
        $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"
                }
            )
        }
        $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
# MIIoPAYJKoZIhvcNAQcCoIIoLTCCKCkCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAVfiHSfy4ZDQtu
# CWtUSDJIbf83Mj2NvjwJD7FJ67Je6KCCDYUwggYDMIID66ADAgECAhMzAAAEhJji
# 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/Xmfwb1tbWrJUnMTDXpQzTGCGg0wghoJAgEBMIGVMH4x
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p
# Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAASEmOIS4HijMV0AAAAA
# BIQwDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw
# HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIAo5
# HSLgh1m3Y6szwxBFYFcMcKYpfd/JgKOgAAM3uT1rMEIGCisGAQQBgjcCAQwxNDAy
# oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20wDQYJKoZIhvcNAQEBBQAEggEArc5trhqnm035gW35KS4PHyzUqVGBm7jDIAX/
# uyZ5BjMh/ADIM0Xr4yrmiPmV/vPxoZQKMa2lwQ/Y/Am/2THhwH1SLCRQM8CCqaWP
# +s6ZoJxqP8siu1ln73PjezFYlIsKO3p7edLNktYRxgbN76IoruHSpfn+JUdLUsta
# BoDdEqS8pxlTiCOFArGpnzGqabzNeHguXYRSpVBeyHnuvy0wTubkbGLHHPEnb4sc
# YE/CGQBM6S0NyoaGRO4phadQNLYS7EzqwZnuU64fbgn63Vv5M1emWiIzNNFgNZUd
# d7yPxA9Ur5vPoihs5JlgRQB8Yk8NDxExQrO49CwiZsfSW20fJqGCF5cwgheTBgor
# BgEEAYI3AwMBMYIXgzCCF38GCSqGSIb3DQEHAqCCF3AwghdsAgEDMQ8wDQYJYIZI
# AWUDBAIBBQAwggFSBgsqhkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGE
# WQoDATAxMA0GCWCGSAFlAwQCAQUABCCEGzxmmmKID0XR7YFEWfP+UcL8GOOlEkN4
# 5DI5VItlbQIGaKOhWNbJGBMyMDI1MDkxODA1MzYxOC4wOTlaMASAAgH0oIHRpIHO
# MIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQL
# ExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxk
# IFRTUyBFU046N0YwMC0wNUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1l
# LVN0YW1wIFNlcnZpY2WgghHtMIIHIDCCBQigAwIBAgITMwAAAgbXvFE4mCPsLAAB
# AAACBjANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz
# aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv
# cnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAx
# MDAeFw0yNTAxMzAxOTQyNTBaFw0yNjA0MjIxOTQyNTBaMIHLMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l
# cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046N0YwMC0w
# NUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Uw
# ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDpRIWbIM3Rlr397cjHaYx8
# 5l7I+ZVWGMCBCM911BpU6+IGWCqksqgqefZFEjKzNVDYC9YcgITAz276NGgvECm4
# ZfNv/FPwcaSDz7xbDbsOoxbwQoHUNRro+x5ubZhT6WJeU97F06+vDjAw/Yt1vWOg
# RTqmP/dNr9oqIbE5oCLYdH3wI/noYmsJVc7966n+B7UAGAWU2se3Lz+xdxnNsNX4
# CR6zIMVJTSezP/2STNcxJTu9k2sl7/vzOhxJhCQ38rdaEoqhGHrXrmVkEhSv+S00
# DMJc1OIXxqfbwPjMqEVp7K3kmczCkbum1BOIJ2wuDAbKuJelpteNZj/S58NSQw6k
# hfuJAluqHK3igkS/Oux49qTP+rU+PQeNuD+GtrCopFucRmanQvxISGNoxnBq3UeD
# Tqphm6aI7GMHtFD6DOjJlllH1gVWXPTyivf+4tN8TmO6yIgB4uP00bH9jn/dyyxS
# jxPQ2nGvZtgtqnvq3h3TRjRnkc+e1XB1uatDa1zUcS7r3iodTpyATe2hgkVX3m4D
# hRzI6A4SJ6fbJM9isLH8AGKcymisKzYupAeFSTJ10JEFa6MjHQYYohoCF77R0CCw
# MNjvE4XfLHu+qKPY8GQfsZdigQ9clUAiydFmVt61hytoxZP7LmXbzjD0VecyzZoL
# 4Equ1XszBsulAr5Ld2KwcwIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFO0wsLKdDGpT
# 97cx3Iymyo/SBm4SMB8GA1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8G
# A1UdHwRYMFYwVKBSoFCGTmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMv
# Y3JsL01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBs
# BggrBgEFBQcBAQRgMF4wXAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0
# LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUy
# MDIwMTAoMSkuY3J0MAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUH
# AwgwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4ICAQB23GZOfe9ThTUv
# D29i4t6lDpxJhpVRMme+UbyZhBFCZhoGTtjDdphAArU2Q61WYg3YVcl2RdJm5PUb
# Z2bA77zk+qtLxC+3dNxVsTcdtxPDSSWgwBHxTj6pCmoDNXolAYsWpvHQFCHDqEfA
# iBxX1dmaXbiTP1d0XffvgR6dshUcqaH/mFfjDZAxLU1s6HcVgCvBQJlJ7xEG5jFK
# dtqapKWcbUHwTVqXQGbIlHVClNJ3yqW6Z3UJH/CFcYiLV/e68urTmGtiZxGSYb4S
# BSPArTrTYeHOlQIj/7loVWmfWX2y4AGV/D+MzyZMyvFw4VyL0Vgq96EzQKyteiVe
# BaVEjxQKo3AcPULRF4Uzz98P2tCM5XbFZ3Qoj9PLg3rgFXr0oJEhfh2tqUrhTJd1
# 3+i4/fek9zWicoshlwXgFu002ZWBVzASEFuqED48qyulZ/2jGJBcta+Fdk2loP2K
# 3oSj4PQQe1MzzVZO52AXO42MHlhm3SHo3/RhQ+I1A0Ny+9uAehkQH6LrxkrVNvZG
# 4f0PAKMbqUcXG7xznKJ0x0HYr5ayWGbHKZRcObU+/34ZpL9NrXOedVDXmSd2ylKS
# l/vvi1QwNJqXJl/+gJkQEetqmHAUFQkFtemi8MUXQG2w/RDHXXwWAjE+qIDZLQ/k
# 4z2Z216tWaR6RDKHGkweCoDtQtzkHTCCB3EwggVZoAMCAQICEzMAAAAVxedrngKb
# SZkAAAAAABUwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
# EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv
# ZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmlj
# YXRlIEF1dGhvcml0eSAyMDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIy
# NVowfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT
# B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UE
# AxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXI
# yjVX9gF/bErg4r25PhdgM/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjo
# YH1qUoNEt6aORmsHFPPFdvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1y
# aa8dq6z2Nr41JmTamDu6GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v
# 3byNpOORj7I5LFGc6XBpDco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pG
# ve2krnopN6zL64NF50ZuyjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viS
# kR4dPf0gz3N9QZpGdc3EXzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYr
# bqgSUei/BQOj0XOmTTd0lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlM
# jgK8QmguEOqEUUbi0b1qGFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSL
# W6CmgyFdXzB0kZSU2LlQ+QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AF
# emzFER1y7435UsSFF5PAPBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIu
# rQIDAQABo4IB3TCCAdkwEgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIE
# FgQUKqdS/mTEmr6CkTxGNSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWn
# G1M1GelyMFwGA1UdIARVMFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEW
# M2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5
# Lmh0bTATBgNVHSUEDDAKBggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBi
# AEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV
# 9lbLj+iiXGJo0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3Js
# Lm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAx
# MC0wNi0yMy5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8v
# d3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2
# LTIzLmNydDANBgkqhkiG9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv
# 6lwUtj5OR2R4sQaTlz0xM7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZn
# OlNN3Zi6th542DYunKmCVgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1
# bSNU5HhTdSRXud2f8449xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4
# rPf5KYnDvBewVIVCs/wMnosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU
# 6ZGyqVvfSaN0DLzskYDSPeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDF
# NLB62FD+CljdQDzHVG2dY3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/
# HltEAY5aGZFrDZ+kKNxnGSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdU
# CbFpAUR+fKFhbHP+CrvsQWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKi
# excdFYmNcP7ntdAoGokLjzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTm
# dHRbatGePu1+oDEzfbzL6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZq
# ELQdVTNYs6FwZvKhggNQMIICOAIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJp
# Y2EgT3BlcmF0aW9uczEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjdGMDAtMDVF
# MC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMK
# AQEwBwYFKw4DAhoDFQAEa0f118XHM/VNdqKBs4QXxNnN96CBgzCBgKR+MHwxCzAJ
# BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k
# MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jv
# c29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA7HWprTAi
# GA8yMDI1MDkxNzIxNDIwNVoYDzIwMjUwOTE4MjE0MjA1WjB3MD0GCisGAQQBhFkK
# BAExLzAtMAoCBQDsdamtAgEAMAoCAQACAhGMAgH/MAcCAQACAhK3MAoCBQDsdvst
# AgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSCh
# CjAIAgEAAgMBhqAwDQYJKoZIhvcNAQELBQADggEBADB8oBFeMgRkxIRTt4KrF7gR
# 3WygAModHuXoVVIX5IRzMrNhB/6T0WoVQhVL0j+bxqLLjMth44j5e6ipgkRfs518
# 9g1VZYeO56ExcjWF1FaQMi54BM0JTNFytrTgvB/SLCz85eVgby4KH5EVw47OJR7M
# bS0x5r6Y/bUb1RXLkVA2Z3BivCScHsG8y1MuhJxMVincxAcyI42WErsXmv9B+SKL
# hcH7ACeL3sXQYXU15ogESXrdJ9P2SyscCsAxRKSBrhdTqDirNfoqAWgJWJHLwvhr
# /zJ0NKOqCUWGv+4myIcwxKvG+X9NLj41IESajU7t5KSH2scg8xHff/IU7kSpxWcx
# ggQNMIIECQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
# bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0
# aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAA
# AgbXvFE4mCPsLAABAAACBjANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkD
# MQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCCbrGP+RYRcEo7FsQkvRC9s
# NmyZ7ISsT06d8oa7Q5BQkTCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIODo
# 9ZSIkZ6dVtKT+E/uZx2WAy7KiXM5R1JIOhNJf0vSMIGYMIGApH4wfDELMAkGA1UE
# BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc
# BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0
# IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAIG17xROJgj7CwAAQAAAgYwIgQg6sq3
# k/M4Hp2UBBNbWdKwIOs9yeeEGTqRI3GpKcaJ4nQwDQYJKoZIhvcNAQELBQAEggIA
# Ccy+MhKU9QqLW8k1XGIkasr9EQMQmU4fDjI2/GV5xIySiVcaFlPWNdmxzHXTsq5m
# G8nb601hUBMlzfJnnrY6EcS4/NLvMirekhDO4HBi3ensVTIrUEMFRs53kBtaCK7P
# 1FnGHQAh9LaZzgQCTX1ESQDcxrx/QpNiFXvNS7+3OCJLxtlR0gVeat9HHRvCpMW6
# H1Y4hCuG+ZxAVpCnviLFEgKGvXiXwcA2AB7+T6y/jADV/3OuMpCaOyDX6fSlNWYB
# ECmKJ7nvzey1HgG6BFd2S+7VOD3B0UOxvVnrkJ+xd7Sh6kBi8V4QVcG8M0b3e4Iw
# uuCBx9Ql6EMdiOgUnmuov60bvjLPOt7Vb4PIAWHYosmlQdqJyLwmNNYYIEvYtrt9
# IKEcLkwEcQ1cgs1jZLqjWDCMQWrqj650lnKOK0sbcfU2CsICs2qYhNwrv58YHiTp
# pyGU4baoQGDzMewQjj2shAm7Ld8zmWH9lu2voYv/TTzB5MtI34EqfRvIOvOg+fBc
# ea5OroJinxKCGODSpq39ZYbcZDdDthbt/iQS/82ZnUcPsASepRTxzrBWFMtQuwa3
# a/EEEQ9lCdrCNCXzcuwnLPD5Zwas3hsLJJhmYBSLIimI/3Bkl+0RvZA+rPUmlTzx
# YDZSOOCRTaRXc+AU/QGE7htop3dV0r0ASKEP/9OL8Rg=
# SIG # End signature block