feat: add routes, lang, tests, stubs, docs, and docker configurations

This commit is contained in:
2026-05-21 16:05:16 +07:00
parent fad70d096b
commit 28a06315b8
3385 changed files with 177070 additions and 0 deletions
+112
View File
@@ -0,0 +1,112 @@
<?php
use App\Services\System\ActivityFormatter;
test('getFriendlyModelName returns System for null', function () {
expect(ActivityFormatter::getFriendlyModelName(null))->toBe('System');
});
test('getFriendlyModelName maps known classes', function () {
expect(ActivityFormatter::getFriendlyModelName('App\\Models\\SystemSetting'))->toBe('System Config');
expect(ActivityFormatter::getFriendlyModelName('App\\Models\\User'))->toBe('User Profile');
expect(ActivityFormatter::getFriendlyModelName('App\\Models\\Role'))->toBe('Access Role');
expect(ActivityFormatter::getFriendlyModelName('App\\Models\\Permission'))->toBe('Access Permission');
});
test('getFriendlyModelName headlines unknown class basename', function () {
expect(ActivityFormatter::getFriendlyModelName('App\\Models\\OtherSetting'))->toBe('Other Setting');
});
test('getEventBadgeClass returns correct class per event', function (string $event, string $expected) {
expect(ActivityFormatter::getEventBadgeClass($event))->toBe($expected);
})->with([
['created', 'text-bg-success'],
['updated', 'text-bg-warning'],
['deleted', 'text-bg-danger'],
['restored', 'text-bg-info'],
['login', 'text-bg-info'],
['logout', 'text-bg-secondary'],
['password_changed', 'text-bg-primary'],
['unknown_event', 'text-bg-theme-1'],
]);
test('getEventBadgeClass is case insensitive', function () {
expect(ActivityFormatter::getEventBadgeClass('CREATED'))->toBe('text-bg-success');
expect(ActivityFormatter::getEventBadgeClass('Login'))->toBe('text-bg-info');
});
test('getEventIcon returns correct icon per event', function (string $event, string $expected) {
expect(ActivityFormatter::getEventIcon($event))->toBe($expected);
})->with([
['created', 'bi-plus-circle'],
['updated', 'bi-pencil-square'],
['deleted', 'bi-trash'],
['logout', 'bi-box-arrow-right'],
['unknown', 'bi-info-circle'],
]);
test('formatChanges on creation shows all non-sensitive fields', function () {
$result = ActivityFormatter::formatChanges([
'old' => [],
'attributes' => ['name' => 'Jane', 'email' => 'j@x.com', 'password' => 'secret'],
]);
expect($result)->toHaveCount(2);
$fields = array_column($result, 'field');
expect($fields)->toContain('Name');
expect($fields)->toContain('Email');
expect($fields)->not->toContain('Password');
});
test('formatChanges on update returns only changed fields', function () {
$result = ActivityFormatter::formatChanges([
'old' => ['name' => 'Old', 'email' => 'same@x.com'],
'attributes' => ['name' => 'New', 'email' => 'same@x.com'],
]);
expect($result)->toHaveCount(1);
expect($result[0]['field'])->toBe('Name');
expect($result[0]['old'])->toBe('Old');
expect($result[0]['new'])->toBe('New');
});
test('formatChanges filters out sensitive fields like password and remember_token', function () {
$result = ActivityFormatter::formatChanges([
'old' => ['password' => 'a', 'remember_token' => 'b', '2fa_secret' => 'c', 'api_token' => 'd'],
'attributes' => ['password' => 'a2', 'remember_token' => 'b2', '2fa_secret' => 'c2', 'api_token' => 'd2'],
]);
expect($result)->toBeEmpty();
});
test('formatChanges formats null, bool, array, empty string', function () {
$result = ActivityFormatter::formatChanges([
'old' => [],
'attributes' => [
'a_null' => null,
'a_bool_true' => true,
'a_bool_false' => false,
'a_array' => ['x' => 1],
'a_empty' => '',
],
]);
$byField = collect($result)->keyBy('field');
expect($byField['A Null']['new'])->toBe('NULL');
expect($byField['A Bool True']['new'])->toBe('TRUE');
expect($byField['A Bool False']['new'])->toBe('FALSE');
expect($byField['A Array']['new'])->toBe(json_encode(['x' => 1], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
expect($byField['A Empty']['new'])->toBe('[empty]');
});
test('formatChanges truncates strings longer than 200 chars', function () {
$long = str_repeat('x', 250);
$result = ActivityFormatter::formatChanges([
'old' => [],
'attributes' => ['big' => $long],
]);
expect($result[0]['new'])->toEndWith('...');
expect(strlen($result[0]['new']))->toBe(200);
});