first commit

This commit is contained in:
2026-10-09 08:21:06 +02:00
parent db7456df87
commit d259c62304
3761 changed files with 896046 additions and 0 deletions
@@ -0,0 +1,78 @@
<?php
use App\Models\Permission;
use App\Models\Role;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
beforeEach(function () {
$this->artisan('db:seed');
});
test('attach and detach permission', function () {
$role = Role::factory()->create();
$permission = Permission::factory()->create();
$role->attachPermission($permission);
$this->assertTrue($role->fresh()->hasPermission($permission->name));
$role->detachPermission($permission);
$this->assertFalse($role->fresh()->hasPermission($permission->name));
});
test('attach permissions supports id and object array', function () {
$role = Role::factory()->create();
$p1 = Permission::factory()->create();
$p2 = Permission::factory()->create();
$role->attachPermissions([$p1, ['id' => $p2->id]]);
$this->assertTrue($role->fresh()->hasPermission($p1->name));
$this->assertTrue($role->fresh()->hasPermission($p2->name));
});
test('sync permissions replaces permissions', function () {
$role = Role::factory()->create();
$p1 = Permission::factory()->create();
$p2 = Permission::factory()->create();
$role->attachPermission($p1);
$role->syncPermissions([$p2->id]);
$fresh = $role->fresh();
$this->assertFalse($fresh->hasPermission($p1->name));
$this->assertTrue($fresh->hasPermission($p2->name));
});
test('save permissions with empty array detaches all', function () {
$role = Role::factory()->create();
$p1 = Permission::factory()->create();
$role->attachPermission($p1);
$role->savePermissions([]);
$this->assertCount(0, $role->fresh()->permissions);
});
test('cached permissions are returned', function () {
$role = Role::factory()->create();
$p1 = Permission::factory()->create();
$role->attachPermission($p1);
$cached = $role->cachedPermissions();
$this->assertTrue($cached->contains('name', $p1->name));
});
test('save clears cache', function () {
$role = Role::factory()->create();
$p1 = Permission::factory()->create();
$role->attachPermission($p1);
$role->cachedPermissions();
$role->detachPermission($p1);
$role->save();
$this->assertFalse($role->cachedPermissions()->contains('name', $p1->name));
});
@@ -0,0 +1,31 @@
<?php
use App\Models\Permission;
use App\Models\Role;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
beforeEach(function () {
$this->artisan('db:seed');
});
test('has role returns true when matching role', function () {
$role = Role::factory()->create(['name' => 'Editor']);
$user = User::factory()->create(['role_id' => $role->id]);
$this->assertTrue($user->hasRole('Editor'));
$this->assertFalse($user->hasRole('Admin'));
});
test('has permission returns true when role has permission', function () {
$role = Role::factory()->create();
$permission = Permission::factory()->create(['name' => 'something.do']);
$role->attachPermission($permission);
$user = User::factory()->create(['role_id' => $role->id]);
$this->assertTrue($user->hasPermission('something.do'));
$this->assertFalse($user->hasPermission('something.else'));
});
@@ -0,0 +1,53 @@
<?php
use App\Models\User;
use App\Support\Authorization\PasswordChangeManager;
use Illuminate\Cache\CacheManager;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\UpdatesSettings;
uses(RefreshDatabase::class);
uses(UpdatesSettings::class);
beforeEach(function () {
$this->artisan('db:seed');
$this->manager = new PasswordChangeManager(app(CacheManager::class));
});
test('force password change returns false when feature disabled', function () {
$this->setSettings(['password-change.enabled' => false]);
$user = User::factory()->create(['force_password_change' => true]);
$this->assertFalse($this->manager->forcePasswordChange($user));
$this->assertFalse($this->manager->isPasswordChangeForcedForUser($user));
});
test('force password change returns false when user flag false', function () {
$this->setSettings(['password-change.enabled' => true]);
$user = User::factory()->create(['force_password_change' => false]);
$this->assertFalse($this->manager->forcePasswordChange($user));
});
test('force password change sets cache when enabled and flagged', function () {
$this->setSettings(['password-change.enabled' => true]);
$user = User::factory()->create(['force_password_change' => true]);
$this->assertTrue($this->manager->forcePasswordChange($user));
$this->assertTrue($this->manager->isPasswordChangeForcedForUser($user));
});
test('lift password change request clears cache', function () {
$this->setSettings(['password-change.enabled' => true]);
$user = User::factory()->create(['force_password_change' => true]);
$this->manager->forcePasswordChange($user);
$this->manager->liftPasswordChangeRequest($user);
$this->assertFalse($this->manager->isPasswordChangeForcedForUser($user));
});
+64
View File
@@ -0,0 +1,64 @@
<?php
use App\Models\User;
use App\Support\Demo;
beforeEach(function () {
config([
'demo.enabled' => true,
'demo.protected_users' => ['admin'],
'demo.message' => null,
]);
});
test('nobody is protected while demo mode is off', function () {
config(['demo.enabled' => false]);
$this->assertFalse(Demo::enabled());
$this->assertFalse(Demo::isProtectedUser(new User(['username' => 'admin'])));
});
test('a guest is never protected', function () {
$this->assertFalse(Demo::isProtectedUser(null));
});
test('a protected account is matched on its username', function () {
$this->assertTrue(Demo::isProtectedUser(new User(['username' => 'admin'])));
});
test('a protected account is matched on its email', function () {
config(['demo.protected_users' => ['admin@example.com']]);
$this->assertTrue(Demo::isProtectedUser(new User(['email' => 'admin@example.com'])));
});
test('matching ignores case on both sides', function () {
config(['demo.protected_users' => ['ADMIN']]);
$this->assertTrue(Demo::isProtectedUser(new User(['username' => 'Admin'])));
});
test('an ordinary account is not protected', function () {
$this->assertFalse(Demo::isProtectedUser(new User([
'username' => 'jane',
'email' => 'jane@example.com',
])));
});
test('a user without a username is not protected by an empty match', function () {
config(['demo.protected_users' => ['']]);
$this->assertFalse(Demo::isProtectedUser(new User(['email' => 'jane@example.com'])));
});
test('the configured message overrides every reason', function () {
config(['demo.message' => 'Nope.']);
$this->assertSame('Nope.', Demo::message());
$this->assertSame('Nope.', Demo::message('Something more specific.'));
});
test('a reason falls back to the generic refusal', function () {
$this->assertSame('This action is disabled in the demo.', Demo::message());
$this->assertSame('Something more specific.', Demo::message('Something more specific.'));
});
@@ -0,0 +1,25 @@
<?php
use App\Support\Enum\UserStatus;
use Tests\UpdatesSettings;
uses(UpdatesSettings::class);
test('lists returns active banned unconfirmed when approval disabled', function () {
$this->setSettings(['approval.enabled' => false]);
$statuses = UserStatus::lists();
$this->assertArrayHasKey(UserStatus::ACTIVE->value, $statuses);
$this->assertArrayHasKey(UserStatus::BANNED->value, $statuses);
$this->assertArrayHasKey(UserStatus::UNCONFIRMED->value, $statuses);
$this->assertArrayNotHasKey(UserStatus::WAITING_APPROVAL->value, $statuses);
});
test('lists includes waiting approval when approval enabled', function () {
$this->setSettings(['approval.enabled' => true]);
$statuses = UserStatus::lists();
$this->assertArrayHasKey(UserStatus::WAITING_APPROVAL->value, $statuses);
});
+24
View File
@@ -0,0 +1,24 @@
<?php
use App\Support\Locale;
test('flag url returns url for known locale', function () {
$this->assertStringContainsString('/flags/GB.png', Locale::flagUrl('en'));
$this->assertStringContainsString('/flags/DE.png', Locale::flagUrl('de'));
$this->assertStringContainsString('/flags/RS.png', Locale::flagUrl('sr'));
});
test('flag url returns null for unknown locale', function () {
$this->assertNull(Locale::flagUrl('fr'));
});
test('validate locale accepts available locales', function () {
foreach (Locale::AVAILABLE_LOCALES as $locale) {
$this->assertTrue(Locale::validateLocale($locale));
}
});
test('validate locale rejects unknown locale', function () {
$this->assertFalse(Locale::validateLocale('fr'));
$this->assertFalse(Locale::validateLocale(''));
});
+95
View File
@@ -0,0 +1,95 @@
<?php
use App\Models\User;
use App\Support\Sidebar\Item;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
beforeEach(function () {
$this->artisan('db:seed');
});
test('create builds item with title', function () {
$item = Item::create('Users');
$this->assertSame('Users', $item->getTitle());
});
test('href takes priority over route', function () {
$item = Item::create('Foo')
->href('https://example.com/bar');
$this->assertSame('https://example.com/bar', $item->getHref());
});
test('get href resolves named route', function () {
$item = Item::create('Foo')->route('dashboard');
$this->assertSame(route('dashboard'), $item->getHref());
});
test('get href returns null when neither set', function () {
$this->assertNull(Item::create('Foo')->getHref());
});
test('icon setter and getter', function () {
$item = Item::create('Foo')->icon('bi-people');
$this->assertSame('bi-people', $item->getIcon());
});
test('active path setter and getter', function () {
$item = Item::create('Foo')->active('users*');
$this->assertSame('users*', $item->getActivePath());
});
test('dropdown returns false without children', function () {
$this->assertFalse(Item::create('Foo')->isDropdown());
});
test('add children makes item a dropdown', function () {
$item = Item::create('Foo')->addChildren([
Item::create('Child 1'),
Item::create('Child 2'),
]);
$this->assertTrue($item->isDropdown());
$this->assertCount(2, $item->children());
});
test('expanded path collects children active paths', function () {
$item = Item::create('Foo')->addChildren([
Item::create('Child 1')->active('users*'),
Item::create('Child 2')->active('roles*'),
]);
$this->assertSame(['users*', 'roles*'], $item->getExpandedPath());
});
test('authorize returns true when no permissions set', function () {
$user = User::factory()->create();
$this->assertTrue(Item::create('Foo')->authorize($user));
});
test('authorize calls closure when closure provided', function () {
$user = User::factory()->create();
$item = Item::create('Foo')->permissions(fn ($u) => $u->id === $user->id);
$this->assertTrue($item->authorize($user));
});
test('authorize checks user has permission', function () {
$user = Mockery::mock(User::class)->makePartial();
$user->shouldReceive('hasPermission')->with('users.manage')->andReturn(true);
$user->shouldReceive('hasPermission')->with('roles.manage')->andReturn(false);
$allowed = Item::create('A')->permissions(['users.manage']);
$denied = Item::create('B')->permissions(['users.manage', 'roles.manage']);
$this->assertTrue($allowed->authorize($user));
$this->assertFalse($denied->authorize($user));
});