tests/ServiceManagement.Tests.ps1
|
#Requires -Version 7.0 <# .SYNOPSIS Pester v5 tests for osquery service management functions and aliases. .NOTES Install Pester v5 before running: Install-PSResource Pester -Version '5.*' Run tests with: Invoke-Pester ./tests/ServiceManagement.Tests.ps1 -Output Detailed #> BeforeAll { $modulePath = Resolve-Path (Join-Path $PSScriptRoot '..' 'osquery.psd1') Import-Module $modulePath -Force -WarningAction SilentlyContinue } AfterAll { Remove-Module osquery -Force -ErrorAction SilentlyContinue } # --------------------------------------------------------------------------- # Aliases # --------------------------------------------------------------------------- Describe 'Alias Exports' { It 'Enable-OsQueryService resolves to Enable-OsQueryDaemon' { $alias = Get-Alias -Name 'Enable-OsQueryService' -ErrorAction SilentlyContinue $alias | Should -Not -BeNullOrEmpty $alias.Definition | Should -Be 'Enable-OsQueryDaemon' } It 'Disable-OsQueryService resolves to Disable-OsQueryDaemon' { $alias = Get-Alias -Name 'Disable-OsQueryService' -ErrorAction SilentlyContinue $alias | Should -Not -BeNullOrEmpty $alias.Definition | Should -Be 'Disable-OsQueryDaemon' } It 'Get-OsQueryServiceStatus resolves to Get-OsQueryDaemonStatus' { $alias = Get-Alias -Name 'Get-OsQueryServiceStatus' -ErrorAction SilentlyContinue $alias | Should -Not -BeNullOrEmpty $alias.Definition | Should -Be 'Get-OsQueryDaemonStatus' } It 'All three aliases are exported by the module manifest' { $exported = (Get-Module osquery).ExportedAliases.Keys $exported | Should -Contain 'Enable-OsQueryService' $exported | Should -Contain 'Disable-OsQueryService' $exported | Should -Contain 'Get-OsQueryServiceStatus' } } # --------------------------------------------------------------------------- # New-OsQueryConfig # --------------------------------------------------------------------------- Describe 'New-OsQueryConfig' { Context 'File creation' { It 'Creates a config file at the specified path' { $path = Join-Path $TestDrive 'osquery.conf' New-OsQueryConfig -OutputPath $path $path | Should -Exist } It 'Returns a PSCustomObject with ConfigPath, FlagsPath, LogPath, PackageTable' { $path = Join-Path $TestDrive 'osquery_props.conf' $result = New-OsQueryConfig -OutputPath $path $result | Should -BeOfType [PSCustomObject] $result.ConfigPath | Should -Be $path $result.PSObject.Properties.Name | Should -Contain 'FlagsPath' $result.PSObject.Properties.Name | Should -Contain 'LogPath' $result.PSObject.Properties.Name | Should -Contain 'PackageTable' } It 'Produces valid JSON' { $path = Join-Path $TestDrive 'osquery_json.conf' New-OsQueryConfig -OutputPath $path { Get-Content $path -Raw | ConvertFrom-Json } | Should -Not -Throw } It 'Config contains the required top-level sections' { $path = Join-Path $TestDrive 'osquery_sections.conf' New-OsQueryConfig -OutputPath $path $json = Get-Content $path -Raw | ConvertFrom-Json $keys = $json.PSObject.Properties.Name $keys | Should -Contain 'options' $keys | Should -Contain 'schedule' $keys | Should -Contain 'decorators' $keys | Should -Contain 'packs' } It 'Options reflect HostIdentifier and ScheduleSplayPercent parameters' { $path = Join-Path $TestDrive 'osquery_opts.conf' New-OsQueryConfig -OutputPath $path -HostIdentifier uuid -ScheduleSplayPercent 20 $json = Get-Content $path -Raw | ConvertFrom-Json $json.options.host_identifier | Should -Be 'uuid' $json.options.schedule_splay_percent | Should -Be 20 } It 'Schedule contains the expected query entries' { $path = Join-Path $TestDrive 'osquery_sched.conf' New-OsQueryConfig -OutputPath $path $json = Get-Content $path -Raw | ConvertFrom-Json $keys = $json.schedule.PSObject.Properties.Name $keys | Should -Contain 'system_info' $keys | Should -Contain 'os_version' $keys | Should -Contain 'users' $keys | Should -Contain 'processes' $keys | Should -Contain 'listening_ports' $keys | Should -Contain 'startup_items' $keys | Should -Contain 'installed_packages' } It 'Decorators section contains at least one load query' { $path = Join-Path $TestDrive 'osquery_dec.conf' New-OsQueryConfig -OutputPath $path $json = Get-Content $path -Raw | ConvertFrom-Json $json.decorators.load.Count | Should -BeGreaterThan 0 } It 'Writes an error when file exists and -Force is not specified' { $path = Join-Path $TestDrive 'osquery_exists.conf' New-OsQueryConfig -OutputPath $path { New-OsQueryConfig -OutputPath $path -ErrorAction Stop } | Should -Throw } It 'Overwrites an existing file when -Force is specified' { $path = Join-Path $TestDrive 'osquery_force.conf' New-OsQueryConfig -OutputPath $path { New-OsQueryConfig -OutputPath $path -Force } | Should -Not -Throw } It 'Does not create a file when -WhatIf is specified' { $path = Join-Path $TestDrive 'osquery_whatif.conf' New-OsQueryConfig -OutputPath $path -WhatIf $path | Should -Not -Exist } It 'Creates the output directory if it does not exist' { $dir = Join-Path $TestDrive 'newsubdir' $path = Join-Path $dir 'osquery.conf' New-OsQueryConfig -OutputPath $path $dir | Should -Exist $path | Should -Exist } } Context 'Flags file generation' { It 'Creates a flags file when -GenerateFlagsFile is specified' { $dir = Join-Path $TestDrive 'flags1' $path = Join-Path $dir 'osquery.conf' $result = New-OsQueryConfig -OutputPath $path -GenerateFlagsFile $result.FlagsPath | Should -Exist } It 'FlagsPath is null when -GenerateFlagsFile is omitted' { $path = Join-Path $TestDrive 'osquery_noflags.conf' $result = New-OsQueryConfig -OutputPath $path -Force $result.FlagsPath | Should -BeNullOrEmpty } It 'Flags file references the correct config path' { $dir = Join-Path $TestDrive 'flags2' $path = Join-Path $dir 'osquery.conf' $result = New-OsQueryConfig -OutputPath $path -GenerateFlagsFile $content = Get-Content $result.FlagsPath -Raw $escapedPath = [regex]::Escape($path) $content | Should -Match '--config_path=' $content | Should -Match $escapedPath } It 'Flags file contains required flag entries' { $dir = Join-Path $TestDrive 'flags3' $path = Join-Path $dir 'osquery.conf' $result = New-OsQueryConfig -OutputPath $path -GenerateFlagsFile $content = Get-Content $result.FlagsPath -Raw $content | Should -Match '--config_plugin=filesystem' $content | Should -Match '--logger_plugin=filesystem' $content | Should -Match '--database_path=' $content | Should -Match '--pidfile=' } } Context 'Package manager selection' { It 'Uses deb_packages when -PackageManager deb is specified' { $path = Join-Path $TestDrive 'osquery_deb.conf' New-OsQueryConfig -OutputPath $path -PackageManager deb $json = Get-Content $path -Raw | ConvertFrom-Json $json.schedule.installed_packages.query | Should -Match 'deb_packages' } It 'Uses rpm_packages when -PackageManager rpm is specified' { $path = Join-Path $TestDrive 'osquery_rpm.conf' New-OsQueryConfig -OutputPath $path -PackageManager rpm $json = Get-Content $path -Raw | ConvertFrom-Json $json.schedule.installed_packages.query | Should -Match 'rpm_packages' } It 'Returns the correct PackageTable in the output object' { $path = Join-Path $TestDrive 'osquery_ptable.conf' $result = New-OsQueryConfig -OutputPath $path -PackageManager rpm $result.PackageTable | Should -Be 'rpm_packages' } It 'Rejects invalid PackageManager values' { $path = Join-Path $TestDrive 'osquery_bad.conf' { New-OsQueryConfig -OutputPath $path -PackageManager 'invalid' } | Should -Throw } } } # --------------------------------------------------------------------------- # Test-ElevatedPrivilege (private helper - tested via InModuleScope) # --------------------------------------------------------------------------- Describe 'Test-ElevatedPrivilege (private)' { It 'Returns a boolean' { InModuleScope osquery { $result = Test-ElevatedPrivilege $result | Should -BeOfType [bool] } } It 'Returns $true when mocked as root' { InModuleScope osquery { Mock id { '0' } if (-not $IsWindows) { Test-ElevatedPrivilege | Should -BeTrue } } } It 'Returns $false when mocked as non-root' { InModuleScope osquery { Mock id { '1000' } if (-not $IsWindows) { Test-ElevatedPrivilege | Should -BeFalse } } } } # --------------------------------------------------------------------------- # Enable-OsQueryDaemon # --------------------------------------------------------------------------- Describe 'Enable-OsQueryDaemon' { Context 'Privilege enforcement' { It 'Writes an error and returns when not elevated' { InModuleScope osquery { Mock Test-ElevatedPrivilege { $false } $err = $null Enable-OsQueryDaemon -ErrorVariable err -ErrorAction SilentlyContinue $err | Should -Not -BeNullOrEmpty } } It 'Does not call systemctl when not elevated' { InModuleScope osquery { Mock Test-ElevatedPrivilege { $false } Mock systemctl { } Enable-OsQueryDaemon -ErrorAction SilentlyContinue Should -Invoke systemctl -Times 0 -Exactly } } } Context 'Config file warning' { It 'Issues a warning when the config file does not exist' { InModuleScope osquery { Mock Test-ElevatedPrivilege { $true } Mock systemctl { $global:LASTEXITCODE = 0 } Mock Get-OsQueryDaemonStatus { [PSCustomObject]@{ Name='osqueryd'; Status='active'; Enabled=$true; PID=$null; Platform='Linux' } } $warn = $null Enable-OsQueryDaemon -ConfigPath '/nonexistent/path/osquery.conf' -WarningVariable warn -WarningAction SilentlyContinue $warn | Should -Not -BeNullOrEmpty } } It 'Does not warn when the config file exists' { InModuleScope osquery { Mock Test-ElevatedPrivilege { $true } Mock systemctl { $global:LASTEXITCODE = 0 } Mock Get-OsQueryDaemonStatus { [PSCustomObject]@{ Name='osqueryd'; Status='active'; Enabled=$true; PID=$null; Platform='Linux' } } $tmpConf = New-TemporaryFile $warn = $null Enable-OsQueryDaemon -ConfigPath $tmpConf.FullName -WarningVariable warn -WarningAction SilentlyContinue Remove-Item $tmpConf -Force $warn | Should -BeNullOrEmpty } } } Context 'Linux - systemctl calls' -Skip:(-not $IsLinux) { It 'Invokes systemctl enable and systemctl start when elevated' { InModuleScope osquery { Mock Test-ElevatedPrivilege { $true } Mock systemctl { $global:LASTEXITCODE = 0 } Mock Get-OsQueryDaemonStatus { [PSCustomObject]@{ Name='osqueryd'; Status='active'; Enabled=$true; PID=1234; Platform='Linux' } } $tmpConf = New-TemporaryFile Enable-OsQueryDaemon -ConfigPath $tmpConf.FullName Remove-Item $tmpConf -Force Should -Invoke systemctl -Times 2 -Exactly } } It 'Returns the daemon status object on success' { InModuleScope osquery { Mock Test-ElevatedPrivilege { $true } Mock systemctl { $global:LASTEXITCODE = 0 } Mock Get-OsQueryDaemonStatus { [PSCustomObject]@{ Name='osqueryd'; Status='active'; Enabled=$true; PID=1234; Platform='Linux' } } $tmpConf = New-TemporaryFile $result = Enable-OsQueryDaemon -ConfigPath $tmpConf.FullName Remove-Item $tmpConf -Force $result.Status | Should -Be 'active' $result.Enabled | Should -BeTrue } } } } # --------------------------------------------------------------------------- # Disable-OsQueryDaemon # --------------------------------------------------------------------------- Describe 'Disable-OsQueryDaemon' { Context 'Privilege enforcement' { It 'Writes an error and returns when not elevated' { InModuleScope osquery { Mock Test-ElevatedPrivilege { $false } $err = $null Disable-OsQueryDaemon -ErrorVariable err -ErrorAction SilentlyContinue $err | Should -Not -BeNullOrEmpty } } It 'Does not call systemctl when not elevated' { InModuleScope osquery { Mock Test-ElevatedPrivilege { $false } Mock systemctl { } Disable-OsQueryDaemon -ErrorAction SilentlyContinue Should -Invoke systemctl -Times 0 -Exactly } } } Context 'WhatIf support' { It 'Does not invoke systemctl when -WhatIf is specified' { InModuleScope osquery { Mock Test-ElevatedPrivilege { $true } Mock systemctl { } Disable-OsQueryDaemon -WhatIf Should -Invoke systemctl -Times 0 -Exactly } } } Context 'Linux - systemctl calls' -Skip:(-not $IsLinux) { It 'Invokes systemctl stop and systemctl disable when elevated' { InModuleScope osquery { Mock Test-ElevatedPrivilege { $true } Mock systemctl { $global:LASTEXITCODE = 0 } Mock Get-OsQueryDaemonStatus { [PSCustomObject]@{ Name='osqueryd'; Status='inactive'; Enabled=$false; PID=$null; Platform='Linux' } } Disable-OsQueryDaemon Should -Invoke systemctl -Times 2 -Exactly } } It 'Returns the daemon status object on success' { InModuleScope osquery { Mock Test-ElevatedPrivilege { $true } Mock systemctl { $global:LASTEXITCODE = 0 } Mock Get-OsQueryDaemonStatus { [PSCustomObject]@{ Name='osqueryd'; Status='inactive'; Enabled=$false; PID=$null; Platform='Linux' } } $result = Disable-OsQueryDaemon $result.Status | Should -Be 'inactive' $result.Enabled | Should -BeFalse } } } } # --------------------------------------------------------------------------- # Get-OsQueryDaemonStatus / Get-OsQueryServiceStatus # --------------------------------------------------------------------------- Describe 'Get-OsQueryDaemonStatus' { Context 'Output shape' { It 'Returns an object with Name, Platform, Status, Enabled, PID properties' { InModuleScope osquery { Mock systemctl { if ($args -contains 'is-active') { return 'active' } if ($args -contains 'is-enabled') { return 'enabled' } if ($args -contains 'show') { return '1234' } } if ($IsLinux) { $result = Get-OsQueryDaemonStatus $props = $result.PSObject.Properties.Name $props | Should -Contain 'Name' $props | Should -Contain 'Platform' $props | Should -Contain 'Status' $props | Should -Contain 'Enabled' $props | Should -Contain 'PID' } } } } Context 'Linux - mocked systemctl' -Skip:(-not $IsLinux) { It 'Reports Status as active when systemctl is-active returns active' { InModuleScope osquery { Mock systemctl { if ($args -contains 'is-active') { return 'active' } if ($args -contains 'is-enabled') { return 'enabled' } if ($args -contains 'show') { return '5678' } } $result = Get-OsQueryDaemonStatus $result.Status | Should -Be 'active' $result.Platform | Should -Be 'Linux' $result.Enabled | Should -BeTrue $result.PID | Should -Be 5678 } } It 'Reports Enabled as $false when systemctl is-enabled returns disabled' { InModuleScope osquery { Mock systemctl { if ($args -contains 'is-active') { return 'inactive' } if ($args -contains 'is-enabled') { return 'disabled' } if ($args -contains 'show') { return '0' } } $result = Get-OsQueryDaemonStatus $result.Enabled | Should -BeFalse $result.PID | Should -BeNullOrEmpty } } It 'PID is null when MainPID is 0' { InModuleScope osquery { Mock systemctl { if ($args -contains 'is-active') { return 'inactive' } if ($args -contains 'is-enabled') { return 'disabled' } if ($args -contains 'show') { return '0' } } $result = Get-OsQueryDaemonStatus $result.PID | Should -BeNullOrEmpty } } } Context 'Alias equivalence' { It 'Get-OsQueryServiceStatus produces the same output as Get-OsQueryDaemonStatus' { InModuleScope osquery { if ($IsLinux) { Mock systemctl { if ($args -contains 'is-active') { return 'active' } if ($args -contains 'is-enabled') { return 'enabled' } if ($args -contains 'show') { return '999' } } $via_daemon = Get-OsQueryDaemonStatus $via_service = Get-OsQueryServiceStatus $via_service.Status | Should -Be $via_daemon.Status $via_service.Enabled | Should -Be $via_daemon.Enabled $via_service.PID | Should -Be $via_daemon.PID } } } } } |