proofDirectory = storage_path('framework/testing/profile-install-'.Str::uuid()); mkdir($this->proofDirectory, 0700, true); config(['portal_profiles.proof_directory' => $this->proofDirectory, 'portal.profile_access_enabled' => false]); } protected function tearDown(): void { foreach (glob($this->proofDirectory.'/*') ?: [] as $file) { unlink($file); } rmdir($this->proofDirectory); parent::tearDown(); } private function manager(): User { $user = $this->actor(); config(['portal.managers' => [$user->email]]); return $user; } private function actor(): User { return User::factory()->create(['two_factor_secret' => Crypt::encryptString('JBSWY3DPEHPK3PXP'), 'two_factor_confirmed_at' => now()]); } private function login(User $user, bool $mfa = true): static { return $this->actingAs($user)->withSession(['portal.mfa_actor' => $mfa ? $user->id : null, 'portal.mfa_at' => $mfa ? time() : 0]); } private function emptySchema(): void { (require database_path('migrations/'.PortalProfileInstallation::MIGRATION.'.php'))->down(); DB::table('migrations')->where('migration', PortalProfileInstallation::MIGRATION)->delete(); } private function data(): array { return ['hash' => $this->getJson('/management/profile-install')->assertOk()->json('hash'), 'confirm' => true, 'idempotency_key' => (string) Str::uuid(), 'reason' => 'Instalação sintética em base isolada de testes']; } private function readinessFixture(): void { // Synthetic files exercise validation; they are never deployment backup evidence. file_put_contents($this->proofDirectory.'/dump', 'unit-fixture-not-a-real-dump'); file_put_contents($this->proofDirectory.'/files', 'unit-fixture-not-real-files'); $target = DB::selectOne('SELECT current_database() AS database,inet_server_port() AS port'); file_put_contents($this->proofDirectory.'/readiness.json', json_encode(['verified_at' => gmdate('c'), 'target' => $target, 'code_hash' => app(PortalProfileInstallation::class)->fingerprint(), 'restore_verified' => true, 'files_verified' => true, 'qa_passed' => true, 'compatibility_verified' => true, 'dump_path' => $this->proofDirectory.'/dump', 'dump_sha256' => hash_file('sha256', $this->proofDirectory.'/dump'), 'files_path' => $this->proofDirectory.'/files', 'files_sha256' => hash_file('sha256', $this->proofDirectory.'/files')], JSON_THROW_ON_ERROR)); } public function test_cold_install_repetition_empty_reversal_and_reinstallation_preserve_accounts(): void { $actor = $this->manager(); $this->emptySchema(); $this->assertFalse(Schema::hasColumn('users', 'portal_access_revision')); $this->login($actor); $this->getJson('/management/profile-install')->assertOk()->assertJsonPath('installed', false)->assertJsonPath('consistent', true)->assertJsonPath('ready', false); $this->postJson('/management/profile-install', $this->data())->assertUnprocessable(); $this->readinessFixture(); $users = DB::table('users')->count(); $data = $this->data(); $result = $this->postJson('/management/profile-install', $data)->assertOk()->assertJsonPath('changed', true)->json(); $this->postJson('/management/profile-install', $data)->assertOk()->assertExactJson($result); $this->postJson('/management/profile-install', [...$data, 'reason' => $data['reason'].' different'])->assertConflict(); $this->postJson('/management/profile-install', $this->data())->assertOk()->assertJsonPath('changed', false); $this->assertSame($users, DB::table('users')->count()); $this->assertSame(0, DB::table('portal_role_assignments')->count()); $this->assertSame(0, DB::table('portal_superuser_pair')->count()); $this->assertFalse(config('portal.profile_access_enabled')); $this->assertFalse(config('portal_profiles.authenticated_installation')); $this->assertSame(2, DB::table('audit_events')->where('source_system', 'portal-profile-installation')->count()); $this->emptySchema(); $this->postJson('/management/profile-install', $this->data())->assertOk()->assertJsonPath('changed', true); } public function test_dependencies_integrity_stale_preview_and_partial_schema_fail_closed(): void { $actor = $this->manager(); $this->emptySchema(); $this->login($actor); $this->readinessFixture(); $data = $this->data(); file_put_contents($this->proofDirectory.'/dump', 'tampered'); $this->postJson('/management/profile-install', $data)->assertConflict(); $this->postJson('/management/profile-install', $this->data())->assertUnprocessable(); $this->readinessFixture(); DB::table('migrations')->where('migration', '2026_10_09_030000_extend_opportunity_resources')->delete(); $this->getJson('/management/profile-install')->assertOk()->assertJsonPath('dependencies.resources_030000', false); $this->postJson('/management/profile-install', $this->data())->assertUnprocessable(); Schema::table('users', fn ($t) => $t->unsignedBigInteger('portal_access_revision')->default(0)); $this->getJson('/management/profile-install')->assertOk()->assertJsonPath('consistent', false); $this->postJson('/management/profile-install', $this->data())->assertConflict(); } public function test_failed_audit_rolls_back_schema_and_ledger_and_can_retry(): void { $actor = $this->manager(); $this->emptySchema(); $this->login($actor); $this->readinessFixture(); DB::statement("ALTER TABLE audit_events ADD CONSTRAINT qa_reject_profile_install CHECK (source_system <> 'portal-profile-installation')"); $this->postJson('/management/profile-install', $this->data())->assertStatus(503); $this->assertFalse(Schema::hasTable('portal_role_assignments')); $this->assertFalse(Schema::hasColumn('users', 'portal_access_revision')); $this->assertFalse(DB::table('migrations')->where('migration', PortalProfileInstallation::MIGRATION)->exists()); DB::statement('ALTER TABLE audit_events DROP CONSTRAINT qa_reject_profile_install'); $this->postJson('/management/profile-install', $this->data())->assertOk()->assertJsonPath('changed', true); } public function test_bootstrap_is_explicit_independent_separate_and_cannot_replace_existing_pair(): void { $actor = $this->manager(); $first = $this->actor(); $second = $this->actor(); $this->login($actor, false); $this->postJson('/management/profile-install', $this->data())->assertForbidden(); $this->login($actor); $this->postJson('/management/profile-install/bootstrap', [...$this->data(), 'first_user_id' => $first->id, 'second_user_id' => $second->id])->assertForbidden(); $pending = $this->postJson('/management/profile-install/bootstrap', [...$this->data(), 'first_user_id' => $actor->id, 'second_user_id' => $second->id])->assertOk()->assertJsonPath('initialized', false)->assertJsonPath('privileges_granted', false)->json(); $this->assertSame(0, DB::table('portal_superuser_pair')->count()); $url = '/management/profile-install/bootstrap/'.$pending['approval_id']; $view = $this->getJson($url)->assertOk()->json(); $this->postJson($url.'/confirm', [...$this->data(), 'hash' => $view['hash']])->assertForbidden(); $this->login($second); $view = $this->getJson($url)->assertOk()->json(); $this->postJson($url.'/confirm', ['hash' => $view['hash'], 'confirm' => true, 'idempotency_key' => (string) Str::uuid(), 'reason' => 'Synthetic independent QA confirmation'])->assertOk()->assertJsonPath('holders', [$actor->id, $second->id]); $this->login($first); $this->getJson('/management/profile-install')->assertForbidden(); $this->login($actor); $this->getJson('/management/profile-install')->assertOk()->assertJsonPath('bootstrap.initialized', true); $this->postJson('/management/profile-install/bootstrap', [...$this->data(), 'first_user_id' => $actor->id, 'second_user_id' => $first->id])->assertConflict(); $this->assertSame(1, DB::table('portal_superuser_pair')->count()); $this->assertSame(0, DB::table('portal_role_assignments')->count()); $this->assertFalse(config('portal_profiles.bootstrap_authorized')); } public function test_auditor_restriction_and_history_prevent_privilege_or_destructive_rollback(): void { $actor = $this->manager(); app(PortalProfileAdministration::class)->insertGrant((string) Str::uuid(), $this->actor(), [ 'user_id' => $actor->id, 'role' => 'auditor', 'opportunity_types' => [], 'source' => 'unit-synthetic-fixture']); $this->login($actor); $this->getJson('/management/profile-install')->assertForbidden(); $this->postJson('/management/profile-install', ['hash' => str_repeat('a', 64), 'confirm' => true, 'idempotency_key' => (string) Str::uuid(), 'reason' => 'Instalação sintética vedada ao Auditor'])->assertForbidden(); try { (require database_path('migrations/'.PortalProfileInstallation::MIGRATION.'.php'))->down(); $this->fail('Used authorization schema must not be dropped.'); } catch (\RuntimeException $e) { $this->assertStringContainsString('Preserve authorization history', $e->getMessage()); } $this->assertSame(1, DB::table('portal_role_assignments')->count()); $this->assertTrue(Schema::hasColumn('users', 'portal_access_revision')); } public function test_rollback_preserves_context_even_without_author_or_grants(): void { DB::table('opportunities')->insert(['reference' => 'QA-rollback-context', 'slug' => 'qa-rollback-context', 'workflow_context' => '{"history":["synthetic-preserve"]}']); try { (require database_path('migrations/'.PortalProfileInstallation::MIGRATION.'.php'))->down(); $this->fail('Context must survive rollback even without an owner.'); } catch (\RuntimeException $e) { $this->assertSame('Preserve scoped opportunities', $e->getMessage()); } $this->assertTrue(Schema::hasColumn('opportunities', 'workflow_context')); $this->assertSame(1, DB::table('opportunities')->whereNotNull('workflow_context')->count()); } public function test_bootstrap_rejects_unreadable_mfa_and_auditor_candidate_without_creating_pair(): void { $manager = $this->manager(); $first = $this->actor(); $second = $this->actor(); $first->forceFill(['two_factor_secret' => 'synthetic-invalid-ciphertext'])->save(); config(['portal.managers' => [$first->email]]); $this->login($first); $this->postJson('/management/profile-install/bootstrap', [...$this->data(), 'first_user_id' => $first->id, 'second_user_id' => $second->id])->assertUnprocessable(); $this->assertSame(0, DB::table('portal_superuser_pair')->count()); $first->forceFill(['two_factor_secret' => Crypt::encryptString('JBSWY3DPEHPK3PXP')])->save(); $input = [...$this->data(), 'first_user_id' => $first->id, 'second_user_id' => $second->id]; app(PortalProfileAdministration::class)->insertGrant((string) Str::uuid(), $manager, [ 'user_id' => $first->id, 'role' => 'auditor', 'opportunity_types' => [], 'source' => 'unit-synthetic-fixture']); $this->postJson('/management/profile-install/bootstrap', $input)->assertForbidden(); $this->assertSame(0, DB::table('portal_superuser_pair')->count()); $this->assertFalse(config('portal_profiles.bootstrap_authorized')); } public function test_partial_schema_cannot_disable_identity_write_controls_but_recovery_is_accessible(): void { $manager = $this->manager(); $before = $manager->two_factor_secret; $this->login($manager); config(['portal.profile_access_enabled' => true]); Schema::table('users', fn ($t) => $t->dropColumn('portal_access_revision')); $this->deleteJson('/user/two-factor-authentication')->assertStatus(503); $this->deleteJson('/settings/profile', ['password' => 'not-a-credential'])->assertStatus(503); $this->assertSame($before, $manager->fresh()->two_factor_secret); $this->getJson('/management/profile-install')->assertOk()->assertJsonPath('consistent', false); $this->postJson('/management/profile-install', $this->data())->assertConflict(); $this->post('/logout')->assertRedirect('/'); $this->assertGuest(); } }