98 lines
4.7 KiB
PHP
98 lines
4.7 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Jobs\NotifyInterestReceived;
|
|
use App\Models\InterestRequest;
|
|
use App\Models\Sector;
|
|
use App\Models\User;
|
|
use App\Services\PortalAudit;
|
|
use Illuminate\Database\QueryException;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Mail\MailManager;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Mail;
|
|
use Tests\TestCase;
|
|
|
|
class PortalOperationsTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
public function test_audit_records_actor_and_changes_without_free_text(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$sector = new Sector;
|
|
PortalAudit::save($sector, ['name' => 'Nome privado', 'slug' => 'test', 'description' => 'Texto confidencial', 'is_active' => true], $user->id);
|
|
PortalAudit::save($sector, ['is_active' => false], $user->id);
|
|
$events = DB::table('audit_events')->where('source_system', 'portal-management')->get();
|
|
$this->assertCount(2, $events);
|
|
$this->assertSame($user->id, $events[0]->actor_user_id);
|
|
$this->assertStringNotContainsString('Texto confidencial', json_encode($events));
|
|
$this->assertStringNotContainsString('Nome privado', json_encode($events));
|
|
$updated = $events->firstWhere('action', 'updated');
|
|
$this->assertFalse(json_decode($updated->after_data, true)['values']['is_active']);
|
|
$this->actingAs($user)->get('/management/history')->assertForbidden();
|
|
config(['portal.managers' => [$user->email]]);
|
|
$this->get('/management/history')->assertOk();
|
|
}
|
|
|
|
public function test_audit_failure_rolls_back_the_business_change(): void
|
|
{
|
|
$sector = Sector::create(['name' => 'Energia', 'slug' => 'energia', 'is_active' => true]);
|
|
try {
|
|
PortalAudit::save($sector, ['is_active' => false], 9999999);
|
|
$this->fail('Expected invalid actor foreign key to reject event.');
|
|
} catch (QueryException) {
|
|
$this->assertTrue($sector->fresh()->is_active);
|
|
}
|
|
}
|
|
|
|
public function test_public_notifications_are_disabled_by_default_and_enqueued_when_enabled(): void
|
|
{
|
|
$payload = ['fullName' => 'Pessoa de teste', 'email' => 'portal-integration-test@gmail.com',
|
|
'investorType' => 'empresa', 'country' => 'Moçambique', 'sectorInterest' => 'Energia',
|
|
'message' => 'Pedido de teste na base isolada.', 'privacyConsent' => true];
|
|
config(['portal.notifications_enabled' => false]);
|
|
$this->postJson('/api/v1/interest-requests', $payload)->assertCreated();
|
|
$this->assertDatabaseCount('jobs', 0);
|
|
config(['portal.notifications_enabled' => true, 'portal.notification_recipients' => ['team@example.invalid']]);
|
|
$this->postJson('/api/v1/interest-requests', $payload)->assertCreated();
|
|
$this->assertDatabaseCount('jobs', 1);
|
|
$this->assertDatabaseCount('interest_requests', 2);
|
|
config(['queue.connections.database.after_commit' => true]);
|
|
$this->postJson('/api/v1/interest-requests', $payload)->assertStatus(500);
|
|
$this->assertDatabaseCount('jobs', 1);
|
|
$this->assertDatabaseCount('interest_requests', 2);
|
|
}
|
|
|
|
public function test_job_checks_current_configuration_and_excludes_personal_data(): void
|
|
{
|
|
$interest = InterestRequest::create(['reference' => 'IR-TEST', 'full_name' => 'Pessoa confidencial',
|
|
'email' => 'personal@example.invalid', 'investor_type' => 'empresa', 'country' => 'Moçambique',
|
|
'sector_interest' => 'Energia', 'message' => 'Mensagem confidencial', 'privacy_consent' => true]);
|
|
$job = new NotifyInterestReceived($interest->id, 'team@example.invalid');
|
|
config(['portal.notifications_enabled' => false]);
|
|
Mail::shouldReceive('raw')->never();
|
|
$job->handle();
|
|
Mail::clearResolvedInstance('mail.manager');
|
|
Mail::swap(\Mockery::mock(MailManager::class));
|
|
config(['portal.notifications_enabled' => true, 'portal.notification_recipients' => ['team@example.invalid']]);
|
|
Mail::shouldReceive('raw')->once()->withArgs(function ($body, $callback) {
|
|
$this->assertStringContainsString('IR-TEST', $body);
|
|
$this->assertStringContainsString('/management/interests/', $body);
|
|
$this->assertStringNotContainsString('confidencial', $body);
|
|
$this->assertStringNotContainsString('personal@example.invalid', $body);
|
|
|
|
return is_callable($callback);
|
|
});
|
|
$job->handle();
|
|
}
|
|
|
|
public function test_readiness_reports_non_production_without_mutating_data(): void
|
|
{
|
|
$this->artisan('portal:readiness')->expectsOutput('[PENDENTE] Ambiente production')->assertExitCode(1);
|
|
$this->assertDatabaseCount('users', 0);
|
|
$this->assertDatabaseCount('jobs', 0);
|
|
}
|
|
}
|