30 lines
3.2 KiB
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";
|