Files
nuqloud-ai/tests/unit/Service/PermissionServiceTest.php

166 lines
6.0 KiB
PHP

<?php
declare(strict_types=1);
namespace OCA\EducAI\Tests\Unit\Service;
use OCA\EducAI\Db\Bot;
use OCA\EducAI\Db\Settings;
use OCA\EducAI\Service\PermissionService;
use OCA\EducAI\Service\SettingsService;
use OCP\App\IAppManager;
use OCP\IGroupManager;
use OCP\IUser;
use OCP\IUserManager;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
class PermissionServiceTest extends TestCase {
public function testCanApproveBotRequiresMatchingScope(): void {
$service = $this->createPermissionService(false, ['group-a', 'group-b'], []);
$bot = $this->createBot('owner', 'groups', ['group-a', 'group-b']);
$this->assertTrue($service->canApproveBot('reviewer', $bot));
$otherScopedBot = $this->createBot('owner', 'groups', ['group-a', 'group-c']);
$this->assertFalse($service->canApproveBot('reviewer', $otherScopedBot));
}
public function testCanApproveBotAllowsOwnerWhenTheyCanManageTargetScope(): void {
$service = $this->createScopedPermissionService(
static fn (string $userId): bool => $userId === 'owner',
static fn (string $userId): array => $userId === 'owner' ? ['group-a'] : [],
static fn (string $userId): array => []
);
$bot = $this->createBot('owner', 'groups', ['group-a']);
$this->assertTrue($service->canApproveBot('owner', $bot));
$this->assertFalse($service->canApproveBot('reviewer', $bot));
}
public function testCanEditBotIsBoundToConcreteBotScope(): void {
$service = $this->createPermissionService(false, ['group-a'], []);
$bot = $this->createBot('owner', 'groups', ['group-b']);
$this->assertTrue($service->canEditBot('owner', $bot));
$this->assertFalse($service->canEditBot('reviewer', $bot));
}
public function testCanApproveBotUsesPendingTargetScopeForVersionedChanges(): void {
$service = $this->createPermissionService(false, ['group-a'], []);
$bot = $this->createBot('owner', 'groups', ['group-a']);
$bot->setApprovalStatus('pending');
$bot->setPendingChangesArray([
'visibility' => 'groups',
'allowed_groups' => json_encode(['group-b']),
'allowed_teams' => json_encode([]),
]);
$this->assertFalse($service->canApproveBot('reviewer', $bot));
}
public function testCanPublishBotToScopeRequiresAllTeamAssignments(): void {
$service = $this->createPermissionService(false, [], ['team-1', 'team-2']);
$this->assertTrue($service->canPublishBotToScope('reviewer', 'teams', null, ['team-1', 'team-2']));
$this->assertFalse($service->canPublishBotToScope('reviewer', 'teams', null, ['team-1', 'team-3']));
}
public function testCanApproveTeamBotRequiresConcreteTeamAdminEvenForGlobalAdmin(): void {
$teamBot = $this->createBot('owner', 'teams', [], ['team-1']);
$globalAdminOnly = $this->createPermissionService(true, [], []);
$this->assertFalse($globalAdminOnly->canApproveBot('admin', $teamBot));
$this->assertTrue($globalAdminOnly->canPublishBotToScope('admin', 'teams', null, ['team-1']));
$teamAdmin = $this->createPermissionService(true, [], ['team-1']);
$this->assertTrue($teamAdmin->canApproveBot('admin', $teamBot));
}
public function testCanCreateBotsAllowsAdminsAndConfiguredGroupMembers(): void {
$settings = new Settings();
$settings->setBotCreationRestricted(true);
$settings->setBotCreationGroup('bot-creators');
$member = $this->createMock(IUser::class);
$otherUser = $this->createMock(IUser::class);
$groupManager = $this->createMock(IGroupManager::class);
$groupManager->method('isAdmin')
->willReturnCallback(static fn (string $userId): bool => $userId === 'admin');
$groupManager->method('getUserGroupIds')
->willReturnCallback(static fn (IUser $user): array => $user === $member ? ['bot-creators'] : []);
$userManager = $this->createMock(IUserManager::class);
$userManager->method('get')
->willReturnCallback(static function (string $userId) use ($member, $otherUser): ?IUser {
return match ($userId) {
'member' => $member,
'other' => $otherUser,
default => null,
};
});
$settingsService = $this->createMock(SettingsService::class);
$settingsService->method('getSettings')->willReturn($settings);
$service = new PermissionService(
$groupManager,
$userManager,
$this->createMock(IAppManager::class),
$settingsService,
$this->createMock(LoggerInterface::class)
);
$this->assertTrue($service->canCreateBots('admin'));
$this->assertTrue($service->canCreateBots('member'));
$this->assertFalse($service->canCreateBots('other'));
}
private function createPermissionService(bool $isAdmin, array $adminGroups, array $adminTeams): PermissionService {
return $this->createScopedPermissionService(
static fn (string $userId): bool => $isAdmin,
static fn (string $userId): array => $adminGroups,
static fn (string $userId): array => $adminTeams
);
}
private function createScopedPermissionService(
callable $isAdminResolver,
callable $adminGroupsResolver,
callable $adminTeamsResolver
): PermissionService {
$settingsService = $this->createMock(SettingsService::class);
$settingsService->method('getSettings')->willReturn(new Settings());
$service = $this->getMockBuilder(PermissionService::class)
->setConstructorArgs([
$this->createMock(IGroupManager::class),
$this->createMock(IUserManager::class),
$this->createMock(IAppManager::class),
$settingsService,
$this->createMock(LoggerInterface::class),
])
->onlyMethods(['isAdmin', 'getAdminGroups', 'getAdminTeams'])
->getMock();
$service->method('isAdmin')->willReturnCallback($isAdminResolver);
$service->method('getAdminGroups')->willReturnCallback($adminGroupsResolver);
$service->method('getAdminTeams')->willReturnCallback($adminTeamsResolver);
return $service;
}
/**
* @param array<int,string> $groups
* @param array<int,string> $teams
*/
private function createBot(string $ownerId, string $visibility, array $groups = [], array $teams = []): Bot {
$bot = new Bot();
$bot->setUserId($ownerId);
$bot->setVisibility($visibility);
$bot->setIsPublic($visibility === 'global');
$bot->setAllowedGroups(json_encode($groups) ?: '[]');
$bot->setAllowedTeams(json_encode($teams) ?: '[]');
return $bot;
}
}