libra/tests/Feature/Admin/AuditLogTest.php

327 lines
9.7 KiB
PHP

<?php
use App\Models\AdminLog;
use App\Models\User;
use Livewire\Volt\Volt;
beforeEach(function () {
$this->admin = User::factory()->admin()->create();
});
// ===========================================
// Authentication Tests
// ===========================================
test('audit log page requires admin authentication', function () {
$this->get(route('admin.audit-logs'))
->assertRedirect(route('login'));
});
test('client cannot access audit logs page', function () {
$client = User::factory()->individual()->create();
$this->actingAs($client)
->get(route('admin.audit-logs'))
->assertForbidden();
});
test('audit log page loads for admin', function () {
$this->actingAs($this->admin)
->get(route('admin.audit-logs'))
->assertOk()
->assertSeeLivewire('admin.audit-logs');
});
// ===========================================
// Display Tests
// ===========================================
test('audit logs display in table', function () {
AdminLog::factory()->count(5)->create();
$this->actingAs($this->admin);
Volt::test('admin.audit-logs')
->assertSee(__('audit.timestamp'))
->assertSee(__('audit.admin'))
->assertSee(__('audit.action'));
});
test('displays empty state when no logs exist', function () {
$this->actingAs($this->admin);
Volt::test('admin.audit-logs')
->assertSee(__('audit.no_logs_found'));
});
test('pagination works correctly with 25 items per page', function () {
AdminLog::factory()->count(50)->create();
$this->actingAs($this->admin);
Volt::test('admin.audit-logs')
->assertViewHas('logs', fn ($logs) => $logs->count() === 25);
});
test('logs are sorted by created_at descending', function () {
$oldLog = AdminLog::factory()->create(['created_at' => now()->subDays(5)]);
$newLog = AdminLog::factory()->create(['created_at' => now()]);
$this->actingAs($this->admin);
$component = Volt::test('admin.audit-logs');
$logs = $component->viewData('logs');
expect($logs->first()->id)->toBe($newLog->id);
});
test('displays system for logs without admin', function () {
AdminLog::factory()->create(['admin_id' => null]);
$this->actingAs($this->admin);
Volt::test('admin.audit-logs')
->assertSee(__('audit.system'));
});
// ===========================================
// Filter Tests
// ===========================================
test('can filter by action type', function () {
AdminLog::factory()->create(['action' => 'create']);
AdminLog::factory()->create(['action' => 'delete']);
$this->actingAs($this->admin);
Volt::test('admin.audit-logs')
->set('actionFilter', 'create')
->assertViewHas('logs', fn ($logs) => $logs->every(fn ($log) => $log->action === 'create'));
});
test('can filter by target type', function () {
AdminLog::factory()->create(['target_type' => 'user']);
AdminLog::factory()->create(['target_type' => 'consultation']);
$this->actingAs($this->admin);
Volt::test('admin.audit-logs')
->set('targetFilter', 'user')
->assertViewHas('logs', fn ($logs) => $logs->every(fn ($log) => $log->target_type === 'user'));
});
test('can filter by date range', function () {
AdminLog::factory()->create(['created_at' => now()->subDays(10)]);
AdminLog::factory()->create(['created_at' => now()]);
$this->actingAs($this->admin);
Volt::test('admin.audit-logs')
->set('dateFrom', now()->subDays(5)->format('Y-m-d'))
->set('dateTo', now()->format('Y-m-d'))
->assertViewHas('logs', fn ($logs) => $logs->count() === 1);
});
test('can search by target id', function () {
AdminLog::factory()->create(['target_id' => 123]);
AdminLog::factory()->create(['target_id' => 456]);
$this->actingAs($this->admin);
Volt::test('admin.audit-logs')
->set('search', '123')
->assertViewHas('logs', fn ($logs) => $logs->count() === 1 && $logs->first()->target_id === 123);
});
test('reset filters clears all filters', function () {
$this->actingAs($this->admin);
Volt::test('admin.audit-logs')
->set('actionFilter', 'create')
->set('targetFilter', 'user')
->set('dateFrom', '2024-01-01')
->set('dateTo', '2024-12-31')
->set('search', '123')
->call('resetFilters')
->assertSet('actionFilter', '')
->assertSet('targetFilter', '')
->assertSet('dateFrom', '')
->assertSet('dateTo', '')
->assertSet('search', '');
});
test('filters reset pagination', function () {
AdminLog::factory()->count(30)->create(['action' => 'create']);
AdminLog::factory()->count(30)->create(['action' => 'delete']);
$this->actingAs($this->admin);
$component = Volt::test('admin.audit-logs')
->call('gotoPage', 2)
->set('actionFilter', 'create');
// After setting filter, should be back to page 1
$logs = $component->viewData('logs');
expect($logs->currentPage())->toBe(1);
});
test('displays empty state when filters return no results', function () {
AdminLog::factory()->create(['action' => 'create']);
$this->actingAs($this->admin);
Volt::test('admin.audit-logs')
->set('actionFilter', 'delete')
->assertSee(__('audit.no_results'));
});
// ===========================================
// Modal Tests
// ===========================================
test('can view log details in modal', function () {
$log = AdminLog::factory()->create([
'old_values' => ['name' => 'Old Name'],
'new_values' => ['name' => 'New Name'],
]);
$this->actingAs($this->admin);
Volt::test('admin.audit-logs')
->call('showDetails', $log->id)
->assertSet('selectedLogId', $log->id)
->assertDispatched('open-modal', name: 'log-details');
});
test('selected log is loaded with admin relation', function () {
$log = AdminLog::factory()->create();
$this->actingAs($this->admin);
$component = Volt::test('admin.audit-logs')
->call('showDetails', $log->id);
$selectedLog = $component->viewData('selectedLog');
expect($selectedLog)->not->toBeNull();
expect($selectedLog->id)->toBe($log->id);
});
test('modal displays old and new values', function () {
$log = AdminLog::factory()->create([
'old_values' => ['status' => 'pending'],
'new_values' => ['status' => 'approved'],
]);
$this->actingAs($this->admin);
$component = Volt::test('admin.audit-logs')
->call('showDetails', $log->id);
$selectedLog = $component->viewData('selectedLog');
expect($selectedLog->old_values)->toBe(['status' => 'pending']);
expect($selectedLog->new_values)->toBe(['status' => 'approved']);
});
test('can close modal', function () {
$log = AdminLog::factory()->create();
$this->actingAs($this->admin);
Volt::test('admin.audit-logs')
->call('showDetails', $log->id)
->assertSet('selectedLogId', $log->id)
->call('closeModal')
->assertSet('selectedLogId', null)
->assertDispatched('close-modal', name: 'log-details');
});
// ===========================================
// CSV Export Tests
// ===========================================
test('can export filtered logs to csv', function () {
AdminLog::factory()->count(3)->create();
$this->actingAs($this->admin);
Volt::test('admin.audit-logs')
->call('exportCsv')
->assertFileDownloaded();
});
test('csv export respects filters', function () {
AdminLog::factory()->count(3)->create(['action' => 'create']);
AdminLog::factory()->count(2)->create(['action' => 'delete']);
$this->actingAs($this->admin);
// Set filter before export
$component = Volt::test('admin.audit-logs')
->set('actionFilter', 'create');
// Export is tested by checking it doesn't error and downloads
$component->call('exportCsv')
->assertFileDownloaded();
});
test('csv filename includes current date', function () {
AdminLog::factory()->create();
$this->actingAs($this->admin);
$response = Volt::test('admin.audit-logs')
->call('exportCsv');
// The response should be a StreamedResponse
$response->assertFileDownloaded('audit-log-'.now()->format('Y-m-d').'.csv');
});
// ===========================================
// Action Types and Target Types
// ===========================================
test('action types are populated from database', function () {
AdminLog::factory()->create(['action' => 'create']);
AdminLog::factory()->create(['action' => 'update']);
AdminLog::factory()->create(['action' => 'delete']);
$this->actingAs($this->admin);
$component = Volt::test('admin.audit-logs');
$actionTypes = $component->viewData('actionTypes');
expect($actionTypes)->toContain('create', 'update', 'delete');
});
test('target types are populated from database', function () {
AdminLog::factory()->create(['target_type' => 'user']);
AdminLog::factory()->create(['target_type' => 'consultation']);
$this->actingAs($this->admin);
$component = Volt::test('admin.audit-logs');
$targetTypes = $component->viewData('targetTypes');
expect($targetTypes)->toContain('user', 'consultation');
});
// ===========================================
// Locale-specific Tests
// ===========================================
test('timestamp format changes based on locale', function () {
$log = AdminLog::factory()->create(['created_at' => '2024-06-15 10:30:00']);
$this->actingAs($this->admin);
// English locale shows m/d/Y format
app()->setLocale('en');
Volt::test('admin.audit-logs')
->assertSee('06/15/2024');
// Arabic locale shows d/m/Y format
app()->setLocale('ar');
Volt::test('admin.audit-logs')
->assertSee('15/06/2024');
});