525 lines
30 KiB
PHP
525 lines
30 KiB
PHP
<?php
|
|
|
|
use Illuminate\Contracts\Console\Kernel;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Str;
|
|
use Symfony\Component\Process\Process;
|
|
|
|
require dirname(__DIR__).'/vendor/autoload.php';
|
|
$app = require dirname(__DIR__).'/bootstrap/app.php';
|
|
$app->make(Kernel::class)->bootstrap();
|
|
$config = DB::connection('pgsql')->getConfig();
|
|
unset($config['name']);
|
|
if ($config['host'] !== '127.0.0.1') {
|
|
throw new LogicException('Prova de contexto apenas em servidor local.');
|
|
}
|
|
$database = 'compras_receipts_test_'.bin2hex(random_bytes(6));
|
|
config(['database.connections.context_proof_admin' => [...$config, 'database' => 'postgres', 'url' => null]]);
|
|
$admin = DB::connection('context_proof_admin');
|
|
$admin->statement('CREATE DATABASE "'.$database.'"');
|
|
$workflow = in_array('--workflow', $argv ?? [], true);
|
|
$pilot = in_array('--pilot', $argv ?? [], true);
|
|
$workdesk = in_array('--workdesk', $argv ?? [], true);
|
|
$readiness = in_array('--readiness', $argv ?? [], true);
|
|
$exchange = in_array('--exchange', $argv ?? [], true);
|
|
$transport = in_array('--transport', $argv ?? [], true);
|
|
$internalTrace = in_array('--internal-trace', $argv ?? [], true);
|
|
$taskInbox = in_array('--task-inbox', $argv ?? [], true);
|
|
$financeIntegration = in_array('--finance-integration', $argv ?? [], true);
|
|
$awardTasks = in_array('--award-tasks', $argv ?? [], true);
|
|
$quotationTasks = in_array('--quotation-tasks', $argv ?? [], true);
|
|
$organizationPreview = in_array('--organization-preview', $argv ?? [], true);
|
|
$pilotAccessProposal = in_array('--pilot-access-proposal', $argv ?? [], true);
|
|
$formalTasks = in_array('--formal-tasks', $argv ?? [], true);
|
|
$supplyTasks = in_array('--supply-tasks', $argv ?? [], true);
|
|
$historyTasks = in_array('--history-tasks', $argv ?? [], true);
|
|
$suggestionTasks = in_array('--suggestion-tasks', $argv ?? [], true);
|
|
$alternativeTasks = in_array('--alternative-tasks', $argv ?? [], true);
|
|
$supplierTasks = in_array('--supplier-tasks', $argv ?? [], true);
|
|
$dispositionTasks = in_array('--disposition-tasks', $argv ?? [], true);
|
|
$inventoryTasks = in_array('--inventory-tasks', $argv ?? [], true);
|
|
$lotTasks = in_array('--lot-tasks', $argv ?? [], true);
|
|
$improvementTasks = in_array('--improvement-tasks', $argv ?? [], true);
|
|
$qualityTasks = in_array('--quality-tasks', $argv ?? [], true);
|
|
$coordinationTasks = in_array('--coordination-tasks', $argv ?? [], true);
|
|
$lotQualityTasks = in_array('--lot-quality-tasks', $argv ?? [], true);
|
|
$lotPagination = in_array('--lot-pagination', $argv ?? [], true);
|
|
$supplyPagination = in_array('--supply-pagination', $argv ?? [], true);
|
|
$correctionPagination = in_array('--correction-pagination', $argv ?? [], true);
|
|
$reservationSources = in_array('--reservation-sources', $argv ?? [], true);
|
|
$historySources = in_array('--history-sources', $argv ?? [], true);
|
|
$policySources = in_array('--policy-sources', $argv ?? [], true);
|
|
$correctionSources = in_array('--correction-sources', $argv ?? [], true);
|
|
$supplyLineSources = in_array('--supply-line-sources', $argv ?? [], true);
|
|
$coordinationFilters = in_array('--coordination-filters', $argv ?? [], true);
|
|
$coordinationExport = in_array('--coordination-export', $argv ?? [], true);
|
|
$taskDeadlines = in_array('--task-deadlines', $argv ?? [], true);
|
|
$noticeConsultation = in_array('--notice-consultation', $argv ?? [], true);
|
|
$agreementSources = in_array('--agreement-sources', $argv ?? [], true);
|
|
$mfaFoundation = in_array('--mfa-foundation', $argv ?? [], true);
|
|
$mfaManagement = in_array('--mfa-management', $argv ?? [], true);
|
|
$mfaEvaluation = in_array('--mfa-evaluation', $argv ?? [], true);
|
|
$peopleDossier = in_array('--people-dossier', $argv ?? [], true);
|
|
$auditMetadata = in_array('--audit-metadata', $argv ?? [], true);
|
|
$equipmentIntegration = in_array('--equipment-integration', $argv ?? [], true);
|
|
$equipmentWarehouse = in_array('--equipment-warehouse', $argv ?? [], true);
|
|
$receivedAvailability = in_array('--received-availability', $argv ?? [], true);
|
|
$candidateTerritory = in_array('--candidate-territory', $argv ?? [], true);
|
|
$candidateAdditionalGeography = in_array('--candidate-additional-geography', $argv ?? [], true);
|
|
$candidateGeography = in_array('--candidate-geography', $argv ?? [], true);
|
|
$operationalGeography = in_array('--operational-geography', $argv ?? [], true);
|
|
$geography = in_array('--geography', $argv ?? [], true);
|
|
$issuedCancellation = in_array('--issued-cancellation', $argv ?? [], true);
|
|
$requisitionAudit = in_array('--requisition-audit', $argv ?? [], true);
|
|
$auditPagination = in_array('--audit-pagination', $argv ?? [], true);
|
|
$neededAttention = in_array('--needed-attention', $argv ?? [], true);
|
|
$approvedAttention = in_array('--approved-attention', $argv ?? [], true);
|
|
$replenishmentHealth = in_array('--replenishment-health', $argv ?? [], true);
|
|
$receiptBlock = in_array('--receipt-block', $argv ?? [], true);
|
|
$receiptPrint = in_array('--receipt-print', $argv ?? [], true);
|
|
$receiptHealth = in_array('--receipt-health', $argv ?? [], true);
|
|
$operationalHealth = in_array('--operational-health', $argv ?? [], true);
|
|
$warehouseDeliveryPrint = in_array('--warehouse-delivery-print', $argv ?? [], true);
|
|
$warehouseRequestPrint = in_array('--warehouse-request-print', $argv ?? [], true);
|
|
$warehouseRequestExport = in_array('--warehouse-request-export', $argv ?? [], true);
|
|
$inbox = in_array('--inbox', $argv ?? [], true) || $transport;
|
|
$warehouse = in_array('--warehouse', $argv ?? [], true) || $pilot || $workdesk || $readiness;
|
|
try {
|
|
$environment = ['APP_ENV' => 'testing', 'DB_CONNECTION' => 'pgsql', 'DB_URL' => '', 'DB_HOST' => $config['host'], 'DB_PORT' => (string) $config['port'],
|
|
'DB_DATABASE' => $database, 'DB_USERNAME' => $config['username'], 'DB_PASSWORD' => $config['password']];
|
|
$tests = $workflow ? ['tests/Feature/InternalRequisitionCandidateTest.php', 'tests/Feature/WorkContextTest.php'] : ['tests/Feature/WorkContextTest.php'];
|
|
if ($warehouse) {
|
|
$tests = ['tests/Feature/WarehouseRequestsTest.php', 'tests/Feature/WarehouseAvailabilityTest.php', 'tests/Feature/WarehouseFulfilmentTest.php', 'tests/Feature/AutomaticPurchasesTest.php'];
|
|
}
|
|
if ($readiness) {
|
|
$tests = [...$tests, 'tests/Feature/WarehousePilotReadinessTest.php', 'tests/Feature/WarehousePilotJourneyTest.php'];
|
|
}
|
|
if ($exchange) {
|
|
$tests = ['tests/Feature/WarehouseExchangeTest.php'];
|
|
}
|
|
if ($inbox) {
|
|
$environment['INBOX_TEST_PG'] = '1';
|
|
$tests = ['tests/Unit/WarehouseInboxTest.php', 'tests/Feature/WarehouseExchangeTest.php'];
|
|
}
|
|
if ($transport) {
|
|
$tests[] = 'tests/Unit/WarehouseHttpSourceTest.php';
|
|
}
|
|
if ($internalTrace) {
|
|
$tests = ['tests/Feature/InternalSupplyTraceTest.php', 'tests/Feature/WarehousePilotJourneyTest.php'];
|
|
}
|
|
if ($taskInbox) {
|
|
$tests = ['tests/Feature/WorkflowTaskInboxTest.php', 'tests/Feature/InternalSupplyTraceTest.php', 'tests/Feature/WarehousePilotJourneyTest.php'];
|
|
}
|
|
if ($financeIntegration) {
|
|
$tests = ['tests/Feature/FinanceIntegrationTest.php', 'tests/Feature/PaymentIntegrationTest.php', 'tests/Feature/InvoiceTest.php', 'tests/Feature/ExternalPaymentTest.php', 'tests/Feature/PurchaseProcessTest.php', 'tests/Feature/PayablePaymentDecisionTest.php'];
|
|
}
|
|
if ($awardTasks) {
|
|
$tests = ['tests/Feature/PurchaseAwardOrderTest.php'];
|
|
}
|
|
if ($quotationTasks) {
|
|
$tests = ['tests/Feature/SupplierQuotationTest.php', 'tests/Feature/PurchaseAwardOrderTest.php'];
|
|
}
|
|
if ($organizationPreview) {
|
|
$tests = ['tests/Feature/OrganizationActivationPreviewTest.php', 'tests/Feature/WorkContextTest.php'];
|
|
}
|
|
if ($pilotAccessProposal) {
|
|
$tests = ['tests/Feature/PilotAccessProposalTest.php'];
|
|
}
|
|
if ($formalTasks) {
|
|
$tests = ['tests/Feature/FormalAdmissionTest.php', 'tests/Feature/FormalRecommendationTest.php', 'tests/Feature/DocumentaryQualificationTest.php'];
|
|
}
|
|
if ($supplyTasks) {
|
|
$tests = ['tests/Feature/SupplyTaskIntegrationTest.php', 'tests/Feature/AutomaticPurchasesTest.php'];
|
|
}
|
|
if ($historyTasks) {
|
|
$tests = ['tests/Feature/SupplyHistoryTest.php', 'tests/Feature/WarehousePilotReadinessTest.php'];
|
|
}
|
|
if ($suggestionTasks) {
|
|
$tests = ['tests/Feature/SupplySuggestionsTest.php'];
|
|
}
|
|
if ($alternativeTasks) {
|
|
$tests = ['tests/Feature/SupplyCoordinationTest.php'];
|
|
}
|
|
if ($supplierTasks) {
|
|
$tests = ['tests/Feature/SupplierMaintenanceTest.php'];
|
|
}
|
|
if ($dispositionTasks) {
|
|
$tests = ['tests/Feature/InventoryDispositionsTest.php'];
|
|
}
|
|
if ($inventoryTasks) {
|
|
$tests = ['tests/Feature/InventoryDispositionsTest.php', 'tests/Feature/InventoryReversalsTest.php'];
|
|
}
|
|
if ($lotTasks) {
|
|
$tests = ['tests/Feature/InventoryLotsTest.php'];
|
|
}
|
|
if ($improvementTasks) {
|
|
$tests = ['tests/Feature/SupplierImprovementTest.php'];
|
|
}
|
|
if ($qualityTasks) {
|
|
$tests = ['tests/Feature/SupplierEvaluationTest.php', 'tests/Feature/SupplierResponseTest.php'];
|
|
}
|
|
if ($coordinationTasks) {
|
|
$tests = ['tests/Feature/SupplyCoordinationTest.php', 'tests/Feature/InventoryReversalsTest.php'];
|
|
}
|
|
if ($lotQualityTasks) {
|
|
$tests = ['tests/Feature/InventoryLotsTest.php'];
|
|
}
|
|
if ($lotPagination) {
|
|
$tests = ['tests/Feature/InventoryLotsTest.php'];
|
|
}
|
|
if ($supplyPagination) {
|
|
$tests = ['tests/Feature/SupplyPaginationTest.php', 'tests/Feature/SupplyHistoryTest.php'];
|
|
}
|
|
if ($correctionPagination) {
|
|
$tests = ['tests/Feature/InventoryCorrectionPaginationTest.php'];
|
|
}
|
|
if ($reservationSources) {
|
|
$tests = ['tests/Feature/SupplyCoordinationTest.php', 'tests/Feature/InventoryReversalsTest.php'];
|
|
}
|
|
if ($historySources) {
|
|
$tests = ['tests/Feature/SupplyHistoryTest.php'];
|
|
}
|
|
if ($policySources) {
|
|
$tests = ['tests/Feature/SupplySuggestionsTest.php'];
|
|
}
|
|
if ($correctionSources) {
|
|
$tests = ['tests/Feature/InventoryCorrectionPaginationTest.php'];
|
|
}
|
|
if ($supplyLineSources) {
|
|
$tests = ['tests/Feature/SupplyCoordinationTest.php'];
|
|
}
|
|
if ($coordinationFilters || $coordinationExport) {
|
|
$tests = ['tests/Feature/SupplyCoordinationTest.php'];
|
|
}
|
|
if ($taskDeadlines) {
|
|
$tests = ['tests/Feature/WorkflowTaskInboxTest.php', 'tests/Feature/SupplyCoordinationTest.php'];
|
|
}
|
|
if ($noticeConsultation) {
|
|
$tests = ['tests/Feature/InternalRequisitionCandidateTest.php'];
|
|
}
|
|
if ($agreementSources) {
|
|
$tests = ['tests/Feature/SupplyPaginationTest.php'];
|
|
}
|
|
if ($mfaFoundation) {
|
|
$tests = ['tests/Feature/Auth/TwoFactorFoundationTest.php'];
|
|
}
|
|
if ($mfaManagement) {
|
|
$tests = ['tests/Feature/Settings/TwoFactorManagementTest.php'];
|
|
}
|
|
if ($mfaEvaluation) {
|
|
$tests = ['tests/Feature/Auth/MfaEvaluationBoundaryTest.php'];
|
|
}
|
|
if ($peopleDossier) {
|
|
$tests = ['tests/Feature/PeopleHiringTest.php'];
|
|
}
|
|
if ($auditMetadata) {
|
|
$tests = ['tests/Feature/AuditMetadataBlockTest.php'];
|
|
}
|
|
if ($equipmentIntegration || $equipmentWarehouse) {
|
|
$tests = ['tests/Feature/EquipmentSparePartsTest.php'];
|
|
}
|
|
if ($receivedAvailability) {
|
|
$tests = ['tests/Feature/CandidateSubmissionsTest.php'];
|
|
}
|
|
if ($candidateTerritory) {
|
|
$tests = ['tests/Feature/CandidateSubmissionsTest.php'];
|
|
}
|
|
if ($candidateAdditionalGeography) {
|
|
$tests = ['tests/Feature/CandidateGeographyTest.php', 'tests/Feature/CandidateSubmissionsTest.php'];
|
|
}
|
|
if ($candidateGeography) {
|
|
$tests = ['tests/Feature/CandidateGeographyTest.php', 'tests/Feature/CandidateSubmissionsTest.php'];
|
|
}
|
|
if ($operationalGeography) {
|
|
$tests = ['tests/Feature/OperationalGeographyTest.php'];
|
|
}
|
|
if ($geography) {
|
|
$tests = ['tests/Feature/AdministrativeGeographyTest.php', 'tests/Feature/AdministrativeGeographyReviewTest.php'];
|
|
}
|
|
if ($issuedCancellation) {
|
|
$tests = ['tests/Feature/IssuedOrderCancellationTest.php'];
|
|
}
|
|
if ($requisitionAudit || $auditPagination) {
|
|
$tests = ['tests/Feature/InternalRequisitionCandidateTest.php'];
|
|
}
|
|
if ($neededAttention || $approvedAttention) {
|
|
$tests = ['tests/Feature/ProcurementReportsTest.php'];
|
|
}
|
|
if ($replenishmentHealth) {
|
|
$tests = ['tests/Feature/ReplenishmentHealthTest.php'];
|
|
}
|
|
if ($receiptBlock) {
|
|
$tests = ['tests/Feature/ReceiptDocumentBlockTest.php'];
|
|
}
|
|
if ($receiptPrint) {
|
|
$tests = ['tests/Feature/ReceiptPrintBlockTest.php'];
|
|
}
|
|
if ($receiptHealth) {
|
|
$tests = ['tests/Feature/ReceiptDocumentHealthTest.php'];
|
|
}
|
|
if ($operationalHealth) {
|
|
$tests = ['tests/Feature/OperationalHealthTest.php'];
|
|
}
|
|
if ($warehouseDeliveryPrint || $warehouseRequestPrint || $warehouseRequestExport) {
|
|
$tests = ['tests/Feature/WarehouseFulfilmentTest.php'];
|
|
}
|
|
$filter = match (true) {
|
|
$receivedAvailability => ['--filter=received availability projection'],
|
|
$candidateTerritory => ['--filter=candidate territory'],
|
|
$issuedCancellation => ['--filter=issued cancellation'],
|
|
$equipmentWarehouse => ['--filter=equipment warehouse requests|reuses the stock operation'],
|
|
$equipmentIntegration => ['--filter=equipment integration block'],
|
|
$auditMetadata => ['--filter=audit metadata block'],
|
|
$peopleDossier => ['--filter=people dossier block'],
|
|
$mfaEvaluation => ['--filter=mfa evaluation boundary'],
|
|
$operationalHealth => ['--filter=operational health'],
|
|
$receiptHealth => ['--filter=receipt document health'],
|
|
$warehouseRequestExport => ['--filter=warehouse request export'],
|
|
$warehouseRequestPrint => ['--filter=warehouse request print'],
|
|
$warehouseDeliveryPrint => ['--filter=warehouse delivery print block'],
|
|
$receiptPrint => ['--filter=receipt print block'],
|
|
$receiptBlock => ['--filter=receipt document block'],
|
|
$replenishmentHealth => ['--filter=replenishment health'],
|
|
$approvedAttention => ['--filter=approved attention reports'],
|
|
$neededAttention => ['--filter=needed attention report'],
|
|
$auditPagination => ['--filter=requisition audit export'],
|
|
$requisitionAudit => ['--filter=requisition audit export'],
|
|
$mfaManagement => ['--filter=mfa management'],
|
|
$mfaFoundation => ['--filter=two factor foundation'],
|
|
$agreementSources => ['--filter=agreement source search'],
|
|
$noticeConsultation => ['--filter=notice consultation'],
|
|
$taskDeadlines => ['--filter=task deadline'],
|
|
$coordinationExport => ['--filter=coordination export'],
|
|
$coordinationFilters => ['--filter=coordination record filters'],
|
|
$supplyLineSources => ['--filter=supply line search'],
|
|
$correctionSources => ['--filter=older selected|correction source search'],
|
|
$policySources => ['--filter=policy source search'],
|
|
$historySources => ['--filter=history source search'],
|
|
$reservationSources => ['--filter=reservation source search|coordination task returns'],
|
|
$supplyPagination => ['--filter=supply pagination'],
|
|
$lotPagination => ['--filter=lot pagination'],
|
|
$lotQualityTasks => ['--filter=lot quality task'],
|
|
$coordinationTasks => ['--filter=coordination task'],
|
|
$qualityTasks => ['--filter=quality task|response task'],
|
|
$improvementTasks => ['--filter=improvement task'],
|
|
$lotTasks => ['--filter=lot setup task'],
|
|
$inventoryTasks => ['--filter=disposition task|reversal task'],
|
|
$dispositionTasks => ['--filter=disposition task'],
|
|
$supplierTasks => ['--filter=supplier task'],
|
|
$alternativeTasks => ['--filter=alternative task'],
|
|
$suggestionTasks => ['--filter=suggestion task'],
|
|
$historyTasks => ['--filter=history task|command readiness'],
|
|
$supplyTasks => ['--filter=supply task'],
|
|
$formalTasks => ['--filter=formal task'],
|
|
$quotationTasks => ['--filter=quotation tasks'],
|
|
$awardTasks => ['--filter=task inbox'],
|
|
default => [],
|
|
};
|
|
$process = new Process([PHP_BINARY, 'vendor/bin/pest', '--compact', ...$tests, ...$filter], dirname(__DIR__), $environment, timeout: $equipmentWarehouse || $operationalHealth || $warehouseRequestExport || $receivedAvailability || $candidateTerritory || $candidateAdditionalGeography || $candidateGeography || $operationalGeography || $geography || $financeIntegration || $awardTasks || $quotationTasks || $organizationPreview || $pilotAccessProposal || $formalTasks || $supplyTasks || $historyTasks || $suggestionTasks || $alternativeTasks || $supplierTasks || $dispositionTasks || $inventoryTasks || $lotTasks || $improvementTasks || $qualityTasks || $coordinationTasks || $lotQualityTasks || $lotPagination || $supplyPagination || $correctionPagination || $reservationSources || $historySources || $policySources || $correctionSources || $supplyLineSources || $noticeConsultation || $peopleDossier ? 300 : 120);
|
|
$process->run();
|
|
echo $process->getOutput();
|
|
if (! $process->isSuccessful()) {
|
|
throw new RuntimeException('Prova PostgreSQL de contexto não aprovada.');
|
|
}
|
|
$races = [];
|
|
if ($issuedCancellation) {
|
|
require __DIR__.'/../tests/Support/order-cancellation-pg-races.php';
|
|
$races = orderCancellationRaces($environment, $config, $database);
|
|
}
|
|
if ($inbox) {
|
|
require __DIR__.'/../tests/Support/inbox-pg-races.php';
|
|
$races = inboxPgRaces($environment, $config, $database);
|
|
}
|
|
if ($warehouse) {
|
|
config(['database.connections.warehouse_proof_read' => [...$config, 'database' => $database, 'url' => null]]);
|
|
$reader = DB::connection('warehouse_proof_read');
|
|
foreach (['same_operation', 'different_operations'] as $scenario) {
|
|
$fixture = new Process([PHP_BINARY, 'tests/Concurrency/warehouse-worker.php', 'prepare'], dirname(__DIR__), $environment, timeout: 20);
|
|
$fixture->mustRun();
|
|
$data = json_decode($fixture->getOutput(), true, 32, JSON_THROW_ON_ERROR);
|
|
$first = new Process([PHP_BINARY, 'tests/Concurrency/warehouse-worker.php', 'hold', json_encode($data, JSON_THROW_ON_ERROR)], dirname(__DIR__), $environment, timeout: 20);
|
|
$first->start();
|
|
$deadline = microtime(true) + 5;
|
|
while ($first->isRunning() && ! str_contains($first->getOutput(), 'locked') && microtime(true) < $deadline) {
|
|
usleep(20000);
|
|
}
|
|
if (! str_contains($first->getOutput(), 'locked')) {
|
|
throw new RuntimeException('Primeiro executor sem bloqueio demonstrado.');
|
|
}
|
|
if ($scenario === 'different_operations') {
|
|
$data['input']['operation_key'] = (string) Str::uuid();
|
|
}
|
|
$second = new Process([PHP_BINARY, 'tests/Concurrency/warehouse-worker.php', 'run', json_encode($data, JSON_THROW_ON_ERROR)], dirname(__DIR__), $environment, timeout: 20);
|
|
$second->start();
|
|
$blocked = false;
|
|
$deadline = microtime(true) + 5;
|
|
while ($first->isRunning() && microtime(true) < $deadline) {
|
|
$blocked = (bool) $reader->selectOne("select exists(select 1 from pg_stat_activity where datname=? and wait_event_type='Lock') as blocked", [$database])->blocked;
|
|
if ($blocked) {
|
|
break;
|
|
} usleep(20000);
|
|
}
|
|
$first->wait();
|
|
$second->wait();
|
|
$a = json_decode(trim(str_replace('locked', '', $first->getOutput())), true, 16, JSON_THROW_ON_ERROR);
|
|
$b = json_decode(trim($second->getOutput()), true, 16, JSON_THROW_ON_ERROR);
|
|
if (! $blocked || $a['result'] !== 'saved' || ($scenario === 'same_operation' ? ($b['result'] !== 'saved' || $a['event'] !== $b['event']) : $b['result'] !== 'rejected')) {
|
|
throw new RuntimeException('Disputa não preservou entrega única.');
|
|
}
|
|
if ((int) $reader->table('warehouse_request_events')->where('warehouse_request_id', $data['request'])->where('action', 'deliver')->count() !== 1
|
|
|| (int) $reader->table('stock_movements')->where('tenant_id', $data['tenant'])->where('stock_item_id', $data['stock'])->where('warehouse_id', $data['warehouse'])->sum('quantity_milli') !== 6000) {
|
|
throw new RuntimeException('Movimento ou entrega duplicados.');
|
|
}
|
|
$races[] = ['scenario' => $scenario, 'real_lock_contention' => true, 'result' => 'passed'];
|
|
}
|
|
$fixture = new Process([PHP_BINARY, 'tests/Concurrency/replenishment-worker.php', 'prepare'], dirname(__DIR__), $environment, timeout: 30);
|
|
$fixture->mustRun();
|
|
$data = json_decode($fixture->getOutput(), true, 32, JSON_THROW_ON_ERROR);
|
|
$first = new Process([PHP_BINARY, 'tests/Concurrency/replenishment-worker.php', 'hold', json_encode($data, JSON_THROW_ON_ERROR)], dirname(__DIR__), $environment, timeout: 30);
|
|
$first->start();
|
|
$deadline = microtime(true) + 5;
|
|
while ($first->isRunning() && ! str_contains($first->getOutput(), 'locked') && microtime(true) < $deadline) {
|
|
usleep(20000);
|
|
}
|
|
if (! str_contains($first->getOutput(), 'locked')) {
|
|
throw new RuntimeException('Falta bloqueio da compra automática.');
|
|
}
|
|
$second = new Process([PHP_BINARY, 'tests/Concurrency/replenishment-worker.php', 'run', json_encode($data, JSON_THROW_ON_ERROR)], dirname(__DIR__), $environment, timeout: 30);
|
|
$second->start();
|
|
$blocked = false;
|
|
$deadline = microtime(true) + 5;
|
|
while ($first->isRunning() && microtime(true) < $deadline) {
|
|
$blocked = (bool) $reader->selectOne("select exists(select 1 from pg_stat_activity where datname=? and wait_event_type='Lock') as blocked", [$database])->blocked;
|
|
if ($blocked) {
|
|
break;
|
|
}usleep(20000);
|
|
}
|
|
$first->wait();
|
|
$second->wait();
|
|
if (! $first->isSuccessful() || ! $second->isSuccessful()) {
|
|
throw new RuntimeException('Falha na activação concorrente: '.$first->getErrorOutput().$second->getErrorOutput());
|
|
}
|
|
$a = json_decode(trim(str_replace('locked', '', $first->getOutput())), true, 32, JSON_THROW_ON_ERROR);
|
|
$b = json_decode(trim($second->getOutput()), true, 32, JSON_THROW_ON_ERROR);
|
|
if (! $blocked || $a['rule'] !== $b['rule'] || count($a['purchases']) !== 1 || $a['purchases'] !== $b['purchases'] || (int) $reader->table('requisitions')->where('tenant_id', $data['tenant'])->where('status', 'draft')->count() !== 1) {
|
|
throw new RuntimeException('A reposição concorrente duplicou o rascunho.');
|
|
}
|
|
$races[] = ['scenario' => 'automatic_purchase_activation_retry', 'real_lock_contention' => true, 'single_draft' => true, 'result' => 'passed'];
|
|
if ($pilot) {
|
|
$fixture = new Process([PHP_BINARY, 'tests/Concurrency/recovery-worker.php', 'prepare'], dirname(__DIR__), $environment, timeout: 30);
|
|
$fixture->mustRun();
|
|
$data = json_decode($fixture->getOutput(), true, 32, JSON_THROW_ON_ERROR);
|
|
$before = $reader->table('replenishment_work')->where('id', $data['work'])->sole();
|
|
if ($before->generation <= $before->processed_generation || $reader->table('requisitions')->where('tenant_id', $data['tenant'])->where('status', 'draft')->exists()) {
|
|
throw new RuntimeException('A prova não deixou sinal durável sem rascunho após interrupção.');
|
|
}
|
|
$first = new Process([PHP_BINARY, 'tests/Concurrency/recovery-worker.php', 'hold', json_encode($data, JSON_THROW_ON_ERROR)], dirname(__DIR__), $environment, timeout: 30);
|
|
$first->start();
|
|
$deadline = microtime(true) + 5;
|
|
while ($first->isRunning() && ! str_contains($first->getOutput(), 'locked') && microtime(true) < $deadline) {
|
|
usleep(20000);
|
|
}
|
|
if (! str_contains($first->getOutput(), 'locked')) {
|
|
throw new RuntimeException('Primeiro recuperador sem bloqueio.');
|
|
}
|
|
$second = new Process([PHP_BINARY, 'tests/Concurrency/recovery-worker.php', 'run', json_encode($data, JSON_THROW_ON_ERROR)], dirname(__DIR__), $environment, timeout: 30);
|
|
$second->start();
|
|
$blocked = false;
|
|
$deadline = microtime(true) + 5;
|
|
while ($first->isRunning() && microtime(true) < $deadline) {
|
|
$blocked = (bool) $reader->selectOne("select exists(select 1 from pg_stat_activity where datname=? and wait_event_type='Lock') as blocked", [$database])->blocked;
|
|
if ($blocked) {
|
|
break;
|
|
}usleep(20000);
|
|
}
|
|
$first->wait();
|
|
$second->wait();
|
|
if (! $first->isSuccessful() || ! $second->isSuccessful()) {
|
|
throw new RuntimeException('Recuperação concorrente falhou.');
|
|
}
|
|
$a = json_decode(trim(str_replace('locked', '', $first->getOutput())), true, 32, JSON_THROW_ON_ERROR);
|
|
$b = json_decode(trim($second->getOutput()), true, 32, JSON_THROW_ON_ERROR);
|
|
$after = $reader->table('replenishment_work')->where('id', $data['work'])->sole();
|
|
if (! $blocked || $a['result'] !== 'saved' || $b['result'] !== 'saved' || $a['authenticated'] || $b['authenticated'] || count($a['drafts']) !== 1 || $a['drafts'] !== $b['drafts'] || $after->generation !== $after->processed_generation) {
|
|
throw new RuntimeException('A recuperação perdeu ou duplicou a compra.');
|
|
}
|
|
$races[] = ['scenario' => 'abrupt_exit_after_physical_commit_then_two_recovery_workers', 'real_lock_contention' => true, 'single_draft' => true, 'no_authenticated_human' => true, 'result' => 'passed'];
|
|
}
|
|
DB::purge('warehouse_proof_read');
|
|
}
|
|
$proofName = match (true) {
|
|
$receivedAvailability => 'received-availability-pg',
|
|
$candidateTerritory => 'candidate-territory-pg',
|
|
$candidateAdditionalGeography => 'candidate-additional-geography-pg',
|
|
$candidateGeography => 'candidate-geography-pg',
|
|
$operationalGeography => 'operational-geography-pg',
|
|
$geography => 'administrative-geography-review-pg',
|
|
$issuedCancellation => 'issued-order-cancellation-pg',
|
|
$equipmentWarehouse => 'equipment-warehouse-pg',
|
|
$equipmentIntegration => 'equipment-integration-pg',
|
|
$auditMetadata => 'audit-metadata-pg',
|
|
$peopleDossier => 'people-dossier-pg',
|
|
$mfaEvaluation => 'mfa-evaluation-boundary-pg',
|
|
$operationalHealth => 'operational-health-pg',
|
|
$receiptHealth => 'receipt-document-health-pg',
|
|
$warehouseRequestExport => 'warehouse-request-export-pg',
|
|
$warehouseRequestPrint => 'warehouse-request-print-pg',
|
|
$warehouseDeliveryPrint => 'warehouse-delivery-print-pg',
|
|
$receiptPrint => 'receipt-print-pg',
|
|
$receiptBlock => 'receipt-block-pg',
|
|
$replenishmentHealth => 'replenishment-health-pg',
|
|
$approvedAttention => 'approved-attention-pg',
|
|
$neededAttention => 'needed-attention-pg',
|
|
$auditPagination => 'audit-pagination-pg',
|
|
$requisitionAudit => 'requisition-audit-pg',
|
|
$mfaManagement => 'mfa-management-pg',
|
|
$mfaFoundation => 'mfa-foundation-pg',
|
|
$agreementSources => 'agreement-source-pg',
|
|
$noticeConsultation => 'notice-consultation-pg',
|
|
$taskDeadlines => 'task-deadlines-pg',
|
|
$coordinationExport => 'coordination-export-pg',
|
|
$coordinationFilters => 'coordination-filters-pg',
|
|
$supplyLineSources => 'supply-line-source-pg',
|
|
$correctionSources => 'correction-source-pg',
|
|
$policySources => 'policy-source-pg',
|
|
$historySources => 'history-source-pg',
|
|
$reservationSources => 'reservation-source-pg',
|
|
$correctionPagination => 'inventory-pagination-pg',
|
|
$supplyPagination => 'supply-pagination-pg',
|
|
$lotPagination => 'lot-pagination-pg',
|
|
$lotQualityTasks => 'lot-quality-task-integration-pg',
|
|
$coordinationTasks => 'coordination-task-integration-pg',
|
|
$qualityTasks => 'supplier-quality-task-integration-pg',
|
|
$improvementTasks => 'supplier-improvement-task-integration-pg',
|
|
$lotTasks => 'lot-task-integration-pg',
|
|
$inventoryTasks => 'inventory-task-integration-pg',
|
|
$dispositionTasks => 'disposition-task-integration-pg',
|
|
$supplierTasks => 'supplier-task-integration-pg',
|
|
$alternativeTasks => 'alternative-task-integration-pg',
|
|
$suggestionTasks => 'suggestion-task-integration-pg',
|
|
$historyTasks => 'history-task-integration-pg',
|
|
$supplyTasks => 'supply-task-integration-pg',
|
|
$formalTasks => 'formal-task-integration-pg',
|
|
$pilotAccessProposal => 'pilot-access-proposal-pg',
|
|
$organizationPreview => 'organization-activation-preview-pg',
|
|
$quotationTasks => 'quotation-task-integration-pg',
|
|
$awardTasks => 'award-task-integration-pg',
|
|
$financeIntegration => 'finance-integration-pg',
|
|
$taskInbox => 'workflow-task-inbox-pg',
|
|
$internalTrace => 'internal-supply-integration-pg',
|
|
$transport => 'warehouse-http-source-pg',
|
|
$inbox => 'warehouse-inbox-pg',
|
|
$exchange => 'warehouse-exchange-pg',
|
|
$readiness => 'warehouse-pilot-readiness-pg',
|
|
$workdesk => 'warehouse-workdesk-pg',
|
|
$pilot => 'replenishment-pilot-pg',
|
|
$warehouse => 'warehouse-drafts-pg',
|
|
$workflow => 'workflow-notices-pg',
|
|
default => 'work-context-pg',
|
|
};
|
|
file_put_contents(dirname(__DIR__).'/docs/proofs/'.$proofName.'.json', json_encode(['checked_at' => gmdate('c'), 'database' => 'disposable PostgreSQL', 'result' => trim($process->getOutput()), 'races' => $races, 'persistent_data_changed' => false, 'database_removed_in_finally' => true], JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
|
|
} finally {
|
|
$admin->select('select pg_terminate_backend(pid) from pg_stat_activity where datname = ? and pid <> pg_backend_pid()', [$database]);
|
|
$admin->statement('DROP DATABASE "'.$database.'"');
|
|
}
|