Integrate release recovery, semantic review, coverage and Windows packaging
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
param(
|
||||
[Parameter(Mandatory=$true)][string]$ServerExe,
|
||||
[Parameter(Mandatory=$true)][string]$PristineFile,
|
||||
[Parameter(Mandatory=$true)][string]$SavedEntry
|
||||
)
|
||||
$ErrorActionPreference='Stop'
|
||||
$taskRoot=(Resolve-Path (Join-Path $PSScriptRoot '../..')).Path
|
||||
$exe=(Resolve-Path -LiteralPath $ServerExe).Path
|
||||
$pristine=(Resolve-Path -LiteralPath $PristineFile).Path
|
||||
$saved=(Resolve-Path -LiteralPath $SavedEntry).Path
|
||||
$include=(Resolve-Path -LiteralPath (Join-Path (Split-Path -Parent $saved) 'sa/model.arcadia')).Path
|
||||
$runRoot=Join-Path $taskRoot ('.local/timed-bundle-oracle-'+(Get-Date -Format 'yyyyMMdd-HHmmss'))
|
||||
$storePath=Join-Path $runRoot 'store'
|
||||
New-Item -ItemType Directory -Path $runRoot,$storePath | Out-Null
|
||||
$listener=[Net.Sockets.TcpListener]::new([Net.IPAddress]::Loopback,0)
|
||||
$listener.Start(); $probePort=$listener.LocalEndpoint.Port; $listener.Stop()
|
||||
$base="http://127.0.0.1:$probePort"
|
||||
$testToken='timed-bundle-oracle-20260906-synthetic-only'
|
||||
$tokenPath=Join-Path $runRoot 'synthetic-token.txt'
|
||||
[IO.File]::WriteAllText($tokenPath,$testToken)
|
||||
$script:ownedServer=$null
|
||||
$script:launch=0
|
||||
function Start-Oracle {
|
||||
$script:launch++
|
||||
$script:ownedServer=Start-Process -FilePath $exe -ArgumentList @('--bind',"127.0.0.1:$probePort",'--data-dir',('"'+$storePath+'"'),'--token-file',('"'+$tokenPath+'"')) -WindowStyle Hidden -PassThru -RedirectStandardOutput (Join-Path $runRoot "server-$script:launch.stdout.log") -RedirectStandardError (Join-Path $runRoot "server-$script:launch.stderr.log")
|
||||
for($n=0;$n -lt 40;$n++) {
|
||||
if($script:ownedServer.HasExited){throw 'Oracle server exited'}
|
||||
try{$r=Invoke-WebRequest "$base/health/ready" -TimeoutSec 1;if($r.StatusCode -eq 200){return}}catch{}
|
||||
Start-Sleep -Milliseconds 250
|
||||
}
|
||||
throw 'Oracle readiness expired'
|
||||
}
|
||||
function Stop-Oracle {
|
||||
if($null -ne $script:ownedServer -and -not $script:ownedServer.HasExited){Stop-Process -InputObject $script:ownedServer;$script:ownedServer.WaitForExit()}
|
||||
$script:ownedServer=$null
|
||||
}
|
||||
function Request-Oracle([string]$label,[string]$method,[string]$path,$body=$null,[int]$expected=200) {
|
||||
$args=@{Uri="$base$path";Method=$method;Headers=@{Authorization="Bearer $testToken"};SkipHttpErrorCheck=$true;TimeoutSec=40}
|
||||
if($null -ne $body){$json=ConvertTo-Json -InputObject $body -Depth 100 -Compress;[IO.File]::WriteAllText((Join-Path $runRoot "$label.request.json"),$json);$args.ContentType='application/json';$args.Body=[Text.Encoding]::UTF8.GetBytes($json)}
|
||||
$r=Invoke-WebRequest @args
|
||||
[IO.File]::WriteAllText((Join-Path $runRoot "$label.response.json"),[string]$r.Content)
|
||||
$tag=[string]($r.Headers.ETag | Select-Object -First 1)
|
||||
@{status=[int]$r.StatusCode;etag=$tag;path=$path} | ConvertTo-Json | Set-Content (Join-Path $runRoot "$label.meta.json")
|
||||
if($r.StatusCode -ne $expected){throw "$label expected $expected, got $($r.StatusCode): $($r.Content)"}
|
||||
return @{data=(ConvertFrom-Json -InputObject $r.Content -AsHashtable);etag=$tag;location=[string]($r.Headers.Location | Select-Object -First 1)}
|
||||
}
|
||||
function Ordered-Json($value) {
|
||||
if($null -eq $value){return $null}
|
||||
if($value -is [Collections.IDictionary]){$map=[ordered]@{};foreach($key in @($value.Keys | Sort-Object)){$map[$key]=Ordered-Json $value[$key]};return $map}
|
||||
if($value -is [Collections.IList]){$items=@(foreach($item in $value){Ordered-Json $item});return ,$items}
|
||||
return $value
|
||||
}
|
||||
function Equal-Graph($left,$right,[string]$label) {
|
||||
$a=ConvertTo-Json -InputObject (Ordered-Json $left) -Depth 100 -Compress
|
||||
$b=ConvertTo-Json -InputObject (Ordered-Json $right) -Depth 100 -Compress
|
||||
if($a -cne $b){[IO.File]::WriteAllText((Join-Path $runRoot "$label.expected.json"),$a);[IO.File]::WriteAllText((Join-Path $runRoot "$label.actual.json"),$b);throw "$label differs"}
|
||||
}
|
||||
try {
|
||||
$hashes=@(Get-FileHash -LiteralPath $exe,$pristine,$saved,$include -Algorithm SHA256 | Select-Object Path,Hash)
|
||||
$hashes | ConvertTo-Json | Set-Content (Join-Path $runRoot 'input-hashes.json')
|
||||
$pristineBundle=@{entry=[IO.Path]::GetFileName($pristine);files=@{}}
|
||||
$pristineBundle.files[$pristineBundle.entry]=[IO.File]::ReadAllText($pristine)
|
||||
$savedBundle=@{entry=[IO.Path]::GetFileName($saved);files=@{}}
|
||||
$savedBundle.files[$savedBundle.entry]=[IO.File]::ReadAllText($saved)
|
||||
$savedBundle.files['sa/model.arcadia']=[IO.File]::ReadAllText($include)
|
||||
Start-Oracle
|
||||
$oldImport=Request-Oracle '01-pristine-import' 'POST' '/api/v1/projects/import-source-project' $pristineBundle 201
|
||||
$newImport=Request-Oracle '02-saved-import' 'POST' '/api/v1/projects/import-source-project' $savedBundle 201
|
||||
$old=Request-Oracle '03-pristine-model' 'GET' "$($oldImport.location)/model"
|
||||
$new=Request-Oracle '04-saved-model' 'GET' "$($newImport.location)/model"
|
||||
$bundle=Request-Oracle '05-saved-source-project' 'GET' "$($newImport.location)/source-project"
|
||||
Equal-Graph $savedBundle $bundle.data 'saved-source-bytes'
|
||||
$eventId='49044cb7-5f91-408a-86fc-1cc012eb2972'
|
||||
$oldEventId='f0060906-0000-4000-8000-000000000013'
|
||||
$transitionId='f0060906-0000-4000-8000-000000000033'
|
||||
if($old.data.elements.Contains($eventId)){throw 'Event already exists in pristine model'}
|
||||
$expectedModel=ConvertFrom-Json -InputObject (ConvertTo-Json -InputObject $old.data -Depth 100) -AsHashtable
|
||||
$event=ConvertFrom-Json -InputObject (ConvertTo-Json -InputObject $expectedModel.elements[$oldEventId] -Depth 100) -AsHashtable
|
||||
$event.id=$eventId;$event.alias='after_30_s';$event.name='After 30 s'
|
||||
$event.body.Sa.Shared.Event.body.Time.expression='30 s'
|
||||
$expectedModel.elements[$eventId]=$event
|
||||
$expectedModel.relations[$transitionId].name='Unfinished timed refresh'
|
||||
$expectedModel.relations[$transitionId].body.StateTransition.triggers=@($expectedModel.relations[$transitionId].body.StateTransition.triggers)+@($eventId)
|
||||
Equal-Graph $expectedModel $new.data 'exact-three-deltas'
|
||||
$validation=Request-Oracle '06-saved-validation' 'GET' "$($newImport.location)/validation"
|
||||
if(-not $validation.data.valid -or @($validation.data.issues | Where-Object severity -eq 'error').Count){throw 'Saved graph invalid'}
|
||||
Stop-Oracle
|
||||
Start-Oracle
|
||||
$reopened=Request-Oracle '07-reopened-model' 'GET' "$($newImport.location)/model"
|
||||
$reopenedBundle=Request-Oracle '08-reopened-source-project' 'GET' "$($newImport.location)/source-project"
|
||||
Equal-Graph $new.data $reopened.data 'reopened-model'
|
||||
Equal-Graph $savedBundle $reopenedBundle.data 'reopened-source-bytes'
|
||||
if($new.etag -cne $reopened.etag){throw 'Restart changed ETag'}
|
||||
$afterHashes=@(Get-FileHash -LiteralPath $exe,$pristine,$saved,$include -Algorithm SHA256 | Select-Object Path,Hash)
|
||||
Equal-Graph $hashes $afterHashes 'input-files-unchanged'
|
||||
@{result='passed';evidence=$runRoot;pristine_elements=$old.data.elements.Count;saved_elements=$new.data.elements.Count;relations=$new.data.relations.Count;diagrams=$new.data.diagrams.Count;placements=@($new.data.diagrams.Values | ForEach-Object {$_.nodes.Count});new_event=$new.data.elements[$eventId];transition_before=$old.data.relations[$transitionId];transition_after=$new.data.relations[$transitionId];issues=$validation.data.issues} | ConvertTo-Json -Depth 40 | Set-Content (Join-Path $runRoot 'result.json')
|
||||
Get-Content (Join-Path $runRoot 'result.json')
|
||||
} catch {$_ | Out-String | Set-Content (Join-Path $runRoot 'failure.txt');throw}
|
||||
finally {Stop-Oracle}
|
||||
Reference in New Issue
Block a user