Public/Tests/Invoke-TelemetryCollection.Tests.ps1
|
BeforeAll { $env:TCS_CONFIG_ROOT = Join-Path -Path $TestDrive -ChildPath 'config' $env:TCS_SKIP_UPDATE_CHECK = '1' $env:TCS_TELEMETRY_OPTOUT = '1' $ModuleRoot = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent Import-Module -Name (Join-Path -Path $ModuleRoot -ChildPath 'tcs.core.psd1') -Force } AfterAll { Remove-Module -Name tcs.core -Force -ErrorAction SilentlyContinue } Describe 'Invoke-TelemetryCollection' { BeforeAll { $uri = 'https://telemetry.example.com/ingest/powershell' } BeforeEach { $env:TCS_TELEMETRY_OPTOUT = $null $env:TCS_TELEMETRY_URI = $null Mock -ModuleName tcs.core Send-TelemetryPayload { } } AfterAll { $env:TCS_TELEMETRY_OPTOUT = '1' } It 'Sends one event on End with the measured duration and the ingestion schema' { Invoke-TelemetryCollection -ModuleName 'tcs.x' -ModuleVersion '1.0.0' -CommandName 'Get-X' -ExecutionID 'e1' -Stage Start -URI $uri Start-Sleep -Milliseconds 50 Invoke-TelemetryCollection -ModuleName 'tcs.x' -ModuleVersion '1.0.0' -CommandName 'Get-X' -ExecutionID 'e1' -Stage End -URI $uri -ApiKey 'key' Should -Invoke -ModuleName tcs.core Send-TelemetryPayload -Times 1 -Exactly -ParameterFilter { $body = $Body | ConvertFrom-Json $Uri -eq 'https://telemetry.example.com/ingest/powershell' -and $ApiKey -eq 'key' -and $body.command_name -eq 'Get-X' -and $body.module_name -eq 'tcs.x' -and $body.version -eq '1.0.0' -and $body.duration_ms -ge 40 -and $body.success -eq $true -and $body.tags.stage -eq 'End' -and $body.os_platform -and $body.ps_version -and $body.host_name -match '^[0-9a-f-]{36}$' } } It 'Sends only the exception type, never the message, user or path' { $err = [System.IO.IOException]::new('C:\Users\secret\file.txt') Invoke-TelemetryCollection -ModuleName 'tcs.x' -ExecutionID 'e2' -Stage End -Failed $true -Exception $err -URI $uri -ModulePath 'C:\Users\secret' Should -Invoke -ModuleName tcs.core Send-TelemetryPayload -Times 1 -Exactly -ParameterFilter { $Body -notmatch 'secret' -and ($Body | ConvertFrom-Json).error_type -eq 'System.IO.IOException' -and ($Body | ConvertFrom-Json).success -eq $false -and $Body -notmatch [regex]::Escape([Environment]::UserName) -and $Body -notmatch [regex]::Escape([Environment]::MachineName) } } It 'Accepts an ErrorRecord as the exception' { try { throw [System.InvalidOperationException]::new('boom') } catch { $record = $_ } Invoke-TelemetryCollection -ModuleName 'tcs.x' -ExecutionID 'e3' -Stage End -Failed $true -Exception $record -URI $uri Should -Invoke -ModuleName tcs.core Send-TelemetryPayload -Times 1 -Exactly -ParameterFilter { ($Body | ConvertFrom-Json).error_type -eq 'System.InvalidOperationException' } } It 'Does not send on Start or In-Progress' { Invoke-TelemetryCollection -ModuleName 'tcs.x' -ExecutionID 'e4' -Stage Start -URI $uri Invoke-TelemetryCollection -ModuleName 'tcs.x' -ExecutionID 'e4' -Stage 'In-Progress' -URI $uri Should -Invoke -ModuleName tcs.core Send-TelemetryPayload -Times 0 -Exactly } It 'Sends nothing when TCS_TELEMETRY_OPTOUT is set' { $env:TCS_TELEMETRY_OPTOUT = '1' Invoke-TelemetryCollection -ModuleName 'tcs.x' -ExecutionID 'e5' -Stage Module-Load -URI $uri Should -Invoke -ModuleName tcs.core Send-TelemetryPayload -Times 0 -Exactly } It 'Sends nothing when the module has telemetry turned off' { InModuleScope tcs.core { $script:ModuleConfigCache['tcs.off'] = @{ Telemetry = $false } } Invoke-TelemetryCollection -ModuleName 'tcs.off' -ExecutionID 'e6' -Stage Module-Load -URI $uri Should -Invoke -ModuleName tcs.core Send-TelemetryPayload -Times 0 -Exactly } It 'Sends nothing when no endpoint is configured' { Invoke-TelemetryCollection -ModuleName 'tcs.x' -ExecutionID 'e7' -Stage Module-Load Should -Invoke -ModuleName tcs.core Send-TelemetryPayload -Times 0 -Exactly } It 'Uses TCS_TELEMETRY_URI when no URI is passed' { $env:TCS_TELEMETRY_URI = $uri Invoke-TelemetryCollection -ModuleName 'tcs.x' -ExecutionID 'e8' -Stage Module-Load Should -Invoke -ModuleName tcs.core Send-TelemetryPayload -Times 1 -Exactly -ParameterFilter { $Uri -eq 'https://telemetry.example.com/ingest/powershell' } } It 'Refuses non-HTTPS endpoints other than localhost' { Invoke-TelemetryCollection -ModuleName 'tcs.x' -ExecutionID 'e9' -Stage Module-Load -URI 'http://telemetry.example.com/ingest' Should -Invoke -ModuleName tcs.core Send-TelemetryPayload -Times 0 -Exactly } It 'Never throws, even if sending fails' { Mock -ModuleName tcs.core Send-TelemetryPayload { throw 'network down' } { Invoke-TelemetryCollection -ModuleName 'tcs.x' -ExecutionID 'e10' -Stage Module-Load -URI $uri } | Should -Not -Throw } It 'Keeps the same installation ID across calls' { $first = InModuleScope tcs.core { Get-TelemetryInstallationId } InModuleScope tcs.core { $script:TelemetryInstallationId = $null } InModuleScope tcs.core { Get-TelemetryInstallationId } | Should -Be $first } } |