134 lines
5.0 KiB
PowerShell
134 lines
5.0 KiB
PowerShell
|
|
#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' }
|
||
|
|
|
||
|
|
<#
|
||
|
|
FR-027: the interactive editor can add a new rule, and rejects a colliding id or
|
||
|
|
priority before the rule is added.
|
||
|
|
|
||
|
|
Add-PersonaConfigRule is exercised directly rather than through the interactive
|
||
|
|
script - it is the pure function the [A] command calls, and pure functions are
|
||
|
|
what this suite can test without driving Read-Host through a subprocess (see
|
||
|
|
NonInteractive.Tests.ps1 for why that path is reserved for hang/prompt proof, not
|
||
|
|
ordinary behaviour).
|
||
|
|
#>
|
||
|
|
|
||
|
|
BeforeAll {
|
||
|
|
$repoRoot = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent
|
||
|
|
. (Join-Path $repoRoot 'tests/TestHelpers.ps1')
|
||
|
|
foreach ($file in (Get-PersonaSourceFile -RepoRoot $repoRoot)) { . $file }
|
||
|
|
|
||
|
|
function New-TestRule {
|
||
|
|
param(
|
||
|
|
[string] $Id = 'RULE-0500-TEST',
|
||
|
|
[int] $Priority = 500,
|
||
|
|
[string] $Persona = 'Employee'
|
||
|
|
)
|
||
|
|
|
||
|
|
[pscustomobject]@{
|
||
|
|
id = $Id
|
||
|
|
name = 'Test rule'
|
||
|
|
description = 'A rule added by a test.'
|
||
|
|
enabled = $true
|
||
|
|
priority = $Priority
|
||
|
|
persona = $Persona
|
||
|
|
match = [pscustomobject]@{
|
||
|
|
operator = 'all'
|
||
|
|
conditions = @([pscustomobject]@{ type = 'property'; property = 'Department'; operator = 'isNotNull' })
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Describe 'Add-PersonaConfigRule (FR-027)' {
|
||
|
|
|
||
|
|
It 'appends the new rule to the collection' {
|
||
|
|
$existing = @((New-TestRule -Id 'RULE-0010-GUEST' -Priority 10))
|
||
|
|
$new = New-TestRule -Id 'RULE-0500-TEST' -Priority 500
|
||
|
|
|
||
|
|
$result = Add-PersonaConfigRule -Rules $existing -Rule $new
|
||
|
|
|
||
|
|
$result.Count | Should -Be 2
|
||
|
|
($result | Where-Object { [string]$_.id -eq 'RULE-0500-TEST' }).Count | Should -Be 1
|
||
|
|
}
|
||
|
|
|
||
|
|
It 'does not mutate the input collection' {
|
||
|
|
$existing = @((New-TestRule -Id 'RULE-0010-GUEST' -Priority 10))
|
||
|
|
$new = New-TestRule -Id 'RULE-0500-TEST' -Priority 500
|
||
|
|
|
||
|
|
Add-PersonaConfigRule -Rules $existing -Rule $new | Out-Null
|
||
|
|
|
||
|
|
$existing.Count | Should -Be 1
|
||
|
|
}
|
||
|
|
|
||
|
|
It 'always returns an array, even when the result has exactly one rule' {
|
||
|
|
# The single-element case is the one PowerShell silently collapses to a
|
||
|
|
# scalar unless the function guards against it (see Add-PersonaConfigRule's
|
||
|
|
# `, (...)` return) - assigning the call's result directly, with no extra
|
||
|
|
# @() at this call site, is what proves the function's own guard is doing
|
||
|
|
# the work rather than an @() here masking a function that does not.
|
||
|
|
$new = New-TestRule -Id 'RULE-0500-TEST' -Priority 500
|
||
|
|
|
||
|
|
$result = Add-PersonaConfigRule -Rules @() -Rule $new
|
||
|
|
|
||
|
|
, $result | Should -BeOfType [array]
|
||
|
|
$result.Count | Should -Be 1
|
||
|
|
}
|
||
|
|
|
||
|
|
It 'rejects a duplicate id before adding' {
|
||
|
|
$existing = @((New-TestRule -Id 'RULE-0010-GUEST' -Priority 10))
|
||
|
|
$colliding = New-TestRule -Id 'RULE-0010-GUEST' -Priority 999
|
||
|
|
|
||
|
|
{ Add-PersonaConfigRule -Rules $existing -Rule $colliding } | Should -Throw '*RULE-0010-GUEST*'
|
||
|
|
}
|
||
|
|
|
||
|
|
It 'rejects a duplicate priority before adding' {
|
||
|
|
$existing = @((New-TestRule -Id 'RULE-0010-GUEST' -Priority 10))
|
||
|
|
$colliding = New-TestRule -Id 'RULE-0500-TEST' -Priority 10
|
||
|
|
|
||
|
|
{ Add-PersonaConfigRule -Rules $existing -Rule $colliding } | Should -Throw '*10*'
|
||
|
|
}
|
||
|
|
|
||
|
|
It 'leaves the original collection unchanged when the add is rejected' {
|
||
|
|
$existing = @((New-TestRule -Id 'RULE-0010-GUEST' -Priority 10))
|
||
|
|
$colliding = New-TestRule -Id 'RULE-0010-GUEST' -Priority 999
|
||
|
|
|
||
|
|
try { Add-PersonaConfigRule -Rules $existing -Rule $colliding } catch { }
|
||
|
|
|
||
|
|
$existing.Count | Should -Be 1
|
||
|
|
}
|
||
|
|
|
||
|
|
It 'rejects a blank id' {
|
||
|
|
$existing = @((New-TestRule -Id 'RULE-0010-GUEST' -Priority 10))
|
||
|
|
$blank = New-TestRule -Id '' -Priority 999
|
||
|
|
|
||
|
|
{ Add-PersonaConfigRule -Rules $existing -Rule $blank } | Should -Throw
|
||
|
|
}
|
||
|
|
|
||
|
|
It 'the added rule survives a full validation pass' {
|
||
|
|
$document = New-TestConfigurationDocument
|
||
|
|
$new = @{
|
||
|
|
id = 'RULE-0500-TEST'
|
||
|
|
name = 'Test rule'
|
||
|
|
description = 'A rule added by a test.'
|
||
|
|
enabled = $true
|
||
|
|
priority = 500
|
||
|
|
persona = 'Employee'
|
||
|
|
match = @{
|
||
|
|
operator = 'all'
|
||
|
|
conditions = @(@{ type = 'property'; property = 'Department'; operator = 'isNotNull' })
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
$document.rules = Add-PersonaConfigRule -Rules $document.rules -Rule $new
|
||
|
|
$path = Save-TestConfiguration -Document $document -Directory ([System.IO.Path]::GetTempPath())
|
||
|
|
|
||
|
|
try {
|
||
|
|
$result = Test-PersonaConfiguration -Path $path
|
||
|
|
$result.IsValid | Should -BeTrue
|
||
|
|
@($result.Document.rules).Count | Should -Be 3
|
||
|
|
}
|
||
|
|
finally {
|
||
|
|
Remove-Item -LiteralPath $path -Force -ErrorAction SilentlyContinue
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|