helpers/test/Invoke-WUGModuleTest.ps1
|
<#
.SYNOPSIS End-to-end integration test harness for every WhatsUpGoldPS cmdlet. .DESCRIPTION Invoke-WUGModuleTest connects to a WhatsUp Gold server, creates temporary test devices and resources, exercises every module cmdlet against the live API, records pass/fail for each endpoint, cleans up all test artefacts, and prints a summary. The script is interactive only for the initial server and credential prompt. Everything else runs automatically with -Confirm:$false on all write operations. .PARAMETER ServerUri The WhatsUp Gold server hostname or IP. If omitted you will be prompted. .PARAMETER Credential A PSCredential for authentication. If omitted you will be prompted via Get-Credential. .PARAMETER Port API port. Default 9644. .PARAMETER Protocol http or https. Default https. .PARAMETER IgnoreSSLErrors Pass -IgnoreSSLErrors to Connect-WUGServer when set. .EXAMPLE .\Invoke-WUGModuleTest.ps1 # Prompts for server and credentials, then runs all tests. .EXAMPLE .\Invoke-WUGModuleTest.ps1 -ServerUri "wug.lab.local" -Credential (Get-Credential) .NOTES Author : Jason Alberino (jason@wug.ninja) Created: 2026-03-08 Requires: WhatsUpGoldPS module loaded or available in $env:PSModulePath. #> [CmdletBinding()] param( [string]$ServerUri, [PSCredential]$Credential, [int]$Port = 9644, [ValidateSet('http', 'https')] [string]$Protocol = 'https', [switch]$IgnoreSSLErrors ) #region -- Helpers ------------------------------------------------------------ $script:TestResults = [System.Collections.Generic.List[PSCustomObject]]::new() function Record-Test { param( [string]$Cmdlet, [string]$Endpoint, [string]$Status, # Pass | Fail | Skipped [string]$Detail = '' ) $script:TestResults.Add([PSCustomObject]@{ Cmdlet = $Cmdlet Endpoint = $Endpoint Status = $Status Detail = $Detail }) $color = switch ($Status) { 'Pass' { 'Green' } 'Fail' { 'Red' } default { 'Yellow' } } Write-Host " [$Status] $Cmdlet ($Endpoint) $Detail" -ForegroundColor $color } function Invoke-Test { <# .SYNOPSIS Helper that runs a script block and records the result. #> param( [string]$Cmdlet, [string]$Endpoint, [scriptblock]$Test ) try { $null = & $Test Record-Test -Cmdlet $Cmdlet -Endpoint $Endpoint -Status 'Pass' } catch { Record-Test -Cmdlet $Cmdlet -Endpoint $Endpoint -Status 'Fail' -Detail $_.Exception.Message } } #endregion #region -- Module import ------------------------------------------------------ if (-not (Get-Module -Name WhatsUpGoldPS)) { try { Import-Module WhatsUpGoldPS -ErrorAction Stop } catch { Write-Error "Cannot load WhatsUpGoldPS module: $_"; return } } #endregion #region -- Prompt for connection details -------------------------------------- if (-not $ServerUri) { $ServerUri = Read-Host "Enter WhatsUp Gold server hostname or IP" } if (-not $Credential) { $Credential = Get-Credential -Message "Enter WhatsUp Gold credentials" } #endregion #region -- Connect ------------------------------------------------------------ Write-Host "`n============================================================" -ForegroundColor Cyan Write-Host " WhatsUpGoldPS End-to-End Test Suite" -ForegroundColor Cyan Write-Host "============================================================`n" -ForegroundColor Cyan $connectParams = @{ serverUri = $ServerUri Credential = $Credential Port = $Port Protocol = $Protocol IgnoreSSLErrors = $true } Write-Host "[1/12] Connecting to $ServerUri ..." -ForegroundColor Cyan Invoke-Test -Cmdlet 'Connect-WUGServer' -Endpoint 'POST /token' -Test { Connect-WUGServer @connectParams -ErrorAction Stop if (-not $global:WUGBearerHeaders) { throw "No bearer headers after connect" } } # Abort early if authentication failed - no point running 130+ tests against a dead connection if (-not $global:WUGBearerHeaders) { Write-Host "`n FATAL: Authentication failed. Cannot continue." -ForegroundColor Red Write-Host " Check server URI, credentials, and SSL/port settings.`n" -ForegroundColor Red $script:TestResults | Format-Table -AutoSize -Property Cmdlet, Endpoint, Status, Detail return } #endregion #region -- System / Product tests --------------------------------------------- Write-Host "`n[2/12] Testing system/product endpoints ..." -ForegroundColor Cyan Invoke-Test -Cmdlet 'Get-WUGProduct' -Endpoint 'GET /product/*' -Test { $p = Get-WUGProduct -ErrorAction Stop if (-not $p.version) { throw "No version returned" } } #endregion #region -- Device Group tests ------------------------------------------------- Write-Host "`n[3/12] Testing device-group endpoints ..." -ForegroundColor Cyan $script:TestGroupId = $null Invoke-Test -Cmdlet 'Get-WUGDeviceGroup (search)' -Endpoint 'GET /device-groups/-' -Test { $groups = Get-WUGDeviceGroup -Limit 5 -ErrorAction Stop if (-not $groups) { throw "No groups returned" } } Invoke-Test -Cmdlet 'Get-WUGDeviceGroup (byId 0)' -Endpoint 'GET /device-groups/{id}' -Test { $g = Get-WUGDeviceGroup -GroupId 0 -ErrorAction Stop if (-not $g) { throw "Root group not found" } } # Create a test group under root (0) Invoke-Test -Cmdlet 'Add-WUGDeviceGroup' -Endpoint 'POST /device-groups/{id}/children' -Test { $name = "WUGPS-Test-$([guid]::NewGuid().ToString('N').Substring(0,8))" $result = Add-WUGDeviceGroup -ParentGroupId 0 -Name $name -Description "Automated test group" -Confirm:$false -ErrorAction Stop if (-not $result) { throw "No result from Add-WUGDeviceGroup" } $script:TestGroupId = if ($result.id) { $result.id } elseif ($result.groupId) { $result.groupId } else { $result } } if ($script:TestGroupId) { Invoke-Test -Cmdlet 'Get-WUGDeviceGroup (children)' -Endpoint 'GET /device-groups/{id}/children' -Test { Get-WUGDeviceGroup -ConfigGroupId 0 -Children -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceGroup (children detail)' -Endpoint 'GET /device-groups/{id}/children?view=detail' -Test { Get-WUGDeviceGroup -ConfigGroupId 0 -Children -View detail -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceGroup (children search)' -Endpoint 'GET /device-groups/{id}/children?search=...' -Test { Get-WUGDeviceGroup -ConfigGroupId 0 -Children -SearchValue "test" -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceGroup (children groupType)' -Endpoint 'GET /device-groups/{id}/children?groupType=static_group' -Test { Get-WUGDeviceGroup -ConfigGroupId 0 -Children -GroupType static_group -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceGroup (children returnHierarchy)' -Endpoint 'GET /device-groups/{id}/children?returnHierarchy=true' -Test { Get-WUGDeviceGroup -ConfigGroupId 0 -Children -ReturnHierarchy -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceGroup (definition)' -Endpoint 'GET /device-groups/{id}/definition' -Test { $def = Get-WUGDeviceGroup -ConfigGroupId $script:TestGroupId -Definition -ErrorAction Stop if (-not $def) { throw "No definition returned" } } Invoke-Test -Cmdlet 'Get-WUGDeviceGroup (status)' -Endpoint 'GET /device-groups/{id}/status' -Test { Get-WUGDeviceGroup -ConfigGroupId $script:TestGroupId -GroupStatus -ErrorAction Stop | Out-Null } # Set-WUGDeviceGroup - Properties (update definition) Invoke-Test -Cmdlet 'Set-WUGDeviceGroup (rename)' -Endpoint 'PUT /device-groups/{id}/definition' -Test { Set-WUGDeviceGroup -GroupId $script:TestGroupId -Description "Renamed by test" -Confirm:$false -ErrorAction Stop | Out-Null } # Set-WUGDeviceGroup - PollNow Invoke-Test -Cmdlet 'Set-WUGDeviceGroup (pollNow)' -Endpoint 'PUT /device-groups/{id}/poll-now' -Test { Set-WUGDeviceGroup -GroupId $script:TestGroupId -PollNow -Confirm:$false -ErrorAction Stop | Out-Null } # Set-WUGDeviceGroup - Refresh with options Invoke-Test -Cmdlet 'Set-WUGDeviceGroup (refresh)' -Endpoint 'PUT /device-groups/{id}/refresh' -Test { Set-WUGDeviceGroup -GroupId $script:TestGroupId -Refresh -Confirm:$false -ErrorAction Stop | Out-Null } # Set-WUGDeviceGroup - ListDeviceIds (deduplicated) Invoke-Test -Cmdlet 'Set-WUGDeviceGroup (listDeviceIds)' -Endpoint 'GET /device-groups/{id}/devices/-' -Test { $ids = Set-WUGDeviceGroup -GroupId $script:TestGroupId -ListDeviceIds -ErrorAction Stop # Empty group is fine - just verify it runs without error } } #endregion #region -- Credential library tests ------------------------------------------- Write-Host "`n[4/12] Testing credential endpoints ..." -ForegroundColor Cyan Invoke-Test -Cmdlet 'Get-WUGCredential (list)' -Endpoint 'GET /credentials/-' -Test { $creds = Get-WUGCredential -Limit 5 -ErrorAction Stop if ($null -eq $creds) { throw "Null result" } } Invoke-Test -Cmdlet 'Get-WUGCredential (allAssignments)' -Endpoint 'GET /credentials/-/assignments/-' -Test { Get-WUGCredential -AllAssignments -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGCredential (allTemplates)' -Endpoint 'GET /credentials/-/config/template' -Test { Get-WUGCredential -AllCredentialTemplates -ErrorAction Stop | Out-Null } # Single credential by ID (pick the first one available) $script:TestCredentialId = $null Invoke-Test -Cmdlet 'Get-WUGCredential (byId)' -Endpoint 'GET /credentials/{id}' -Test { $firstCred = Get-WUGCredential -Limit 1 -View id -ErrorAction Stop if (-not $firstCred) { throw "No credentials in library to test with" } $script:TestCredentialId = if ($firstCred[0].id) { "$($firstCred[0].id)" } else { "$($firstCred[0])" } Get-WUGCredential -CredentialId $script:TestCredentialId -ErrorAction Stop | Out-Null } if ($script:TestCredentialId) { Invoke-Test -Cmdlet 'Get-WUGCredential (assignments)' -Endpoint 'GET /credentials/{id}/assignments/-' -Test { Get-WUGCredential -CredentialId $script:TestCredentialId -Assignments -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGCredential (template)' -Endpoint 'GET /credentials/{id}/config/template' -Test { Get-WUGCredential -CredentialId $script:TestCredentialId -CredentialTemplate -ErrorAction Stop | Out-Null } } #endregion #region -- Device Role library tests ------------------------------------------ Write-Host "`n[5/12] Testing device-role library endpoints ..." -ForegroundColor Cyan $script:TestRoleId = $null Invoke-Test -Cmdlet 'Get-WUGRole (list)' -Endpoint 'GET /device-role/-' -Test { $roles = Get-WUGRole -Limit 5 -ErrorAction Stop if ($null -eq $roles) { throw "Null result" } # Capture a role ID for subsequent tests if ($roles.Count -gt 0) { $script:TestRoleId = if ($roles[0].id) { "$($roles[0].id)" } else { "$($roles[0])" } } } if ($script:TestRoleId) { Invoke-Test -Cmdlet 'Get-WUGRole (byId)' -Endpoint 'GET /device-role/{roleId}' -Test { Get-WUGRole -RoleId $script:TestRoleId -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGRole (byId summary)' -Endpoint 'GET /device-role/{roleId}?view=summary' -Test { Get-WUGRole -RoleId $script:TestRoleId -View summary -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGRole (assignments byId)' -Endpoint 'GET /device-role/{roleId}/assignments/-' -Test { Get-WUGRole -RoleId $script:TestRoleId -Assignments -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGRole (template byId)' -Endpoint 'GET /device-role/{roleId}/config/template' -Test { Get-WUGRole -RoleId $script:TestRoleId -Template -ErrorAction Stop | Out-Null } } Invoke-Test -Cmdlet 'Get-WUGRole (list filtered)' -Endpoint 'GET /device-role/-?kind=role' -Test { Get-WUGRole -Kind role -Limit 5 -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGRole (allAssignments)' -Endpoint 'GET /device-role/-/assignments/-' -Test { Get-WUGRole -AllAssignments -Limit 5 -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGRole (allAssignments kind)' -Endpoint 'GET /device-role/-/assignments/-?kind=role' -Test { Get-WUGRole -AllAssignments -AssignmentKind role -Limit 5 -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGRole (allTemplates)' -Endpoint 'GET /device-role/-/config/template' -Test { Get-WUGRole -AllTemplates -Limit 5 -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGRole (allTemplates kind)' -Endpoint 'GET /device-role/-/config/template?kind=role' -Test { Get-WUGRole -AllTemplates -TemplateKind role -Limit 5 -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGRole (percentVars)' -Endpoint 'GET /device-role/-/percentVariables' -Test { Get-WUGRole -PercentVariables -Choice monitoredDevice -ErrorAction Stop | Out-Null } # -- Import / Export device role templates ------------------------------------ Invoke-Test -Cmdlet 'Import-WUGRoleTemplate (verify)' -Endpoint 'POST /device-role/-/config/import/verify' -Test { $pkg = '{"pkg":{"package":{"name":"test"}},"apply":{}}' Import-WUGRoleTemplate -Body $pkg -Verify -Confirm:$false -ErrorAction Stop | Out-Null } # Disabled - API returns 400 regardless of body shape; revisit when WUG documents the correct request format #Invoke-Test -Cmdlet 'Export-WUGRoleTemplate (content)' -Endpoint 'POST /device-role/-/config/export/content' -Test { # $roles = Get-WUGRole -Limit 1 -ErrorAction Stop # if ($null -eq $roles -or $roles.Count -eq 0) { throw "No roles available" } # $roleId = if ($roles[0].id) { "$($roles[0].id)" } else { "$($roles[0])" } # $exportBody = @{ # roles = @(@{ id = $roleId }) # } | ConvertTo-Json -Depth 5 # Export-WUGRoleTemplate -Body $exportBody -Content -Confirm:$false -ErrorAction Stop | Out-Null #} # -- Community device role template import (helper script) -------------------- Invoke-Test -Cmdlet 'Import-CommunityDeviceRoleTemplates (listOnly)' -Endpoint 'GitHub API (list)' -Test { $helperPath = Join-Path $PSScriptRoot '..\templates\Import-CommunityDeviceRoleTemplates.ps1' if (-not (Test-Path $helperPath)) { throw "Helper script not found at $helperPath" } & $helperPath -ListOnly -ErrorAction Stop } Invoke-Test -Cmdlet 'Import-CommunityDeviceRoleTemplates (single)' -Endpoint 'POST /device-role/-/config/import' -Test { $helperPath = Join-Path $PSScriptRoot '..\templates\Import-CommunityDeviceRoleTemplates.ps1' & $helperPath -TemplateNames "WhatsUp Gold" -ErrorAction Stop } #endregion #region -- Monitor template library tests ------------------------------------- Write-Host "`n[6/12] Testing monitor template endpoints ..." -ForegroundColor Cyan Invoke-Test -Cmdlet 'Get-WUGActiveMonitor (templates)' -Endpoint 'GET /monitors/-' -Test { $mons = Get-WUGActiveMonitor -Limit 5 -ErrorAction Stop if ($null -eq $mons) { throw "Null result" } } Invoke-Test -Cmdlet 'Get-WUGMonitorTemplate (types)' -Endpoint 'GET /monitors/-/config/supported-types' -Test { Get-WUGMonitorTemplate -SupportedTypes -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGMonitorTemplate (allTemplates)' -Endpoint 'GET /monitors/-/config/template' -Test { Get-WUGMonitorTemplate -AllMonitorTemplates -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Import-WUGMonitorTemplate (clone)' -Endpoint 'PATCH /monitors/-/config/template' -Test { $templates = Get-WUGMonitorTemplate -AllMonitorTemplates -ErrorAction Stop $json = $templates | ConvertTo-Json -Depth 20 Import-WUGMonitorTemplate -Body $json -Options clone -Confirm:$false -ErrorAction Stop | Out-Null } # Create a test Ping monitor in the library and capture its name for cleanup $script:TestMonitorId = $null $script:TestMonitorName = "WUGPS-TestPing-$([guid]::NewGuid().ToString('N').Substring(0,8))" Invoke-Test -Cmdlet 'Add-WUGActiveMonitor (Ping)' -Endpoint 'POST /monitors/-' -Test { $result = Add-WUGActiveMonitor -Type Ping -Name $script:TestMonitorName -Confirm:$false -ErrorAction Stop if (-not $result) { throw "No result from Add-WUGActiveMonitor" } $script:TestMonitorId = if ($result.monitorId) { "$($result.monitorId)" } elseif ($result.id) { "$($result.id)" } else { "$result" } } if ($script:TestMonitorId) { Invoke-Test -Cmdlet 'Get-WUGActiveMonitor (byMonitorId)' -Endpoint 'GET /monitors/{monitorId}' -Test { Get-WUGActiveMonitor -MonitorId $script:TestMonitorId -ErrorAction Stop | Out-Null } # Get-WUGMonitorTemplate requires -MonitorTemplate switch + -MonitorId (string) Invoke-Test -Cmdlet 'Get-WUGMonitorTemplate (byId)' -Endpoint 'GET /monitors/{id}/config/template' -Test { Get-WUGMonitorTemplate -MonitorTemplate -MonitorId $script:TestMonitorId -ErrorAction Stop | Out-Null } } #endregion #region -- Device creation & CRUD tests --------------------------------------- Write-Host "`n[7/12] Creating test device and running device CRUD ..." -ForegroundColor Cyan # We create a device via Add-WUGDeviceTemplate using the loopback address $script:TestDeviceId = $null $script:TestDeviceDisplayName = "WUGPS-TestDevice-$([guid]::NewGuid().ToString('N').Substring(0,8))" Invoke-Test -Cmdlet 'Add-WUGDeviceTemplate' -Endpoint 'POST /devices/-/config/template' -Test { $params = @{ DeviceAddress = '127.0.0.1' displayName = $script:TestDeviceDisplayName primaryRole = 'Device' ActiveMonitors = @('Ping') note = "Auto-created by Invoke-WUGModuleTest on $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" } $result = Add-WUGDeviceTemplate @params -Confirm:$false -ErrorAction Stop if (-not $result) { throw "No result" } $script:TestDeviceId = $result.idMap.resultId if (-not $script:TestDeviceId) { throw "No resultId in response" } } # Allow async scan to finish if ($script:TestDeviceId) { Write-Host " Waiting 5 seconds for async device provisioning ..." -ForegroundColor DarkGray Start-Sleep -Seconds 5 } # -- Create test credentials and assign to device ----------------------------- $script:TestCredentialIds = [System.Collections.Generic.List[string]]::new() if ($script:TestDeviceId) { # SNMP v2 credential (needed for SNMP monitors) Invoke-Test -Cmdlet 'Add-WUGCredential (snmpV2)' -Endpoint 'POST /credentials/-' -Test { $cred = Add-WUGCredential -Name "WUGPS-Test-SNMPv2-$(Get-Date -Format 'yyyyMMddHHmmss')" -Type snmpV2 ` -SnmpReadCommunity 'public' -Confirm:$false -ErrorAction Stop if (-not $cred) { throw "No result" } $credId = if ($cred.idMap) { $cred.idMap.resultId } elseif ($cred.credentialId) { $cred.credentialId } elseif ($cred.id) { $cred.id } else { "$cred" } $script:TestCredentialIds.Add("$credId") } # Windows credential (needed for WMI, Performance Counter, Process, Service monitors) Invoke-Test -Cmdlet 'Add-WUGCredential (windows)' -Endpoint 'POST /credentials/-' -Test { $cred = Add-WUGCredential -Name "WUGPS-Test-Windows-$(Get-Date -Format 'yyyyMMddHHmmss')" -Type windows ` -WindowsUser '.\wugtest' -WindowsPassword 'TestPass123!' ` -Confirm:$false -ErrorAction Stop if (-not $cred) { throw "No result" } $credId = if ($cred.idMap) { $cred.idMap.resultId } elseif ($cred.credentialId) { $cred.credentialId } elseif ($cred.id) { $cred.id } else { "$cred" } $script:TestCredentialIds.Add("$credId") } # SSH credential (needed for SSH monitors) Invoke-Test -Cmdlet 'Add-WUGCredential (ssh)' -Endpoint 'POST /credentials/-' -Test { $cred = Add-WUGCredential -Name "WUGPS-Test-SSH-$(Get-Date -Format 'yyyyMMddHHmmss')" -Type ssh ` -SshUsername 'wugtest' -SshPassword 'TestPass123!' -SshEnablePassword '' -Confirm:$false -ErrorAction Stop if (-not $cred) { throw "No result" } $credId = if ($cred.idMap) { $cred.idMap.resultId } elseif ($cred.credentialId) { $cred.credentialId } elseif ($cred.id) { $cred.id } else { "$cred" } $script:TestCredentialIds.Add("$credId") } # Assign all test credentials to the test device foreach ($credId in $script:TestCredentialIds) { Invoke-Test -Cmdlet "Set-WUGDeviceCredential (assign $credId)" -Endpoint 'PUT /devices/{id}/credentials/-' -Test { $result = Set-WUGDeviceCredential -DeviceId "$($script:TestDeviceId)" -CredentialId $credId -Assign -ErrorAction Stop if (-not $result) { throw "No result" } } } } # -- Device GET operations ---------------------------------------------------- if ($script:TestDeviceId) { Invoke-Test -Cmdlet 'Get-WUGDevice (byId)' -Endpoint 'GET /devices/{id}' -Test { $d = Get-WUGDevice -DeviceId $script:TestDeviceId -ErrorAction Stop if (-not $d) { throw "Device not returned" } } Invoke-Test -Cmdlet 'Get-WUGDevice (search)' -Endpoint 'GET /device-groups/{id}/devices/-' -Test { $d = Get-WUGDevice -SearchValue $script:TestDeviceDisplayName -ErrorAction Stop if (-not $d) { throw "Search returned nothing" } } Invoke-Test -Cmdlet 'Get-WUGDeviceProperties' -Endpoint 'GET /devices/{id}/properties' -Test { $p = Get-WUGDeviceProperties -DeviceId $script:TestDeviceId -ErrorAction Stop if (-not $p) { throw "No properties" } } Invoke-Test -Cmdlet 'Get-WUGDeviceStatus' -Endpoint 'GET /devices/{id}/status' -Test { Get-WUGDeviceStatus -DeviceId $script:TestDeviceId -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceInterface' -Endpoint 'GET /devices/{id}/interfaces/-' -Test { Get-WUGDeviceInterface -DeviceId $script:TestDeviceId -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceCredential' -Endpoint 'GET /devices/{id}/credentials' -Test { Get-WUGDeviceCredential -DeviceId $script:TestDeviceId -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceTemplate' -Endpoint 'GET /devices/{id}/config/template' -Test { Get-WUGDeviceTemplate -DeviceId $script:TestDeviceId -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDevicePollingConfig' -Endpoint 'GET /devices/{id}/config/polling' -Test { Get-WUGDevicePollingConfig -DeviceId $script:TestDeviceId -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceGroupMembership' -Endpoint 'GET /devices/{id}/group/-' -Test { Get-WUGDeviceGroupMembership -DeviceId "$($script:TestDeviceId)" -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceGroupMembership (isMember)' -Endpoint 'GET /devices/{id}/group/{gid}/is-member' -Test { Get-WUGDeviceGroupMembership -DeviceId "$($script:TestDeviceId)" -IsMember -TargetGroupId 0 -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceRole' -Endpoint 'GET /devices/{id}/roles/-' -Test { Get-WUGDeviceRole -DeviceId "$($script:TestDeviceId)" -ErrorAction Stop | Out-Null } # -- Device SET/UPDATE operations ----------------------------------------- Invoke-Test -Cmdlet 'Set-WUGDeviceProperties' -Endpoint 'PUT /devices/{id}/properties' -Test { Set-WUGDeviceProperties -DeviceId $script:TestDeviceId -note "Updated by test at $(Get-Date -Format 'HH:mm:ss')" -Confirm:$false -ErrorAction Stop | Out-Null } # -- Attributes ----------------------------------------------------------- Invoke-Test -Cmdlet 'Set-WUGDeviceAttribute (add)' -Endpoint 'POST /devices/{id}/attributes/-' -Test { Set-WUGDeviceAttribute -DeviceId $script:TestDeviceId -Name "WUGPSTest" -Value "TestValue1" -Confirm:$false -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceAttribute' -Endpoint 'GET /devices/{id}/attributes/-' -Test { $attrs = Get-WUGDeviceAttribute -DeviceId $script:TestDeviceId -ErrorAction Stop if ($null -eq $attrs) { throw "Null result" } } Invoke-Test -Cmdlet 'Get-WUGDeviceAttribute (byName)' -Endpoint 'GET /devices/{id}/attributes/- (names)' -Test { Get-WUGDeviceAttribute -DeviceId $script:TestDeviceId -Names "WUGPSTest" -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Set-WUGDeviceAttribute (update)' -Endpoint 'PUT /devices/{id}/attributes/{id}' -Test { Set-WUGDeviceAttribute -DeviceId $script:TestDeviceId -Name "WUGPSTest" -Value "TestValue2" -Confirm:$false -ErrorAction Stop | Out-Null } # Remove-WUGDeviceAttribute - use -All (attribute IDs are not reliably returned by the add/get APIs) Invoke-Test -Cmdlet 'Remove-WUGDeviceAttribute (all)' -Endpoint 'DELETE /devices/{id}/attributes/-' -Test { Remove-WUGDeviceAttribute -DeviceId $script:TestDeviceId -All -Confirm:$false -ErrorAction Stop | Out-Null } # -- Maintenance ---------------------------------------------------------- Invoke-Test -Cmdlet 'Set-WUGDeviceMaintenance (enable)' -Endpoint 'PUT /devices/-/maintenance' -Test { Set-WUGDeviceMaintenance -DeviceId $script:TestDeviceId -Enabled $true -Reason "Test maintenance" -TimeInterval "30m" -Confirm:$false -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceMaintenanceSchedule' -Endpoint 'GET /devices/{id}/config/maintenance' -Test { Get-WUGDeviceMaintenanceSchedule -DeviceId $script:TestDeviceId -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Set-WUGDeviceMaintenance (disable)' -Endpoint 'PUT /devices/-/maintenance' -Test { Set-WUGDeviceMaintenance -DeviceId $script:TestDeviceId -Enabled $false -Reason "Test done" -Confirm:$false -ErrorAction Stop | Out-Null } # Single-device maintenance uses PUT /devices/{id}/config/maintenance auto-routing Invoke-Test -Cmdlet 'Set-WUGDeviceMaintenance (single-device)' -Endpoint 'PUT /devices/{id}/config/maintenance' -Test { Set-WUGDeviceMaintenance -DeviceId $script:TestDeviceId -Enabled $true -Reason "Single device test" -TimeInterval "15m" -Confirm:$false -ErrorAction Stop | Out-Null Set-WUGDeviceMaintenance -DeviceId $script:TestDeviceId -Enabled $false -Reason "Cleanup" -Confirm:$false -ErrorAction Stop | Out-Null } # -- Monitors on device --------------------------------------------------- Invoke-Test -Cmdlet 'Get-WUGActiveMonitor (device)' -Endpoint 'GET /devices/{id}/monitors/-' -Test { $devMons = Get-WUGActiveMonitor -DeviceId "$($script:TestDeviceId)" -ErrorAction Stop if ($null -eq $devMons) { throw "Null result" } } # Assign test monitor to device (if we created one) $script:TestAssignmentId = $null if ($script:TestMonitorId) { Invoke-Test -Cmdlet 'Add-WUGActiveMonitorToDevice' -Endpoint 'POST /devices/{id}/monitors/-' -Test { $result = Add-WUGActiveMonitorToDevice -DeviceId $script:TestDeviceId -MonitorId ([int]$script:TestMonitorId) -Comment "Test assignment" -ErrorAction Stop if (-not $result) { throw "No result" } $script:TestAssignmentId = if ($result.assignmentId) { "$($result.assignmentId)" } elseif ($result.id) { "$($result.id)" } else { $null } } } if ($script:TestAssignmentId) { Invoke-Test -Cmdlet 'Get-WUGActiveMonitor (assignment)' -Endpoint 'GET /devices/{id}/monitors/{aId}' -Test { Get-WUGActiveMonitor -DeviceId "$($script:TestDeviceId)" -AssignmentId "$($script:TestAssignmentId)" -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Remove-WUGDeviceMonitor (single)' -Endpoint 'DELETE /devices/{id}/monitors/{aId}' -Test { Remove-WUGDeviceMonitor -DeviceId "$($script:TestDeviceId)" -AssignmentId "$($script:TestAssignmentId)" -Confirm:$false -ErrorAction Stop | Out-Null } } # -- Group membership ----------------------------------------------------- if ($script:TestGroupId) { Invoke-Test -Cmdlet 'Add-WUGDeviceGroupMember' -Endpoint 'POST /device-groups/{id}/devices/-' -Test { Add-WUGDeviceGroupMember -GroupId $script:TestGroupId -DeviceId "$($script:TestDeviceId)" -Confirm:$false -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceGroup (devices)' -Endpoint 'GET /device-groups/{id}/devices/-' -Test { Get-WUGDeviceGroup -ConfigGroupId $script:TestGroupId -GroupDevices -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Remove-WUGDeviceGroupMember' -Endpoint 'DELETE /device-groups/{id}/devices/{dId}' -Test { Remove-WUGDeviceGroupMember -GroupId $script:TestGroupId -DeviceId "$($script:TestDeviceId)" -Confirm:$false -ErrorAction Stop | Out-Null } # Re-add to test device-side removal Add-WUGDeviceGroupMember -GroupId $script:TestGroupId -DeviceId "$($script:TestDeviceId)" -Confirm:$false -ErrorAction SilentlyContinue | Out-Null Invoke-Test -Cmdlet 'Remove-WUGDeviceGroupMember (fromDevice)' -Endpoint 'DELETE /devices/{id}/group/{gid}' -Test { Remove-WUGDeviceGroupMember -FromDeviceId "$($script:TestDeviceId)" -FromGroupId ([int]$script:TestGroupId) -Confirm:$false -ErrorAction Stop | Out-Null } } # -- Polling - use ByGroup (single-device POST may 404 on some WUG versions) if ($script:TestGroupId) { Invoke-Test -Cmdlet 'Invoke-WUGDevicePollNow (byGroup)' -Endpoint 'PUT /device-groups/{id}/poll-now' -Test { Invoke-WUGDevicePollNow -GroupId ([int]$script:TestGroupId) -Confirm:$false -ErrorAction Stop | Out-Null } } # -- Refresh - use ByGroup (PATCH /devices/refresh may 405 on some WUG versions) if ($script:TestGroupId) { Invoke-Test -Cmdlet 'Invoke-WUGDeviceRefresh (byGroup)' -Endpoint 'PUT /device-groups/{id}/refresh' -Test { Invoke-WUGDeviceRefresh -GroupId ([int]$script:TestGroupId) -Confirm:$false -ErrorAction Stop | Out-Null } } # Single-device refresh uses PUT /devices/{id}/refresh auto-routing Invoke-Test -Cmdlet 'Invoke-WUGDeviceRefresh (single-device)' -Endpoint 'PUT /devices/{id}/refresh' -Test { Invoke-WUGDeviceRefresh -DeviceId $script:TestDeviceId -Confirm:$false -ErrorAction Stop | Out-Null } # -- Device Scan ---------------------------------------------------------- Invoke-Test -Cmdlet 'Get-WUGDeviceScan (list)' -Endpoint 'GET /device-scans/-' -Test { Get-WUGDeviceScan -Limit 5 -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceScan (activeOnly)' -Endpoint 'GET /device-scans/- (active)' -Test { Get-WUGDeviceScan -ActiveOnly true -Limit 5 -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceScan (model filter)' -Endpoint 'GET /device-scans/- (model)' -Test { Get-WUGDeviceScan -Model newDevice -Limit 5 -ErrorAction Stop | Out-Null } # -- Add-WUGDevice (scan-based) ------------------------------------------ $script:ScanDeviceId = $null Invoke-Test -Cmdlet 'Add-WUGDevice (scan)' -Endpoint 'PATCH /device-groups/{id}/newDevice' -Test { $scanResult = Add-WUGDevice -IpOrName '127.0.0.2' -GroupId 0 -Confirm:$false -ErrorAction Stop if (-not $scanResult) { throw "No result from Add-WUGDevice" } # The API returns a scan/discovery result - capture the scan ID if available $script:AddDeviceScanResult = $scanResult } if ($script:AddDeviceScanResult) { # Wait for scan to process Write-Host " Waiting 8 seconds for discovery scan ..." -ForegroundColor DarkGray Start-Sleep -Seconds 8 # Try to retrieve the scan status using the most recent scan Invoke-Test -Cmdlet 'Get-WUGDeviceScan (recent scan)' -Endpoint 'GET /device-scans/- (recent)' -Test { $scans = Get-WUGDeviceScan -Model newDevice -Limit 1 -ErrorAction Stop if (-not $scans) { throw "No scans found" } } } } #endregion #region -- Performance monitor tests ------------------------------------------ Write-Host "`n[8/12] Testing Add-WUGPerformanceMonitor ..." -ForegroundColor Cyan $script:PerfMonitorIds = [System.Collections.Generic.List[string]]::new() if ($script:TestDeviceId) { # -- Per-type basic assignment (9 types) ---------------------------------- Invoke-Test -Cmdlet 'Add-WUGPerformanceMonitor (RestApi)' -Endpoint 'POST /monitors/- + POST /devices/{id}/monitors/-' -Test { $r = Add-WUGPerformanceMonitor -DeviceId $script:TestDeviceId -Type RestApi -RestApiUrl 'https://localhost/api/health' -RestApiJsonPath '$.status' -Confirm:$false -ErrorAction Stop if (-not $r -or -not $r.MonitorId) { throw "No result or MonitorId returned" } $script:PerfMonitorIds.Add("$($r.MonitorId)") } Invoke-Test -Cmdlet 'Add-WUGPerformanceMonitor (PowerShell)' -Endpoint 'POST /monitors/- + POST /devices/{id}/monitors/-' -Test { $r = Add-WUGPerformanceMonitor -DeviceId $script:TestDeviceId -Type PowerShell -ScriptText '$Context.SetValue(1)' -Confirm:$false -ErrorAction Stop if (-not $r -or -not $r.MonitorId) { throw "No result or MonitorId returned" } $script:PerfMonitorIds.Add("$($r.MonitorId)") } Invoke-Test -Cmdlet 'Add-WUGPerformanceMonitor (WmiRaw)' -Endpoint 'POST /monitors/- + POST /devices/{id}/monitors/-' -Test { $r = Add-WUGPerformanceMonitor -DeviceId $script:TestDeviceId -Type WmiRaw -WmiRawRelativePath 'Win32_PerfRawData_PerfOS_Memory' -WmiRawPropertyName 'AvailableBytes' -WmiRawDisplayname 'Memory \\ Available Bytes' -Confirm:$false -ErrorAction Stop if (-not $r -or -not $r.MonitorId) { throw "No result or MonitorId returned" } $script:PerfMonitorIds.Add("$($r.MonitorId)") } Invoke-Test -Cmdlet 'Add-WUGPerformanceMonitor (WmiFormatted)' -Endpoint 'POST /monitors/- + POST /devices/{id}/monitors/-' -Test { $r = Add-WUGPerformanceMonitor -DeviceId $script:TestDeviceId -Type WmiFormatted -WmiFormattedRelativePath 'Win32_PerfFormattedData_PerfOS_Memory' -WmiFormattedPropertyName 'AvailableMBytes' -WmiFormattedDisplayname 'Memory \\ Available MBytes' -Confirm:$false -ErrorAction Stop if (-not $r -or -not $r.MonitorId) { throw "No result or MonitorId returned" } $script:PerfMonitorIds.Add("$($r.MonitorId)") } Invoke-Test -Cmdlet 'Add-WUGPerformanceMonitor (WinPerfCounter)' -Endpoint 'POST /monitors/- + POST /devices/{id}/monitors/-' -Test { $r = Add-WUGPerformanceMonitor -DeviceId $script:TestDeviceId -Type WindowsPerformanceCounter -PerfCounterCategory 'Processor' -PerfCounterName '% Processor Time' -Confirm:$false -ErrorAction Stop if (-not $r -or -not $r.MonitorId) { throw "No result or MonitorId returned" } $script:PerfMonitorIds.Add("$($r.MonitorId)") } Invoke-Test -Cmdlet 'Add-WUGPerformanceMonitor (Ssh)' -Endpoint 'POST /monitors/- + POST /devices/{id}/monitors/-' -Test { $r = Add-WUGPerformanceMonitor -DeviceId $script:TestDeviceId -Type Ssh -SshCommand 'cat /proc/loadavg | awk ''{print $1}''' -Confirm:$false -ErrorAction Stop if (-not $r -or -not $r.MonitorId) { throw "No result or MonitorId returned" } $script:PerfMonitorIds.Add("$($r.MonitorId)") } Invoke-Test -Cmdlet 'Add-WUGPerformanceMonitor (Snmp)' -Endpoint 'POST /monitors/- + POST /devices/{id}/monitors/-' -Test { $r = Add-WUGPerformanceMonitor -DeviceId $script:TestDeviceId -Type Snmp -SnmpOID '1.3.6.1.4.1.9.9.13.1.4.1.3' -Confirm:$false -ErrorAction Stop if (-not $r -or -not $r.MonitorId) { throw "No result or MonitorId returned" } $script:PerfMonitorIds.Add("$($r.MonitorId)") } Invoke-Test -Cmdlet 'Add-WUGPerformanceMonitor (AzureMetrics)' -Endpoint 'POST /monitors/- + POST /devices/{id}/monitors/-' -Test { $r = Add-WUGPerformanceMonitor -DeviceId $script:TestDeviceId -Type AzureMetrics ` -AzureResourceId '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.Network/virtualNetworks/test-vnet' ` -AzureResourceMetric 'PacketsInDDoS' ` -AzureResourceType 'Microsoft.Network/virtualNetworks' ` -AzureSubscriptionId '00000000-0000-0000-0000-000000000000' ` -AzureResourceName 'test-vnet' ` -AzureResourceGroup 'test-rg' ` -Confirm:$false -ErrorAction Stop if (-not $r -or -not $r.MonitorId) { throw "No result or MonitorId returned" } $script:PerfMonitorIds.Add("$($r.MonitorId)") } Invoke-Test -Cmdlet 'Add-WUGPerformanceMonitor (CloudWatch)' -Endpoint 'POST /monitors/- + POST /devices/{id}/monitors/-' -Test { $r = Add-WUGPerformanceMonitor -DeviceId $script:TestDeviceId -Type CloudWatch -CloudWatchNamespace 'AWS/Usage' -CloudWatchMetric 'CallCount' -CloudWatchRegion 'us-east-1' -Confirm:$false -ErrorAction Stop if (-not $r -or -not $r.MonitorId) { throw "No result or MonitorId returned" } $script:PerfMonitorIds.Add("$($r.MonitorId)") } # -- Required field validation (mandatory params - PowerShell enforces) --- Invoke-Test -Cmdlet 'Add-WUGPerformanceMonitor (RestApi mandatory params)' -Endpoint '(validation)' -Test { $param = (Get-Command Add-WUGPerformanceMonitor).Parameters['RestApiUrl'] $isMandatory = $param.Attributes | Where-Object { $_ -is [System.Management.Automation.ParameterAttribute] -and $_.Mandatory } if (-not $isMandatory) { throw "RestApiUrl should be a mandatory parameter" } } Invoke-Test -Cmdlet 'Add-WUGPerformanceMonitor (WmiRaw mandatory params)' -Endpoint '(validation)' -Test { $param = (Get-Command Add-WUGPerformanceMonitor).Parameters['WmiRawRelativePath'] $isMandatory = $param.Attributes | Where-Object { $_ -is [System.Management.Automation.ParameterAttribute] -and $_.Mandatory } if (-not $isMandatory) { throw "WmiRawRelativePath should be a mandatory parameter" } } # -- Invalid Type -------------------------------------------------- Invoke-Test -Cmdlet 'Add-WUGPerformanceMonitor (invalid type)' -Endpoint '(validation)' -Test { $threw = $false try { Add-WUGPerformanceMonitor -DeviceId $script:TestDeviceId -Type 'NonExistentType' -Confirm:$false -ErrorAction Stop 2>$null } catch { $threw = $true } if (-not $threw) { throw "Expected parameter validation error but none occurred" } } # -- Invalid DeviceId ----------------------------------------------------- Invoke-Test -Cmdlet 'Add-WUGPerformanceMonitor (invalid device)' -Endpoint 'POST /monitors/- + POST /devices/{id}/monitors/-' -Test { $threw = $false try { Add-WUGPerformanceMonitor -DeviceId ([int]::MaxValue) -Type Snmp -SnmpOID '1.3.6.1.4.1.9.9.13.1.4.1.3' -Confirm:$false -ErrorAction Stop 2>$null } catch { $threw = $true } if (-not $threw) { Write-Verbose "Function did not throw - acceptable if it returned warning or failed assignment" } } # -- Pipeline input ------------------------------------------------------- Invoke-Test -Cmdlet 'Add-WUGPerformanceMonitor (pipeline)' -Endpoint 'POST /monitors/- + POST /devices/{id}/monitors/-' -Test { $obj = [PSCustomObject]@{ DeviceId = $script:TestDeviceId } $r = $obj | Add-WUGPerformanceMonitor -Type Snmp -SnmpOID '1.3.6.1.2.1.1.3.0' -Confirm:$false -ErrorAction Stop if (-not $r -or -not $r.MonitorId) { throw "No result from pipeline input" } $script:PerfMonitorIds.Add("$($r.MonitorId)") } } else { Record-Test -Cmdlet 'Add-WUGPerformanceMonitor (all)' -Endpoint '(skipped)' -Status 'Skipped' -Detail 'No test device available' } #endregion #region -- Assign performance monitors to device tests ------------------------ Write-Host "`n--- Phase [8.5/12] Add-WUGPerformanceMonitorToDevice ---" -ForegroundColor Cyan if ($script:TestDeviceId) { # Create 5 perf monitors in library ONLY (no device assignment) for assignment testing. # Remove-WUGActiveMonitor -RemoveAssignments does not reliably unassign performance monitors, # so we create fresh library-only entries via direct API call and assign them here. $script:PerfMonAssignIds = [System.Collections.Generic.List[string]]::new() $snmpClassId = '2f300544-cba3-4341-9b05-2d1786f68e07' $irmParams = @{ Headers = $global:WUGBearerHeaders; ContentType = 'application/json'; Method = 'POST' } if ($PSVersionTable.PSVersion.Major -ge 6) { $irmParams['SkipCertificateCheck'] = $true } for ($i = 1; $i -le 5; $i++) { $monName = "PerfMon-Assign-Test$i-$(Get-Date -Format 'yyyyMMddHHmmss')" $payload = @{ allowSystemMonitorCreation = $true name = $monName description = "PerfMonToDevice test monitor $i" monitorTypeInfo = @{ baseType = 'performance'; classId = $snmpClassId } propertyBags = @( @{ name = 'SNMP:OID'; value = "1.3.6.1.2.1.1.3.$i" } @{ name = 'SNMP:Instance'; value = '0' } @{ name = 'SNMP:UseRawValues'; value = '1' } @{ name = 'SNMP:Retries'; value = '1' } @{ name = 'SNMP:Timeout'; value = '3' } ) useInDiscovery = $false } | ConvertTo-Json -Depth 5 $uri = "$($global:WhatsUpServerBaseURI)/api/v1/monitors/-" try { $r = Invoke-RestMethod -Uri $uri @irmParams -Body $payload -ErrorAction Stop if ($r.data.successful -eq 1) { $script:PerfMonAssignIds.Add("$($r.data.idMap.resultId)") } } catch { Write-Warning "Failed to create PerfMonAssign monitor $i : $_" } } if ($script:PerfMonAssignIds.Count -ge 5) { # Single assignment Invoke-Test -Cmdlet 'Add-WUGPerformanceMonitorToDevice (single)' -Endpoint 'POST /devices/{id}/monitors/-' -Test { $result = Add-WUGPerformanceMonitorToDevice -DeviceId $script:TestDeviceId -MonitorId ([int]$script:PerfMonAssignIds[0]) -ErrorAction Stop if (-not $result) { throw "No result" } } # Multiple monitors to single device Invoke-Test -Cmdlet 'Add-WUGPerformanceMonitorToDevice (multi-monitor)' -Endpoint 'POST /devices/{id}/monitors/-' -Test { $ids = @($script:PerfMonAssignIds[1], $script:PerfMonAssignIds[2]) | ForEach-Object { [int]$_ } $result = Add-WUGPerformanceMonitorToDevice -DeviceId $script:TestDeviceId -MonitorId $ids -PollingIntervalMinutes 10 -ErrorAction Stop if (-not $result) { throw "No result" } } # Pipeline input Invoke-Test -Cmdlet 'Add-WUGPerformanceMonitorToDevice (pipeline)' -Endpoint 'POST /devices/{id}/monitors/-' -Test { $obj = [PSCustomObject]@{ DeviceId = $script:TestDeviceId } $result = $obj | Add-WUGPerformanceMonitorToDevice -MonitorId ([int]$script:PerfMonAssignIds[3]) -ErrorAction Stop if (-not $result) { throw "No result from pipeline input" } } # Disabled assignment (separate monitor so no unassign needed) Invoke-Test -Cmdlet 'Add-WUGPerformanceMonitorToDevice (disabled)' -Endpoint 'POST /devices/{id}/monitors/-' -Test { $result = Add-WUGPerformanceMonitorToDevice -DeviceId $script:TestDeviceId -MonitorId ([int]$script:PerfMonAssignIds[4]) -Enabled false -ErrorAction Stop if (-not $result) { throw "No result" } } } else { Record-Test -Cmdlet 'Add-WUGPerformanceMonitorToDevice (all - insufficient monitors)' -Endpoint '(skipped)' -Status 'Skipped' -Detail "Only $($script:PerfMonAssignIds.Count) of 5 library monitors created" } } else { Record-Test -Cmdlet 'Add-WUGPerformanceMonitorToDevice (all - no device)' -Endpoint '(skipped)' -Status 'Skipped' -Detail 'No test device available' } #endregion #region -- Active monitor (extended types) tests ------------------------------ Write-Host "`n[9/12] Testing Add-WUGActiveMonitor (extended types) ..." -ForegroundColor Cyan $script:ActiveMonitorTestNames = [System.Collections.Generic.List[string]]::new() $script:FirstExtActiveMonId = $null # -- Per-type basic creation (11 extended types) ------------------------------ Invoke-Test -Cmdlet 'Add-WUGActiveMonitor (Dns)' -Endpoint 'POST /monitors/-' -Test { $monName = "_test_activemon_dns_$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGActiveMonitor -Type Dns -Name $monName -DnsDomain 'example.com' -DnsRecordType a -Confirm:$false -ErrorAction Stop if (-not $r) { throw "No monitor ID returned" } $script:ActiveMonitorTestNames.Add($monName) $script:FirstExtActiveMonId = if ($r.monitorId) { "$($r.monitorId)" } elseif ($r.id) { "$($r.id)" } else { "$r" } } Invoke-Test -Cmdlet 'Add-WUGActiveMonitor (FileContent)' -Endpoint 'POST /monitors/-' -Test { $monName = "_test_activemon_filecontent_$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGActiveMonitor -Type FileContent -Name $monName -FileContentFolderPath 'C:\Logs' -FileContentPattern 'ERROR' -Confirm:$false -ErrorAction Stop if (-not $r) { throw "No monitor ID returned" } $script:ActiveMonitorTestNames.Add($monName) } Invoke-Test -Cmdlet 'Add-WUGActiveMonitor (FileProperties)' -Endpoint 'POST /monitors/-' -Test { $monName = "_test_activemon_fileprops_$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGActiveMonitor -Type FileProperties -Name $monName -FilePropertiesPath 'C:\Windows\System32\drivers\etc\hosts' -Confirm:$false -ErrorAction Stop if (-not $r) { throw "No monitor ID returned" } $script:ActiveMonitorTestNames.Add($monName) } Invoke-Test -Cmdlet 'Add-WUGActiveMonitor (Folder)' -Endpoint 'POST /monitors/-' -Test { $monName = "_test_activemon_folder_$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGActiveMonitor -Type Folder -Name $monName -FolderPath 'C:\Temp' -Confirm:$false -ErrorAction Stop if (-not $r) { throw "No monitor ID returned" } $script:ActiveMonitorTestNames.Add($monName) } Invoke-Test -Cmdlet 'Add-WUGActiveMonitor (HttpContent)' -Endpoint 'POST /monitors/-' -Test { $monName = "_test_activemon_http_$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGActiveMonitor -Type HttpContent -Name $monName -HttpContentUrl 'https://localhost/' -HttpContentContent 'OK' -Confirm:$false -ErrorAction Stop if (-not $r) { throw "No monitor ID returned" } $script:ActiveMonitorTestNames.Add($monName) } Invoke-Test -Cmdlet 'Add-WUGActiveMonitor (NetworkStatistics)' -Endpoint 'POST /monitors/-' -Test { $monName = "_test_activemon_netstat_$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGActiveMonitor -Type NetworkStatistics -Name $monName -Confirm:$false -ErrorAction Stop if (-not $r) { throw "No monitor ID returned" } $script:ActiveMonitorTestNames.Add($monName) } Invoke-Test -Cmdlet 'Add-WUGActiveMonitor (PingJitter)' -Endpoint 'POST /monitors/-' -Test { $monName = "_test_activemon_jitter_$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGActiveMonitor -Type PingJitter -Name $monName -Confirm:$false -ErrorAction Stop if (-not $r) { throw "No monitor ID returned" } $script:ActiveMonitorTestNames.Add($monName) } Invoke-Test -Cmdlet 'Add-WUGActiveMonitor (PowerShell)' -Endpoint 'POST /monitors/-' -Test { $monName = "_test_activemon_ps_$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGActiveMonitor -Type PowerShell -Name $monName -PowerShellScriptText '$context.SetResult(0, "OK")' -Confirm:$false -ErrorAction Stop if (-not $r) { throw "No monitor ID returned" } $script:ActiveMonitorTestNames.Add($monName) } Invoke-Test -Cmdlet 'Add-WUGActiveMonitor (RestApi)' -Endpoint 'POST /monitors/-' -Test { $monName = "_test_activemon_restapi_$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGActiveMonitor -Type RestApi -Name $monName -RestApiUrl 'https://localhost/api/health' -Confirm:$false -ErrorAction Stop if (-not $r) { throw "No monitor ID returned" } $script:ActiveMonitorTestNames.Add($monName) } Invoke-Test -Cmdlet 'Add-WUGActiveMonitor (Ssh)' -Endpoint 'POST /monitors/-' -Test { $monName = "_test_activemon_ssh_$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGActiveMonitor -Type Ssh -Name $monName -SshCommand 'uptime' -SshExpectedOutput 'load average' -Confirm:$false -ErrorAction Stop if (-not $r) { throw "No monitor ID returned" } $script:ActiveMonitorTestNames.Add($monName) } # -- Missing core types (TcpIp, SNMP constant, SNMP range, SNMPTable, Process, Certificate, Service, WMIFormatted, Ftp) -- Invoke-Test -Cmdlet 'Add-WUGActiveMonitor (TcpIp)' -Endpoint 'POST /monitors/-' -Test { $monName = "_test_activemon_tcpip_$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGActiveMonitor -Type TcpIp -Name $monName -TcpIpPort 443 -TcpIpProtocol SSL -Confirm:$false -ErrorAction Stop if (-not $r) { throw "No monitor ID returned" } $script:ActiveMonitorTestNames.Add($monName) } Invoke-Test -Cmdlet 'Add-WUGActiveMonitor (SNMP constant)' -Endpoint 'POST /monitors/-' -Test { $monName = "_test_activemon_snmp_const_$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGActiveMonitor -Type SNMP -Name $monName -SnmpOID '1.3.6.1.2.1.1.7.0' -SnmpCheckType constant -SnmpValue 72 -Confirm:$false -ErrorAction Stop if (-not $r) { throw "No monitor ID returned" } $script:ActiveMonitorTestNames.Add($monName) } Invoke-Test -Cmdlet 'Add-WUGActiveMonitor (SNMP range)' -Endpoint 'POST /monitors/-' -Test { $monName = "_test_activemon_snmp_range_$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGActiveMonitor -Type SNMP -Name $monName -SnmpOID '1.3.6.1.2.1.1.7.0' -SnmpCheckType range -SnmpLowValue 0 -SnmpHighValue 100 -Confirm:$false -ErrorAction Stop if (-not $r) { throw "No monitor ID returned" } $script:ActiveMonitorTestNames.Add($monName) } Invoke-Test -Cmdlet 'Add-WUGActiveMonitor (SNMPTable)' -Endpoint 'POST /monitors/-' -Test { $monName = "_test_activemon_snmptable_$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGActiveMonitor -Type SNMPTable -Name $monName ` -SnmpTableDiscOID '1.3.6.1.2.1.25.4.2.1.2' -SnmpTableDiscOperator equals -SnmpTableDiscValue 'svchost.exe' ` -SnmpTableMonitoredOID '1.3.6.1.2.1.25.4.2.1.7' -SnmpTableMonitorOperator constant -SnmpTableMonitoredValue '1' ` -Confirm:$false -ErrorAction Stop if (-not $r) { throw "No monitor ID returned" } $script:ActiveMonitorTestNames.Add($monName) } Invoke-Test -Cmdlet 'Add-WUGActiveMonitor (Process)' -Endpoint 'POST /monitors/-' -Test { $monName = "_test_activemon_process_$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGActiveMonitor -Type Process -Name $monName -ProcessName 'svchost.exe' -ProcessDownIfRunning 'false' -Confirm:$false -ErrorAction Stop if (-not $r) { throw "No monitor ID returned" } $script:ActiveMonitorTestNames.Add($monName) } Invoke-Test -Cmdlet 'Add-WUGActiveMonitor (Certificate)' -Endpoint 'POST /monitors/-' -Test { $monName = "_test_activemon_cert_$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGActiveMonitor -Type Certificate -Name $monName -CertOption url -CertPath 'https://localhost' -CertExpiresDays 30 -CertCheckExpires 'true' -Confirm:$false -ErrorAction Stop if (-not $r) { throw "No monitor ID returned" } $script:ActiveMonitorTestNames.Add($monName) } Invoke-Test -Cmdlet 'Add-WUGActiveMonitor (Service)' -Endpoint 'POST /monitors/-' -Test { $monName = "_test_activemon_service_$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGActiveMonitor -Type Service -Name $monName -ServiceDisplayName 'Windows Update' -ServiceInternalName 'wuauserv' -Confirm:$false -ErrorAction Stop if (-not $r) { throw "No monitor ID returned" } $script:ActiveMonitorTestNames.Add($monName) } Invoke-Test -Cmdlet 'Add-WUGActiveMonitor (WMIFormatted)' -Endpoint 'POST /monitors/-' -Test { $monName = "_test_activemon_wmi_$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGActiveMonitor -Type WMIFormatted -Name $monName ` -WMIFormattedRelativePath 'Win32_PerfFormattedData_PerfOS_Memory' ` -WMIFormattedPerformanceCounter 'Available MBytes' -WMIFormattedPerformanceInstance 'NULL' ` -WMIFormattedCheckType constant -WMIFormattedConstantValue 0 ` -WMIFormattedPropertyName 'AvailableMBytes' -WMIFormattedComputerName 'localhost' ` -Confirm:$false -ErrorAction Stop if (-not $r) { throw "No monitor ID returned" } $script:ActiveMonitorTestNames.Add($monName) } Invoke-Test -Cmdlet 'Add-WUGActiveMonitor (Ftp)' -Endpoint 'POST /monitors/-' -Test { $monName = "_test_activemon_ftp_$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGActiveMonitor -Type Ftp -Name $monName -FtpUsername 'anonymous' -FtpPassword 'test@test.com' -Confirm:$false -ErrorAction Stop if (-not $r) { throw "No monitor ID returned" } $script:ActiveMonitorTestNames.Add($monName) } # -- Assign extended active monitors to device -------------------------------- if ($script:TestDeviceId -and $script:FirstExtActiveMonId) { Invoke-Test -Cmdlet 'Add-WUGActiveMonitorToDevice (extended)' -Endpoint 'POST /devices/{id}/monitors/-' -Test { $result = Add-WUGActiveMonitorToDevice -DeviceId $script:TestDeviceId -MonitorId ([int]$script:FirstExtActiveMonId) -ErrorAction Stop if (-not $result) { throw "No result" } } } # -- Validation: mandatory parameter declarations ----------------------------- Invoke-Test -Cmdlet 'Add-WUGActiveMonitor (Ssh mandatory params)' -Endpoint '(validation)' -Test { $param = (Get-Command Add-WUGActiveMonitor).Parameters['SshExpectedOutput'] $isMandatory = $param.Attributes | Where-Object { $_ -is [System.Management.Automation.ParameterAttribute] -and $_.Mandatory } if (-not $isMandatory) { throw "SshExpectedOutput should be a mandatory parameter" } } Invoke-Test -Cmdlet 'Add-WUGActiveMonitor (RestApi mandatory params)' -Endpoint '(validation)' -Test { $param = (Get-Command Add-WUGActiveMonitor).Parameters['RestApiUrl'] $isMandatory = $param.Attributes | Where-Object { $_ -is [System.Management.Automation.ParameterAttribute] -and $_.Mandatory } if (-not $isMandatory) { throw "RestApiUrl should be a mandatory parameter" } } # -- Validation: invalid type ------------------------------------------------- Invoke-Test -Cmdlet 'Add-WUGActiveMonitor (invalid type)' -Endpoint '(validation)' -Test { $threw = $false try { Add-WUGActiveMonitor -Type 'NotAType' -Name '_test_should_fail' -Confirm:$false -ErrorAction Stop 2>$null } catch { $threw = $true } if (-not $threw) { throw "Expected parameter validation error but none occurred" } } #endregion #region -- Reports (device-level) --------------------------------------------- Write-Host "`n[10/12] Testing device report endpoints ..." -ForegroundColor Cyan if ($script:TestDeviceId) { # Test the umbrella Get-WUGDeviceReport with each ReportType $deviceReportTypes = @('Cpu','Disk','DiskSpaceFree','Interface','InterfaceDiscards','InterfaceErrors','InterfaceTraffic','Memory','PingAvailability','PingResponseTime','StateChange') foreach ($rt in $deviceReportTypes) { Invoke-Test -Cmdlet "Get-WUGDeviceReport ($rt)" -Endpoint "GET /devices/{id}/reports/$rt" -Test ([scriptblock]::Create( "Get-WUGDeviceReport -DeviceId $($script:TestDeviceId) -ReportType $rt -Range today -ErrorAction Stop | Out-Null" )) } # Also test the individual typed cmdlets $reportCmdlets = @( @{ Cmdlet = 'Get-WUGDeviceReportCpu'; Endpoint = 'GET /devices/{id}/reports/cpu' }, @{ Cmdlet = 'Get-WUGDeviceReportMemory'; Endpoint = 'GET /devices/{id}/reports/memory' }, @{ Cmdlet = 'Get-WUGDeviceReportDisk'; Endpoint = 'GET /devices/{id}/reports/disk' }, @{ Cmdlet = 'Get-WUGDeviceReportDiskSpaceFree'; Endpoint = 'GET /devices/{id}/reports/diskFree' }, @{ Cmdlet = 'Get-WUGDeviceReportInterface'; Endpoint = 'GET /devices/{id}/reports/interface' }, @{ Cmdlet = 'Get-WUGDeviceReportInterfaceTraffic'; Endpoint = 'GET /devices/{id}/reports/interfaceTraffic' }, @{ Cmdlet = 'Get-WUGDeviceReportInterfaceErrors'; Endpoint = 'GET /devices/{id}/reports/interfaceErrors' }, @{ Cmdlet = 'Get-WUGDeviceReportInterfaceDiscards'; Endpoint = 'GET /devices/{id}/reports/interfaceDiscards' }, @{ Cmdlet = 'Get-WUGDeviceReportPingAvailability'; Endpoint = 'GET /devices/{id}/reports/ping/availability' }, @{ Cmdlet = 'Get-WUGDeviceReportPingResponseTime'; Endpoint = 'GET /devices/{id}/reports/ping/responseTime' }, @{ Cmdlet = 'Get-WUGDeviceReportStateChange'; Endpoint = 'GET /devices/{id}/reports/stateChange' } ) foreach ($r in $reportCmdlets) { Invoke-Test -Cmdlet $r.Cmdlet -Endpoint $r.Endpoint -Test ([scriptblock]::Create( "& '$($r.Cmdlet)' -DeviceId $($script:TestDeviceId) -Range today -ErrorAction Stop | Out-Null" )) } # -- Report parameter variations ------------------------------------------ Invoke-Test -Cmdlet 'Get-WUGDeviceReport (lastNHours)' -Endpoint 'GET /devices/{id}/reports/cpu (lastN)' -Test { Get-WUGDeviceReport -DeviceId $script:TestDeviceId -ReportType Cpu -Range lastNHours -RangeN 4 -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceReport (lastWeek)' -Endpoint 'GET /devices/{id}/reports/memory (lastWeek)' -Test { Get-WUGDeviceReport -DeviceId $script:TestDeviceId -ReportType Memory -Range lastWeek -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceReport (custom range)' -Endpoint 'GET /devices/{id}/reports/cpu (custom)' -Test { $start = (Get-Date).AddDays(-7).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') $end = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') Get-WUGDeviceReport -DeviceId $script:TestDeviceId -ReportType Cpu -Range custom -RangeStartUtc $start -RangeEndUtc $end -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceReport (threshold)' -Endpoint 'GET /devices/{id}/reports/cpu (threshold)' -Test { Get-WUGDeviceReport -DeviceId $script:TestDeviceId -ReportType Cpu -Range today -ApplyThreshold true -OverThreshold true -ThresholdValue 90 -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceReport (sorted)' -Endpoint 'GET /devices/{id}/reports/cpu (sorted)' -Test { Get-WUGDeviceReport -DeviceId $script:TestDeviceId -ReportType Cpu -Range today -SortBy deviceName -SortByDir asc -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceReport (rollup)' -Endpoint 'GET /devices/{id}/reports/cpu (rollup)' -Test { Get-WUGDeviceReport -DeviceId $script:TestDeviceId -ReportType Cpu -Range today -RollupByDevice true -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceReport (lastNDays)' -Endpoint 'GET /devices/{id}/reports/disk (lastNDays)' -Test { Get-WUGDeviceReport -DeviceId $script:TestDeviceId -ReportType Disk -Range lastNDays -RangeN 3 -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceReport (pipeline)' -Endpoint 'GET /devices/{id}/reports/ping (pipeline)' -Test { $script:TestDeviceId | Get-WUGDeviceReport -ReportType PingAvailability -Range today -ErrorAction Stop | Out-Null } } #endregion #region -- Reports (group-level) ---------------------------------------------- Write-Host "`n[11/12] Testing device-group report endpoints ..." -ForegroundColor Cyan # Use root group (0) for group reports - always exists and has aggregated data # Test the umbrella Get-WUGDeviceGroupReport with each ReportType $groupReportTypes = @('Cpu','Disk','DiskSpaceFree','Interface','InterfaceDiscards','InterfaceErrors','InterfaceTraffic','Memory','PingAvailability','PingResponseTime','StateChange','Maintenance') foreach ($rt in $groupReportTypes) { Invoke-Test -Cmdlet "Get-WUGDeviceGroupReport ($rt)" -Endpoint "GET /device-groups/{id}/reports/$rt" -Test ([scriptblock]::Create( "Get-WUGDeviceGroupReport -GroupId 0 -ReportType $rt -Range today -ErrorAction Stop | Out-Null" )) } # Also test the individual typed cmdlets $groupReportCmdlets = @( @{ Cmdlet = 'Get-WUGDeviceGroupReportCpu'; Endpoint = 'GET /device-groups/{id}/reports/cpu' }, @{ Cmdlet = 'Get-WUGDeviceGroupReportMemory'; Endpoint = 'GET /device-groups/{id}/reports/memory' }, @{ Cmdlet = 'Get-WUGDeviceGroupReportDisk'; Endpoint = 'GET /device-groups/{id}/reports/disk' }, @{ Cmdlet = 'Get-WUGDeviceGroupReportDiskSpaceFree'; Endpoint = 'GET /device-groups/{id}/reports/diskFree' }, @{ Cmdlet = 'Get-WUGDeviceGroupReportInterface'; Endpoint = 'GET /device-groups/{id}/reports/interface' }, @{ Cmdlet = 'Get-WUGDeviceGroupReportInterfaceTraffic'; Endpoint = 'GET /device-groups/{id}/reports/interfaceTraffic' }, @{ Cmdlet = 'Get-WUGDeviceGroupReportInterfaceErrors'; Endpoint = 'GET /device-groups/{id}/reports/interfaceErrors' }, @{ Cmdlet = 'Get-WUGDeviceGroupReportInterfaceDiscards'; Endpoint = 'GET /device-groups/{id}/reports/interfaceDiscards' }, @{ Cmdlet = 'Get-WUGDeviceGroupReportPingAvailability'; Endpoint = 'GET /device-groups/{id}/reports/ping/availability' }, @{ Cmdlet = 'Get-WUGDeviceGroupReportPingResponseTime'; Endpoint = 'GET /device-groups/{id}/reports/ping/responseTime' }, @{ Cmdlet = 'Get-WUGDeviceGroupReportStateChange'; Endpoint = 'GET /device-groups/{id}/reports/stateChange' }, @{ Cmdlet = 'Get-WUGDeviceGroupReportMaintenance'; Endpoint = 'GET /device-groups/{id}/reports/maintenance' } ) foreach ($r in $groupReportCmdlets) { Invoke-Test -Cmdlet $r.Cmdlet -Endpoint $r.Endpoint -Test ([scriptblock]::Create( "& '$($r.Cmdlet)' -GroupId 0 -Range today -ErrorAction Stop | Out-Null" )) } # -- Group report parameter variations ---------------------------------------- Invoke-Test -Cmdlet 'Get-WUGDeviceGroupReport (hierarchy)' -Endpoint 'GET /device-groups/{id}/reports/cpu (hierarchy)' -Test { Get-WUGDeviceGroupReport -GroupId 0 -ReportType Cpu -Range today -ReturnHierarchy true -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceGroupReport (lastNHours)' -Endpoint 'GET /device-groups/{id}/reports/memory (lastN)' -Test { Get-WUGDeviceGroupReport -GroupId 0 -ReportType Memory -Range lastNHours -RangeN 6 -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceGroupReport (custom range)' -Endpoint 'GET /device-groups/{id}/reports/disk (custom)' -Test { $start = (Get-Date).AddDays(-7).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') $end = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') Get-WUGDeviceGroupReport -GroupId 0 -ReportType Disk -Range custom -RangeStartUtc $start -RangeEndUtc $end -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceGroupReport (threshold)' -Endpoint 'GET /device-groups/{id}/reports/cpu (threshold)' -Test { Get-WUGDeviceGroupReport -GroupId 0 -ReportType Cpu -Range today -ApplyThreshold true -OverThreshold true -ThresholdValue 90 -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceGroupReport (sorted)' -Endpoint 'GET /device-groups/{id}/reports/ping (sorted)' -Test { Get-WUGDeviceGroupReport -GroupId 0 -ReportType PingAvailability -Range today -SortBy deviceName -SortByDir asc -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceGroupReport (rollup)' -Endpoint 'GET /device-groups/{id}/reports/memory (rollup)' -Test { Get-WUGDeviceGroupReport -GroupId 0 -ReportType Memory -Range lastWeek -RollupByDevice true -ErrorAction Stop | Out-Null } Invoke-Test -Cmdlet 'Get-WUGDeviceGroupReport (pipeline)' -Endpoint 'GET /device-groups/{id}/reports/stateChange (pipeline)' -Test { @(0) | Get-WUGDeviceGroupReport -ReportType StateChange -Range today -ErrorAction Stop | Out-Null } #endregion #region -- Passive monitor tests ---------------------------------------------- Write-Host "`n--- Phase [10.5/12] Add-WUGPassiveMonitor + Add-WUGPassiveMonitorToDevice ---" -ForegroundColor Cyan $script:PassiveMonitorIds = [System.Collections.Generic.List[string]]::new() if ($script:TestDeviceId) { # -- SNMP Trap creation --------------------------------------------------- Invoke-Test -Cmdlet 'Add-WUGPassiveMonitor (SnmpTrap basic)' -Endpoint 'POST /monitors/-' -Test { $monName = "WhatsUpGoldPS-Test-SnmpTrap-$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGPassiveMonitor -Type SnmpTrap -Name $monName -SnmpTrapExpression 'test trap pattern' -Confirm:$false -ErrorAction Stop if (-not $r -or -not $r.MonitorId) { throw "No result or MonitorId returned" } $script:PassiveMonitorIds.Add("$($r.MonitorId)") } Invoke-Test -Cmdlet 'Add-WUGPassiveMonitor (SnmpTrap enterprise)' -Endpoint 'POST /monitors/-' -Test { $monName = "WhatsUpGoldPS-Test-SnmpTrapEnt-$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGPassiveMonitor -Type SnmpTrap -Name $monName -SnmpTrapOID '1.3.6.1.4.1.9.9.13.1.4.1.3' -SnmpTrapSpecificType '1' -SnmpTrapExpression 'enterprise match' -SnmpTrapMatchCase '1' -SnmpTrapInvertResult '1' -Confirm:$false -ErrorAction Stop if (-not $r -or -not $r.MonitorId) { throw "No result or MonitorId returned" } $script:PassiveMonitorIds.Add("$($r.MonitorId)") } # Create a 3rd SNMP trap for pipeline testing Invoke-Test -Cmdlet 'Add-WUGPassiveMonitor (SnmpTrap pipeline)' -Endpoint 'POST /monitors/-' -Test { $monName = "WhatsUpGoldPS-Test-SnmpTrapPipe-$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGPassiveMonitor -Type SnmpTrap -Name $monName -SnmpTrapExpression 'pipeline test pattern' -Confirm:$false -ErrorAction Stop if (-not $r -or -not $r.MonitorId) { throw "No result or MonitorId returned" } $script:PassiveMonitorIds.Add("$($r.MonitorId)") } # -- Assign to device ----------------------------------------------------- if ($script:PassiveMonitorIds.Count -ge 3) { Invoke-Test -Cmdlet 'Add-WUGPassiveMonitorToDevice (single)' -Endpoint 'POST /devices/{id}/monitors/-' -Test { $result = Add-WUGPassiveMonitorToDevice -DeviceId $script:TestDeviceId -MonitorId ([int]$script:PassiveMonitorIds[0]) -ErrorAction Stop if (-not $result) { throw "No result" } } Invoke-Test -Cmdlet 'Add-WUGPassiveMonitorToDevice (pipeline)' -Endpoint 'POST /devices/{id}/monitors/-' -Test { $obj = [PSCustomObject]@{ DeviceId = $script:TestDeviceId } $result = $obj | Add-WUGPassiveMonitorToDevice -MonitorId ([int]$script:PassiveMonitorIds[2]) -ErrorAction Stop if (-not $result) { throw "No result from pipeline input" } } } elseif ($script:PassiveMonitorIds.Count -gt 0) { Invoke-Test -Cmdlet 'Add-WUGPassiveMonitorToDevice (single fallback)' -Endpoint 'POST /devices/{id}/monitors/-' -Test { $result = Add-WUGPassiveMonitorToDevice -DeviceId $script:TestDeviceId -MonitorId ([int]$script:PassiveMonitorIds[0]) -ErrorAction Stop if (-not $result) { throw "No result" } } } # -- Validation: invalid type --------------------------------------------- Invoke-Test -Cmdlet 'Add-WUGPassiveMonitor (invalid type)' -Endpoint '(validation)' -Test { $threw = $false try { Add-WUGPassiveMonitor -Type 'NonExistentType' -Name 'bad' -Confirm:$false -ErrorAction Stop 2>$null } catch { $threw = $true } if (-not $threw) { throw "Expected parameter validation error but none occurred" } } # -- Syslog passive monitors --------------------------------------------- Invoke-Test -Cmdlet 'Add-WUGPassiveMonitor (Syslog)' -Endpoint 'POST /monitors/-' -Test { $monName = "WhatsUpGoldPS-Test-Syslog-$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGPassiveMonitor -Type Syslog -Name $monName -SyslogExpression "error|critical" -Confirm:$false -ErrorAction Stop if (-not $r) { throw "No result" } $script:PassiveMonitorIds.Add("$($r.MonitorId)") } Invoke-Test -Cmdlet 'Add-WUGPassiveMonitor (Syslog case-sensitive)' -Endpoint 'POST /monitors/-' -Test { $monName = "WhatsUpGoldPS-Test-SyslogCase-$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGPassiveMonitor -Type Syslog -Name $monName -SyslogExpression "CRITICAL" -SyslogMatchCase '1' -Confirm:$false -ErrorAction Stop if (-not $r) { throw "No result" } $script:PassiveMonitorIds.Add("$($r.MonitorId)") } # -- WinEvent passive monitors -------------------------------------------- Invoke-Test -Cmdlet 'Add-WUGPassiveMonitor (WinEvent)' -Endpoint 'POST /monitors/-' -Test { $monName = "WhatsUpGoldPS-Test-WinEvent-$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGPassiveMonitor -Type WinEvent -Name $monName -WinEventExpression "Application Error" -Confirm:$false -ErrorAction Stop if (-not $r) { throw "No result" } $script:PassiveMonitorIds.Add("$($r.MonitorId)") } Invoke-Test -Cmdlet 'Add-WUGPassiveMonitor (WinEvent inverted)' -Endpoint 'POST /monitors/-' -Test { $monName = "WhatsUpGoldPS-Test-WinEventInv-$(Get-Date -Format 'yyyyMMddHHmmss')" $r = Add-WUGPassiveMonitor -Type WinEvent -Name $monName -WinEventExpression "Healthy" -WinEventInvertResult '1' -Confirm:$false -ErrorAction Stop if (-not $r) { throw "No result" } $script:PassiveMonitorIds.Add("$($r.MonitorId)") } # -- Assign remaining passive monitors to device -------------------------- if ($script:PassiveMonitorIds.Count -ge 2) { Invoke-Test -Cmdlet 'Add-WUGPassiveMonitorToDevice (multi-monitor)' -Endpoint 'POST /devices/{id}/monitors/-' -Test { $ids = $script:PassiveMonitorIds | ForEach-Object { [int]$_ } $result = Add-WUGPassiveMonitorToDevice -DeviceId $script:TestDeviceId -MonitorId $ids -ErrorAction Stop if (-not $result) { throw "No result" } } } } else { Record-Test -Cmdlet 'Add-WUGPassiveMonitor (all)' -Endpoint '(skipped)' -Status 'Skipped' -Detail 'No test device available' } #endregion #region -- Cleanup ------------------------------------------------------------ Write-Host "`n[12/12] Cleaning up test artefacts ..." -ForegroundColor Cyan # Remove passive monitors created during phase 10.5 (use BySearch - ById endpoint only works for active monitors) if ($script:PassiveMonitorNames.Count -gt 0) { foreach ($pmName in $script:PassiveMonitorNames) { Invoke-Test -Cmdlet "Remove-WUGActiveMonitor (passiveMon $pmName)" -Endpoint 'DELETE /monitors/-?type=passive' -Test { Remove-WUGActiveMonitor -Search $pmName -Type passive -Confirm:$false -ErrorAction Stop | Out-Null } } } # Remove performance monitors created during phase 8 (use BySearch - ById endpoint only works for active monitors) # Performance monitors auto-named: PerfMon-{Type}-Device{DeviceId}-{timestamp} Invoke-Test -Cmdlet 'Remove-WUGActiveMonitor (perfMons by search)' -Endpoint 'DELETE /monitors/-?type=performance' -Test { Remove-WUGActiveMonitor -Search "PerfMon-" -Type performance -FailIfInUse $false -Confirm:$false -ErrorAction Stop | Out-Null } # Remove active monitors created during phase 9 (by search name) if ($script:ActiveMonitorTestNames.Count -gt 0) { foreach ($amName in $script:ActiveMonitorTestNames) { Invoke-Test -Cmdlet "Remove-WUGActiveMonitor (activeMon $amName)" -Endpoint 'DELETE /monitors/-' -Test { Remove-WUGActiveMonitor -Search $amName -Type active -Confirm:$false -ErrorAction Stop | Out-Null } } } # Remove the test monitor from library via BySearch (ById DELETE may 405 on some WUG versions) if ($script:TestMonitorName) { Invoke-Test -Cmdlet 'Remove-WUGActiveMonitor (bySearch)' -Endpoint 'DELETE /monitors/-' -Test { Remove-WUGActiveMonitor -Search $script:TestMonitorName -Type active -Confirm:$false -ErrorAction Stop | Out-Null } } # Remove the test device if ($script:TestDeviceId) { Invoke-Test -Cmdlet 'Remove-WUGDevice' -Endpoint 'DELETE /devices/{id}' -Test { Remove-WUGDevice -DeviceId $script:TestDeviceId -Confirm:$false -ErrorAction Stop | Out-Null } } # Remove the scan-created device (127.0.0.2) if it was added if ($script:AddDeviceScanResult) { Invoke-Test -Cmdlet 'Remove-WUGDevice (scan device)' -Endpoint 'DELETE /devices/{id}' -Test { $scanDev = Get-WUGDevice -SearchValue '127.0.0.2' -ErrorAction SilentlyContinue if ($scanDev) { $scanDevId = if ($scanDev[0].id) { $scanDev[0].id } elseif ($scanDev[0]) { $scanDev[0] } else { $null } if ($scanDevId) { Remove-WUGDevice -DeviceId $scanDevId -Confirm:$false -ErrorAction Stop | Out-Null } } } } # Remove the test group if ($script:TestGroupId) { Invoke-Test -Cmdlet 'Remove-WUGDeviceGroup' -Endpoint 'DELETE /device-groups/{id}' -Test { Remove-WUGDeviceGroup -GroupId $script:TestGroupId -Confirm:$false -ErrorAction Stop | Out-Null } } # Remove test credentials from library if ($script:TestCredentialIds.Count -gt 0) { foreach ($credId in $script:TestCredentialIds) { Invoke-Test -Cmdlet "Set-WUGCredential (delete $credId)" -Endpoint 'DELETE /credentials/{id}' -Test { Set-WUGCredential -CredentialId $credId -Remove -Confirm:$false -ErrorAction Stop | Out-Null } } } # Disconnect Invoke-Test -Cmdlet 'Disconnect-WUGServer' -Endpoint '(session cleanup)' -Test { Disconnect-WUGServer -ErrorAction Stop if ($global:WUGBearerHeaders) { throw "Headers still set after disconnect" } } #endregion #region -- Summary ------------------------------------------------------------ Write-Host "`n============================================================" -ForegroundColor Cyan Write-Host " TEST RESULTS SUMMARY" -ForegroundColor Cyan Write-Host "============================================================" -ForegroundColor Cyan $passed = ($script:TestResults | Where-Object Status -eq 'Pass').Count $failed = ($script:TestResults | Where-Object Status -eq 'Fail').Count $skipped = ($script:TestResults | Where-Object Status -eq 'Skipped').Count $total = $script:TestResults.Count Write-Host "`n Total : $total" -ForegroundColor White Write-Host " Pass : $passed" -ForegroundColor Green Write-Host " Fail : $failed" -ForegroundColor $(if ($failed -gt 0) { 'Red' } else { 'Green' }) Write-Host " Skip : $skipped" -ForegroundColor Yellow if ($failed -gt 0) { Write-Host "`n FAILED TESTS:" -ForegroundColor Red $script:TestResults | Where-Object Status -eq 'Fail' | ForEach-Object { Write-Host " - $($_.Cmdlet) [$($_.Endpoint)]" -ForegroundColor Red if ($_.Detail) { Write-Host " $($_.Detail)" -ForegroundColor DarkRed } } } Write-Host "`n============================================================" -ForegroundColor Cyan # Output structured results for programmatic consumption $script:TestResults | Format-Table -AutoSize -Property Cmdlet, Endpoint, Status, Detail #region -- HTML Dashboard ----------------------------------------------------- try { $templatePath = Join-Path $PSScriptRoot 'Test-Dashboard-Template.html' if (Test-Path $templatePath) { # Build Bootstrap Table columns JSON $columns = @( @{ field = 'Cmdlet'; title = 'Cmdlet'; sortable = $true; filterControl = 'input' } @{ field = 'Endpoint'; title = 'Endpoint'; sortable = $true; filterControl = 'input' } @{ field = 'Status'; title = 'Status'; sortable = $true; filterControl = 'select'; formatter = 'formatTestStatus' } @{ field = 'Detail'; title = 'Detail'; sortable = $true; filterControl = 'input' } ) $columnsJson = ($columns | ConvertTo-Json -Depth 4 -Compress) -replace '"formatTestStatus"', 'formatTestStatus' # Build data JSON (escape for safe embedding) $dataRows = $script:TestResults | ForEach-Object { @{ Cmdlet = $_.Cmdlet Endpoint = $_.Endpoint Status = $_.Status Detail = $_.Detail } } $dataJson = @($dataRows) | ConvertTo-Json -Depth 4 -Compress if ($script:TestResults.Count -eq 1) { $dataJson = "[$dataJson]" } # Build the bootstrap-table init block $tableInit = "columns: $columnsJson,`n data: $dataJson" # Read template and replace tokens $html = Get-Content $templatePath -Raw $html = $html -replace 'replaceThisHere', $tableInit $html = $html -replace 'ReplaceYourReportNameHere', 'WhatsUpGoldPS Test Results' $html = $html -replace 'ReplaceUpdateTimeHere', (Get-Date -Format 'yyyy-MM-dd HH:mm:ss') # Write dashboard to %TEMP% $dashboardPath = Join-Path $env:TEMP "WhatsUpGoldPS-TestResults-$(Get-Date -Format 'yyyyMMddHHmmss').html" $html | Out-File -FilePath $dashboardPath -Encoding utf8 -Force Write-Host "`n Dashboard: $dashboardPath" -ForegroundColor Green Start-Process $dashboardPath } else { Write-Host "`n [WARN] Dashboard template not found at: $templatePath" -ForegroundColor Yellow } } catch { Write-Host "`n [WARN] Dashboard generation failed: $($_.Exception.Message)" -ForegroundColor Yellow } #endregion # Return results object $script:TestResults #endregion # SIG # Begin signature block # MIIVlwYJKoZIhvcNAQcCoIIViDCCFYQCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBSpQwhNUX09UsA # cWkTXJdcaPRwtPh5mPH5vEmUPPYmCqCCEdMwggVvMIIEV6ADAgECAhBI/JO0YFWU # jTanyYqJ1pQWMA0GCSqGSIb3DQEBDAUAMHsxCzAJBgNVBAYTAkdCMRswGQYDVQQI # DBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoM # EUNvbW9kbyBDQSBMaW1pdGVkMSEwHwYDVQQDDBhBQUEgQ2VydGlmaWNhdGUgU2Vy # dmljZXMwHhcNMjEwNTI1MDAwMDAwWhcNMjgxMjMxMjM1OTU5WjBWMQswCQYDVQQG # EwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMS0wKwYDVQQDEyRTZWN0aWdv # IFB1YmxpYyBDb2RlIFNpZ25pbmcgUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEBAQUA # A4ICDwAwggIKAoICAQCN55QSIgQkdC7/FiMCkoq2rjaFrEfUI5ErPtx94jGgUW+s # hJHjUoq14pbe0IdjJImK/+8Skzt9u7aKvb0Ffyeba2XTpQxpsbxJOZrxbW6q5KCD # J9qaDStQ6Utbs7hkNqR+Sj2pcaths3OzPAsM79szV+W+NDfjlxtd/R8SPYIDdub7 # P2bSlDFp+m2zNKzBenjcklDyZMeqLQSrw2rq4C+np9xu1+j/2iGrQL+57g2extme # me/G3h+pDHazJyCh1rr9gOcB0u/rgimVcI3/uxXP/tEPNqIuTzKQdEZrRzUTdwUz # T2MuuC3hv2WnBGsY2HH6zAjybYmZELGt2z4s5KoYsMYHAXVn3m3pY2MeNn9pib6q # RT5uWl+PoVvLnTCGMOgDs0DGDQ84zWeoU4j6uDBl+m/H5x2xg3RpPqzEaDux5mcz # mrYI4IAFSEDu9oJkRqj1c7AGlfJsZZ+/VVscnFcax3hGfHCqlBuCF6yH6bbJDoEc # QNYWFyn8XJwYK+pF9e+91WdPKF4F7pBMeufG9ND8+s0+MkYTIDaKBOq3qgdGnA2T # OglmmVhcKaO5DKYwODzQRjY1fJy67sPV+Qp2+n4FG0DKkjXp1XrRtX8ArqmQqsV/ # AZwQsRb8zG4Y3G9i/qZQp7h7uJ0VP/4gDHXIIloTlRmQAOka1cKG8eOO7F/05QID # AQABo4IBEjCCAQ4wHwYDVR0jBBgwFoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQwHQYD # VR0OBBYEFDLrkpr/NZZILyhAQnAgNpFcF4XmMA4GA1UdDwEB/wQEAwIBhjAPBgNV # HRMBAf8EBTADAQH/MBMGA1UdJQQMMAoGCCsGAQUFBwMDMBsGA1UdIAQUMBIwBgYE # VR0gADAIBgZngQwBBAEwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21v # ZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEE # KDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZI # hvcNAQEMBQADggEBABK/oe+LdJqYRLhpRrWrJAoMpIpnuDqBv0WKfVIHqI0fTiGF # OaNrXi0ghr8QuK55O1PNtPvYRL4G2VxjZ9RAFodEhnIq1jIV9RKDwvnhXRFAZ/ZC # J3LFI+ICOBpMIOLbAffNRk8monxmwFE2tokCVMf8WPtsAO7+mKYulaEMUykfb9gZ # pk+e96wJ6l2CxouvgKe9gUhShDHaMuwV5KZMPWw5c9QLhTkg4IUaaOGnSDip0TYl # d8GNGRbFiExmfS9jzpjoad+sPKhdnckcW67Y8y90z7h+9teDnRGWYpquRRPaf9xH # +9/DUp/mBlXpnYzyOmJRvOwkDynUWICE5EV7WtgwggYaMIIEAqADAgECAhBiHW0M # UgGeO5B5FSCJIRwKMA0GCSqGSIb3DQEBDAUAMFYxCzAJBgNVBAYTAkdCMRgwFgYD # VQQKEw9TZWN0aWdvIExpbWl0ZWQxLTArBgNVBAMTJFNlY3RpZ28gUHVibGljIENv # ZGUgU2lnbmluZyBSb290IFI0NjAeFw0yMTAzMjIwMDAwMDBaFw0zNjAzMjEyMzU5 # NTlaMFQxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxKzAp # BgNVBAMTIlNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYwggGiMA0G # CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCbK51T+jU/jmAGQ2rAz/V/9shTUxjI # ztNsfvxYB5UXeWUzCxEeAEZGbEN4QMgCsJLZUKhWThj/yPqy0iSZhXkZ6Pg2A2NV # DgFigOMYzB2OKhdqfWGVoYW3haT29PSTahYkwmMv0b/83nbeECbiMXhSOtbam+/3 # 6F09fy1tsB8je/RV0mIk8XL/tfCK6cPuYHE215wzrK0h1SWHTxPbPuYkRdkP05Zw # mRmTnAO5/arnY83jeNzhP06ShdnRqtZlV59+8yv+KIhE5ILMqgOZYAENHNX9SJDm # +qxp4VqpB3MV/h53yl41aHU5pledi9lCBbH9JeIkNFICiVHNkRmq4TpxtwfvjsUe # dyz8rNyfQJy/aOs5b4s+ac7IH60B+Ja7TVM+EKv1WuTGwcLmoU3FpOFMbmPj8pz4 # 4MPZ1f9+YEQIQty/NQd/2yGgW+ufflcZ/ZE9o1M7a5Jnqf2i2/uMSWymR8r2oQBM # dlyh2n5HirY4jKnFH/9gRvd+QOfdRrJZb1sCAwEAAaOCAWQwggFgMB8GA1UdIwQY # MBaAFDLrkpr/NZZILyhAQnAgNpFcF4XmMB0GA1UdDgQWBBQPKssghyi47G9IritU # pimqF6TNDDAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADATBgNV # HSUEDDAKBggrBgEFBQcDAzAbBgNVHSAEFDASMAYGBFUdIAAwCAYGZ4EMAQQBMEsG # A1UdHwREMEIwQKA+oDyGOmh0dHA6Ly9jcmwuc2VjdGlnby5jb20vU2VjdGlnb1B1 # YmxpY0NvZGVTaWduaW5nUm9vdFI0Ni5jcmwwewYIKwYBBQUHAQEEbzBtMEYGCCsG # AQUFBzAChjpodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2Rl # U2lnbmluZ1Jvb3RSNDYucDdjMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5zZWN0 # aWdvLmNvbTANBgkqhkiG9w0BAQwFAAOCAgEABv+C4XdjNm57oRUgmxP/BP6YdURh # w1aVcdGRP4Wh60BAscjW4HL9hcpkOTz5jUug2oeunbYAowbFC2AKK+cMcXIBD0Zd # OaWTsyNyBBsMLHqafvIhrCymlaS98+QpoBCyKppP0OcxYEdU0hpsaqBBIZOtBajj # cw5+w/KeFvPYfLF/ldYpmlG+vd0xqlqd099iChnyIMvY5HexjO2AmtsbpVn0OhNc # WbWDRF/3sBp6fWXhz7DcML4iTAWS+MVXeNLj1lJziVKEoroGs9Mlizg0bUMbOalO # hOfCipnx8CaLZeVme5yELg09Jlo8BMe80jO37PU8ejfkP9/uPak7VLwELKxAMcJs # zkyeiaerlphwoKx1uHRzNyE6bxuSKcutisqmKL5OTunAvtONEoteSiabkPVSZ2z7 # 6mKnzAfZxCl/3dq3dUNw4rg3sTCggkHSRqTqlLMS7gjrhTqBmzu1L90Y1KWN/Y5J # KdGvspbOrTfOXyXvmPL6E52z1NZJ6ctuMFBQZH3pwWvqURR8AgQdULUvrxjUYbHH # j95Ejza63zdrEcxWLDX6xWls/GDnVNueKjWUH3fTv1Y8Wdho698YADR7TNx8X8z2 # Bev6SivBBOHY+uqiirZtg0y9ShQoPzmCcn63Syatatvx157YK9hlcPmVoa1oDE5/ # L9Uo2bC5a4CH2RwwggY+MIIEpqADAgECAhAHnODk0RR/hc05c892LTfrMA0GCSqG # SIb3DQEBDAUAMFQxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0 # ZWQxKzApBgNVBAMTIlNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYw # HhcNMjYwMjA5MDAwMDAwWhcNMjkwNDIxMjM1OTU5WjBVMQswCQYDVQQGEwJVUzEU # MBIGA1UECAwLQ29ubmVjdGljdXQxFzAVBgNVBAoMDkphc29uIEFsYmVyaW5vMRcw # FQYDVQQDDA5KYXNvbiBBbGJlcmlubzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC # AgoCggIBAPN6aN4B1yYWkI5b5TBj3I0VV/peETrHb6EY4BHGxt8Ap+eT+WpEpJyE # tRYPxEmNJL3A38Bkg7mwzPE3/1NK570ZBCuBjSAn4mSDIgIuXZnvyBO9W1OQs5d6 # 7MlJLUAEufl18tOr3ST1DeO9gSjQSAE5Nql0QDxPnm93OZBon+Fz3CmE+z3MwAe2 # h4KdtRAnCqwM+/V7iBdbw+JOxolpx+7RVjGyProTENIG3pe/hKvPb501lf8uBAAD # LdjZr5ip8vIWbf857Yw1Bu10nVI7HW3eE8Cl5//d1ribHlzTzQLfttW+k+DaFsKZ # BBL56l4YAlIVRsrOiE1kdHYYx6IGrEA809R7+TZA9DzGqyFiv9qmJAbL4fDwetDe # yIq+Oztz1LvEdy8Rcd0JBY+J4S0eDEFIA3X0N8VcLeAwabKb9AjulKXwUeqCJLvN # 79CJ90UTZb2+I+tamj0dn+IKMEsJ4v4Ggx72sxFr9+6XziodtTg5Luf2xd6+Phha # mOxF2px9LObhBLLEMyRsCHZIzVZOFKu9BpHQH7ufGB+Sa80Tli0/6LEyn9+bMYWi # 2ttn6lLOPThXMiQaooRUq6q2u3+F4SaPlxVFLI7OJVMhar6nW6joBvELTJPmANSM # jDSRFDfHRCdGbZsL/keELJNy+jZctF6VvxQEjFM8/bazu6qYhrA7AgMBAAGjggGJ # MIIBhTAfBgNVHSMEGDAWgBQPKssghyi47G9IritUpimqF6TNDDAdBgNVHQ4EFgQU # 6YF0o0D5AVhKHbVocr8GaSIBibAwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQC # MAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwSgYDVR0gBEMwQTA1BgwrBgEEAbIxAQIB # AwIwJTAjBggrBgEFBQcCARYXaHR0cHM6Ly9zZWN0aWdvLmNvbS9DUFMwCAYGZ4EM # AQQBMEkGA1UdHwRCMEAwPqA8oDqGOGh0dHA6Ly9jcmwuc2VjdGlnby5jb20vU2Vj # dGlnb1B1YmxpY0NvZGVTaWduaW5nQ0FSMzYuY3JsMHkGCCsGAQUFBwEBBG0wazBE # BggrBgEFBQcwAoY4aHR0cDovL2NydC5zZWN0aWdvLmNvbS9TZWN0aWdvUHVibGlj # Q29kZVNpZ25pbmdDQVIzNi5jcnQwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLnNl # Y3RpZ28uY29tMA0GCSqGSIb3DQEBDAUAA4IBgQAEIsm4xnOd/tZMVrKwi3doAXvC # wOA/RYQnFJD7R/bSQRu3wXEK4o9SIefye18B/q4fhBkhNAJuEvTQAGfqbbpxow03 # J5PrDTp1WPCWbXKX8Oz9vGWJFyJxRGftkdzZ57JE00synEMS8XCwLO9P32MyR9Z9 # URrpiLPJ9rQjfHMb1BUdvaNayomm7aWLAnD+X7jm6o8sNT5An1cwEAob7obWDM6s # X93wphwJNBJAstH9Ozs6LwISOX6sKS7CKm9N3Kp8hOUue0ZHAtZdFl6o5u12wy+z # zieGEI50fKnN77FfNKFOWKlS6OJwlArcbFegB5K89LcE5iNSmaM3VMB2ADV1FEcj # GSHw4lTg1Wx+WMAMdl/7nbvfFxJ9uu5tNiT54B0s+lZO/HztwXYQUczdsFon3pjs # Nrsk9ZlalBi5SHkIu+F6g7tWiEv3rtVApmJRnLkUr2Xq2a4nbslUCt4jKs5UX4V1 # nSX8OM++AXoyVGO+iTj7z+pl6XE9Gw/Td6WKKKsxggMaMIIDFgIBATBoMFQxCzAJ # BgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxKzApBgNVBAMTIlNl # Y3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYCEAec4OTRFH+FzTlzz3Yt # N+swDQYJYIZIAWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZ # BgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYB # BAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQg983dNys7LbM3yqfpYY4HmFiZSRUv2TzB # 0PwW0H4gR+AwDQYJKoZIhvcNAQEBBQAEggIA3BZIWL0UhlPsjjohtgyV7wvDnVaD # s/grNU3useBLYf8ux5QC46b4hDMvveCWVydplPLJX+ZPzdo4iE8FLWDS5RAbpO/H # 6dEG4Rvb8VlylqtBj8tqNObIQXWZ0wHBDZVqfIpYJw38EuFHELUI8dpTzQXYV9ez # gqALFEfI3N9tB/t+KkScNV3ScbEd0dMglPeGCWTiwzo0npIUdhtWWyJ3qb4HHip2 # 95BY48QJ6klJ43bLniUn3vVx1oiLiHkZvdZ+TROXPwlmX34jwZmL2nBpZkRN8SYd # 8hSMHcqpFKH5NZHRIZXIRECM431LY6/vIarDonmWAq+5ih4XT8dP4xDU/SeX1S9V # T8CB1PzlXMn2uwMipj+L/CTmb2bixBeKJiegC7BvByZCWwgrpX2YH68kJMDa1cd7 # G/VZ6XCind7B2HSMIdhYcQXm7zE9197o7zdead+448+MObB4gxh0WhxITzwA+Xig # QGBOVY9l22x281PSKYCkrKqmg8RAvkW1ZL60V79ORlV/qBC93DjRiiRHwCkILePp # EVxrZAVnsVih0ZqcK4UrO1biJlcYMniAESdf9MW0bJr0PhghB6xd7C6wXLBXTEDo # T9HekSa/zbOmCVEaHrJDIx9xwqnZ+0utR0sGACFSB4oCH9G38dKQp0AULfgxjRhz # 4/R7uHhWRO2mWcI= # SIG # End signature block |