Files
Investidor-V2/tools/portal-ab-remaining-persistence-qa.php
T
nala 287e991486
tests / ci (push) Waiting to run
oportunidades 7
2026-10-13 11:40:29 +02:00

82 lines
5.7 KiB
PHP

<?php
// Guarded read-only trace of the existing, explicitly synthetic A/B dossiers. No credentials or ciphertexts in evidence.
use App\Models\SimulationFormDraft;
use App\Services\VirtualDesk;
use Illuminate\Contracts\Console\Kernel;
use Illuminate\Support\Facades\DB;
require dirname(__DIR__).'/vendor/autoload.php';
require __DIR__.'/portal-qa-environment.php';
$target = portalQaEnvironment();
$app = require dirname(__DIR__).'/bootstrap/app.php';
$app->make(Kernel::class)->bootstrap();
portalQaAssertTarget($target);
DB::beginTransaction();
DB::statement('SET TRANSACTION READ ONLY');
$dir = storage_path('app/private/investmoz-v2/portal-audit');
$prior = json_decode(file_get_contents($dir.'/investor-ab-chromium-20261010.json'), true, 512, JSON_THROW_ON_ERROR);
$proof = ['captured_at' => gmdate('c'), 'target' => $target, 'read_only' => true, 'active_writes' => 0, 'scenarios' => []];
foreach ($prior['scenarios'] as $name => $entry) {
$case = DB::table('desk_cases')->where('id', $entry['id'])->firstOrFail();
abort_unless((int) $case->user_id === 18 && $case->simulation_id === $entry['draft_id'], 403);
$desk = app(VirtualDesk::class);
$payload = $desk->decode($case->payload);
$path = $payload['investor_path'];
$result = collect($path['results'])->firstWhere('id', $entry['result_id']);
abort_unless($result && $result['draft_id'] === $case->simulation_id, 422);
$draft = SimulationFormDraft::findOrFail($entry['draft_id']);
abort_unless((int) $draft->owner_user_id === 18, 403);
$economic = SimulationFormDraft::decryptSnapshot($draft->economic_snapshot);
$request = DB::table('desk_requests')->where('id', $entry['request_id'])->where('case_id', $case->id)->firstOrFail();
$requestPayload = $desk->decode($request->payload);
$task = DB::table('tasks')->where('id', $entry['task_id'])->where('desk_case_id', $case->id)->firstOrFail();
abort_unless($task->guidance_key === 'path:S02' && $task->status === 'done', 422);
$steps = DB::table('journey_steps')->where('journey_id', $case->journey_id)->whereIn('step_code', ['S02', 'S10'])->get();
$stepProof = [];
foreach ($steps as $step) {
$detail = $desk->decode($step->path_detail);
$stepProof[$step->step_code] = ['state' => $detail['state'], 'recorded_at' => $detail['recorded_at'] ?? null, 'scope' => $detail['scope'] ?? null];
}
$timeline = [];
$requestPrefill = [];
foreach (DB::table('desk_events')->where('case_id', $case->id)->orderBy('event_order')->get() as $event) {
$data = $desk->decode($event->payload);
if ($event->request_id === $request->id && in_array($event->action, ['request_prefill_prepared', 'request_prefill_reused'], true)) {
$requestPrefill[] = ['action' => $event->action, 'order' => $event->event_order, 'created_at' => $event->created_at,
'draft_id' => $data['draft_id'] ?? null, 'draft_version' => $data['draft_version'] ?? null, 'fields' => $data['fields'] ?? []];
}
if (($event->action === 'path_stage_recorded' && in_array($data['code'] ?? '', ['S02', 'S10'], true))
|| (in_array($event->action, ['path_task_accepted', 'guidance_task_updated'], true) && ($data['task_id'] ?? '') === $task->id)) {
$timeline[] = ['order' => $event->event_order, 'action' => $event->action, 'actor_id' => $event->actor_id,
'created_at' => $event->created_at, 'code' => $data['code'] ?? null, 'state' => $data['state'] ?? null,
'task_status' => $data['status'] ?? null, 'task_id' => $data['task_id'] ?? null];
}
}
if ($name === 'B') {
$wait = collect($timeline)->first(fn ($e) => $e['code'] === 'S10' && $e['state'] === 'waiting_external');
$done = collect($timeline)->first(fn ($e) => $e['task_status'] === 'done');
abort_unless($wait && $done && $wait['order'] < $done['order'] && $stepProof['S10']['state'] === 'waiting_external', 422);
}
$proof['scenarios'][$name] = ['case_id' => $case->id, 'case_version' => $case->version, 'owner_id' => $case->user_id,
'facts' => $path['facts'], 'steps' => $stepProof, 'payload_sha256' => hash('sha256', $case->payload),
'draft_id' => $draft->id, 'draft_version' => $draft->version_no, 'draft_snapshot_sha256' => $draft->snapshot_sha256,
'project_title' => $economic['project_title'] ?? null, 'result_id' => $result['id'], 'result_draft_id' => $result['draft_id'],
'tool_summary_length' => strlen($economic['project_summary'] ?? ''), 'request_description_length' => strlen($requestPayload['description'] ?? ''),
'request_payload_fields' => array_keys($requestPayload), 'request_created_at' => $request->created_at,
'result_draft_version' => $result['draft_version'], 'result_source_key' => $result['source_key'],
'request_id' => $request->id, 'request_status' => $request->status, 'request_version' => $request->version,
'request_country' => $requestPayload['country'] ?? null, 'request_origin' => $requestPayload['declared_origin_country_code'] ?? null,
'task_id' => $task->id, 'task_status' => $task->status, 'task_guidance_key' => $task->guidance_key,
'task_version' => $task->version_no, 'timeline' => $timeline];
$proof['scenarios'][$name]['request_prefill_provenance'] = $requestPrefill;
}
DB::rollBack();
$proof['result'] = 'passed_read_only_existing_path';
$file = $dir.'/investor-ab-remaining-persistence-qa.json';
if (is_file($file)) {
copy($file, $dir.'/investor-ab-remaining-persistence-qa-'.gmdate('Ymd_His').'.json');
}
file_put_contents($file, json_encode($proof, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
echo json_encode(['result' => $proof['result'], 'target' => $target, 'read_only' => true, 'scenarios' => array_keys($proof['scenarios'])]).PHP_EOL;