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

95 lines
8.4 KiB
PHP

<?php
// Only read the explicitly guarded QA database; do not alter or publish records.
use App\Models\ExistingBusiness;
use App\Models\Opportunity;
use App\Models\SimulationFormDraft;
use App\Models\User;
use App\Services\DeclaredOriginStatistics;
use App\Services\VirtualDesk;
use Illuminate\Contracts\Console\Kernel;
use Illuminate\Support\Facades\Crypt;
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();
$identity = portalQaAssertTarget($target);
$dir = storage_path('app/private/investmoz-v2/portal-audit');
$load = function (string $name) use ($dir) {
$r = json_decode(file_get_contents($dir.'/'.$name), true, 512, JSON_THROW_ON_ERROR);
throw_unless($r['result'] === 'passed', RuntimeException::class, 'Completed browser proof required: '.$name);
return $r;
};
$entity = $load('consumer-entity-chromium-20261010.json');
$desk = $load('consumer-stats-chromium-20261010.json');
$business = $load('consumer-business-chromium-20261010.json');
$territory = $load('consumer-structures-deep-chromium-20261010.json');
$workflow = $load('consumer-workflow-chromium-20261010.json');
$proof = DB::transaction(function () use ($identity, $dir, $entity, $desk, $business, $territory, $workflow) {
DB::statement('SET TRANSACTION READ ONLY');
$proof = ['at' => gmdate('c'), 'target' => $identity, 'read_only' => true, 'active_writes' => 0];
$c = DB::table('investment_company_declarations')->where('id', $entity['entity']['id'])->firstOrFail();
$p = json_decode(Crypt::decryptString($c->payload), true, 512, JSON_THROW_ON_ERROR);
throw_unless((int) $c->user_id === 18 && $p['incorporation_country_code'] === 'FR' && (int) $c->version === $entity['entity']['version'], RuntimeException::class, 'Entity ownership/version/country diverged');
$proof['entity'] = ['id' => $c->id, 'owner' => 18, 'version' => $c->version, 'incorporation' => 'FR', 'residence' => 'PT', 'scope' => 'private company declaration, not organization master'];
$request = DB::table('desk_requests')->where('id', $desk['stats']['id'])->firstOrFail();
$payload = app(VirtualDesk::class)->decode($request->payload);
$submitted = DB::table('desk_events')->where('request_id', $request->id)->where('action', 'submitted')->count();
$revisions = DB::table('desk_revisions')->where('request_id', $request->id)->count();
throw_unless($submitted === 1 && $revisions === 1 && $request->protocol === $desk['stats']['protocol'] && (int) $request->version === (int) $desk['stats']['version'] && $payload['country'] === 'PT' && $payload['declared_origin_country_code'] === 'ZA', RuntimeException::class, 'Desk repetition changed version/history');
throw_unless(DB::table('desk_requests')->where('protocol', $request->protocol)->count() === 1, RuntimeException::class, 'Duplicate protocol');
$proof['desk'] = ['id' => $request->id, 'status' => $request->status, 'version' => $request->version, 'protocol' => $request->protocol, 'country' => 'PT', 'origin' => 'ZA', 'submitted_events' => $submitted, 'revisions' => $revisions, 'one_protocol' => true];
$b = ExistingBusiness::findOrFail($business['business']['id']);
$transitions = DB::table('business_workflow_transitions')->where('existing_business_id', $b->id)->where('to_status', 'submitted')->count();
throw_unless($b->status === 'submitted' && $transitions === 1 && $b->version_no === $business['business']['version'] && $b->payload['country'] === 'PT' && $b->payload['declared_origin_country_code'] === 'FR', RuntimeException::class, 'Business submission/replay diverged');
$proof['business'] = ['id' => $b->id, 'status' => $b->status, 'version' => $b->version_no, 'code' => $b->code, 'country' => 'PT', 'origin' => 'FR', 'submitted_transitions' => $transitions];
foreach (['projects' => 'projects', 'clusters' => 'economic_clusters', 'corridors' => 'development_corridors'] as $kind => $table) {
$entry = $territory['structures'][$kind];
$r = DB::table($table)->where('id', $entry['id'])->firstOrFail();
throw_unless($r->primary_territory_id === $entry['locality']['id'], RuntimeException::class, 'Saved deepest territory diverged');
$proof['territories'][$kind] = ['id' => $r->id, 'code' => $r->code, 'primary_territory_id' => $r->primary_territory_id, 'version' => $r->territory_version, 'geometry' => $entry['geometry']];
}
$o = Opportunity::findOrFail($workflow['workflow']['id']);
$context = $o->workflow_context;
$events = $context['events'];
throw_unless($o->status === 'published' && (int) $context['approval']['actor_id'] === 24 && (int) $context['publication']['actor_id'] === 25 && (int) $o->owner_user_id === 21, RuntimeException::class, 'Independent approval/publication diverged');
foreach ([['submit', 21], ['request_correction', 24], ['approve', 24], ['publish', 25]] as [$action,$actor]) {
throw_unless(collect($events)->contains(fn ($e) => $e['action'] === $action && (int) $e['actor_id'] === $actor), RuntimeException::class, 'Workflow event missing');
}
throw_unless($context['approval']['hash'] === $context['publication']['hash'], RuntimeException::class, 'Published version differs from approval');
$proof['workflow'] = ['id' => $o->id, 'category' => $o->opportunity_category, 'owner' => 21, 'approver' => 24, 'publisher' => 25, 'status' => $o->status, 'approval_publication_same_hash' => true, 'events' => array_map(fn ($e) => ['actor' => $e['actor_id'], 'action' => $e['action'], 'from' => $e['from'], 'to' => $e['to']], $events), 'visibility' => $context['publication']['visibility']];
foreach (['opportunity_documents' => $workflow['workflow']['document'], 'opportunity_images' => $workflow['workflow']['image']] as $table => $id) {
$r = DB::table($table)->where('id', $id)->firstOrFail();
$d = DB::table('documents')->where('id', $r->document_id)->firstOrFail();
throw_unless($d->malware_scan_status === 'clean' && $d->malware_engine === 'ClamAV' && trim($d->sha256) === $d->malware_scanned_sha256, RuntimeException::class, 'Own original scan missing');
$proof['workflow_files'][$table] = ['id' => $id, 'document_id' => $d->id, 'sha256' => trim($d->sha256), 'scan' => 'clean', 'engine' => $d->malware_engine];
if ($table === 'opportunity_images') {
foreach (json_decode($r->payload, true)['derivatives'] as $derived) {
$d = DB::table('documents')->where('id', $derived['document_id'])->firstOrFail();
throw_unless($d->malware_scan_status === 'clean' && $d->malware_engine === 'ClamAV' && trim($d->sha256) === $d->malware_scanned_sha256, RuntimeException::class, 'Own derivative scan missing');
$proof['workflow_files']['derivatives'][] = ['document_id' => $d->id, 'sha256' => trim($d->sha256), 'engine' => $d->malware_engine, 'scan' => 'clean'];
}
}
}
$old = json_decode(file_get_contents($dir.'/investor-ab-remaining-persistence-qa.json'), true, 512, JSON_THROW_ON_ERROR);
foreach ($old['scenarios'] as $name => $entry) {
$d = SimulationFormDraft::findOrFail($entry['draft_id']);
$request = DB::table('desk_requests')->where('id', $entry['request_id'])->firstOrFail();
$task = DB::table('tasks')->where('id', $entry['task_id'])->firstOrFail();
throw_unless($d->snapshot_sha256 === $entry['draft_snapshot_sha256'] && (int) $d->version_no === (int) $entry['draft_version'] && $request->status === $entry['request_status'] && (int) $request->version === (int) $entry['request_version'] && $task->status === $entry['task_status'] && (int) $task->version_no === (int) $entry['task_version'], RuntimeException::class, 'A/B original criterion changed');
$proof['ab_preserved'][$name] = ['draft_snapshot' => true, 'original_draft_request' => true, 'independent_task' => true];
}
$manager = User::findOrFail(14);
config(['portal.managers' => [$manager->email]]);
$proof['statistics'] = app(DeclaredOriginStatistics::class)->forManager($manager);
$proof['result'] = 'passed_consumer_persistence_read_only';
return $proof;
});
file_put_contents($dir.'/consumer-final-persistence-qa.json', json_encode($proof, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR));
echo json_encode(['result' => $proof['result'], 'target' => $identity, 'active_writes' => 0]).PHP_EOL;