Files
SGF/scripts/service-fiscal-concurrency.php

30 lines
3.2 KiB
PHP

<?php
use Illuminate\Support\Facades\DB;
use Symfony\Component\Process\Process;
require __DIR__.'/../vendor/autoload.php';
$app=require __DIR__.'/../bootstrap/app.php'; $app->make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap();
\App\Shared\Testing\TestingDatabaseGuard::assertSafe();
$f=json_decode(file_get_contents('C:/laragon/data/sgf-tempo/secrets/browser-fixture.json'),true,32,JSON_THROW_ON_ERROR);
$context=app(\App\Shared\Context\CompanyContext::class);
$old=DB::table('sf_proposals')->where('draft_id',$f['fiscal_draft'])->orderByDesc('version')->first();
if(!$old) throw new RuntimeException('Synthetic temporal fixture required.');
$data=json_decode($old->data,true,32,JSON_THROW_ON_ERROR); $data['evidence']='New synthetic temporal version for overlapping process proof.';
$context->selectFor($f['manager_id'],$f['tenant'],$f['company'],$f['unit']);
$temporal=app(\App\Modules\Services\ServiceFiscal::class)->propose($f['manager_id'],['kind'=>'temporal','identifier'=>$old->identifier,'previous_id'=>$old->id,'data'=>$data]);
$context->selectFor($f['reader_id'],$f['tenant'],$f['company'],$f['unit']);
app(\App\Modules\Services\ServiceFiscal::class)->decide($temporal['id'],$f['reader_id'],['version'=>$temporal['version'],'decision'=>'approved','reason'=>'Synthetic concurrent proof only.']);
$profile=DB::table('sf_proposals')->where('id',$f['fiscal_profile_proposal'])->first();
$input=['actor'=>$f['manager_id'],'tenant'=>$f['tenant'],'company'=>$f['company'],'unit'=>$f['unit'],'draft'=>$f['fiscal_draft'],'command'=>['profile_proposal_id'=>$profile->id,'profile_version'=>$profile->version,'temporal_proposal_id'=>$temporal['id'],'temporal_version'=>$temporal['version'],'draft_version'=>1,'preparation_version'=>1]];
$before=DB::table('service_calculation_results')->where('billing_draft_id',$input['draft'])->count();
$a=new Process([PHP_BINARY,'scripts/service-fiscal-worker.php','--env=testing'],base_path(),null,json_encode($input+['pause'=>true]));
$b=new Process([PHP_BINARY,'scripts/service-fiscal-worker.php','--env=testing'],base_path(),null,json_encode($input));
$a->start(); if(!$a->waitUntil(fn($type,$buffer)=>str_contains($buffer,'LOCK_HELD'))) throw new RuntimeException('No overlap established.');
$b->start(); $a->wait(); $b->wait(); $results=[];
foreach([$a,$b] as $worker) {
if(!$worker->isSuccessful()) throw new RuntimeException('Fiscal worker failed; no output containing input disclosed.');
$lines=array_values(array_filter(explode("\n",trim($worker->getOutput())),fn($s)=>str_starts_with($s,'{'))); $results[]=json_decode(end($lines),true,32,JSON_THROW_ON_ERROR);
}
if($results[0]['id']!==$results[1]['id'] || $results[0]['replayed'] || !$results[1]['replayed'] || DB::table('service_calculation_results')->where('billing_draft_id',$input['draft'])->count()!==$before+1) throw new RuntimeException('Concurrent duplicate or unexpected result.');
file_put_contents(base_path('docs/proofs/service-fiscal-concurrency.json'),json_encode(['database'=>'sgf_tempo_test','overlapping_php_processes'=>2,'lock_overlap_observed'=>true,'one_insert_one_replay'=>true,'prior_history_preserved'=>true],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));
echo "Fiscal concurrency proof: two overlapping processes, one insertion and one replay; history preserved.\n";