Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Changed

- `Invoke-Logging` now reads the pre-parsed scriptblock from
`$script:GatekeeperLogging` (populated by `Import-GatekeeperConfig`) instead
of re-creating the scriptblock from raw configuration on every call. This
eliminates redundant compilation overhead and preserves any closure context
that would have been lost by stringifying an existing scriptblock through

Check warning on line 16 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Continuous Integration / Run Linters

Unknown word (stringifying) Suggestions: (stringify, stringing, stringified, stringifies)
`[scriptblock]::Create()`.

## [0.3.2] 2026-01-30

### Fixed
Expand Down Expand Up @@ -42,7 +53,7 @@
### Changed

- `Test-Condition` function now accepts `ConditionGroup` objects instead of
hashtables for the Condition parameter, providing stronger type safety.

Check warning on line 56 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Continuous Integration / Run Linters

Unknown word (hashtables) Suggestions: (hashables, hashtable, hashable, hatable, hashtags)
- `Test-Condition` function now accepts `PropertySet` objects instead of a
generic Properties hashtable for the Properties parameter.
- Internal condition evaluation logic updated to use null checks on
Expand Down
118 changes: 118 additions & 0 deletions tests/Invoke-Logging.tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
BeforeDiscovery {
$manifest = Import-PowerShellDataFile -Path $env:BHPSModuleManifest
$outputDir = Join-Path -Path $env:BHProjectPath -ChildPath 'Output'
$outputModDir = Join-Path -Path $outputDir -ChildPath $env:BHProjectName
$outputModVerDir = Join-Path -Path $outputModDir -ChildPath $manifest.ModuleVersion
$outputModVerManifest = Join-Path -Path $outputModVerDir -ChildPath "$($env:BHProjectName).psd1"

Get-Module $env:BHProjectName -All | Remove-Module -Force -ErrorAction Ignore
Get-Module 'Configuration' -All | Remove-Module -Force -ErrorAction Ignore
Import-Module -Name $outputModVerManifest -Verbose:$false -ErrorAction Stop
}

Describe 'Invoke-Logging' {
BeforeAll {
# Prevent actual config loading during tests
Mock -CommandName 'Import-GatekeeperConfig' -ModuleName $env:BHProjectName {}

$script:testRule = [Rule]::new(@{
Name = 'TestRule'
Effect = 'Allow'
Conditions = @{ Property = 'IsCompliant'; Operator = 'Equals'; Value = $true }
})
}

AfterEach {
# Reset the logging cache between tests
InModuleScope $env:BHProjectName {
$script:GatekeeperLogging = $null
}
}

Context 'Uses pre-parsed scriptblock cache' {
It 'invokes the cached scriptblock for the matching effect' {
$captureRule = $script:testRule

InModuleScope $env:BHProjectName -Parameters @{ Rule = $captureRule } {
param($Rule)
$script:GatekeeperLogging = @{
Allow = { param($Rule) $script:lastLoggedRule = $Rule }
}
$script:lastLoggedRule = $null
}

InModuleScope $env:BHProjectName -Parameters @{ Rule = $captureRule } {
param($Rule)
Invoke-Logging -Effect 'Allow' -Rule $Rule
}

InModuleScope $env:BHProjectName {
$script:lastLoggedRule | Should -Not -BeNull
$script:lastLoggedRule.Name | Should -Be 'TestRule'
}
}

It 'does not invoke a scriptblock for a different effect' {
$captureRule = $script:testRule

InModuleScope $env:BHProjectName -Parameters @{ Rule = $captureRule } {
param($Rule)
$script:GatekeeperLogging = @{
Deny = { param($Rule) $script:lastLoggedRule = $Rule }
}
$script:lastLoggedRule = $null
}

InModuleScope $env:BHProjectName -Parameters @{ Rule = $captureRule } {
param($Rule)
Invoke-Logging -Effect 'Allow' -Rule $Rule
}

InModuleScope $env:BHProjectName {
$script:lastLoggedRule | Should -BeNull
}
}
}

Context 'Graceful no-ops' {
It 'returns without error when GatekeeperLogging cache is null' {
InModuleScope $env:BHProjectName {
$script:GatekeeperLogging = $null
}

$captureRule = $script:testRule
{
InModuleScope $env:BHProjectName -Parameters @{ Rule = $captureRule } {
param($Rule)
Invoke-Logging -Effect 'Allow' -Rule $Rule
}
} | Should -Not -Throw
}

It 'returns without error when effect is not present in cache' {
InModuleScope $env:BHProjectName {
$script:GatekeeperLogging = @{}
}

$captureRule = $script:testRule
{
InModuleScope $env:BHProjectName -Parameters @{ Rule = $captureRule } {
param($Rule)
Invoke-Logging -Effect 'Allow' -Rule $Rule
}
} | Should -Not -Throw
}
}

Context 'Config loading' {
It 'calls Import-GatekeeperConfig to ensure cache is initialized' {
$captureRule = $script:testRule
InModuleScope $env:BHProjectName -Parameters @{ Rule = $captureRule } {
param($Rule)
Invoke-Logging -Effect 'Allow' -Rule $Rule
}

Should -Invoke 'Import-GatekeeperConfig' -ModuleName $env:BHProjectName -Times 1 -Exactly
}
}
}
Loading