diff --git a/tests/AcDream.App.Tests/Diagnostics/ConnectedRenderPackGateContractTests.cs b/tests/AcDream.App.Tests/Diagnostics/ConnectedRenderPackGateContractTests.cs index 843f157c..5130f489 100644 --- a/tests/AcDream.App.Tests/Diagnostics/ConnectedRenderPackGateContractTests.cs +++ b/tests/AcDream.App.Tests/Diagnostics/ConnectedRenderPackGateContractTests.cs @@ -125,6 +125,8 @@ foreach ($name in $script:ConnectedGateEnvironmentNames) {{ }} [Environment]::SetEnvironmentVariable('ACDREAM_FUTURE_GATE_KNOB', 'sentinel-future', 'Process') $state = New-ConnectedRenderPackGateState -Root {rootQuoted} -Preset medium +$sessionConfigPath = New-ConnectedGraphicalSessionConfig -State $state -Account 'fixture-account' +$sessionConfig = Get-Content -Raw -LiteralPath $sessionConfigPath | ConvertFrom-Json $isolated=@($state.PreviousEnvironment.Keys | Where-Object {{ $_ -notin @('ACDREAM_CONFIG_DIR', 'ACDREAM_DATA_DIR', 'ACDREAM_CACHE_DIR') -and [Environment]::GetEnvironmentVariable($_, 'Process') -ne $null @@ -141,11 +143,16 @@ $unsafeLeafRejected=$false try {{ Assert-ConnectedGateSafeLeafName '../escape' }} catch {{ $unsafeLeafRejected=$true }} $escapeRejected=$false try {{ Assert-ConnectedGateContainedPath {rootQuoted} (Join-Path {rootQuoted} '..\escape') }} catch {{ $escapeRejected=$true }} +Remove-ConnectedGraphicalSessionConfig -State $state -Path $sessionConfigPath [pscustomobject]@{{ Password=$env:ACDREAM_TEST_PASS; User=$env:ACDREAM_TEST_USER; Config=$env:ACDREAM_CONFIG_DIR; History=$env:ACDREAM_FRAME_HISTORY; Future=$env:ACDREAM_FUTURE_GATE_KNOB; Isolated=$isolated; Mismatches=$mismatches; UnsafeLeafRejected=$unsafeLeafRejected; EscapeRejected=$escapeRejected; + SessionConfigRemoved=(-not (Test-Path -LiteralPath $sessionConfigPath)); + SessionCharacterIndex=$sessionConfig.sessions[0].character.index; + SessionCredentialProvider=$sessionConfig.sessions[0].credential.provider; + SessionCredentialReference=$sessionConfig.sessions[0].credential.reference; Settings=(Get-Content -Raw -LiteralPath (Join-Path $state.ConfigDirectory 'settings.json') | ConvertFrom-Json).display.renderPack.presetId }} | ConvertTo-Json -Compress"; using JsonDocument result = JsonDocument.Parse(RunPowerShell(command)); @@ -159,11 +166,32 @@ try {{ Assert-ConnectedGateContainedPath {rootQuoted} (Join-Path {rootQuoted} '. Assert.Empty(rootElement.GetProperty("Mismatches").EnumerateArray()); Assert.True(rootElement.GetProperty("UnsafeLeafRejected").GetBoolean()); Assert.True(rootElement.GetProperty("EscapeRejected").GetBoolean()); + Assert.True(rootElement.GetProperty("SessionConfigRemoved").GetBoolean()); + Assert.Equal(0, rootElement.GetProperty("SessionCharacterIndex").GetInt32()); + Assert.Equal("Environment", rootElement.GetProperty("SessionCredentialProvider").GetString()); + Assert.Equal("ACDREAM_TEST_PASS", rootElement.GetProperty("SessionCredentialReference").GetString()); Assert.Equal("medium", rootElement.GetProperty("Settings").GetString()); } finally { Directory.Delete(root, recursive: true); } } + [Fact] + public void ConnectedGraphicalGatesSelectTheFirstCharacterThroughSessionConfig() + { + foreach (string scriptName in new[] { LifecycleScript, SoakScript }) + { + string source = ReadTool(scriptName); + Assert.Contains("New-ConnectedGraphicalSessionConfig", source, StringComparison.Ordinal); + Assert.Contains("-ArgumentList @('--session-config'", source, StringComparison.Ordinal); + Assert.Contains("Remove-ConnectedGraphicalSessionConfig", source, StringComparison.Ordinal); + } + + string common = ReadTool(CommonScript); + Assert.Contains("character = [ordered]@{ index = $CharacterIndex }", common, StringComparison.Ordinal); + Assert.Contains("provider = 'Environment'", common, StringComparison.Ordinal); + Assert.Contains("reference = $CredentialEnvironmentVariable", common, StringComparison.Ordinal); + } + [Fact] public void EveryConnectedScreenshotMustProveExactFailureFreeActivation() { diff --git a/tools/connected-render-pack-gate-common.ps1 b/tools/connected-render-pack-gate-common.ps1 index 0a0bb9bc..6264bbb9 100644 --- a/tools/connected-render-pack-gate-common.ps1 +++ b/tools/connected-render-pack-gate-common.ps1 @@ -160,6 +160,63 @@ function Restore-ConnectedRenderPackGateEnvironment { } } +function New-ConnectedGraphicalSessionConfig { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)][object]$State, + [Parameter(Mandatory = $true)][string]$Account, + [string]$HostName = '127.0.0.1', + [ValidateRange(1, 65535)][int]$Port = 9000, + [ValidateRange(0, [int]::MaxValue)][int]$CharacterIndex = 0, + [string]$CredentialEnvironmentVariable = 'ACDREAM_TEST_PASS' + ) + + if ([string]::IsNullOrWhiteSpace($Account)) { + throw 'Connected graphical session account cannot be empty.' + } + if ([string]::IsNullOrWhiteSpace($HostName)) { + throw 'Connected graphical session host cannot be empty.' + } + if ($CredentialEnvironmentVariable -notmatch '^[A-Za-z_][A-Za-z0-9_]*$') { + throw "Unsafe credential environment-variable name '$CredentialEnvironmentVariable'." + } + + $path = Assert-ConnectedGateContainedPath $State.StateDirectory ( + Join-Path $State.StateDirectory 'graphical-session.json') + if (Test-Path -LiteralPath $path) { + throw "Connected graphical session config already exists: '$path'." + } + + [ordered]@{ + version = 1 + sessions = @([ordered]@{ + id = 'connected-gate' + endpoint = [ordered]@{ host = $HostName; port = $Port } + account = $Account + character = [ordered]@{ index = $CharacterIndex } + credential = [ordered]@{ + provider = 'Environment' + reference = $CredentialEnvironmentVariable + } + }) + } | ConvertTo-Json -Depth 8 | + Set-Content -LiteralPath $path -Encoding utf8 + return $path +} + +function Remove-ConnectedGraphicalSessionConfig { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)][object]$State, + [Parameter(Mandatory = $true)][string]$Path + ) + + $containedPath = Assert-ConnectedGateContainedPath $State.StateDirectory $Path + if (Test-Path -LiteralPath $containedPath -PathType Leaf) { + Remove-Item -LiteralPath $containedPath -Force + } +} + function Get-ConnectedGateBinaryIdentity { [CmdletBinding()] param([Parameter(Mandatory = $true)][string]$Repository, diff --git a/tools/run-connected-r6-soak.ps1 b/tools/run-connected-r6-soak.ps1 index a5dfd37f..f3812cd2 100644 --- a/tools/run-connected-r6-soak.ps1 +++ b/tools/run-connected-r6-soak.ps1 @@ -730,6 +730,9 @@ $renderPackGate = New-ConnectedRenderPackGateState ` -Root $artifactDir ` -Preset $RenderPackPreset ` -SettingOverrides $RenderPackSettingOverrides +$sessionConfigPath = New-ConnectedGraphicalSessionConfig ` + -State $renderPackGate ` + -Account $Account try { $binaryIdentity = Get-ConnectedGateBinaryIdentity ` -Repository $Repository -Executable $exe -SkipBuild:$SkipBuild @@ -805,6 +808,7 @@ $envDisclosure | ConvertTo-Json -Depth 4 | try { $process = Start-Process -FilePath $exe -WorkingDirectory $Repository ` + -ArgumentList @('--session-config', ('"' + $sessionConfigPath + '"')) ` -RedirectStandardOutput $stdoutLog -RedirectStandardError $stderrLog -PassThru Write-Marker ( "launch pid=$($process.Id) binaryCommit=$commit sourceCommit=$sourceCommit " + @@ -1055,7 +1059,13 @@ finally { } } finally { - Restore-ConnectedRenderPackGateEnvironment $renderPackGate + try { + Remove-ConnectedGraphicalSessionConfig ` + -State $renderPackGate -Path $sessionConfigPath + } + finally { + Restore-ConnectedRenderPackGateEnvironment $renderPackGate + } } if ($failures.Count -gt 0) { exit 1 } diff --git a/tools/run-connected-world-lifecycle-gate.ps1 b/tools/run-connected-world-lifecycle-gate.ps1 index b5db79d6..704a244d 100644 --- a/tools/run-connected-world-lifecycle-gate.ps1 +++ b/tools/run-connected-world-lifecycle-gate.ps1 @@ -287,6 +287,7 @@ function Invoke-Session( try { $client = Start-Process -FilePath $exe -WorkingDirectory $Repository ` + -ArgumentList @('--session-config', ('"' + $sessionConfigPath + '"')) ` -RedirectStandardOutput $stdout -RedirectStandardError $stderr -PassThru Wait-ForPattern $client $stdout '[UI-PROBE] UI probe script complete' $SessionTimeoutSeconds @@ -558,6 +559,9 @@ $renderPackGate = New-ConnectedRenderPackGateState ` -Root $root ` -Preset $RenderPackPreset ` -SettingOverrides $RenderPackSettingOverrides +$sessionConfigPath = New-ConnectedGraphicalSessionConfig ` + -State $renderPackGate ` + -Account $Account try { if (@(Get-Process -Name AcDream.App -ErrorAction SilentlyContinue).Count -gt 0) { throw 'an AcDream.App client is already running; close it gracefully before the gate' @@ -683,7 +687,13 @@ try { foreach ($warning in $warnings) { Write-Output "WARNING=$warning" } } finally { - Restore-ConnectedRenderPackGateEnvironment $renderPackGate + try { + Remove-ConnectedGraphicalSessionConfig ` + -State $renderPackGate -Path $sessionConfigPath + } + finally { + Restore-ConnectedRenderPackGateEnvironment $renderPackGate + } } if ($failures.Count -gt 0) { exit 1 }