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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@
bind and always threw). `-PropertySet` is now a mandatory parameter on
`Test-FeatureFlag`, matching `Test-Condition`, so the missing value is
reported with a clear mandatory-parameter error instead. (#84)
- `Test-Condition`/`Test-FeatureFlag` no longer throw an opaque
"You cannot call a method on a null-valued expression" error when a rule's
`Condition` is `$null` (e.g. omitted or misspelled). `ConditionTransformAttribute`
now null-guards and raises a clear `ArgumentNullException` explaining that the
rule must define a `Condition`. `FeatureFlagTransformAttribute` gained the same
guard for a null `FeatureFlag`. (#83)

## [1.0.0] 2026-06-05

Expand Down Expand Up @@ -78,7 +84,7 @@
constraints now correctly returns `$false` instead of continuing
evaluation.
- `PropertyValidation` threshold fields (`Minimum`, `Maximum`,
`MinLength`, `MaxLength`) changed to `Nullable[int]` so an unconfigured

Check warning on line 87 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Continuous Integration / Run Linters

Unknown word (unconfigured) Suggestions: (unconfined, unconfused, unconfirmed, unconsidered)
constraint is no longer treated as zero.
- `PropertyValidation.ToHashtable` now omits null/unset fields to
prevent invalid entries in serialized JSON during Save/FromFile
Expand Down Expand Up @@ -149,7 +155,7 @@
### Changed

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

Check warning on line 158 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
8 changes: 8 additions & 0 deletions Gatekeeper/Classes/FeatureFlag.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,10 @@ class GatekeeperPath {
class FeatureFlagTransformAttribute : System.Management.Automation.ArgumentTransformationAttribute {

[object] Transform([System.Management.Automation.EngineIntrinsics]$engineIntrinsics, [object] $inputData) {
if ($null -eq $inputData) {
throw [System.ArgumentNullException]::new('FeatureFlag',
"FeatureFlag cannot be null.")
}
Comment on lines +239 to +242
if ($inputData -is [FeatureFlag]) {
return $inputData
}
Expand All @@ -258,6 +262,10 @@ class FeatureFlagTransformAttribute : System.Management.Automation.ArgumentTrans
class ConditionTransformAttribute : System.Management.Automation.ArgumentTransformationAttribute {

[object] Transform([System.Management.Automation.EngineIntrinsics]$engineIntrinsics, [object] $inputData) {
if ($null -eq $inputData) {
throw [System.ArgumentNullException]::new('Condition',
"Rule has no Condition. Each rule must define a 'Condition'.")
}
Comment on lines +265 to +268
if ($inputData -is [Condition]) {
return $inputData
}
Expand Down
6 changes: 6 additions & 0 deletions tests/Classes.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,12 @@
Test-Condition -Condition 42 -PropertySet $script:propertySet -Context $script:context
} | Should -Throw -ExpectedMessage '*Cannot convert type*'
}

It 'Throws a clear, actionable error when Condition is null' {
{
Test-Condition -Condition $null -PropertySet $script:propertySet -Context $script:context
} | Should -Throw -ExpectedMessage "*Rule has no Condition*"
}
}

Describe 'PropertySetTransformAttribute' {
Expand Down Expand Up @@ -323,7 +329,7 @@
Type = 'string'
Validation = @{ MinLength = 1; MaxLength = 5 }
})
$pd.Validate('toolongstring') | Should -BeFalse

Check warning on line 332 in tests/Classes.tests.ps1

View workflow job for this annotation

GitHub Actions / Continuous Integration / Run Linters

Unknown word (toolongstring)
}

It 'Returns $false when string does not match pattern' {
Expand Down
12 changes: 12 additions & 0 deletions tests/Test-FeatureFlag.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,18 @@ Describe 'Test-FeatureFlag' {
}
}

Context 'Malformed rules' {
It 'Rule with a null Condition throws a clear, actionable error' {
$flag = [FeatureFlag]::new(@{
Name = 'MissingConditionFlag'; DefaultEffect = 'Deny'; Version = '1.0.0'; Author = 'Test'
Rules = @(@{ Name = 'Allow group'; Effect = 'Allow' })
})
{
Test-FeatureFlag -FeatureFlag $flag -PropertySet $script:propertySet -Context $script:context
} | Should -Throw -ExpectedMessage '*Rule has no Condition*'
}
}

Context 'Ordering — first-match-wins' {
It 'Deny before matching Allow returns $false (Deny wins)' {
$flag = [FeatureFlag]::new(@{
Expand Down
Loading