tests/Test-IWStrictKeys.ps1
|
#Requires -Version 7 # Regression: the wizard engine runs under Set-StrictMode -Version Latest, where dot-access to a # missing hashtable key throws PropertyNotFoundException. Definitions and contexts are bags of # optional keys, so every optional read must use indexer access (['Key']). # This test drives the real wizard flow with prompts answered by stubs, so the code paths that # used to throw (Google Entro -Wizard, Google Workspace connector -Wizard with Entro declined) are exercised. $ErrorActionPreference = 'Stop' . (Join-Path $PSScriptRoot '_TestHelpers.ps1') $tmp = Join-Path ([System.IO.Path]::GetTempPath()) ("iw-strictkeys-{0}" -f [Guid]::NewGuid().ToString('N')) New-Item -ItemType Directory -Path $tmp -Force | Out-Null $env:SAILPOINT_CONNECT_ROOT = $tmp $wizardRoot = Split-Path $PSScriptRoot -Parent Import-Module (Join-Path $wizardRoot 'modules/ISC.OperatorConsole.psm1') -Force -Global -WarningAction SilentlyContinue Import-Module (Join-Path $wizardRoot 'modules/IW.Core.psm1') -Force -Global Import-Module (Join-Path $wizardRoot 'modules/IW.Render.psm1') -Force -Global Import-Module (Join-Path $wizardRoot 'modules/Definitions/Google.Connector.psm1') -Force Import-Module (Join-Path $wizardRoot 'modules/Definitions/Google.Entro.psm1') -Force # Prompt stubs (defined after the console import so they override the interactive readers). # Choices take their default, typed input is a fixed value, and the "Also configure Entro?" # question is declined so the Google Workspace connector path stays on a single target. $script:ConnectionChoice = 'UseSession' function global:Read-Choice { param($Prompt, $Options, $Labels, $Default) # Pin the connection choice so the test does not depend on this machine's gcloud sign-in state. if ($Prompt -eq 'How do you want to connect?') { $script:ConnectionOptions = @($Options) return $script:ConnectionChoice } if ($Default) { return $Default } return @($Options)[0] } function global:Read-InputString { param($Prompt, $Default, [switch]$Required) if ($Default) { return $Default } return 'typed-value' } function global:Read-YesNo { param($Prompt, $Default) if ($Prompt -like 'Also configure Entro*') { return $false } return $true } Register-IntegrationWizardPlatform -Name 'GoogleCloud' -Title 'Google Cloud' ` -CoreOptions @('SaaSConnector', 'CiemConnector', 'GcpEntro', 'WorkspaceEntro') -CliName 'gcloud' ` -EntroCompanions @{ SaaSConnector = @{ Variants = @('GcpEntro', 'WorkspaceEntro') } } Register-GoogleConnectorDefinitions Register-GoogleEntroDefinitions $coreHelp = [ordered]@{ SaaSConnector = @{ Short = 'Workspace connector'; Long = 'Google Workspace connector' } GcpEntro = @{ Short = 'GCP Entro'; Long = 'Entro Google GCP organization integration' } } $noDiscover = { param($name, $ctx) @() } # 1) Google Entro via -Wizard: the exact path from the reported error # (ValidateSet-less discover inputs, then the Method ValidateSet input). $script:Executed = $null $ctx = @{ Wizard = $true; NonInteractive = $false SaaSConnector = $false; CiemConnector = $false; GcpEntro = $true; WorkspaceEntro = $false Access = $null; Action = $null; OrganizationId = $null; ProjectId = $null ImpersonateUser = $null; Method = $null; ClientId = $null; OutputDirectory = $null } Start-IntegrationWizardFlow -Platform 'GoogleCloud' -ScriptName 'Google Cloud.ps1' -CoreOptionHelp $coreHelp -Context $ctx ` -Discover $noDiscover ` -Execute { param($def, $c) $script:Executed = @{ Def = $def; Ctx = $c } } Assert-True ($null -ne $script:Executed) 'GCP Entro -Wizard reaches execute' Assert-Equal 'google-gcp-entro' $script:Executed.Def.Id 'GCP Entro -Wizard resolves the Entro definition' Assert-Equal 'PrivateKey' $script:Executed.Ctx['Method'] 'Method ValidateSet input collected' Assert-Equal 'typed-value' $script:Executed.Ctx['OrganizationId'] 'discover input without ValidateSet falls back to typed input' Assert-Equal 'entro-automatic-onboard' $script:Executed.Ctx['ServiceAccountName'] 'optional input default applied' # 2) Google Workspace connector via -Wizard with Entro declined: exercises the Entro addon # companion (which has no EntroSwitch key) and the PickTarget branch. $script:Executed = $null $ctx2 = @{ Wizard = $true; NonInteractive = $false SaaSConnector = $false; CiemConnector = $false; GcpEntro = $false; WorkspaceEntro = $false Access = $null; Action = $null; OrganizationId = $null; ProjectId = $null ImpersonateUser = $null; Method = $null; ClientId = $null; OutputDirectory = $null } $pickSaas = { $ctx2['SaaSConnector'] = $true Get-IntegrationWizardDefinition -Id 'google-saas-connector' } Start-IntegrationWizardFlow -Platform 'GoogleCloud' -ScriptName 'Google Cloud.ps1' -CoreOptionHelp $coreHelp -Context $ctx2 ` -PickTarget $pickSaas -Discover $noDiscover ` -Execute { param($def, $c) $script:Executed = @{ Def = $def; Ctx = $c } } Assert-True ($null -ne $script:Executed) 'Google Workspace connector -Wizard reaches execute' Assert-Equal 'google-saas-connector' $script:Executed.Def.Id 'Google Workspace connector resolves its definition' Assert-Equal 'typed-value' $script:Executed.Ctx['ImpersonateUser'] 'SaaS required input collected' # 2b) Combined SaaS + GCP Entro: one composite plan, shared inputs merged once (no purpose collision). $combined = Resolve-IntegrationWizardPlan -Platform 'GoogleCloud' -Context @{ SaaSConnector = $true; GcpEntro = $true } Assert-True ([bool]$combined['IsComposite']) 'SaaS + GCP Entro resolves to a composite plan' Assert-Equal 1 @($combined.Inputs | Where-Object { $_.Name -eq 'OrganizationId' }).Count 'shared OrganizationId input merged once' # 2c) Every combinable ISC + Entro pair merges without an input-name collision. # (Shared input names must have the same Purpose, see Merge-IntegrationWizardDefinitions.) Import-Module (Join-Path $wizardRoot 'modules/Definitions/Aws.Connector.psm1') -Force Import-Module (Join-Path $wizardRoot 'modules/Definitions/Aws.Entro.psm1') -Force Import-Module (Join-Path $wizardRoot 'modules/Definitions/Azure.Connector.psm1') -Force Import-Module (Join-Path $wizardRoot 'modules/Definitions/Azure.Entro.psm1') -Force Register-AwsConnectorDefinitions Register-AwsEntroDefinitions Register-AzureConnectorDefinitions Register-AzureEntroDefinitions $pairs = @( @{ Label = 'AWS SaaS + Entro'; Platform = 'AWS'; Context = @{ SaaSConnector = $true; Entro = $true } } @{ Label = 'Azure Connector + Entro'; Platform = 'Azure'; Context = @{ Connector = $true; Entro = $true } } @{ Label = 'Google SaaS + GCP Entro'; Platform = 'GoogleCloud'; Context = @{ SaaSConnector = $true; GcpEntro = $true } } @{ Label = 'Google SaaS + Workspace Entro'; Platform = 'GoogleCloud'; Context = @{ SaaSConnector = $true; WorkspaceEntro = $true } } ) foreach ($pair in $pairs) { $pairPlan = Resolve-IntegrationWizardPlan -Platform $pair.Platform -Context $pair.Context Assert-True ([bool]$pairPlan['IsComposite']) "$($pair.Label) resolves to a composite plan" } # 3) Requirements document build for a definition with no composite/legacy keys. $tmp = Join-Path ([System.IO.Path]::GetTempPath()) ("iw-strictkeys-" + [guid]::NewGuid().ToString('N')) New-Item -ItemType Directory -Path $tmp | Out-Null $entroDef = Get-IntegrationWizardDefinition -Id 'google-gcp-entro' $reqCtx = @{ Action = 'BuildRequirements'; OutputDirectory = $tmp; Method = 'PrivateKey'; OrganizationId = '1'; ProjectId = 'p' } Invoke-IntegrationWizardAction -Definition $entroDef -Context $reqCtx $reqFile = Join-Path $tmp 'google-gcp-entro-requirements.md' Assert-True (Test-Path $reqFile) 'requirements doc written for Entro definition' Remove-Item $tmp -Recurse -Force # 4) Summary and missing-input checks on definitions without optional keys. $missing = Test-IntegrationWizardRequiredInputs -Definition $entroDef -Context @{ OrganizationId = '1' } Assert-True ($missing -contains 'ProjectId') 'required input reported missing without throwing' # 5) Connection choices: "Use this session" only when a usable session exists. $script:ConnectionChoice = $null function global:Read-Choice { param($Prompt, $Options, $Labels, $Default) if ($Prompt -eq 'How do you want to connect?') { $script:ConnectionOptions = @($Options) } if ($Default) { return $Default } return @($Options)[0] } $unsigned = @{ Installed = $true; SignedIn = $false; Summary = 'credentials expired' } $unsignedMode = Resolve-IntegrationWizardConnectionChoice -CliName 'gcloud' -SessionState $unsigned -Wizard Assert-Equal 'SignIn' $unsignedMode 'unsigned session defaults to sign in' Assert-True (@($script:ConnectionOptions) -notcontains 'UseSession') 'unsigned session hides Use this session' $signed = @{ Installed = $true; SignedIn = $true; Summary = 'account a project p' } $signedMode = Resolve-IntegrationWizardConnectionChoice -CliName 'gcloud' -SessionState $signed -Wizard Assert-Equal 'UseSession' $signedMode 'signed-in session defaults to this session' Assert-True (@($script:ConnectionOptions) -contains 'UseSession') 'signed-in session offers Use this session' Remove-Item env:SAILPOINT_CONNECT_ROOT -ErrorAction SilentlyContinue Remove-Item -LiteralPath $tmp -Recurse -Force -ErrorAction SilentlyContinue Write-Host 'PASS (20 assertions)' |