From 3ae19a21b5792fb1895e48eb0e31e895d4db675d Mon Sep 17 00:00:00 2001 From: Louis Chmn Date: Thu, 11 Jun 2026 21:57:14 +0200 Subject: [PATCH 1/2] test: Introduce fakes for app config And use it in some tests Signed-off-by: Louis Chmn --- .../tests/Controller/ApiControllerTest.php | 7 +- .../tests/RequestHandlerControllerTest.php | 6 +- apps/dashboard/tests/DashboardServiceTest.php | 9 +- .../CalendarFederationConfigTest.php | 17 +- tests/lib/FakeAppConfig.php | 284 ++++++++++++++++++ tests/lib/FakeFrameworkAppConfig.php | 114 +++++++ 6 files changed, 411 insertions(+), 26 deletions(-) create mode 100644 tests/lib/FakeAppConfig.php create mode 100644 tests/lib/FakeFrameworkAppConfig.php diff --git a/apps/appstore/tests/Controller/ApiControllerTest.php b/apps/appstore/tests/Controller/ApiControllerTest.php index 711042dffc86c..8ef878a65ee5e 100644 --- a/apps/appstore/tests/Controller/ApiControllerTest.php +++ b/apps/appstore/tests/Controller/ApiControllerTest.php @@ -16,13 +16,13 @@ use OC\Installer; use OCA\Appstore\Controller\ApiController; use OCP\AppFramework\Http\DataResponse; -use OCP\IAppConfig; use OCP\IConfig; use OCP\IRequest; use OCP\L10N\IFactory; use OCP\Support\Subscription\IRegistry; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; +use Test\FakeAppConfig; use Test\TestCase; #[\PHPUnit\Framework\Attributes\Group(name: 'DB')] @@ -31,8 +31,6 @@ final class ApiControllerTest extends TestCase { private IConfig&MockObject $config; - private IAppConfig&MockObject $appConfig; - private AppManager&MockObject $appManager; private DependencyAnalyzer&MockObject $dependencyAnalyzer; @@ -59,7 +57,6 @@ protected function setUp(): void { $this->request = $this->createMock(IRequest::class); $this->config = $this->createMock(IConfig::class); - $this->appConfig = $this->createMock(IAppConfig::class); $this->appManager = $this->createMock(AppManager::class); $this->dependencyAnalyzer = $this->createMock(DependencyAnalyzer::class); $this->categoryFetcher = $this->createMock(CategoryFetcher::class); @@ -73,7 +70,7 @@ protected function setUp(): void { $this->apiController = new ApiController( $this->request, $this->config, - $this->appConfig, + new FakeAppConfig(), $this->appManager, $this->dependencyAnalyzer, $this->categoryFetcher, diff --git a/apps/cloud_federation_api/tests/RequestHandlerControllerTest.php b/apps/cloud_federation_api/tests/RequestHandlerControllerTest.php index 326da930d9c6f..443d11fe5dde2 100644 --- a/apps/cloud_federation_api/tests/RequestHandlerControllerTest.php +++ b/apps/cloud_federation_api/tests/RequestHandlerControllerTest.php @@ -20,7 +20,6 @@ use OCP\Federation\ICloudFederationFactory; use OCP\Federation\ICloudFederationProviderManager; use OCP\Federation\ICloudIdManager; -use OCP\IAppConfig; use OCP\IGroupManager; use OCP\IRequest; use OCP\IURLGenerator; @@ -29,6 +28,7 @@ use OCP\OCM\IOCMDiscoveryService; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; +use Test\FakeAppConfig; use Test\TestCase; class RequestHandlerControllerTest extends TestCase { @@ -41,7 +41,6 @@ class RequestHandlerControllerTest extends TestCase { private Config&MockObject $config; private IEventDispatcher&MockObject $eventDispatcher; private FederatedInviteMapper&MockObject $federatedInviteMapper; - private IAppConfig&MockObject $appConfig; private ICloudFederationFactory&MockObject $cloudFederationFactory; private ICloudIdManager&MockObject $cloudIdManager; @@ -62,7 +61,6 @@ protected function setUp(): void { $this->config = $this->createMock(Config::class); $this->eventDispatcher = $this->createMock(IEventDispatcher::class); $this->federatedInviteMapper = $this->createMock(FederatedInviteMapper::class); - $this->appConfig = $this->createMock(IAppConfig::class); $this->cloudFederationFactory = $this->createMock(ICloudFederationFactory::class); $this->cloudIdManager = $this->createMock(ICloudIdManager::class); $this->discoveryService = $this->createMock(IOCMDiscoveryService::class); @@ -79,7 +77,7 @@ protected function setUp(): void { $this->config, $this->eventDispatcher, $this->federatedInviteMapper, - $this->appConfig, + new FakeAppConfig(), $this->cloudFederationFactory, $this->cloudIdManager, $this->discoveryService, diff --git a/apps/dashboard/tests/DashboardServiceTest.php b/apps/dashboard/tests/DashboardServiceTest.php index 15cf3382f403c..3ae5ce0513343 100644 --- a/apps/dashboard/tests/DashboardServiceTest.php +++ b/apps/dashboard/tests/DashboardServiceTest.php @@ -18,11 +18,13 @@ use OCP\IUserManager; use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; +use Test\FakeAppConfig; +use Test\FakeFrameworkAppConfig; class DashboardServiceTest extends TestCase { private IUserConfig&MockObject $userConfig; - private IAppConfig&MockObject $appConfig; + private IAppConfig $appConfig; private IUserManager&MockObject $userManager; private IAccountManager&MockObject $accountManager; private DashboardService $service; @@ -31,7 +33,7 @@ protected function setUp(): void { parent::setUp(); $this->userConfig = $this->createMock(IUserConfig::class); - $this->appConfig = $this->createMock(IAppConfig::class); + $this->appConfig = new FakeFrameworkAppConfig('dashboard'); $this->userManager = $this->createMock(IUserManager::class); $this->accountManager = $this->createMock(IAccountManager::class); @@ -45,9 +47,6 @@ protected function setUp(): void { } public function testGetLayoutRemovesEmptyAndDuplicateEntries(): void { - $this->appConfig->method('getAppValueString') - ->with('layout', 'recommendations,spreed,mail,calendar') - ->willReturn('recommendations,spreed,mail,calendar'); $this->userConfig->method('getValueString') ->with('alice', 'dashboard', 'layout', 'recommendations,spreed,mail,calendar') ->willReturn('spreed,,mail,mail,calendar,spreed'); diff --git a/apps/dav/tests/unit/CalDAV/Federation/CalendarFederationConfigTest.php b/apps/dav/tests/unit/CalDAV/Federation/CalendarFederationConfigTest.php index b8b37eea16748..bd0f1b5f5e331 100644 --- a/apps/dav/tests/unit/CalDAV/Federation/CalendarFederationConfigTest.php +++ b/apps/dav/tests/unit/CalDAV/Federation/CalendarFederationConfigTest.php @@ -12,22 +12,19 @@ use OCA\DAV\CalDAV\Federation\CalendarFederationConfig; use OCP\AppFramework\Services\IAppConfig; use PHPUnit\Framework\Attributes\DataProvider; -use PHPUnit\Framework\MockObject\MockObject; +use Test\FakeFrameworkAppConfig; use Test\TestCase; class CalendarFederationConfigTest extends TestCase { private CalendarFederationConfig $config; - private IAppConfig&MockObject $appConfig; + private IAppConfig $appConfig; protected function setUp(): void { parent::setUp(); - $this->appConfig = $this->createMock(IAppConfig::class); - - $this->config = new CalendarFederationConfig( - $this->appConfig, - ); + $this->appConfig = new FakeFrameworkAppConfig('dav'); + $this->config = new CalendarFederationConfig($this->appConfig); } public static function provideIsFederationEnabledData(): array { @@ -39,11 +36,7 @@ public static function provideIsFederationEnabledData(): array { #[DataProvider(methodName: 'provideIsFederationEnabledData')] public function testIsFederationEnabled(bool $configValue): void { - $this->appConfig->expects(self::once()) - ->method('getAppValueBool') - ->with('enableCalendarFederation', true) - ->willReturn($configValue); - + $this->appConfig->setAppValueBool('enableCalendarFederation', $configValue); $this->assertEquals($configValue, $this->config->isFederationEnabled()); } } diff --git a/tests/lib/FakeAppConfig.php b/tests/lib/FakeAppConfig.php new file mode 100644 index 0000000000000..bc7cc3bd80be5 --- /dev/null +++ b/tests/lib/FakeAppConfig.php @@ -0,0 +1,284 @@ + + * > $appConfig */ + private array $appConfig = [], + /** @var array + * > $lexicon */ + private array $lexicon = [], + ) { + } + + #[\Override] + public function getApps(): array { + return array_keys($this->appConfig); + } + + #[\Override] + public function getKeys(string $app): array { + return array_keys($this->appConfig[$app] ?? []); + } + + #[\Override] + public function searchKeys(string $app, string $prefix = '', bool $lazy = false): array { + return array_filter($this->getKeys($app), fn (string $key) => str_starts_with($key, $prefix)); + } + + #[\Override] + public function hasKey(string $app, string $key, ?bool $lazy = false): bool { + return isset($this->appConfig[$app][$key]); + } + + #[\Override] + public function isSensitive(string $app, string $key, ?bool $lazy = false): bool { + return $this->appConfig[$app][$key]['sensitive'] ?? false; + } + + #[\Override] + public function isLazy(string $app, string $key): bool { + return $this->appConfig[$app][$key]['lazy']; + } + + #[\Override] + public function getAllValues(string $app, string $prefix = '', bool $filtered = false): array { + return array_map(fn (string $key) => $this->appConfig[$app][$key]['value'], $this->searchKeys($app, $prefix)); + } + + #[\Override] + public function searchValues(string $key, bool $lazy = false, ?int $typedAs = null): array { + $values = []; + foreach ($this->getApps() as $app) { + foreach ($this->searchKeys($app, $key, $lazy) as $appKey) { + if ($appKey === $key) { + $values[$app] = $this->appConfig[$app][$appKey]['value']; + } + } + } + return $values; + } + + private function getValue(string $app, string $key, mixed $default): mixed { + return $this->appConfig[$app][$key]['value'] ?? $default; + } + + #[\Override] + public function getValueString(string $app, string $key, string $default = '', bool $lazy = false): string { + return (string)$this->getValue($app, $key, $default); + } + + #[\Override] + public function getValueInt(string $app,string $key,int $default = 0,bool $lazy = false): int { + return (int)$this->getValue($app, $key, $default); + } + + #[\Override] + public function getValueFloat(string $app, string $key, float $default = 0, bool $lazy = false): float { + return (float)$this->getValue($app, $key, $default); + } + + #[\Override] + public function getValueBool(string $app, string $key, bool $default = false, bool $lazy = false): bool { + return (bool)$this->getValue($app, $key, $default); + } + + #[\Override] + public function getValueArray(string $app, string $key, array $default = [], bool $lazy = false): array { + return (array)$this->getValue($app, $key, $default); + } + + #[\Override] + public function getValueType(string $app, string $key, ?bool $lazy = null): int { + return (int)$this->appConfig[$app][$key]['type']; + } + + private function setValue(string $app, string $key, mixed $value, int $type, bool $lazy, bool $sensitive): bool { + $this->appConfig[$app][$key] = [ + 'value' => $value, + 'type' => $type, + 'lazy' => $lazy, + 'sensitive' => $sensitive, + ]; + return true; + } + + #[\Override] + public function setValueString(string $app, string $key, string $value, bool $lazy = false, bool $sensitive = false): bool { + return $this->setValue($app, $key, $value, self::VALUE_STRING, $lazy, $sensitive);} + + #[\Override] + public function setValueInt(string $app, string $key, int $value, bool $lazy = false, bool $sensitive = false): bool { + return $this->setValue($app, $key, $value, self::VALUE_INT, $lazy, $sensitive); + } + + #[\Override] + public function setValueFloat(string $app, string $key, float $value, bool $lazy = false, bool $sensitive = false): bool { + return $this->setValue($app, $key, $value, self::VALUE_FLOAT, $lazy, $sensitive); + } + + #[\Override] + public function setValueBool(string $app, string $key, bool $value, bool $lazy = false): bool { + return $this->setValue($app, $key, $value, self::VALUE_BOOL, $lazy, false); + } + + #[\Override] + public function setValueArray(string $app, string $key, array $value, bool $lazy = false, bool $sensitive = false): bool { + return $this->setValue($app, $key, $value, self::VALUE_ARRAY, $lazy, $sensitive); + } + + #[\Override] + public function updateSensitive(string $app, string $key, bool $sensitive): bool { + $this->appConfig[$app][$key]['sensitive'] = $sensitive; + return true; + } + + #[\Override] + public function updateLazy(string $app, string $key, bool $lazy): bool { + $this->appConfig[$app][$key]['lazy'] = $lazy; + return true; + } + + #[\Override] + public function getDetails(string $app, string $key): array { + return [ + 'app' => $app, + 'key' => $key, + 'typeString' => $this->convertTypeToString($this->getValueType($app, $key)), + 'value' => $this->appConfig[$app][$key]['value'] ?? $this->lexicon[$app][$key]['default'], + 'type' => $this->appConfig[$app][$key]['type'] ?? $this->lexicon[$app][$key]['valueType'] ?? IAppConfig::VALUE_STRING, + 'lazy' => $this->appConfig[$app][$key]['lazy'] ?? $this->lexicon[$app][$key]['lazy'] ?? false, + 'sensitive' => $this->appConfig[$app][$key]['sensitive'] ?? $this->lexicon[$app][$key]['sensitive'] ?? false, + ]; + } + + #[\Override] + public function getKeyDetails(string $app, string $key): array { + return [ + 'app' => $app, + 'key' => $key, + 'lazy' => $this->lexicon[$app][$key]['lazy'] ?? false, + 'valueType' => $this->lexicon[$app][$key]['valueType'] ?? IAppConfig::VALUE_STRING, + 'valueTypeName' => $this->lexicon[$app][$key]['valueTypeName'] ?? $this->convertTypeToString($this->lexicon[$app][$key]['valueType'] ?? IAppConfig::VALUE_STRING), + 'sensitive' => $this->lexicon[$app][$key]['sensitive'] ?? false, + 'internal' => $this->lexicon[$app][$key]['internal'] ?? false, + 'default' => $this->lexicon[$app][$key]['default'] ?? null, + 'definition' => $this->lexicon[$app][$key]['definition'] ?? null, + 'note' => $this->lexicon[$app][$key]['note'] ?? null, + ]; + } + + #[\Override] + public function convertTypeToInt(string $type): int { + return match (strtolower($type)) { + 'mixed' => IAppConfig::VALUE_MIXED, + 'string' => IAppConfig::VALUE_STRING, + 'integer' => IAppConfig::VALUE_INT, + 'float' => IAppConfig::VALUE_FLOAT, + 'boolean' => IAppConfig::VALUE_BOOL, + 'array' => IAppConfig::VALUE_ARRAY, + default => throw new AppConfigIncorrectTypeException('Unknown type ' . $type) + }; + } + + #[\Override] + public function convertTypeToString(int $type): string { + $type &= ~self::VALUE_SENSITIVE; + + return match ($type) { + IAppConfig::VALUE_MIXED => 'mixed', + IAppConfig::VALUE_STRING => 'string', + IAppConfig::VALUE_INT => 'integer', + IAppConfig::VALUE_FLOAT => 'float', + IAppConfig::VALUE_BOOL => 'boolean', + IAppConfig::VALUE_ARRAY => 'array', + default => throw new AppConfigIncorrectTypeException('Unknown numeric type ' . $type) + }; + } + + #[\Override] + public function deleteKey(string $app, string $key): void { + $this->appConfig[$app][$key] = null; + } + + #[\Override] + public function deleteApp(string $app): void { + $this->appConfig[$app] = null; + } + + #[\Override] + public function clearCache(bool $reload = false): void { + $this->appConfig = []; + } + + #[\Override] + public function getValues($app, $key) { + if (($app !== false) === ($key !== false)) { + return false; + } + + $key = ($key === false) ? '' : $key; + if (!$app) { + return $this->searchValues($key, false, self::VALUE_MIXED); + } else { + return $this->getAllValues($app, $key); + } + } + + #[\Override] + public function getFilteredValues($app) { + return $this->getAllValues($app, filtered: true); + } + + #[\Override] + public function getAppInstalledVersions(bool $onlyEnabled = false): array { + $appVersionsCache = $this->searchValues('installed_version', false, IAppConfig::VALUE_STRING); + + if ($onlyEnabled) { + return array_filter( + $appVersionsCache, + fn (string $app): bool => $this->getValueString($app, 'enabled', 'no') !== 'no', + ARRAY_FILTER_USE_KEY + ); + } + + return $appVersionsCache; + } +} diff --git a/tests/lib/FakeFrameworkAppConfig.php b/tests/lib/FakeFrameworkAppConfig.php new file mode 100644 index 0000000000000..0bf3eae2563e0 --- /dev/null +++ b/tests/lib/FakeFrameworkAppConfig.php @@ -0,0 +1,114 @@ +appConfig->getKeys($this->appId); + } + + public function hasAppKey(string $key, ?bool $lazy = false): bool { + return $this->appConfig->hasKey($this->appId, $key, $lazy); + } + + public function isSensitive(string $key, ?bool $lazy = false): bool { + return $this->appConfig->isSensitive($this->appId, $key, $lazy); + } + + public function isLazy(string $key): bool { + return $this->appConfig->isLazy($this->appId, $key); + } + + public function getAllAppValues(string $key = '', bool $filtered = false): array { + return $this->appConfig->getAllValues($this->appId, $key, $filtered); + } + + public function setAppValue(string $key, string $value): void { + throw new \Exception('Deprecated. Use typed method.'); + } + + public function setAppValueString(string $key, string $value, bool $lazy = false, bool $sensitive = false): bool { + return $this->appConfig->setValueString($this->appId, $key, $value, $lazy, $sensitive); + } + + public function setAppValueInt(string $key, int $value, bool $lazy = false, bool $sensitive = false): bool { + return $this->appConfig->setValueInt($this->appId, $key, $value, $lazy, $sensitive); + } + + public function setAppValueFloat(string $key, float $value, bool $lazy = false, bool $sensitive = false): bool { + return $this->appConfig->setValueFloat($this->appId, $key, $value, $lazy, $sensitive); + } + + public function setAppValueBool(string $key, bool $value, bool $lazy = false): bool { + return $this->appConfig->setValueBool($this->appId, $key, $value, $lazy); + } + + public function setAppValueArray(string $key, array $value, bool $lazy = false, bool $sensitive = false): bool { + return $this->appConfig->setValueArray($this->appId, $key, $value, $lazy, $sensitive); + } + + public function getAppValue(string $key, string $default = ''): string { + throw new \Exception('Deprecated. Use typed method.'); + } + + public function getAppValueString(string $key, string $default = '', bool $lazy = false): string { + return $this->appConfig->getValueString($this->appId, $key, $default, $lazy); + } + + public function getAppValueInt(string $key, int $default = 0, bool $lazy = false): int { + return $this->appConfig->getValueInt($this->appId, $key, $default, $lazy); + } + + public function getAppValueFloat(string $key, float $default = 0, bool $lazy = false): float { + return $this->appConfig->getValueFloat($this->appId, $key, $default, $lazy); + } + + public function getAppValueBool(string $key, bool $default = false, bool $lazy = false): bool { + return $this->appConfig->getValueBool($this->appId, $key, $default, $lazy); + } + + public function getAppValueArray(string $key, array $default = [], bool $lazy = false): array { + return $this->appConfig->getValueArray($this->appId, $key, $default, $lazy); + } + + public function deleteAppValue(string $key): void { + $this->appConfig->deleteKey($this->appId, $key); + } + + public function deleteAppValues(): void { + $this->appConfig->deleteApp($this->appId); + } + + public function setUserValue(string $userId, string $key, string $value, ?string $preCondition = null): void { + throw new \Exception('Fake method not implemented.'); + } + + public function getUserValue(string $userId, string $key, string $default = ''): string { + throw new \Exception('Fake method not implemented.'); + } + + public function deleteUserValue(string $userId, string $key): void { + throw new \Exception('Fake method not implemented.'); + } +} + From 6ab50072417d86725a2350d36ce52bd922c14822 Mon Sep 17 00:00:00 2001 From: Louis Chmn Date: Thu, 11 Jun 2026 23:44:49 +0200 Subject: [PATCH 2/2] test: Introduce fakes for usermanager And use it in some tests Signed-off-by: Louis Chmn --- tests/lib/User/FakeUser.php | 183 +++++++++++++++++++++++++++ tests/lib/User/FakeUserManager.php | 190 +++++++++++++++++++++++++++++ 2 files changed, 373 insertions(+) create mode 100644 tests/lib/User/FakeUser.php create mode 100644 tests/lib/User/FakeUserManager.php diff --git a/tests/lib/User/FakeUser.php b/tests/lib/User/FakeUser.php new file mode 100644 index 0000000000000..9760a502494fa --- /dev/null +++ b/tests/lib/User/FakeUser.php @@ -0,0 +1,183 @@ + */ + private array $managers = []; + + public function __construct( + private readonly string $uid, + string $password, + private readonly FakeUserManager $userManager, + private readonly ?UserInterface $backend = null, + ) { + $this->passwordHash = md5($password); + } + + public function getUID(): string { + return $this->uid; + } + + public function getDisplayName(): string { + return $this->displayName ?? $this->getUID(); + } + + public function setDisplayName(string $displayName): bool { + $this->displayName = $displayName; + return true; + } + + public function getLastLogin(): int { + return $this->lastLoginTimestamp; + } + + public function getFirstLogin(): int { + return $this->firstLoginTimestamp; + } + + public function updateLastLoginTimestamp(): bool { + $this->lastLoginTimestamp = time(); + return true; + } + + public function delete(): bool { + return $this->userManager->deleteUser($this); + } + + public function setPassword($password, $recoveryPassword = null): bool { + $this->passwordHash = md5($password); + return true; + } + + public function getPasswordHash(): ?string { + return $this->passwordHash; + } + + public function setPasswordHash(string $passwordHash): bool { + $this->passwordHash = $passwordHash; + return true; + } + + public function getHome(): string { + return '/files/' . $this->getUID(); + } + + public function getBackendClassName(): string { + return get_class($this->backend); + } + + public function getBackend(): ?UserInterface { + return $this->backend; + } + + public function canChangeAvatar(): bool { + return true; + } + + public function canChangePassword(): bool { + return true; + } + + public function canChangeDisplayName(): bool { + return true; + } + + public function canChangeEmail(): bool { + return true; + } + + public function canEditProperty(string $property): bool { + return true; + } + + public function isEnabled(): bool { + return $this->enabled; + } + + public function setEnabled(bool $enabled = true): void { + $this->enabled = $enabled; + } + + public function getEMailAddress(): ?string { + return $this->email; + } + + public function getSystemEMailAddress(): ?string { + return $this->email; + } + + public function getPrimaryEMailAddress(): ?string { + return $this->email; + } + + public function getAvatarImage($size): ?IImage { + return null; + } + + public function getCloudId(): string { + return 'cloudid:' . $this->getUID(); + } + + public function setEMailAddress($mailAddress): void { + $this->email = $mailAddress; + } + + public function setSystemEMailAddress(string $mailAddress): void { + $this->email = $mailAddress; + } + + public function setPrimaryEMailAddress(string $mailAddress): void { + $this->email = $mailAddress; + } + + public function getQuota(): string { + return $this->quota; + } + + public function getQuotaBytes(): int|float { + $quota = $this->getQuota(); + if ($quota === 'none') { + return FileInfo::SPACE_UNLIMITED; + } + + $bytes = Util::computerFileSize($quota); + if ($bytes === false) { + return FileInfo::SPACE_UNKNOWN; + } + return $bytes; + } + + public function setQuota($quota): void { + $this->quota = $quota; + } + + public function getManagerUids(): array { + return $this->managers; + } + + public function setManagerUids(array $uids): void { + $this->managers = $uids; + } +} diff --git a/tests/lib/User/FakeUserManager.php b/tests/lib/User/FakeUserManager.php new file mode 100644 index 0000000000000..7542f2b1ea884 --- /dev/null +++ b/tests/lib/User/FakeUserManager.php @@ -0,0 +1,190 @@ + */ + private array $users = []; + + #[\Override] + public function registerBackend(UserInterface $backend) { + $this->backends[] = $backend; + } + + #[\Override] + public function getBackends() { + return $this->backends; + } + + #[\Override] + public function removeBackend(UserInterface $backend) { + $this->backends = array_filter($this->backends, fn ($b) => $b !== $backend); + } + + #[\Override] + public function clearBackends() { + $this->backends = []; + } + + #[\Override] + public function get($uid) { + return $this->users[$uid] ?? null; + } + + #[\Override] + public function getDisplayName(string $uid): ?string { + return $this->get($uid)?->getDisplayName(); + } + + #[\Override] + public function userExists($uid) { + return $this->get($uid) !== null; + } + + #[\Override] + public function checkPassword($loginName, $password) { + return $this->get($loginName)->getPasswordHash() === md5($password); + } + + #[\Override] + public function search($pattern, $limit = null, $offset = 0) { + $results = array_filter($this->users, fn (IUser $user) => str_contains($user->getUID(), $pattern)); + if ($limit !== null) { + return array_slice($results, $offset, $limit); + } + return $results; + } + + #[\Override] + public function searchDisplayName($pattern, $limit = null, $offset = 0) { + $results = array_filter($this->users, fn (IUser $user) => str_contains($user->getDisplayName(), $pattern)); + if ($limit !== null) { + return array_slice($results, $offset, $limit); + } + return $results; + } + + #[\Override] + public function getDisabledUsers(?int $limit = null, int $offset = 0, string $search = ''): array { + $results = array_filter($this->users, fn (IUser $user) => !$user->isEnabled() && str_contains($user->getUID(), $search)); + if ($limit !== null) { + return array_slice($results, $offset, $limit); + } + return $results; + } + + #[\Override] + public function searchKnownUsersByDisplayName(string $searcher, string $pattern, ?int $limit = null, ?int $offset = null): array { + throw new \Exception('Fake method not implemented.'); + } + + #[\Override] + public function createUser($uid, $password) { + return $this->users[$uid] = new FakeUser($uid, $password, $this); + } + + #[\Override] + public function createUserFromBackend($uid, $password, UserInterface $backend) { + return $this->users[$uid] = new FakeUser($uid, $password, $this, $backend); + } + + #[\Override] + public function countUsers(bool $onlyMappedUsers = false) { + return count($this->users); + } + + #[\Override] + public function countUsersTotal(int $limit = 0, bool $onlyMappedUsers = false): int|false { + return count($this->users); + } + + #[\Override] + public function callForAllUsers(\Closure $callback, $search = '') { + foreach ($this->users as $user) { + if (str_contains($user->getUID(), $search)) { + $callback($user); + } + } + } + + #[\Override] + public function countDisabledUsers() { + return count(array_filter($this->users, fn (IUser $user) => !$user->isEnabled())); + } + + #[\Override] + public function countSeenUsers() { + return count(array_filter($this->users, fn (IUser $user) => $user->getFirstLogin() > 0)); + } + + #[\Override] + public function callForSeenUsers(\Closure $callback) { + foreach ($this->users as $user) { + if ($user->getFirstLogin() > 0) { + $callback($user); + } + } + } + + #[\Override] + public function getByEmail($email) { + foreach ($this->users as $user) { + if ($user->getEMailAddress() === $email) { + return $user; + } + } + return null; + } + + #[\Override] + public function validateUserId(string $uid, bool $checkDataDirectory = false): void { + throw new \Exception('Fake method not implemented.'); + } + + #[\Override] + public function getLastLoggedInUsers(?int $limit = null, int $offset = 0, string $search = ''): array { + $results = array_filter($this->users, fn (IUser $user) => str_contains($user->getUID(), $search)); + usort($results, fn (IUser $a, IUser $b) => $b->getLastLogin() <=> $a->getLastLogin()); + return array_slice($results, $offset, $limit); + } + + #[\Override] + public function getSeenUsers(int $offset = 0, ?int $limit = null): \Iterator { + $results = array_filter($this->users, fn (IUser $user) => $user->getFirstLogin() > 0); + return new \ArrayIterator(array_slice($results, $offset, $limit)); + } + + #[\Override] + public function getExistingUser(string $userId, ?string $displayName = null): IUser { + return $this->users[$userId]; + } + + #[\Override] + public function getAvatarUrlLight(string $userId, int $size): string { + return 'core/avatar/getAvatar?userId=' . $userId . '&size=' . $size; + } + + #[\Override] + public function getAvatarUrlDark(string $userId, int $size): string { + return 'core/avatar/getAvatarDark?userId=' . $userId . '&size=' . $size; + } + + public function deleteUser(IUser $user): bool { + unset($this->users[$user->getUID()]); + return true; + } +}