make(Kernel::class)->bootstrap(); $identity = portalQaAssertTarget($target); config(['portal.profile_access_enabled' => true, 'portal.opportunity_specialized_drafts_enabled' => true]); $directory = $root.'/storage/app/private/investmoz-v2/portal-audit'; $actors = json_decode(file_get_contents($directory.'/profiles-v1-actors-private.json'), true, 512, JSON_THROW_ON_ERROR); $promoter = User::whereKey($actors['promoter']['id'])->firstOrFail(); throw_unless($promoter->id === 21 && str_ends_with($promoter->email, '@example.invalid'), RuntimeException::class, 'Synthetic QA actor mismatch'); $label = 'Promotor QA — conta sintética 21'; if ($promoter->name !== $label) { $before = $promoter->name; $promoter->name = $label; $promoter->save(); app(PortalProfileAccess::class)->audit($promoter, 'qa_fixture_labeled', ['actor_id' => $promoter->id, 'before' => $before, 'after' => $label, 'reason' => 'Identificação inequívoca do ensaio QA; sem alterar identidade/permissões']); } DB::beginTransaction(); DB::statement('SET TRANSACTION READ ONLY'); $proof = json_decode(file_get_contents($directory.'/opportunity-workflow-http-qa.json'), true, 512, JSON_THROW_ON_ERROR); $result = ['target' => $identity, 'captured_at' => gmdate('c'), 'active_writes' => 0, 'visual_pending' => true, 'promoter_qa' => ['id' => $promoter->id, 'name' => $promoter->name, 'mfa_confirmed' => $promoter->two_factor_confirmed_at !== null, 'contexts' => app(PortalProfileAccess::class)->navigation($promoter)['contexts']], 'categories' => [], 'five_phases_nineteen_steps_preserved' => true]; foreach ($proof['cases'] as $case) { $record = Opportunity::whereKey($case['id'])->firstOrFail(); $row = ['id' => $record->id, 'category' => $case['category'], 'working_state' => app(PortalProfileAccess::class)->state($record), 'publication_status' => $record->status]; try { $snapshot = app(OpportunityCanonicalContract::class)->validateSubmission($record); $row['canonical_validation'] = 'passed'; $row['payload_sha256'] = $snapshot['hash']; $row['resources'] = array_map('count', $snapshot['resources']); $row['country'] = array_map(fn ($l) => $l['payload']['country_code'], $snapshot['resources']['locations']); } catch (ValidationException $error) { $row['canonical_validation'] = 'blocked'; $row['missing'] = array_keys($error->errors()); } $result['categories'][] = $row; } $docIds = DB::table('opportunity_documents')->whereIn('opportunity_id', array_column($proof['cases'], 'id'))->pluck('document_id')->all(); $imageIds = DB::table('opportunity_images')->whereIn('opportunity_id', array_column($proof['cases'], 'id'))->pluck('document_id')->all(); $result['document_scans'] = DB::table('documents')->whereIn('id', [...$docIds, ...$imageIds])->get(['id', 'media_type', 'sha256', 'malware_scanned_sha256', 'malware_scan_status', 'malware_engine', 'storage_encryption']); $result['territories'] = ['registered' => DB::table('geo_locations')->whereNull('deleted_at')->count(), 'geometries' => DB::table('geo_geometries')->count(), 'visual' => 'pending']; $result['task_counts'] = DB::table('tasks')->where('source_system', 'opportunity-workflow')->selectRaw('status,count(*) as total')->groupBy('status')->pluck('total', 'status'); DB::rollBack(); file_put_contents($directory.'/opportunity-workflow-verification-qa.json', json_encode($result, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR)); echo json_encode(['target' => $identity, 'promoter_qa_id' => $promoter->id, 'categories' => array_count_values(array_column($result['categories'], 'canonical_validation')), 'active_writes' => 0, 'visual_pending' => true]).PHP_EOL;