Tests/Get-JevChoice.Tests.ps1
|
BeforeAll { Import-Module (Join-Path $PSScriptRoot '..' 'Jev.psd1') -Force } AfterAll { Remove-Module Jev -Force -ErrorAction SilentlyContinue } Describe 'Get-JevChoice compact label selection' { BeforeEach { Mock -ModuleName Jev Invoke-JevDecision { param($State, $Questions) $Questions.Count | Should -Be 1 $Questions.selection.type | Should -Be 'choice' $Questions.selection.instructions | Should -Be 'Which team owns this request?' @($Questions.selection.criteria.Keys) | Should -Be @('billing', 'shipping', 'account', 'other') $Questions.selection.criteria['billing'] | Should -Be 'billing' $selected = switch ($State) { 'Refund please.' { 'billing' } 'Order missing.' { 'shipping' } 'Cannot sign in.' { 'account' } default { 'other' } } [pscustomobject]@{ answers = @{ selection = @{ type = 'choice'; choice = $selected; confidence = 0.9 } } } } } It 'binds the question and all remaining choices without stealing the pipeline state' { $result = 'Refund please.' | Get-JevChoice 'Which team owns this request?' billing shipping account other $result | Should -BeExactly 'billing' $result | Should -BeOfType [string] Should -Invoke Invoke-JevDecision -ModuleName Jev -Exactly 1 -Scope It -ParameterFilter { $State -eq 'Refund please.' } } It 'returns one label per input in input order' { $results = @(@('Order missing.', 'Cannot sign in.', 'Refund please.', 'Feature suggestion.') | Get-JevChoice 'Which team owns this request?' billing shipping account other) $results | Should -Be @('shipping', 'account', 'billing', 'other') Should -Invoke Invoke-JevDecision -ModuleName Jev -Exactly 4 -Scope It } It 'accepts named state and a named choices array' { $result = Get-JevChoice -State 'Order missing.' -Question 'Which team owns this request?' ` -Choices @('billing', 'shipping', 'account', 'other') $result | Should -Be 'shipping' Should -Invoke Invoke-JevDecision -ModuleName Jev -Exactly 1 -Scope It } It 'accepts a named question with trailing choices and common parameters' { $result = 'Cannot sign in.' | Get-JevChoice -Question 'Which team owns this request?' billing shipping account other -ErrorAction Stop $result | Should -Be 'account' Should -Invoke Invoke-JevDecision -ModuleName Jev -Exactly 1 -Scope It } It 'passes objects unchanged as request state without modifying their properties' { $record = [pscustomobject]@{ Id = 42; Message = 'Refund please.'; selection = 'Existing data' } Mock -ModuleName Jev Invoke-JevDecision { param($State, $Questions) [object]::ReferenceEquals($State, $record) | Should -BeTrue [pscustomobject]@{ answers = @{ selection = @{ choice = 'billing' } } } } $result = $record | Get-JevChoice 'Which team owns this request?' billing shipping account other $result | Should -Be 'billing' $record.selection | Should -Be 'Existing data' @($record.PSObject.Properties.Name) | Should -Be @('Id', 'Message', 'selection') } It 'keeps a named array as one state and supports dictionary responses' { $stateArray = @('Refund please.', 'Duplicate payment.') Mock -ModuleName Jev Invoke-JevDecision { param($State, $Questions) [object]::ReferenceEquals($State, $stateArray) | Should -BeTrue @{ answers = @{ selection = @{ choice = 'billing' } } } } $result = Get-JevChoice -State $stateArray 'Which team owns this request?' billing shipping account other $result | Should -Be 'billing' Should -Invoke Invoke-JevDecision -ModuleName Jev -Exactly 1 -Scope It } It 'accepts quoted multi-word labels and preserves their spelling' { Mock -ModuleName Jev Invoke-JevDecision { param($State, $Questions) @($Questions.selection.criteria.Keys) | Should -Be @('account access', 'technical support', 'Other') @{ answers = @{ selection = @{ choice = 'ACCOUNT ACCESS' } } } } $result = 'Cannot sign in.' | Get-JevChoice 'Which team owns this request?' 'account access' 'technical support' Other $result | Should -BeExactly 'account access' } It 'does not add an implicit fallback or apply a confidence threshold' { Mock -ModuleName Jev Invoke-JevDecision { param($State, $Questions) $Questions.selection.criteria.Count | Should -Be 2 @{ answers = @{ selection = @{ choice = 'shipping'; confidence = 0.1 } } } } 'Order missing.' | Get-JevChoice 'Which team owns this request?' billing shipping | Should -Be 'shipping' } It 'makes no request for empty pipeline input' { @(@() | Get-JevChoice 'Which team owns this request?' billing shipping account other).Count | Should -Be 0 Should -Invoke Invoke-JevDecision -ModuleName Jev -Exactly 0 -Scope It } It 'accepts the 255-label boundary' { $labels = @(1..255 | ForEach-Object { "label$_" }) Mock -ModuleName Jev Invoke-JevDecision { param($State, $Questions) $Questions.selection.criteria.Count | Should -Be 255 @{ answers = @{ selection = @{ choice = 'label255' } } } } 'A message.' | Get-JevChoice 'Which team owns this request?' -Choices $labels | Should -Be 'label255' } It 'rejects too many labels before making a request' { $labels = @(1..256 | ForEach-Object { "label$_" }) { 'A message.' | Get-JevChoice 'Which team owns this request?' -Choices $labels } | Should -Throw Should -Invoke Invoke-JevDecision -ModuleName Jev -Exactly 0 -Scope It } It 'rejects empty labels: <Case>' -TestCases @( @{ Case = 'empty array'; Labels = @() } @{ Case = 'empty element'; Labels = @('billing', '') } @{ Case = 'whitespace element'; Labels = @('billing', ' ') } ) { param($Case, $Labels) { 'A message.' | Get-JevChoice 'Which team owns this request?' -Choices $Labels } | Should -Throw Should -Invoke Invoke-JevDecision -ModuleName Jev -Exactly 0 -Scope It } It 'rejects duplicate labels ignoring case before making a request' { { 'A message.' | Get-JevChoice 'Which team owns this request?' billing Billing shipping } | Should -Throw '*case-insensitive*' Should -Invoke Invoke-JevDecision -ModuleName Jev -Exactly 0 -Scope It } It 'rejects an unknown returned label rather than treating it as other' { Mock -ModuleName Jev Invoke-JevDecision { @{ answers = @{ selection = @{ choice = 'sales' } } } } { 'A message.' | Get-JevChoice 'Which team owns this request?' billing shipping account other } | Should -Throw '*unknown choice*' } It 'rejects missing response fields: <Case>' -TestCases @( @{ Case = 'answers'; Response = [pscustomobject]@{ model = 'test' } } @{ Case = 'selection'; Response = [pscustomobject]@{ answers = @{} } } @{ Case = 'choice'; Response = [pscustomobject]@{ answers = @{ selection = @{ confidence = 0.9 } } } } @{ Case = 'null choice'; Response = [pscustomobject]@{ answers = @{ selection = @{ choice = $null } } } } ) { param($Case, $Response) Mock -ModuleName Jev Invoke-JevDecision { $Response } { 'A message.' | Get-JevChoice 'Which team owns this request?' billing shipping account other } | Should -Throw '*Jev did not return*' } It 'propagates request failures without fabricating a label' { Mock -ModuleName Jev Invoke-JevDecision { throw 'API unavailable.' } $emitted = [System.Collections.Generic.List[object]]::new() { 'A message.' | Get-JevChoice 'Which team owns this request?' billing shipping account other | ForEach-Object { $emitted.Add($_) } } | Should -Throw '*API unavailable*' $emitted.Count | Should -Be 0 } } |