#Requires -Modules @{ ModuleName = 'Pester'; ModuleVersion = '5.0.0' } <# SC-005: every write body has exactly one key, equal to engine.targetAttribute. This is the assertion that bounds the blast radius. OTD-003 records that Graph application permissions have no per-property scope: whatever this engine can write to the persona attribute, it could equally write to any other user property. The directory will not stop a body with a second key, so this test is the thing that does. #> BeforeAll { $repoRoot = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent . (Join-Path $repoRoot 'tests/TestHelpers.ps1') foreach ($file in (Get-PersonaSourceFile -RepoRoot $repoRoot)) { . $file } $script:target = 'extension__' $script:approved = @($script:target) } Describe 'Write body construction (SC-005)' -Tag 'Safety' { It 'produces a body with exactly one key' { $body = New-PersonaWriteBody -AttributeName $target -Value 'Employee' ` -TargetAttribute $target -ApprovedWritableAttributes $approved $body.Count | Should -Be 1 } It 'names that key exactly the target attribute' { $body = New-PersonaWriteBody -AttributeName $target -Value 'Employee' ` -TargetAttribute $target -ApprovedWritableAttributes $approved @($body.Keys)[0] | Should -BeExactly $target } It 'carries the calculated value unchanged' { $body = New-PersonaWriteBody -AttributeName $target -Value 'Tier0-Admin' ` -TargetAttribute $target -ApprovedWritableAttributes $approved $body[$target] | Should -BeExactly 'Tier0-Admin' } It 'permits an empty value, which clears the attribute' { # Clearing is a legitimate outcome when a rule set stops matching an account. # It must go through the same single-key path as any other write. $body = New-PersonaWriteBody -AttributeName $target -Value '' ` -TargetAttribute $target -ApprovedWritableAttributes $approved $body.Count | Should -Be 1 $body[$target] | Should -Be '' } } Describe 'Every body issued during a run has exactly one key' -Tag 'Safety' { It 'holds across a full enforcing population' { # The unit test above proves the builder is correct. This proves the run loop # actually uses it, on every account, with no other path to a PATCH. $script:captured = [System.Collections.Generic.List[object]]::new() Mock Write-Host { } Mock Get-PersonaUsers { @(New-TestPopulation -Count 20 -TargetAttribute $script:target) } Mock Invoke-PersonaGraphRequest { if ($Method -eq 'PATCH') { $script:captured.Add($Body) } @{} } $outcome = Invoke-PersonaEngineRun ` -Configuration (New-TestRuntimeConfiguration -TargetAttribute $script:target) ` -TargetAttribute $script:target -Context (New-TestAuditContext -Mode 'Enforce') ` -IsEnforcing -ShouldProcessGate { param($t, $d) $true } $script:captured.Count | Should -BeGreaterThan 0 $script:captured.Count | Should -Be $outcome.Counters.Updated foreach ($body in $script:captured) { $body.Count | Should -Be 1 @($body.Keys)[0] | Should -BeExactly $script:target } } It 'issues a PATCH and nothing else as a write method' { $script:methods = [System.Collections.Generic.List[string]]::new() Mock Write-Host { } Mock Get-PersonaUsers { @(New-TestPopulation -Count 10 -TargetAttribute $script:target) } Mock Invoke-PersonaGraphRequest { $script:methods.Add($Method) @{} } $null = Invoke-PersonaEngineRun ` -Configuration (New-TestRuntimeConfiguration -TargetAttribute $script:target) ` -TargetAttribute $script:target -Context (New-TestAuditContext -Mode 'Enforce') ` -IsEnforcing -ShouldProcessGate { param($t, $d) $true } # No PUT and no DELETE: a PUT would replace the whole user object, and there # is no circumstance in which this engine removes one. $script:methods | Should -Not -Contain 'PUT' $script:methods | Should -Not -Contain 'DELETE' $script:methods | Should -Not -Contain 'POST' $script:methods | Should -Contain 'PATCH' } } Describe 'New-PersonaWriteBody is the only construction path' -Tag 'Safety' { It 'is the only source file that builds a PATCH body' { # A second construction site would make SC-005 a property of a convention # rather than of a testable function. $sources = Get-ChildItem -Path (Join-Path $repoRoot 'src') -Filter '*.ps1' -Recurse -File $offenders = foreach ($file in $sources) { if ($file.Name -eq 'New-PersonaWriteBody.ps1') { continue } $text = Get-Content -LiteralPath $file.FullName -Raw if ($text -match "Method\s*=?\s*'PATCH'" -and $text -notmatch 'New-PersonaWriteBody') { # Invoke-PersonaGraphRequest declares PATCH in a ValidateSet; it does # not construct a body. if ($file.Name -ne 'Invoke-PersonaGraphRequest.ps1') { $file.Name } } } $offenders | Should -BeNullOrEmpty } }