feat(wms): add controlled three-channel mixed-storage pilot
This commit is contained in:
121
scripts/mixed-pilot/test-mixed-pilot.ps1
Normal file
121
scripts/mixed-pilot/test-mixed-pilot.ps1
Normal file
@@ -0,0 +1,121 @@
|
||||
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()
|
||||
}
|
||||
Reference in New Issue
Block a user