Skip to content
Open
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
64 changes: 61 additions & 3 deletions crates/openshell-driver-mxc/examples/run-mxc-e2e.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,63 @@ $script:registered = $false
$tomlBase = $null
$gwLog = $null
$gwErrLog = $null
$cliStateRoot = $null
$cliEnvironmentSnapshot = @{}
$cliEnvironmentNames = @(
"APPDATA",
"LOCALAPPDATA",
"XDG_CONFIG_HOME",
"XDG_STATE_HOME",
"XDG_DATA_HOME",
"OPENSHELL_GATEWAY",
"OPENSHELL_GATEWAY_ENDPOINT",
"OPENSHELL_GATEWAY_INSECURE",
"OPENSHELL_GATEWAY_CONFIG",
"OPENSHELL_GATEWAY_NAME"
)

# --- Helpers ------------------------------------------------------------------

function Enter-IsolatedCliEnvironment {
foreach ($name in $cliEnvironmentNames) {
$script:cliEnvironmentSnapshot[$name] = [Environment]::GetEnvironmentVariable($name, "Process")
}
$script:cliStateRoot = Join-Path ([IO.Path]::GetTempPath()) "openshell-mxc-e2e-cli-$PID-$([Guid]::NewGuid().ToString('N'))"
$isolatedPaths = @{
APPDATA = Join-Path $script:cliStateRoot "appdata"
LOCALAPPDATA = Join-Path $script:cliStateRoot "localappdata"
XDG_CONFIG_HOME = Join-Path $script:cliStateRoot "xdg-config"
XDG_STATE_HOME = Join-Path $script:cliStateRoot "xdg-state"
XDG_DATA_HOME = Join-Path $script:cliStateRoot "xdg-data"
}
try {
New-Item -ItemType Directory -Force -Path @($isolatedPaths.Values) | Out-Null
foreach ($entry in $isolatedPaths.GetEnumerator()) {
[Environment]::SetEnvironmentVariable($entry.Key, $entry.Value, "Process")
}
foreach ($name in $cliEnvironmentNames | Where-Object { -not $isolatedPaths.ContainsKey($_) }) {
Remove-Item "Env:$name" -ErrorAction SilentlyContinue
}
} catch {
Exit-IsolatedCliEnvironment
throw
}
}

function Exit-IsolatedCliEnvironment {
foreach ($name in $cliEnvironmentNames) {
$value = $script:cliEnvironmentSnapshot[$name]
if ($null -eq $value) {
Remove-Item "Env:$name" -ErrorAction SilentlyContinue
} else {
[Environment]::SetEnvironmentVariable($name, $value, "Process")
}
}
if ($script:cliStateRoot -and (Test-Path -LiteralPath $script:cliStateRoot)) {
Remove-Item -LiteralPath $script:cliStateRoot -Recurse -Force -ErrorAction SilentlyContinue
}
}

