param( [string]$Server = 'lpc:.', [Parameter(Mandatory=$true)][string]$FixtureJson, [switch]$Baseline ) $ErrorActionPreference = 'Stop' [Console]::OutputEncoding = [Text.UTF8Encoding]::new($false) # Synthetic rows only. Never run fixtures in a production database. $testDb = 'FT546WMS_MixedPilot_Test_' + [Guid]::NewGuid().ToString('N') $cn = [Data.SqlClient.SqlConnection]::new("Server=$Server;Database=master;Integrated Security=True;Connect Timeout=8;Application Name=FT546MixedPilotTests") $cn.Open() function Exec([string]$sql) { $c=$cn.CreateCommand(); $c.CommandTimeout=15 $c.CommandText=$sql try { [void]$c.ExecuteNonQuery() } finally {$c.Dispose()} } function Scalar([string]$sql) { $c=$cn.CreateCommand(); $c.CommandTimeout=15; $c.CommandText=$sql try { $c.ExecuteScalar() } finally {$c.Dispose()} } function BatchFile([string]$file) { foreach($batch in ([IO.File]::ReadAllText($file) -split '(?im)^\s*GO\s*\r?$')) { if($batch.Trim()) { Exec $batch } } } function Assert([bool]$ok,[string]$name) { if(-not $ok){throw "FAIL: $name"}; Write-Output "PASS: $name" } $created=$false try { Exec "CREATE DATABASE [$testDb] ON PRIMARY (NAME=N'$testDb', FILENAME=N'$(Scalar "SELECT CONVERT(nvarchar(4000),SERVERPROPERTY('InstanceDefaultDataPath'))")$testDb.mdf',SIZE=8MB,MAXSIZE=64MB,FILEGROWTH=8MB) LOG ON (NAME=N'${testDb}_log', FILENAME=N'$(Scalar "SELECT CONVERT(nvarchar(4000),SERVERPROPERTY('InstanceDefaultLogPath'))")${testDb}_log.ldf',SIZE=8MB,MAXSIZE=64MB,FILEGROWTH=8MB)" $created=$true; $cn.ChangeDatabase($testDb) Exec 'SET NOCOUNT ON; SET LOCK_TIMEOUT 2000; SET DEADLOCK_PRIORITY LOW;' $fixture=Get-Content -LiteralPath $FixtureJson -Raw -Encoding UTF8 | ConvertFrom-Json $allowed=@('bas_cargo_space','bas_stock','mydo_job','mydo_job_detail','mydo_job_finish','bas_led','move_msg','out_stockout','out_stockout_detail') foreach($table in ($fixture.columns|Where-Object table_name -in $allowed|Group-Object table_name)) { $defs=@(foreach($c in $table.Group){ $type=$c.type_name if($type -in @('varchar','char','nvarchar','nchar','varbinary','binary')) { $size=if($c.max_length -eq -1){'max'}elseif($type.StartsWith('n')){[string]($c.max_length/2)}else{[string]$c.max_length} $type+="($size)" } elseif($type -in @('decimal','numeric')) {$type+="($($c.precision),$($c.scale))"} # Nullability is relaxed for concise synthetic fixtures, types/order match site. "[$($c.column_name)] $type NULL" }) Exec "CREATE TABLE dbo.[$($table.Name)] ($($defs -join ','))" } foreach($p in $fixture.wms_objects|Where-Object name -in @('cargo_space_group_count','proc_allot_space','proc_yk_job_add','proc_yk_complete','proc_out_auto_allocation','proc_out_hand_allocation')) {Exec $p.definition} Exec "CREATE TABLE dbo.bas_roadway(roadway_id varchar(80),out_locked varchar(10),out_locked2 varchar(10))" Exec "CREATE PROCEDURE dbo.util_serial_job @kind varchar(10),@id varchar(255) OUTPUT AS SET @id=CONVERT(varchar(36),NEWID())" Exec "CREATE SEQUENCE dbo.test_task_seq AS int START WITH 900001" Exec "CREATE PROCEDURE dbo.util_serial_task @kind varchar(10),@id int OUTPUT AS SET @id=NEXT VALUE FOR dbo.test_task_seq" if(-not $Baseline){ & "$PSScriptRoot/install-mixed-pilot.ps1" -Server $Server -Database $testDb -TestDatabase } BatchFile "$PSScriptRoot/tests/seed.sql" if($Baseline){ $result=Scalar "DECLARE @r int,@m varchar(255); EXEC dbo.proc_allot_space 'NEW-TRAY','TEST',@r OUTPUT,@m OUTPUT; SELECT @r" Assert ($result -eq 10) 'new project gets designated front slot when Legacy has no position' } else { & "$PSScriptRoot/control-mixed-pilot.ps1" -Server $Server -Database $testDb -TestDatabase -Mode enable -ConfirmPhysicalFit -AllProjectsMayMix Assert ((Scalar 'SELECT COUNT(*) FROM mixed_pilot.lane WHERE enabled=1') -eq 3) 'enable command opens exactly three whole channels' & "$PSScriptRoot/control-mixed-pilot.ps1" -Server $Server -Database $testDb -TestDatabase -Mode disable $refused=$false try { & "$PSScriptRoot/control-mixed-pilot.ps1" -Server $Server -Database $testDb -TestDatabase -Mode uninstall } catch {$refused=$_.Exception.Message -like '*not drained*'} Assert $refused 'uninstall refuses occupied trial channels' BatchFile "$PSScriptRoot/tests/behavior.sql" Write-Output 'PASS: mixed-pilot SQL behavior suite' Exec 'DELETE dbo.mydo_job; DELETE dbo.mydo_job_detail; DELETE dbo.bas_stock; DELETE dbo.bas_cargo_space; DELETE mixed_pilot.dispatch_hold; DELETE mixed_pilot.move_audit; DELETE mixed_pilot.admission;' BatchFile "$PSScriptRoot/tests/seed.sql" BatchFile "$PSScriptRoot/tests/legacy-outbound.sql" Write-Output 'PASS: actual site Legacy outbound procedure generates blocker relocation and holds rear outbound' Exec 'DELETE dbo.mydo_job; DELETE dbo.mydo_job_detail; DELETE dbo.bas_stock; DELETE dbo.bas_cargo_space; DELETE mixed_pilot.dispatch_hold; DELETE mixed_pilot.move_audit; DELETE mixed_pilot.admission;' BatchFile "$PSScriptRoot/tests/seed.sql" Exec "UPDATE mixed_pilot.control SET enabled=1,all_projects_may_mix=1; UPDATE mixed_pilot.lane SET enabled=CASE WHEN lane_code='C1203' THEN 1 ELSE 0 END,physical_fit_confirmed=1; INSERT dbo.mydo_job(job_id,tray_code,warehouse_id,line_id,in_or_out,status,fault_flag) VALUES('IN-RACE','RACE-TRAY','01','RACE',1,'1','0'); INSERT dbo.mydo_job_detail(job_id,spare4,spare1,spare2,spare3,in_quantity) VALUES('IN-RACE','RACE','X','Y','Z',1)" $raceCn=[Data.SqlClient.SqlConnection]::new("Server=$Server;Database=$testDb;Integrated Security=True;Connect Timeout=8;Application Name=FT546MixedPilotRaceTest") $raceCn.Open() try { Exec 'BEGIN TRANSACTION;' Exec "DECLARE @r int,@m varchar(255); EXEC dbo.proc_allot_space 'NEW-TRAY','TEST',@r OUTPUT,@m OUTPUT; IF @r<>10 THROW 51950,'First racing allocation failed',1;" $raceCmd=$raceCn.CreateCommand();$raceCmd.CommandTimeout=10 $raceCmd.CommandText="DECLARE @r int,@m varchar(255); EXEC dbo.proc_allot_space 'RACE-TRAY','TEST',@r OUTPUT,@m OUTPUT; SELECT @r;" $pending=$raceCmd.ExecuteScalarAsync() # Hold the first reservation across the second request's lock timeout. $second=$pending.GetAwaiter().GetResult() Assert ($second -ne 10) 'concurrent request cannot allocate a reserved pilot slot' Exec 'COMMIT TRANSACTION;' Assert ((Scalar "SELECT COUNT(*) FROM dbo.mydo_job WHERE cargo_space_id='120703'") -eq 1) 'only one inbound task owns the pilot slot' } finally { if((Scalar 'SELECT @@TRANCOUNT') -gt 0){Exec 'ROLLBACK TRANSACTION;'} if($raceCmd){$raceCmd.Dispose()};$raceCn.Dispose() } # Re-running install must preserve runtime switches and history. Exec 'UPDATE mixed_pilot.control SET enabled=1 WHERE id=1' & "$PSScriptRoot/install-mixed-pilot.ps1" -Server $Server -Database $testDb -TestDatabase Assert ((Scalar 'SELECT enabled FROM mixed_pilot.control WHERE id=1') -eq 1) 'repeat install preserves switch' $releaseDefinition=Scalar "SELECT OBJECT_DEFINITION(OBJECT_ID('mixed_pilot.release_ready_jobs'))" Exec 'ALTER PROCEDURE mixed_pilot.release_ready_jobs AS RETURN;' $driftRejected=$false try { & "$PSScriptRoot/install-mixed-pilot.ps1" -Server $Server -Database $testDb -TestDatabase } catch {$driftRejected=$_.Exception.Message -like '*drift*'} Assert $driftRejected 'repeat install detects altered pilot helpers' Exec 'DROP PROCEDURE mixed_pilot.release_ready_jobs;' Exec $releaseDefinition & "$PSScriptRoot/control-mixed-pilot.ps1" -Server $Server -Database $testDb -TestDatabase -Mode disable Exec 'DELETE dbo.mydo_job; DELETE dbo.bas_stock; UPDATE dbo.bas_cargo_space SET status=''1'',tray_code=NULL;' & "$PSScriptRoot/control-mixed-pilot.ps1" -Server $Server -Database $testDb -TestDatabase -Mode uninstall Assert ((Scalar 'SELECT installed FROM mixed_pilot.control WHERE id=1') -eq 0) 'uninstall removes runtime protection only after draining' foreach($p in $fixture.wms_objects|Where-Object name -in @('proc_allot_space','proc_yk_job_add','proc_yk_complete')) { $restored=Scalar "SELECT OBJECT_DEFINITION(OBJECT_ID('dbo.$($p.name)'))" # SQL Server may persist CREATE even when ALTER was submitted. Compare the full body with only that keyword normalized. $expected=[regex]::Replace($p.definition,'(?i)\b(?:CREATE|ALTER)\s+PROCEDURE','CREATE PROCEDURE') $normalized=[regex]::Replace($restored,'(?i)\b(?:CREATE|ALTER)\s+PROCEDURE','CREATE PROCEDURE') Assert ($normalized -ceq $expected) "uninstall restores $($p.name) body exactly" } } } finally { if($created){ [Data.SqlClient.SqlConnection]::ClearAllPools() $cn.ChangeDatabase('master') if($testDb -notmatch '^FT546WMS_MixedPilot_Test_[a-f0-9]{32}$'){throw 'Invalid scratch database name'} Exec "DROP DATABASE [$testDb]" Write-Output "Scratch database removed: $testDb" } $cn.Dispose() }