# Render host-runtime settings from the pristine base. Sandbox workload
# settings are create-time driver config, not gateway-wide TOML.
function Render-Toml {
Expand Down Expand Up @@ -233,14 +287,14 @@ function Stop-Gw($p) {

function Register-Cli {
if ($script:registered) { return }
$env:OPENSHELL_GATEWAY = ""

$expectedEndpoint = "http://127.0.0.1:$Port"
$addResult = Invoke-NativeCaptured $cli @(
"gateway", "add", "http://127.0.0.1:$Port", "--local", "--name", $GatewayName
"gateway", "add", $expectedEndpoint, "--local", "--name", $GatewayName
)
$addText = ($addResult.Output -join "`n")
if ($addText) { $addResult.Output | ForEach-Object { Info $_ } }
if ($addResult.ExitCode -ne 0 -and $addText -notmatch '(?i)already exists') {
if ($addResult.ExitCode -ne 0) {
throw "gateway add failed (exit $($addResult.ExitCode)): $addText"
}

Expand Down Expand Up @@ -379,6 +433,8 @@ $backendProbe = @{ Live = $false; Reason = "not probed" }
$runId = Get-Date -Format 'MMddHHmmss'

try {
Enter-IsolatedCliEnvironment

# Start the transcript inside the guarded region so a Start-Transcript failure
# is caught and the results bundle is still produced. Pre-flight runs
# immediately below, so the transcript still captures the whole run.
Expand Down Expand Up @@ -758,6 +814,8 @@ catch {
Bad "harness error: $harnessError"
}
finally {
Exit-IsolatedCliEnvironment

# --- Summary + results bundle ---------------------------------------------
Step "Summary"
$results | Format-Table -AutoSize
Expand Down
69 changes: 65 additions & 4 deletions crates/openshell-driver-mxc/examples/run-ocsf-audit.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,10 @@ function Invoke-Cli([string[]]$CommandArgs, [switch]$AllowFailure) {
if (-not $AllowFailure -and $process.ExitCode -ne 0) {
throw "openshell $($CommandArgs -join ' ') failed (exit $($process.ExitCode)): $text"
}
return @{ ExitCode = $process.ExitCode; Text = $text }
return @{
ExitCode = $process.ExitCode
Text = $text
}
}

function Resolve-Artifact([string]$explicit, [string]$leaf) {
Expand All @@ -152,6 +155,61 @@ function Get-MxcEtwSessions {
}
}

$cliStateRoot = $null
$cliEnvironmentSnapshot = @{}
$cliEnvironmentNames = @(
"APPDATA",
"LOCALAPPDATA",
"XDG_CONFIG_HOME",
"XDG_STATE_HOME",
"XDG_DATA_HOME",
"OPENSHELL_GATEWAY",
"OPENSHELL_GATEWAY_ENDPOINT",
"OPENSHELL_GATEWAY_INSECURE",
"OPENSHELL_GATEWAY_CONFIG",
"OPENSHELL_GATEWAY_NAME"
)

function Enter-IsolatedCliEnvironment {
foreach ($name in $cliEnvironmentNames) {
$script:cliEnvironmentSnapshot[$name] = [Environment]::GetEnvironmentVariable($name, "Process")
}
$script:cliStateRoot = Join-Path ([IO.Path]::GetTempPath()) "openshell-mxc-ocsf-cli-$PID-$([Guid]::NewGuid().ToString('N'))"
$isolatedPaths = @{
APPDATA = Join-Path $script:cliStateRoot "appdata"
LOCALAPPDATA = Join-Path $script:cliStateRoot "localappdata"
XDG_CONFIG_HOME = Join-Path $script:cliStateRoot "xdg-config"
XDG_STATE_HOME = Join-Path $script:cliStateRoot "xdg-state"
XDG_DATA_HOME = Join-Path $script:cliStateRoot "xdg-data"
}
try {
New-Item -ItemType Directory -Force -Path @($isolatedPaths.Values) | Out-Null
foreach ($entry in $isolatedPaths.GetEnumerator()) {
[Environment]::SetEnvironmentVariable($entry.Key, $entry.Value, "Process")
}
foreach ($name in $cliEnvironmentNames | Where-Object { -not $isolatedPaths.ContainsKey($_) }) {
Remove-Item "Env:$name" -ErrorAction SilentlyContinue
}
} catch {
Exit-IsolatedCliEnvironment
throw
}
}

function Exit-IsolatedCliEnvironment {
foreach ($name in $cliEnvironmentNames) {
$value = $script:cliEnvironmentSnapshot[$name]
if ($null -eq $value) {
Remove-Item "Env:$name" -ErrorAction SilentlyContinue
} else {
[Environment]::SetEnvironmentVariable($name, $value, "Process")
}
}
if ($script:cliStateRoot -and (Test-Path -LiteralPath $script:cliStateRoot)) {
Remove-Item -LiteralPath $script:cliStateRoot -Recurse -Force -ErrorAction SilentlyContinue
}
}

$gateway = Resolve-Artifact $GatewayPath "openshell-gateway.exe"
$cli = Resolve-Artifact $CliPath "openshell.exe"
$policySrc = Join-Path $here "ocsf-audit.yaml"
Expand All @@ -167,6 +225,8 @@ $proxyOn = -not $NoProxy
$oldMockWxc = $env:OPENSHELL_MXC_MOCK_WXC

try {
Enter-IsolatedCliEnvironment

# 1. Validate artifacts + privilege.
Step "Validate package artifacts"
foreach ($f in @($gateway, $cli, $policySrc, $tomlSrc)) {
Expand Down Expand Up @@ -319,10 +379,10 @@ try {

# 9. Register CLI -> gateway.
Step "Register CLI -> gateway"
$env:OPENSHELL_GATEWAY = ""
$gatewayAdd = Invoke-Cli @("gateway", "add", "http://127.0.0.1:$Port", "--local", "--name", $GatewayName) -AllowFailure
$expectedEndpoint = "http://127.0.0.1:$Port"
$gatewayAdd = Invoke-Cli @("gateway", "add", $expectedEndpoint, "--local", "--name", $GatewayName) -AllowFailure
if ($gatewayAdd.Text) { Info $gatewayAdd.Text }
if ($gatewayAdd.ExitCode -ne 0 -and $gatewayAdd.Text -notmatch '(?i)already exists') {
if ($gatewayAdd.ExitCode -ne 0) {
throw "gateway registration failed (exit $($gatewayAdd.ExitCode)): $($gatewayAdd.Text)"
}
$gatewaySelect = Invoke-Cli @("gateway", "select", $GatewayName)
Expand Down Expand Up @@ -382,6 +442,7 @@ finally {
} else {
$env:OPENSHELL_MXC_MOCK_WXC = $oldMockWxc
}
Exit-IsolatedCliEnvironment

# ---- summarise the OCSF audit trail --------------------------------------
$logText = @()
Expand Down
18 changes: 18 additions & 0 deletions crates/openshell-driver-mxc/tests/demo_examples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,24 @@ fn shipped_aggregate_e2e_assets_support_mock_wiring_validation() {
assert!(runner.contains("not evidence of native MXC or OS enforcement"));
}

#[test]
fn shipped_runners_isolate_cli_state_and_gateway_overrides() {
for name in ["run-mxc-e2e.ps1", "run-ocsf-audit.ps1"] {
let runner = read_example(name);
for required in [
"Enter-IsolatedCliEnvironment",
"Exit-IsolatedCliEnvironment",
"APPDATA",
"LOCALAPPDATA",
"OPENSHELL_GATEWAY",
"OPENSHELL_GATEWAY_ENDPOINT",
"Remove-Item \"Env:$name\"",
] {
assert!(runner.contains(required), "{name} is missing {required}");
}
}
}

#[test]
fn shipped_audit_and_websocket_configs_use_current_schema() {
for name in ["mxc-ocsf-audit.toml", "mxc-ws-gateway.toml"] {
Expand Down
91 changes: 91 additions & 0 deletions crates/openshell-driver-mxc/tests/windows_e2e_harness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,97 @@ foreach ($name in $names) {
);
}

#[test]
fn shipped_runners_remove_empty_overrides_in_both_powershell_versions() {
let examples = Path::new(env!("CARGO_MANIFEST_DIR")).join("examples");
let script = r#"
$ErrorActionPreference = "Stop"
$tokens = $null
$errors = $null
$ast = [System.Management.Automation.Language.Parser]::ParseFile(
$env:OPENSHELL_MXC_RUNNER,
[ref]$tokens,
[ref]$errors
)
if ($errors.Count -gt 0) { throw ($errors.Message -join "; ") }
foreach ($functionName in @("Enter-IsolatedCliEnvironment", "Exit-IsolatedCliEnvironment")) {
$function = $ast.Find({
param($node)
$node -is [System.Management.Automation.Language.FunctionDefinitionAst] -and
$node.Name -eq $functionName
}, $true)
if ($null -eq $function) { throw "$functionName was not found" }
Invoke-Expression $function.Extent.Text
}

$cliStateRoot = $null
$cliEnvironmentSnapshot = @{}
$cliEnvironmentNames = @(
"APPDATA", "LOCALAPPDATA", "XDG_CONFIG_HOME", "XDG_STATE_HOME", "XDG_DATA_HOME",
"OPENSHELL_GATEWAY", "OPENSHELL_GATEWAY_ENDPOINT", "OPENSHELL_GATEWAY_INSECURE",
"OPENSHELL_GATEWAY_CONFIG", "OPENSHELL_GATEWAY_NAME"
)
$env:OPENSHELL_GATEWAY_ENDPOINT = "http://127.0.0.1:1"
Remove-Item Env:OPENSHELL_GATEWAY -ErrorAction SilentlyContinue

Enter-IsolatedCliEnvironment
try {
if (Test-Path Env:OPENSHELL_GATEWAY_ENDPOINT) {
throw "OPENSHELL_GATEWAY_ENDPOINT was not removed"
}
if (Test-Path Env:OPENSHELL_GATEWAY) {
throw "OPENSHELL_GATEWAY was not removed"
}
} finally {
Exit-IsolatedCliEnvironment
}

if ($env:OPENSHELL_GATEWAY_ENDPOINT -ne "http://127.0.0.1:1") {
throw "OPENSHELL_GATEWAY_ENDPOINT was not restored"
}
if (Test-Path Env:OPENSHELL_GATEWAY) {
throw "null OPENSHELL_GATEWAY snapshot was restored as an empty variable"
}
"#;

for runner in ["run-mxc-e2e.ps1", "run-ocsf-audit.ps1"] {
for shell in ["powershell.exe", "pwsh.exe"] {
let output = std::process::Command::new(shell)
.args(["-NoProfile", "-Command", script])
.env("OPENSHELL_MXC_RUNNER", examples.join(runner))
.output()
.unwrap_or_else(|error| panic!("failed to launch {shell}: {error}"));
assert!(
output.status.success(),
"{runner} environment isolation failed under {shell}:\nstdout:\n{}\nstderr:\n{}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
}
}
}

#[test]
fn aggregate_and_ocsf_harnesses_preserve_caller_gateway_state() {
let repo_root = Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
for name in [
"windows-mxc-aggregate-e2e.ps1",
"windows-mxc-ocsf-audit-e2e.ps1",
] {
let source = std::fs::read_to_string(repo_root.join("tasks/scripts").join(name))
.unwrap_or_else(|error| panic!("failed to read {name}: {error}"));
for required in [
"$sentinelEndpoint",
"OPENSHELL_GATEWAY_ENDPOINT",
"gateway list -o json",
"$sentinel.active",
"pwsh.exe",
] {
assert!(source.contains(required), "{name} is missing {required}");
}
}
}

#[test]
fn aggregate_harness_exercises_a_staging_path_with_spaces() {
let harness = Path::new(env!("CARGO_MANIFEST_DIR"))
Expand Down
50 changes: 38 additions & 12 deletions tasks/scripts/windows-mxc-aggregate-e2e.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -96,18 +96,44 @@ try {

$runner = Join-Path $StageDir "run-mxc-e2e.ps1"
$demoDir = Join-Path $StageDir "demo"
$port = Get-AvailablePort
$output = & powershell.exe -NoProfile -ExecutionPolicy Bypass -File $runner `
-Mock `
-GatewayPath $GatewayPath `
-CliPath $CliPath `
-DemoDir $demoDir `
-Port $port `
-GatewayName "openshell-mxc-aggregate-ci" 2>&1
$exitCode = $LASTEXITCODE
$output | ForEach-Object { Write-Host $_ }
if ($exitCode -ne 0) {
throw "shipped aggregate MXC example failed in mock mode (exit $exitCode)"
$gatewayName = "openshell-mxc-aggregate-ci"
$sentinelEndpoint = "http://127.0.0.1:9"
$inheritedEndpoint = "http://127.0.0.1:1"
$previousErrorActionPreference = $ErrorActionPreference
try {
$ErrorActionPreference = "Continue"
$sentinelAdd = & $CliPath gateway add $sentinelEndpoint --local --name $gatewayName 2>&1
$sentinelAddExitCode = $LASTEXITCODE
} finally {
$ErrorActionPreference = $previousErrorActionPreference
}
if ($sentinelAddExitCode -ne 0) {
throw "failed to seed sentinel gateway '$gatewayName': $($sentinelAdd -join [Environment]::NewLine)"
}
$env:OPENSHELL_GATEWAY_ENDPOINT = $inheritedEndpoint
foreach ($powerShell in @("powershell.exe", "pwsh.exe")) {
$port = Get-AvailablePort
$output = & $powerShell -NoProfile -ExecutionPolicy Bypass -File $runner `
-Mock `
-GatewayPath $GatewayPath `
-CliPath $CliPath `
-DemoDir $demoDir `
-Port $port `
-GatewayName $gatewayName 2>&1
$exitCode = $LASTEXITCODE
$output | ForEach-Object { Write-Host $_ }
if ($exitCode -ne 0) {
throw "shipped aggregate MXC example failed under $powerShell in mock mode (exit $exitCode)"
}

$gateways = & $CliPath gateway list -o json | ConvertFrom-Json
$sentinel = $gateways | Where-Object { $_.name -eq $gatewayName } | Select-Object -First 1
if ($null -eq $sentinel -or $sentinel.endpoint -ne $sentinelEndpoint -or -not $sentinel.active) {
throw "aggregate runner changed the caller's sentinel gateway registration or active selection under $powerShell"
}
if ($env:OPENSHELL_GATEWAY_ENDPOINT -ne $inheritedEndpoint) {
throw "aggregate runner changed the caller's OPENSHELL_GATEWAY_ENDPOINT under $powerShell"
}
}

$resultDir = Get-ChildItem -LiteralPath $StageDir -Directory -Filter "results-e2e-*" |
Expand Down
Loading
Loading