--- a/.devcontainer/Dockerfile
+++ /dev/null
@@ -1,91 +0,0 @@
-FROM ubuntu:noble
-
-ARG DEBIAN_FRONTEND=noninteractive
-
-# PHP
-RUN apt-get update -y && \
- apt install -y apache2 vim software-properties-common sudo nano gnupg2 wget curl git \
- lsb-release ca-certificates apt-transport-https && \
- add-apt-repository ppa:ondrej/php -y && \
- apt-get update -y
-
-RUN apt-get install --no-install-recommends -y \
- php8.4 \
- php8.4-common \
- php8.4-gd \
- php8.4-zip \
- php8.4-curl \
- php8.4-xml \
- php8.4-xmlrpc \
- php8.4-mbstring \
- php8.4-sqlite \
- php8.4-xdebug \
- php8.4-pgsql \
- php8.4-intl \
- php8.4-imagick \
- php8.4-gmp \
- php8.4-apcu \
- php8.4-bcmath \
- php8.4-redis \
- php8.4-soap \
- php8.4-imap \
- php8.4-opcache \
- php8.4-cli \
- php8.4-dev \
- libmagickcore-6.q16-7-extra \
- lsof \
- make \
- unzip
-
-# Composer
-RUN curl -sS https://getcomposer.org/installer -o /tmp/composer-setup.php && \
- curl -sS https://composer.github.io/installer.sig -o /tmp/composer-setup.sig && \
- php -r "if (hash_file('sha384', '/tmp/composer-setup.php') !== trim(file_get_contents('/tmp/composer-setup.sig'))) { echo 'Composer installation failed, invalid hash'; exit(1); }" && \
- php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer && \
- rm /tmp/composer-setup.php /tmp/composer-setup.sig
-
-RUN echo "xdebug.remote_enable = 1" >> /etc/php/8.4/cli/conf.d/20-xdebug.ini && \
- echo "xdebug.remote_autostart = 1" >> /etc/php/8.4/cli/conf.d/20-xdebug.ini && \
- echo "apc.enable_cli=1" >> /etc/php/8.4/cli/conf.d/20-apcu.ini
-
-# Autostart XDebug for apache
-RUN { \
- echo "xdebug.mode=debug"; \
- echo "xdebug.start_with_request=yes"; \
-} >> /etc/php/8.4/apache2/conf.d/20-xdebug.ini
-
-# Increase PHP memory limit to 512mb
-RUN sed -i 's/memory_limit = .*/memory_limit = 512M/' /etc/php/8.4/apache2/php.ini
-
-# Docker CLI only (for controlling host Docker via socket)
-RUN install -m 0755 -d /etc/apt/keyrings && \
- curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc && \
- chmod a+r /etc/apt/keyrings/docker.asc && \
- echo \
- "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
- $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
- tee /etc/apt/sources.list.d/docker.list > /dev/null && \
- apt-get update -y && \
- apt-get install -y docker-ce-cli && \
- ln -s /var/run/docker-host.sock /var/run/docker.sock
-
-# Dedicated DevContainer user runs Apache
-ENV APACHE_RUN_USER=devcontainer
-ENV APACHE_RUN_GROUP=devcontainer
-# Delete any existing user/group with UID/GID 1000 first
-RUN (getent passwd 1000 && userdel -r $(getent passwd 1000 | cut -d: -f1)) || true && \
- (getent group 1000 && groupdel $(getent group 1000 | cut -d: -f1)) || true && \
- groupadd -g 1000 ${APACHE_RUN_GROUP} && \
- useradd -u 1000 -g 1000 -ms /bin/bash ${APACHE_RUN_USER} && \
- adduser ${APACHE_RUN_USER} sudo && \
- echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers && \
- sed -ri "s/^export APACHE_RUN_USER=.*$/export APACHE_RUN_USER=${APACHE_RUN_USER}/" "/etc/apache2/envvars" && \
- sed -ri "s/^export APACHE_RUN_GROUP=.*$/export APACHE_RUN_GROUP=${APACHE_RUN_GROUP}/" "/etc/apache2/envvars"
-
-USER devcontainer
-
-# NVM
-RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
-RUN bash --login -i -c 'source /home/devcontainer/.bashrc && nvm install 22'
-
-WORKDIR /var/www/html
--- a/.devcontainer/Dockerfile.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
-SPDX-License-Identifier: AGPL-3.0-or-later
--- a/.devcontainer/README.md
+++ /dev/null
@@ -1,76 +0,0 @@
-
-# Nextcloud DevContainer
-
-## Usage
-
-Make sure you have the [VSCode DevContainer](https://code.visualstudio.com/docs/devcontainers/containers) extensions installed. If you open the project, VSCode will ask you if you want to open it inside of the DevContainer. If that's not the case, use F1→*Dev Containers: Open Folder in Container*.
-
-Alternatively open the project directly in [GitHub Codespaces](https://github.com/codespaces/new?hide_repo_select=true&ref=stable35&repo=60243197&skip_quickstart=true).
-
-That's already it. Everything else will be configured automatically by the Containers startup routine.
-
-## Credentials
-
-On first start the Container installs and configures Nextcloud with the following credentials:
-
-**Nextcloud Admin Login**
-
-Username: `admin`
-Password: `admin`
-
-**Postgres credentials**
-
-Username: `postgres`
-Password: `postgres`
-Database: `postgres`
-
-## Services
-
-The following services will be started:
-
-| Service | Local port | Description |
-|---------|------------|-------------|
-| Nextcloud (served via Apache) | `80` | The main application |
-| Mailhog | `8025` | SMTP email delivery for testing |
-| Adminer | `8080` | Database viewer. Use credentials from above and connect to `localhost` to get access to the NC database |
-
-## Permissions
-
-The container runs with the user `devcontainer` who is also running the Apache2 process. All mounted source files have
-proper permissions so that this user can access everything which is inside the current workspace. If you need to
-get root permissions for whatever reason, use `sudo su` or `sudo Show Output
-
- \`\`\`
- ${{ steps.compare.outputs.compare }}
- \`\`\`
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &$uid]
- )]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- getPath())]]>
-
- getPath())]]>
-
-
-
-
-
-
-
-
-
-
- RRULE]]>
- stream, strlen($this->boundary))]]>
-
- strtolower($matches[0]),
- urlencode($raw),
- )]]>
- UID]]>
- vobject->getBaseComponent()->{'LAST-MODIFIED'}]]>
- vobject->getBaseComponent()->{'LAST-MODIFIED'}]]>
-
-
-
-
- PRODID]]>
- VERSION]]>
- DTEND]]>
- DTSTART]]>
- UID]]>
- VEVENT]]>
- VEVENT]]>
- DTEND]]>
- DTSTART]]>
- {'SUMMARY'}]]>
- objectData['calendardata']]]>
-
-
-
-
-
-
-
-
-
-
-
- CLASS]]>
- DTEND]]>
- DTEND]]>
- DTSTART]]>
- DUE]]>
- DUE]]>
- DURATION]]>
- DURATION]]>
- RDATE]]>
- RDATE]]>
- RRULE]]>
- UID]]>
-
-
-
- getCalendarInCalendarHome($this->principalInfo['uri'], $calendarUri)]]>
-
-
-
- baseEvent->DTSTART]]>
-
- PRODID]]>
- VERSION]]>
- UID]]>
- PRODID]]>
- VERSION]]>
- VTIMEZONE]]>
- VTIMEZONE]]>
- VTIMEZONE]]>
- source, $end - $start)]]>
- source, $end - $start)]]>
-
-
-
-
- DTEND]]>
- DTSTART]]>
- DTSTART]]>
- STATUS]]>
-
- ]]>
- DTSTART]]>
- ORGANIZER]]>
-
- DTSTART]]>
- UID]]>
-
-
-
-
-
-
-
- DTSTART]]>
- VALARM]]>
- VALARM]]>
- ACTION]]>
- ACTION]]>
- ACTION]]>
- DURATION]]>
- DURATION]]>
- REPEAT]]>
- TRIGGER]]>
- TRIGGER]]>
- DTEND]]>
- DTSTART]]>
- DTSTART]]>
- DURATION]]>
- EXDATE]]>
- RDATE]]>
- RRULE]]>
- STATUS]]>
- {'RECURRENCE-ID'}]]>
- {'RECURRENCE-ID'}]]>
- {'RECURRENCE-ID'}]]>
- UID]]>
- UID]]>
- VTIMEZONE]]>
-
- parent->UID]]>
-
-
-
-
-
-
- recipientName]]>
-
-
- DTSTART]]>
- UID]]>
- message->VEVENT]]>
- message->VEVENT]]>
- VEVENT]]>
- {'UID'}]]>
-
-
-
-
- DTEND]]>
-
- METHOD]]>
- message->VEVENT]]>
- DTSTART]]>
- DTSTART]]>
- DTSTART]]>
- UID]]>
-
-
-
- $property,
- 'parameter' => $parameter,
- ]]]>
-
-
-
-
-
-
-
- VTIMEZONE]]>
- ATTENDEE]]>
- ORGANIZER]]>
- SEQUENCE]]>
- METHOD]]>
- {'X-PUBLISHED-TTL'}]]>
- UID]]>
- getKey()]]>
- getKey()]]>
-
-
- UID]]>
-
-
-
-
-
- UID]]>
-
- UID]]>
- VERSION]]>
- PHOTO]]>
-
- PHOTO]]>
-
- UID]]>
-
-
-
-
-
-
-
-
-
- principalUri]]>
- principalUri]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- dirContent]]>
-
- node]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- node]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- getPrincipalPropertiesByPath($path)]]>
-
-
-
-
-
-
-
-
-
-
-
-
- getShareType()]]>
-
-
-
-
- {'VEVENT'}]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- getTimestamp() !== false]]>
- arguments[0]->name]]>
- arguments[0]->name]]>
- backend->getArbiterPath()]]>
-
- VEVENT]]>
- ATTENDEE]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {'RECURRENCE-ID'}]]>
- getCursor()]]>
- FN]]>
- PHOTO]]>
- TEL]]>
- UID]]>
- getCursor()]]>
- DTSTART]]>
- DTSTART]]>
- DTSTART]]>
- DTSTART]]>
- DTSTART]]>
-
- getCursor()]]>
- COMPLETED]]>
- DUE]]>
-
-
-
-
-
-
- Note]]>
- REV]]>
- UID]]>
- DTEND]]>
- DTSTART]]>
- UID]]>
-
-
-
-
-
-
-
-
-
-
-
-
- currentStream]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
- getPath())]]>
- getPath())]]>
- $addressBookNode->getName(),
- 'displayName' => $addressBookInfo['{DAV:}displayname'],
- 'description' => $addressBookInfo['{' . CardDAVPlugin::NS_CARDDAV . '}addressbook-description'],
- 'vCards' => $vCards,
- ]]]>
-
- PRODID]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &$user1]
- )]]>
- &$user2]
- )]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &$shareWith]
- )]]>
-
-
-
-
- ]]>
- fileEncrypted[$fileId]]]>
-
- fileEncrypted[$fileId]]]>
-
- fileIsEncrypted]]>
- fileIsEncrypted]]>
- fileIsEncrypted]]>
- fileIsEncrypted]]>
- fileIsEncrypted]]>
- fileIsEncrypted]]>
- fileIsEncrypted]]>
- fileIsEncrypted]]>
- fileIsEncrypted]]>
- fileIsEncrypted]]>
- fileIsEncrypted]]>
- fileIsEncrypted]]>
- fileIsEncrypted]]>
- fileIsEncrypted]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- getUID())]]>
- getUID())]]>
- getUID())]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- getCode()]]>
-
- storageClass]]>
- ]]>
-
-
-
- getCode()]]>
- getCode()]]>
- getCode()]]>
- getCode()]]>
- getCode()]]>
- getCode()]]>
- getCode()]]>
- lastModified]]>
-
-
-
-
-
-
-
-
-
- $mountPoint,
- Filesystem::signal_param_mount_type => $mountType,
- Filesystem::signal_param_users => $applicable,
- ]
- )]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- sourceRootInfo]]>
-
-
-
- sourceRootInfo]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- data->getId()]]>
-
-
-
-
-
-
-
-
- Filesystem::normalizePath($file_path),
- 'trashPath' => Filesystem::normalizePath(static::getTrashFilename($filename, $timestamp))])]]>
- $targetPath, 'trashPath' => $sourcePath])]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- getUID() . '/files')]]>
- getUID())]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- getUID() . '/files')]]>
- getUID())]]>
-
-
-
-
-
-
-
-
- getUID())]]>
- getUID())]]>
-
-
-
-
- getUID())]]>
- getUID())]]>
-
-
-
-
-
-
-
-
-
-
- getUID())]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- session->get(self::WEBAUTHN_REGISTRATION))]]>
-
-
- getEMailAddress() => $user->getDisplayName()]]]>
-
-
-
-
-
-
-
- container->get($taskTypeClass);
- } catch (NotFoundExceptionInterface $e) {
- continue;
- } catch (ContainerExceptionInterface $e) {
- continue;
- }]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
- getUID())]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- getRgb())]]>
-
-
-
-
-
-
-
-
- getCode()]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 0)]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- config)]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- timeFactory->getTime()]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &$user]
- )]]>
-
- getCode()]]>
-
-
-
-
-
-
-
-
- getUserId())]]>
-
- userId, $identifier)]]>
-
-
-
-
- userId, $identifier)]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- session->get(self::WEBAUTHN_LOGIN))]]>
- &$uid]
- )]]>
- request->server]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- filterClasses]]>
- providerClasses]]>
- settingsClasses]]>
- settings]]>
-
-
- enabledAppsCache]]>
-
-
-
-
-
-
-
-
-
-
- inputStream, 'rb')]]>
- getOverwriteHost()]]>
-
-
-
-
- request->server]]>
- request->server]]>
- request->server]]>
-
-
-
-
-
- getCode()]]>
- appName]]>
- tar->extractInString($path)]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- providers]]>
- providers]]>
-
- providers]]>
-
- providers]]>
-
-
-
-
-
- container->get($registration->getService());
- } catch (Throwable $e) {
- $this->logger->error('Could not load calendar provider ' . $registration->getService() . ': ' . $e->getMessage(), [
- 'exception' => $e,
- ]);
- return [];
- }
-
- return $provider->getCalendars($principalUri, $calendarUris);
- }, $context->getCalendarProviders())
- )]]>
-
- container->get($registration->getService());
- } catch (Throwable $e) {
- $this->logger->error('Could not load calendar provider ' . $registration->getService() . ': ' . $e->getMessage(), [
- 'exception' => $e,
- ]);
- return [];
- }
-
- return $provider->getCalendars($principalUri, $calendarUris);
- }, $context->getCalendarProviders())]]>
- METHOD]]>
- VFREEBUSY]]>
- VFREEBUSY]]>
-
-
-
-
-
-
-
-
- delete($key)]]>
- set($key, $value)]]>
-
-
- request->server]]>
-
-
-
-
-
- getCode())]]>
-
- functionBuilder->lower($x)]]>
-
-
-
-
-
-
-
-
-
-
- l($type, $timestamp, [
- 'width' => $format,
- ])]]>
- l($type, $timestamp, [
- 'width' => $format,
- ])]]>
-
-
-
-
-
-
- manager->getFileForToken($this->data['user_id'], $this->data['file_id'], $this->data['file_path'])]]>
-
-
-
-
-
-
-
- folder]]>
-
- querySearchHelper->searchInCaches($query, [$this]))]]>
-
-
-
-
-
-
-
-
-
-
-
- querySearchHelper->searchInCaches($query, [$this]))]]>
-
-
-
-
- cacheInfoCache[$fileId]]]>
-
-
-
-
- view->hash($type, $this->path, $raw)]]>
-
- createNode($file->getPath(), $file);
- }, $files)]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- __call(__FUNCTION__, func_get_args())]]>
-
-
-
-
- parent]]>
-
- fileInfo]]>
-
- fileInfo]]>
- fileInfo]]>
-
-
-
-
-
-
-
- copyFromStorage($sourceStorage, $sourceInternalPath . '/' . $file, $targetInternalPath . '/' . $file)]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- getContent())]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- syslogId,
- 'MESSAGE=' . $this->logDetailsAsJSON($app, $message, $level))]]>
- getPrefix() . $key, $value, $ttl)]]>
-
-
-
-
-
-
-
-
-
-
-
- userToNotify]]>
-
- collectionName]]>
- request->server]]>
- value)]]>
- getDefaultCertificatesBundlePath())]]>
-
-
-
-
-
- dbPort)]]>
-
-
-
-
-
-
-
-
- circleToTeam(...), $this->circlesManager->probeCircles())]]>
-
-
-
-
-
-
-
-
-
- server]]>
- server]]>
- manager instanceof PublicEmitter]]>
-
-
-
-
-
-
-
-
- headers)]]>
-
-
-
-
-
-
-
-
-
-
-
The methods and constants adhere closely to the names and behavior used by the underlying ICU library.
- * @since 7.0 - */ -class IntlChar { - const UNICODE_VERSION = 6.3; - const CODEPOINT_MIN = 0; - const CODEPOINT_MAX = 1114111; - const FOLD_CASE_DEFAULT = 0; - const FOLD_CASE_EXCLUDE_SPECIAL_I = 1; - const PROPERTY_ALPHABETIC = 0; - const PROPERTY_BINARY_START = 0; - const PROPERTY_ASCII_HEX_DIGIT = 1; - const PROPERTY_BIDI_CONTROL = 2; - const PROPERTY_BIDI_MIRRORED = 3; - const PROPERTY_DASH = 4; - const PROPERTY_DEFAULT_IGNORABLE_CODE_POINT = 5; - const PROPERTY_DEPRECATED = 6; - const PROPERTY_DIACRITIC = 7; - const PROPERTY_EXTENDER = 8; - const PROPERTY_FULL_COMPOSITION_EXCLUSION = 9; - const PROPERTY_GRAPHEME_BASE = 10; - const PROPERTY_GRAPHEME_EXTEND = 11; - const PROPERTY_GRAPHEME_LINK = 12; - const PROPERTY_HEX_DIGIT = 13; - const PROPERTY_HYPHEN = 14; - const PROPERTY_ID_CONTINUE = 15; - const PROPERTY_ID_START = 16; - const PROPERTY_IDEOGRAPHIC = 17; - const PROPERTY_IDS_BINARY_OPERATOR = 18; - const PROPERTY_IDS_TRINARY_OPERATOR = 19; - const PROPERTY_JOIN_CONTROL = 20; - const PROPERTY_LOGICAL_ORDER_EXCEPTION = 21; - const PROPERTY_LOWERCASE = 22; - const PROPERTY_MATH = 23; - const PROPERTY_NONCHARACTER_CODE_POINT = 24; - const PROPERTY_QUOTATION_MARK = 25; - const PROPERTY_RADICAL = 26; - const PROPERTY_SOFT_DOTTED = 27; - const PROPERTY_TERMINAL_PUNCTUATION = 28; - const PROPERTY_UNIFIED_IDEOGRAPH = 29; - const PROPERTY_UPPERCASE = 30; - const PROPERTY_WHITE_SPACE = 31; - const PROPERTY_XID_CONTINUE = 32; - const PROPERTY_XID_START = 33; - const PROPERTY_CASE_SENSITIVE = 34; - const PROPERTY_S_TERM = 35; - const PROPERTY_VARIATION_SELECTOR = 36; - const PROPERTY_NFD_INERT = 37; - const PROPERTY_NFKD_INERT = 38; - const PROPERTY_NFC_INERT = 39; - const PROPERTY_NFKC_INERT = 40; - const PROPERTY_SEGMENT_STARTER = 41; - const PROPERTY_PATTERN_SYNTAX = 42; - const PROPERTY_PATTERN_WHITE_SPACE = 43; - const PROPERTY_POSIX_ALNUM = 44; - const PROPERTY_POSIX_BLANK = 45; - const PROPERTY_POSIX_GRAPH = 46; - const PROPERTY_POSIX_PRINT = 47; - const PROPERTY_POSIX_XDIGIT = 48; - const PROPERTY_CASED = 49; - const PROPERTY_CASE_IGNORABLE = 50; - const PROPERTY_CHANGES_WHEN_LOWERCASED = 51; - const PROPERTY_CHANGES_WHEN_UPPERCASED = 52; - const PROPERTY_CHANGES_WHEN_TITLECASED = 53; - const PROPERTY_CHANGES_WHEN_CASEFOLDED = 54; - const PROPERTY_CHANGES_WHEN_CASEMAPPED = 55; - const PROPERTY_CHANGES_WHEN_NFKC_CASEFOLDED = 56; - const PROPERTY_BINARY_LIMIT = 57; - const PROPERTY_BIDI_CLASS = 4096; - const PROPERTY_INT_START = 4096; - const PROPERTY_BLOCK = 4097; - const PROPERTY_CANONICAL_COMBINING_CLASS = 4098; - const PROPERTY_DECOMPOSITION_TYPE = 4099; - const PROPERTY_EAST_ASIAN_WIDTH = 4100; - const PROPERTY_GENERAL_CATEGORY = 4101; - const PROPERTY_JOINING_GROUP = 4102; - const PROPERTY_JOINING_TYPE = 4103; - const PROPERTY_LINE_BREAK = 4104; - const PROPERTY_NUMERIC_TYPE = 4105; - const PROPERTY_SCRIPT = 4106; - const PROPERTY_HANGUL_SYLLABLE_TYPE = 4107; - const PROPERTY_NFD_QUICK_CHECK = 4108; - const PROPERTY_NFKD_QUICK_CHECK = 4109; - const PROPERTY_NFC_QUICK_CHECK = 4110; - const PROPERTY_NFKC_QUICK_CHECK = 4111; - const PROPERTY_LEAD_CANONICAL_COMBINING_CLASS = 4112; - const PROPERTY_TRAIL_CANONICAL_COMBINING_CLASS = 4113; - const PROPERTY_GRAPHEME_CLUSTER_BREAK = 4114; - const PROPERTY_SENTENCE_BREAK = 4115; - const PROPERTY_WORD_BREAK = 4116; - const PROPERTY_BIDI_PAIRED_BRACKET_TYPE = 4117; - const PROPERTY_INT_LIMIT = 4118; - const PROPERTY_GENERAL_CATEGORY_MASK = 8192; - const PROPERTY_MASK_START = 8192; - const PROPERTY_MASK_LIMIT = 8193; - const PROPERTY_NUMERIC_VALUE = 12288; - const PROPERTY_DOUBLE_START = 12288; - const PROPERTY_DOUBLE_LIMIT = 12289; - const PROPERTY_AGE = 16384; - const PROPERTY_STRING_START = 16384; - const PROPERTY_BIDI_MIRRORING_GLYPH = 16385; - const PROPERTY_CASE_FOLDING = 16386; - const PROPERTY_ISO_COMMENT = 16387; - const PROPERTY_LOWERCASE_MAPPING = 16388; - const PROPERTY_NAME = 16389; - const PROPERTY_SIMPLE_CASE_FOLDING = 16390; - const PROPERTY_SIMPLE_LOWERCASE_MAPPING = 16391; - const PROPERTY_SIMPLE_TITLECASE_MAPPING = 16392; - const PROPERTY_SIMPLE_UPPERCASE_MAPPING = 16393; - const PROPERTY_TITLECASE_MAPPING = 16394; - const PROPERTY_UNICODE_1_NAME = 16395; - const PROPERTY_UPPERCASE_MAPPING = 16396; - const PROPERTY_BIDI_PAIRED_BRACKET = 16397; - const PROPERTY_STRING_LIMIT = 16398; - const PROPERTY_SCRIPT_EXTENSIONS = 28672; - const PROPERTY_OTHER_PROPERTY_START = 28672; - const PROPERTY_OTHER_PROPERTY_LIMIT = 28673; - const PROPERTY_INVALID_CODE = -1; - const CHAR_CATEGORY_UNASSIGNED = 0; - const CHAR_CATEGORY_GENERAL_OTHER_TYPES = 0; - const CHAR_CATEGORY_UPPERCASE_LETTER = 1; - const CHAR_CATEGORY_LOWERCASE_LETTER = 2; - const CHAR_CATEGORY_TITLECASE_LETTER = 3; - const CHAR_CATEGORY_MODIFIER_LETTER = 4; - const CHAR_CATEGORY_OTHER_LETTER = 5; - const CHAR_CATEGORY_NON_SPACING_MARK = 6; - const CHAR_CATEGORY_ENCLOSING_MARK = 7; - const CHAR_CATEGORY_COMBINING_SPACING_MARK = 8; - const CHAR_CATEGORY_DECIMAL_DIGIT_NUMBER = 9; - const CHAR_CATEGORY_LETTER_NUMBER = 10; - const CHAR_CATEGORY_OTHER_NUMBER = 11; - const CHAR_CATEGORY_SPACE_SEPARATOR = 12; - const CHAR_CATEGORY_LINE_SEPARATOR = 13; - const CHAR_CATEGORY_PARAGRAPH_SEPARATOR = 14; - const CHAR_CATEGORY_CONTROL_CHAR = 15; - const CHAR_CATEGORY_FORMAT_CHAR = 16; - const CHAR_CATEGORY_PRIVATE_USE_CHAR = 17; - const CHAR_CATEGORY_SURROGATE = 18; - const CHAR_CATEGORY_DASH_PUNCTUATION = 19; - const CHAR_CATEGORY_START_PUNCTUATION = 20; - const CHAR_CATEGORY_END_PUNCTUATION = 21; - const CHAR_CATEGORY_CONNECTOR_PUNCTUATION = 22; - const CHAR_CATEGORY_OTHER_PUNCTUATION = 23; - const CHAR_CATEGORY_MATH_SYMBOL = 24; - const CHAR_CATEGORY_CURRENCY_SYMBOL = 25; - const CHAR_CATEGORY_MODIFIER_SYMBOL = 26; - const CHAR_CATEGORY_OTHER_SYMBOL = 27; - const CHAR_CATEGORY_INITIAL_PUNCTUATION = 28; - const CHAR_CATEGORY_FINAL_PUNCTUATION = 29; - const CHAR_CATEGORY_CHAR_CATEGORY_COUNT = 30; - const CHAR_DIRECTION_LEFT_TO_RIGHT = 0; - const CHAR_DIRECTION_RIGHT_TO_LEFT = 1; - const CHAR_DIRECTION_EUROPEAN_NUMBER = 2; - const CHAR_DIRECTION_EUROPEAN_NUMBER_SEPARATOR = 3; - const CHAR_DIRECTION_EUROPEAN_NUMBER_TERMINATOR = 4; - const CHAR_DIRECTION_ARABIC_NUMBER = 5; - const CHAR_DIRECTION_COMMON_NUMBER_SEPARATOR = 6; - const CHAR_DIRECTION_BLOCK_SEPARATOR = 7; - const CHAR_DIRECTION_SEGMENT_SEPARATOR = 8; - const CHAR_DIRECTION_WHITE_SPACE_NEUTRAL = 9; - const CHAR_DIRECTION_OTHER_NEUTRAL = 10; - const CHAR_DIRECTION_LEFT_TO_RIGHT_EMBEDDING = 11; - const CHAR_DIRECTION_LEFT_TO_RIGHT_OVERRIDE = 12; - const CHAR_DIRECTION_RIGHT_TO_LEFT_ARABIC = 13; - const CHAR_DIRECTION_RIGHT_TO_LEFT_EMBEDDING = 14; - const CHAR_DIRECTION_RIGHT_TO_LEFT_OVERRIDE = 15; - const CHAR_DIRECTION_POP_DIRECTIONAL_FORMAT = 16; - const CHAR_DIRECTION_DIR_NON_SPACING_MARK = 17; - const CHAR_DIRECTION_BOUNDARY_NEUTRAL = 18; - const CHAR_DIRECTION_FIRST_STRONG_ISOLATE = 19; - const CHAR_DIRECTION_LEFT_TO_RIGHT_ISOLATE = 20; - const CHAR_DIRECTION_RIGHT_TO_LEFT_ISOLATE = 21; - const CHAR_DIRECTION_POP_DIRECTIONAL_ISOLATE = 22; - const CHAR_DIRECTION_CHAR_DIRECTION_COUNT = 23; - const BLOCK_CODE_NO_BLOCK = 0; - const BLOCK_CODE_BASIC_LATIN = 1; - const BLOCK_CODE_LATIN_1_SUPPLEMENT = 2; - const BLOCK_CODE_LATIN_EXTENDED_A = 3; - const BLOCK_CODE_LATIN_EXTENDED_B = 4; - const BLOCK_CODE_IPA_EXTENSIONS = 5; - const BLOCK_CODE_SPACING_MODIFIER_LETTERS = 6; - const BLOCK_CODE_COMBINING_DIACRITICAL_MARKS = 7; - const BLOCK_CODE_GREEK = 8; - const BLOCK_CODE_CYRILLIC = 9; - const BLOCK_CODE_ARMENIAN = 10; - const BLOCK_CODE_HEBREW = 11; - const BLOCK_CODE_ARABIC = 12; - const BLOCK_CODE_SYRIAC = 13; - const BLOCK_CODE_THAANA = 14; - const BLOCK_CODE_DEVANAGARI = 15; - const BLOCK_CODE_BENGALI = 16; - const BLOCK_CODE_GURMUKHI = 17; - const BLOCK_CODE_GUJARATI = 18; - const BLOCK_CODE_ORIYA = 19; - const BLOCK_CODE_TAMIL = 20; - const BLOCK_CODE_TELUGU = 21; - const BLOCK_CODE_KANNADA = 22; - const BLOCK_CODE_MALAYALAM = 23; - const BLOCK_CODE_SINHALA = 24; - const BLOCK_CODE_THAI = 25; - const BLOCK_CODE_LAO = 26; - const BLOCK_CODE_TIBETAN = 27; - const BLOCK_CODE_MYANMAR = 28; - const BLOCK_CODE_GEORGIAN = 29; - const BLOCK_CODE_HANGUL_JAMO = 30; - const BLOCK_CODE_ETHIOPIC = 31; - const BLOCK_CODE_CHEROKEE = 32; - const BLOCK_CODE_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS = 33; - const BLOCK_CODE_OGHAM = 34; - const BLOCK_CODE_RUNIC = 35; - const BLOCK_CODE_KHMER = 36; - const BLOCK_CODE_MONGOLIAN = 37; - const BLOCK_CODE_LATIN_EXTENDED_ADDITIONAL = 38; - const BLOCK_CODE_GREEK_EXTENDED = 39; - const BLOCK_CODE_GENERAL_PUNCTUATION = 40; - const BLOCK_CODE_SUPERSCRIPTS_AND_SUBSCRIPTS = 41; - const BLOCK_CODE_CURRENCY_SYMBOLS = 42; - const BLOCK_CODE_COMBINING_MARKS_FOR_SYMBOLS = 43; - const BLOCK_CODE_LETTERLIKE_SYMBOLS = 44; - const BLOCK_CODE_NUMBER_FORMS = 45; - const BLOCK_CODE_ARROWS = 46; - const BLOCK_CODE_MATHEMATICAL_OPERATORS = 47; - const BLOCK_CODE_MISCELLANEOUS_TECHNICAL = 48; - const BLOCK_CODE_CONTROL_PICTURES = 49; - const BLOCK_CODE_OPTICAL_CHARACTER_RECOGNITION = 50; - const BLOCK_CODE_ENCLOSED_ALPHANUMERICS = 51; - const BLOCK_CODE_BOX_DRAWING = 52; - const BLOCK_CODE_BLOCK_ELEMENTS = 53; - const BLOCK_CODE_GEOMETRIC_SHAPES = 54; - const BLOCK_CODE_MISCELLANEOUS_SYMBOLS = 55; - const BLOCK_CODE_DINGBATS = 56; - const BLOCK_CODE_BRAILLE_PATTERNS = 57; - const BLOCK_CODE_CJK_RADICALS_SUPPLEMENT = 58; - const BLOCK_CODE_KANGXI_RADICALS = 59; - const BLOCK_CODE_IDEOGRAPHIC_DESCRIPTION_CHARACTERS = 60; - const BLOCK_CODE_CJK_SYMBOLS_AND_PUNCTUATION = 61; - const BLOCK_CODE_HIRAGANA = 62; - const BLOCK_CODE_KATAKANA = 63; - const BLOCK_CODE_BOPOMOFO = 64; - const BLOCK_CODE_HANGUL_COMPATIBILITY_JAMO = 65; - const BLOCK_CODE_KANBUN = 66; - const BLOCK_CODE_BOPOMOFO_EXTENDED = 67; - const BLOCK_CODE_ENCLOSED_CJK_LETTERS_AND_MONTHS = 68; - const BLOCK_CODE_CJK_COMPATIBILITY = 69; - const BLOCK_CODE_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A = 70; - const BLOCK_CODE_CJK_UNIFIED_IDEOGRAPHS = 71; - const BLOCK_CODE_YI_SYLLABLES = 72; - const BLOCK_CODE_YI_RADICALS = 73; - const BLOCK_CODE_HANGUL_SYLLABLES = 74; - const BLOCK_CODE_HIGH_SURROGATES = 75; - const BLOCK_CODE_HIGH_PRIVATE_USE_SURROGATES = 76; - const BLOCK_CODE_LOW_SURROGATES = 77; - const BLOCK_CODE_PRIVATE_USE_AREA = 78; - const BLOCK_CODE_PRIVATE_USE = 78; - const BLOCK_CODE_CJK_COMPATIBILITY_IDEOGRAPHS = 79; - const BLOCK_CODE_ALPHABETIC_PRESENTATION_FORMS = 80; - const BLOCK_CODE_ARABIC_PRESENTATION_FORMS_A = 81; - const BLOCK_CODE_COMBINING_HALF_MARKS = 82; - const BLOCK_CODE_CJK_COMPATIBILITY_FORMS = 83; - const BLOCK_CODE_SMALL_FORM_VARIANTS = 84; - const BLOCK_CODE_ARABIC_PRESENTATION_FORMS_B = 85; - const BLOCK_CODE_SPECIALS = 86; - const BLOCK_CODE_HALFWIDTH_AND_FULLWIDTH_FORMS = 87; - const BLOCK_CODE_OLD_ITALIC = 88; - const BLOCK_CODE_GOTHIC = 89; - const BLOCK_CODE_DESERET = 90; - const BLOCK_CODE_BYZANTINE_MUSICAL_SYMBOLS = 91; - const BLOCK_CODE_MUSICAL_SYMBOLS = 92; - const BLOCK_CODE_MATHEMATICAL_ALPHANUMERIC_SYMBOLS = 93; - const BLOCK_CODE_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B = 94; - const BLOCK_CODE_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT = 95; - const BLOCK_CODE_TAGS = 96; - const BLOCK_CODE_CYRILLIC_SUPPLEMENT = 97; - const BLOCK_CODE_CYRILLIC_SUPPLEMENTARY = 97; - const BLOCK_CODE_TAGALOG = 98; - const BLOCK_CODE_HANUNOO = 99; - const BLOCK_CODE_BUHID = 100; - const BLOCK_CODE_TAGBANWA = 101; - const BLOCK_CODE_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A = 102; - const BLOCK_CODE_SUPPLEMENTAL_ARROWS_A = 103; - const BLOCK_CODE_SUPPLEMENTAL_ARROWS_B = 104; - const BLOCK_CODE_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B = 105; - const BLOCK_CODE_SUPPLEMENTAL_MATHEMATICAL_OPERATORS = 106; - const BLOCK_CODE_KATAKANA_PHONETIC_EXTENSIONS = 107; - const BLOCK_CODE_VARIATION_SELECTORS = 108; - const BLOCK_CODE_SUPPLEMENTARY_PRIVATE_USE_AREA_A = 109; - const BLOCK_CODE_SUPPLEMENTARY_PRIVATE_USE_AREA_B = 110; - const BLOCK_CODE_LIMBU = 111; - const BLOCK_CODE_TAI_LE = 112; - const BLOCK_CODE_KHMER_SYMBOLS = 113; - const BLOCK_CODE_PHONETIC_EXTENSIONS = 114; - const BLOCK_CODE_MISCELLANEOUS_SYMBOLS_AND_ARROWS = 115; - const BLOCK_CODE_YIJING_HEXAGRAM_SYMBOLS = 116; - const BLOCK_CODE_LINEAR_B_SYLLABARY = 117; - const BLOCK_CODE_LINEAR_B_IDEOGRAMS = 118; - const BLOCK_CODE_AEGEAN_NUMBERS = 119; - const BLOCK_CODE_UGARITIC = 120; - const BLOCK_CODE_SHAVIAN = 121; - const BLOCK_CODE_OSMANYA = 122; - const BLOCK_CODE_CYPRIOT_SYLLABARY = 123; - const BLOCK_CODE_TAI_XUAN_JING_SYMBOLS = 124; - const BLOCK_CODE_VARIATION_SELECTORS_SUPPLEMENT = 125; - const BLOCK_CODE_ANCIENT_GREEK_MUSICAL_NOTATION = 126; - const BLOCK_CODE_ANCIENT_GREEK_NUMBERS = 127; - const BLOCK_CODE_ARABIC_SUPPLEMENT = 128; - const BLOCK_CODE_BUGINESE = 129; - const BLOCK_CODE_CJK_STROKES = 130; - const BLOCK_CODE_COMBINING_DIACRITICAL_MARKS_SUPPLEMENT = 131; - const BLOCK_CODE_COPTIC = 132; - const BLOCK_CODE_ETHIOPIC_EXTENDED = 133; - const BLOCK_CODE_ETHIOPIC_SUPPLEMENT = 134; - const BLOCK_CODE_GEORGIAN_SUPPLEMENT = 135; - const BLOCK_CODE_GLAGOLITIC = 136; - const BLOCK_CODE_KHAROSHTHI = 137; - const BLOCK_CODE_MODIFIER_TONE_LETTERS = 138; - const BLOCK_CODE_NEW_TAI_LUE = 139; - const BLOCK_CODE_OLD_PERSIAN = 140; - const BLOCK_CODE_PHONETIC_EXTENSIONS_SUPPLEMENT = 141; - const BLOCK_CODE_SUPPLEMENTAL_PUNCTUATION = 142; - const BLOCK_CODE_SYLOTI_NAGRI = 143; - const BLOCK_CODE_TIFINAGH = 144; - const BLOCK_CODE_VERTICAL_FORMS = 145; - const BLOCK_CODE_NKO = 146; - const BLOCK_CODE_BALINESE = 147; - const BLOCK_CODE_LATIN_EXTENDED_C = 148; - const BLOCK_CODE_LATIN_EXTENDED_D = 149; - const BLOCK_CODE_PHAGS_PA = 150; - const BLOCK_CODE_PHOENICIAN = 151; - const BLOCK_CODE_CUNEIFORM = 152; - const BLOCK_CODE_CUNEIFORM_NUMBERS_AND_PUNCTUATION = 153; - const BLOCK_CODE_COUNTING_ROD_NUMERALS = 154; - const BLOCK_CODE_SUNDANESE = 155; - const BLOCK_CODE_LEPCHA = 156; - const BLOCK_CODE_OL_CHIKI = 157; - const BLOCK_CODE_CYRILLIC_EXTENDED_A = 158; - const BLOCK_CODE_VAI = 159; - const BLOCK_CODE_CYRILLIC_EXTENDED_B = 160; - const BLOCK_CODE_SAURASHTRA = 161; - const BLOCK_CODE_KAYAH_LI = 162; - const BLOCK_CODE_REJANG = 163; - const BLOCK_CODE_CHAM = 164; - const BLOCK_CODE_ANCIENT_SYMBOLS = 165; - const BLOCK_CODE_PHAISTOS_DISC = 166; - const BLOCK_CODE_LYCIAN = 167; - const BLOCK_CODE_CARIAN = 168; - const BLOCK_CODE_LYDIAN = 169; - const BLOCK_CODE_MAHJONG_TILES = 170; - const BLOCK_CODE_DOMINO_TILES = 171; - const BLOCK_CODE_SAMARITAN = 172; - const BLOCK_CODE_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED = 173; - const BLOCK_CODE_TAI_THAM = 174; - const BLOCK_CODE_VEDIC_EXTENSIONS = 175; - const BLOCK_CODE_LISU = 176; - const BLOCK_CODE_BAMUM = 177; - const BLOCK_CODE_COMMON_INDIC_NUMBER_FORMS = 178; - const BLOCK_CODE_DEVANAGARI_EXTENDED = 179; - const BLOCK_CODE_HANGUL_JAMO_EXTENDED_A = 180; - const BLOCK_CODE_JAVANESE = 181; - const BLOCK_CODE_MYANMAR_EXTENDED_A = 182; - const BLOCK_CODE_TAI_VIET = 183; - const BLOCK_CODE_MEETEI_MAYEK = 184; - const BLOCK_CODE_HANGUL_JAMO_EXTENDED_B = 185; - const BLOCK_CODE_IMPERIAL_ARAMAIC = 186; - const BLOCK_CODE_OLD_SOUTH_ARABIAN = 187; - const BLOCK_CODE_AVESTAN = 188; - const BLOCK_CODE_INSCRIPTIONAL_PARTHIAN = 189; - const BLOCK_CODE_INSCRIPTIONAL_PAHLAVI = 190; - const BLOCK_CODE_OLD_TURKIC = 191; - const BLOCK_CODE_RUMI_NUMERAL_SYMBOLS = 192; - const BLOCK_CODE_KAITHI = 193; - const BLOCK_CODE_EGYPTIAN_HIEROGLYPHS = 194; - const BLOCK_CODE_ENCLOSED_ALPHANUMERIC_SUPPLEMENT = 195; - const BLOCK_CODE_ENCLOSED_IDEOGRAPHIC_SUPPLEMENT = 196; - const BLOCK_CODE_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C = 197; - const BLOCK_CODE_MANDAIC = 198; - const BLOCK_CODE_BATAK = 199; - const BLOCK_CODE_ETHIOPIC_EXTENDED_A = 200; - const BLOCK_CODE_BRAHMI = 201; - const BLOCK_CODE_BAMUM_SUPPLEMENT = 202; - const BLOCK_CODE_KANA_SUPPLEMENT = 203; - const BLOCK_CODE_PLAYING_CARDS = 204; - const BLOCK_CODE_MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS = 205; - const BLOCK_CODE_EMOTICONS = 206; - const BLOCK_CODE_TRANSPORT_AND_MAP_SYMBOLS = 207; - const BLOCK_CODE_ALCHEMICAL_SYMBOLS = 208; - const BLOCK_CODE_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D = 209; - const BLOCK_CODE_ARABIC_EXTENDED_A = 210; - const BLOCK_CODE_ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS = 211; - const BLOCK_CODE_CHAKMA = 212; - const BLOCK_CODE_MEETEI_MAYEK_EXTENSIONS = 213; - const BLOCK_CODE_MEROITIC_CURSIVE = 214; - const BLOCK_CODE_MEROITIC_HIEROGLYPHS = 215; - const BLOCK_CODE_MIAO = 216; - const BLOCK_CODE_SHARADA = 217; - const BLOCK_CODE_SORA_SOMPENG = 218; - const BLOCK_CODE_SUNDANESE_SUPPLEMENT = 219; - const BLOCK_CODE_TAKRI = 220; - const BLOCK_CODE_BASSA_VAH = 221; - const BLOCK_CODE_CAUCASIAN_ALBANIAN = 222; - const BLOCK_CODE_COPTIC_EPACT_NUMBERS = 223; - const BLOCK_CODE_COMBINING_DIACRITICAL_MARKS_EXTENDED = 224; - const BLOCK_CODE_DUPLOYAN = 225; - const BLOCK_CODE_ELBASAN = 226; - const BLOCK_CODE_GEOMETRIC_SHAPES_EXTENDED = 227; - const BLOCK_CODE_GRANTHA = 228; - const BLOCK_CODE_KHOJKI = 229; - const BLOCK_CODE_KHUDAWADI = 230; - const BLOCK_CODE_LATIN_EXTENDED_E = 231; - const BLOCK_CODE_LINEAR_A = 232; - const BLOCK_CODE_MAHAJANI = 233; - const BLOCK_CODE_MANICHAEAN = 234; - const BLOCK_CODE_MENDE_KIKAKUI = 235; - const BLOCK_CODE_MODI = 236; - const BLOCK_CODE_MRO = 237; - const BLOCK_CODE_MYANMAR_EXTENDED_B = 238; - const BLOCK_CODE_NABATAEAN = 239; - const BLOCK_CODE_OLD_NORTH_ARABIAN = 240; - const BLOCK_CODE_OLD_PERMIC = 241; - const BLOCK_CODE_ORNAMENTAL_DINGBATS = 242; - const BLOCK_CODE_PAHAWH_HMONG = 243; - const BLOCK_CODE_PALMYRENE = 244; - const BLOCK_CODE_PAU_CIN_HAU = 245; - const BLOCK_CODE_PSALTER_PAHLAVI = 246; - const BLOCK_CODE_SHORTHAND_FORMAT_CONTROLS = 247; - const BLOCK_CODE_SIDDHAM = 248; - const BLOCK_CODE_SINHALA_ARCHAIC_NUMBERS = 249; - const BLOCK_CODE_SUPPLEMENTAL_ARROWS_C = 250; - const BLOCK_CODE_TIRHUTA = 251; - const BLOCK_CODE_WARANG_CITI = 252; - const BLOCK_CODE_COUNT = 263; - const BLOCK_CODE_INVALID_CODE = -1; - const BPT_NONE = 0; - const BPT_OPEN = 1; - const BPT_CLOSE = 2; - const BPT_COUNT = 3; - const EA_NEUTRAL = 0; - const EA_AMBIGUOUS = 1; - const EA_HALFWIDTH = 2; - const EA_FULLWIDTH = 3; - const EA_NARROW = 4; - const EA_WIDE = 5; - const EA_COUNT = 6; - const UNICODE_CHAR_NAME = 0; - const UNICODE_10_CHAR_NAME = 1; - const EXTENDED_CHAR_NAME = 2; - const CHAR_NAME_ALIAS = 3; - const CHAR_NAME_CHOICE_COUNT = 4; - const SHORT_PROPERTY_NAME = 0; - const LONG_PROPERTY_NAME = 1; - const PROPERTY_NAME_CHOICE_COUNT = 2; - const DT_NONE = 0; - const DT_CANONICAL = 1; - const DT_COMPAT = 2; - const DT_CIRCLE = 3; - const DT_FINAL = 4; - const DT_FONT = 5; - const DT_FRACTION = 6; - const DT_INITIAL = 7; - const DT_ISOLATED = 8; - const DT_MEDIAL = 9; - const DT_NARROW = 10; - const DT_NOBREAK = 11; - const DT_SMALL = 12; - const DT_SQUARE = 13; - const DT_SUB = 14; - const DT_SUPER = 15; - const DT_VERTICAL = 16; - const DT_WIDE = 17; - const DT_COUNT = 18; - const JT_NON_JOINING = 0; - const JT_JOIN_CAUSING = 1; - const JT_DUAL_JOINING = 2; - const JT_LEFT_JOINING = 3; - const JT_RIGHT_JOINING = 4; - const JT_TRANSPARENT = 5; - const JT_COUNT = 6; - const JG_NO_JOINING_GROUP = 0; - const JG_AIN = 1; - const JG_ALAPH = 2; - const JG_ALEF = 3; - const JG_BEH = 4; - const JG_BETH = 5; - const JG_DAL = 6; - const JG_DALATH_RISH = 7; - const JG_E = 8; - const JG_FEH = 9; - const JG_FINAL_SEMKATH = 10; - const JG_GAF = 11; - const JG_GAMAL = 12; - const JG_HAH = 13; - const JG_TEH_MARBUTA_GOAL = 14; - const JG_HAMZA_ON_HEH_GOAL = 14; - const JG_HE = 15; - const JG_HEH = 16; - const JG_HEH_GOAL = 17; - const JG_HETH = 18; - const JG_KAF = 19; - const JG_KAPH = 20; - const JG_KNOTTED_HEH = 21; - const JG_LAM = 22; - const JG_LAMADH = 23; - const JG_MEEM = 24; - const JG_MIM = 25; - const JG_NOON = 26; - const JG_NUN = 27; - const JG_PE = 28; - const JG_QAF = 29; - const JG_QAPH = 30; - const JG_REH = 31; - const JG_REVERSED_PE = 32; - const JG_SAD = 33; - const JG_SADHE = 34; - const JG_SEEN = 35; - const JG_SEMKATH = 36; - const JG_SHIN = 37; - const JG_SWASH_KAF = 38; - const JG_SYRIAC_WAW = 39; - const JG_TAH = 40; - const JG_TAW = 41; - const JG_TEH_MARBUTA = 42; - const JG_TETH = 43; - const JG_WAW = 44; - const JG_YEH = 45; - const JG_YEH_BARREE = 46; - const JG_YEH_WITH_TAIL = 47; - const JG_YUDH = 48; - const JG_YUDH_HE = 49; - const JG_ZAIN = 50; - const JG_FE = 51; - const JG_KHAPH = 52; - const JG_ZHAIN = 53; - const JG_BURUSHASKI_YEH_BARREE = 54; - const JG_FARSI_YEH = 55; - const JG_NYA = 56; - const JG_ROHINGYA_YEH = 57; - const JG_MANICHAEAN_ALEPH = 58; - const JG_MANICHAEAN_AYIN = 59; - const JG_MANICHAEAN_BETH = 60; - const JG_MANICHAEAN_DALETH = 61; - const JG_MANICHAEAN_DHAMEDH = 62; - const JG_MANICHAEAN_FIVE = 63; - const JG_MANICHAEAN_GIMEL = 64; - const JG_MANICHAEAN_HETH = 65; - const JG_MANICHAEAN_HUNDRED = 66; - const JG_MANICHAEAN_KAPH = 67; - const JG_MANICHAEAN_LAMEDH = 68; - const JG_MANICHAEAN_MEM = 69; - const JG_MANICHAEAN_NUN = 70; - const JG_MANICHAEAN_ONE = 71; - const JG_MANICHAEAN_PE = 72; - const JG_MANICHAEAN_QOPH = 73; - const JG_MANICHAEAN_RESH = 74; - const JG_MANICHAEAN_SADHE = 75; - const JG_MANICHAEAN_SAMEKH = 76; - const JG_MANICHAEAN_TAW = 77; - const JG_MANICHAEAN_TEN = 78; - const JG_MANICHAEAN_TETH = 79; - const JG_MANICHAEAN_THAMEDH = 80; - const JG_MANICHAEAN_TWENTY = 81; - const JG_MANICHAEAN_WAW = 82; - const JG_MANICHAEAN_YODH = 83; - const JG_MANICHAEAN_ZAYIN = 84; - const JG_STRAIGHT_WAW = 85; - const JG_COUNT = 86; - const GCB_OTHER = 0; - const GCB_CONTROL = 1; - const GCB_CR = 2; - const GCB_EXTEND = 3; - const GCB_L = 4; - const GCB_LF = 5; - const GCB_LV = 6; - const GCB_LVT = 7; - const GCB_T = 8; - const GCB_V = 9; - const GCB_SPACING_MARK = 10; - const GCB_PREPEND = 11; - const GCB_REGIONAL_INDICATOR = 12; - const GCB_COUNT = 13; - const WB_OTHER = 0; - const WB_ALETTER = 1; - const WB_FORMAT = 2; - const WB_KATAKANA = 3; - const WB_MIDLETTER = 4; - const WB_MIDNUM = 5; - const WB_NUMERIC = 6; - const WB_EXTENDNUMLET = 7; - const WB_CR = 8; - const WB_EXTEND = 9; - const WB_LF = 10; - const WB_MIDNUMLET = 11; - const WB_NEWLINE = 12; - const WB_REGIONAL_INDICATOR = 13; - const WB_HEBREW_LETTER = 14; - const WB_SINGLE_QUOTE = 15; - const WB_DOUBLE_QUOTE = 16; - const WB_COUNT = 17; - const SB_OTHER = 0; - const SB_ATERM = 1; - const SB_CLOSE = 2; - const SB_FORMAT = 3; - const SB_LOWER = 4; - const SB_NUMERIC = 5; - const SB_OLETTER = 6; - const SB_SEP = 7; - const SB_SP = 8; - const SB_STERM = 9; - const SB_UPPER = 10; - const SB_CR = 11; - const SB_EXTEND = 12; - const SB_LF = 13; - const SB_SCONTINUE = 14; - const SB_COUNT = 15; - const LB_UNKNOWN = 0; - const LB_AMBIGUOUS = 1; - const LB_ALPHABETIC = 2; - const LB_BREAK_BOTH = 3; - const LB_BREAK_AFTER = 4; - const LB_BREAK_BEFORE = 5; - const LB_MANDATORY_BREAK = 6; - const LB_CONTINGENT_BREAK = 7; - const LB_CLOSE_PUNCTUATION = 8; - const LB_COMBINING_MARK = 9; - const LB_CARRIAGE_RETURN = 10; - const LB_EXCLAMATION = 11; - const LB_GLUE = 12; - const LB_HYPHEN = 13; - const LB_IDEOGRAPHIC = 14; - const LB_INSEPARABLE = 15; - const LB_INSEPERABLE = 15; - const LB_INFIX_NUMERIC = 16; - const LB_LINE_FEED = 17; - const LB_NONSTARTER = 18; - const LB_NUMERIC = 19; - const LB_OPEN_PUNCTUATION = 20; - const LB_POSTFIX_NUMERIC = 21; - const LB_PREFIX_NUMERIC = 22; - const LB_QUOTATION = 23; - const LB_COMPLEX_CONTEXT = 24; - const LB_SURROGATE = 25; - const LB_SPACE = 26; - const LB_BREAK_SYMBOLS = 27; - const LB_ZWSPACE = 28; - const LB_NEXT_LINE = 29; - const LB_WORD_JOINER = 30; - const LB_H2 = 31; - const LB_H3 = 32; - const LB_JL = 33; - const LB_JT = 34; - const LB_JV = 35; - const LB_CLOSE_PARENTHESIS = 36; - const LB_CONDITIONAL_JAPANESE_STARTER = 37; - const LB_HEBREW_LETTER = 38; - const LB_REGIONAL_INDICATOR = 39; - const LB_COUNT = 40; - const NT_NONE = 0; - const NT_DECIMAL = 1; - const NT_DIGIT = 2; - const NT_NUMERIC = 3; - const NT_COUNT = 4; - const HST_NOT_APPLICABLE = 0; - const HST_LEADING_JAMO = 1; - const HST_VOWEL_JAMO = 2; - const HST_TRAILING_JAMO = 3; - const HST_LV_SYLLABLE = 4; - const HST_LVT_SYLLABLE = 5; - const HST_COUNT = 6; - - /** - * Check a binary Unicode property for a code point - * @link https://php.net/manual/ru/intlchar.hasbinaryproperty.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @param int $property The Unicode property to lookup (see the IntlChar::PROPERTY_* constants). - * @return bool|null Returns TRUE or FALSE according to the binary Unicode property value for codepoint. - * Also FALSE if property is out of bounds or if the Unicode version does not have data for the property at all, or not for this code point. - * Or NULL if codepoint is out of bounds. - * @since 7.0 - */ - static public function hasBinaryProperty($codepoint, $property){} - - /** - * @link https://php.net/manual/ru/intlchar.charage.php - * Get the "age" of the code point - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return array|null The Unicode version number, as an array. For example, version 1.3.31.2 would be represented as [1, 3, 31, 2]. - * Or NULL if codepoint is out of bounds. - * @since 7.0 - */ - public static function charAge($codepoint) {} - - /** - * @link https://php.net/manual/ru/intlchar.chardigitvalue.php - * Get the decimal digit value of a decimal digit character - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return int|null The decimal digit value of codepoint, or -1 if it is not a decimal digit character. - * Or NULL if codepoint is out of bounds. - * @since 7.0 - */ - public static function charDigitValue($codepoint){} - - /** - * Get bidirectional category value for a code point - * @link https://php.net/manual/ru/intlchar.chardirection.php - * @param int|string $codepointThe integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}")
- * @return int|nullThe bidirectional category value; one of the following constants: - *
- *Full name of the Unicode character.
- * @param int $nameChoice [optional]- * Which set of names to use for the lookup. Can be any of these constants: - *
Or NULL if codepoint is out of bound.
The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return string|null A string containing the single character specified by the Unicode code point value. - * Or NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function chr ($codepoint) - { - - } - - /** - * Get the decimal digit value of a code point for a given radix - * @link https://php.net/manual/ru/intlchar.digit.php - * @param int|string $codepointThe integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}")
- * @param int $radixThe radix (defaults to 10).
- * @return int|false|null Returns the numeric value represented by the character in the specified radix, - * or FALSE if there is no value or if the value exceeds the radix, - * or NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function digit ($codepoint,$radix = 10 ) {} - - /** - * Enumerate all assigned Unicode characters within a range - * @link https://php.net/manual/ru/intlchar.enumcharnames.php - * @param int|string $start The first code point in the enumeration range. - * @param int|string $limit One more than the last code point in the enumeration range (the first one after the range). - * @param callable $callback- * The function that is to be called for each character name. The following three arguments will be passed into it: - *
- * Selector for which kind of names to enumerate. Can be any of these constants: - *
- * The function that is to be called for each contiguous range of code points with the same general category. - * The following three arguments will be passed into it: - *
The number to convert to a character.
- * @param int $radix [optional]The radix (defaults to 10).
- * @return int The character representation (as a string) of the specified digit in the specified radix. - * @since 7.0 - */ - public static function forDigit ($digit, $radix = 10) {} - - /** - * Get the paired bracket character for a code point - * @link https://php.net/manual/ru/intlchar.getbidipairedbracket.php - * @param int|string $codepointThe integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}")
- * @return int|string|null Returns the paired bracket code point, or codepoint itself if there is no such mapping. - * The return type will be integer unless the code point was passed as a UTF-8 string, in which case a string will be returned. - * Or NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function getBidiPairedBracket($codepoint) {} - - /** - * Get the Unicode allocation block containing a code point - * @link https://php.net/manual/ru/intlchar.getblockcode.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return int|null Returns the block value for codepoint, or NULL if codepoint is out of bound. - * See the IntlChar::BLOCK_CODE_* constants for possible return values. - * @since 7.0 - */ - public static function getBlockCode($codepoint) {} - - /** - * Get the combining class of a code point - * @link https://php.net/manual/ru/intlchar.getcombiningclass.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return int|null Returns the combining class of the character. - * Or NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function getCombiningClass ($codepoint) {} - - /** - * Get the FC_NFKC_Closure property for a code point - * @link https://php.net/manual/ru/intlchar.getfc-nfkc-closure.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return string|false|null Returns the FC_NFKC_Closure property string for the codepoint, or an empty string if there is none, - * or NULL if codepoint is out of bound, - * or FALSE if there was an error. - * @since 7.0 - */ - public static function getFC_NFKC_Closure ($codepoint) {} - - /** - * Get the max value for a Unicode property - * @link https://php.net/manual/ru/intlchar.getintpropertymaxvalue.php - * @param int $property The Unicode property to lookup (see the IntlChar::PROPERTY_* constants). - * @return int The maximum value returned by {@see IntlChar::getIntPropertyValue()} for a Unicode property. <=0 if the property selector is out of range. - * @since 7.0 - */ - public static function getIntPropertyMaxValue ($property) {} - - /** - * Get the min value for a Unicode property - * @link https://php.net/manual/ru/intlchar.getintpropertyminvalue.php - * @param int $property The Unicode property to lookup (see the IntlChar::PROPERTY_* constants). - * @return int The minimum value returned by {@see IntlChar::getIntPropertyValue()} for a Unicode property. 0 if the property selector is out of range. - * @since 7.0 - */ - public static function getIntPropertyMinValue ($property) {} - - /** - * Get the value for a Unicode property for a code point - * @link https://php.net/manual/ru/intlchar.getintpropertyvalue.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @param int $property The Unicode property to lookup (see the IntlChar::PROPERTY_* constants). - * @return int|null- * Returns the numeric value that is directly the property value or, for enumerated properties, corresponds to the - * numeric value of the enumerated constant of the respective property value enumeration type. - *
- *- * Returns 0 or 1 (for FALSE/TRUE) for binary Unicode properties. - *
- *- * Returns a bit-mask for mask properties. - *
- *- * Returns 0 if property is out of bounds or if the Unicode version does not - * have data for the property at all, or not for this code point. - *
- *- * Returns NULL if codepoint is out of bound. - *
- * @since 7.0 - */ - public static function getIntPropertyValue ($codepoint, $property ) {} - - /** - * Get the numeric value for a Unicode code point - * @link https://php.net/manual/ru/intlchar.getnumericvalue.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return float|null Numeric value of codepoint, or float(-123456789) if none is defined, or NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function getNumericValue ($codepoint) {} - - /** - * Get the property constant value for a given property name - * @link https://php.net/manual/ru/intlchar.getpropertyenum.php - * @param string $alias The property name to be matched. The name is compared using "loose matching" as described in PropertyAliases.txt. - * @return int Returns an IntlChar::PROPERTY_ constant value, or IntlChar::PROPERTY_INVALID_CODE if the given name does not match any property. - * @since 7.0 - */ - public static function getPropertyEnum ($alias ) {} - - /** - * Get the Unicode name for a property - * @link https://php.net/manual/ru/intlchar.getpropertyname.php - * @param int $propertyThe Unicode property to lookup (see the IntlChar::PROPERTY_* constants).
- *IntlChar::PROPERTY_INVALID_CODE should not be used. Also, if property is out of range, FALSE is returned.
- * @param int $nameChoiceSelector for which name to get. If out of range, FALSE is returned.
- *All properties have a long name. Most have a short name, but some do not. Unicode allows for additional names; if present these will be returned by adding 1, 2, etc. to IntlChar::LONG_PROPERTY_NAME.
- * @return string|false- * Returns the name, or FALSE if either the property or the nameChoice - * is out of range. - *
- *- * If a given nameChoice returns FALSE, then all larger values of - * nameChoice will return FALSE, with one exception: if FALSE is returned for - * IntlChar::SHORT_PROPERTY_NAME, then IntlChar::LONG_PROPERTY_NAME - * (and higher) may still return a non-FALSE value. - *
- * @since 7.0 - */ - public static function getPropertyName ($property, $nameChoice = IntlChar::LONG_PROPERTY_NAME) {} - - /** - * Get the property value for a given value name - * @link https://php.net/manual/ru/intlchar.getpropertyvalueenum.php - * @param int $propertyThe Unicode property to lookup (see the IntlChar::PROPERTY_* constants). - * If out of range, or this method doesn't work with the given value, IntlChar::PROPERTY_INVALID_CODE is returned
- * @param string $nameThe value name to be matched. The name is compared using "loose matching" as described in PropertyValueAliases.txt.
- * @return int Returns the corresponding value integer, or IntlChar::PROPERTY_INVALID_CODE if the given name does not match any value of the given property, or if the property is invalid. - * @since 7.0 - */ - public static function getPropertyValueEnum ($property, $name) {} - - /** - * Get the Unicode name for a property value - * @link https://php.net/manual/ru/intlchar.getpropertyvaluename.php - * @param int $property- * The Unicode property to lookup (see the IntlChar::PROPERTY_* constants). - * If out of range, or this method doesn't work with the given value, FALSE is returned. - *
- * @param int $value- * Selector for a value for the given property. If out of range, FALSE is returned. - *
- *- * In general, valid values range from 0 up to some maximum. There are a couple exceptions: - *
- * Selector for which name to get. If out of range, FALSE is returned. - * All values have a long name. Most have a short name, but some do not. Unicode allows for additional names; if present these will be returned by adding 1, 2, etc. to IntlChar::LONG_PROPERTY_NAME. - *
- * @return string|false Returns the name, or FALSE if either the property or the nameChoice is out of range. - * If a given nameChoice returns FALSE, then all larger values of nameChoice will return FALSE, with one exception: if FALSE is returned for IntlChar::SHORT_PROPERTY_NAME, then IntlChar::LONG_PROPERTY_NAME (and higher) may still return a non-FALSE value. - * @since 7.0 - */ - public static function getPropertyValueName ($property, $value, $nameChoice = IntlChar::LONG_PROPERTY_NAME) {} - - /** - * Get the Unicode version - * @link https://php.net/manual/ru/intlchar.getunicodeversion.php - * @return array An array containing the Unicode version number. - * @since 7.0 - */ - public static function getUnicodeVersion() {} - - /** - * Check if code point is an alphanumeric character - * @link https://php.net/manual/ru/intlchar.isalnum.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return bool|null Returns TRUE if codepoint is an alphanumeric character, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function isalnum ($codepoint) {} - - /** - * Check if code point is a letter character - * @link https://php.net/manual/ru/intlchar.isalpha.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return bool|null Returns TRUE if codepoint is a letter character, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function isalpha ($codepoint) {} - /** - * Check if code point is a base character - * @link https://php.net/manual/ru/intlchar.isbase.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return bool|null Returns TRUE if codepoint is a base character, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function isbase ($codepoint ){} - /** - * Check if code point is a "blank" or "horizontal space" character - * @link https://php.net/manual/ru/intlchar.isblank.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return bool|null Returns TRUE if codepoint is either a "blank" or "horizontal space" character, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function isblank ($codepoint){} - - /** - * Check if code point is a control character - * @link https://php.net/manual/ru/intlchar.iscntrl.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return bool|null Returns TRUE if codepoint is a control character, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function iscntrl ($codepoint ) {} - - /** - * Check whether the code point is defined - * @link https://php.net/manual/ru/intlchar.isdefined.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return bool|null Returns TRUE if codepoint is a defined character, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function isdefined ($codepoint ) {} - - /** - * Check if code point is a digit character - * @link https://php.net/manual/ru/intlchar.isdigit.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return bool|null Returns TRUE if codepoint is a digit character, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function isdigit ($codepoint) {} - /** - * Check if code point is a graphic character - * @link https://php.net/manual/ru/intlchar.isgraph.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return bool|null Returns TRUE if codepoint is a "graphic" character, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function isgraph ($codepoint ) {} - /** - * Check if code point is an ignorable character - * @link https://php.net/manual/ru/intlchar.isidignorable.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return bool|null Returns TRUE if codepoint is ignorable in identifiers, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function isIDIgnorable ($codepoint ) {} - /** - * Check if code point is permissible in an identifier - * @link https://php.net/manual/ru/intlchar.isidpart.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return bool|null Returns TRUE if codepoint is the code point may occur in an identifier, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function isIDPart ($codepoint ) {} - - /** - * Check if code point is permissible as the first character in an identifier - * @link https://php.net/manual/ru/intlchar.isidstart.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return bool|null Returns TRUE if codepoint may start an identifier, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function isIDStart ($codepoint ) {} - /** - * Check if code point is an ISO control code - * @link https://php.net/manual/ru/intlchar.isisocontrol.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return bool|null Returns TRUE if codepoint is an ISO control code, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function isISOControl ($codepoint ) {} - /** - * Check if code point is permissible in a Java identifier - * @link https://php.net/manual/ru/intlchar.isjavaidpart.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return bool|null Returns TRUE if codepoint may occur in a Java identifier, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function isJavaIDPart ($codepoint ) {} - /** - * Check if code point is permissible as the first character in a Java identifier - * @link https://php.net/manual/ru/intlchar.isjavaidstart.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return bool|null Returns TRUE if codepoint may start a Java identifier, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function isJavaIDStart ($codepoint ) {} - /** - * Check if code point is a space character according to Java - * @link https://php.net/manual/ru/intlchar.isjavaspacechar.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return bool|null Returns TRUE if codepoint is a space character according to Java, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function isJavaSpaceChar ($codepoint ) {} - - /** - * Check if code point is a lowercase letter - * @link https://php.net/manual/ru/intlchar.islower.php - * @param int|string $codepointThe integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), - * or the character encoded as a UTF-8 string (e.g. "\u{2603}")
- * @return bool|null Returns TRUE if codepoint is an Ll lowercase letter, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function islower ($codepoint ) {} - /** - * Check if code point has the Bidi_Mirrored property - * @link https://php.net/manual/ru/intlchar.ismirrored.php - * @param int|string $codepointThe integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}")
- * @return bool|null Returns TRUE if codepoint has the Bidi_Mirrored property, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function isMirrored ($codepoint ) {} - - /** - * Check if code point is a printable character - * @link https://php.net/manual/ru/intlchar.isprint.php - * @param int|string $codepointThe integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}")
- * @return bool|null Returns TRUE if codepoint is a printable character, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function isprint ($codepoint ) {} - - /** - * Check if code point is punctuation character - * @link https://php.net/manual/ru/intlchar.ispunct.php - * @param int|string $codepointThe integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), - * or the character encoded as a UTF-8 string (e.g. "\u{2603}")
- * @return bool|null Returns TRUE if codepoint is a punctuation character, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function ispunct ($codepoint ) {} - /** - * Check if code point is a space character - * @link https://php.net/manual/ru/intlchar.isspace.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return bool|null Returns TRUE if codepoint is a space character, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function isspace ($codepoint ) {} - /** - * Check if code point is a titlecase letter - * @link https://php.net/manual/ru/intlchar.istitle.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return bool|null Returns TRUE if codepoint is a titlecase letter, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function istitle ($codepoint ){} - - /** - * Check if code point has the Alphabetic Unicode property - * @link https://php.net/manual/ru/intlchar.isualphabetic.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return bool|null Returns TRUE if codepoint has the Alphabetic Unicode property, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function isUAlphabetic ($codepoint ) {} - /** - * Check if code point has the Lowercase Unicode property - * @link https://php.net/manual/ru/intlchar.isulowercase.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return bool|null Returns TRUE if codepoint has the Lowercase Unicode property, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function isULowercase ($codepoint ) {} - /** - * Check if code point has the general category "Lu" (uppercase letter) - * @link https://php.net/manual/ru/intlchar.isupper.php - * @param int|string $codepointThe integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), - * or the character encoded as a UTF-8 string (e.g. "\u{2603}")
- * @return bool|null Returns TRUE if codepoint is an Lu uppercase letter, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function isupper ($codepoint) {} - /** - * Check if code point has the Uppercase Unicode property - * @link https://php.net/manual/ru/intlchar.isuuppercase.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return bool|null Returns TRUE if codepoint has the Uppercase Unicode property, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function isUUppercase ($codepoint) {} - /** - * Check if code point has the White_Space Unicode property - * @link https://php.net/manual/ru/intlchar.isuwhitespace.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return bool|null Returns TRUE if codepoint has the White_Space Unicode property, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function isUWhiteSpace ($codepoint ) {} - /** - * Check if code point is a whitespace character according to ICU - * @link https://php.net/manual/ru/intlchar.iswhitespace.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return bool|null Returns TRUE if codepoint is a whitespace character according to ICU, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function isWhitespace($codepoint) {} - - /** - * Check if code point is a hexadecimal digit - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return bool|null Returns TRUE if codepoint is a hexadecimal character, FALSE if not, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function isxdigit ($codepoint){} - - /** - * Return Unicode code point value of character - * @link https://php.net/manual/ru/intlchar.ord.php - * @param int|string $characterA Unicode character.
- * @return int|null Returns the Unicode code point value as an integer, NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function ord ($character) {} - - /** - * Make Unicode character lowercase - * @link https://php.net/manual/en/intlchar.tolower.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return int|string|null Returns the Simple_Lowercase_Mapping of the code point, if any; otherwise the code point itself. - * The return type will be integer unless the code point was passed as a UTF-8 string, in which case a string will be returned. - * Or NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function tolower($codepoint) {} - /** - * Make Unicode character titlecase - * @link https://php.net/manual/ru/intlchar.totitle.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return int|string|null Returns the Simple_Titlecase_Mapping of the code point, if any; otherwise the code point itself. - * The return type will be integer unless the code point was passed as a UTF-8 string, in which case a string will be returned. - * Or NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function totitle ($codepoint ) {} - - /** - * Make Unicode character uppercase - * @link https://php.net/manual/ru/intlchar.toupper.php - * @param int|string $codepoint The integer codepoint value (e.g. 0x2603 for U+2603 SNOWMAN), or the character encoded as a UTF-8 string (e.g. "\u{2603}") - * @return int|string|null Returns the Simple_Uppercase_Mapping of the code point, if any; otherwise the code point itself. - * The return type will be integer unless the code point was passed as a UTF-8 string, in which case a string will be returned. - * Or NULL if codepoint is out of bound. - * @since 7.0 - */ - public static function toupper ($codepoint ) {} -} --- a/build/stubs/SensitiveParameter.phpstub +++ /dev/null @@ -1,5 +0,0 @@ - value pairs. - * The constant_name must follow the normal constant naming rules. Value must evaluate to a scalar value. - * @param bool $case_sensitive The default behaviour for constants is to be declared case-sensitive; - * i.e. CONSTANT and Constant represent different values. If this parameter evaluates to FALSE - * the constants will be declared as case-insensitive symbols. - * @return bool Returns TRUE on success or FALSE on failure. - */ -function apc_define_constants($key, array $constants, $case_sensitive = true){} - -/** - * Caches a variable in the data store, only if it's not already stored - * @link https://php.net/manual/en/function.apc-add.php - * @param string $key Store the variable using this name. Keys are cache-unique, - * so attempting to use apc_add() to store data with a key that already exists will not - * overwrite the existing data, and will instead return FALSE. (This is the only difference - * between apc_add() and apc_store().) - * @param mixed $var The variable to store - * @param int $ttl Time To Live; store var in the cache for ttl seconds. After the ttl has passed, - * the stored variable will be expunged from the cache (on the next request). If no ttl is supplied - * (or if the ttl is 0), the value will persist until it is removed from the cache manually, - * or otherwise fails to exist in the cache (clear, restart, etc.). - * @return bool - */ -function apc_add($key, $var, $ttl = 0){} - -/** - * Stores a file in the bytecode cache, bypassing all filters - * @link https://php.net/manual/en/function.apc-compile-file.php - * @param string|string[] $filename Full or relative path to a PHP file that will be - * compiled and stored in the bytecode cache. - * @param bool $atomic - * @return bool Returns TRUE on success or FALSE on failure. - */ -function apc_compile_file($filename, $atomic = true){} - -/** - * Loads a set of constants from the cache - * @link https://php.net/manual/en/function.apc-load-constants.php - * @param string $key The name of the constant set (that was stored - * with apc_define_constants()) to be retrieved. - * @param bool $case_sensitive The default behaviour for constants is to be declared case-sensitive; - * i.e. CONSTANT and Constant represent different values. If this parameter evaluates to FALSE - * the constants will be declared as case-insensitive symbols. - * @return bool Returns TRUE on success or FALSE on failure. - */ -function apc_load_constants($key, $case_sensitive = true){} - -/** - * Checks if APC key exists - * @link https://php.net/manual/en/function.apc-exists.php - * @param bool|string[] $keys A string, or an array of strings, that contain keys. - * @return bool|string[] Returns TRUE if the key exists, otherwise FALSE - * Or if an array was passed to keys, then an array is returned that - * contains all existing keys, or an empty array if none exist. - */ -function apc_exists($keys){} - -/** - * Deletes the given files from the opcode cache - * - * Accepts a string, array of strings, or APCIterator object. - * Returns True/False, or for an Array an Array of failed files. - * - * @link https://php.net/manual/en/function.apc-delete-file.php - * @param string|string[]|APCIterator $keys - * @return bool|string[] - */ -function apc_delete_file($keys){} - -/** - * Increase a stored number - * @link https://php.net/manual/en/function.apc-inc.php - * @param string $key The key of the value being increased. - * @param int $step The step, or value to increase. - * @param bool $success Optionally pass the success or fail boolean value to this referenced variable. - * @return int|bool Returns the current value of key's value on success, or FALSE on failure. - */ -function apc_inc($key, $step = 1, &$success = null){} - -/** - * Decrease a stored number - * @link https://php.net/manual/en/function.apc-dec.php - * @param string $key The key of the value being decreased. - * @param int $step The step, or value to decrease. - * @param bool $success Optionally pass the success or fail boolean value to this referenced variable. - * @return int|bool Returns the current value of key's value on success, or FALSE on failure. - */ -function apc_dec($key, $step = 1, &$success = null){} - -/** - * @link https://php.net/manual/en/function.apc-cas.php - * @param string $key - * @param int $old - * @param int $new - * @return bool - */ -function apc_cas($key, $old, $new){} - -/** - * Returns a binary dump of the given files and user variables from the APC cache - * - * A NULL for files or user_vars signals a dump of every entry, while array() will dump nothing. - * - * @link https://php.net/manual/en/function.apc-bin-dump.php - * @param string[]|null $files The files. Passing in NULL signals a dump of every entry, while passing in array() will dump nothing. - * @param string[]|null $user_vars The user vars. Passing in NULL signals a dump of every entry, while passing in array() will dump nothing. - * @return string|bool|null Returns a binary dump of the given files and user variables from the APC cache, FALSE if APC is not enabled, or NULL if an unknown error is encountered. - */ -function apc_bin_dump($files = null, $user_vars = null){} - -/** - * Output a binary dump of the given files and user variables from the APC cache to the named file - * @link https://php.net/manual/en/function.apc-bin-dumpfile.php - * @param string[]|null $files The file names being dumped. - * @param string[]|null $user_vars The user variables being dumped. - * @param string $filename The filename where the dump is being saved. - * @param int $flags Flags passed to the filename stream. See the file_put_contents() documentation for details. - * @param resource $context The context passed to the filename stream. See the file_put_contents() documentation for details. - * @return int|bool The number of bytes written to the file, otherwise FALSE if APC - * is not enabled, filename is an invalid file name, filename can't be opened, - * the file dump can't be completed (e.g., the hard drive is out of disk space), - * or an unknown error was encountered. - */ -function apc_bin_dumpfile($files, $user_vars, $filename, $flags = 0, $context = null){} - -/** - * Load the given binary dump into the APC file/user cache - * @link https://php.net/manual/en/function.apc-bin-load.php - * @param string $data The binary dump being loaded, likely from apc_bin_dump(). - * @param int $flags Either APC_BIN_VERIFY_CRC32, APC_BIN_VERIFY_MD5, or both. - * @return bool Returns TRUE if the binary dump data was loaded with success, otherwise FALSE is returned. - * FALSE is returned if APC is not enabled, or if the data is not a valid APC binary dump (e.g., unexpected size). - */ -function apc_bin_load($data, $flags = 0){} - -/** - * Load the given binary dump from the named file into the APC file/user cache - * @link https://php.net/manual/en/function.apc-bin-loadfile.php - * @param string $filename The file name containing the dump, likely from apc_bin_dumpfile(). - * @param resource $context The files context. - * @param int $flags Either APC_BIN_VERIFY_CRC32, APC_BIN_VERIFY_MD5, or both. - * @return bool Returns TRUE on success, otherwise FALSE Reasons it may return FALSE include APC - * is not enabled, filename is an invalid file name or empty, filename can't be opened, - * the file dump can't be completed, or if the data is not a valid APC binary dump (e.g., unexpected size). - */ -function apc_bin_loadfile($filename, $context = null, $flags = 0){} - -/** - * The APCIterator class - * - * The APCIterator class makes it easier to iterate over large APC caches. - * This is helpful as it allows iterating over large caches in steps, while grabbing a defined number - * of entries per lock instance, so it frees the cache locks for other activities rather than hold up - * the entire cache to grab 100 (the default) entries. Also, using regular expression matching is more - * efficient as it's been moved to the C level. - * - * @link https://php.net/manual/en/class.apciterator.php - */ -class APCIterator implements Iterator -{ - /** - * Constructs an APCIterator iterator object - * @link https://php.net/manual/en/apciterator.construct.php - * @param string $cache The cache type, which will be 'user' or 'file'. - * @param string|string[]|null $search A PCRE regular expression that matches against APC key names, - * either as a string for a single regular expression, or as an array of regular expressions. - * Or, optionally pass in NULL to skip the search. - * @param int $format The desired format, as configured with one ore more of the APC_ITER_* constants. - * @param int $chunk_size The chunk size. Must be a value greater than 0. The default value is 100. - * @param int $list The type to list. Either pass in APC_LIST_ACTIVE or APC_LIST_INACTIVE. - */ - public function __construct($cache, $search = null, $format = APC_ITER_ALL, $chunk_size = 100, $list = APC_LIST_ACTIVE){} - - /** - * Rewinds back the iterator to the first element - * @link https://php.net/manual/en/apciterator.rewind.php - */ - public function rewind(){} - - /** - * Checks if the current iterator position is valid - * @link https://php.net/manual/en/apciterator.valid.php - * @return bool Returns TRUE if the current iterator position is valid, otherwise FALSE. - */ - public function valid(){} - - /** - * Gets the current item from the APCIterator stack - * @link https://php.net/manual/en/apciterator.current.php - * @return mixed Returns the current item on success, or FALSE if no more items or exist, or on failure. - */ - public function current(){} - - /** - * Gets the current iterator key - * @link https://php.net/manual/en/apciterator.key.php - * @return string|int|bool Returns the key on success, or FALSE upon failure. - */ - public function key(){} - - /** - * Moves the iterator pointer to the next element - * @link https://php.net/manual/en/apciterator.next.php - * @return bool Returns TRUE on success or FALSE on failure. - */ - public function next(){} - - /** - * Gets the total number of cache hits - * @link https://php.net/manual/en/apciterator.gettotalhits.php - * @return int|bool The number of hits on success, or FALSE on failure. - */ - public function getTotalHits(){} - - /** - * Gets the total cache size - * @link https://php.net/manual/en/apciterator.gettotalsize.php - * @return int|bool The total cache size. - */ - public function getTotalSize(){} - - /** - * Get the total count - * @link https://php.net/manual/en/apciterator.gettotalcount.php - * @return int|bool The total count. - */ - public function getTotalCount(){} -} - -/** - * Stubs for APCu 5.0.0 - */ - -/** - * @link https://php.net/manual/en/apcu.constants.php - */ -define('APC_LIST_ACTIVE', 1); -/** - * @link https://php.net/manual/en/apcu.constants.php - */ -define('APC_LIST_DELETED', 2); -/** - * @link https://php.net/manual/en/apcu.constants.php - */ -define('APC_ITER_TYPE', 1); -/** - * @link https://php.net/manual/en/apcu.constants.php - */ -define('APC_ITER_KEY', 2); -/** - * @link https://php.net/manual/en/apcu.constants.php - */ -define('APC_ITER_FILENAME', 4); -/** - * @link https://php.net/manual/en/apcu.constants.php - */ -define('APC_ITER_DEVICE', 8); -/** - * @link https://php.net/manual/en/apcu.constants.php - */ -define('APC_ITER_INODE', 16); -/** - * @link https://php.net/manual/en/apcu.constants.php - */ -define('APC_ITER_VALUE', 32); -/** - * @link https://php.net/manual/en/apcu.constants.php - */ -define('APC_ITER_MD5', 64); -/** - * @link https://php.net/manual/en/apcu.constants.php - */ -define('APC_ITER_NUM_HITS', 128); -/** - * @link https://php.net/manual/en/apcu.constants.php - */ -define('APC_ITER_MTIME', 256); -/** - * @link https://php.net/manual/en/apcu.constants.php - */ -define('APC_ITER_CTIME', 512); -/** - * @link https://php.net/manual/en/apcu.constants.php - */ -define('APC_ITER_DTIME', 1024); -/** - * @link https://php.net/manual/en/apcu.constants.php - */ -define('APC_ITER_ATIME', 2048); -/** - * @link https://php.net/manual/en/apcu.constants.php - */ -define('APC_ITER_REFCOUNT', 4096); -/** - * @link https://php.net/manual/en/apcu.constants.php - */ -define('APC_ITER_MEM_SIZE', 8192); -/** - * @link https://php.net/manual/en/apcu.constants.php - */ -define('APC_ITER_TTL', 16384); -/** - * @link https://php.net/manual/en/apcu.constants.php - */ -define('APC_ITER_NONE', 0); -/** - * @link https://php.net/manual/en/apcu.constants.php - */ -define('APC_ITER_ALL', -1); - - -/** - * Clears the APCu cache - * @link https://php.net/manual/en/function.apcu-clear-cache.php - * - * @return bool Returns TRUE always. - */ -function apcu_clear_cache(){} - -/** - * Retrieves APCu Shared Memory Allocation information - * @link https://php.net/manual/en/function.apcu-sma-info.php - * @param bool $limited When set to FALSE (default) apcu_sma_info() will - * return a detailed information about each segment. - * - * @return array|false Array of Shared Memory Allocation data; FALSE on failure. - */ -function apcu_sma_info($limited = false){} - -/** - * Cache a variable in the data store - * @link https://php.net/manual/en/function.apcu-store.php - * @param string|array $key String: Store the variable using this name. Keys are cache-unique, - * so storing a second value with the same key will overwrite the original value. - * Array: Names in key, variables in value. - * @param mixed $var [optional] The variable to store - * @param int $ttl [optional] Time To Live; store var in the cache for ttl seconds. After the ttl has passed, - * the stored variable will be expunged from the cache (on the next request). If no ttl is supplied - * (or if the ttl is 0), the value will persist until it is removed from the cache manually, - * or otherwise fails to exist in the cache (clear, restart, etc.). - * @return bool|array Returns TRUE on success or FALSE on failure | array with error keys. - */ -function apcu_store($key, $var, $ttl = 0){} - -/** - * Fetch a stored variable from the cache - * @link https://php.net/manual/en/function.apcu-fetch.php - * @param string|string[] $key The key used to store the value (with apcu_store()). - * If an array is passed then each element is fetched and returned. - * @param bool $success Set to TRUE in success and FALSE in failure. - * @return mixed The stored variable or array of variables on success; FALSE on failure. - */ -function apcu_fetch($key, &$success = null){} - -/** - * Removes a stored variable from the cache - * @link https://php.net/manual/en/function.apcu-delete.php - * @param string|string[]|APCuIterator $key The key used to store the value (with apcu_store()). - * @return bool|string[] Returns TRUE on success or FALSE on failure. For array of keys returns list of failed keys. - */ -function apcu_delete($key){} - -/** - * Caches a variable in the data store, only if it's not already stored - * @link https://php.net/manual/en/function.apcu-add.php - * @param string|array $key Store the variable using this name. Keys are cache-unique, - * so attempting to use apcu_add() to store data with a key that already exists will not - * overwrite the existing data, and will instead return FALSE. (This is the only difference - * between apcu_add() and apcu_store().) - * Array: Names in key, variables in value. - * @param mixed $var The variable to store - * @param int $ttl Time To Live; store var in the cache for ttl seconds. After the ttl has passed, - * the stored variable will be expunged from the cache (on the next request). If no ttl is supplied - * (or if the ttl is 0), the value will persist until it is removed from the cache manually, - * or otherwise fails to exist in the cache (clear, restart, etc.). - * @return bool|array Returns TRUE if something has effectively been added into the cache, FALSE otherwise. - * Second syntax returns array with error keys. - */ -function apcu_add($key, $var, $ttl = 0){} - -/** - * Checks if APCu key exists - * @link https://php.net/manual/en/function.apcu-exists.php - * @param string|string[] $keys A string, or an array of strings, that contain keys. - * @return bool|string[] Returns TRUE if the key exists, otherwise FALSE - * Or if an array was passed to keys, then an array is returned that - * contains all existing keys, or an empty array if none exist. - */ -function apcu_exists($keys){} - -/** - * Increase a stored number - * @link https://php.net/manual/en/function.apcu-inc.php - * @param string $key The key of the value being increased. - * @param int $step The step, or value to increase. - * @param int $ttl Time To Live; store var in the cache for ttl seconds. After the ttl has passed, - * the stored variable will be expunged from the cache (on the next request). If no ttl is supplied - * (or if the ttl is 0), the value will persist until it is removed from the cache manually, - * or otherwise fails to exist in the cache (clear, restart, etc.). - * @param bool $success Optionally pass the success or fail boolean value to this referenced variable. - * @return int|false Returns the current value of key's value on success, or FALSE on failure. - */ -function apcu_inc($key, $step = 1, &$success = null, $ttl = 0){} - -/** - * Decrease a stored number - * @link https://php.net/manual/en/function.apcu-dec.php - * @param string $key The key of the value being decreased. - * @param int $step The step, or value to decrease. - * @param int $ttl Time To Live; store var in the cache for ttl seconds. After the ttl has passed, - * the stored variable will be expunged from the cache (on the next request). If no ttl is supplied - * (or if the ttl is 0), the value will persist until it is removed from the cache manually, - * or otherwise fails to exist in the cache (clear, restart, etc.). - * @param bool $success Optionally pass the success or fail boolean value to this referenced variable. - * @return int|false Returns the current value of key's value on success, or FALSE on failure. - */ -function apcu_dec($key, $step = 1, &$success = null, $ttl = 0){} - -/** - * Updates an old value with a new value - * - * apcu_cas() updates an already existing integer value if the old parameter matches the currently stored value - * with the value of the new parameter. - * - * @link https://php.net/manual/en/function.apcu-cas.php - * @param string $key The key of the value being updated. - * @param int $old The old value (the value currently stored). - * @param int $new The new value to update to. - * @return bool Returns TRUE on success or FALSE on failure. - */ -function apcu_cas($key, $old, $new){} - -/** - * Atomically fetch or generate a cache entry - * - *Atomically attempts to find key in the cache, if it cannot be found generator is called, - * passing key as the only argument. The return value of the call is then cached with the optionally - * specified ttl, and returned. - *
- * - *Note: When control enters apcu_entry() the lock for the cache is acquired exclusively, it is released when - * control leaves apcu_entry(): In effect, this turns the body of generator into a critical section, - * disallowing two processes from executing the same code paths concurrently. - * In addition, it prohibits the concurrent execution of any other APCu functions, - * since they will acquire the same lock. - *
- * - * @link https://php.net/manual/en/function.apcu-entry.php - * - * @param string $key Identity of cache entry - * @param callable $generator A callable that accepts key as the only argument and returns the value to cache. - *Warning - * The only APCu function that can be called safely by generator is apcu_entry().
- * @param int $ttl [optional] Time To Live; store var in the cache for ttl seconds. - * After the ttl has passed, the stored variable will be expunged from the cache (on the next request). - * If no ttl is supplied (or if the ttl is 0), the value will persist until it is removed from the cache manually, - * or otherwise fails to exist in the cache (clear, restart, etc.). - * @return mixed Returns the cached value - * @since APCu 5.1.0 - */ -function apcu_entry($key, callable $generator, $ttl = 0){} - -/** - * Retrieves cached information from APCu's data store - * - * @link https://php.net/manual/en/function.apcu-cache-info.php - * - * @param bool $limited If limited is TRUE, the return value will exclude the individual list of cache entries. - * This is useful when trying to optimize calls for statistics gathering. - * @return array|false Array of cached data (and meta-data) or FALSE on failure - */ -function apcu_cache_info($limited = false){} - -/** - * Whether APCu is usable in the current environment - * - * @link https://www.php.net/manual/en/function.apcu-enabled.php - * - * @return bool - */ -function apcu_enabled(){} - -/** - * @param string $key - */ -function apcu_key_info($key){} - -/** - * The APCuIterator class - * - * The APCuIterator class makes it easier to iterate over large APCu caches. - * This is helpful as it allows iterating over large caches in steps, while grabbing a defined number - * of entries per lock instance, so it frees the cache locks for other activities rather than hold up - * the entire cache to grab 100 (the default) entries. Also, using regular expression matching is more - * efficient as it's been moved to the C level. - * - * @link https://php.net/manual/en/class.apcuiterator.php - * @since APCu 5.0.0 - */ -class APCuIterator implements Iterator -{ - /** - * Constructs an APCuIterator iterator object - * @link https://php.net/manual/en/apcuiterator.construct.php - * @param string|string[]|null $search A PCRE regular expression that matches against APCu key names, - * either as a string for a single regular expression, or as an array of regular expressions. - * Or, optionally pass in NULL to skip the search. - * @param int $format The desired format, as configured with one ore more of the APC_ITER_* constants. - * @param int $chunk_size The chunk size. Must be a value greater than 0. The default value is 100. - * @param int $list The type to list. Either pass in APC_LIST_ACTIVE or APC_LIST_DELETED. - */ - public function __construct($search = null, $format = APC_ITER_ALL, $chunk_size = 100, $list = APC_LIST_ACTIVE){} - - /** - * Rewinds back the iterator to the first element - * @link https://php.net/manual/en/apcuiterator.rewind.php - */ - public function rewind(){} - - /** - * Checks if the current iterator position is valid - * @link https://php.net/manual/en/apcuiterator.valid.php - * @return bool Returns TRUE if the current iterator position is valid, otherwise FALSE. - */ - public function valid(){} - - /** - * Gets the current item from the APCuIterator stack - * @link https://php.net/manual/en/apcuiterator.current.php - * @return mixed Returns the current item on success, or FALSE if no more items or exist, or on failure. - */ - public function current(){} - - /** - * Gets the current iterator key - * @link https://php.net/manual/en/apcuiterator.key.php - * @return string|int|false Returns the key on success, or FALSE upon failure. - */ - public function key(){} - - /** - * Moves the iterator pointer to the next element - * @link https://php.net/manual/en/apcuiterator.next.php - * @return bool Returns TRUE on success or FALSE on failure. - */ - public function next(){} - - /** - * Gets the total number of cache hits - * @link https://php.net/manual/en/apcuiterator.gettotalhits.php - * @return int|false The number of hits on success, or FALSE on failure. - */ - public function getTotalHits(){} - - /** - * Gets the total cache size - * @link https://php.net/manual/en/apcuiterator.gettotalsize.php - * @return int|false The total cache size. - */ - public function getTotalSize(){} - - /** - * Get the total count - * @link https://php.net/manual/en/apcuiterator.gettotalcount.php - * @return int|false The total count. - */ - public function getTotalCount(){} -} --- a/build/stubs/app_api.php +++ /dev/null @@ -1,92 +0,0 @@ -exAppService->getExApp($appId); - if ($exApp === null) { - return ['error' => sprintf('ExApp `%s` not found', $appId)]; - } - return $this->service->requestToExApp($exApp, $route, $userId, $method, $params, $options, $request); - } - - /** - * Async request to ExApp with AppAPI auth headers - * - * @throws \Exception if ExApp not found - */ - public function asyncExAppRequest( - string $appId, - string $route, - ?string $userId = null, - string $method = 'POST', - array $params = [], - array $options = [], - ?IRequest $request = null, - ): IPromise { - $exApp = $this->exAppService->getExApp($appId); - if ($exApp === null) { - throw new \Exception(sprintf('ExApp `%s` not found', $appId)); - } - return $this->service->requestToExAppAsync($exApp, $route, $userId, $method, $params, $options, $request); - } - - /** - * Get basic ExApp info by appid - * - * @param string $appId - * - * @return array|null ExApp info (appid, version, name, enabled) or null if no ExApp found - */ - public function getExApp(string $appId): ?array { - $exApp = $this->exAppService->getExApp($appId); - if ($exApp !== null) { - $info = $exApp->jsonSerialize(); - return [ - 'appid' => $info['appid'], - 'version' => $info['version'], - 'name' => $info['name'], - 'enabled' => $info['enabled'], - ]; - } - return null; - } - } -} --- a/build/stubs/excimer.php +++ /dev/null @@ -1,282 +0,0 @@ -getLog() as $entry ) { - * var_dump( $entry->getTrace() ); - * } - */ -class ExcimerLog implements ArrayAccess, Iterator { - /** - * ExcimerLog is not constructible by user code. Objects of this type - * are available via: - * - ExcimerProfiler::getLog() - * - ExcimerProfiler::flush() - * - The callback to ExcimerProfiler::setFlushCallback() - */ - final private function __construct() { - } - - /** - * Aggregate the stack traces and convert them to a line-based format - * understood by Brendan Gregg's FlameGraph utility. Each stack trace is - * represented as a series of function names, separated by semicolons. - * After this identifier, there is a single space character, then a number - * giving the number of times the stack appeared. Then there is a line - * break. This is repeated for each unique stack trace. - * - * @return string - */ - public function formatCollapsed() { - } - - /** - * Produce an array with an element for every function which appears in - * the log. The key is a human-readable unique identifier for the function, - * method or closure. The value is an associative array with the following - * elements: - * - * - self: The number of events in which the function itself was running, - * no other userspace function was being called. This includes time - * spent in internal functions that this function called. - * - inclusive: The number of events in which this function appeared - * somewhere in the stack. - * - * And optionally the following elements, if they are relevant: - * - * - file: The filename in which the function appears - * - line: The exact line number at which the first relevant event - * occurred. - * - class: The class name in which the method is defined - * - function: The name of the function or method - * - closure_line: The line number at which the closure was defined - * - * The event counts in the "self" and "inclusive" fields are adjusted for - * overruns. They represent an estimate of the number of profiling periods - * in which those functions were present. - * - * @return array - */ - public function aggregateByFunction() { - } - - /** - * Get an array which can be JSON encoded for import into speedscope - * - * @return array - */ - public function getSpeedscopeData() { - } - - /** - * Get the total number of profiling periods represented by this log. - * - * @return int - */ - public function getEventCount() { - } - - /** - * Get the current ExcimerLogEntry object. Part of the Iterator interface. - * - * @return ExcimerLogEntry|null - */ - public function current() { - } - - /** - * Get the current integer key or null. Part of the Iterator interface. - * - * @return int|null - */ - public function key() { - } - - /** - * Advance to the next log entry. Part of the Iterator interface. - */ - public function next() { - } - - /** - * Rewind back to the first log entry. Part of the Iterator interface. - */ - public function rewind() { - } - - /** - * Check if the current position is valid. Part of the Iterator interface. - * - * @return bool - */ - public function valid() { - } - - /** - * Get the number of log entries contained in this log. This is always less - * than or equal to the number returned by getEventCount(), which includes - * overruns. - * - * @return int - */ - public function count() { - } - - /** - * Determine whether a log entry exists at the specified array offset. - * Part of the ArrayAccess interface. - * - * @param int $offset - * @return bool - */ - public function offsetExists($offset) { - } - - /** - * Get the ExcimerLogEntry object at the specified array offset. - * - * @param int $offset - * @return ExcimerLogEntry|null - */ - public function offsetGet($offset) { - } - - /** - * This function is included for compliance with the ArrayAccess interface. - * It raises a warning and does nothing. - * - * @param int $offset - * @param mixed $value - */ - public function offsetSet($offset, $value) { - } - - /** - * This function is included for compliance with the ArrayAccess interface. - * It raises a warning and does nothing. - * - * @param int $offset - */ - public function offsetUnset($offset) { - } -} --- a/build/stubs/ftp.php +++ /dev/null @@ -1,95 +0,0 @@ -|null - * @refcount 1 - */ - function ftp_raw(FTP\Connection $ftp, string $command): ?array {} - function ftp_mkdir(FTP\Connection $ftp, string $directory): string|false {} - function ftp_rmdir(FTP\Connection $ftp, string $directory): bool {} - function ftp_chmod(FTP\Connection $ftp, int $permissions, string $filename): int|false {} - - /** @param string $response */ - function ftp_alloc(FTP\Connection $ftp, int $size, &$response = null): bool {} - - /** - * @return array- * FRENCH_COLLATION rules - *
- * F=OFF cote < coté < côte < côté - * F=ON cote < côte < coté < côté - *
- * - * @link https://php.net/manual/en/class.collator.php#intl.collator-constants - */ - public const FRENCH_COLLATION = 0; - - /** - *- * The Alternate attribute is used to control the handling of the so called - * variable characters in the UCA: whitespace, punctuation and symbols. If - * Alternate is set to NonIgnorable - * (N), then differences among these characters are of the same importance - * as differences among letters. If Alternate is set to - * Shifted - * (S), then these characters are of only minor importance. The - * Shifted value is often used in combination with - * Strength - * set to Quaternary. In such a case, whitespace, punctuation, and symbols - * are considered when comparing strings, but only if all other aspects of - * the strings (base letters, accents, and case) are identical. If - * Alternate is not set to Shifted, then there is no difference between a - * Strength of 3 and a Strength of 4. For more information and examples, - * see Variable_Weighting in the - * UCA. - * The reason the Alternate values are not simply - * On and Off - * is that additional Alternate values may be added in the future. The UCA - * option Blanked is expressed with Strength set to 3, and Alternate set to - * Shifted. The default for most locales is NonIgnorable. If Shifted is - * selected, it may be slower if there are many strings that are the same - * except for punctuation; sort key length will not be affected unless the - * strength level is also increased. - *
- *- * Possible values are: - * Collator::NON_IGNORABLE(default) - * Collator::SHIFTED - * Collator::DEFAULT_VALUE - *
- *- * ALTERNATE_HANDLING rules - *
- * S=3, A=N di Silva < Di Silva < diSilva < U.S.A. < USA - * S=3, A=S di Silva = diSilva < Di Silva < U.S.A. = USA - * S=4, A=S di Silva < diSilva < Di Silva < U.S.A. < USA - *
- * - * @link https://php.net/manual/en/class.collator.php#intl.collator-constants - */ - public const ALTERNATE_HANDLING = 1; - - /** - *- * The Case_First attribute is used to control whether uppercase letters - * come before lowercase letters or vice versa, in the absence of other - * differences in the strings. The possible values are - * Uppercase_First - * (U) and Lowercase_First - * (L), plus the standard Default - * and Off. - * There is almost no difference between the Off and Lowercase_First - * options in terms of results, so typically users will not use - * Lowercase_First: only Off or Uppercase_First. (People interested in the - * detailed differences between X and L should consult the Collation - * Customization). Specifying either L or U won't affect string comparison - * performance, but will affect the sort key length. - *
- *- * Possible values are: - * Collator::OFF(default) - * Collator::LOWER_FIRST - * Collator::UPPER_FIRST - * Collator:DEFAULT - *
- *- * CASE_FIRST rules - *
- * C=X or C=L "china" < "China" < "denmark" < "Denmark" - * C=U "China" < "china" < "Denmark" < "denmark" - *
- * - * @link https://php.net/manual/en/class.collator.php#intl.collator-constants - */ - public const CASE_FIRST = 2; - - /** - *- * The Case_Level attribute is used when ignoring accents but not case. In - * such a situation, set Strength to be Primary, - * and Case_Level to be On. - * In most locales, this setting is Off by default. There is a small - * string comparison performance and sort key impact if this attribute is - * set to be On. - *
- *- * Possible values are: - * Collator::OFF(default) - * Collator::ON - * Collator::DEFAULT_VALUE - *
- *- * CASE_LEVEL rules - *
- * S=1, E=X role = Role = rôle - * S=1, E=O role = rôle < Role - *
- * - * @link https://php.net/manual/en/class.collator.php#intl.collator-constants - */ - public const CASE_LEVEL = 3; - - /** - *- * The Normalization setting determines whether text is thoroughly - * normalized or not in comparison. Even if the setting is off (which is - * the default for many locales), text as represented in common usage will - * compare correctly (for details, see UTN #5). Only if the accent marks - * are in noncanonical order will there be a problem. If the setting is - * On, - * then the best results are guaranteed for all possible text input. - * There is a medium string comparison performance cost if this attribute - * is On, - * depending on the frequency of sequences that require normalization. - * There is no significant effect on sort key length. If the input text is - * known to be in NFD or NFKD normalization forms, there is no need to - * enable this Normalization option. - *
- *- * Possible values are: - * Collator::OFF(default) - * Collator::ON - * Collator::DEFAULT_VALUE - *
- * @link https://php.net/manual/en/class.collator.php#intl.collator-constants - */ - public const NORMALIZATION_MODE = 4; - - /** - *- * The ICU Collation Service supports many levels of comparison (named - * "Levels", but also known as "Strengths"). Having these categories - * enables ICU to sort strings precisely according to local conventions. - * However, by allowing the levels to be selectively employed, searching - * for a string in text can be performed with various matching conditions. - * For more detailed information, see - * collator_set_strength chapter. - *
- *- * Possible values are: - * Collator::PRIMARY - * Collator::SECONDARY - * Collator::TERTIARY(default) - * Collator::QUATERNARY - * Collator::IDENTICAL - * Collator::DEFAULT_VALUE - *
- * @link https://php.net/manual/en/class.collator.php#intl.collator-constants - */ - public const STRENGTH = 5; - - /** - *- * Compatibility with JIS x 4061 requires the introduction of an additional - * level to distinguish Hiragana and Katakana characters. If compatibility - * with that standard is required, then this attribute should be set - * On, - * and the strength set to Quaternary. This will affect sort key length - * and string comparison string comparison performance. - *
- *- * Possible values are: - * Collator::OFF(default) - * Collator::ON - * Collator::DEFAULT_VALUE - *
- * @link https://php.net/manual/en/class.collator.php#intl.collator-constants - */ - public const HIRAGANA_QUATERNARY_MODE = 6; - - /** - *- * When turned on, this attribute generates a collation key for the numeric - * value of substrings of digits. This is a way to get '100' to sort AFTER - * '2'. - *
- *- * Possible values are: - * Collator::OFF(default) - * Collator::ON - * Collator::DEFAULT_VALUE - *
- * @link https://php.net/manual/en/class.collator.php#intl.collator-constants - */ - public const NUMERIC_COLLATION = 7; - public const SORT_REGULAR = 0; - public const SORT_STRING = 1; - public const SORT_NUMERIC = 2; - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale containing the required collation rules. Special values for - * locales can be passed in - if null is passed for the locale, the - * default locale collation rules will be used. If empty string ("") or - * "root" are passed, UCA rules will be used. - *
- * @return Collator|null Return new instance of Collator object, or NULL - * on error. - */ - public static function create(string $locale): ?Collator {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The first string to compare. - *
- * @param string $string2- * The second string to compare. - *
- * @return int|false Return comparison result: - *- *
- * 1 if str1 is greater than - * str2 ; - *
- *- * 0 if str1 is equal to - * str2; - *
- *- * -1 if str1 is less than - * str2 . - *
- * On error - * boolean - * FALSE - * is returned. - */ - public function compare( - string $string1, - string $string2 - ): int|false {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Array of strings to sort. - *
- * @param int $flags [optional]- * Optional sorting type, one of the following: - *
- *- * Collator::SORT_REGULAR - * - compare items normally (don't change types) - *
- * @return bool TRUE on success or FALSE on failure. - */ - public function sort( - array &$array, - int $flags = 0 - ): bool {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)Array of strings to sort
- * @return bool TRUE on success or FALSE on failure. - */ - public function sortWithSortKeys( - array &$array, - ): bool {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)Array of strings to sort.
- * @param int $flags [optional]- * Optional sorting type, one of the following: - * Collator::SORT_REGULAR - * - compare items normally (don't change types) - *
- * @return bool TRUE on success or FALSE on failure. - */ - public function asort( - array &$array, - int $flags = 0 - ): bool {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Attribute to get value for. - *
- * @return int|false Attribute value, or boolean FALSE on error. - */ - public function getAttribute(int $attribute): int|false {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)Attribute.
- * @param int $value- * Attribute value. - *
- * @return bool TRUE on success or FALSE on failure. - */ - public function setAttribute( - int $attribute, - int $value - ): bool {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)Strength to set.
- *- * Possible values are: - * Collator::PRIMARY - *
- * @return bool TRUE on success or FALSE on failure. - */ - public function setStrength(int $strength): bool {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * You can choose between valid and actual locale ( - * Locale::VALID_LOCALE and - * Locale::ACTUAL_LOCALE, - * respectively). - *
- * @return string|false Real locale name from which the collation data comes. If the collator was - * instantiated from rules or an error occurred, returns - * boolean FALSE. - */ - public function getLocale( - int $type - ): string|false {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The string to produce the key from. - *
- * @return string|false the collation key for the string. Collation keys can be compared directly instead of strings. - */ - public function getSortKey( - string $string, - ): string|false {} -} - -class NumberFormatter -{ - public const CURRENCY_ACCOUNTING = 12; - - /** - * Decimal format defined by pattern - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const PATTERN_DECIMAL = 0; - - /** - * Decimal format - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const DECIMAL = 1; - - /** - * Currency format - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const CURRENCY = 2; - - /** - * Percent format - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const PERCENT = 3; - - /** - * Scientific format - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const SCIENTIFIC = 4; - - /** - * Spellout rule-based format - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const SPELLOUT = 5; - - /** - * Ordinal rule-based format - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const ORDINAL = 6; - - /** - * Duration rule-based format - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const DURATION = 7; - - /** - * Rule-based format defined by pattern - * @link https://php.net/manual/en/class.locale.php#intl.locale-constants - */ - public const PATTERN_RULEBASED = 9; - - /** - * Alias for PATTERN_DECIMAL - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const IGNORE = 0; - - /** - * Default format for the locale - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const DEFAULT_STYLE = 1; - - /** - * Rounding mode to round towards positive infinity. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const ROUND_CEILING = 0; - - /** - * Rounding mode to round towards negative infinity. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const ROUND_FLOOR = 1; - - /** - * Rounding mode to round towards zero. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const ROUND_DOWN = 2; - - /** - * Rounding mode to round away from zero. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const ROUND_UP = 3; - - /** - * Rounding mode to round towards the "nearest neighbor" unless both - * neighbors are equidistant, in which case, round towards the even - * neighbor. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const ROUND_HALFEVEN = 4; - - /** - * Rounding mode to round towards "nearest neighbor" unless both neighbors - * are equidistant, in which case round down. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const ROUND_HALFDOWN = 5; - - /** - * Rounding mode to round towards "nearest neighbor" unless both neighbors - * are equidistant, in which case round up. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const ROUND_HALFUP = 6; - - /** - * Pad characters inserted before the prefix. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const PAD_BEFORE_PREFIX = 0; - - /** - * Pad characters inserted after the prefix. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const PAD_AFTER_PREFIX = 1; - - /** - * Pad characters inserted before the suffix. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const PAD_BEFORE_SUFFIX = 2; - - /** - * Pad characters inserted after the suffix. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const PAD_AFTER_SUFFIX = 3; - - /** - * Parse integers only. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const PARSE_INT_ONLY = 0; - - /** - * Use grouping separator. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const GROUPING_USED = 1; - - /** - * Always show decimal point. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const DECIMAL_ALWAYS_SHOWN = 2; - - /** - * Maximum integer digits. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const MAX_INTEGER_DIGITS = 3; - - /** - * Minimum integer digits. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const MIN_INTEGER_DIGITS = 4; - - /** - * Integer digits. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const INTEGER_DIGITS = 5; - - /** - * Maximum fraction digits. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const MAX_FRACTION_DIGITS = 6; - - /** - * Minimum fraction digits. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const MIN_FRACTION_DIGITS = 7; - - /** - * Fraction digits. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const FRACTION_DIGITS = 8; - - /** - * Multiplier. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const MULTIPLIER = 9; - - /** - * Grouping size. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const GROUPING_SIZE = 10; - - /** - * Rounding Mode. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const ROUNDING_MODE = 11; - - /** - * Rounding increment. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const ROUNDING_INCREMENT = 12; - - /** - * The width to which the output of format() is padded. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const FORMAT_WIDTH = 13; - - /** - * The position at which padding will take place. See pad position - * constants for possible argument values. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const PADDING_POSITION = 14; - - /** - * Secondary grouping size. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const SECONDARY_GROUPING_SIZE = 15; - - /** - * Use significant digits. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const SIGNIFICANT_DIGITS_USED = 16; - - /** - * Minimum significant digits. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const MIN_SIGNIFICANT_DIGITS = 17; - - /** - * Maximum significant digits. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const MAX_SIGNIFICANT_DIGITS = 18; - - /** - * Lenient parse mode used by rule-based formats. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const LENIENT_PARSE = 19; - - /** - * Positive prefix. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const POSITIVE_PREFIX = 0; - - /** - * Positive suffix. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const POSITIVE_SUFFIX = 1; - - /** - * Negative prefix. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const NEGATIVE_PREFIX = 2; - - /** - * Negative suffix. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const NEGATIVE_SUFFIX = 3; - - /** - * The character used to pad to the format width. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const PADDING_CHARACTER = 4; - - /** - * The ISO currency code. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const CURRENCY_CODE = 5; - - /** - * The default rule set. This is only available with rule-based - * formatters. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const DEFAULT_RULESET = 6; - - /** - * The public rule sets. This is only available with rule-based - * formatters. This is a read-only attribute. The public rulesets are - * returned as a single string, with each ruleset name delimited by ';' - * (semicolon). - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const PUBLIC_RULESETS = 7; - - /** - * The decimal separator. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const DECIMAL_SEPARATOR_SYMBOL = 0; - - /** - * The grouping separator. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const GROUPING_SEPARATOR_SYMBOL = 1; - - /** - * The pattern separator. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const PATTERN_SEPARATOR_SYMBOL = 2; - - /** - * The percent sign. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const PERCENT_SYMBOL = 3; - - /** - * Zero. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const ZERO_DIGIT_SYMBOL = 4; - - /** - * Character representing a digit in the pattern. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const DIGIT_SYMBOL = 5; - - /** - * The minus sign. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const MINUS_SIGN_SYMBOL = 6; - - /** - * The plus sign. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const PLUS_SIGN_SYMBOL = 7; - - /** - * The currency symbol. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const CURRENCY_SYMBOL = 8; - - /** - * The international currency symbol. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const INTL_CURRENCY_SYMBOL = 9; - - /** - * The monetary separator. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const MONETARY_SEPARATOR_SYMBOL = 10; - - /** - * The exponential symbol. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const EXPONENTIAL_SYMBOL = 11; - - /** - * Per mill symbol. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const PERMILL_SYMBOL = 12; - - /** - * Escape padding character. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const PAD_ESCAPE_SYMBOL = 13; - - /** - * Infinity symbol. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const INFINITY_SYMBOL = 14; - - /** - * Not-a-number symbol. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const NAN_SYMBOL = 15; - - /** - * Significant digit symbol. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const SIGNIFICANT_DIGIT_SYMBOL = 16; - - /** - * The monetary grouping separator. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const MONETARY_GROUPING_SEPARATOR_SYMBOL = 17; - - /** - * Derive the type from variable type - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const TYPE_DEFAULT = 0; - - /** - * Format/parse as 32-bit integer - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const TYPE_INT32 = 1; - - /** - * Format/parse as 64-bit integer - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const TYPE_INT64 = 2; - - /** - * Format/parse as floating point value - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const TYPE_DOUBLE = 3; - - /** - * Format/parse as currency value - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - * @deprecated 8.3 - */ - public const TYPE_CURRENCY = 4; - - /** - * @since 8.4 - */ - public const ROUND_TOWARD_ZERO = 2; - - /** - * @since 8.4 - */ - public const ROUND_AWAY_FROM_ZERO = 3; - - /** - * @since 8.4 - */ - public const ROUND_HALFODD = 8; - - /** - * @link https://www.php.net/manual/en/class.numberformatter.php - * @param string $locale - * @param int $style - * @param string $pattern [optional] - */ - public function __construct( - string $locale, - int $style, - string|null $pattern = null - ) {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Locale in which the number would be formatted (locale name, e.g. en_CA). - *
- * @param int $style- * Style of the formatting, one of the - * format style constants. If - * NumberFormatter::PATTERN_DECIMAL - * or NumberFormatter::PATTERN_RULEBASED - * is passed then the number format is opened using the given pattern, - * which must conform to the syntax described in - * ICU DecimalFormat - * documentation or - * ICU RuleBasedNumberFormat - * documentation, respectively. - *
- * @param string $pattern [optional]- * Pattern string if the chosen style requires a pattern. - *
- * @return NumberFormatter|false NumberFormatter object or FALSE on error. - */ - public static function create( - string $locale, - int $style, - string|null $pattern = null - ): ?NumberFormatter {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The value to format. Can be integer or float, - * other values will be converted to a numeric value. - *
- * @param int $type [optional]- * The - * formatting type to use. - *
- * @return string|false the string containing formatted value, or FALSE on error. - */ - public function format( - int|float $num, - int $type = 0 - ): string|false {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The - * formatting type to use. By default, - * NumberFormatter::TYPE_DOUBLE is used. - *
- * @param int &$offset [optional]- * Offset in the string at which to begin parsing. On return, this value - * will hold the offset at which parsing ended. - *
- * @return mixed The value of the parsed number or FALSE on error. - */ - public function parse( - string $string, - int $type = NumberFormatter::TYPE_DOUBLE, - &$offset = null - ): int|float|false {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The numeric currency value. - *
- * @param string $currency- * The 3-letter ISO 4217 currency code indicating the currency to use. - *
- * @return string|false String representing the formatted currency value. - */ - public function formatCurrency( - float $amount, - string $currency - ): string|false {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Parameter to receive the currency name (3-letter ISO 4217 currency - * code). - *
- * @param int &$offset [optional]- * Offset in the string at which to begin parsing. On return, this value - * will hold the offset at which parsing ended. - *
- * @return float|false The parsed numeric value or FALSE on error. - */ - public function parseCurrency(string $string, &$currency, &$offset = null): float|false {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Attribute specifier - one of the - * numeric attribute constants. - *
- * @param int $value- * The attribute value. - *
- * @return bool TRUE on success or FALSE on failure. - */ - public function setAttribute( - int $attribute, - int|float $value - ): bool {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Attribute specifier - one of the - * numeric attribute constants. - *
- * @return int|float|false Return attribute value on success, or FALSE on error. - */ - public function getAttribute(int $attribute): int|float|false {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Attribute specifier - one of the - * text attribute - * constants. - *
- * @param string $value- * Text for the attribute value. - *
- * @return bool TRUE on success or FALSE on failure. - */ - public function setTextAttribute( - int $attribute, - string $value - ): bool {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Attribute specifier - one of the - * text attribute constants. - *
- * @return string|false Return attribute value on success, or FALSE on error. - */ - public function getTextAttribute(int $attribute): string|false {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Symbol specifier, one of the - * format symbol constants. - *
- * @param string $value- * Text for the symbol. - *
- * @return bool TRUE on success or FALSE on failure. - */ - public function setSymbol( - int $symbol, - string $value - ): bool {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Symbol specifier, one of the - * format symbol constants. - *
- * @return string|false The symbol string or FALSE on error. - */ - public function getSymbol(int $symbol): string|false {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Pattern in syntax described in - * ICU DecimalFormat - * documentation. - *
- * @return bool TRUE on success or FALSE on failure. - */ - public function setPattern(string $pattern): bool {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * You can choose between valid and actual locale ( - * Locale::VALID_LOCALE, - * Locale::ACTUAL_LOCALE, - * respectively). The default is the actual locale. - *
- * @return string|false The locale name used to create the formatter. - */ - public function getLocale( - int $type = 0 - ): string|false {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)The input string to normalize
- * @param int $formOne of the normalization forms.
- * @return string|false The normalized string or FALSE if an error occurred. - */ - public static function normalize( - string $string, - int $form = Normalizer::FORM_C, - ): string|false {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)The input string to normalize
- * @param int $form- * One of the normalization forms. - *
- * @return bool TRUE if normalized, FALSE otherwise or if there an error - */ - public static function isNormalized( - string $string, - int $form = Normalizer::FORM_C, - ): bool {} - - /** - * @param string $stringThe input string to normalize
- * @param int $form - * @return string|nullReturns a string containing the Decomposition_Mapping property, if present in the UCD. - * Returns null if there is no Decomposition_Mapping property for the character.
- * @link https://www.php.net/manual/en/normalizer.getrawdecomposition.php - * @since 7.3 - */ - public static function getRawDecomposition( - string $string, - int $form = 16 - ): ?string {} -} - -class Locale -{ - /** - * This is locale the data actually comes from. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const ACTUAL_LOCALE = 0; - - /** - * This is the most specific locale supported by ICU. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const VALID_LOCALE = 1; - - /** - * Used as locale parameter with the methods of the various locale affected classes, - * such as NumberFormatter. This constant would make the methods to use default - * locale. - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const DEFAULT_LOCALE = null; - - /** - * Language subtag - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const LANG_TAG = "language"; - - /** - * Extended language subtag - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const EXTLANG_TAG = "extlang"; - - /** - * Script subtag - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const SCRIPT_TAG = "script"; - - /** - * Region subtag - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const REGION_TAG = "region"; - - /** - * Variant subtag - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const VARIANT_TAG = "variant"; - - /** - * Grandfathered Language subtag - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const GRANDFATHERED_LANG_TAG = "grandfathered"; - - /** - * Private subtag - * @link https://php.net/manual/en/class.numberformatter.php#intl.numberformatter-constants - */ - public const PRIVATE_TAG = "private"; - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Is a BCP 47 compliant language tag containing the - *
- * @return bool TRUE on success or FALSE on failure. - */ - public static function setDefault(string $locale): bool {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to extract the primary language code from - *
- * @return string|null The language code associated with the language or NULL in case of error. - */ - public static function getPrimaryLanguage(string $locale): ?string {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to extract the script code from - *
- * @return string|null The script subtag for the locale or NULL if not present - */ - public static function getScript(string $locale): ?string {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to extract the region code from - *
- * @return string|null The region subtag for the locale or NULL if not present - */ - public static function getRegion(string $locale): ?string {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to extract the keywords from - *
- * @return array|false|null Associative array containing the keyword-value pairs for this locale - */ - public static function getKeywords(string $locale): array|false|null {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to return a display script for - *
- * @param string $displayLocale- * Optional format locale to use to display the script name - *
- * @return string|false Display name of the script for the $locale in the format appropriate for - * $in_locale. - */ - public static function getDisplayScript( - string $locale, - string|null $displayLocale = null - ): string|false {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to return a display region for. - *
- * @param string $displayLocale- * Optional format locale to use to display the region name - *
- * @return string|false display name of the region for the $locale in the format appropriate for - * $in_locale. - */ - public static function getDisplayRegion( - string $locale, - string|null $displayLocale = null - ): string|false {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to return a display name for. - *
- * @param string $displayLocaleoptional format locale
- * @return string|false Display name of the locale in the format appropriate for $in_locale. - */ - public static function getDisplayName( - string $locale, - string|null $displayLocale = null - ): string|false {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to return a display language for - *
- * @param string $displayLocale- * Optional format locale to use to display the language name - *
- * @return string|false display name of the language for the $locale in the format appropriate for - * $in_locale. - */ - public static function getDisplayLanguage( - string $locale, - string|null $displayLocale = null - ): string|false {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to return a display variant for - *
- * @param string $displayLocale- * Optional format locale to use to display the variant name - *
- * @return string|false Display name of the variant for the $locale in the format appropriate for - * $in_locale. - */ - public static function getDisplayVariant( - string $locale, - string|null $displayLocale = null - ): string|false {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * an array containing a list of key-value pairs, where the keys identify - * the particular locale ID subtags, and the values are the associated - * subtag values. - *
- * The 'variant' and 'private' subtags can take maximum 15 values - * whereas 'extlang' can take maximum 3 values.e.g. Variants are allowed - * with the suffix ranging from 0-14. Hence the keys for the input array - * can be variant0, variant1, ...,variant14. In the returned locale id, - * the subtag is ordered by suffix resulting in variant0 followed by - * variant1 followed by variant2 and so on. - *
- *- * The 'variant', 'private' and 'extlang' multiple values can be specified both - * as array under specific key (e.g. 'variant') and as multiple numbered keys - * (e.g. 'variant0', 'variant1', etc.). - *
- * - * @return string|false The corresponding locale identifier. - */ - public static function composeLocale(array $subtags): string|false {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to extract the subtag array from. Note: The 'variant' and - * 'private' subtags can take maximum 15 values whereas 'extlang' can take - * maximum 3 values. - *
- * @return array|null an array containing a list of key-value pairs, where the keys - * identify the particular locale ID subtags, and the values are the - * associated subtag values. The array will be ordered as the locale id - * subtags e.g. in the locale id if variants are '-varX-varY-varZ' then the - * returned array will have variant0=>varX , variant1=>varY , - * variant2=>varZ - */ - public static function parseLocale(string $locale): ?array {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to extract the variants from - *
- * @return array|null The array containing the list of all variants subtag for the locale - * or NULL if not present - */ - public static function getAllVariants(string $locale): ?array {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The language tag to check - *
- * @param string $locale- * The language range to check against - *
- * @param bool $canonicalize- * If true, the arguments will be converted to canonical form before - * matching. - *
- * @return bool|null TRUE if $locale matches $langtag FALSE otherwise. - */ - public static function filterMatches( - string $languageTag, - string $locale, - bool $canonicalize = false - ): ?bool {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * An array containing a list of language tags to compare to - * locale. Maximum 100 items allowed. - *
- * @param string $locale- * The locale to use as the language range when matching. - *
- * @param bool $canonicalize- * If true, the arguments will be converted to canonical form before - * matching. - *
- * @param string $defaultLocale- * The locale to use if no match is found. - *
- * @return string|null The closest matching language tag or default value. - */ - public static function lookup( - array $languageTag, - string $locale, - bool $canonicalize = false, - string|null $defaultLocale = null - ): ?string {} - - /** - * @link https://php.net/manual/en/locale.canonicalize.php - * @param string $locale - * @return string|null - */ - public static function canonicalize(string $locale): ?string {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The string containing the "Accept-Language" header according to format in RFC 2616. - *
- * @return string|false The corresponding locale identifier. - */ - public static function acceptFromHttp(string $header): string|false {} -} - -class MessageFormatter -{ - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to use when formatting arguments - *
- * @param string $pattern- * The pattern string to stick arguments into. - * The pattern uses an 'apostrophe-friendly' syntax; it is run through - * umsg_autoQuoteApostrophe - * before being interpreted. - *
- * @throws IntlException on failure. - */ - public function __construct( - string $locale, - string $pattern - ) {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to use when formatting arguments - *
- * @param string $pattern- * The pattern string to stick arguments into. - * The pattern uses an 'apostrophe-friendly' syntax; it is run through - * umsg_autoQuoteApostrophe - * before being interpreted. - *
- * @return MessageFormatter|null The formatter object - */ - public static function create( - string $locale, - string $pattern - ): ?MessageFormatter {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Arguments to insert into the format string - *
- * @return string|false The formatted string, or FALSE if an error occurred - */ - public function format(array $values): string|false {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to use for formatting locale-dependent parts - *
- * @param string $pattern- * The pattern string to insert things into. - * The pattern uses an 'apostrophe-friendly' syntax; it is run through - * umsg_autoQuoteApostrophe - * before being interpreted. - *
- * @param array $values- * The array of values to insert into the format string - *
- * @return string|false The formatted pattern string or FALSE if an error occurred - */ - public static function formatMessage( - string $locale, - string $pattern, - array $values - ): string|false {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The string to parse - *
- * @return array|false An array containing the items extracted, or FALSE on error - */ - public function parse(string $string): array|false {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to use for parsing locale-dependent parts - *
- * @param string $pattern- * The pattern with which to parse the value. - *
- * @param string $message- * The string to parse, conforming to the pattern. - *
- * @return array|false An array containing items extracted, or FALSE on error - */ - public static function parseMessage( - string $locale, - string $pattern, - string $message - ): array|false {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The pattern string to use in this message formatter. - * The pattern uses an 'apostrophe-friendly' syntax; it is run through - * umsg_autoQuoteApostrophe - * before being interpreted. - *
- * @return bool TRUE on success or FALSE on failure. - */ - public function setPattern(string $pattern): bool {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Locale to use when formatting or parsing; default is specified in the ini setting intl.default_locale. - *
- * @param int $dateType- * Date type to use (none, - * short, medium, - * long, full). - * This is one of the - * IntlDateFormatter constants. - *
- * @param int $timeType- * Time type to use (none, - * short, medium, - * long, full). - * This is one of the - * IntlDateFormatter constants. - *
- * @param string $timezone [optional]- * Time zone ID, default is system default. - *
- * @param int $calendar [optional]- * Calendar to use for formatting or parsing; default is Gregorian. - * This is one of the - * IntlDateFormatter calendar constants. - *
- * @param string $pattern [optional]- * Optional pattern to use when formatting or parsing. - * Possible patterns are documented at http://userguide.icu-project.org/formatparse/datetime. - *
- * @return IntlDateFormatter|null - */ - public static function create( - string|null $locale, - int $dateType = 0, - int $timeType = 0, - $timezone = null, - IntlCalendar|int|null $calendar = null, - string|null $pattern = null - ): ?IntlDateFormatter {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The calendar to use. - * Default is IntlDateFormatter::GREGORIAN. - *
- * @return bool TRUE on success or FALSE on failure. - */ - public function setCalendar(IntlCalendar|int|null $calendar): bool {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The timezone to use for this formatter. This can be specified in the - * following forms: - *
- * NULL, in which case the default timezone will be used, as specified in - * the ini setting {@link "https://secure.php.net/manual/en/datetime.configuration.php#ini.date.timezone" date.timezone} or - * through the function {@link "https://secure.php.net/manual/en/function.date-default-timezone-set.php" date_default_timezone_set()} and as - * returned by {@link "https://secure.php.net/manual/en/function.date-default-timezone-get.php" date_default_timezone_get()}. - *
- *- * An {@link "https://secure.php.net/manual/en/class.intltimezone.php" IntlTimeZone}, which will be used directly. - *
- *- * A {@link "https://secure.php.net/manual/en/class.datetimezone.php" DateTimeZone}. Its identifier will be extracted - * and an ICU timezone object will be created; the timezone will be backed - * by ICU's database, not PHP's. - *
- *- * A {@link "https://secure.php.net/manual/en/language.types.string.php" string}, which should be a valid ICU timezone identifier. - * See IntlTimeZone::createTimeZoneIDEnumeration(). Raw offsets such as "GMT+08:30" are also accepted. - *
- *- * New pattern string to use. - * Possible patterns are documented at http://userguide.icu-project.org/formatparse/datetime. - *
- * @return bool TRUE on success or FALSE on failure. - * Bad formatstrings are usually the cause of the failure. - */ - public function setPattern(string $pattern): bool {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Sets whether the parser is lenient or not, default is TRUE (lenient). - *
- * @return void - */ - public function setLenient(bool $lenient): void {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Value to format. This may be a DateTime object, - * an integer representing a Unix timestamp value (seconds - * since epoch, UTC) or an array in the format output by - * localtime. - *
- * @return string|false The formatted string or, if an error occurred, FALSE. - */ - public function format( - $datetime, - ): string|false {} - - /** - * (PHP 5 >= 5.5.0, PECL intl >= 3.0.0)- * An object of type {@link "https://secure.php.net/manual/en/class.intlcalendar.php" IntlCalendar} or {@link "https://secure.php.net/manual/en/class.datetime.php" DateTime}. The timezone information in the object will be used. - *
- * @param mixed $format [optional]- * How to format the date/time. This can either be an {@link "https://secure.php.net/manual/en/language.types.array.php" array} with - * two elements (first the date style, then the time style, these being one - * of the constants IntlDateFormatter::NONE, - * IntlDateFormatter::SHORT, - * IntlDateFormatter::MEDIUM, - * IntlDateFormatter::LONG, - * IntlDateFormatter::FULL), a long with - * the value of one of these constants (in which case it will be used both - * for the time and the date) or a {@link "https://secure.php.net/manual/en/language.types.string.php" string} with the format - * described in {@link "http://www.icu-project.org/apiref/icu4c/classSimpleDateFormat.html#details" the ICU documentation}. - * If NULL, the default style will be used. - *
- * @param string|null $locale [optional]- * The locale to use, or NULL to use the {@link "https://secure.php.net/manual/en/intl.configuration.php#ini.intl.default-locale" default one}.
- * @return string|false A string with result or FALSE on failure. - */ - public static function formatObject($datetime, $format = null, string|null $locale = null): string|false {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * string to convert to a time - *
- * @param int &$offset [optional]- * Position at which to start the parsing in $value (zero-based). - * If no error occurs before $value is consumed, $parse_pos will contain -1 - * otherwise it will contain the position at which parsing ended (and the error occurred). - * This variable will contain the end position if the parse fails. - * If $parse_pos > strlen($value), the parse fails immediately. - *
- * @return int|float|false timestamp parsed value - */ - public function parse(string $string, &$offset = null): int|float|false {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * string to convert to a time - *
- * @param int &$offset [optional]- * Position at which to start the parsing in $value (zero-based). - * If no error occurs before $value is consumed, $parse_pos will contain -1 - * otherwise it will contain the position at which parsing ended . - * If $parse_pos > strlen($value), the parse fails immediately. - *
- * @return array|false Localtime compatible array of integers : contains 24 hour clock value in tm_hour field - */ - public function localtime(string $string, &$offset = null): array|false {} - - /** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)Locale for which the resources should be loaded (locale name, e.g. en_CA).
- * @param string $bundleThe directory where the data is stored or the name of the .dat file.
- * @param bool $fallback [optional]Whether locale should match exactly or fallback to parent locale is allowed.
- */ - public function __construct( - string|null $locale, - string|null $bundle, - bool $fallback = true - ) {} - - /** - * (PHP >= 5.3.2, PECL intl >= 2.0.0)- * Locale for which the resources should be loaded (locale name, e.g. en_CA). - *
- * @param string $bundle- * The directory where the data is stored or the name of the .dat file. - *
- * @param bool $fallback [optional]- * Whether locale should match exactly or fallback to parent locale is allowed. - *
- * @return ResourceBundle|null ResourceBundle object or null on error. - */ - public static function create( - string|null $locale, - string|null $bundle, - bool $fallback = true - ): ?ResourceBundle {} - - /** - * (PHP >= 5.3.2, PECL intl >= 2.0.0)- * Data index, must be string or integer. - *
- * @param bool $fallback - * @return mixed the data located at the index or NULL on error. Strings, integers and binary data strings - * are returned as corresponding PHP types, integer array is returned as PHP array. Complex types are - * returned as ResourceBundle object. - */ - public function get($index, bool $fallback = true): mixed {} - - /** - * (PHP >= 5.3.2, PECL intl >= 2.0.0)- * Path of ResourceBundle for which to get available locales, or - * empty string for default locales list. - *
- * @return array|false the list of locales supported by the bundle. - */ - public static function getLocales(string $bundle): array|false {} - - /** - * (PHP >= 5.3.2, PECL intl >= 2.0.0)- * The id. - *
- * @param int $direction [optional]- * The direction, defaults to - * Transliterator::FORWARD. - * May also be set to - * Transliterator::REVERSE. - *
- * @return Transliterator|null a Transliterator object on success, - * or NULL on failure. - */ - public static function create( - string $id, - int $direction = 0 - ): ?Transliterator {} - - /** - * (PHP >= 5.4.0, PECL intl >= 2.0.0)- * The rules. - *
- * @param int $direction [optional]- * The direction, defaults to - * {@see Transliterator::FORWARD}. - * May also be set to - * {@see Transliterator::REVERSE}. - *
- * @return Transliterator|null a Transliterator object on success, - * or NULL on failure. - */ - public static function createFromRules( - string $rules, - int $direction = 0 - ): ?Transliterator {} - - /** - * (PHP >= 5.4.0, PECL intl >= 2.0.0)- * The string to be transformed. - *
- * @param int $start [optional]- * The start index (in UTF-16 code units) from which the string will start - * to be transformed, inclusive. Indexing starts at 0. The text before will - * be left as is. - *
- * @param int $end [optional]- * The end index (in UTF-16 code units) until which the string will be - * transformed, exclusive. Indexing starts at 0. The text after will be - * left as is. - *
- * @return string|false The transfomed string on success, or FALSE on failure. - */ - public function transliterate( - string $string, - int $start = 0, - int $end = -1 - ): string|false {} - - /** - * (PHP >= 5.4.0, PECL intl >= 2.0.0)- *
- * @param string &$errorCode [optional]- *
- * @return bool - */ - public function isSuspicious(string $string, &$errorCode = null): bool {} - - /** - * (PHP >= 5.4.0, PECL intl >= 2.0.0)- *
- * @param string $string2- *
- * @param int &$errorCode [optional]- *
- * @return bool - */ - public function areConfusable( - string $string1, - string $string2, - &$errorCode = null - ): bool {} - - /** - * (PHP >= 5.4.0, PECL intl >= 2.0.0)- *
- * @return void - */ - public function setAllowedLocales(string $locales): void {} - - /** - * (PHP >= 5.4.0, PECL intl >= 2.0.0)- *
- * @return void - */ - public function setChecks(int $checks): void {} - - /** - * @param int $level - */ - public function setRestrictionLevel(int $level): void {} - - /** - * @since 8.4 - */ - public function setAllowedChars(string $pattern, int $patternOptions = 0): void {} -} - -/** - * @since 5.5 - */ -class IntlGregorianCalendar extends IntlCalendar -{ - /** - * @link https://www.php.net/manual/en/intlgregoriancalendar.construct - * @param int $timezoneOrYear [optional] - * @param int $localeOrMonth [optional] - * @param int $day [optional] - * @param int $hour [optional] - * @param int $minute [optional] - * @param int $second [optional] - */ - public function __construct($timezoneOrYear, $localeOrMonth, $day, $hour, $minute, $second) {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field constants}. - * These are integer values between 0 and - * IntlCalendar::FIELD_COUNT. - *
- * @param int $valueThe signed amount to add to the current field. If the amount is positive, the instant will be moved forward; if it is negative, the instant wil be moved into the past. The unit is implicit to the field type. - * For instance, hours for IntlCalendar::FIELD_HOUR_OF_DAY.
- * @return bool Returns TRUE on success or FALSE on failure. - */ - public function add( - int $field, - int $value - ): bool {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)The calendar whose time will be checked against this object's time.
- * @return bool - * Returns TRUE if this object's current time is after that of the - * calendar argument's time. Returns FALSE otherwise. - * Also returns FALSE on failure. You can use {@link https://secure.php.net/manual/en/intl.configuration.php#ini.intl.use-exceptions exceptions} or - * {@link https://secure.php.net/manual/en/function.intl-get-error-code.php intl_get_error_code()} to detect error conditions. - */ - public function after(IntlCalendar $other): bool {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)The calendar whose time will be checked against this object's time.
- * @return bool - * Returns TRUE if this object's current time is before that of the - * calendar argument's time. Returns FALSE otherwise. - * Also returns FALSE on failure. You can use {@link https://secure.php.net/manual/en/intl.configuration.php#ini.intl.use-exceptions exceptions} or - * {@link https://secure.php.net/manual/en/function.intl-get-error-code.php intl_get_error_code()} to detect error conditions. - */ - public function before(IntlCalendar $other): bool {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field constants}. These are integer - * values between 0 and - * IntlCalendar::FIELD_COUNT. - *
- * @return bool Returns TRUE on success or FALSE on failure. Failure can only occur is invalid arguments are provided. - */ - public function clear(int|null $field = null): bool {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)
- * The timezone to use. - *
- * - *- * NULL, in which case the default timezone will be used, as specified in - * the ini setting {@link https://secure.php.net/manual/en/datetime.configuration.php#ini.date.timezone date.timezone} or - * through the function {@link https://secure.php.net/manual/en/function.date-default-timezone-set.php date_default_timezone_set()} and as - * returned by {@link https://secure.php.net/manual/en/function.date-default-timezone-get.php date_default_timezone_get()}. - *
- *- * An {@link https://secure.php.net/manual/en/class.intltimezone.php IntlTimeZone}, which will be used directly. - *
- *- * A {@link https://secure.php.net/manual/en/class.datetimezone.php DateTimeZone}. Its identifier will be extracted - * and an ICU timezone object will be created; the timezone will be backed - * by ICU's database, not PHP's. - *
- *- * A {@link https://secure.php.net/manual/en/language.types.string.php string}, which should be a valid ICU timezone identifier. - * See IntlTimeZone::createTimeZoneIDEnumeration(). Raw - * offsets such as "GMT+08:30" are also accepted. - *
- *- * A locale to use or NULL to use {@link https://secure.php.net/manual/en/intl.configuration.php#ini.intl.default-locale the default locale}. - *
- * @return IntlCalendar|null - * The created {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} instance or NULL on - * failure. - */ - public static function createInstance($timezone = null, string|null $locale = null): ?IntlCalendar {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * Returns TRUE if the current time of both this and the passed in - * {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} object are the same, or FALSE - * otherwise. The value FALSE can also be returned on failure. This can only - * happen if bad arguments are passed in. In any case, the two cases can be - * distinguished by calling {@link https://secure.php.net/manual/en/function.intl-get-error-code.php intl_get_error_code()}. - *
- */ - public function equals(IntlCalendar $other): bool {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The time against which to compare the quantity represented by the - * field. For the result to be positive, the time - * given for this parameter must be ahead of the time of the object the - * method is being invoked on. - *
- * @param int $field- * The field that represents the quantity being compared. - *
- * - *- * One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field constants}. These are integer - * values between 0 and - * IntlCalendar::FIELD_COUNT. - *
- * @return int|false Returns a (signed) difference of time in the unit associated with the - * specified field or FALSE on failure. - */ - public function fieldDifference( - float $timestamp, - int $field - ): int|false {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a2)- * A {@link https://secure.php.net/manual/en/class.datetime.php DateTime} object or a {@link https://secure.php.net/manual/en/language.types.string.php string} that - * can be passed to {@link https://secure.php.net/manual/en/datetime.construct.php DateTime::__construct()}. - *
- * @param $locale [optional] - * @return IntlCalendar|null - * The created {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} object or NULL in case of - * failure. If a {@link https://secure.php.net/manual/en/language.types.string.php string} is passed, any exception that occurs - * inside the {@link https://secure.php.net/manual/en/class.datetime.php DateTime} constructor is propagated. - */ - public static function fromDateTime( - DateTime|string $datetime, - string|null $locale - ): ?IntlCalendar {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field constants}. These are integer - * values between 0 and - * IntlCalendar::FIELD_COUNT. - *
- * @return int|false An integer with the value of the time field. - */ - public function get(int $field): int|false {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field constants}. These are integer - * values between 0 and - * IntlCalendar::FIELD_COUNT. - *
- * @return int|false - * An {@link https://secure.php.net/manual/en/language.types.integer.php int} representing the maximum value in the units associated - * with the given field or FALSE on failure. - */ - public function getActualMaximum(int $field): int|false {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field constants}. - * These are integer values between 0 and - * IntlCalendar::FIELD_COUNT. - *
- * @return int|false - * An {@link https://secure.php.net/manual/en/language.types.integer.php int} representing the minimum value in the field's - * unit or FALSE on failure. - */ - public function getActualMinimum(int $field): int|false {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * One of the constants IntlCalendar::DOW_SUNDAY, - * IntlCalendar::DOW_MONDAY, ..., - * IntlCalendar::DOW_SATURDAY. - *
- * @return int|false - * Returns one of the constants - * IntlCalendar::DOW_TYPE_WEEKDAY, - * IntlCalendar::DOW_TYPE_WEEKEND, - * IntlCalendar::DOW_TYPE_WEEKEND_OFFSET or - * IntlCalendar::DOW_TYPE_WEEKEND_CEASE or FALSE on failure. - */ - public function getDayOfWeekType(int $dayOfWeek): int|false {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The locale keyword for which relevant values are to be queried. Only - * 'calendar' is supported. - *
- * @param string $locale- * The locale onto which the keyword/value pair are to be appended. - *
- * @param bool $onlyCommon - *- * Whether to show only the values commonly used for the specified locale. - *
- * @return Iterator|false An iterator that yields strings with the locale keyword values or FALSE on failure. - */ - public static function getKeywordValuesForLocale( - string $keyword, - string $locale, - bool $onlyCommon - ): IntlIterator|false {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field constants}. These are integer - * values between 0 and - * IntlCalendar::FIELD_COUNT. - *
- * @return int|false - * An {@link https://secure.php.net/manual/en/language.types.integer.ph int} representing a field value in the field's - * unit or FALSE on failure. - */ - public function getLeastMaximum(int $field): int|false {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * Whether to fetch the actual locale (the locale from which the calendar - * data originates, with Locale::ACTUAL_LOCALE) or the - * valid locale, i.e., the most specific locale supported by ICU relatively - * to the requested locale – see Locale::VALID_LOCALE. - * From the most general to the most specific, the locales are ordered in - * this fashion – actual locale, valid locale, requested locale. - *
- * @return string|false - */ - public function getLocale(int $type): string|false {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field constants}. These are integer - * values between 0 and - * IntlCalendar::FIELD_COUNT. - *
- * @return int|false - */ - public function getMaximum(int $field): int|false {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field}. These are integer - * values between 0 and - * IntlCalendar::FIELD_COUNT. - *
- * @return int|false - * An int representing a value for the given field in the field's unit or FALSE on failure. - */ - public function getMinimum(int $field): int|false {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * One of the constants IntlCalendar::DOW_SUNDAY, - * IntlCalendar::DOW_MONDAY, ..., - * IntlCalendar::DOW_SATURDAY. - *
- * @return int|false - * The number of milliseconds into the day at which the the weekend begins or - * ends or FALSE on failure. - */ - public function getWeekendTransition(int $dayOfWeek): int|false {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * An optional timestamp representing the number of milliseconds since the - * epoch, excluding leap seconds. If NULL, this object's current time is - * used instead. - *
- * @return bool - *A {@link https://secure.php.net/manual/en/language.types.boolean.php bool} indicating whether the given or this object's time occurs - * in a weekend. - *
- *- * The value FALSE may also be returned on failure, for instance after giving - * a date out of bounds on non-lenient mode; use {@link https://secure.php.net/manual/en/intl.configuration.php#ini.intl.use-exceptions exceptions} or query - * {@link https://secure.php.net/manual/en/function.intl-get-error-code.php intl_get_error_code()} to disambiguate.
- */ - public function isWeekend(float|null $timestamp = null): bool {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time - * {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field constants}. These are integer - * values between 0 and - * IntlCalendar::FIELD_COUNT. - *
- * @param mixed $value- * The (signed) amount to add to the field, TRUE for rolling up (adding - * 1), or FALSE for rolling down (subtracting - * 1). - *
- * @return bool Returns TRUE on success or FALSE on failure. - */ - public function roll(int $field, $value): bool {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time - * {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field constants}. - * These are integer values between 0 and - * IntlCalendar::FIELD_COUNT. - *
- * @return bool Assuming there are no argument errors, returns TRUE iif the field is set. - */ - public function PS_UNRESERVE_PREFIX_isSet(int $field): bool {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field constants}. These are integer - * values between 0 and - * IntlCalendar::FIELD_COUNT. - *
- * @param int $month- * The new value for IntlCalendar::FIELD_MONTH. - *
- * @param int $dayOfMonth [optional]- * The new value for IntlCalendar::FIELD_DAY_OF_MONTH. - * The month sequence is zero-based, i.e., January is represented by 0, - * February by 1, ..., December is 11 and Undecember (if the calendar has - * it) is 12. - *
- * @param int $hour [optional] - *- * The new value for IntlCalendar::FIELD_HOUR_OF_DAY. - *
- * @param int $minute [optional] - *- * The new value for IntlCalendar::FIELD_MINUTE. - *
- * @param int $second [optional]- * The new value for IntlCalendar::FIELD_SECOND. - *
- * @return bool Returns TRUE on success and FALSE on failure. - */ - public function set($year, $month, $dayOfMonth = null, $hour = null, $minute = null, $second = null) {} - - /** - * (PHP 5 >= 5.5.0 PECL intl >= 3.0.0a1)- * One of the constants IntlCalendar::DOW_SUNDAY, - * IntlCalendar::DOW_MONDAY, ..., - * IntlCalendar::DOW_SATURDAY. - *
- * @return bool Returns TRUE on success. Failure can only happen due to invalid parameters. - */ - public function setFirstDayOfWeek(int $dayOfWeek): bool {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * Use TRUE to activate the lenient mode; FALSE otherwise. - *
- * @return bool Returns TRUE on success. Failure can only happen due to invalid parameters. - */ - public function setLenient(bool $lenient): bool {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * One of the constants IntlCalendar::WALLTIME_FIRST or - * IntlCalendar::WALLTIME_LAST. - *
- * @return bool - * Returns TRUE on success. Failure can only happen due to invalid parameters. - */ - public function setRepeatedWallTimeOption(int $option): bool {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * One of the constants IntlCalendar::WALLTIME_FIRST, - * IntlCalendar::WALLTIME_LAST or - * IntlCalendar::WALLTIME_NEXT_VALID. - *
- * @return bool - *- * Returns TRUE on success. Failure can only happen due to invalid parameters. - *
- */ - public function setSkippedWallTimeOption(int $option): bool {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * An instant represented by the number of number of milliseconds between - * such instant and the epoch, ignoring leap seconds. - *
- * @return bool - * Returns TRUE on success and FALSE on failure. - */ - public function setTime(float $timestamp): bool {} - - /** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The new timezone to be used by this calendar. It can be specified in the - * following ways: - * - *
- * NULL, in which case the default timezone will be used, as specified in - * the ini setting {@link https://secure.php.net/manual/en/datetime.configuration.php#ini.date.timezone date.timezone} or - * through the function {@link https://secure.php.net/manual/en/function.date-default-timezone-set.php date_default_timezone_set()} and as - * returned by {@link https://secure.php.net/manual/en/function.date-default-timezone-get.php date_default_timezone_get()}. - *
- *- * An {@link https://secure.php.net/manual/en/class.intltimezone.php IntlTimeZone}, which will be used directly. - *
- *- * A {@link https://secure.php.net/manual/en/class.datetimezone.php DateTimeZone}. Its identifier will be extracted - * and an ICU timezone object will be created; the timezone will be backed - * by ICU's database, not PHP's. - *
- *- * A {@link https://secure.php.net/manual/en/language.types.string.php string}, which should be a valid ICU timezone identifier. - * See b>IntlTimeZone::createTimeZoneIDEnumeration(). Raw - * offsets such as "GMT+08:30" are also accepted. - *
- *- * The locale containing the required collation rules. Special values for - * locales can be passed in - if null is passed for the locale, the - * default locale collation rules will be used. If empty string ("") or - * "root" are passed, UCA rules will be used. - *
- * @return Collator|null Return new instance of Collator object, or NULL - * on error. - */ -function collator_create(string $locale): ?Collator {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The first string to compare. - *
- * @param string $string2- * The second string to compare. - *
- * @return int|false Return comparison result: - *- *
- * 1 if string1 is greater than - * string2 ; - *
- *- * 0 if string1 is equal to - * string2; - *
- *- * -1 if string1 is less than - * string2 . - *
- * On error - * boolean - * FALSE - * is returned. - */ -function collator_compare(Collator $object, string $string1, string $string2): int|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Attribute to get value for. - *
- * @return int|false Attribute value, or boolean FALSE on error. - */ -function collator_get_attribute(Collator $object, int $attribute): int|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)Attribute.
- * @param int $value- * Attribute value. - *
- * @return bool TRUE on success or FALSE on failure. - */ -function collator_set_attribute(Collator $object, int $attribute, int $value): bool {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)Strength to set.
- *- * Possible values are: - * Collator::PRIMARY - *
- * @return bool TRUE on success or FALSE on failure. - */ -function collator_set_strength(Collator $object, int $strength): bool {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Array of strings to sort. - *
- * @param int $flags- * Optional sorting type, one of the following: - *
- *- * Collator::SORT_REGULAR - * - compare items normally (don't change types) - *
- * @return bool TRUE on success or FALSE on failure. - */ -function collator_sort(Collator $object, array &$array, int $flags = 0): bool {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)Array of strings to sort
- * @return bool TRUE on success or FALSE on failure. - */ -function collator_sort_with_sort_keys( - Collator $object, - array &$array, -): bool {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)Array of strings to sort.
- * @param int $flags- * Optional sorting type, one of the following: - * Collator::SORT_REGULAR - * - compare items normally (don't change types) - *
- * @return bool TRUE on success or FALSE on failure. - */ -function collator_asort(Collator $object, array &$array, int $flags = 0): bool {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * You can choose between valid and actual locale ( - * Locale::VALID_LOCALE and - * Locale::ACTUAL_LOCALE, - * respectively). The default is the actual locale. - *
- * @return string|false Real locale name from which the collation data comes. If the collator was - * instantiated from rules or an error occurred, returns - * boolean FALSE. - */ -function collator_get_locale(Collator $object, int $type): string|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The string to produce the key from. - *
- * @return string|false the collation key for the string. Collation keys can be compared directly instead of strings. - */ -function collator_get_sort_key( - Collator $object, - string $string, -): string|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Locale in which the number would be formatted (locale name, e.g. en_CA). - *
- * @param int $style- * Style of the formatting, one of the - * format style constants. If - * NumberFormatter::PATTERN_DECIMAL - * or NumberFormatter::PATTERN_RULEBASED - * is passed then the number format is opened using the given pattern, - * which must conform to the syntax described in - * ICU DecimalFormat - * documentation or - * ICU RuleBasedNumberFormat - * documentation, respectively. - *
- * @param string|null $pattern [optional]- * Pattern string if the chosen style requires a pattern. - *
- * @return NumberFormatter|null NumberFormatter object or NULL on error. - */ -function numfmt_create(string $locale, int $style, ?string $pattern = null): ?NumberFormatter {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The value to format. Can be integer or float, - * other values will be converted to a numeric value. - *
- * @param int $type- * The - * formatting type to use. - *
- * @return string|false the string containing formatted value, or FALSE on error. - */ -function numfmt_format(NumberFormatter $formatter, int|float $num, int $type = 0): string|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The - * formatting type to use. By default, - * NumberFormatter::TYPE_DOUBLE is used. - *
- * @param int &$offset [optional]- * Offset in the string at which to begin parsing. On return, this value - * will hold the offset at which parsing ended. - *
- * @return int|float|false The value of the parsed number or FALSE on error. - */ -function numfmt_parse(NumberFormatter $formatter, string $string, int $type = NumberFormatter::TYPE_DOUBLE, &$offset = null): int|float|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The numeric currency value. - *
- * @param string $currency- * The 3-letter ISO 4217 currency code indicating the currency to use. - *
- * @return string|false String representing the formatted currency value. - */ -function numfmt_format_currency(NumberFormatter $formatter, float $amount, string $currency): string|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Parameter to receive the currency name (3-letter ISO 4217 currency - * code). - *
- * @param int &$offset [optional]- * Offset in the string at which to begin parsing. On return, this value - * will hold the offset at which parsing ended. - *
- * @return float|false The parsed numeric value or FALSE on error. - */ -function numfmt_parse_currency(NumberFormatter $formatter, string $string, &$currency, &$offset = null): float|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Attribute specifier - one of the - * numeric attribute constants. - *
- * @param int|float $value- * The attribute value. - *
- * @return bool TRUE on success or FALSE on failure. - */ -function numfmt_set_attribute(NumberFormatter $formatter, int $attribute, int|float $value): bool {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Attribute specifier - one of the - * numeric attribute constants. - *
- * @return int|float|false Return attribute value on success, or FALSE on error. - */ -function numfmt_get_attribute(NumberFormatter $formatter, int $attribute): int|float|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Attribute specifier - one of the - * text attribute - * constants. - *
- * @param string $value- * Text for the attribute value. - *
- * @return bool TRUE on success or FALSE on failure. - */ -function numfmt_set_text_attribute(NumberFormatter $formatter, int $attribute, string $value): bool {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Attribute specifier - one of the - * text attribute constants. - *
- * @return string|false Return attribute value on success, or FALSE on error. - */ -function numfmt_get_text_attribute(NumberFormatter $formatter, int $attribute): string|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Symbol specifier, one of the - * format symbol constants. - *
- * @param string $value- * Text for the symbol. - *
- * @return bool TRUE on success or FALSE on failure. - */ -function numfmt_set_symbol(NumberFormatter $formatter, int $symbol, string $value): bool {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Symbol specifier, one of the - * format symbol constants. - *
- * @return string|false The symbol string or FALSE on error. - */ -function numfmt_get_symbol(NumberFormatter $formatter, int $symbol): string|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Pattern in syntax described in - * ICU DecimalFormat - * documentation. - *
- * @return bool TRUE on success or FALSE on failure. - */ -function numfmt_set_pattern(NumberFormatter $formatter, string $pattern): bool {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * You can choose between valid and actual locale ( - * Locale::VALID_LOCALE, - * Locale::ACTUAL_LOCALE, - * respectively). The default is the actual locale. - *
- * @return string|false The locale name used to create the formatter. - */ -function numfmt_get_locale(NumberFormatter $formatter, int $type = 0): string|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)The input string to normalize
- * @param int $form [optional]One of the normalization forms.
- * @return string|false The normalized string or FALSE if an error occurred. - */ -function normalizer_normalize(string $string, int $form = Normalizer::FORM_C): string|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)The input string to normalize
- * @param int $form [optional]- * One of the normalization forms. - *
- * @return bool TRUE if normalized, FALSE otherwise or if there an error - */ -function normalizer_is_normalized(string $string, int $form = Normalizer::FORM_C): bool {} - -/** - * Gets the default locale value from the intl global 'default_locale' - * @link https://php.net/manual/en/function.locale-get-default.php - * @return string a string with the current Locale. - */ -function locale_get_default(): string {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The new Locale name. A comprehensive list of the supported locales is - * available at . - *
- * @return bool TRUE on success or FALSE on failure. - */ -function locale_set_default(string $locale): bool {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to extract the primary language code from - *
- * @return string|null The language code associated with the language or NULL in case of error. - */ -function locale_get_primary_language(string $locale): ?string {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to extract the script code from - *
- * @return string|null The script subtag for the locale or NULL if not present - */ -function locale_get_script(string $locale): ?string {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to extract the region code from - *
- * @return string|null The region subtag for the locale or NULL if not present - */ -function locale_get_region(string $locale): ?string {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to extract the keywords from - *
- * @return array|false|null Associative array containing the keyword-value pairs for this locale - */ -function locale_get_keywords(string $locale): array|false|null {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to return a display script for - *
- * @param string|null $displayLocale- * Optional format locale to use to display the script name - *
- * @return string|false Display name of the script for the $locale in the format appropriate for - * $in_locale. - */ -function locale_get_display_script( - string $locale, - ?string $displayLocale = null -): string|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to return a display region for. - *
- * @param string|null $displayLocale- * Optional format locale to use to display the region name - *
- * @return string|false display name of the region for the $locale in the format appropriate for - * $in_locale. - */ -function locale_get_display_region( - string $locale, - ?string $displayLocale = null -): string|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to return a display name for. - *
- * @param string|null $displayLocaleoptional format locale
- * @return string|false Display name of the locale in the format appropriate for $in_locale. - */ -function locale_get_display_name( - string $locale, - ?string $displayLocale = null -): string|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to return a display language for - *
- * @param string|null $displayLocale- * Optional format locale to use to display the language name - *
- * @return string|false display name of the language for the $locale in the format appropriate for - * $in_locale. - */ -function locale_get_display_language( - string $locale, - ?string $displayLocale = null -): string|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to return a display variant for - *
- * @param string|null $displayLocale- * Optional format locale to use to display the variant name - *
- * @return string|false Display name of the variant for the $locale in the format appropriate for - * $in_locale. - */ -function locale_get_display_variant( - string $locale, - ?string $displayLocale = null -): string|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * an array containing a list of key-value pairs, where the keys identify - * the particular locale ID subtags, and the values are the associated - * subtag values. - *
- * The 'variant' and 'private' subtags can take maximum 15 values - * whereas 'extlang' can take maximum 3 values.e.g. Variants are allowed - * with the suffix ranging from 0-14. Hence the keys for the input array - * can be variant0, variant1, ...,variant14. In the returned locale id, - * the subtag is ordered by suffix resulting in variant0 followed by - * variant1 followed by variant2 and so on. - *
- *- * The 'variant', 'private' and 'extlang' multiple values can be specified both - * as array under specific key (e.g. 'variant') and as multiple numbered keys - * (e.g. 'variant0', 'variant1', etc.). - *
- * - * @return string|false The corresponding locale identifier. - */ -function locale_compose(array $subtags): string|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to extract the subtag array from. Note: The 'variant' and - * 'private' subtags can take maximum 15 values whereas 'extlang' can take - * maximum 3 values. - *
- * @return string[]|null an array containing a list of key-value pairs, where the keys - * identify the particular locale ID subtags, and the values are the - * associated subtag values. The array will be ordered as the locale id - * subtags e.g. in the locale id if variants are '-varX-varY-varZ' then the - * returned array will have variant0=>varX , variant1=>varY , - * variant2=>varZ - */ -function locale_parse(string $locale): ?array {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to extract the variants from - *
- * @return array|null The array containing the list of all variants subtag for the locale - * or NULL if not present - */ -function locale_get_all_variants(string $locale): ?array {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The language tag to check - *
- * @param string $locale- * The language range to check against - *
- * @param bool $canonicalize- * If true, the arguments will be converted to canonical form before - * matching. - *
- * @return bool|null TRUE if $locale matches $langtag FALSE otherwise. - */ -function locale_filter_matches( - string $languageTag, - string $locale, - bool $canonicalize = false -): ?bool {} - -/** - * Canonicalize the locale string - * @param string $locale - * - * @return null|string - */ -function locale_canonicalize(string $locale): ?string {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * An array containing a list of language tags to compare to - * locale. Maximum 100 items allowed. - *
- * @param string $locale- * The locale to use as the language range when matching. - *
- * @param bool $canonicalize- * If true, the arguments will be converted to canonical form before - * matching. - *
- * @param string|null $defaultLocale- * The locale to use if no match is found. - *
- * @return string|null The closest matching language tag or default value. - */ -function locale_lookup( - array $languageTag, - string $locale, - bool $canonicalize = false, - ?string $defaultLocale = null, -): ?string {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The string containing the "Accept-Language" header according to format in RFC 2616. - *
- * @return string|false The corresponding locale identifier. - */ -function locale_accept_from_http(string $header): string|false {} - -/** - * Constructs a new message formatter - * @param string $locale - * @param string $pattern - * @return MessageFormatter|null - */ -function msgfmt_create(string $locale, string $pattern): ?MessageFormatter {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Arguments to insert into the format string - *
- * @return string|false The formatted string, or FALSE if an error occurred - */ -function msgfmt_format(MessageFormatter $formatter, array $values): string|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to use for formatting locale-dependent parts - *
- * @param string $pattern- * The pattern string to insert things into. - * The pattern uses an 'apostrophe-friendly' syntax; it is run through - * umsg_autoQuoteApostrophe - * before being interpreted. - *
- * @param array $values- * The array of values to insert into the format string - *
- * @return string|false The formatted pattern string or FALSE if an error occurred - */ -function msgfmt_format_message(string $locale, string $pattern, array $values): string|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The string to parse - *
- * @return array|false An array containing the items extracted, or FALSE on error - */ -function msgfmt_parse(MessageFormatter $formatter, string $string): array|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The locale to use for parsing locale-dependent parts - *
- * @param string $pattern- * The pattern with which to parse the value. - *
- * @param string $message- * The string to parse, conforming to the pattern. - *
- * @return array|false An array containing items extracted, or FALSE on error - */ -function msgfmt_parse_message(string $locale, string $pattern, string $message): array|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The pattern string to use in this message formatter. - * The pattern uses an 'apostrophe-friendly' syntax; it is run through - * umsg_autoQuoteApostrophe - * before being interpreted. - *
- * @return bool TRUE on success or FALSE on failure. - */ -function msgfmt_set_pattern(MessageFormatter $formatter, string $pattern): bool {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Locale to use when formatting or parsing. - *
- * @param int $dateType- * Date type to use (none, - * short, medium, - * long, full). - * This is one of the - * IntlDateFormatter constants. - *
- * @param int $timeType- * Time type to use (none, - * short, medium, - * long, full). - * This is one of the - * IntlDateFormatter constants. - *
- * @param string|null $timezone [optional]- * Time zone ID, default is system default. - *
- * @param IntlCalendar|int|null $calendar [optional]- * Calendar to use for formatting or parsing; default is Gregorian. - * This is one of the - * IntlDateFormatter calendar constants. - *
- * @param string|null $pattern [optional]- * Optional pattern to use when formatting or parsing. - * Possible patterns are documented at http://userguide.icu-project.org/formatparse/datetime. - *
- * @return IntlDateFormatter|null - */ -function datefmt_create( - ?string $locale, - int $dateType = 0, - int $timeType = 0, - $timezone = null, - IntlCalendar|int|null $calendar = null, - string|null $pattern = null -): ?IntlDateFormatter {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The calendar to use. - * Default is IntlDateFormatter::GREGORIAN. - *
- * @return bool TRUE on success or FALSE on failure. - */ -function datefmt_set_calendar(IntlDateFormatter $formatter, IntlCalendar|int|null $calendar): bool {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The timezone to use for this formatter. This can be specified in the - * following forms: - *
- * NULL, in which case the default timezone will be used, as specified in - * the ini setting {@link "https://secure.php.net/manual/en/datetime.configuration.php#ini.date.timezone" date.timezone} or - * through the function {@link "https://secure.php.net/manual/en/function.date-default-timezone-set.php" date_default_timezone_set()} and as - * returned by {@link "https://secure.php.net/manual/en/function.date-default-timezone-get.php" date_default_timezone_get()}. - *
- *- * An {@link "https://secure.php.net/manual/en/class.intltimezone.php" IntlTimeZone}, which will be used directly. - *
- *- * A {@link "https://secure.php.net/manual/en/class.datetimezone.php" DateTimeZone}. Its identifier will be extracted - * and an ICU timezone object will be created; the timezone will be backed - * by ICU's database, not PHP's. - *
- *- * A {@link "https://secure.php.net/manual/en/language.types.string.php" string}, which should be a valid ICU timezone identifier. - * See IntlTimeZone::createTimeZoneIDEnumeration(). Raw offsets such as "GMT+08:30" are also accepted. - *
- *- * New pattern string to use. - * Possible patterns are documented at http://userguide.icu-project.org/formatparse/datetime. - *
- * @return bool TRUE on success or FALSE on failure. - * Bad formatstrings are usually the cause of the failure. - */ -function datefmt_set_pattern(IntlDateFormatter $formatter, string $pattern): bool {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Sets whether the parser is lenient or not, default is TRUE (lenient). - *
- * @return void - */ -function datefmt_set_lenient( - IntlDateFormatter $formatter, - bool $lenient -): void {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * Value to format. This may be a DateTime object, - * an integer representing a Unix timestamp value (seconds - * since epoch, UTC) or an array in the format output by - * localtime. - *
- * @return string|false The formatted string or, if an error occurred, FALSE. - */ -function datefmt_format( - IntlDateFormatter $formatter, - $datetime -): string|false {} - -/** - * (PHP 5 >= 5.5.0, PECL intl >= 3.0.0)- * An object of type IntlCalendar or DateTime. The timezone information in the object will be used. - *
- * @param array|int|string|null $format [optional]- * How to format the date/time. This can either be an {https://secure.php.net/manual/en/language.types.array.php array} with - * two elements (first the date style, then the time style, these being one - * of the constants IntlDateFormatter::NONE, - * IntlDateFormatter::SHORT, - * IntlDateFormatter::MEDIUM, - * IntlDateFormatter::LONG, - * IntlDateFormatter::FULL), a long with - * the value of one of these constants (in which case it will be used both - * for the time and the date) or a {@link https://secure.php.net/manual/en/language.types.string.php} with the format - * described in {@link http://www.icu-project.org/apiref/icu4c/classSimpleDateFormat.html#details the ICU documentation} - * documentation. If NULL, the default style will be used. - *
- * @param string|null $locale [optional]- * The locale to use, or NULL to use the default one.
- * @return string|false The formatted string or, if an error occurred, FALSE. - */ -function datefmt_format_object($datetime, $format = null, ?string $locale = null): string|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * string to convert to a time - *
- * @param int &$offset [optional]- * Position at which to start the parsing in $value (zero-based). - * If no error occurs before $value is consumed, $parse_pos will contain -1 - * otherwise it will contain the position at which parsing ended (and the error occurred). - * This variable will contain the end position if the parse fails. - * If $parse_pos > strlen($value), the parse fails immediately. - *
- * @return int|float|false timestamp parsed value - */ -function datefmt_parse(IntlDateFormatter $formatter, string $string, &$offset = null): int|float|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * string to convert to a time - *
- * @param int &$offset [optional]- * Position at which to start the parsing in $value (zero-based). - * If no error occurs before $value is consumed, $parse_pos will contain -1 - * otherwise it will contain the position at which parsing ended . - * If $parse_pos > strlen($value), the parse fails immediately. - *
- * @return array|false Localtime compatible array of integers : contains 24 hour clock value in tm_hour field - */ -function datefmt_localtime(IntlDateFormatter $formatter, string $string, &$offset = null): array|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The string being measured for length. It must be a valid UTF-8 string. - *
- * @return int|false|null The length of the string on success, and 0 if the string is empty. - */ -function grapheme_strlen(string $string): int|false|null {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The string to look in. Must be valid UTF-8. - *
- * @param string $needle- * The string to look for. Must be valid UTF-8. - *
- * @param int $offset [optional]- * The optional $offset parameter allows you to specify where in $haystack to - * start searching as an offset in grapheme units (not bytes or characters). - * The position returned is still relative to the beginning of haystack - * regardless of the value of $offset. - *
- * @return int|false the position as an integer. If needle is not found, strpos() will return boolean FALSE. - */ -function grapheme_strpos(string $haystack, string $needle, int $offset = 0): int|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The string to look in. Must be valid UTF-8. - *
- * @param string $needle- * The string to look for. Must be valid UTF-8. - *
- * @param int $offset [optional]- * The optional $offset parameter allows you to specify where in haystack to - * start searching as an offset in grapheme units (not bytes or characters). - * The position returned is still relative to the beginning of haystack - * regardless of the value of $offset. - *
- * @return int|false the position as an integer. If needle is not found, grapheme_stripos() will return boolean FALSE. - */ -function grapheme_stripos(string $haystack, string $needle, int $offset = 0): int|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The string to look in. Must be valid UTF-8. - *
- * @param string $needle- * The string to look for. Must be valid UTF-8. - *
- * @param int $offset [optional]- * The optional $offset parameter allows you to specify where in $haystack to - * start searching as an offset in grapheme units (not bytes or characters). - * The position returned is still relative to the beginning of haystack - * regardless of the value of $offset. - *
- * @return int|false the position as an integer. If needle is not found, grapheme_strrpos() will return boolean FALSE. - */ -function grapheme_strrpos(string $haystack, string $needle, int $offset = 0): int|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The string to look in. Must be valid UTF-8. - *
- * @param string $needle- * The string to look for. Must be valid UTF-8. - *
- * @param int $offset [optional]- * The optional $offset parameter allows you to specify where in $haystack to - * start searching as an offset in grapheme units (not bytes or characters). - * The position returned is still relative to the beginning of haystack - * regardless of the value of $offset. - *
- * @return int|false the position as an integer. If needle is not found, grapheme_strripos() will return boolean FALSE. - */ -function grapheme_strripos(string $haystack, string $needle, int $offset = 0): int|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The input string. Must be valid UTF-8. - *
- * @param int $offset- * Start position in default grapheme units. - * If $start is non-negative, the returned string will start at the - * $start'th position in $string, counting from zero. If $start is negative, - * the returned string will start at the $start'th grapheme unit from the - * end of string. - *
- * @param int|null $length [optional]- * Length in grapheme units. - * If $length is given and is positive, the string returned will contain - * at most $length grapheme units beginning from $start (depending on the - * length of string). If $length is given and is negative, then - * that many grapheme units will be omitted from the end of string (after the - * start position has been calculated when a start is negative). If $start - * denotes a position beyond this truncation, FALSE will be returned. - *
- * @return string|falsethe extracted part of $string,
- * or FALSE if $length is negative and $start denotes a position beyond truncation $length,
- * or also FALSE if $start denotes a position beyond $string length
- * The input string. Must be valid UTF-8. - *
- * @param string $needle- * The string to look for. Must be valid UTF-8. - *
- * @param bool $beforeNeedle [optional]- * If TRUE, grapheme_strstr() returns the part of the - * haystack before the first occurrence of the needle (excluding the needle). - *
- * @return string|false the portion of string, or FALSE if needle is not found. - */ -function grapheme_strstr(string $haystack, string $needle, bool $beforeNeedle = false): string|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * The input string. Must be valid UTF-8. - *
- * @param string $needle- * The string to look for. Must be valid UTF-8. - *
- * @param bool $beforeNeedle [optional]- * If TRUE, grapheme_strstr() returns the part of the - * haystack before the first occurrence of the needle (excluding needle). - *
- * @return string|false the portion of $haystack, or FALSE if $needle is not found. - */ -function grapheme_stristr(string $haystack, string $needle, bool $beforeNeedle = false): string|false {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * String to search. - *
- * @param int $size- * Maximum number items - based on the $extract_type - to return. - *
- * @param int $type- * Defines the type of units referred to by the $size parameter: - *
- *- * GRAPHEME_EXTR_COUNT (default) - $size is the number of default - * grapheme clusters to extract. - * GRAPHEME_EXTR_MAXBYTES - $size is the maximum number of bytes - * returned. - * GRAPHEME_EXTR_MAXCHARS - $size is the maximum number of UTF-8 - * characters returned. - *
- * @param int $offset [optional]- * Starting position in $haystack in bytes - if given, it must be zero or a - * positive value that is less than or equal to the length of $haystack in - * bytes. If $start does not point to the first byte of a UTF-8 - * character, the start position is moved to the next character boundary. - *
- * @param int &$next [optional]- * Reference to a value that will be set to the next starting position. - * When the call returns, this may point to the first byte position past the end of the string. - *
- * @return string|false A string starting at offset $start and ending on a default grapheme cluster - * boundary that conforms to the $size and $extract_type specified. - */ -function grapheme_extract(string $haystack, int $size, int $type = 0, int $offset = 0, &$next = null): string|false {} - -/** - * (PHP 5 >= 5.3.0, PHP 7, PECL intl >= 1.0.2, PHP 7, PECL idn >= 0.1)- * Domain to convert. In PHP 5 must be UTF-8 encoded. - * If e.g. an ISO-8859-1 (aka Western Europe latin1) encoded string is - * passed it will be converted into an ACE encoded "xn--" string. - * It will not be the one you expected though! - *
- * @param int $flags [optional]- * Conversion options - combination of IDNA_* constants (except IDNA_ERROR_* constants). - *
- * @param int $variant [optional]- * Either INTL_IDNA_VARIANT_2003 for IDNA 2003 or INTL_IDNA_VARIANT_UTS46 for UTS #46. - *
- * @param array &$idna_info [optional]- * This parameter can be used only if INTL_IDNA_VARIANT_UTS46 was used for variant. - * In that case, it will be filled with an array with the keys 'result', - * the possibly illegal result of the transformation, 'isTransitionalDifferent', - * a boolean indicating whether the usage of the transitional mechanisms of UTS #46 - * either has or would have changed the result and 'errors', - * which is an int representing a bitset of the error constants IDNA_ERROR_*. - *
- * @return string|false The ACE encoded version of the domain name or FALSE on failure. - */ -function idn_to_ascii(string $domain, int $flags = 0, int $variant = INTL_IDNA_VARIANT_UTS46, &$idna_info): string|false {} - -/** - * (PHP 5 >= 5.3.0, PHP 7, PECL intl >= 1.0.2, PHP 7, PECL idn >= 0.1)- * Domain to convert in IDNA ASCII-compatible format. - * The ASCII encoded domain name. Looks like "xn--..." if the it originally contained non-ASCII characters. - *
- * @param int $flags [optional]- * Conversion options - combination of IDNA_* constants (except IDNA_ERROR_* constants). - *
- * @param int $variant [optional]- * Either INTL_IDNA_VARIANT_2003 for IDNA 2003 or INTL_IDNA_VARIANT_UTS46 for UTS #46. - *
- * @param array &$idna_info [optional]- * This parameter can be used only if INTL_IDNA_VARIANT_UTS46 was used for variant. - * In that case, it will be filled with an array with the keys 'result', - * the possibly illegal result of the transformation, 'isTransitionalDifferent', - * a boolean indicating whether the usage of the transitional mechanisms of UTS #46 - * either has or would have changed the result and 'errors', - * which is an int representing a bitset of the error constants IDNA_ERROR_*. - *
- * @return string|false The UTF-8 encoded version of the domain name or FALSE on failure. - * RFC 3490 4.2 states though "ToUnicode never fails. If any step fails, then the original input - * sequence is returned immediately in that step." - */ -function idn_to_utf8( - string $domain, - int $flags = 0, - int $variant = INTL_IDNA_VARIANT_UTS46, - &$idna_info = null, -): string|false {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)
- * The timezone to use. - *
- * - *- * NULL, in which case the default timezone will be used, as specified in - * the ini setting {@link https://secure.php.net/manual/en/datetime.configuration.php#ini.date.timezone date.timezone} or - * through the function {@link https://secure.php.net/manual/en/function.date-default-timezone-set.php date_default_timezone_set()} and as - * returned by {@link https://secure.php.net/manual/en/function.date-default-timezone-get.php date_default_timezone_get()}. - *
- *- * An {@link https://secure.php.net/manual/en/class.intltimezone.php IntlTimeZone}, which will be used directly. - *
- *- * A {@link https://secure.php.net/manual/en/class.datetimezone.php DateTimeZone}. Its identifier will be extracted - * and an ICU timezone object will be created; the timezone will be backed - * by ICU's database, not PHP's. - *
- *- * A {@link https://secure.php.net/manual/en/language.types.string.php string}, which should be a valid ICU timezone identifier. - * See IntlTimeZone::createTimeZoneIDEnumeration(). Raw - * offsets such as "GMT+08:30" are also accepted. - *
- *- * A locale to use or NULL to use {@link https://secure.php.net/manual/en/intl.configuration.php#ini.intl.default-locale the default locale}. - *
- * @return IntlCalendar|null - * The created {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} instance or NULL on - * failure. - * @since 5.5 - */ -function intlcal_create_instance($timezone = null, ?string $locale = null): ?IntlCalendar {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The locale keyword for which relevant values are to be queried. Only - * 'calendar' is supported. - *
- * @param string $locale- * The locale onto which the keyword/value pair are to be appended. - *
- * @param bool $onlyCommon - *- * Whether to show only the values commonly used for the specified locale. - *
- * @return IntlIterator|false An iterator that yields strings with the locale keyword values or FALSE on failure. - * @since 5.5 - */ -function intlcal_get_keyword_values_for_locale(string $keyword, string $locale, bool $onlyCommon): IntlIterator|false {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param int $field- * One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field constants}. These are integer - * values between 0 and - * IntlCalendar::FIELD_COUNT. - *
- * @return int An integer with the value of the time field. - * @since 5.5 - */ -function intl_get($calendar, $field) {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)The calendar whose time will be checked against this object's time.
- * @return float - * A {@link https://secure.php.net/manual/en/language.types.float.php float} representing the number of milliseconds elapsed since the - * reference time (1 Jan 1970 00:00:00 UTC). - * @since 5.5 - */ -function intlcal_get_time(IntlCalendar $calendar): float|false {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The IntlCalendar resource. - *
- * @param float $timestamp- * An instant represented by the number of number of milliseconds between - * such instant and the epoch, ignoring leap seconds. - *
- * @return bool - * Returns TRUE on success and FALSE on failure. - * @since 5.5 - */ -function intlcal_set_time(IntlCalendar $calendar, float $timestamp): bool {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param int $field- * One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field constants}. - * These are integer values between 0 and - * IntlCalendar::FIELD_COUNT. - *
- * @param int $valueThe signed amount to add to the current field. If the amount is positive, the instant will be moved forward; if it is negative, the instant wil be moved into the past. The unit is implicit to the field type. - * For instance, hours for IntlCalendar::FIELD_HOUR_OF_DAY.
- * @return bool Returns TRUE on success or FALSE on failure. - * @since 5.5 - */ -function intlcal_add(IntlCalendar $calendar, int $field, int $value): bool {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param IntlTimeZone|DateTimeZone|string|null $timezone- * The new timezone to be used by this calendar. It can be specified in the - * following ways: - * - *
- * NULL, in which case the default timezone will be used, as specified in - * the ini setting {@link https://secure.php.net/manual/en/datetime.configuration.php#ini.date.timezone date.timezone} or - * through the function {@link https://secure.php.net/manual/en/function.date-default-timezone-set.php date_default_timezone_set()} and as - * returned by {@link https://secure.php.net/manual/en/function.date-default-timezone-get.php date_default_timezone_get()}. - *
- *- * An {@link https://secure.php.net/manual/en/class.intltimezone.php IntlTimeZone}, which will be used directly. - *
- *- * A {@link https://secure.php.net/manual/en/class.datetimezone.php DateTimeZone}. Its identifier will be extracted - * and an ICU timezone object will be created; the timezone will be backed - * by ICU's database, not PHP's. - *
- *- * A {@link https://secure.php.net/manual/en/language.types.string.php string}, which should be a valid ICU timezone identifier. - * See IntlTimeZone::createTimeZoneIDEnumeration(). Raw - * offsets such as "GMT+08:30" are also accepted. - *
- *- * The calendar object, on the procedural style interface. - *
- * @param IntlCalendar $otherThe calendar whose time will be checked against this object's time.
- * @return bool - * Returns TRUE if this object's current time is after that of the - * calendar argument's time. Returns FALSE otherwise. - * Also returns FALSE on failure. You can use {@link https://secure.php.net/manual/en/intl.configuration.php#ini.intl.use-exceptions exceptions} or - * {@link https://secure.php.net/manual/en/function.intl-get-error-code.php intl_get_error_code()} to detect error conditions. - * @since 5.5 - */ -function intlcal_after(IntlCalendar $calendar, IntlCalendar $other): bool {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param IntlCalendar $otherThe calendar whose time will be checked against this object's time.
- * @return bool - *- * Returns TRUE if this object's current time is before that of the - * calendar argument's time. Returns FALSE otherwise. - * Also returns FALSE on failure. You can use {@link https://secure.php.net/manual/en/intl.configuration.php#ini.intl.use-exceptions exceptions} or - * {@link https://secure.php.net/manual/en/function.intl-get-error-code.php intl_get_error_code()} to detect error conditions. - *
- * @since 5.5 - */ -function intlcal_before(IntlCalendar $calendar, IntlCalendar $other): bool {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param int $year- * One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field constants}. These are integer - * values between 0 and - * IntlCalendar::FIELD_COUNT. - *
- * @param int $month- * The new value for IntlCalendar::FIELD_MONTH. - *
- * @param int $dayOfMonth [optional]- * The new value for IntlCalendar::FIELD_DAY_OF_MONTH. - * The month sequence is zero-based, i.e., January is represented by 0, - * February by 1, ..., December is 11 and Undecember (if the calendar has - * it) is 12. - *
- * @param int $hour [optional] - *- * The new value for IntlCalendar::FIELD_HOUR_OF_DAY. - *
- * @param int $minute [optional] - *- * The new value for IntlCalendar::FIELD_MINUTE. - *
- * @param int $second [optional]- * The new value for IntlCalendar::FIELD_SECOND. - *
- * @return bool Returns TRUE on success and FALSE on failure. - * @since 5.5 - */ -function intlcal_set(IntlCalendar $calendar, int $year, int $month, int $dayOfMonth, int $hour, int $minute, int $second): bool {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param int $fieldOne of the - * {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time - * {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field constants}. - * These are integer values between 0 and - * IntlCalendar::FIELD_COUNT. - *
- * @param int|bool $value- * The (signed) amount to add to the field, TRUE for rolling up (adding - * 1), or FALSE for rolling down (subtracting - * 1). - *
- * @return bool Returns TRUE on success or FALSE on failure. - * @since 5.5 - */ -function intlcal_roll( - IntlCalendar $calendar, - int $field, - $value -): bool {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param int|null $field [optional]- * One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field constants}. These are integer - * values between 0 and - * IntlCalendar::FIELD_COUNT. - *
- * @return bool Returns TRUE on success or FALSE on failure. Failure can only occur is invalid arguments are provided. - * @since 5.5 - */ -function intlcal_clear(IntlCalendar $calendar, ?int $field = null): bool {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param float $timestamp- * The time against which to compare the quantity represented by the - * field. For the result to be positive, the time - * given for this parameter must be ahead of the time of the object the - * method is being invoked on. - *
- * @param int $field- * The field that represents the quantity being compared. - *
- * - *- * One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field constants}. These are integer - * values between 0 and - * IntlCalendar::FIELD_COUNT. - *
- * @return int Returns a (signed) difference of time in the unit associated with the - * specified field or FALSE on failure. - * @since 5.5 - */ -function intlcal_field_difference(IntlCalendar $calendar, float $timestamp, int $field): int|false {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param int $field- * One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field constants}. These are integer - * values between 0 and - * IntlCalendar::FIELD_COUNT. - *
- * @return int - * An {@link https://secure.php.net/manual/en/language.types.integer.php int} representing the maximum value in the units associated - * with the given field or FALSE on failure. - * @since 5.5 - */ -function intlcal_get_actual_maximum(IntlCalendar $calendar, int $field): int|false {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param int $field- * One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field constants}. - * These are integer values between 0 and - * IntlCalendar::FIELD_COUNT. - *
- * @return int - * An {@link https://secure.php.net/manual/en/language.types.integer.php int} representing the minimum value in the field's - * unit or FALSE on failure. - * @since 5.5 - */ -function intlcal_get_actual_minimum(IntlCalendar $calendar, int $field): int|false {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param int $dayOfWeek- * One of the constants IntlCalendar::DOW_SUNDAY, - * IntlCalendar::DOW_MONDAY, ..., - * IntlCalendar::DOW_SATURDAY. - *
- * @return int - * Returns one of the constants - * IntlCalendar::DOW_TYPE_WEEKDAY, - * IntlCalendar::DOW_TYPE_WEEKEND, - * IntlCalendar::DOW_TYPE_WEEKEND_OFFSET or - * IntlCalendar::DOW_TYPE_WEEKEND_CEASE or FALSE on failure. - * @since 5.5 - */ -function intlcal_get_day_of_week_type(IntlCalendar $calendar, int $dayOfWeek): int|false {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @return int - * One of the constants IntlCalendar::DOW_SUNDAY, - * IntlCalendar::DOW_MONDAY, ..., - * IntlCalendar::DOW_SATURDAY or FALSE on failure. - * @since 5.5 - */ -function intlcal_get_first_day_of_week(IntlCalendar $calendar): int|false {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param int $field- * One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field constants}. These are integer - * values between 0 and - * IntlCalendar::FIELD_COUNT.
- * @return int - * An {@link https://secure.php.net/manual/en/language.types.integer.php int} representing a field value, in the field's - * unit, or FALSE on failure. - * @since 5.5 - */ -function intlcal_greates_minimum($calendar, $field) {} - -/** - * (PHP >= 5.5.0, PECL intl >= 3.0.0a1)- * The IntlCalendar resource. - *
- * @param int $field- * One of the IntlCalendar date/time field constants. These are integer values between 0 and IntlCalendar::FIELD_COUNT. - *
- * @return int An integer with the value of the time field. - */ -function intlcal_get(IntlCalendar $calendar, int $field): int|false {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param int $field- * One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field constants}. These are integer - * values between 0 and - * IntlCalendar::FIELD_COUNT. - *
- * @return int - *An {@link https://secure.php.net/manual/en/language.types.integer.ph int} representing a field value in the field's - * unit or FALSE on failure. - *
- * @since 5.5 - */ -function intlcal_get_least_maximum(IntlCalendar $calendar, int $field): int|false {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param int $field- * One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field constants}. These are integer - * values between 0 and - * IntlCalendar::FIELD_COUNT.
- * @return int - * An {@link https://secure.php.net/manual/en/language.types.integer.php int} representing a field value, in the field's - * unit, or FALSE on failure. - * @since 5.5 - */ -function intlcal_get_greatest_minimum(IntlCalendar $calendar, int $field): int|false {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param int $type- * Whether to fetch the actual locale (the locale from which the calendar - * data originates, with Locale::ACTUAL_LOCALE) or the - * valid locale, i.e., the most specific locale supported by ICU relatively - * to the requested locale – see Locale::VALID_LOCALE. - * From the most general to the most specific, the locales are ordered in - * this fashion – actual locale, valid locale, requested locale. - *
- * @return string - * A locale string or FALSE on failure. - * @since 5.5 - */ -function intlcal_get_locale(IntlCalendar $calendar, int $type): string|false {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param int $field- * One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field constants}. These are integer - * values between 0 and - * IntlCalendar::FIELD_COUNT. - *
- * @return int|false - * @since 5.5 - */ -function intcal_get_maximum($calendar, $field) {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @return int - * An {@link https://secure.php.net/manual/en/language.types.integer.php int} representing a number of days or FALSE on failure. - * @since 5.5 - */ -function intlcal_get_minimal_days_in_first_week(IntlCalendar $calendar): int|false {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param int $field- * One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field}. These are integer - * values between 0 and - * IntlCalendar::FIELD_COUNT. - *
- * @return int - * An int representing a value for the given field in the field's unit or FALSE on failure. - * @since 5.5 - */ -function intlcal_get_minimum(IntlCalendar $calendar, int $field): int|false {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @return IntlTimeZone|false - * An {@link https://secure.php.net/manual/en/class.intltimezone.php IntlTimeZone} object corresponding to the one used - * internally in this object. - * @since 5.5 - */ -function intlcal_get_time_zone(IntlCalendar $calendar): IntlTimeZone|false {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @return string - * A {@link https://secure.php.net/manual/en/language.types.string.php string} representing the calendar type, such as - * 'gregorian', 'islamic', etc. - * @since 5.5 - */ -function intlcal_get_type(IntlCalendar $calendar): string {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param int $dayOfWeek- * One of the constants IntlCalendar::DOW_SUNDAY, - * IntlCalendar::DOW_MONDAY, ..., - * IntlCalendar::DOW_SATURDAY. - *
- * @return int - * The number of milliseconds into the day at which the the weekend begins or - * ends or FALSE on failure. - * @since 5.5 - */ -function intlcal_get_weekend_transition(IntlCalendar $calendar, int $dayOfWeek): int|false {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @return bool - * Returns TRUE if the date is in Daylight Savings Time, FALSE otherwise. - * The value FALSE may also be returned on failure, for instance after - * specifying invalid field values on non-lenient mode; use {@link https://secure.php.net/manual/en/intl.configuration.php#ini.intl.use-exceptions exceptions} or query - * {@link https://secure.php.net/manual/en/function.intl-get-error-code.php intl_get_error_code()} to disambiguate. - * @since 5.5 - */ -function intlcal_in_daylight_time(IntlCalendar $calendar): bool {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @return bool - * A {@link https://secure.php.net/manual/en/language.types.boolean.php bool} representing whether the calendar is set to lenient mode. - * @since 5.5 - */ -function intlcal_is_lenient(IntlCalendar $calendar): bool {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param int $field- * One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field constants}. These are integer - * values between 0 and - * IntlCalendar::FIELD_COUNT. - *
- * @return bool Assuming there are no argument errors, returns TRUE iif the field is set. - * @since 5.5 - */ -function intlcal_is_set(IntlCalendar $calendar, int $field): bool {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param int $field- * One of the {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} date/time {@link https://secure.php.net/manual/en/class.intlcalendar.php#intlcalendar.constants field constants}. These are integer - * values between 0 and - * IntlCalendar::FIELD_COUNT. - *
- * @return int|false - * @since 5.5 - */ -function intlcal_get_maximum(IntlCalendar $calendar, int $field): int|false {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param IntlCalendar $other The other calendar against which the comparison is to be made. - * @return bool - * Assuming there are no argument errors, returns TRUE iif the calendars are equivalent except possibly for their set time. - * @since 5.5 - */ -function intlcal_is_equivalent_to(IntlCalendar $calendar, IntlCalendar $other): bool {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param float|null $timestamp [optional]- * An optional timestamp representing the number of milliseconds since the - * epoch, excluding leap seconds. If NULL, this object's current time is - * used instead. - *
- * @return bool - *A {@link https://secure.php.net/manual/en/language.types.boolean.php bool} indicating whether the given or this object's time occurs - * in a weekend. - *
- *- * The value FALSE may also be returned on failure, for instance after giving - * a date out of bounds on non-lenient mode; use {@link https://secure.php.net/manual/en/intl.configuration.php#ini.intl.use-exceptions exceptions} or query - * {@link https://secure.php.net/manual/en/function.intl-get-error-code.php intl_get_error_code()} to disambiguate.
- * @since 5.5 - */ -function intlcal_is_weekend(IntlCalendar $calendar, ?float $timestamp = null): bool {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param int $dayOfWeek- * One of the constants IntlCalendar::DOW_SUNDAY, - * IntlCalendar::DOW_MONDAY, ..., - * IntlCalendar::DOW_SATURDAY. - *
- * @return bool Returns TRUE on success. Failure can only happen due to invalid parameters. - * @since 5.5 - */ -function intlcal_set_first_day_of_week(IntlCalendar $calendar, int $dayOfWeek): bool {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param bool $lenient- * Use TRUE to activate the lenient mode; FALSE otherwise. - *
- * @return bool Returns TRUE on success. Failure can only happen due to invalid parameters. - * @since 5.5 - */ -function intlcal_set_lenient(IntlCalendar $calendar, bool $lenient): bool {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @return int - * One of the constants IntlCalendar::WALLTIME_FIRST or - * IntlCalendar::WALLTIME_LAST. - * @since 5.5 - */ -function intlcal_get_repeated_wall_time_option(IntlCalendar $calendar): int {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param IntlCalendar $other - * @return bool- * Returns TRUE if the current time of both this and the passed in - * {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} object are the same, or FALSE - * otherwise. The value FALSE can also be returned on failure. This can only - * happen if bad arguments are passed in. In any case, the two cases can be - * distinguished by calling {@link https://secure.php.net/manual/en/function.intl-get-error-code.php intl_get_error_code()}. - *
- * @since 5.5 - */ -function intlcal_equals(IntlCalendar $calendar, IntlCalendar $other): bool {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @return int - * One of the constants IntlCalendar::WALLTIME_FIRST, - * IntlCalendar::WALLTIME_LAST or - * IntlCalendar::WALLTIME_NEXT_VALID. - * @since 5.5 - */ -function intlcal_get_skipped_wall_time_option(IntlCalendar $calendar): int {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param int $option- * One of the constants IntlCalendar::WALLTIME_FIRST or - * IntlCalendar::WALLTIME_LAST. - *
- * @return bool - * Returns TRUE on success. Failure can only happen due to invalid parameters. - * @since 5.5 - */ -function intlcal_set_repeated_wall_time_option(IntlCalendar $calendar, int $option): bool {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @param int $option- * One of the constants IntlCalendar::WALLTIME_FIRST, - * IntlCalendar::WALLTIME_LAST or - * IntlCalendar::WALLTIME_NEXT_VALID. - *
- * @return bool - *- * Returns TRUE on success. Failure can only happen due to invalid parameters. - *
- * @since 5.5 - */ -function intlcal_set_skipped_wall_time_option(IntlCalendar $calendar, int $option): bool {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a2)- * A {@link https://secure.php.net/manual/en/class.datetime.php DateTime} object or a {@link https://secure.php.net/manual/en/language.types.string.php string} that - * can be passed to {@link https://secure.php.net/manual/en/datetime.construct.php DateTime::__construct()}. - *
- * @param null|string $locale - * @return IntlCalendar|null - * The created {@link https://secure.php.net/manual/en/class.intlcalendar.php IntlCalendar} object or NULL in case of - * failure. If a {@link https://secure.php.net/manual/en/language.types.string.php string} is passed, any exception that occurs - * inside the {@link https://secure.php.net/manual/en/class.datetime.php DateTime} constructor is propagated. - * @since 5.5 - */ -function intlcal_from_date_time( - DateTime|string $datetime, - ?string $locale = null -): ?IntlCalendar {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a2)- * The calendar object, on the procedural style interface. - *
- * @return DateTime|false - * A {@link https://secure.php.net/manual/en/class.datetime.php DateTime} object with the same timezone as this - * object (though using PHP's database instead of ICU's) and the same time, - * except for the smaller precision (second precision instead of millisecond). - * Returns FALSE on failure. - * @since 5.5 - */ -function intlcal_to_date_time(IntlCalendar $calendar): DateTime|false {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @return int|false An ICU error code indicating either success, failure or a warning. - * @since 5.5 - */ -function intlcal_get_error_code(IntlCalendar $calendar): int|false {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The calendar object, on the procedural style interface. - *
- * @return string|false The error message associated with last error that occurred in a function call on this object, or a string indicating the non-existance of an error. - * @since 5.5 - */ -function intlcal_get_error_message(IntlCalendar $calendar): string|false {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The time zone object, on the procedural style interface. - *
- * @param bool $dst [optional] - * @param int $style [optional] - * @param string|null $locale [optional] - * @return string|false - * @since 5.5 - */ -function intltz_get_display_name(IntlTimeZone $timezone, bool $dst = false, int $style = 2, ?string $locale): string|false {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The time zone object, on the procedural style interface. - *
- * @return int - * @link https://secure.php.net/manual/en/intltimezone.getequivalentid.php - * @since 5.5 - */ -function intltz_get_dst_savings(IntlTimeZone $timezone): int {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The time zone object, on the procedural style interface. - *
- * @return int|false - * @since 5.5 - */ -function intltz_get_error_code(IntlTimeZone $timezone): int|false {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * The time zone object, on the procedural style interface. - *
- * @return string|false - * @since 5.5 - */ -function intltz_get_error_message(IntlTimeZone $timezone): string|false {} - -/** - * (PHP 5 >=5.5.0 PECL intl >= 3.0.0a1)- * Locale for which the resources should be loaded (locale name, e.g. en_CA). - *
- * @param string|null $bundle- * The directory where the data is stored or the name of the .dat file. - *
- * @param bool $fallback [optional]- * Whether locale should match exactly or fallback to parent locale is allowed. - *
- * @return ResourceBundle|null ResourceBundle object or NULL on error. - */ -function resourcebundle_create(?string $locale, ?string $bundle, bool $fallback = true): ?ResourceBundle {} - -/** - * (PHP >= 5.3.2, PECL intl >= 2.0.0)- * Data index, must be string or integer. - *
- * @param bool $fallback - * @return mixed the data located at the index or NULL on error. Strings, integers and binary data strings - * are returned as corresponding PHP types, integer array is returned as PHP array. Complex types are - * returned as ResourceBundle object. - */ -function resourcebundle_get(ResourceBundle $bundle, string|int $index, bool $fallback = true) {} - -/** - * (PHP >= 5.3.2, PECL intl >= 2.0.0)- * Path of ResourceBundle for which to get available locales, or - * empty string for default locales list. - *
- * @return array|false the list of locales supported by the bundle. - */ -function resourcebundle_locales(string $bundle): array|false {} - -/** - * (PHP >= 5.3.2, PECL intl >= 2.0.0)- * The id. - *
- * @param int $direction- * The direction, defaults to - * Transliterator::FORWARD. - * May also be set to - * Transliterator::REVERSE. - *
- * @return Transliterator|null a Transliterator object on success, - * or NULL on failure. - * @since 5.4 - */ -function transliterator_create(string $id, int $direction = 0): ?Transliterator {} - -/** - * (PHP >= 5.4.0, PECL intl >= 2.0.0)- * The rules. - *
- * @param int $direction- * The direction, defaults to - * Transliterator::FORWARD. - * May also be set to - * Transliterator::REVERSE. - *
- * @return Transliterator|null a Transliterator object on success, - * or NULL on failure. - * @since 5.4 - */ -function transliterator_create_from_rules(string $rules, int $direction = 0): ?Transliterator {} - -/** - * (PHP >= 5.4.0, PECL intl >= 2.0.0)- * The string to be transformed. - *
- * @param int $start- * The start index (in UTF-16 code units) from which the string will start - * to be transformed, inclusive. Indexing starts at 0. The text before will - * be left as is. - *
- * @param int $end- * The end index (in UTF-16 code units) until which the string will be - * transformed, exclusive. Indexing starts at 0. The text after will be - * left as is. - *
- * @return string|false The transfomed string on success, or FALSE on failure. - * @since 5.4 - */ -function transliterator_transliterate(Transliterator|string $transliterator, string $string, int $start = 0, int $end = -1): string|false {} - -/** - * (PHP >= 5.4.0, PECL intl >= 2.0.0)- * is a value that returned by functions: - * intl_get_error_code, - * collator_get_error_code . - *
- * @return bool TRUE if it the code indicates some failure, and FALSE - * in case of success or a warning. - */ -function intl_is_failure(int $errorCode): bool {} - -/** - * (PHP 5 >= 5.3.0, PECL intl >= 1.0.0)- * ICU error code. - *
- * @return string The returned string will be the same as the name of the error code - * constant. - */ -function intl_error_name(int $errorCode): string {} - -/** - * Gets the Decomposition_Mapping property for the given UTF-8 encoded code point - * - * @link https://www.php.net/manual/en/normalizer.getrawdecomposition.php - * - * @param string $string - * @param int $form - * @return string|null - * - * @since 7.3 - */ -function normalizer_get_raw_decomposition(string $string, int $form = Normalizer::FORM_C): ?string {} - -/** - * @return IntlTimeZone - * @since 5.5 - */ -function intltz_create_default(): IntlTimeZone {} - -/** - * @return IntlTimeZone - * @since 5.5 - */ -function intltz_get_gmt(): IntlTimeZone {} - -/** - * @return IntlTimeZone - * @since 5.5 - */ -function intltz_get_unknown(): IntlTimeZone {} - -/** - * @param int $type - * @param null|string $region - * @param null|int $rawOffset - * @return IntlIterator|false - * @since 5.5 - */ -function intltz_create_time_zone_id_enumeration(int $type, ?string $region = null, ?int $rawOffset = null): IntlIterator|false {} - -/** - * @param string $timezoneId - * @return string|false - * @since 5.5 - */ -function intltz_get_region(string $timezoneId): string|false {} - -/** - * Set minimal number of days the first week in a year or month can have - * - * @link https://www.php.net/manual/en/intlcalendar.setminimaldaysinfirstweek.php - * - * @param IntlCalendar $calendar - * @param int $days - * @return bool - * - * @since 5.5.1 - */ -function intlcal_set_minimal_days_in_first_week(IntlCalendar $calendar, int $days): bool {} - -function intltz_get_windows_id(string $timezoneId): string|false {} - -function intltz_get_id_for_windows_id(string $timezoneId, ?string $region = null): string|false {} - -/** - * @since 8.4 - */ -function grapheme_str_split(string $string, int $length = 1): array|false {} - -/** - * @since 8.4 - */ -function intltz_get_iana_id(string $timezoneId): string|false {} - -/** - * Limit on locale length, set to 80 in PHP code. Locale names longer - * than this limit will not be accepted. - * @link https://php.net/manual/en/intl.constants.php - */ -define('INTL_MAX_LOCALE_LEN', 156); -define('INTL_ICU_VERSION', "74.1"); -define('INTL_ICU_DATA_VERSION', "74.1"); -define('ULOC_ACTUAL_LOCALE', 0); -define('ULOC_VALID_LOCALE', 1); -define('GRAPHEME_EXTR_COUNT', 0); -define('GRAPHEME_EXTR_MAXBYTES', 1); -define('GRAPHEME_EXTR_MAXCHARS', 2); -define('U_USING_FALLBACK_WARNING', -128); -define('U_ERROR_WARNING_START', -128); -define('U_USING_DEFAULT_WARNING', -127); -define('U_SAFECLONE_ALLOCATED_WARNING', -126); -define('U_STATE_OLD_WARNING', -125); -define('U_STRING_NOT_TERMINATED_WARNING', -124); -define('U_SORT_KEY_TOO_SHORT_WARNING', -123); -define('U_AMBIGUOUS_ALIAS_WARNING', -122); -define('U_DIFFERENT_UCA_VERSION', -121); -define('U_ERROR_WARNING_LIMIT', -119); -define('U_ZERO_ERROR', 0); -define('U_ILLEGAL_ARGUMENT_ERROR', 1); -define('U_MISSING_RESOURCE_ERROR', 2); -define('U_INVALID_FORMAT_ERROR', 3); -define('U_FILE_ACCESS_ERROR', 4); -define('U_INTERNAL_PROGRAM_ERROR', 5); -define('U_MESSAGE_PARSE_ERROR', 6); -define('U_MEMORY_ALLOCATION_ERROR', 7); -define('U_INDEX_OUTOFBOUNDS_ERROR', 8); -define('U_PARSE_ERROR', 9); -define('U_INVALID_CHAR_FOUND', 10); -define('U_TRUNCATED_CHAR_FOUND', 11); -define('U_ILLEGAL_CHAR_FOUND', 12); -define('U_INVALID_TABLE_FORMAT', 13); -define('U_INVALID_TABLE_FILE', 14); -define('U_BUFFER_OVERFLOW_ERROR', 15); -define('U_UNSUPPORTED_ERROR', 16); -define('U_RESOURCE_TYPE_MISMATCH', 17); -define('U_ILLEGAL_ESCAPE_SEQUENCE', 18); -define('U_UNSUPPORTED_ESCAPE_SEQUENCE', 19); -define('U_NO_SPACE_AVAILABLE', 20); -define('U_CE_NOT_FOUND_ERROR', 21); -define('U_PRIMARY_TOO_LONG_ERROR', 22); -define('U_STATE_TOO_OLD_ERROR', 23); -define('U_TOO_MANY_ALIASES_ERROR', 24); -define('U_ENUM_OUT_OF_SYNC_ERROR', 25); -define('U_INVARIANT_CONVERSION_ERROR', 26); -define('U_INVALID_STATE_ERROR', 27); -define('U_COLLATOR_VERSION_MISMATCH', 28); -define('U_USELESS_COLLATOR_ERROR', 29); -define('U_NO_WRITE_PERMISSION', 30); -define('U_STANDARD_ERROR_LIMIT', 32); -define('U_BAD_VARIABLE_DEFINITION', 65536); -define('U_PARSE_ERROR_START', 65536); -define('U_MALFORMED_RULE', 65537); -define('U_MALFORMED_SET', 65538); -define('U_MALFORMED_SYMBOL_REFERENCE', 65539); -define('U_MALFORMED_UNICODE_ESCAPE', 65540); -define('U_MALFORMED_VARIABLE_DEFINITION', 65541); -define('U_MALFORMED_VARIABLE_REFERENCE', 65542); -define('U_MISMATCHED_SEGMENT_DELIMITERS', 65543); -define('U_MISPLACED_ANCHOR_START', 65544); -define('U_MISPLACED_CURSOR_OFFSET', 65545); -define('U_MISPLACED_QUANTIFIER', 65546); -define('U_MISSING_OPERATOR', 65547); -define('U_MISSING_SEGMENT_CLOSE', 65548); -define('U_MULTIPLE_ANTE_CONTEXTS', 65549); -define('U_MULTIPLE_CURSORS', 65550); -define('U_MULTIPLE_POST_CONTEXTS', 65551); -define('U_TRAILING_BACKSLASH', 65552); -define('U_UNDEFINED_SEGMENT_REFERENCE', 65553); -define('U_UNDEFINED_VARIABLE', 65554); -define('U_UNQUOTED_SPECIAL', 65555); -define('U_UNTERMINATED_QUOTE', 65556); -define('U_RULE_MASK_ERROR', 65557); -define('U_MISPLACED_COMPOUND_FILTER', 65558); -define('U_MULTIPLE_COMPOUND_FILTERS', 65559); -define('U_INVALID_RBT_SYNTAX', 65560); -define('U_INVALID_PROPERTY_PATTERN', 65561); -define('U_MALFORMED_PRAGMA', 65562); -define('U_UNCLOSED_SEGMENT', 65563); -define('U_ILLEGAL_CHAR_IN_SEGMENT', 65564); -define('U_VARIABLE_RANGE_EXHAUSTED', 65565); -define('U_VARIABLE_RANGE_OVERLAP', 65566); -define('U_ILLEGAL_CHARACTER', 65567); -define('U_INTERNAL_TRANSLITERATOR_ERROR', 65568); -define('U_INVALID_ID', 65569); -define('U_INVALID_FUNCTION', 65570); -define('U_PARSE_ERROR_LIMIT', 65571); -define('U_UNEXPECTED_TOKEN', 65792); -define('U_FMT_PARSE_ERROR_START', 65792); -define('U_MULTIPLE_DECIMAL_SEPARATORS', 65793); -define('U_MULTIPLE_DECIMAL_SEPERATORS', 65793); -define('U_MULTIPLE_EXPONENTIAL_SYMBOLS', 65794); -define('U_MALFORMED_EXPONENTIAL_PATTERN', 65795); -define('U_MULTIPLE_PERCENT_SYMBOLS', 65796); -define('U_MULTIPLE_PERMILL_SYMBOLS', 65797); -define('U_MULTIPLE_PAD_SPECIFIERS', 65798); -define('U_PATTERN_SYNTAX_ERROR', 65799); -define('U_ILLEGAL_PAD_POSITION', 65800); -define('U_UNMATCHED_BRACES', 65801); -define('U_UNSUPPORTED_PROPERTY', 65802); -define('U_UNSUPPORTED_ATTRIBUTE', 65803); -define('U_FMT_PARSE_ERROR_LIMIT', 65812); -define('U_BRK_INTERNAL_ERROR', 66048); -define('U_BRK_ERROR_START', 66048); -define('U_BRK_HEX_DIGITS_EXPECTED', 66049); -define('U_BRK_SEMICOLON_EXPECTED', 66050); -define('U_BRK_RULE_SYNTAX', 66051); -define('U_BRK_UNCLOSED_SET', 66052); -define('U_BRK_ASSIGN_ERROR', 66053); -define('U_BRK_VARIABLE_REDFINITION', 66054); -define('U_BRK_MISMATCHED_PAREN', 66055); -define('U_BRK_NEW_LINE_IN_QUOTED_STRING', 66056); -define('U_BRK_UNDEFINED_VARIABLE', 66057); -define('U_BRK_INIT_ERROR', 66058); -define('U_BRK_RULE_EMPTY_SET', 66059); -define('U_BRK_UNRECOGNIZED_OPTION', 66060); -define('U_BRK_MALFORMED_RULE_TAG', 66061); -define('U_BRK_ERROR_LIMIT', 66062); -define('U_REGEX_INTERNAL_ERROR', 66304); -define('U_REGEX_ERROR_START', 66304); -define('U_REGEX_RULE_SYNTAX', 66305); -define('U_REGEX_INVALID_STATE', 66306); -define('U_REGEX_BAD_ESCAPE_SEQUENCE', 66307); -define('U_REGEX_PROPERTY_SYNTAX', 66308); -define('U_REGEX_UNIMPLEMENTED', 66309); -define('U_REGEX_MISMATCHED_PAREN', 66310); -define('U_REGEX_NUMBER_TOO_BIG', 66311); -define('U_REGEX_BAD_INTERVAL', 66312); -define('U_REGEX_MAX_LT_MIN', 66313); -define('U_REGEX_INVALID_BACK_REF', 66314); -define('U_REGEX_INVALID_FLAG', 66315); -define('U_REGEX_LOOK_BEHIND_LIMIT', 66316); -define('U_REGEX_SET_CONTAINS_STRING', 66317); -define('U_REGEX_ERROR_LIMIT', 66326); -define('U_IDNA_PROHIBITED_ERROR', 66560); -define('U_IDNA_ERROR_START', 66560); -define('U_IDNA_UNASSIGNED_ERROR', 66561); -define('U_IDNA_CHECK_BIDI_ERROR', 66562); -define('U_IDNA_STD3_ASCII_RULES_ERROR', 66563); -define('U_IDNA_ACE_PREFIX_ERROR', 66564); -define('U_IDNA_VERIFICATION_ERROR', 66565); -define('U_IDNA_LABEL_TOO_LONG_ERROR', 66566); -define('U_IDNA_ZERO_LENGTH_LABEL_ERROR', 66567); -define('U_IDNA_DOMAIN_NAME_TOO_LONG_ERROR', 66568); -define('U_IDNA_ERROR_LIMIT', 66569); -define('U_STRINGPREP_PROHIBITED_ERROR', 66560); -define('U_STRINGPREP_UNASSIGNED_ERROR', 66561); -define('U_STRINGPREP_CHECK_BIDI_ERROR', 66562); -define('U_ERROR_LIMIT', 66818); - -/** - * Prohibit processing of unassigned codepoints in the input for IDN - * functions and do not check if the input conforms to domain name ASCII rules. - * @link https://php.net/manual/en/intl.constants.php - */ -define('IDNA_DEFAULT', 0); - -/** - * Allow processing of unassigned codepoints in the input for IDN functions. - * @link https://php.net/manual/en/intl.constants.php - */ -define('IDNA_ALLOW_UNASSIGNED', 1); - -/** - * Check if the input for IDN functions conforms to domain name ASCII rules. - * @link https://php.net/manual/en/intl.constants.php - */ -define('IDNA_USE_STD3_RULES', 2); - -/** - * Check whether the input conforms to the BiDi rules. - * Ignored by the IDNA2003 implementation, which always performs this check. - * @link https://php.net/manual/en/intl.constants.php - */ -define('IDNA_CHECK_BIDI', 4); - -/** - * Check whether the input conforms to the CONTEXTJ rules. - * Ignored by the IDNA2003 implementation, as this check is new in IDNA2008. - * @link https://php.net/manual/en/intl.constants.php - */ -define('IDNA_CHECK_CONTEXTJ', 8); - -/** - * Option for nontransitional processing in - * idn_to_ascii. Transitional processing is activated - * by default. This option is ignored by the IDNA2003 implementation. - * @link https://php.net/manual/en/intl.constants.php - */ -define('IDNA_NONTRANSITIONAL_TO_ASCII', 16); - -/** - * Option for nontransitional processing in - * idn_to_utf8. Transitional processing is activated - * by default. This option is ignored by the IDNA2003 implementation. - * @link https://php.net/manual/en/intl.constants.php - */ -define('IDNA_NONTRANSITIONAL_TO_UNICODE', 32); - -/** - * Use IDNA 2003 algorithm in {@see idn_to_utf8} and - * {@see idn_to_ascii}. This is the default. - * @link https://php.net/manual/en/intl.constants.php - * @deprecated 7.2 Use {@see INTL_IDNA_VARIANT_UTS46} instead. - */ -define('INTL_IDNA_VARIANT_2003', 0); - -/** - * Use UTS #46 algorithm in idn_to_utf8 and - * idn_to_ascii. - * @link https://php.net/manual/en/intl.constants.php - */ -define('INTL_IDNA_VARIANT_UTS46', 1); - -/** - * Errors reported in a bitset returned by the UTS #46 algorithm in - * idn_to_utf8 and - * idn_to_ascii. - * @link https://php.net/manual/en/intl.constants.php - */ -define('IDNA_ERROR_EMPTY_LABEL', 1); -/** - * @link https://secure.php.net/manual/en/migration54.global-constants.php - * @since 5.4 - */ -define('IDNA_ERROR_LABEL_TOO_LONG', 2); -/** - * @link https://secure.php.net/manual/en/migration54.global-constants.php - * @since 5.4 - */ -define('IDNA_ERROR_DOMAIN_NAME_TOO_LONG', 4); -/** - * @link https://secure.php.net/manual/en/migration54.global-constants.php - * @since 5.4 - */ -define('IDNA_ERROR_LEADING_HYPHEN', 8); -/** - * @link https://secure.php.net/manual/en/migration54.global-constants.php - * @since 5.4 - */ -define('IDNA_ERROR_TRAILING_HYPHEN', 16); -/** - * @link https://secure.php.net/manual/en/migration54.global-constants.php - * @since 5.4 - */ -define('IDNA_ERROR_HYPHEN_3_4', 32); -/** - * @link https://secure.php.net/manual/en/migration54.global-constants.php - * @since 5.4 - */ -define('IDNA_ERROR_LEADING_COMBINING_MARK', 64); -/** - * @link https://secure.php.net/manual/en/migration54.global-constants.php - * @since 5.4 - */ -define('IDNA_ERROR_DISALLOWED', 128); -/** - * @link https://secure.php.net/manual/en/migration54.global-constants.php - * @since 5.4 - */ -define('IDNA_ERROR_PUNYCODE', 256); -/** - * @link https://secure.php.net/manual/en/migration54.global-constants.php - * @since 5.4 - */ -define('IDNA_ERROR_LABEL_HAS_DOT', 512); -/** - * @link https://secure.php.net/manual/en/migration54.global-constants.php - * @since 5.4 - */ -define('IDNA_ERROR_INVALID_ACE_LABEL', 1024); -/** - * @link https://secure.php.net/manual/en/migration54.global-constants.php - * @since 5.4 - */ -define('IDNA_ERROR_BIDI', 2048); -/** - * @link https://secure.php.net/manual/en/migration54.global-constants.php - * @since 5.4 - */ -define('IDNA_ERROR_CONTEXTJ', 4096); - -/** - * @since 5.5 - */ -class IntlBreakIterator implements IteratorAggregate -{ - /* Constants */ - public const DONE = -1; - public const WORD_NONE = 0; - public const WORD_NONE_LIMIT = 100; - public const WORD_NUMBER = 100; - public const WORD_NUMBER_LIMIT = 200; - public const WORD_LETTER = 200; - public const WORD_LETTER_LIMIT = 300; - public const WORD_KANA = 300; - public const WORD_KANA_LIMIT = 400; - public const WORD_IDEO = 400; - public const WORD_IDEO_LIMIT = 500; - public const LINE_SOFT = 0; - public const LINE_SOFT_LIMIT = 100; - public const LINE_HARD = 100; - public const LINE_HARD_LIMIT = 200; - public const SENTENCE_TERM = 0; - public const SENTENCE_TERM_LIMIT = 100; - public const SENTENCE_SEP = 100; - public const SENTENCE_SEP_LIMIT = 200; - - /* Methods */ - /** - * (PHP 5 >=5.5.0)- * Optional key type. Possible values are: - *
Type: boolean, default: TRUE.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_COMPRESSION = -1001; - const OPT_COMPRESSION_TYPE = -1004; - - /** - *This can be used to create a "domain" for your item keys. The value - * specified here will be prefixed to each of the keys. It cannot be - * longer than 128 characters and will reduce the - * maximum available key size. The prefix is applied only to the item keys, - * not to the server keys.
- *Type: string, default: "".
- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_PREFIX_KEY = -1002; - - /** - *- * Specifies the serializer to use for serializing non-scalar values. - * The valid serializers are Memcached::SERIALIZER_PHP - * or Memcached::SERIALIZER_IGBINARY. The latter is - * supported only when memcached is configured with - * --enable-memcached-igbinary option and the - * igbinary extension is loaded. - *
- *Type: integer, default: Memcached::SERIALIZER_PHP.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_SERIALIZER = -1003; - - /** - *Indicates whether igbinary serializer support is available.
- *Type: boolean.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const HAVE_IGBINARY = 0; - - /** - *Indicates whether JSON serializer support is available.
- *Type: boolean.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const HAVE_JSON = 0; - - /** - *Indicates whether msgpack serializer support is available.
- *Type: boolean.
- * Available as of Memcached 3.0.0. - * @since 3.0.0 - * @link https://php.net/manual/en/memcached.constants.php - */ - const HAVE_MSGPACK = 0; - - /** - *Indicate whether set_encoding_key is available
- *Type: boolean.
- * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/memcached-api.php, https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/php_memcached.c#L4387 - */ - const HAVE_ENCODING = 0; - - /** - * Feature support - */ - const HAVE_SESSION = 1; - const HAVE_SASL = 0; - - /** - *Specifies the hashing algorithm used for the item keys. The valid - * values are supplied via Memcached::HASH_* constants. - * Each hash algorithm has its advantages and its disadvantages. Go with the - * default if you don't know or don't care.
- *Type: integer, default: Memcached::HASH_DEFAULT
- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_HASH = 2; - - /** - *The default (Jenkins one-at-a-time) item key hashing algorithm.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const HASH_DEFAULT = 0; - - /** - *MD5 item key hashing algorithm.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const HASH_MD5 = 1; - - /** - *CRC item key hashing algorithm.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const HASH_CRC = 2; - - /** - *FNV1_64 item key hashing algorithm.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const HASH_FNV1_64 = 3; - - /** - *FNV1_64A item key hashing algorithm.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const HASH_FNV1A_64 = 4; - - /** - *FNV1_32 item key hashing algorithm.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const HASH_FNV1_32 = 5; - - /** - *FNV1_32A item key hashing algorithm.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const HASH_FNV1A_32 = 6; - - /** - *Hsieh item key hashing algorithm.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const HASH_HSIEH = 7; - - /** - *Murmur item key hashing algorithm.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const HASH_MURMUR = 8; - - /** - *Specifies the method of distributing item keys to the servers. - * Currently supported methods are modulo and consistent hashing. Consistent - * hashing delivers better distribution and allows servers to be added to - * the cluster with minimal cache losses.
- *Type: integer, default: Memcached::DISTRIBUTION_MODULA.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_DISTRIBUTION = 9; - - /** - *Modulo-based key distribution algorithm.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const DISTRIBUTION_MODULA = 0; - - /** - *Consistent hashing key distribution algorithm (based on libketama).
- * @link https://php.net/manual/en/memcached.constants.php - */ - const DISTRIBUTION_CONSISTENT = 1; - const DISTRIBUTION_VIRTUAL_BUCKET = 6; - - /** - *Enables or disables compatibility with libketama-like behavior. When - * enabled, the item key hashing algorithm is set to MD5 and distribution is - * set to be weighted consistent hashing distribution. This is useful - * because other libketama-based clients (Python, Ruby, etc.) with the same - * server configuration will be able to access the keys transparently. - *
- *- * It is highly recommended to enable this option if you want to use - * consistent hashing, and it may be enabled by default in future - * releases. - *
- *Type: boolean, default: FALSE.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_LIBKETAMA_COMPATIBLE = 16; - const OPT_LIBKETAMA_HASH = 17; - const OPT_TCP_KEEPALIVE = 32; - - /** - *Enables or disables buffered I/O. Enabling buffered I/O causes - * storage commands to "buffer" instead of being sent. Any action that - * retrieves data causes this buffer to be sent to the remote connection. - * Quitting the connection or closing down the connection will also cause - * the buffered data to be pushed to the remote connection.
- *Type: boolean, default: FALSE.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_BUFFER_WRITES = 10; - - /** - *Enable the use of the binary protocol. Please note that you cannot - * toggle this option on an open connection.
- *Type: boolean, default: FALSE.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_BINARY_PROTOCOL = 18; - - /** - *Enables or disables asynchronous I/O. This is the fastest transport - * available for storage functions.
- *Type: boolean, default: FALSE.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_NO_BLOCK = 0; - - /** - *Enables or disables the no-delay feature for connecting sockets (may - * be faster in some environments).
- *Type: boolean, default: FALSE.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_TCP_NODELAY = 1; - - /** - *The maximum socket send buffer in bytes.
- *Type: integer, default: varies by platform/kernel - * configuration.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_SOCKET_SEND_SIZE = 4; - - /** - *The maximum socket receive buffer in bytes.
- *Type: integer, default: varies by platform/kernel - * configuration.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_SOCKET_RECV_SIZE = 5; - - /** - *In non-blocking mode this set the value of the timeout during socket - * connection, in milliseconds.
- *Type: integer, default: 1000.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_CONNECT_TIMEOUT = 14; - - /** - *The amount of time, in seconds, to wait until retrying a failed - * connection attempt.
- *Type: integer, default: 0.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_RETRY_TIMEOUT = 15; - - /** - *Socket sending timeout, in microseconds. In cases where you cannot - * use non-blocking I/O this will allow you to still have timeouts on the - * sending of data.
- *Type: integer, default: 0.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_SEND_TIMEOUT = 19; - - /** - *Socket reading timeout, in microseconds. In cases where you cannot - * use non-blocking I/O this will allow you to still have timeouts on the - * reading of data.
- *Type: integer, default: 0.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_RECV_TIMEOUT = 20; - - /** - *Timeout for connection polling, in milliseconds.
- *Type: integer, default: 1000.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_POLL_TIMEOUT = 8; - - /** - *Enables or disables caching of DNS lookups.
- *Type: boolean, default: FALSE.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_CACHE_LOOKUPS = 6; - - /** - *Specifies the failure limit for server connection attempts. The - * server will be removed after this many continuous connection - * failures.
- *Type: integer, default: 0.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const OPT_SERVER_FAILURE_LIMIT = 21; - const OPT_AUTO_EJECT_HOSTS = 28; - const OPT_HASH_WITH_PREFIX_KEY = 25; - const OPT_NOREPLY = 26; - const OPT_SORT_HOSTS = 12; - const OPT_VERIFY_KEY = 13; - const OPT_USE_UDP = 27; - const OPT_NUMBER_OF_REPLICAS = 29; - const OPT_RANDOMIZE_REPLICA_READ = 30; - const OPT_CORK = 31; - const OPT_REMOVE_FAILED_SERVERS = 35; - const OPT_DEAD_TIMEOUT = 36; - const OPT_SERVER_TIMEOUT_LIMIT = 37; - const OPT_MAX = 38; - const OPT_IO_BYTES_WATERMARK = 23; - const OPT_IO_KEY_PREFETCH = 24; - const OPT_IO_MSG_WATERMARK = 22; - const OPT_LOAD_FROM_FILE = 34; - const OPT_SUPPORT_CAS = 7; - const OPT_TCP_KEEPIDLE = 33; - const OPT_USER_DATA = 11; - - - /** - * libmemcached result codes - */ - /** - *The operation was successful.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_SUCCESS = 0; - - /** - *The operation failed in some fashion.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_FAILURE = 1; - - /** - *DNS lookup failed.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_HOST_LOOKUP_FAILURE = 2; - - /** - *Failed to read network data.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_UNKNOWN_READ_FAILURE = 7; - - /** - *Bad command in memcached protocol.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_PROTOCOL_ERROR = 8; - - /** - *Error on the client side.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_CLIENT_ERROR = 9; - - /** - *Error on the server side.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_SERVER_ERROR = 10; - - /** - *Failed to write network data.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_WRITE_FAILURE = 5; - - /** - *Failed to do compare-and-swap: item you are trying to store has been - * modified since you last fetched it.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_DATA_EXISTS = 12; - - /** - *Item was not stored: but not because of an error. This normally - * means that either the condition for an "add" or a "replace" command - * wasn't met, or that the item is in a delete queue.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_NOTSTORED = 14; - - /** - *Item with this key was not found (with "get" operation or "cas" - * operations).
- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_NOTFOUND = 16; - - /** - *Partial network data read error.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_PARTIAL_READ = 18; - - /** - *Some errors occurred during multi-get.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_SOME_ERRORS = 19; - - /** - *Server list is empty.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_NO_SERVERS = 20; - - /** - *End of result set.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_END = 21; - - /** - *System error.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_ERRNO = 26; - - /** - *The operation was buffered.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_BUFFERED = 32; - - /** - *The operation timed out.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_TIMEOUT = 31; - - /** - *Bad key.
- * @link https://php.net/manual/en/memcached.constants.php, http://docs.libmemcached.org/index.html - */ - /** - *MEMCACHED_BAD_KEY_PROVIDED: The key provided is not a valid key.
- */ - const RES_BAD_KEY_PROVIDED = 33; - /** - *MEMCACHED_STORED: The requested object has been successfully stored on the server.
- */ - const RES_STORED = 15; - /** - *MEMCACHED_DELETED: The object requested by the key has been deleted.
- */ - const RES_DELETED = 22; - /** - *MEMCACHED_STAT: A “stat” command has been returned in the protocol.
- */ - const RES_STAT = 24; - /** - *MEMCACHED_ITEM: An item has been fetched (this is an internal error only).
- */ - const RES_ITEM = 25; - /** - *MEMCACHED_NOT_SUPPORTED: The given method is not supported in the server.
- */ - const RES_NOT_SUPPORTED = 28; - /** - *MEMCACHED_FETCH_NOTFINISHED: A request has been made, but the server has not finished the fetch of the last request.
- */ - const RES_FETCH_NOTFINISHED = 30; - /** - *MEMCACHED_SERVER_MARKED_DEAD: The requested server has been marked dead.
- */ - const RES_SERVER_MARKED_DEAD = 35; - /** - *MEMCACHED_UNKNOWN_STAT_KEY: The server you are communicating with has a stat key which has not be defined in the protocol.
- */ - const RES_UNKNOWN_STAT_KEY = 36; - /** - *MEMCACHED_INVALID_HOST_PROTOCOL: The server you are connecting too has an invalid protocol. Most likely you are connecting to an older server that does not speak the binary protocol.
- */ - const RES_INVALID_HOST_PROTOCOL = 34; - /** - *MEMCACHED_MEMORY_ALLOCATION_FAILURE: An error has occurred while trying to allocate memory.
- */ - const RES_MEMORY_ALLOCATION_FAILURE = 17; - /** - *MEMCACHED_E2BIG: Item is too large for the server to store.
- */ - const RES_E2BIG = 37; - /** - *MEMCACHED_KEY_TOO_BIG: The key that has been provided is too large for the given server.
- */ - const RES_KEY_TOO_BIG = 39; - /** - *MEMCACHED_SERVER_TEMPORARILY_DISABLED
- */ - const RES_SERVER_TEMPORARILY_DISABLED = 47; - /** - *MEMORY_ALLOCATION_FAILURE: An error has occurred while trying to allocate memory. - * - * #if defined(LIBMEMCACHED_VERSION_HEX) && LIBMEMCACHED_VERSION_HEX >= 0x01000008
- */ - const RES_SERVER_MEMORY_ALLOCATION_FAILURE = 48; - /** - *MEMCACHED_AUTH_PROBLEM: An unknown issue has occured during authentication.
- */ - const RES_AUTH_PROBLEM = 40; - /** - *MEMCACHED_AUTH_FAILURE: The credentials provided are not valid for this server.
- */ - const RES_AUTH_FAILURE = 41; - /** - *MEMCACHED_AUTH_CONTINUE: Authentication has been paused.
- */ - const RES_AUTH_CONTINUE = 42; - /** - *MEMCACHED_CONNECTION_FAILURE: A unknown error has occured while trying to connect to a server.
- */ - const RES_CONNECTION_FAILURE = 3; - /** - *MEMCACHED_CONNECTION_BIND_FAILURE: Deprecated since version <0.30(libmemcached). - * We were not able to bind() to the socket.
- */ - const RES_CONNECTION_BIND_FAILURE = 4; - /** - *MEMCACHED_READ_FAILURE: A read failure has occurred.
- */ - const RES_READ_FAILURE = 6; - /** - *MEMCACHED_DATA_DOES_NOT_EXIST: The data requested with the key given was not found.
- */ - const RES_DATA_DOES_NOT_EXIST = 13; - /** - *MEMCACHED_VALUE: A value has been returned from the server (this is an internal condition only).
- */ - const RES_VALUE = 23; - /** - *MEMCACHED_FAIL_UNIX_SOCKET: A connection was not established with the server via a unix domain socket.
- */ - const RES_FAIL_UNIX_SOCKET = 27; - /** - *MEMCACHED_NO_KEY_PROVIDED: Deprecated since version <0.30(libmemcached): Use MEMCACHED_BAD_KEY_PROVIDED instead. - * No key was provided.
- */ - const RES_NO_KEY_PROVIDED = 29; - /** - *MEMCACHED_INVALID_ARGUMENTS: The arguments supplied to the given function were not valid.
- */ - const RES_INVALID_ARGUMENTS = 38; - /** - *MEMCACHED_PARSE_ERROR: An error has occurred while trying to parse the configuration string. You should use memparse to determine what the error was.
- */ - const RES_PARSE_ERROR = 43; - /** - *MEMCACHED_PARSE_USER_ERROR: An error has occurred in parsing the configuration string.
- */ - const RES_PARSE_USER_ERROR = 44; - /** - *MEMCACHED_DEPRECATED: The method that was requested has been deprecated.
- */ - const RES_DEPRECATED = 45; - //unknow - const RES_IN_PROGRESS = 46; - /** - *MEMCACHED_MAXIMUM_RETURN: This in an internal only state.
- */ - const RES_MAXIMUM_RETURN = 49; - - /** - * Server callbacks, if compiled with --memcached-protocol - * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/memcached-api.php - */ - const ON_CONNECT = 0; - const ON_ADD = 1; - const ON_APPEND = 2; - const ON_DECREMENT = 3; - const ON_DELETE = 4; - const ON_FLUSH = 5; - const ON_GET = 6; - const ON_INCREMENT = 7; - const ON_NOOP = 8; - const ON_PREPEND = 9; - const ON_QUIT = 10; - const ON_REPLACE = 11; - const ON_SET = 12; - const ON_STAT = 13; - const ON_VERSION = 14; - /** - * Constants used when compiled with --memcached-protocol - * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/memcached-api.php - */ - const RESPONSE_SUCCESS = 0; - const RESPONSE_KEY_ENOENT = 1; - const RESPONSE_KEY_EEXISTS = 2; - const RESPONSE_E2BIG = 3; - const RESPONSE_EINVAL = 4; - const RESPONSE_NOT_STORED = 5; - const RESPONSE_DELTA_BADVAL = 6; - const RESPONSE_NOT_MY_VBUCKET = 7; - const RESPONSE_AUTH_ERROR = 32; - const RESPONSE_AUTH_CONTINUE = 33; - const RESPONSE_UNKNOWN_COMMAND = 129; - const RESPONSE_ENOMEM = 130; - const RESPONSE_NOT_SUPPORTED = 131; - const RESPONSE_EINTERNAL = 132; - const RESPONSE_EBUSY = 133; - const RESPONSE_ETMPFAIL = 134; - - - /** - *Failed to create network socket.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_CONNECTION_SOCKET_CREATE_FAILURE = 11; - - /** - *Payload failure: could not compress/decompress or serialize/unserialize the value.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const RES_PAYLOAD_FAILURE = -1001; - - /** - *The default PHP serializer.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const SERIALIZER_PHP = 1; - - /** - *The igbinary serializer. - * Instead of textual representation it stores PHP data structures in a - * compact binary form, resulting in space and time gains.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const SERIALIZER_IGBINARY = 2; - - /** - *The JSON serializer. Requires PHP 5.2.10+.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const SERIALIZER_JSON = 3; - const SERIALIZER_JSON_ARRAY = 4; - /** - *The msgpack serializer.
- * @link https://github.com/php-memcached-dev/php-memcached/blob/v3.1.5/memcached-api.php - */ - const SERIALIZER_MSGPACK = 5; - - const COMPRESSION_FASTLZ = 2; - const COMPRESSION_ZLIB = 1; - - /** - *A flag for Memcached::getMulti and - * Memcached::getMultiByKey to ensure that the keys are - * returned in the same order as they were requested in. Non-existing keys - * get a default value of NULL.
- * @link https://php.net/manual/en/memcached.constants.php - */ - const GET_PRESERVE_ORDER = 1; - - /** - * A flag for Memcached::get(), Memcached::getMulti() and - * Memcached::getMultiByKey() to ensure that the CAS token values are returned as well. - * @link https://php.net/manual/en/memcached.constants.php - */ - const GET_EXTENDED = 2; - - const GET_ERROR_RETURN_VALUE = false; - - /** - * (PECL memcached >= 0.1.0)- * The key of the item to retrieve. - *
- * @param callable $cache_cb [optional]- * Read-through caching callback or NULL. - *
- * @param int $flags [optional]- * The flags for the get operation. - *
- * @return mixed the value stored in the cache or FALSE otherwise. - * The Memcached::getResultCode will return - * Memcached::RES_NOTFOUND if the key does not exist. - */ - public function get ($key, callable $cache_cb = null, $flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *
- * @param string $key- * The key of the item to fetch. - *
- * @param callable $cache_cb [optional]- * Read-through caching callback or NULL - *
- * @param int $flags [optional]- * The flags for the get operation. - *
- * @return mixed the value stored in the cache or FALSE otherwise. - * The Memcached::getResultCode will return - * Memcached::RES_NOTFOUND if the key does not exist. - */ - public function getByKey ($server_key, $key, callable $cache_cb = null, $flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)- * Array of keys to retrieve. - *
- * @param int $flags [optional]- * The flags for the get operation. - *
- * @return mixed the array of found items or FALSE on failure. - * Use Memcached::getResultCode if necessary. - */ - public function getMulti (array $keys, $flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *
- * @param array $keys- * Array of keys to retrieve. - *
- * @param int $flags [optional]- * The flags for the get operation. - *
- * @return array|false the array of found items or FALSE on failure. - * Use Memcached::getResultCode if necessary. - */ - public function getMultiByKey ($server_key, array $keys, $flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)- * Array of keys to request. - *
- * @param bool $with_cas [optional]- * Whether to request CAS token values also. - *
- * @param callable $value_cb [optional]- * The result callback or NULL. - *
- * @return bool TRUE on success or FALSE on failure. - * Use Memcached::getResultCode if necessary. - */ - public function getDelayed (array $keys, $with_cas = null, callable $value_cb = null) {} - - /** - * (PECL memcached >= 0.1.0)- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *
- * @param array $keys- * Array of keys to request. - *
- * @param bool $with_cas [optional]- * Whether to request CAS token values also. - *
- * @param callable $value_cb [optional]- * The result callback or NULL. - *
- * @return bool TRUE on success or FALSE on failure. - * Use Memcached::getResultCode if necessary. - */ - public function getDelayedByKey ($server_key, array $keys, $with_cas = null, callable $value_cb = null) {} - - /** - * (PECL memcached >= 0.1.0)- * The key under which to store the value. - *
- * @param mixed $value- * The value to store. - *
- * @param int $expiration [optional]- * The expiration time, defaults to 0. See Expiration Times for more info. - *
- * @param int $udf_flags [optional] - * @return bool TRUE on success or FALSE on failure. - * Use Memcached::getResultCode if necessary. - */ - public function set ($key, $value, $expiration = 0, $udf_flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *
- * @param string $key- * The key under which to store the value. - *
- * @param mixed $value- * The value to store. - *
- * @param int $expiration [optional]- * The expiration time, defaults to 0. See Expiration Times for more info. - *
- * @param int $udf_flags [optional] - * @return bool TRUE on success or FALSE on failure. - * Use Memcached::getResultCode if necessary. - */ - public function setByKey ($server_key, $key, $value, $expiration = 0, $udf_flags = 0) {} - - /** - * (PECL memcached >= 2.0.0)- * The key under which to store the value. - *
- * @param int $expiration- * The expiration time, defaults to 0. See Expiration Times for more info. - *
- * @return bool TRUE on success or FALSE on failure. - * Use Memcached::getResultCode if necessary. - */ - public function touch ($key, $expiration = 0) {} - - /** - * (PECL memcached >= 2.0.0)- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *
- * @param string $key- * The key under which to store the value. - *
- * @param int $expiration- * The expiration time, defaults to 0. See Expiration Times for more info. - *
- * @return bool TRUE on success or FALSE on failure. - * Use Memcached::getResultCode if necessary. - */ - public function touchByKey ($server_key, $key, $expiration) {} - - /** - * (PECL memcached >= 0.1.0)- * An array of key/value pairs to store on the server. - *
- * @param int $expiration [optional]- * The expiration time, defaults to 0. See Expiration Times for more info. - *
- * @param int $udf_flags [optional] - * @return bool TRUE on success or FALSE on failure. - * Use Memcached::getResultCode if necessary. - */ - public function setMulti (array $items, $expiration = 0, $udf_flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *
- * @param array $items- * An array of key/value pairs to store on the server. - *
- * @param int $expiration [optional]- * The expiration time, defaults to 0. See Expiration Times for more info. - *
- * @param int $udf_flags [optional] - * @return bool TRUE on success or FALSE on failure. - * Use Memcached::getResultCode if necessary. - */ - public function setMultiByKey ($server_key, array $items, $expiration = 0, $udf_flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)- * Unique value associated with the existing item. Generated by memcache. - *
- * @param string $key- * The key under which to store the value. - *
- * @param mixed $value- * The value to store. - *
- * @param int $expiration [optional]- * The expiration time, defaults to 0. See Expiration Times for more info. - *
- * @param int $udf_flags [optional] - * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_DATA_EXISTS if the item you are trying - * to store has been modified since you last fetched it. - */ - public function cas ($cas_token, $key, $value, $expiration = 0, $udf_flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)- * Unique value associated with the existing item. Generated by memcache. - *
- * @param string $server_key- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *
- * @param string $key- * The key under which to store the value. - *
- * @param mixed $value- * The value to store. - *
- * @param int $expiration [optional]- * The expiration time, defaults to 0. See Expiration Times for more info. - *
- * @param int $udf_flags [optional] - * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_DATA_EXISTS if the item you are trying - * to store has been modified since you last fetched it. - */ - public function casByKey ($cas_token, $server_key, $key, $value, $expiration = 0, $udf_flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)- * The key under which to store the value. - *
- * @param mixed $value- * The value to store. - *
- * @param int $expiration [optional]- * The expiration time, defaults to 0. See Expiration Times for more info. - *
- * @param int $udf_flags [optional] - * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_NOTSTORED if the key already exists. - */ - public function add ($key, $value, $expiration = 0, $udf_flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *
- * @param string $key- * The key under which to store the value. - *
- * @param mixed $value- * The value to store. - *
- * @param int $expiration [optional]- * The expiration time, defaults to 0. See Expiration Times for more info. - *
- * @param int $udf_flags [optional] - * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_NOTSTORED if the key already exists. - */ - public function addByKey ($server_key, $key, $value, $expiration = 0, $udf_flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)- * The key under which to store the value. - *
- * @param string $value- * The string to append. - *
- * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_NOTSTORED if the key does not exist. - */ - public function append ($key, $value) {} - - /** - * (PECL memcached >= 0.1.0)- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *
- * @param string $key- * The key under which to store the value. - *
- * @param string $value- * The string to append. - *
- * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_NOTSTORED if the key does not exist. - */ - public function appendByKey ($server_key, $key, $value) {} - - /** - * (PECL memcached >= 0.1.0)- * The key of the item to prepend the data to. - *
- * @param string $value- * The string to prepend. - *
- * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_NOTSTORED if the key does not exist. - */ - public function prepend ($key, $value) {} - - /** - * (PECL memcached >= 0.1.0)- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *
- * @param string $key- * The key of the item to prepend the data to. - *
- * @param string $value- * The string to prepend. - *
- * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_NOTSTORED if the key does not exist. - */ - public function prependByKey ($server_key, $key, $value) {} - - /** - * (PECL memcached >= 0.1.0)- * The key under which to store the value. - *
- * @param mixed $value- * The value to store. - *
- * @param int $expiration [optional]- * The expiration time, defaults to 0. See Expiration Times for more info. - *
- * @param int $udf_flags [optional] - * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_NOTSTORED if the key does not exist. - */ - public function replace ($key, $value, $expiration = null, $udf_flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *
- * @param string $key- * The key under which to store the value. - *
- * @param mixed $value- * The value to store. - *
- * @param int $expiration [optional]- * The expiration time, defaults to 0. See Expiration Times for more info. - *
- * @param int $udf_flags [optional] - * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_NOTSTORED if the key does not exist. - */ - public function replaceByKey ($server_key, $key, $value, $expiration = null, $udf_flags = 0) {} - - /** - * (PECL memcached >= 0.1.0)- * The key to be deleted. - *
- * @param int $time [optional]- * The amount of time the server will wait to delete the item. - *
- * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_NOTFOUND if the key does not exist. - */ - public function delete ($key, $time = 0) {} - - /** - * (PECL memcached >= 2.0.0)- * The keys to be deleted. - *
- * @param int $time [optional]- * The amount of time the server will wait to delete the items. - *
- * @return array Returns array indexed by keys and where values are indicating whether operation succeeded or not. - * The Memcached::getResultCode will return - * Memcached::RES_NOTFOUND if the key does not exist. - */ - public function deleteMulti (array $keys, $time = 0) {} - - /** - * (PECL memcached >= 0.1.0)- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *
- * @param string $key- * The key to be deleted. - *
- * @param int $time [optional]- * The amount of time the server will wait to delete the item. - *
- * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_NOTFOUND if the key does not exist. - */ - public function deleteByKey ($server_key, $key, $time = 0) {} - - /** - * (PECL memcached >= 2.0.0)- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *
- * @param array $keys- * The keys to be deleted. - *
- * @param int $time [optional]- * The amount of time the server will wait to delete the items. - *
- * @return bool TRUE on success or FALSE on failure. - * The Memcached::getResultCode will return - * Memcached::RES_NOTFOUND if the key does not exist. - */ - public function deleteMultiByKey ($server_key, array $keys, $time = 0) {} - - /** - * (PECL memcached >= 0.1.0)- * The key of the item to increment. - *
- * @param int $offset [optional]- * The amount by which to increment the item's value. - *
- * @param int $initial_value [optional]- * The value to set the item to if it doesn't currently exist. - *
- * @param int $expiry [optional]- * The expiry time to set on the item. - *
- * @return int|false new item's value on success or FALSE on failure. - */ - public function increment ($key, $offset = 1, $initial_value = 0, $expiry = 0) {} - - /** - * (PECL memcached >= 0.1.0)- * The key of the item to decrement. - *
- * @param int $offset [optional]- * The amount by which to decrement the item's value. - *
- * @param int $initial_value [optional]- * The value to set the item to if it doesn't currently exist. - *
- * @param int $expiry [optional]- * The expiry time to set on the item. - *
- * @return int|false item's new value on success or FALSE on failure. - */ - public function decrement ($key, $offset = 1, $initial_value = 0, $expiry = 0) {} - - /** - * (PECL memcached >= 2.0.0)- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *
- * @param string $key- * The key of the item to increment. - *
- * @param int $offset [optional]- * The amount by which to increment the item's value. - *
- * @param int $initial_value [optional]- * The value to set the item to if it doesn't currently exist. - *
- * @param int $expiry [optional]- * The expiry time to set on the item. - *
- * @return int|false new item's value on success or FALSE on failure. - */ - public function incrementByKey ($server_key, $key, $offset = 1, $initial_value = 0, $expiry = 0) {} - - /** - * (PECL memcached >= 2.0.0)- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *
- * @param string $key- * The key of the item to decrement. - *
- * @param int $offset [optional]- * The amount by which to decrement the item's value. - *
- * @param int $initial_value [optional]- * The value to set the item to if it doesn't currently exist. - *
- * @param int $expiry [optional]- * The expiry time to set on the item. - *
- * @return int|false item's new value on success or FALSE on failure. - */ - public function decrementByKey ($server_key, $key, $offset = 1, $initial_value = 0, $expiry = 0) {} - - /** - * (PECL memcached >= 0.1.0)- * The hostname of the memcache server. If the hostname is invalid, data-related - * operations will set - * Memcached::RES_HOST_LOOKUP_FAILURE result code. - *
- * @param int $port- * The port on which memcache is running. Usually, this is - * 11211. - *
- * @param int $weight [optional]- * The weight of the server relative to the total weight of all the - * servers in the pool. This controls the probability of the server being - * selected for operations. This is used only with consistent distribution - * option and usually corresponds to the amount of memory available to - * memcache on that server. - *
- * @return bool TRUE on success or FALSE on failure. - */ - public function addServer ($host, $port, $weight = 0) {} - - /** - * (PECL memcached >= 0.1.1)- * The key identifying the server to store the value on or retrieve it from. Instead of hashing on the actual key for the item, we hash on the server key when deciding which memcached server to talk to. This allows related items to be grouped together on a single server for efficiency with multi operations. - *
- * @return array an array containing three keys of host, - * port, and weight on success or FALSE - * on failure. - * Use Memcached::getResultCode if necessary. - */ - public function getServerByKey ($server_key) {} - - /** - * (PECL memcached >= 2.0.0)items, slabs, sizes ...
- * @return array Array of server statistics, one entry per server. - */ - public function getStats ($type = null) {} - - /** - * (PECL memcached >= 0.1.5)- * Numer of seconds to wait before invalidating the items. - *
- * @return bool TRUE on success or FALSE on failure. - * Use Memcached::getResultCode if necessary. - */ - public function flush ($delay = 0) {} - - /** - * (PECL memcached >= 0.1.0)- * One of the Memcached::OPT_* constants. - *
- * @return mixed the value of the requested option, or FALSE on - * error. - */ - public function getOption ($option) {} - - /** - * (PECL memcached >= 0.1.0)- * An associative array of options where the key is the option to set and - * the value is the new value for the option. - *
- * @return bool TRUE on success or FALSE on failure. - */ - public function setOptions (array $options) {} - - /** - * (PECL memcached >= 2.0.0)- * The username to use for authentication. - *
- * @param string $password- * The password to use for authentication. - *
- * @return bool TRUE on success or FALSE on failure. - */ - public function setSaslAuthData (string $username , string $password) {} - - /** - * (PECL memcached >= 2.0.0)
- * $redis->connect('127.0.0.1', 6379);
- * $redis->connect('127.0.0.1'); // port 6379 by default
- * $redis->connect('127.0.0.1', 6379, 2.5); // 2.5 sec timeout.
- * $redis->connect('/tmp/redis.sock'); // unix domain socket.
- *
- */
- public function connect(
- $host,
- $port = 6379,
- $timeout = 0.0,
- $reserved = null,
- $retryInterval = 0,
- $readTimeout = 0.0,
- $connectionParameters = []
- ) {
- }
-
- /**
- * Connects to a Redis instance.
- *
- * @param string $host can be a host, or the path to a unix domain socket
- * @param int $port optional
- * @param float $timeout value in seconds (optional, default is 0.0 meaning unlimited)
- * @param null $reserved should be null if $retry_interval is specified
- * @param int $retryInterval retry interval in milliseconds.
- * @param float $readTimeout value in seconds (optional, default is 0 meaning unlimited)
- *
- * @return bool TRUE on success, FALSE on error
- */
- #[Deprecated(replacement: '%class%->connect(%parametersList%)')]
- public function open(
- $host,
- $port = 6379,
- $timeout = 0.0,
- $reserved = null,
- $retryInterval = 0,
- $readTimeout = 0.0
- ) {
- }
-
- /**
- * A method to determine if a phpredis object thinks it's connected to a server
- *
- * @return bool Returns TRUE if phpredis thinks it's connected and FALSE if not
- */
- public function isConnected()
- {
- }
-
- /**
- * Retrieve our host or unix socket that we're connected to
- *
- * @return string|false The host or unix socket we're connected to or FALSE if we're not connected
- */
- public function getHost()
- {
- }
-
- /**
- * Get the port we're connected to
- *
- * @return int|false Returns the port we're connected to or FALSE if we're not connected
- */
- public function getPort()
- {
- }
-
- /**
- * Get the database number phpredis is pointed to
- *
- * @return int|bool Returns the database number (int) phpredis thinks it's pointing to
- * or FALSE if we're not connected
- */
- public function getDbNum()
- {
- }
-
- /**
- * Get the (write) timeout in use for phpredis
- *
- * @return float|false The timeout (DOUBLE) specified in our connect call or FALSE if we're not connected
- */
- public function getTimeout()
- {
- }
-
- /**
- * Get the read timeout specified to phpredis or FALSE if we're not connected
- *
- * @return float|bool Returns the read timeout (which can be set using setOption and Redis::OPT_READ_TIMEOUT)
- * or FALSE if we're not connected
- */
- public function getReadTimeout()
- {
- }
-
- /**
- * Gets the persistent ID that phpredis is using
- *
- * @return string|null|bool Returns the persistent id phpredis is using
- * (which will only be set if connected with pconnect),
- * NULL if we're not using a persistent ID,
- * and FALSE if we're not connected
- */
- public function getPersistentID()
- {
- }
-
- /**
- * Get the password used to authenticate the phpredis connection
- *
- * @return string|null|bool Returns the password used to authenticate a phpredis session or NULL if none was used,
- * and FALSE if we're not connected
- */
- public function getAuth()
- {
- }
-
- /**
- * Connects to a Redis instance or reuse a connection already established with pconnect/popen.
- *
- * The connection will not be closed on close or end of request until the php process ends.
- * So be patient on to many open FD's (specially on redis server side) when using persistent connections on
- * many servers connecting to one redis server.
- *
- * Also more than one persistent connection can be made identified by either host + port + timeout
- * or host + persistentId or unix socket + timeout.
- *
- * This feature is not available in threaded versions. pconnect and popen then working like their non persistent
- * equivalents.
- *
- * @param string $host can be a host, or the path to a unix domain socket
- * @param int $port optional
- * @param float $timeout value in seconds (optional, default is 0 meaning unlimited)
- * @param string $persistentId identity for the requested persistent connection
- * @param int $retryInterval retry interval in milliseconds.
- * @param float $readTimeout value in seconds (optional, default is 0 meaning unlimited)
- *
- * @return bool TRUE on success, FALSE on ertcnror.
- *
- * @example
- *
- * $redis->pconnect('127.0.0.1', 6379);
- *
- * // port 6379 by default - same connection like before
- * $redis->pconnect('127.0.0.1');
- *
- * // 2.5 sec timeout and would be another connection than the two before.
- * $redis->pconnect('127.0.0.1', 6379, 2.5);
- *
- * // x is sent as persistent_id and would be another connection than the three before.
- * $redis->pconnect('127.0.0.1', 6379, 2.5, 'x');
- *
- * // unix domain socket - would be another connection than the four before.
- * $redis->pconnect('/tmp/redis.sock');
- *
- */
- public function pconnect(
- $host,
- $port = 6379,
- $timeout = 0.0,
- $persistentId = null,
- $retryInterval = 0,
- $readTimeout = 0.0
- ) {
- }
-
- /**
- * @param string $host
- * @param int $port
- * @param float $timeout
- * @param string $persistentId
- * @param int $retryInterval
- * @param float $readTimeout
- *
- * @return bool
- */
- #[Deprecated(replacement: '%class%->pconnect(%parametersList%)')]
- public function popen(
- $host,
- $port = 6379,
- $timeout = 0.0,
- $persistentId = '',
- $retryInterval = 0,
- $readTimeout = 0.0
- ) {
- }
-
- /**
- * Disconnects from the Redis instance.
- *
- * Note: Closing a persistent connection requires PhpRedis >= 4.2.0
- *
- * @since >= 4.2 Closing a persistent connection requires PhpRedis
- *
- * @return bool TRUE on success, FALSE on error
- */
- public function close()
- {
- }
-
- /**
- * Swap one Redis database with another atomically
- *
- * Note: Requires Redis >= 4.0.0
- *
- * @param int $db1
- * @param int $db2
- *
- * @return bool TRUE on success and FALSE on failure
- *
- * @link https://redis.io/commands/swapdb
- * @since >= 4.0
- * @example
- * - * // Swaps DB 0 with DB 1 atomically - * $redis->swapdb(0, 1); - *- */ - public function swapdb(int $db1, int $db2) - { - } - - /** - * Set client option - * - * @param int $option option name - * @param mixed $value option value - * - * @return bool TRUE on success, FALSE on error - * - * @example - *
- * $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); // don't serialize data - * $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); // use built-in serialize/unserialize - * $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY); // use igBinary serialize/unserialize - * $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_MSGPACK); // Use msgpack serialize/unserialize - * $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_JSON); // Use json serialize/unserialize - * - * $redis->setOption(Redis::OPT_PREFIX, 'myAppName:'); // use custom prefix on all keys - * - * // Options for the SCAN family of commands, indicating whether to abstract - * // empty results from the user. If set to SCAN_NORETRY (the default), phpredis - * // will just issue one SCAN command at a time, sometimes returning an empty - * // array of results. If set to SCAN_RETRY, phpredis will retry the scan command - * // until keys come back OR Redis returns an iterator of zero - * $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY); - * $redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY); - *- */ - public function setOption($option, $value) - { - } - - /** - * Get client option - * - * @param int $option parameter name - * - * @return mixed|null Parameter value - * - * @see setOption() - * @example - * // return option value - * $redis->getOption(Redis::OPT_SERIALIZER); - */ - public function getOption($option) - { - } - - /** - * Check the current connection status - * - * @param string $message [optional] - * - * @return bool|string TRUE if the command is successful or returns message - * Throws a RedisException object on connectivity error, as described above. - * @throws RedisException - * @link https://redis.io/commands/ping - */ - public function ping($message = null) - { - } - - /** - * Echo the given string - * - * @param string $message - * - * @return string Returns message - * - * @link https://redis.io/commands/echo - */ - public function echo($message) - { - } - - /** - * Get the value related to the specified key - * - * @param string $key - * - * @return string|mixed|false If key didn't exist, FALSE is returned. - * Otherwise, the value related to this key is returned - * - * @link https://redis.io/commands/get - * @example - *
- * $redis->set('key', 'hello');
- * $redis->get('key');
- *
- * // set and get with serializer
- * $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_JSON);
- *
- * $redis->set('key', ['asd' => 'as', 'dd' => 123, 'b' => true]);
- * var_dump($redis->get('key'));
- * // Output:
- * array(3) {
- * 'asd' => string(2) "as"
- * 'dd' => int(123)
- * 'b' => bool(true)
- * }
- *
- */
- public function get($key)
- {
- }
-
- /**
- * Set the string value in argument as value of the key.
- *
- * @since If you're using Redis >= 2.6.12, you can pass extended options as explained in example
- *
- * @param string $key
- * @param string|mixed $value string if not used serializer
- * @param int|array $timeout [optional] Calling setex() is preferred if you want a timeout.
- * // Simple key -> value set
- * $redis->set('key', 'value');
- *
- * // Will redirect, and actually make an SETEX call
- * $redis->set('key','value', 10);
- *
- * // Will set the key, if it doesn't exist, with a ttl of 10 seconds
- * $redis->set('key', 'value', ['nx', 'ex' => 10]);
- *
- * // Will set a key, if it does exist, with a ttl of 1000 milliseconds
- * $redis->set('key', 'value', ['xx', 'px' => 1000]);
- *
- *
- * @return bool TRUE if the command is successful
- *
- * @link https://redis.io/commands/set
- */
- public function set($key, $value, $timeout = null)
- {
- }
-
- /**
- * Set the string value in argument as value of the key, with a time to live.
- *
- * @param string $key
- * @param int $ttl
- * @param string|mixed $value
- *
- * @return bool TRUE if the command is successful
- *
- * @link https://redis.io/commands/setex
- * @example $redis->setex('key', 3600, 'value'); // sets key → value, with 1h TTL.
- */
- public function setex($key, $ttl, $value)
- {
- }
-
- /**
- * Set the value and expiration in milliseconds of a key.
- *
- * @see setex()
- * @param string $key
- * @param int $ttl, in milliseconds.
- * @param string|mixed $value
- *
- * @return bool TRUE if the command is successful
- *
- * @link https://redis.io/commands/psetex
- * @example $redis->psetex('key', 1000, 'value'); // sets key → value, with 1sec TTL.
- */
- public function psetex($key, $ttl, $value)
- {
- }
-
- /**
- * Set the string value in argument as value of the key if the key doesn't already exist in the database.
- *
- * @param string $key
- * @param string|mixed $value
- *
- * @return bool TRUE in case of success, FALSE in case of failure
- *
- * @link https://redis.io/commands/setnx
- * @example
- *
- * $redis->setnx('key', 'value'); // return TRUE
- * $redis->setnx('key', 'value'); // return FALSE
- *
- */
- public function setnx($key, $value)
- {
- }
-
- /**
- * Remove specified keys.
- *
- * @param int|string|array $key1 An array of keys, or an undefined number of parameters, each a key: key1 key2 key3 ... keyN
- * @param int|string ...$otherKeys
- *
- * @return int Number of keys deleted
- *
- * @link https://redis.io/commands/del
- * @example
- *
- * $redis->set('key1', 'val1');
- * $redis->set('key2', 'val2');
- * $redis->set('key3', 'val3');
- * $redis->set('key4', 'val4');
- *
- * $redis->del('key1', 'key2'); // return 2
- * $redis->del(['key3', 'key4']); // return 2
- *
- */
- public function del($key1, ...$otherKeys)
- {
- }
-
- /**
- * @param string|string[] $key1
- * @param string $key2
- * @param string $key3
- *
- * @return int Number of keys deleted
- */
- #[Deprecated(replacement: "%class%->del(%parametersList%)")]
- public function delete($key1, $key2 = null, $key3 = null)
- {
- }
-
- /**
- * Delete a key asynchronously in another thread. Otherwise it is just as DEL, but non blocking.
- *
- * @see del()
- * @param string|string[] $key1
- * @param string $key2
- * @param string $key3
- *
- * @return int Number of keys unlinked.
- *
- * @link https://redis.io/commands/unlink
- * @example
- *
- * $redis->set('key1', 'val1');
- * $redis->set('key2', 'val2');
- * $redis->set('key3', 'val3');
- * $redis->set('key4', 'val4');
- * $redis->unlink('key1', 'key2'); // return 2
- * $redis->unlink(array('key3', 'key4')); // return 2
- *
- */
- public function unlink($key1, $key2 = null, $key3 = null)
- {
- }
-
- /**
- * Enter and exit transactional mode.
- *
- * @param int $mode Redis::MULTI|Redis::PIPELINE
- * Defaults to Redis::MULTI.
- * A Redis::MULTI block of commands runs as a single transaction;
- * a Redis::PIPELINE block is simply transmitted faster to the server, but without any guarantee of atomicity.
- * discard cancels a transaction.
- *
- * @return static returns the Redis instance and enters multi-mode.
- * Once in multi-mode, all subsequent method calls return the same object until exec() is called.
- *
- * @link https://redis.io/commands/multi
- * @example
- *
- * $ret = $redis->multi()
- * ->set('key1', 'val1')
- * ->get('key1')
- * ->set('key2', 'val2')
- * ->get('key2')
- * ->exec();
- *
- * //$ret == array (
- * // 0 => TRUE,
- * // 1 => 'val1',
- * // 2 => TRUE,
- * // 3 => 'val2');
- *
- */
- public function multi($mode = Redis::MULTI)
- {
- }
-
- /**
- * Returns a Redis instance which can simply transmitted faster to the server.
- *
- * @return Redis returns the Redis instance.
- * Once in pipeline-mode, all subsequent method calls return the same object until exec() is called.
- * Pay attention, that Pipeline is not a transaction, so you can get unexpected
- * results in case of big pipelines and small read/write timeouts.
- *
- * @link https://redis.io/topics/pipelining
- * @example
- *
- * $ret = $this->redis->pipeline()
- * ->ping()
- * ->multi()->set('x', 42)->incr('x')->exec()
- * ->ping()
- * ->multi()->get('x')->del('x')->exec()
- * ->ping()
- * ->exec();
- *
- * //$ret == array (
- * // 0 => '+PONG',
- * // 1 => [TRUE, 43],
- * // 2 => '+PONG',
- * // 3 => [43, 1],
- * // 4 => '+PONG');
- *
- */
- public function pipeline()
- {
- }
-
-
- /**
- * @return void|array
- *
- * @see multi()
- * @link https://redis.io/commands/exec
- */
- public function exec()
- {
- }
-
- /**
- * @see multi()
- * @link https://redis.io/commands/discard
- */
- public function discard()
- {
- }
-
- /**
- * Watches a key for modifications by another client. If the key is modified between WATCH and EXEC,
- * the MULTI/EXEC transaction will fail (return FALSE). unwatch cancels all the watching of all keys by this client.
- * @param string|string[] $key a list of keys
- *
- * @return void
- *
- * @link https://redis.io/commands/watch
- * @example
- *
- * $redis->watch('x');
- * // long code here during the execution of which other clients could well modify `x`
- * $ret = $redis->multi()
- * ->incr('x')
- * ->exec();
- * // $ret = FALSE if x has been modified between the call to WATCH and the call to EXEC.
- *
- */
- public function watch($key)
- {
- }
-
- /**
- * @see watch()
- * @link https://redis.io/commands/unwatch
- */
- public function unwatch()
- {
- }
-
- /**
- * Subscribe to channels.
- *
- * Warning: this function will probably change in the future.
- *
- * @param string[] $channels an array of channels to subscribe
- * @param string|array $callback either a string or an array($instance, 'method_name').
- * The callback function receives 3 parameters: the redis instance, the channel name, and the message.
- *
- * @return mixed|null Any non-null return value in the callback will be returned to the caller.
- *
- * @link https://redis.io/commands/subscribe
- * @example
- *
- * function f($redis, $chan, $msg) {
- * switch($chan) {
- * case 'chan-1':
- * ...
- * break;
- *
- * case 'chan-2':
- * ...
- * break;
- *
- * case 'chan-2':
- * ...
- * break;
- * }
- * }
- *
- * $redis->subscribe(array('chan-1', 'chan-2', 'chan-3'), 'f'); // subscribe to 3 chans
- *
- */
- public function subscribe($channels, $callback)
- {
- }
-
- /**
- * Subscribe to channels by pattern
- *
- * @param array $patterns an array of glob-style patterns to subscribe
- * @param string|array $callback Either a string or an array with an object and method.
- * The callback will get four arguments ($redis, $pattern, $channel, $message)
- * @param mixed Any non-null return value in the callback will be returned to the caller
- *
- * @link https://redis.io/commands/psubscribe
- * @example
- *
- * function psubscribe($redis, $pattern, $chan, $msg) {
- * echo "Pattern: $pattern\n";
- * echo "Channel: $chan\n";
- * echo "Payload: $msg\n";
- * }
- *
- */
- public function psubscribe($patterns, $callback)
- {
- }
-
- /**
- * Publish messages to channels.
- *
- * Warning: this function will probably change in the future.
- *
- * @param string $channel a channel to publish to
- * @param string $message string
- *
- * @return int Number of clients that received the message
- *
- * @link https://redis.io/commands/publish
- * @example $redis->publish('chan-1', 'hello, world!'); // send message.
- */
- public function publish($channel, $message)
- {
- }
-
- /**
- * A command allowing you to get information on the Redis pub/sub system
- *
- * @param string $keyword String, which can be: "channels", "numsub", or "numpat"
- * @param string|array $argument Optional, variant.
- * For the "channels" subcommand, you can pass a string pattern.
- * For "numsub" an array of channel names
- *
- * @return array|int Either an integer or an array.
- * - channels Returns an array where the members are the matching channels.
- * - numsub Returns a key/value array where the keys are channel names and
- * values are their counts.
- * - numpat Integer return containing the number active pattern subscriptions
- *
- * @link https://redis.io/commands/pubsub
- * @example
- *
- * $redis->pubsub('channels'); // All channels
- * $redis->pubsub('channels', '*pattern*'); // Just channels matching your pattern
- * $redis->pubsub('numsub', array('chan1', 'chan2')); // Get subscriber counts for 'chan1' and 'chan2'
- * $redis->pubsub('numpat'); // Get the number of pattern subscribers
- *
- */
- public function pubsub($keyword, $argument)
- {
- }
-
- /**
- * Stop listening for messages posted to the given channels.
- *
- * @param array $channels an array of channels to usubscribe
- *
- * @link https://redis.io/commands/unsubscribe
- */
- public function unsubscribe($channels = null)
- {
- }
-
- /**
- * Stop listening for messages posted to the given channels.
- *
- * @param array $patterns an array of glob-style patterns to unsubscribe
- *
- * @link https://redis.io/commands/punsubscribe
- */
- public function punsubscribe($patterns = null)
- {
- }
-
- /**
- * Verify if the specified key/keys exists
- *
- * This function took a single argument and returned TRUE or FALSE in phpredis versions < 4.0.0.
- *
- * @since >= 4.0 Returned int, if < 4.0 returned bool
- *
- * @param string|string[] $key
- *
- * @return int|bool The number of keys tested that do exist
- *
- * @link https://redis.io/commands/exists
- * @link https://github.com/phpredis/phpredis#exists
- * @example
- *
- * $redis->exists('key'); // 1
- * $redis->exists('NonExistingKey'); // 0
- *
- * $redis->mset(['foo' => 'foo', 'bar' => 'bar', 'baz' => 'baz']);
- * $redis->exists(['foo', 'bar', 'baz]); // 3
- * $redis->exists('foo', 'bar', 'baz'); // 3
- *
- */
- public function exists($key)
- {
- }
-
- /**
- * Increment the number stored at key by one.
- *
- * @param string $key
- *
- * @return int the new value
- *
- * @link https://redis.io/commands/incr
- * @example
- *
- * $redis->incr('key1'); // key1 didn't exists, set to 0 before the increment and now has the value 1
- * $redis->incr('key1'); // 2
- * $redis->incr('key1'); // 3
- * $redis->incr('key1'); // 4
- *
- */
- public function incr($key)
- {
- }
-
- /**
- * Increment the float value of a key by the given amount
- *
- * @param string $key
- * @param float $increment
- *
- * @return float
- *
- * @link https://redis.io/commands/incrbyfloat
- * @example
- *
- * $redis->set('x', 3);
- * $redis->incrByFloat('x', 1.5); // float(4.5)
- * $redis->get('x'); // float(4.5)
- *
- */
- public function incrByFloat($key, $increment)
- {
- }
-
- /**
- * Increment the number stored at key by one.
- * If the second argument is filled, it will be used as the integer value of the increment.
- *
- * @param string $key key
- * @param int $value value that will be added to key (only for incrBy)
- *
- * @return int the new value
- *
- * @link https://redis.io/commands/incrby
- * @example
- *
- * $redis->incr('key1'); // key1 didn't exists, set to 0 before the increment and now has the value 1
- * $redis->incr('key1'); // 2
- * $redis->incr('key1'); // 3
- * $redis->incr('key1'); // 4
- * $redis->incrBy('key1', 10); // 14
- *
- */
- public function incrBy($key, $value)
- {
- }
-
- /**
- * Decrement the number stored at key by one.
- *
- * @param string $key
- *
- * @return int the new value
- *
- * @link https://redis.io/commands/decr
- * @example
- *
- * $redis->decr('key1'); // key1 didn't exists, set to 0 before the increment and now has the value -1
- * $redis->decr('key1'); // -2
- * $redis->decr('key1'); // -3
- *
- */
- public function decr($key)
- {
- }
-
- /**
- * Decrement the number stored at key by one.
- * If the second argument is filled, it will be used as the integer value of the decrement.
- *
- * @param string $key
- * @param int $value that will be subtracted to key (only for decrBy)
- *
- * @return int the new value
- *
- * @link https://redis.io/commands/decrby
- * @example
- *
- * $redis->decr('key1'); // key1 didn't exists, set to 0 before the increment and now has the value -1
- * $redis->decr('key1'); // -2
- * $redis->decr('key1'); // -3
- * $redis->decrBy('key1', 10); // -13
- *
- */
- public function decrBy($key, $value)
- {
- }
-
- /**
- * Adds the string values to the head (left) of the list.
- * Creates the list if the key didn't exist.
- * If the key exists and is not a list, FALSE is returned.
- *
- * @param string $key
- * @param string|mixed ...$value1 Variadic list of values to push in key, if dont used serialized, used string
- *
- * @return int|false The new length of the list in case of success, FALSE in case of Failure
- *
- * @link https://redis.io/commands/lpush
- * @example
- *
- * $redis->lPush('l', 'v1', 'v2', 'v3', 'v4') // int(4)
- * var_dump( $redis->lRange('l', 0, -1) );
- * // Output:
- * // array(4) {
- * // [0]=> string(2) "v4"
- * // [1]=> string(2) "v3"
- * // [2]=> string(2) "v2"
- * // [3]=> string(2) "v1"
- * // }
- *
- */
- public function lPush($key, ...$value1)
- {
- }
-
- /**
- * Adds the string values to the tail (right) of the list.
- * Creates the list if the key didn't exist.
- * If the key exists and is not a list, FALSE is returned.
- *
- * @param string $key
- * @param string|mixed ...$value1 Variadic list of values to push in key, if dont used serialized, used string
- *
- * @return int|false The new length of the list in case of success, FALSE in case of Failure
- *
- * @link https://redis.io/commands/rpush
- * @example
- *
- * $redis->rPush('l', 'v1', 'v2', 'v3', 'v4'); // int(4)
- * var_dump( $redis->lRange('l', 0, -1) );
- * // Output:
- * // array(4) {
- * // [0]=> string(2) "v1"
- * // [1]=> string(2) "v2"
- * // [2]=> string(2) "v3"
- * // [3]=> string(2) "v4"
- * // }
- *
- */
- public function rPush($key, ...$value1)
- {
- }
-
- /**
- * Adds the string value to the head (left) of the list if the list exists.
- *
- * @param string $key
- * @param string|mixed $value String, value to push in key
- *
- * @return int|false The new length of the list in case of success, FALSE in case of Failure.
- *
- * @link https://redis.io/commands/lpushx
- * @example
- *
- * $redis->del('key1');
- * $redis->lPushx('key1', 'A'); // returns 0
- * $redis->lPush('key1', 'A'); // returns 1
- * $redis->lPushx('key1', 'B'); // returns 2
- * $redis->lPushx('key1', 'C'); // returns 3
- * // key1 now points to the following list: [ 'A', 'B', 'C' ]
- *
- */
- public function lPushx($key, $value)
- {
- }
-
- /**
- * Adds the string value to the tail (right) of the list if the ist exists. FALSE in case of Failure.
- *
- * @param string $key
- * @param string|mixed $value String, value to push in key
- *
- * @return int|false The new length of the list in case of success, FALSE in case of Failure.
- *
- * @link https://redis.io/commands/rpushx
- * @example
- *
- * $redis->del('key1');
- * $redis->rPushx('key1', 'A'); // returns 0
- * $redis->rPush('key1', 'A'); // returns 1
- * $redis->rPushx('key1', 'B'); // returns 2
- * $redis->rPushx('key1', 'C'); // returns 3
- * // key1 now points to the following list: [ 'A', 'B', 'C' ]
- *
- */
- public function rPushx($key, $value)
- {
- }
-
- /**
- * Returns and removes the first element of the list.
- *
- * @param string $key
- *
- * @return mixed|bool if command executed successfully BOOL FALSE in case of failure (empty list)
- *
- * @link https://redis.io/commands/lpop
- * @example
- *
- * $redis->rPush('key1', 'A');
- * $redis->rPush('key1', 'B');
- * $redis->rPush('key1', 'C'); // key1 => [ 'A', 'B', 'C' ]
- * $redis->lPop('key1'); // key1 => [ 'B', 'C' ]
- *
- */
- public function lPop($key)
- {
- }
-
- /**
- * Returns and removes the last element of the list.
- *
- * @param string $key
- *
- * @return mixed|bool if command executed successfully BOOL FALSE in case of failure (empty list)
- *
- * @link https://redis.io/commands/rpop
- * @example
- *
- * $redis->rPush('key1', 'A');
- * $redis->rPush('key1', 'B');
- * $redis->rPush('key1', 'C'); // key1 => [ 'A', 'B', 'C' ]
- * $redis->rPop('key1'); // key1 => [ 'A', 'B' ]
- *
- */
- public function rPop($key)
- {
- }
-
- /**
- * Is a blocking lPop primitive. If at least one of the lists contains at least one element,
- * the element will be popped from the head of the list and returned to the caller.
- * Il all the list identified by the keys passed in arguments are empty, blPop will block
- * during the specified timeout until an element is pushed to one of those lists. This element will be popped.
- *
- * @param string|string[] $keys String array containing the keys of the lists OR variadic list of strings
- * @param int $timeout Timeout is always the required final parameter
- *
- * @return array ['listName', 'element']
- *
- * @link https://redis.io/commands/blpop
- * @example
- *
- * // Non blocking feature
- * $redis->lPush('key1', 'A');
- * $redis->del('key2');
- *
- * $redis->blPop('key1', 'key2', 10); // array('key1', 'A')
- * // OR
- * $redis->blPop(['key1', 'key2'], 10); // array('key1', 'A')
- *
- * $redis->brPop('key1', 'key2', 10); // array('key1', 'A')
- * // OR
- * $redis->brPop(['key1', 'key2'], 10); // array('key1', 'A')
- *
- * // Blocking feature
- *
- * // process 1
- * $redis->del('key1');
- * $redis->blPop('key1', 10);
- * // blocking for 10 seconds
- *
- * // process 2
- * $redis->lPush('key1', 'A');
- *
- * // process 1
- * // array('key1', 'A') is returned
- *
- */
- public function blPop($keys, $timeout)
- {
- }
-
- /**
- * Is a blocking rPop primitive. If at least one of the lists contains at least one element,
- * the element will be popped from the head of the list and returned to the caller.
- * Il all the list identified by the keys passed in arguments are empty, brPop will
- * block during the specified timeout until an element is pushed to one of those lists. T
- * his element will be popped.
- *
- * @param string|string[] $keys String array containing the keys of the lists OR variadic list of strings
- * @param int $timeout Timeout is always the required final parameter
- *
- * @return array ['listName', 'element']
- *
- * @link https://redis.io/commands/brpop
- * @example
- *
- * // Non blocking feature
- * $redis->lPush('key1', 'A');
- * $redis->del('key2');
- *
- * $redis->blPop('key1', 'key2', 10); // array('key1', 'A')
- * // OR
- * $redis->blPop(array('key1', 'key2'), 10); // array('key1', 'A')
- *
- * $redis->brPop('key1', 'key2', 10); // array('key1', 'A')
- * // OR
- * $redis->brPop(array('key1', 'key2'), 10); // array('key1', 'A')
- *
- * // Blocking feature
- *
- * // process 1
- * $redis->del('key1');
- * $redis->blPop('key1', 10);
- * // blocking for 10 seconds
- *
- * // process 2
- * $redis->lPush('key1', 'A');
- *
- * // process 1
- * // array('key1', 'A') is returned
- *
- */
- public function brPop(array $keys, $timeout)
- {
- }
-
- /**
- * Returns the size of a list identified by Key. If the list didn't exist or is empty,
- * the command returns 0. If the data type identified by Key is not a list, the command return FALSE.
- *
- * @param string $key
- *
- * @return int|bool The size of the list identified by Key exists.
- * bool FALSE if the data type identified by Key is not list
- *
- * @link https://redis.io/commands/llen
- * @example
- *
- * $redis->rPush('key1', 'A');
- * $redis->rPush('key1', 'B');
- * $redis->rPush('key1', 'C'); // key1 => [ 'A', 'B', 'C' ]
- * $redis->lLen('key1'); // 3
- * $redis->rPop('key1');
- * $redis->lLen('key1'); // 2
- *
- */
- public function lLen($key)
- {
- }
-
- /**
- * @link https://redis.io/commands/llen
- *
- * @param string $key
- *
- * @return int The size of the list identified by Key exists
- */
- #[Deprecated(replacement: '%class%->lLen(%parametersList%)')]
- public function lSize($key)
- {
- }
-
- /**
- * Return the specified element of the list stored at the specified key.
- * 0 the first element, 1 the second ... -1 the last element, -2 the penultimate ...
- * Return FALSE in case of a bad index or a key that doesn't point to a list.
- *
- * @param string $key
- * @param int $index
- *
- * @return mixed|bool the element at this index
- *
- * Bool FALSE if the key identifies a non-string data type, or no value corresponds to this index in the list Key.
- *
- * @link https://redis.io/commands/lindex
- * @example
- *
- * $redis->rPush('key1', 'A');
- * $redis->rPush('key1', 'B');
- * $redis->rPush('key1', 'C'); // key1 => [ 'A', 'B', 'C' ]
- * $redis->lIndex('key1', 0); // 'A'
- * $redis->lIndex('key1', -1); // 'C'
- * $redis->lIndex('key1', 10); // `FALSE`
- *
- */
- public function lIndex($key, $index)
- {
- }
-
- /**
- * @link https://redis.io/commands/lindex
- *
- * @param string $key
- * @param int $index
- * @return mixed|bool the element at this index
- */
- #[Deprecated(replacement: '%class%->lIndex(%parametersList%)')]
- public function lGet($key, $index)
- {
- }
-
- /**
- * Set the list at index with the new value.
- *
- * @param string $key
- * @param int $index
- * @param string $value
- *
- * @return bool TRUE if the new value is setted.
- * FALSE if the index is out of range, or data type identified by key is not a list.
- *
- * @link https://redis.io/commands/lset
- * @example
- *
- * $redis->rPush('key1', 'A');
- * $redis->rPush('key1', 'B');
- * $redis->rPush('key1', 'C'); // key1 => [ 'A', 'B', 'C' ]
- * $redis->lIndex('key1', 0); // 'A'
- * $redis->lSet('key1', 0, 'X');
- * $redis->lIndex('key1', 0); // 'X'
- *
- */
- public function lSet($key, $index, $value)
- {
- }
-
- /**
- * Returns the specified elements of the list stored at the specified key in
- * the range [start, end]. start and stop are interpretated as indices: 0 the first element,
- * 1 the second ... -1 the last element, -2 the penultimate ...
- *
- * @param string $key
- * @param int $start
- * @param int $end
- *
- * @return array containing the values in specified range.
- *
- * @link https://redis.io/commands/lrange
- * @example
- *
- * $redis->rPush('key1', 'A');
- * $redis->rPush('key1', 'B');
- * $redis->rPush('key1', 'C');
- * $redis->lRange('key1', 0, -1); // array('A', 'B', 'C')
- *
- */
- public function lRange($key, $start, $end)
- {
- }
-
- /**
- * @link https://redis.io/commands/lrange
- *
- * @param string $key
- * @param int $start
- * @param int $end
- * @return array
- */
- #[Deprecated(replacement: '%class%->lRange(%parametersList%)')]
- public function lGetRange($key, $start, $end)
- {
- }
-
- /**
- * Trims an existing list so that it will contain only a specified range of elements.
- *
- * @param string $key
- * @param int $start
- * @param int $stop
- *
- * @return array|false Bool return FALSE if the key identify a non-list value
- *
- * @link https://redis.io/commands/ltrim
- * @example
- *
- * $redis->rPush('key1', 'A');
- * $redis->rPush('key1', 'B');
- * $redis->rPush('key1', 'C');
- * $redis->lRange('key1', 0, -1); // array('A', 'B', 'C')
- * $redis->lTrim('key1', 0, 1);
- * $redis->lRange('key1', 0, -1); // array('A', 'B')
- *
- */
- public function lTrim($key, $start, $stop)
- {
- }
-
- /**
- * @link https://redis.io/commands/ltrim
- *
- * @param string $key
- * @param int $start
- * @param int $stop
- */
- #[Deprecated(replacement: '%class%->lTrim(%parametersList%)')]
- public function listTrim($key, $start, $stop)
- {
- }
-
- /**
- * Removes the first count occurrences of the value element from the list.
- * If count is zero, all the matching elements are removed. If count is negative,
- * elements are removed from tail to head.
- *
- * @param string $key
- * @param string $value
- * @param int $count
- *
- * @return int|bool the number of elements to remove
- * bool FALSE if the value identified by key is not a list.
- *
- * @link https://redis.io/commands/lrem
- * @example
- *
- * $redis->lPush('key1', 'A');
- * $redis->lPush('key1', 'B');
- * $redis->lPush('key1', 'C');
- * $redis->lPush('key1', 'A');
- * $redis->lPush('key1', 'A');
- *
- * $redis->lRange('key1', 0, -1); // array('A', 'A', 'C', 'B', 'A')
- * $redis->lRem('key1', 'A', 2); // 2
- * $redis->lRange('key1', 0, -1); // array('C', 'B', 'A')
- *
- */
- public function lRem($key, $value, $count)
- {
- }
-
- /**
- * @link https://redis.io/commands/lremove
- *
- * @param string $key
- * @param string $value
- * @param int $count
- */
- #[Deprecated(replacement: '%class%->lRem(%parametersList%)')]
- public function lRemove($key, $value, $count)
- {
- }
-
- /**
- * Insert value in the list before or after the pivot value. the parameter options
- * specify the position of the insert (before or after). If the list didn't exists,
- * or the pivot didn't exists, the value is not inserted.
- *
- * @param string $key
- * @param int $position Redis::BEFORE | Redis::AFTER
- * @param string $pivot
- * @param string|mixed $value
- *
- * @return int The number of the elements in the list, -1 if the pivot didn't exists.
- *
- * @link https://redis.io/commands/linsert
- * @example
- *
- * $redis->del('key1');
- * $redis->lInsert('key1', Redis::AFTER, 'A', 'X'); // 0
- *
- * $redis->lPush('key1', 'A');
- * $redis->lPush('key1', 'B');
- * $redis->lPush('key1', 'C');
- *
- * $redis->lInsert('key1', Redis::BEFORE, 'C', 'X'); // 4
- * $redis->lRange('key1', 0, -1); // array('A', 'B', 'X', 'C')
- *
- * $redis->lInsert('key1', Redis::AFTER, 'C', 'Y'); // 5
- * $redis->lRange('key1', 0, -1); // array('A', 'B', 'X', 'C', 'Y')
- *
- * $redis->lInsert('key1', Redis::AFTER, 'W', 'value'); // -1
- *
- */
- public function lInsert($key, $position, $pivot, $value)
- {
- }
-
- /**
- * Adds a values to the set value stored at key.
- *
- * @param string $key Required key
- * @param string|mixed ...$value1 Variadic list of values
- *
- * @return int|bool The number of elements added to the set.
- * If this value is already in the set, FALSE is returned
- *
- * @link https://redis.io/commands/sadd
- * @example
- *
- * $redis->sAdd('k', 'v1'); // int(1)
- * $redis->sAdd('k', 'v1', 'v2', 'v3'); // int(2)
- *
- */
- public function sAdd($key, ...$value1)
- {
- }
-
- /**
- * Removes the specified members from the set value stored at key.
- *
- * @param string $key
- * @param string|mixed ...$member1 Variadic list of members
- *
- * @return int The number of elements removed from the set
- *
- * @link https://redis.io/commands/srem
- * @example
- *
- * var_dump( $redis->sAdd('k', 'v1', 'v2', 'v3') ); // int(3)
- * var_dump( $redis->sRem('k', 'v2', 'v3') ); // int(2)
- * var_dump( $redis->sMembers('k') );
- * //// Output:
- * // array(1) {
- * // [0]=> string(2) "v1"
- * // }
- *
- */
- public function sRem($key, ...$member1)
- {
- }
-
- /**
- * @link https://redis.io/commands/srem
- *
- * @param string $key
- * @param string|mixed ...$member1
- */
- #[Deprecated(replacement: '%class%->sRem(%parametersList%)')]
- public function sRemove($key, ...$member1)
- {
- }
-
- /**
- * Moves the specified member from the set at srcKey to the set at dstKey.
- *
- * @param string $srcKey
- * @param string $dstKey
- * @param string|mixed $member
- *
- * @return bool If the operation is successful, return TRUE.
- * If the srcKey and/or dstKey didn't exist, and/or the member didn't exist in srcKey, FALSE is returned.
- *
- * @link https://redis.io/commands/smove
- * @example
- *
- * $redis->sAdd('key1' , 'set11');
- * $redis->sAdd('key1' , 'set12');
- * $redis->sAdd('key1' , 'set13'); // 'key1' => {'set11', 'set12', 'set13'}
- * $redis->sAdd('key2' , 'set21');
- * $redis->sAdd('key2' , 'set22'); // 'key2' => {'set21', 'set22'}
- * $redis->sMove('key1', 'key2', 'set13'); // 'key1' => {'set11', 'set12'}
- * // 'key2' => {'set21', 'set22', 'set13'}
- *
- */
- public function sMove($srcKey, $dstKey, $member)
- {
- }
-
- /**
- * Checks if value is a member of the set stored at the key key.
- *
- * @param string $key
- * @param string|mixed $value
- *
- * @return bool TRUE if value is a member of the set at key key, FALSE otherwise
- *
- * @link https://redis.io/commands/sismember
- * @example
- *
- * $redis->sAdd('key1' , 'set1');
- * $redis->sAdd('key1' , 'set2');
- * $redis->sAdd('key1' , 'set3'); // 'key1' => {'set1', 'set2', 'set3'}
- *
- * $redis->sIsMember('key1', 'set1'); // TRUE
- * $redis->sIsMember('key1', 'setX'); // FALSE
- *
- */
- public function sIsMember($key, $value)
- {
- }
-
- /**
- * @link https://redis.io/commands/sismember
- *
- * @param string $key
- * @param string|mixed $value
- */
- #[Deprecated(replacement: '%class%->sIsMember(%parametersList%)')]
- public function sContains($key, $value)
- {
- }
-
- /**
- * Returns the cardinality of the set identified by key.
- *
- * @param string $key
- *
- * @return int the cardinality of the set identified by key, 0 if the set doesn't exist.
- *
- * @link https://redis.io/commands/scard
- * @example
- *
- * $redis->sAdd('key1' , 'set1');
- * $redis->sAdd('key1' , 'set2');
- * $redis->sAdd('key1' , 'set3'); // 'key1' => {'set1', 'set2', 'set3'}
- * $redis->sCard('key1'); // 3
- * $redis->sCard('keyX'); // 0
- *
- */
- public function sCard($key)
- {
- }
-
- /**
- * Removes and returns a random element from the set value at Key.
- *
- * @param string $key
- * @param int $count [optional]
- *
- * @return string|mixed|array|bool "popped" values
- * bool FALSE if set identified by key is empty or doesn't exist.
- *
- * @link https://redis.io/commands/spop
- * @example
- *
- * $redis->sAdd('key1' , 'set1');
- * $redis->sAdd('key1' , 'set2');
- * $redis->sAdd('key1' , 'set3'); // 'key1' => {'set3', 'set1', 'set2'}
- * $redis->sPop('key1'); // 'set1', 'key1' => {'set3', 'set2'}
- * $redis->sPop('key1'); // 'set3', 'key1' => {'set2'}
- *
- * // With count
- * $redis->sAdd('key2', 'set1', 'set2', 'set3');
- * var_dump( $redis->sPop('key2', 3) ); // Will return all members but in no particular order
- *
- * // array(3) {
- * // [0]=> string(4) "set2"
- * // [1]=> string(4) "set3"
- * // [2]=> string(4) "set1"
- * // }
- *
- */
- public function sPop($key, $count = 1)
- {
- }
-
- /**
- * Returns a random element(s) from the set value at Key, without removing it.
- *
- * @param string $key
- * @param int $count [optional]
- *
- * @return string|mixed|array|bool value(s) from the set
- * bool FALSE if set identified by key is empty or doesn't exist and count argument isn't passed.
- *
- * @link https://redis.io/commands/srandmember
- * @example
- *
- * $redis->sAdd('key1' , 'one');
- * $redis->sAdd('key1' , 'two');
- * $redis->sAdd('key1' , 'three'); // 'key1' => {'one', 'two', 'three'}
- *
- * var_dump( $redis->sRandMember('key1') ); // 'key1' => {'one', 'two', 'three'}
- *
- * // string(5) "three"
- *
- * var_dump( $redis->sRandMember('key1', 2) ); // 'key1' => {'one', 'two', 'three'}
- *
- * // array(2) {
- * // [0]=> string(2) "one"
- * // [1]=> string(5) "three"
- * // }
- *
- */
- public function sRandMember($key, $count = 1)
- {
- }
-
- /**
- * Returns the members of a set resulting from the intersection of all the sets
- * held at the specified keys. If just a single key is specified, then this command
- * produces the members of this set. If one of the keys is missing, FALSE is returned.
- *
- * @param string $key1 keys identifying the different sets on which we will apply the intersection.
- * @param string ...$otherKeys variadic list of keys
- *
- * @return array|false contain the result of the intersection between those keys
- * If the intersection between the different sets is empty, the return value will be empty array.
- *
- * @link https://redis.io/commands/sinter
- * @example
- *
- * $redis->sAdd('key1', 'val1');
- * $redis->sAdd('key1', 'val2');
- * $redis->sAdd('key1', 'val3');
- * $redis->sAdd('key1', 'val4');
- *
- * $redis->sAdd('key2', 'val3');
- * $redis->sAdd('key2', 'val4');
- *
- * $redis->sAdd('key3', 'val3');
- * $redis->sAdd('key3', 'val4');
- *
- * var_dump($redis->sInter('key1', 'key2', 'key3'));
- *
- * //array(2) {
- * // [0]=>
- * // string(4) "val4"
- * // [1]=>
- * // string(4) "val3"
- * //}
- *
- */
- public function sInter($key1, ...$otherKeys)
- {
- }
-
- /**
- * Performs a sInter command and stores the result in a new set.
- *
- * @param string $dstKey the key to store the diff into.
- * @param string $key1 keys identifying the different sets on which we will apply the intersection.
- * @param string ...$otherKeys variadic list of keys
- *
- * @return int|false The cardinality of the resulting set, or FALSE in case of a missing key
- *
- * @link https://redis.io/commands/sinterstore
- * @example
- *
- * $redis->sAdd('key1', 'val1');
- * $redis->sAdd('key1', 'val2');
- * $redis->sAdd('key1', 'val3');
- * $redis->sAdd('key1', 'val4');
- *
- * $redis->sAdd('key2', 'val3');
- * $redis->sAdd('key2', 'val4');
- *
- * $redis->sAdd('key3', 'val3');
- * $redis->sAdd('key3', 'val4');
- *
- * var_dump($redis->sInterStore('output', 'key1', 'key2', 'key3'));
- * var_dump($redis->sMembers('output'));
- *
- * //int(2)
- * //
- * //array(2) {
- * // [0]=>
- * // string(4) "val4"
- * // [1]=>
- * // string(4) "val3"
- * //}
- *
- */
- public function sInterStore($dstKey, $key1, ...$otherKeys)
- {
- }
-
- /**
- * Performs the union between N sets and returns it.
- *
- * @param string $key1 first key for union
- * @param string ...$otherKeys variadic list of keys corresponding to sets in redis
- *
- * @return array string[] The union of all these sets
- *
- * @link https://redis.io/commands/sunionstore
- * @example
- *
- * $redis->sAdd('s0', '1');
- * $redis->sAdd('s0', '2');
- * $redis->sAdd('s1', '3');
- * $redis->sAdd('s1', '1');
- * $redis->sAdd('s2', '3');
- * $redis->sAdd('s2', '4');
- *
- * var_dump($redis->sUnion('s0', 's1', 's2'));
- *
- * array(4) {
- * // [0]=>
- * // string(1) "3"
- * // [1]=>
- * // string(1) "4"
- * // [2]=>
- * // string(1) "1"
- * // [3]=>
- * // string(1) "2"
- * //}
- *
- */
- public function sUnion($key1, ...$otherKeys)
- {
- }
-
- /**
- * Performs the same action as sUnion, but stores the result in the first key
- *
- * @param string $dstKey the key to store the diff into.
- * @param string $key1 first key for union
- * @param string ...$otherKeys variadic list of keys corresponding to sets in redis
- *
- * @return int Any number of keys corresponding to sets in redis
- *
- * @link https://redis.io/commands/sunionstore
- * @example
- *
- * $redis->del('s0', 's1', 's2');
- *
- * $redis->sAdd('s0', '1');
- * $redis->sAdd('s0', '2');
- * $redis->sAdd('s1', '3');
- * $redis->sAdd('s1', '1');
- * $redis->sAdd('s2', '3');
- * $redis->sAdd('s2', '4');
- *
- * var_dump($redis->sUnionStore('dst', 's0', 's1', 's2'));
- * var_dump($redis->sMembers('dst'));
- *
- * //int(4)
- * //array(4) {
- * // [0]=>
- * // string(1) "3"
- * // [1]=>
- * // string(1) "4"
- * // [2]=>
- * // string(1) "1"
- * // [3]=>
- * // string(1) "2"
- * //}
- *
- */
- public function sUnionStore($dstKey, $key1, ...$otherKeys)
- {
- }
-
- /**
- * Performs the difference between N sets and returns it.
- *
- * @param string $key1 first key for diff
- * @param string ...$otherKeys variadic list of keys corresponding to sets in redis
- *
- * @return array string[] The difference of the first set will all the others
- *
- * @link https://redis.io/commands/sdiff
- * @example
- *
- * $redis->del('s0', 's1', 's2');
- *
- * $redis->sAdd('s0', '1');
- * $redis->sAdd('s0', '2');
- * $redis->sAdd('s0', '3');
- * $redis->sAdd('s0', '4');
- *
- * $redis->sAdd('s1', '1');
- * $redis->sAdd('s2', '3');
- *
- * var_dump($redis->sDiff('s0', 's1', 's2'));
- *
- * //array(2) {
- * // [0]=>
- * // string(1) "4"
- * // [1]=>
- * // string(1) "2"
- * //}
- *
- */
- public function sDiff($key1, ...$otherKeys)
- {
- }
-
- /**
- * Performs the same action as sDiff, but stores the result in the first key
- *
- * @param string $dstKey the key to store the diff into.
- * @param string $key1 first key for diff
- * @param string ...$otherKeys variadic list of keys corresponding to sets in redis
- *
- * @return int|false The cardinality of the resulting set, or FALSE in case of a missing key
- *
- * @link https://redis.io/commands/sdiffstore
- * @example
- *
- * $redis->del('s0', 's1', 's2');
- *
- * $redis->sAdd('s0', '1');
- * $redis->sAdd('s0', '2');
- * $redis->sAdd('s0', '3');
- * $redis->sAdd('s0', '4');
- *
- * $redis->sAdd('s1', '1');
- * $redis->sAdd('s2', '3');
- *
- * var_dump($redis->sDiffStore('dst', 's0', 's1', 's2'));
- * var_dump($redis->sMembers('dst'));
- *
- * //int(2)
- * //array(2) {
- * // [0]=>
- * // string(1) "4"
- * // [1]=>
- * // string(1) "2"
- * //}
- *
- */
- public function sDiffStore($dstKey, $key1, ...$otherKeys)
- {
- }
-
- /**
- * Returns the contents of a set.
- *
- * @param string $key
- *
- * @return array An array of elements, the contents of the set
- *
- * @link https://redis.io/commands/smembers
- * @example
- *
- * $redis->del('s');
- * $redis->sAdd('s', 'a');
- * $redis->sAdd('s', 'b');
- * $redis->sAdd('s', 'a');
- * $redis->sAdd('s', 'c');
- * var_dump($redis->sMembers('s'));
- *
- * //array(3) {
- * // [0]=>
- * // string(1) "c"
- * // [1]=>
- * // string(1) "a"
- * // [2]=>
- * // string(1) "b"
- * //}
- * // The order is random and corresponds to redis' own internal representation of the set structure.
- *
- */
- public function sMembers($key)
- {
- }
-
- /**
- * @link https://redis.io/commands/smembers
- *
- * @param string $key
- * @return array An array of elements, the contents of the set
- */
- #[Deprecated(replacement: '%class%->sMembers(%parametersList%)')]
- public function sGetMembers($key)
- {
- }
-
- /**
- * Scan a set for members
- *
- * @param string $key The set to search.
- * @param int &$iterator LONG (reference) to the iterator as we go.
- * @param string $pattern String, optional pattern to match against.
- * @param int $count How many members to return at a time (Redis might return a different amount)
- *
- * @return array|false PHPRedis will return an array of keys or FALSE when we're done iterating
- *
- * @link https://redis.io/commands/sscan
- * @example
- *
- * $iterator = null;
- * while ($members = $redis->sScan('set', $iterator)) {
- * foreach ($members as $member) {
- * echo $member . PHP_EOL;
- * }
- * }
- *
- */
- public function sScan($key, &$iterator, $pattern = null, $count = 0)
- {
- }
-
- /**
- * Sets a value and returns the previous entry at that key.
- *
- * @param string $key
- * @param string|mixed $value
- *
- * @return string|mixed A string (mixed, if used serializer), the previous value located at this key
- *
- * @link https://redis.io/commands/getset
- * @example
- *
- * $redis->set('x', '42');
- * $exValue = $redis->getSet('x', 'lol'); // return '42', replaces x by 'lol'
- * $newValue = $redis->get('x')' // return 'lol'
- *
- */
- public function getSet($key, $value)
- {
- }
-
- /**
- * Returns a random key
- *
- * @return string an existing key in redis
- *
- * @link https://redis.io/commands/randomkey
- * @example
- * - * $key = $redis->randomKey(); - * $surprise = $redis->get($key); // who knows what's in there. - *- */ - public function randomKey() - { - } - - /** - * Switches to a given database - * - * @param int $dbIndex - * - * @return bool TRUE in case of success, FALSE in case of failure - * - * @link https://redis.io/commands/select - * @example - *
- * $redis->select(0); // switch to DB 0
- * $redis->set('x', '42'); // write 42 to x
- * $redis->move('x', 1); // move to DB 1
- * $redis->select(1); // switch to DB 1
- * $redis->get('x'); // will return 42
- *
- */
- public function select($dbIndex)
- {
- }
-
- /**
- * Moves a key to a different database.
- *
- * @param string $key
- * @param int $dbIndex
- *
- * @return bool TRUE in case of success, FALSE in case of failure
- *
- * @link https://redis.io/commands/move
- * @example
- *
- * $redis->select(0); // switch to DB 0
- * $redis->set('x', '42'); // write 42 to x
- * $redis->move('x', 1); // move to DB 1
- * $redis->select(1); // switch to DB 1
- * $redis->get('x'); // will return 42
- *
- */
- public function move($key, $dbIndex)
- {
- }
-
- /**
- * Renames a key
- *
- * @param string $srcKey
- * @param string $dstKey
- *
- * @return bool TRUE in case of success, FALSE in case of failure
- *
- * @link https://redis.io/commands/rename
- * @example
- *
- * $redis->set('x', '42');
- * $redis->rename('x', 'y');
- * $redis->get('y'); // → 42
- * $redis->get('x'); // → `FALSE`
- *
- */
- public function rename($srcKey, $dstKey)
- {
- }
-
- /**
- * @link https://redis.io/commands/rename
- *
- * @param string $srcKey
- * @param string $dstKey
- */
- #[Deprecated(replacement: '%class%->rename(%parametersList%)')]
- public function renameKey($srcKey, $dstKey)
- {
- }
-
- /**
- * Renames a key
- *
- * Same as rename, but will not replace a key if the destination already exists.
- * This is the same behaviour as setNx.
- *
- * @param string $srcKey
- * @param string $dstKey
- *
- * @return bool TRUE in case of success, FALSE in case of failure
- *
- * @link https://redis.io/commands/renamenx
- * @example
- *
- * $redis->set('x', '42');
- * $redis->rename('x', 'y');
- * $redis->get('y'); // → 42
- * $redis->get('x'); // → `FALSE`
- *
- */
- public function renameNx($srcKey, $dstKey)
- {
- }
-
- /**
- * Sets an expiration date (a timeout) on an item
- *
- * @param string $key The key that will disappear
- * @param int $ttl The key's remaining Time To Live, in seconds
- *
- * @return bool TRUE in case of success, FALSE in case of failure
- *
- * @link https://redis.io/commands/expire
- * @example
- *
- * $redis->set('x', '42');
- * $redis->expire('x', 3); // x will disappear in 3 seconds.
- * sleep(5); // wait 5 seconds
- * $redis->get('x'); // will return `FALSE`, as 'x' has expired.
- *
- */
- public function expire($key, $ttl)
- {
- }
-
- /**
- * Sets an expiration date (a timeout in milliseconds) on an item
- *
- * @param string $key The key that will disappear.
- * @param int $ttl The key's remaining Time To Live, in milliseconds
- *
- * @return bool TRUE in case of success, FALSE in case of failure
- *
- * @link https://redis.io/commands/pexpire
- * @example
- *
- * $redis->set('x', '42');
- * $redis->pExpire('x', 11500); // x will disappear in 11500 milliseconds.
- * $redis->ttl('x'); // 12
- * $redis->pttl('x'); // 11500
- *
- */
- public function pExpire($key, $ttl)
- {
- }
-
- /**
- * @link https://redis.io/commands/expire
- *
- * @param string $key
- * @param int $ttl
- * @return bool
- */
- #[Deprecated(replacement: '%class%->expire(%parametersList%)')]
- public function setTimeout($key, $ttl)
- {
- }
-
- /**
- * Sets an expiration date (a timestamp) on an item.
- *
- * @param string $key The key that will disappear.
- * @param int $timestamp Unix timestamp. The key's date of death, in seconds from Epoch time.
- *
- * @return bool TRUE in case of success, FALSE in case of failure
- *
- * @link https://redis.io/commands/expireat
- * @example
- *
- * $redis->set('x', '42');
- * $now = time(NULL); // current timestamp
- * $redis->expireAt('x', $now + 3); // x will disappear in 3 seconds.
- * sleep(5); // wait 5 seconds
- * $redis->get('x'); // will return `FALSE`, as 'x' has expired.
- *
- */
- public function expireAt($key, $timestamp)
- {
- }
-
- /**
- * Sets an expiration date (a timestamp) on an item. Requires a timestamp in milliseconds
- *
- * @param string $key The key that will disappear
- * @param int $timestamp Unix timestamp. The key's date of death, in seconds from Epoch time
- *
- * @return bool TRUE in case of success, FALSE in case of failure
- *
- * @link https://redis.io/commands/pexpireat
- * @example
- *
- * $redis->set('x', '42');
- * $redis->pExpireAt('x', 1555555555005);
- * echo $redis->ttl('x'); // 218270121
- * echo $redis->pttl('x'); // 218270120575
- *
- */
- public function pExpireAt($key, $timestamp)
- {
- }
-
- /**
- * Returns the keys that match a certain pattern.
- *
- * @param string $pattern pattern, using '*' as a wildcard
- *
- * @return string[]|false The keys that match a certain pattern.
- *
- * @link https://redis.io/commands/keys
- * @example
- *
- * $allKeys = $redis->keys('*'); // all keys will match this.
- * $keyWithUserPrefix = $redis->keys('user*');
- *
- */
- public function keys($pattern)
- {
- }
-
- /**
- * @param string $pattern
- * @link https://redis.io/commands/keys
- */
- #[Deprecated(replacement: '%class%->keys(%parametersList%)')]
- public function getKeys($pattern)
- {
- }
-
- /**
- * Returns the current database's size
- *
- * @return int DB size, in number of keys
- *
- * @link https://redis.io/commands/dbsize
- * @example
- * - * $count = $redis->dbSize(); - * echo "Redis has $count keys\n"; - *- */ - public function dbSize() - { - } - - /** - * Authenticate the connection using a password. - * Warning: The password is sent in plain-text over the network. - * - * @param string|string[] $password - * - * @return bool TRUE if the connection is authenticated, FALSE otherwise - * - * @link https://redis.io/commands/auth - * @example $redis->auth('foobared'); - */ - public function auth($password) - { - } - - /** - * Starts the background rewrite of AOF (Append-Only File) - * - * @return bool TRUE in case of success, FALSE in case of failure - * - * @link https://redis.io/commands/bgrewriteaof - * @example $redis->bgrewriteaof(); - */ - public function bgrewriteaof() - { - } - - /** - * Changes the slave status - * Either host and port, or no parameter to stop being a slave. - * - * @param string $host [optional] - * @param int $port [optional] - * - * @return bool TRUE in case of success, FALSE in case of failure - * - * @link https://redis.io/commands/slaveof - * @example - *
- * $redis->slaveof('10.0.1.7', 6379);
- * // ...
- * $redis->slaveof();
- *
- */
- public function slaveof($host = '127.0.0.1', $port = 6379)
- {
- }
-
- /**
- * Access the Redis slowLog
- *
- * @param string $operation This can be either GET, LEN, or RESET
- * @param int|null $length If executing a SLOWLOG GET command, you can pass an optional length.
- *
- * @return mixed The return value of SLOWLOG will depend on which operation was performed.
- * - SLOWLOG GET: Array of slowLog entries, as provided by Redis
- * - SLOGLOG LEN: Integer, the length of the slowLog
- * - SLOWLOG RESET: Boolean, depending on success
- *
- * @example
- *
- * // Get ten slowLog entries
- * $redis->slowLog('get', 10);
- * // Get the default number of slowLog entries
- *
- * $redis->slowLog('get');
- * // Reset our slowLog
- * $redis->slowLog('reset');
- *
- * // Retrieve slowLog length
- * $redis->slowLog('len');
- *
- *
- * @link https://redis.io/commands/slowlog
- */
- public function slowLog(string $operation, int $length = null)
- {
- }
-
-
- /**
- * Describes the object pointed to by a key.
- * The information to retrieve (string) and the key (string).
- * Info can be one of the following:
- * - "encoding"
- * - "refcount"
- * - "idletime"
- *
- * @param string $string
- * @param string $key
- *
- * @return string|int|false for "encoding", int for "refcount" and "idletime", FALSE if the key doesn't exist.
- *
- * @link https://redis.io/commands/object
- * @example
- *
- * $redis->lPush('l', 'Hello, world!');
- * $redis->object("encoding", "l"); // → ziplist
- * $redis->object("refcount", "l"); // → 1
- * $redis->object("idletime", "l"); // → 400 (in seconds, with a precision of 10 seconds).
- *
- */
- public function object($string = '', $key = '')
- {
- }
-
- /**
- * Performs a synchronous save.
- *
- * @return bool TRUE in case of success, FALSE in case of failure
- * If a save is already running, this command will fail and return FALSE.
- *
- * @link https://redis.io/commands/save
- * @example $redis->save();
- */
- public function save()
- {
- }
-
- /**
- * Performs a background save.
- *
- * @return bool TRUE in case of success, FALSE in case of failure
- * If a save is already running, this command will fail and return FALSE
- *
- * @link https://redis.io/commands/bgsave
- * @example $redis->bgSave();
- */
- public function bgsave()
- {
- }
-
- /**
- * Returns the timestamp of the last disk save.
- *
- * @return int timestamp
- *
- * @link https://redis.io/commands/lastsave
- * @example $redis->lastSave();
- */
- public function lastSave()
- {
- }
-
- /**
- * Blocks the current client until all the previous write commands are successfully transferred and
- * acknowledged by at least the specified number of slaves.
- *
- * @param int $numSlaves Number of slaves that need to acknowledge previous write commands.
- * @param int $timeout Timeout in milliseconds.
- *
- * @return int The command returns the number of slaves reached by all the writes performed in the
- * context of the current connection
- *
- * @link https://redis.io/commands/wait
- * @example $redis->wait(2, 1000);
- */
- public function wait($numSlaves, $timeout)
- {
- }
-
- /**
- * Returns the type of data pointed by a given key.
- *
- * @param string $key
- *
- * @return int
- * Depending on the type of the data pointed by the key,
- * this method will return the following value:
- * - string: Redis::REDIS_STRING
- * - set: Redis::REDIS_SET
- * - list: Redis::REDIS_LIST
- * - zset: Redis::REDIS_ZSET
- * - hash: Redis::REDIS_HASH
- * - other: Redis::REDIS_NOT_FOUND
- *
- * @link https://redis.io/commands/type
- * @example $redis->type('key');
- */
- public function type($key)
- {
- }
-
- /**
- * Append specified string to the string stored in specified key.
- *
- * @param string $key
- * @param string|mixed $value
- *
- * @return int Size of the value after the append
- *
- * @link https://redis.io/commands/append
- * @example
- *
- * $redis->set('key', 'value1');
- * $redis->append('key', 'value2'); // 12
- * $redis->get('key'); // 'value1value2'
- *
- */
- public function append($key, $value)
- {
- }
-
- /**
- * Return a substring of a larger string
- *
- * @param string $key
- * @param int $start
- * @param int $end
- *
- * @return string the substring
- *
- * @link https://redis.io/commands/getrange
- * @example
- *
- * $redis->set('key', 'string value');
- * $redis->getRange('key', 0, 5); // 'string'
- * $redis->getRange('key', -5, -1); // 'value'
- *
- */
- public function getRange($key, $start, $end)
- {
- }
-
- /**
- * Return a substring of a larger string
- *
- * @param string $key
- * @param int $start
- * @param int $end
- */
- #[Deprecated]
- public function substr($key, $start, $end)
- {
- }
-
- /**
- * Changes a substring of a larger string.
- *
- * @param string $key
- * @param int $offset
- * @param string $value
- *
- * @return int the length of the string after it was modified
- *
- * @link https://redis.io/commands/setrange
- * @example
- *
- * $redis->set('key', 'Hello world');
- * $redis->setRange('key', 6, "redis"); // returns 11
- * $redis->get('key'); // "Hello redis"
- *
- */
- public function setRange($key, $offset, $value)
- {
- }
-
- /**
- * Get the length of a string value.
- *
- * @param string $key
- * @return int
- *
- * @link https://redis.io/commands/strlen
- * @example
- *
- * $redis->set('key', 'value');
- * $redis->strlen('key'); // 5
- *
- */
- public function strlen($key)
- {
- }
-
- /**
- * Return the position of the first bit set to 1 or 0 in a string. The position is returned, thinking of the
- * string as an array of bits from left to right, where the first byte's most significant bit is at position 0,
- * the second byte's most significant bit is at position 8, and so forth.
- *
- * @param string $key
- * @param int $bit
- * @param int $start
- * @param int $end
- *
- * @return int The command returns the position of the first bit set to 1 or 0 according to the request.
- * If we look for set bits (the bit argument is 1) and the string is empty or composed of just
- * zero bytes, -1 is returned. If we look for clear bits (the bit argument is 0) and the string
- * only contains bit set to 1, the function returns the first bit not part of the string on the
- * right. So if the string is three bytes set to the value 0xff the command BITPOS key 0 will
- * return 24, since up to bit 23 all the bits are 1. Basically, the function considers the right
- * of the string as padded with zeros if you look for clear bits and specify no range or the
- * start argument only. However, this behavior changes if you are looking for clear bits and
- * specify a range with both start and end. If no clear bit is found in the specified range, the
- * function returns -1 as the user specified a clear range and there are no 0 bits in that range.
- *
- * @link https://redis.io/commands/bitpos
- * @example
- *
- * $redis->set('key', '\xff\xff');
- * $redis->bitpos('key', 1); // int(0)
- * $redis->bitpos('key', 1, 1); // int(8)
- * $redis->bitpos('key', 1, 3); // int(-1)
- * $redis->bitpos('key', 0); // int(16)
- * $redis->bitpos('key', 0, 1); // int(16)
- * $redis->bitpos('key', 0, 1, 5); // int(-1)
- *
- */
- public function bitpos($key, $bit, $start = 0, $end = null)
- {
- }
-
- /**
- * Return a single bit out of a larger string
- *
- * @param string $key
- * @param int $offset
- *
- * @return int the bit value (0 or 1)
- *
- * @link https://redis.io/commands/getbit
- * @example
- *
- * $redis->set('key', "\x7f"); // this is 0111 1111
- * $redis->getBit('key', 0); // 0
- * $redis->getBit('key', 1); // 1
- *
- */
- public function getBit($key, $offset)
- {
- }
-
- /**
- * Changes a single bit of a string.
- *
- * @param string $key
- * @param int $offset
- * @param bool|int $value bool or int (1 or 0)
- *
- * @return int 0 or 1, the value of the bit before it was set
- *
- * @link https://redis.io/commands/setbit
- * @example
- *
- * $redis->set('key', "*"); // ord("*") = 42 = 0x2f = "0010 1010"
- * $redis->setBit('key', 5, 1); // returns 0
- * $redis->setBit('key', 7, 1); // returns 0
- * $redis->get('key'); // chr(0x2f) = "/" = b("0010 1111")
- *
- */
- public function setBit($key, $offset, $value)
- {
- }
-
- /**
- * Count bits in a string
- *
- * @param string $key
- *
- * @return int The number of bits set to 1 in the value behind the input key
- *
- * @link https://redis.io/commands/bitcount
- * @example
- *
- * $redis->set('bit', '345'); // // 11 0011 0011 0100 0011 0101
- * var_dump( $redis->bitCount('bit', 0, 0) ); // int(4)
- * var_dump( $redis->bitCount('bit', 1, 1) ); // int(3)
- * var_dump( $redis->bitCount('bit', 2, 2) ); // int(4)
- * var_dump( $redis->bitCount('bit', 0, 2) ); // int(11)
- *
- */
- public function bitCount($key)
- {
- }
-
- /**
- * Bitwise operation on multiple keys.
- *
- * @param string $operation either "AND", "OR", "NOT", "XOR"
- * @param string $retKey return key
- * @param string $key1 first key
- * @param string ...$otherKeys variadic list of keys
- *
- * @return int The size of the string stored in the destination key
- *
- * @link https://redis.io/commands/bitop
- * @example
- *
- * $redis->set('bit1', '1'); // 11 0001
- * $redis->set('bit2', '2'); // 11 0010
- *
- * $redis->bitOp('AND', 'bit', 'bit1', 'bit2'); // bit = 110000
- * $redis->bitOp('OR', 'bit', 'bit1', 'bit2'); // bit = 110011
- * $redis->bitOp('NOT', 'bit', 'bit1', 'bit2'); // bit = 110011
- * $redis->bitOp('XOR', 'bit', 'bit1', 'bit2'); // bit = 11
- *
- */
- public function bitOp($operation, $retKey, $key1, ...$otherKeys)
- {
- }
-
- /**
- * Removes all entries from the current database.
- *
- * @return bool Always TRUE
- * @link https://redis.io/commands/flushdb
- * @example $redis->flushDB();
- */
- public function flushDB()
- {
- }
-
- /**
- * Removes all entries from all databases.
- *
- * @return bool Always TRUE
- *
- * @link https://redis.io/commands/flushall
- * @example $redis->flushAll();
- */
- public function flushAll()
- {
- }
-
- /**
- * Sort
- *
- * @param string $key
- * @param array $option array(key => value, ...) - optional, with the following keys and values:
- * - 'by' => 'some_pattern_*',
- * - 'limit' => array(0, 1),
- * - 'get' => 'some_other_pattern_*' or an array of patterns,
- * - 'sort' => 'asc' or 'desc',
- * - 'alpha' => TRUE,
- * - 'store' => 'external-key'
- *
- * @return array
- * An array of values, or a number corresponding to the number of elements stored if that was used
- *
- * @link https://redis.io/commands/sort
- * @example
- *
- * $redis->del('s');
- * $redis->sadd('s', 5);
- * $redis->sadd('s', 4);
- * $redis->sadd('s', 2);
- * $redis->sadd('s', 1);
- * $redis->sadd('s', 3);
- *
- * var_dump($redis->sort('s')); // 1,2,3,4,5
- * var_dump($redis->sort('s', array('sort' => 'desc'))); // 5,4,3,2,1
- * var_dump($redis->sort('s', array('sort' => 'desc', 'store' => 'out'))); // (int)5
- *
- */
- public function sort($key, $option = null)
- {
- }
-
- /**
- * Returns an associative array of strings and integers
- *
- * @param string $option Optional. The option to provide redis.
- * SERVER | CLIENTS | MEMORY | PERSISTENCE | STATS | REPLICATION | CPU | CLASTER | KEYSPACE | COMANDSTATS
- *
- * Returns an associative array of strings and integers, with the following keys:
- * - redis_version
- * - redis_git_sha1
- * - redis_git_dirty
- * - arch_bits
- * - multiplexing_api
- * - process_id
- * - uptime_in_seconds
- * - uptime_in_days
- * - lru_clock
- * - used_cpu_sys
- * - used_cpu_user
- * - used_cpu_sys_children
- * - used_cpu_user_children
- * - connected_clients
- * - connected_slaves
- * - client_longest_output_list
- * - client_biggest_input_buf
- * - blocked_clients
- * - used_memory
- * - used_memory_human
- * - used_memory_peak
- * - used_memory_peak_human
- * - mem_fragmentation_ratio
- * - mem_allocator
- * - loading
- * - aof_enabled
- * - changes_since_last_save
- * - bgsave_in_progress
- * - last_save_time
- * - total_connections_received
- * - total_commands_processed
- * - expired_keys
- * - evicted_keys
- * - keyspace_hits
- * - keyspace_misses
- * - hash_max_zipmap_entries
- * - hash_max_zipmap_value
- * - pubsub_channels
- * - pubsub_patterns
- * - latest_fork_usec
- * - vm_enabled
- * - role
- *
- * @return array
- *
- * @link https://redis.io/commands/info
- * @example
- *
- * $redis->info();
- *
- * or
- *
- * $redis->info("COMMANDSTATS"); //Information on the commands that have been run (>=2.6 only)
- * $redis->info("CPU"); // just CPU information from Redis INFO
- *
- */
- public function info($option = null)
- {
- }
-
- /**
- * Resets the statistics reported by Redis using the INFO command (`info()` function).
- * These are the counters that are reset:
- * - Keyspace hits
- * - Keyspace misses
- * - Number of commands processed
- * - Number of connections received
- * - Number of expired keys
- *
- * @return bool `TRUE` in case of success, `FALSE` in case of failure.
- *
- * @example $redis->resetStat();
- * @link https://redis.io/commands/config-resetstat
- */
- public function resetStat()
- {
- }
-
- /**
- * Returns the time to live left for a given key, in seconds. If the key doesn't exist, FALSE is returned.
- *
- * @param string $key
- *
- * @return int|bool the time left to live in seconds
- *
- * @link https://redis.io/commands/ttl
- * @example
- *
- * $redis->setex('key', 123, 'test');
- * $redis->ttl('key'); // int(123)
- *
- */
- public function ttl($key)
- {
- }
-
- /**
- * Returns a time to live left for a given key, in milliseconds.
- *
- * If the key doesn't exist, FALSE is returned.
- *
- * @param string $key
- *
- * @return int|bool the time left to live in milliseconds
- *
- * @link https://redis.io/commands/pttl
- * @example
- *
- * $redis->setex('key', 123, 'test');
- * $redis->pttl('key'); // int(122999)
- *
- */
- public function pttl($key)
- {
- }
-
- /**
- * Remove the expiration timer from a key.
- *
- * @param string $key
- *
- * @return bool TRUE if a timeout was removed, FALSE if the key didn’t exist or didn’t have an expiration timer.
- *
- * @link https://redis.io/commands/persist
- * @example $redis->persist('key');
- */
- public function persist($key)
- {
- }
-
- /**
- * Sets multiple key-value pairs in one atomic command.
- * MSETNX only returns TRUE if all the keys were set (see SETNX).
- *
- * @param array $array Pairs: array(key => value, ...)
- *
- * @return bool TRUE in case of success, FALSE in case of failure
- *
- * @link https://redis.io/commands/mset
- * @example
- *
- * $redis->mset(array('key0' => 'value0', 'key1' => 'value1'));
- * var_dump($redis->get('key0'));
- * var_dump($redis->get('key1'));
- * // Output:
- * // string(6) "value0"
- * // string(6) "value1"
- *
- */
- public function mset(array $array)
- {
- }
-
- /**
- * Get the values of all the specified keys.
- * If one or more keys dont exist, the array will contain FALSE at the position of the key.
- *
- * @param array $keys Array containing the list of the keys
- *
- * @return array Array containing the values related to keys in argument
- *
- * @example
- *
- * $redis->set('key1', 'value1');
- * $redis->set('key2', 'value2');
- * $redis->set('key3', 'value3');
- * $redis->getMultiple(array('key1', 'key2', 'key3')); // array('value1', 'value2', 'value3');
- * $redis->getMultiple(array('key0', 'key1', 'key5')); // array(`FALSE`, 'value2', `FALSE`);
- *
- */
- #[Deprecated(replacement: '%class%->mGet(%parametersList%)')]
- public function getMultiple(array $keys)
- {
- }
-
- /**
- * Returns the values of all specified keys.
- *
- * For every key that does not hold a string value or does not exist,
- * the special value false is returned. Because of this, the operation never fails.
- *
- * @param array $array
- *
- * @return array
- *
- * @link https://redis.io/commands/mget
- * @example
- *
- * $redis->del('x', 'y', 'z', 'h'); // remove x y z
- * $redis->mset(array('x' => 'a', 'y' => 'b', 'z' => 'c'));
- * $redis->hset('h', 'field', 'value');
- * var_dump($redis->mget(array('x', 'y', 'z', 'h')));
- * // Output:
- * // array(3) {
- * // [0]=> string(1) "a"
- * // [1]=> string(1) "b"
- * // [2]=> string(1) "c"
- * // [3]=> bool(false)
- * // }
- *
- */
- public function mget(array $array)
- {
- }
-
- /**
- * @see mset()
- * @param array $array
- * @return int 1 (if the keys were set) or 0 (no key was set)
- *
- * @link https://redis.io/commands/msetnx
- */
- public function msetnx(array $array)
- {
- }
-
- /**
- * Pops a value from the tail of a list, and pushes it to the front of another list.
- * Also return this value.
- *
- * @since redis >= 1.1
- *
- * @param string $srcKey
- * @param string $dstKey
- *
- * @return string|mixed|false The element that was moved in case of success, FALSE in case of failure.
- *
- * @link https://redis.io/commands/rpoplpush
- * @example
- *
- * $redis->del('x', 'y');
- *
- * $redis->lPush('x', 'abc');
- * $redis->lPush('x', 'def');
- * $redis->lPush('y', '123');
- * $redis->lPush('y', '456');
- *
- * // move the last of x to the front of y.
- * var_dump($redis->rpoplpush('x', 'y'));
- * var_dump($redis->lRange('x', 0, -1));
- * var_dump($redis->lRange('y', 0, -1));
- *
- * //Output:
- * //
- * //string(3) "abc"
- * //array(1) {
- * // [0]=>
- * // string(3) "def"
- * //}
- * //array(3) {
- * // [0]=>
- * // string(3) "abc"
- * // [1]=>
- * // string(3) "456"
- * // [2]=>
- * // string(3) "123"
- * //}
- *
- */
- public function rpoplpush($srcKey, $dstKey)
- {
- }
-
- /**
- * A blocking version of rpoplpush, with an integral timeout in the third parameter.
- *
- * @param string $srcKey
- * @param string $dstKey
- * @param int $timeout
- *
- * @return string|mixed|bool The element that was moved in case of success, FALSE in case of timeout
- *
- * @link https://redis.io/commands/brpoplpush
- */
- public function brpoplpush($srcKey, $dstKey, $timeout)
- {
- }
-
- /**
- * Adds the specified member with a given score to the sorted set stored at key
- *
- * @param string $key Required key
- * @param array|float $options Options if needed or score if omitted
- * @param float|string|mixed $score1 Required score or value if options omitted
- * @param string|float|mixed $value1 Required value or optional score if options omitted
- * @param float|string|mixed $score2 Optional score or value if options omitted
- * @param string|float|mixed $value2 Optional value or score if options omitted
- * @param float|string|mixed $scoreN Optional score or value if options omitted
- * @param string|float|mixed $valueN Optional value or score if options omitted
- *
- * @return int Number of values added
- *
- * @link https://redis.io/commands/zadd
- * @example
- *
- * $redis->zAdd('z', 1, 'v1', 2, 'v2', 3, 'v3', 4, 'v4' ); // int(2)
- * $redis->zRem('z', 'v2', 'v3'); // int(2)
- * $redis->zAdd('z', ['NX'], 5, 'v5'); // int(1)
- * $redis->zAdd('z', ['NX'], 6, 'v5'); // int(0)
- * $redis->zAdd('z', 7, 'v6'); // int(1)
- * $redis->zAdd('z', 8, 'v6'); // int(0)
- *
- * var_dump( $redis->zRange('z', 0, -1) );
- * // Output:
- * // array(4) {
- * // [0]=> string(2) "v1"
- * // [1]=> string(2) "v4"
- * // [2]=> string(2) "v5"
- * // [3]=> string(2) "v8"
- * // }
- *
- * var_dump( $redis->zRange('z', 0, -1, true) );
- * // Output:
- * // array(4) {
- * // ["v1"]=> float(1)
- * // ["v4"]=> float(4)
- * // ["v5"]=> float(5)
- * // ["v6"]=> float(8)
- *
- */
- public function zAdd($key, $options, $score1, $value1 = null, $score2 = null, $value2 = null, $scoreN = null, $valueN = null)
- {
- }
-
- /**
- * Returns a range of elements from the ordered set stored at the specified key,
- * with values in the range [start, end]. start and stop are interpreted as zero-based indices:
- * 0 the first element,
- * 1 the second ...
- * -1 the last element,
- * -2 the penultimate ...
- *
- * @param string $key
- * @param int $start
- * @param int $end
- * @param bool $withscores
- *
- * @return array Array containing the values in specified range.
- *
- * @link https://redis.io/commands/zrange
- * @example
- *
- * $redis->zAdd('key1', 0, 'val0');
- * $redis->zAdd('key1', 2, 'val2');
- * $redis->zAdd('key1', 10, 'val10');
- * $redis->zRange('key1', 0, -1); // array('val0', 'val2', 'val10')
- * // with scores
- * $redis->zRange('key1', 0, -1, true); // array('val0' => 0, 'val2' => 2, 'val10' => 10)
- *
- */
- public function zRange($key, $start, $end, $withscores = null)
- {
- }
-
- /**
- * Deletes a specified member from the ordered set.
- *
- * @param string $key
- * @param string|mixed $member1
- * @param string|mixed ...$otherMembers
- *
- * @return int Number of deleted values
- *
- * @link https://redis.io/commands/zrem
- * @example
- *
- * $redis->zAdd('z', 1, 'v1', 2, 'v2', 3, 'v3', 4, 'v4' ); // int(2)
- * $redis->zRem('z', 'v2', 'v3'); // int(2)
- * var_dump( $redis->zRange('z', 0, -1) );
- * //// Output:
- * // array(2) {
- * // [0]=> string(2) "v1"
- * // [1]=> string(2) "v4"
- * // }
- *
- */
- public function zRem($key, $member1, ...$otherMembers)
- {
- }
-
- /**
- * @link https://redis.io/commands/zrem
- *
- * @param string $key
- * @param string|mixed $member1
- * @param string|mixed ...$otherMembers
- *
- * @return int Number of deleted values
- */
- #[Deprecated(replacement: '%class%->zRem(%parametersList%)')]
- public function zDelete($key, $member1, ...$otherMembers)
- {
- }
-
- /**
- * Returns the elements of the sorted set stored at the specified key in the range [start, end]
- * in reverse order. start and stop are interpretated as zero-based indices:
- * 0 the first element,
- * 1 the second ...
- * -1 the last element,
- * -2 the penultimate ...
- *
- * @param string $key
- * @param int $start
- * @param int $end
- * @param bool $withscore
- *
- * @return array Array containing the values in specified range.
- *
- * @link https://redis.io/commands/zrevrange
- * @example
- *
- * $redis->zAdd('key', 0, 'val0');
- * $redis->zAdd('key', 2, 'val2');
- * $redis->zAdd('key', 10, 'val10');
- * $redis->zRevRange('key', 0, -1); // array('val10', 'val2', 'val0')
- *
- * // with scores
- * $redis->zRevRange('key', 0, -1, true); // array('val10' => 10, 'val2' => 2, 'val0' => 0)
- *
- */
- public function zRevRange($key, $start, $end, $withscore = null)
- {
- }
-
- /**
- * Returns the elements of the sorted set stored at the specified key which have scores in the
- * range [start,end]. Adding a parenthesis before start or end excludes it from the range.
- * +inf and -inf are also valid limits.
- *
- * zRevRangeByScore returns the same items in reverse order, when the start and end parameters are swapped.
- *
- * @param string $key
- * @param int $start
- * @param int $end
- * @param array $options Two options are available:
- * - withscores => TRUE,
- * - and limit => array($offset, $count)
- *
- * @return array Array containing the values in specified range.
- *
- * @link https://redis.io/commands/zrangebyscore
- * @example
- *
- * $redis->zAdd('key', 0, 'val0');
- * $redis->zAdd('key', 2, 'val2');
- * $redis->zAdd('key', 10, 'val10');
- * $redis->zRangeByScore('key', 0, 3); // array('val0', 'val2')
- * $redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE); // array('val0' => 0, 'val2' => 2)
- * $redis->zRangeByScore('key', 0, 3, array('limit' => array(1, 1)); // array('val2')
- * $redis->zRangeByScore('key', 0, 3, array('withscores' => TRUE, 'limit' => array(1, 1)); // array('val2' => 2)
- *
- */
- public function zRangeByScore($key, $start, $end, array $options = array())
- {
- }
-
- /**
- * @see zRangeByScore()
- * @param string $key
- * @param int $start
- * @param int $end
- * @param array $options
- *
- * @return array
- */
- public function zRevRangeByScore($key, $start, $end, array $options = array())
- {
- }
-
- /**
- * Returns a lexigraphical range of members in a sorted set, assuming the members have the same score. The
- * min and max values are required to start with '(' (exclusive), '[' (inclusive), or be exactly the values
- * '-' (negative inf) or '+' (positive inf). The command must be called with either three *or* five
- * arguments or will return FALSE.
- *
- * @param string $key The ZSET you wish to run against.
- * @param int $min The minimum alphanumeric value you wish to get.
- * @param int $max The maximum alphanumeric value you wish to get.
- * @param int $offset Optional argument if you wish to start somewhere other than the first element.
- * @param int $limit Optional argument if you wish to limit the number of elements returned.
- *
- * @return array|false Array containing the values in the specified range.
- *
- * @link https://redis.io/commands/zrangebylex
- * @example
- *
- * foreach (array('a', 'b', 'c', 'd', 'e', 'f', 'g') as $char) {
- * $redis->zAdd('key', $char);
- * }
- *
- * $redis->zRangeByLex('key', '-', '[c'); // array('a', 'b', 'c')
- * $redis->zRangeByLex('key', '-', '(c'); // array('a', 'b')
- * $redis->zRangeByLex('key', '-', '[c'); // array('b', 'c')
- *
- */
- public function zRangeByLex($key, $min, $max, $offset = null, $limit = null)
- {
- }
-
- /**
- * @see zRangeByLex()
- * @param string $key
- * @param int $min
- * @param int $max
- * @param int $offset
- * @param int $limit
- *
- * @return array
- *
- * @link https://redis.io/commands/zrevrangebylex
- */
- public function zRevRangeByLex($key, $min, $max, $offset = null, $limit = null)
- {
- }
-
- /**
- * Returns the number of elements of the sorted set stored at the specified key which have
- * scores in the range [start,end]. Adding a parenthesis before start or end excludes it
- * from the range. +inf and -inf are also valid limits.
- *
- * @param string $key
- * @param string $start
- * @param string $end
- *
- * @return int the size of a corresponding zRangeByScore
- *
- * @link https://redis.io/commands/zcount
- * @example
- *
- * $redis->zAdd('key', 0, 'val0');
- * $redis->zAdd('key', 2, 'val2');
- * $redis->zAdd('key', 10, 'val10');
- * $redis->zCount('key', 0, 3); // 2, corresponding to array('val0', 'val2')
- *
- */
- public function zCount($key, $start, $end)
- {
- }
-
- /**
- * Deletes the elements of the sorted set stored at the specified key which have scores in the range [start,end].
- *
- * @param string $key
- * @param float|string $start double or "+inf" or "-inf" string
- * @param float|string $end double or "+inf" or "-inf" string
- *
- * @return int The number of values deleted from the sorted set
- *
- * @link https://redis.io/commands/zremrangebyscore
- * @example
- *
- * $redis->zAdd('key', 0, 'val0');
- * $redis->zAdd('key', 2, 'val2');
- * $redis->zAdd('key', 10, 'val10');
- * $redis->zRemRangeByScore('key', 0, 3); // 2
- *
- */
- public function zRemRangeByScore($key, $start, $end)
- {
- }
-
- /**
- * @param string $key
- * @param float $start
- * @param float $end
- */
- #[Deprecated(replacement: '%class%->zRemRangeByScore(%parametersList%)')]
- public function zDeleteRangeByScore($key, $start, $end)
- {
- }
-
- /**
- * Deletes the elements of the sorted set stored at the specified key which have rank in the range [start,end].
- *
- * @param string $key
- * @param int $start
- * @param int $end
- *
- * @return int The number of values deleted from the sorted set
- *
- * @link https://redis.io/commands/zremrangebyrank
- * @example
- *
- * $redis->zAdd('key', 1, 'one');
- * $redis->zAdd('key', 2, 'two');
- * $redis->zAdd('key', 3, 'three');
- * $redis->zRemRangeByRank('key', 0, 1); // 2
- * $redis->zRange('key', 0, -1, array('withscores' => TRUE)); // array('three' => 3)
- *
- */
- public function zRemRangeByRank($key, $start, $end)
- {
- }
-
- /**
- * @link https://redis.io/commands/zremrangebyscore
- *
- * @param string $key
- * @param int $start
- * @param int $end
- */
- #[Deprecated(replacement: '%class%->zRemRangeByRank(%parametersList%)')]
- public function zDeleteRangeByRank($key, $start, $end)
- {
- }
-
- /**
- * Returns the cardinality of an ordered set.
- *
- * @param string $key
- *
- * @return int the set's cardinality
- *
- * @link https://redis.io/commands/zsize
- * @example
- *
- * $redis->zAdd('key', 0, 'val0');
- * $redis->zAdd('key', 2, 'val2');
- * $redis->zAdd('key', 10, 'val10');
- * $redis->zCard('key'); // 3
- *
- */
- public function zCard($key)
- {
- }
-
- /**
- * @param string $key
- * @return int
- */
- #[Deprecated(replacement: '%class%->zCard(%parametersList%)')]
- public function zSize($key)
- {
- }
-
- /**
- * Returns the score of a given member in the specified sorted set.
- *
- * @param string $key
- * @param string|mixed $member
- *
- * @return float|bool false if member or key not exists
- *
- * @link https://redis.io/commands/zscore
- * @example
- *
- * $redis->zAdd('key', 2.5, 'val2');
- * $redis->zScore('key', 'val2'); // 2.5
- *
- */
- public function zScore($key, $member)
- {
- }
-
- /**
- * Returns the rank of a given member in the specified sorted set, starting at 0 for the item
- * with the smallest score. zRevRank starts at 0 for the item with the largest score.
- *
- * @param string $key
- * @param string|mixed $member
- *
- * @return int|false the item's score, or false if key or member is not exists
- *
- * @link https://redis.io/commands/zrank
- * @example
- *
- * $redis->del('z');
- * $redis->zAdd('key', 1, 'one');
- * $redis->zAdd('key', 2, 'two');
- * $redis->zRank('key', 'one'); // 0
- * $redis->zRank('key', 'two'); // 1
- * $redis->zRevRank('key', 'one'); // 1
- * $redis->zRevRank('key', 'two'); // 0
- *
- */
- public function zRank($key, $member)
- {
- }
-
- /**
- * @see zRank()
- * @param string $key
- * @param string|mixed $member
- *
- * @return int|false the item's score, false - if key or member is not exists
- *
- * @link https://redis.io/commands/zrevrank
- */
- public function zRevRank($key, $member)
- {
- }
-
- /**
- * Increments the score of a member from a sorted set by a given amount.
- *
- * @param string $key
- * @param float $value (double) value that will be added to the member's score
- * @param string $member
- *
- * @return float the new value
- *
- * @link https://redis.io/commands/zincrby
- * @example
- *
- * $redis->del('key');
- * $redis->zIncrBy('key', 2.5, 'member1'); // key or member1 didn't exist, so member1's score is to 0
- * // before the increment and now has the value 2.5
- * $redis->zIncrBy('key', 1, 'member1'); // 3.5
- *
- */
- public function zIncrBy($key, $value, $member)
- {
- }
-
- /**
- * Creates an union of sorted sets given in second argument.
- * The result of the union will be stored in the sorted set defined by the first argument.
- * The third optionnel argument defines weights to apply to the sorted sets in input.
- * In this case, the weights will be multiplied by the score of each element in the sorted set
- * before applying the aggregation. The forth argument defines the AGGREGATE option which
- * specify how the results of the union are aggregated.
- *
- * @param string $output
- * @param array $zSetKeys
- * @param null|array $weights
- * @param string $aggregateFunction Either "SUM", "MIN", or "MAX": defines the behaviour to use on
- * duplicate entries during the zUnionStore
- *
- * @return int The number of values in the new sorted set
- *
- * @link https://redis.io/commands/zunionstore
- * @example
- *
- * $redis->del('k1');
- * $redis->del('k2');
- * $redis->del('k3');
- * $redis->del('ko1');
- * $redis->del('ko2');
- * $redis->del('ko3');
- *
- * $redis->zAdd('k1', 0, 'val0');
- * $redis->zAdd('k1', 1, 'val1');
- *
- * $redis->zAdd('k2', 2, 'val2');
- * $redis->zAdd('k2', 3, 'val3');
- *
- * $redis->zUnionStore('ko1', array('k1', 'k2')); // 4, 'ko1' => array('val0', 'val1', 'val2', 'val3')
- *
- * // Weighted zUnionStore
- * $redis->zUnionStore('ko2', array('k1', 'k2'), array(1, 1)); // 4, 'ko2' => array('val0', 'val1', 'val2', 'val3')
- * $redis->zUnionStore('ko3', array('k1', 'k2'), array(5, 1)); // 4, 'ko3' => array('val0', 'val2', 'val3', 'val1')
- *
- */
- public function zUnionStore($output, $zSetKeys, ?array $weights = null, $aggregateFunction = 'SUM')
- {
- }
-
- /**
- * @param string $Output
- * @param array $ZSetKeys
- * @param array|null $Weights
- * @param string $aggregateFunction
- */
- #[Deprecated(replacement: '%class%->zUnionStore(%parametersList%)')]
- public function zUnion($Output, $ZSetKeys, array $Weights = null, $aggregateFunction = 'SUM')
- {
- }
-
- /**
- * Creates an intersection of sorted sets given in second argument.
- * The result of the union will be stored in the sorted set defined by the first argument.
- * The third optional argument defines weights to apply to the sorted sets in input.
- * In this case, the weights will be multiplied by the score of each element in the sorted set
- * before applying the aggregation. The forth argument defines the AGGREGATE option which
- * specify how the results of the union are aggregated.
- *
- * @param string $output
- * @param array $zSetKeys
- * @param null|array $weights
- * @param string $aggregateFunction Either "SUM", "MIN", or "MAX":
- * defines the behaviour to use on duplicate entries during the zInterStore.
- *
- * @return int The number of values in the new sorted set.
- *
- * @link https://redis.io/commands/zinterstore
- * @example
- *
- * $redis->del('k1');
- * $redis->del('k2');
- * $redis->del('k3');
- *
- * $redis->del('ko1');
- * $redis->del('ko2');
- * $redis->del('ko3');
- * $redis->del('ko4');
- *
- * $redis->zAdd('k1', 0, 'val0');
- * $redis->zAdd('k1', 1, 'val1');
- * $redis->zAdd('k1', 3, 'val3');
- *
- * $redis->zAdd('k2', 2, 'val1');
- * $redis->zAdd('k2', 3, 'val3');
- *
- * $redis->zInterStore('ko1', array('k1', 'k2')); // 2, 'ko1' => array('val1', 'val3')
- * $redis->zInterStore('ko2', array('k1', 'k2'), array(1, 1)); // 2, 'ko2' => array('val1', 'val3')
- *
- * // Weighted zInterStore
- * $redis->zInterStore('ko3', array('k1', 'k2'), array(1, 5), 'min'); // 2, 'ko3' => array('val1', 'val3')
- * $redis->zInterStore('ko4', array('k1', 'k2'), array(1, 5), 'max'); // 2, 'ko4' => array('val3', 'val1')
- *
- */
- public function zInterStore($output, $zSetKeys, array $weights = null, $aggregateFunction = 'SUM')
- {
- }
-
- /**
- * @param $Output
- * @param $ZSetKeys
- * @param array|null $Weights
- * @param string $aggregateFunction
- */
- #[Deprecated(replacement: '%class%->zInterStore(%parametersList%)')]
- public function zInter($Output, $ZSetKeys, array $Weights = null, $aggregateFunction = 'SUM')
- {
- }
-
- /**
- * Scan a sorted set for members, with optional pattern and count
- *
- * @param string $key String, the set to scan.
- * @param int &$iterator Long (reference), initialized to NULL.
- * @param string $pattern String (optional), the pattern to match.
- * @param int $count How many keys to return per iteration (Redis might return a different number).
- *
- * @return array|false PHPRedis will return matching keys from Redis, or FALSE when iteration is complete
- *
- * @link https://redis.io/commands/zscan
- * @example
- *
- * $iterator = null;
- * while ($members = $redis-zscan('zset', $iterator)) {
- * foreach ($members as $member => $score) {
- * echo $member . ' => ' . $score . PHP_EOL;
- * }
- * }
- *
- */
- public function zScan($key, &$iterator, $pattern = null, $count = 0)
- {
- }
-
- /**
- * Block until Redis can pop the highest or lowest scoring members from one or more ZSETs.
- * There are two commands (BZPOPMIN and BZPOPMAX for popping the lowest and highest scoring elements respectively.)
- *
- * @param string|array $key1
- * @param string|array $key2 ...
- * @param int $timeout
- *
- * @return array Either an array with the key member and score of the highest or lowest element or an empty array
- * if the timeout was reached without an element to pop.
- *
- * @since >= 5.0
- * @link https://redis.io/commands/bzpopmax
- * @example
- *
- * // Wait up to 5 seconds to pop the *lowest* scoring member from sets `zs1` and `zs2`.
- * $redis->bzPopMin(['zs1', 'zs2'], 5);
- * $redis->bzPopMin('zs1', 'zs2', 5);
- *
- * // Wait up to 5 seconds to pop the *highest* scoring member from sets `zs1` and `zs2`
- * $redis->bzPopMax(['zs1', 'zs2'], 5);
- * $redis->bzPopMax('zs1', 'zs2', 5);
- *
- */
- public function bzPopMax($key1, $key2, $timeout)
- {
- }
-
- /**
- * @param string|array $key1
- * @param string|array $key2 ...
- * @param int $timeout
- *
- * @return array Either an array with the key member and score of the highest or lowest element or an empty array
- * if the timeout was reached without an element to pop.
- *
- * @see bzPopMax
- * @since >= 5.0
- * @link https://redis.io/commands/bzpopmin
- */
- public function bzPopMin($key1, $key2, $timeout)
- {
- }
-
- /**
- * Can pop the highest scoring members from one ZSET.
- *
- * @param string $key
- * @param int $count
- *
- * @return array Either an array with the key member and score of the highest element or an empty array
- * if there is no element to pop.
- *
- * @since >= 5.0
- * @link https://redis.io/commands/zpopmax
- * @example
- *
- * // Pop the *lowest* scoring member from set `zs1`.
- * $redis->zPopMax('zs1');
- * // Pop the *lowest* 3 scoring member from set `zs1`.
- * $redis->zPopMax('zs1', 3);
- *
- */
- public function zPopMax($key, $count = 1)
- {
- }
-
- /**
- * Can pop the lowest scoring members from one ZSET.
- *
- * @param string $key
- * @param int $count
- *
- * @return array Either an array with the key member and score of the lowest element or an empty array
- * if there is no element to pop.
- *
- * @since >= 5.0
- * @link https://redis.io/commands/zpopmin
- * @example
- *
- * // Pop the *lowest* scoring member from set `zs1`.
- * $redis->zPopMin('zs1');
- * // Pop the *lowest* 3 scoring member from set `zs1`.
- * $redis->zPopMin('zs1', 3);
- *
- */
- public function zPopMin($key, $count = 1)
- {
- }
-
- /**
- * Adds a value to the hash stored at key. If this value is already in the hash, FALSE is returned.
- *
- * @param string $key
- * @param string $hashKey
- * @param string $value
- *
- * @return int|bool
- * - 1 if value didn't exist and was added successfully,
- * - 0 if the value was already present and was replaced, FALSE if there was an error.
- *
- * @link https://redis.io/commands/hset
- * @example
- *
- * $redis->del('h')
- * $redis->hSet('h', 'key1', 'hello'); // 1, 'key1' => 'hello' in the hash at "h"
- * $redis->hGet('h', 'key1'); // returns "hello"
- *
- * $redis->hSet('h', 'key1', 'plop'); // 0, value was replaced.
- * $redis->hGet('h', 'key1'); // returns "plop"
- *
- */
- public function hSet($key, $hashKey, $value)
- {
- }
-
- /**
- * Adds a value to the hash stored at key only if this field isn't already in the hash.
- *
- * @param string $key
- * @param string $hashKey
- * @param string $value
- *
- * @return bool TRUE if the field was set, FALSE if it was already present.
- *
- * @link https://redis.io/commands/hsetnx
- * @example
- *
- * $redis->del('h')
- * $redis->hSetNx('h', 'key1', 'hello'); // TRUE, 'key1' => 'hello' in the hash at "h"
- * $redis->hSetNx('h', 'key1', 'world'); // FALSE, 'key1' => 'hello' in the hash at "h". No change since the field
- * wasn't replaced.
- *
- */
- public function hSetNx($key, $hashKey, $value)
- {
- }
-
- /**
- * Gets a value from the hash stored at key.
- * If the hash table doesn't exist, or the key doesn't exist, FALSE is returned.
- *
- * @param string $key
- * @param string $hashKey
- *
- * @return string|false The value, if the command executed successfully BOOL FALSE in case of failure
- *
- * @link https://redis.io/commands/hget
- */
- public function hGet($key, $hashKey)
- {
- }
-
- /**
- * Returns the length of a hash, in number of items
- *
- * @param string $key
- *
- * @return int|false the number of items in a hash, FALSE if the key doesn't exist or isn't a hash
- *
- * @link https://redis.io/commands/hlen
- * @example
- *
- * $redis->del('h')
- * $redis->hSet('h', 'key1', 'hello');
- * $redis->hSet('h', 'key2', 'plop');
- * $redis->hLen('h'); // returns 2
- *
- */
- public function hLen($key)
- {
- }
-
- /**
- * Removes a values from the hash stored at key.
- * If the hash table doesn't exist, or the key doesn't exist, FALSE is returned.
- *
- * @param string $key
- * @param string $hashKey1
- * @param string ...$otherHashKeys
- *
- * @return int|bool Number of deleted fields
- *
- * @link https://redis.io/commands/hdel
- * @example
- *
- * $redis->hMSet('h',
- * array(
- * 'f1' => 'v1',
- * 'f2' => 'v2',
- * 'f3' => 'v3',
- * 'f4' => 'v4',
- * ));
- *
- * var_dump( $redis->hDel('h', 'f1') ); // int(1)
- * var_dump( $redis->hDel('h', 'f2', 'f3') ); // int(2)
- * s
- * var_dump( $redis->hGetAll('h') );
- * //// Output:
- * // array(1) {
- * // ["f4"]=> string(2) "v4"
- * // }
- *
- */
- public function hDel($key, $hashKey1, ...$otherHashKeys)
- {
- }
-
- /**
- * Returns the keys in a hash, as an array of strings.
- *
- * @param string $key
- *
- * @return array An array of elements, the keys of the hash. This works like PHP's array_keys().
- *
- * @link https://redis.io/commands/hkeys
- * @example
- *
- * $redis->del('h');
- * $redis->hSet('h', 'a', 'x');
- * $redis->hSet('h', 'b', 'y');
- * $redis->hSet('h', 'c', 'z');
- * $redis->hSet('h', 'd', 't');
- * var_dump($redis->hKeys('h'));
- *
- * // Output:
- * // array(4) {
- * // [0]=>
- * // string(1) "a"
- * // [1]=>
- * // string(1) "b"
- * // [2]=>
- * // string(1) "c"
- * // [3]=>
- * // string(1) "d"
- * // }
- * // The order is random and corresponds to redis' own internal representation of the set structure.
- *
- */
- public function hKeys($key)
- {
- }
-
- /**
- * Returns the values in a hash, as an array of strings.
- *
- * @param string $key
- *
- * @return array An array of elements, the values of the hash. This works like PHP's array_values().
- *
- * @link https://redis.io/commands/hvals
- * @example
- *
- * $redis->del('h');
- * $redis->hSet('h', 'a', 'x');
- * $redis->hSet('h', 'b', 'y');
- * $redis->hSet('h', 'c', 'z');
- * $redis->hSet('h', 'd', 't');
- * var_dump($redis->hVals('h'));
- *
- * // Output
- * // array(4) {
- * // [0]=>
- * // string(1) "x"
- * // [1]=>
- * // string(1) "y"
- * // [2]=>
- * // string(1) "z"
- * // [3]=>
- * // string(1) "t"
- * // }
- * // The order is random and corresponds to redis' own internal representation of the set structure.
- *
- */
- public function hVals($key)
- {
- }
-
- /**
- * Returns the whole hash, as an array of strings indexed by strings.
- *
- * @param string $key
- *
- * @return array An array of elements, the contents of the hash.
- *
- * @link https://redis.io/commands/hgetall
- * @example
- *
- * $redis->del('h');
- * $redis->hSet('h', 'a', 'x');
- * $redis->hSet('h', 'b', 'y');
- * $redis->hSet('h', 'c', 'z');
- * $redis->hSet('h', 'd', 't');
- * var_dump($redis->hGetAll('h'));
- *
- * // Output:
- * // array(4) {
- * // ["a"]=>
- * // string(1) "x"
- * // ["b"]=>
- * // string(1) "y"
- * // ["c"]=>
- * // string(1) "z"
- * // ["d"]=>
- * // string(1) "t"
- * // }
- * // The order is random and corresponds to redis' own internal representation of the set structure.
- *
- */
- public function hGetAll($key)
- {
- }
-
- /**
- * Verify if the specified member exists in a key.
- *
- * @param string $key
- * @param string $hashKey
- *
- * @return bool If the member exists in the hash table, return TRUE, otherwise return FALSE.
- *
- * @link https://redis.io/commands/hexists
- * @example
- *
- * $redis->hSet('h', 'a', 'x');
- * $redis->hExists('h', 'a'); // TRUE
- * $redis->hExists('h', 'NonExistingKey'); // FALSE
- *
- */
- public function hExists($key, $hashKey)
- {
- }
-
- /**
- * Increments the value of a member from a hash by a given amount.
- *
- * @param string $key
- * @param string $hashKey
- * @param int $value (integer) value that will be added to the member's value
- *
- * @return int the new value
- *
- * @link https://redis.io/commands/hincrby
- * @example
- *
- * $redis->del('h');
- * $redis->hIncrBy('h', 'x', 2); // returns 2: h[x] = 2 now.
- * $redis->hIncrBy('h', 'x', 1); // h[x] ← 2 + 1. Returns 3
- *
- */
- public function hIncrBy($key, $hashKey, $value)
- {
- }
-
- /**
- * Increment the float value of a hash field by the given amount
- *
- * @param string $key
- * @param string $field
- * @param float $increment
- *
- * @return float
- *
- * @link https://redis.io/commands/hincrbyfloat
- * @example
- *
- * $redis = new Redis();
- * $redis->connect('127.0.0.1');
- * $redis->hset('h', 'float', 3);
- * $redis->hset('h', 'int', 3);
- * var_dump( $redis->hIncrByFloat('h', 'float', 1.5) ); // float(4.5)
- *
- * var_dump( $redis->hGetAll('h') );
- *
- * // Output
- * array(2) {
- * ["float"]=>
- * string(3) "4.5"
- * ["int"]=>
- * string(1) "3"
- * }
- *
- */
- public function hIncrByFloat($key, $field, $increment)
- {
- }
-
- /**
- * Fills in a whole hash. Non-string values are converted to string, using the standard (string) cast.
- * NULL values are stored as empty strings
- *
- * @param string $key
- * @param array $hashKeys key → value array
- *
- * @return bool
- *
- * @link https://redis.io/commands/hmset
- * @example
- *
- * $redis->del('user:1');
- * $redis->hMSet('user:1', array('name' => 'Joe', 'salary' => 2000));
- * $redis->hIncrBy('user:1', 'salary', 100); // Joe earns 100 more now.
- *
- */
- public function hMSet($key, $hashKeys)
- {
- }
-
- /**
- * Retrieve the values associated to the specified fields in the hash.
- *
- * @param string $key
- * @param array $hashKeys
- *
- * @return array Array An array of elements, the values of the specified fields in the hash,
- * with the hash keys as array keys.
- *
- * @link https://redis.io/commands/hmget
- * @example
- *
- * $redis->del('h');
- * $redis->hSet('h', 'field1', 'value1');
- * $redis->hSet('h', 'field2', 'value2');
- * $redis->hmGet('h', array('field1', 'field2')); // returns array('field1' => 'value1', 'field2' => 'value2')
- *
- */
- public function hMGet($key, $hashKeys)
- {
- }
-
- /**
- * Scan a HASH value for members, with an optional pattern and count.
- *
- * @param string $key
- * @param int &$iterator
- * @param string $pattern Optional pattern to match against.
- * @param int $count How many keys to return in a go (only a sugestion to Redis).
- *
- * @return array An array of members that match our pattern.
- *
- * @link https://redis.io/commands/hscan
- * @example
- *
- * // $iterator = null;
- * // while($elements = $redis->hscan('hash', $iterator)) {
- * // foreach($elements as $key => $value) {
- * // echo $key . ' => ' . $value . PHP_EOL;
- * // }
- * // }
- *
- */
- public function hScan($key, &$iterator, $pattern = null, $count = 0)
- {
- }
-
- /**
- * Get the string length of the value associated with field in the hash stored at key
- *
- * @param string $key
- * @param string $field
- *
- * @return int the string length of the value associated with field, or zero when field is not present in the hash
- * or key does not exist at all.
- *
- * @link https://redis.io/commands/hstrlen
- * @since >= 3.2
- */
- public function hStrLen(string $key, string $field)
- {
- }
-
- /**
- * Add one or more geospatial items to the specified key.
- * This function must be called with at least one longitude, latitude, member triplet.
- *
- * @param string $key
- * @param float $longitude
- * @param float $latitude
- * @param string $member
- *
- * @return int The number of elements added to the geospatial key
- *
- * @link https://redis.io/commands/geoadd
- * @since >= 3.2
- *
- * @example
- *
- * $redis->del("myplaces");
- *
- * // Since the key will be new, $result will be 2
- * $result = $redis->geoAdd(
- * "myplaces",
- * -122.431, 37.773, "San Francisco",
- * -157.858, 21.315, "Honolulu"
- * ); // 2
- *
- */
- public function geoadd($key, $longitude, $latitude, $member)
- {
- }
-
- /**
- * Retrieve Geohash strings for one or more elements of a geospatial index.
-
- * @param string $key
- * @param string ...$member variadic list of members
- *
- * @return array One or more Redis Geohash encoded strings
- *
- * @link https://redis.io/commands/geohash
- * @since >= 3.2
- *
- * @example
- *
- * $redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui");
- * $hashes = $redis->geoHash("hawaii", "Honolulu", "Maui");
- * var_dump($hashes);
- * // Output: array(2) {
- * // [0]=>
- * // string(11) "87z9pyek3y0"
- * // [1]=>
- * // string(11) "8e8y6d5jps0"
- * // }
- *
- */
- public function geohash($key, ...$member)
- {
- }
-
- /**
- * Return longitude, latitude positions for each requested member.
- *
- * @param string $key
- * @param string $member
- * @return array One or more longitude/latitude positions
- *
- * @link https://redis.io/commands/geopos
- * @since >= 3.2
- *
- * @example
- *
- * $redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui");
- * $positions = $redis->geoPos("hawaii", "Honolulu", "Maui");
- * var_dump($positions);
- *
- * // Output:
- * array(2) {
- * [0]=> array(2) {
- * [0]=> string(22) "-157.85800248384475708"
- * [1]=> string(19) "21.3060004581273077"
- * }
- * [1]=> array(2) {
- * [0]=> string(22) "-156.33099943399429321"
- * [1]=> string(20) "20.79799924753607598"
- * }
- * }
- *
- */
- public function geopos(string $key, string $member)
- {
- }
-
- /**
- * Return the distance between two members in a geospatial set.
- *
- * If units are passed it must be one of the following values:
- * - 'm' => Meters
- * - 'km' => Kilometers
- * - 'mi' => Miles
- * - 'ft' => Feet
- *
- * @param string $key
- * @param string $member1
- * @param string $member2
- * @param string|null $unit
- *
- * @return float The distance between the two passed members in the units requested (meters by default)
- *
- * @link https://redis.io/commands/geodist
- * @since >= 3.2
- *
- * @example
- *
- * $redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui");
- *
- * $meters = $redis->geoDist("hawaii", "Honolulu", "Maui");
- * $kilometers = $redis->geoDist("hawaii", "Honolulu", "Maui", 'km');
- * $miles = $redis->geoDist("hawaii", "Honolulu", "Maui", 'mi');
- * $feet = $redis->geoDist("hawaii", "Honolulu", "Maui", 'ft');
- *
- * echo "Distance between Honolulu and Maui:\n";
- * echo " meters : $meters\n";
- * echo " kilometers: $kilometers\n";
- * echo " miles : $miles\n";
- * echo " feet : $feet\n";
- *
- * // Bad unit
- * $inches = $redis->geoDist("hawaii", "Honolulu", "Maui", 'in');
- * echo "Invalid unit returned:\n";
- * var_dump($inches);
- *
- * // Output
- * Distance between Honolulu and Maui:
- * meters : 168275.204
- * kilometers: 168.2752
- * miles : 104.5616
- * feet : 552084.0028
- * Invalid unit returned:
- * bool(false)
- *
- */
- public function geodist($key, $member1, $member2, $unit = null)
- {
- }
-
- /**
- * Return members of a set with geospatial information that are within the radius specified by the caller.
- *
- * @param $key
- * @param $longitude
- * @param $latitude
- * @param $radius
- * @param $unit
- * @param array|null $options
- * - * |Key |Value |Description | - * |------------|---------------|---------------------------------------------------| - * |COUNT |integer > 0 |Limit how many results are returned | - * | |WITHCOORD |Return longitude and latitude of matching members | - * | |WITHDIST |Return the distance from the center | - * | |WITHHASH |Return the raw geohash-encoded score | - * | |ASC |Sort results in ascending order | - * | |DESC |Sort results in descending order | - * |STORE |key |Store results in key | - * |STOREDIST |key |Store the results as distances in key | - *- * Note: It doesn't make sense to pass both ASC and DESC options but if both are passed - * the last one passed will be used. - * Note: When using STORE[DIST] in Redis Cluster, the store key must has to the same slot as - * the query key or you will get a CROSSLOT error. - * @return mixed When no STORE option is passed, this function returns an array of results. - * If it is passed this function returns the number of stored entries. - * - * @link https://redis.io/commands/georadius - * @since >= 3.2 - * @example - *
- * // Add some cities
- * $redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui");
- *
- * echo "Within 300 miles of Honolulu:\n";
- * var_dump($redis->geoRadius("hawaii", -157.858, 21.306, 300, 'mi'));
- *
- * echo "\nWithin 300 miles of Honolulu with distances:\n";
- * $options = ['WITHDIST'];
- * var_dump($redis->geoRadius("hawaii", -157.858, 21.306, 300, 'mi', $options));
- *
- * echo "\nFirst result within 300 miles of Honolulu with distances:\n";
- * $options['count'] = 1;
- * var_dump($redis->geoRadius("hawaii", -157.858, 21.306, 300, 'mi', $options));
- *
- * echo "\nFirst result within 300 miles of Honolulu with distances in descending sort order:\n";
- * $options[] = 'DESC';
- * var_dump($redis->geoRadius("hawaii", -157.858, 21.306, 300, 'mi', $options));
- *
- * // Output
- * Within 300 miles of Honolulu:
- * array(2) {
- * [0]=> string(8) "Honolulu"
- * [1]=> string(4) "Maui"
- * }
- *
- * Within 300 miles of Honolulu with distances:
- * array(2) {
- * [0]=>
- * array(2) {
- * [0]=>
- * string(8) "Honolulu"
- * [1]=>
- * string(6) "0.0002"
- * }
- * [1]=>
- * array(2) {
- * [0]=>
- * string(4) "Maui"
- * [1]=>
- * string(8) "104.5615"
- * }
- * }
- *
- * First result within 300 miles of Honolulu with distances:
- * array(1) {
- * [0]=>
- * array(2) {
- * [0]=>
- * string(8) "Honolulu"
- * [1]=>
- * string(6) "0.0002"
- * }
- * }
- *
- * First result within 300 miles of Honolulu with distances in descending sort order:
- * array(1) {
- * [0]=>
- * array(2) {
- * [0]=>
- * string(4) "Maui"
- * [1]=>
- * string(8) "104.5615"
- * }
- * }
- *
- */
- public function georadius($key, $longitude, $latitude, $radius, $unit, array $options = null)
- {
- }
-
- /**
- * This method is identical to geoRadius except that instead of passing a longitude and latitude as the "source"
- * you pass an existing member in the geospatial set
- *
- * @param string $key
- * @param string $member
- * @param $radius
- * @param $units
- * @param array|null $options see georadius
- *
- * @return array The zero or more entries that are close enough to the member given the distance and radius specified
- *
- * @link https://redis.io/commands/georadiusbymember
- * @since >= 3.2
- * @see georadius
- * @example
- *
- * $redis->geoAdd("hawaii", -157.858, 21.306, "Honolulu", -156.331, 20.798, "Maui");
- *
- * echo "Within 300 miles of Honolulu:\n";
- * var_dump($redis->geoRadiusByMember("hawaii", "Honolulu", 300, 'mi'));
- *
- * echo "\nFirst match within 300 miles of Honolulu:\n";
- * var_dump($redis->geoRadiusByMember("hawaii", "Honolulu", 300, 'mi', ['count' => 1]));
- *
- * // Output
- * Within 300 miles of Honolulu:
- * array(2) {
- * [0]=> string(8) "Honolulu"
- * [1]=> string(4) "Maui"
- * }
- *
- * First match within 300 miles of Honolulu:
- * array(1) {
- * [0]=> string(8) "Honolulu"
- * }
- *
- */
- public function georadiusbymember($key, $member, $radius, $units, array $options = null)
- {
- }
-
- /**
- * Get or Set the redis config keys.
- *
- * @param string $operation either `GET` or `SET`
- * @param string $key for `SET`, glob-pattern for `GET`
- * @param string|mixed $value optional string (only for `SET`)
- *
- * @return array Associative array for `GET`, key -> value
- *
- * @link https://redis.io/commands/config-get
- * @example
- *
- * $redis->config("GET", "*max-*-entries*");
- * $redis->config("SET", "dir", "/var/run/redis/dumps/");
- *
- */
- public function config($operation, $key, $value)
- {
- }
-
- /**
- * Evaluate a LUA script serverside
- *
- * @param string $script
- * @param array $args
- * @param int $numKeys
- *
- * @return mixed What is returned depends on what the LUA script itself returns, which could be a scalar value
- * (int/string), or an array. Arrays that are returned can also contain other arrays, if that's how it was set up in
- * your LUA script. If there is an error executing the LUA script, the getLastError() function can tell you the
- * message that came back from Redis (e.g. compile error).
- *
- * @link https://redis.io/commands/eval
- * @example
- *
- * $redis->eval("return 1"); // Returns an integer: 1
- * $redis->eval("return {1,2,3}"); // Returns Array(1,2,3)
- * $redis->del('mylist');
- * $redis->rpush('mylist','a');
- * $redis->rpush('mylist','b');
- * $redis->rpush('mylist','c');
- * // Nested response: Array(1,2,3,Array('a','b','c'));
- * $redis->eval("return {1,2,3,redis.call('lrange','mylist',0,-1)}}");
- *
- */
- public function eval($script, $args = array(), $numKeys = 0)
- {
- }
-
- /**
- * @param string $script
- * @param array $args
- * @param int $numKeys
- * @return mixed @see eval()
- */
- #[Deprecated(replacement: '%class%->eval(%parametersList%)')]
- public function evaluate($script, $args = array(), $numKeys = 0)
- {
- }
-
- /**
- * Evaluate a LUA script serverside, from the SHA1 hash of the script instead of the script itself.
- * In order to run this command Redis will have to have already loaded the script, either by running it or via
- * the SCRIPT LOAD command.
- *
- * @param string $scriptSha
- * @param array $args
- * @param int $numKeys
- *
- * @return mixed @see eval()
- *
- * @see eval()
- * @link https://redis.io/commands/evalsha
- * @example
- *
- * $script = 'return 1';
- * $sha = $redis->script('load', $script);
- * $redis->evalSha($sha); // Returns 1
- *
- */
- public function evalSha($scriptSha, $args = array(), $numKeys = 0)
- {
- }
-
- /**
- * @param string $scriptSha
- * @param array $args
- * @param int $numKeys
- */
- #[Deprecated(replacement: '%class%->evalSha(%parametersList%)')]
- public function evaluateSha($scriptSha, $args = array(), $numKeys = 0)
- {
- }
-
- /**
- * Execute the Redis SCRIPT command to perform various operations on the scripting subsystem.
- * @param string $command load | flush | kill | exists
- * @param string $script
- *
- * @return mixed
- *
- * @link https://redis.io/commands/script-load
- * @link https://redis.io/commands/script-kill
- * @link https://redis.io/commands/script-flush
- * @link https://redis.io/commands/script-exists
- * @example
- *
- * $redis->script('load', $script);
- * $redis->script('flush');
- * $redis->script('kill');
- * $redis->script('exists', $script1, [$script2, $script3, ...]);
- *
- *
- * SCRIPT LOAD will return the SHA1 hash of the passed script on success, and FALSE on failure.
- * SCRIPT FLUSH should always return TRUE
- * SCRIPT KILL will return true if a script was able to be killed and false if not
- * SCRIPT EXISTS will return an array with TRUE or FALSE for each passed script
- */
- public function script($command, $script)
- {
- }
-
- /**
- * The last error message (if any)
- *
- * @return string|null A string with the last returned script based error message, or NULL if there is no error
- *
- * @example
- *
- * $redis->eval('this-is-not-lua');
- * $err = $redis->getLastError();
- * // "ERR Error compiling script (new function): user_script:1: '=' expected near '-'"
- *
- */
- public function getLastError()
- {
- }
-
- /**
- * Clear the last error message
- *
- * @return bool true
- *
- * @example
- *
- * $redis->set('x', 'a');
- * $redis->incr('x');
- * $err = $redis->getLastError();
- * // "ERR value is not an integer or out of range"
- * $redis->clearLastError();
- * $err = $redis->getLastError();
- * // NULL
- *
- */
- public function clearLastError()
- {
- }
-
- /**
- * Issue the CLIENT command with various arguments.
- * The Redis CLIENT command can be used in four ways:
- * - CLIENT LIST
- * - CLIENT GETNAME
- * - CLIENT SETNAME [name]
- * - CLIENT KILL [ip:port]
- *
- * @param string $command
- * @param string $value
- * @return mixed This will vary depending on which client command was executed:
- * - CLIENT LIST will return an array of arrays with client information.
- * - CLIENT GETNAME will return the client name or false if none has been set
- * - CLIENT SETNAME will return true if it can be set and false if not
- * - CLIENT KILL will return true if the client can be killed, and false if not
- *
- * Note: phpredis will attempt to reconnect so you can actually kill your own connection but may not notice losing it!
- *
- * @link https://redis.io/commands/client-list
- * @link https://redis.io/commands/client-getname
- * @link https://redis.io/commands/client-setname
- * @link https://redis.io/commands/client-kill
- *
- * @example
- *
- * $redis->client('list'); // Get a list of clients
- * $redis->client('getname'); // Get the name of the current connection
- * $redis->client('setname', 'somename'); // Set the name of the current connection
- * $redis->client('kill', ); // Kill the process at ip:port
- *
- */
- public function client($command, $value = '')
- {
- }
-
- /**
- * A utility method to prefix the value with the prefix setting for phpredis.
- *
- * @param mixed $value The value you wish to prefix
- *
- * @return string If a prefix is set up, the value now prefixed.
- * If there is no prefix, the value will be returned unchanged.
- *
- * @example
- *
- * $redis->setOption(Redis::OPT_PREFIX, 'my-prefix:');
- * $redis->_prefix('my-value'); // Will return 'my-prefix:my-value'
- *
- */
- public function _prefix($value)
- {
- }
-
- /**
- * A utility method to unserialize data with whatever serializer is set up. If there is no serializer set, the
- * value will be returned unchanged. If there is a serializer set up, and the data passed in is malformed, an
- * exception will be thrown. This can be useful if phpredis is serializing values, and you return something from
- * redis in a LUA script that is serialized.
- *
- * @param string $value The value to be unserialized
- *
- * @return mixed
- * @example
- *
- * $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
- * $redis->_unserialize('a:3:{i:0;i:1;i:1;i:2;i:2;i:3;}'); // Will return Array(1,2,3)
- *
- */
- public function _unserialize($value)
- {
- }
-
- /**
- * A utility method to serialize values manually. This method allows you to serialize a value with whatever
- * serializer is configured, manually. This can be useful for serialization/unserialization of data going in
- * and out of EVAL commands as phpredis can't automatically do this itself. Note that if no serializer is
- * set, phpredis will change Array values to 'Array', and Objects to 'Object'.
- *
- * @param mixed $value The value to be serialized.
- *
- * @return mixed
- * @example
- *
- * $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE);
- * $redis->_serialize("foo"); // returns "foo"
- * $redis->_serialize(Array()); // Returns "Array"
- * $redis->_serialize(new stdClass()); // Returns "Object"
- *
- * $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
- * $redis->_serialize("foo"); // Returns 's:3:"foo";'
- *
- */
- public function _serialize($value)
- {
- }
-
- /**
- * Dump a key out of a redis database, the value of which can later be passed into redis using the RESTORE command.
- * The data that comes out of DUMP is a binary representation of the key as Redis stores it.
- * @param string $key
- *
- * @return string|false The Redis encoded value of the key, or FALSE if the key doesn't exist
- *
- * @link https://redis.io/commands/dump
- * @example
- *
- * $redis->set('foo', 'bar');
- * $val = $redis->dump('foo'); // $val will be the Redis encoded key value
- *
- */
- public function dump($key)
- {
- }
-
- /**
- * Restore a key from the result of a DUMP operation.
- *
- * @param string $key The key name
- * @param int $ttl How long the key should live (if zero, no expire will be set on the key)
- * @param string $value (binary). The Redis encoded key value (from DUMP)
- *
- * @return bool
- *
- * @link https://redis.io/commands/restore
- * @example
- *
- * $redis->set('foo', 'bar');
- * $val = $redis->dump('foo');
- * $redis->restore('bar', 0, $val); // The key 'bar', will now be equal to the key 'foo'
- *
- */
- public function restore($key, $ttl, $value)
- {
- }
-
- /**
- * Migrates a key to a different Redis instance.
- *
- * @param string $host The destination host
- * @param int $port The TCP port to connect to.
- * @param string $key The key to migrate.
- * @param int $db The target DB.
- * @param int $timeout The maximum amount of time given to this transfer.
- * @param bool $copy Should we send the COPY flag to redis.
- * @param bool $replace Should we send the REPLACE flag to redis.
- *
- * @return bool
- *
- * @link https://redis.io/commands/migrate
- * @example
- *
- * $redis->migrate('backup', 6379, 'foo', 0, 3600);
- *
- */
- public function migrate($host, $port, $key, $db, $timeout, $copy = false, $replace = false)
- {
- }
-
- /**
- * Return the current Redis server time.
- *
- * @return array If successful, the time will come back as an associative array with element zero being the
- * unix timestamp, and element one being microseconds.
- *
- * @link https://redis.io/commands/time
- * @example
- *
- * var_dump( $redis->time() );
- * // array(2) {
- * // [0] => string(10) "1342364352"
- * // [1] => string(6) "253002"
- * // }
- *
- */
- public function time()
- {
- }
-
- /**
- * Scan the keyspace for keys
- *
- * @param int &$iterator Iterator, initialized to NULL.
- * @param string $pattern Pattern to match.
- * @param int $count Count of keys per iteration (only a suggestion to Redis).
- *
- * @return array|false This function will return an array of keys or FALSE if there are no more keys.
- *
- * @link https://redis.io/commands/scan
- * @example
- *
- * $iterator = null;
- * while(false !== ($keys = $redis->scan($iterator))) {
- * foreach($keys as $key) {
- * echo $key . PHP_EOL;
- * }
- * }
- *
- */
- public function scan(&$iterator, $pattern = null, $count = 0)
- {
- }
-
- /**
- * Adds all the element arguments to the HyperLogLog data structure stored at the key.
- *
- * @param string $key
- * @param array $elements
- *
- * @return bool
- *
- * @link https://redis.io/commands/pfadd
- * @example $redis->pfAdd('key', array('elem1', 'elem2'))
- */
- public function pfAdd($key, array $elements)
- {
- }
-
- /**
- * When called with a single key, returns the approximated cardinality computed by the HyperLogLog data
- * structure stored at the specified variable, which is 0 if the variable does not exist.
- *
- * @param string|array $key
- *
- * @return int
- *
- * @link https://redis.io/commands/pfcount
- * @example
- *
- * $redis->pfAdd('key1', array('elem1', 'elem2'));
- * $redis->pfAdd('key2', array('elem3', 'elem2'));
- * $redis->pfCount('key1'); // int(2)
- * $redis->pfCount(array('key1', 'key2')); // int(3)
- *
- */
- public function pfCount($key)
- {
- }
-
- /**
- * Merge multiple HyperLogLog values into an unique value that will approximate the cardinality
- * of the union of the observed Sets of the source HyperLogLog structures.
- *
- * @param string $destKey
- * @param array $sourceKeys
- *
- * @return bool
- *
- * @link https://redis.io/commands/pfmerge
- * @example
- *
- * $redis->pfAdd('key1', array('elem1', 'elem2'));
- * $redis->pfAdd('key2', array('elem3', 'elem2'));
- * $redis->pfMerge('key3', array('key1', 'key2'));
- * $redis->pfCount('key3'); // int(3)
- *
- */
- public function pfMerge($destKey, array $sourceKeys)
- {
- }
-
- /**
- * Send arbitrary things to the redis server.
- *
- * @param string $command Required command to send to the server.
- * @param mixed $arguments Optional variable amount of arguments to send to the server.
- *
- * @return mixed
- *
- * @example
- *
- * $redis->rawCommand('SET', 'key', 'value'); // bool(true)
- * $redis->rawCommand('GET", 'key'); // string(5) "value"
- *
- */
- public function rawCommand($command, $arguments)
- {
- }
-
- /**
- * Detect whether we're in ATOMIC/MULTI/PIPELINE mode.
- *
- * @return int Either Redis::ATOMIC, Redis::MULTI or Redis::PIPELINE
- *
- * @example $redis->getMode();
- */
- public function getMode()
- {
- }
-
- /**
- * Acknowledge one or more messages on behalf of a consumer group.
- *
- * @param string $stream
- * @param string $group
- * @param array $messages
- *
- * @return int The number of messages Redis reports as acknowledged.
- *
- * @link https://redis.io/commands/xack
- * @example
- *
- * $redis->xAck('stream', 'group1', ['1530063064286-0', '1530063064286-1']);
- *
- */
- public function xAck($stream, $group, $messages)
- {
- }
-
- /**
- * Add a message to a stream
- *
- * @param string $key
- * @param string $id
- * @param array $messages
- * @param int $maxLen
- * @param bool $isApproximate
- *
- * @return string The added message ID.
- *
- * @link https://redis.io/commands/xadd
- * @example
- *
- * $redis->xAdd('mystream', "*", ['field' => 'value']);
- * $redis->xAdd('mystream', "*", ['field' => 'value'], 10);
- * $redis->xAdd('mystream', "*", ['field' => 'value'], 10, true);
- *
- */
- public function xAdd($key, $id, $messages, $maxLen = 0, $isApproximate = false)
- {
- }
-
- /**
- * Claim ownership of one or more pending messages
- *
- * @param string $key
- * @param string $group
- * @param string $consumer
- * @param int $minIdleTime
- * @param array $ids
- * @param array $options ['IDLE' => $value, 'TIME' => $value, 'RETRYCOUNT' => $value, 'FORCE', 'JUSTID']
- *
- * @return array Either an array of message IDs along with corresponding data, or just an array of IDs
- * (if the 'JUSTID' option was passed).
- *
- * @link https://redis.io/commands/xclaim
- * @example
- *
- * $ids = ['1530113681011-0', '1530113681011-1', '1530113681011-2'];
- *
- * // Without any options
- * $redis->xClaim('mystream', 'group1', 'myconsumer1', 0, $ids);
- *
- * // With options
- * $redis->xClaim(
- * 'mystream', 'group1', 'myconsumer2', 0, $ids,
- * [
- * 'IDLE' => time() * 1000,
- * 'RETRYCOUNT' => 5,
- * 'FORCE',
- * 'JUSTID'
- * ]
- * );
- *
- */
- public function xClaim($key, $group, $consumer, $minIdleTime, $ids, $options = [])
- {
- }
-
- /**
- * Delete one or more messages from a stream
- *
- * @param string $key
- * @param array $ids
- *
- * @return int The number of messages removed
- *
- * @link https://redis.io/commands/xdel
- * @example
- *
- * $redis->xDel('mystream', ['1530115304877-0', '1530115305731-0']);
- *
- */
- public function xDel($key, $ids)
- {
- }
-
- /**
- * @param string $operation e.g.: 'HELP', 'SETID', 'DELGROUP', 'CREATE', 'DELCONSUMER'
- * @param string $key
- * @param string $group
- * @param string $msgId
- * @param bool $mkStream
- *
- * @return mixed This command returns different types depending on the specific XGROUP command executed.
- *
- * @link https://redis.io/commands/xgroup
- * @example
- *
- * $redis->xGroup('CREATE', 'mystream', 'mygroup', 0);
- * $redis->xGroup('CREATE', 'mystream', 'mygroup', 0, true); // create stream
- * $redis->xGroup('DESTROY', 'mystream', 'mygroup');
- *
- */
- public function xGroup($operation, $key, $group, $msgId = '', $mkStream = false)
- {
- }
-
- /**
- * Get information about a stream or consumer groups
- *
- * @param string $operation e.g.: 'CONSUMERS', 'GROUPS', 'STREAM', 'HELP'
- * @param string $stream
- * @param string $group
- *
- * @return mixed This command returns different types depending on which subcommand is used.
- *
- * @link https://redis.io/commands/xinfo
- * @example
- *
- * $redis->xInfo('STREAM', 'mystream');
- *
- */
- public function xInfo($operation, $stream, $group)
- {
- }
-
- /**
- * Get the length of a given stream.
- *
- * @param string $stream
- *
- * @return int The number of messages in the stream.
- *
- * @link https://redis.io/commands/xlen
- * @example
- *
- * $redis->xLen('mystream');
- *
- */
- public function xLen($stream)
- {
- }
-
- /**
- * Get information about pending messages in a given stream
- *
- * @param string $stream
- * @param string $group
- * @param string $start
- * @param string $end
- * @param int $count
- * @param string $consumer
- *
- * @return array Information about the pending messages, in various forms depending on
- * the specific invocation of XPENDING.
- *
- * @link https://redis.io/commands/xpending
- * @example
- *
- * $redis->xPending('mystream', 'mygroup');
- * $redis->xPending('mystream', 'mygroup', '-', '+', 1, 'consumer-1');
- *
- */
- public function xPending($stream, $group, $start = null, $end = null, $count = null, $consumer = null)
- {
- }
-
- /**
- * Get a range of messages from a given stream
- *
- * @param string $stream
- * @param string $start
- * @param string $end
- * @param int $count
- *
- * @return array The messages in the stream within the requested range.
- *
- * @link https://redis.io/commands/xrange
- * @example
- *
- * // Get everything in this stream
- * $redis->xRange('mystream', '-', '+');
- * // Only the first two messages
- * $redis->xRange('mystream', '-', '+', 2);
- *
- */
- public function xRange($stream, $start, $end, $count = null)
- {
- }
-
- /**
- * Read data from one or more streams and only return IDs greater than sent in the command.
- *
- * @param array $streams
- * @param int|string $count
- * @param int|string $block
- *
- * @return array The messages in the stream newer than the IDs passed to Redis (if any)
- *
- * @link https://redis.io/commands/xread
- * @example
- * - * $redis->xRead(['stream1' => '1535222584555-0', 'stream2' => '1535222584555-0']); - *- */ - public function xRead($streams, $count = null, $block = null) - { - } - - /** - * This method is similar to xRead except that it supports reading messages for a specific consumer group. - * - * @param string $group - * @param string $consumer - * @param array $streams - * @param int|null $count - * @param int|null $block - * - * @return array The messages delivered to this consumer group (if any). - * - * @link https://redis.io/commands/xreadgroup - * @example - *
- * // Consume messages for 'mygroup', 'consumer1'
- * $redis->xReadGroup('mygroup', 'consumer1', ['s1' => 0, 's2' => 0]);
- * // Read a single message as 'consumer2' for up to a second until a message arrives.
- * $redis->xReadGroup('mygroup', 'consumer2', ['s1' => 0, 's2' => 0], 1, 1000);
- *
- */
- public function xReadGroup($group, $consumer, $streams, $count = null, $block = null)
- {
- }
-
- /**
- * This is identical to xRange except the results come back in reverse order.
- * Also note that Redis reverses the order of "start" and "end".
- *
- * @param string $stream
- * @param string $end
- * @param string $start
- * @param int $count
- *
- * @return array The messages in the range specified
- *
- * @link https://redis.io/commands/xrevrange
- * @example
- *
- * $redis->xRevRange('mystream', '+', '-');
- *
- */
- public function xRevRange($stream, $end, $start, $count = null)
- {
- }
-
- /**
- * Trim the stream length to a given maximum.
- * If the "approximate" flag is pasesed, Redis will use your size as a hint but only trim trees in whole nodes
- * (this is more efficient)
- *
- * @param string $stream
- * @param int $maxLen
- * @param bool $isApproximate
- *
- * @return int The number of messages trimed from the stream.
- *
- * @link https://redis.io/commands/xtrim
- * @example
- *
- * // Trim to exactly 100 messages
- * $redis->xTrim('mystream', 100);
- * // Let Redis approximate the trimming
- * $redis->xTrim('mystream', 100, true);
- *
- */
- public function xTrim($stream, $maxLen, $isApproximate)
- {
- }
-
- /**
- * Adds a values to the set value stored at key.
- *
- * @param string $key Required key
- * @param array $values Required values
- *
- * @return int|bool The number of elements added to the set.
- * If this value is already in the set, FALSE is returned
- *
- * @link https://redis.io/commands/sadd
- * @link https://github.com/phpredis/phpredis/commit/3491b188e0022f75b938738f7542603c7aae9077
- * @since phpredis 2.2.8
- * @example
- *
- * $redis->sAddArray('k', array('v1')); // boolean
- * $redis->sAddArray('k', array('v1', 'v2', 'v3')); // boolean
- *
- */
- public function sAddArray($key, array $values)
- {
- }
-}
-
-class RedisException extends Exception
-{
-}
-
-/**
- * @mixin \Redis
- */
-class RedisArray
-{
- /**
- * Constructor
- *
- * @param string|string[] $hosts Name of the redis array from redis.ini or array of hosts to construct the array with
- * @param null|array $opts Array of options
- *
- * @link https://github.com/nicolasff/phpredis/blob/master/arrays.markdown
- */
- public function __construct($hosts, ?array $opts = null)
- {
- }
-
- /**
- * @return array list of hosts for the selected array
- */
- public function _hosts()
- {
- }
-
- /**
- * @return string the name of the function used to extract key parts during consistent hashing
- */
- public function _function()
- {
- }
-
- /**
- * @param string $key The key for which you want to lookup the host
- *
- * @return string the host to be used for a certain key
- */
- public function _target($key)
- {
- }
-
- /**
- * @param string $host The host you want to retrieve the instance for
- *
- * @return Redis a redis instance connected to a specific node
- */
- public function _instance($host)
- {
- }
-
- /**
- * Use this function when a new node is added and keys need to be rehashed.
- */
- public function _rehash()
- {
- }
-
- /**
- * Returns an associative array of strings and integers, with the following keys:
- * - redis_version
- * - redis_git_sha1
- * - redis_git_dirty
- * - redis_build_id
- * - redis_mode
- * - os
- * - arch_bits
- * - multiplexing_api
- * - atomicvar_api
- * - gcc_version
- * - process_id
- * - run_id
- * - tcp_port
- * - uptime_in_seconds
- * - uptime_in_days
- * - hz
- * - lru_clock
- * - executable
- * - config_file
- * - connected_clients
- * - client_longest_output_list
- * - client_biggest_input_buf
- * - blocked_clients
- * - used_memory
- * - used_memory_human
- * - used_memory_rss
- * - used_memory_rss_human
- * - used_memory_peak
- * - used_memory_peak_human
- * - used_memory_peak_perc
- * - used_memory_peak
- * - used_memory_overhead
- * - used_memory_startup
- * - used_memory_dataset
- * - used_memory_dataset_perc
- * - total_system_memory
- * - total_system_memory_human
- * - used_memory_lua
- * - used_memory_lua_human
- * - maxmemory
- * - maxmemory_human
- * - maxmemory_policy
- * - mem_fragmentation_ratio
- * - mem_allocator
- * - active_defrag_running
- * - lazyfree_pending_objects
- * - mem_fragmentation_ratio
- * - loading
- * - rdb_changes_since_last_save
- * - rdb_bgsave_in_progress
- * - rdb_last_save_time
- * - rdb_last_bgsave_status
- * - rdb_last_bgsave_time_sec
- * - rdb_current_bgsave_time_sec
- * - rdb_last_cow_size
- * - aof_enabled
- * - aof_rewrite_in_progress
- * - aof_rewrite_scheduled
- * - aof_last_rewrite_time_sec
- * - aof_current_rewrite_time_sec
- * - aof_last_bgrewrite_status
- * - aof_last_write_status
- * - aof_last_cow_size
- * - changes_since_last_save
- * - aof_current_size
- * - aof_base_size
- * - aof_pending_rewrite
- * - aof_buffer_length
- * - aof_rewrite_buffer_length
- * - aof_pending_bio_fsync
- * - aof_delayed_fsync
- * - loading_start_time
- * - loading_total_bytes
- * - loading_loaded_bytes
- * - loading_loaded_perc
- * - loading_eta_seconds
- * - total_connections_received
- * - total_commands_processed
- * - instantaneous_ops_per_sec
- * - total_net_input_bytes
- * - total_net_output_bytes
- * - instantaneous_input_kbps
- * - instantaneous_output_kbps
- * - rejected_connections
- * - maxclients
- * - sync_full
- * - sync_partial_ok
- * - sync_partial_err
- * - expired_keys
- * - evicted_keys
- * - keyspace_hits
- * - keyspace_misses
- * - pubsub_channels
- * - pubsub_patterns
- * - latest_fork_usec
- * - migrate_cached_sockets
- * - slave_expires_tracked_keys
- * - active_defrag_hits
- * - active_defrag_misses
- * - active_defrag_key_hits
- * - active_defrag_key_misses
- * - role
- * - master_replid
- * - master_replid2
- * - master_repl_offset
- * - second_repl_offset
- * - repl_backlog_active
- * - repl_backlog_size
- * - repl_backlog_first_byte_offset
- * - repl_backlog_histlen
- * - master_host
- * - master_port
- * - master_link_status
- * - master_last_io_seconds_ago
- * - master_sync_in_progress
- * - slave_repl_offset
- * - slave_priority
- * - slave_read_only
- * - master_sync_left_bytes
- * - master_sync_last_io_seconds_ago
- * - master_link_down_since_seconds
- * - connected_slaves
- * - min-slaves-to-write
- * - min-replicas-to-write
- * - min_slaves_good_slaves
- * - used_cpu_sys
- * - used_cpu_user
- * - used_cpu_sys_children
- * - used_cpu_user_children
- * - cluster_enabled
- *
- * @link https://redis.io/commands/info
- * @return array
- * @example
- * - * $redis->info(); - *- */ - public function info() { - } -} --- a/build/stubs/redis_cluster.php +++ /dev/null @@ -1,3575 +0,0 @@ - - * @link https://github.com/zgb7mtr/phpredis_cluster_phpdoc - * - * @method mixed eval($script, $args = array(), $numKeys = 0) - * - */ -class RedisCluster { - const AFTER = 'after'; - const BEFORE = 'before'; - - /** - * Options - */ - const OPT_SERIALIZER = 1; - const OPT_PREFIX = 2; - const OPT_READ_TIMEOUT = 3; - const OPT_SCAN = 4; - const OPT_SLAVE_FAILOVER = 5; - - /** - * Cluster options - */ - const FAILOVER_NONE = 0; - const FAILOVER_ERROR = 1; - const FAILOVER_DISTRIBUTE = 2; - const FAILOVER_DISTRIBUTE_SLAVES = 3; - - /** - * SCAN options - */ - const SCAN_NORETRY = 0; - const SCAN_RETRY = 1; - - /** - * @since 5.3.0 - */ - const SCAN_PREFIX = 2; - - /** - * @since 5.3.0 - */ - const SCAN_NOPREFIX = 3; - - /** - * Serializers - */ - const SERIALIZER_NONE = 0; - const SERIALIZER_PHP = 1; - const SERIALIZER_IGBINARY = 2; - const SERIALIZER_MSGPACK = 3; - const SERIALIZER_JSON = 4; - - /** - * Multi - */ - const ATOMIC = 0; - const MULTI = 1; - const PIPELINE = 2; - - /** - * Type - */ - const REDIS_NOT_FOUND = 0; - const REDIS_STRING = 1; - const REDIS_SET = 2; - const REDIS_LIST = 3; - const REDIS_ZSET = 4; - const REDIS_HASH = 5; - - /** - * Creates a Redis Cluster client - * - * @param string|null $name - * @param array $seeds - * @param float $timeout - * @param float $readTimeout - * @param bool $persistent - * @param string|string[]|null $auth - * @param array $connectionParameters extra config to send to redis - * @throws RedisClusterException - * - * @example - *
- * // Declaring a cluster with an array of seeds
- * $redisCluster = new RedisCluster(null,['127.0.0.1:6379']);
- *
- * // Loading a cluster configuration by name
- * // In order to load a named array, one must first define the seed nodes in redis.ini.
- * // The following lines would define the cluster 'mycluster', and be loaded automatically by phpredis.
- *
- * // # In redis.ini
- * // redis.clusters.seeds = "mycluster[]=localhost:7000&test[]=localhost:7001"
- * // redis.clusters.timeout = "mycluster=5"
- * // redis.clusters.read_timeout = "mycluster=10"
- *
- * //Then, this cluster can be loaded by doing the following
- *
- * $redisClusterPro = new RedisCluster('mycluster');
- * $redisClusterDev = new RedisCluster('test');
- *
- */
- public function __construct($name, $seeds, $timeout = null, $readTimeout = null, $persistent = false, $auth = null, $connectionParameters = []) { }
-
- /**
- * Disconnects from the Redis instance, except when pconnect is used.
- */
- public function close() { }
-
- /**
- * Get the value related to the specified key
- *
- * @param string $key
- *
- * @return string|false If key didn't exist, FALSE is returned. Otherwise, the value related to this key is
- * returned.
- *
- * @link https://redis.io/commands/get
- * @example
- *
- * $redisCluster->get('key');
- *
- */
- public function get($key) { }
-
- /**
- * Set the string value in argument as value of the key.
- *
- * @since If you're using Redis >= 2.6.12, you can pass extended options as explained in example
- *
- * @param string $key
- * @param string $value
- * @param int|array $timeout If you pass an integer, phpredis will redirect to SETEX, and will try to use Redis
- * >= 2.6.12 extended options if you pass an array with valid values.
- *
- * @return bool TRUE if the command is successful.
- *
- * @link https://redis.io/commands/set
- * @example
- *
- * // Simple key -> value set
- * $redisCluster->set('key', 'value');
- *
- * // Will redirect, and actually make an SETEX call
- * $redisCluster->set('key','value', 10);
- *
- * // Will set the key, if it doesn't exist, with a ttl of 10 seconds
- * $redisCluster->set('key', 'value', Array('nx', 'ex'=>10));
- *
- * // Will set a key, if it does exist, with a ttl of 1000 milliseconds
- * $redisCluster->set('key', 'value', Array('xx', 'px'=>1000));
- *
- */
- public function set($key, $value, $timeout = null) { }
-
- /**
- * Returns the values of all specified keys.
- *
- * For every key that does not hold a string value or does not exist,
- * the special value false is returned. Because of this, the operation never fails.
- *
- * @param array $array
- *
- * @return array
- *
- * @link https://redis.io/commands/mget
- * @example
- *
- * $redisCluster->del('x', 'y', 'z', 'h'); // remove x y z
- * $redisCluster->mset(array('x' => 'a', 'y' => 'b', 'z' => 'c'));
- * $redisCluster->hset('h', 'field', 'value');
- * var_dump($redisCluster->mget(array('x', 'y', 'z', 'h')));
- * // Output:
- * // array(3) {
- * // [0]=>
- * // string(1) "a"
- * // [1]=>
- * // string(1) "b"
- * // [2]=>
- * // string(1) "c"
- * // [3]=>
- * // bool(false)
- * // }
- *
- */
- public function mget(array $array) { }
-
- /**
- * Sets multiple key-value pairs in one atomic command.
- * MSETNX only returns TRUE if all the keys were set (see SETNX).
- *
- * @param array $array Pairs: array(key => value, ...)
- *
- * @return bool TRUE in case of success, FALSE in case of failure.
- * @link https://redis.io/commands/mset
- * @example
- *
- * $redisCluster->mset(array('key0' => 'value0', 'key1' => 'value1'));
- * var_dump($redisCluster->get('key0'));
- * var_dump($redisCluster->get('key1'));
- * // Output:
- * // string(6) "value0"
- * // string(6) "value1"
- *
- */
- public function mset(array $array) { }
-
- /**
- * @see mset()
- *
- * @param array $array
- *
- * @return int 1 (if the keys were set) or 0 (no key was set)
- * @link https://redis.io/commands/msetnx
- */
- public function msetnx(array $array) { }
-
- /**
- * Remove specified keys.
- *
- * @param int|string|array $key1 An array of keys, or an undefined number of parameters, each a key: key1 key2 key3
- * ... keyN
- * @param int|string ...$otherKeys
- *
- * @return int Number of keys deleted.
- * @link https://redis.io/commands/del
- * @example
- *
- * $redisCluster->set('key1', 'val1');
- * $redisCluster->set('key2', 'val2');
- * $redisCluster->set('key3', 'val3');
- * $redisCluster->set('key4', 'val4');
- * $redisCluster->del('key1', 'key2'); // return 2
- * $redisCluster->del(array('key3', 'key4')); // return 2
- *
- */
- public function del($key1, ...$otherKeys) { }
-
- /**
- * Set the string value in argument as value of the key, with a time to live.
- *
- * @param string $key
- * @param int $ttl
- * @param string $value
- *
- * @return bool TRUE if the command is successful.
- * @link https://redis.io/commands/setex
- * @example
- *
- * $redisCluster->setex('key', 3600, 'value'); // sets key → value, with 1h TTL.
- *
- */
- public function setex($key, $ttl, $value) { }
-
- /**
- * PSETEX works exactly like SETEX with the sole difference that the expire time is specified in milliseconds
- * instead of seconds.
- *
- * @param string $key
- * @param int $ttl
- * @param string $value
- *
- * @return bool TRUE if the command is successful.
- * @link https://redis.io/commands/psetex
- * @example
- *
- * $redisCluster->psetex('key', 1000, 'value'); // sets key → value, with 1s TTL.
- *
- */
- public function psetex($key, $ttl, $value) { }
-
- /**
- * Set the string value in argument as value of the key if the key doesn't already exist in the database.
- *
- * @param string $key
- * @param string $value
- *
- * @return bool TRUE in case of success, FALSE in case of failure.
- * @link https://redis.io/commands/setnx
- * @example
- *
- * $redisCluster->setnx('key', 'value'); // return TRUE
- * $redisCluster->setnx('key', 'value'); // return FALSE
- *
- */
- public function setnx($key, $value) { }
-
- /**
- * Sets a value and returns the previous entry at that key.
- *
- * @param string $key
- * @param string $value
- *
- * @return string A string, the previous value located at this key.
- * @link https://redis.io/commands/getset
- * @example
- *
- * $redisCluster->set('x', '42');
- * $exValue = $redisCluster->getSet('x', 'lol'); // return '42', replaces x by 'lol'
- * $newValue = $redisCluster->get('x'); // return 'lol'
- *
- */
- public function getSet($key, $value) { }
-
- /**
- * Verify if the specified key exists.
- *
- * @param string $key
- *
- * @return bool If the key exists, return TRUE, otherwise return FALSE.
- * @link https://redis.io/commands/exists
- * @example
- *
- * $redisCluster->set('key', 'value');
- * $redisCluster->exists('key'); // TRUE
- * $redisCluster->exists('NonExistingKey'); // FALSE
- *
- */
- public function exists($key) { }
-
- /**
- * Returns the keys that match a certain pattern.
- *
- * @param string $pattern pattern, using '*' as a wildcard.
- *
- * @return array of STRING: The keys that match a certain pattern.
- * @link https://redis.io/commands/keys
- * @example
- *
- * $allKeys = $redisCluster->keys('*'); // all keys will match this.
- * $keyWithUserPrefix = $redisCluster->keys('user*');
- *
- */
- public function keys($pattern) { }
-
- /**
- * Returns the type of data pointed by a given key.
- *
- * @param string $key
- *
- * @return int
- *
- * Depending on the type of the data pointed by the key,
- * this method will return the following value:
- * - string: RedisCluster::REDIS_STRING
- * - set: RedisCluster::REDIS_SET
- * - list: RedisCluster::REDIS_LIST
- * - zset: RedisCluster::REDIS_ZSET
- * - hash: RedisCluster::REDIS_HASH
- * - other: RedisCluster::REDIS_NOT_FOUND
- * @link https://redis.io/commands/type
- * @example $redisCluster->type('key');
- */
- public function type($key) { }
-
- /**
- * Returns and removes the first element of the list.
- *
- * @param string $key
- *
- * @return string|false if command executed successfully BOOL FALSE in case of failure (empty list)
- * @link https://redis.io/commands/lpop
- * @example
- *
- * $redisCluster->rPush('key1', 'A');
- * $redisCluster->rPush('key1', 'B');
- * $redisCluster->rPush('key1', 'C');
- * var_dump( $redisCluster->lRange('key1', 0, -1) );
- * // Output:
- * // array(3) {
- * // [0]=> string(1) "A"
- * // [1]=> string(1) "B"
- * // [2]=> string(1) "C"
- * // }
- * $redisCluster->lPop('key1');
- * var_dump( $redisCluster->lRange('key1', 0, -1) );
- * // Output:
- * // array(2) {
- * // [0]=> string(1) "B"
- * // [1]=> string(1) "C"
- * // }
- *
- */
- public function lPop($key) { }
-
- /**
- * Returns and removes the last element of the list.
- *
- * @param string $key
- *
- * @return string|false if command executed successfully BOOL FALSE in case of failure (empty list)
- * @link https://redis.io/commands/rpop
- * @example
- *
- * $redisCluster->rPush('key1', 'A');
- * $redisCluster->rPush('key1', 'B');
- * $redisCluster->rPush('key1', 'C');
- * var_dump( $redisCluster->lRange('key1', 0, -1) );
- * // Output:
- * // array(3) {
- * // [0]=> string(1) "A"
- * // [1]=> string(1) "B"
- * // [2]=> string(1) "C"
- * // }
- * $redisCluster->rPop('key1');
- * var_dump( $redisCluster->lRange('key1', 0, -1) );
- * // Output:
- * // array(2) {
- * // [0]=> string(1) "A"
- * // [1]=> string(1) "B"
- * // }
- *
- */
- public function rPop($key) { }
-
- /**
- * Set the list at index with the new value.
- *
- * @param string $key
- * @param int $index
- * @param string $value
- *
- * @return bool TRUE if the new value is setted. FALSE if the index is out of range, or data type identified by key
- * is not a list.
- * @link https://redis.io/commands/lset
- * @example
- *
- * $redisCluster->rPush('key1', 'A');
- * $redisCluster->rPush('key1', 'B');
- * $redisCluster->rPush('key1', 'C'); // key1 => [ 'A', 'B', 'C' ]
- * $redisCluster->lGet('key1', 0); // 'A'
- * $redisCluster->lSet('key1', 0, 'X');
- * $redisCluster->lGet('key1', 0); // 'X'
- *
- */
- public function lSet($key, $index, $value) { }
-
- /**
- * Removes and returns a random element from the set value at Key.
- *
- * @param string $key
- *
- * @return string "popped" value
- * bool FALSE if set identified by key is empty or doesn't exist.
- * @link https://redis.io/commands/spop
- * @example
- *
- * $redisCluster->sAdd('key1' , 'set1');
- * $redisCluster->sAdd('key1' , 'set2');
- * $redisCluster->sAdd('key1' , 'set3');
- * var_dump($redisCluster->sMembers('key1'));// 'key1' => {'set3', 'set1', 'set2'}
- * $redisCluster->sPop('key1');// 'set1'
- * var_dump($redisCluster->sMembers('key1'));// 'key1' => {'set3', 'set2'}
- * $redisCluster->sPop('key1');// 'set3',
- * var_dump($redisCluster->sMembers('key1'));// 'key1' => {'set2'}
- *
- */
- public function sPop($key) { }
-
- /**
- * Adds the string values to the head (left) of the list. Creates the list if the key didn't exist.
- * If the key exists and is not a list, FALSE is returned.
- *
- * @param string $key
- * @param string $value1 String, value to push in key
- * @param string $value2 Optional
- * @param string $valueN Optional
- *
- * @return int|false The new length of the list in case of success, FALSE in case of Failure.
- * @link https://redis.io/commands/lpush
- * @example
- *
- * $redisCluster->lPush('l', 'v1', 'v2', 'v3', 'v4') // int(4)
- * var_dump( $redisCluster->lRange('l', 0, -1) );
- * //// Output:
- * // array(4) {
- * // [0]=> string(2) "v4"
- * // [1]=> string(2) "v3"
- * // [2]=> string(2) "v2"
- * // [3]=> string(2) "v1"
- * // }
- *
- */
- public function lPush($key, $value1, $value2 = null, $valueN = null) { }
-
- /**
- * Adds the string values to the tail (right) of the list. Creates the list if the key didn't exist.
- * If the key exists and is not a list, FALSE is returned.
- *
- * @param string $key
- * @param string $value1 String, value to push in key
- * @param string $value2 Optional
- * @param string $valueN Optional
- *
- * @return int|false The new length of the list in case of success, FALSE in case of Failure.
- * @link https://redis.io/commands/rpush
- * @example
- *
- * $redisCluster->rPush('r', 'v1', 'v2', 'v3', 'v4'); // int(4)
- * var_dump( $redisCluster->lRange('r', 0, -1) );
- * //// Output:
- * // array(4) {
- * // [0]=> string(2) "v1"
- * // [1]=> string(2) "v2"
- * // [2]=> string(2) "v3"
- * // [3]=> string(2) "v4"
- * // }
- *
- */
- public function rPush($key, $value1, $value2 = null, $valueN = null) { }
-
- /**
- * BLPOP is a blocking list pop primitive.
- * It is the blocking version of LPOP because it blocks the connection when
- * there are no elements to pop from any of the given lists.
- * An element is popped from the head of the first list that is non-empty,
- * with the given keys being checked in the order that they are given.
- *
- * @param array $keys Array containing the keys of the lists
- * Or STRING Key1 STRING Key2 STRING Key3 ... STRING Keyn
- * @param int $timeout Timeout
- *
- * @return array array('listName', 'element')
- * @link https://redis.io/commands/blpop
- * @example
- *
- * // Non blocking feature
- * $redisCluster->lPush('key1', 'A');
- * $redisCluster->del('key2');
- *
- * $redisCluster->blPop('key1', 'key2', 10); // array('key1', 'A')
- * // OR
- * $redisCluster->blPop(array('key1', 'key2'), 10); // array('key1', 'A')
- *
- * $redisCluster->brPop('key1', 'key2', 10); // array('key1', 'A')
- * // OR
- * $redisCluster->brPop(array('key1', 'key2'), 10); // array('key1', 'A')
- *
- * // Blocking feature
- *
- * // process 1
- * $redisCluster->del('key1');
- * $redisCluster->blPop('key1', 10);
- * // blocking for 10 seconds
- *
- * // process 2
- * $redisCluster->lPush('key1', 'A');
- *
- * // process 1
- * // array('key1', 'A') is returned
- *
- */
- public function blPop(array $keys, $timeout) { }
-
- /**
- * BRPOP is a blocking list pop primitive.
- * It is the blocking version of RPOP because it blocks the connection when
- * there are no elements to pop from any of the given lists.
- * An element is popped from the tail of the first list that is non-empty,
- * with the given keys being checked in the order that they are given.
- * See the BLPOP documentation(https://redis.io/commands/blpop) for the exact semantics,
- * since BRPOP is identical to BLPOP with the only difference being that
- * it pops elements from the tail of a list instead of popping from the head.
- *
- * @param array $keys Array containing the keys of the lists
- * Or STRING Key1 STRING Key2 STRING Key3 ... STRING Keyn
- * @param int $timeout Timeout
- *
- * @return array array('listName', 'element')
- * @link https://redis.io/commands/brpop
- * @example
- *
- * // Non blocking feature
- * $redisCluster->lPush('key1', 'A');
- * $redisCluster->del('key2');
- *
- * $redisCluster->blPop('key1', 'key2', 10); // array('key1', 'A')
- * // OR
- * $redisCluster->blPop(array('key1', 'key2'), 10); // array('key1', 'A')
- *
- * $redisCluster->brPop('key1', 'key2', 10); // array('key1', 'A')
- * // OR
- * $redisCluster->brPop(array('key1', 'key2'), 10); // array('key1', 'A')
- *
- * // Blocking feature
- *
- * // process 1
- * $redisCluster->del('key1');
- * $redisCluster->blPop('key1', 10);
- * // blocking for 10 seconds
- *
- * // process 2
- * $redisCluster->lPush('key1', 'A');
- *
- * // process 1
- * // array('key1', 'A') is returned
- *
- */
- public function brPop(array $keys, $timeout) { }
-
- /**
- * Adds the string value to the tail (right) of the list if the ist exists. FALSE in case of Failure.
- *
- * @param string $key
- * @param string $value String, value to push in key
- *
- * @return int|false The new length of the list in case of success, FALSE in case of Failure.
- * @link https://redis.io/commands/rpushx
- * @example
- *
- * $redisCluster->del('key1');
- * $redisCluster->rPushx('key1', 'A'); // returns 0
- * $redisCluster->rPush('key1', 'A'); // returns 1
- * $redisCluster->rPushx('key1', 'B'); // returns 2
- * $redisCluster->rPushx('key1', 'C'); // returns 3
- * // key1 now points to the following list: [ 'A', 'B', 'C' ]
- *
- */
- public function rPushx($key, $value) { }
-
- /**
- * Adds the string value to the head (left) of the list if the list exists.
- *
- * @param string $key
- * @param string $value String, value to push in key
- *
- * @return int|false The new length of the list in case of success, FALSE in case of Failure.
- * @link https://redis.io/commands/lpushx
- * @example
- *
- * $redisCluster->del('key1');
- * $redisCluster->lPushx('key1', 'A'); // returns 0
- * $redisCluster->lPush('key1', 'A'); // returns 1
- * $redisCluster->lPushx('key1', 'B'); // returns 2
- * $redisCluster->lPushx('key1', 'C'); // returns 3
- * // key1 now points to the following list: [ 'C', 'B', 'A' ]
- *
- */
- public function lPushx($key, $value) { }
-
- /**
- * Insert value in the list before or after the pivot value. the parameter options
- * specify the position of the insert (before or after). If the list didn't exists,
- * or the pivot didn't exists, the value is not inserted.
- *
- * @param string $key
- * @param int $position RedisCluster::BEFORE | RedisCluster::AFTER
- * @param string $pivot
- * @param string $value
- *
- * @return int The number of the elements in the list, -1 if the pivot didn't exists.
- * @link https://redis.io/commands/linsert
- * @example
- *
- * $redisCluster->del('key1');
- * $redisCluster->lInsert('key1', RedisCluster::AFTER, 'A', 'X'); // 0
- *
- * $redisCluster->lPush('key1', 'A');
- * $redisCluster->lPush('key1', 'B');
- * $redisCluster->lPush('key1', 'C');
- *
- * $redisCluster->lInsert('key1', RedisCluster::BEFORE, 'C', 'X'); // 4
- * $redisCluster->lRange('key1', 0, -1); // array('X', 'C', 'B', 'A')
- *
- * $redisCluster->lInsert('key1', RedisCluster::AFTER, 'C', 'Y'); // 5
- * $redisCluster->lRange('key1', 0, -1); // array('X', 'C', 'Y', 'B', 'A')
- *
- * $redisCluster->lInsert('key1', RedisCluster::AFTER, 'W', 'value'); // -1
- *
- */
- public function lInsert($key, $position, $pivot, $value) { }
-
- /**
- * Return the specified element of the list stored at the specified key.
- * 0 the first element, 1 the second ... -1 the last element, -2 the penultimate ...
- * Return FALSE in case of a bad index or a key that doesn't point to a list.
- *
- * @param string $key
- * @param int $index
- *
- * @return string|false the element at this index
- * Bool FALSE if the key identifies a non-string data type, or no value corresponds to this index in the list Key.
- * @link https://redis.io/commands/lindex
- * @example
- *
- * $redisCluster->rPush('key1', 'A');
- * $redisCluster->rPush('key1', 'B');
- * $redisCluster->rPush('key1', 'C'); // key1 => [ 'A', 'B', 'C' ]
- * $redisCluster->lGet('key1', 0); // 'A'
- * $redisCluster->lGet('key1', -1); // 'C'
- * $redisCluster->lGet('key1', 10); // `FALSE`
- *
- */
- public function lIndex($key, $index) { }
-
- /**
- * Removes the first count occurrences of the value element from the list.
- * If count is zero, all the matching elements are removed. If count is negative,
- * elements are removed from tail to head.
- *
- * @param string $key
- * @param string $value
- * @param int $count
- *
- * @return int the number of elements to remove
- * bool FALSE if the value identified by key is not a list.
- * @link https://redis.io/commands/lrem
- * @example
- *
- * $redisCluster->lPush('key1', 'A');
- * $redisCluster->lPush('key1', 'B');
- * $redisCluster->lPush('key1', 'C');
- * $redisCluster->lPush('key1', 'A');
- * $redisCluster->lPush('key1', 'A');
- *
- * $redisCluster->lRange('key1', 0, -1); // array('A', 'A', 'C', 'B', 'A')
- * $redisCluster->lRem('key1', 'A', 2); // 2
- * $redisCluster->lRange('key1', 0, -1); // array('C', 'B', 'A')
- *
- */
- public function lRem($key, $value, $count) { }
-
- /**
- * A blocking version of rpoplpush, with an integral timeout in the third parameter.
- *
- * @param string $srcKey
- * @param string $dstKey
- * @param int $timeout
- *
- * @return string|false The element that was moved in case of success, FALSE in case of timeout.
- * @link https://redis.io/commands/brpoplpush
- */
- public function brpoplpush($srcKey, $dstKey, $timeout) { }
-
- /**
- * Pops a value from the tail of a list, and pushes it to the front of another list.
- * Also return this value.
- *
- * @since redis >= 1.2
- *
- * @param string $srcKey
- * @param string $dstKey
- *
- * @return string|false The element that was moved in case of success, FALSE in case of failure.
- * @link https://redis.io/commands/rpoplpush
- * @example
- *
- * $redisCluster->del('x', 'y');
- *
- * $redisCluster->lPush('x', 'abc');
- * $redisCluster->lPush('x', 'def');
- * $redisCluster->lPush('y', '123');
- * $redisCluster->lPush('y', '456');
- *
- * // move the last of x to the front of y.
- * var_dump($redisCluster->rpoplpush('x', 'y'));
- * var_dump($redisCluster->lRange('x', 0, -1));
- * var_dump($redisCluster->lRange('y', 0, -1));
- *
- * ////Output:
- * //
- * //string(3) "abc"
- * //array(1) {
- * // [0]=>
- * // string(3) "def"
- * //}
- * //array(3) {
- * // [0]=>
- * // string(3) "abc"
- * // [1]=>
- * // string(3) "456"
- * // [2]=>
- * // string(3) "123"
- * //}
- *
- */
- public function rpoplpush($srcKey, $dstKey) { }
-
- /**
- * Returns the size of a list identified by Key. If the list didn't exist or is empty,
- * the command returns 0. If the data type identified by Key is not a list, the command return FALSE.
- *
- * @param string $key
- *
- * @return int The size of the list identified by Key exists.
- * bool FALSE if the data type identified by Key is not list
- * @link https://redis.io/commands/llen
- * @example
- *
- * $redisCluster->rPush('key1', 'A');
- * $redisCluster->rPush('key1', 'B');
- * $redisCluster->rPush('key1', 'C'); // key1 => [ 'A', 'B', 'C' ]
- * $redisCluster->lLen('key1'); // 3
- * $redisCluster->rPop('key1');
- * $redisCluster->lLen('key1'); // 2
- *
- */
- public function lLen($key) { }
-
- /**
- * Returns the set cardinality (number of elements) of the set stored at key.
- *
- * @param string $key
- *
- * @return int the cardinality (number of elements) of the set, or 0 if key does not exist.
- * @link https://redis.io/commands/scard
- * @example
- *
- * $redisCluster->sAdd('key1' , 'set1');
- * $redisCluster->sAdd('key1' , 'set2');
- * $redisCluster->sAdd('key1' , 'set3'); // 'key1' => {'set1', 'set2', 'set3'}
- * $redisCluster->sCard('key1'); // 3
- * $redisCluster->sCard('keyX'); // 0
- *
- */
- public function sCard($key) { }
-
- /**
- * Returns all the members of the set value stored at key.
- * This has the same effect as running SINTER with one argument key.
- *
- * @param string $key
- *
- * @return array All elements of the set.
- * @link https://redis.io/commands/smembers
- * @example
- *
- * $redisCluster->del('s');
- * $redisCluster->sAdd('s', 'a');
- * $redisCluster->sAdd('s', 'b');
- * $redisCluster->sAdd('s', 'a');
- * $redisCluster->sAdd('s', 'c');
- * var_dump($redisCluster->sMembers('s'));
- *
- * ////Output:
- * //
- * //array(3) {
- * // [0]=>
- * // string(1) "b"
- * // [1]=>
- * // string(1) "c"
- * // [2]=>
- * // string(1) "a"
- * //}
- * // The order is random and corresponds to redis' own internal representation of the set structure.
- *
- */
- public function sMembers($key) { }
-
- /**
- * Returns if member is a member of the set stored at key.
- *
- * @param string $key
- * @param string $value
- *
- * @return bool TRUE if value is a member of the set at key key, FALSE otherwise.
- * @link https://redis.io/commands/sismember
- * @example
- *
- * $redisCluster->sAdd('key1' , 'set1');
- * $redisCluster->sAdd('key1' , 'set2');
- * $redisCluster->sAdd('key1' , 'set3'); // 'key1' => {'set1', 'set2', 'set3'}
- *
- * $redisCluster->sIsMember('key1', 'set1'); // TRUE
- * $redisCluster->sIsMember('key1', 'setX'); // FALSE
- *
- */
- public function sIsMember($key, $value) { }
-
- /**
- * Adds a values to the set value stored at key.
- * If this value is already in the set, FALSE is returned.
- *
- * @param string $key Required key
- * @param string $value1 Required value
- * @param string $value2 Optional value
- * @param string $valueN Optional value
- *
- * @return int|false The number of elements added to the set
- * @link https://redis.io/commands/sadd
- * @example
- *
- * $redisCluster->sAdd('k', 'v1'); // int(1)
- * $redisCluster->sAdd('k', 'v1', 'v2', 'v3'); // int(2)
- *
- */
- public function sAdd($key, $value1, $value2 = null, $valueN = null) { }
-
- /**
- * Adds a values to the set value stored at key.
- * If this value is already in the set, FALSE is returned.
- *
- * @param string $key Required key
- * @param array $valueArray
- *
- * @return int|false The number of elements added to the set
- * @example
- *
- * $redisCluster->sAddArray('k', ['v1', 'v2', 'v3']);
- * //This is a feature in php only. Same as $redisCluster->sAdd('k', 'v1', 'v2', 'v3');
- *
- */
- public function sAddArray($key, array $valueArray) { }
-
- /**
- * Removes the specified members from the set value stored at key.
- *
- * @param string $key
- * @param string $member1
- * @param string $member2
- * @param string $memberN
- *
- * @return int The number of elements removed from the set.
- * @link https://redis.io/commands/srem
- * @example
- *
- * var_dump( $redisCluster->sAdd('k', 'v1', 'v2', 'v3') ); // int(3)
- * var_dump( $redisCluster->sRem('k', 'v2', 'v3') ); // int(2)
- * var_dump( $redisCluster->sMembers('k') );
- * //// Output:
- * // array(1) {
- * // [0]=> string(2) "v1"
- * // }
- *
- */
- public function sRem($key, $member1, $member2 = null, $memberN = null) { }
-
- /**
- * Performs the union between N sets and returns it.
- *
- * @param string $key1 Any number of keys corresponding to sets in redis.
- * @param string $key2 ...
- * @param string $keyN ...
- *
- * @return array of strings: The union of all these sets.
- * @link https://redis.io/commands/sunionstore
- * @example
- *
- * $redisCluster->del('s0', 's1', 's2');
- *
- * $redisCluster->sAdd('s0', '1');
- * $redisCluster->sAdd('s0', '2');
- * $redisCluster->sAdd('s1', '3');
- * $redisCluster->sAdd('s1', '1');
- * $redisCluster->sAdd('s2', '3');
- * $redisCluster->sAdd('s2', '4');
- *
- * var_dump($redisCluster->sUnion('s0', 's1', 's2'));
- *
- * //// Output:
- * //
- * //array(4) {
- * // [0]=>
- * // string(1) "3"
- * // [1]=>
- * // string(1) "4"
- * // [2]=>
- * // string(1) "1"
- * // [3]=>
- * // string(1) "2"
- * //}
- *
- */
- public function sUnion($key1, $key2, $keyN = null) { }
-
- /**
- * Performs the same action as sUnion, but stores the result in the first key
- *
- * @param string $dstKey the key to store the diff into.
- * @param string $key1 Any number of keys corresponding to sets in redis.
- * @param string $key2 ...
- * @param string $keyN ...
- *
- * @return int Any number of keys corresponding to sets in redis.
- * @link https://redis.io/commands/sunionstore
- * @example
- *
- * $redisCluster->del('s0', 's1', 's2');
- *
- * $redisCluster->sAdd('s0', '1');
- * $redisCluster->sAdd('s0', '2');
- * $redisCluster->sAdd('s1', '3');
- * $redisCluster->sAdd('s1', '1');
- * $redisCluster->sAdd('s2', '3');
- * $redisCluster->sAdd('s2', '4');
- *
- * var_dump($redisCluster->sUnionStore('dst', 's0', 's1', 's2'));
- * var_dump($redisCluster->sMembers('dst'));
- *
- * //// Output:
- * //
- * //int(4)
- * //array(4) {
- * // [0]=>
- * // string(1) "3"
- * // [1]=>
- * // string(1) "4"
- * // [2]=>
- * // string(1) "1"
- * // [3]=>
- * // string(1) "2"
- * //}
- *
- */
- public function sUnionStore($dstKey, $key1, $key2, $keyN = null) { }
-
- /**
- * Returns the members of a set resulting from the intersection of all the sets
- * held at the specified keys. If just a single key is specified, then this command
- * produces the members of this set. If one of the keys is missing, FALSE is returned.
- *
- * @param string $key1 keys identifying the different sets on which we will apply the intersection.
- * @param string $key2 ...
- * @param string $keyN ...
- *
- * @return array contain the result of the intersection between those keys.
- * If the intersection between the different sets is empty, the return value will be empty array.
- * @link https://redis.io/commands/sinterstore
- * @example
- *
- * $redisCluster->sAdd('key1', 'val1');
- * $redisCluster->sAdd('key1', 'val2');
- * $redisCluster->sAdd('key1', 'val3');
- * $redisCluster->sAdd('key1', 'val4');
- *
- * $redisCluster->sAdd('key2', 'val3');
- * $redisCluster->sAdd('key2', 'val4');
- *
- * $redisCluster->sAdd('key3', 'val3');
- * $redisCluster->sAdd('key3', 'val4');
- *
- * var_dump($redisCluster->sInter('key1', 'key2', 'key3'));
- *
- * // Output:
- * //
- * //array(2) {
- * // [0]=>
- * // string(4) "val4"
- * // [1]=>
- * // string(4) "val3"
- * //}
- *
- */
- public function sInter($key1, $key2, $keyN = null) { }
-
- /**
- * Performs a sInter command and stores the result in a new set.
- *
- * @param string $dstKey the key to store the diff into.
- * @param string $key1 are intersected as in sInter.
- * @param string $key2 ...
- * @param string $keyN ...
- *
- * @return int|false The cardinality of the resulting set, or FALSE in case of a missing key.
- * @link https://redis.io/commands/sinterstore
- * @example
- *
- * $redisCluster->sAdd('key1', 'val1');
- * $redisCluster->sAdd('key1', 'val2');
- * $redisCluster->sAdd('key1', 'val3');
- * $redisCluster->sAdd('key1', 'val4');
- *
- * $redisCluster->sAdd('key2', 'val3');
- * $redisCluster->sAdd('key2', 'val4');
- *
- * $redisCluster->sAdd('key3', 'val3');
- * $redisCluster->sAdd('key3', 'val4');
- *
- * var_dump($redisCluster->sInterStore('output', 'key1', 'key2', 'key3'));
- * var_dump($redisCluster->sMembers('output'));
- *
- * //// Output:
- * //
- * //int(2)
- * //array(2) {
- * // [0]=>
- * // string(4) "val4"
- * // [1]=>
- * // string(4) "val3"
- * //}
- *
- */
- public function sInterStore($dstKey, $key1, $key2, $keyN = null) { }
-
- /**
- * Performs the difference between N sets and returns it.
- *
- * @param string $key1 Any number of keys corresponding to sets in redis.
- * @param string $key2 ...
- * @param string $keyN ...
- *
- * @return array of strings: The difference of the first set will all the others.
- * @link https://redis.io/commands/sdiff
- * @example
- *
- * $redisCluster->del('s0', 's1', 's2');
- *
- * $redisCluster->sAdd('s0', '1');
- * $redisCluster->sAdd('s0', '2');
- * $redisCluster->sAdd('s0', '3');
- * $redisCluster->sAdd('s0', '4');
- *
- * $redisCluster->sAdd('s1', '1');
- * $redisCluster->sAdd('s2', '3');
- *
- * var_dump($redisCluster->sDiff('s0', 's1', 's2'));
- *
- * //// Output:
- * //
- * //array(2) {
- * // [0]=>
- * // string(1) "4"
- * // [1]=>
- * // string(1) "2"
- * //}
- *
- */
- public function sDiff($key1, $key2, $keyN = null) { }
-
- /**
- * Performs the same action as sDiff, but stores the result in the first key
- *
- * @param string $dstKey the key to store the diff into.
- * @param string $key1 Any number of keys corresponding to sets in redis
- * @param string $key2 ...
- * @param string $keyN ...
- *
- * @return int|false The cardinality of the resulting set, or FALSE in case of a missing key.
- * @link https://redis.io/commands/sdiffstore
- * @example
- *
- * $redisCluster->del('s0', 's1', 's2');
- *
- * $redisCluster->sAdd('s0', '1');
- * $redisCluster->sAdd('s0', '2');
- * $redisCluster->sAdd('s0', '3');
- * $redisCluster->sAdd('s0', '4');
- *
- * $redisCluster->sAdd('s1', '1');
- * $redisCluster->sAdd('s2', '3');
- *
- * var_dump($redisCluster->sDiffStore('dst', 's0', 's1', 's2'));
- * var_dump($redisCluster->sMembers('dst'));
- *
- * //// Output:
- * //
- * //int(2)
- * //array(2) {
- * // [0]=>
- * // string(1) "4"
- * // [1]=>
- * // string(1) "2"
- * //}
- *
- */
- public function sDiffStore($dstKey, $key1, $key2, $keyN = null) { }
-
- /**
- * Returns a random element(s) from the set value at Key, without removing it.
- *
- * @param string $key
- * @param int $count [optional]
- *
- * @return string|array value(s) from the set
- * bool FALSE if set identified by key is empty or doesn't exist and count argument isn't passed.
- * @link https://redis.io/commands/srandmember
- * @example
- *
- * $redisCluster->sAdd('key1' , 'one');
- * $redisCluster->sAdd('key1' , 'two');
- * $redisCluster->sAdd('key1' , 'three'); // 'key1' => {'one', 'two', 'three'}
- *
- * var_dump( $redisCluster->sRandMember('key1') ); // 'key1' => {'one', 'two', 'three'}
- *
- * // string(5) "three"
- *
- * var_dump( $redisCluster->sRandMember('key1', 2) ); // 'key1' => {'one', 'two', 'three'}
- *
- * // array(2) {
- * // [0]=> string(2) "one"
- * // [1]=> string(2) "three"
- * // }
- *
- */
- public function sRandMember($key, $count = null) { }
-
- /**
- * Get the length of a string value.
- *
- * @param string $key
- *
- * @return int
- * @link https://redis.io/commands/strlen
- * @example
- *
- * $redisCluster->set('key', 'value');
- * $redisCluster->strlen('key'); // 5
- *
- */
- public function strlen($key) { }
-
- /**
- * Remove the expiration timer from a key.
- *
- * @param string $key
- *
- * @return bool TRUE if a timeout was removed, FALSE if the key didn’t exist or didn’t have an expiration timer.
- * @link https://redis.io/commands/persist
- * @example $redisCluster->persist('key');
- */
- public function persist($key) { }
-
- /**
- * Returns the remaining time to live of a key that has a timeout.
- * This introspection capability allows a Redis client to check how many seconds a given key will continue to be
- * part of the dataset. In Redis 2.6 or older the command returns -1 if the key does not exist or if the key exist
- * but has no associated expire. Starting with Redis 2.8 the return value in case of error changed: Returns -2 if
- * the key does not exist. Returns -1 if the key exists but has no associated expire.
- *
- * @param string $key
- *
- * @return int the time left to live in seconds.
- * @link https://redis.io/commands/ttl
- * @example $redisCluster->ttl('key');
- */
- public function ttl($key) { }
-
- /**
- * Returns the remaining time to live of a key that has an expire set,
- * with the sole difference that TTL returns the amount of remaining time in seconds while PTTL returns it in
- * milliseconds. In Redis 2.6 or older the command returns -1 if the key does not exist or if the key exist but has
- * no associated expire. Starting with Redis 2.8 the return value in case of error changed: Returns -2 if the key
- * does not exist. Returns -1 if the key exists but has no associated expire.
- *
- * @param string $key
- *
- * @return int the time left to live in milliseconds.
- * @link https://redis.io/commands/pttl
- * @example $redisCluster->pttl('key');
- */
- public function pttl($key) { }
-
- /**
- * Returns the cardinality of an ordered set.
- *
- * @param string $key
- *
- * @return int the set's cardinality
- * @link https://redis.io/commands/zsize
- * @example
- *
- * $redisCluster->zAdd('key', 0, 'val0');
- * $redisCluster->zAdd('key', 2, 'val2');
- * $redisCluster->zAdd('key', 10, 'val10');
- * $redisCluster->zCard('key'); // 3
- *
- */
- public function zCard($key) { }
-
- /**
- * Returns the number of elements of the sorted set stored at the specified key which have
- * scores in the range [start,end]. Adding a parenthesis before start or end excludes it
- * from the range. +inf and -inf are also valid limits.
- *
- * @param string $key
- * @param string $start
- * @param string $end
- *
- * @return int the size of a corresponding zRangeByScore.
- * @link https://redis.io/commands/zcount
- * @example
- *
- * $redisCluster->zAdd('key', 0, 'val0');
- * $redisCluster->zAdd('key', 2, 'val2');
- * $redisCluster->zAdd('key', 10, 'val10');
- * $redisCluster->zCount('key', 0, 3); // 2, corresponding to array('val0', 'val2')
- *
- */
- public function zCount($key, $start, $end) { }
-
- /**
- * Deletes the elements of the sorted set stored at the specified key which have scores in the range [start,end].
- *
- * @param string $key
- * @param float|string $start double or "+inf" or "-inf" string
- * @param float|string $end double or "+inf" or "-inf" string
- *
- * @return int The number of values deleted from the sorted set
- * @link https://redis.io/commands/zremrangebyscore
- * @example
- *
- * $redisCluster->zAdd('key', 0, 'val0');
- * $redisCluster->zAdd('key', 2, 'val2');
- * $redisCluster->zAdd('key', 10, 'val10');
- * $redisCluster->zRemRangeByScore('key', 0, 3); // 2
- *
- */
- public function zRemRangeByScore($key, $start, $end) { }
-
- /**
- * Returns the score of a given member in the specified sorted set.
- *
- * @param string $key
- * @param string $member
- *
- * @return float
- * @link https://redis.io/commands/zscore
- * @example
- *
- * $redisCluster->zAdd('key', 2.5, 'val2');
- * $redisCluster->zScore('key', 'val2'); // 2.5
- *
- */
- public function zScore($key, $member) { }
-
- /**
- * Adds the specified member with a given score to the sorted set stored at key.
- *
- * @param string $key Required key
- * @param float $score1 Required score
- * @param string $value1 Required value
- * @param float $score2 Optional score
- * @param string $value2 Optional value
- * @param float $scoreN Optional score
- * @param string $valueN Optional value
- *
- * @return int Number of values added
- * @link https://redis.io/commands/zadd
- * @example
- *
- * $redisCluster->zAdd('z', 1, 'v2', 2, 'v2', 3, 'v3', 4, 'v4' ); // int(3)
- * $redisCluster->zRem('z', 'v2', 'v3'); // int(2)
- * var_dump( $redisCluster->zRange('z', 0, -1) );
- *
- * //// Output:
- * // array(1) {
- * // [0]=> string(2) "v4"
- * // }
- *
- */
- public function zAdd($key, $score1, $value1, $score2 = null, $value2 = null, $scoreN = null, $valueN = null) { }
-
- /**
- * Increments the score of a member from a sorted set by a given amount.
- *
- * @param string $key
- * @param float $value (double) value that will be added to the member's score
- * @param string $member
- *
- * @return float the new value
- * @link https://redis.io/commands/zincrby
- * @example
- *
- * $redisCluster->del('key');
- * $redisCluster->zIncrBy('key', 2.5, 'member1');// key or member1 didn't exist, so member1's score is to 0 ;
- * //before the increment and now has the value 2.5
- * $redisCluster->zIncrBy('key', 1, 'member1'); // 3.5
- *
- */
- public function zIncrBy($key, $value, $member) { }
-
- /**
- * Returns the length of a hash, in number of items
- *
- * @param string $key
- *
- * @return int|false the number of items in a hash, FALSE if the key doesn't exist or isn't a hash.
- * @link https://redis.io/commands/hlen
- * @example
- *
- * $redisCluster->del('h');
- * $redisCluster->hSet('h', 'key1', 'hello');
- * $redisCluster->hSet('h', 'key2', 'plop');
- * $redisCluster->hLen('h'); // returns 2
- *
- */
- public function hLen($key) { }
-
- /**
- * Returns the keys in a hash, as an array of strings.
- *
- * @param string $key
- *
- * @return array An array of elements, the keys of the hash. This works like PHP's array_keys().
- * @link https://redis.io/commands/hkeys
- * @example
- *
- * $redisCluster->del('h');
- * $redisCluster->hSet('h', 'a', 'x');
- * $redisCluster->hSet('h', 'b', 'y');
- * $redisCluster->hSet('h', 'c', 'z');
- * $redisCluster->hSet('h', 'd', 't');
- * var_dump($redisCluster->hKeys('h'));
- *
- * //// Output:
- * //
- * // array(4) {
- * // [0]=>
- * // string(1) "a"
- * // [1]=>
- * // string(1) "b"
- * // [2]=>
- * // string(1) "c"
- * // [3]=>
- * // string(1) "d"
- * // }
- * // The order is random and corresponds to redis' own internal representation of the set structure.
- *
- */
- public function hKeys($key) { }
-
- /**
- * Returns the values in a hash, as an array of strings.
- *
- * @param string $key
- *
- * @return array An array of elements, the values of the hash. This works like PHP's array_values().
- * @link https://redis.io/commands/hvals
- * @example
- *
- * $redisCluster->del('h');
- * $redisCluster->hSet('h', 'a', 'x');
- * $redisCluster->hSet('h', 'b', 'y');
- * $redisCluster->hSet('h', 'c', 'z');
- * $redisCluster->hSet('h', 'd', 't');
- * var_dump($redisCluster->hVals('h'));
- *
- * //// Output:
- * //
- * // array(4) {
- * // [0]=>
- * // string(1) "x"
- * // [1]=>
- * // string(1) "y"
- * // [2]=>
- * // string(1) "z"
- * // [3]=>
- * // string(1) "t"
- * // }
- * // The order is random and corresponds to redis' own internal representation of the set structure.
- *
- */
- public function hVals($key) { }
-
- /**
- * Gets a value from the hash stored at key.
- * If the hash table doesn't exist, or the key doesn't exist, FALSE is returned.
- *
- * @param string $key
- * @param string $hashKey
- *
- * @return string|false The value, if the command executed successfully BOOL FALSE in case of failure
- * @link https://redis.io/commands/hget
- * @example
- *
- * $redisCluster->del('h');
- * $redisCluster->hSet('h', 'a', 'x');
- * $redisCluster->hGet('h', 'a'); // 'X'
- *
- */
- public function hGet($key, $hashKey) { }
-
- /**
- * Returns the whole hash, as an array of strings indexed by strings.
- *
- * @param string $key
- *
- * @return array An array of elements, the contents of the hash.
- * @link https://redis.io/commands/hgetall
- * @example
- *
- * $redisCluster->del('h');
- * $redisCluster->hSet('h', 'a', 'x');
- * $redisCluster->hSet('h', 'b', 'y');
- * $redisCluster->hSet('h', 'c', 'z');
- * $redisCluster->hSet('h', 'd', 't');
- * var_dump($redisCluster->hGetAll('h'));
- *
- * //// Output:
- * //
- * // array(4) {
- * // ["a"]=>
- * // string(1) "x"
- * // ["b"]=>
- * // string(1) "y"
- * // ["c"]=>
- * // string(1) "z"
- * // ["d"]=>
- * // string(1) "t"
- * // }
- * // The order is random and corresponds to redis' own internal representation of the set structure.
- *
- */
- public function hGetAll($key) { }
-
- /**
- * Verify if the specified member exists in a key.
- *
- * @param string $key
- * @param string $hashKey
- *
- * @return bool If the member exists in the hash table, return TRUE, otherwise return FALSE.
- * @link https://redis.io/commands/hexists
- * @example
- *
- * $redisCluster->hSet('h', 'a', 'x');
- * $redisCluster->hExists('h', 'a'); // TRUE
- * $redisCluster->hExists('h', 'NonExistingKey'); // FALSE
- *
- */
- public function hExists($key, $hashKey) { }
-
- /**
- * Increments the value of a member from a hash by a given amount.
- *
- * @param string $key
- * @param string $hashKey
- * @param int $value (integer) value that will be added to the member's value
- *
- * @return int the new value
- * @link https://redis.io/commands/hincrby
- * @example
- *
- * $redisCluster->del('h');
- * $redisCluster->hIncrBy('h', 'x', 2); // returns 2: h[x] = 2 now.
- * $redisCluster->hIncrBy('h', 'x', 1); // h[x] ← 2 + 1. Returns 3
- *
- */
- public function hIncrBy($key, $hashKey, $value) { }
-
- /**
- * Adds a value to the hash stored at key. If this value is already in the hash, FALSE is returned.
- *
- * @param string $key
- * @param string $hashKey
- * @param string $value
- *
- * @return int
- * 1 if value didn't exist and was added successfully,
- * 0 if the value was already present and was replaced, FALSE if there was an error.
- * @link https://redis.io/commands/hset
- * @example
- *
- * $redisCluster->del('h')
- * $redisCluster->hSet('h', 'key1', 'hello'); // 1, 'key1' => 'hello' in the hash at "h"
- * $redisCluster->hGet('h', 'key1'); // returns "hello"
- *
- * $redisCluster->hSet('h', 'key1', 'plop'); // 0, value was replaced.
- * $redisCluster->hGet('h', 'key1'); // returns "plop"
- *
- */
- public function hSet($key, $hashKey, $value) { }
-
- /**
- * Adds a value to the hash stored at key only if this field isn't already in the hash.
- *
- * @param string $key
- * @param string $hashKey
- * @param string $value
- *
- * @return bool TRUE if the field was set, FALSE if it was already present.
- * @link https://redis.io/commands/hsetnx
- * @example
- *
- * $redisCluster->del('h')
- * $redisCluster->hSetNx('h', 'key1', 'hello'); // TRUE, 'key1' => 'hello' in the hash at "h"
- * $redisCluster->hSetNx('h', 'key1', 'world'); // FALSE, 'key1' => 'hello' in the hash at "h". No change since the
- * field wasn't replaced.
- *
- */
- public function hSetNx($key, $hashKey, $value) { }
-
- /**
- * Retrieve the values associated to the specified fields in the hash.
- *
- * @param string $key
- * @param array $hashKeys
- *
- * @return array Array An array of elements, the values of the specified fields in the hash,
- * with the hash keys as array keys.
- * @link https://redis.io/commands/hmget
- * @example
- *
- * $redisCluster->del('h');
- * $redisCluster->hSet('h', 'field1', 'value1');
- * $redisCluster->hSet('h', 'field2', 'value2');
- * $redisCluster->hMGet('h', array('field1', 'field2')); // returns array('field1' => 'value1', 'field2' =>
- * 'value2')
- *
- */
- public function hMGet($key, $hashKeys) { }
-
- /**
- * Fills in a whole hash. Non-string values are converted to string, using the standard (string) cast.
- * NULL values are stored as empty strings
- *
- * @param string $key
- * @param array $hashKeys key → value array
- *
- * @return bool
- * @link https://redis.io/commands/hmset
- * @example
- *
- * $redisCluster->del('user:1');
- * $redisCluster->hMSet('user:1', array('name' => 'Joe', 'salary' => 2000));
- * $redisCluster->hIncrBy('user:1', 'salary', 100); // Joe earns 100 more now.
- *
- */
- public function hMSet($key, $hashKeys) { }
-
- /**
- * Removes a values from the hash stored at key.
- * If the hash table doesn't exist, or the key doesn't exist, FALSE is returned.
- *
- * @param string $key
- * @param string $hashKey1
- * @param string $hashKey2
- * @param string $hashKeyN
- *
- * @return int Number of deleted fields
- * @link https://redis.io/commands/hdel
- * @example
- *
- * $redisCluster->hMSet('h',
- * array(
- * 'f1' => 'v1',
- * 'f2' => 'v2',
- * 'f3' => 'v3',
- * 'f4' => 'v4',
- * ));
- *
- * var_dump( $redisCluster->hDel('h', 'f1') ); // int(1)
- * var_dump( $redisCluster->hDel('h', 'f2', 'f3') ); // int(2)
- *
- * var_dump( $redisCluster->hGetAll('h') );
- *
- * //// Output:
- * //
- * // array(1) {
- * // ["f4"]=> string(2) "v4"
- * // }
- *
- */
- public function hDel($key, $hashKey1, $hashKey2 = null, $hashKeyN = null) { }
-
- /**
- * Increment the float value of a hash field by the given amount
- *
- * @param string $key
- * @param string $field
- * @param float $increment
- *
- * @return float
- * @link https://redis.io/commands/hincrbyfloat
- * @example
- *
- * $redisCluster->hset('h', 'float', 3);
- * $redisCluster->hset('h', 'int', 3);
- * var_dump( $redisCluster->hIncrByFloat('h', 'float', 1.5) ); // float(4.5)
- *
- * var_dump( $redisCluster->hGetAll('h') );
- *
- * //// Output:
- * //
- * // array(2) {
- * // ["float"]=>
- * // string(3) "4.5"
- * // ["int"]=>
- * // string(1) "3"
- * // }
- *
- */
- public function hIncrByFloat($key, $field, $increment) { }
-
- /**
- * Dump a key out of a redis database, the value of which can later be passed into redis using the RESTORE command.
- * The data that comes out of DUMP is a binary representation of the key as Redis stores it.
- *
- * @param string $key
- *
- * @return string|false The Redis encoded value of the key, or FALSE if the key doesn't exist
- * @link https://redis.io/commands/dump
- * @example
- *
- * $redisCluster->set('foo', 'bar');
- * $val = $redisCluster->dump('foo'); // $val will be the Redis encoded key value
- *
- */
- public function dump($key) { }
-
- /**
- * Returns the rank of a given member in the specified sorted set, starting at 0 for the item
- * with the smallest score. zRevRank starts at 0 for the item with the largest score.
- *
- * @param string $key
- * @param string $member
- *
- * @return int the item's score.
- * @link https://redis.io/commands/zrank
- * @example
- *
- * $redisCluster->del('z');
- * $redisCluster->zAdd('key', 1, 'one');
- * $redisCluster->zAdd('key', 2, 'two');
- * $redisCluster->zRank('key', 'one'); // 0
- * $redisCluster->zRank('key', 'two'); // 1
- * $redisCluster->zRevRank('key', 'one'); // 1
- * $redisCluster->zRevRank('key', 'two'); // 0
- *
- */
- public function zRank($key, $member) { }
-
- /**
- * @see zRank()
- *
- * @param string $key
- * @param string $member
- *
- * @return int the item's score
- * @link https://redis.io/commands/zrevrank
- */
- public function zRevRank($key, $member) { }
-
- /**
- * Increment the number stored at key by one.
- *
- * @param string $key
- *
- * @return int the new value
- * @link https://redis.io/commands/incr
- * @example
- *
- * $redisCluster->incr('key1'); // key1 didn't exists, set to 0 before the increment and now has the value 1
- * $redisCluster->incr('key1'); // 2
- * $redisCluster->incr('key1'); // 3
- * $redisCluster->incr('key1'); // 4
- *
- */
- public function incr($key) { }
-
- /**
- * Decrement the number stored at key by one.
- *
- * @param string $key
- *
- * @return int the new value
- * @link https://redis.io/commands/decr
- * @example
- *
- * $redisCluster->decr('key1'); // key1 didn't exists, set to 0 before the increment and now has the value -1
- * $redisCluster->decr('key1'); // -2
- * $redisCluster->decr('key1'); // -3
- *
- */
- public function decr($key) { }
-
- /**
- * Increment the number stored at key by one. If the second argument is filled, it will be used as the integer
- * value of the increment.
- *
- * @param string $key key
- * @param int $value value that will be added to key (only for incrBy)
- *
- * @return int the new value
- * @link https://redis.io/commands/incrby
- * @example
- *
- * $redisCluster->incr('key1'); // key1 didn't exists, set to 0 before the increment and now has the value 1
- * $redisCluster->incr('key1'); // 2
- * $redisCluster->incr('key1'); // 3
- * $redisCluster->incr('key1'); // 4
- * $redisCluster->incrBy('key1', 10); // 14
- *
- */
- public function incrBy($key, $value) { }
-
- /**
- * Decrement the number stored at key by one. If the second argument is filled, it will be used as the integer
- * value of the decrement.
- *
- * @param string $key
- * @param int $value that will be subtracted to key (only for decrBy)
- *
- * @return int the new value
- * @link https://redis.io/commands/decrby
- * @example
- *
- * $redisCluster->decr('key1'); // key1 didn't exists, set to 0 before the increment and now has the value -1
- * $redisCluster->decr('key1'); // -2
- * $redisCluster->decr('key1'); // -3
- * $redisCluster->decrBy('key1', 10); // -13
- *
- */
- public function decrBy($key, $value) { }
-
- /**
- * Increment the float value of a key by the given amount
- *
- * @param string $key
- * @param float $increment
- *
- * @return float
- * @link https://redis.io/commands/incrbyfloat
- * @example
- *
- * $redisCluster->set('x', 3);
- * var_dump( $redisCluster->incrByFloat('x', 1.5) ); // float(4.5)
- *
- * var_dump( $redisCluster->get('x') ); // string(3) "4.5"
- *
- */
- public function incrByFloat($key, $increment) { }
-
- /**
- * Sets an expiration date (a timeout) on an item.
- *
- * @param string $key The key that will disappear.
- * @param int $ttl The key's remaining Time To Live, in seconds.
- *
- * @return bool TRUE in case of success, FALSE in case of failure.
- * @link https://redis.io/commands/expire
- * @example
- *
- * $redisCluster->set('x', '42');
- * $redisCluster->expire('x', 3); // x will disappear in 3 seconds.
- * sleep(5); // wait 5 seconds
- * $redisCluster->get('x'); // will return `FALSE`, as 'x' has expired.
- *
- */
- public function expire($key, $ttl) { }
-
- /**
- * Sets an expiration date (a timeout in milliseconds) on an item.
- *
- * @param string $key The key that will disappear.
- * @param int $ttl The key's remaining Time To Live, in milliseconds.
- *
- * @return bool TRUE in case of success, FALSE in case of failure.
- * @link https://redis.io/commands/pexpire
- * @example
- *
- * $redisCluster->set('x', '42');
- * $redisCluster->pExpire('x', 11500); // x will disappear in 11500 milliseconds.
- * $redisCluster->ttl('x'); // 12
- * $redisCluster->pttl('x'); // 11500
- *
- */
- public function pExpire($key, $ttl) { }
-
- /**
- * Sets an expiration date (a timestamp) on an item.
- *
- * @param string $key The key that will disappear.
- * @param int $timestamp Unix timestamp. The key's date of death, in seconds from Epoch time.
- *
- * @return bool TRUE in case of success, FALSE in case of failure.
- * @link https://redis.io/commands/expireat
- * @example
- *
- * $redisCluster->set('x', '42');
- * $now = time(); // current timestamp
- * $redisCluster->expireAt('x', $now + 3); // x will disappear in 3 seconds.
- * sleep(5); // wait 5 seconds
- * $redisCluster->get('x'); // will return `FALSE`, as 'x' has expired.
- *
- */
- public function expireAt($key, $timestamp) { }
-
- /**
- * Sets an expiration date (a timestamp) on an item. Requires a timestamp in milliseconds
- *
- * @param string $key The key that will disappear.
- * @param int $timestamp Unix timestamp. The key's date of death, in seconds from Epoch time.
- *
- * @return bool TRUE in case of success, FALSE in case of failure.
- * @link https://redis.io/commands/pexpireat
- * @example
- *
- * $redisCluster->set('x', '42');
- * $redisCluster->pExpireAt('x', 1555555555005);
- * $redisCluster->ttl('x'); // 218270121
- * $redisCluster->pttl('x'); // 218270120575
- *
- */
- public function pExpireAt($key, $timestamp) { }
-
- /**
- * Append specified string to the string stored in specified key.
- *
- * @param string $key
- * @param string $value
- *
- * @return int Size of the value after the append
- * @link https://redis.io/commands/append
- * @example
- *
- * $redisCluster->set('key', 'value1');
- * $redisCluster->append('key', 'value2'); // 12
- * $redisCluster->get('key'); // 'value1value2'
- *
- */
- public function append($key, $value) { }
-
- /**
- * Return a single bit out of a larger string
- *
- * @param string $key
- * @param int $offset
- *
- * @return int the bit value (0 or 1)
- * @link https://redis.io/commands/getbit
- * @example
- *
- * $redisCluster->set('key', "\x7f"); // this is 0111 1111
- * $redisCluster->getBit('key', 0); // 0
- * $redisCluster->getBit('key', 1); // 1
- *
- */
- public function getBit($key, $offset) { }
-
- /**
- * Changes a single bit of a string.
- *
- * @param string $key
- * @param int $offset
- * @param bool|int $value bool or int (1 or 0)
- *
- * @return int 0 or 1, the value of the bit before it was set.
- * @link https://redis.io/commands/setbit
- * @example
- *
- * $redisCluster->set('key', "*"); // ord("*") = 42 = 0x2f = "0010 1010"
- * $redisCluster->setBit('key', 5, 1); // returns 0
- * $redisCluster->setBit('key', 7, 1); // returns 0
- * $redisCluster->get('key'); // chr(0x2f) = "/" = b("0010 1111")
- *
- */
- public function setBit($key, $offset, $value) { }
-
- /**
- * Bitwise operation on multiple keys.
- *
- * @param string $operation either "AND", "OR", "NOT", "XOR"
- * @param string $retKey return key
- * @param string $key1
- * @param string $key2
- * @param string $key3
- *
- * @return int The size of the string stored in the destination key.
- * @link https://redis.io/commands/bitop
- * @example
- *
- * $redisCluster->set('bit1', '1'); // 11 0001
- * $redisCluster->set('bit2', '2'); // 11 0010
- *
- * $redisCluster->bitOp('AND', 'bit', 'bit1', 'bit2'); // bit = 110000
- * $redisCluster->bitOp('OR', 'bit', 'bit1', 'bit2'); // bit = 110011
- * $redisCluster->bitOp('NOT', 'bit', 'bit1', 'bit2'); // bit = 110011
- * $redisCluster->bitOp('XOR', 'bit', 'bit1', 'bit2'); // bit = 11
- *
- */
- public function bitOp($operation, $retKey, $key1, $key2, $key3 = null) { }
-
- /**
- * Return the position of the first bit set to 1 or 0 in a string. The position is returned, thinking of the
- * string as an array of bits from left to right, where the first byte's most significant bit is at position 0,
- * the second byte's most significant bit is at position 8, and so forth.
- *
- * @param string $key
- * @param int $bit
- * @param int $start
- * @param int $end
- *
- * @return int The command returns the position of the first bit set to 1 or 0 according to the request.
- * If we look for set bits (the bit argument is 1) and the string is empty or composed of just
- * zero bytes, -1 is returned. If we look for clear bits (the bit argument is 0) and the string
- * only contains bit set to 1, the function returns the first bit not part of the string on the
- * right. So if the string is three bytes set to the value 0xff the command BITPOS key 0 will
- * return 24, since up to bit 23 all the bits are 1. Basically, the function considers the right
- * of the string as padded with zeros if you look for clear bits and specify no range or the
- * start argument only. However, this behavior changes if you are looking for clear bits and
- * specify a range with both start and end. If no clear bit is found in the specified range, the
- * function returns -1 as the user specified a clear range and there are no 0 bits in that range.
- * @link https://redis.io/commands/bitpos
- * @example
- *
- * $redisCluster->set('key', '\xff\xff');
- * $redisCluster->bitpos('key', 1); // int(0)
- * $redisCluster->bitpos('key', 1, 1); // int(8)
- * $redisCluster->bitpos('key', 1, 3); // int(-1)
- * $redisCluster->bitpos('key', 0); // int(16)
- * $redisCluster->bitpos('key', 0, 1); // int(16)
- * $redisCluster->bitpos('key', 0, 1, 5); // int(-1)
- *
- */
- public function bitpos($key, $bit, $start = 0, $end = null) { }
-
- /**
- * Count bits in a string.
- *
- * @param string $key
- *
- * @return int The number of bits set to 1 in the value behind the input key.
- * @link https://redis.io/commands/bitcount
- * @example
- *
- * $redisCluster->set('bit', '345'); // // 11 0011 0011 0100 0011 0101
- * var_dump( $redisCluster->bitCount('bit', 0, 0) ); // int(4)
- * var_dump( $redisCluster->bitCount('bit', 1, 1) ); // int(3)
- * var_dump( $redisCluster->bitCount('bit', 2, 2) ); // int(4)
- * var_dump( $redisCluster->bitCount('bit', 0, 2) ); // int(11)
- *
- */
- public function bitCount($key) { }
-
- /**
- * @see lIndex()
- *
- * @param string $key
- * @param int $index
- *
- * @link https://redis.io/commands/lindex
- */
- public function lGet($key, $index) { }
-
- /**
- * Return a substring of a larger string
- *
- * @param string $key
- * @param int $start
- * @param int $end
- *
- * @return string the substring
- * @link https://redis.io/commands/getrange
- * @example
- *
- * $redisCluster->set('key', 'string value');
- * $redisCluster->getRange('key', 0, 5); // 'string'
- * $redisCluster->getRange('key', -5, -1); // 'value'
- *
- */
- public function getRange($key, $start, $end) { }
-
- /**
- * Trims an existing list so that it will contain only a specified range of elements.
- *
- * @param string $key
- * @param int $start
- * @param int $stop
- *
- * @return array|false Bool return FALSE if the key identify a non-list value.
- * @link https://redis.io/commands/ltrim
- * @example
- *
- * $redisCluster->rPush('key1', 'A');
- * $redisCluster->rPush('key1', 'B');
- * $redisCluster->rPush('key1', 'C');
- * $redisCluster->lRange('key1', 0, -1); // array('A', 'B', 'C')
- * $redisCluster->lTrim('key1', 0, 1);
- * $redisCluster->lRange('key1', 0, -1); // array('A', 'B')
- *
- */
- public function lTrim($key, $start, $stop) { }
-
- /**
- * Returns the specified elements of the list stored at the specified key in
- * the range [start, end]. start and stop are interpretated as indices: 0 the first element,
- * 1 the second ... -1 the last element, -2 the penultimate ...
- *
- * @param string $key
- * @param int $start
- * @param int $end
- *
- * @return array containing the values in specified range.
- * @link https://redis.io/commands/lrange
- * @example
- *
- * $redisCluster->rPush('key1', 'A');
- * $redisCluster->rPush('key1', 'B');
- * $redisCluster->rPush('key1', 'C');
- * $redisCluster->lRange('key1', 0, -1); // array('A', 'B', 'C')
- *
- */
- public function lRange($key, $start, $end) { }
-
- /**
- * Deletes the elements of the sorted set stored at the specified key which have rank in the range [start,end].
- *
- * @param string $key
- * @param int $start
- * @param int $end
- *
- * @return int The number of values deleted from the sorted set
- * @link https://redis.io/commands/zremrangebyrank
- * @example
- *
- * $redisCluster->zAdd('key', 1, 'one');
- * $redisCluster->zAdd('key', 2, 'two');
- * $redisCluster->zAdd('key', 3, 'three');
- * $redisCluster->zRemRangeByRank('key', 0, 1); // 2
- * $redisCluster->zRange('key', 0, -1, true); // array('three' => 3)
- *
- */
- public function zRemRangeByRank($key, $start, $end) { }
-
- /**
- * Publish messages to channels. Warning: this function will probably change in the future.
- *
- * @param string $channel a channel to publish to
- * @param string $message string
- *
- * @link https://redis.io/commands/publish
- * @return int Number of clients that received the message
- * @example $redisCluster->publish('chan-1', 'hello, world!'); // send message.
- */
- public function publish($channel, $message) { }
-
- /**
- * Renames a key.
- *
- * @param string $srcKey
- * @param string $dstKey
- *
- * @return bool TRUE in case of success, FALSE in case of failure.
- * @link https://redis.io/commands/rename
- * @example
- *
- * $redisCluster->set('x', '42');
- * $redisCluster->rename('x', 'y');
- * $redisCluster->get('y'); // → 42
- * $redisCluster->get('x'); // → `FALSE`
- *
- */
- public function rename($srcKey, $dstKey) { }
-
- /**
- * Renames a key.
- *
- * Same as rename, but will not replace a key if the destination already exists.
- * This is the same behaviour as setNx.
- *
- * @param string $srcKey
- * @param string $dstKey
- *
- * @return bool TRUE in case of success, FALSE in case of failure.
- * @link https://redis.io/commands/renamenx
- * @example
- *
- * $redisCluster->set('x', '42');
- * $redisCluster->renameNx('x', 'y');
- * $redisCluster->get('y'); // → 42
- * $redisCluster->get('x'); // → `FALSE`
- *
- */
- public function renameNx($srcKey, $dstKey) { }
-
- /**
- * When called with a single key, returns the approximated cardinality computed by the HyperLogLog data
- * structure stored at the specified variable, which is 0 if the variable does not exist.
- *
- * @param string|array $key
- *
- * @return int
- * @link https://redis.io/commands/pfcount
- * @example
- *
- * $redisCluster->pfAdd('key1', array('elem1', 'elem2'));
- * $redisCluster->pfAdd('key2', array('elem3', 'elem2'));
- * $redisCluster->pfCount('key1'); // int(2)
- * $redisCluster->pfCount(array('key1', 'key2')); // int(3)
- *
- */
- public function pfCount($key) { }
-
- /**
- * Adds all the element arguments to the HyperLogLog data structure stored at the key.
- *
- * @param string $key
- * @param array $elements
- *
- * @return bool
- * @link https://redis.io/commands/pfadd
- * @example $redisCluster->pfAdd('key', array('elem1', 'elem2'))
- */
- public function pfAdd($key, array $elements) { }
-
- /**
- * Merge multiple HyperLogLog values into an unique value that will approximate the cardinality
- * of the union of the observed Sets of the source HyperLogLog structures.
- *
- * @param string $destKey
- * @param array $sourceKeys
- *
- * @return bool
- * @link https://redis.io/commands/pfmerge
- * @example
- *
- * $redisCluster->pfAdd('key1', array('elem1', 'elem2'));
- * $redisCluster->pfAdd('key2', array('elem3', 'elem2'));
- * $redisCluster->pfMerge('key3', array('key1', 'key2'));
- * $redisCluster->pfCount('key3'); // int(3)
- *
- */
- public function pfMerge($destKey, array $sourceKeys) { }
-
- /**
- * Changes a substring of a larger string.
- *
- * @param string $key
- * @param int $offset
- * @param string $value
- *
- * @return string the length of the string after it was modified.
- * @link https://redis.io/commands/setrange
- * @example
- *
- * $redisCluster->set('key', 'Hello world');
- * $redisCluster->setRange('key', 6, "redis"); // returns 11
- * $redisCluster->get('key'); // "Hello redis"
- *
- */
- public function setRange($key, $offset, $value) { }
-
- /**
- * Restore a key from the result of a DUMP operation.
- *
- * @param string $key The key name
- * @param int $ttl How long the key should live (if zero, no expire will be set on the key)
- * @param string $value (binary). The Redis encoded key value (from DUMP)
- *
- * @return bool
- * @link https://redis.io/commands/restore
- * @example
- *
- * $redisCluster->set('foo', 'bar');
- * $val = $redisCluster->dump('foo');
- * $redisCluster->restore('bar', 0, $val); // The key 'bar', will now be equal to the key 'foo'
- *
- */
- public function restore($key, $ttl, $value) { }
-
- /**
- * Moves the specified member from the set at srcKey to the set at dstKey.
- *
- * @param string $srcKey
- * @param string $dstKey
- * @param string $member
- *
- * @return bool If the operation is successful, return TRUE.
- * If the srcKey and/or dstKey didn't exist, and/or the member didn't exist in srcKey, FALSE is returned.
- * @link https://redis.io/commands/smove
- * @example
- *
- * $redisCluster->sAdd('key1' , 'set11');
- * $redisCluster->sAdd('key1' , 'set12');
- * $redisCluster->sAdd('key1' , 'set13'); // 'key1' => {'set11', 'set12', 'set13'}
- * $redisCluster->sAdd('key2' , 'set21');
- * $redisCluster->sAdd('key2' , 'set22'); // 'key2' => {'set21', 'set22'}
- * $redisCluster->sMove('key1', 'key2', 'set13'); // 'key1' => {'set11', 'set12'}
- * // 'key2' => {'set21', 'set22', 'set13'}
- *
- */
- public function sMove($srcKey, $dstKey, $member) { }
-
- /**
- * Returns a range of elements from the ordered set stored at the specified key,
- * with values in the range [start, end]. start and stop are interpreted as zero-based indices:
- * 0 the first element,
- * 1 the second ...
- * -1 the last element,
- * -2 the penultimate ...
- *
- * @param string $key
- * @param int $start
- * @param int $end
- * @param bool $withscores
- *
- * @return array Array containing the values in specified range.
- * @link https://redis.io/commands/zrange
- * @example
- *
- * $redisCluster->zAdd('key1', 0, 'val0');
- * $redisCluster->zAdd('key1', 2, 'val2');
- * $redisCluster->zAdd('key1', 10, 'val10');
- * $redisCluster->zRange('key1', 0, -1); // array('val0', 'val2', 'val10')
- * // with scores
- * $redisCluster->zRange('key1', 0, -1, true); // array('val0' => 0, 'val2' => 2, 'val10' => 10)
- *
- */
- public function zRange($key, $start, $end, $withscores = null) { }
-
- /**
- * Returns the elements of the sorted set stored at the specified key in the range [start, end]
- * in reverse order. start and stop are interpretated as zero-based indices:
- * 0 the first element,
- * 1 the second ...
- * -1 the last element,
- * -2 the penultimate ...
- *
- * @param string $key
- * @param int $start
- * @param int $end
- * @param bool $withscore
- *
- * @return array Array containing the values in specified range.
- * @link https://redis.io/commands/zrevrange
- * @example
- *
- * $redisCluster->zAdd('key', 0, 'val0');
- * $redisCluster->zAdd('key', 2, 'val2');
- * $redisCluster->zAdd('key', 10, 'val10');
- * $redisCluster->zRevRange('key', 0, -1); // array('val10', 'val2', 'val0')
- *
- * // with scores
- * $redisCluster->zRevRange('key', 0, -1, true); // array('val10' => 10, 'val2' => 2, 'val0' => 0)
- *
- */
- public function zRevRange($key, $start, $end, $withscore = null) { }
-
- /**
- * Returns the elements of the sorted set stored at the specified key which have scores in the
- * range [start,end]. Adding a parenthesis before start or end excludes it from the range.
- * +inf and -inf are also valid limits.
- *
- * zRevRangeByScore returns the same items in reverse order, when the start and end parameters are swapped.
- *
- * @param string $key
- * @param int $start
- * @param int $end
- * @param array $options Two options are available:
- * - withscores => TRUE,
- * - and limit => array($offset, $count)
- *
- * @return array Array containing the values in specified range.
- * @link https://redis.io/commands/zrangebyscore
- * @example
- *
- * $redisCluster->zAdd('key', 0, 'val0');
- * $redisCluster->zAdd('key', 2, 'val2');
- * $redisCluster->zAdd('key', 10, 'val10');
- * $redisCluster->zRangeByScore('key', 0, 3);
- * // array('val0', 'val2')
- * $redisCluster->zRangeByScore('key', 0, 3, array('withscores' => TRUE);
- * // array('val0' => 0, 'val2' => 2)
- * $redisCluster->zRangeByScore('key', 0, 3, array('limit' => array(1, 1));
- * // array('val2' => 2)
- * $redisCluster->zRangeByScore('key', 0, 3, array('limit' => array(1, 1));
- * // array('val2')
- * $redisCluster->zRangeByScore('key', 0, 3, array('withscores' => TRUE, 'limit' => array(1, 1));
- * // array('val2'=> 2)
- *
- */
- public function zRangeByScore($key, $start, $end, array $options = array()) { }
-
- /**
- * @see zRangeByScore()
- *
- * @param string $key
- * @param int $start
- * @param int $end
- * @param array $options
- *
- * @return array
- */
- public function zRevRangeByScore($key, $start, $end, array $options = array()) { }
-
- /**
- * Returns a range of members in a sorted set, by lexicographical range
- *
- * @param string $key The ZSET you wish to run against.
- * @param int $min The minimum alphanumeric value you wish to get.
- * @param int $max The maximum alphanumeric value you wish to get.
- * @param int $offset Optional argument if you wish to start somewhere other than the first element.
- * @param int $limit Optional argument if you wish to limit the number of elements returned.
- *
- * @return array Array containing the values in the specified range.
- * @link https://redis.io/commands/zrangebylex
- * @example
- *
- * foreach (array('a', 'b', 'c', 'd', 'e', 'f', 'g') as $k => $char) {
- * $redisCluster->zAdd('key', $k, $char);
- * }
- *
- * $redisCluster->zRangeByLex('key', '-', '[c'); // array('a', 'b', 'c')
- * $redisCluster->zRangeByLex('key', '-', '(c'); // array('a', 'b')
- * $redisCluster->zRevRangeByLex('key', '(c','-'); // array('b', 'a')
- *
- */
- public function zRangeByLex($key, $min, $max, $offset = null, $limit = null) { }
-
- /**
- * @see zRangeByLex()
- *
- * @param string $key
- * @param int $min
- * @param int $max
- * @param int $offset
- * @param int $limit
- *
- * @return array
- * @link https://redis.io/commands/zrevrangebylex
- */
- public function zRevRangeByLex($key, $min, $max, $offset = null, $limit = null) { }
-
- /**
- * Count the number of members in a sorted set between a given lexicographical range.
- *
- * @param string $key
- * @param int $min
- * @param int $max
- *
- * @return int The number of elements in the specified score range.
- * @link https://redis.io/commands/zlexcount
- * @example
- *
- * foreach (array('a', 'b', 'c', 'd', 'e', 'f', 'g') as $k => $char) {
- * $redisCluster->zAdd('key', $k, $char);
- * }
- * $redisCluster->zLexCount('key', '[b', '[f'); // 5
- *
- */
- public function zLexCount($key, $min, $max) { }
-
- /**
- * Remove all members in a sorted set between the given lexicographical range.
- *
- * @param string $key The ZSET you wish to run against.
- * @param int $min The minimum alphanumeric value you wish to get.
- * @param int $max The maximum alphanumeric value you wish to get.
- *
- * @return array the number of elements removed.
- * @link https://redis.io/commands/zremrangebylex
- * @example
- *
- * foreach (array('a', 'b', 'c', 'd', 'e', 'f', 'g') as $k => $char) {
- * $redisCluster->zAdd('key', $k, $char);
- * }
- * $redisCluster->zRemRangeByLex('key', '(b','[d'); // 2 , remove element 'c' and 'd'
- * $redisCluster->zRange('key',0,-1);// array('a','b','e','f','g')
- *
- */
- public function zRemRangeByLex($key, $min, $max) {
- }
-
- /**
- * Add multiple sorted sets and store the resulting sorted set in a new key
- *
- * @param string $Output
- * @param array $ZSetKeys
- * @param null|array $Weights
- * @param string $aggregateFunction Either "SUM", "MIN", or "MAX": defines the behaviour to use on
- * duplicate entries during the zUnion.
- *
- * @return int The number of values in the new sorted set.
- * @link https://redis.io/commands/zunionstore
- * @example
- *
- * $redisCluster->del('k1');
- * $redisCluster->del('k2');
- * $redisCluster->del('k3');
- * $redisCluster->del('ko1');
- * $redisCluster->del('ko2');
- * $redisCluster->del('ko3');
- *
- * $redisCluster->zAdd('k1', 0, 'val0');
- * $redisCluster->zAdd('k1', 1, 'val1');
- *
- * $redisCluster->zAdd('k2', 2, 'val2');
- * $redisCluster->zAdd('k2', 3, 'val3');
- *
- * $redisCluster->zUnionStore('ko1', array('k1', 'k2')); // 4, 'ko1' => array('val0', 'val1', 'val2', 'val3')
- *
- * // Weighted zUnionStore
- * $redisCluster->zUnionStore('ko2', array('k1', 'k2'), array(1, 1)); // 4, 'ko2' => array('val0', 'val1', 'val2','val3')
- * $redisCluster->zUnionStore('ko3', array('k1', 'k2'), array(5, 1)); // 4, 'ko3' => array('val0', 'val2', 'val3','val1')
- *
- */
- public function zUnionStore($Output, $ZSetKeys, ?array $Weights = null, $aggregateFunction = 'SUM') { }
-
- /**
- * Intersect multiple sorted sets and store the resulting sorted set in a new key
- *
- * @param string $Output
- * @param array $ZSetKeys
- * @param null|array $Weights
- * @param string $aggregateFunction Either "SUM", "MIN", or "MAX":
- * defines the behaviour to use on duplicate entries during the zInterStore.
- *
- * @return int The number of values in the new sorted set.
- * @link https://redis.io/commands/zinterstore
- * @example
- *
- * $redisCluster->del('k1');
- * $redisCluster->del('k2');
- * $redisCluster->del('k3');
- *
- * $redisCluster->del('ko1');
- * $redisCluster->del('ko2');
- * $redisCluster->del('ko3');
- * $redisCluster->del('ko4');
- *
- * $redisCluster->zAdd('k1', 0, 'val0');
- * $redisCluster->zAdd('k1', 1, 'val1');
- * $redisCluster->zAdd('k1', 3, 'val3');
- *
- * $redisCluster->zAdd('k2', 2, 'val1');
- * $redisCluster->zAdd('k2', 3, 'val3');
- *
- * $redisCluster->zInterStore('ko1', array('k1', 'k2')); // 2, 'ko1' => array('val1', 'val3')
- * $redisCluster->zInterStore('ko2', array('k1', 'k2'), array(1, 1)); // 2, 'ko2' => array('val1', 'val3')
- *
- * // Weighted zInterStore
- * $redisCluster->zInterStore('ko3', array('k1', 'k2'), array(1, 5), 'min'); // 2, 'ko3' => array('val1', 'val3')
- * $redisCluster->zInterStore('ko4', array('k1', 'k2'), array(1, 5), 'max'); // 2, 'ko4' => array('val3', 'val1')
- *
- */
- public function zInterStore($Output, $ZSetKeys, array $Weights = null, $aggregateFunction = 'SUM') { }
-
- /**
- * Deletes a specified member from the ordered set.
- *
- * @param string $key
- * @param string $member1
- * @param string $member2
- * @param string $memberN
- *
- * @return int Number of deleted values
- * @link https://redis.io/commands/zrem
- * @example
- *
- * $redisCluster->zAdd('z', 1, 'v1', 2, 'v2', 3, 'v3', 4, 'v4' ); // int(2)
- * $redisCluster->zRem('z', 'v2', 'v3'); // int(2)
- * var_dump( $redisCluster->zRange('z', 0, -1) );
- * //// Output:
- * //
- * // array(2) {
- * // [0]=> string(2) "v1"
- * // [1]=> string(2) "v4"
- * // }
- *
- */
- public function zRem($key, $member1, $member2 = null, $memberN = null) { }
-
- /**
- * Sort
- *
- * @param string $key
- * @param array $option array(key => value, ...) - optional, with the following keys and values:
- * - 'by' => 'some_pattern_*',
- * - 'limit' => array(0, 1),
- * - 'get' => 'some_other_pattern_*' or an array of patterns,
- * - 'sort' => 'asc' or 'desc',
- * - 'alpha' => TRUE,
- * - 'store' => 'external-key'
- *
- * @return array
- * An array of values, or a number corresponding to the number of elements stored if that was used.
- * @link https://redis.io/commands/sort
- * @example
- *
- * $redisCluster->del('s');
- * $redisCluster->sadd('s', 5);
- * $redisCluster->sadd('s', 4);
- * $redisCluster->sadd('s', 2);
- * $redisCluster->sadd('s', 1);
- * $redisCluster->sadd('s', 3);
- *
- * var_dump($redisCluster->sort('s')); // 1,2,3,4,5
- * var_dump($redisCluster->sort('s', array('sort' => 'desc'))); // 5,4,3,2,1
- * var_dump($redisCluster->sort('s', array('sort' => 'desc', 'store' => 'out'))); // (int)5
- *
- */
- public function sort($key, $option = null) { }
-
- /**
- * Describes the object pointed to by a key.
- * The information to retrieve (string) and the key (string).
- * Info can be one of the following:
- * - "encoding"
- * - "refcount"
- * - "idletime"
- *
- * @param string $string
- * @param string $key
- *
- * @return string|false for "encoding", int for "refcount" and "idletime", FALSE if the key doesn't exist.
- * @link https://redis.io/commands/object
- * @example
- *
- * $redisCluster->object("encoding", "l"); // → ziplist
- * $redisCluster->object("refcount", "l"); // → 1
- * $redisCluster->object("idletime", "l"); // → 400 (in seconds, with a precision of 10 seconds).
- *
- */
- public function object($string = '', $key = '') { }
-
- /**
- * Subscribe to channels. Warning: this function will probably change in the future.
- *
- * @param array $channels an array of channels to subscribe to
- * @param string|array $callback either a string or an array($instance, 'method_name').
- * The callback function receives 3 parameters: the redis instance, the channel
- * name, and the message.
- *
- * @return mixed Any non-null return value in the callback will be returned to the caller.
- * @link https://redis.io/commands/subscribe
- * @example
- *
- * function f($redisCluster, $chan, $msg) {
- * switch($chan) {
- * case 'chan-1':
- * ...
- * break;
- *
- * case 'chan-2':
- * ...
- * break;
- *
- * case 'chan-2':
- * ...
- * break;
- * }
- * }
- *
- * $redisCluster->subscribe(array('chan-1', 'chan-2', 'chan-3'), 'f'); // subscribe to 3 chans
- *
- */
- public function subscribe($channels, $callback) { }
-
- /**
- * Subscribe to channels by pattern
- *
- * @param array $patterns The number of elements removed from the set.
- * @param string|array $callback Either a string or an array with an object and method.
- * The callback will get four arguments ($redis, $pattern, $channel, $message)
- *
- * @return mixed Any non-null return value in the callback will be returned to the caller.
- *
- * @link https://redis.io/commands/psubscribe
- * @example
- *
- * function psubscribe($redisCluster, $pattern, $chan, $msg) {
- * echo "Pattern: $pattern\n";
- * echo "Channel: $chan\n";
- * echo "Payload: $msg\n";
- * }
- *
- */
- public function psubscribe($patterns, $callback) { }
-
- /**
- * Unsubscribes the client from the given channels, or from all of them if none is given.
- *
- * @param $channels
- * @param $callback
- */
- public function unSubscribe($channels, $callback) { }
-
- /**
- * Unsubscribes the client from the given patterns, or from all of them if none is given.
- *
- * @param $channels
- * @param $callback
- */
- public function punSubscribe($channels, $callback) { }
-
- /**
- * Evaluate a LUA script serverside, from the SHA1 hash of the script instead of the script itself.
- * In order to run this command Redis will have to have already loaded the script, either by running it or via
- * the SCRIPT LOAD command.
- *
- * @param string $scriptSha
- * @param array $args
- * @param int $numKeys
- *
- * @return mixed @see eval()
- * @see eval()
- * @link https://redis.io/commands/evalsha
- * @example
- *
- * $script = 'return 1';
- * $sha = $redisCluster->script('load', $script);
- * $redisCluster->evalSha($sha); // Returns 1
- *
- */
- public function evalSha($scriptSha, $args = array(), $numKeys = 0) { }
-
- /**
- * Scan the keyspace for keys.
- *
- * @param int &$iterator Iterator, initialized to NULL.
- * @param string|array $node Node identified by key or host/port array
- * @param string $pattern Pattern to match.
- * @param int $count Count of keys per iteration (only a suggestion to Redis).
- *
- * @return array|false This function will return an array of keys or FALSE if there are no more keys.
- * @link https://redis.io/commands/scan
- * @example
- *
- * $iterator = null;
- * while($keys = $redisCluster->scan($iterator)) {
- * foreach($keys as $key) {
- * echo $key . PHP_EOL;
- * }
- * }
- *
- */
- public function scan(&$iterator, $node, $pattern = null, $count = 0) { }
-
- /**
- * Scan a set for members.
- *
- * @param string $key The set to search.
- * @param int &$iterator LONG (reference) to the iterator as we go.
- * @param null $pattern String, optional pattern to match against.
- * @param int $count How many members to return at a time (Redis might return a different amount).
- *
- * @return array|false PHPRedis will return an array of keys or FALSE when we're done iterating.
- * @link https://redis.io/commands/sscan
- * @example
- *
- * $iterator = null;
- * while ($members = $redisCluster->sScan('set', $iterator)) {
- * foreach ($members as $member) {
- * echo $member . PHP_EOL;
- * }
- * }
- *
- */
- public function sScan($key, &$iterator, $pattern = null, $count = 0) { }
-
- /**
- * Scan a sorted set for members, with optional pattern and count.
- *
- * @param string $key String, the set to scan.
- * @param int &$iterator Long (reference), initialized to NULL.
- * @param string $pattern String (optional), the pattern to match.
- * @param int $count How many keys to return per iteration (Redis might return a different number).
- *
- * @return array|false PHPRedis will return matching keys from Redis, or FALSE when iteration is complete.
- * @link https://redis.io/commands/zscan
- * @example
- *
- * $iterator = null;
- * while ($members = $redis-zscan('zset', $iterator)) {
- * foreach ($members as $member => $score) {
- * echo $member . ' => ' . $score . PHP_EOL;
- * }
- * }
- *
- */
- public function zScan($key, &$iterator, $pattern = null, $count = 0) { }
-
- /**
- * Scan a HASH value for members, with an optional pattern and count.
- *
- * @param string $key
- * @param int &$iterator
- * @param string $pattern Optional pattern to match against.
- * @param int $count How many keys to return in a go (only a sugestion to Redis).
- *
- * @return array An array of members that match our pattern.
- * @link https://redis.io/commands/hscan
- * @example
- *
- * $iterator = null;
- * while($elements = $redisCluster->hscan('hash', $iterator)) {
- * foreach($elements as $key => $value) {
- * echo $key . ' => ' . $value . PHP_EOL;
- * }
- * }
- *
- */
- public function hScan($key, &$iterator, $pattern = null, $count = 0) { }
-
- /**
- * Detect whether we're in ATOMIC/MULTI/PIPELINE mode.
- *
- * @return int Either RedisCluster::ATOMIC, RedisCluster::MULTI or RedisCluster::PIPELINE
- * @example $redisCluster->getMode();
- */
- public function getMode() { }
-
- /**
- * The last error message (if any)
- *
- * @return string|null A string with the last returned script based error message, or NULL if there is no error
- * @example
- *
- * $redisCluster->eval('this-is-not-lua');
- * $err = $redisCluster->getLastError();
- * // "ERR Error compiling script (new function): user_script:1: '=' expected near '-'"
- *
- */
- public function getLastError() { }
-
- /**
- * Clear the last error message
- *
- * @return bool true
- * @example
- *
- * $redisCluster->set('x', 'a');
- * $redisCluster->incr('x');
- * $err = $redisCluster->getLastError();
- * // "ERR value is not an integer or out of range"
- * $redisCluster->clearLastError();
- * $err = $redisCluster->getLastError();
- * // NULL
- *
- */
- public function clearLastError() { }
-
- /**
- * Get client option
- *
- * @param string $name parameter name
- *
- * @return int Parameter value.
- * @example
- * // return RedisCluster::SERIALIZER_NONE, RedisCluster::SERIALIZER_PHP, or RedisCluster::SERIALIZER_IGBINARY.
- * $redisCluster->getOption(RedisCluster::OPT_SERIALIZER);
- */
- public function getOption($name) { }
-
- /**
- * Set client option.
- *
- * @param string $name parameter name
- * @param string $value parameter value
- *
- * @return bool TRUE on success, FALSE on error.
- * @example
- * - * $redisCluster->setOption(RedisCluster::OPT_SERIALIZER, RedisCluster::SERIALIZER_NONE); // don't serialize data - * $redisCluster->setOption(RedisCluster::OPT_SERIALIZER, RedisCluster::SERIALIZER_PHP); // use built-in serialize/unserialize - * $redisCluster->setOption(RedisCluster::OPT_SERIALIZER, RedisCluster::SERIALIZER_IGBINARY); // use igBinary serialize/unserialize - * $redisCluster->setOption(RedisCluster::OPT_PREFIX, 'myAppName:'); // use custom prefix on all keys - *- */ - public function setOption($name, $value) { } - - /** - * A utility method to prefix the value with the prefix setting for phpredis. - * - * @param mixed $value The value you wish to prefix - * - * @return string If a prefix is set up, the value now prefixed. If there is no prefix, the value will be returned unchanged. - * @example - *
- * $redisCluster->setOption(RedisCluster::OPT_PREFIX, 'my-prefix:');
- * $redisCluster->_prefix('my-value'); // Will return 'my-prefix:my-value'
- *
- */
- public function _prefix($value) { }
-
- /**
- * A utility method to serialize values manually. This method allows you to serialize a value with whatever
- * serializer is configured, manually. This can be useful for serialization/unserialization of data going in
- * and out of EVAL commands as phpredis can't automatically do this itself. Note that if no serializer is
- * set, phpredis will change Array values to 'Array', and Objects to 'Object'.
- *
- * @param mixed $value The value to be serialized.
- *
- * @return mixed
- * @example
- *
- * $redisCluster->setOption(RedisCluster::OPT_SERIALIZER, RedisCluster::SERIALIZER_NONE);
- * $redisCluster->_serialize("foo"); // returns "foo"
- * $redisCluster->_serialize(Array()); // Returns "Array"
- * $redisCluster->_serialize(new stdClass()); // Returns "Object"
- *
- * $redisCluster->setOption(RedisCluster::OPT_SERIALIZER, RedisCluster::SERIALIZER_PHP);
- * $redisCluster->_serialize("foo"); // Returns 's:3:"foo";'
- *
- */
- public function _serialize($value) { }
-
- /**
- * A utility method to unserialize data with whatever serializer is set up. If there is no serializer set, the
- * value will be returned unchanged. If there is a serializer set up, and the data passed in is malformed, an
- * exception will be thrown. This can be useful if phpredis is serializing values, and you return something from
- * redis in a LUA script that is serialized.
- *
- * @param string $value The value to be unserialized
- *
- * @return mixed
- * @example
- *
- * $redisCluster->setOption(RedisCluster::OPT_SERIALIZER, RedisCluster::SERIALIZER_PHP);
- * $redisCluster->_unserialize('a:3:{i:0;i:1;i:1;i:2;i:2;i:3;}'); // Will return Array(1,2,3)
- *
- */
- public function _unserialize($value) { }
-
- /**
- * Return all redis master nodes
- *
- * @return array
- * @example
- * - * $redisCluster->_masters(); // Will return [[0=>'127.0.0.1','6379'],[0=>'127.0.0.1','6380']] - *- */ - public function _masters() { } - - /** - * Enter and exit transactional mode. - * - * @param int $mode RedisCluster::MULTI|RedisCluster::PIPELINE - * Defaults to RedisCluster::MULTI. - * A RedisCluster::MULTI block of commands runs as a single transaction; - * a RedisCluster::PIPELINE block is simply transmitted faster to the server, but without any guarantee - * of atomicity. discard cancels a transaction. - * - * @return Redis returns the Redis instance and enters multi-mode. - * Once in multi-mode, all subsequent method calls return the same object until exec() is called. - * @link https://redis.io/commands/multi - * @example - *
- * $ret = $redisCluster->multi()
- * ->set('key1', 'val1')
- * ->get('key1')
- * ->set('key2', 'val2')
- * ->get('key2')
- * ->exec();
- *
- * //$ret == array (
- * // 0 => TRUE,
- * // 1 => 'val1',
- * // 2 => TRUE,
- * // 3 => 'val2');
- *
- */
- public function multi($mode = RedisCluster::MULTI) { }
-
- /**
- * @see multi()
- * @return void|array
- * @link https://redis.io/commands/exec
- */
- public function exec() { }
-
- /**
- * @see multi()
- * @link https://redis.io/commands/discard
- */
- public function discard() { }
-
- /**
- * Watches a key for modifications by another client. If the key is modified between WATCH and EXEC,
- * the MULTI/EXEC transaction will fail (return FALSE). unwatch cancels all the watching of all keys by this client.
- *
- * @param string|array $key : a list of keys
- *
- * @return void
- * @link https://redis.io/commands/watch
- * @example
- *
- * $redisCluster->watch('x');
- * // long code here during the execution of which other clients could well modify `x`
- * $ret = $redisCluster->multi()
- * ->incr('x')
- * ->exec();
- * // $ret = FALSE if x has been modified between the call to WATCH and the call to EXEC.
- *
- */
- public function watch($key) { }
-
- /**
- * @see watch()
- * @link https://redis.io/commands/unwatch
- */
- public function unwatch() { }
-
- /**
- * Performs a synchronous save at a specific node.
- *
- * @param string|array $nodeParams key or [host,port]
- *
- * @return bool TRUE in case of success, FALSE in case of failure.
- * If a save is already running, this command will fail and return FALSE.
- * @link https://redis.io/commands/save
- * @example
- * $redisCluster->save('x'); //key
- * $redisCluster->save(['127.0.0.1',6379]); //[host,port]
- */
- public function save($nodeParams) { }
-
- /**
- * Performs a background save at a specific node.
- *
- * @param string|array $nodeParams key or [host,port]
- *
- * @return bool TRUE in case of success, FALSE in case of failure.
- * If a save is already running, this command will fail and return FALSE.
- * @link https://redis.io/commands/bgsave
- */
- public function bgsave($nodeParams) { }
-
- /**
- * Removes all entries from the current database at a specific node.
- *
- * @param string|array $nodeParams key or [host,port]
- *
- * @return bool Always TRUE.
- * @link https://redis.io/commands/flushdb
- */
- public function flushDB($nodeParams) { }
-
- /**
- * Removes all entries from all databases at a specific node.
- *
- * @param string|array $nodeParams key or [host,port]
- *
- * @return bool Always TRUE.
- * @link https://redis.io/commands/flushall
- */
- public function flushAll($nodeParams) { }
-
- /**
- * Returns the current database's size at a specific node.
- *
- * @param string|array $nodeParams key or [host,port]
- *
- * @return int DB size, in number of keys.
- * @link https://redis.io/commands/dbsize
- * @example
- *
- * $count = $redisCluster->dbSize('x');
- * echo "Redis has $count keys\n";
- *
- */
- public function dbSize($nodeParams) { }
-
- /**
- * Starts the background rewrite of AOF (Append-Only File) at a specific node.
- *
- * @param string|array $nodeParams key or [host,port]
- *
- * @return bool TRUE in case of success, FALSE in case of failure.
- * @link https://redis.io/commands/bgrewriteaof
- * @example $redisCluster->bgrewriteaof('x');
- */
- public function bgrewriteaof($nodeParams) { }
-
- /**
- * Returns the timestamp of the last disk save at a specific node.
- *
- * @param string|array $nodeParams key or [host,port]
- *
- * @return int timestamp.
- * @link https://redis.io/commands/lastsave
- * @example $redisCluster->lastSave('x');
- */
- public function lastSave($nodeParams) { }
-
- /**
- * Returns an associative array of strings and integers
- *
- * @param string $option Optional. The option to provide redis.
- * SERVER | CLIENTS | MEMORY | PERSISTENCE | STATS | REPLICATION | CPU | CLASTER | KEYSPACE
- * | COMANDSTATS
- *
- * Returns an associative array of strings and integers, with the following keys:
- * - redis_version
- * - redis_git_sha1
- * - redis_git_dirty
- * - redis_build_id
- * - redis_mode
- * - os
- * - arch_bits
- * - multiplexing_api
- * - atomicvar_api
- * - gcc_version
- * - process_id
- * - run_id
- * - tcp_port
- * - uptime_in_seconds
- * - uptime_in_days
- * - hz
- * - lru_clock
- * - executable
- * - config_file
- * - connected_clients
- * - client_longest_output_list
- * - client_biggest_input_buf
- * - blocked_clients
- * - used_memory
- * - used_memory_human
- * - used_memory_rss
- * - used_memory_rss_human
- * - used_memory_peak
- * - used_memory_peak_human
- * - used_memory_peak_perc
- * - used_memory_peak
- * - used_memory_overhead
- * - used_memory_startup
- * - used_memory_dataset
- * - used_memory_dataset_perc
- * - total_system_memory
- * - total_system_memory_human
- * - used_memory_lua
- * - used_memory_lua_human
- * - maxmemory
- * - maxmemory_human
- * - maxmemory_policy
- * - mem_fragmentation_ratio
- * - mem_allocator
- * - active_defrag_running
- * - lazyfree_pending_objects
- * - mem_fragmentation_ratio
- * - loading
- * - rdb_changes_since_last_save
- * - rdb_bgsave_in_progress
- * - rdb_last_save_time
- * - rdb_last_bgsave_status
- * - rdb_last_bgsave_time_sec
- * - rdb_current_bgsave_time_sec
- * - rdb_last_cow_size
- * - aof_enabled
- * - aof_rewrite_in_progress
- * - aof_rewrite_scheduled
- * - aof_last_rewrite_time_sec
- * - aof_current_rewrite_time_sec
- * - aof_last_bgrewrite_status
- * - aof_last_write_status
- * - aof_last_cow_size
- * - changes_since_last_save
- * - aof_current_size
- * - aof_base_size
- * - aof_pending_rewrite
- * - aof_buffer_length
- * - aof_rewrite_buffer_length
- * - aof_pending_bio_fsync
- * - aof_delayed_fsync
- * - loading_start_time
- * - loading_total_bytes
- * - loading_loaded_bytes
- * - loading_loaded_perc
- * - loading_eta_seconds
- * - total_connections_received
- * - total_commands_processed
- * - instantaneous_ops_per_sec
- * - total_net_input_bytes
- * - total_net_output_bytes
- * - instantaneous_input_kbps
- * - instantaneous_output_kbps
- * - rejected_connections
- * - maxclients
- * - sync_full
- * - sync_partial_ok
- * - sync_partial_err
- * - expired_keys
- * - evicted_keys
- * - keyspace_hits
- * - keyspace_misses
- * - pubsub_channels
- * - pubsub_patterns
- * - latest_fork_usec
- * - migrate_cached_sockets
- * - slave_expires_tracked_keys
- * - active_defrag_hits
- * - active_defrag_misses
- * - active_defrag_key_hits
- * - active_defrag_key_misses
- * - role
- * - master_replid
- * - master_replid2
- * - master_repl_offset
- * - second_repl_offset
- * - repl_backlog_active
- * - repl_backlog_size
- * - repl_backlog_first_byte_offset
- * - repl_backlog_histlen
- * - master_host
- * - master_port
- * - master_link_status
- * - master_last_io_seconds_ago
- * - master_sync_in_progress
- * - slave_repl_offset
- * - slave_priority
- * - slave_read_only
- * - master_sync_left_bytes
- * - master_sync_last_io_seconds_ago
- * - master_link_down_since_seconds
- * - connected_slaves
- * - min-slaves-to-write
- * - min-replicas-to-write
- * - min_slaves_good_slaves
- * - used_cpu_sys
- * - used_cpu_user
- * - used_cpu_sys_children
- * - used_cpu_user_children
- * - cluster_enabled
- *
- * @link https://redis.io/commands/info
- * @return array
- * @example
- *
- * $redisCluster->info();
- *
- * or
- *
- * $redisCluster->info("COMMANDSTATS"); //Information on the commands that have been run (>=2.6 only)
- * $redisCluster->info("CPU"); // just CPU information from Redis INFO
- *
- */
- public function info($option = null) { }
-
- /**
- * @since redis >= 2.8.12.
- * Returns the role of the instance in the context of replication
- *
- * @param string|array $nodeParams key or [host,port]
- *
- * @return array
- * @link https://redis.io/commands/role
- * @example
- * - * $redisCluster->role(['127.0.0.1',6379]); - * // [ 0=>'master',1 => 3129659, 2 => [ ['127.0.0.1','9001','3129242'], ['127.0.0.1','9002','3129543'] ] ] - *- */ - public function role($nodeParams) { } - - /** - * Returns a random key at the specified node - * - * @param string|array $nodeParams key or [host,port] - * - * @return string an existing key in redis. - * @link https://redis.io/commands/randomkey - * @example - *
- * $key = $redisCluster->randomKey('x');
- * $surprise = $redisCluster->get($key); // who knows what's in there.
- *
- */
- public function randomKey($nodeParams) { }
-
- /**
- * Return the specified node server time.
- *
- * @param string|array $nodeParams key or [host,port]
- *
- * @return array If successfully, the time will come back as an associative array with element zero being the
- * unix timestamp, and element one being microseconds.
- * @link https://redis.io/commands/time
- * @example
- *
- * var_dump( $redisCluster->time('x') );
- * //// Output:
- * //
- * // array(2) {
- * // [0] => string(10) "1342364352"
- * // [1] => string(6) "253002"
- * // }
- *
- */
- public function time($nodeParams) { }
-
- /**
- * Check the specified node status
- *
- * @param string|array $nodeParams key or [host,port]
- *
- * @return string STRING: +PONG on success. Throws a RedisException object on connectivity error, as described
- * above.
- * @link https://redis.io/commands/ping
- */
- public function ping($nodeParams) { }
-
- /**
- * Returns message.
- *
- * @param string|array $nodeParams key or [host,port]
- * @param string $msg
- *
- * @return mixed
- */
- public function echo ($nodeParams, $msg) { }
-
- /**
- * Returns Array reply of details about all Redis Cluster commands.
- *
- * @return mixed array | bool
- */
- public function command() { }
-
- /**
- * Send arbitrary things to the redis server at the specified node
- *
- * @param string|array $nodeParams key or [host,port]
- * @param string $command Required command to send to the server.
- * @param mixed $arguments Optional variable amount of arguments to send to the server.
- *
- * @return mixed
- */
- public function rawCommand($nodeParams, $command, $arguments) { }
-
- /**
- * @since redis >= 3.0
- * Executes cluster command
- *
- * @param string|array $nodeParams key or [host,port]
- * @param string $command Required command to send to the server.
- * @param mixed $arguments Optional variable amount of arguments to send to the server.
- *
- * @return mixed
- * @link https://redis.io/commands#cluster
- * @example
- * - * $redisCluster->cluster(['127.0.0.1',6379],'INFO'); - *- */ - public function cluster($nodeParams, $command, $arguments) { } - - /** - * Allows you to get information of the cluster client - * - * @param string|array $nodeParams key or [host,port] - * @param string $subCmd can be: 'LIST', 'KILL', 'GETNAME', or 'SETNAME' - * @param string $args optional arguments - */ - public function client($nodeParams, $subCmd, $args) { } - - /** - * Get or Set the redis config keys. - * - * @param string|array $nodeParams key or [host,port] - * @param string $operation either `GET` or `SET` - * @param string $key for `SET`, glob-pattern for `GET`. See https://redis.io/commands/config-get for examples. - * @param string $value optional string (only for `SET`) - * - * @return array Associative array for `GET`, key -> value - * @link https://redis.io/commands/config-get - * @link https://redis.io/commands/config-set - * @example - *
- * $redisCluster->config(['127.0.0.1',6379], "GET", "*max-*-entries*"); - * $redisCluster->config(['127.0.0.1',6379], "SET", "dir", "/var/run/redis/dumps/"); - *- */ - public function config($nodeParams, $operation, $key, $value) { } - - /** - * A command allowing you to get information on the Redis pub/sub system. - * - * @param string|array $nodeParams key or [host,port] - * - * @param string $keyword String, which can be: "channels", "numsub", or "numpat" - * @param string|array $argument Optional, variant. - * For the "channels" subcommand, you can pass a string pattern. - * For "numsub" an array of channel names - * - * @return array|int Either an integer or an array. - * - channels Returns an array where the members are the matching channels. - * - numsub Returns a key/value array where the keys are channel names and - * values are their counts. - * - numpat Integer return containing the number active pattern subscriptions. - * @link https://redis.io/commands/pubsub - * @example - *
- * $redisCluster->pubsub(['127.0.0.1',6379], 'channels'); // All channels
- * $redisCluster->pubsub(['127.0.0.1',6379], 'channels', '*pattern*'); // Just channels matching your pattern
- * $redisCluster->pubsub(['127.0.0.1',6379], 'numsub', array('chan1', 'chan2')); // Get subscriber counts for
- * 'chan1' and 'chan2'
- * $redisCluster->pubsub(['127.0.0.1',6379], 'numpat'); // Get the number of pattern subscribers
- *
- */
- public function pubsub($nodeParams, $keyword, $argument) { }
-
- /**
- * Execute the Redis SCRIPT command to perform various operations on the scripting subsystem.
- *
- * @param string|array $nodeParams key or [host,port]
- * @param string $command load | flush | kill | exists
- * @param string $script
- *
- * @return mixed
- * @link https://redis.io/commands/script-load
- * @link https://redis.io/commands/script-kill
- * @link https://redis.io/commands/script-flush
- * @link https://redis.io/commands/script-exists
- * @example
- * - * $redisCluster->script(['127.0.0.1',6379], 'load', $script); - * $redisCluster->script(['127.0.0.1',6379], 'flush'); - * $redisCluster->script(['127.0.0.1',6379], 'kill'); - * $redisCluster->script(['127.0.0.1',6379], 'exists', $script1, [$script2, $script3, ...]); - *- * - * SCRIPT LOAD will return the SHA1 hash of the passed script on success, and FALSE on failure. - * SCRIPT FLUSH should always return TRUE - * SCRIPT KILL will return true if a script was able to be killed and false if not - * SCRIPT EXISTS will return an array with TRUE or FALSE for each passed script - */ - public function script($nodeParams, $command, $script) { } - - /** - * This function is used in order to read and reset the Redis slow queries log. - * - * @param string|array $nodeParams key or [host,port] - * @param string $command - * @param mixed $argument - * - * @link https://redis.io/commands/slowlog - * @example - *
- * $redisCluster->slowLog(['127.0.0.1',6379],'get','2'); - *- */ - public function slowLog($nodeParams, $command, $argument) { } - - /** - * Add one or more geospatial items in the geospatial index represented using a sorted set - * - * @param string $key - * @param float $longitude - * @param float $latitude - * @param string $member - * - * @link https://redis.io/commands/geoadd - * @example - *
- * $redisCluster->geoAdd('Sicily', 13.361389, 38.115556, 'Palermo'); // int(1)
- * $redisCluster->geoAdd('Sicily', 15.087269, 37.502669, "Catania"); // int(1)
- *
- */
- public function geoAdd($key, $longitude, $latitude, $member) { }
-
- /**
- * Returns members of a geospatial index as standard geohash strings
- *
- * @param string $key
- * @param string $member1
- * @param string $member2
- * @param string $memberN
- *
- * @example
- *
- * $redisCluster->geoAdd('Sicily', 13.361389, 38.115556, 'Palermo'); // int(1)
- * $redisCluster->geoAdd('Sicily', 15.087269, 37.502669, "Catania"); // int(1)
- * $redisCluster->geohash('Sicily','Palermo','Catania');//['sqc8b49rny0','sqdtr74hyu0']
- *
- */
- public function geohash($key, $member1, $member2 = null, $memberN = null) { }
-
- /**
- * Returns longitude and latitude of members of a geospatial index
- *
- * @param string $key
- * @param string $member1
- * @param string $member2
- * @param string $memberN
- * @example
- *
- * $redisCluster->geoAdd('Sicily', 15.087269, 37.502669, "Catania"); // int(1)
- * $redisCluster->geopos('Sicily','Palermo');//[['13.36138933897018433','38.11555639549629859']]
- *
- */
- public function geopos($key, $member1, $member2 = null, $memberN = null) { }
-
- /**
- *
- * Returns the distance between two members of a geospatial index
- *
- * @param string $key
- * @param string $member1
- * @param string $member2
- * @param string $unit The unit must be one of the following, and defaults to meters:
- * m for meters.
- * km for kilometers.
- * mi for miles.
- * ft for feet.
- *
- * @link https://redis.io/commands/geoadd
- * @example
- *
- * $redisCluster->geoAdd('Sicily', 13.361389, 38.115556, 'Palermo'); // int(1)
- * $redisCluster->geoAdd('Sicily', 15.087269, 37.502669, "Catania"); // int(1)
- * $redisCluster->geoDist('Sicily', 'Palermo' ,'Catania'); // float(166274.1516)
- * $redisCluster->geoDist('Sicily', 'Palermo','Catania', 'km'); // float(166.2742)
- *
- */
- public function geoDist($key, $member1, $member2, $unit = 'm') { }
-
- /**
- * Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a point
- *
- * @param string $key
- * @param float $longitude
- * @param float $latitude
- * @param float $radius
- * @param string $radiusUnit String can be: "m" for meters; "km" for kilometers , "mi" for miles, or "ft" for feet.
- * @param array $options
- *
- * @link https://redis.io/commands/georadius
- * @example
- *
- * $redisCluster->del('Sicily');
- * $redisCluster->geoAdd('Sicily', 12.361389, 35.115556, 'Palermo'); // int(1)
- * $redisCluster->geoAdd('Sicily', 15.087269, 37.502669, "Catania"); // int(1)
- * $redisCluster->geoAdd('Sicily', 13.3585, 35.330022, "Agrigento"); // int(1)
- *
- * var_dump( $redisCluster->geoRadius('Sicily',13.3585, 35.330022, 300, 'km', ['WITHDIST' ,'DESC']) );
- *
- * array(3) {
- * [0]=>
- * array(2) {
- * [0]=>
- * string(7) "Catania"
- * [1]=>
- * string(8) "286.9362"
- * }
- * [1]=>
- * array(2) {
- * [0]=>
- * string(7) "Palermo"
- * [1]=>
- * string(7) "93.6874"
- * }
- * [2]=>
- * array(2) {
- * [0]=>
- * string(9) "Agrigento"
- * [1]=>
- * string(6) "0.0002"
- * }
- * }
- * var_dump( $redisCluster->geoRadiusByMember('Sicily','Agrigento', 100, 'km', ['WITHDIST' ,'DESC']) );
- *
- * * array(2) {
- * [0]=>
- * array(2) {
- * [0]=>
- * string(7) "Palermo"
- * [1]=>
- * string(7) "93.6872"
- * }
- * [1]=>
- * array(2) {
- * [0]=>
- * string(9) "Agrigento"
- * [1]=>
- * string(6) "0.0000"
- * }
- * }
- *
- *
- */
- public function geoRadius($key, $longitude, $latitude, $radius, $radiusUnit, array $options) { }
-
- /**
- * Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a member
- *
- * @see geoRadius
- *
- * @param string $key
- * @param string $member
- * @param float $radius
- * @param string $radiusUnit
- * @param array $options
- */
- public function geoRadiusByMember($key, $member, $radius, $radiusUnit, array $options) { }
-
-}
-
-class RedisClusterException extends Exception {}
--- a/build/stubs/sftp.php
+++ /dev/null
@@ -1,83 +0,0 @@
-
- * The imported style sheet as a DOMDocument or
- * SimpleXMLElement object.
- *
- * @return bool TRUE on success or FALSE on failure.
- */
- public function importStylesheet ($stylesheet) {}
-
- /**
- * Transform to a DOMDocument
- * @link https://php.net/manual/en/xsltprocessor.transformtodoc.php
- * @param DOMNode $doc
- * The node to be transformed.
- *
- * @return DOMDocument|false The resulting DOMDocument or FALSE on error.
- */
- public function transformToDoc (DOMNode $doc) {}
-
- /**
- * Transform to URI
- * @link https://php.net/manual/en/xsltprocessor.transformtouri.php
- * @param DOMDocument|SimpleXMLElement $doc
- * The document to transform.
- *
- * @param string $uri
- * The target URI for the transformation.
- *
- * @return int|false the number of bytes written or FALSE if an error occurred.
- */
- public function transformToUri ($doc, $uri) {}
-
- /**
- * Transform to XML
- * @link https://php.net/manual/en/xsltprocessor.transformtoxml.php
- * @param DOMDocument|SimpleXMLElement $doc
- * The transformed document.
- *
- * @return string|false The result of the transformation as a string or FALSE on error.
- */
- public function transformToXml ($doc) {}
-
- /**
- * Set value for a parameter
- * @link https://php.net/manual/en/xsltprocessor.setparameter.php
- * @param string $namespace
- * The namespace URI of the XSLT parameter.
- *
- * @param string $name
- * The local name of the XSLT parameter.
- *
- * @param string $value
- * The new value of the XSLT parameter.
- *
- * @return bool TRUE on success or FALSE on failure.
- */
- public function setParameter ($namespace, $name, $value) {}
-
- /**
- * Get value of a parameter
- * @link https://php.net/manual/en/xsltprocessor.getparameter.php
- * @param string $namespaceURI
- * The namespace URI of the XSLT parameter.
- *
- * @param string $localName
- * The local name of the XSLT parameter.
- *
- * @return string|false The value of the parameter (as a string), or FALSE if it's not set.
- */
- public function getParameter ($namespaceURI, $localName) {}
-
- /**
- * Remove parameter
- * @link https://php.net/manual/en/xsltprocessor.removeparameter.php
- * @param string $namespaceURI
- * The namespace URI of the XSLT parameter.
- *
- * @param string $localName
- * The local name of the XSLT parameter.
- *
- * @return bool TRUE on success or FALSE on failure.
- */
- public function removeParameter ($namespaceURI, $localName) {}
-
- /**
- * Determine if PHP has EXSLT support
- * @link https://php.net/manual/en/xsltprocessor.hasexsltsupport.php
- * @return bool TRUE on success or FALSE on failure.
- * @since 5.0.4
- */
- public function hasExsltSupport () {}
-
- /**
- * Enables the ability to use PHP functions as XSLT functions
- * @link https://php.net/manual/en/xsltprocessor.registerphpfunctions.php
- * @param mixed $restrict [optional]
- * Use this parameter to only allow certain functions to be called from
- * XSLT.
- *
- *
- * This parameter can be either a string (a function name) or an array of
- * functions.
- *
- * @return void No value is returned.
- * @since 5.0.4
- */
- public function registerPHPFunctions ($restrict = null) {}
-
- /**
- * Sets profiling output file
- * @link https://php.net/manual/en/xsltprocessor.setprofiling.php
- * @param string $filename
- * Path to the file to dump profiling information.
- *
- * @return bool TRUE on success or FALSE on failure.
- */
- public function setProfiling ($filename) {}
-
- /**
- * Set security preferences
- * @link https://php.net/manual/en/xsltprocessor.setsecurityprefs.php
- * @param int $securityPrefs
- * @return int
- * @since 5.4
- */
- public function setSecurityPrefs ($securityPrefs) {}
-
- /**
- * Get security preferences
- * @link https://php.net/manual/en/xsltprocessor.getsecurityprefs.php
- * @return int
- * @since 5.4
- */
- public function getSecurityPrefs () {}
-
-}
-define ('XSL_CLONE_AUTO', 0);
-define ('XSL_CLONE_NEVER', -1);
-define ('XSL_CLONE_ALWAYS', 1);
-
-/** @link https://php.net/manual/en/xsl.constants.php */
-define ('XSL_SECPREF_NONE', 0);
-/** @link https://php.net/manual/en/xsl.constants.php */
-define ('XSL_SECPREF_READ_FILE', 2);
-/** @link https://php.net/manual/en/xsl.constants.php */
-define ('XSL_SECPREF_WRITE_FILE', 4);
-/** @link https://php.net/manual/en/xsl.constants.php */
-define ('XSL_SECPREF_CREATE_DIRECTORY', 8);
-/** @link https://php.net/manual/en/xsl.constants.php */
-define ('XSL_SECPREF_READ_NETWORK', 16);
-/** @link https://php.net/manual/en/xsl.constants.php */
-define ('XSL_SECPREF_WRITE_NETWORK', 32);
-/** @link https://php.net/manual/en/xsl.constants.php */
-define ('XSL_SECPREF_DEFAULT', 44);
-
-/**
- * libxslt version like 10117. Available as of PHP 5.1.2.
- * @link https://php.net/manual/en/xsl.constants.php
- */
-define ('LIBXSLT_VERSION', 10128);
-
-/**
- * libxslt version like 1.1.17. Available as of PHP 5.1.2.
- * @link https://php.net/manual/en/xsl.constants.php
- */
-define ('LIBXSLT_DOTTED_VERSION', "1.1.28");
-
-/**
- * libexslt version like 813. Available as of PHP 5.1.2.
- * @link https://php.net/manual/en/xsl.constants.php
- */
-define ('LIBEXSLT_VERSION', 817);
-
-/**
- * libexslt version like 1.1.17. Available as of PHP 5.1.2.
- * @link https://php.net/manual/en/xsl.constants.php
- */
-define ('LIBEXSLT_DOTTED_VERSION', "1.1.28");
-
-// End of xsl v.0.1
-?>
--- a/build/stubs/zip.php
+++ /dev/null
@@ -1,1482 +0,0 @@
-
- * Open a ZIP file archive
- *
- * @link https://php.net/manual/en/ziparchive.open.php
- *
- * @param string $filename
- * The file name of the ZIP archive to open.
- *
- * @param int $flags [optional]
- * The mode to use to open the archive.
- *
- *
- * ZipArchive::OVERWRITE
- *
- *
- * @return mixed Error codes
- *
- * Returns TRUE on success or the error code.
- *
- *
- * ZipArchive::ER_EXISTS
- *
- *
- * File already exists.
- *
- *
- * ZipArchive::ER_INCONS
- *
- *
- * Zip archive inconsistent.
- *
- *
- * ZipArchive::ER_INVAL
- *
- *
- * Invalid argument.
- *
- *
- * ZipArchive::ER_MEMORY
- *
- *
- * Malloc failure.
- *
- *
- * ZipArchive::ER_NOENT
- *
- *
- * No such file.
- *
- *
- * ZipArchive::ER_NOZIP
- *
- *
- * Not a zip archive.
- *
- *
- * ZipArchive::ER_OPEN
- *
- *
- * Can't open file.
- *
- *
- * ZipArchive::ER_READ
- *
- *
- * Read error.
- *
- *
- * ZipArchive::ER_SEEK
- *
- *
- * Seek error.
- *
- */
- public function open(
- $filename,
- $flags = null
- ) {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.1.0)
- * Close the active archive (opened or newly created)
- * @link https://php.net/manual/en/ziparchive.close.php
- * @return bool TRUE on success or FALSE on failure.
- */
- public function close() {}
-
- /**
- * (PHP 7 >= 7.2.0, PECL zip >= 1.15.0)
- * Counts the number of files in the archive.
- * @link https://www.php.net/manual/en/ziparchive.count.php
- * @return int
- * @since 7.2
- */
- public function count() {}
-
- /**
- * Returns the status error message, system and/or zip messages
- * @link https://php.net/manual/en/ziparchive.getstatusstring.php
- * @return string|false a string with the status message on success or FALSE on failure.
- * @since 5.2.7
- */
- public function getStatusString() {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.8.0)
- * Add a new directory
- * @link https://php.net/manual/en/ziparchive.addemptydir.php
- * @param string $dirname
- * The directory to add.
- *
- * @param int $flags [optional] Set how to manage name encoding (ZipArchive::FL_ENC_*) and entry replacement (ZipArchive::FL_OVERWRITE)
- * @return bool TRUE on success or FALSE on failure.
- */
- public function addEmptyDir(
- string $dirname,
- int $flags = 0
- ) {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.1.0)
- * Add a file to a ZIP archive using its contents
- * @link https://php.net/manual/en/ziparchive.addfromstring.php
- * @param string $name
- * The name of the entry to create.
- *
- * @param string $content
- * The contents to use to create the entry. It is used in a binary
- * safe mode.
- *
- * @param int $flags [optional] Set how to manage name encoding (ZipArchive::FL_ENC_*) and entry replacement (ZipArchive::FL_OVERWRITE)
- * @return bool TRUE on success or FALSE on failure.
- */
- public function addFromString(
- $name,
- $content,
- $flags = 8192
- ) {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.1.0)
- * Adds a file to a ZIP archive from the given path
- * @link https://php.net/manual/en/ziparchive.addfile.php
- * @param string $filepath
- * The path to the file to add.
- *
- * @param string $entryname [optional]
- * If supplied, this is the local name inside the ZIP archive that will override the filename.
- *
- * @param int $start [optional]
- * This parameter is not used but is required to extend ZipArchive.
- *
- * @param int $length [optional]
- * This parameter is not used but is required to extend ZipArchive.
- *
- * @param int $flags [optional] Set how to manage name encoding (ZipArchive::FL_ENC_*) and entry replacement (ZipArchive::FL_OVERWRITE)
- * @return bool TRUE on success or FALSE on failure.
- */
- public function addFile(
- $filepath,
- $entryname = null,
- $start = 0,
- $length = 0,
- $flags = 8192
- ) {}
-
- /**
- * (PHP 5 >= 5.3.0, PECL zip >= 1.9.0)
- * Add files from a directory by glob pattern
- * @link https://php.net/manual/en/ziparchive.addglob.php
- * @param string $pattern
- * A glob pattern against which files will be matched.
- *
- * @param int $flags [optional]
- * A bit mask of glob() flags.
- *
- * @param array $options [optional]
- * An associative array of options. Available options are:
- *
- *
- * "add_path"
- *
- *
- * Prefix to prepend when translating to the local path of the file within
- * the archive. This is applied after any remove operations defined by the
- * "remove_path" or "remove_all_path"
- * options.
- *
- * @return bool TRUE on success or FALSE on failure.
- */
- public function addGlob(
- $pattern,
- $flags = 0,
- array $options = []
- ) {}
-
- /**
- * (PHP 5 >= 5.3.0, PECL zip >= 1.9.0)
- * Add files from a directory by PCRE pattern
- * @link https://php.net/manual/en/ziparchive.addpattern.php
- * @param string $pattern
- * A PCRE pattern against which files will be matched.
- *
- * @param string $path [optional]
- * The directory that will be scanned. Defaults to the current working directory.
- *
- * @param array $options [optional]
- * An associative array of options accepted by ZipArchive::addGlob.
- *
- * @return bool TRUE on success or FALSE on failure.
- */
- public function addPattern(
- $pattern,
- $path = '.',
- array $options = []
- ) {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.5.0)
- * Renames an entry defined by its index
- * @link https://php.net/manual/en/ziparchive.renameindex.php
- * @param int $index
- * Index of the entry to rename.
- *
- * @param string $new_name
- * New name.
- *
- * @return bool TRUE on success or FALSE on failure.
- */
- public function renameIndex(
- $index,
- $new_name
- ) {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.5.0)
- * Renames an entry defined by its name
- * @link https://php.net/manual/en/ziparchive.renamename.php
- * @param string $name
- * Name of the entry to rename.
- *
- * @param string $new_name
- * New name.
- *
- * @return bool TRUE on success or FALSE on failure.
- */
- public function renameName(
- $name,
- $new_name
- ) {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.4.0)
- * Set the comment of a ZIP archive
- * @link https://php.net/manual/en/ziparchive.setarchivecomment.php
- * @param string $comment
- * The contents of the comment.
- *
- * @return bool TRUE on success or FALSE on failure.
- */
- public function setArchiveComment( $comment) {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.1.0)
- * Returns the Zip archive comment
- * @link https://php.net/manual/en/ziparchive.getarchivecomment.php
- * @param int $flags [optional]
- * If flags is set to ZipArchive::FL_UNCHANGED, the original unchanged
- * comment is returned.
- *
- * @return string|false the Zip archive comment or FALSE on failure.
- */
- public function getArchiveComment( $flags = null) {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.4.0)
- * Set the comment of an entry defined by its index
- * @link https://php.net/manual/en/ziparchive.setcommentindex.php
- * @param int $index
- * Index of the entry.
- *
- * @param string $comment
- * The contents of the comment.
- *
- * @return bool TRUE on success or FALSE on failure.
- */
- public function setCommentIndex(
- $index,
- $comment
- ) {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.4.0)
- * Set the comment of an entry defined by its name
- * @link https://php.net/manual/en/ziparchive.setcommentname.php
- * @param string $name
- * Name of the entry.
- *
- * @param string $comment
- * The contents of the comment.
- *
- * @return bool TRUE on success or FALSE on failure.
- */
- public function setCommentName(
- $name,
- $comment
- ) {}
-
- /**
- * Set the compression method of an entry defined by its index
- * @link https://php.net/manual/en/ziparchive.setcompressionindex.php
- * @param int $index Index of the entry.
- * @param int $method The compression method. Either ZipArchive::CM_DEFAULT, ZipArchive::CM_STORE or ZipArchive::CM_DEFLATE.
- * @param int $compflags [optional] Compression flags. Currently unused.
- * @return bool Returns TRUE on success or FALSE on failure.
- * @since 7.0
- */
- public function setCompressionIndex(int $index, int $method, int $compflags = 0) {}
-
- /**
- * Set the compression method of an entry defined by its name
- * https://secure.php.net/manual/en/ziparchive.setcompressionname.php
- * @param string $name Name of the entry.
- * @param int $method The compression method. Either ZipArchive::CM_DEFAULT, ZipArchive::CM_STORE or ZipArchive::CM_DEFLATE.
- * @param int $compflags [optional] Compression flags. Currently unused.
- * @return bool Returns TRUE on success or FALSE on failure.
- * @since 7.0
- */
- public function setCompressionName(string $name, int $method, int $compflags = 0) {}
-
- /**
- * Set the encryption method of an entry defined by its index
- * @link https://php.net/manual/en/ziparchive.setencryptionindex.php
- * @param int $index Index of the entry.
- * @param int $method The encryption method defined by one of the ZipArchive::EM_ constants.
- * @param string|null $password [optional] Optional password, default used when missing.
- * @return bool Returns TRUE on success or FALSE on failure.
- * @since 7.2
- */
- public function setEncryptionIndex(int $index, int $method, ?string $password = null) {}
-
- /**
- * Set the encryption method of an entry defined by its name
- * @link https://php.net/manual/en/ziparchive.setencryptionname.php
- * @param string $name Name of the entry.
- * @param int $method The encryption method defined by one of the ZipArchive::EM_ constants.
- * @param string|null $password [optional] Optional password, default used when missing.
- * @return bool Returns TRUE on success or FALSE on failure.
- * @since 7.2
- */
- public function setEncryptionName(string $name, int $method, ?string $password = null) {}
-
- /**
- * (PHP 5 >= 5.6.0, PECL zip >= 1.12.0)
- * @param string $password
- * @return bool
- */
- public function setPassword( $password) {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.4.0)
- * Returns the comment of an entry using the entry index
- * @link https://php.net/manual/en/ziparchive.getcommentindex.php
- * @param int $index
- * Index of the entry
- *
- * @param int $flags [optional]
- * If flags is set to ZipArchive::FL_UNCHANGED, the original unchanged
- * comment is returned.
- *
- * @return string|false the comment on success or FALSE on failure.
- */
- public function getCommentIndex(
- $index,
- $flags = null
- ) {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.4.0)
- * Returns the comment of an entry using the entry name
- * @link https://php.net/manual/en/ziparchive.getcommentname.php
- * @param string $name
- * Name of the entry
- *
- * @param int $flags [optional]
- * If flags is set to ZipArchive::FL_UNCHANGED, the original unchanged
- * comment is returned.
- *
- * @return string|false the comment on success or FALSE on failure.
- */
- public function getCommentName(
- $name,
- $flags = null
- ) {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.5.0)
- * delete an entry in the archive using its index
- * @link https://php.net/manual/en/ziparchive.deleteindex.php
- * @param int $index
- * Index of the entry to delete.
- *
- * @return bool TRUE on success or FALSE on failure.
- */
- public function deleteIndex( $index) {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.5.0)
- * delete an entry in the archive using its name
- * @link https://php.net/manual/en/ziparchive.deletename.php
- * @param string $name
- * Name of the entry to delete.
- *
- * @return bool TRUE on success or FALSE on failure.
- */
- public function deleteName( $name) {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.5.0)
- * Get the details of an entry defined by its name.
- * @link https://php.net/manual/en/ziparchive.statname.php
- * @param string $name
- * Name of the entry
- *
- * @param int $flags [optional]
- * The flags argument specifies how the name lookup should be done.
- * Also, ZipArchive::FL_UNCHANGED may be ORed to it to request
- * information about the original file in the archive,
- * ignoring any changes made.
- * ZipArchive::FL_NOCASE
- *
- * @return array|false an array containing the entry details or FALSE on failure.
- */
- public function statName(
- $name,
- $flags = null
- ) {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.1.0)
- * Get the details of an entry defined by its index.
- * @link https://php.net/manual/en/ziparchive.statindex.php
- * @param int $index
- * Index of the entry
- *
- * @param int $flags [optional]
- * ZipArchive::FL_UNCHANGED may be ORed to it to request
- * information about the original file in the archive,
- * ignoring any changes made.
- *
- * @return array|false an array containing the entry details or FALSE on failure.
- */
- public function statIndex(
- $index,
- $flags = null
- ) {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.5.0)
- * Returns the index of the entry in the archive
- * @link https://php.net/manual/en/ziparchive.locatename.php
- * @param string $name
- * The name of the entry to look up
- *
- * @param int $flags [optional]
- * The flags are specified by ORing the following values,
- * or 0 for none of them.
- * ZipArchive::FL_NOCASE
- *
- * @return int|false the index of the entry on success or FALSE on failure.
- */
- public function locateName(
- $name,
- $flags = null
- ) {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.5.0)
- * Returns the name of an entry using its index
- * @link https://php.net/manual/en/ziparchive.getnameindex.php
- * @param int $index
- * Index of the entry.
- *
- * @param int $flags [optional]
- * If flags is set to ZipArchive::FL_UNCHANGED, the original unchanged
- * name is returned.
- *
- * @return string|false the name on success or FALSE on failure.
- */
- public function getNameIndex(
- $index,
- $flags = null
- ) {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.1.0)
- * Revert all global changes done in the archive.
- * @link https://php.net/manual/en/ziparchive.unchangearchive.php
- * @return bool TRUE on success or FALSE on failure.
- */
- public function unchangeArchive() {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.1.0)
- * Undo all changes done in the archive
- * @link https://php.net/manual/en/ziparchive.unchangeall.php
- * @return bool TRUE on success or FALSE on failure.
- */
- public function unchangeAll() {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.1.0)
- * Revert all changes done to an entry at the given index
- * @link https://php.net/manual/en/ziparchive.unchangeindex.php
- * @param int $index
- * Index of the entry.
- *
- * @return bool TRUE on success or FALSE on failure.
- */
- public function unchangeIndex( $index) {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.5.0)
- * Revert all changes done to an entry with the given name.
- * @link https://php.net/manual/en/ziparchive.unchangename.php
- * @param string $name
- * Name of the entry.
- *
- * @return bool TRUE on success or FALSE on failure.
- */
- public function unchangeName( $name) {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.1.0)
- * Extract the archive contents
- * @link https://php.net/manual/en/ziparchive.extractto.php
- * @param string $pathto
- * Location where to extract the files.
- *
- * @param mixed $files [optional]
- * The entries to extract. It accepts either a single entry name or
- * an array of names.
- *
- * @return bool TRUE on success or FALSE on failure.
- */
- public function extractTo(
- $pathto,
- $files = null
- ) {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.1.0)
- * Returns the entry contents using its name
- * @link https://php.net/manual/en/ziparchive.getfromname.php
- * @param string $name
- * Name of the entry
- *
- * @param int $len [optional]
- * The length to be read from the entry. If 0, then the
- * entire entry is read.
- *
- * @param int $flags [optional]
- * The flags to use to open the archive. the following values may
- * be ORed to it.
- * ZipArchive::FL_UNCHANGED
- *
- * @return string|false the contents of the entry on success or FALSE on failure.
- */
- public function getFromName(
- $name,
- $len = 0,
- $flags = null
- ) {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.3.0)
- * Returns the entry contents using its index
- * @link https://php.net/manual/en/ziparchive.getfromindex.php
- * @param int $index
- * Index of the entry
- *
- * @param int $len [optional]
- * The length to be read from the entry. If 0, then the
- * entire entry is read.
- *
- * @param int $flags [optional]
- * The flags to use to open the archive. the following values may
- * be ORed to it.
- *
- *
- * ZipArchive::FL_UNCHANGED
- *
- * @return string|false the contents of the entry on success or FALSE on failure.
- */
- public function getFromIndex(
- $index,
- $len = 0,
- $flags = null
- ) {}
-
- /**
- * (PHP 5 >= 5.2.0, PECL zip >= 1.1.0)
- * Get a file handler to the entry defined by its name (read only).
- * @link https://php.net/manual/en/ziparchive.getstream.php
- * @param string $name
- * The name of the entry to use.
- *
- * @return resource|false a file pointer (resource) on success or FALSE on failure.
- */
- public function getStream( $name) {}
-
- /**
- * Set the external attributes of an entry defined by its name
- * @link https://www.php.net/manual/en/ziparchive.setexternalattributesname.php
- * @param string $name Name of the entry
- * @param int $opsys The operating system code defined by one of the ZipArchive::OPSYS_ constants.
- * @param int $attr The external attributes. Value depends on operating system.
- * @param int $flags [optional] Optional flags. Currently unused.
- * @return bool Returns TRUE on success or FALSE on failure.
- */
- public function setExternalAttributesName(
- $name,
- $opsys,
- $attr,
- $flags = null
- ) {}
-
- /**
- * Retrieve the external attributes of an entry defined by its name
- * @link https://www.php.net/manual/en/ziparchive.getexternalattributesname.php
- * @param string $name Name of the entry
- * @param int &$opsys On success, receive the operating system code defined by one of the ZipArchive::OPSYS_ constants.
- * @param int &$attr On success, receive the external attributes. Value depends on operating system.
- * @param int $flags [optional] If flags is set to ZipArchive::FL_UNCHANGED, the original unchanged attributes are returned.
- * @return bool Returns TRUE on success or FALSE on failure.
- */
- public function getExternalAttributesName(
- $name,
- &$opsys,
- &$attr,
- $flags = null
- ) {}
-
- /**
- * Set the external attributes of an entry defined by its index
- * @link https://www.php.net/manual/en/ziparchive.setexternalattributesindex.php
- * @param int $index Index of the entry.
- * @param int $opsys The operating system code defined by one of the ZipArchive::OPSYS_ constants.
- * @param int $attr The external attributes. Value depends on operating system.
- * @param int $flags [optional] Optional flags. Currently unused.
- * @return bool Returns TRUE on success or FALSE on failure.
- */
- public function setExternalAttributesIndex(
- $index,
- $opsys,
- $attr,
- $flags = null
- ) {}
-
- /**
- * Retrieve the external attributes of an entry defined by its index
- * @link https://www.php.net/manual/en/ziparchive.getexternalattributesindex.php
- * @param int $index Index of the entry.
- * @param int &$opsys On success, receive the operating system code defined by one of the ZipArchive::OPSYS_ constants.
- * @param int &$attr On success, receive the external attributes. Value depends on operating system.
- * @param int $flags [optional] If flags is set to ZipArchive::FL_UNCHANGED, the original unchanged attributes are returned.
- * @return bool Returns TRUE on success or FALSE on failure.
- */
- public function getExternalAttributesIndex(
- $index,
- &$opsys,
- &$attr,
- $flags = null
- ) {}
-
- public static function isEncryptionMethodSupported(
- $method,
- $enc = true
- ) {}
-
- public static function isCompressionMethodSupported(
- $method,
- $enc = true
- ) {}
-
- public function registerCancelCallback( $callback) {}
-
- public function registerProgressCallback(
- $rate,
- $callback
- ) {}
-
- public function setMtimeName(
- $name,
- $timestamp,
- $flags = null
- ) {}
-
- public function setMtimeIndex(
- $index,
- $timestamp,
- $flags = null
- ) {}
-
- public function replaceFile(
- $filepath,
- $index,
- $start = null,
- $length = null,
- $flags = null
- ) {}
-}
-
-/**
- * (PHP 4 >= 4.1.0, PHP 5 >= 5.2.0, PECL zip >= 1.0.0)
- * Open a ZIP file archive
- * @link https://php.net/manual/en/function.zip-open.php
- * @param string $filename
- * The file name of the ZIP archive to open.
- *
- * @return resource|int|false a resource handle for later use with
- * zip_read and zip_close
- * or returns the number of error if filename does not
- * exist or in case of other error.
- * @deprecated 8.0 Use {@link ZipArchive} instead.
- */
-function zip_open(string $filename) {}
-
-/**
- * (PHP 4 >= 4.1.0, PHP 5 >= 5.2.0, PECL zip >= 1.0.0)
- * Close a ZIP file archive
- * @link https://php.net/manual/en/function.zip-close.php
- * @param resource $zip
- * A ZIP file previously opened with zip_open.
- *
- * @return void No value is returned.
- * @deprecated 8.0 Use {@link ZipArchive} instead.
- */
-function zip_close($zip): void {}
-
-/**
- * (PHP 4 >= 4.1.0, PHP 5 >= 5.2.0, PECL zip >= 1.0.0)
- * Read next entry in a ZIP file archive
- * @link https://php.net/manual/en/function.zip-read.php
- * @param resource $zip
- * A ZIP file previously opened with zip_open.
- *
- * @return resource|false a directory entry resource for later use with the
- * zip_entry_... functions, or FALSE if
- * there are no more entries to read, or an error code if an error
- * occurred.
- * @deprecated 8.0 Use {@link ZipArchive} instead.
- */
-function zip_read($zip) {}
-
-/**
- * (PHP 4 >= 4.1.0, PHP 5 >= 5.2.0, PECL zip >= 1.0.0)
- * Open a directory entry for reading
- * @link https://php.net/manual/en/function.zip-entry-open.php
- * @param resource $zip_dp
- * A valid resource handle returned by zip_open.
- *
- * @param resource $zip_entry
- * A directory entry returned by zip_read.
- *
- * @param string $mode [optional]
- * Any of the modes specified in the documentation of
- * fopen.
- *
- *
- * Currently, mode is ignored and is always
- * "rb". This is due to the fact that zip support
- * in PHP is read only access.
- *
- * @return bool TRUE on success or FALSE on failure.
- *
- * Unlike fopen and other similar functions,
- * the return value of zip_entry_open only
- * indicates the result of the operation and is not needed for
- * reading or closing the directory entry.
- *
- */
-function zip_entry_open($zip_dp, $zip_entry, string $mode = 'rb'): bool {}
-
-/**
- * (PHP 4 >= 4.1.0, PHP 5 >= 5.2.0, PECL zip >= 1.0.0)
- * Close a directory entry
- * @link https://php.net/manual/en/function.zip-entry-close.php
- * @param resource $zip_entry
- * A directory entry previously opened zip_entry_open.
- *
- * @return bool TRUE on success or FALSE on failure.
- * @deprecated 8.0 Use {@link ZipArchive} instead.
- */
-function zip_entry_close($zip_entry): bool {}
-
-/**
- * (PHP 4 >= 4.1.0, PHP 5 >= 5.2.0, PECL zip >= 1.0.0)
- * Read from an open directory entry
- * @link https://php.net/manual/en/function.zip-entry-read.php
- * @param resource $zip_entry
- * A directory entry returned by zip_read.
- *
- * @param int $len [optional]
- * The number of bytes to return.
- *
- *
- * This should be the uncompressed length you wish to read.
- *
- * @return string|false the data read, empty string on end of a file, or FALSE on error.
- * @deprecated 8.0 Use {@link ZipArchive} instead.
- */
-function zip_entry_read($zip_entry, int $len = 1024): string|false {}
-
-/**
- * (PHP 4 >= 4.1.0, PHP 5 >= 5.2.0, PECL zip >= 1.0.0)
- * Retrieve the actual file size of a directory entry
- * @link https://php.net/manual/en/function.zip-entry-filesize.php
- * @param resource $zip_entry
- * A directory entry returned by zip_read.
- *
- * @return int|false The size of the directory entry.
- * @deprecated 8.0 Use {@link ZipArchive} instead.
- */
-function zip_entry_filesize($zip_entry): int|false {}
-
-/**
- * (PHP 4 >= 4.1.0, PHP 5 >= 5.2.0, PECL zip >= 1.0.0)
- * Retrieve the name of a directory entry
- * @link https://php.net/manual/en/function.zip-entry-name.php
- * @param resource $zip_entry
- * A directory entry returned by zip_read.
- *
- * @return string|false The name of the directory entry.
- * @deprecated 8.0 Use {@link ZipArchive} instead.
- */
-function zip_entry_name($zip_entry): string|false {}
-
-/**
- * (PHP 4 >= 4.1.0, PHP 5 >= 5.2.0, PECL zip >= 1.0.0)
- * Retrieve the compressed size of a directory entry
- * @link https://php.net/manual/en/function.zip-entry-compressedsize.php
- * @param resource $zip_entry
- * A directory entry returned by zip_read.
- *
- * @return int|false The compressed size.
- * @deprecated 8.0 Use {@link ZipArchive} instead.
- */
-function zip_entry_compressedsize($zip_entry): int|false {}
-
-/**
- * (PHP 4 >= 4.1.0, PHP 5 >= 5.2.0, PECL zip >= 1.0.0)
- * Retrieve the compression method of a directory entry
- * @link https://php.net/manual/en/function.zip-entry-compressionmethod.php
- * @param resource $zip_entry
- * A directory entry returned by zip_read.
- *
- * @return string|false The compression method.
- * @deprecated 8.0 Use {@link ZipArchive} instead.
- */
-function zip_entry_compressionmethod($zip_entry): string|false {}
-
-// End of zip v.1.11.0
--- a/build/translation-checker.php
+++ /dev/null
@@ -1,123 +0,0 @@
- 'appstore-shipped-with-server',
- 'dashboard' => 'dashboard-shipped-with-server',
- 'encryption' => 'files_encryption',
- 'settings' => 'settings-1',
-];
-
-// Next line only looks messed up, but it works. Don't touch it!
-$rtlCharacters = [
- '\x{061C}', // ARABIC LETTER MARK
- '\x{0623}', // ARABIC LETTER ALEF WITH HAMZA ABOVE
- '\x{200E}', // LEFT-TO-RIGHT MARK
- '\x{200F}', // RIGHT-TO-LEFT MARK
- '\x{202A}', // LEFT-TO-RIGHT EMBEDDING
- '\x{202B}', // RIGHT-TO-LEFT EMBEDDING
- '\x{202C}', // POP DIRECTIONAL FORMATTING
- '\x{202D}', // LEFT-TO-RIGHT OVERRIDE
- '\x{202E}', // RIGHT-TO-LEFT OVERRIDE
- '\x{2066}', // LEFT-TO-RIGHT ISOLATE
- '\x{2067}', // RIGHT-TO-LEFT ISOLATE
- '\x{2068}', // FIRST STRONG ISOLATE
- '\x{2069}', // POP DIRECTIONAL ISOLATE
- '\x{206C}', // INHIBIT ARABIC FORM SHAPING
- '\x{206D}', // ACTIVATE ARABIC FORM SHAPING
-];
-
-$rtlLanguages = [
- 'ar', // Arabic
- 'fa', // Persian
- 'he', // Hebrew
- 'ps', // Pashto,
- 'ug', // 'Uyghurche / Uyghur
- 'ur_PK', // Urdu
-];
-
-$valid = 0;
-$errors = [];
-$apps = new \DirectoryIterator(__DIR__ . '/../apps');
-foreach ($apps as $app) {
- if ($app->isDot() || in_array($app->getBasename(), $untranslatedApps, true)) {
- continue;
- }
-
- $resourceName = $txConfigAppMap[$app->getBasename()] ?? $app->getBasename();
- if (!file_exists($app->getPathname() . '/l10n') || !str_contains($txConfig, '[o:nextcloud:p:nextcloud:r:' . $resourceName . ']')) {
- $errors[] = $app->getBasename() . "\n" . ' App is not correctly configured for translation sync via transifex and also not marked as untranslated' . "\n";
- continue;
- }
-
- $directories[] = $app->getPathname() . '/l10n';
-}
-
-foreach ($directories as $dir) {
- if (!file_exists($dir)) {
- continue;
- }
- $directory = new \DirectoryIterator($dir);
-
- foreach ($directory as $file) {
- if ($file->getExtension() !== 'json') {
- continue;
- }
-
- $content = file_get_contents($file->getPathname());
-
- $language = pathinfo($file->getFilename(), PATHINFO_FILENAME);
- if (!in_array($language, $rtlLanguages, true) && preg_match_all('/^(.+[' . implode('', $rtlCharacters) . '].+)$/mu', $content, $matches)) {
- $errors[] = $file->getPathname() . "\n" . ' ' . 'Contains a RTL limited characters in the translations. Offending strings:' . "\n" . implode("\n", $matches[0]) . "\n";
- }
-
- $json = json_decode($content, true);
-
- $translations = json_encode($json['translations']);
- if (strpos($translations, '|') !== false) {
- $errors[] = $file->getPathname() . "\n" . ' ' . 'Contains a | in the translations.' . "\n";
- }
-
- if (json_last_error() !== JSON_ERROR_NONE) {
- $errors[] = $file->getPathname() . "\n" . ' ' . json_last_error_msg() . "\n";
- } else {
- $valid++;
- }
-
- if ($isDebug && $file->getFilename() === 'en_GB.json') {
- $sourceStrings = json_encode(array_keys($json['translations']));
-
- if (strpos($sourceStrings, '\u2019') !== false) {
- $errors[] = $file->getPathname() . "\n"
- . ' ' . 'Contains a unicode single quote "’" in the english source string, please replace with normal single quotes.' . "\n"
- . ' ' . 'Please note that this only updates after a sync to transifex.' . "\n";
- }
- }
- }
-}
-
-if (count($errors) > 0) {
- echo "\033[0;31m" . sprintf('ERROR: There were %d errors:', count($errors)) . "\033[0m\n\n";
- echo implode("\n", $errors);
- exit(1);
-}
-
-echo "\033[0;32m" . 'OK: ' . $valid . ' files parse' . "\033[0m\n";
-exit(0);
--- a/build/triple-dot-checker.php
+++ /dev/null
@@ -1,47 +0,0 @@
-getExtension() === 'map') || $file->isDir()) {
- continue;
- }
- $content = file_get_contents($file->getPathname());
- $matches = preg_grep('/[^A-Za-z][tn]\([\'"][^\'"]*?[\'"],( ?)[\'"][^\'"]*?(\.\.\.)[^\'"]*?[\'"]/sU', explode(PHP_EOL, $content));
-
- foreach ($matches as $ln => $match) {
- if (file_exists($file->getPathname() . '.map')) {
- $errors[] = sprintf('At line %d in file %s (file may be transpiled)', $ln, $file);
- } else {
- $errors[] = sprintf('At line %d in file %s', $ln, $file);
- }
- }
- }
-}
-
-echo PHP_EOL . PHP_EOL;
-if (count($errors) > 0) {
- if (count($errors) > 1) {
- echo sprintf('ERROR: There are %d uses of triple dot instead of ellipsis:', count($errors)) . PHP_EOL;
- } else {
- echo 'ERROR: There is 1 use of triple dot instead of ellipsis:' . PHP_EOL;
- }
- echo implode(PHP_EOL, $errors) . PHP_EOL;
- exit(1);
-}
-
-echo 'OK: all good! No use of triple dot instead of ellipsis found.' . PHP_EOL;
-exit(0);
--- a/build/update-apps.sh
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/bin/bash
-#
-# SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
-# SPDX-License-Identifier: AGPL-3.0-or-later
-#
-# Update Nextcloud apps from latest git stable35
-# For local development environment
-# Use from Nextcloud server folder with `./build/update-apps.sh`
-#
-# It automatically:
-# - goes through all apps which are not shipped via server
-# - shows the app name in bold and uses whitespace for separation
-# - changes to stable35 and pulls quietly
-# - shows the 3 most recent commits for context
-# - removes branches merged into stable35
-# - … could even do the build steps if they are consistent for the apps (like `make`)
-
-set -euo pipefail
-
-for path in apps*/*/.git; do
- (
- path="$(dirname "$path")"
- cd "$path"
- printf "\n\033[1m${PWD##*/}\033[0m\n"
- branch="$(git remote show origin | sed -n '/HEAD branch/s/.*: //p')"
- git checkout "$branch"
- git pull --quiet -p
- git --no-pager log -3 --pretty=format:"%h %Cblue%ar%x09%an %Creset%s"
- printf "\n"
- git branch --merged "$branch" | grep -v "$branch$" | xargs git branch -d || true
- )
-done
--- a/build/update.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
-# SPDX-License-Identifier: AGPL-3.0-or-later
-#
-# Update Nextcloud server and apps from latest git stable35
-# For local development environment
-# Use from Nextcloud server folder with `./build/update.sh`
-
-# Update server
-printf "\n\033[1m${PWD##*/}\033[0m\n"
-git checkout stable35
-git pull --quiet -p
-git --no-pager log -3 --pretty=format:"%h %Cblue%ar%x09%an %Creset%s"
-printf "\n"
-git branch --merged stable35 | grep -v "stable35$" | xargs git branch -d
-git submodule update --init
-
-# Update apps
-source ./build/update-apps.sh
--- a/contribute/HowToApplyALicense.md
+++ /dev/null
@@ -1,87 +0,0 @@
-
-# How to apply a license
-
-Originally Nextcloud was licensed under the GNU AGPLv3 only. From
-June, 16 2016 on we switched to "GNU AGPLv3 or any later version" for
-better long-term maintainability and to make it more secure from a
-legal point of view.
-
-Additionally Nextcloud doesn't require a CLA (Contributor License
-Agreement). The copyright belongs to all the individual
-contributors.
-
-## Apply a license to a new file
-
-If you create a new file please use a SPDX license header.
-The year should then be the creation time and the email address is optional.
-
-#### Frontend source (`.js`, `.ts`, `.css` and etc)
-
-```js
-/**
- * SPDX-FileCopyrightText: [year] [your name] []
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-````
-
-or `.vue` files
-
-```html
-
-```
-
-#### Backend source (`.php`)
-
-```php
-/**
- * SPDX-FileCopyrightText: [year] [your name] []
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-```
-
-## Apply a licence to an existing file
-
-If you modify an existing file, please keep the existing license header as
-it is and just add your copyright notice.
-In order to do so there are two options:
-
-* If a generic header is already present, please just add yourself to the AUTHORS.md file
-* If no generic header is present, you can add yourself with a copyright line as described below
-
-````diff
-/**
- * SPDX-FileCopyrightText: 2022 Alice
- * SPDX-FileCopyrightText: 2023 Bob
-+* SPDX-FileCopyrightText: [year] [your name] []
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-````
-
-An example of a generic license header where adding yourself to the AUTHORS.md
-file is prefered please see the example below
-
-```
-/**
- * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-```
-
-For more information on SPDX headers, please see
-
-* https://reuse.software/
-* https://spdx.dev/
-
-## DCO
-
-Additionally we require a Developer Certificate of Origin (DCO), look
-at [CONTRIBUTING.md][contributing] to learn more how to sign your commits.
-
-[contributing]: ../.github/CONTRIBUTING.md#sign-your-work
--- a/contribute/developer-certificate-of-origin
+++ /dev/null
@@ -1,35 +0,0 @@
-Developer Certificate of Origin
-Version 1.1
-
-Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
-660 York Street, Suite 102,
-San Francisco, CA 94110 USA
-
-Everyone is permitted to copy and distribute verbatim copies of this
-license document, but changing it is not allowed.
-
-Developer's Certificate of Origin 1.1
-
-By making a contribution to this project, I certify that:
-
-(a) The contribution was created in whole or in part by me and I
- have the right to submit it under the open source license
- indicated in the file; or
-
-(b) The contribution is based upon previous work that, to the best
- of my knowledge, is covered under an appropriate open source
- license and I have the right under that license to submit that
- work with modifications, whether created in whole or in part
- by me, under the same open source license (unless I am
- permitted to submit under a different license), as indicated
- in the file; or
-
-(c) The contribution was provided directly to me by some other
- person who certified (a), (b) or (c) and I have not modified
- it.
-
-(d) I understand and agree that this project and the contribution
- are public and that a record of the contribution (including all
- personal information I submit with it, including my sign-off) is
- maintained indefinitely and may be redistributed consistent with
- this project or the open source license(s) involved.
--- a/contribute/developer-certificate-of-origin.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-FileCopyrightText: 2004, 2006 The Linux Foundation and its contributors.
-SPDX-License-Identifier: LicenseRef-DCO
--- a/tests/Core/BackgroundJobs/CheckCodeIntegrityJobTest.php
+++ /dev/null
@@ -1,296 +0,0 @@
- [
- 'EXTRA_FILE' => [
- 'core/extra.php' => ['expected' => '', 'current' => 'abc'],
- ],
- ],
- ];
-
- private Checker&MockObject $checker;
- private IConfig&MockObject $config;
- private IAppConfig&MockObject $appConfig;
- private IGroupManager&MockObject $groupManager;
- private INotificationManager&MockObject $notificationManager;
- private IMailer&MockObject $mailer;
- private IL10N&MockObject $l10n;
- /** @var list recipients collected by expectAdminNotifications() */
- private array $users = [];
- private CheckCodeIntegrityJob $job;
-
- protected function setUp(): void {
- parent::setUp();
-
- $this->checker = $this->createMock(Checker::class);
- $this->config = $this->createMock(IConfig::class);
- $this->appConfig = $this->createMock(IAppConfig::class);
- $this->groupManager = $this->createMock(IGroupManager::class);
- $this->notificationManager = $this->createMock(INotificationManager::class);
- $this->mailer = $this->createMock(IMailer::class);
-
- $this->config->method('getSystemValueBool')
- ->with('integrity.check.scheduled', true)
- ->willReturn(true);
- $this->checker->method('isCodeCheckEnforced')->willReturn(true);
-
- $this->l10n = $this->createMock(IL10N::class);
- $this->l10n->method('t')->willReturnArgument(0);
- $this->l10n->method('n')->willReturnCallback(
- fn (string $singular, string $plural, int $count, array $parameters = []): string
- => vsprintf(str_replace('%n', (string)$count, $count === 1 ? $singular : $plural), $parameters)
- );
-
- $this->mailer->method('createEMailTemplate')->willReturn($this->createMock(IEMailTemplate::class));
- $this->mailer->method('createMessage')->willReturn($this->createMock(IMessage::class));
-
- $this->notificationManager->method('createNotification')->willReturnCallback(
- fn (): INotification => new Notification($this->createMock(IValidator::class), $this->createMock(IRichTextFormatter::class))
- );
-
- $this->job = $this->createJob($this->checker, $this->config);
- }
-
- private function createJob(Checker $checker, IConfig $config): CheckCodeIntegrityJob {
- $time = $this->createMock(ITimeFactory::class);
- $time->method('getDateTime')->willReturn(new \DateTime('2026-09-25 12:00:00'));
- $l10nFactory = $this->createMock(IFactory::class);
- $l10nFactory->method('get')->willReturn($this->l10n);
-
- return new CheckCodeIntegrityJob(
- $time,
- $checker,
- $config,
- $this->appConfig,
- $this->groupManager,
- $this->notificationManager,
- $this->mailer,
- $l10nFactory,
- $this->createMock(IURLGenerator::class),
- $this->createMock(LoggerInterface::class),
- );
- }
-
- private function mockAdmins(): void {
- $withEmail = $this->createMock(IUser::class);
- $withEmail->method('getUID')->willReturn('admin1');
- $withEmail->method('getEMailAddress')->willReturn('admin1@example.com');
- $withoutEmail = $this->createMock(IUser::class);
- $withoutEmail->method('getUID')->willReturn('admin2');
- $withoutEmail->method('getEMailAddress')->willReturn(null);
-
- $group = $this->createMock(IGroup::class);
- $group->method('getUsers')->willReturn([$withEmail, $withoutEmail]);
- $this->groupManager->method('get')->with('admin')->willReturn($group);
- }
-
- private function fingerprint(array $results): string {
- return self::invokePrivate($this->job, 'fingerprint', [$results]);
- }
-
- /**
- * Expects one notification per admin for the given fingerprint and summary
- */
- private function expectAdminNotifications(string $fingerprint, array $summary): void {
- $this->notificationManager->expects($this->exactly(2))->method('notify')
- ->willReturnCallback(function (INotification $notification) use ($fingerprint, $summary): void {
- $this->assertSame('core', $notification->getApp());
- $this->assertSame(CheckCodeIntegrityJob::NOTIFICATION_OBJECT_TYPE, $notification->getObjectType());
- $this->assertSame($fingerprint, $notification->getObjectId());
- $this->assertSame('code_integrity_changed', $notification->getSubject());
- $this->assertSame($summary, $notification->getSubjectParameters());
- $this->users[] = $notification->getUser();
- });
- }
-
- private function expectRemovedNotification(string $fingerprint): void {
- $this->notificationManager->expects($this->once())->method('markProcessed')
- ->with($this->callback(fn (INotification $notification): bool
- => $notification->getApp() === 'core'
- && $notification->getObjectType() === CheckCodeIntegrityJob::NOTIFICATION_OBJECT_TYPE
- && $notification->getObjectId() === $fingerprint));
- }
-
- private function expectStoredFingerprint(string $fingerprint): void {
- $this->appConfig->expects($this->once())->method('setValueString')
- ->with('core', ConfigLexicon::INTEGRITY_CHECK_NOTIFIED_RESULT, $fingerprint);
- }
-
- public function testSkipsWhenDisabledByConfig(): void {
- $config = $this->createMock(IConfig::class);
- $config->method('getSystemValueBool')
- ->with('integrity.check.scheduled', true)
- ->willReturn(false);
-
- $this->checker->expects($this->never())->method('runInstanceVerification');
-
- self::invokePrivate($this->createJob($this->checker, $config), 'run', [null]);
- }
-
- public function testSkipsWhenCodeCheckIsNotEnforced(): void {
- $checker = $this->createMock(Checker::class);
- $checker->method('isCodeCheckEnforced')->willReturn(false);
- $checker->expects($this->never())->method('runInstanceVerification');
-
- self::invokePrivate($this->createJob($checker, $this->config), 'run', [null]);
- }
-
- public function testNotifiesAndMailsAdminsWhenCheckStartsFailing(): void {
- $this->mockAdmins();
- $this->checker->expects($this->once())->method('runInstanceVerification');
- $this->checker->method('getResults')->willReturn(self::FAILED_RESULT);
- $this->appConfig->method('getValueString')
- ->with('core', ConfigLexicon::INTEGRITY_CHECK_NOTIFIED_RESULT)
- ->willReturn('');
-
- $fingerprint = $this->fingerprint(self::FAILED_RESULT);
- $this->notificationManager->expects($this->never())->method('markProcessed');
- $this->expectAdminNotifications($fingerprint, ['files' => 1, 'unverified' => []]);
- $this->mailer->expects($this->once())->method('send');
- $this->expectStoredFingerprint($fingerprint);
-
- self::invokePrivate($this->job, 'run', [null]);
-
- $this->assertSame(['admin1', 'admin2'], $this->users);
- }
-
- public function testDoesNothingWhenResultIsUnchanged(): void {
- $this->checker->method('getResults')->willReturn(self::FAILED_RESULT);
- $this->appConfig->method('getValueString')->willReturn($this->fingerprint(self::FAILED_RESULT));
-
- $this->notificationManager->expects($this->never())->method('markProcessed');
- $this->notificationManager->expects($this->never())->method('notify');
- $this->mailer->expects($this->never())->method('send');
- $this->appConfig->expects($this->never())->method('setValueString');
-
- self::invokePrivate($this->job, 'run', [null]);
- }
-
- public function testReplacesNotificationWhenFailureChanges(): void {
- $this->mockAdmins();
- $changed = self::FAILED_RESULT;
- $changed['core']['EXTRA_FILE']['core/another.php'] = ['expected' => '', 'current' => 'def'];
- $this->checker->method('getResults')->willReturn($changed);
- $previous = $this->fingerprint(self::FAILED_RESULT);
- $this->appConfig->method('getValueString')->willReturn($previous);
-
- $fingerprint = $this->fingerprint($changed);
- $this->expectRemovedNotification($previous);
- $this->expectAdminNotifications($fingerprint, ['files' => 2, 'unverified' => []]);
- $this->mailer->expects($this->once())->method('send');
- $this->expectStoredFingerprint($fingerprint);
-
- self::invokePrivate($this->job, 'run', [null]);
- }
-
- public function testClearsNotificationWhenCheckPassesAgain(): void {
- $this->checker->method('getResults')->willReturn([]);
- $previous = $this->fingerprint(self::FAILED_RESULT);
- $this->appConfig->method('getValueString')->willReturn($previous);
-
- $this->expectRemovedNotification($previous);
- $this->notificationManager->expects($this->never())->method('notify');
- $this->mailer->expects($this->never())->method('send');
- $this->expectStoredFingerprint('');
-
- self::invokePrivate($this->job, 'run', [null]);
- }
-
- public function testMailFailureDoesNotPreventStoringTheResult(): void {
- $this->mockAdmins();
- $this->checker->method('getResults')->willReturn(self::FAILED_RESULT);
- $this->appConfig->method('getValueString')->willReturn('');
- $this->mailer->method('send')->willThrowException(new \Exception('SMTP down'));
-
- $fingerprint = $this->fingerprint(self::FAILED_RESULT);
- $this->expectAdminNotifications($fingerprint, ['files' => 1, 'unverified' => []]);
- $this->expectStoredFingerprint($fingerprint);
-
- self::invokePrivate($this->job, 'run', [null]);
- }
-
- public function testFingerprintIgnoresOrder(): void {
- $result = [
- 'core' => ['EXTRA_FILE' => ['b.php' => ['current' => '2'], 'a.php' => ['current' => '1']]],
- 'myapp' => ['INVALID_HASH' => ['x.php' => ['current' => 'c']]],
- ];
- $reordered = [
- 'myapp' => ['INVALID_HASH' => ['x.php' => ['current' => 'c']]],
- 'core' => ['EXTRA_FILE' => ['a.php' => ['current' => '1'], 'b.php' => ['current' => '2']]],
- ];
-
- $this->assertSame($this->fingerprint($result), $this->fingerprint($reordered));
- }
-
- public function testFingerprintChangesWhenFailingFileChangesAgain(): void {
- $changedAgain = self::FAILED_RESULT;
- $changedAgain['core']['EXTRA_FILE']['core/extra.php']['current'] = 'def';
-
- $this->assertNotSame($this->fingerprint(self::FAILED_RESULT), $this->fingerprint($changedAgain));
- }
-
- public function testSummarizeSeparatesFilesFromUnverifiedScopes(): void {
- $result = [
- 'myapp' => ['EXTRA_FILE' => ['extra1.php' => [], 'extra2.php' => []], 'INVALID_HASH' => ['lib/Changed.php' => []]],
- 'files' => ['EXCEPTION' => ['class' => 'X', 'message' => 'Signature data not found.']],
- 'core' => ['EXCEPTION' => ['class' => 'X', 'message' => 'Certificate is not valid.']],
- ];
-
- $this->assertSame(
- ['files' => 3, 'unverified' => ['core', 'files']],
- self::invokePrivate($this->job, 'summarize', [$result]),
- );
- }
-
- public static function dataFormatSummary(): array {
- return [
- 'files only' => [['files' => 2, 'unverified' => []], ['2 files do not match the signed release. They may have been modified or added without authorization.']],
- 'unverified only' => [['files' => 0, 'unverified' => ['core']], ['The signature of core is missing or invalid, so it could not be verified.']],
- 'both' => [['files' => 1, 'unverified' => ['core', 'files']], [
- '1 file does not match the signed release. It may have been modified or added without authorization.',
- 'The signatures of core, files are missing or invalid, so they could not be verified.',
- ]],
- 'missing parameters' => [[], []],
- ];
- }
-
- #[DataProvider('dataFormatSummary')]
- public function testFormatSummary(array $summary, array $expected): void {
- $this->assertSame($expected, CheckCodeIntegrityJob::formatSummary($this->l10n, $summary));
- }
-}
--- a/tests/Core/Command/Apps/AppsDisableTest.php
+++ /dev/null
@@ -1,73 +0,0 @@
-commandTester = new CommandTester($command);
-
- Server::get(IAppManager::class)->enableApp('admin_audit');
- Server::get(IAppManager::class)->enableApp('comments');
- }
-
- /**
- * @param $appId
- * @param $groups
- * @param $statusCode
- * @param $pattern
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataCommandInput')]
- public function testCommandInput($appId, $statusCode, $pattern): void {
- $input = ['app-id' => $appId];
-
- $this->commandTester->execute($input);
-
- $this->assertMatchesRegularExpression('/' . $pattern . '/', $this->commandTester->getDisplay());
- $this->assertSame($statusCode, $this->commandTester->getStatusCode());
- }
-
- public const VERSION_REGEX = '([\d\.\-dev]*)';
-
- public static function dataCommandInput(): array {
- return [
- [['admin_audit'], 0, 'admin_audit ' . self::VERSION_REGEX . ' disabled'],
- [['comments'], 0, 'comments ' . self::VERSION_REGEX . ' disabled'],
- [['invalid_app'], 0, 'No such app enabled: invalid_app'],
-
- [['admin_audit', 'comments'], 0, 'admin_audit ' . self::VERSION_REGEX . " disabled\ncomments " . self::VERSION_REGEX . ' disabled'],
- [['admin_audit', 'comments', 'invalid_app'], 0, 'admin_audit ' . self::VERSION_REGEX . " disabled\ncomments " . self::VERSION_REGEX . " disabled\nNo such app enabled: invalid_app"],
-
- [['files'], 2, "files can't be disabled"],
- [['provisioning_api'], 2, "provisioning_api can't be disabled"],
-
- [['files', 'admin_audit'], 2, "files can't be disabled.\nadmin_audit " . self::VERSION_REGEX . ' disabled'],
- [['provisioning_api', 'comments'], 2, "provisioning_api can't be disabled.\ncomments " . self::VERSION_REGEX . ' disabled'],
- ];
- }
-}
--- a/tests/Core/Command/Apps/AppsEnableTest.php
+++ /dev/null
@@ -1,90 +0,0 @@
-commandTester = new CommandTester($command);
-
- // Not every setup disables the app store, and enabling an unknown app pulls
- // the whole catalogue (~30 MB, cached for an hour) to find it is not there.
- $this->overwriteSystemConfig('appstoreenabled', false);
-
- Server::get(IAppManager::class)->disableApp('admin_audit');
- Server::get(IAppManager::class)->disableApp('comments');
- }
-
- /**
- * @param $appId
- * @param $groups
- * @param $statusCode
- * @param $pattern
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataCommandInput')]
- public function testCommandInput($appId, $groups, $statusCode, $pattern): void {
- $input = ['app-id' => $appId];
-
- if (is_array($groups)) {
- $input['--groups'] = $groups;
- }
-
- $this->commandTester->execute($input);
-
- $this->assertMatchesRegularExpression('/' . $pattern . '/', $this->commandTester->getDisplay());
- $this->assertSame($statusCode, $this->commandTester->getStatusCode());
- }
-
- public const VERSION_REGEX = '([\d\.\-dev]*)';
-
- public static function dataCommandInput(): array {
- return [
- [['admin_audit'], null, 0, 'admin_audit ' . self::VERSION_REGEX . ' enabled'],
- [['comments'], null, 0, 'comments ' . self::VERSION_REGEX . ' enabled'],
- [['comments', 'comments'], null, 0, 'comments ' . self::VERSION_REGEX . " enabled\ncomments already enabled"],
- [['invalid_app'], null, 1, 'Could not download app invalid_app'],
-
- [['admin_audit', 'comments'], null, 0, 'admin_audit ' . self::VERSION_REGEX . " enabled\ncomments " . self::VERSION_REGEX . ' enabled'],
- [['admin_audit', 'comments', 'invalid_app'], null, 1, 'admin_audit ' . self::VERSION_REGEX . " enabled\ncomments " . self::VERSION_REGEX . " enabled\nCould not download app invalid_app"],
-
- [['admin_audit'], ['admin'], 1, "admin_audit can't be enabled for groups"],
- [['comments'], ['admin'], 1, "comments can't be enabled for groups"],
-
- [['updatenotification'], ['admin'], 0, 'updatenotification ' . self::VERSION_REGEX . ' enabled for groups: admin'],
- [['updatenotification', 'dashboard'], ['admin'], 0, 'updatenotification ' . self::VERSION_REGEX . " enabled for groups: admin\ndashboard " . self::VERSION_REGEX . ' enabled for groups: admin'],
-
- [['updatenotification'], ['admin', 'invalid_group'], 0, 'updatenotification ' . self::VERSION_REGEX . ' enabled for groups: admin'],
- [['updatenotification', 'dashboard'], ['admin', 'invalid_group'], 0, 'updatenotification ' . self::VERSION_REGEX . " enabled for groups: admin\ndashboard " . self::VERSION_REGEX . ' enabled for groups: admin'],
- [['updatenotification', 'dashboard', 'invalid_app'], ['admin', 'invalid_group'], 1, 'updatenotification ' . self::VERSION_REGEX . " enabled for groups: admin\ndashboard " . self::VERSION_REGEX . " enabled for groups: admin\nCould not download app invalid_app"],
- ];
- }
-}
--- a/tests/Core/Command/Config/App/DeleteConfigTest.php
+++ /dev/null
@@ -1,99 +0,0 @@
-appConfig = $this->createMock(IAppConfig::class);
- $this->configManager = $this->createMock(ConfigManager::class);
- $this->consoleInput = $this->createMock(InputInterface::class);
- $this->consoleOutput = $this->createMock(OutputInterface::class);
-
- $this->command = new DeleteConfig($this->appConfig, $this->configManager);
- }
-
- public static function dataDelete(): array {
- return [
- [
- 'name',
- true,
- true,
- 0,
- 'info',
- ],
- [
- 'name',
- true,
- false,
- 0,
- 'info',
- ],
- [
- 'name',
- false,
- false,
- 0,
- 'info',
- ],
- [
- 'name',
- false,
- true,
- 1,
- 'error',
- ],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataDelete')]
- public function testDelete(string $configName, bool $configExists, bool $checkIfExists, int $expectedReturn, string $expectedMessage): void {
- $this->appConfig->expects(($checkIfExists) ? $this->once() : $this->never())
- ->method('getKeys')
- ->with('app-name')
- ->willReturn($configExists ? [$configName] : []);
-
- $this->appConfig->expects(($expectedReturn === 0) ? $this->once() : $this->never())
- ->method('deleteKey')
- ->with('app-name', $configName);
-
- $this->consoleInput->expects($this->exactly(2))
- ->method('getArgument')
- ->willReturnMap([
- ['app', 'app-name'],
- ['name', $configName],
- ]);
- $this->consoleInput->method('hasParameterOption')
- ->with('--error-if-not-exists')
- ->willReturn($checkIfExists);
-
- $this->consoleOutput->method('writeln')
- ->with($this->stringContains($expectedMessage));
-
- $this->assertSame($expectedReturn, self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]));
- }
-}
--- a/tests/Core/Command/Config/App/GetConfigTest.php
+++ /dev/null
@@ -1,137 +0,0 @@
-appConfig = $this->createMock(IAppConfig::class);
- $this->configManager = $this->createMock(ConfigManager::class);
- $this->consoleInput = $this->createMock(InputInterface::class);
- $this->consoleOutput = $this->createMock(OutputInterface::class);
-
- $this->command = new GetConfig($this->appConfig, $this->configManager);
- }
-
- public static function dataGet(): array {
- return [
- // String output as json
- ['name', 'newvalue', true, null, false, 'json', 0, json_encode('newvalue')],
- // String output as plain text
- ['name', 'newvalue', true, null, false, 'plain', 0, 'newvalue'],
- // String falling back to default output as json
- ['name', null, false, 'newvalue', true, 'json', 0, json_encode('newvalue')],
- // String falling back without default: error
- ['name', null, false, null, false, 'json', 1, null],
-
- // Int "0" output as json/plain
- ['name', 0, true, null, false, 'json', 0, json_encode(0)],
- ['name', 0, true, null, false, 'plain', 0, '0'],
- // Int "1" output as json/plain
- ['name', 1, true, null, false, 'json', 0, json_encode(1)],
- ['name', 1, true, null, false, 'plain', 0, '1'],
-
- // Bool "true" output as json/plain
- ['name', true, true, null, false, 'json', 0, json_encode(true)],
- ['name', true, true, null, false, 'plain', 0, 'true'],
- // Bool "false" output as json/plain
- ['name', false, true, null, false, 'json', 0, json_encode(false)],
- ['name', false, true, null, false, 'plain', 0, 'false'],
-
- // Null output as json/plain
- ['name', null, true, null, false, 'json', 0, json_encode(null)],
- ['name', null, true, null, false, 'plain', 0, 'null'],
-
- // Array output as json/plain
- ['name', ['a', 'b'], true, null, false, 'json', 0, json_encode(['a', 'b'])],
- ['name', ['a', 'b'], true, null, false, 'plain', 0, "a\nb"],
- // Key array output as json/plain
- ['name', [0 => 'a', 1 => 'b'], true, null, false, 'json', 0, json_encode(['a', 'b'])],
- ['name', [0 => 'a', 1 => 'b'], true, null, false, 'plain', 0, "a\nb"],
- // Associative array output as json/plain
- ['name', ['a' => 1, 'b' => 2], true, null, false, 'json', 0, json_encode(['a' => 1, 'b' => 2])],
- ['name', ['a' => 1, 'b' => 2], true, null, false, 'plain', 0, "a: 1\nb: 2"],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGet')]
- public function testGet(string $configName, mixed $value, bool $configExists, mixed $defaultValue, bool $hasDefault, string $outputFormat, int $expectedReturn, ?string $expectedMessage): void {
- if (!$expectedReturn) {
- if ($configExists) {
- $this->appConfig->expects($this->once())
- ->method('getDetails')
- ->with('app-name', $configName)
- ->willReturn(['value' => $value]);
- }
- }
-
- if (!$configExists) {
- $this->appConfig->expects($this->once())
- ->method('getDetails')
- ->with('app-name', $configName)
- ->willThrowException(new AppConfigUnknownKeyException());
- }
-
- $this->consoleInput->expects($this->exactly(2))
- ->method('getArgument')
- ->willReturnMap([
- ['app', 'app-name'],
- ['name', $configName],
- ]);
- $this->consoleInput->method('getOption')
- ->willReturnMap([
- ['default-value', $defaultValue],
- ['output', $outputFormat],
- ]);
- $this->consoleInput->method('hasParameterOption')
- ->willReturnMap([
- ['--output', false, true],
- ['--default-value', false, $hasDefault],
- ]);
-
- if ($expectedMessage !== null) {
- global $output;
-
- $output = '';
- $this->consoleOutput->method('writeln')
- ->willReturnCallback(function ($value) {
- global $output;
- $output .= $value . "\n";
- return $output;
- });
- }
-
- $this->assertSame($expectedReturn, self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]));
-
- if ($expectedMessage !== null) {
- global $output;
- // Remove the trailing newline
- $this->assertSame($expectedMessage, substr($output, 0, -1));
- }
- }
-}
--- a/tests/Core/Command/Config/App/SetConfigTest.php
+++ /dev/null
@@ -1,107 +0,0 @@
-appConfig = $this->createMock(AppConfig::class);
- $this->configManager = $this->createMock(ConfigManager::class);
- $this->consoleInput = $this->createMock(InputInterface::class);
- $this->consoleOutput = $this->createMock(OutputInterface::class);
-
- $this->command = new SetConfig($this->appConfig, $this->configManager);
- }
-
- public static function dataSet(): array {
- return [
- [
- 'name',
- 'newvalue',
- true,
- true,
- true,
- 'info',
- ],
- [
- 'name',
- 'newvalue',
- false,
- true,
- false,
- 'comment',
- ],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSet')]
- public function testSet(string $configName, mixed $newValue, bool $configExists, bool $updateOnly, bool $updated, string $expectedMessage): void {
- $this->appConfig->method('hasKey')
- ->with('app-name', $configName)
- ->willReturn($configExists);
-
- if (!$configExists) {
- $this->appConfig->method('getValueType')
- ->willThrowException(new AppConfigUnknownKeyException());
- } else {
- $this->appConfig->method('getValueType')
- ->willReturn(IAppConfig::VALUE_MIXED);
- }
-
- if ($updated) {
- $this->appConfig->expects($this->once())
- ->method('setValueMixed')
- ->with('app-name', $configName, $newValue);
- }
-
- $this->consoleInput->expects($this->exactly(2))
- ->method('getArgument')
- ->willReturnMap([
- ['app', 'app-name'],
- ['name', $configName],
- ]);
- $this->consoleInput->method('getOption')
- ->willReturnMap([
- ['value', $newValue],
- ['lazy', null],
- ['sensitive', null],
- ['no-interaction', true],
- ]);
- $this->consoleInput->method('hasParameterOption')
- ->willReturnMap([
- ['--type', false, false],
- ['--value', false, true],
- ['--update-only', false, $updateOnly]
- ]);
- $this->consoleOutput->method('writeln')
- ->with($this->stringContains($expectedMessage));
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-}
--- a/tests/Core/Command/Config/ImportTest.php
+++ /dev/null
@@ -1,170 +0,0 @@
-config = $this->getMockBuilder(IConfig::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->consoleInput = $this->getMockBuilder(InputInterface::class)->getMock();
- $this->consoleOutput = $this->getMockBuilder(OutputInterface::class)->getMock();
-
- /** @var IConfig $config */
- $this->command = new Import($config);
- }
-
- public static function validateAppsArrayData(): array {
- return [
- [0],
- [1],
- [null],
- ['new \Exception()'],
- [json_encode([])],
- ];
- }
-
- /**
- * @param mixed $configValue
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('validateAppsArrayData')]
- public function testValidateAppsArray($configValue): void {
- $this->invokePrivate($this->command, 'validateAppsArray', [['app' => ['name' => $configValue]]]);
- $this->assertTrue(true, 'Asserting that no exception is thrown');
- }
-
- public static function validateAppsArrayThrowsData(): array {
- return [
- [false],
- [true],
- [[]],
- [new \Exception()],
- ];
- }
-
- /**
- * @param mixed $configValue
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('validateAppsArrayThrowsData')]
- public function testValidateAppsArrayThrows($configValue): void {
- try {
- $this->invokePrivate($this->command, 'validateAppsArray', [['app' => ['name' => $configValue]]]);
- $this->fail('Did not throw expected UnexpectedValueException');
- } catch (\UnexpectedValueException $e) {
- $this->assertStringStartsWith('Invalid app config value for "app":"name".', $e->getMessage());
- }
- }
-
- public static function checkTypeRecursivelyData(): array {
- return [
- [0],
- [1],
- [null],
- ['new \Exception()'],
- [json_encode([])],
- [false],
- [true],
- [[]],
- [['string']],
- [['test' => 'string']],
- [['test' => ['sub' => 'string']]],
- ];
- }
-
- /**
- * @param mixed $configValue
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('checkTypeRecursivelyData')]
- public function testCheckTypeRecursively($configValue): void {
- $this->invokePrivate($this->command, 'checkTypeRecursively', [$configValue, 'name']);
- $this->assertTrue(true, 'Asserting that no exception is thrown');
- }
-
- public static function checkTypeRecursivelyThrowsData(): array {
- return [
- [new \Exception()],
- [[new \Exception()]],
- [['test' => new \Exception()]],
- [['test' => ['sub' => new \Exception()]]],
- ];
- }
-
- /**
- * @param mixed $configValue
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('checkTypeRecursivelyThrowsData')]
- public function testCheckTypeRecursivelyThrows($configValue): void {
- try {
- $this->invokePrivate($this->command, 'checkTypeRecursively', [$configValue, 'name']);
- $this->fail('Did not throw expected UnexpectedValueException');
- } catch (\UnexpectedValueException $e) {
- $this->assertStringStartsWith('Invalid system config value for "name"', $e->getMessage());
- }
- }
-
- public static function validateArrayData(): array {
- return [
- [['system' => []]],
- [['apps' => []]],
- [['system' => [], 'apps' => []]],
- ];
- }
-
- /**
- * @param array $configArray
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('validateArrayData')]
- public function testValidateArray($configArray): void {
- $this->invokePrivate($this->command, 'validateArray', [$configArray]);
- $this->assertTrue(true, 'Asserting that no exception is thrown');
- }
-
- public static function validateArrayThrowsData(): array {
- return [
- [[], 'At least one key of the following is expected:'],
- [[0 => []], 'Found invalid entries in root'],
- [['string' => []], 'Found invalid entries in root'],
- ];
- }
-
- /**
- *
- * @param mixed $configArray
- * @param string $expectedException
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('validateArrayThrowsData')]
- public function testValidateArrayThrows($configArray, $expectedException): void {
- try {
- $this->invokePrivate($this->command, 'validateArray', [$configArray]);
- $this->fail('Did not throw expected UnexpectedValueException');
- } catch (\UnexpectedValueException $e) {
- $this->assertStringStartsWith($expectedException, $e->getMessage());
- }
- }
-}
--- a/tests/Core/Command/Config/ListConfigsTest.php
+++ /dev/null
@@ -1,328 +0,0 @@
-systemConfig = $this->getMockBuilder(SystemConfig::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->appConfig = $this->getMockBuilder(IAppConfig::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->configManager = $this->getMockBuilder(ConfigManager::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->appManager = $this->createMock(IAppManager::class);
-
- $this->consoleInput = $this->createMock(InputInterface::class);
- $this->consoleOutput = $this->createMock(OutputInterface::class);
-
- $this->command = new ListConfigs(
- $this->systemConfig,
- $this->appConfig,
- $this->configManager,
- $this->appManager,
- );
- }
-
- public static function listData(): array {
- return [
- [
- 'all',
- // config.php
- [
- 'secret',
- 'overwrite.cli.url',
- ],
- [
- ['secret', 'N;', IConfig::SENSITIVE_VALUE],
- ['overwrite.cli.url', 'N;', 'http://localhost'],
- ],
- // app config
- [
- ['files', [
- 'enabled' => 'yes',
- ]],
- ['core', [
- 'global_cache_gc_lastrun' => '1430388388',
- ]],
- ],
- false,
- json_encode([
- 'system' => [
- 'secret' => IConfig::SENSITIVE_VALUE,
- 'overwrite.cli.url' => 'http://localhost',
- ],
- 'apps' => [
- 'core' => [
- 'global_cache_gc_lastrun' => '1430388388',
- ],
- 'files' => [
- 'enabled' => 'yes',
- ],
- ],
- ]),
- ],
- [
- 'all',
- // config.php
- [
- 'secret',
- 'overwrite.cli.url',
- ],
- [
- ['secret', 'N;', 'my secret'],
- ['overwrite.cli.url', 'N;', 'http://localhost'],
- ],
- // app config
- [
- ['files', [
- 'enabled' => 'yes',
- ]],
- ['core', [
- 'global_cache_gc_lastrun' => '1430388388',
- ]],
- ],
- true,
- json_encode([
- 'system' => [
- 'secret' => 'my secret',
- 'overwrite.cli.url' => 'http://localhost',
- ],
- 'apps' => [
- 'core' => [
- 'global_cache_gc_lastrun' => '1430388388',
- ],
- 'files' => [
- 'enabled' => 'yes',
- ],
- ],
- ]),
- ],
- [
- 'system',
- // config.php
- [
- 'secret',
- 'objectstore',
- 'overwrite.cli.url',
- ],
- [
- ['secret', 'N;', IConfig::SENSITIVE_VALUE],
- ['objectstore', 'N;', [
- 'class' => 'OC\\Files\\ObjectStore\\Swift',
- 'arguments' => [
- 'username' => 'facebook100000123456789',
- 'password' => IConfig::SENSITIVE_VALUE,
- ],
- ]],
- ['overwrite.cli.url', 'N;', 'http://localhost'],
- ],
- // app config
- [
- ['files', [
- 'enabled' => 'yes',
- ]],
- ['core', [
- 'global_cache_gc_lastrun' => '1430388388',
- ]],
- ],
- false,
- json_encode([
- 'system' => [
- 'secret' => IConfig::SENSITIVE_VALUE,
- 'objectstore' => [
- 'class' => 'OC\\Files\\ObjectStore\\Swift',
- 'arguments' => [
- 'username' => 'facebook100000123456789',
- 'password' => IConfig::SENSITIVE_VALUE,
- ],
- ],
- 'overwrite.cli.url' => 'http://localhost',
- ],
- ]),
- ],
- [
- 'system',
- // config.php
- [
- 'secret',
- 'overwrite.cli.url',
- ],
- [
- ['secret', 'N;', 'my secret'],
- ['overwrite.cli.url', 'N;', 'http://localhost'],
- ],
- // app config
- [
- ['files', [
- 'enabled' => 'yes',
- ]],
- ['core', [
- 'global_cache_gc_lastrun' => '1430388388',
- ]],
- ],
- true,
- json_encode([
- 'system' => [
- 'secret' => 'my secret',
- 'overwrite.cli.url' => 'http://localhost',
- ],
- ]),
- ],
- [
- 'files',
- // config.php
- [
- 'secret',
- 'overwrite.cli.url',
- ],
- [
- ['secret', 'N;', 'my secret'],
- ['overwrite.cli.url', 'N;', 'http://localhost'],
- ],
- // app config
- [
- ['files', [
- 'enabled' => 'yes',
- ]],
- ['core', [
- 'global_cache_gc_lastrun' => '1430388388',
- ]],
- ],
- false,
- json_encode([
- 'apps' => [
- 'files' => [
- 'enabled' => 'yes',
- ],
- ],
- ]),
- ],
- [
- 'files',
- // config.php
- [
- 'secret',
- 'overwrite.cli.url',
- ],
- [
- ['secret', 'N;', 'my secret'],
- ['overwrite.cli.url', 'N;', 'http://localhost'],
- ],
- // app config
- [
- ['files', [
- 'enabled' => 'yes',
- ]],
- ['core', [
- 'global_cache_gc_lastrun' => '1430388388',
- ]],
- ],
- true,
- json_encode([
- 'apps' => [
- 'files' => [
- 'enabled' => 'yes',
- ],
- ],
- ]),
- ],
- ];
- }
-
- /**
- *
- * @param string $app
- * @param array $systemConfigs
- * @param array $systemConfigMap
- * @param array $appConfig
- * @param bool $private
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('listData')]
- public function testList($app, $systemConfigs, $systemConfigMap, $appConfig, $private, $expected): void {
- $this->systemConfig->expects($this->any())
- ->method('getKeys')
- ->willReturn($systemConfigs);
- if ($private) {
- $this->systemConfig->expects($this->any())
- ->method('getValue')
- ->willReturnMap($systemConfigMap);
- $this->appConfig->expects($this->any())
- ->method('getAllValues')
- ->willReturnMap($appConfig);
- } else {
- $this->systemConfig->expects($this->any())
- ->method('getFilteredValue')
- ->willReturnMap($systemConfigMap);
- $this->appConfig->expects($this->any())
- ->method('getFilteredValues')
- ->willReturnMap($appConfig);
- }
-
- $this->appConfig->expects($this->any())
- ->method('getApps')
- ->willReturn(['core', 'files']);
- $this->appConfig->expects($this->any())
- ->method('getAllValues')
- ->willReturnMap($appConfig);
-
- $this->consoleInput->expects($this->once())
- ->method('getArgument')
- ->with('app')
- ->willReturn($app);
-
- $this->consoleInput->expects($this->any())
- ->method('getOption')
- ->willReturnMap([
- ['output', 'json'],
- ['private', $private],
- ]);
-
- global $output;
-
- $output = '';
- $this->consoleOutput->expects($this->any())
- ->method('writeln')
- ->willReturnCallback(function ($value) use (&$output) {
- $output .= $value . "\n";
- return $output;
- });
-
- $this->invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
-
- $this->assertEquals($expected, trim($output, "\n"));
- }
-}
--- a/tests/Core/Command/Config/System/CastHelperTest.php
+++ /dev/null
@@ -1,67 +0,0 @@
-castHelper = new CastHelper();
- }
-
- public static function castValueProvider(): array {
- return [
- [null, 'string', ['value' => '', 'readable-value' => 'empty string']],
-
- ['abc', 'string', ['value' => 'abc', 'readable-value' => 'string abc']],
-
- ['123', 'integer', ['value' => 123, 'readable-value' => 'integer 123']],
- ['456', 'int', ['value' => 456, 'readable-value' => 'integer 456']],
-
- ['2.25', 'double', ['value' => 2.25, 'readable-value' => 'double 2.25']],
- ['0.5', 'float', ['value' => 0.5, 'readable-value' => 'double 0.5']],
-
- ['', 'null', ['value' => null, 'readable-value' => 'null']],
-
- ['true', 'boolean', ['value' => true, 'readable-value' => 'boolean true']],
- ['false', 'bool', ['value' => false, 'readable-value' => 'boolean false']],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('castValueProvider')]
- public function testCastValue($value, $type, $expectedValue): void {
- $this->assertSame(
- $expectedValue,
- $this->castHelper->castValue($value, $type)
- );
- }
-
- public static function castValueInvalidProvider(): array {
- return [
- ['123', 'foobar'],
-
- [null, 'integer'],
- ['abc', 'integer'],
- ['76ggg', 'double'],
- ['true', 'float'],
- ['foobar', 'boolean'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('castValueInvalidProvider')]
- public function testCastValueInvalid($value, $type): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $this->castHelper->castValue($value, $type);
- }
-}
--- a/tests/Core/Command/Config/System/DeleteConfigTest.php
+++ /dev/null
@@ -1,208 +0,0 @@
-systemConfig = $this->getMockBuilder(SystemConfig::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->consoleInput = $this->getMockBuilder(InputInterface::class)->getMock();
- $this->consoleOutput = $this->getMockBuilder(OutputInterface::class)->getMock();
-
- /** @var SystemConfig $systemConfig */
- $this->command = new DeleteConfig($systemConfig);
- }
-
- public static function deleteData(): array {
- return [
- [
- 'name1',
- true,
- true,
- 0,
- 'info',
- ],
- [
- 'name2',
- true,
- false,
- 0,
- 'info',
- ],
- [
- 'name3',
- false,
- false,
- 0,
- 'info',
- ],
- [
- 'name4',
- false,
- true,
- 1,
- 'error',
- ],
- ];
- }
-
- /**
- *
- * @param string $configName
- * @param bool $configExists
- * @param bool $checkIfExists
- * @param int $expectedReturn
- * @param string $expectedMessage
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('deleteData')]
- public function testDelete($configName, $configExists, $checkIfExists, $expectedReturn, $expectedMessage): void {
- $this->systemConfig->expects(($checkIfExists) ? $this->once() : $this->never())
- ->method('getKeys')
- ->willReturn($configExists ? [$configName] : []);
-
- $this->systemConfig->expects(($expectedReturn === 0) ? $this->once() : $this->never())
- ->method('deleteValue')
- ->with($configName);
-
- $this->consoleInput->expects($this->once())
- ->method('getArgument')
- ->with('name')
- ->willReturn([$configName]);
- $this->consoleInput->expects($this->any())
- ->method('hasParameterOption')
- ->with('--error-if-not-exists')
- ->willReturn($checkIfExists);
-
- $this->consoleOutput->expects($this->any())
- ->method('writeln')
- ->with($this->stringContains($expectedMessage));
-
- $this->assertSame($expectedReturn, $this->invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]));
- }
-
- public static function deleteArrayData(): array {
- return [
- [
- ['name', 'sub'],
- true,
- false,
- true,
- true,
- 0,
- 'info',
- ],
- [
- ['name', 'sub', '2sub'],
- true,
- false,
- ['sub' => ['2sub' => 1], 'sub2' => false],
- ['sub' => [], 'sub2' => false],
- 0,
- 'info',
- ],
- [
- ['name', 'sub3'],
- true,
- false,
- ['sub' => ['2sub' => 1], 'sub2' => false],
- ['sub' => ['2sub' => 1], 'sub2' => false],
- 0,
- 'info',
- ],
- [
- ['name', 'sub'],
- false,
- true,
- true,
- true,
- 1,
- 'error',
- ],
- [
- ['name', 'sub'],
- true,
- true,
- true,
- true,
- 1,
- 'error',
- ],
- [
- ['name', 'sub3'],
- true,
- true,
- ['sub' => ['2sub' => 1], 'sub2' => false],
- ['sub' => ['2sub' => 1], 'sub2' => false],
- 1,
- 'error',
- ],
- ];
- }
-
- /**
- *
- * @param string[] $configNames
- * @param bool $configKeyExists
- * @param bool $checkIfKeyExists
- * @param mixed $configValue
- * @param mixed $updateValue
- * @param int $expectedReturn
- * @param string $expectedMessage
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('deleteArrayData')]
- public function testArrayDelete(array $configNames, $configKeyExists, $checkIfKeyExists, $configValue, $updateValue, $expectedReturn, $expectedMessage): void {
- $this->systemConfig->expects(($checkIfKeyExists) ? $this->once() : $this->never())
- ->method('getKeys')
- ->willReturn($configKeyExists ? [$configNames[0]] : []);
-
- $this->systemConfig->expects(($configKeyExists) ? $this->once() : $this->never())
- ->method('getValue')
- ->willReturn($configValue);
-
- $this->systemConfig->expects(($expectedReturn === 0) ? $this->once() : $this->never())
- ->method('setValue')
- ->with($configNames[0], $updateValue);
-
- $this->consoleInput->expects($this->once())
- ->method('getArgument')
- ->with('name')
- ->willReturn($configNames);
- $this->consoleInput->expects($this->any())
- ->method('hasParameterOption')
- ->with('--error-if-not-exists')
- ->willReturn($checkIfKeyExists);
-
- $this->consoleOutput->expects($this->any())
- ->method('writeln')
- ->with($this->stringContains($expectedMessage));
-
- $this->assertSame($expectedReturn, $this->invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]));
- }
-}
--- a/tests/Core/Command/Config/System/GetConfigTest.php
+++ /dev/null
@@ -1,160 +0,0 @@
-systemConfig = $this->getMockBuilder(SystemConfig::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->consoleInput = $this->getMockBuilder(InputInterface::class)->getMock();
- $this->consoleOutput = $this->getMockBuilder(OutputInterface::class)->getMock();
-
- /** @var SystemConfig $systemConfig */
- $this->command = new GetConfig($systemConfig);
- }
-
- public static function getData(): array {
- return [
- // String output as json
- ['name', 'newvalue', true, null, false, 'json', 0, json_encode('newvalue')],
- // String output as plain text
- ['name', 'newvalue', true, null, false, 'plain', 0, 'newvalue'],
- // String falling back to default output as json
- ['name', null, false, 'newvalue', true, 'json', 0, json_encode('newvalue')],
- // String falling back without default: error
- ['name', null, false, null, false, 'json', 1, null],
-
- // Int "0" output as json/plain
- ['name', 0, true, null, false, 'json', 0, json_encode(0)],
- ['name', 0, true, null, false, 'plain', 0, '0'],
- // Int "1" output as json/plain
- ['name', 1, true, null, false, 'json', 0, json_encode(1)],
- ['name', 1, true, null, false, 'plain', 0, '1'],
-
- // Bool "true" output as json/plain
- ['name', true, true, null, false, 'json', 0, json_encode(true)],
- ['name', true, true, null, false, 'plain', 0, 'true'],
- // Bool "false" output as json/plain
- ['name', false, true, null, false, 'json', 0, json_encode(false)],
- ['name', false, true, null, false, 'plain', 0, 'false'],
-
- // Null output as json/plain
- ['name', null, true, null, false, 'json', 0, json_encode(null)],
- ['name', null, true, null, false, 'plain', 0, 'null'],
-
- // Array output as json/plain
- ['name', ['a', 'b'], true, null, false, 'json', 0, json_encode(['a', 'b'])],
- ['name', ['a', 'b'], true, null, false, 'plain', 0, "a\nb"],
- // Key array output as json/plain
- ['name', [0 => 'a', 1 => 'b'], true, null, false, 'json', 0, json_encode(['a', 'b'])],
- ['name', [0 => 'a', 1 => 'b'], true, null, false, 'plain', 0, "a\nb"],
- // Associative array output as json/plain
- ['name', ['a' => 1, 'b' => 2], true, null, false, 'json', 0, json_encode(['a' => 1, 'b' => 2])],
- ['name', ['a' => 1, 'b' => 2], true, null, false, 'plain', 0, "a: 1\nb: 2"],
-
- // Nested depth
- [['name', 'a'], ['a' => 1, 'b' => 2], true, null, false, 'json', 0, json_encode(1)],
- [['name', 'a'], ['a' => 1, 'b' => 2], true, null, false, 'plain', 0, '1'],
- [['name', 'c'], ['a' => 1, 'b' => 2], true, true, true, 'json', 0, json_encode(true)],
- [['name', 'c'], ['a' => 1, 'b' => 2], true, true, false, 'json', 1, null],
- ];
- }
-
- /**
- *
- * @param string[] $configNames
- * @param mixed $value
- * @param bool $configExists
- * @param mixed $defaultValue
- * @param bool $hasDefault
- * @param string $outputFormat
- * @param int $expectedReturn
- * @param string $expectedMessage
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('getData')]
- public function testGet($configNames, $value, $configExists, $defaultValue, $hasDefault, $outputFormat, $expectedReturn, $expectedMessage): void {
- if (is_array($configNames)) {
- $configName = $configNames[0];
- } else {
- $configName = $configNames;
- $configNames = [$configName];
- }
- $this->systemConfig->expects($this->atLeastOnce())
- ->method('getKeys')
- ->willReturn($configExists ? [$configName] : []);
-
- if (!$expectedReturn) {
- if ($configExists) {
- $this->systemConfig->expects($this->once())
- ->method('getValue')
- ->with($configName)
- ->willReturn($value);
- }
- }
-
- $this->consoleInput->expects($this->once())
- ->method('getArgument')
- ->with('name')
- ->willReturn($configNames);
- $this->consoleInput->expects($this->any())
- ->method('getOption')
- ->willReturnMap([
- ['default-value', $defaultValue],
- ['output', $outputFormat],
- ]);
- $this->consoleInput->expects($this->any())
- ->method('hasParameterOption')
- ->willReturnMap([
- ['--output', false, true],
- ['--default-value', false,$hasDefault],
- ]);
-
- if ($expectedMessage !== null) {
- global $output;
-
- $output = '';
- $this->consoleOutput->expects($this->any())
- ->method('writeln')
- ->willReturnCallback(function ($value) {
- global $output;
- $output .= $value . "\n";
- return $output;
- });
- }
-
- $this->assertSame($expectedReturn, $this->invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]));
-
- if ($expectedMessage !== null) {
- global $output;
- // Remove the trailing newline
- $this->assertSame($expectedMessage, substr($output, 0, -1));
- }
- }
-}
--- a/tests/Core/Command/Config/System/SetConfigTest.php
+++ /dev/null
@@ -1,115 +0,0 @@
-systemConfig = $this->getMockBuilder(SystemConfig::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->consoleInput = $this->getMockBuilder(InputInterface::class)->getMock();
- $this->consoleOutput = $this->getMockBuilder(OutputInterface::class)->getMock();
-
- /** @var SystemConfig $systemConfig */
- $this->command = new SetConfig($systemConfig, new CastHelper());
- }
-
- public static function dataTest() {
- return [
- [['name'], 'newvalue', null, 'newvalue'],
- [['a', 'b', 'c'], 'foobar', null, ['b' => ['c' => 'foobar']]],
- [['a', 'b', 'c'], 'foobar', ['b' => ['d' => 'barfoo']], ['b' => ['d' => 'barfoo', 'c' => 'foobar']]],
- ];
- }
-
- /**
- *
- * @param array $configNames
- * @param string $newValue
- * @param mixed $existingData
- * @param mixed $expectedValue
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTest')]
- public function testSet($configNames, $newValue, $existingData, $expectedValue): void {
- $this->systemConfig->expects($this->once())
- ->method('setValue')
- ->with($configNames[0], $expectedValue);
- $this->systemConfig->method('getValue')
- ->with($configNames[0])
- ->willReturn($existingData);
-
- $this->consoleInput->expects($this->once())
- ->method('getArgument')
- ->with('name')
- ->willReturn($configNames);
- $this->consoleInput->method('getOption')
- ->willReturnMap([
- ['value', $newValue],
- ['type', 'string'],
- ]);
-
- $this->invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-
- public static function setUpdateOnlyProvider(): array {
- return [
- [['name'], null],
- [['a', 'b', 'c'], null],
- [['a', 'b', 'c'], ['b' => 'foobar']],
- [['a', 'b', 'c'], ['b' => ['d' => 'foobar']]],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('setUpdateOnlyProvider')]
- public function testSetUpdateOnly($configNames, $existingData): void {
- $this->expectException(\UnexpectedValueException::class);
-
- $this->systemConfig->expects($this->never())
- ->method('setValue');
- $this->systemConfig->method('getValue')
- ->with($configNames[0])
- ->willReturn($existingData);
- $this->systemConfig->method('getKeys')
- ->willReturn($existingData ? $configNames[0] : []);
-
- $this->consoleInput->expects($this->once())
- ->method('getArgument')
- ->with('name')
- ->willReturn($configNames);
- $this->consoleInput->method('getOption')
- ->willReturnMap([
- ['value', 'foobar'],
- ['type', 'string'],
- ['update-only', true],
- ]);
-
- $this->invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-}
--- a/tests/Core/Command/Db/DbIndexUsageTest.php
+++ /dev/null
@@ -1,169 +0,0 @@
-connection = $this->createMock(Connection::class);
- $this->input = $this->createMock(InputInterface::class);
- $this->command = new DbIndexUsage($this->connection);
- }
-
- private function mockMySQLRows(): array {
- return [
- ['table' => 'oc_filecache', 'index' => 'idx_fc_name', 'reads' => 0, 'writes' => 150],
- ['table' => 'oc_share', 'index' => 'idx_sh_par', 'reads' => 0, 'writes' => 42],
- ];
- }
-
- private function mockPostgreSQLRows(): array {
- return [
- ['table' => 'oc_filecache', 'index' => 'idx_fc_name', 'reads' => 0, 'tuples_read' => 0, 'tuples_fetched' => 0],
- ];
- }
-
- private function mockResult(array $rows): Result&MockObject {
- $result = $this->createMock(Result::class);
- $result->method('fetchAllAssociative')->willReturn($rows);
- return $result;
- }
-
- public function testNoUnusedIndexesPrintsSuccessMessage(): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(MySQLPlatform::class));
- $this->connection->method('executeQuery')
- ->willReturn($this->mockResult([]));
- $this->input->method('getOption')->willReturnMap([['json', false], ['all', false]]);
-
- $output = new BufferedOutput();
- $exit = self::invokePrivate($this->command, 'execute', [$this->input, $output]);
-
- $this->assertSame(0, $exit);
- $this->assertStringContainsString('No unused indexes found', $output->fetch());
- }
-
- public function testMySQLUnusedIndexesRendersTable(): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(MySQLPlatform::class));
- $this->connection->method('executeQuery')
- ->willReturn($this->mockResult($this->mockMySQLRows()));
- $this->input->method('getOption')->willReturnMap([['json', false], ['all', false]]);
-
- $output = new BufferedOutput();
- $exit = self::invokePrivate($this->command, 'execute', [$this->input, $output]);
-
- $this->assertSame(0, $exit);
- $content = $output->fetch();
- $this->assertStringContainsString('Reads', $content);
- $this->assertStringContainsString('Writes', $content);
- $this->assertStringContainsString('idx_fc_name', $content);
- $this->assertStringContainsString('Found 2 unused index(es)', $content);
- }
-
- public function testPostgreSQLUnusedIndexesRendersTable(): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(PostgreSQLPlatform::class));
- $this->connection->method('executeQuery')
- ->willReturn($this->mockResult($this->mockPostgreSQLRows()));
- $this->input->method('getOption')->willReturnMap([['json', false], ['all', false]]);
-
- $output = new BufferedOutput();
- $exit = self::invokePrivate($this->command, 'execute', [$this->input, $output]);
-
- $this->assertSame(0, $exit);
- $content = $output->fetch();
- $this->assertStringContainsString('Tuples Read', $content);
- $this->assertStringContainsString('Tuples Fetched', $content);
- }
-
- public function testAllFlagSuppressesCountMessage(): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(MySQLPlatform::class));
- $this->connection->method('executeQuery')
- ->willReturn($this->mockResult($this->mockMySQLRows()));
- $this->input->method('getOption')->willReturnMap([['json', false], ['all', true]]);
-
- $output = new BufferedOutput();
- self::invokePrivate($this->command, 'execute', [$this->input, $output]);
-
- $this->assertStringNotContainsString('Found', $output->fetch());
- }
-
- public function testDefaultFilterIncludedInQuery(): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(MySQLPlatform::class));
- $this->connection->expects($this->once())
- ->method('executeQuery')
- ->with($this->stringContains('count_read = 0'))
- ->willReturn($this->mockResult([]));
- $this->input->method('getOption')->willReturnMap([['json', false], ['all', false]]);
-
- self::invokePrivate($this->command, 'execute', [$this->input, new BufferedOutput()]);
- }
-
- public function testAllFlagRemovesFilterFromQuery(): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(MySQLPlatform::class));
- $this->connection->expects($this->once())
- ->method('executeQuery')
- ->with($this->logicalNot($this->stringContains('count_read = 0')))
- ->willReturn($this->mockResult([]));
- $this->input->method('getOption')->willReturnMap([['json', false], ['all', true]]);
-
- self::invokePrivate($this->command, 'execute', [$this->input, new BufferedOutput()]);
- }
-
- public function testJsonOutputWhenRowsExist(): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(MySQLPlatform::class));
- $this->connection->method('executeQuery')
- ->willReturn($this->mockResult($this->mockMySQLRows()));
- $this->input->method('getOption')->willReturnMap([['json', true], ['all', false]]);
-
- $output = new BufferedOutput();
- $exit = self::invokePrivate($this->command, 'execute', [$this->input, $output]);
-
- $this->assertSame(0, $exit);
- $data = json_decode($output->fetch(), true);
- $this->assertIsArray($data);
- $this->assertCount(2, $data);
- $this->assertArrayHasKey('table', $data[0]);
- $this->assertArrayHasKey('index', $data[0]);
- }
-
- public function testSQLiteReturnsSuccessWithMessage(): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(SqlitePlatform::class));
- $this->input->method('getOption')->willReturnMap([['json', false], ['all', false]]);
-
- $output = new BufferedOutput();
- $exit = self::invokePrivate($this->command, 'execute', [$this->input, $output]);
-
- $this->assertSame(0, $exit);
- $this->assertStringContainsString('not supported for SQLite', $output->fetch());
- }
-}
--- a/tests/Core/Command/Db/DbInfoTest.php
+++ /dev/null
@@ -1,166 +0,0 @@
-connection = $this->createMock(Connection::class);
- $this->input = $this->createMock(InputInterface::class);
- $this->command = new DbInfo($this->connection);
- }
-
- private function mockMySQLResult(array $overrides = []): Result&MockObject {
- $result = $this->createMock(Result::class);
- $result->method('fetchAssociative')->willReturn(array_merge([
- 'version' => '8.0.30',
- 'buffer_pool' => 1073741824, // 1 GB
- 'max_conn' => '200',
- 'charset' => 'utf8mb4',
- 'tx_isolation' => 'READ-COMMITTED',
- ], $overrides));
- return $result;
- }
-
- private function mockPostgreSQLResult(): Result&MockObject {
- $result = $this->createMock(Result::class);
- $result->method('fetchAssociative')->willReturn([
- 'version' => 'PostgreSQL 15.2 on x86_64',
- 'max_conn' => '100',
- 'shared_buffers' => '128MB',
- 'work_mem' => '4MB',
- ]);
- return $result;
- }
-
- public function testMySQLTableOutput(): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(MySQLPlatform::class));
- $this->connection->method('executeQuery')
- ->willReturn($this->mockMySQLResult());
- $this->input->method('getOption')->willReturnMap([['json', false]]);
-
- $output = new BufferedOutput();
- $exit = self::invokePrivate($this->command, 'execute', [$this->input, $output]);
-
- $this->assertSame(0, $exit);
- $content = $output->fetch();
- $this->assertStringContainsString('Setting', $content);
- $this->assertStringContainsString('MySQL/MariaDB', $content);
- }
-
- public function testPostgreSQLTableOutput(): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(PostgreSQLPlatform::class));
- $this->connection->method('executeQuery')
- ->willReturn($this->mockPostgreSQLResult());
- $this->input->method('getOption')->willReturnMap([['json', false]]);
-
- $output = new BufferedOutput();
- $exit = self::invokePrivate($this->command, 'execute', [$this->input, $output]);
-
- $this->assertSame(0, $exit);
- $this->assertStringContainsString('PostgreSQL', $output->fetch());
- }
-
- public function testSQLiteTableOutput(): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(SqlitePlatform::class));
- $result = $this->createMock(Result::class);
- $result->method('fetchAssociative')->willReturn(['version' => '3.43.0']);
- $this->connection->method('executeQuery')->willReturn($result);
- $this->input->method('getOption')->willReturnMap([['json', false]]);
-
- $output = new BufferedOutput();
- $exit = self::invokePrivate($this->command, 'execute', [$this->input, $output]);
-
- $this->assertSame(0, $exit);
- $this->assertStringContainsString('SQLite', $output->fetch());
- }
-
- public function testUnsupportedPlatformReturnsFailure(): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(AbstractPlatform::class));
- $this->input->method('getOption')->willReturnMap([['json', false]]);
-
- $output = new BufferedOutput();
- $exit = self::invokePrivate($this->command, 'execute', [$this->input, $output]);
-
- $this->assertSame(1, $exit);
- $this->assertStringContainsString('Unsupported', $output->fetch());
- }
-
- public function testJsonOutputContainsSettingKeys(): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(MySQLPlatform::class));
- $this->connection->method('executeQuery')
- ->willReturn($this->mockMySQLResult());
- $this->input->method('getOption')->willReturnMap([['json', true]]);
-
- $output = new BufferedOutput();
- $exit = self::invokePrivate($this->command, 'execute', [$this->input, $output]);
-
- $this->assertSame(0, $exit);
- $data = json_decode($output->fetch(), true);
- $this->assertIsArray($data);
- $this->assertArrayHasKey('setting', $data[0]);
- $this->assertArrayHasKey('value', $data[0]);
- }
-
- public static function dataMySQLHealthChecks(): array {
- return [
- 'charset utf8mb4 → OK' => ['charset', 'utf8mb4', true, 'Character Set'],
- 'charset latin1 → CHECK' => ['charset', 'latin1', false, 'Character Set'],
- 'max_conn 200 → OK' => ['max_conn', '200', true, 'Max Connections'],
- 'max_conn 50 → CHECK' => ['max_conn', '50', false, 'Max Connections'],
- 'tx_isolation READ-COMMITTED → OK' => ['tx_isolation', 'READ-COMMITTED', true, 'Transaction Isolation'],
- 'tx_isolation REPEATABLE-READ → CHECK' => ['tx_isolation', 'REPEATABLE-READ', false, 'Transaction Isolation'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataMySQLHealthChecks')]
- public function testMySQLHealthCheckStatus(
- string $field,
- string $value,
- bool $expectedOk,
- string $settingLabel,
- ): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(MySQLPlatform::class));
- $this->connection->method('executeQuery')
- ->willReturn($this->mockMySQLResult([$field => $value]));
- $this->input->method('getOption')->willReturnMap([['json', true]]);
-
- $output = new BufferedOutput();
- self::invokePrivate($this->command, 'execute', [$this->input, $output]);
-
- $data = json_decode($output->fetch(), true);
- $rows = array_values(array_filter($data, fn ($r) => $r['setting'] === $settingLabel));
- $this->assertNotEmpty($rows, "Setting '{$settingLabel}' not found in JSON output");
- $this->assertSame($expectedOk, $rows[0]['ok']);
- }
-}
--- a/tests/Core/Command/Db/DbLocksTest.php
+++ /dev/null
@@ -1,161 +0,0 @@
-connection = $this->createMock(Connection::class);
- $this->input = $this->createMock(InputInterface::class);
- $this->command = new DbLocks($this->connection);
- }
-
- private function mockMySQLLocks(): array {
- return [[
- 'waiting_trx_id' => '12345',
- 'waiting_thread' => '42',
- 'waiting_query' => 'UPDATE oc_filecache SET path_hash = ?',
- 'blocking_trx_id' => '12344',
- 'blocking_thread' => '41',
- 'blocking_query' => null, // NULL — deve ser renderizado como '—'
- ]];
- }
-
- private function mockPostgreSQLLocks(): array {
- return [[
- 'blocked_pid' => 1234,
- 'blocked_user' => 'nextcloud',
- 'blocking_pid' => 1233,
- 'blocking_user' => 'nextcloud',
- 'blocked_query' => 'SELECT * FROM oc_filecache WHERE parent = ?',
- 'blocked_duration' => '00:00:05.123456',
- ]];
- }
-
- private function mockResult(array $rows): Result&MockObject {
- $result = $this->createMock(Result::class);
- $result->method('fetchAllAssociative')->willReturn($rows);
- return $result;
- }
-
- public function testMySQLNoLocksShowsInfoMessage(): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(MySQLPlatform::class));
- $this->connection->method('executeQuery')
- ->willReturn($this->mockResult([]));
- $this->input->method('getOption')->willReturnMap([['json', false]]);
-
- $output = new BufferedOutput();
- $exit = self::invokePrivate($this->command, 'execute', [$this->input, $output]);
-
- $this->assertSame(0, $exit);
- $this->assertStringContainsString('No active locks', $output->fetch());
- }
-
- public function testPostgreSQLNoLocksShowsInfoMessage(): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(PostgreSQLPlatform::class));
- $this->connection->method('executeQuery')
- ->willReturn($this->mockResult([]));
- $this->input->method('getOption')->willReturnMap([['json', false]]);
-
- $output = new BufferedOutput();
- $exit = self::invokePrivate($this->command, 'execute', [$this->input, $output]);
-
- $this->assertSame(0, $exit);
- $this->assertStringContainsString('No active locks', $output->fetch());
- }
-
- public function testMySQLLocksFoundShowsErrorMessage(): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(MySQLPlatform::class));
- $this->connection->method('executeQuery')
- ->willReturn($this->mockResult($this->mockMySQLLocks()));
- $this->input->method('getOption')->willReturnMap([['json', false]]);
-
- $output = new BufferedOutput();
- $exit = self::invokePrivate($this->command, 'execute', [$this->input, $output]);
-
- $this->assertSame(0, $exit);
- $this->assertStringContainsString('Found 1 blocking transaction(s)', $output->fetch());
- }
-
- public function testPostgreSQLLocksFoundShowsErrorMessage(): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(PostgreSQLPlatform::class));
- $this->connection->method('executeQuery')
- ->willReturn($this->mockResult($this->mockPostgreSQLLocks()));
- $this->input->method('getOption')->willReturnMap([['json', false]]);
-
- $output = new BufferedOutput();
- $exit = self::invokePrivate($this->command, 'execute', [$this->input, $output]);
-
- $this->assertSame(0, $exit);
- $this->assertStringContainsString('Found 1 blocking transaction(s)', $output->fetch());
- }
-
- public function testJsonOutputWhenLocksExist(): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(MySQLPlatform::class));
- $this->connection->method('executeQuery')
- ->willReturn($this->mockResult($this->mockMySQLLocks()));
- $this->input->method('getOption')->willReturnMap([['json', true]]);
-
- $output = new BufferedOutput();
- $exit = self::invokePrivate($this->command, 'execute', [$this->input, $output]);
-
- $this->assertSame(0, $exit);
- $data = json_decode($output->fetch(), true);
- $this->assertIsArray($data);
- $this->assertCount(1, $data);
- $this->assertArrayHasKey('waiting_trx_id', $data[0]);
- }
-
- public function testSQLiteReturnsSuccessWithMessage(): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(SqlitePlatform::class));
- $this->input->method('getOption')->willReturnMap([['json', false]]);
-
- $output = new BufferedOutput();
- $exit = self::invokePrivate($this->command, 'execute', [$this->input, $output]);
-
- $this->assertSame(0, $exit);
- $this->assertStringContainsString('file-level locking', $output->fetch());
- }
-
- public function testNullColumnRenderedAsDash(): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(MySQLPlatform::class));
- $this->connection->method('executeQuery')
- ->willReturn($this->mockResult($this->mockMySQLLocks())); // blocking_query = null
- $this->input->method('getOption')->willReturnMap([['json', false]]);
-
- $output = new BufferedOutput();
- self::invokePrivate($this->command, 'execute', [$this->input, $output]);
-
- $this->assertStringContainsString('—', $output->fetch());
- }
-}
--- a/tests/Core/Command/Db/DbSizeTest.php
+++ /dev/null
@@ -1,122 +0,0 @@
-connection = $this->createMock(Connection::class);
- $this->input = $this->createMock(InputInterface::class);
- $this->command = new DbSize($this->connection);
- }
-
- private function mockRows(): array {
- return [
- ['table' => 'oc_filecache', 'total_mb' => 12.50, 'data_mb' => 10.00, 'index_mb' => 2.50, 'rows' => 5000, 'avg_row_bytes' => 2560],
- ['table' => 'oc_share', 'total_mb' => 3.25, 'data_mb' => 2.00, 'index_mb' => 1.25, 'rows' => 200, 'avg_row_bytes' => 16384],
- ];
- }
-
- private function mockResult(array $rows): Result&MockObject {
- $result = $this->createMock(Result::class);
- $result->method('fetchAllAssociative')->willReturn($rows);
- return $result;
- }
-
- public function testMySQLOutputContainsTableAndTotal(): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(MySQLPlatform::class));
- $this->connection->method('executeQuery')
- ->willReturn($this->mockResult($this->mockRows()));
- $this->input->method('getOption')->willReturnMap([['json', false]]);
-
- $output = new BufferedOutput();
- $exit = self::invokePrivate($this->command, 'execute', [$this->input, $output]);
-
- $this->assertSame(0, $exit);
- $content = $output->fetch();
- $this->assertStringContainsString('oc_filecache', $content);
- $this->assertStringContainsString('Total database size', $content);
- }
-
- public function testPostgreSQLOutputContainsTableAndTotal(): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(PostgreSQLPlatform::class));
- $this->connection->method('executeQuery')
- ->willReturn($this->mockResult($this->mockRows()));
- $this->input->method('getOption')->willReturnMap([['json', false]]);
-
- $output = new BufferedOutput();
- $exit = self::invokePrivate($this->command, 'execute', [$this->input, $output]);
-
- $this->assertSame(0, $exit);
- $this->assertStringContainsString('Total database size', $output->fetch());
- }
-
- public function testSQLiteReturnsSuccessWithMessage(): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(SqlitePlatform::class));
- $this->input->method('getOption')->willReturnMap([['json', false]]);
-
- $output = new BufferedOutput();
- $exit = self::invokePrivate($this->command, 'execute', [$this->input, $output]);
-
- $this->assertSame(0, $exit);
- $this->assertStringContainsString('not supported for SQLite', $output->fetch());
- }
-
- public function testJsonOutputIsValidArray(): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(MySQLPlatform::class));
- $this->connection->method('executeQuery')
- ->willReturn($this->mockResult($this->mockRows()));
- $this->input->method('getOption')->willReturnMap([['json', true]]);
-
- $output = new BufferedOutput();
- $exit = self::invokePrivate($this->command, 'execute', [$this->input, $output]);
-
- $this->assertSame(0, $exit);
- $data = json_decode($output->fetch(), true);
- $this->assertIsArray($data);
- $this->assertCount(2, $data);
- $this->assertArrayHasKey('table', $data[0]);
- $this->assertArrayHasKey('total_mb', $data[0]);
- }
-
- public function testTotalSizeCalculation(): void {
- $this->connection->method('getDatabasePlatform')
- ->willReturn($this->createMock(MySQLPlatform::class));
- $this->connection->method('executeQuery')
- ->willReturn($this->mockResult($this->mockRows()));
- $this->input->method('getOption')->willReturnMap([['json', false]]);
-
- $output = new BufferedOutput();
- self::invokePrivate($this->command, 'execute', [$this->input, $output]);
-
- // 12.50 + 3.25 = 15.75
- $this->assertStringContainsString('15.75', $output->fetch());
- }
-}
--- a/tests/Core/Command/Encryption/ChangeKeyStorageRootTest.php
+++ /dev/null
@@ -1,358 +0,0 @@
-view = $this->getMockBuilder(View::class)->getMock();
- $this->userManager = $this->getMockBuilder(IUserManager::class)->getMock();
- $this->config = $this->getMockBuilder(IConfig::class)->getMock();
- $this->util = $this->getMockBuilder('OC\Encryption\Util')->disableOriginalConstructor()->getMock();
- $this->questionHelper = $this->getMockBuilder(QuestionHelper::class)->getMock();
- $this->inputInterface = $this->getMockBuilder(InputInterface::class)->getMock();
- $this->outputInterface = $this->getMockBuilder(OutputInterface::class)->getMock();
- $this->userInterface = $this->getMockBuilder(UserInterface::class)->getMock();
-
- /* We need format method to return a string */
- $outputFormatter = $this->createMock(OutputFormatterInterface::class);
- $outputFormatter->method('isDecorated')->willReturn(false);
- $outputFormatter->method('format')->willReturnArgument(0);
-
- $this->outputInterface->expects($this->any())->method('getFormatter')
- ->willReturn($outputFormatter);
-
- $this->changeKeyStorageRoot = new ChangeKeyStorageRoot(
- $this->view,
- $this->userManager,
- $this->config,
- $this->util,
- $this->questionHelper
- );
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestExecute')]
- public function testExecute($newRoot, $answer, $successMoveKey): void {
- $changeKeyStorageRoot = $this->getMockBuilder('OC\Core\Command\Encryption\ChangeKeyStorageRoot')
- ->setConstructorArgs(
- [
- $this->view,
- $this->userManager,
- $this->config,
- $this->util,
- $this->questionHelper
- ]
- )->onlyMethods(['moveAllKeys'])->getMock();
-
- $this->util->expects($this->once())->method('getKeyStorageRoot')
- ->willReturn('');
- $this->inputInterface->expects($this->once())->method('getArgument')
- ->with('newRoot')->willReturn($newRoot);
-
- if ($answer === true || $newRoot !== null) {
- $changeKeyStorageRoot->expects($this->once())->method('moveAllKeys')
- ->willReturn($successMoveKey);
- } else {
- $changeKeyStorageRoot->expects($this->never())->method('moveAllKeys');
- }
-
- if ($successMoveKey === true) {
- $this->util->expects($this->once())->method('setKeyStorageRoot');
- } else {
- $this->util->expects($this->never())->method('setKeyStorageRoot');
- }
-
- if ($newRoot === null) {
- $this->questionHelper->expects($this->once())->method('ask')->willReturn($answer);
- } else {
- $this->questionHelper->expects($this->never())->method('ask');
- }
-
- $this->invokePrivate(
- $changeKeyStorageRoot,
- 'execute',
- [$this->inputInterface, $this->outputInterface]
- );
- }
-
- public static function dataTestExecute(): array {
- return [
- [null, true, true],
- [null, true, false],
- [null, false, null],
- ['/newRoot', null, true],
- ['/newRoot', null, false]
- ];
- }
-
- public function testMoveAllKeys(): void {
- /** @var ChangeKeyStorageRoot $changeKeyStorageRoot */
- $changeKeyStorageRoot = $this->getMockBuilder('OC\Core\Command\Encryption\ChangeKeyStorageRoot')
- ->setConstructorArgs(
- [
- $this->view,
- $this->userManager,
- $this->config,
- $this->util,
- $this->questionHelper
- ]
- )->onlyMethods(['prepareNewRoot', 'moveSystemKeys', 'moveUserKeys'])->getMock();
-
- $changeKeyStorageRoot->expects($this->once())->method('prepareNewRoot')->with('newRoot');
- $changeKeyStorageRoot->expects($this->once())->method('moveSystemKeys')->with('oldRoot', 'newRoot');
- $changeKeyStorageRoot->expects($this->once())->method('moveUserKeys')->with('oldRoot', 'newRoot', $this->outputInterface);
-
- $this->invokePrivate($changeKeyStorageRoot, 'moveAllKeys', ['oldRoot', 'newRoot', $this->outputInterface]);
- }
-
- public function testPrepareNewRoot(): void {
- $this->view->expects($this->once())->method('is_dir')->with('newRoot')
- ->willReturn(true);
-
- $this->view->expects($this->once())->method('file_put_contents')
- ->with('newRoot/' . Storage::KEY_STORAGE_MARKER,
- 'Nextcloud will detect this folder as key storage root only if this file exists')->willReturn(true);
-
- $this->invokePrivate($this->changeKeyStorageRoot, 'prepareNewRoot', ['newRoot']);
- }
-
- /**
- *
- * @param bool $dirExists
- * @param bool $couldCreateFile
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestPrepareNewRootException')]
- public function testPrepareNewRootException($dirExists, $couldCreateFile): void {
- $this->expectException(\Exception::class);
-
- $this->view->expects($this->once())->method('is_dir')->with('newRoot')
- ->willReturn($dirExists);
- $this->view->expects($this->any())->method('file_put_contents')->willReturn($couldCreateFile);
-
- $this->invokePrivate($this->changeKeyStorageRoot, 'prepareNewRoot', ['newRoot']);
- }
-
- public static function dataTestPrepareNewRootException(): array {
- return [
- [true, false],
- [true, null],
- [false, true]
- ];
- }
-
- /**
- *
- * @param bool $dirExists
- * @param bool $targetExists
- * @param bool $executeRename
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestMoveSystemKeys')]
- public function testMoveSystemKeys($dirExists, $targetExists, $executeRename): void {
- $changeKeyStorageRoot = $this->getMockBuilder('OC\Core\Command\Encryption\ChangeKeyStorageRoot')
- ->setConstructorArgs(
- [
- $this->view,
- $this->userManager,
- $this->config,
- $this->util,
- $this->questionHelper
- ]
- )->onlyMethods(['targetExists'])->getMock();
-
- $this->view->expects($this->once())->method('is_dir')
- ->with('oldRoot/files_encryption')->willReturn($dirExists);
- $changeKeyStorageRoot->expects($this->any())->method('targetExists')
- ->with('newRoot/files_encryption')->willReturn($targetExists);
-
- if ($executeRename) {
- $this->view->expects($this->once())->method('rename')
- ->with('oldRoot/files_encryption', 'newRoot/files_encryption');
- } else {
- $this->view->expects($this->never())->method('rename');
- }
-
- $this->invokePrivate($changeKeyStorageRoot, 'moveSystemKeys', ['oldRoot', 'newRoot']);
- }
-
- public static function dataTestMoveSystemKeys(): array {
- return [
- [true, false, true],
- [false, true, false],
- [true, true, false],
- [false, false, false]
- ];
- }
-
- public function testMoveUserKeys(): void {
- $changeKeyStorageRoot = $this->getMockBuilder('OC\Core\Command\Encryption\ChangeKeyStorageRoot')
- ->setConstructorArgs(
- [
- $this->view,
- $this->userManager,
- $this->config,
- $this->util,
- $this->questionHelper
- ]
- )->onlyMethods(['setupUserFS', 'moveUserEncryptionFolder'])->getMock();
-
- $this->userManager->expects($this->once())->method('getBackends')
- ->willReturn([$this->userInterface]);
- $this->userInterface->expects($this->once())->method('getUsers')
- ->willReturn(['user1', 'user2']);
- $changeKeyStorageRoot->expects($this->exactly(2))->method('setupUserFS');
- $changeKeyStorageRoot->expects($this->exactly(2))->method('moveUserEncryptionFolder');
-
- $this->invokePrivate($changeKeyStorageRoot, 'moveUserKeys', ['oldRoot', 'newRoot', $this->outputInterface]);
- }
-
- /**
- *
- * @param bool $userExists
- * @param bool $isDir
- * @param bool $targetExists
- * @param bool $shouldRename
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestMoveUserEncryptionFolder')]
- public function testMoveUserEncryptionFolder($userExists, $isDir, $targetExists, $shouldRename): void {
- $changeKeyStorageRoot = $this->getMockBuilder('OC\Core\Command\Encryption\ChangeKeyStorageRoot')
- ->setConstructorArgs(
- [
- $this->view,
- $this->userManager,
- $this->config,
- $this->util,
- $this->questionHelper
- ]
- )->onlyMethods(['targetExists', 'prepareParentFolder'])->getMock();
-
- $this->userManager->expects($this->once())->method('userExists')
- ->willReturn($userExists);
- $this->view->expects($this->any())->method('is_dir')
- ->willReturn($isDir);
- $changeKeyStorageRoot->expects($this->any())->method('targetExists')
- ->willReturn($targetExists);
-
- if ($shouldRename) {
- $changeKeyStorageRoot->expects($this->once())->method('prepareParentFolder')
- ->with('newRoot/user1');
- $this->view->expects($this->once())->method('rename')
- ->with('oldRoot/user1/files_encryption', 'newRoot/user1/files_encryption');
- } else {
- $changeKeyStorageRoot->expects($this->never())->method('prepareParentFolder');
- $this->view->expects($this->never())->method('rename');
- }
-
- $this->invokePrivate($changeKeyStorageRoot, 'moveUserEncryptionFolder', ['user1', 'oldRoot', 'newRoot']);
- }
-
- public static function dataTestMoveUserEncryptionFolder(): array {
- return [
- [true, true, false, true],
- [true, false, true, false],
- [false, true, true, false],
- [false, false, true, false],
- [false, true, false, false],
- [false, true, true, false],
- [false, false, false, false]
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestPrepareParentFolder')]
- public function testPrepareParentFolder($path, $pathExists): void {
- $this->view->expects($this->any())->method('file_exists')
- ->willReturnCallback(
- function ($fileExistsPath) use ($path, $pathExists) {
- if ($path === $fileExistsPath) {
- return $pathExists;
- }
- return false;
- }
- );
-
- if ($pathExists === false) {
- $subDirs = explode('/', ltrim($path, '/'));
- $this->view->expects($this->exactly(count($subDirs)))->method('mkdir');
- } else {
- $this->view->expects($this->never())->method('mkdir');
- }
-
- $this->invokePrivate(
- $this->changeKeyStorageRoot,
- 'prepareParentFolder',
- [$path]
- );
- }
-
- public static function dataTestPrepareParentFolder(): array {
- return [
- ['/user/folder/sub_folder/keystorage', true],
- ['/user/folder/sub_folder/keystorage', false]
- ];
- }
-
- public function testTargetExists(): void {
- $this->view->expects($this->once())->method('file_exists')->with('path')
- ->willReturn(false);
-
- $this->assertFalse(
- $this->invokePrivate($this->changeKeyStorageRoot, 'targetExists', ['path'])
- );
- }
-
- public function testTargetExistsException(): void {
- $this->expectException(\Exception::class);
-
- $this->view->expects($this->once())->method('file_exists')->with('path')
- ->willReturn(true);
-
- $this->invokePrivate($this->changeKeyStorageRoot, 'targetExists', ['path']);
- }
-}
--- a/tests/Core/Command/Encryption/DecryptAllTest.php
+++ /dev/null
@@ -1,199 +0,0 @@
-config = $this->createMock(IConfig::class);
- $this->appConfig = $this->createMock(IAppConfig::class);
- $this->appManager = $this->createMock(IAppManager::class);
- $this->questionHelper = $this->createMock(QuestionHelper::class);
- $this->decryptAll = $this->createMock(\OC\Encryption\DecryptAll::class);
-
- $this->consoleInput = $this->getMockBuilder(InputInterface::class)->getMock();
- $this->consoleInput->expects($this->any())
- ->method('isInteractive')
- ->willReturn(true);
- $this->consoleOutput = $this->getMockBuilder(OutputInterface::class)->getMock();
-
- $this->config->expects($this->any())
- ->method('getSystemValue')
- ->with('maintenance', false)
- ->willReturn(false);
- $this->appManager->expects($this->any())
- ->method('isEnabledForUser')
- ->with('files_trashbin')->willReturn(true);
- }
-
- public function testMaintenanceAndTrashbin(): void {
- // on construct we enable single-user-mode and disable the trash bin
- // on destruct we disable single-user-mode again and enable the trash bin
- $calls = [
- ['maintenance', true],
- ['maintenance', false],
- ];
- $this->config->expects($this->exactly(2))
- ->method('setSystemValue')
- ->willReturnCallback(function () use (&$calls): void {
- $expected = array_shift($calls);
- $this->assertEquals($expected, func_get_args());
- });
- $this->appManager->expects($this->once())
- ->method('disableApp')
- ->with('files_trashbin');
- $this->appManager->expects($this->once())
- ->method('enableApp')
- ->with('files_trashbin');
-
- $instance = new DecryptAll(
- $this->appManager,
- $this->config,
- $this->appConfig,
- $this->decryptAll,
- $this->questionHelper
- );
- $this->invokePrivate($instance, 'forceMaintenanceAndTrashbin');
-
- $this->assertTrue(
- $this->invokePrivate($instance, 'wasTrashbinEnabled')
- );
-
- $this->assertFalse(
- $this->invokePrivate($instance, 'wasMaintenanceModeEnabled')
- );
- $this->invokePrivate($instance, 'resetMaintenanceAndTrashbin');
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestExecute')]
- public function testExecute($encryptionEnabled, $continue): void {
- $instance = new DecryptAll(
- $this->appManager,
- $this->config,
- $this->appConfig,
- $this->decryptAll,
- $this->questionHelper
- );
-
- $this->appConfig->expects($this->once())
- ->method('getValueBool')
- ->with('core', 'encryption_enabled', false)
- ->willReturn($encryptionEnabled);
-
- $this->consoleInput->expects($this->any())
- ->method('getArgument')
- ->with('user')
- ->willReturn('user1');
-
- if ($encryptionEnabled) {
- $calls = [
- ['core', 'encryption_enabled', false, false],
- ['core', 'encryption_enabled', true, false],
- ];
- $this->appConfig->expects($this->exactly(count($calls)))
- ->method('setValueBool')
- ->willReturnCallback(function () use (&$calls): bool {
- $expected = array_shift($calls);
- $this->assertEquals($expected, func_get_args());
- return true;
- });
- } else {
- $this->appConfig->expects($this->never())
- ->method('setValueBool');
- }
- $this->questionHelper->expects($this->once())
- ->method('ask')
- ->willReturn($continue);
- if ($continue) {
- $this->decryptAll->expects($this->once())
- ->method('decryptAll')
- ->with($this->consoleInput, $this->consoleOutput, 'user1');
- } else {
- $this->decryptAll->expects($this->never())->method('decryptAll');
- }
-
- $this->invokePrivate($instance, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-
- public static function dataTestExecute(): array {
- return [
- [true, true],
- [true, false],
- [false, true],
- [false, false]
- ];
- }
-
- public function testExecuteFailure(): void {
- $this->expectException(\Exception::class);
-
- $instance = new DecryptAll(
- $this->appManager,
- $this->config,
- $this->appConfig,
- $this->decryptAll,
- $this->questionHelper
- );
-
- // make sure that we enable encryption again after a exception was thrown
- $calls = [
- ['core', 'encryption_enabled', false, false],
- ['core', 'encryption_enabled', true, false],
- ];
- $this->appConfig->expects($this->exactly(2))
- ->method('setValueBool')
- ->willReturnCallback(function () use (&$calls): bool {
- $expected = array_shift($calls);
- $this->assertEquals($expected, func_get_args());
- return true;
- });
- $this->appConfig->expects($this->once())
- ->method('getValueBool')
- ->with('core', 'encryption_enabled', false)
- ->willReturn(true);
-
- $this->consoleInput->expects($this->any())
- ->method('getArgument')
- ->with('user')
- ->willReturn('user1');
-
- $this->questionHelper->expects($this->once())
- ->method('ask')
- ->willReturn(true);
-
- $this->decryptAll->expects($this->once())
- ->method('decryptAll')
- ->with($this->consoleInput, $this->consoleOutput, 'user1')
- ->willReturnCallback(function (): void {
- throw new \Exception();
- });
-
- $this->invokePrivate($instance, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-}
--- a/tests/Core/Command/Encryption/DisableTest.php
+++ /dev/null
@@ -1,74 +0,0 @@
-appConfig = $this->getMockBuilder(IAppConfig::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->consoleInput = $this->getMockBuilder(InputInterface::class)->getMock();
- $this->consoleOutput = $this->getMockBuilder(OutputInterface::class)->getMock();
-
- /** @var IAppConfig $appConfig */
- $this->command = new Disable($appConfig);
- }
-
- public static function dataDisable(): array {
- return [
- [true, true, 'Encryption disabled'],
- [false, false, 'Encryption is already disabled'],
- ];
- }
-
- /**
- *
- * @param bool $oldStatus
- * @param bool $isUpdating
- * @param string $expectedString
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataDisable')]
- public function testDisable(bool $oldStatus, bool $isUpdating, string $expectedString): void {
- $this->appConfig->expects($this->once())
- ->method('getValueBool')
- ->with('core', 'encryption_enabled', false)
- ->willReturn($oldStatus);
-
- $this->consoleOutput->expects($this->once())
- ->method('writeln')
- ->with($this->stringContains($expectedString));
-
- if ($isUpdating) {
- $this->appConfig->expects($this->once())
- ->method('setValueBool')
- ->with('core', 'encryption_enabled', false);
- }
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-}
--- a/tests/Core/Command/Encryption/EnableTest.php
+++ /dev/null
@@ -1,98 +0,0 @@
-appConfig = $this->getMockBuilder(IAppConfig::class)
- ->disableOriginalConstructor()
- ->getMock();
- $manager = $this->manager = $this->getMockBuilder(IManager::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->consoleInput = $this->getMockBuilder(InputInterface::class)->getMock();
- $this->consoleOutput = $this->getMockBuilder(OutputInterface::class)->getMock();
-
- /** @var \OCP\IAppConfig $appConfig */
- /** @var \OCP\Encryption\IManager $manager */
- $this->command = new Enable($appConfig, $manager);
- }
-
- public static function dataEnable(): array {
- return [
- [false, '', [], true, 'Encryption enabled', 'No encryption module is loaded'],
- [true, '', [], false, 'Encryption is already enabled', 'No encryption module is loaded'],
- [false, '', ['OC_TEST_MODULE' => []], true, 'Encryption enabled', 'No default module is set'],
- [false, 'OC_NO_MODULE', ['OC_TEST_MODULE' => []], true, 'Encryption enabled', 'The current default module does not exist: OC_NO_MODULE'],
- [false, 'OC_TEST_MODULE', ['OC_TEST_MODULE' => []], true, 'Encryption enabled', 'Default module: OC_TEST_MODULE'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataEnable')]
- public function testEnable(bool $oldStatus, ?string $defaultModule, array $availableModules, bool $isUpdating, string $expectedString, string $expectedDefaultModuleString): void {
- if ($isUpdating) {
- $this->appConfig->expects($this->once())
- ->method('setValueBool')
- ->with('core', 'encryption_enabled', true);
- }
-
- $this->manager->expects($this->atLeastOnce())
- ->method('getEncryptionModules')
- ->willReturn($availableModules);
-
- $this->appConfig->expects($this->once())
- ->method('getValueBool')
- ->with('core', 'encryption_enabled', false)
- ->willReturn($oldStatus);
-
- if (!empty($availableModules)) {
- $this->appConfig->expects($this->once())
- ->method('getValueString')
- ->with('core', 'default_encryption_module', '')
- ->willReturn((string)$defaultModule);
- }
-
- $calls = [
- [$expectedString, 0],
- ['', 0],
- [$expectedDefaultModuleString, 0],
- ];
- $this->consoleOutput->expects($this->exactly(3))
- ->method('writeln')
- ->willReturnCallback(function (string $message, int $level) use (&$calls): void {
- $call = array_shift($calls);
- $this->assertStringContainsString($call[0], $message);
- $this->assertSame($call[1], $level);
- });
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-}
--- a/tests/Core/Command/Encryption/EncryptAllTest.php
+++ /dev/null
@@ -1,94 +0,0 @@
-config = $this->createMock(IConfig::class);
- $this->encryptionManager = $this->createMock(IManager::class);
- $this->appManager = $this->createMock(IAppManager::class);
- $this->encryptionModule = $this->createMock(IEncryptionModule::class);
- $this->questionHelper = $this->createMock(QuestionHelper::class);
- $this->consoleInput = $this->createMock(InputInterface::class);
- $this->consoleInput->expects($this->any())
- ->method('isInteractive')
- ->willReturn(true);
- $this->consoleOutput = $this->createMock(OutputInterface::class);
- }
-
- public function testEncryptAll(): void {
- // trash bin needs to be disabled in order to avoid adding dummy files to the users
- // trash bin which gets deleted during the encryption process
- $this->appManager->expects($this->once())->method('disableApp')->with('files_trashbin');
-
- $instance = new EncryptAll($this->encryptionManager, $this->appManager, $this->config, $this->questionHelper);
- $this->invokePrivate($instance, 'forceMaintenanceAndTrashbin');
- $this->invokePrivate($instance, 'resetMaintenanceAndTrashbin');
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestExecute')]
- public function testExecute($answer, $askResult): void {
- $command = new EncryptAll($this->encryptionManager, $this->appManager, $this->config, $this->questionHelper);
-
- $this->encryptionManager->expects($this->once())->method('isEnabled')->willReturn(true);
- $this->questionHelper->expects($this->once())->method('ask')->willReturn($askResult);
-
- if ($answer === 'Y' || $answer === 'y') {
- $this->encryptionManager->expects($this->once())
- ->method('getEncryptionModule')->willReturn($this->encryptionModule);
- $this->encryptionModule->expects($this->once())
- ->method('encryptAll')->with($this->consoleInput, $this->consoleOutput);
- } else {
- $this->encryptionManager->expects($this->never())->method('getEncryptionModule');
- $this->encryptionModule->expects($this->never())->method('encryptAll');
- }
-
- $this->invokePrivate($command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-
- public static function dataTestExecute(): array {
- return [
- ['y', true], ['Y', true], ['n', false], ['N', false], ['', false]
- ];
- }
-
- public function testExecuteException(): void {
- $this->expectException(\Exception::class);
-
- $command = new EncryptAll($this->encryptionManager, $this->appManager, $this->config, $this->questionHelper);
- $this->encryptionManager->expects($this->once())->method('isEnabled')->willReturn(false);
- $this->encryptionManager->expects($this->never())->method('getEncryptionModule');
- $this->encryptionModule->expects($this->never())->method('encryptAll');
- $this->invokePrivate($command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-}
--- a/tests/Core/Command/Encryption/SetDefaultModuleTest.php
+++ /dev/null
@@ -1,130 +0,0 @@
-manager = $this->getMockBuilder(IManager::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->config = $this->getMockBuilder(IConfig::class)
- ->getMock();
-
- $this->consoleInput = $this->getMockBuilder(InputInterface::class)->getMock();
- $this->consoleOutput = $this->getMockBuilder(OutputInterface::class)->getMock();
-
- $this->command = new SetDefaultModule($this->manager, $this->config);
- }
-
- public static function dataSetDefaultModule(): array {
- return [
- ['ID0', 'ID0', null, null, 'already'],
- ['ID0', 'ID1', 'ID1', true, 'info'],
- ['ID0', 'ID1', 'ID1', false, 'error'],
- ];
- }
-
- /**
- *
- * @param string $oldModule
- * @param string $newModule
- * @param string $updateModule
- * @param bool $updateSuccess
- * @param string $expectedString
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetDefaultModule')]
- public function testSetDefaultModule($oldModule, $newModule, $updateModule, $updateSuccess, $expectedString): void {
- $this->consoleInput->expects($this->once())
- ->method('getArgument')
- ->with('module')
- ->willReturn($newModule);
-
- $this->manager->expects($this->once())
- ->method('getDefaultEncryptionModuleId')
- ->willReturn($oldModule);
-
- $this->config->expects($this->once())
- ->method('getSystemValue')
- ->with('maintenance', false)
- ->willReturn(false);
-
- if ($updateModule) {
- $this->manager->expects($this->once())
- ->method('setDefaultEncryptionModule')
- ->with($updateModule)
- ->willReturn($updateSuccess);
- }
-
- $this->consoleOutput->expects($this->once())
- ->method('writeln')
- ->with($this->stringContains($expectedString));
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-
- /**
- *
- * @param string $oldModule
- * @param string $newModule
- * @param string $updateModule
- * @param bool $updateSuccess
- * @param string $expectedString
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetDefaultModule')]
- public function testMaintenanceMode($oldModule, $newModule, $updateModule, $updateSuccess, $expectedString): void {
- $this->consoleInput->expects($this->never())
- ->method('getArgument')
- ->with('module')
- ->willReturn($newModule);
-
- $this->manager->expects($this->never())
- ->method('getDefaultEncryptionModuleId')
- ->willReturn($oldModule);
-
- $this->config->expects($this->once())
- ->method('getSystemValue')
- ->with('maintenance', false)
- ->willReturn(true);
-
- $calls = [
- 'Maintenance mode must be disabled when setting default module,',
- 'in order to load the relevant encryption modules correctly.',
- ];
- $this->consoleOutput->expects($this->exactly(2))
- ->method('writeln')
- ->willReturnCallback(function ($message) use (&$calls): void {
- $expected = array_shift($calls);
- $this->assertStringContainsString($expected, $message);
- });
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-}
--- a/tests/Core/Command/Group/AddTest.php
+++ /dev/null
@@ -1,81 +0,0 @@
-groupManager = $this->createMock(IGroupManager::class);
- $this->command = new Add($this->groupManager);
-
- $this->input = $this->createMock(InputInterface::class);
- $this->input->method('getArgument')
- ->willReturnCallback(function ($arg) {
- if ($arg === 'groupid') {
- return 'myGroup';
- }
- throw new \Exception();
- });
- $this->output = $this->createMock(OutputInterface::class);
- }
-
- public function testGroupExists(): void {
- $gid = 'myGroup';
- $group = $this->createMock(IGroup::class);
- $this->groupManager->method('get')
- ->with($gid)
- ->willReturn($group);
-
- $this->groupManager->expects($this->never())
- ->method('createGroup');
- $this->output->expects($this->once())
- ->method('writeln')
- ->with($this->equalTo('Group "' . $gid . '" already exists. '));
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-
- public function testAdd(): void {
- $gid = 'myGroup';
- $group = $this->createMock(IGroup::class);
- $group->method('getGID')
- ->willReturn($gid);
- $this->groupManager->method('createGroup')
- ->willReturn($group);
-
- $this->groupManager->expects($this->once())
- ->method('createGroup')
- ->with($this->equalTo($gid));
- $this->output->expects($this->once())
- ->method('writeln')
- ->with($this->equalTo('Created group "' . $group->getGID() . '"'));
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-}
--- a/tests/Core/Command/Group/AddUserTest.php
+++ /dev/null
@@ -1,173 +0,0 @@
-groupManager = $this->createMock(IGroupManager::class);
- $this->userManager = $this->createMock(IUserManager::class);
- $this->command = new AddUser($this->userManager, $this->groupManager);
- $this->output = $this->createMock(OutputInterface::class);
- }
-
- protected function configureInput(array|string $returnGroup, array|string $returnUser): void {
- $this->input = $this->createMock(InputInterface::class);
- $this->input->method('getArgument')
- ->willReturnCallback(function ($arg) use ($returnGroup, $returnUser) {
- if ($arg === 'group') {
- return $returnGroup;
- }
- if ($arg === 'user') {
- return $returnUser;
- }
- throw new \Exception();
- });
- }
-
- public function testNoGroup(): void {
- $this->configureInput('myGroup', 'myUser');
-
- $this->groupManager->method('get')
- ->with('myGroup')
- ->willReturn(null);
-
- $this->output->expects($this->once())
- ->method('writeln')
- ->with('group not found ');
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-
- public function testNoUser(): void {
- $this->configureInput('myGroup', 'myUser');
-
- $group = $this->createMock(IGroup::class);
- $this->groupManager->method('get')
- ->with('myGroup')
- ->willReturn($group);
-
- $this->userManager->method('get')
- ->with('myUser')
- ->willReturn(null);
-
- $this->output->expects($this->once())
- ->method('writeln')
- ->with('user myUser not found ');
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-
- public function testAdd(): void {
- $this->configureInput('myGroup', 'myUser');
-
- $group = $this->createMock(IGroup::class);
- $this->groupManager->method('get')
- ->with('myGroup')
- ->willReturn($group);
-
- $user = $this->createMock(IUser::class);
- $this->userManager->method('get')
- ->with('myUser')
- ->willReturn($user);
-
- $group->expects($this->once())
- ->method('addUser')
- ->with($user);
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-
- public function testAddMultiple(): void {
- $this->configureInput('myGroup', ['myUser', 'myOtherUser']);
-
- $group = $this->createMock(IGroup::class);
- $this->groupManager->method('get')
- ->with('myGroup')
- ->willReturn($group);
-
- $user1 = $this->createMock(IUser::class);
- $user2 = $this->createMock(IUser::class);
- $this->userManager->method('get')
- ->willReturnMap([
- ['myUser', $user1],
- ['myOtherUser', $user2],
- ]);
-
- $group->expects($this->exactly(2))
- ->method('addUser')
- ->with($this->callback(static fn (IUser $user): bool => in_array($user, [$user1, $user2], true)));
-
- $this->output->expects($this->exactly(2))
- ->method('writeln')
- ->with($this->callback(static fn (string $message): bool => in_array($message,
- [
- 'user myUser added ',
- 'user myOtherUser added ',
- ], true)));
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-
- public function testAddMultiplePartialSuccess(): void {
- $this->configureInput('myGroup', ['myUser', 'myOtherUser']);
-
- $group = $this->createMock(IGroup::class);
- $this->groupManager->method('get')
- ->with('myGroup')
- ->willReturn($group);
-
- $user = $this->createMock(IUser::class);
- $this->userManager->method('get')
- ->willReturnMap([
- ['myUser', $user],
- ['myOtherUser', null],
- ]);
-
- $group->expects($this->once())
- ->method('addUser')
- ->with($user);
-
- $this->output->expects($this->exactly(3))
- ->method('writeln')
- ->with($this->callback(static fn (string $message): bool => in_array($message,
- [
- 'user myUser added ',
- 'user myOtherUser not found ',
- 'Some users were not found, all others where added to the group. ',
- ], true)));
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-}
--- a/tests/Core/Command/Group/DeleteTest.php
+++ /dev/null
@@ -1,133 +0,0 @@
-groupManager = $this->createMock(IGroupManager::class);
- $this->command = new Delete($this->groupManager);
-
- $this->input = $this->createMock(InputInterface::class);
- $this->output = $this->createMock(OutputInterface::class);
- }
-
- public function testDoesNotExists(): void {
- $gid = 'myGroup';
- $this->input->method('getArgument')
- ->willReturnCallback(function ($arg) use ($gid) {
- if ($arg === 'groupid') {
- return $gid;
- }
- throw new \Exception();
- });
- $this->groupManager->method('groupExists')
- ->with($gid)
- ->willReturn(false);
-
- $this->groupManager->expects($this->never())
- ->method('get');
- $this->output->expects($this->once())
- ->method('writeln')
- ->with($this->equalTo('Group "' . $gid . '" does not exist. '));
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-
- public function testDeleteAdmin(): void {
- $gid = 'admin';
- $this->input->method('getArgument')
- ->willReturnCallback(function ($arg) use ($gid) {
- if ($arg === 'groupid') {
- return $gid;
- }
- throw new \Exception();
- });
-
- $this->groupManager->expects($this->never())
- ->method($this->anything());
- $this->output->expects($this->once())
- ->method('writeln')
- ->with($this->equalTo('Group "' . $gid . '" could not be deleted. '));
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-
- public function testDeleteFailed(): void {
- $gid = 'myGroup';
- $this->input->method('getArgument')
- ->willReturnCallback(function ($arg) use ($gid) {
- if ($arg === 'groupid') {
- return $gid;
- }
- throw new \Exception();
- });
- $group = $this->createMock(IGroup::class);
- $group->method('delete')
- ->willReturn(false);
- $this->groupManager->method('groupExists')
- ->with($gid)
- ->willReturn(true);
- $this->groupManager->method('get')
- ->with($gid)
- ->willReturn($group);
-
- $this->output->expects($this->once())
- ->method('writeln')
- ->with($this->equalTo('Group "' . $gid . '" could not be deleted. Please check the logs. '));
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-
- public function testDelete(): void {
- $gid = 'myGroup';
- $this->input->method('getArgument')
- ->willReturnCallback(function ($arg) use ($gid) {
- if ($arg === 'groupid') {
- return $gid;
- }
- throw new \Exception();
- });
- $group = $this->createMock(IGroup::class);
- $group->method('delete')
- ->willReturn(true);
- $this->groupManager->method('groupExists')
- ->with($gid)
- ->willReturn(true);
- $this->groupManager->method('get')
- ->with($gid)
- ->willReturn($group);
-
- $this->output->expects($this->once())
- ->method('writeln')
- ->with($this->equalTo('Group "' . $gid . '" was removed'));
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-}
--- a/tests/Core/Command/Group/InfoTest.php
+++ /dev/null
@@ -1,99 +0,0 @@
-groupManager = $this->createMock(IGroupManager::class);
- $this->command = $this->getMockBuilder(Info::class)
- ->setConstructorArgs([$this->groupManager])
- ->onlyMethods(['writeArrayInOutputFormat'])
- ->getMock();
-
- $this->input = $this->createMock(InputInterface::class);
- $this->output = $this->createMock(OutputInterface::class);
- }
-
- public function testDoesNotExists(): void {
- $gid = 'myGroup';
- $this->input->method('getArgument')
- ->willReturnCallback(function ($arg) use ($gid) {
- if ($arg === 'groupid') {
- return $gid;
- }
- throw new \Exception();
- });
- $this->groupManager->method('get')
- ->with($gid)
- ->willReturn(null);
-
- $this->output->expects($this->once())
- ->method('writeln')
- ->with($this->equalTo('Group "' . $gid . '" does not exist. '));
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-
- public function testInfo(): void {
- $gid = 'myGroup';
- $this->input->method('getArgument')
- ->willReturnCallback(function ($arg) use ($gid) {
- if ($arg === 'groupid') {
- return $gid;
- }
- throw new \Exception();
- });
-
- $group = $this->createMock(IGroup::class);
- $group->method('getGID')->willReturn($gid);
- $group->method('getDisplayName')
- ->willReturn('My Group');
- $group->method('getBackendNames')
- ->willReturn(['Database']);
-
- $this->groupManager->method('get')
- ->with($gid)
- ->willReturn($group);
-
- $this->command->expects($this->once())
- ->method('writeArrayInOutputFormat')
- ->with(
- $this->equalTo($this->input),
- $this->equalTo($this->output),
- [
- 'groupID' => 'myGroup',
- 'displayName' => 'My Group',
- 'backends' => ['Database'],
- ]
- );
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-}
--- a/tests/Core/Command/Group/ListCommandTest.php
+++ /dev/null
@@ -1,205 +0,0 @@
-groupManager = $this->createMock(IGroupManager::class);
- $this->command = $this->getMockBuilder(ListCommand::class)
- ->setConstructorArgs([$this->groupManager])
- ->onlyMethods(['writeArrayInOutputFormat'])
- ->getMock();
-
- $this->input = $this->createMock(InputInterface::class);
- $this->output = $this->createMock(OutputInterface::class);
- }
-
- public function testExecute(): void {
- $group1 = $this->createMock(IGroup::class);
- $group1->method('getGID')->willReturn('group1');
- $group2 = $this->createMock(IGroup::class);
- $group2->method('getGID')->willReturn('group2');
- $group3 = $this->createMock(IGroup::class);
- $group3->method('getGID')->willReturn('group3');
-
- $user = $this->createMock(IUser::class);
-
- $this->groupManager->method('search')
- ->with(
- '',
- 100,
- 42,
- )->willReturn([$group1, $group2, $group3]);
-
- $group1->method('getUsers')
- ->willReturn([
- 'user1' => $user,
- 'user2' => $user,
- ]);
-
- $group2->method('getUsers')
- ->willReturn([
- ]);
-
- $group3->method('getUsers')
- ->willReturn([
- 'user1' => $user,
- 'user3' => $user,
- ]);
-
- $this->input->method('getOption')
- ->willReturnCallback(function ($arg) {
- if ($arg === 'limit') {
- return '100';
- } elseif ($arg === 'offset') {
- return '42';
- } elseif ($arg === 'info') {
- return null;
- }
- throw new \Exception();
- });
-
- $this->command->expects($this->once())
- ->method('writeArrayInOutputFormat')
- ->with(
- $this->equalTo($this->input),
- $this->equalTo($this->output),
- $this->callback(
- fn ($iterator) => iterator_to_array($iterator) === [
- 'group1' => [
- 'user1',
- 'user2',
- ],
- 'group2' => [
- ],
- 'group3' => [
- 'user1',
- 'user3',
- ]
- ]
- )
- );
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-
- public function testInfo(): void {
- $group1 = $this->createMock(IGroup::class);
- $group1->method('getGID')->willReturn('group1');
- $group1->method('getDisplayName')->willReturn('Group 1');
- $group2 = $this->createMock(IGroup::class);
- $group2->method('getGID')->willReturn('group2');
- $group2->method('getDisplayName')->willReturn('Group 2');
- $group3 = $this->createMock(IGroup::class);
- $group3->method('getGID')->willReturn('group3');
- $group3->method('getDisplayName')->willReturn('Group 3');
-
- $user = $this->createMock(IUser::class);
-
- $this->groupManager->method('search')
- ->with(
- '',
- 100,
- 42,
- )->willReturn([$group1, $group2, $group3]);
-
- $group1->method('getUsers')
- ->willReturn([
- 'user1' => $user,
- 'user2' => $user,
- ]);
-
- $group1->method('getBackendNames')
- ->willReturn(['Database']);
-
- $group2->method('getUsers')
- ->willReturn([
- ]);
-
- $group2->method('getBackendNames')
- ->willReturn(['Database']);
-
- $group3->method('getUsers')
- ->willReturn([
- 'user1' => $user,
- 'user3' => $user,
- ]);
-
- $group3->method('getBackendNames')
- ->willReturn(['LDAP']);
-
- $this->input->method('getOption')
- ->willReturnCallback(function ($arg) {
- if ($arg === 'limit') {
- return '100';
- } elseif ($arg === 'offset') {
- return '42';
- } elseif ($arg === 'info') {
- return true;
- }
- throw new \Exception();
- });
-
- $this->command->expects($this->once())
- ->method('writeArrayInOutputFormat')
- ->with(
- $this->equalTo($this->input),
- $this->equalTo($this->output),
- $this->callback(
- fn ($iterator) => iterator_to_array($iterator) === [
- 'group1' => [
- 'displayName' => 'Group 1',
- 'backends' => ['Database'],
- 'users' => [
- 'user1',
- 'user2',
- ],
- ],
- 'group2' => [
- 'displayName' => 'Group 2',
- 'backends' => ['Database'],
- 'users' => [],
- ],
- 'group3' => [
- 'displayName' => 'Group 3',
- 'backends' => ['LDAP'],
- 'users' => [
- 'user1',
- 'user3',
- ],
- ]
- ]
- )
- );
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-}
--- a/tests/Core/Command/Group/RemoveUserTest.php
+++ /dev/null
@@ -1,173 +0,0 @@
-groupManager = $this->createMock(IGroupManager::class);
- $this->userManager = $this->createMock(IUserManager::class);
- $this->command = new RemoveUser($this->userManager, $this->groupManager);
- $this->output = $this->createMock(OutputInterface::class);
- }
-
- protected function configureInput(array|string $returnGroup, array|string $returnUser): void {
- $this->input = $this->createMock(InputInterface::class);
- $this->input->method('getArgument')
- ->willReturnCallback(function ($arg) use ($returnGroup, $returnUser) {
- if ($arg === 'group') {
- return $returnGroup;
- }
- if ($arg === 'user') {
- return $returnUser;
- }
- throw new \Exception();
- });
- }
-
- public function testNoGroup(): void {
- $this->configureInput('myGroup', 'myUser');
-
- $this->groupManager->method('get')
- ->with('myGroup')
- ->willReturn(null);
-
- $this->output->expects($this->once())
- ->method('writeln')
- ->with('group not found ');
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-
- public function testNoUser(): void {
- $this->configureInput('myGroup', 'myUser');
-
- $group = $this->createMock(IGroup::class);
- $this->groupManager->method('get')
- ->with('myGroup')
- ->willReturn($group);
-
- $this->userManager->method('get')
- ->with('myUser')
- ->willReturn(null);
-
- $this->output->expects($this->once())
- ->method('writeln')
- ->with('user myUser not found ');
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-
- public function testRemove(): void {
- $this->configureInput('myGroup', 'myUser');
-
- $group = $this->createMock(IGroup::class);
- $this->groupManager->method('get')
- ->with('myGroup')
- ->willReturn($group);
-
- $user = $this->createMock(IUser::class);
- $this->userManager->method('get')
- ->with('myUser')
- ->willReturn($user);
-
- $group->expects($this->once())
- ->method('removeUser')
- ->with($user);
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-
- public function testRemoveMultiple(): void {
- $this->configureInput('myGroup', ['myUser', 'myOtherUser']);
-
- $group = $this->createMock(IGroup::class);
- $this->groupManager->method('get')
- ->with('myGroup')
- ->willReturn($group);
-
- $user1 = $this->createMock(IUser::class);
- $user2 = $this->createMock(IUser::class);
- $this->userManager->method('get')
- ->willReturnMap([
- ['myUser', $user1],
- ['myOtherUser', $user2],
- ]);
-
- $group->expects($this->exactly(2))
- ->method('removeUser')
- ->with($this->callback(static fn (IUser $user): bool => in_array($user, [$user1, $user2], true)));
-
- $this->output->expects($this->exactly(2))
- ->method('writeln')
- ->with($this->callback(static fn (string $message): bool => in_array($message,
- [
- 'user myUser removed ',
- 'user myOtherUser removed ',
- ], true)));
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-
- public function testRemoveMultiplePartialSuccess(): void {
- $this->configureInput('myGroup', ['myUser', 'myOtherUser']);
-
- $group = $this->createMock(IGroup::class);
- $this->groupManager->method('get')
- ->with('myGroup')
- ->willReturn($group);
-
- $user = $this->createMock(IUser::class);
- $this->userManager->method('get')
- ->willReturnMap([
- ['myUser', $user],
- ['myOtherUser', null],
- ]);
-
- $group->expects($this->once())
- ->method('removeUser')
- ->with($user);
-
- $this->output->expects($this->exactly(3))
- ->method('writeln')
- ->with($this->callback(static fn (string $message): bool => in_array($message,
- [
- 'user myUser removed ',
- 'user myOtherUser not found ',
- 'Some users were not found, all others where removed from the group. ',
- ], true)));
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-}
--- a/tests/Core/Command/Log/FileTest.php
+++ /dev/null
@@ -1,113 +0,0 @@
-config = $this->getMockBuilder(IConfig::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->consoleInput = $this->getMockBuilder(InputInterface::class)->getMock();
- $this->consoleOutput = $this->getMockBuilder(OutputInterface::class)->getMock();
-
- $this->command = new File($config);
- }
-
- public function testEnable(): void {
- $this->config->method('getSystemValue')->willReturnArgument(1);
- $this->consoleInput->method('getOption')
- ->willReturnMap([
- ['enable', 'true']
- ]);
- $this->config->expects($this->once())
- ->method('setSystemValue')
- ->with('log_type', 'file');
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-
- public function testChangeFile(): void {
- $this->config->method('getSystemValue')->willReturnArgument(1);
- $this->consoleInput->method('getOption')
- ->willReturnMap([
- ['file', '/foo/bar/file.log']
- ]);
- $this->config->expects($this->once())
- ->method('setSystemValue')
- ->with('logfile', '/foo/bar/file.log');
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-
- public static function changeRotateSizeProvider(): array {
- return [
- ['42', 42],
- ['0', 0],
- ['1 kB', 1024],
- ['5MB', 5 * 1024 * 1024],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('changeRotateSizeProvider')]
- public function testChangeRotateSize($optionValue, $configValue): void {
- $this->config->method('getSystemValue')->willReturnArgument(1);
- $this->consoleInput->method('getOption')
- ->willReturnMap([
- ['rotate-size', $optionValue]
- ]);
- $this->config->expects($this->once())
- ->method('setSystemValue')
- ->with('log_rotate_size', $configValue);
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-
- public function testGetConfiguration(): void {
- $this->config->method('getSystemValue')
- ->willReturnMap([
- ['log_type', 'file', 'log_type_value'],
- ['datadirectory', \OC::$SERVERROOT . '/data', '/data/directory/'],
- ['logfile', '/data/directory/nextcloud.log', '/var/log/nextcloud.log'],
- ['log_rotate_size', 100 * 1024 * 1024, 5 * 1024 * 1024],
- ]);
-
- $calls = [
- ['Log backend file: disabled'],
- ['Log file: /var/log/nextcloud.log'],
- ['Rotate at: 5 MB'],
- ];
- $this->consoleOutput->expects($this->exactly(3))
- ->method('writeln')
- ->willReturnCallback(function (string $message) use (&$calls): void {
- $expected = array_shift($calls);
- $this->assertEquals($expected[0], $message);
- });
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-}
--- a/tests/Core/Command/Log/ManageTest.php
+++ /dev/null
@@ -1,161 +0,0 @@
-config = $this->getMockBuilder(IConfig::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->consoleInput = $this->getMockBuilder(InputInterface::class)->getMock();
- $this->consoleOutput = $this->getMockBuilder(OutputInterface::class)->getMock();
-
- $this->command = new Manage($config);
- }
-
- public function testChangeBackend(): void {
- $this->consoleInput->method('getOption')
- ->willReturnMap([
- ['backend', 'syslog']
- ]);
- $this->config->expects($this->once())
- ->method('setSystemValue')
- ->with('log_type', 'syslog');
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-
- public function testChangeLevel(): void {
- $this->consoleInput->method('getOption')
- ->willReturnMap([
- ['level', 'debug']
- ]);
- $this->config->expects($this->once())
- ->method('setSystemValue')
- ->with('loglevel', 0);
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-
- public function testChangeTimezone(): void {
- $this->consoleInput->method('getOption')
- ->willReturnMap([
- ['timezone', 'UTC']
- ]);
- $this->config->expects($this->once())
- ->method('setSystemValue')
- ->with('logtimezone', 'UTC');
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-
- public function testValidateBackend(): void {
- $this->expectException(\InvalidArgumentException::class);
-
- self::invokePrivate($this->command, 'validateBackend', ['notabackend']);
- }
-
- public function testValidateTimezone(): void {
- $this->expectException(\Exception::class);
-
- // this might need to be changed when humanity colonises Mars
- self::invokePrivate($this->command, 'validateTimezone', ['Mars/OlympusMons']);
- }
-
- public static function dataConvertLevelString(): array {
- return [
- ['dEbug', 0],
- ['inFO', 1],
- ['Warning', 2],
- ['wArn', 2],
- ['error', 3],
- ['eRr', 3],
- ['fAtAl', 4],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataConvertLevelString')]
- public function testConvertLevelString(string $levelString, int $expectedInt): void {
- $this->assertEquals($expectedInt,
- self::invokePrivate($this->command, 'convertLevelString', [$levelString])
- );
- }
-
- public function testConvertLevelStringInvalid(): void {
- $this->expectException(\InvalidArgumentException::class);
-
- self::invokePrivate($this->command, 'convertLevelString', ['abc']);
- }
-
- public static function dataConvertLevelNumber(): array {
- return [
- [0, 'Debug'],
- [1, 'Info'],
- [2, 'Warning'],
- [3, 'Error'],
- [4, 'Fatal'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataConvertLevelNumber')]
- public function testConvertLevelNumber(int $levelNum, string $expectedString): void {
- $this->assertEquals($expectedString,
- self::invokePrivate($this->command, 'convertLevelNumber', [$levelNum])
- );
- }
-
- public function testConvertLevelNumberInvalid(): void {
- $this->expectException(\InvalidArgumentException::class);
-
- self::invokePrivate($this->command, 'convertLevelNumber', [11]);
- }
-
- public function testGetConfiguration(): void {
- $this->config->expects($this->exactly(3))
- ->method('getSystemValue')
- ->willReturnMap([
- ['log_type', 'file', 'log_type_value'],
- ['loglevel', 2, 0],
- ['logtimezone', 'UTC', 'logtimezone_value'],
- ]);
-
- $calls = [
- ['Enabled logging backend: log_type_value'],
- ['Log level: Debug (0)'],
- ['Log timezone: logtimezone_value'],
- ];
- $this->consoleOutput->expects($this->exactly(3))
- ->method('writeln')
- ->willReturnCallback(function (string $message) use (&$calls): void {
- $call = array_shift($calls);
- $this->assertStringContainsString($call[0], $message);
- });
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-}
--- a/tests/Core/Command/Maintenance/DataFingerprintTest.php
+++ /dev/null
@@ -1,54 +0,0 @@
-config = $this->getMockBuilder(IConfig::class)->getMock();
- $this->timeFactory = $this->getMockBuilder(ITimeFactory::class)->getMock();
- $this->consoleInput = $this->getMockBuilder(InputInterface::class)->getMock();
- $this->consoleOutput = $this->getMockBuilder(OutputInterface::class)->getMock();
-
- /** @var IConfig $config */
- $this->command = new DataFingerprint($this->config, $this->timeFactory);
- }
-
- public function testSetFingerPrint(): void {
- $this->timeFactory->expects($this->once())
- ->method('getTime')
- ->willReturn(42);
- $this->config->expects($this->once())
- ->method('setSystemValue')
- ->with('data-fingerprint', md5(42));
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-}
--- a/tests/Core/Command/Maintenance/InstallTest.php
+++ /dev/null
@@ -1,91 +0,0 @@
-command = new Install(
- $this->createMock(SystemConfig::class),
- $this->createMock(IniGetWrapper::class),
- );
- }
-
- /**
- * @param array $parameters
- * @return array The installer options built from the command line input
- */
- private function validateInput(array $parameters): array {
- $input = new ArrayInput(array_merge([
- '--database-name' => 'nextcloud',
- '--database-user' => 'admin',
- '--database-pass' => 'admin-password',
- '--admin-pass' => 'admin-password',
- ], $parameters), $this->command->getDefinition());
-
- return self::invokePrivate($this->command, 'validateInput', [$input, new NullOutput(), ['sqlite', 'mysql', 'pgsql', 'oci']]);
- }
-
- public static function encryptionOptions(): array {
- return [
- '--database-ssl-mode' => ['--database-ssl-mode', 'verify-full', 'dbsslmode', 'verify-full'],
- '--database-ssl-ca' => ['--database-ssl-ca', '/ca.pem', 'dbsslca', '/ca.pem'],
- '--database-ssl-cert' => ['--database-ssl-cert', '/client.crt', 'dbsslcert', '/client.crt'],
- '--database-ssl-key' => ['--database-ssl-key', '/client.key', 'dbsslkey', '/client.key'],
- '--database-ssl-crl' => ['--database-ssl-crl', '/crl.pem', 'dbsslcrl', '/crl.pem'],
- '--database-ssl-no-verify' => ['--database-ssl-no-verify', true, 'dbsslnoverify', true],
- ];
- }
-
- /**
- * The command only forwards the options, the database setup translates them into the
- * system config values and rejects the ones it does not support.
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('encryptionOptions')]
- public function testForwardsEncryptionOptions(string $parameter, string|bool $value, string $option, string|bool $expected): void {
- $options = $this->validateInput([
- '--database' => 'pgsql',
- $parameter => $value,
- ]);
-
- $this->assertSame($expected, $options[$option]);
- }
-
- public function testNoEncryptionOptions(): void {
- $options = $this->validateInput(['--database' => 'mysql']);
-
- foreach (['dbsslmode', 'dbsslca', 'dbsslcert', 'dbsslkey', 'dbsslcrl', 'dbsslnoverify'] as $option) {
- $this->assertArrayNotHasKey($option, $options);
- }
- }
-
- /**
- * An option that does not apply to the chosen database is not filtered out here, it
- * has to be reported by the database setup instead of being silently dropped.
- */
- public function testForwardsEncryptionOptionsRegardlessOfDatabase(): void {
- $options = $this->validateInput([
- '--database' => 'sqlite',
- '--database-ssl-ca' => '/ca.pem',
- ]);
-
- $this->assertSame('/ca.pem', $options['dbsslca']);
- }
-}
--- a/tests/Core/Command/Maintenance/Mimetype/UpdateDBTest.php
+++ /dev/null
@@ -1,175 +0,0 @@
-detector = $this->createMock(Detection::class);
- $this->loader = $this->createMock(Loader::class);
- $this->consoleInput = $this->createMock(InputInterface::class);
- $this->consoleOutput = $this->createMock(OutputInterface::class);
-
- $this->command = new UpdateDB($this->detector, $this->loader);
- }
-
- public function testNoop(): void {
- $this->consoleInput->method('getOption')
- ->with('repair-filecache')
- ->willReturn(false);
-
- $this->detector->expects($this->once())
- ->method('getAllMappings')
- ->willReturn([
- 'ext' => ['testing/existingmimetype']
- ]);
- $this->loader->expects($this->once())
- ->method('exists')
- ->with('testing/existingmimetype')
- ->willReturn(true);
-
- $this->loader->expects($this->never())
- ->method('updateFilecache');
-
- $calls = [
- 'Added 0 new mimetypes',
- 'Updated 0 filecache rows',
- ];
- $this->consoleOutput->expects($this->exactly(2))
- ->method('writeln')
- ->willReturnCallback(function ($message) use (&$calls): void {
- $expected = array_shift($calls);
- $this->assertStringContainsString($expected, $message);
- });
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-
- public function testAddMimetype(): void {
- $this->consoleInput->method('getOption')
- ->with('repair-filecache')
- ->willReturn(false);
-
- $this->detector->expects($this->once())
- ->method('getAllMappings')
- ->willReturn([
- 'ext' => ['testing/existingmimetype'],
- 'new' => ['testing/newmimetype']
- ]);
- $this->loader->expects($this->exactly(2))
- ->method('exists')
- ->willReturnMap([
- ['testing/existingmimetype', true],
- ['testing/newmimetype', false],
- ]);
- $this->loader->expects($this->exactly(2))
- ->method('getId')
- ->willReturnMap([
- ['testing/existingmimetype', 1],
- ['testing/newmimetype', 2],
- ]);
-
- $this->loader->expects($this->once())
- ->method('updateFilecache')
- ->with('new', 2)
- ->willReturn(3);
-
- $calls = [
- 'Added mimetype "testing/newmimetype" to database',
- 'Updated 3 filecache rows for mimetype "testing/newmimetype"',
- 'Added 1 new mimetypes',
- 'Updated 3 filecache rows',
- ];
- $this->consoleOutput->expects($this->exactly(4))
- ->method('writeln')
- ->willReturnCallback(function ($message) use (&$calls): void {
- $expected = array_shift($calls);
- $this->assertStringContainsString($expected, $message);
- });
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-
- public function testSkipComments(): void {
- $this->detector->expects($this->once())
- ->method('getAllMappings')
- ->willReturn([
- '_comment' => 'some comment in the JSON'
- ]);
- $this->loader->expects($this->never())
- ->method('exists');
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-
- public function testRepairFilecache(): void {
- $this->consoleInput->method('getOption')
- ->with('repair-filecache')
- ->willReturn(true);
-
- $this->detector->expects($this->once())
- ->method('getAllMappings')
- ->willReturn([
- 'ext' => ['testing/existingmimetype'],
- ]);
- $this->loader->expects($this->exactly(1))
- ->method('exists')
- ->willReturnMap([
- ['testing/existingmimetype', true],
- ]);
- $this->loader->expects($this->exactly(1))
- ->method('getId')
- ->willReturnMap([
- ['testing/existingmimetype', 1],
- ]);
-
- $this->loader->expects($this->once())
- ->method('updateFilecache')
- ->with('ext', 1)
- ->willReturn(3);
-
- $calls = [
- 'Updated 3 filecache rows for mimetype "testing/existingmimetype"',
- 'Added 0 new mimetypes',
- 'Updated 3 filecache rows',
- ];
- $this->consoleOutput->expects($this->exactly(3))
- ->method('writeln')
- ->willReturnCallback(function ($message) use (&$calls): void {
- $expected = array_shift($calls);
- $this->assertStringContainsString($expected, $message);
- });
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-}
--- a/tests/Core/Command/Maintenance/ModeTest.php
+++ /dev/null
@@ -1,153 +0,0 @@
-config = $this->getMockBuilder(IConfig::class)
- ->getMock();
- $this->mode = new Mode($this->config);
- $this->input = $this->getMockBuilder(InputInterface::class)
- ->getMock();
- $this->output = $this->getMockBuilder(OutputInterface::class)
- ->getMock();
- }
-
- /**
- * Provides test data for the execute test.
- *
- * @return array
- */
- public static function getExecuteTestData(): array {
- return [
- 'off -> on' => [
- 'on', // command option
- false, // current maintenance mode state
- true, // expected maintenance mode state, null for no change
- 'Maintenance mode enabled', // expected output
- ],
- 'on -> off' => [
- 'off',
- true,
- false,
- 'Maintenance mode disabled',
- ],
- 'on -> on' => [
- 'on',
- true,
- null,
- 'Maintenance mode already enabled',
- ],
- 'off -> off' => [
- 'off',
- false,
- null,
- 'Maintenance mode already disabled',
- ],
- 'no option, maintenance enabled' => [
- '',
- true,
- null,
- 'Maintenance mode is currently enabled',
- ],
- 'no option, maintenance disabled' => [
- '',
- false,
- null,
- 'Maintenance mode is currently disabled',
- ],
- ];
- }
-
- /**
- * Asserts that execute works as expected.
- *
- * @param string $option The command option.
- * @param bool $currentMaintenanceState The current maintenance state.
- * @param null|bool $expectedMaintenanceState
- * The expected maintenance state. Null for no change.
- * @param string $expectedOutput The expected command output.
- * @throws \Exception
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('getExecuteTestData')]
- public function testExecute(
- string $option,
- bool $currentMaintenanceState,
- $expectedMaintenanceState,
- string $expectedOutput,
- ): void {
- $this->config->expects($this->any())
- ->method('getSystemValueBool')
- ->willReturn($currentMaintenanceState);
-
- if ($expectedMaintenanceState !== null) {
- $this->config->expects($this->once())
- ->method('setSystemValue')
- ->with('maintenance', $expectedMaintenanceState);
- }
-
- $this->input->expects($this->any())
- ->method('getOption')
- ->willReturnCallback(function ($callOption) use ($option) {
- return $callOption === $option;
- });
-
- $this->output->expects($this->once())
- ->method('writeln')
- ->with($expectedOutput);
-
- $this->mode->run($this->input, $this->output);
- }
-}
--- a/tests/Core/Command/Maintenance/UpdateTheme.php
+++ /dev/null
@@ -1,63 +0,0 @@
-detector = $this->createMock(Detection::class);
- $this->cacheFactory = $this->createMock(ICacheFactory::class);
-
- $this->consoleInput = $this->getMockBuilder(InputInterface::class)->getMock();
- $this->consoleOutput = $this->getMockBuilder(OutputInterface::class)->getMock();
-
- $this->command = new UpdateTheme($this->detector, $this->cacheFactory);
- }
-
- public function testThemeUpdate(): void {
- $this->consoleInput->method('getOption')
- ->with('maintenance:theme:update')
- ->willReturn(true);
- $this->detector->expects($this->once())
- ->method('getAllAliases')
- ->willReturn([]);
- $cache = $this->createMock(ICache::class);
- $cache->expects($this->once())
- ->method('clear')
- ->with('');
- $this->cacheFactory->expects($this->once())
- ->method('createDistributed')
- ->with('imagePath')
- ->willReturn($cache);
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-}
--- a/tests/Core/Command/Preview/CleanupTest.php
+++ /dev/null
@@ -1,156 +0,0 @@
-rootFolder = $this->createMock(IRootFolder::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->previewService = $this->createMock(PreviewService::class);
- $this->repair = new Cleanup(
- $this->rootFolder,
- $this->logger,
- $this->previewService,
- );
-
- $this->input = $this->createMock(InputInterface::class);
- $this->output = $this->createMock(OutputInterface::class);
- }
-
- public function testCleanup(): void {
- $this->previewService->expects($this->once())->method('deleteAll');
-
- $previewFolder = $this->createMock(Folder::class);
- $previewFolder->expects($this->once())
- ->method('isDeletable')
- ->willReturn(true);
-
- $previewFolder->expects($this->once())
- ->method('delete');
-
- $appDataFolder = $this->createMock(Folder::class);
- $appDataFolder->expects($this->once())->method('get')->with('preview')->willReturn($previewFolder);
-
- $this->rootFolder->expects($this->once())
- ->method('getAppDataDirectoryName')
- ->willReturn('appdata_some_id');
-
- $this->rootFolder->expects($this->once())
- ->method('get')
- ->with('appdata_some_id')
- ->willReturn($appDataFolder);
-
- $this->output->expects($this->exactly(2))->method('writeln')
- ->with(self::callback(function (string $message): bool {
- static $i = 0;
- return match (++$i) {
- 1 => $message === 'Preview folder deleted',
- 2 => $message === 'Previews removed'
- };
- }));
-
- $this->assertEquals(0, $this->repair->run($this->input, $this->output));
- }
-
- public function testCleanupWhenNotDeletable(): void {
- $previewFolder = $this->createMock(Folder::class);
- $previewFolder->expects($this->once())
- ->method('isDeletable')
- ->willReturn(false);
-
- $previewFolder->expects($this->never())
- ->method('delete');
-
- $appDataFolder = $this->createMock(Folder::class);
- $appDataFolder->expects($this->once())->method('get')->with('preview')->willReturn($previewFolder);
-
- $this->rootFolder->expects($this->once())
- ->method('getAppDataDirectoryName')
- ->willReturn('appdata_some_id');
-
- $this->rootFolder->expects($this->once())
- ->method('get')
- ->with('appdata_some_id')
- ->willReturn($appDataFolder);
-
- $this->logger->expects($this->once())->method('error')->with("Previews can't be removed: preview folder isn't deletable");
- $this->output->expects($this->once())->method('writeln')->with("Previews can't be removed: preview folder isn't deletable");
-
- $this->assertEquals(1, $this->repair->run($this->input, $this->output));
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataForTestCleanupWithDeleteException')]
- public function testCleanupWithDeleteException(string $exceptionClass, string $errorMessage): void {
- $previewFolder = $this->createMock(Folder::class);
- $previewFolder->expects($this->once())
- ->method('isDeletable')
- ->willReturn(true);
-
- $previewFolder->expects($this->once())
- ->method('delete')
- ->willThrowException(new $exceptionClass());
-
- $appDataFolder = $this->createMock(Folder::class);
- $appDataFolder->expects($this->once())->method('get')->with('preview')->willReturn($previewFolder);
-
- $this->rootFolder->expects($this->once())
- ->method('getAppDataDirectoryName')
- ->willReturn('appdata_some_id');
-
- $this->rootFolder->expects($this->once())
- ->method('get')
- ->with('appdata_some_id')
- ->willReturn($appDataFolder);
-
- $this->logger->expects($this->once())->method('error')->with($errorMessage);
- $this->output->expects($this->once())->method('writeln')->with($errorMessage);
-
- $this->assertEquals(1, $this->repair->run($this->input, $this->output));
- }
-
- public static function dataForTestCleanupWithDeleteException(): array {
- return [
- [NotFoundException::class, "Previews weren't deleted: preview folder was not found while deleting it"],
- [NotPermittedException::class, "Previews weren't deleted: you don't have the permission to delete preview folder"],
- ];
- }
-
- public function testCleanupWithPreviewServiceException(): void {
- $this->rootFolder->method('getAppDataDirectoryName')
- ->willThrowException(new NotFoundException());
-
- $this->previewService->expects($this->once())->method('deleteAll')
- ->willThrowException(new NotPermittedException('abc'));
-
- $this->logger->expects($this->once())->method('info')->with("Legacy previews can't be removed: appdata folder can't be found");
- $this->logger->expects($this->once())->method('error')->with("Previews can't be removed: exception occurred: abc");
-
- $this->assertEquals(1, $this->repair->run($this->input, $this->output));
- }
-}
--- a/tests/Core/Command/SetupChecksTest.php
+++ /dev/null
@@ -1,146 +0,0 @@
-setupCheckManager = $this->createMock(ISetupCheckManager::class);
- $this->richTextFormatter = $this->createMock(IRichTextFormatter::class);
-
- $this->commandTester = new CommandTester(
- new SetupChecks($this->setupCheckManager, $this->richTextFormatter)
- );
- }
-
- /**
- * Report one check through the output the command passes to the manager, and return its result.
- */
- private function runOneCheck(?IOutput $output): array {
- $this->assertInstanceOf(IOutput::class, $output);
- $check = new SetupChecksTestCheck();
- $output->debug('Starting check ' . $check->getName() . ' (' . $check::class . ')');
- $output->debug('Check ' . $check->getName() . ' (' . $check::class . ') done in 0.01 seconds, peak memory usage: 8 MB');
- $result = SetupResult::success('Everything is fine');
- $result->setName($check->getName());
- return ['system' => [$check::class => $result]];
- }
-
- private function expectRunAll(): void {
- $this->setupCheckManager->expects($this->once())
- ->method('runAll')
- ->willReturnCallback($this->runOneCheck(...));
- }
-
- public function testProgressIsReportedOnVerboseOutput(): void {
- $this->expectRunAll();
-
- $this->assertSame(Command::SUCCESS, $this->commandTester->execute([], [
- 'verbosity' => OutputInterface::VERBOSITY_VERBOSE,
- 'capture_stderr_separately' => true,
- ]));
-
- $this->assertStringContainsString('Starting check Test check (' . SetupChecksTestCheck::class . ')', $this->commandTester->getErrorOutput());
- $this->assertStringContainsString('done in 0.01 seconds, peak memory usage: 8 MB', $this->commandTester->getErrorOutput());
- $this->assertStringContainsString('Everything is fine', $this->commandTester->getDisplay());
- $this->assertStringNotContainsString('Starting check Test check', $this->commandTester->getDisplay());
- }
-
- public function testProgressIsNotReportedOnNormalOutput(): void {
- $this->expectRunAll();
-
- $this->assertSame(Command::SUCCESS, $this->commandTester->execute([], [
- 'capture_stderr_separately' => true,
- ]));
-
- $this->assertStringNotContainsString('Starting check Test check', $this->commandTester->getErrorOutput());
- $this->assertStringNotContainsString('Starting check Test check', $this->commandTester->getDisplay());
- }
-
- public function testProgressKeepsJsonOutputParsable(): void {
- $this->expectRunAll();
-
- $this->assertSame(Command::SUCCESS, $this->commandTester->execute(['--output' => 'json'], [
- 'verbosity' => OutputInterface::VERBOSITY_VERBOSE,
- 'capture_stderr_separately' => true,
- ]));
-
- $this->assertStringContainsString('Starting check Test check', $this->commandTester->getErrorOutput());
- $this->assertIsArray(json_decode($this->commandTester->getDisplay(), true, flags: JSON_THROW_ON_ERROR));
- }
-
- public function testFilterByCategory(): void {
- $this->setupCheckManager->expects($this->once())
- ->method('runByCategory')
- ->willReturnCallback(function (string $category, ?IOutput $output): array {
- $this->assertSame('system', $category);
- return $this->runOneCheck($output);
- });
-
- $this->assertSame(Command::SUCCESS, $this->commandTester->execute(['category' => 'system']));
- }
-
- public function testFilterByClass(): void {
- $this->setupCheckManager->expects($this->once())
- ->method('runByClass')
- ->willReturnCallback(function (string $class, ?IOutput $output): array {
- $this->assertSame(SetupChecksTestCheck::class, $class);
- return $this->runOneCheck($output);
- });
-
- $this->assertSame(Command::SUCCESS, $this->commandTester->execute(['class' => SetupChecksTestCheck::class]));
- }
-
- public function testFilterByCategoryAndClassIsRejected(): void {
- $this->setupCheckManager->expects($this->never())
- ->method($this->anything());
-
- $this->assertSame(Command::FAILURE, $this->commandTester->execute([
- 'category' => 'system',
- 'class' => SetupChecksTestCheck::class,
- ]));
- $this->assertStringContainsString('Please specify only one of category or class', $this->commandTester->getDisplay());
- }
-}
--- a/tests/Core/Command/SystemTag/AddTest.php
+++ /dev/null
@@ -1,122 +0,0 @@
-systemTagManager = $this->createMock(ISystemTagManager::class);
- $this->command = $this->getMockBuilder(Add::class)
- ->setConstructorArgs([$this->systemTagManager])
- ->onlyMethods(['writeArrayInOutputFormat'])
- ->getMock();
-
- $this->input = $this->createMock(InputInterface::class);
- $this->output = $this->createMock(OutputInterface::class);
- }
-
- public function testExecute(): void {
- $tagId = '42';
- $tagName = 'wichtig';
- $tagAccess = 'public';
-
- $tag = $this->createMock(ISystemTag::class);
- $tag->method('getId')->willReturn($tagId);
- $tag->method('getName')->willReturn($tagName);
- $tag->method('getAccessLevel')->willReturn(ISystemTag::ACCESS_LEVEL_PUBLIC);
-
- $this->systemTagManager->method('createTag')
- ->with(
- $tagName,
- true,
- true
- )->willReturn($tag);
-
- $this->input->method('getArgument')
- ->willReturnCallback(function ($arg) use ($tagName, $tagAccess) {
- if ($arg === 'name') {
- return $tagName;
- } elseif ($arg === 'access') {
- return $tagAccess;
- }
- throw new \Exception();
- });
-
- $this->command->expects($this->once())
- ->method('writeArrayInOutputFormat')
- ->with(
- $this->equalTo($this->input),
- $this->equalTo($this->output),
- [
- 'id' => $tagId,
- 'name' => $tagName,
- 'access' => $tagAccess,
- ]
- );
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-
- public function testAlreadyExists(): void {
- $tagId = '42';
- $tagName = 'wichtig';
- $tagAccess = 'public';
-
- $tag = $this->createMock(ISystemTag::class);
- $tag->method('getId')->willReturn($tagId);
- $tag->method('getName')->willReturn($tagName);
- $tag->method('getAccessLevel')->willReturn(ISystemTag::ACCESS_LEVEL_PUBLIC);
-
- $this->systemTagManager->method('createTag')
- ->willReturnCallback(function ($tagName, $userVisible, $userAssignable): void {
- throw new TagAlreadyExistsException(
- 'Tag ("' . $tagName . '", ' . $userVisible . ', ' . $userAssignable . ') already exists'
- );
- });
-
- $this->input->method('getArgument')
- ->willReturnCallback(function ($arg) use ($tagName, $tagAccess) {
- if ($arg === 'name') {
- return $tagName;
- } elseif ($arg === 'access') {
- return $tagAccess;
- }
- throw new \Exception();
- });
-
- $this->output->expects($this->once())
- ->method('writeln')
- ->with(
- 'Tag ("wichtig", 1, 1) already exists '
- );
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-}
--- a/tests/Core/Command/SystemTag/DeleteTest.php
+++ /dev/null
@@ -1,84 +0,0 @@
-systemTagManager = $this->createMock(ISystemTagManager::class);
- $this->command = $this->getMockBuilder(Delete::class)
- ->setConstructorArgs([$this->systemTagManager])
- ->onlyMethods(['writeArrayInOutputFormat'])
- ->getMock();
-
- $this->input = $this->createMock(InputInterface::class);
- $this->output = $this->createMock(OutputInterface::class);
- }
-
- public function testExecute(): void {
- $tagId = 69;
-
- $this->input->method('getArgument')
- ->willReturnCallback(function ($arg) use ($tagId) {
- if ($arg === 'id') {
- return $tagId;
- }
- throw new \Exception();
- });
-
- $this->output->expects($this->once())
- ->method('writeln')
- ->with('The specified tag was deleted ');
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-
- public function testNotFound(): void {
- $tagId = 69;
-
- $this->input->method('getArgument')
- ->willReturnCallback(function ($arg) use ($tagId) {
- if ($arg === 'id') {
- return $tagId;
- }
- throw new \Exception();
- });
-
- $this->systemTagManager->method('deleteTags')
- ->willReturnCallback(function ($tagId): void {
- throw new TagNotFoundException();
- });
-
- $this->output->expects($this->once())
- ->method('writeln')
- ->with('Tag not found ');
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-}
--- a/tests/Core/Command/SystemTag/EditTest.php
+++ /dev/null
@@ -1,187 +0,0 @@
-systemTagManager = $this->createMock(ISystemTagManager::class);
- $this->command = $this->getMockBuilder(Edit::class)
- ->setConstructorArgs([$this->systemTagManager])
- ->onlyMethods(['writeArrayInOutputFormat'])
- ->getMock();
-
- $this->input = $this->createMock(InputInterface::class);
- $this->output = $this->createMock(OutputInterface::class);
- }
-
- public function testExecute(): void {
- $tagId = '5';
- $tagName = 'unwichtige Dateien';
- $newTagName = 'moderat wichtige Dateien';
- $newTagAccess = 'restricted';
- $newTagUserVisible = true;
- $newTagUserAssignable = false;
-
- $tag = $this->createMock(ISystemTag::class);
- $tag->method('getId')->willReturn($tagId);
- $tag->method('getName')->willReturn($tagName);
- $tag->method('getAccessLevel')->willReturn(ISystemTag::ACCESS_LEVEL_INVISIBLE);
-
- $this->systemTagManager->method('getTagsByIds')
- ->with($tagId)
- ->willReturn([$tag]);
-
- $this->input->method('getArgument')
- ->willReturnCallback(function ($arg) use ($tagId) {
- if ($arg === 'id') {
- return $tagId;
- }
- throw new \Exception();
- });
-
- $this->input->method('getOption')
- ->willReturnCallback(function ($arg) use ($newTagName, $newTagAccess) {
- if ($arg === 'name') {
- return $newTagName;
- } elseif ($arg === 'access') {
- return $newTagAccess;
- }
- throw new \Exception();
- });
-
- $this->systemTagManager->expects($this->once())
- ->method('updateTag')
- ->with(
- $tagId,
- $newTagName,
- $newTagUserVisible,
- $newTagUserAssignable,
- ''
- );
-
- $this->output->expects($this->once())
- ->method('writeln')
- ->with(
- 'Tag updated ("' . $newTagName . '", ' . json_encode($newTagUserVisible) . ', ' . json_encode($newTagUserAssignable) . ', "") '
- );
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-
- public function testAlreadyExists(): void {
- $tagId = '5';
- $tagName = 'unwichtige Dateien';
- $tagUserVisible = false;
- $tagUserAssignable = false;
- $newTagName = 'moderat wichtige Dateien';
- $newTagAccess = 'restricted';
- $newTagUserVisible = true;
- $newTagUserAssignable = false;
-
- $tag = $this->createMock(ISystemTag::class);
- $tag->method('getId')->willReturn($tagId);
- $tag->method('getName')->willReturn($tagName);
- $tag->method('isUserVisible')->willReturn($tagUserVisible);
- $tag->method('isUserAssignable')->willReturn($tagUserAssignable);
- $tag->method('getAccessLevel')->willReturn(ISystemTag::ACCESS_LEVEL_INVISIBLE);
-
- $this->systemTagManager->method('getTagsByIds')
- ->with($tagId)
- ->willReturn([$tag]);
-
- $this->input->method('getArgument')
- ->willReturnCallback(function ($arg) use ($tagId) {
- if ($arg === 'id') {
- return $tagId;
- }
- throw new \Exception();
- });
-
- $this->input->method('getOption')
- ->willReturnCallback(function ($arg) use ($newTagName, $newTagAccess) {
- if ($arg === 'name') {
- return $newTagName;
- } elseif ($arg === 'access') {
- return $newTagAccess;
- }
- throw new \Exception();
- });
-
- $this->systemTagManager->method('updateTag')
- ->willReturnCallback(function ($tagId, $tagName, $userVisible, $userAssignable): void {
- throw new TagAlreadyExistsException(
- 'Tag ("' . $tagName . '", ' . $userVisible . ', ' . $userAssignable . ') already exists'
- );
- });
-
- $this->systemTagManager->expects($this->once())
- ->method('updateTag')
- ->with(
- $tagId,
- $newTagName,
- $newTagUserVisible,
- $newTagUserAssignable,
- ''
- );
-
- $this->output->expects($this->once())
- ->method('writeln')
- ->with(
- 'Tag ("' . $newTagName . '", ' . $newTagUserVisible . ', ' . $newTagUserAssignable . ') already exists '
- );
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-
- public function testNotFound(): void {
- $tagId = '404';
-
- $this->input->method('getArgument')
- ->willReturnCallback(function ($arg) use ($tagId) {
- if ($arg === 'id') {
- return $tagId;
- }
- throw new \Exception();
- });
-
- $this->systemTagManager->method('getTagsByIds')
- ->with($tagId)
- ->willReturn([]);
-
- $this->output->expects($this->once())
- ->method('writeln')
- ->with(
- 'Tag not found '
- );
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-}
--- a/tests/Core/Command/SystemTag/ListCommandTest.php
+++ /dev/null
@@ -1,97 +0,0 @@
-systemTagManager = $this->createMock(ISystemTagManager::class);
- $this->command = $this->getMockBuilder(ListCommand::class)
- ->setConstructorArgs([$this->systemTagManager])
- ->onlyMethods(['writeArrayInOutputFormat'])
- ->getMock();
-
- $this->input = $this->createMock(InputInterface::class);
- $this->output = $this->createMock(OutputInterface::class);
- }
-
- public function testExecute(): void {
- $tag1 = $this->createMock(ISystemTag::class);
- $tag1->method('getId')->willReturn('1');
- $tag1->method('getName')->willReturn('public_tag');
- $tag1->method('getAccessLevel')->willReturn(ISystemTag::ACCESS_LEVEL_PUBLIC);
- $tag2 = $this->createMock(ISystemTag::class);
- $tag2->method('getId')->willReturn('2');
- $tag2->method('getName')->willReturn('restricted_tag');
- $tag2->method('getAccessLevel')->willReturn(ISystemTag::ACCESS_LEVEL_RESTRICTED);
- $tag3 = $this->createMock(ISystemTag::class);
- $tag3->method('getId')->willReturn('3');
- $tag3->method('getName')->willReturn('invisible_tag');
- $tag3->method('getAccessLevel')->willReturn(ISystemTag::ACCESS_LEVEL_INVISIBLE);
-
- $this->systemTagManager->method('getAllTags')
- ->with(
- null,
- null
- )->willReturn([$tag1, $tag2, $tag3]);
-
- $this->input->method('getOption')
- ->willReturnCallback(function ($arg) {
- if ($arg === 'visibilityFilter') {
- return null;
- } elseif ($arg === 'nameSearchPattern') {
- return null;
- }
- throw new \Exception();
- });
-
- $this->command->expects($this->once())
- ->method('writeArrayInOutputFormat')
- ->with(
- $this->equalTo($this->input),
- $this->equalTo($this->output),
- [
- '1' => [
- 'name' => 'public_tag',
- 'access' => 'public',
- ],
- '2' => [
- 'name' => 'restricted_tag',
- 'access' => 'restricted',
- ],
- '3' => [
- 'name' => 'invisible_tag',
- 'access' => 'invisible',
- ]
- ]
- );
-
- $this->invokePrivate($this->command, 'execute', [$this->input, $this->output]);
- }
-}
--- a/tests/Core/Command/TaskProcessing/WorkerCommandTest.php
+++ /dev/null
@@ -1,352 +0,0 @@
-manager = $this->createMock(IManager::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->appConfig = $this->createMock(IAppConfig::class);
- $this->timeFactory = $this->createMock(ITimeFactory::class);
- $this->timeFactory->method('now')->willReturnCallback(fn () => new \DateTimeImmutable());
- $this->command = new WorkerCommand($this->manager, $this->logger, $this->appConfig, $this->timeFactory);
- }
-
- /**
- * Helper to create a minimal ISynchronousProvider mock.
- */
- private function createProvider(string $id, string $taskTypeId): ISynchronousProvider&MockObject {
- $provider = $this->createMock(ISynchronousProvider::class);
- $provider->method('getId')->willReturn($id);
- $provider->method('getTaskTypeId')->willReturn($taskTypeId);
- return $provider;
- }
-
- /**
- * Helper to create a real Task with a given id and optional task type.
- */
- private function createTask(int $id, string $type = 'test_task_type'): Task {
- $task = new Task($type, [], 'testapp', null);
- $task->setId($id);
- return $task;
- }
-
- public function testOnceExitsAfterNoTask(): void {
- $this->manager->expects($this->once())
- ->method('getProviders')
- ->willReturn([]);
-
- $input = new ArrayInput(['--once' => true], $this->command->getDefinition());
- $output = new NullOutput();
-
- $result = $this->command->run($input, $output);
-
- $this->assertSame(0, $result);
- }
-
- public function testOnceProcessesOneTask(): void {
- $taskTypeId = 'test_task_type';
- $provider = $this->createProvider('test_provider', $taskTypeId);
- $task = $this->createTask(42);
-
- $this->manager->expects($this->once())
- ->method('getProviders')
- ->willReturn([$provider]);
-
- $this->manager->expects($this->once())
- ->method('getPreferredProvider')
- ->with($taskTypeId)
- ->willReturn($provider);
-
- $this->manager->expects($this->once())
- ->method('claimNextScheduledTask')
- ->with([$taskTypeId])
- ->willReturn($task);
-
- $this->manager->expects($this->once())
- ->method('processTask')
- ->with($task, $provider)
- ->willReturn(true);
-
- $input = new ArrayInput(['--once' => true], $this->command->getDefinition());
- $output = new NullOutput();
-
- $result = $this->command->run($input, $output);
-
- $this->assertSame(0, $result);
- }
-
- public function testSkipsNonSynchronousProviders(): void {
- // A provider that is NOT an ISynchronousProvider
- $nonSyncProvider = $this->createMock(\OCP\TaskProcessing\IProvider::class);
- $nonSyncProvider->method('getId')->willReturn('non_sync_provider');
- $nonSyncProvider->method('getTaskTypeId')->willReturn('some_type');
-
- $this->manager->expects($this->once())
- ->method('getProviders')
- ->willReturn([$nonSyncProvider]);
-
- $this->manager->expects($this->never())
- ->method('getPreferredProvider');
-
- $this->manager->expects($this->never())
- ->method('claimNextScheduledTask');
-
- $input = new ArrayInput(['--once' => true], $this->command->getDefinition());
- $output = new NullOutput();
-
- $result = $this->command->run($input, $output);
-
- $this->assertSame(0, $result);
- }
-
- public function testSkipsNonPreferredProviders(): void {
- $taskTypeId = 'test_task_type';
- $provider = $this->createProvider('provider_a', $taskTypeId);
- $preferredProvider = $this->createProvider('provider_b', $taskTypeId);
-
- $this->manager->expects($this->once())
- ->method('getProviders')
- ->willReturn([$provider]);
-
- $this->manager->expects($this->once())
- ->method('getPreferredProvider')
- ->with($taskTypeId)
- ->willReturn($preferredProvider);
-
- // provider_a is not preferred (provider_b is), so claimNextScheduledTask is never called
- $this->manager->expects($this->never())
- ->method('claimNextScheduledTask');
-
- $input = new ArrayInput(['--once' => true], $this->command->getDefinition());
- $output = new NullOutput();
-
- $result = $this->command->run($input, $output);
-
- $this->assertSame(0, $result);
- }
-
- public function testContinuesWhenNoTaskFound(): void {
- $taskTypeId = 'test_task_type';
- $provider = $this->createProvider('test_provider', $taskTypeId);
-
- $this->manager->expects($this->once())
- ->method('getProviders')
- ->willReturn([$provider]);
-
- $this->manager->expects($this->once())
- ->method('getPreferredProvider')
- ->with($taskTypeId)
- ->willReturn($provider);
-
- // The no-task path is now claimNextScheduledTask returning null (not an exception).
- $this->manager->expects($this->once())
- ->method('claimNextScheduledTask')
- ->with([$taskTypeId])
- ->willReturn(null);
-
- $this->manager->expects($this->never())
- ->method('processTask');
-
- $input = new ArrayInput(['--once' => true], $this->command->getDefinition());
- $output = new NullOutput();
-
- $result = $this->command->run($input, $output);
-
- $this->assertSame(0, $result);
- }
-
- public function testLogsErrorAndContinuesOnException(): void {
- $taskTypeId = 'test_task_type';
- $provider = $this->createProvider('test_provider', $taskTypeId);
-
- $this->manager->expects($this->once())
- ->method('getProviders')
- ->willReturn([$provider]);
-
- $this->manager->expects($this->once())
- ->method('getPreferredProvider')
- ->with($taskTypeId)
- ->willReturn($provider);
-
- $exception = new Exception('DB error');
- $this->manager->expects($this->once())
- ->method('claimNextScheduledTask')
- ->with([$taskTypeId])
- ->willThrowException($exception);
-
- $this->logger->expects($this->once())
- ->method('error')
- ->with('Unknown error while claiming scheduled TaskProcessing tasks', ['exception' => $exception]);
-
- $this->manager->expects($this->never())
- ->method('processTask');
-
- $input = new ArrayInput(['--once' => true], $this->command->getDefinition());
- $output = new NullOutput();
-
- $result = $this->command->run($input, $output);
-
- $this->assertSame(0, $result);
- }
-
- public function testProcessesCorrectProviderForReturnedTaskType(): void {
- $taskTypeId1 = 'type_a';
- $taskTypeId2 = 'type_b';
-
- $provider1 = $this->createProvider('provider_a', $taskTypeId1);
- $provider2 = $this->createProvider('provider_b', $taskTypeId2);
- // Task has type_a, so provider1 must be chosen to process it
- $task = $this->createTask(7, $taskTypeId1);
-
- $this->manager->expects($this->once())
- ->method('getProviders')
- ->willReturn([$provider1, $provider2]);
-
- // Both providers are eligible, so getPreferredProvider is called for each
- $this->manager->expects($this->exactly(2))
- ->method('getPreferredProvider')
- ->willReturnMap([
- [$taskTypeId1, $provider1],
- [$taskTypeId2, $provider2],
- ]);
-
- // All eligible task types are passed in a single atomic claim
- $this->manager->expects($this->once())
- ->method('claimNextScheduledTask')
- ->with($this->equalTo([$taskTypeId1, $taskTypeId2]))
- ->willReturn($task);
-
- $this->manager->expects($this->once())
- ->method('processTask')
- ->with($task, $provider1)
- ->willReturn(true);
-
- $input = new ArrayInput(['--once' => true], $this->command->getDefinition());
- $output = new NullOutput();
-
- $result = $this->command->run($input, $output);
-
- $this->assertSame(0, $result);
- }
-
- public function testTaskTypesWhitelistFiltersProviders(): void {
- $taskTypeId1 = 'type_a';
- $taskTypeId2 = 'type_b';
-
- $provider1 = $this->createProvider('provider_a', $taskTypeId1);
- $provider2 = $this->createProvider('provider_b', $taskTypeId2);
- $task = $this->createTask(99, $taskTypeId2);
-
- $this->manager->expects($this->once())
- ->method('getProviders')
- ->willReturn([$provider1, $provider2]);
-
- // Only type_b is whitelisted, so provider_a (type_a) must be skipped entirely
- $this->manager->expects($this->once())
- ->method('getPreferredProvider')
- ->with($taskTypeId2)
- ->willReturn($provider2);
-
- $this->manager->expects($this->once())
- ->method('claimNextScheduledTask')
- ->with([$taskTypeId2])
- ->willReturn($task);
-
- $this->manager->expects($this->once())
- ->method('processTask')
- ->with($task, $provider2)
- ->willReturn(true);
-
- $input = new ArrayInput(['--once' => true, '--taskTypes' => [$taskTypeId2]], $this->command->getDefinition());
- $output = new NullOutput();
-
- $result = $this->command->run($input, $output);
-
- $this->assertSame(0, $result);
- }
-
- public function testTaskTypesWhitelistWithNoMatchingProviders(): void {
- $provider = $this->createProvider('provider_a', 'type_a');
-
- $this->manager->expects($this->once())
- ->method('getProviders')
- ->willReturn([$provider]);
-
- // Whitelist does not include type_a so nothing should be processed
- $this->manager->expects($this->never())
- ->method('getPreferredProvider');
-
- $this->manager->expects($this->never())
- ->method('claimNextScheduledTask');
-
- $input = new ArrayInput(['--once' => true, '--taskTypes' => ['type_b']], $this->command->getDefinition());
- $output = new NullOutput();
-
- $result = $this->command->run($input, $output);
-
- $this->assertSame(0, $result);
- }
-
- public function testEmptyTaskTypesAllowsAllProviders(): void {
- $taskTypeId = 'type_a';
- $provider = $this->createProvider('provider_a', $taskTypeId);
- $task = $this->createTask(5, $taskTypeId);
-
- $this->manager->expects($this->once())
- ->method('getProviders')
- ->willReturn([$provider]);
-
- $this->manager->expects($this->once())
- ->method('getPreferredProvider')
- ->with($taskTypeId)
- ->willReturn($provider);
-
- $this->manager->expects($this->once())
- ->method('claimNextScheduledTask')
- ->with([$taskTypeId])
- ->willReturn($task);
-
- $this->manager->expects($this->once())
- ->method('processTask')
- ->with($task, $provider)
- ->willReturn(true);
-
- // No --taskTypes option provided
- $input = new ArrayInput(['--once' => true], $this->command->getDefinition());
- $output = new NullOutput();
-
- $result = $this->command->run($input, $output);
-
- $this->assertSame(0, $result);
- }
-}
--- a/tests/Core/Command/TwoFactorAuth/CleanupTest.php
+++ /dev/null
@@ -1,53 +0,0 @@
-registry = $this->createMock(IRegistry::class);
- $this->userManager = $this->createMock(IUserManager::class);
-
- $cmd = new Cleanup($this->registry, $this->userManager);
- $this->cmd = new CommandTester($cmd);
- }
-
- public function testCleanup(): void {
- $this->registry->expects($this->once())
- ->method('cleanUp')
- ->with('u2f');
-
- $rc = $this->cmd->execute([
- 'provider-id' => 'u2f',
- ]);
-
- $this->assertEquals(0, $rc);
- $output = $this->cmd->getDisplay();
- $this->assertStringContainsString('All user-provider associations for provider u2f have been removed', $output);
- }
-}
--- a/tests/Core/Command/TwoFactorAuth/DisableTest.php
+++ /dev/null
@@ -1,95 +0,0 @@
-providerManager = $this->createMock(ProviderManager::class);
- $this->userManager = $this->createMock(IUserManager::class);
-
- $cmd = new Disable($this->providerManager, $this->userManager);
- $this->command = new CommandTester($cmd);
- }
-
- public function testInvalidUID(): void {
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('nope')
- ->willReturn(null);
-
- $rc = $this->command->execute([
- 'uid' => 'nope',
- 'provider_id' => 'nope',
- ]);
-
- $this->assertEquals(1, $rc);
- $this->assertStringContainsString('Invalid UID', $this->command->getDisplay());
- }
-
- public function testEnableNotSupported(): void {
- $user = $this->createMock(IUser::class);
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('ricky')
- ->willReturn($user);
- $this->providerManager->expects($this->once())
- ->method('tryDisableProviderFor')
- ->with('totp', $user)
- ->willReturn(false);
-
- $rc = $this->command->execute([
- 'uid' => 'ricky',
- 'provider_id' => 'totp',
- ]);
-
- $this->assertEquals(2, $rc);
- $this->assertStringContainsString('The provider does not support this operation', $this->command->getDisplay());
- }
-
- public function testEnabled(): void {
- $user = $this->createMock(IUser::class);
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('ricky')
- ->willReturn($user);
- $this->providerManager->expects($this->once())
- ->method('tryDisableProviderFor')
- ->with('totp', $user)
- ->willReturn(true);
-
- $rc = $this->command->execute([
- 'uid' => 'ricky',
- 'provider_id' => 'totp',
- ]);
-
- $this->assertEquals(0, $rc);
- $this->assertStringContainsString('Two-factor provider totp disabled for user ricky', $this->command->getDisplay());
- }
-}
--- a/tests/Core/Command/TwoFactorAuth/EnableTest.php
+++ /dev/null
@@ -1,95 +0,0 @@
-providerManager = $this->createMock(ProviderManager::class);
- $this->userManager = $this->createMock(IUserManager::class);
-
- $cmd = new Enable($this->providerManager, $this->userManager);
- $this->command = new CommandTester($cmd);
- }
-
- public function testInvalidUID(): void {
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('nope')
- ->willReturn(null);
-
- $rc = $this->command->execute([
- 'uid' => 'nope',
- 'provider_id' => 'nope',
- ]);
-
- $this->assertEquals(1, $rc);
- $this->assertStringContainsString('Invalid UID', $this->command->getDisplay());
- }
-
- public function testEnableNotSupported(): void {
- $user = $this->createMock(IUser::class);
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('belle')
- ->willReturn($user);
- $this->providerManager->expects($this->once())
- ->method('tryEnableProviderFor')
- ->with('totp', $user)
- ->willReturn(false);
-
- $rc = $this->command->execute([
- 'uid' => 'belle',
- 'provider_id' => 'totp',
- ]);
-
- $this->assertEquals(2, $rc);
- $this->assertStringContainsString('The provider does not support this operation', $this->command->getDisplay());
- }
-
- public function testEnabled(): void {
- $user = $this->createMock(IUser::class);
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('belle')
- ->willReturn($user);
- $this->providerManager->expects($this->once())
- ->method('tryEnableProviderFor')
- ->with('totp', $user)
- ->willReturn(true);
-
- $rc = $this->command->execute([
- 'uid' => 'belle',
- 'provider_id' => 'totp',
- ]);
-
- $this->assertEquals(0, $rc);
- $this->assertStringContainsString('Two-factor provider totp enabled for user belle', $this->command->getDisplay());
- }
-}
--- a/tests/Core/Command/TwoFactorAuth/EnforceTest.php
+++ /dev/null
@@ -1,129 +0,0 @@
-mandatoryTwoFactor = $this->createMock(MandatoryTwoFactor::class);
- $command = new Enforce($this->mandatoryTwoFactor);
-
- $this->command = new CommandTester($command);
- }
-
- public function testEnforce(): void {
- $this->mandatoryTwoFactor->expects($this->once())
- ->method('setState')
- ->with($this->equalTo(new EnforcementState(true)));
- $this->mandatoryTwoFactor->expects($this->once())
- ->method('getState')
- ->willReturn(new EnforcementState(true));
-
- $rc = $this->command->execute([
- '--on' => true,
- ]);
-
- $this->assertEquals(0, $rc);
- $display = $this->command->getDisplay();
- $this->assertStringContainsString('Two-factor authentication is enforced for all users', $display);
- }
-
- public function testEnforceForOneGroup(): void {
- $this->mandatoryTwoFactor->expects($this->once())
- ->method('setState')
- ->with($this->equalTo(new EnforcementState(true, ['twofactorers'])));
- $this->mandatoryTwoFactor->expects($this->once())
- ->method('getState')
- ->willReturn(new EnforcementState(true, ['twofactorers']));
-
- $rc = $this->command->execute([
- '--on' => true,
- '--group' => ['twofactorers'],
- ]);
-
- $this->assertEquals(0, $rc);
- $display = $this->command->getDisplay();
- $this->assertStringContainsString('Two-factor authentication is enforced for members of the group(s) twofactorers', $display);
- }
-
- public function testEnforceForAllExceptOneGroup(): void {
- $this->mandatoryTwoFactor->expects($this->once())
- ->method('setState')
- ->with($this->equalTo(new EnforcementState(true, [], ['yoloers'])));
- $this->mandatoryTwoFactor->expects($this->once())
- ->method('getState')
- ->willReturn(new EnforcementState(true, [], ['yoloers']));
-
- $rc = $this->command->execute([
- '--on' => true,
- '--exclude' => ['yoloers'],
- ]);
-
- $this->assertEquals(0, $rc);
- $display = $this->command->getDisplay();
- $this->assertStringContainsString('Two-factor authentication is enforced for all users, except members of yoloers', $display);
- }
-
- public function testDisableEnforced(): void {
- $this->mandatoryTwoFactor->expects($this->once())
- ->method('setState')
- ->with(new EnforcementState(false));
- $this->mandatoryTwoFactor->expects($this->once())
- ->method('getState')
- ->willReturn(new EnforcementState(false));
-
- $rc = $this->command->execute([
- '--off' => true,
- ]);
-
- $this->assertEquals(0, $rc);
- $display = $this->command->getDisplay();
- $this->assertStringContainsString('Two-factor authentication is not enforced', $display);
- }
-
- public function testCurrentStateEnabled(): void {
- $this->mandatoryTwoFactor->expects($this->once())
- ->method('getState')
- ->willReturn(new EnforcementState(true));
-
- $rc = $this->command->execute([]);
-
- $this->assertEquals(0, $rc);
- $display = $this->command->getDisplay();
- $this->assertStringContainsString('Two-factor authentication is enforced for all users', $display);
- }
-
- public function testCurrentStateDisabled(): void {
- $this->mandatoryTwoFactor->expects($this->once())
- ->method('getState')
- ->willReturn(new EnforcementState(false));
-
- $rc = $this->command->execute([]);
-
- $this->assertEquals(0, $rc);
- $display = $this->command->getDisplay();
- $this->assertStringContainsString('Two-factor authentication is not enforced', $display);
- }
-}
--- a/tests/Core/Command/TwoFactorAuth/StateTest.php
+++ /dev/null
@@ -1,95 +0,0 @@
-registry = $this->createMock(IRegistry::class);
- $this->userManager = $this->createMock(IUserManager::class);
-
- $cmd = new State($this->registry, $this->userManager);
- $this->cmd = new CommandTester($cmd);
- }
-
- public function testWrongUID(): void {
- $this->cmd->execute([
- 'uid' => 'nope',
- ]);
-
- $output = $this->cmd->getDisplay();
- $this->assertStringContainsString('Invalid UID', $output);
- }
-
- public function testStateNoProvidersActive(): void {
- $user = $this->createMock(IUser::class);
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('eldora')
- ->willReturn($user);
- $states = [
- 'u2f' => false,
- 'totp' => false,
- ];
- $this->registry->expects($this->once())
- ->method('getProviderStates')
- ->with($user)
- ->willReturn($states);
-
- $this->cmd->execute([
- 'uid' => 'eldora',
- ]);
-
- $output = $this->cmd->getDisplay();
- $this->assertStringContainsString('Two-factor authentication is not enabled for user eldora', $output);
- }
-
- public function testStateOneProviderActive(): void {
- $user = $this->createMock(IUser::class);
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('mohamed')
- ->willReturn($user);
- $states = [
- 'u2f' => true,
- 'totp' => false,
- ];
- $this->registry->expects($this->once())
- ->method('getProviderStates')
- ->with($user)
- ->willReturn($states);
-
- $this->cmd->execute([
- 'uid' => 'mohamed',
- ]);
-
- $output = $this->cmd->getDisplay();
- $this->assertStringContainsString('Two-factor authentication is enabled for user mohamed', $output);
- }
-}
--- a/tests/Core/Command/User/AddTest.php
+++ /dev/null
@@ -1,157 +0,0 @@
-userManager = static::createMock(IUserManager::class);
- $this->groupManager = static::createStub(IGroupManager::class);
- $this->appConfig = static::createMock(IAppConfig::class);
- $this->mailHelper = static::createMock(NewUserMailHelper::class);
- $this->eventDispatcher = static::createStub(IEventDispatcher::class);
- $this->secureRandom = static::createStub(ISecureRandom::class);
-
- $this->user = static::createMock(IUser::class);
-
- $this->consoleInput = static::createMock(InputInterface::class);
- $this->consoleOutput = static::createMock(OutputInterface::class);
-
- $this->addCommand = new Add(
- $this->userManager,
- $this->groupManager,
- $this->getEmailValidatorWithStrictEmailCheck(),
- $this->appConfig,
- $this->mailHelper,
- $this->eventDispatcher,
- $this->secureRandom
- );
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('addEmailDataProvider')]
- public function testAddEmail(
- ?string $email,
- bool $isEmailValid,
- bool $shouldSendEmail,
- ): void {
- $this->user->expects($isEmailValid ? static::once() : static::never())
- ->method('setSystemEMailAddress')
- ->with(static::equalTo($email));
-
- $this->userManager->method('createUser')
- ->willReturn($this->user);
-
- $this->appConfig->method('getValueBool')
- ->willReturn($shouldSendEmail);
-
- $this->mailHelper->method('generateTemplate')
- ->willReturn(static::createMock(IEMailTemplate::class));
-
- $this->mailHelper->expects($isEmailValid && $shouldSendEmail ? static::once() : static::never())
- ->method('sendMail');
-
- $this->consoleInput->method('getArgument')
- ->willReturnMap([
- ['uid', 'JohnDoe'],
- ]);
-
- $this->consoleInput->method('getOption')
- ->willReturnMap([
- ['generate-password', 'true'],
- ['email', $email],
- ['group', []],
- ]);
-
- $this->invokePrivate($this->addCommand, 'execute', [
- $this->consoleInput,
- $this->consoleOutput
- ]);
- }
-
- /**
- * @return array
- */
- public static function addEmailDataProvider(): array {
- return [
- 'Valid E-Mail' => [
- 'info@example.com',
- true,
- true,
- ],
- 'Invalid E-Mail' => [
- 'info@@example.com',
- false,
- false,
- ],
- 'No E-Mail' => [
- '',
- false,
- false,
- ],
- 'Valid E-Mail, but no mail should be sent' => [
- 'info@example.com',
- true,
- false,
- ],
- ];
- }
-}
--- a/tests/Core/Command/User/AuthTokens/DeleteTest.php
+++ /dev/null
@@ -1,214 +0,0 @@
-tokenProvider = $this->getMockBuilder(IProvider::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->consoleInput = $this->getMockBuilder(InputInterface::class)->getMock();
- $this->consoleOutput = $this->getMockBuilder(OutputInterface::class)->getMock();
-
- /** @var \OC\Authentication\Token\IProvider $tokenProvider */
- $this->command = new Delete($tokenProvider);
- }
-
- /**
- * Default option mapping: --last-used-before unset, --cancel-wipe unset.
- *
- * @param string|null $lastUsedBefore
- * @param bool $cancelWipe
- */
- private function mockOptions(?string $lastUsedBefore = null, bool $cancelWipe = false): void {
- $this->consoleInput->method('getOption')
- ->willReturnMap([
- ['last-used-before', $lastUsedBefore],
- ['cancel-wipe', $cancelWipe],
- ]);
- }
-
- public function testDeleteTokenById(): void {
- $this->consoleInput->expects($this->exactly(2))
- ->method('getArgument')
- ->willReturnMap([
- ['uid', 'user'],
- ['id', '42']
- ]);
-
- $this->mockOptions();
-
- $this->tokenProvider->expects($this->once())
- ->method('invalidateTokenById')
- ->with('user', 42);
-
- $result = self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- $this->assertSame(Command::SUCCESS, $result);
- }
-
- public function testDeleteTokenByIdRequiresTokenId(): void {
- $this->consoleInput->expects($this->exactly(2))
- ->method('getArgument')
- ->willReturnMap([
- ['uid', 'user'],
- ['id', null]
- ]);
-
- $this->mockOptions();
-
- $this->expectException(RuntimeException::class);
-
- $this->tokenProvider->expects($this->never())->method('invalidateTokenById');
-
- $result = self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- $this->assertSame(Command::FAILURE, $result);
- }
-
- public function testDeleteTokensLastUsedBefore(): void {
- $this->consoleInput->expects($this->exactly(2))
- ->method('getArgument')
- ->willReturnMap([
- ['uid', 'user'],
- ['id', null]
- ]);
-
- $this->mockOptions('946684800');
-
- $this->tokenProvider->expects($this->once())
- ->method('invalidateLastUsedBefore')
- ->with('user', 946684800);
-
- $result = self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- $this->assertSame(Command::SUCCESS, $result);
- }
-
- public function testLastUsedBeforeAcceptsIso8601Expanded(): void {
- $this->consoleInput->expects($this->exactly(2))
- ->method('getArgument')
- ->willReturnMap([
- ['uid', 'user'],
- ['id', null]
- ]);
-
- $this->mockOptions('2000-01-01T00:00:00Z');
-
- $this->tokenProvider->expects($this->once())
- ->method('invalidateLastUsedBefore')
- ->with('user', 946684800);
-
- $result = self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- $this->assertSame(Command::SUCCESS, $result);
- }
-
- public function testLastUsedBeforeAcceptsYmd(): void {
- $this->consoleInput->expects($this->exactly(2))
- ->method('getArgument')
- ->willReturnMap([
- ['uid', 'user'],
- ['id', null]
- ]);
-
- $this->mockOptions('2000-01-01');
-
- $this->tokenProvider->expects($this->once())
- ->method('invalidateLastUsedBefore')
- ->with('user', 946684800);
-
- $result = self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- $this->assertSame(Command::SUCCESS, $result);
- }
-
- public function testIdAndLastUsedBeforeAreMutuallyExclusive(): void {
- $this->consoleInput->expects($this->exactly(2))
- ->method('getArgument')
- ->willReturnMap([
- ['uid', 'user'],
- ['id', '42']
- ]);
-
- $this->mockOptions('946684800');
-
- $this->expectException(RuntimeException::class);
-
- $this->tokenProvider->expects($this->never())->method('invalidateLastUsedBefore');
-
- $result = self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- $this->assertSame(Command::SUCCESS, $result);
- }
-
- public function testDeleteByIdRefusesWipePendingWithoutFlag(): void {
- $this->consoleInput->expects($this->exactly(2))
- ->method('getArgument')
- ->willReturnMap([
- ['uid', 'user'],
- ['id', '42']
- ]);
-
- $this->mockOptions();
-
- $wipeToken = $this->createMock(IToken::class);
- $this->tokenProvider->expects($this->once())
- ->method('getTokenById')
- ->with(42)
- ->willThrowException(new WipeTokenException($wipeToken));
-
- $this->tokenProvider->expects($this->never())->method('invalidateTokenById');
-
- $this->consoleOutput->expects($this->once())
- ->method('writeln')
- ->with($this->stringContains('marked for remote wipe'));
-
- $result = self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- $this->assertSame(Command::FAILURE, $result);
- }
-
- public function testDeleteByIdAllowsWipePendingWithFlag(): void {
- $this->consoleInput->expects($this->exactly(2))
- ->method('getArgument')
- ->willReturnMap([
- ['uid', 'user'],
- ['id', '42']
- ]);
-
- $this->mockOptions(null, true);
-
- // With --cancel-wipe, the wipe-state pre-check is skipped entirely
- // (the operator has already opted in), so getTokenById should not run.
- $this->tokenProvider->expects($this->never())->method('getTokenById');
-
- $this->tokenProvider->expects($this->once())
- ->method('invalidateTokenById')
- ->with('user', 42);
-
- $result = self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- $this->assertSame(Command::SUCCESS, $result);
- }
-}
--- a/tests/Core/Command/User/DeleteTest.php
+++ /dev/null
@@ -1,96 +0,0 @@
-userManager = $this->getMockBuilder(IUserManager::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->consoleInput = $this->getMockBuilder(InputInterface::class)->getMock();
- $this->consoleOutput = $this->getMockBuilder(OutputInterface::class)->getMock();
-
- /** @var IUserManager $userManager */
- $this->command = new Delete($userManager);
- }
-
- public static function validUserLastSeen(): array {
- return [
- [true, 'The specified user was deleted'],
- [false, 'The specified user could not be deleted'],
- ];
- }
-
- /**
- *
- * @param bool $deleteSuccess
- * @param string $expectedString
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('validUserLastSeen')]
- public function testValidUser($deleteSuccess, $expectedString): void {
- $user = $this->getMockBuilder(IUser::class)->getMock();
- $user->expects($this->once())
- ->method('delete')
- ->willReturn($deleteSuccess);
-
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('user')
- ->willReturn($user);
-
- $this->consoleInput->expects($this->once())
- ->method('getArgument')
- ->with('uid')
- ->willReturn('user');
-
- $this->consoleOutput->expects($this->once())
- ->method('writeln')
- ->with($this->stringContains($expectedString));
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-
- public function testInvalidUser(): void {
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('user')
- ->willReturn(null);
-
- $this->consoleInput->expects($this->once())
- ->method('getArgument')
- ->with('uid')
- ->willReturn('user');
-
- $this->consoleOutput->expects($this->once())
- ->method('writeln')
- ->with($this->stringContains('User does not exist'));
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-}
--- a/tests/Core/Command/User/DisableTest.php
+++ /dev/null
@@ -1,79 +0,0 @@
-userManager = $this->createMock(IUserManager::class);
- $this->consoleInput = $this->createMock(InputInterface::class);
- $this->consoleOutput = $this->createMock(OutputInterface::class);
-
- $this->command = new Disable($this->userManager);
- }
-
- public function testValidUser(): void {
- $user = $this->createMock(IUser::class);
- $user->expects($this->once())
- ->method('setEnabled')
- ->with(false);
-
- $this->userManager
- ->method('get')
- ->with('user')
- ->willReturn($user);
-
- $this->consoleInput
- ->method('getArgument')
- ->with('uid')
- ->willReturn('user');
-
- $this->consoleOutput->expects($this->once())
- ->method('writeln')
- ->with($this->stringContains('The specified user is disabled'));
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-
- public function testInvalidUser(): void {
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('user')
- ->willReturn(null);
-
- $this->consoleInput
- ->method('getArgument')
- ->with('uid')
- ->willReturn('user');
-
- $this->consoleOutput->expects($this->once())
- ->method('writeln')
- ->with($this->stringContains('User does not exist'));
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-}
--- a/tests/Core/Command/User/EnableTest.php
+++ /dev/null
@@ -1,79 +0,0 @@
-userManager = $this->createMock(IUserManager::class);
- $this->consoleInput = $this->createMock(InputInterface::class);
- $this->consoleOutput = $this->createMock(OutputInterface::class);
-
- $this->command = new Enable($this->userManager);
- }
-
- public function testValidUser(): void {
- $user = $this->createMock(IUser::class);
- $user->expects($this->once())
- ->method('setEnabled')
- ->with(true);
-
- $this->userManager
- ->method('get')
- ->with('user')
- ->willReturn($user);
-
- $this->consoleInput
- ->method('getArgument')
- ->with('uid')
- ->willReturn('user');
-
- $this->consoleOutput->expects($this->once())
- ->method('writeln')
- ->with($this->stringContains('The specified user is enabled'));
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-
- public function testInvalidUser(): void {
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('user')
- ->willReturn(null);
-
- $this->consoleInput
- ->method('getArgument')
- ->with('uid')
- ->willReturn('user');
-
- $this->consoleOutput->expects($this->once())
- ->method('writeln')
- ->with($this->stringContains('User does not exist'));
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-}
--- a/tests/Core/Command/User/LastSeenTest.php
+++ /dev/null
@@ -1,190 +0,0 @@
-userManager = $this->getMockBuilder(IUserManager::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->consoleInput = $this->getMockBuilder(InputInterface::class)->getMock();
- $this->consoleOutput = $this->getMockBuilder(OutputInterface::class)->getMock();
-
- /** @var IUserManager $userManager */
- $this->command = new LastSeen($userManager);
- }
-
- public static function validUserLastSeen(): array {
- return [
- [0, 'never logged in'],
- [time(), 'last login'],
- ];
- }
-
- /**
- *
- * @param int $lastSeen
- * @param string $expectedString
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('validUserLastSeen')]
- public function testValidUser($lastSeen, $expectedString): void {
- $user = $this->getMockBuilder(IUser::class)->getMock();
- $user->expects($this->once())
- ->method('getLastLogin')
- ->willReturn($lastSeen);
-
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('user')
- ->willReturn($user);
-
- $this->consoleInput->expects($this->once())
- ->method('getArgument')
- ->with('uid')
- ->willReturn('user');
-
- $this->consoleOutput->expects($this->once())
- ->method('writeln')
- ->with($this->stringContains($expectedString));
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-
- public function testInvalidUser(): void {
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('user')
- ->willReturn(null);
-
- $this->consoleInput->expects($this->once())
- ->method('getArgument')
- ->with('uid')
- ->willReturn('user');
-
- $this->consoleOutput->expects($this->once())
- ->method('writeln')
- ->with($this->stringContains('User does not exist'));
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-
- public function testAllUsersWithoutExcludeDisabled(): void {
- $enabledUser = $this->getMockBuilder(IUser::class)->getMock();
- $enabledUser->expects($this->once())
- ->method('getLastLogin')
- ->willReturn(time());
- $enabledUser->expects($this->once())
- ->method('getUID')
- ->willReturn('enabled_user');
- $enabledUser->expects($this->never())
- ->method('isEnabled');
-
- $disabledUser = $this->getMockBuilder(IUser::class)->getMock();
- $disabledUser->expects($this->once())
- ->method('getLastLogin')
- ->willReturn(time());
- $disabledUser->expects($this->once())
- ->method('getUID')
- ->willReturn('disabled_user');
- $disabledUser->expects($this->never())
- ->method('isEnabled');
-
- $this->consoleInput->expects($this->once())
- ->method('getArgument')
- ->with('uid')
- ->willReturn(null);
-
- $this->consoleInput->expects($this->exactly(2))
- ->method('getOption')
- ->willReturnMap([
- ['all', true],
- ['exclude-disabled', false],
- ]);
-
- $this->userManager->expects($this->once())
- ->method('callForAllUsers')
- ->willReturnCallback(function ($callback) use ($enabledUser, $disabledUser): void {
- $callback($enabledUser);
- $callback($disabledUser);
- });
-
- $this->consoleOutput->expects($this->exactly(2))
- ->method('writeln')
- ->with($this->stringContains("'s last login:"));
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-
- public function testAllUsersWithExcludeDisabled(): void {
- $enabledUser = $this->getMockBuilder(IUser::class)->getMock();
- $enabledUser->expects($this->once())
- ->method('getLastLogin')
- ->willReturn(time());
- $enabledUser->expects($this->once())
- ->method('getUID')
- ->willReturn('enabled_user');
- $enabledUser->expects($this->once())
- ->method('isEnabled')
- ->willReturn(true);
-
- $disabledUser = $this->getMockBuilder(IUser::class)->getMock();
- $disabledUser->expects($this->never())
- ->method('getLastLogin');
- $disabledUser->expects($this->never())
- ->method('getUID');
- $disabledUser->expects($this->once())
- ->method('isEnabled')
- ->willReturn(false);
-
- $this->consoleInput->expects($this->once())
- ->method('getArgument')
- ->with('uid')
- ->willReturn(null);
-
- $this->consoleInput->expects($this->exactly(2))
- ->method('getOption')
- ->willReturnMap([
- ['all', true],
- ['exclude-disabled', true],
- ]);
-
- $this->userManager->expects($this->once())
- ->method('callForAllUsers')
- ->willReturnCallback(function ($callback) use ($enabledUser, $disabledUser): void {
- $callback($enabledUser);
- $callback($disabledUser);
- });
-
- $this->consoleOutput->expects($this->once())
- ->method('writeln')
- ->with($this->stringContains("enabled_user's last login:"));
-
- self::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
- }
-}
--- a/tests/Core/Command/User/ProfileTest.php
+++ /dev/null
@@ -1,466 +0,0 @@
-accountManager = $this->createMock(IAccountManager::class);
- $this->userManager = $this->createMock(IUserManager::class);
- $this->connection = $this->createMock(IDBConnection::class);
- $this->consoleInput = $this->createMock(InputInterface::class);
- $this->consoleOutput = $this->createMock(OutputInterface::class);
- }
-
- public function getCommand(array $methods = []): Profile|MockObject {
- if (empty($methods)) {
- return new Profile($this->userManager, $this->accountManager);
- } else {
- return $this->getMockBuilder(Profile::class)
- ->setConstructorArgs([
- $this->userManager,
- $this->accountManager,
- ])
- ->onlyMethods($methods)
- ->getMock();
- }
- }
-
- public static function dataCheckInput(): array {
- return [
- 'Call with existing user should pass check' => [
- [['uid', 'username']],
- [],
- [],
- true,
- null,
- ],
- 'Call with non-existing user should fail check' => [
- [['uid', 'username']],
- [],
- [],
- false,
- 'The user "username" does not exist.',
- ],
-
- 'Call with uid, key and --default value should pass check' => [
- [['uid', 'username'], ['key', 'configkey']],
- [],
- [['--default-value', false, true]],
- true,
- null,
- ],
- 'Call with uid and empty key with default-value option should fail check' => [
- [['uid', 'username'], ['key', '']],
- [],
- [['--default-value', false, true]],
- true,
- 'The "default-value" option can only be used when specifying a key.',
- ],
-
- 'Call with uid, key, value should pass check' => [
- [['uid', 'username'], ['key', 'configkey'], ['value', '']],
- [],
- [],
- true,
- null,
- ],
- 'Call with uid, empty key and empty value should fail check' => [
- [['uid', 'username'], ['key', ''], ['value', '']],
- [],
- [],
- true,
- 'The value argument can only be used when specifying a key.',
- ],
- 'Call with uid, key, empty value and default-value option should fail check' => [
- [['uid', 'username'], ['key', 'configkey'], ['value', '']],
- [],
- [['--default-value', false, true]],
- true,
- 'The value argument can not be used together with "default-value".',
- ],
- 'Call with uid, key, empty value and update-only option should pass check' => [
- [['uid', 'username'], ['key', 'configkey'], ['value', '']],
- [['update-only', true]],
- [],
- true,
- null,
- ],
- 'Call with uid, key, null value and update-only option should fail check' => [
- [['uid', 'username'], ['key', 'configkey'], ['value', null]],
- [['update-only', true]],
- [],
- true,
- 'The "update-only" option can only be used together with "value".',
- ],
-
- 'Call with uid, key and delete option should pass check' => [
- [['uid', 'username'], ['key', 'configkey']],
- [['delete', true]],
- [],
- true,
- null,
- ],
- 'Call with uid, empty key and delete option should fail check' => [
- [['uid', 'username'], ['key', '']],
- [['delete', true]],
- [],
- true,
- 'The "delete" option can only be used when specifying a key.',
- ],
- 'Call with uid, key, delete option and default-value should fail check' => [
- [['uid', 'username'], ['key', 'configkey']],
- [['delete', true]],
- [['--default-value', false, true]],
- true,
- 'The "delete" option can not be used together with "default-value".',
- ],
- 'Call with uid, key, empty value and delete option should fail check' => [
- [['uid', 'username'], ['key', 'configkey'], ['value', '']],
- [['delete', true]],
- [],
- true,
- 'The "delete" option can not be used together with "value".',
- ],
- 'Call with uid, key, delete and error-if-not-exists should pass check' => [
- [['uid', 'username'], ['key', 'configkey']],
- [['delete', true], ['error-if-not-exists', true]],
- [],
- true,
- null,
- ],
- 'Call with uid, key and error-if-not-exists should fail check' => [
- [['uid', 'username'], ['key', 'configkey']],
- [['delete', false], ['error-if-not-exists', true]],
- [],
- true,
- 'The "error-if-not-exists" option can only be used together with "delete".',
- ],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataCheckInput')]
- public function testCheckInput(array $arguments, array $options, array $parameterOptions, bool $existingUser, ?string $expectedException): void {
- $this->consoleInput->expects($this->any())
- ->method('getArgument')
- ->willReturnMap($arguments);
- $this->consoleInput->expects($this->any())
- ->method('getOption')
- ->willReturnMap($options);
- $this->consoleInput->expects($this->any())
- ->method('hasParameterOption')
- ->willReturnCallback(function (string|array $values, bool $onlyParams = false) use ($parameterOptions): bool {
- $arguments = func_get_args();
- foreach ($parameterOptions as $parameterOption) {
- // check the arguments of the function, if they are the same, return the mocked value
- if (array_diff($arguments, $parameterOption) === []) {
- return end($parameterOption);
- }
- }
-
- return false;
- });
-
- $returnedUser = null;
- if ($existingUser) {
- $mockUser = $this->createMock(IUser::class);
- $mockUser->expects($this->once())->method('getUID')->willReturn('user');
- $returnedUser = $mockUser;
- }
- $this->userManager->expects($this->once())
- ->method('get')
- ->willReturn($returnedUser);
-
- $command = $this->getCommand();
- try {
- $this->invokePrivate($command, 'checkInput', [$this->consoleInput]);
- $this->assertNull($expectedException);
- } catch (\InvalidArgumentException $e) {
- $this->assertEquals($expectedException, $e->getMessage());
- }
- }
-
- public function testCheckInputExceptionCatch(): void {
- $command = $this->getCommand(['checkInput']);
- $command->expects($this->once())
- ->method('checkInput')
- ->willThrowException(new \InvalidArgumentException('test'));
-
- $this->consoleOutput->expects($this->once())
- ->method('writeln')
- ->with('test ');
-
- $this->assertEquals(1, $this->invokePrivate($command, 'execute', [$this->consoleInput, $this->consoleOutput]));
- }
-
- public static function dataExecuteDeleteProfileProperty(): array {
- return [
- 'Deleting existing property should succeed' => ['address', 'Berlin', false, null, Command::SUCCESS],
- 'Deleting existing property with error-if-not-exists should succeed' => ['address', 'Berlin', true, null, Command::SUCCESS],
- 'Deleting non-existing property should succeed' => ['address', '', false, null, Command::SUCCESS],
- 'Deleting non-existing property with error-if-not-exists should fail' => ['address', '', true, 'The property does not exist for user "username". ', Command::FAILURE],
- ];
- }
-
- /**
- * Tests the deletion mechanism on profile settings.
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataExecuteDeleteProfileProperty')]
- public function testExecuteDeleteProfileProperty(string $configKey, string $value, bool $errorIfNotExists, ?string $expectedLine, int $expectedReturn): void {
- $uid = 'username';
- $appName = 'profile';
- $command = $this->getCommand([
- 'writeArrayInOutputFormat',
- 'checkInput',
- ]);
-
- $this->consoleInput->expects($this->any())
- ->method('getArgument')
- ->willReturnMap([
- ['uid', $uid],
- ['app', $appName],
- ['key', $configKey],
- ]);
-
- $mocks = $this->setupProfilePropertiesMock([$configKey => $value]);
-
- $command->expects($this->once())
- ->method('checkInput')
- ->willReturn($mocks['userMock']);
-
- $this->consoleInput->expects($this->atLeastOnce())
- ->method('hasParameterOption')
- ->willReturnMap([
- ['--delete', false, true],
- ['--error-if-not-exists', false, $errorIfNotExists],
- ]);
-
- if ($expectedLine === null) {
- $this->consoleOutput->expects($this->never())
- ->method('writeln');
- $mocks['profilePropertiesMocks'][0]->expects($this->once())
- ->method('setValue')
- ->with('');
- $this->accountManager->expects($this->once())
- ->method('updateAccount')
- ->with($mocks['accountMock']);
- } else {
- $this->consoleOutput->expects($this->once())
- ->method('writeln')
- ->with($expectedLine);
- $this->accountManager->expects($this->never())
- ->method('updateAccount');
- }
-
- $this->assertEquals($expectedReturn, $this->invokePrivate($command, 'execute', [$this->consoleInput, $this->consoleOutput]));
- }
-
- public function testExecuteSetProfileProperty(): void {
- $command = $this->getCommand([
- 'writeArrayInOutputFormat',
- 'checkInput',
- ]);
-
- $uid = 'username';
- $propertyKey = 'address';
- $propertyValue = 'Barcelona';
-
- $this->consoleInput->expects($this->atLeast(3))
- ->method('getArgument')
- ->willReturnMap([
- ['uid', $uid],
- ['key', $propertyKey],
- ['value', $propertyValue],
- ]);
-
- $mocks = $this->setupProfilePropertiesMock([$propertyKey => $propertyValue]);
-
- $command->expects($this->once())
- ->method('checkInput')
- ->willReturn($mocks['userMock']);
-
- $mocks['profilePropertiesMocks'][0]->expects($this->once())
- ->method('setValue')
- ->with($propertyValue);
- $this->accountManager->expects($this->once())
- ->method('updateAccount')
- ->with($mocks['accountMock']);
-
- $this->assertEquals(0, $this->invokePrivate($command, 'execute', [$this->consoleInput, $this->consoleOutput]));
- }
-
- public static function dataExecuteGet(): array {
- return [
- 'Get property with set value should pass' => ['configkey', 'value', null, 'value', Command::SUCCESS],
- 'Get property with empty value and default-value option should pass' => ['configkey', '', 'default-value', 'default-value', Command::SUCCESS],
- 'Get property with empty value should fail' => ['configkey', '', null, 'The property does not exist for user "username". ', Command::FAILURE],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataExecuteGet')]
- public function testExecuteGet(string $key, string $value, ?string $defaultValue, string $expectedLine, int $expectedReturn): void {
- $command = $this->getCommand([
- 'writeArrayInOutputFormat',
- 'checkInput',
- ]);
-
- $uid = 'username';
-
- $this->consoleInput->expects($this->any())
- ->method('getArgument')
- ->willReturnMap([
- ['uid', $uid],
- ['key', $key],
- ]);
-
- $mocks = $this->setupProfilePropertiesMock([$key => $value]);
-
- $command->expects($this->once())
- ->method('checkInput')
- ->willReturn($mocks['userMock']);
-
- if ($value === '') {
- if ($defaultValue === null) {
- $this->consoleInput->expects($this->atLeastOnce())
- ->method('hasParameterOption')
- ->willReturn(false);
- } else {
- $this->consoleInput->expects($this->atLeastOnce())
- ->method('hasParameterOption')
- ->willReturnCallback(fn (string|array $values): bool => $values === '--default-value');
- $this->consoleInput->expects($this->once())
- ->method('getOption')
- ->with('default-value')
- ->willReturn($defaultValue);
- }
- }
-
- $this->consoleOutput->expects($this->once())
- ->method('writeln')
- ->with($expectedLine);
-
- $this->assertEquals($expectedReturn, $this->invokePrivate($command, 'execute', [$this->consoleInput, $this->consoleOutput]));
- }
-
- public function testExecuteList(): void {
- $uid = 'username';
- $profileData = [
- 'pronouns' => 'they/them',
- 'address' => 'Berlin',
- ];
-
- $command = $this->getCommand([
- 'writeArrayInOutputFormat',
- 'checkInput',
- ]);
-
- $this->consoleInput->expects($this->any())
- ->method('getArgument')
- ->willReturnMap([
- ['uid', $uid],
- ['key', ''],
- ]);
-
- $mocks = $this->setupProfilePropertiesMock(['address' => $profileData['address'], 'pronouns' => $profileData['pronouns']]);
-
- $command->expects($this->once())
- ->method('checkInput')
- ->willReturn($mocks['userMock']);
-
- $command->expects($this->once())
- ->method('writeArrayInOutputFormat')
- ->with($this->consoleInput, $this->consoleOutput, $profileData);
-
- $this->assertEquals(0, $this->invokePrivate($command, 'execute', [$this->consoleInput, $this->consoleOutput]));
- }
-
- /**
- * Helper to avoid boilerplate in tests in this file when mocking objects
- * of IAccountProperty type.
- *
- * @param array $properties the properties to be set up as key => value
- * @return array{
- * userMock: IUser&MockObject,
- * accountMock: IAccount&MockObject,
- * profilePropertiesMocks: IAccountProperty&MockObject[]
- * }
- */
- private function setupProfilePropertiesMock(array $properties): array {
- $userMock = $this->createMock(IUser::class);
- $accountMock = $this->createMock(IAccount::class);
- $this->accountManager->expects($this->atLeastOnce())
- ->method('getAccount')
- ->with($userMock)
- ->willReturn($accountMock);
-
- /** @var IAccountProperty&MockObject[] $propertiesMocks */
- $propertiesMocks = [];
- foreach ($properties as $key => $value) {
- $propertiesMocks[] = $this->getAccountPropertyMock($key, $value);
- }
-
- if (count($properties) === 1) {
- $accountMock->expects($this->atLeastOnce())
- ->method('getProperty')
- ->with(array_keys($properties)[0])
- ->willReturn($propertiesMocks[array_key_first($propertiesMocks)]);
- } else {
- $accountMock->expects($this->atLeastOnce())
- ->method('getAllProperties')
- ->willReturnCallback(function () use ($propertiesMocks) {
- foreach ($propertiesMocks as $property) {
- yield $property;
- }
- });
- }
-
- return [
- 'userMock' => $userMock,
- 'accountMock' => $accountMock,
- 'profilePropertiesMocks' => $propertiesMocks,
- ];
- }
-
- private function getAccountPropertyMock(string $name, string $value): IAccountProperty&MockObject {
- $propertyMock = $this->getMockBuilder(IAccountProperty::class)
- ->disableOriginalConstructor()
- ->getMock();
- $propertyMock->expects($this->any())
- ->method('getName')
- ->willReturn($name);
- $propertyMock->expects($this->any())
- ->method('getValue')
- ->willReturn($value);
-
- return $propertyMock;
- }
-}
--- a/tests/Core/Command/User/ResetPasswordTest.php
+++ /dev/null
@@ -1,117 +0,0 @@
-createMock(IUser::class);
- $user->expects(self::once())
- ->method('setPassword')
- ->with('NewPassword')
- ->willReturnCallback(static function () use (&$sequence, $passwordChangeSucceeds): bool {
- $sequence[] = 'setPassword';
- return $passwordChangeSucceeds;
- });
-
- $userManager = $this->createMock(IUserManager::class);
- $userManager->expects(self::once())
- ->method('get')
- ->with('alice')
- ->willReturn($user);
-
- $eventDispatcher = $this->createMock(IEventDispatcher::class);
- $eventDispatcher->expects(self::exactly(count($expectedSequence) - 1))
- ->method('dispatchTyped')
- ->willReturnCallback(static function (object $event) use (&$sequence, $user): void {
- if ($event instanceof BeforePasswordResetEvent) {
- self::assertSame($user, $event->getUser());
- self::assertSame('NewPassword', $event->getPassword());
- $sequence[] = 'beforeReset';
- return;
- }
-
- self::assertInstanceOf(PasswordResetEvent::class, $event);
- self::assertSame($user, $event->getUser());
- self::assertSame('NewPassword', $event->getPassword());
- $sequence[] = 'afterReset';
- });
-
- $input = $this->createMock(InputInterface::class);
- $input->method('getArgument')
- ->with('user')
- ->willReturn('alice');
- $input->method('getOption')
- ->with('password-from-env')
- ->willReturn(true);
-
- $output = $this->createMock(OutputInterface::class);
- $output->expects(self::once())
- ->method('writeln')
- ->with($expectedOutput);
-
- $command = new ResetPassword(
- $userManager,
- $this->createStub(IAppManager::class),
- $eventDispatcher,
- );
-
- $previousPassword = getenv('NC_PASS');
- putenv('NC_PASS=NewPassword');
-
- try {
- $exitCode = self::invokePrivate($command, 'execute', [$input, $output]);
- } finally {
- if ($previousPassword === false) {
- putenv('NC_PASS');
- } else {
- putenv('NC_PASS=' . $previousPassword);
- }
- }
-
- self::assertSame($expectedExitCode, $exitCode);
- self::assertSame($expectedSequence, $sequence);
- }
-
- public static function passwordResetResults(): array {
- return [
- 'success dispatches both reset events around the password change' => [
- true,
- 0,
- ['beforeReset', 'setPassword', 'afterReset'],
- 'Successfully reset password for alice ',
- ],
- 'failure does not dispatch the after-reset event' => [
- false,
- 1,
- ['beforeReset', 'setPassword'],
- 'Error while resetting password! ',
- ],
- ];
- }
-}
--- a/tests/Core/Command/User/SettingTest.php
+++ /dev/null
@@ -1,452 +0,0 @@
-userManager = $this->createMock(IUserManager::class);
- $this->config = $this->createMock(IConfig::class);
- $this->connection = $this->createMock(IDBConnection::class);
- $this->consoleInput = $this->createMock(InputInterface::class);
- $this->consoleOutput = $this->createMock(OutputInterface::class);
- }
-
- public function getCommand(array $methods = []) {
- if (empty($methods)) {
- return new Setting($this->userManager, $this->config);
- } else {
- $mock = $this->getMockBuilder(Setting::class)
- ->setConstructorArgs([
- $this->userManager,
- $this->config,
- ])
- ->onlyMethods($methods)
- ->getMock();
- return $mock;
- }
- }
-
- public static function dataCheckInput(): array {
- return [
- [
- [['uid', 'username']],
- [['ignore-missing-user', true]],
- [],
- false,
- false,
- ],
- [
- [['uid', 'username']],
- [['ignore-missing-user', false]],
- [],
- null,
- 'The user "username" does not exist.',
- ],
-
- [
- [['uid', 'username'], ['key', 'configkey']],
- [['ignore-missing-user', true]],
- [['--default-value', false, true]],
- false,
- false,
- ],
- [
- [['uid', 'username'], ['key', '']],
- [['ignore-missing-user', true]],
- [['--default-value', false, true]],
- false,
- 'The "default-value" option can only be used when specifying a key.',
- ],
-
- [
- [['uid', 'username'], ['key', 'configkey'], ['value', '']],
- [['ignore-missing-user', true]],
- [],
- false,
- false,
- ],
- [
- [['uid', 'username'], ['key', ''], ['value', '']],
- [['ignore-missing-user', true]],
- [],
- false,
- 'The value argument can only be used when specifying a key.',
- ],
- [
- [['uid', 'username'], ['key', 'configkey'], ['value', '']],
- [['ignore-missing-user', true]],
- [['--default-value', false, true]],
- false,
- 'The value argument can not be used together with "default-value".',
- ],
- [
- [['uid', 'username'], ['key', 'configkey'], ['value', '']],
- [['ignore-missing-user', true], ['update-only', true]],
- [],
- false,
- false,
- ],
- [
- [['uid', 'username'], ['key', 'configkey'], ['value', null]],
- [['ignore-missing-user', true], ['update-only', true]],
- [],
- false,
- 'The "update-only" option can only be used together with "value".',
- ],
-
- [
- [['uid', 'username'], ['key', 'configkey']],
- [['ignore-missing-user', true], ['delete', true]],
- [],
- false,
- false,
- ],
- [
- [['uid', 'username'], ['key', '']],
- [['ignore-missing-user', true], ['delete', true]],
- [],
- false,
- 'The "delete" option can only be used when specifying a key.',
- ],
- [
- [['uid', 'username'], ['key', 'configkey']],
- [['ignore-missing-user', true], ['delete', true]],
- [['--default-value', false, true]],
- false,
- 'The "delete" option can not be used together with "default-value".',
- ],
- [
- [['uid', 'username'], ['key', 'configkey'], ['value', '']],
- [['ignore-missing-user', true], ['delete', true]],
- [],
- false,
- 'The "delete" option can not be used together with "value".',
- ],
- [
- [['uid', 'username'], ['key', 'configkey']],
- [['ignore-missing-user', true], ['delete', true], ['error-if-not-exists', true]],
- [],
- false,
- false,
- ],
- [
- [['uid', 'username'], ['key', 'configkey']],
- [['ignore-missing-user', true], ['delete', false], ['error-if-not-exists', true]],
- [],
- false,
- 'The "error-if-not-exists" option can only be used together with "delete".',
- ],
- ];
- }
-
- /**
- *
- * @param array $arguments
- * @param array $options
- * @param array $parameterOptions
- * @param mixed $user
- * @param string $expectedException
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataCheckInput')]
- public function testCheckInput($arguments, $options, $parameterOptions, $user, $expectedException): void {
- $this->consoleInput->expects($this->any())
- ->method('getArgument')
- ->willReturnMap($arguments);
- $this->consoleInput->expects($this->any())
- ->method('getOption')
- ->willReturnMap($options);
- $this->consoleInput->expects($this->any())
- ->method('hasParameterOption')
- ->willReturnCallback(function (string|array $config, bool $default = false) use ($parameterOptions): bool {
- foreach ($parameterOptions as $parameterOption) {
- if ($config === $parameterOption[0]
- // Check the default value if the maps has 3 entries
- && (!isset($parameterOption[2]) || $default === $parameterOption[1])) {
- return end($parameterOption);
- }
- }
- return false;
- });
-
- if ($user !== false) {
- $this->userManager->expects($this->once())
- ->method('get')
- ->willReturn($user);
- } else {
- $this->userManager->expects($this->never())
- ->method('get');
- }
-
- $command = $this->getCommand();
- try {
- $this->invokePrivate($command, 'checkInput', [$this->consoleInput]);
- $this->assertFalse($expectedException);
- } catch (InvalidArgumentException $e) {
- $this->assertEquals($expectedException, $e->getMessage());
- }
- }
-
- public function testCheckInputExceptionCatch(): void {
- $command = $this->getCommand(['checkInput']);
- $command->expects($this->once())
- ->method('checkInput')
- ->willThrowException(new InvalidArgumentException('test'));
-
- $this->consoleOutput->expects($this->once())
- ->method('writeln')
- ->with('test ');
-
- $this->assertEquals(1, $this->invokePrivate($command, 'execute', [$this->consoleInput, $this->consoleOutput]));
- }
-
- public static function dataExecuteDelete(): array {
- return [
- ['config', false, null, 0],
- ['config', true, null, 0],
- [null, false, null, 0],
- [null, true, 'The setting does not exist for user "username". ', 1],
- ];
- }
-
- /**
- *
- * @param string|null $value
- * @param bool $errorIfNotExists
- * @param string $expectedLine
- * @param int $expectedReturn
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataExecuteDelete')]
- public function testExecuteDelete($value, $errorIfNotExists, $expectedLine, $expectedReturn): void {
- $command = $this->getCommand([
- 'writeArrayInOutputFormat',
- 'checkInput',
- 'getUserSettings',
- ]);
-
- $this->consoleInput->expects($this->any())
- ->method('getArgument')
- ->willReturnMap([
- ['uid', 'username'],
- ['app', 'appname'],
- ['key', 'configkey'],
- ]);
-
- $command->expects($this->once())
- ->method('checkInput');
-
- $this->config->expects($this->once())
- ->method('getUserValue')
- ->with('username', 'appname', 'configkey', null)
- ->willReturn($value);
-
- $this->consoleInput->expects($this->atLeastOnce())
- ->method('hasParameterOption')
- ->willReturnMap([
- ['--delete', false, true],
- ['--error-if-not-exists', false, $errorIfNotExists],
- ]);
-
- if ($expectedLine === null) {
- $this->consoleOutput->expects($this->never())
- ->method('writeln');
- $this->config->expects($this->once())
- ->method('deleteUserValue')
- ->with('username', 'appname', 'configkey');
- } else {
- $this->consoleOutput->expects($this->once())
- ->method('writeln')
- ->with($expectedLine);
- $this->config->expects($this->never())
- ->method('deleteUserValue');
- }
-
- $this->assertEquals($expectedReturn, $this->invokePrivate($command, 'execute', [$this->consoleInput, $this->consoleOutput]));
- }
-
- public static function dataExecuteSet(): array {
- return [
- ['config', false, null, 0],
- ['config', true, null, 0],
- [null, false, null, 0],
- [null, true, 'The setting does not exist for user "username". ', 1],
- ];
- }
-
- /**
- *
- * @param string|null $value
- * @param bool $updateOnly
- * @param string $expectedLine
- * @param int $expectedReturn
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataExecuteSet')]
- public function testExecuteSet($value, $updateOnly, $expectedLine, $expectedReturn): void {
- $command = $this->getCommand([
- 'writeArrayInOutputFormat',
- 'checkInput',
- 'getUserSettings',
- ]);
-
- $this->consoleInput->expects($this->atLeast(4))
- ->method('getArgument')
- ->willReturnMap([
- ['uid', 'username'],
- ['app', 'appname'],
- ['key', 'configkey'],
- ['value', 'setValue'],
- ]);
-
- $command->expects($this->once())
- ->method('checkInput');
-
- $this->config->expects($this->once())
- ->method('getUserValue')
- ->with('username', 'appname', 'configkey', null)
- ->willReturn($value);
-
- $this->consoleInput->expects($this->atLeastOnce())
- ->method('hasParameterOption')
- ->willReturnMap([
- ['--update-only', false, $updateOnly],
- ]);
-
- if ($expectedLine === null) {
- $this->consoleOutput->expects($this->never())
- ->method('writeln');
-
- $this->consoleInput->expects($this->never())
- ->method('getOption');
-
- $this->config->expects($this->once())
- ->method('setUserValue')
- ->with('username', 'appname', 'configkey', 'setValue');
- } else {
- $this->consoleOutput->expects($this->once())
- ->method('writeln')
- ->with($expectedLine);
- $this->config->expects($this->never())
- ->method('setUserValue');
- }
-
- $this->assertEquals($expectedReturn, $this->invokePrivate($command, 'execute', [$this->consoleInput, $this->consoleOutput]));
- }
-
- public static function dataExecuteGet(): array {
- return [
- ['config', null, 'config', 0],
- [null, 'config', 'config', 0],
- [null, null, 'The setting does not exist for user "username". ', 1],
- ];
- }
-
- /**
- *
- * @param string|null $value
- * @param string|null $defaultValue
- * @param string $expectedLine
- * @param int $expectedReturn
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataExecuteGet')]
- public function testExecuteGet($value, $defaultValue, $expectedLine, $expectedReturn): void {
- $command = $this->getCommand([
- 'writeArrayInOutputFormat',
- 'checkInput',
- 'getUserSettings',
- ]);
-
- $this->consoleInput->expects($this->any())
- ->method('getArgument')
- ->willReturnMap([
- ['uid', 'username'],
- ['app', 'appname'],
- ['key', 'configkey'],
- ]);
-
- $command->expects($this->once())
- ->method('checkInput');
-
- $this->config->expects($this->once())
- ->method('getUserValue')
- ->with('username', 'appname', 'configkey', null)
- ->willReturn($value);
-
- if ($value === null) {
- if ($defaultValue === null) {
- $this->consoleInput->expects($this->atLeastOnce())
- ->method('hasParameterOption')
- ->willReturn(false);
- } else {
- $this->consoleInput->expects($this->atLeastOnce())
- ->method('hasParameterOption')
- ->willReturnCallback(function (string|array $config, bool $default = false): bool {
- if ($config === '--default-value' && $default === false) {
- return true;
- }
- return false;
- });
- $this->consoleInput->expects($this->once())
- ->method('getOption')
- ->with('default-value')
- ->willReturn($defaultValue);
- }
- }
-
- $this->consoleOutput->expects($this->once())
- ->method('writeln')
- ->with($expectedLine);
-
- $this->assertEquals($expectedReturn, $this->invokePrivate($command, 'execute', [$this->consoleInput, $this->consoleOutput]));
- }
-
- public function testExecuteList(): void {
- $command = $this->getCommand([
- 'writeArrayInOutputFormat',
- 'checkInput',
- 'getUserSettings',
- ]);
-
- $this->consoleInput->expects($this->any())
- ->method('getArgument')
- ->willReturnMap([
- ['uid', 'username'],
- ['app', 'appname'],
- ['key', ''],
- ]);
-
- $command->expects($this->once())
- ->method('checkInput');
- $command->expects($this->once())
- ->method('getUserSettings')
- ->willReturn(['settings']);
- $command->expects($this->once())
- ->method('writeArrayInOutputFormat')
- ->with($this->consoleInput, $this->consoleOutput, ['settings']);
-
- $this->assertEquals(0, $this->invokePrivate($command, 'execute', [$this->consoleInput, $this->consoleOutput]));
- }
-}
--- a/tests/Core/Controller/AppPasswordControllerTest.php
+++ /dev/null
@@ -1,296 +0,0 @@
-session = $this->createMock(ISession::class);
- $this->random = $this->createMock(ISecureRandom::class);
- $this->tokenProvider = $this->createMock(IProvider::class);
- $this->credentialStore = $this->createMock(IStore::class);
- $this->request = $this->createMock(IRequest::class);
- $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
- $this->userSession = $this->createMock(Session::class);
- $this->userManager = $this->createMock(IUserManager::class);
- $this->throttler = $this->createMock(IThrottler::class);
- $this->serverConfig = $this->createMock(IConfig::class);
-
- $this->controller = new AppPasswordController(
- 'core',
- $this->request,
- $this->session,
- $this->random,
- $this->tokenProvider,
- $this->credentialStore,
- $this->eventDispatcher,
- $this->userSession,
- $this->userManager,
- $this->throttler,
- $this->serverConfig,
- );
- }
-
- public function testGetAppPasswordWithAppPassword(): void {
- $this->serverConfig->method('getSystemValueBool')
- ->with('auth_can_create_app_token', true)
- ->willReturn(true);
- $this->session->method('exists')
- ->with('app_password')
- ->willReturn(true);
-
- $this->tokenProvider->expects($this->never())
- ->method('generateToken');
-
- $this->eventDispatcher->expects($this->never())
- ->method('dispatchTyped');
-
- $this->expectException(OCSForbiddenException::class);
-
- $this->controller->getAppPassword();
- }
-
- public function testGetAppPasswordNoLoginCreds(): void {
- $this->serverConfig->method('getSystemValueBool')
- ->with('auth_can_create_app_token', true)
- ->willReturn(true);
- $this->session->method('exists')
- ->with('app_password')
- ->willReturn(false);
- $this->credentialStore->method('getLoginCredentials')
- ->willThrowException(new CredentialsUnavailableException());
-
- $this->tokenProvider->expects($this->never())
- ->method('generateToken');
-
- $this->eventDispatcher->expects($this->never())
- ->method('dispatchTyped');
-
- $this->expectException(OCSForbiddenException::class);
-
- $this->controller->getAppPassword();
- }
-
- public function testGetAppPassword(): void {
- $this->serverConfig->method('getSystemValueBool')
- ->with('auth_can_create_app_token', true)
- ->willReturn(true);
-
- $credentials = $this->createMock(ICredentials::class);
-
- $this->session->method('exists')
- ->with('app_password')
- ->willReturn(false);
- $this->credentialStore->method('getLoginCredentials')
- ->willReturn($credentials);
- $credentials->method('getUid')
- ->willReturn('myUID');
- $credentials->method('getPassword')
- ->willReturn('myPassword');
- $credentials->method('getLoginName')
- ->willReturn('myLoginName');
- $this->request->method('getHeader')
- ->with('user-agent')
- ->willReturn('myUA');
- $this->random->method('generate')
- ->with(
- 72,
- ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS
- )->willReturn('myToken');
-
- $this->tokenProvider->expects($this->once())
- ->method('generateToken')
- ->with(
- 'myToken',
- 'myUID',
- 'myLoginName',
- 'myPassword',
- 'myUA',
- IToken::PERMANENT_TOKEN,
- IToken::DO_NOT_REMEMBER
- );
-
- $this->eventDispatcher->expects($this->once())
- ->method('dispatchTyped');
-
- $this->controller->getAppPassword();
- }
-
- public function testGetAppPasswordNoPassword(): void {
- $this->serverConfig->method('getSystemValueBool')
- ->with('auth_can_create_app_token', true)
- ->willReturn(true);
-
- $credentials = $this->createMock(ICredentials::class);
-
- $this->session->method('exists')
- ->with('app_password')
- ->willReturn(false);
- $this->credentialStore->method('getLoginCredentials')
- ->willReturn($credentials);
- $credentials->method('getUid')
- ->willReturn('myUID');
- $credentials->method('getPassword')
- ->willThrowException(new PasswordUnavailableException());
- $credentials->method('getLoginName')
- ->willReturn('myLoginName');
- $this->request->method('getHeader')
- ->with('user-agent')
- ->willReturn('myUA');
- $this->random->method('generate')
- ->with(
- 72,
- ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS
- )->willReturn('myToken');
-
- $this->tokenProvider->expects($this->once())
- ->method('generateToken')
- ->with(
- 'myToken',
- 'myUID',
- 'myLoginName',
- null,
- 'myUA',
- IToken::PERMANENT_TOKEN,
- IToken::DO_NOT_REMEMBER
- );
-
- $this->eventDispatcher->expects($this->once())
- ->method('dispatchTyped');
-
- $this->controller->getAppPassword();
- }
-
- public function testGetAppPasswordDisabledBySystemConfig(): void {
- $this->serverConfig->method('getSystemValueBool')
- ->with('auth_can_create_app_token', true)
- ->willReturn(false);
-
- $this->tokenProvider->expects($this->never())
- ->method('generateToken');
-
- $this->eventDispatcher->expects($this->never())
- ->method('dispatchTyped');
-
- $this->expectException(OCSForbiddenException::class);
-
- $this->controller->getAppPassword();
- }
-
- public function testDeleteAppPasswordNoAppPassword(): void {
- $this->session->method('exists')
- ->with('app_password')
- ->willReturn(false);
-
- $this->expectException(OCSForbiddenException::class);
-
- $this->controller->deleteAppPassword();
- }
-
- public function testDeleteAppPasswordFails(): void {
- $this->session->method('exists')
- ->with('app_password')
- ->willReturn(true);
- $this->session->method('get')
- ->with('app_password')
- ->willReturn('myAppPassword');
-
- $this->tokenProvider->method('getToken')
- ->with('myAppPassword')
- ->willThrowException(new InvalidTokenException());
-
- $this->expectException(OCSForbiddenException::class);
-
- $this->controller->deleteAppPassword();
- }
-
- public function testDeleteAppPasswordSuccess(): void {
- $this->session->method('exists')
- ->with('app_password')
- ->willReturn(true);
- $this->session->method('get')
- ->with('app_password')
- ->willReturn('myAppPassword');
-
- $token = $this->createMock(IToken::class);
- $this->tokenProvider->method('getToken')
- ->with('myAppPassword')
- ->willReturn($token);
-
- $token->method('getUID')
- ->willReturn('myUID');
- $token->method('getId')
- ->willReturn(42);
-
- $this->tokenProvider->expects($this->once())
- ->method('invalidateTokenById')
- ->with(
- 'myUID',
- 42
- );
-
- $result = $this->controller->deleteAppPassword();
-
- $this->assertEquals(new DataResponse(), $result);
- }
-}
--- a/tests/Core/Controller/AutoCompleteControllerTest.php
+++ /dev/null
@@ -1,169 +0,0 @@
-createMock(IRequest::class);
- $this->collaboratorSearch = $this->createMock(ISearch::class);
- $this->autoCompleteManager = $this->createMock(IManager::class);
-
- $this->controller = new AutoCompleteController(
- 'core',
- $request,
- $this->collaboratorSearch,
- $this->autoCompleteManager,
- );
- }
-
- public static function searchDataProvider(): array {
- return [
- [ #0 – regular search
- // searchResults
- [
- 'exact' => [
- 'users' => [],
- 'robots' => [],
- ],
- 'users' => [
- ['label' => 'Alice A.', 'value' => ['shareWith' => 'alice']],
- ['label' => 'Bob Y.', 'value' => ['shareWith' => 'bob']],
- ],
- ],
- // expected
- [
- [ 'id' => 'alice', 'label' => 'Alice A.', 'icon' => '', 'source' => 'users', 'status' => '', 'subline' => '', 'shareWithDisplayNameUnique' => ''],
- [ 'id' => 'bob', 'label' => 'Bob Y.', 'icon' => '', 'source' => 'users', 'status' => '', 'subline' => '', 'shareWithDisplayNameUnique' => ''],
- ],
- '',
- 'files',
- '42',
- null
- ],
- [ #1 – missing itemtype and id
- [
- 'exact' => [
- 'users' => [],
- 'robots' => [],
- ],
- 'users' => [
- ['label' => 'Alice A.', 'value' => ['shareWith' => 'alice']],
- ['label' => 'Bob Y.', 'value' => ['shareWith' => 'bob']],
- ],
- ],
- // expected
- [
- [ 'id' => 'alice', 'label' => 'Alice A.', 'icon' => '', 'source' => 'users', 'status' => '', 'subline' => '', 'shareWithDisplayNameUnique' => ''],
- [ 'id' => 'bob', 'label' => 'Bob Y.', 'icon' => '', 'source' => 'users', 'status' => '', 'subline' => '', 'shareWithDisplayNameUnique' => ''],
- ],
- '',
- null,
- null,
- null
- ],
- [ #2 – with sorter
- [
- 'exact' => [
- 'users' => [],
- 'robots' => [],
- ],
- 'users' => [
- ['label' => 'Alice A.', 'value' => ['shareWith' => 'alice']],
- ['label' => 'Bob Y.', 'value' => ['shareWith' => 'bob']],
- ],
- ],
- // expected
- [
- [ 'id' => 'alice', 'label' => 'Alice A.', 'icon' => '', 'source' => 'users', 'status' => '', 'subline' => '', 'shareWithDisplayNameUnique' => ''],
- [ 'id' => 'bob', 'label' => 'Bob Y.', 'icon' => '', 'source' => 'users', 'status' => '', 'subline' => '', 'shareWithDisplayNameUnique' => ''],
- ],
- '',
- 'files',
- '42',
- 'karma|bus-factor'
- ],
- [ #3 – exact Match
- [
- 'exact' => [
- 'users' => [
- ['label' => 'Bob Y.', 'value' => ['shareWith' => 'bob']],
- ],
- 'robots' => [],
- ],
- 'users' => [
- ['label' => 'Robert R.', 'value' => ['shareWith' => 'bobby']],
- ],
- ],
- [
- [ 'id' => 'bob', 'label' => 'Bob Y.', 'icon' => '', 'source' => 'users', 'status' => '', 'subline' => '', 'shareWithDisplayNameUnique' => ''],
- [ 'id' => 'bobby', 'label' => 'Robert R.', 'icon' => '', 'source' => 'users', 'status' => '', 'subline' => '', 'shareWithDisplayNameUnique' => ''],
- ],
- 'bob',
- 'files',
- '42',
- null
- ],
- [ #4 – with unique name
- [
- 'exact' => [
- 'users' => [],
- 'robots' => [],
- ],
- 'users' => [
- ['label' => 'Alice A.', 'value' => ['shareWith' => 'alice'], 'shareWithDisplayNameUnique' => 'alica@nextcloud.com'],
- ['label' => 'Alice A.', 'value' => ['shareWith' => 'alicea'], 'shareWithDisplayNameUnique' => 'alicaa@nextcloud.com'],
- ],
- ],
- // expected
- [
- [ 'id' => 'alice', 'label' => 'Alice A.', 'icon' => '', 'source' => 'users', 'status' => '', 'subline' => '', 'shareWithDisplayNameUnique' => 'alica@nextcloud.com'],
- [ 'id' => 'alicea', 'label' => 'Alice A.', 'icon' => '', 'source' => 'users', 'status' => '', 'subline' => '', 'shareWithDisplayNameUnique' => 'alicaa@nextcloud.com'],
- ],
- '',
- 'files',
- '42',
- 'karma|bus-factor'
- ],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('searchDataProvider')]
- public function testGet(array $searchResults, array $expected, string $searchTerm, ?string $itemType, ?string $itemId, ?string $sorter): void {
- $this->collaboratorSearch->expects($this->once())
- ->method('filteredSearch')
- ->willReturn([$searchResults, false]);
-
- $runSorterFrequency = $sorter === null ? $this->never() : $this->once();
- $this->autoCompleteManager->expects($runSorterFrequency)
- ->method('runSorters');
-
- $response = $this->controller->get($searchTerm, $itemType, $itemId, $sorter);
-
- $list = $response->getData();
- $this->assertEquals($expected, $list); // has better error output…
- $this->assertSame($expected, $list);
- }
-}
--- a/tests/Core/Controller/AvatarControllerTest.php
+++ /dev/null
@@ -1,450 +0,0 @@
-avatarManager = $this->getMockBuilder('OCP\IAvatarManager')->getMock();
- $this->l = $this->getMockBuilder(IL10N::class)->getMock();
- $this->l->method('t')->willReturnArgument(0);
- $this->userManager = $this->getMockBuilder(IUserManager::class)->getMock();
- $this->request = $this->getMockBuilder(IRequest::class)->getMock();
- $this->rootFolder = $this->getMockBuilder('OCP\Files\IRootFolder')->getMock();
- $this->logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
- $this->timeFactory = $this->getMockBuilder('OC\AppFramework\Utility\TimeFactory')->getMock();
-
- $this->avatarMock = $this->getMockBuilder('OCP\IAvatar')->getMock();
- $this->userMock = $this->getMockBuilder(IUser::class)->getMock();
-
- $this->guestAvatarController = new GuestAvatarController(
- 'core',
- $this->request,
- $this->avatarManager,
- $this->logger
- );
-
- $this->avatarController = new AvatarController(
- 'core',
- $this->request,
- $this->avatarManager,
- $this->l,
- $this->userManager,
- $this->rootFolder,
- $this->logger,
- 'userid',
- $this->timeFactory,
- $this->guestAvatarController,
- );
-
- // Configure userMock
- $this->userMock->method('getDisplayName')->willReturn('displayName');
- $this->userMock->method('getUID')->willReturn('userId');
- $this->userManager->method('get')
- ->willReturnMap([['userId', $this->userMock]]);
-
- $this->avatarFile = $this->getMockBuilder(ISimpleFile::class)->getMock();
- $this->avatarFile->method('getContent')->willReturn('image data');
- $this->avatarFile->method('getMimeType')->willReturn('image type');
- $this->avatarFile->method('getEtag')->willReturn('my etag');
- $this->avatarFile->method('getName')->willReturn('my name');
- $this->avatarFile->method('getMTime')->willReturn(42);
- }
-
- #[\Override]
- protected function tearDown(): void {
- parent::tearDown();
- }
-
- /**
- * Fetch an avatar if a user has no avatar
- */
- public function testGetAvatarNoAvatar(): void {
- $this->avatarManager->method('getAvatar')->willReturn($this->avatarMock);
- $this->avatarMock->method('getFile')->willThrowException(new NotFoundException());
- $response = $this->avatarController->getAvatar('userId', 32);
-
- //Comment out until JS is fixed
- $this->assertEquals(Http::STATUS_NOT_FOUND, $response->getStatus());
- }
-
- /**
- * Fetch the user's avatar
- */
- public function testGetAvatar(): void {
- $this->avatarMock->method('getFile')->willReturn($this->avatarFile);
- $this->avatarManager->method('getAvatar')->with('userId')->willReturn($this->avatarMock);
- $this->avatarMock->expects($this->once())
- ->method('isCustomAvatar')
- ->willReturn(true);
-
- $response = $this->avatarController->getAvatar('userId', 32);
-
- $this->assertEquals(Http::STATUS_OK, $response->getStatus());
- $this->assertArrayHasKey('Content-Type', $response->getHeaders());
- $this->assertEquals('image type', $response->getHeaders()['Content-Type']);
- $this->assertArrayHasKey('X-NC-IsCustomAvatar', $response->getHeaders());
- $this->assertEquals('1', $response->getHeaders()['X-NC-IsCustomAvatar']);
-
- $this->assertEquals('my etag', $response->getETag());
- }
-
- /**
- * Fetch the user's avatar
- */
- public function testGetGeneratedAvatar(): void {
- $this->avatarMock->method('getFile')->willReturn($this->avatarFile);
- $this->avatarManager->method('getAvatar')->with('userId')->willReturn($this->avatarMock);
-
- $response = $this->avatarController->getAvatar('userId', 32);
-
- $this->assertEquals(Http::STATUS_OK, $response->getStatus());
- $this->assertArrayHasKey('Content-Type', $response->getHeaders());
- $this->assertEquals('image type', $response->getHeaders()['Content-Type']);
- $this->assertArrayHasKey('X-NC-IsCustomAvatar', $response->getHeaders());
- $this->assertEquals('0', $response->getHeaders()['X-NC-IsCustomAvatar']);
-
- $this->assertEquals('my etag', $response->getETag());
- }
-
- /**
- * Fetch the avatar of a non-existing user
- */
- public function testGetAvatarNoUser(): void {
- $this->avatarManager
- ->method('getAvatar')
- ->with('userDoesNotExist')
- ->willThrowException(new \Exception('user does not exist'));
-
- $response = $this->avatarController->getAvatar('userDoesNotExist', 32);
-
- //Comment out until JS is fixed
- $this->assertEquals(Http::STATUS_NOT_FOUND, $response->getStatus());
- }
-
- public function testGetAvatarSize64(): void {
- $this->avatarMock->expects($this->once())
- ->method('getFile')
- ->with($this->equalTo(64))
- ->willReturn($this->avatarFile);
-
- $this->avatarManager->method('getAvatar')->willReturn($this->avatarMock);
-
- $this->logger->expects($this->never())
- ->method('debug');
-
- $this->avatarController->getAvatar('userId', 64);
- }
-
- public function testGetAvatarSize512(): void {
- $this->avatarMock->expects($this->once())
- ->method('getFile')
- ->with($this->equalTo(512))
- ->willReturn($this->avatarFile);
-
- $this->avatarManager->method('getAvatar')->willReturn($this->avatarMock);
-
- $this->logger->expects($this->never())
- ->method('debug');
-
- $this->avatarController->getAvatar('userId', 512);
- }
-
- /**
- * Small sizes return 64 and generate a log
- */
- public function testGetAvatarSizeTooSmall(): void {
- $this->avatarMock->expects($this->once())
- ->method('getFile')
- ->with($this->equalTo(64))
- ->willReturn($this->avatarFile);
-
- $this->avatarManager->method('getAvatar')->willReturn($this->avatarMock);
-
- $this->logger->expects($this->once())
- ->method('debug')
- ->with('Avatar requested in deprecated size 32');
-
- $this->avatarController->getAvatar('userId', 32);
- }
-
- /**
- * Avatars between 64 and 512 are upgraded to 512
- */
- public function testGetAvatarSizeBetween(): void {
- $this->avatarMock->expects($this->once())
- ->method('getFile')
- ->with($this->equalTo(512))
- ->willReturn($this->avatarFile);
-
- $this->avatarManager->method('getAvatar')->willReturn($this->avatarMock);
-
- $this->logger->expects($this->once())
- ->method('debug')
- ->with('Avatar requested in deprecated size 65');
-
- $this->avatarController->getAvatar('userId', 65);
- }
-
- /**
- * We do not support avatars larger than 512
- */
- public function testGetAvatarSizeTooBig(): void {
- $this->avatarMock->expects($this->once())
- ->method('getFile')
- ->with($this->equalTo(512))
- ->willReturn($this->avatarFile);
-
- $this->avatarManager->method('getAvatar')->willReturn($this->avatarMock);
-
- $this->logger->expects($this->once())
- ->method('debug')
- ->with('Avatar requested in deprecated size 513');
-
- $this->avatarController->getAvatar('userId', 513);
- }
-
- /**
- * Remove an avatar
- */
- public function testDeleteAvatar(): void {
- $this->avatarManager->method('getAvatar')->willReturn($this->avatarMock);
-
- $response = $this->avatarController->deleteAvatar();
- $this->assertEquals(Http::STATUS_OK, $response->getStatus());
- }
-
- /**
- * Test what happens if the removing of the avatar fails
- */
- public function testDeleteAvatarException(): void {
- $this->avatarMock->method('remove')->willThrowException(new \Exception('foo'));
- $this->avatarManager->method('getAvatar')->willReturn($this->avatarMock);
-
- $this->logger->expects($this->once())
- ->method('error')
- ->with('foo', ['exception' => new \Exception('foo'), 'app' => 'core']);
- $expectedResponse = new Http\JSONResponse(['data' => ['message' => 'An error occurred. Please contact your admin.']], Http::STATUS_BAD_REQUEST);
- $this->assertEquals($expectedResponse, $this->avatarController->deleteAvatar());
- }
-
- /**
- * When trying to post a new avatar a path or image should be posted.
- */
- public function testPostAvatarNoPathOrImage(): void {
- $response = $this->avatarController->postAvatar(null);
-
- $this->assertEquals(Http::STATUS_BAD_REQUEST, $response->getStatus());
- }
-
- /**
- * Test a correct post of an avatar using POST
- */
- public function testPostAvatarFile(): void {
- //Create temp file
- $fileName = tempnam('', 'avatarTest');
- $copyRes = copy(\OC::$SERVERROOT . '/tests/data/testimage.jpg', $fileName);
- $this->assertTrue($copyRes);
-
- //Create request return
- $reqRet = ['error' => [0], 'tmp_name' => [$fileName], 'size' => [filesize(\OC::$SERVERROOT . '/tests/data/testimage.jpg')]];
- $this->request->method('getUploadedFile')->willReturn($reqRet);
-
- $response = $this->avatarController->postAvatar(null);
-
- //On correct upload always respond with the notsquare message
- $this->assertEquals('notsquare', $response->getData()['data']);
-
- //File should be deleted
- $this->assertFalse(file_exists($fileName));
- }
-
- /**
- * Test invalid post os an avatar using POST
- */
- public function testPostAvatarInvalidFile(): void {
- //Create request return
- $reqRet = ['error' => [1], 'tmp_name' => ['foo']];
- $this->request->method('getUploadedFile')->willReturn($reqRet);
-
- $response = $this->avatarController->postAvatar(null);
-
- $this->assertEquals(Http::STATUS_BAD_REQUEST, $response->getStatus());
- }
-
- /**
- * Check what happens when we upload a GIF
- */
- public function testPostAvatarFileGif(): void {
- //Create temp file
- $fileName = tempnam('', 'avatarTest');
- $copyRes = copy(\OC::$SERVERROOT . '/tests/data/testimage.gif', $fileName);
- $this->assertTrue($copyRes);
-
- //Create request return
- $reqRet = ['error' => [0], 'tmp_name' => [$fileName], 'size' => [filesize(\OC::$SERVERROOT . '/tests/data/testimage.gif')]];
- $this->request->method('getUploadedFile')->willReturn($reqRet);
-
- $response = $this->avatarController->postAvatar(null);
-
- $this->assertEquals('Unknown filetype', $response->getData()['data']['message']);
-
- //File should be deleted
- $this->assertFalse(file_exists($fileName));
- }
-
- /**
- * Test posting avatar from existing file
- */
- public function testPostAvatarFromFile(): void {
- //Mock node API call
- $file = $this->getMockBuilder('OCP\Files\File')
- ->disableOriginalConstructor()->getMock();
- $file->expects($this->once())
- ->method('getContent')
- ->willReturn(file_get_contents(\OC::$SERVERROOT . '/tests/data/testimage.jpg'));
- $file->expects($this->once())
- ->method('getMimeType')
- ->willReturn('image/jpeg');
- $userFolder = $this->getMockBuilder('OCP\Files\Folder')->getMock();
- $this->rootFolder->method('getUserFolder')->with('userid')->willReturn($userFolder);
- $userFolder->method('get')->willReturn($file);
-
- //Create request return
- $response = $this->avatarController->postAvatar('avatar.jpg');
-
- //On correct upload always respond with the notsquare message
- $this->assertEquals('notsquare', $response->getData()['data']);
- }
-
- /**
- * Test posting avatar from existing folder
- */
- public function testPostAvatarFromNoFile(): void {
- $file = $this->getMockBuilder('OCP\Files\Node')->getMock();
- $userFolder = $this->getMockBuilder('OCP\Files\Folder')->getMock();
- $this->rootFolder->method('getUserFolder')->with('userid')->willReturn($userFolder);
- $userFolder
- ->method('get')
- ->with('folder')
- ->willReturn($file);
-
- //Create request return
- $response = $this->avatarController->postAvatar('folder');
-
- //On correct upload always respond with the notsquare message
- $this->assertEquals(['data' => ['message' => 'Please select a file.']], $response->getData());
- }
-
- public function testPostAvatarInvalidType(): void {
- $file = $this->getMockBuilder('OCP\Files\File')
- ->disableOriginalConstructor()->getMock();
- $file->expects($this->never())
- ->method('getContent');
- $file->expects($this->exactly(2))
- ->method('getMimeType')
- ->willReturn('text/plain');
- $userFolder = $this->getMockBuilder('OCP\Files\Folder')->getMock();
- $this->rootFolder->method('getUserFolder')->with('userid')->willReturn($userFolder);
- $userFolder->method('get')->willReturn($file);
-
- $expectedResponse = new Http\JSONResponse(['data' => ['message' => 'The selected file is not an image.']], Http::STATUS_BAD_REQUEST);
- $this->assertEquals($expectedResponse, $this->avatarController->postAvatar('avatar.jpg'));
- }
-
- public function testPostAvatarNotPermittedException(): void {
- $file = $this->getMockBuilder('OCP\Files\File')
- ->disableOriginalConstructor()->getMock();
- $file->expects($this->once())
- ->method('getContent')
- ->willThrowException(new NotPermittedException());
- $file->expects($this->once())
- ->method('getMimeType')
- ->willReturn('image/jpeg');
- $userFolder = $this->getMockBuilder('OCP\Files\Folder')->getMock();
- $this->rootFolder->method('getUserFolder')->with('userid')->willReturn($userFolder);
- $userFolder->method('get')->willReturn($file);
-
- $expectedResponse = new Http\JSONResponse(['data' => ['message' => 'The selected file cannot be read.']], Http::STATUS_BAD_REQUEST);
- $this->assertEquals($expectedResponse, $this->avatarController->postAvatar('avatar.jpg'));
- }
-
- /**
- * Check for proper reply on proper crop argument
- */
- public function testFileTooBig(): void {
- $fileName = \OC::$SERVERROOT . '/tests/data/testimage.jpg';
- //Create request return
- $reqRet = ['error' => [0], 'tmp_name' => [$fileName], 'size' => [21 * 1024 * 1024]];
- $this->request->method('getUploadedFile')->willReturn($reqRet);
-
- $response = $this->avatarController->postAvatar(null);
-
- $this->assertEquals('File is too big', $response->getData()['data']['message']);
- }
-}
--- a/tests/Core/Controller/CSRFTokenControllerTest.php
+++ /dev/null
@@ -1,63 +0,0 @@
-request = $this->createMock(IRequest::class);
- $this->tokenManager = $this->createMock(CsrfTokenManager::class);
-
- $this->controller = new CSRFTokenController('core', $this->request,
- $this->tokenManager);
- }
-
- public function testGetToken(): void {
- $this->request->method('passesStrictCookieCheck')->willReturn(true);
-
- $token = $this->createMock(CsrfToken::class);
- $this->tokenManager->method('getToken')->willReturn($token);
- $token->method('getEncryptedValue')->willReturn('toktok123');
-
- $response = $this->controller->index();
-
- $this->assertInstanceOf(JSONResponse::class, $response);
- $this->assertSame(Http::STATUS_OK, $response->getStatus());
- $this->assertEquals([
- 'token' => 'toktok123'
- ], $response->getData());
- }
-
- public function testGetTokenNoStrictSameSiteCookie(): void {
- $this->request->method('passesStrictCookieCheck')->willReturn(false);
-
- $response = $this->controller->index();
-
- $this->assertInstanceOf(JSONResponse::class, $response);
- $this->assertSame(Http::STATUS_FORBIDDEN, $response->getStatus());
- }
-}
--- a/tests/Core/Controller/ChangePasswordControllerTest.php
+++ /dev/null
@@ -1,194 +0,0 @@
-userManager = $this->createMock(\OC\User\Manager::class);
- $this->userSession = $this->createMock(Session::class);
- $this->groupManager = $this->createMock(\OC\Group\Manager::class);
- $this->appManager = $this->createMock(IAppManager::class);
- $this->l = $this->createMock(IL10N::class);
- $this->l->method('t')->willReturnArgument(0);
-
- /** @var IRequest|\PHPUnit\Framework\MockObject\MockObject $request */
- $request = $this->createMock(IRequest::class);
-
- $this->controller = new ChangePasswordController(
- 'core',
- $request,
- $this->userId,
- $this->userManager,
- $this->userSession,
- $this->groupManager,
- $this->appManager,
- $this->l
- );
- }
-
- public function testChangePersonalPasswordWrongPassword(): void {
- $this->userSession->expects($this->once())
- ->method('getLoginName')
- ->willReturn($this->loginName);
-
- $this->userManager->expects($this->once())
- ->method('checkPassword')
- ->with($this->loginName, 'old')
- ->willReturn(false);
-
- $expects = new JSONResponse([
- 'status' => 'error',
- 'data' => [
- 'message' => 'Wrong password',
- ],
- ]);
- $expects->throttle();
-
- $actual = $this->controller->changePersonalPassword('old', 'new');
- $this->assertEquals($expects, $actual);
- }
-
- public function testChangePersonalPasswordCommonPassword(): void {
- $this->userSession->expects($this->once())
- ->method('getLoginName')
- ->willReturn($this->loginName);
-
- $user = $this->getMockBuilder(IUser::class)->getMock();
- $this->userManager->expects($this->once())
- ->method('checkPassword')
- ->with($this->loginName, 'old')
- ->willReturn($user);
-
- $user->expects($this->once())
- ->method('setPassword')
- ->with('new')
- ->willThrowException(new HintException('Common password'));
-
- $expects = new JSONResponse([
- 'status' => 'error',
- 'data' => [
- 'message' => 'Common password',
- ],
- ]);
-
- $actual = $this->controller->changePersonalPassword('old', 'new');
- $this->assertEquals($expects, $actual);
- }
-
- public function testChangePersonalPasswordNoNewPassword(): void {
- $this->userSession->expects($this->once())
- ->method('getLoginName')
- ->willReturn($this->loginName);
-
- $user = $this->getMockBuilder(IUser::class)->getMock();
- $this->userManager->expects($this->once())
- ->method('checkPassword')
- ->with($this->loginName, 'old')
- ->willReturn($user);
-
- $expects = [
- 'status' => 'error',
- 'data' => [
- 'message' => 'Unable to change personal password',
- ],
- ];
-
- $res = $this->controller->changePersonalPassword('old');
-
- $this->assertEquals($expects, $res->getData());
- }
-
- public function testChangePersonalPasswordCantSetPassword(): void {
- $this->userSession->expects($this->once())
- ->method('getLoginName')
- ->willReturn($this->loginName);
-
- $user = $this->getMockBuilder(IUser::class)->getMock();
- $this->userManager->expects($this->once())
- ->method('checkPassword')
- ->with($this->loginName, 'old')
- ->willReturn($user);
-
- $user->expects($this->once())
- ->method('setPassword')
- ->with('new')
- ->willReturn(false);
-
- $expects = new JSONResponse([
- 'status' => 'error',
- 'data' => [
- 'message' => 'Unable to change personal password',
- ],
- ]);
-
- $actual = $this->controller->changePersonalPassword('old', 'new');
- $this->assertEquals($expects, $actual);
- }
-
- public function testChangePersonalPassword(): void {
- $this->userSession->expects($this->once())
- ->method('getLoginName')
- ->willReturn($this->loginName);
-
- $user = $this->getMockBuilder(IUser::class)->getMock();
- $this->userManager->expects($this->once())
- ->method('checkPassword')
- ->with($this->loginName, 'old')
- ->willReturn($user);
-
- $user->expects($this->once())
- ->method('setPassword')
- ->with('new')
- ->willReturn(true);
-
- $this->userSession->expects($this->once())
- ->method('updateSessionTokenPassword')
- ->with('new');
-
- $expects = new JSONResponse([
- 'status' => 'success',
- 'data' => [
- 'message' => 'Saved',
- ],
- ]);
-
- $actual = $this->controller->changePersonalPassword('old', 'new');
- $this->assertEquals($expects, $actual);
- }
-}
--- a/tests/Core/Controller/ClientFlowLoginControllerTest.php
+++ /dev/null
@@ -1,715 +0,0 @@
-request = $this->createMock(IRequest::class);
- $this->userSession = $this->createMock(IUserSession::class);
- $this->l10n = $this->createMock(IL10N::class);
- $this->l10n
- ->expects($this->any())
- ->method('t')
- ->willReturnCallback(function ($text, $parameters = []) {
- return vsprintf($text, $parameters);
- });
- $this->defaults = $this->createMock(Defaults::class);
- $this->session = $this->createMock(ISession::class);
- $this->tokenProvider = $this->createMock(IProvider::class);
- $this->random = $this->createMock(ISecureRandom::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->clientMapper = $this->createMock(ClientMapper::class);
- $this->accessTokenMapper = $this->createMock(AccessTokenMapper::class);
- $this->crypto = $this->createMock(ICrypto::class);
- $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
- $this->timeFactory = $this->createMock(ITimeFactory::class);
- $this->config = $this->createMock(IConfig::class);
- $this->initialState = $this->createMock(IInitialState::class);
-
- $this->clientFlowLoginController = new ClientFlowLoginController(
- 'core',
- $this->request,
- $this->userSession,
- $this->l10n,
- $this->defaults,
- $this->session,
- $this->tokenProvider,
- $this->random,
- $this->urlGenerator,
- $this->clientMapper,
- $this->accessTokenMapper,
- $this->crypto,
- $this->eventDispatcher,
- $this->timeFactory,
- $this->config,
- $this->initialState,
- );
- }
-
- public function testShowAuthPickerPageNoClientOrOauthRequest(): void {
- $expected = new StandaloneTemplateResponse(
- 'core',
- 'error',
- [
- 'errors'
- => [
- [
- 'error' => 'Access Forbidden',
- 'hint' => 'Invalid request',
- ],
- ],
- ],
- 'guest'
- );
-
- $this->assertEquals($expected, $this->clientFlowLoginController->showAuthPickerPage());
- }
-
- public function testShowAuthPickerPageWithOcsHeader(): void {
- $this->request
- ->method('getHeader')
- ->willReturnMap([
- ['user-agent', 'Mac OS X Sync Client'],
- ['OCS-APIREQUEST', 'true'],
- ]);
- $this->random
- ->expects($this->once())
- ->method('generate')
- ->with(
- 64,
- ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS
- )
- ->willReturn('StateToken');
- $this->session
- ->expects($this->once())
- ->method('set')
- ->with('client.flow.state.token', 'StateToken');
- $this->session
- ->expects($this->atLeastOnce())
- ->method('get')
- ->with('oauth.state')
- ->willReturn('OauthStateToken');
- $this->defaults
- ->expects($this->once())
- ->method('getName')
- ->willReturn('ExampleCloud');
- $this->request
- ->expects($this->once())
- ->method('getServerHost')
- ->willReturn('example.com');
- $this->request
- ->method('getServerProtocol')
- ->willReturn('https');
-
- $initialState = [];
- $this->initialState->expects($this->exactly(2))
- ->method('provideInitialState')
- ->willReturnCallback(function () use (&$initialState): void {
- $initialState[] = func_get_args();
- });
-
- $expected = new StandaloneTemplateResponse(
- 'core',
- 'loginflow',
- renderAs: 'guest'
- );
- $csp = new ContentSecurityPolicy();
- $csp->addAllowedFormActionDomain('nc://*');
- $expected->setContentSecurityPolicy($csp);
- $this->assertEquals($expected, $this->clientFlowLoginController->showAuthPickerPage());
- self::assertEquals([
- ['loginFlowState', 'auth'],
- [
- 'loginFlowAuth', [
- 'client' => 'Mac OS X Sync Client',
- 'clientIdentifier' => '',
- 'instanceName' => 'ExampleCloud',
- 'stateToken' => 'StateToken',
- 'serverHost' => 'https://example.com',
- 'oauthState' => 'OauthStateToken',
- 'direct' => false,
- 'providedRedirectUri' => '',
- 'appTokenUrl' => '',
- 'loginRedirectUrl' => '',
- ],
- ],
- ], $initialState);
- }
-
- public function testShowAuthPickerPageWithOauth(): void {
- $this->request
- ->method('getHeader')
- ->willReturnMap([
- ['user-agent', 'Mac OS X Sync Client'],
- ['OCS-APIREQUEST', 'false'],
- ]);
- $client = new Client();
- $client->name = 'My external service';
- $client->redirectUri = 'https://example.com/redirect.php';
- $this->clientMapper
- ->expects($this->once())
- ->method('getByIdentifier')
- ->with('MyClientIdentifier')
- ->willReturn($client);
- $this->random
- ->expects($this->once())
- ->method('generate')
- ->with(
- 64,
- ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS
- )
- ->willReturn('StateToken');
- $this->session
- ->expects($this->once())
- ->method('set')
- ->with('client.flow.state.token', 'StateToken');
- $this->session
- ->expects($this->atLeastOnce())
- ->method('get')
- ->with('oauth.state')
- ->willReturn('OauthStateToken');
- $this->defaults
- ->expects($this->once())
- ->method('getName')
- ->willReturn('ExampleCloud');
- $this->request
- ->expects($this->once())
- ->method('getServerHost')
- ->willReturn('example.com');
- $this->request
- ->method('getServerProtocol')
- ->willReturn('https');
-
- $initialState = [];
- $this->initialState->expects($this->exactly(2))
- ->method('provideInitialState')
- ->willReturnCallback(function () use (&$initialState): void {
- $initialState[] = func_get_args();
- });
-
- $expected = new StandaloneTemplateResponse(
- 'core',
- 'loginflow',
- renderAs: 'guest'
- );
- $csp = new ContentSecurityPolicy();
- $csp->addAllowedFormActionDomain('https://example.com/redirect.php');
- $expected->setContentSecurityPolicy($csp);
- $this->assertEquals($expected, $this->clientFlowLoginController->showAuthPickerPage('MyClientIdentifier'));
- self::assertEquals([
- ['loginFlowState', 'auth'],
- [
- 'loginFlowAuth', [
- 'client' => 'My external service',
- 'clientIdentifier' => 'MyClientIdentifier',
- 'instanceName' => 'ExampleCloud',
- 'stateToken' => 'StateToken',
- 'serverHost' => 'https://example.com',
- 'oauthState' => 'OauthStateToken',
- 'direct' => false,
- 'providedRedirectUri' => '',
- 'appTokenUrl' => '',
- 'loginRedirectUrl' => '',
- ],
- ],
- ], $initialState);
- }
-
- public function testGenerateAppPasswordWithInvalidToken(): void {
- $this->session
- ->expects($this->once())
- ->method('get')
- ->with('client.flow.state.token')
- ->willReturn('OtherToken');
- $this->session
- ->expects($this->once())
- ->method('remove')
- ->with('client.flow.state.token');
-
- $expected = new StandaloneTemplateResponse(
- 'core',
- '403',
- [
- 'message' => 'State token does not match',
- ],
- 'guest'
- );
- $expected->setStatus(Http::STATUS_FORBIDDEN);
- $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken'));
- }
-
- public function testGenerateAppPasswordWithSessionNotAvailableException(): void {
- $this->session
- ->expects($this->once())
- ->method('get')
- ->with('client.flow.state.token')
- ->willReturn('MyStateToken');
- $this->session
- ->expects($this->once())
- ->method('remove')
- ->with('client.flow.state.token');
- $this->session
- ->expects($this->once())
- ->method('getId')
- ->willThrowException(new SessionNotAvailableException());
-
- $expected = new Response();
- $expected->setStatus(Http::STATUS_FORBIDDEN);
- $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken'));
- }
-
- public function testGenerateAppPasswordWithInvalidTokenException(): void {
- $this->session
- ->expects($this->once())
- ->method('get')
- ->with('client.flow.state.token')
- ->willReturn('MyStateToken');
- $this->session
- ->expects($this->once())
- ->method('remove')
- ->with('client.flow.state.token');
- $this->session
- ->expects($this->once())
- ->method('getId')
- ->willReturn('SessionId');
- $this->tokenProvider
- ->expects($this->once())
- ->method('getToken')
- ->with('SessionId')
- ->willThrowException(new InvalidTokenException());
-
- $expected = new Response();
- $expected->setStatus(Http::STATUS_FORBIDDEN);
- $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken'));
- }
-
- public function testGeneratePasswordWithPassword(): void {
- $this->session
- ->expects($this->once())
- ->method('get')
- ->with('client.flow.state.token')
- ->willReturn('MyStateToken');
- $this->session
- ->expects($this->once())
- ->method('remove')
- ->with('client.flow.state.token');
- $this->session
- ->expects($this->once())
- ->method('getId')
- ->willReturn('SessionId');
- $myToken = $this->createMock(IToken::class);
- $myToken
- ->expects($this->once())
- ->method('getLoginName')
- ->willReturn('MyLoginName');
- $this->tokenProvider
- ->expects($this->once())
- ->method('getToken')
- ->with('SessionId')
- ->willReturn($myToken);
- $this->tokenProvider
- ->expects($this->once())
- ->method('getPassword')
- ->with($myToken, 'SessionId')
- ->willReturn('MyPassword');
- $this->random
- ->expects($this->once())
- ->method('generate')
- ->with(72)
- ->willReturn('MyGeneratedToken');
- $user = $this->createMock(IUser::class);
- $user
- ->expects($this->once())
- ->method('getUID')
- ->willReturn('MyUid');
- $this->userSession
- ->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
- $this->tokenProvider
- ->expects($this->once())
- ->method('generateToken')
- ->with(
- 'MyGeneratedToken',
- 'MyUid',
- 'MyLoginName',
- 'MyPassword',
- 'unknown',
- IToken::PERMANENT_TOKEN,
- IToken::DO_NOT_REMEMBER
- );
- $this->request
- ->expects($this->once())
- ->method('getServerProtocol')
- ->willReturn('http');
- $this->request
- ->expects($this->once())
- ->method('getServerHost')
- ->willReturn('example.com');
- $this->request
- ->expects($this->any())
- ->method('getHeader')
- ->willReturn('');
-
- $this->eventDispatcher->expects($this->once())
- ->method('dispatchTyped');
-
- $expected = new RedirectResponse('nc://login/server:http://example.com&user:MyLoginName&password:MyGeneratedToken');
- $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken'));
- }
-
- /**
- * @param string $redirectUri
- * @param string $redirectUrl
- *
- * @testWith
- * ["https://example.com/redirect.php", "https://example.com/redirect.php?state=MyOauthState&code=MyAccessCode"]
- * ["https://example.com/redirect.php?hello=world", "https://example.com/redirect.php?hello=world&state=MyOauthState&code=MyAccessCode"]
- *
- */
- public function testGeneratePasswordWithPasswordForOauthClient($redirectUri, $redirectUrl): void {
- $this->session
- ->method('get')
- ->willReturnMap([
- ['client.flow.state.token', 'MyStateToken'],
- ['oauth.state', 'MyOauthState'],
- ]);
- $calls = [
- 'client.flow.state.token',
- 'oauth.state',
- ];
- $this->session
- ->method('remove')
- ->willReturnCallback(function ($key) use (&$calls): void {
- $expected = array_shift($calls);
- $this->assertEquals($expected, $key);
- });
- $this->session
- ->expects($this->once())
- ->method('getId')
- ->willReturn('SessionId');
- $myToken = $this->createMock(IToken::class);
- $myToken
- ->expects($this->once())
- ->method('getLoginName')
- ->willReturn('MyLoginName');
- $this->tokenProvider
- ->expects($this->once())
- ->method('getToken')
- ->with('SessionId')
- ->willReturn($myToken);
- $this->tokenProvider
- ->expects($this->once())
- ->method('getPassword')
- ->with($myToken, 'SessionId')
- ->willReturn('MyPassword');
- $this->random
- ->method('generate')
- ->willReturnMap([
- [72, ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS, 'MyGeneratedToken'],
- [128, ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS, 'MyAccessCode'],
- ]);
- $user = $this->createMock(IUser::class);
- $user
- ->expects($this->once())
- ->method('getUID')
- ->willReturn('MyUid');
- $this->userSession
- ->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
- $token = $this->createMock(IToken::class);
- $this->tokenProvider
- ->expects($this->once())
- ->method('generateToken')
- ->with(
- 'MyGeneratedToken',
- 'MyUid',
- 'MyLoginName',
- 'MyPassword',
- 'My OAuth client',
- IToken::PERMANENT_TOKEN,
- IToken::DO_NOT_REMEMBER
- )
- ->willReturn($token);
- $client = new Client();
- $client->id = 42;
- $client->name = 'My OAuth client';
- $client->redirectUri = $redirectUri;
- $this->clientMapper
- ->expects($this->once())
- ->method('getByIdentifier')
- ->with('MyClientIdentifier')
- ->willReturn($client);
-
- $this->eventDispatcher->expects($this->once())
- ->method('dispatchTyped');
-
- $expected = new RedirectResponse($redirectUrl);
- $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken', 'MyClientIdentifier'));
- }
-
- public function testGeneratePasswordWithoutPassword(): void {
- $this->session
- ->expects($this->once())
- ->method('get')
- ->with('client.flow.state.token')
- ->willReturn('MyStateToken');
- $this->session
- ->expects($this->once())
- ->method('remove')
- ->with('client.flow.state.token');
- $this->session
- ->expects($this->once())
- ->method('getId')
- ->willReturn('SessionId');
- $myToken = $this->createMock(IToken::class);
- $myToken
- ->expects($this->once())
- ->method('getLoginName')
- ->willReturn('MyLoginName');
- $this->tokenProvider
- ->expects($this->once())
- ->method('getToken')
- ->with('SessionId')
- ->willReturn($myToken);
- $this->tokenProvider
- ->expects($this->once())
- ->method('getPassword')
- ->with($myToken, 'SessionId')
- ->willThrowException(new PasswordlessTokenException());
- $this->random
- ->expects($this->once())
- ->method('generate')
- ->with(72)
- ->willReturn('MyGeneratedToken');
- $user = $this->createMock(IUser::class);
- $user
- ->expects($this->once())
- ->method('getUID')
- ->willReturn('MyUid');
- $this->userSession
- ->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
- $this->tokenProvider
- ->expects($this->once())
- ->method('generateToken')
- ->with(
- 'MyGeneratedToken',
- 'MyUid',
- 'MyLoginName',
- null,
- 'unknown',
- IToken::PERMANENT_TOKEN,
- IToken::DO_NOT_REMEMBER
- );
- $this->request
- ->expects($this->once())
- ->method('getServerProtocol')
- ->willReturn('http');
- $this->request
- ->expects($this->once())
- ->method('getServerHost')
- ->willReturn('example.com');
- $this->request
- ->expects($this->any())
- ->method('getHeader')
- ->willReturn('');
-
- $this->eventDispatcher->expects($this->once())
- ->method('dispatchTyped');
-
- $expected = new RedirectResponse('nc://login/server:http://example.com&user:MyLoginName&password:MyGeneratedToken');
- $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken'));
- }
-
- public static function dataGeneratePasswordWithHttpsProxy(): array {
- return [
- [
- [
- ['X-Forwarded-Proto', 'http'],
- ['X-Forwarded-Ssl', 'off'],
- ['user-agent', ''],
- ],
- 'http',
- 'http',
- ],
- [
- [
- ['X-Forwarded-Proto', 'http'],
- ['X-Forwarded-Ssl', 'off'],
- ['user-agent', ''],
- ],
- 'https',
- 'https',
- ],
- [
- [
- ['X-Forwarded-Proto', 'https'],
- ['X-Forwarded-Ssl', 'off'],
- ['user-agent', ''],
- ],
- 'http',
- 'https',
- ],
- [
- [
- ['X-Forwarded-Proto', 'https'],
- ['X-Forwarded-Ssl', 'on'],
- ['user-agent', ''],
- ],
- 'http',
- 'https',
- ],
- [
- [
- ['X-Forwarded-Proto', 'http'],
- ['X-Forwarded-Ssl', 'on'],
- ['user-agent', ''],
- ],
- 'http',
- 'https',
- ],
- ];
- }
-
- /**
- * @param array $headers
- * @param string $protocol
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGeneratePasswordWithHttpsProxy')]
- public function testGeneratePasswordWithHttpsProxy(array $headers, $protocol, $expected): void {
- $this->session
- ->expects($this->once())
- ->method('get')
- ->with('client.flow.state.token')
- ->willReturn('MyStateToken');
- $this->session
- ->expects($this->once())
- ->method('remove')
- ->with('client.flow.state.token');
- $this->session
- ->expects($this->once())
- ->method('getId')
- ->willReturn('SessionId');
- $myToken = $this->createMock(IToken::class);
- $myToken
- ->expects($this->once())
- ->method('getLoginName')
- ->willReturn('MyLoginName');
- $this->tokenProvider
- ->expects($this->once())
- ->method('getToken')
- ->with('SessionId')
- ->willReturn($myToken);
- $this->tokenProvider
- ->expects($this->once())
- ->method('getPassword')
- ->with($myToken, 'SessionId')
- ->willReturn('MyPassword');
- $this->random
- ->expects($this->once())
- ->method('generate')
- ->with(72)
- ->willReturn('MyGeneratedToken');
- $user = $this->createMock(IUser::class);
- $user
- ->expects($this->once())
- ->method('getUID')
- ->willReturn('MyUid');
- $this->userSession
- ->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
- $this->tokenProvider
- ->expects($this->once())
- ->method('generateToken')
- ->with(
- 'MyGeneratedToken',
- 'MyUid',
- 'MyLoginName',
- 'MyPassword',
- 'unknown',
- IToken::PERMANENT_TOKEN,
- IToken::DO_NOT_REMEMBER
- );
- $this->request
- ->expects($this->once())
- ->method('getServerProtocol')
- ->willReturn($protocol);
- $this->request
- ->expects($this->once())
- ->method('getServerHost')
- ->willReturn('example.com');
- $this->request
- ->expects($this->atLeastOnce())
- ->method('getHeader')
- ->willReturnMap($headers);
-
- $this->eventDispatcher->expects($this->once())
- ->method('dispatchTyped');
-
- $expected = new RedirectResponse('nc://login/server:' . $expected . '://example.com&user:MyLoginName&password:MyGeneratedToken');
- $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken'));
- }
-}
--- a/tests/Core/Controller/ClientFlowLoginV2ControllerTest.php
+++ /dev/null
@@ -1,390 +0,0 @@
-request = $this->createMock(IRequest::class);
- $this->loginFlowV2Service = $this->createMock(LoginFlowV2Service::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->session = $this->createMock(ISession::class);
- $this->userSession = $this->createMock(IUserSession::class);
- $this->random = $this->createMock(ISecureRandom::class);
- $this->defaults = $this->createMock(Defaults::class);
- $this->initialState = $this->createMock(IInitialState::class);
- $this->l = $this->createMock(IL10N::class);
- $this->l
- ->expects($this->any())
- ->method('t')
- ->willReturnCallback(function ($text, $parameters = []) {
- return vsprintf($text, $parameters);
- });
- $this->controller = new ClientFlowLoginV2Controller(
- 'core',
- $this->request,
- $this->loginFlowV2Service,
- $this->urlGenerator,
- $this->session,
- $this->userSession,
- $this->random,
- $this->defaults,
- 'user',
- $this->l,
- $this->initialState,
- );
- }
-
- public function testPollInvalid(): void {
- $this->loginFlowV2Service->method('poll')
- ->with('token')
- ->willThrowException(new LoginFlowV2NotFoundException());
-
- $result = $this->controller->poll('token');
-
- $this->assertSame([], $result->getData());
- $this->assertSame(Http::STATUS_NOT_FOUND, $result->getStatus());
- }
-
- public function testPollValid(): void {
- $creds = new LoginFlowV2Credentials('server', 'login', 'pass');
- $this->loginFlowV2Service->method('poll')
- ->with('token')
- ->willReturn($creds);
-
- $result = $this->controller->poll('token');
-
- $this->assertSame($creds->jsonSerialize(), $result->getData());
- $this->assertSame(Http::STATUS_OK, $result->getStatus());
- }
-
- public function testLandingInvalid(): void {
- $this->session->expects($this->never())
- ->method($this->anything());
-
- $this->loginFlowV2Service->method('startLoginFlow')
- ->with('token')
- ->willReturn(false);
-
- $result = $this->controller->landing('token');
-
- $this->assertSame(Http::STATUS_FORBIDDEN, $result->getStatus());
- $this->assertInstanceOf(StandaloneTemplateResponse::class, $result);
- }
-
- public function testLandingValid(): void {
- $this->session->expects($this->once())
- ->method('set')
- ->with('client.flow.v2.login.token', 'token');
-
- $this->loginFlowV2Service->method('startLoginFlow')
- ->with('token')
- ->willReturn(true);
-
- $this->urlGenerator->method('linkToRouteAbsolute')
- ->with('core.ClientFlowLoginV2.showAuthPickerPage')
- ->willReturn('https://server/path');
-
- $result = $this->controller->landing('token');
-
- $this->assertInstanceOf(RedirectResponse::class, $result);
- $this->assertSame(Http::STATUS_SEE_OTHER, $result->getStatus());
- $this->assertSame('https://server/path', $result->getRedirectURL());
- }
-
- public function testShowAuthPickerNoLoginToken(): void {
- $this->session->method('get')
- ->willReturn(null);
-
- $result = $this->controller->showAuthPickerPage();
-
- $this->assertSame(Http::STATUS_FORBIDDEN, $result->getStatus());
- }
-
- public function testShowAuthPickerInvalidLoginToken(): void {
- $this->session->method('get')
- ->with('client.flow.v2.login.token')
- ->willReturn('loginToken');
-
- $this->loginFlowV2Service->method('getByLoginToken')
- ->with('loginToken')
- ->willThrowException(new LoginFlowV2NotFoundException());
-
- $result = $this->controller->showAuthPickerPage();
-
- $this->assertSame(Http::STATUS_FORBIDDEN, $result->getStatus());
- }
-
- public function testShowAuthPickerForbiddenUserClient() {
- $this->session->method('get')
- ->with('client.flow.v2.login.token')
- ->willReturn('loginToken');
-
- $this->loginFlowV2Service->method('getByLoginToken')
- ->with('loginToken')
- ->willThrowException(new LoginFlowV2ClientForbiddenException());
-
- $result = $this->controller->showAuthPickerPage();
-
- $this->assertInstanceOf(StandaloneTemplateResponse::class, $result);
- $this->assertSame(Http::STATUS_FORBIDDEN, $result->getStatus());
- $this->assertSame('Please use original client', $result->getParams()['message']);
- }
-
- public function testShowAuthPickerValidLoginToken(): void {
- $this->session->method('get')
- ->with('client.flow.v2.login.token')
- ->willReturn('loginToken');
-
- $flow = new LoginFlowV2();
- $this->loginFlowV2Service->method('getByLoginToken')
- ->with('loginToken')
- ->willReturn($flow);
-
- $this->random->method('generate')
- ->with(64, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS)
- ->willReturn('random');
- $this->session->expects($this->once())
- ->method('set')
- ->with('client.flow.v2.state.token', 'random');
-
- $this->controller->showAuthPickerPage();
- }
-
- public function testGrantPageNoStateToken(): void {
- $result = $this->controller->grantPage(null);
-
- $this->assertSame(Http::STATUS_FORBIDDEN, $result->getStatus());
- }
-
- public function testGrantPageInvalidStateToken(): void {
- $this->session->method('get')
- ->willReturnCallback(function ($name) {
- return null;
- });
-
- $result = $this->controller->grantPage('stateToken');
- $this->assertSame(Http::STATUS_FORBIDDEN, $result->getStatus());
- }
-
- public function testGrantPageInvalidLoginToken(): void {
- $this->session->method('get')
- ->willReturnCallback(function ($name) {
- if ($name === 'client.flow.v2.state.token') {
- return 'stateToken';
- }
- if ($name === 'client.flow.v2.login.token') {
- return 'loginToken';
- }
- return null;
- });
-
- $this->loginFlowV2Service->method('getByLoginToken')
- ->with('loginToken')
- ->willThrowException(new LoginFlowV2NotFoundException());
-
- $result = $this->controller->grantPage('stateToken');
- $this->assertSame(Http::STATUS_FORBIDDEN, $result->getStatus());
- }
-
- public function testGrantPageForbiddenUserClient() {
- $this->session->method('get')
- ->willReturnCallback(function ($name) {
- if ($name === 'client.flow.v2.state.token') {
- return 'stateToken';
- }
- if ($name === 'client.flow.v2.login.token') {
- return 'loginToken';
- }
- return null;
- });
-
- $this->loginFlowV2Service->method('getByLoginToken')
- ->with('loginToken')
- ->willThrowException(new LoginFlowV2ClientForbiddenException());
-
- $result = $this->controller->grantPage('stateToken');
-
- $this->assertInstanceOf(StandaloneTemplateResponse::class, $result);
- $this->assertSame(Http::STATUS_FORBIDDEN, $result->getStatus());
- $this->assertSame('Please use original client', $result->getParams()['message']);
- }
-
- public function testGrantPageValid(): void {
- $this->session->method('get')
- ->willReturnCallback(function ($name) {
- if ($name === 'client.flow.v2.state.token') {
- return 'stateToken';
- }
- if ($name === 'client.flow.v2.login.token') {
- return 'loginToken';
- }
- return null;
- });
-
- $user = $this->createMock(IUser::class);
- $user->method('getUID')
- ->willReturn('uid');
- $user->method('getDisplayName')
- ->willReturn('display name');
- $this->userSession->method('getUser')
- ->willReturn($user);
-
- $flow = new LoginFlowV2();
- $this->loginFlowV2Service->method('getByLoginToken')
- ->with('loginToken')
- ->willReturn($flow);
-
- $result = $this->controller->grantPage('stateToken');
- $this->assertSame(Http::STATUS_OK, $result->getStatus());
- }
-
- public function testGenerateAppPasswordInvalidStateToken(): void {
- $this->session->method('get')
- ->willReturnCallback(function ($name) {
- return null;
- });
-
- $result = $this->controller->generateAppPassword('stateToken');
- $this->assertSame(Http::STATUS_FORBIDDEN, $result->getStatus());
- }
-
- public function testGenerateAppPassworInvalidLoginToken(): void {
- $this->session->method('get')
- ->willReturnCallback(function ($name) {
- if ($name === 'client.flow.v2.state.token') {
- return 'stateToken';
- }
- if ($name === 'client.flow.v2.login.token') {
- return 'loginToken';
- }
- return null;
- });
-
- $this->loginFlowV2Service->method('getByLoginToken')
- ->with('loginToken')
- ->willThrowException(new LoginFlowV2NotFoundException());
-
- $result = $this->controller->generateAppPassword('stateToken');
- $this->assertSame(Http::STATUS_FORBIDDEN, $result->getStatus());
- }
-
- public function testGenerateAppPasswordForbiddenUserClient() {
- $this->session->method('get')
- ->willReturnCallback(function ($name) {
- if ($name === 'client.flow.v2.state.token') {
- return 'stateToken';
- }
- if ($name === 'client.flow.v2.login.token') {
- return 'loginToken';
- }
- return null;
- });
-
- $this->loginFlowV2Service->method('getByLoginToken')
- ->with('loginToken')
- ->willThrowException(new LoginFlowV2ClientForbiddenException());
-
- $result = $this->controller->generateAppPassword('stateToken');
-
- $this->assertInstanceOf(StandaloneTemplateResponse::class, $result);
- $this->assertSame(Http::STATUS_FORBIDDEN, $result->getStatus());
- $this->assertSame('Please use original client', $result->getParams()['message']);
- }
-
- public function testGenerateAppPassworValid(): void {
- $this->session->method('get')
- ->willReturnCallback(function ($name) {
- if ($name === 'client.flow.v2.state.token') {
- return 'stateToken';
- }
- if ($name === 'client.flow.v2.login.token') {
- return 'loginToken';
- }
- return null;
- });
-
- $flow = new LoginFlowV2();
- $this->loginFlowV2Service->method('getByLoginToken')
- ->with('loginToken')
- ->willReturn($flow);
-
- $clearedState = false;
- $clearedLogin = false;
- $this->session->method('remove')
- ->willReturnCallback(function ($name) use (&$clearedLogin, &$clearedState): void {
- if ($name === 'client.flow.v2.state.token') {
- $clearedState = true;
- }
- if ($name === 'client.flow.v2.login.token') {
- $clearedLogin = true;
- }
- });
-
- $this->session->method('getId')
- ->willReturn('sessionId');
-
- $this->loginFlowV2Service->expects($this->once())
- ->method('flowDone')
- ->with(
- 'loginToken',
- 'sessionId',
- 'https://server',
- 'user'
- )->willReturn(true);
-
- $this->request->method('getServerProtocol')
- ->willReturn('https');
- $this->request->method('getRequestUri')
- ->willReturn('/login/v2');
- $this->request->method('getServerHost')
- ->willReturn('server');
-
- $result = $this->controller->generateAppPassword('stateToken');
- $this->assertSame(Http::STATUS_OK, $result->getStatus());
-
- $this->assertTrue($clearedLogin);
- $this->assertTrue($clearedState);
- }
-}
--- a/tests/Core/Controller/ContactsMenuControllerTest.php
+++ /dev/null
@@ -1,128 +0,0 @@
-createMock(IRequest::class);
- $this->userSession = $this->createMock(IUserSession::class);
- $this->contactsManager = $this->createMock(Manager::class);
- $this->teamManager = $this->createMock(ITeamManager::class);
-
- $this->controller = new ContactsMenuController(
- $request,
- $this->userSession,
- $this->contactsManager,
- $this->teamManager,
- );
- }
-
- public function testIndex(): void {
- $user = $this->createMock(IUser::class);
- $entries = [
- $this->createMock(IEntry::class),
- $this->createMock(IEntry::class),
- ];
- $this->userSession->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
- $this->contactsManager->expects($this->once())
- ->method('getEntries')
- ->with($this->equalTo($user), $this->equalTo(null))
- ->willReturn($entries);
-
- $response = $this->controller->index();
-
- $this->assertEquals($entries, $response);
- }
-
- public function testIndex_withTeam(): void {
- $user = $this->createMock(IUser::class);
- $user->method('getUID')
- ->willReturn('current-user');
-
- $entries = [
- $this->createMock(IEntry::class),
- $this->createMock(IEntry::class),
- ];
- $entries[0]->method('getProperty')
- ->with('UID')
- ->willReturn('member1');
- $entries[1]->method('getProperty')
- ->with('UID')
- ->willReturn('member2');
-
- $this->userSession->expects($this->atLeastOnce())
- ->method('getUser')
- ->willReturn($user);
- $this->contactsManager->expects($this->once())
- ->method('getEntries')
- ->with($this->equalTo($user), $this->equalTo(null))
- ->willReturn(['contacts' => $entries]);
-
- $this->teamManager->expects($this->once())
- ->method('getMembersOfTeam')
- ->with('team-id', 'current-user')
- ->willReturn(['member1' => 'Member 1', 'member3' => 'Member 3']);
-
- $response = $this->controller->index(teamId: 'team-id');
-
- $this->assertEquals([$entries[0]], $response['contacts']);
- }
-
- public function testFindOne(): void {
- $user = $this->createMock(IUser::class);
- $entry = $this->createMock(IEntry::class);
- $this->userSession->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
- $this->contactsManager->expects($this->once())
- ->method('findOne')
- ->with($this->equalTo($user), $this->equalTo(42), $this->equalTo('test-search-phrase'))
- ->willReturn($entry);
-
- $response = $this->controller->findOne(42, 'test-search-phrase');
-
- $this->assertEquals($entry, $response);
- }
-
- public function testFindOne404(): void {
- $user = $this->createMock(IUser::class);
- $this->userSession->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
- $this->contactsManager->expects($this->once())
- ->method('findOne')
- ->with($this->equalTo($user), $this->equalTo(42), $this->equalTo('test-search-phrase'))
- ->willReturn(null);
-
- $response = $this->controller->findOne(42, 'test-search-phrase');
-
- $this->assertEquals([], $response->getData());
- $this->assertEquals(404, $response->getStatus());
- }
-}
--- a/tests/Core/Controller/CssControllerTest.php
+++ /dev/null
@@ -1,171 +0,0 @@
-createMock(Factory::class);
- $this->appData = $this->createMock(AppData::class);
- $factory->expects($this->once())
- ->method('get')
- ->with('css')
- ->willReturn($this->appData);
-
- /** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject $timeFactory */
- $timeFactory = $this->createMock(ITimeFactory::class);
- $timeFactory->method('getTime')
- ->willReturn(1337);
-
- $this->request = $this->createMock(IRequest::class);
-
- $this->controller = new CssController(
- 'core',
- $this->request,
- $factory,
- $timeFactory
- );
- }
-
- public function testNoCssFolderForApp(): void {
- $this->appData->method('getFolder')
- ->with('myapp')
- ->willThrowException(new NotFoundException());
-
- $result = $this->controller->getCss('file.css', 'myapp');
-
- $this->assertInstanceOf(NotFoundResponse::class, $result);
- }
-
- public function testNoCssFile(): void {
- $folder = $this->createMock(ISimpleFolder::class);
- $this->appData->method('getFolder')
- ->with('myapp')
- ->willReturn($folder);
-
- $folder->method('getFile')
- ->willThrowException(new NotFoundException());
-
- $result = $this->controller->getCss('file.css', 'myapp');
-
- $this->assertInstanceOf(NotFoundResponse::class, $result);
- }
-
- public function testGetFile(): void {
- $folder = $this->createMock(ISimpleFolder::class);
- $file = $this->createMock(ISimpleFile::class);
- $file->method('getName')->willReturn('my name');
- $file->method('getMTime')->willReturn(42);
- $this->appData->method('getFolder')
- ->with('myapp')
- ->willReturn($folder);
-
- $folder->method('getFile')
- ->with('file.css')
- ->willReturn($file);
-
- $expected = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => 'text/css']);
- $expected->addHeader('Cache-Control', 'max-age=31536000, immutable');
- $expires = new \DateTime();
- $expires->setTimestamp(1337);
- $expires->add(new \DateInterval('PT31536000S'));
- $expected->addHeader('Expires', $expires->format(\DateTime::RFC1123));
-
- $result = $this->controller->getCss('file.css', 'myapp');
- $this->assertEquals($expected, $result);
- }
-
- public function testGetGzipFile(): void {
- $folder = $this->createMock(ISimpleFolder::class);
- $gzipFile = $this->createMock(ISimpleFile::class);
- $gzipFile->method('getName')->willReturn('my name');
- $gzipFile->method('getMTime')->willReturn(42);
- $this->appData->method('getFolder')
- ->with('myapp')
- ->willReturn($folder);
-
- $folder->method('getFile')
- ->with('file.css.gzip')
- ->willReturn($gzipFile);
-
- $this->request->method('getHeader')
- ->with('Accept-Encoding')
- ->willReturn('gzip, deflate');
-
- $expected = new FileDisplayResponse($gzipFile, Http::STATUS_OK, ['Content-Type' => 'text/css']);
- $expected->addHeader('Content-Encoding', 'gzip');
- $expected->addHeader('Cache-Control', 'max-age=31536000, immutable');
- $expires = new \DateTime();
- $expires->setTimestamp(1337);
- $expires->add(new \DateInterval('PT31536000S'));
- $expected->addHeader('Expires', $expires->format(\DateTime::RFC1123));
-
- $result = $this->controller->getCss('file.css', 'myapp');
- $this->assertEquals($expected, $result);
- }
-
- public function testGetGzipFileNotFound(): void {
- $folder = $this->createMock(ISimpleFolder::class);
- $file = $this->createMock(ISimpleFile::class);
- $file->method('getName')->willReturn('my name');
- $file->method('getMTime')->willReturn(42);
- $this->appData->method('getFolder')
- ->with('myapp')
- ->willReturn($folder);
-
- $folder->method('getFile')
- ->willReturnCallback(
- function ($fileName) use ($file) {
- if ($fileName === 'file.css') {
- return $file;
- }
- throw new NotFoundException();
- }
- );
-
- $this->request->method('getHeader')
- ->with('Accept-Encoding')
- ->willReturn('gzip, deflate');
-
- $expected = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => 'text/css']);
- $expected->addHeader('Cache-Control', 'max-age=31536000, immutable');
- $expires = new \DateTime();
- $expires->setTimestamp(1337);
- $expires->add(new \DateInterval('PT31536000S'));
- $expected->addHeader('Expires', $expires->format(\DateTime::RFC1123));
-
- $result = $this->controller->getCss('file.css', 'myapp');
- $this->assertEquals($expected, $result);
- }
-}
--- a/tests/Core/Controller/GuestAvatarControllerTest.php
+++ /dev/null
@@ -1,97 +0,0 @@
-logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
- $this->request = $this->getMockBuilder(IRequest::class)->getMock();
- $this->avatar = $this->getMockBuilder(IAvatar::class)->getMock();
- $this->avatarManager = $this->getMockBuilder(IAvatarManager::class)->getMock();
- $this->file = $this->getMockBuilder(ISimpleFile::class)->getMock();
- $this->file->method('getName')->willReturn('my name');
- $this->file->method('getMTime')->willReturn(42);
- $this->guestAvatarController = new GuestAvatarController(
- 'core',
- $this->request,
- $this->avatarManager,
- $this->logger
- );
- }
-
- /**
- * Tests getAvatar returns the guest avatar.
- */
- public function testGetAvatar(): void {
- $this->avatarManager->expects($this->once())
- ->method('getGuestAvatar')
- ->with('Peter')
- ->willReturn($this->avatar);
-
- $this->avatar->expects($this->once())
- ->method('getFile')
- ->with(512)
- ->willReturn($this->file);
-
- $this->file->method('getMimeType')
- ->willReturn('image/svg+xml');
-
- $response = $this->guestAvatarController->getAvatar('Peter', 128);
-
- $this->assertGreaterThanOrEqual(201, $response->getStatus());
- $this->assertInstanceOf(FileDisplayResponse::class, $response);
- }
-}
--- a/tests/Core/Controller/JsControllerTest.php
+++ /dev/null
@@ -1,171 +0,0 @@
-createMock(Factory::class);
- $this->appData = $this->createMock(AppData::class);
- $factory->expects($this->once())
- ->method('get')
- ->with('js')
- ->willReturn($this->appData);
-
- /** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject $timeFactory */
- $timeFactory = $this->createMock(ITimeFactory::class);
- $timeFactory->method('getTime')
- ->willReturn(1337);
-
- $this->request = $this->createMock(IRequest::class);
-
- $this->controller = new JsController(
- 'core',
- $this->request,
- $factory,
- $timeFactory
- );
- }
-
- public function testNoCssFolderForApp(): void {
- $this->appData->method('getFolder')
- ->with('myapp')
- ->willThrowException(new NotFoundException());
-
- $result = $this->controller->getJs('file.css', 'myapp');
-
- $this->assertInstanceOf(NotFoundResponse::class, $result);
- }
-
- public function testNoCssFile(): void {
- $folder = $this->createMock(ISimpleFolder::class);
- $this->appData->method('getFolder')
- ->with('myapp')
- ->willReturn($folder);
-
- $folder->method('getFile')
- ->willThrowException(new NotFoundException());
-
- $result = $this->controller->getJs('file.css', 'myapp');
-
- $this->assertInstanceOf(NotFoundResponse::class, $result);
- }
-
- public function testGetFile(): void {
- $folder = $this->createMock(ISimpleFolder::class);
- $file = $this->createMock(ISimpleFile::class);
- $file->method('getName')->willReturn('my name');
- $file->method('getMTime')->willReturn(42);
- $this->appData->method('getFolder')
- ->with('myapp')
- ->willReturn($folder);
-
- $folder->method('getFile')
- ->with('file.js')
- ->willReturn($file);
-
- $expected = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => 'application/javascript']);
- $expected->addHeader('Cache-Control', 'max-age=31536000, immutable');
- $expires = new \DateTime();
- $expires->setTimestamp(1337);
- $expires->add(new \DateInterval('PT31536000S'));
- $expected->addHeader('Expires', $expires->format(\DateTime::RFC1123));
-
- $result = $this->controller->getJs('file.js', 'myapp');
- $this->assertEquals($expected, $result);
- }
-
- public function testGetGzipFile(): void {
- $folder = $this->createMock(ISimpleFolder::class);
- $gzipFile = $this->createMock(ISimpleFile::class);
- $gzipFile->method('getName')->willReturn('my name');
- $gzipFile->method('getMTime')->willReturn(42);
- $this->appData->method('getFolder')
- ->with('myapp')
- ->willReturn($folder);
-
- $folder->method('getFile')
- ->with('file.js.gzip')
- ->willReturn($gzipFile);
-
- $this->request->method('getHeader')
- ->with('Accept-Encoding')
- ->willReturn('gzip, deflate');
-
- $expected = new FileDisplayResponse($gzipFile, Http::STATUS_OK, ['Content-Type' => 'application/javascript']);
- $expected->addHeader('Content-Encoding', 'gzip');
- $expected->addHeader('Cache-Control', 'max-age=31536000, immutable');
- $expires = new \DateTime();
- $expires->setTimestamp(1337);
- $expires->add(new \DateInterval('PT31536000S'));
- $expected->addHeader('Expires', $expires->format(\DateTime::RFC1123));
-
- $result = $this->controller->getJs('file.js', 'myapp');
- $this->assertEquals($expected, $result);
- }
-
- public function testGetGzipFileNotFound(): void {
- $folder = $this->createMock(ISimpleFolder::class);
- $file = $this->createMock(ISimpleFile::class);
- $file->method('getName')->willReturn('my name');
- $file->method('getMTime')->willReturn(42);
- $this->appData->method('getFolder')
- ->with('myapp')
- ->willReturn($folder);
-
- $folder->method('getFile')
- ->willReturnCallback(
- function ($fileName) use ($file) {
- if ($fileName === 'file.js') {
- return $file;
- }
- throw new NotFoundException();
- }
- );
-
- $this->request->method('getHeader')
- ->with('Accept-Encoding')
- ->willReturn('gzip, deflate');
-
- $expected = new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => 'application/javascript']);
- $expected->addHeader('Cache-Control', 'max-age=31536000, immutable');
- $expires = new \DateTime();
- $expires->setTimestamp(1337);
- $expires->add(new \DateInterval('PT31536000S'));
- $expected->addHeader('Expires', $expires->format(\DateTime::RFC1123));
-
- $result = $this->controller->getJs('file.js', 'myapp');
- $this->assertEquals($expected, $result);
- }
-}
--- a/tests/Core/Controller/LoginControllerTest.php
+++ /dev/null
@@ -1,727 +0,0 @@
-request = $this->createMock(IRequest::class);
- $this->userManager = $this->createMock(\OC\User\Manager::class);
- $this->config = $this->createMock(IConfig::class);
- $this->session = $this->createMock(ISession::class);
- $this->userSession = $this->createMock(Session::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->twoFactorManager = $this->createMock(Manager::class);
- $this->defaults = $this->createMock(Defaults::class);
- $this->throttler = $this->createMock(IThrottler::class);
- $this->initialState = $this->createMock(IInitialState::class);
- $this->webAuthnManager = $this->createMock(\OC\Authentication\WebAuthn\Manager::class);
- $this->notificationManager = $this->createMock(IManager::class);
- $this->l = $this->createMock(IL10N::class);
- $this->appManager = $this->createMock(IAppManager::class);
-
- $this->l->expects($this->any())
- ->method('t')
- ->willReturnCallback(function ($text, $parameters = []) {
- return vsprintf($text, $parameters);
- });
-
- $this->request->method('getRemoteAddress')
- ->willReturn('1.2.3.4');
- $this->request->method('getHeader')
- ->with('Origin')
- ->willReturn('domain.example.com');
- $this->throttler->method('getDelay')
- ->with(
- $this->equalTo('1.2.3.4'),
- $this->equalTo('')
- )->willReturn(1000);
-
- $this->loginController = new LoginController(
- 'core',
- $this->request,
- $this->userManager,
- $this->config,
- $this->session,
- $this->userSession,
- $this->urlGenerator,
- $this->defaults,
- $this->throttler,
- $this->initialState,
- $this->webAuthnManager,
- $this->notificationManager,
- $this->l,
- $this->appManager,
- );
- }
-
- public function testLogoutWithoutToken(): void {
- $this->request
- ->expects($this->once())
- ->method('getCookie')
- ->with('nc_token')
- ->willReturn(null);
- $this->request
- ->method('getServerProtocol')
- ->willReturn('https');
- $this->request
- ->expects($this->once())
- ->method('isUserAgent')
- ->willReturn(false);
- $this->config
- ->expects($this->never())
- ->method('deleteUserValue');
- $this->urlGenerator
- ->expects($this->once())
- ->method('linkToRouteAbsolute')
- ->with('core.login.showLoginForm')
- ->willReturn('/login');
-
- $expected = new RedirectResponse('/login');
- $expected->addHeader('Clear-Site-Data', '"cache", "storage"');
- $this->assertEquals($expected, $this->loginController->logout());
- }
-
- public function testLogoutNoClearSiteData(): void {
- $this->request
- ->expects($this->once())
- ->method('getCookie')
- ->with('nc_token')
- ->willReturn(null);
- $this->request
- ->method('getServerProtocol')
- ->willReturn('https');
- $this->request
- ->expects($this->once())
- ->method('isUserAgent')
- ->willReturn(true);
- $this->urlGenerator
- ->expects($this->once())
- ->method('linkToRouteAbsolute')
- ->with('core.login.showLoginForm')
- ->willReturn('/login');
-
- $expected = new RedirectResponse('/login');
- $this->assertEquals($expected, $this->loginController->logout());
- }
-
- public function testLogoutWithToken(): void {
- $this->request
- ->expects($this->once())
- ->method('getCookie')
- ->with('nc_token')
- ->willReturn('MyLoginToken');
- $this->request
- ->method('getServerProtocol')
- ->willReturn('https');
- $this->request
- ->expects($this->once())
- ->method('isUserAgent')
- ->willReturn(false);
- $user = $this->createMock(IUser::class);
- $user
- ->expects($this->once())
- ->method('getUID')
- ->willReturn('JohnDoe');
- $this->userSession
- ->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
- $this->config
- ->expects($this->once())
- ->method('deleteUserValue')
- ->with('JohnDoe', 'login_token', 'MyLoginToken');
- $this->urlGenerator
- ->expects($this->once())
- ->method('linkToRouteAbsolute')
- ->with('core.login.showLoginForm')
- ->willReturn('/login');
-
- $expected = new RedirectResponse('/login');
- $expected->addHeader('Clear-Site-Data', '"cache", "storage"');
- $expected->addHeader('X-User-Id', 'JohnDoe');
- $this->assertEquals($expected, $this->loginController->logout());
- }
-
- public function testShowLoginFormForLoggedInUsers(): void {
- $this->userSession
- ->expects($this->once())
- ->method('isLoggedIn')
- ->willReturn(true);
- $this->urlGenerator
- ->expects($this->once())
- ->method('linkToDefaultPageUrl')
- ->willReturn('/default/foo');
-
- $expectedResponse = new RedirectResponse('/default/foo');
- $this->assertEquals($expectedResponse, $this->loginController->showLoginForm('', ''));
- }
-
- public function testShowLoginFormWithErrorsInSession(): void {
- $this->userSession
- ->expects($this->once())
- ->method('isLoggedIn')
- ->willReturn(false);
- $this->session
- ->expects($this->once())
- ->method('get')
- ->with('loginMessages')
- ->willReturn(
- [
- [
- 'ErrorArray1',
- 'ErrorArray2',
- ],
- [
- 'MessageArray1',
- 'MessageArray2',
- ],
- ]
- );
-
- $calls = [
- [
- 'loginMessages',
- [
- 'MessageArray1',
- 'MessageArray2',
- 'This community release of Nextcloud is unsupported and push notifications are limited.',
- ],
- ],
- [
- 'loginErrors',
- [
- 'ErrorArray1',
- 'ErrorArray2',
- ],
- ],
- [
- 'loginUsername',
- '',
- ]
- ];
- $this->initialState->expects($this->exactly(14))
- ->method('provideInitialState')
- ->willReturnCallback(function () use (&$calls): void {
- $expected = array_shift($calls);
- if (!empty($expected)) {
- $this->assertEquals($expected, func_get_args());
- }
- });
-
- $expectedResponse = new TemplateResponse(
- 'core',
- 'login',
- [
- 'alt_login' => [],
- 'pageTitle' => 'Login'
- ],
- 'guest'
- );
- $this->assertEquals($expectedResponse, $this->loginController->showLoginForm('', ''));
- }
-
- public function testShowLoginFormForFlowAuth(): void {
- $this->userSession
- ->expects($this->once())
- ->method('isLoggedIn')
- ->willReturn(false);
- $calls = [
- [], [], [],
- [
- 'loginAutocomplete',
- false
- ],
- [
- 'loginCanRememberme',
- false
- ],
- [
- 'loginRedirectUrl',
- 'login/flow'
- ],
- ];
- $this->initialState->expects($this->exactly(15))
- ->method('provideInitialState')
- ->willReturnCallback(function () use (&$calls): void {
- $expected = array_shift($calls);
- if (!empty($expected)) {
- $this->assertEquals($expected, func_get_args());
- }
- });
-
- $expectedResponse = new TemplateResponse(
- 'core',
- 'login',
- [
- 'alt_login' => [],
- 'pageTitle' => 'Login'
- ],
- 'guest'
- );
- $this->assertEquals($expectedResponse, $this->loginController->showLoginForm('', 'login/flow'));
- }
-
- /**
- * @return array
- */
- public static function passwordResetDataProvider(): array {
- return [
- [
- true,
- true,
- ],
- [
- false,
- false,
- ],
- ];
- }
-
- #[DataProvider('passwordResetDataProvider')]
- public function testShowLoginFormWithPasswordResetOption($canChangePassword,
- $expectedResult): void {
- $this->userSession
- ->expects($this->once())
- ->method('isLoggedIn')
- ->willReturn(false);
- $this->config
- ->expects(self::once())
- ->method('getSystemValue')
- ->willReturnMap([
- ['login_form_autocomplete', true, true],
- ]);
- $this->config
- ->expects(self::once())
- ->method('getSystemValueString')
- ->willReturnMap([
- ['lost_password_link', '', ''],
- ]);
- $user = $this->createMock(IUser::class);
- $user
- ->expects($this->once())
- ->method('canChangePassword')
- ->willReturn($canChangePassword);
- $this->userManager
- ->expects($this->once())
- ->method('get')
- ->with('LdapUser')
- ->willReturn($user);
- $calls = [
- [], [],
- [
- 'loginUsername',
- 'LdapUser'
- ],
- [], [], [], [],
- [
- 'loginCanResetPassword',
- $expectedResult
- ],
- ];
- $this->initialState->expects($this->exactly(14))
- ->method('provideInitialState')
- ->willReturnCallback(function () use (&$calls): void {
- $expected = array_shift($calls);
- if (!empty($expected)) {
- $this->assertEquals($expected, func_get_args());
- }
- });
-
- $expectedResponse = new TemplateResponse(
- 'core',
- 'login',
- [
- 'alt_login' => [],
- 'pageTitle' => 'Login'
- ],
- 'guest'
- );
- $this->assertEquals($expectedResponse, $this->loginController->showLoginForm('LdapUser', ''));
- }
-
- public function testShowLoginFormForUserNamed0(): void {
- $this->userSession
- ->expects($this->once())
- ->method('isLoggedIn')
- ->willReturn(false);
- $this->config
- ->expects(self::once())
- ->method('getSystemValue')
- ->willReturnMap([
- ['login_form_autocomplete', true, true],
- ]);
- $this->config
- ->expects(self::once())
- ->method('getSystemValueString')
- ->willReturnMap([
- ['lost_password_link', '', ''],
- ]);
- $user = $this->createMock(IUser::class);
- $user->expects($this->once())
- ->method('canChangePassword')
- ->willReturn(false);
- $this->userManager
- ->expects($this->once())
- ->method('get')
- ->with('0')
- ->willReturn($user);
- $calls = [
- [], [], [],
- [
- 'loginAutocomplete',
- true
- ],
- [
- 'loginCanRememberme',
- false
- ],
- [],
- [
- 'loginResetPasswordLink',
- false
- ],
- [
- 'loginCanResetPassword',
- false
- ],
- ];
- $this->initialState->expects($this->exactly(14))
- ->method('provideInitialState')
- ->willReturnCallback(function () use (&$calls): void {
- $expected = array_shift($calls);
- if (!empty($expected)) {
- $this->assertEquals($expected, func_get_args());
- }
- });
-
- $expectedResponse = new TemplateResponse(
- 'core',
- 'login',
- [
- 'alt_login' => [],
- 'pageTitle' => 'Login'
- ],
- 'guest'
- );
- $this->assertEquals($expectedResponse, $this->loginController->showLoginForm('0', ''));
- }
-
- public static function remembermeProvider(): array {
- return [
- [
- true,
- ],
- [
- false,
- ],
- ];
- }
-
- #[DataProvider('remembermeProvider')]
- public function testLoginWithInvalidCredentials(bool $rememberme): void {
- $user = 'MyUserName';
- $password = 'secret';
- $loginPageUrl = '/login?redirect_url=/apps/files';
- $loginChain = $this->createMock(LoginChain::class);
- $trustedDomainHelper = $this->createMock(ITrustedDomainHelper::class);
- $trustedDomainHelper->method('isTrustedUrl')->willReturn(true);
- $this->request
- ->expects($this->once())
- ->method('passesCSRFCheck')
- ->willReturn(true);
- $loginData = new LoginData(
- $this->request,
- $user,
- $password,
- $rememberme,
- '/apps/files'
- );
- $loginResult = LoginResult::failure(LoginController::LOGIN_MSG_INVALIDPASSWORD);
- $loginChain->expects($this->once())
- ->method('process')
- ->with($this->equalTo($loginData))
- ->willReturn($loginResult);
- $this->urlGenerator->expects($this->once())
- ->method('linkToRoute')
- ->with('core.login.showLoginForm', [
- 'user' => $user,
- 'redirect_url' => '/apps/files',
- 'direct' => 1,
- ])
- ->willReturn($loginPageUrl);
- $expected = new RedirectResponse($loginPageUrl);
- $expected->throttle(['user' => 'MyUserName']);
-
- $response = $this->loginController->tryLogin($loginChain, $trustedDomainHelper, $user, $password, $rememberme, '/apps/files');
-
- $this->assertEquals($expected, $response);
- }
-
- #[DataProvider('remembermeProvider')]
- public function testLoginWithValidCredentials(bool $rememberme): void {
- $user = 'MyUserName';
- $password = 'secret';
- $loginChain = $this->createMock(LoginChain::class);
- $trustedDomainHelper = $this->createMock(ITrustedDomainHelper::class);
- $trustedDomainHelper->method('isTrustedUrl')->willReturn(true);
- $this->request
- ->expects($this->once())
- ->method('passesCSRFCheck')
- ->willReturn(true);
- $loginData = new LoginData(
- $this->request,
- $user,
- $password,
- $rememberme,
- );
- $loginResult = LoginResult::success();
- $loginChain->expects($this->once())
- ->method('process')
- ->with($this->equalTo($loginData))
- ->willReturn($loginResult);
- $this->urlGenerator
- ->expects($this->once())
- ->method('linkToDefaultPageUrl')
- ->willReturn('/default/foo');
-
- $expected = new RedirectResponse('/default/foo');
- $this->assertEquals($expected, $this->loginController->tryLogin($loginChain, $trustedDomainHelper, $user, $password, $rememberme));
- }
-
- #[DataProvider('remembermeProvider')]
- public function testLoginWithoutPassedCsrfCheckAndNotLoggedIn(bool $rememberme): void {
- /** @var IUser|MockObject $user */
- $user = $this->createMock(IUser::class);
- $user->expects($this->any())
- ->method('getUID')
- ->willReturn('jane');
- $password = 'secret';
- $originalUrl = 'another%20url';
- $loginChain = $this->createMock(LoginChain::class);
- $trustedDomainHelper = $this->createMock(ITrustedDomainHelper::class);
- $trustedDomainHelper->method('isTrustedUrl')->willReturn(true);
- $this->request
- ->expects($this->once())
- ->method('passesCSRFCheck')
- ->willReturn(false);
- $this->userSession
- ->method('isLoggedIn')
- ->with()
- ->willReturn(false);
- $this->config->expects($this->never())
- ->method('deleteUserValue');
- $this->userSession->expects($this->never())
- ->method('createRememberMeToken');
-
- $response = $this->loginController->tryLogin($loginChain, $trustedDomainHelper, 'Jane', $password, $rememberme, $originalUrl);
-
- $expected = new RedirectResponse('');
- $this->assertEquals($expected, $response);
- }
-
- #[DataProvider('remembermeProvider')]
- public function testLoginWithoutPassedCsrfCheckAndLoggedIn(bool $rememberme): void {
- /** @var IUser|MockObject $user */
- $user = $this->createMock(IUser::class);
- $user->expects($this->any())
- ->method('getUID')
- ->willReturn('jane');
- $password = 'secret';
- $originalUrl = 'another url';
- $redirectUrl = 'http://localhost/another url';
- $loginChain = $this->createMock(LoginChain::class);
- $trustedDomainHelper = $this->createMock(ITrustedDomainHelper::class);
- $trustedDomainHelper->method('isTrustedUrl')->willReturn(true);
- $this->request
- ->expects($this->once())
- ->method('passesCSRFCheck')
- ->willReturn(false);
- $this->userSession
- ->method('isLoggedIn')
- ->with()
- ->willReturn(true);
- $this->urlGenerator->expects($this->once())
- ->method('getAbsoluteURL')
- ->with(urldecode($originalUrl))
- ->willReturn($redirectUrl);
- $this->config->expects($this->never())
- ->method('deleteUserValue');
- $this->userSession->expects($this->never())
- ->method('createRememberMeToken');
- $this->config
- ->method('getSystemValue')
- ->with('remember_login_cookie_lifetime')
- ->willReturn(1234);
-
- $response = $this->loginController->tryLogin($loginChain, $trustedDomainHelper, 'Jane', $password, $rememberme, $originalUrl);
-
- $expected = new RedirectResponse($redirectUrl);
- $this->assertEquals($expected, $response);
- }
-
- #[DataProvider('remembermeProvider')]
- public function testLoginWithValidCredentialsAndRedirectUrl(bool $rememberme): void {
- $user = 'MyUserName';
- $password = 'secret';
- $redirectUrl = 'https://next.cloud/apps/mail';
- $loginChain = $this->createMock(LoginChain::class);
- $trustedDomainHelper = $this->createMock(ITrustedDomainHelper::class);
- $trustedDomainHelper->method('isTrustedUrl')->willReturn(true);
- $this->request
- ->expects($this->once())
- ->method('passesCSRFCheck')
- ->willReturn(true);
- $loginData = new LoginData(
- $this->request,
- $user,
- $password,
- $rememberme,
- '/apps/mail'
- );
- $loginResult = LoginResult::success();
- $loginChain->expects($this->once())
- ->method('process')
- ->with($this->equalTo($loginData))
- ->willReturn($loginResult);
- $this->userSession->expects($this->once())
- ->method('isLoggedIn')
- ->willReturn(true);
- $this->urlGenerator->expects($this->once())
- ->method('getAbsoluteURL')
- ->with('/apps/mail')
- ->willReturn($redirectUrl);
- $expected = new RedirectResponse($redirectUrl);
-
- $response = $this->loginController->tryLogin($loginChain, $trustedDomainHelper, $user, $password, $rememberme, '/apps/mail');
-
- $this->assertEquals($expected, $response);
- }
-
- #[DataProvider('remembermeProvider')]
- public function testToNotLeakLoginName(bool $rememberme): void {
- $loginChain = $this->createMock(LoginChain::class);
- $trustedDomainHelper = $this->createMock(ITrustedDomainHelper::class);
- $trustedDomainHelper->method('isTrustedUrl')->willReturn(true);
- $this->request
- ->expects($this->once())
- ->method('passesCSRFCheck')
- ->willReturn(true);
- $loginPageUrl = '/login?redirect_url=/apps/files';
- $loginData = new LoginData(
- $this->request,
- 'john@doe.com',
- 'just wrong',
- $rememberme,
- '/apps/files'
- );
- $loginResult = LoginResult::failure(LoginController::LOGIN_MSG_INVALIDPASSWORD);
- $loginChain->expects($this->once())
- ->method('process')
- ->with($this->equalTo($loginData))
- ->willReturnCallback(function (LoginData $data) use ($loginResult) {
- $data->setUsername('john');
- return $loginResult;
- });
- $this->urlGenerator->expects($this->once())
- ->method('linkToRoute')
- ->with('core.login.showLoginForm', [
- 'user' => 'john@doe.com',
- 'redirect_url' => '/apps/files',
- 'direct' => 1,
- ])
- ->willReturn($loginPageUrl);
- $expected = new RedirectResponse($loginPageUrl);
- $expected->throttle(['user' => 'john']);
-
- $response = $this->loginController->tryLogin(
- $loginChain,
- $trustedDomainHelper,
- 'john@doe.com',
- 'just wrong',
- $rememberme,
- '/apps/files'
- );
-
- $this->assertEquals($expected, $response);
- }
-}
--- a/tests/Core/Controller/LostControllerTest.php
+++ /dev/null
@@ -1,782 +0,0 @@
-existingUser = $this->createMock(IUser::class);
- $this->existingUser->expects($this->any())
- ->method('getEMailAddress')
- ->willReturn('test@example.com');
- $this->existingUser->expects($this->any())
- ->method('getUID')
- ->willReturn('ExistingUser');
- $this->existingUser->expects($this->any())
- ->method('getDisplayName')
- ->willReturn('Existing User');
- $this->existingUser->expects($this->any())
- ->method('isEnabled')
- ->willReturn(true);
-
- $this->config = $this->createMock(IConfig::class);
- $this->config->expects($this->any())
- ->method('getSystemValue')
- ->willReturnCallback(function (string $key, $default = '') {
- return match ($key) {
- 'secret' => 'SECRET',
- 'lost_password_link' => $this->lostPasswordLink,
- default => $default,
- };
- });
- $this->l10n = $this->createMock(IL10N::class);
- $this->l10n
- ->expects($this->any())
- ->method('t')
- ->willReturnCallback(function ($text, $parameters = []) {
- return vsprintf($text, $parameters);
- });
- $this->defaults = $this->createMock(Defaults::class);
- $this->userManager = $this->createMock(IUserManager::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->mailer = $this->createMock(IMailer::class);
- $this->request = $this->createMock(IRequest::class);
- $this->encryptionManager = $this->createMock(IManager::class);
- $this->encryptionManager->expects($this->any())
- ->method('isEnabled')
- ->willReturn(true);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->twofactorManager = $this->createMock(Manager::class);
- $this->initialState = $this->createMock(IInitialState::class);
- $this->verificationToken = $this->createMock(IVerificationToken::class);
- $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
- $this->limiter = $this->createMock(Limiter::class);
- $this->lostController = new LostController(
- 'Core',
- $this->request,
- $this->urlGenerator,
- $this->userManager,
- $this->defaults,
- $this->l10n,
- $this->config,
- 'lostpassword-noreply@localhost',
- $this->encryptionManager,
- $this->mailer,
- $this->logger,
- $this->twofactorManager,
- $this->initialState,
- $this->verificationToken,
- $this->eventDispatcher,
- $this->limiter
- );
- }
-
- public function testResetFormTokenError(): void {
- $this->userManager->method('get')
- ->with('ValidTokenUser')
- ->willReturn($this->existingUser);
- $this->verificationToken->expects($this->once())
- ->method('check')
- ->with('12345:MySecretToken', $this->existingUser, 'lostpassword', 'test@example.com')
- ->willThrowException(new InvalidTokenException(InvalidTokenException::TOKEN_DECRYPTION_ERROR));
-
- $response = $this->lostController->resetform('12345:MySecretToken', 'ValidTokenUser');
- $expectedResponse = new TemplateResponse('core',
- 'error',
- [
- 'errors' => [
- ['error' => 'Could not reset password because the token is invalid'],
- ]
- ],
- 'guest');
- $expectedResponse->throttle();
- $this->assertEquals($expectedResponse, $response);
- }
-
- public function testResetFormTokenErrorWithDisabledLink(): void {
- $this->lostPasswordLink = 'disabled';
- $this->userManager->method('get')
- ->with('ValidTokenUser')
- ->willReturn($this->existingUser);
- $this->verificationToken->expects($this->once())
- ->method('check')
- ->with('12345:MySecretToken', $this->existingUser, 'lostpassword', 'test@example.com')
- ->willThrowException(new InvalidTokenException(InvalidTokenException::TOKEN_NOT_FOUND));
-
- $response = $this->lostController->resetform('12345:MySecretToken', 'ValidTokenUser');
- $expectedResponse = new TemplateResponse('core',
- 'error',
- [
- 'errors' => [
- ['error' => 'Password reset is disabled'],
- ]
- ],
- 'guest');
- $expectedResponse->throttle();
- $this->assertEquals($expectedResponse, $response);
- }
-
- public function testResetFormValidToken(): void {
- $this->userManager->method('get')
- ->with('ValidTokenUser')
- ->willReturn($this->existingUser);
- $this->verificationToken->expects($this->once())
- ->method('check')
- ->with('MySecretToken', $this->existingUser, 'lostpassword', 'test@example.com');
- $this->urlGenerator
- ->expects($this->once())
- ->method('linkToRouteAbsolute')
- ->with('core.lost.setPassword', ['userId' => 'ValidTokenUser', 'token' => 'MySecretToken'])
- ->willReturn('https://example.tld/index.php/lostpassword/set/sometoken/someuser');
-
- $calls = [
- ['resetPasswordUser', 'ValidTokenUser'],
- ['resetPasswordTarget', 'https://example.tld/index.php/lostpassword/set/sometoken/someuser'],
- ];
- $this->initialState
- ->expects($this->exactly(2))
- ->method('provideInitialState')
- ->willReturnCallback(function () use (&$calls): void {
- $expected = array_shift($calls);
- $this->assertEquals($expected, func_get_args());
- });
-
- $response = $this->lostController->resetform('MySecretToken', 'ValidTokenUser');
- $expectedResponse = new TemplateResponse('core',
- 'login',
- [],
- 'guest');
- $this->assertEquals($expectedResponse, $response);
- }
-
- public function testEmailUnsuccessful(): void {
- $existingUser = 'ExistingUser';
- $nonExistingUser = 'NonExistingUser';
- $this->userManager
- ->expects($this->any())
- ->method('userExists')
- ->willReturnMap([
- [true, $existingUser],
- [false, $nonExistingUser]
- ]);
-
- $this->logger->expects($this->exactly(0))
- ->method('error');
- $this->logger->expects($this->exactly(2))
- ->method('warning');
-
- $this->userManager
- ->method('getByEmail')
- ->willReturn([]);
-
- // With a non existing user
- $response = $this->lostController->email($nonExistingUser);
- $expectedResponse = new JSONResponse([
- 'status' => 'success',
- ]);
- $expectedResponse->throttle();
- $this->assertEquals($expectedResponse, $response);
-
- // With no mail address
- $this->config
- ->expects($this->any())
- ->method('getUserValue')
- ->with($existingUser, 'settings', 'email')
- ->willReturn(null);
- $response = $this->lostController->email($existingUser);
- $expectedResponse = new JSONResponse([
- 'status' => 'success',
- ]);
- $expectedResponse->throttle();
- $this->assertEquals($expectedResponse, $response);
- }
-
- public function testEmailSuccessful(): void {
- $this->userManager
- ->expects($this->any())
- ->method('get')
- ->with('ExistingUser')
- ->willReturn($this->existingUser);
- $this->verificationToken->expects($this->once())
- ->method('create')
- ->willReturn('ThisIsMaybeANotSoSecretToken!');
- $this->urlGenerator
- ->expects($this->once())
- ->method('linkToRouteAbsolute')
- ->with('core.lost.resetform', ['userId' => 'ExistingUser', 'token' => 'ThisIsMaybeANotSoSecretToken!'])
- ->willReturn('https://example.tld/index.php/lostpassword/');
- $message = $this->getMockBuilder('\OC\Mail\Message')
- ->disableOriginalConstructor()->getMock();
- $message
- ->expects($this->once())
- ->method('setTo')
- ->with(['test@example.com' => 'Existing User']);
- $message
- ->expects($this->once())
- ->method('setFrom')
- ->with(['lostpassword-noreply@localhost' => null]);
-
- $emailTemplate = $this->createMock(IEMailTemplate::class);
- $emailTemplate->expects($this->any())
- ->method('renderHtml')
- ->willReturn('HTML body');
- $emailTemplate->expects($this->any())
- ->method('renderText')
- ->willReturn('text body');
-
- $message
- ->expects($this->once())
- ->method('useTemplate')
- ->with($emailTemplate);
-
- $this->mailer
- ->expects($this->once())
- ->method('createEMailTemplate')
- ->willReturn($emailTemplate);
- $this->mailer
- ->expects($this->once())
- ->method('createMessage')
- ->willReturn($message);
- $this->mailer
- ->expects($this->once())
- ->method('send')
- ->with($message);
-
- $response = $this->lostController->email('ExistingUser');
- $expectedResponse = new JSONResponse(['status' => 'success']);
- $expectedResponse->throttle();
- $this->assertEquals($expectedResponse, $response);
- }
-
- public function testEmailWithMailSuccessful(): void {
- $this->userManager
- ->expects($this->any())
- ->method('get')
- ->with('test@example.com')
- ->willReturn(null);
- $this->userManager
- ->expects($this->any())
- ->method('getByEmail')
- ->with('test@example.com')
- ->willReturn([$this->existingUser]);
- $this->verificationToken->expects($this->once())
- ->method('create')
- ->willReturn('ThisIsMaybeANotSoSecretToken!');
- $this->urlGenerator
- ->expects($this->once())
- ->method('linkToRouteAbsolute')
- ->with('core.lost.resetform', ['userId' => 'ExistingUser', 'token' => 'ThisIsMaybeANotSoSecretToken!'])
- ->willReturn('https://example.tld/index.php/lostpassword/');
- $message = $this->getMockBuilder('\OC\Mail\Message')
- ->disableOriginalConstructor()->getMock();
- $message
- ->expects($this->once())
- ->method('setTo')
- ->with(['test@example.com' => 'Existing User']);
- $message
- ->expects($this->once())
- ->method('setFrom')
- ->with(['lostpassword-noreply@localhost' => null]);
-
- $emailTemplate = $this->createMock(IEMailTemplate::class);
- $emailTemplate->expects($this->any())
- ->method('renderHtml')
- ->willReturn('HTML body');
- $emailTemplate->expects($this->any())
- ->method('renderText')
- ->willReturn('text body');
-
- $message
- ->expects($this->once())
- ->method('useTemplate')
- ->with($emailTemplate);
-
- $this->mailer
- ->expects($this->once())
- ->method('createEMailTemplate')
- ->willReturn($emailTemplate);
- $this->mailer
- ->expects($this->once())
- ->method('createMessage')
- ->willReturn($message);
- $this->mailer
- ->expects($this->once())
- ->method('send')
- ->with($message);
-
- $response = $this->lostController->email('test@example.com');
- $expectedResponse = new JSONResponse(['status' => 'success']);
- $expectedResponse->throttle();
- $this->assertEquals($expectedResponse, $response);
- }
-
- public function testEmailCantSendException(): void {
- $this->userManager
- ->expects($this->any())
- ->method('get')
- ->with('ExistingUser')
- ->willReturn($this->existingUser);
- $this->verificationToken->expects($this->once())
- ->method('create')
- ->willReturn('ThisIsMaybeANotSoSecretToken!');
- $this->urlGenerator
- ->expects($this->once())
- ->method('linkToRouteAbsolute')
- ->with('core.lost.resetform', ['userId' => 'ExistingUser', 'token' => 'ThisIsMaybeANotSoSecretToken!'])
- ->willReturn('https://example.tld/index.php/lostpassword/');
- $message = $this->createMock(Message::class);
- $message
- ->expects($this->once())
- ->method('setTo')
- ->with(['test@example.com' => 'Existing User']);
- $message
- ->expects($this->once())
- ->method('setFrom')
- ->with(['lostpassword-noreply@localhost' => null]);
-
- $emailTemplate = $this->createMock(IEMailTemplate::class);
- $emailTemplate->expects($this->any())
- ->method('renderHtml')
- ->willReturn('HTML body');
- $emailTemplate->expects($this->any())
- ->method('renderText')
- ->willReturn('text body');
-
- $message
- ->expects($this->once())
- ->method('useTemplate')
- ->with($emailTemplate);
-
- $this->mailer
- ->expects($this->once())
- ->method('createEMailTemplate')
- ->willReturn($emailTemplate);
- $this->mailer
- ->expects($this->once())
- ->method('createMessage')
- ->willReturn($message);
- $this->mailer
- ->expects($this->once())
- ->method('send')
- ->with($message)
- ->willThrowException(new \Exception());
-
- $this->logger->expects($this->exactly(1))
- ->method('error');
-
- $response = $this->lostController->email('ExistingUser');
- $expectedResponse = new JSONResponse(['status' => 'success']);
- $expectedResponse->throttle();
- $this->assertEquals($expectedResponse, $response);
- }
-
- public function testSetPasswordUnsuccessful(): void {
- $this->config->method('getUserValue')
- ->with('ValidTokenUser', 'core', 'lostpassword', null)
- ->willReturn('encryptedData');
- $this->existingUser->method('getLastLogin')
- ->willReturn(12344);
- $this->existingUser->expects($this->once())
- ->method('setPassword')
- ->with('NewPassword')
- ->willReturn(false);
- $this->userManager->method('get')
- ->with('ValidTokenUser')
- ->willReturn($this->existingUser);
- $beforePasswordResetEvent = new BeforePasswordResetEvent($this->existingUser, 'NewPassword');
- $this->eventDispatcher
- ->expects($this->once())
- ->method('dispatchTyped')
- ->with($beforePasswordResetEvent);
- $this->config->expects($this->never())
- ->method('deleteUserValue');
-
- $response = $this->lostController->setPassword('TheOnlyAndOnlyOneTokenToResetThePassword', 'ValidTokenUser', 'NewPassword', true);
- $expectedResponse = ['status' => 'error', 'msg' => ''];
- $this->assertSame($expectedResponse, $response->getData());
- }
-
- public function testSetPasswordSuccessful(): void {
- $this->config->method('getUserValue')
- ->with('ValidTokenUser', 'core', 'lostpassword', null)
- ->willReturn('encryptedData');
- $this->existingUser->method('getLastLogin')
- ->willReturn(12344);
- $this->existingUser->expects($this->once())
- ->method('setPassword')
- ->with('NewPassword')
- ->willReturn(true);
- $this->userManager->method('get')
- ->with('ValidTokenUser')
- ->willReturn($this->existingUser);
-
- $calls = [
- [new BeforePasswordResetEvent($this->existingUser, 'NewPassword')],
- [new PasswordResetEvent($this->existingUser, 'NewPassword')],
- ];
- $this->eventDispatcher
- ->expects($this->exactly(2))
- ->method('dispatchTyped')
- ->willReturnCallback(function () use (&$calls): void {
- $expected = array_shift($calls);
- $this->assertEquals($expected, func_get_args());
- });
-
- $this->config->expects($this->once())
- ->method('deleteUserValue')
- ->with('ValidTokenUser', 'core', 'lostpassword');
-
- $response = $this->lostController->setPassword('TheOnlyAndOnlyOneTokenToResetThePassword', 'ValidTokenUser', 'NewPassword', true);
- $expectedResponse = ['user' => 'ValidTokenUser', 'status' => 'success'];
- $this->assertSame($expectedResponse, $response->getData());
- }
-
- public function testSetPasswordExpiredToken(): void {
- $this->config->method('getUserValue')
- ->with('ValidTokenUser', 'core', 'lostpassword', null)
- ->willReturn('encryptedData');
- $this->userManager->method('get')
- ->with('ValidTokenUser')
- ->willReturn($this->existingUser);
- $this->verificationToken->expects($this->atLeastOnce())
- ->method('check')
- ->willThrowException(new InvalidTokenException(InvalidTokenException::TOKEN_EXPIRED));
-
- $response = $this->lostController->setPassword('TheOnlyAndOnlyOneTokenToResetThePassword', 'ValidTokenUser', 'NewPassword', true);
- $expectedResponse = [
- 'status' => 'error',
- 'msg' => 'Could not reset password because the token is expired',
- ];
- $this->assertSame($expectedResponse, $response->getData());
- }
-
- public function testSetPasswordInvalidDataInDb(): void {
- $this->config->method('getUserValue')
- ->with('ValidTokenUser', 'core', 'lostpassword', null)
- ->willReturn('invalidEncryptedData');
- $this->userManager
- ->method('get')
- ->with('ValidTokenUser')
- ->willReturn($this->existingUser);
- $this->verificationToken->expects($this->atLeastOnce())
- ->method('check')
- ->willThrowException(new InvalidTokenException(InvalidTokenException::TOKEN_INVALID_FORMAT));
-
- $response = $this->lostController->setPassword('TheOnlyAndOnlyOneTokenToResetThePassword', 'ValidTokenUser', 'NewPassword', true);
- $expectedResponse = [
- 'status' => 'error',
- 'msg' => 'Could not reset password because the token is invalid',
- ];
- $this->assertSame($expectedResponse, $response->getData());
- }
-
- public function testIsSetPasswordWithoutTokenFailing(): void {
- $this->config->method('getUserValue')
- ->with('ValidTokenUser', 'core', 'lostpassword', null)
- ->willReturn('aValidtoken');
- $this->userManager->method('get')
- ->with('ValidTokenUser')
- ->willReturn($this->existingUser);
- $this->verificationToken->expects($this->atLeastOnce())
- ->method('check')
- ->willThrowException(new InvalidTokenException(InvalidTokenException::TOKEN_MISMATCH));
-
- $response = $this->lostController->setPassword('', 'ValidTokenUser', 'NewPassword', true);
- $expectedResponse = [
- 'status' => 'error',
- 'msg' => 'Could not reset password because the token is invalid'
- ];
- $this->assertSame($expectedResponse, $response->getData());
- }
-
- public function testSetPasswordForDisabledUser(): void {
- $user = $this->createMock(IUser::class);
- $user->expects($this->any())
- ->method('isEnabled')
- ->willReturn(false);
- $user->expects($this->never())
- ->method('setPassword');
- $user->expects($this->any())
- ->method('getEMailAddress')
- ->willReturn('random@example.org');
-
- $this->config->method('getUserValue')
- ->with('ValidTokenUser', 'core', 'lostpassword', null)
- ->willReturn('encryptedData');
- $this->userManager->method('get')
- ->with('DisabledUser')
- ->willReturn($user);
-
- $this->verificationToken->expects($this->atLeastOnce())
- ->method('check')
- ->willThrowException(new InvalidTokenException(InvalidTokenException::USER_UNKNOWN));
-
- $response = $this->lostController->setPassword('TheOnlyAndOnlyOneTokenToResetThePassword', 'DisabledUser', 'NewPassword', true);
- $expectedResponse = [
- 'status' => 'error',
- 'msg' => 'Could not reset password because the token is invalid'
- ];
- $this->assertSame($expectedResponse, $response->getData());
- }
-
- public function testSendEmailNoEmail(): void {
- $user = $this->createMock(IUser::class);
- $user->expects($this->any())
- ->method('isEnabled')
- ->willReturn(true);
- $this->userManager->method('userExists')
- ->with('ExistingUser')
- ->willReturn(true);
- $this->userManager->method('get')
- ->with('ExistingUser')
- ->willReturn($user);
-
- $this->logger->expects($this->exactly(0))
- ->method('error');
- $this->logger->expects($this->once())
- ->method('warning');
-
- $response = $this->lostController->email('ExistingUser');
- $expectedResponse = new JSONResponse(['status' => 'success']);
- $expectedResponse->throttle();
- $this->assertEquals($expectedResponse, $response);
- }
-
- public function testSetPasswordEncryptionDontProceedPerUserKey(): void {
- /** @var IEncryptionModule|MockObject $encryptionModule */
- $encryptionModule = $this->createMock(IEncryptionModule::class);
- $encryptionModule->expects($this->once())->method('needDetailedAccessList')->willReturn(true);
- $this->encryptionManager->expects($this->once())->method('getEncryptionModules')
- ->willReturn([0 => ['callback' => function () use ($encryptionModule) {
- return $encryptionModule;
- }]]);
- $response = $this->lostController->setPassword('myToken', 'user', 'newpass', false);
- $expectedResponse = ['status' => 'error', 'msg' => '', 'encryption' => true];
- $this->assertSame($expectedResponse, $response->getData());
- }
-
- public function testSetPasswordDontProceedMasterKey(): void {
- $encryptionModule = $this->createMock(IEncryptionModule::class);
- $encryptionModule->expects($this->once())->method('needDetailedAccessList')->willReturn(false);
- $this->encryptionManager->expects($this->once())->method('getEncryptionModules')
- ->willReturn([0 => ['callback' => function () use ($encryptionModule) {
- return $encryptionModule;
- }]]);
- $this->config->method('getUserValue')
- ->with('ValidTokenUser', 'core', 'lostpassword', null)
- ->willReturn('encryptedData');
- $this->existingUser->method('getLastLogin')
- ->willReturn(12344);
- $this->existingUser->expects($this->once())
- ->method('setPassword')
- ->with('NewPassword')
- ->willReturn(true);
- $this->userManager->method('get')
- ->with('ValidTokenUser')
- ->willReturn($this->existingUser);
- $this->config->expects($this->once())
- ->method('deleteUserValue')
- ->with('ValidTokenUser', 'core', 'lostpassword');
-
- $response = $this->lostController->setPassword('TheOnlyAndOnlyOneTokenToResetThePassword', 'ValidTokenUser', 'NewPassword', false);
- $expectedResponse = ['user' => 'ValidTokenUser', 'status' => 'success'];
- $this->assertSame($expectedResponse, $response->getData());
- }
-
- public function testTwoUsersWithSameEmail(): void {
- $user1 = $this->createMock(IUser::class);
- $user1->expects($this->any())
- ->method('getEMailAddress')
- ->willReturn('test@example.com');
- $user1->expects($this->any())
- ->method('getUID')
- ->willReturn('User1');
- $user1->expects($this->any())
- ->method('isEnabled')
- ->willReturn(true);
-
- $user2 = $this->createMock(IUser::class);
- $user2->expects($this->any())
- ->method('getEMailAddress')
- ->willReturn('test@example.com');
- $user2->expects($this->any())
- ->method('getUID')
- ->willReturn('User2');
- $user2->expects($this->any())
- ->method('isEnabled')
- ->willReturn(true);
-
- $this->userManager
- ->method('get')
- ->willReturn(null);
-
- $this->userManager
- ->method('getByEmail')
- ->willReturn([$user1, $user2]);
-
- $this->logger->expects($this->exactly(0))
- ->method('error');
- $this->logger->expects($this->once())
- ->method('warning');
-
- // request password reset for test@example.com
- $response = $this->lostController->email('test@example.com');
-
- $expectedResponse = new JSONResponse([
- 'status' => 'success'
- ]);
- $expectedResponse->throttle();
-
- $this->assertEquals($expectedResponse, $response);
- }
-
- /**
- * @return array
- */
- public static function dataTwoUsersWithSameEmailOneDisabled(): array {
- return [
- ['userEnabled1' => true, 'userEnabled2' => false],
- ['userEnabled1' => false, 'userEnabled2' => true]
- ];
- }
-
- /**
- * @param bool $userEnabled1
- * @param bool $userEnabled2
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTwoUsersWithSameEmailOneDisabled')]
- public function testTwoUsersWithSameEmailOneDisabled(bool $userEnabled1, bool $userEnabled2): void {
- $user1 = $this->createMock(IUser::class);
- $user1->method('getEMailAddress')
- ->willReturn('test@example.com');
- $user1->method('getUID')
- ->willReturn('User1');
- $user1->method('isEnabled')
- ->willReturn($userEnabled1);
-
- $user2 = $this->createMock(IUser::class);
- $user2->method('getEMailAddress')
- ->willReturn('test@example.com');
- $user2->method('getUID')
- ->willReturn('User2');
- $user2->method('isEnabled')
- ->willReturn($userEnabled2);
-
- $this->userManager
- ->method('get')
- ->willReturn(null);
-
- $this->userManager
- ->method('getByEmail')
- ->willReturn([$user1, $user2]);
-
- $result = self::invokePrivate($this->lostController, 'findUserByIdOrMail', ['test@example.com']);
- $this->assertInstanceOf(IUser::class, $result);
- }
-
- public function testTrimEmailInput(): void {
- $this->userManager
- ->expects($this->once())
- ->method('getByEmail')
- ->with('test@example.com')
- ->willReturn([$this->existingUser]);
-
- $this->mailer
- ->expects($this->once())
- ->method('send');
-
- $response = $this->lostController->email(' test@example.com ');
- $expectedResponse = new JSONResponse(['status' => 'success']);
- $expectedResponse->throttle();
- $this->assertEquals($expectedResponse, $response);
- }
-
- public function testUsernameInput(): void {
- $this->userManager
- ->expects($this->once())
- ->method('get')
- ->with('ExistingUser')
- ->willReturn($this->existingUser);
-
- $this->mailer
- ->expects($this->once())
- ->method('send');
-
- $response = $this->lostController->email(' ExistingUser ');
- $expectedResponse = new JSONResponse(['status' => 'success']);
- $expectedResponse->throttle();
- $this->assertEquals($expectedResponse, $response);
- }
-}
--- a/tests/Core/Controller/NavigationControllerTest.php
+++ /dev/null
@@ -1,139 +0,0 @@
-request = $this->createMock(IRequest::class);
- $this->navigationManager = $this->createMock(INavigationManager::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
-
- $this->controller = new NavigationController(
- 'core',
- $this->request,
- $this->navigationManager,
- $this->urlGenerator
- );
- }
-
- public static function dataGetNavigation(): array {
- return [
- [false],
- [true],
- ];
- }
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetNavigation')]
- public function testGetAppNavigation(bool $absolute): void {
- $this->navigationManager->expects($this->once())
- ->method('getAll')
- ->with('link')
- ->willReturn(['files' => ['id' => 'files', 'href' => '/index.php/apps/files', 'icon' => 'icon' ] ]);
- if ($absolute) {
- $this->urlGenerator->expects($this->any())
- ->method('getBaseURL')
- ->willReturn('http://localhost/');
- $this->urlGenerator->expects($this->exactly(2))
- ->method('getAbsoluteURL')
- ->willReturnMap([
- ['/index.php/apps/files', 'http://localhost/index.php/apps/files'],
- ['icon', 'http://localhost/icon'],
- ]);
- $actual = $this->controller->getAppsNavigation($absolute);
- $this->assertInstanceOf(DataResponse::class, $actual);
- $this->assertEquals('http://localhost/index.php/apps/files', $actual->getData()[0]['href']);
- $this->assertEquals('http://localhost/icon', $actual->getData()[0]['icon']);
- } else {
- $actual = $this->controller->getAppsNavigation($absolute);
- $this->assertInstanceOf(DataResponse::class, $actual);
- $this->assertEquals('/index.php/apps/files', $actual->getData()[0]['href']);
- $this->assertEquals('icon', $actual->getData()[0]['icon']);
- }
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetNavigation')]
- public function testGetSettingsNavigation(bool $absolute): void {
- $this->navigationManager->expects($this->once())
- ->method('getAll')
- ->with('settings')
- ->willReturn(['settings' => ['id' => 'settings', 'href' => '/index.php/settings/user', 'icon' => '/core/img/settings.svg'] ]);
- if ($absolute) {
- $this->urlGenerator->expects($this->any())
- ->method('getBaseURL')
- ->willReturn('http://localhost/');
- $this->urlGenerator->expects($this->exactly(2))
- ->method('getAbsoluteURL')
- ->willReturnMap([
- ['/index.php/settings/user', 'http://localhost/index.php/settings/user'],
- ['/core/img/settings.svg', 'http://localhost/core/img/settings.svg']
- ]);
- $actual = $this->controller->getSettingsNavigation($absolute);
- $this->assertInstanceOf(DataResponse::class, $actual);
- $this->assertEquals('http://localhost/index.php/settings/user', $actual->getData()[0]['href']);
- $this->assertEquals('http://localhost/core/img/settings.svg', $actual->getData()[0]['icon']);
- } else {
- $actual = $this->controller->getSettingsNavigation($absolute);
- $this->assertInstanceOf(DataResponse::class, $actual);
- $this->assertEquals('/index.php/settings/user', $actual->getData()[0]['href']);
- $this->assertEquals('/core/img/settings.svg', $actual->getData()[0]['icon']);
- }
- }
-
- public function testEtagIgnoresLogout(): void {
- $navigation1 = [
- ['id' => 'files', 'href' => '/index.php/apps/files', 'icon' => 'icon' ],
- ['id' => 'logout', 'href' => '/index.php/logout?requesttoken=abcd', 'icon' => 'icon' ],
- ];
- $navigation2 = [
- ['id' => 'files', 'href' => '/index.php/apps/files', 'icon' => 'icon' ],
- ['id' => 'logout', 'href' => '/index.php/logout?requesttoken=1234', 'icon' => 'icon' ],
- ];
- $navigation3 = [
- ['id' => 'files', 'href' => '/index.php/apps/files/test', 'icon' => 'icon' ],
- ['id' => 'logout', 'href' => '/index.php/logout?requesttoken=1234', 'icon' => 'icon' ],
- ];
- $this->navigationManager->expects($this->exactly(3))
- ->method('getAll')
- ->with('link')
- ->willReturnOnConsecutiveCalls(
- $navigation1,
- $navigation2,
- $navigation3,
- );
-
- // Changes in the logout url should not change the ETag
- $request1 = $this->controller->getAppsNavigation();
- $request2 = $this->controller->getAppsNavigation();
- $this->assertEquals($request1->getETag(), $request2->getETag());
-
- // Changes in non-logout urls should result in a different ETag
- $request3 = $this->controller->getAppsNavigation();
- $this->assertNotEquals($request2->getETag(), $request3->getETag());
- }
-}
--- a/tests/Core/Controller/OCSControllerTest.php
+++ /dev/null
@@ -1,220 +0,0 @@
-request = $this->createMock(IRequest::class);
- $this->capabilitiesManager = $this->createMock(CapabilitiesManager::class);
- $this->userSession = $this->createMock(IUserSession::class);
- $this->userManager = $this->createMock(IUserManager::class);
- $this->keyManager = $this->createMock(Manager::class);
- $serverVersion = Server::get(ServerVersion::class);
-
- $this->controller = new OCSController(
- 'core',
- $this->request,
- $this->capabilitiesManager,
- $this->userSession,
- $this->userManager,
- $this->keyManager,
- $serverVersion
- );
- }
-
- public function testGetConfig() {
- $this->request->method('getServerHost')
- ->willReturn('awesomehost.io');
-
- $data = [
- 'version' => '1.7',
- 'website' => 'Nextcloud',
- 'host' => 'awesomehost.io',
- 'contact' => '',
- 'ssl' => 'false',
- ];
-
- $expected = new DataResponse($data);
- $this->assertEquals($expected, $this->controller->getConfig());
-
- return new DataResponse($data);
- }
-
- public function testGetCapabilities(): void {
- $this->userSession->expects($this->once())
- ->method('isLoggedIn')
- ->willReturn(true);
-
- $serverVersion = Server::get(ServerVersion::class);
-
- $result = [];
- $result['version'] = [
- 'major' => $serverVersion->getMajorVersion(),
- 'minor' => $serverVersion->getMinorVersion(),
- 'micro' => $serverVersion->getPatchVersion(),
- 'string' => $serverVersion->getVersionString(),
- 'edition' => '',
- 'extendedSupport' => false
- ];
-
- $capabilities = [
- 'foo' => 'bar',
- 'a' => [
- 'b' => true,
- 'c' => 11,
- ]
- ];
- $this->capabilitiesManager->method('getCapabilities')
- ->willReturn($capabilities);
-
- $result['capabilities'] = $capabilities;
-
- $expected = new DataResponse($result);
- $expected->setETag(md5(json_encode($result)));
- $this->assertEquals($expected, $this->controller->getCapabilities());
- }
-
- public function testGetCapabilitiesPublic(): void {
- $this->userSession->expects($this->once())
- ->method('isLoggedIn')
- ->willReturn(false);
- $serverVersion = Server::get(ServerVersion::class);
-
- $result = [];
- $result['version'] = [
- 'major' => $serverVersion->getMajorVersion(),
- 'minor' => $serverVersion->getMinorVersion(),
- 'micro' => $serverVersion->getPatchVersion(),
- 'string' => $serverVersion->getVersionString(),
- 'edition' => '',
- 'extendedSupport' => false
- ];
-
- $capabilities = [
- 'foo' => 'bar',
- 'a' => [
- 'b' => true,
- 'c' => 11,
- ]
- ];
- $this->capabilitiesManager->method('getCapabilities')
- ->with(true)
- ->willReturn($capabilities);
-
- $result['capabilities'] = $capabilities;
-
- $expected = new DataResponse($result);
- $expected->setETag(md5(json_encode($result)));
- $this->assertEquals($expected, $this->controller->getCapabilities());
- }
-
- public function testPersonCheckValid(): void {
- $this->userManager->method('checkPassword')
- ->with(
- $this->equalTo('user'),
- $this->equalTo('pass')
- )->willReturn($this->createMock(IUser::class));
-
- $expected = new DataResponse([
- 'person' => [
- 'personid' => 'user'
- ]
- ]);
- $this->assertEquals($expected, $this->controller->personCheck('user', 'pass'));
- }
-
- public function testPersonInvalid(): void {
- $this->userManager->method('checkPassword')
- ->with(
- $this->equalTo('user'),
- $this->equalTo('wrongpass')
- )->willReturn(false);
-
- $expected = new DataResponse([], 102);
- $expected->throttle();
- $this->assertEquals($expected, $this->controller->personCheck('user', 'wrongpass'));
- }
-
- public function testPersonNoLogin(): void {
- $this->userManager->method('checkPassword')
- ->with(
- $this->equalTo('user'),
- $this->equalTo('wrongpass')
- )->willReturn(false);
-
- $expected = new DataResponse([], 101);
- $this->assertEquals($expected, $this->controller->personCheck('', ''));
- }
-
- public function testGetIdentityProofWithNotExistingUser(): void {
- $this->userManager
- ->expects($this->once())
- ->method('get')
- ->with('NotExistingUser')
- ->willReturn(null);
-
- $expected = new DataResponse(['Account not found'], 404);
- $this->assertEquals($expected, $this->controller->getIdentityProof('NotExistingUser'));
- }
-
- public function testGetIdentityProof(): void {
- $user = $this->createMock(IUser::class);
- $key = $this->createMock(Key::class);
- $this->userManager
- ->expects($this->once())
- ->method('get')
- ->with('ExistingUser')
- ->willReturn($user);
- $this->keyManager
- ->expects($this->once())
- ->method('getKey')
- ->with($user)
- ->willReturn($key);
- $key
- ->expects($this->once())
- ->method('getPublic')
- ->willReturn('Existing Users public key');
-
- $expected = new DataResponse([
- 'public' => 'Existing Users public key',
- ]);
- $this->assertEquals($expected, $this->controller->getIdentityProof('ExistingUser'));
- }
-}
--- a/tests/Core/Controller/OpenMetricsControllerTest.php
+++ /dev/null
@@ -1,111 +0,0 @@
-request = $this->createMock(IRequest::class);
- $this->request->method('getRemoteAddress')
- ->willReturn('192.168.1.1');
- $this->config = $this->createMock(IConfig::class);
- $this->exporterManager = $this->createMock(ExporterManager::class);
- $this->exporterManager->method('export')->willReturnCallback([$this, 'getFakeMetrics']);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->controller = new OpenMetricsController('core', $this->request, $this->config, $this->exporterManager, $this->logger);
- }
-
- public function getFakeMetrics(): Generator {
- $metric = $this->createMock(IMetricFamily::class);
- $metric->method('type')->willReturn(MetricType::gauge);
- $metric->method('unit')->willReturn('fake');
- $metric->method('name')->willReturn('fake_count');
- $metric->method('help')->willReturn('A fake count used for tests');
- $metric->method('metrics')->willReturnCallback(function () {
- yield new Metric(42, ['type' => 'used']);
- yield new Metric(24, ['type' => 'unused']);
- });
- yield $metric;
- }
- public function testGetMetrics(): void {
- $output = $this->createMock(IOutput::class);
- $fullOutput = '';
- $output->method('setOutput')
- ->willReturnCallback(function ($output) use (&$fullOutput): void {
- $fullOutput .= $output;
- });
- $this->config->expects($this->once())
- ->method('getSystemValue')
- ->with('openmetrics_allowed_clients')
- ->willReturn(['192.168.0.0/16']);
- $response = $this->controller->export();
- $this->assertInstanceOf(StreamTraversableResponse::class, $response);
- $this->assertEquals('200', $response->getStatus());
- $this->assertEquals('application/openmetrics-text; version=1.0.0; charset=utf-8', $response->getHeaders()['Content-Type']);
- $expected = <<callback($output);
- $this->assertStringMatchesFormat($expected, $fullOutput);
- }
-
- public function testMetricRejectsInvalidLabelNames(): void {
- $this->expectException(\InvalidArgumentException::class);
- new Metric(1, ['hide-photos' => '1.0.0']);
- }
-
- public function testMetricAcceptsValidLabelNames(): void {
- $metric = new Metric(1, ['hide_photos' => '1.0.0', 'normal_app' => '2.0.0']);
- $this->assertEquals('1.0.0', $metric->label('hide_photos'));
- $this->assertEquals('2.0.0', $metric->label('normal_app'));
- }
-
- public function testGetMetricsFromForbiddenIp(): void {
- $this->config->expects($this->once())
- ->method('getSystemValue')
- ->with('openmetrics_allowed_clients')
- ->willReturn(['1.2.3.4']);
- $response = $this->controller->export();
- $this->assertInstanceOf(Response::class, $response);
- $this->assertEquals('403', $response->getStatus());
- }
-}
--- a/tests/Core/Controller/PreviewControllerTest.php
+++ /dev/null
@@ -1,368 +0,0 @@
-userId = 'user';
- $this->rootFolder = $this->createMock(IRootFolder::class);
- $this->previewManager = $this->createMock(IPreview::class);
- $this->request = $this->createMock(IRequest::class);
-
- $this->controller = new PreviewController(
- 'core',
- $this->request,
- $this->previewManager,
- $this->rootFolder,
- $this->userId,
- $this->createMock(IMimeIconProvider::class)
- );
- }
-
- public function testInvalidFile(): void {
- $res = $this->controller->getPreview('');
- $expected = new DataResponse([], Http::STATUS_BAD_REQUEST);
-
- $this->assertEquals($expected, $res);
- }
-
- public function testInvalidWidth(): void {
- $res = $this->controller->getPreview('file', 0);
- $expected = new DataResponse([], Http::STATUS_BAD_REQUEST);
-
- $this->assertEquals($expected, $res);
- }
-
- public function testInvalidHeight(): void {
- $res = $this->controller->getPreview('file', 10, 0);
- $expected = new DataResponse([], Http::STATUS_BAD_REQUEST);
-
- $this->assertEquals($expected, $res);
- }
-
- public function testFileNotFound(): void {
- $userFolder = $this->createMock(Folder::class);
- $this->rootFolder->method('getUserFolder')
- ->with($this->equalTo($this->userId))
- ->willReturn($userFolder);
-
- $userFolder->method('get')
- ->with($this->equalTo('file'))
- ->willThrowException(new NotFoundException());
-
- $res = $this->controller->getPreview('file');
- $expected = new DataResponse([], Http::STATUS_NOT_FOUND);
-
- $this->assertEquals($expected, $res);
- }
-
- public function testNotAFile(): void {
- $userFolder = $this->createMock(Folder::class);
- $this->rootFolder->method('getUserFolder')
- ->with($this->equalTo($this->userId))
- ->willReturn($userFolder);
-
- $folder = $this->createMock(Folder::class);
- $userFolder->method('get')
- ->with($this->equalTo('file'))
- ->willReturn($folder);
-
- $res = $this->controller->getPreview('file');
- $expected = new DataResponse([], Http::STATUS_NOT_FOUND);
-
- $this->assertEquals($expected, $res);
- }
-
- public function testNoPreviewAndNoIcon(): void {
- $userFolder = $this->createMock(Folder::class);
- $this->rootFolder->method('getUserFolder')
- ->with($this->equalTo($this->userId))
- ->willReturn($userFolder);
-
- $file = $this->createMock(File::class);
- $userFolder->method('get')
- ->with($this->equalTo('file'))
- ->willReturn($file);
-
- $this->previewManager->method('isAvailable')
- ->with($this->equalTo($file))
- ->willReturn(false);
-
- $res = $this->controller->getPreview('file', 10, 10, true, false);
- $expected = new DataResponse([], Http::STATUS_NOT_FOUND);
-
- $this->assertEquals($expected, $res);
- }
-
- public function testNoPreview() {
- $userFolder = $this->createMock(Folder::class);
- $this->rootFolder->method('getUserFolder')
- ->with($this->equalTo($this->userId))
- ->willReturn($userFolder);
-
- $file = $this->createMock(File::class);
- $userFolder->method('get')
- ->with($this->equalTo('file'))
- ->willReturn($file);
-
- $storage = $this->createMock(IStorage::class);
- $file->method('getStorage')
- ->willReturn($storage);
-
- $this->previewManager->method('isAvailable')
- ->with($this->equalTo($file))
- ->willReturn(true);
-
- $file->method('isReadable')
- ->willReturn(true);
-
- $this->previewManager->method('getPreview')
- ->with($this->equalTo($file), 10, 10, false, $this->equalTo('myMode'))
- ->willThrowException(new NotFoundException());
-
- $res = $this->controller->getPreview('file', 10, 10, true, true, 'myMode');
- $expected = new DataResponse([], Http::STATUS_NOT_FOUND);
-
- $this->assertEquals($expected, $res);
- }
- public function testFileWithoutReadPermission() {
- $userFolder = $this->createMock(Folder::class);
- $this->rootFolder->method('getUserFolder')
- ->with($this->equalTo($this->userId))
- ->willReturn($userFolder);
-
- $file = $this->createMock(File::class);
- $userFolder->method('get')
- ->with($this->equalTo('file'))
- ->willReturn($file);
-
- $this->previewManager->method('isAvailable')
- ->with($this->equalTo($file))
- ->willReturn(true);
-
- $file->method('isReadable')
- ->willReturn(false);
-
- $res = $this->controller->getPreview('file', 10, 10, true, true);
- $expected = new DataResponse([], Http::STATUS_FORBIDDEN);
-
- $this->assertEquals($expected, $res);
- }
-
- public function testFileWithoutDownloadPermission() {
- $userFolder = $this->createMock(Folder::class);
- $this->rootFolder->method('getUserFolder')
- ->with($this->equalTo($this->userId))
- ->willReturn($userFolder);
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(123);
- $userFolder->method('get')
- ->with($this->equalTo('file'))
- ->willReturn($file);
-
- $this->previewManager->method('isAvailable')
- ->with($this->equalTo($file))
- ->willReturn(true);
-
- $share = $this->createMock(IShare::class);
- $share->method('canSeeContent')
- ->willReturn(false);
-
- $storage = $this->createMock(ISharedStorage::class);
- $storage->method('instanceOfStorage')
- ->with(ISharedStorage::class)
- ->willReturn(true);
- $storage->method('getShare')
- ->willReturn($share);
-
- $file->method('getStorage')
- ->willReturn($storage);
- $file->method('isReadable')
- ->willReturn(true);
-
- $this->request->method('getHeader')->willReturn('');
-
- $res = $this->controller->getPreview('file', 10, 10, true, true);
- $expected = new DataResponse([], Http::STATUS_FORBIDDEN);
-
- $this->assertEquals($expected, $res);
- }
-
- public function testFileWithoutDownloadPermissionButHeader() {
- $userFolder = $this->createMock(Folder::class);
- $this->rootFolder->method('getUserFolder')
- ->with($this->equalTo($this->userId))
- ->willReturn($userFolder);
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(123);
- $userFolder->method('get')
- ->with($this->equalTo('file'))
- ->willReturn($file);
-
- $this->previewManager->method('isAvailable')
- ->with($this->equalTo($file))
- ->willReturn(true);
-
- $share = $this->createMock(IShare::class);
- $share->method('canSeeContent')
- ->willReturn(false);
-
- $storage = $this->createMock(ISharedStorage::class);
- $storage->method('instanceOfStorage')
- ->with(ISharedStorage::class)
- ->willReturn(true);
- $storage->method('getShare')
- ->willReturn($share);
-
- $file->method('getStorage')
- ->willReturn($storage);
- $file->method('isReadable')
- ->willReturn(true);
-
- $this->request
- ->method('getHeader')
- ->with('x-nc-preview')
- ->willReturn('true');
-
- $preview = $this->createMock(ISimpleFile::class);
- $preview->method('getName')->willReturn('my name');
- $preview->method('getMTime')->willReturn(42);
- $this->previewManager->method('getPreview')
- ->with($this->equalTo($file), 10, 10, false, $this->equalTo('myMode'))
- ->willReturn($preview);
- $preview->method('getMimeType')
- ->willReturn('myMime');
-
- $res = $this->controller->getPreview('file', 10, 10, true, true, 'myMode');
-
- $this->assertEquals('myMime', $res->getHeaders()['Content-Type']);
- $this->assertEquals(Http::STATUS_OK, $res->getStatus());
- $this->assertEquals($preview, $this->invokePrivate($res, 'file'));
- }
-
- public function testValidPreview(): void {
- $userFolder = $this->createMock(Folder::class);
- $this->rootFolder->method('getUserFolder')
- ->with($this->equalTo($this->userId))
- ->willReturn($userFolder);
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(123);
- $userFolder->method('get')
- ->with($this->equalTo('file'))
- ->willReturn($file);
-
- $this->previewManager->method('isAvailable')
- ->with($this->equalTo($file))
- ->willReturn(true);
-
- $file->method('isReadable')
- ->willReturn(true);
-
- $storage = $this->createMock(IStorage::class);
- $file->method('getStorage')
- ->willReturn($storage);
-
- $preview = $this->createMock(ISimpleFile::class);
- $preview->method('getName')->willReturn('my name');
- $preview->method('getMTime')->willReturn(42);
- $this->previewManager->method('getPreview')
- ->with($this->equalTo($file), 10, 10, false, $this->equalTo('myMode'))
- ->willReturn($preview);
- $preview->method('getMimeType')
- ->willReturn('myMime');
-
- $res = $this->controller->getPreview('file', 10, 10, true, true, 'myMode');
-
- $this->assertEquals('myMime', $res->getHeaders()['Content-Type']);
- $this->assertEquals(Http::STATUS_OK, $res->getStatus());
- $this->assertEquals($preview, $this->invokePrivate($res, 'file'));
- }
-
- public function testValidPreviewOfShare() {
- $userFolder = $this->createMock(Folder::class);
- $this->rootFolder->method('getUserFolder')
- ->with($this->equalTo($this->userId))
- ->willReturn($userFolder);
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(123);
- $userFolder->method('get')
- ->with($this->equalTo('file'))
- ->willReturn($file);
-
- $this->previewManager->method('isAvailable')
- ->with($this->equalTo($file))
- ->willReturn(true);
-
- // No attributes set -> download permitted
- $share = $this->createMock(IShare::class);
- $share->method('canSeeContent')
- ->willReturn(true);
-
- $storage = $this->createMock(ISharedStorage::class);
- $storage->method('instanceOfStorage')
- ->with(ISharedStorage::class)
- ->willReturn(true);
- $storage->method('getShare')
- ->willReturn($share);
-
- $file->method('getStorage')
- ->willReturn($storage);
- $file->method('isReadable')
- ->willReturn(true);
-
- $this->request
- ->method('getHeader')
- ->willReturn('');
-
- $preview = $this->createMock(ISimpleFile::class);
- $preview->method('getName')->willReturn('my name');
- $preview->method('getMTime')->willReturn(42);
- $this->previewManager->method('getPreview')
- ->with($this->equalTo($file), 10, 10, false, $this->equalTo('myMode'))
- ->willReturn($preview);
- $preview->method('getMimeType')
- ->willReturn('myMime');
-
- $res = $this->controller->getPreview('file', 10, 10, true, true, 'myMode');
-
- $this->assertEquals('myMime', $res->getHeaders()['Content-Type']);
- $this->assertEquals(Http::STATUS_OK, $res->getStatus());
- $this->assertEquals($preview, $this->invokePrivate($res, 'file'));
- }
-}
--- a/tests/Core/Controller/TwoFactorChallengeControllerTest.php
+++ /dev/null
@@ -1,451 +0,0 @@
-request = $this->createMock(IRequest::class);
- $this->twoFactorManager = $this->createMock(Manager::class);
- $this->userSession = $this->createMock(IUserSession::class);
- $this->session = $this->createMock(ISession::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $this->controller = $this->getMockBuilder(TwoFactorChallengeController::class)
- ->setConstructorArgs([
- 'core',
- $this->request,
- $this->twoFactorManager,
- $this->userSession,
- $this->session,
- $this->urlGenerator,
- $this->logger,
- ])
- ->onlyMethods(['getLogoutUrl'])
- ->getMock();
- $this->controller->expects($this->any())
- ->method('getLogoutUrl')
- ->willReturn('logoutAttribute');
- }
-
- public function testSelectChallenge(): void {
- $user = $this->getMockBuilder(IUser::class)->getMock();
- $p1 = $this->createMock(IActivatableAtLogin::class);
- $p1->method('getId')->willReturn('p1');
- $backupProvider = $this->createMock(IProvider::class);
- $backupProvider->method('getId')->willReturn('backup_codes');
- $providerSet = new ProviderSet([$p1, $backupProvider], true);
- $this->twoFactorManager->expects($this->once())
- ->method('getLoginSetupProviders')
- ->with($user)
- ->willReturn([$p1]);
-
- $this->userSession->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
- $this->twoFactorManager->expects($this->once())
- ->method('getProviderSet')
- ->with($user)
- ->willReturn($providerSet);
-
- $expected = new StandaloneTemplateResponse('core', 'twofactorselectchallenge', [
- 'providers' => [
- $p1,
- ],
- 'providerMissing' => true,
- 'backupProvider' => $backupProvider,
- 'redirect_url' => '/some/url',
- 'logout_url' => 'logoutAttribute',
- 'hasSetupProviders' => true,
- ], 'guest');
-
- $this->assertEquals($expected, $this->controller->selectChallenge('/some/url'));
- }
-
- public function testShowChallenge(): void {
- $user = $this->createMock(IUser::class);
- $provider = $this->createMock(IProvider::class);
- $provider->method('getId')->willReturn('myprovider');
- $backupProvider = $this->createMock(IProvider::class);
- $backupProvider->method('getId')->willReturn('backup_codes');
- $tmpl = $this->createMock(ITemplate::class);
- $providerSet = new ProviderSet([$provider, $backupProvider], true);
-
- $this->userSession->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
- $this->twoFactorManager->expects($this->once())
- ->method('getProviderSet')
- ->with($user)
- ->willReturn($providerSet);
- $provider->expects($this->once())
- ->method('getId')
- ->willReturn('u2f');
- $backupProvider->expects($this->once())
- ->method('getId')
- ->willReturn('backup_codes');
-
- $this->session->expects($this->once())
- ->method('exists')
- ->with('two_factor_auth_error')
- ->willReturn(true);
- $this->session->expects($this->exactly(2))
- ->method('remove')
- ->with($this->logicalOr($this->equalTo('two_factor_auth_error'), $this->equalTo('two_factor_auth_error_message')));
- $provider->expects($this->once())
- ->method('getTemplate')
- ->with($user)
- ->willReturn($tmpl);
- $tmpl->expects($this->once())
- ->method('fetchPage')
- ->willReturn(' ');
-
- $expected = new StandaloneTemplateResponse('core', 'twofactorshowchallenge', [
- 'error' => true,
- 'provider' => $provider,
- 'backupProvider' => $backupProvider,
- 'logout_url' => 'logoutAttribute',
- 'template' => ' ',
- 'redirect_url' => '/re/dir/ect/url',
- 'error_message' => null,
- ], 'guest');
-
- $this->assertEquals($expected, $this->controller->showChallenge('myprovider', '/re/dir/ect/url'));
- }
-
- public function testShowInvalidChallenge(): void {
- $user = $this->createMock(IUser::class);
- $providerSet = new ProviderSet([], false);
-
- $this->userSession->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
- $this->twoFactorManager->expects($this->once())
- ->method('getProviderSet')
- ->with($user)
- ->willReturn($providerSet);
- $this->urlGenerator->expects($this->once())
- ->method('linkToRoute')
- ->with('core.TwoFactorChallenge.selectChallenge')
- ->willReturn('select/challenge/url');
-
- $expected = new RedirectResponse('select/challenge/url');
-
- $this->assertEquals($expected, $this->controller->showChallenge('myprovider', 'redirect/url'));
- }
-
- public function testSolveChallenge(): void {
- $user = $this->createMock(IUser::class);
- $provider = $this->createMock(IProvider::class);
-
- $this->userSession->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
- $this->twoFactorManager->expects($this->once())
- ->method('getProvider')
- ->with($user, 'myprovider')
- ->willReturn($provider);
-
- $this->twoFactorManager->expects($this->once())
- ->method('verifyChallenge')
- ->with('myprovider', $user, 'token')
- ->willReturn(true);
- $this->urlGenerator
- ->expects($this->once())
- ->method('linkToDefaultPageUrl')
- ->willReturn('/default/foo');
-
- $expected = new RedirectResponse('/default/foo');
- $this->assertEquals($expected, $this->controller->solveChallenge('myprovider', 'token'));
- }
-
- public function testSolveValidChallengeAndRedirect(): void {
- $user = $this->createMock(IUser::class);
- $provider = $this->createMock(IProvider::class);
-
- $this->userSession->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
- $this->twoFactorManager->expects($this->once())
- ->method('getProvider')
- ->with($user, 'myprovider')
- ->willReturn($provider);
-
- $this->twoFactorManager->expects($this->once())
- ->method('verifyChallenge')
- ->with('myprovider', $user, 'token')
- ->willReturn(true);
- $this->urlGenerator->expects($this->once())
- ->method('getAbsoluteURL')
- ->with('redirect url')
- ->willReturn('redirect/url');
-
- $expected = new RedirectResponse('redirect/url');
- $this->assertEquals($expected, $this->controller->solveChallenge('myprovider', 'token', 'redirect%20url'));
- }
-
- public function testSolveChallengeInvalidProvider(): void {
- $user = $this->getMockBuilder(IUser::class)->getMock();
-
- $this->userSession->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
- $this->twoFactorManager->expects($this->once())
- ->method('getProvider')
- ->with($user, 'myprovider')
- ->willReturn(null);
- $this->urlGenerator->expects($this->once())
- ->method('linkToRoute')
- ->with('core.TwoFactorChallenge.selectChallenge')
- ->willReturn('select/challenge/url');
-
- $expected = new RedirectResponse('select/challenge/url');
-
- $this->assertEquals($expected, $this->controller->solveChallenge('myprovider', 'token'));
- }
-
- public function testSolveInvalidChallenge(): void {
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('myuser');
- $provider = $this->createMock(IProvider::class);
-
- $this->userSession->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
- $this->twoFactorManager->expects($this->once())
- ->method('getProvider')
- ->with($user, 'myprovider')
- ->willReturn($provider);
-
- $this->twoFactorManager->expects($this->once())
- ->method('verifyChallenge')
- ->with('myprovider', $user, 'token')
- ->willReturn(false);
- $this->session->expects($this->once())
- ->method('set')
- ->with('two_factor_auth_error', true);
- $this->urlGenerator->expects($this->once())
- ->method('linkToRoute')
- ->with('core.TwoFactorChallenge.showChallenge', [
- 'challengeProviderId' => 'myprovider',
- 'redirect_url' => '/url',
- ])
- ->willReturn('files/index/url');
- $provider->expects($this->once())
- ->method('getId')
- ->willReturn('myprovider');
-
- $expected = new RedirectResponse('files/index/url');
- $expected->throttle(['user' => 'myuser', 'provider' => 'myprovider']);
- $this->assertEquals($expected, $this->controller->solveChallenge('myprovider', 'token', '/url'));
- }
-
- public function testSolveChallengeTwoFactorException(): void {
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('myuser');
- $provider = $this->createMock(IProvider::class);
- $exception = new TwoFactorException('2FA failed');
-
- $this->userSession->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
- $this->twoFactorManager->expects($this->once())
- ->method('getProvider')
- ->with($user, 'myprovider')
- ->willReturn($provider);
-
- $this->twoFactorManager->expects($this->once())
- ->method('verifyChallenge')
- ->with('myprovider', $user, 'token')
- ->willThrowException($exception);
- $calls = [
- ['two_factor_auth_error_message', '2FA failed'],
- ['two_factor_auth_error', true],
- ];
- $this->session->expects($this->exactly(2))
- ->method('set')
- ->willReturnCallback(function () use (&$calls): void {
- $expected = array_shift($calls);
- $this->assertEquals($expected, func_get_args());
- });
- $this->urlGenerator->expects($this->once())
- ->method('linkToRoute')
- ->with('core.TwoFactorChallenge.showChallenge', [
- 'challengeProviderId' => 'myprovider',
- 'redirect_url' => '/url',
- ])
- ->willReturn('files/index/url');
- $provider->expects($this->once())
- ->method('getId')
- ->willReturn('myprovider');
-
- $expected = new RedirectResponse('files/index/url');
- $expected->throttle(['user' => 'myuser', 'provider' => 'myprovider']);
- $this->assertEquals($expected, $this->controller->solveChallenge('myprovider', 'token', '/url'));
- }
-
- public function testSetUpProviders(): void {
- $user = $this->createMock(IUser::class);
- $this->userSession->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
- $provider = $this->createMock(IActivatableAtLogin::class);
- $this->twoFactorManager->expects($this->once())
- ->method('getLoginSetupProviders')
- ->with($user)
- ->willReturn([
- $provider,
- ]);
- $expected = new StandaloneTemplateResponse(
- 'core',
- 'twofactorsetupselection',
- [
- 'providers' => [
- $provider,
- ],
- 'logout_url' => 'logoutAttribute',
- 'redirect_url' => null,
- ],
- 'guest'
- );
-
- $response = $this->controller->setupProviders();
-
- $this->assertEquals($expected, $response);
- }
-
- public function testSetUpInvalidProvider(): void {
- $user = $this->createMock(IUser::class);
- $this->userSession->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
- $provider = $this->createMock(IActivatableAtLogin::class);
- $provider->expects($this->any())
- ->method('getId')
- ->willReturn('prov1');
- $this->twoFactorManager->expects($this->once())
- ->method('getLoginSetupProviders')
- ->with($user)
- ->willReturn([
- $provider,
- ]);
- $this->urlGenerator->expects($this->once())
- ->method('linkToRoute')
- ->with('core.TwoFactorChallenge.selectChallenge')
- ->willReturn('2fa/select/page');
- $expected = new RedirectResponse('2fa/select/page');
-
- $response = $this->controller->setupProvider('prov2');
-
- $this->assertEquals($expected, $response);
- }
-
- public function testSetUpProvider(): void {
- $user = $this->createMock(IUser::class);
- $this->userSession->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
- $provider = $this->createMock(IActivatableAtLogin::class);
- $provider->expects($this->any())
- ->method('getId')
- ->willReturn('prov1');
- $this->twoFactorManager->expects($this->once())
- ->method('getLoginSetupProviders')
- ->with($user)
- ->willReturn([
- $provider,
- ]);
- $loginSetup = $this->createMock(ILoginSetupProvider::class);
- $provider->expects($this->any())
- ->method('getLoginSetup')
- ->with($user)
- ->willReturn($loginSetup);
- $tmpl = $this->createMock(ITemplate::class);
- $loginSetup->expects($this->once())
- ->method('getBody')
- ->willReturn($tmpl);
- $tmpl->expects($this->once())
- ->method('fetchPage')
- ->willReturn('tmpl');
- $expected = new StandaloneTemplateResponse(
- 'core',
- 'twofactorsetupchallenge',
- [
- 'provider' => $provider,
- 'logout_url' => 'logoutAttribute',
- 'template' => 'tmpl',
- 'redirect_url' => null,
- ],
- 'guest'
- );
-
- $response = $this->controller->setupProvider('prov1');
-
- $this->assertEquals($expected, $response);
- }
-
- public function testConfirmProviderSetup(): void {
- $this->urlGenerator->expects($this->once())
- ->method('linkToRoute')
- ->with(
- 'core.TwoFactorChallenge.showChallenge',
- [
- 'challengeProviderId' => 'totp',
- 'redirect_url' => null,
- ])
- ->willReturn('2fa/select/page');
- $expected = new RedirectResponse('2fa/select/page');
-
- $response = $this->controller->confirmProviderSetup('totp');
-
- $this->assertEquals($expected, $response);
- }
-}
--- a/tests/Core/Controller/UserControllerTest.php
+++ /dev/null
@@ -1,61 +0,0 @@
-userManager = $this->createMock(IUserManager::class);
- $this->controller = new UserController(
- 'core',
- $this->createMock(IRequest::class),
- $this->userManager
- );
- }
-
- public function testGetDisplayNames(): void {
- $user = $this->createMock(IUser::class);
- $user->method('getDisplayName')
- ->willReturn('FooDisplay Name');
-
- $this->userManager
- ->method('get')
- ->willReturnCallback(function ($uid) use ($user) {
- if ($uid === 'foo') {
- return $user;
- }
- return null;
- });
-
- $expected = new JSONResponse([
- 'users' => [
- 'foo' => 'FooDisplay Name',
- 'bar' => 'bar',
- ],
- 'status' => 'success'
- ]);
-
- $result = $this->controller->getDisplayNames(['foo', 'bar']);
- $this->assertEquals($expected, $result);
- }
-}
--- a/tests/Core/Controller/WellKnownControllerTest.php
+++ /dev/null
@@ -1,70 +0,0 @@
-request = $this->createMock(IRequest::class);
- $this->manager = $this->createMock(RequestManager::class);
-
- $this->controller = new WellKnownController(
- $this->request,
- $this->manager,
- );
- }
-
- public function testHandleNotProcessed(): void {
- $httpResponse = $this->controller->handle('nodeinfo');
-
- self::assertInstanceOf(JSONResponse::class, $httpResponse);
- self::assertArrayHasKey('X-NEXTCLOUD-WELL-KNOWN', $httpResponse->getHeaders());
- }
-
- public function testHandle(): void {
- $response = $this->createMock(IResponse::class);
- $jsonResponse = $this->createMock(JSONResponse::class);
- $response->expects(self::once())
- ->method('toHttpResponse')
- ->willReturn($jsonResponse);
- $this->manager->expects(self::once())
- ->method('process')
- ->with(
- 'nodeinfo',
- $this->request
- )->willReturn($response);
- $jsonResponse->expects(self::once())
- ->method('addHeader')
- ->willReturnSelf();
-
- $httpResponse = $this->controller->handle('nodeinfo');
-
- self::assertInstanceOf(JSONResponse::class, $httpResponse);
- }
-}
--- a/tests/Core/Controller/WipeControllerTest.php
+++ /dev/null
@@ -1,102 +0,0 @@
-remoteWipe = $this->createMock(RemoteWipe::class);
- $this->controller = new WipeController(
- 'core',
- $this->createMock(IRequest::class),
- $this->remoteWipe);
- }
-
- public static function dataTest(): array {
- return [
- // valid token, could perform operation, valid result
- [ true, true, true],
- [ true, false, false],
- [false, true, false],
- [false, false, false],
- ];
- }
-
- /**
- * @param bool $valid
- * @param bool $couldPerform
- * @param bool $result
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTest')]
- public function testCheckWipe(bool $valid, bool $couldPerform, bool $result): void {
- if (!$valid) {
- $this->remoteWipe->method('start')
- ->with('mytoken')
- ->willThrowException(new InvalidTokenException());
- } else {
- $this->remoteWipe->method('start')
- ->with('mytoken')
- ->willReturn($couldPerform);
- }
-
- $result = $this->controller->checkWipe('mytoken');
-
- if (!$valid || !$couldPerform) {
- $this->assertSame(Http::STATUS_NOT_FOUND, $result->getStatus());
- $this->assertSame([], $result->getData());
- } else {
- $this->assertSame(Http::STATUS_OK, $result->getStatus());
- $this->assertSame(['wipe' => true], $result->getData());
- }
- }
-
- /**
- * @param bool $valid
- * @param bool $couldPerform
- * @param bool $result
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTest')]
- public function testWipeDone(bool $valid, bool $couldPerform, bool $result): void {
- if (!$valid) {
- $this->remoteWipe->method('finish')
- ->with('mytoken')
- ->willThrowException(new InvalidTokenException());
- } else {
- $this->remoteWipe->method('finish')
- ->with('mytoken')
- ->willReturn($couldPerform);
- }
-
- $result = $this->controller->wipeDone('mytoken');
-
- if (!$valid || !$couldPerform) {
- $this->assertSame(Http::STATUS_NOT_FOUND, $result->getStatus());
- $this->assertSame([], $result->getData());
- } else {
- $this->assertSame(Http::STATUS_OK, $result->getStatus());
- $this->assertSame([], $result->getData());
- }
- }
-}
--- a/tests/Core/Data/LoginFlowV2CredentialsTest.php
+++ /dev/null
@@ -1,48 +0,0 @@
-fixture = new LoginFlowV2Credentials('server', 'login', 'pass');
- }
-
- public function testImplementsJsonSerializable(): void {
- $this->assertTrue($this->fixture instanceof JsonSerializable);
- }
-
- /**
- * Test getter functions.
- */
- public function testGetter(): void {
- $this->assertEquals('server', $this->fixture->getServer());
- $this->assertEquals('login', $this->fixture->getLoginName());
- $this->assertEquals('pass', $this->fixture->getAppPassword());
- }
-
- public function testJsonSerialize(): void {
- $this->assertEquals(
- [
- 'server' => 'server',
- 'loginName' => 'login',
- 'appPassword' => 'pass',
- ],
- $this->fixture->jsonSerialize()
- );
- }
-}
--- a/tests/Core/Listener/RestrictInteractionListenerTest.php
+++ /dev/null
@@ -1,223 +0,0 @@
-createUser('user', 'password');
- $this->assertNotFalse($user);
- $this->user = $user;
-
- Server::get(ISetupManager::class)->setupForUser($user);
-
- /** @var CappedMemoryCache $cache */
- $cache = self::invokePrivate(Server::get(ShareDisableChecker::class), 'sharingDisabledForUsersCache');
- $cache->clear();
- }
-
- #[\Override]
- protected function tearDown(): void {
- Server::get(ISetupManager::class)->tearDown();
-
- $this->assertTrue($this->user->delete());
-
- parent::tearDown();
- }
-
- /** @psalm-suppress DeprecatedMethod The configs are not migrated to IAppConfig, so using deprecated IConfig is required for now. */
- public function testShareActionApiDisabled(): void {
- $config = Server::get(IConfig::class);
- $config->setAppValue('core', 'shareapi_enabled', 'no');
-
- $event = new RestrictInteractionEvent($this->user->getUID(), $this->user, [], new ShareAction(), []);
- $this->assertEquals('Sharing is not allowed.', $event->isInteractionRestricted());
-
- $config->deleteAppValue('core', 'shareapi_enabled');
- }
-
- /** @psalm-suppress DeprecatedMethod The configs are not migrated to IAppConfig, so using deprecated IConfig is required for now. */
- public function testShareActionDisabledForUser(): void {
- $group = Server::get(IGroupManager::class)->createGroup('group');
- $this->assertNotNull($group);
- $group->addUser($this->user);
-
- $config = Server::get(IConfig::class);
- $config->setAppValue('core', 'shareapi_exclude_groups', 'yes');
- $config->setAppValue('core', 'shareapi_exclude_groups_list', json_encode([$group->getGID()], JSON_THROW_ON_ERROR));
-
- $event = new RestrictInteractionEvent($this->user->getUID(), $this->user, [], new ShareAction(), []);
- $this->assertEquals('Sharing is not allowed for you.', $event->isInteractionRestricted());
-
- $config->deleteAppValue('core', 'shareapi_exclude_groups');
- $config->deleteAppValue('core', 'shareapi_exclude_groups_list');
-
- $this->assertTrue($group->delete());
- }
-
- /** @psalm-suppress DeprecatedMethod The configs are not migrated to IAppConfig, so using deprecated IConfig is required for now. */
- public function testShareActionUserReceiverGroupMembersOnly(): void {
- $user2 = Server::get(IUserManager::class)->createUser('user2', 'password');
- $this->assertNotFalse($user2);
-
- $group = Server::get(IGroupManager::class)->createGroup('group');
- $this->assertNotNull($group);
- $group->addUser($this->user);
-
- $config = Server::get(IConfig::class);
- $config->setAppValue('core', 'shareapi_only_share_with_group_members', 'yes');
-
- $event = new RestrictInteractionEvent($this->user->getUID(), $this->user, [], new ShareAction(), [new UserReceiver($user2->getUID(), $user2)]);
- $this->assertEquals('Sharing is only allowed with group members.', $event->isInteractionRestricted());
-
- $config->deleteAppValue('core', 'shareapi_only_share_with_group_members');
-
- $this->assertTrue($group->delete());
- $this->assertTrue($user2->delete());
- }
-
- /** @psalm-suppress DeprecatedMethod The configs are not migrated to IAppConfig, so using deprecated IConfig is required for now. */
- public function testShareActionUserReceiverGroupMembersOnlyExclude(): void {
- $user2 = Server::get(IUserManager::class)->createUser('user2', 'password');
- $this->assertNotFalse($user2);
-
- $group = Server::get(IGroupManager::class)->createGroup('group');
- $this->assertNotNull($group);
- $group->addUser($this->user);
- $group->addUser($user2);
-
- $config = Server::get(IConfig::class);
- $config->setAppValue('core', 'shareapi_only_share_with_group_members', 'yes');
- $config->setAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list', json_encode([$group->getGID()], JSON_THROW_ON_ERROR));
-
- $event = new RestrictInteractionEvent($this->user->getUID(), $this->user, [], new ShareAction(), [new UserReceiver($user2->getUID(), $user2)]);
- $this->assertEquals('Sharing is only allowed with group members.', $event->isInteractionRestricted());
-
- $config->deleteAppValue('core', 'shareapi_only_share_with_group_members');
- $config->deleteAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list');
-
- $this->assertTrue($group->delete());
- $this->assertTrue($user2->delete());
- }
-
- /** @psalm-suppress DeprecatedMethod The configs are not migrated to IAppConfig, so using deprecated IConfig is required for now. */
- public function testShareActionGroupReceiverGroupMembersOnly(): void {
- $group = Server::get(IGroupManager::class)->createGroup('group');
- $this->assertNotNull($group);
-
- $config = Server::get(IConfig::class);
- $config->setAppValue('core', 'shareapi_only_share_with_group_members', 'yes');
-
- $event = new RestrictInteractionEvent($this->user->getUID(), $this->user, [], new ShareAction(), [new GroupReceiver($group->getGID(), $group)]);
- $this->assertEquals('Sharing is only allowed within your own groups.', $event->isInteractionRestricted());
-
- $config->deleteAppValue('core', 'shareapi_only_share_with_group_members');
-
- $this->assertTrue($group->delete());
- }
-
- /** @psalm-suppress DeprecatedMethod The configs are not migrated to IAppConfig, so using deprecated IConfig is required for now. */
- public function testShareActionGroupReceiverGroupMembersOnlyExclude(): void {
- $group = Server::get(IGroupManager::class)->createGroup('group');
- $this->assertNotNull($group);
- $group->addUser($this->user);
-
- $config = Server::get(IConfig::class);
- $config->setAppValue('core', 'shareapi_only_share_with_group_members', 'yes');
- $config->setAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list', json_encode([$group->getGID()], JSON_THROW_ON_ERROR));
-
- $event = new RestrictInteractionEvent($this->user->getUID(), $this->user, [], new ShareAction(), [new GroupReceiver($group->getGID(), $group)]);
- $this->assertEquals('Sharing is only allowed within your own groups.', $event->isInteractionRestricted());
-
- $config->deleteAppValue('core', 'shareapi_only_share_with_group_members');
- $config->deleteAppValue('core', 'shareapi_only_share_with_group_members_exclude_group_list');
-
- $this->assertTrue($group->delete());
- }
-
- /** @psalm-suppress DeprecatedMethod The configs are not migrated to IAppConfig, so using deprecated IConfig is required for now. */
- public function testShareActionGroupReceiverGroupSharingDisabled(): void {
- $group = Server::get(IGroupManager::class)->createGroup('group');
- $this->assertNotNull($group);
- $group->addUser($this->user);
-
- $config = Server::get(IConfig::class);
- $config->setAppValue('core', 'shareapi_allow_group_sharing', 'no');
-
- $event = new RestrictInteractionEvent($this->user->getUID(), $this->user, [], new ShareAction(), [new GroupReceiver($group->getGID(), $group)]);
- $this->assertEquals('Group sharing is not allowed.', $event->isInteractionRestricted());
-
- $config->deleteAppValue('core', 'shareapi_allow_group_sharing');
-
- $this->assertTrue($group->delete());
- }
-
- /** @psalm-suppress DeprecatedMethod The configs are not migrated to IAppConfig, so using deprecated IConfig is required for now. */
- public function testShareActionLinkEmailReceiverLinkSharingDisabled(): void {
- $config = Server::get(IConfig::class);
- $config->setAppValue('core', 'shareapi_allow_links', 'no');
-
- foreach ([
- new LinkReceiver(),
- new EmailReceiver(''),
- ] as $receiver) {
- $event = new RestrictInteractionEvent($this->user->getUID(), $this->user, [], new ShareAction(), [$receiver]);
- $this->assertEquals('Public link sharing is not allowed.', $event->isInteractionRestricted());
- }
-
- $config->deleteAppValue('core', 'shareapi_allow_links');
- }
-
- /** @psalm-suppress DeprecatedMethod The configs are not migrated to IAppConfig, so using deprecated IConfig is required for now. */
- public function testShareActionRemoteUserReceiverServer2ServerSharingDisabled(): void {
- $config = Server::get(IConfig::class);
- $config->setAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'no');
-
- $event = new RestrictInteractionEvent($this->user->getUID(), $this->user, [], new ShareAction(), [new RemoteUserReceiver('')]);
- $this->assertEquals('Sharing to remote users is not allowed.', $event->isInteractionRestricted());
-
- $config->deleteAppValue('files_sharing', 'outgoing_server2server_share_enabled');
- }
-
- /** @psalm-suppress DeprecatedMethod The configs are not migrated to IAppConfig, so using deprecated IConfig is required for now. */
- public function testShareActionRemoteGroupReceiverServer2ServerGroupSharingDisabled(): void {
- $config = Server::get(IConfig::class);
- $config->setAppValue('files_sharing', 'outgoing_server2server_group_share_enabled', 'no');
-
- $event = new RestrictInteractionEvent($this->user->getUID(), $this->user, [], new ShareAction(), [new RemoteGroupReceiver('')]);
- $this->assertEquals('Sharing to remote groups is not allowed.', $event->isInteractionRestricted());
-
- $config->deleteAppValue('files_sharing', 'outgoing_server2server_group_share_enabled');
- }
-}
--- a/tests/Core/Middleware/TwoFactorMiddlewareTest.php
+++ /dev/null
@@ -1,305 +0,0 @@
-twoFactorManager = $this->getMockBuilder(Manager::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->userSession = $this->getMockBuilder(Session::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->session = $this->createMock(ISession::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->reflector = $this->createMock(ControllerMethodReflector::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->request = new Request(
- [
- 'server' => [
- 'REQUEST_URI' => 'test/url'
- ]
- ],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
-
- $this->middleware = new TwoFactorMiddleware($this->twoFactorManager, $this->userSession, $this->session, $this->urlGenerator, $this->reflector, $this->request);
- }
-
- public function testBeforeControllerNotLoggedIn(): void {
- $this->userSession->expects($this->once())
- ->method('isLoggedIn')
- ->willReturn(false);
-
- $this->userSession->expects($this->never())
- ->method('getUser');
-
- $controller = $this->getMockBuilder(NoTwoFactorAnnotationController::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->middleware->beforeController($controller, 'index');
- }
-
- public function testBeforeSetupController(): void {
- $user = $this->createMock(IUser::class);
- $this->userSession->expects($this->any())
- ->method('getUser')
- ->willReturn($user);
- $this->twoFactorManager->expects($this->once())
- ->method('needsSecondFactor')
- ->willReturn(true);
- $this->userSession->expects($this->never())
- ->method('isLoggedIn');
-
- $this->middleware->beforeController(new LoginSetupController('foo', $this->request), 'index');
- }
-
- public function testBeforeControllerNoTwoFactorCheckNeeded(): void {
- $user = $this->createMock(IUser::class);
-
- $this->userSession->expects($this->once())
- ->method('isLoggedIn')
- ->willReturn(true);
- $this->userSession->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
- $this->twoFactorManager->expects($this->once())
- ->method('isTwoFactorAuthenticated')
- ->with($user)
- ->willReturn(false);
-
- $controller = $this->getMockBuilder(NoTwoFactorAnnotationController::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->middleware->beforeController($controller, 'index');
- }
-
- public function testBeforeControllerTwoFactorAuthRequired(): void {
- $this->expectException(TwoFactorAuthRequiredException::class);
-
- $user = $this->createMock(IUser::class);
-
- $this->userSession->expects($this->once())
- ->method('isLoggedIn')
- ->willReturn(true);
- $this->userSession->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
- $this->twoFactorManager->expects($this->once())
- ->method('isTwoFactorAuthenticated')
- ->with($user)
- ->willReturn(true);
- $this->twoFactorManager->expects($this->once())
- ->method('needsSecondFactor')
- ->with($user)
- ->willReturn(true);
-
- $controller = $this->getMockBuilder(NoTwoFactorAnnotationController::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->middleware->beforeController($controller, 'index');
- }
-
- public function testBeforeControllerUserAlreadyLoggedIn(): void {
- $this->expectException(UserAlreadyLoggedInException::class);
-
- $user = $this->createMock(IUser::class);
-
- $this->userSession->expects($this->once())
- ->method('isLoggedIn')
- ->willReturn(true);
- $this->userSession
- ->method('getUser')
- ->willReturn($user);
- $this->twoFactorManager->expects($this->once())
- ->method('isTwoFactorAuthenticated')
- ->with($user)
- ->willReturn(true);
- $this->twoFactorManager->expects($this->once())
- ->method('needsSecondFactor')
- ->with($user)
- ->willReturn(false);
-
- $twoFactorChallengeController = $this->getMockBuilder(NoTwoFactorChallengeAnnotationController::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->middleware->beforeController($twoFactorChallengeController, 'index');
- }
-
- public function testAfterExceptionTwoFactorAuthRequired(): void {
- $ex = new TwoFactorAuthRequiredException();
-
- $this->urlGenerator->expects($this->once())
- ->method('linkToRoute')
- ->with('core.TwoFactorChallenge.selectChallenge')
- ->willReturn('test/url');
- $expected = new RedirectResponse('test/url');
-
- $controller = new HasTwoFactorAnnotationController('foo', $this->request);
- $this->assertEquals($expected, $this->middleware->afterException($controller, 'index', $ex));
- }
-
- public function testAfterException(): void {
- $ex = new UserAlreadyLoggedInException();
-
- $this->urlGenerator->expects($this->once())
- ->method('linkToRoute')
- ->with('files.view.index')
- ->willReturn('redirect/url');
- $expected = new RedirectResponse('redirect/url');
-
- $controller = new HasTwoFactorAnnotationController('foo', $this->request);
- $this->assertEquals($expected, $this->middleware->afterException($controller, 'index', $ex));
- }
-
- public function testRequires2FASetupDoneAnnotated(): void {
- $user = $this->createMock(IUser::class);
-
- $this->userSession->expects($this->once())
- ->method('isLoggedIn')
- ->willReturn(true);
- $this->userSession
- ->method('getUser')
- ->willReturn($user);
- $this->twoFactorManager->expects($this->once())
- ->method('isTwoFactorAuthenticated')
- ->with($user)
- ->willReturn(true);
- $this->twoFactorManager->expects($this->once())
- ->method('needsSecondFactor')
- ->with($user)
- ->willReturn(false);
-
- $this->expectException(UserAlreadyLoggedInException::class);
-
- $controller = $this->getMockBuilder(HasTwoFactorSetUpDoneAnnotationController::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->middleware->beforeController($controller, 'index');
- }
-
- public static function dataRequires2FASetupDone(): array {
- return [
- [false, false, false],
- [false, true, true],
- [true, false, true],
- [true, true, true],
- ];
- }
-
- #[DataProvider('dataRequires2FASetupDone')]
- public function testRequires2FASetupDone(bool $hasProvider, bool $missingProviders, bool $expectEception): void {
- if ($hasProvider) {
- $provider = $this->createMock(IProvider::class);
- $provider->method('getId')
- ->willReturn('2FAftw');
- $providers = [$provider];
- } else {
- $providers = [];
- }
-
- $user = $this->createMock(IUser::class);
-
- $this->userSession
- ->method('getUser')
- ->willReturn($user);
- $providerSet = new ProviderSet($providers, $missingProviders);
- $this->twoFactorManager->method('getProviderSet')
- ->with($user)
- ->willReturn($providerSet);
- $this->userSession
- ->method('isLoggedIn')
- ->willReturn(false);
-
- if ($expectEception) {
- $this->expectException(TwoFactorAuthRequiredException::class);
- } else {
- // hack to make phpunit shut up. Since we don't expect an exception here...
- $this->assertTrue(true);
- }
-
- $controller = $this->getMockBuilder(NoTwoFactorChallengeAnnotationController::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->middleware->beforeController($controller, 'index');
- }
-}
--- a/tests/Core/Notification/CoreNotifierTest.php
+++ /dev/null
@@ -1,55 +0,0 @@
-createMock(IL10N::class);
- $l10n->method('t')->willReturnArgument(0);
- $l10n->method('n')->willReturnCallback(
- fn (string $singular, string $plural, int $count, array $parameters = []): string
- => vsprintf(str_replace('%n', (string)$count, $count === 1 ? $singular : $plural), $parameters)
- );
- $factory = $this->createMock(IFactory::class);
- $factory->method('get')->with('core', 'de')->willReturn($l10n);
-
- $url = $this->createMock(IURLGenerator::class);
- $url->method('linkToRouteAbsolute')
- ->with('settings.AdminSettings.index', ['section' => 'overview'])
- ->willReturn('https://cloud.example.com/settings/admin/overview');
- $url->method('getAbsoluteURL')->willReturn('https://cloud.example.com/core/img/actions/error.svg');
-
- $notification = new Notification($this->createMock(IValidator::class), $this->createMock(IRichTextFormatter::class));
- $notification->setApp('core')
- ->setSubject('code_integrity_changed', ['files' => 1, 'unverified' => ['core']]);
-
- $notifier = new CoreNotifier($this->createMock(IConfig::class), $factory, $url);
- $notifier->prepare($notification, 'de');
-
- $this->assertSame('The code integrity check result has changed', $notification->getParsedSubject());
- $this->assertSame(
- "1 file does not match the signed release. It may have been modified or added without authorization.\n"
- . "The signature of core is missing or invalid, so it could not be verified.\n"
- . 'Review the results in the administration overview.',
- $notification->getParsedMessage(),
- );
- $this->assertSame('https://cloud.example.com/settings/admin/overview', $notification->getLink());
- }
-}
--- a/tests/Core/Service/LoginFlowV2ServiceUnitTest.php
+++ /dev/null
@@ -1,449 +0,0 @@
-setupSubjectUnderTest();
- }
-
- /**
- * Setup subject under test with mocked constructor arguments.
- *
- * Code was moved to separate function to keep setUp function small and clear.
- */
- private function setupSubjectUnderTest(): void {
- $this->config = $this->createMock(IConfig::class);
- $this->crypto = $this->createMock(ICrypto::class);
- $this->mapper = $this->createMock(LoginFlowV2Mapper::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->tokenProvider = $this->createMock(IProvider::class);
- $this->secureRandom = $this->createMock(ISecureRandom::class);
- $this->timeFactory = $this->createMock(ITimeFactory::class);
-
- $this->subjectUnderTest = new LoginFlowV2Service(
- $this->mapper,
- $this->secureRandom,
- $this->timeFactory,
- $this->config,
- $this->crypto,
- $this->logger,
- $this->tokenProvider
- );
- }
-
- /**
- * Generates for a given password required OpenSSL parts.
- *
- * @return array Array contains encrypted password, private key and public key.
- */
- private function getOpenSSLEncryptedPublicAndPrivateKey(string $appPassword): array {
- // Create the private and public key
- $res = openssl_pkey_new([
- 'digest_alg' => 'md5', // take fast algorithm for testing purposes
- 'private_key_bits' => 512,
- 'private_key_type' => OPENSSL_KEYTYPE_RSA,
- ]);
-
- // Extract the private key from $res
- openssl_pkey_export($res, $privateKey);
-
- // Extract the public key from $res
- $publicKey = openssl_pkey_get_details($res);
- $publicKey = $publicKey['key'];
-
- // Encrypt the data to $encrypted using the public key
- openssl_public_encrypt($appPassword, $encrypted, $publicKey, OPENSSL_PKCS1_OAEP_PADDING);
-
- return [$encrypted, $privateKey, $publicKey];
- }
-
- /*
- * Tests for poll
- */
- public function testPollPrivateKeyCouldNotBeDecrypted(): void {
- $this->expectException(LoginFlowV2NotFoundException::class);
- $this->expectExceptionMessage('Apptoken could not be decrypted');
-
- $this->crypto->expects($this->once())
- ->method('decrypt')
- ->willThrowException(new \Exception('HMAC mismatch'));
-
- /*
- * Cannot be mocked, because functions like getLoginName are magic functions.
- * To be able to set internal properties, we have to use the real class here.
- */
- $loginFlowV2 = new LoginFlowV2();
- $loginFlowV2->setLoginName('test');
- $loginFlowV2->setServer('test');
- $loginFlowV2->setAppPassword('test');
- $loginFlowV2->setPrivateKey('test');
-
- $this->mapper->expects($this->once())
- ->method('getByPollToken')
- ->willReturn($loginFlowV2);
-
- $this->subjectUnderTest->poll('');
- }
-
- public function testPollApptokenCouldNotBeDecrypted(): void {
- $this->expectException(LoginFlowV2NotFoundException::class);
- $this->expectExceptionMessage('Apptoken could not be decrypted');
-
- /*
- * Cannot be mocked, because functions like getLoginName are magic functions.
- * To be able to set internal properties, we have to use the real class here.
- */
- [$encrypted, $privateKey,] = $this->getOpenSSLEncryptedPublicAndPrivateKey('test');
- $loginFlowV2 = new LoginFlowV2();
- $loginFlowV2->setLoginName('test');
- $loginFlowV2->setServer('test');
- $loginFlowV2->setAppPassword('broken#' . $encrypted);
- $loginFlowV2->setPrivateKey('encrypted(test)');
-
- $this->crypto->expects($this->once())
- ->method('decrypt')
- ->willReturn($privateKey);
-
- $this->mapper->expects($this->once())
- ->method('getByPollToken')
- ->willReturn($loginFlowV2);
-
- $this->subjectUnderTest->poll('test');
- }
-
- public function testPollInvalidToken(): void {
- $this->expectException(LoginFlowV2NotFoundException::class);
- $this->expectExceptionMessage('Invalid token');
-
- $this->mapper->expects($this->once())
- ->method('getByPollToken')
- ->willThrowException(new DoesNotExistException(''));
-
- $this->subjectUnderTest->poll('');
- }
-
- public function testPollTokenNotYetReady(): void {
- $this->expectException(LoginFlowV2NotFoundException::class);
- $this->expectExceptionMessage('Token not yet ready');
-
- $this->subjectUnderTest->poll('');
- }
-
- public function testPollRemoveDataFromDb(): void {
- [$encrypted, $privateKey] = $this->getOpenSSLEncryptedPublicAndPrivateKey('test_pass');
-
- $this->crypto->expects($this->once())
- ->method('decrypt')
- ->willReturn($privateKey);
-
- /*
- * Cannot be mocked, because functions like getLoginName are magic functions.
- * To be able to set internal properties, we have to use the real class here.
- */
- $loginFlowV2 = new LoginFlowV2();
- $loginFlowV2->setLoginName('test_login');
- $loginFlowV2->setServer('test_server');
- $loginFlowV2->setAppPassword(base64_encode($encrypted));
- $loginFlowV2->setPrivateKey($privateKey);
-
- $this->mapper->expects($this->once())
- ->method('delete')
- ->with($this->equalTo($loginFlowV2));
-
- $this->mapper->expects($this->once())
- ->method('getByPollToken')
- ->willReturn($loginFlowV2);
-
- $credentials = $this->subjectUnderTest->poll('');
-
- $this->assertTrue($credentials instanceof LoginFlowV2Credentials);
- $this->assertEquals(
- [
- 'server' => 'test_server',
- 'loginName' => 'test_login',
- 'appPassword' => 'test_pass',
- ],
- $credentials->jsonSerialize()
- );
- }
-
- /*
- * Tests for getByLoginToken
- */
-
- public function testGetByLoginToken(): void {
- $loginFlowV2 = new LoginFlowV2();
- $loginFlowV2->setLoginName('test_login');
- $loginFlowV2->setServer('test_server');
- $loginFlowV2->setAppPassword('test');
-
- $this->mapper->expects($this->once())
- ->method('getByLoginToken')
- ->willReturn($loginFlowV2);
-
- $result = $this->subjectUnderTest->getByLoginToken('test_token');
-
- $this->assertTrue($result instanceof LoginFlowV2);
- $this->assertEquals('test_server', $result->getServer());
- $this->assertEquals('test_login', $result->getLoginName());
- $this->assertEquals('test', $result->getAppPassword());
- }
-
- public function testGetByLoginTokenLoginTokenInvalid(): void {
- $this->expectException(LoginFlowV2NotFoundException::class);
- $this->expectExceptionMessage('Login token invalid');
-
- $this->mapper->expects($this->once())
- ->method('getByLoginToken')
- ->willThrowException(new DoesNotExistException(''));
-
- $this->subjectUnderTest->getByLoginToken('test_token');
- }
-
- public function testGetByLoginTokenClientForbidden() {
- $this->expectException(LoginFlowV2ClientForbiddenException::class);
- $this->expectExceptionMessage('Client not allowed');
-
- $allowedClients = [
- '/Custom Allowed Client/i'
- ];
-
- $this->config->expects($this->exactly(1))
- ->method('getSystemValue')
- ->willReturnCallback(function ($key) use ($allowedClients) {
- // Note: \OCP\IConfig::getSystemValue returns either an array or string.
- return $key == 'core.login_flow_v2.allowed_user_agents' ? $allowedClients : '';
- });
-
- $loginFlowV2 = new LoginFlowV2();
- $loginFlowV2->setClientName('Rogue Curl Client/1.0');
-
- $this->mapper->expects($this->once())
- ->method('getByLoginToken')
- ->willReturn($loginFlowV2);
-
- $this->subjectUnderTest->getByLoginToken('test_token');
- }
-
- public function testGetByLoginTokenClientAllowed() {
- $allowedClients = [
- '/Foo Allowed Client/i',
- '/Custom Allowed Client/i'
- ];
-
- $loginFlowV2 = new LoginFlowV2();
- $loginFlowV2->setClientName('Custom Allowed Client Curl Client/1.0');
-
- $this->config->expects($this->exactly(1))
- ->method('getSystemValue')
- ->willReturnCallback(function ($key) use ($allowedClients) {
- // Note: \OCP\IConfig::getSystemValue returns either an array or string.
- return $key == 'core.login_flow_v2.allowed_user_agents' ? $allowedClients : '';
- });
-
- $this->mapper->expects($this->once())
- ->method('getByLoginToken')
- ->willReturn($loginFlowV2);
-
- $result = $this->subjectUnderTest->getByLoginToken('test_token');
-
- $this->assertTrue($result instanceof LoginFlowV2);
- $this->assertEquals('Custom Allowed Client Curl Client/1.0', $result->getClientName());
- }
-
- /*
- * Tests for startLoginFlow
- */
-
- public function testStartLoginFlow(): void {
- $loginFlowV2 = new LoginFlowV2();
-
- $this->mapper->expects($this->once())
- ->method('getByLoginToken')
- ->willReturn($loginFlowV2);
-
- $this->mapper->expects($this->once())
- ->method('update');
-
- $this->assertTrue($this->subjectUnderTest->startLoginFlow('test_token'));
- }
-
- public function testStartLoginFlowDoesNotExistException(): void {
- $this->mapper->expects($this->once())
- ->method('getByLoginToken')
- ->willThrowException(new DoesNotExistException(''));
-
- $this->assertFalse($this->subjectUnderTest->startLoginFlow('test_token'));
- }
-
- /**
- * If an exception not of type DoesNotExistException is thrown,
- * it is expected that it is not being handled by startLoginFlow.
- */
- public function testStartLoginFlowException(): void {
- $this->expectException(Exception::class);
-
- $this->mapper->expects($this->once())
- ->method('getByLoginToken')
- ->willThrowException(new Exception(''));
-
- $this->subjectUnderTest->startLoginFlow('test_token');
- }
-
- /*
- * Tests for flowDone
- */
-
- public function testFlowDone(): void {
- [,, $publicKey] = $this->getOpenSSLEncryptedPublicAndPrivateKey('test_pass');
-
- $loginFlowV2 = new LoginFlowV2();
- $loginFlowV2->setPublicKey($publicKey);
- $loginFlowV2->setClientName('client_name');
-
- $this->mapper->expects($this->once())
- ->method('getByLoginToken')
- ->willReturn($loginFlowV2);
-
- $this->mapper->expects($this->once())
- ->method('update');
-
- $this->secureRandom->expects($this->once())
- ->method('generate')
- ->with(72, ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS)
- ->willReturn('test_pass');
-
- // session token
- $sessionToken = $this->getMockBuilder(IToken::class)->disableOriginalConstructor()->getMock();
- $sessionToken->expects($this->once())
- ->method('getLoginName')
- ->willReturn('login_name');
-
- $this->tokenProvider->expects($this->once())
- ->method('getPassword')
- ->willReturn('test_pass');
-
- $this->tokenProvider->expects($this->once())
- ->method('getToken')
- ->willReturn($sessionToken);
-
- $this->tokenProvider->expects($this->once())
- ->method('generateToken')
- ->with(
- 'test_pass',
- 'user_id',
- 'login_name',
- 'test_pass',
- 'client_name',
- IToken::PERMANENT_TOKEN,
- IToken::DO_NOT_REMEMBER
- );
-
- $result = $this->subjectUnderTest->flowDone(
- 'login_token',
- 'session_id',
- 'server',
- 'user_id'
- );
- $this->assertTrue($result);
-
- // app password is encrypted and must look like:
- // ZACZOOzxTpKz4+KXL5kZ/gCK0xvkaVi/8yzupAn6Ui6+5qCSKvfPKGgeDRKs0sivvSLzk/XSp811SZCZmH0Y3g==
- $this->assertMatchesRegularExpression('/[a-zA-Z\/0-9+=]+/', $loginFlowV2->getAppPassword());
-
- $this->assertEquals('server', $loginFlowV2->getServer());
- }
-
- public function testFlowDoneDoesNotExistException(): void {
- $this->mapper->expects($this->once())
- ->method('getByLoginToken')
- ->willThrowException(new DoesNotExistException(''));
-
- $result = $this->subjectUnderTest->flowDone(
- 'login_token',
- 'session_id',
- 'server',
- 'user_id'
- );
- $this->assertFalse($result);
- }
-
- public function testFlowDonePasswordlessTokenException(): void {
- $this->tokenProvider->expects($this->once())
- ->method('getToken')
- ->willThrowException(new InvalidTokenException(''));
-
- $result = $this->subjectUnderTest->flowDone(
- 'login_token',
- 'session_id',
- 'server',
- 'user_id'
- );
- $this->assertFalse($result);
- }
-
- /*
- * Tests for createTokens
- */
-
- public function testCreateTokens(): void {
- $this->config->expects($this->exactly(2))
- ->method('getSystemValue')
- ->willReturnCallback(function ($key) {
- // Note: \OCP\IConfig::getSystemValue returns either an array or string.
- return $key == 'openssl' ? [] : '';
- });
-
- $this->mapper->expects($this->once())
- ->method('insert');
-
- $this->secureRandom->expects($this->exactly(2))
- ->method('generate');
-
- $token = $this->subjectUnderTest->createTokens('user_agent');
- $this->assertTrue($token instanceof LoginFlowV2Tokens);
- }
-}
--- a/tests/Core/Sharing/Property/ExpirationDateSharePropertyTypeTest.php
+++ /dev/null
@@ -1,224 +0,0 @@
-createUser('user', 'password');
- $this->assertNotFalse($user);
- $this->user = $user;
-
- $this->propertyType = Server::get(ExpirationDateSharePropertyType::class);
-
- $this->tokenRecipient = new ShareRecipient(TokenShareRecipientType::class, 'token', null);
- $this->emailRecipient = new ShareRecipient(EmailShareRecipientType::class, 'example@example.com', null);
- $this->remoteRecipient = new ShareRecipient(UserShareRecipientType::class, 'user', 'https://example.com');
- $this->localNonTokenAndEmailRecipient = new ShareRecipient(UserShareRecipientType::class, 'user', null);
- }
-
- #[\Override]
- protected function tearDown(): void {
- parent::tearDown();
-
- $this->user->delete();
- }
-
- private function createDummyShare(ShareProperty $property): Share {
- return new Share(
- '123',
- new ShareUser($this->user->getUID(), null),
- new DateTimeImmutable(),
- ShareState::Active,
- null,
- [],
- [],
- [$property->class => $property],
- [],
- );
- }
-
- /**
- * @return list
- */
- public static function dataConfigKeys(): array {
- return [
- ['shareapi_default_expire_date', 'shareapi_enforce_expire_date', 'shareapi_expire_after_n_days'],
- ['shareapi_default_remote_expire_date', 'shareapi_enforce_remote_expire_date', 'shareapi_remote_expire_after_n_days'],
- ['shareapi_default_internal_expire_date', 'shareapi_enforce_internal_expire_date', 'shareapi_internal_expire_after_n_days'],
- ];
- }
-
- /** @psalm-suppress DeprecatedMethod The configs are only partly migrated to IAppConfig, so using deprecated IConfig is easier for now. */
- #[DataProvider('dataConfigKeys')]
- public function testGetRequired(string $defaultEnabledKey, string $defaultEnforcedKey, string $defaultValueKey): void {
- $share = new Share(
- '123',
- new ShareUser('user', null),
- new DateTimeImmutable(),
- ShareState::Active,
- null,
- [],
- [
- $this->tokenRecipient,
- $this->emailRecipient,
- $this->remoteRecipient,
- $this->localNonTokenAndEmailRecipient,
- ],
- [],
- [],
- );
-
- $config = Server::get(IConfig::class);
- foreach (array_merge(...self::dataConfigKeys()) as $key) {
- $config->deleteAppValue(Application::APP_ID, $key);
- }
-
- $config->setAppValue(Application::APP_ID, $defaultEnabledKey, 'yes');
-
- $this->assertFalse($this->propertyType->isRequired($share));
-
- $config->setAppValue(Application::APP_ID, $defaultEnforcedKey, 'yes');
- $this->assertTrue($this->propertyType->isRequired($share));
-
- $config->deleteAppValue(Application::APP_ID, $defaultEnabledKey);
- $config->deleteAppValue(Application::APP_ID, $defaultEnforcedKey);
- }
-
- /** @psalm-suppress DeprecatedMethod The configs are only partly migrated to IAppConfig, so using deprecated IConfig is easier for now. */
- #[DataProvider('dataConfigKeys')]
- public function testGetDefaultValue(string $defaultEnabledKey, string $defaultEnforcedKey, string $defaultValueKey): void {
- $share = new Share(
- '123',
- new ShareUser('user', null),
- new DateTimeImmutable(),
- ShareState::Active,
- null,
- [],
- [
- $this->tokenRecipient,
- $this->emailRecipient,
- $this->remoteRecipient,
- $this->localNonTokenAndEmailRecipient,
- ],
- [],
- [],
- );
-
- /** @var DateTimeImmutable $now */
- $now = self::invokePrivate($this->propertyType, 'now');
-
- $config = Server::get(IConfig::class);
- foreach (array_merge(...self::dataConfigKeys()) as $key) {
- $config->deleteAppValue(Application::APP_ID, $key);
- }
-
- $this->assertNull($this->propertyType->getDefaultValue($share));
-
- $config->setAppValue(Application::APP_ID, $defaultEnabledKey, 'yes');
- $this->assertEquals($now->add(new DateInterval('P7D'))->format(DateTimeInterface::ATOM), $this->propertyType->getDefaultValue($share));
- $config->deleteAppValue(Application::APP_ID, $defaultEnabledKey);
- }
-
- /** @psalm-suppress DeprecatedMethod The configs are only partly migrated to IAppConfig, so using deprecated IConfig is easier for now. */
- #[DataProvider('dataConfigKeys')]
- public function testGetMinMaxDate(string $defaultEnabledKey, string $defaultEnforcedKey, string $defaultValueKey): void {
- $share = new Share(
- '123',
- new ShareUser('user', null),
- new DateTimeImmutable(),
- ShareState::Active,
- null,
- [],
- [
- $this->tokenRecipient,
- $this->emailRecipient,
- $this->remoteRecipient,
- $this->localNonTokenAndEmailRecipient,
- ],
- [],
- [],
- );
-
- /** @var DateTimeImmutable $now */
- $now = self::invokePrivate($this->propertyType, 'now');
-
- $config = Server::get(IConfig::class);
- foreach (array_merge(...self::dataConfigKeys()) as $key) {
- $config->deleteAppValue(Application::APP_ID, $key);
- }
-
- $this->assertEquals($now->add(new DateInterval('PT5M')), $this->propertyType->getMinDate($share));
- $this->assertNull($this->propertyType->getMaxDate($share));
-
- $config->setAppValue(Application::APP_ID, $defaultEnabledKey, 'yes');
-
- $this->assertEquals($now->add(new DateInterval('PT5M')), $this->propertyType->getMinDate($share));
- $this->assertNull($this->propertyType->getMaxDate($share));
-
- $config->setAppValue(Application::APP_ID, $defaultEnforcedKey, 'yes');
- $config->setAppValue(Application::APP_ID, $defaultValueKey, '123');
-
- $this->assertEquals($now->add(new DateInterval('PT5M')), $this->propertyType->getMinDate($share));
- $this->assertEquals($now->add(new DateInterval('P123DT5M')), $this->propertyType->getMaxDate($share));
-
- $config->deleteAppValue(Application::APP_ID, $defaultEnabledKey);
- $config->deleteAppValue(Application::APP_ID, $defaultEnforcedKey);
- $config->deleteAppValue(Application::APP_ID, $defaultValueKey);
- }
-
- public function testIsFiltered(): void {
- /** @var DateTimeImmutable $now */
- $now = self::invokePrivate($this->propertyType, 'now');
- $future = $now->add(new DateInterval('PT1M'))->format(DateTimeInterface::ATOM);
- $past = $now->sub(new DateInterval('PT1M'))->format(DateTimeInterface::ATOM);
-
- $this->assertFalse($this->propertyType->isFiltered(new ShareAccessContext(), $this->createDummyShare(new ShareProperty($this->propertyType::class, $future))));
- $this->assertTrue($this->propertyType->isFiltered(new ShareAccessContext(), $this->createDummyShare(new ShareProperty($this->propertyType::class, $now->format(DateTimeInterface::ATOM)))));
- $this->assertTrue($this->propertyType->isFiltered(new ShareAccessContext(), $this->createDummyShare(new ShareProperty($this->propertyType::class, $past))));
- }
-}
--- a/tests/Core/Sharing/Property/PasswordSharePropertyTypeTest.php
+++ /dev/null
@@ -1,153 +0,0 @@
-createUser('user', 'password');
- $this->assertNotFalse($user);
- $this->user = $user;
-
- $this->propertyType = Server::get(PasswordSharePropertyType::class);
- }
-
- #[\Override]
- protected function tearDown(): void {
- parent::tearDown();
-
- $this->user->delete();
- }
-
- private function createDummyShare(?ShareProperty $property): Share {
- $properties = [];
- if ($property instanceof ShareProperty) {
- $properties[$property->class] = $property;
- }
-
- return new Share(
- '123',
- new ShareUser($this->user->getUID(), null),
- new DateTimeImmutable(),
- ShareState::Active,
- null,
- [],
- [],
- $properties,
- [],
- );
- }
-
- public function testGetDefaultValue(): void {
- $share = new Share(
- '123',
- new ShareUser('user', null),
- new DateTimeImmutable(),
- ShareState::Active,
- null,
- [],
- [],
- [],
- [],
- );
-
- $appConfig = Server::get(IAppConfig::class);
- $appConfig->deleteKey(Application::APP_ID, ConfigLexicon::SHARE_LINK_PASSWORD_ENFORCED);
-
- $this->assertNull($this->propertyType->getDefaultValue($share));
-
- $appConfig->setValueBool(Application::APP_ID, ConfigLexicon::SHARE_LINK_PASSWORD_ENFORCED, true);
-
- $value = $this->propertyType->getDefaultValue($share);
- $this->assertNotNull($value);
- /** @psalm-suppress RedundantCastGivenDocblockType psalm:strict and rector:strict fight over the cast -_- */
- $this->assertGreaterThan(1, strlen((string)$value));
- $this->assertTrue($this->propertyType->validateValue(Server::get(IFactory::class), $share, $value));
-
- $appConfig->deleteKey(Application::APP_ID, ConfigLexicon::SHARE_LINK_PASSWORD_ENFORCED);
- }
-
- public function testIsFiltered(): void {
- $this->assertFalse($this->propertyType->isFiltered(new ShareAccessContext(arguments: [$this->propertyType::class => '123']), $this->createDummyShare(new ShareProperty($this->propertyType::class, Server::get(IHasher::class)->hash('123')))));
- $this->assertFalse($this->propertyType->isFiltered(new ShareAccessContext(arguments: [$this->propertyType::class => '123']), $this->createDummyShare(new ShareProperty($this->propertyType::class, null))));
- $this->assertFalse($this->propertyType->isFiltered(new ShareAccessContext(arguments: [$this->propertyType::class => '123']), $this->createDummyShare(null)));
- $this->assertTrue($this->propertyType->isFiltered(new ShareAccessContext(arguments: [$this->propertyType::class => '456']), $this->createDummyShare(new ShareProperty($this->propertyType::class, Server::get(IHasher::class)->hash('123')))));
- $this->assertTrue($this->propertyType->isFiltered(new ShareAccessContext(arguments: [$this->propertyType::class => null]), $this->createDummyShare(new ShareProperty($this->propertyType::class, Server::get(IHasher::class)->hash('123')))));
- $this->assertTrue($this->propertyType->isFiltered(new ShareAccessContext(), $this->createDummyShare(new ShareProperty($this->propertyType::class, Server::get(IHasher::class)->hash('123')))));
- }
-
- public function testIsExpiredEmailPasswordFiltered(): void {
- /** @var DateTimeImmutable $now */
- $now = self::invokePrivate($this->propertyType, 'now');
-
- $password = '123';
- $hashedPassword = Server::get(IHasher::class)->hash($password);
-
- $secret = 'abc';
-
- $createShare = fn (DateTimeImmutable $lastUpdated): Share => new Share(
- '456',
- new ShareUser('user', null),
- $lastUpdated,
- ShareState::Active,
- null,
- [],
- [
- new ShareRecipient(EmailShareRecipientType::class, 'test@example.com', null, $secret),
- ],
- [
- $this->propertyType::class => new ShareProperty($this->propertyType::class, $hashedPassword),
- ],
- [],
- );
-
- $expirationIntervalSeconds = 10;
- $config = Server::get(IConfig::class);
- $config->setSystemValue('sharing.enable_mail_link_password_expiration', true);
- $config->setSystemValue('sharing.mail_link_password_expiration_interval', $expirationIntervalSeconds);
-
- $this->assertFalse($this->propertyType->isFiltered(new ShareAccessContext(secret: $secret, arguments: [$this->propertyType::class => $password]), $createShare($now)));
- $this->assertFalse($this->propertyType->isFiltered(new ShareAccessContext(secret: $secret, arguments: [$this->propertyType::class => $password]), $createShare($now->sub(new DateInterval('PT' . ($expirationIntervalSeconds - 1) . 'S')))));
- $this->assertFalse($this->propertyType->isFiltered(new ShareAccessContext(secret: $secret, arguments: [$this->propertyType::class => $password]), $createShare($now->sub(new DateInterval('PT' . ($expirationIntervalSeconds) . 'S')))));
- $this->assertTrue($this->propertyType->isFiltered(new ShareAccessContext(secret: $secret, arguments: [$this->propertyType::class => $password]), $createShare($now->sub(new DateInterval('PT' . ($expirationIntervalSeconds + 1) . 'S')))));
-
- $config->deleteSystemValue('sharing.enable_mail_link_password_expiration');
- $config->deleteSystemValue('sharing.mail_link_password_expiration_interval');
- }
-}
--- a/tests/Core/Sharing/Recipient/EmailShareRecipientTypeTest.php
+++ /dev/null
@@ -1,124 +0,0 @@
-createUser($uid, $password);
- $this->assertNotFalse($user);
- return $user;
- }
-
- #[\Override]
- public function setUp(): void {
- parent::setUp();
-
- $userManager = Server::get(IUserManager::class);
- $userManager->clearBackends();
- $userManager->registerBackend(new Database());
-
- $this->user1 = $this->createUser($userManager, 'user1', 'password');
-
- self::loginAsUser($this->user1->getUID());
-
- $this->recipientType = Server::get(EmailShareRecipientType::class);
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->user1->delete();
-
- parent::tearDown();
- }
-
- public function testValidateRecipient(): void {
- $this->assertTrue($this->recipientType->validateRecipient('example@example.com'));
- $this->assertFalse($this->recipientType->validateRecipient('example'));
- $this->assertFalse($this->recipientType->validateRecipient('example@example'));
- $this->assertFalse($this->recipientType->validateRecipient('example.com'));
- $this->assertFalse($this->recipientType->validateRecipient('@'));
- }
-
- public function testGetRecipientDisplayName(): void {
- $this->assertNull($this->recipientType->getRecipientDisplayName('example@example.com', null));
- $this->assertNull($this->recipientType->getRecipientDisplayName('example@example.com', 'https://example.com'));
-
- $cardDavBackend = Server::get(CardDavBackend::class);
- $contactsManager = Server::get(IManager::class);
- $addressBookId = $cardDavBackend->createAddressBook('principals/users/' . $this->user1->getUID(), 'Personal', []);
- Server::get(ContactsManager::class)->setupContactsProvider($contactsManager, $this->user1->getUID(), Server::get(IURLGenerator::class));
-
- $contactsManager->createOrUpdate(['EMAIL' => 'example@example.com', 'FN' => 'Email 1'], (string)$addressBookId);
-
- $this->assertEquals('Email 1', $this->recipientType->getRecipientDisplayName('example@example.com', null));
- $this->assertNull($this->recipientType->getRecipientDisplayName('example@example.com', 'https://example.com'));
- $this->assertNull($this->recipientType->getRecipientDisplayName('example@example.org', null));
-
- $cardDavBackend->deleteAddressBook($addressBookId);
- }
-
- public function testSearchRecipients(): void {
- if (Server::get(IDBConnection::class)->getDatabaseProvider() === IDBConnection::PLATFORM_POSTGRES) {
- $this->markTestSkipped('PostgreSQL has unstable test results');
- }
-
- $cardDavBackend = Server::get(CardDavBackend::class);
- $contactsManager = Server::get(IManager::class);
- $addressBookId = $cardDavBackend->createAddressBook('principals/users/' . $this->user1->getUID(), 'Personal', []);
- Server::get(ContactsManager::class)->setupContactsProvider($contactsManager, $this->user1->getUID(), Server::get(IURLGenerator::class));
-
- foreach (['email1@example.com', 'email2@example.com', 'email3@example.com', 'email4@example.com'] as $email) {
- $contactsManager->createOrUpdate(['EMAIL' => $email], (string)$addressBookId);
- }
-
- $accessContext = new ShareAccessContext(currentUser: $this->user1);
-
- /** @psalm-suppress ArgumentTypeCoercion */
- $generateRecipient = static fn (string $email): ShareRecipient => new ShareRecipient(
- EmailShareRecipientType::class,
- $email,
- null,
- );
-
- $this->assertEquals(array_map($generateRecipient(...), ['email1@example.com', 'email2@example.com', 'email3@example.com']), $this->recipientType->searchRecipients($accessContext, 'email', 3, 0));
- $this->assertEquals(array_map($generateRecipient(...), ['email2@example.com', 'email3@example.com', 'email4@example.com']), $this->recipientType->searchRecipients($accessContext, 'email', 3, 1));
- $this->assertEquals(array_map($generateRecipient(...), ['email3@example.com', 'email4@example.com']), $this->recipientType->searchRecipients($accessContext, 'email', 4, 2));
-
- $this->assertEquals(array_map($generateRecipient(...), ['email1@example.com']), $this->recipientType->searchRecipients($accessContext, 'email', 1, 0));
- $this->assertEquals(array_map($generateRecipient(...), ['email1@example.com', 'email2@example.com']), $this->recipientType->searchRecipients($accessContext, 'email', 2, 0));
- $this->assertEquals(array_map($generateRecipient(...), ['email2@example.com', 'email3@example.com']), $this->recipientType->searchRecipients($accessContext, 'email', 2, 1));
- $this->assertEquals(array_map($generateRecipient(...), ['email3@example.com', 'email4@example.com']), $this->recipientType->searchRecipients($accessContext, 'email', 2, 2));
- $this->assertEquals(array_map($generateRecipient(...), ['email4@example.com']), $this->recipientType->searchRecipients($accessContext, 'email', 2, 3));
-
- $this->assertEquals(array_map($generateRecipient(...), ['email1@example.com']), $this->recipientType->searchRecipients($accessContext, 'email1', 2, 0));
-
- $cardDavBackend->deleteAddressBook($addressBookId);
- }
-}
--- a/tests/Core/Sharing/Recipient/GroupShareRecipientTypeTest.php
+++ /dev/null
@@ -1,181 +0,0 @@
- 'Group 1',
- 'group2' => 'Group 2',
- 'group3' => 'Group 3',
- ];
-
- private function createUser(IUserManager $userManager, string $uid, string $password): IUser {
- $user = $userManager->createUser($uid, $password);
- $this->assertNotFalse($user);
- return $user;
- }
-
- private function createGroup(IGroupManager $groupManager, string $gid): IGroup {
- $group = $groupManager->createGroup($gid);
- $this->assertNotNull($group);
- $this->assertTrue($group->setDisplayName(self::DISPLAY_NAMES[$gid]));
- return $group;
- }
-
- #[\Override]
- public function setUp(): void {
- parent::setUp();
-
- $this->dbConnection = Server::get(IDBConnection::class);
-
- $this->manager = Server::get(ISharingManager::class);
-
- $userManager = Server::get(IUserManager::class);
- $this->user1 = $this->createUser($userManager, 'user1', 'password');
-
- $groupManager = Server::get(IGroupManager::class);
- $groupManager->clearBackends();
- $groupManager->addBackend(new Database());
-
- $this->group1 = $this->createGroup($groupManager, 'group1');
- $this->group2 = $this->createGroup($groupManager, 'group2');
- $this->group3 = $this->createGroup($groupManager, 'group3');
-
- $this->group1->addUser($this->user1);
-
- $this->recipientType = new GroupShareRecipientType(Server::get(IEventDispatcher::class), $this->dbConnection, $groupManager, $this->manager);
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->user1->delete();
- $this->group1->delete();
- $this->group2->delete();
- $this->group3->delete();
-
- parent::tearDown();
- }
-
- public function testValidateRecipient(): void {
- $this->assertTrue($this->recipientType->validateRecipient('group1'));
- $this->assertFalse($this->recipientType->validateRecipient('invalid'));
- }
-
- public function testGetRecipientValues(): void {
- $this->assertEquals(['group1'], $this->recipientType->getRecipients($this->user1, null));
- }
-
- public function testGetRecipientDisplayName(): void {
- // Clear display name cache, because setting the display name on the group doesn't update it in the cache of the manager
- self::invokePrivate(self::invokePrivate(Server::get(IGroupManager::class), 'displayNameCache'), 'clear');
-
- $this->assertEquals('Group 1', $this->recipientType->getRecipientDisplayName($this->group1->getGID(), null));
- $this->assertNull($this->recipientType->getRecipientDisplayName($this->group1->getGID(), 'https://example.com'));
-
- $cardDavBackend = Server::get(CardDavBackend::class);
- $contactsManager = Server::get(IManager::class);
- $addressBookId = $cardDavBackend->createAddressBook('principals/users/' . $this->user1->getUID(), 'Personal', []);
- Server::get(ContactsManager::class)->setupContactsProvider($contactsManager, $this->user1->getUID(), Server::get(IURLGenerator::class));
-
- $contactsManager->createOrUpdate(['CLOUD' => $this->group1->getGID() . '@https://example.com', 'FN' => 'Remote Group 1'], (string)$addressBookId);
-
- $this->assertEquals('Remote Group 1', $this->recipientType->getRecipientDisplayName($this->group1->getGID(), 'https://example.com'));
- $this->assertNull($this->recipientType->getRecipientDisplayName($this->group2->getGID(), 'https://example.com'));
- $this->assertNull($this->recipientType->getRecipientDisplayName($this->group1->getGID(), 'https://example.org'));
- $this->assertNull($this->recipientType->getRecipientDisplayName($this->group1->getGID(), 'http://example.com'));
- $this->assertNull($this->recipientType->getRecipientDisplayName($this->group1->getGID(), 'example.com'));
-
- $cardDavBackend->deleteAddressBook($addressBookId);
- }
-
- public function testSearchRecipients(): void {
- $accessContext = new ShareAccessContext(currentUser: $this->user1);
- self::loginAsUser($this->user1->getUID());
-
- $generateRecipient = static fn (IGroup $group): ShareRecipient => new ShareRecipient(
- GroupShareRecipientType::class,
- $group->getGID(),
- null,
- );
-
- $this->assertEquals(array_map($generateRecipient(...), [$this->group1, $this->group2, $this->group3]), $this->recipientType->searchRecipients($accessContext, 'group', 3, 0));
- $this->assertEquals(array_map($generateRecipient(...), [$this->group1]), $this->recipientType->searchRecipients($accessContext, 'group', 1, 0));
- $this->assertEquals(array_map($generateRecipient(...), [$this->group2, $this->group3]), $this->recipientType->searchRecipients($accessContext, 'group', 3, 1));
- $this->assertEquals(array_map($generateRecipient(...), [$this->group2]), $this->recipientType->searchRecipients($accessContext, 'group', 1, 1));
-
- $this->assertEquals(array_map($generateRecipient(...), [$this->group1]), $this->recipientType->searchRecipients($accessContext, 'group1', 1, 0));
-
- $this->assertEquals([new ShareRecipient(GroupShareRecipientType::class, 'example', 'example.com')], $this->recipientType->searchRecipients($accessContext, 'example@example.com', 1, 0));
- $this->assertEquals([new ShareRecipient(GroupShareRecipientType::class, 'example', 'https://example.com')], $this->recipientType->searchRecipients($accessContext, 'example@https://example.com', 1, 0));
- }
-
- public function testDelete(): void {
- $registry = Server::get(ISharingRegistry::class);
- $registry->clear();
- $registry->registerRecipientType($this->recipientType);
-
- $accessContext = new ShareAccessContext(currentUser: $this->user1);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient($this->recipientType::class, $this->group1->getGID(), null));
- $this->dbConnection->commit();
-
- $before = $this->manager->getTime();
- $this->group1->delete();
- $after = $this->manager->getTime();
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->getShare($accessContext, $share->id);
- $this->assertGreaterThanOrEqual(SharingManager::timeToMs($before), SharingManager::timeToMs($share->lastUpdated));
- $this->assertLessThanOrEqual(SharingManager::timeToMs($after), SharingManager::timeToMs($share->lastUpdated));
- $this->assertEquals([], $share->recipients);
-
- $this->manager->deleteShare($accessContext, $share);
- $this->dbConnection->commit();
- $registry->clear();
- }
-}
--- a/tests/Core/Sharing/Recipient/TeamShareRecipientTypeTest.php
+++ /dev/null
@@ -1,188 +0,0 @@
- 'Team 1',
- 'team2' => 'Team 2',
- 'team3' => 'Team 3',
- ];
-
- private function createUser(IUserManager $userManager, string $uid, string $password): IUser {
- $user = $userManager->createUser($uid, $password);
- $this->assertNotFalse($user);
- return $user;
- }
-
- private function createTeam(ITeamManager $teamManager, string $name): Team {
- $circlesManager = Server::get(CirclesManager::class);
- $circlesManager->startSession($circlesManager->getLocalFederatedUser($this->user1->getUID()));
-
- $circle = $circlesManager->createCircle($name);
- Server::get(CircleService::class)->updateName($circle->getSingleId(), self::DISPLAY_NAMES[$name]);
-
- $team = $teamManager->getTeam($circle->getSingleId(), $this->user1->getUID());
- $this->assertNotNull($team);
- return $team;
- }
-
- #[\Override]
- public static function setUpBeforeClass(): void {
- parent::setUpBeforeClass();
-
- if (!class_exists(CirclesManager::class)) {
- self::markTestSkipped('Circles app is not installed');
- }
- }
-
- #[\Override]
- public function setUp(): void {
- parent::setUp();
-
- $this->dbConnection = Server::get(IDBConnection::class);
-
- $this->manager = Server::get(ISharingManager::class);
-
- $userManager = Server::get(IUserManager::class);
- $this->user1 = $this->createUser($userManager, 'user1', 'password');
-
- $teamManager = Server::get(ITeamManager::class);
-
- $this->team1 = $this->createTeam($teamManager, 'team1');
- $this->team2 = $this->createTeam($teamManager, 'team2');
- $this->team3 = $this->createTeam($teamManager, 'team3');
-
- $this->recipientType = new TeamShareRecipientType(Server::get(IEventDispatcher::class), $this->dbConnection, $this->manager);
- }
-
- #[\Override]
- protected function tearDown(): void {
- $circlesManager = Server::get(CirclesManager::class);
- $circlesManager->startSession($circlesManager->getLocalFederatedUser($this->user1->getUID()));
- try {
- $circlesManager->destroyCircle($this->team1->getId());
- } catch (CircleNotFoundException) {
- }
-
- $circlesManager->destroyCircle($this->team2->getId());
- $circlesManager->destroyCircle($this->team3->getId());
-
- $this->user1->delete();
-
- parent::tearDown();
- }
-
- /** @psalm-suppress UnevaluatedCode Test is skipped */
- public function testValidateRecipient(): void {
- $this->markTestSkipped('This test is broken if run together with other test suites because circles 🤷♀️');
- $this->assertTrue($this->recipientType->validateRecipient($this->team1->getId()));
- $this->assertFalse($this->recipientType->validateRecipient('invalid'));
- }
-
- public function testGetRecipientValues(): void {
- $this->assertContains($this->team1->getId(), $this->recipientType->getRecipients($this->user1, null));
- }
-
- /** @psalm-suppress UnevaluatedCode Test is skipped */
- public function testGetRecipientDisplayName(): void {
- $this->markTestSkipped('This test is broken if run together with other test suites because circles 🤷♀️');
- $this->assertEquals('Team 1', $this->recipientType->getRecipientDisplayName($this->team1->getId()));
- }
-
- public function testSearchRecipients(): void {
- $accessContext = new ShareAccessContext(currentUser: $this->user1);
- self::loginAsUser($this->user1->getUID());
-
- $generateRecipient = static fn (Team $team): ShareRecipient => new ShareRecipient(
- TeamShareRecipientType::class,
- $team->getId(),
- null,
- );
-
- $this->assertEquals(array_map($generateRecipient(...), [$this->team1, $this->team2, $this->team3]), $this->recipientType->searchRecipients($accessContext, 'Team', 3, 0));
- $this->assertEquals(array_map($generateRecipient(...), [$this->team1]), $this->recipientType->searchRecipients($accessContext, 'Team', 1, 0));
- $this->assertEquals(array_map($generateRecipient(...), [$this->team2, $this->team3]), $this->recipientType->searchRecipients($accessContext, 'Team', 3, 1));
- $this->assertEquals(array_map($generateRecipient(...), [$this->team2]), $this->recipientType->searchRecipients($accessContext, 'Team', 1, 1));
-
- $this->assertEquals(array_map($generateRecipient(...), [$this->team1]), $this->recipientType->searchRecipients($accessContext, 'Team 1', 1, 0));
- }
-
- /** @psalm-suppress UnevaluatedCode Test is skipped */
- public function testDelete(): void {
- $this->markTestSkipped('This test is broken if run together with other test suites because circles 🤷♀️');
-
- $registry = Server::get(ISharingRegistry::class);
- $registry->clear();
- $registry->registerRecipientType($this->recipientType);
-
- $accessContext = new ShareAccessContext(currentUser: $this->user1);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient($this->recipientType::class, $this->team1->getId(), null));
- $this->dbConnection->commit();
-
- $circlesManager = Server::get(CirclesManager::class);
- $circlesManager->startSession($circlesManager->getLocalFederatedUser($this->user1->getUID()));
-
- $before = $this->manager->getTime();
- $circlesManager->destroyCircle($this->team1->getId());
- $after = $this->manager->getTime();
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->getShare($accessContext, $share->id);
- $this->assertGreaterThanOrEqual($before, $share->lastUpdated->getTimestamp());
- $this->assertLessThanOrEqual($after, $share->lastUpdated->getTimestamp());
- $this->assertEquals([], $share->recipients);
-
- $this->manager->deleteShare($accessContext, $share);
- $this->dbConnection->commit();
- $registry->clear();
- }
-}
--- a/tests/Core/Sharing/Recipient/TokenShareRecipientTypeTest.php
+++ /dev/null
@@ -1,34 +0,0 @@
-recipientType = Server::get(TokenShareRecipientType::class);
- }
-
- public function testValidateRecipient(): void {
- $this->assertTrue($this->recipientType->validateRecipient(str_repeat('a', 32)));
- $this->assertFalse($this->recipientType->validateRecipient(str_repeat('a', 32 - 1)));
- $this->assertTrue($this->recipientType->validateRecipient(str_repeat('a', 255)));
- $this->assertFalse($this->recipientType->validateRecipient(str_repeat('a', 255 + 1)));
- }
-}
--- a/tests/Core/Sharing/Recipient/UserShareRecipientTypeTest.php
+++ /dev/null
@@ -1,190 +0,0 @@
- 'User 1',
- 'user2' => 'User 2',
- 'user3' => 'User 3',
- 'user4' => 'User 4',
- ];
-
- private function createUser(IUserManager $userManager, string $uid, string $password): IUser {
- $user = $userManager->createUser($uid, $password);
- $this->assertNotFalse($user);
- $this->assertTrue($user->setDisplayName(self::DISPLAY_NAMES[$uid]));
- $user->setSystemEMailAddress($uid . '@example.com');
- return $user;
- }
-
- #[\Override]
- public function setUp(): void {
- parent::setUp();
-
- $this->dbConnection = Server::get(IDBConnection::class);
-
- $this->manager = Server::get(ISharingManager::class);
-
- $userManager = Server::get(IUserManager::class);
- $userManager->clearBackends();
- $userManager->registerBackend(new Database());
-
- $this->user1 = $this->createUser($userManager, 'user1', 'password');
- $this->user2 = $this->createUser($userManager, 'user2', 'password');
- $this->user3 = $this->createUser($userManager, 'user3', 'password');
- $this->user4 = $this->createUser($userManager, 'user4', 'password');
-
- $this->recipientType = new UserShareRecipientType(Server::get(IEventDispatcher::class), $this->dbConnection, $userManager, $this->manager);
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->user1->delete();
- $this->user2->delete();
- $this->user3->delete();
- $this->user4->delete();
-
- parent::tearDown();
- }
-
- public function testValidateRecipient(): void {
- $this->assertTrue($this->recipientType->validateRecipient('user1'));
- $this->assertFalse($this->recipientType->validateRecipient('invalid'));
- }
-
- public function testGetRecipientValues(): void {
- $this->assertEquals(['user1'], $this->recipientType->getRecipients($this->user1, null));
- }
-
- public function testGetRecipientDisplayName(): void {
- $this->assertEquals('User 1', $this->recipientType->getRecipientDisplayName($this->user1->getUID(), null));
- $this->assertNull($this->recipientType->getRecipientDisplayName($this->user1->getUID(), 'https://example.com'));
-
- $cardDavBackend = Server::get(CardDavBackend::class);
- $contactsManager = Server::get(IManager::class);
- $addressBookId = $cardDavBackend->createAddressBook('principals/users/' . $this->user1->getUID(), 'Personal', []);
- Server::get(ContactsManager::class)->setupContactsProvider($contactsManager, $this->user1->getUID(), Server::get(IURLGenerator::class));
-
- $contactsManager->createOrUpdate(['CLOUD' => $this->user1->getUID() . '@https://example.com', 'FN' => 'Remote User 1'], (string)$addressBookId);
-
- $this->assertEquals('Remote User 1', $this->recipientType->getRecipientDisplayName($this->user1->getUID(), 'https://example.com'));
- $this->assertNull($this->recipientType->getRecipientDisplayName($this->user2->getUID(), 'https://example.com'));
- $this->assertNull($this->recipientType->getRecipientDisplayName($this->user1->getUID(), 'https://example.org'));
- $this->assertNull($this->recipientType->getRecipientDisplayName($this->user1->getUID(), 'http://example.com'));
- $this->assertNull($this->recipientType->getRecipientDisplayName($this->user1->getUID(), 'example.com'));
-
- $cardDavBackend->deleteAddressBook($addressBookId);
- }
-
- public function testGetRecipientIcon(): void {
- $this->assertEquals(new ShareIconURL('http://localhost/index.php/avatar/user1/64', 'http://localhost/index.php/avatar/user1/64/dark'), $this->recipientType->getRecipientIcon($this->user1->getUID(), null));
- $this->assertEquals(new ShareIconURL('http://localhost/index.php/avatar/user1@https%253A%2F%2Fexample.com/64', 'http://localhost/index.php/avatar/user1@https%253A%2F%2Fexample.com/64/dark'), $this->recipientType->getRecipientIcon($this->user1->getUID(), 'https://example.com'));
- }
-
- public function testSearchRecipients(): void {
- if (in_array(Server::get(IDBConnection::class)->getDatabaseProvider(), [IDBConnection::PLATFORM_POSTGRES, IDBConnection::PLATFORM_ORACLE], true)) {
- $this->markTestSkipped('PostgreSQL and Oracle have unstable test results');
- }
-
- $accessContext = new ShareAccessContext(currentUser: $this->user1);
- self::loginAsUser($this->user1->getUID());
-
- $generateRecipient = static fn (IUser $user): ShareRecipient => new ShareRecipient(
- UserShareRecipientType::class,
- $user->getUID(),
- null,
- );
-
- // The UserPlugin already removes the current user (user1 here), leading to one result less than requested.
- // This is an issue of the Collaborators API and can't be easily fixed.
- // If the following tests fail, because different numbers of results are returned: congratulations, you fixed the problem!
-
- $this->assertEquals(array_map($generateRecipient(...), [$this->user2, $this->user3, $this->user4]), $this->recipientType->searchRecipients($accessContext, 'user', 3, 0));
- $this->assertEquals(array_map($generateRecipient(...), [$this->user2, $this->user3, $this->user4]), $this->recipientType->searchRecipients($accessContext, 'user', 4, 0));
- // Wrong: Offset not applied correctly
- $this->assertEquals(array_map($generateRecipient(...), [$this->user2, $this->user3, $this->user4]), $this->recipientType->searchRecipients($accessContext, 'user', 4, 1));
- $this->assertEquals(array_map($generateRecipient(...), [$this->user3, $this->user4]), $this->recipientType->searchRecipients($accessContext, 'user', 4, 2));
-
- $this->assertEquals(array_map($generateRecipient(...), [$this->user2]), $this->recipientType->searchRecipients($accessContext, 'user', 1, 0));
- $this->assertEquals(array_map($generateRecipient(...), [$this->user2, $this->user3]), $this->recipientType->searchRecipients($accessContext, 'user', 2, 0));
- $this->assertEquals(array_map($generateRecipient(...), [$this->user2, $this->user3]), $this->recipientType->searchRecipients($accessContext, 'user', 2, 1));
- $this->assertEquals(array_map($generateRecipient(...), [$this->user3, $this->user4]), $this->recipientType->searchRecipients($accessContext, 'user', 2, 2));
- $this->assertEquals(array_map($generateRecipient(...), [$this->user4]), $this->recipientType->searchRecipients($accessContext, 'user', 2, 3));
-
- $this->assertEquals(array_map($generateRecipient(...), [$this->user2]), $this->recipientType->searchRecipients($accessContext, 'user2', 2, 0));
- $this->assertEquals(array_map($generateRecipient(...), [$this->user2]), $this->recipientType->searchRecipients($accessContext, 'user2@example.com', 2, 0));
-
- $this->assertEquals([new ShareRecipient(UserShareRecipientType::class, 'example', 'example.com')], $this->recipientType->searchRecipients($accessContext, 'example@example.com', 1, 0));
- $this->assertEquals([new ShareRecipient(UserShareRecipientType::class, 'example', 'https://example.com')], $this->recipientType->searchRecipients($accessContext, 'example@https://example.com', 1, 0));
- }
-
- public function testDelete(): void {
- $registry = Server::get(ISharingRegistry::class);
- $registry->clear();
- $registry->registerRecipientType($this->recipientType);
-
- $accessContext = new ShareAccessContext(currentUser: $this->user1);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient($this->recipientType::class, $this->user2->getUID(), null));
- $this->dbConnection->commit();
-
- $before = $this->manager->getTime();
- $this->user2->delete();
- $after = $this->manager->getTime();
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->getShare($accessContext, $share->id);
- $this->assertGreaterThanOrEqual(SharingManager::timeToMs($before), SharingManager::timeToMs($share->lastUpdated));
- $this->assertLessThanOrEqual(SharingManager::timeToMs($after), SharingManager::timeToMs($share->lastUpdated));
- $this->assertEquals([], $share->recipients);
-
- $this->manager->deleteShare($accessContext, $share);
- $this->dbConnection->commit();
- $registry->clear();
- }
-}
--- a/tests/apps/testapp_dependency_missing/appinfo/info.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
- testapp_infoxml
- Test app
- A b c
- A b c
- 1.2.3
- agpl
- Jane
- games
- https://example.org
-
--- a/tests/apps/testapp_infoxml/appinfo/info.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
- testapp_infoxml
- Test app
- A b c
- A b c
- 1.2.3
- agpl
- Jane
- games
- https://example.org
-
-
-
-
--- a/tests/apps/testapp_name_missing/appinfo/info.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
- testapp_name_missing
- A b c
- A b c
- 1.2.3
- agpl
- Jane
- games
- https://example.org
-
-
-
-
--- a/tests/apps/testapp_version/appinfo/info.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
- testapp_version
- Test app
- A b c
- A b c
- agpl
- Jane
- games
- https://example.org
-
-
-
-
--- a/tests/autoload.php
+++ /dev/null
@@ -1,16 +0,0 @@
-addPsr4('Test\\', OC::$SERVERROOT . '/tests/lib/', true);
--- a/tests/blueprints/basic.toml
+++ /dev/null
@@ -1,25 +0,0 @@
-# SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-# Used for performance testing, see .github/workflows/performance.yml
-
-[[user]]
-id = "test"
-groups = ["test_group"]
-files = [
- "test.txt",
- "foo/sub.png",
- "empty",
-]
-
-[[user]]
-id = "test2"
-groups = ["test"]
-files = [
- "many_files/file_[1..100].txt"
-]
-
-[[share]]
-from = "test2"
-to = "test"
-file = "many_files"
--- a/tests/bootstrap.php
+++ /dev/null
@@ -1,50 +0,0 @@
-addPsr4('Tests\\Core\\', OC::$SERVERROOT . '/tests/Core/', true);
-
-$dontLoadApps = getenv('TEST_DONT_LOAD_APPS');
-if (!$dontLoadApps) {
- // load all apps
- $appManager = Server::get(IAppManager::class);
- foreach (new \DirectoryIterator(__DIR__ . '/../apps/') as $file) {
- if ($file->isDot()) {
- continue;
- }
- $appManager->loadApp($file->getFilename());
- }
-}
-
-// Recorded on CI by default; set TEST_LOG_HTTP to a path for a manual run.
-$logHttp = getenv('TEST_LOG_HTTP') ?: (getenv('CI') ? OC::$SERVERROOT . '/http-requests.log' : '');
-if ($logHttp !== '') {
- \Test\HttpRequestLogger::install($logHttp);
-}
-
-OC_Hook::clear();
-
-set_include_path(
- get_include_path() . PATH_SEPARATOR
- . '/usr/share/php' . PATH_SEPARATOR
- . __DIR__ . '/..'
-);
--- a/tests/data/REUSE.toml
+++ /dev/null
@@ -1,124 +0,0 @@
-version = 1
-SPDX-PackageName = "nextcloud"
-SPDX-PackageSupplier = "Nextcloud "
-SPDX-PackageDownloadLocation = "https://github.com/nextcloud/server"
-
-[[annotations]]
-path = ["integritycheck/htaccessWithValidModifiedContent/.htaccess"]
-precedence = "aggregate"
-SPDX-FileCopyrightText = "2016 ownCloud, Inc., 2019 Nextcloud GmbH and Nextcloud contributors"
-SPDX-License-Identifier = "AGPL-3.0-only"
-
-[[annotations]]
-path = ["block-aligned-plus-one.txt", "block-aligned.txt", "data.tar.gz", "data.zip", "desktopapp.png", "desktopapp.svg", "certificates/badCertificate.crt", "certificates/expiredCertificate.crt", "certificates/goodCertificate.crt", "certificates/openSslTrustedCertificate.crt", "integritycheck/app/AnotherFile.txt", "integritycheck/app/subfolder/file.txt", "integritycheck/appWithInvalidData/AnotherFile.txt", "integritycheck/appWithInvalidData/UnecessaryFile"]
-precedence = "aggregate"
-SPDX-FileCopyrightText = "2015 ownCloud, Inc."
-SPDX-License-Identifier = "AGPL-3.0-only"
-
-[[annotations]]
-path = ["integritycheck/htaccessUnmodified/.htaccess", "integritycheck/htaccessUnmodified/subfolder/.htaccess", "integritycheck/htaccessWithInvalidModifiedContent/.htaccess", "integritycheck/htaccessWithValidModifiedContent/subfolder/.htaccess"]
-precedence = "aggregate"
-SPDX-FileCopyrightText = "2016 ownCloud, Inc."
-SPDX-License-Identifier = "AGPL-3.0-only"
-
-[[annotations]]
-path = ["l10n/cs.json", "l10n/de.json", "l10n/ru.json"]
-precedence = "aggregate"
-SPDX-FileCopyrightText = "2015 ownCloud, Inc., 2021 Nextcloud GmbH and Nextcloud contributors"
-SPDX-License-Identifier = "AGPL-3.0-only"
-
-[[annotations]]
-path = ["lorem.txt"]
-precedence = "aggregate"
-SPDX-FileCopyrightText = "2012 ownCloud, Inc."
-SPDX-License-Identifier = "AGPL-3.0-only"
-
-[[annotations]]
-path = ["testavatar.png", "testimage.gif", "testimage.jpg", "testimage.png"]
-precedence = "aggregate"
-SPDX-FileCopyrightText = "2013 ownCloud, Inc."
-SPDX-License-Identifier = "AGPL-3.0-only"
-
-[[annotations]]
-path = ["lorem-big.txt", "strängé**filename**(duplicate**#2).txt"]
-precedence = "aggregate"
-SPDX-FileCopyrightText = "2014 ownCloud, Inc."
-SPDX-License-Identifier = "AGPL-3.0-only"
-
-[[annotations]]
-path = "testimagelarge.svg"
-precedence = "aggregate"
-SPDX-FileCopyrightText = "2015 ownCloud, Inc., 2016 Nextcloud GmbH and Nextcloud contributors"
-SPDX-License-Identifier = "AGPL-3.0-only"
-
-[[annotations]]
-path = ["setUploadLimit/htaccess", "setUploadLimit/user.ini"]
-precedence = "aggregate"
-SPDX-FileCopyrightText = "2015 ownCloud, Inc., 2018 Nextcloud GmbH and Nextcloud contributors"
-SPDX-License-Identifier = "AGPL-3.0-only"
-
-[[annotations]]
-path = ["testimage-wide.png", "testimage.eps", "testimage.mp3", "testimage.mp4", "testimage.odt"]
-precedence = "aggregate"
-SPDX-FileCopyrightText = "2015 ownCloud, Inc."
-SPDX-License-Identifier = "AGPL-3.0-only"
-
-[[annotations]]
-path = ["testapp.zip", "testapp2.zip"]
-precedence = "aggregate"
-SPDX-FileCopyrightText = "2016 ownCloud, Inc."
-SPDX-License-Identifier = "AGPL-3.0-only"
-
-[[annotations]]
-path = ["testimage.webp", "broken-video.webm"]
-precedence = "aggregate"
-SPDX-FileCopyrightText = "2016-2024 Nextcloud GmbH"
-SPDX-License-Identifier = "LicenseRef-NextcloudTrademarks"
-
-[[annotations]]
-path = ["testapp.0.8.tar.gz", "testapp.tar.gz", "testapp1.tar.gz"]
-precedence = "aggregate"
-SPDX-FileCopyrightText = "2016 Nextcloud GmbH and Nextcloud contributors"
-SPDX-License-Identifier = "AGPL-3.0-or-later"
-
-[[annotations]]
-path = ["certificates/goodCertificate.crt", "emails/new-account-email-custom-text-alternative.txt", "emails/new-account-email-custom.html", "emails/new-account-email-custom.txt", "emails/new-account-email-single-button.html", "emails/new-account-email-single-button.txt", "emails/new-account-email.html", "emails/new-account-email.txt"]
-precedence = "aggregate"
-SPDX-FileCopyrightText = "2017 Nextcloud GmbH and Nextcloud contributors"
-SPDX-License-Identifier = "AGPL-3.0-or-later"
-
-[[annotations]]
-path = ["testimage.heic"]
-precedence = "aggregate"
-SPDX-FileCopyrightText = "2018 Nextcloud GmbH and Nextcloud contributors"
-SPDX-License-Identifier = "AGPL-3.0-or-later"
-
-[[annotations]]
-path = ["integritycheck/mimetypeListModified/core/js/mimetypelist.js", "integritycheck/mimetypeListModified/core/signature.json", "guest_avatar_einstein_32.png", "guest_avatar_einstein_32.svg", "test.pdf"]
-precedence = "aggregate"
-SPDX-FileCopyrightText = "2019 Nextcloud GmbH and Nextcloud contributors"
-SPDX-License-Identifier = "AGPL-3.0-or-later"
-
-[[annotations]]
-path = ["testimage-badheader.jpg"]
-precedence = "aggregate"
-SPDX-FileCopyrightText = "2021 Nextcloud GmbH and Nextcloud contributors"
-SPDX-License-Identifier = "AGPL-3.0-or-later"
-
-[[annotations]]
-path = ["testimage-large.jpg"]
-precedence = "aggregate"
-SPDX-FileCopyrightText = "2023 Nextcloud GmbH and Nextcloud contributors"
-SPDX-License-Identifier = "AGPL-3.0-or-later"
-
-[[annotations]]
-path = ["testvideo-remote-file.mp4"]
-precedence = "aggregate"
-SPDX-FileCopyrightText = "2025 Nextcloud GmbH and Nextcloud contributors"
-SPDX-License-Identifier = "AGPL-3.0-or-later"
-
-[[annotations]]
-path = "images/image.jpg"
-precedence = "aggregate"
-SPDX-FileCopyrightText = "2019 Tom Gainor "
-SPDX-License-Identifier = "LicenseRef-Unsplash"
--- a/tests/data/app/appinfo-attributes-once.json
+++ /dev/null
@@ -1,162 +0,0 @@
-{
- "id": "attributes_once",
- "name": {
- "@attributes": {
- "lang": "en"
- },
- "@value": "Attributes Once"
- },
- "summary": {
- "@attributes": {
- "lang": "en"
- },
- "@value": "Single occurrence with attributes set on allowed elements."
- },
- "description": {
- "@attributes": {
- "lang": "en"
- },
- "@value": "Fixture that sets attributes where allowed (e.g., lang, type, min-version, for)."
- },
- "version": "1.2.3",
- "licence": "agpl",
- "author": {
- "@attributes": {
- "homepage": "http://example.com",
- "mail": "jane@example.com"
- },
- "@value": "Jane Doe"
- },
- "types": [
- "filesystem"
- ],
- "documentation": {
- "user": "https://example.test/attributes-once/user"
- },
- "category": [
- "tools"
- ],
- "website": "https://example.test/attributes-once",
- "bugs": "https://example.com/issues",
- "repository": {
- "@attributes": {
- "type": "git"
- },
- "@value": "https://example.test/attributes-once.git"
- },
- "screenshot": {
- "@attributes": {
- "small-thumbnail": "https://example.test/attributes-once-small.png"
- },
- "@value": "https://example.test/attributes-once.png"
- },
- "dependencies": {
- "php": {
- "@attributes": {
- "min-version": "8.2"
- }
- },
- "database": {
- "@attributes": {
- "min-version": "2.0"
- },
- "@value": "pgsql"
- },
- "lib": {
- "@attributes": {
- "min-version": "1.5"
- },
- "@value": "curl"
- },
- "owncloud": {
- "@attributes": {
- "min-version": "1.0",
- "max-version": "2.0"
- }
- },
- "nextcloud": {
- "@attributes": {
- "min-version": "30.0",
- "max-version": "31.0"
- }
- },
- "backend": [
- "caldav"
- ]
- },
- "background-jobs": {
- "job": "OCA\\AttributesOnce\\BackgroundJob\\Job"
- },
- "repair-steps": {
- "install": {
- "step": "OCA\\AttributesOnce\\RepairStep\\Install"
- },
- "pre-migration": [],
- "post-migration": [],
- "live-migration": [],
- "uninstall": []
- },
- "commands": {
- "command": "OCA\\AttributesOnce\\Command\\Run"
- },
- "settings": {
- "admin": [
- "OCA\\AttributesOnce\\Settings\\Admin"
- ],
- "admin-section": [],
- "personal": [],
- "personal-section": []
- },
- "activity": {
- "providers": {
- "provider": "OCA\\AttributesOnce\\Activity\\Provider"
- },
- "filters": [],
- "settings": []
- },
- "navigations": {
- "navigation": [
- {
- "@attributes": {
- "role": "admin"
- },
- "name": "Attributes",
- "route": "attributes.once.route",
- "icon": "attributes-once.svg",
- "order": "5"
- }
- ]
- },
- "collaboration": {
- "plugins": {
- "@attributes": {
- "type": "collaborator-search"
- },
- "@value": "OCA\\AttributesOnce\\Collaboration\\Plugin"
- }
- },
- "sabre": {
- "plugins": {
- "plugin": "OCA\\AttributesOnce\\Sabre\\Plugin"
- }
- },
- "trash": {
- "backend": {
- "@attributes": {
- "for": "files"
- },
- "@value": "OCA\\AttributesOnce\\Trash\\Backend"
- }
- },
- "versions": {
- "backend": {
- "@attributes": {
- "for": "files"
- },
- "@value": "OCA\\AttributesOnce\\Versions\\Backend"
- }
- },
- "remote": [],
- "public": [],
- "two-factor-providers": []
-}
--- a/tests/data/app/appinfo-attributes-once.json.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
-SPDX-License-Identifier: AGPL-3.0-or-later
--- a/tests/data/app/appinfo-attributes-once.xml
+++ /dev/null
@@ -1,77 +0,0 @@
-
-
-
- attributes_once
- Attributes Once
- Single occurrence with attributes set on allowed elements.
- Fixture that sets attributes where allowed (e.g., lang, type, min-version, for).
- 1.2.3
- agpl
- Jane Doe
-
-
-
-
- https://example.test/attributes-once/user
-
- tools
- https://example.test/attributes-once
- https://example.com/issues
- https://example.test/attributes-once.git
- https://example.test/attributes-once.png
-
-
- pgsql
- curl
-
-
- caldav
-
-
- OCA\AttributesOnce\BackgroundJob\Job
-
-
-
- OCA\AttributesOnce\RepairStep\Install
-
-
-
- OCA\AttributesOnce\Command\Run
-
-
- OCA\AttributesOnce\Settings\Admin
-
-
-
- OCA\AttributesOnce\Activity\Provider
-
-
-
-
- Attributes
- attributes.once.route
- attributes-once.svg
- 5
-
-
-
-
- OCA\AttributesOnce\Collaboration\Plugin
-
-
-
-
- OCA\AttributesOnce\Sabre\Plugin
-
-
-
- OCA\AttributesOnce\Trash\Backend
-
-
- OCA\AttributesOnce\Versions\Backend
-
-
--- a/tests/data/app/appinfo-multi-once.json
+++ /dev/null
@@ -1,192 +0,0 @@
-{
- "id": "multi_once",
- "name": "Multi Once",
- "summary": "Every repeatable element is used exactly once.",
- "description": "Fixture that exercises the single-item normalization path.",
- "version": "1.0.0",
- "licence": "agpl",
- "author": [
- "Jane Doe"
- ],
- "types": [
- "filesystem",
- "logging"
- ],
- "documentation": {
- "user": "https://example.test/multi-once/user",
- "admin": "https://example.test/multi-once/admin",
- "developer": "https://example.test/multi-once/developer"
- },
- "category": [
- "monitoring"
- ],
- "website": "https://example.test/multi-once",
- "discussion": "https://example.test/multi-once/discussion",
- "bugs": "https://example.test/multi-once/issues",
- "repository": "https://example.test/multi-once.git",
- "screenshot": [
- "https://example.test/multi-once.png"
- ],
- "donation": "https://example.test/donate",
- "dependencies": {
- "database": "sqlite",
- "command": "awk",
- "lib": {
- "@attributes": {
- "min-version": "1.0"
- },
- "@value": "curl"
- },
- "nextcloud": {
- "@attributes": {
- "min-version": "30.0",
- "max-version": "31.0"
- }
- },
- "architecture": "x86_64",
- "backend": [
- "caldav"
- ]
- },
- "background-jobs": {
- "job": "OCA\\MultiOnce\\BackgroundJob\\Cleanup"
- },
- "repair-steps": {
- "pre-migration": {
- "step": "OCA\\MultiOnce\\RepairStep\\PreMigration"
- },
- "post-migration": {
- "step": "OCA\\MultiOnce\\RepairStep\\PostMigration"
- },
- "live-migration": {
- "step": "OCA\\MultiOnce\\RepairStep\\LiveMigration"
- },
- "install": {
- "step": "OCA\\MultiOnce\\RepairStep\\Install"
- },
- "uninstall": {
- "step": "OCA\\MultiOnce\\RepairStep\\Uninstall"
- }
- },
- "two-factor-providers": {
- "provider": "OCA\\MultiOnce\\TwoFactor\\Provider"
- },
- "commands": {
- "command": "OCA\\MultiOnce\\Command\\Migrate"
- },
- "settings": {
- "admin": [
- "OCA\\MultiOnce\\Settings\\Admin"
- ],
- "admin-section": [
- "OCA\\MultiOnce\\Settings\\AdminSection"
- ],
- "personal": [
- "OCA\\MultiOnce\\Settings\\Personal"
- ],
- "personal-section": [
- "OCA\\MultiOnce\\Settings\\PersonalSection"
- ],
- "admin-delegation": [
- "OCA\\MultiOnce\\Settings\\AdminDelegation"
- ],
- "admin-delegation-section": [
- "OCA\\MultiOnce\\Settings\\AdminDelegationSection"
- ]
- },
- "activity": {
- "settings": {
- "setting": "OCA\\MultiOnce\\Activity\\Setting"
- },
- "filters": {
- "filter": "OCA\\MultiOnce\\Activity\\Filter"
- },
- "providers": {
- "provider": "OCA\\MultiOnce\\Activity\\Provider"
- }
- },
- "dashboard": {
- "widget": "OCA\\MultiOnce\\Dashboard\\Widget"
- },
- "fulltextsearch": {
- "platform": "OCA\\MultiOnce\\Search\\Platform",
- "provider": "OCA\\MultiOnce\\Search\\Provider"
- },
- "navigations": {
- "navigation": [
- {
- "name": "Multi Once",
- "route": "multi.once.route",
- "icon": "multi-once.svg",
- "order": "1"
- }
- ]
- },
- "contactsmenu": {
- "provider": "OCA\\MultiOnce\\ContactsMenu\\Provider"
- },
- "collaboration": {
- "plugins": {
- "@attributes": {
- "type": "collaborator-search"
- },
- "@value": "OCA\\MultiOnce\\Collaboration\\Plugin"
- }
- },
- "openmetrics": {
- "exporter": [
- "OCA\\MultiOnce\\OpenMetrics\\Exporter"
- ]
- },
- "sabre": {
- "collections": {
- "collection": "OCA\\MultiOnce\\Sabre\\Collection"
- },
- "plugins": {
- "plugin": "OCA\\MultiOnce\\Sabre\\Plugin"
- },
- "address-book-plugins": {
- "plugin": "OCA\\MultiOnce\\Sabre\\AddressBookPlugin"
- },
- "calendar-plugins": {
- "plugin": "OCA\\MultiOnce\\Sabre\\CalendarPlugin"
- }
- },
- "trash": {
- "backend": {
- "@attributes": {
- "for": "files"
- },
- "@value": "OCA\\MultiOnce\\Trash\\Backend"
- }
- },
- "versions": {
- "backend": {
- "@attributes": {
- "for": "files"
- },
- "@value": "OCA\\MultiOnce\\Versions\\Backend"
- }
- },
- "external-app": {
- "docker-install": {
- "registry": "registry.example.test",
- "image": "multi-once",
- "image-tag": "1.0.0"
- },
- "scopes": {
- "value": "scope-one"
- },
- "system": "true",
- "environment-variables": {
- "variable": {
- "name": "MULTI_ONCE_ONE",
- "display-name": "Multi Once One",
- "description": "First variable",
- "default": "one"
- }
- }
- },
- "remote": [],
- "public": []
-}
--- a/tests/data/app/appinfo-multi-once.json.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
-SPDX-License-Identifier: AGPL-3.0-or-later
\ No newline at end of file
--- a/tests/data/app/appinfo-multi-once.xml
+++ /dev/null
@@ -1,149 +0,0 @@
-
-
-
- multi_once
- Multi Once
- Every repeatable element is used exactly once.
- Fixture that exercises the single-item normalization path.
- 1.0.0
- agpl
- Jane Doe
-
-
-
-
-
- https://example.test/multi-once/user
- https://example.test/multi-once/admin
- https://example.test/multi-once/developer
-
- monitoring
- https://example.test/multi-once
- https://example.test/multi-once/discussion
- https://example.test/multi-once/issues
- https://example.test/multi-once.git
- https://example.test/multi-once.png
- https://example.test/donate
-
- sqlite
- awk
- curl
-
- x86_64
- caldav
-
-
- OCA\MultiOnce\BackgroundJob\Cleanup
-
-
-
- OCA\MultiOnce\RepairStep\PreMigration
-
-
- OCA\MultiOnce\RepairStep\PostMigration
-
-
- OCA\MultiOnce\RepairStep\LiveMigration
-
-
- OCA\MultiOnce\RepairStep\Install
-
-
- OCA\MultiOnce\RepairStep\Uninstall
-
-
-
- OCA\MultiOnce\TwoFactor\Provider
-
-
- OCA\MultiOnce\Command\Migrate
-
-
- OCA\MultiOnce\Settings\Admin
- OCA\MultiOnce\Settings\AdminSection
- OCA\MultiOnce\Settings\Personal
- OCA\MultiOnce\Settings\PersonalSection
- OCA\MultiOnce\Settings\AdminDelegation
- OCA\MultiOnce\Settings\AdminDelegationSection
-
-
-
- OCA\MultiOnce\Activity\Setting
-
-
- OCA\MultiOnce\Activity\Filter
-
-
- OCA\MultiOnce\Activity\Provider
-
-
-
- OCA\MultiOnce\Dashboard\Widget
-
-
- OCA\MultiOnce\Search\Platform
- OCA\MultiOnce\Search\Provider
-
-
-
- Multi Once
- multi.once.route
- multi-once.svg
- 1
-
-
-
- OCA\MultiOnce\ContactsMenu\Provider
-
-
-
- OCA\MultiOnce\Collaboration\Plugin
-
-
-
- OCA\MultiOnce\OpenMetrics\Exporter
-
-
-
- OCA\MultiOnce\Sabre\Collection
-
-
- OCA\MultiOnce\Sabre\Plugin
-
-
- OCA\MultiOnce\Sabre\AddressBookPlugin
-
-
- OCA\MultiOnce\Sabre\CalendarPlugin
-
-
-
- OCA\MultiOnce\Trash\Backend
-
-
- OCA\MultiOnce\Versions\Backend
-
-
-
- registry.example.test
- multi-once
- 1.0.0
-
-
- scope-one
-
- true
-
-
- MULTI_ONCE_ONE
- Multi Once One
- First variable
- one
-
-
-
-
\ No newline at end of file
--- a/tests/data/app/appinfo-multi-twice.json
+++ /dev/null
@@ -1,325 +0,0 @@
-{
- "id": "multi_twice",
- "name": "Multi Twice",
- "summary": "Every repeatable element is used exactly twice.",
- "description": "Fixture that exercises the list normalization path.",
- "version": "1.0.0",
- "licence": [
- "agpl",
- "mit"
- ],
- "author": [
- "Jane Doe",
- "John Doe"
- ],
- "types": [
- "filesystem",
- "logging"
- ],
- "documentation": {
- "user": "https://example.test/multi-twice/user",
- "admin": "https://example.test/multi-twice/admin",
- "developer": "https://example.test/multi-twice/developer"
- },
- "category": [
- "monitoring",
- "social"
- ],
- "website": "https://example.test/multi-twice",
- "discussion": "https://example.test/multi-twice/discussion",
- "bugs": "https://example.test/multi-twice/issues",
- "repository": {
- "@attributes": {
- "type": "git"
- },
- "@value": "https://example.test/multi-twice.git"
- },
- "screenshot": [
- "https://example.test/multi-twice-1.png",
- "https://example.test/multi-twice-2.png"
- ],
- "donation": [
- "https://example.test/donate/1",
- "https://example.test/donate/2"
- ],
- "dependencies": {
- "php": {
- "@attributes": {
- "min-version": "8.2"
- }
- },
- "database": [
- {
- "@attributes": {
- "min-version": "1.0"
- },
- "@value": "sqlite"
- },
- {
- "@attributes": {
- "min-version": "1.0"
- },
- "@value": "mysql"
- }
- ],
- "command": [
- "awk",
- "grep"
- ],
- "lib": [
- {
- "@attributes": {
- "min-version": "1.0"
- },
- "@value": "curl"
- },
- {
- "@attributes": {
- "min-version": "1.0"
- },
- "@value": "intl"
- }
- ],
- "owncloud": {
- "@attributes": {
- "min-version": "1.0",
- "max-version": "2.0"
- }
- },
- "nextcloud": {
- "@attributes": {
- "min-version": "30.0",
- "max-version": "31.0"
- }
- },
- "architecture": [
- "x86_64",
- "aarch64"
- ],
- "backend": [
- "caldav",
- "caldav"
- ]
- },
- "background-jobs": [
- "OCA\\MultiTwice\\BackgroundJob\\CleanupOne",
- "OCA\\MultiTwice\\BackgroundJob\\CleanupTwo"
- ],
- "repair-steps": {
- "pre-migration": [
- "OCA\\MultiTwice\\RepairStep\\PreMigrationOne",
- "OCA\\MultiTwice\\RepairStep\\PreMigrationTwo"
- ],
- "post-migration": [
- "OCA\\MultiTwice\\RepairStep\\PostMigrationOne",
- "OCA\\MultiTwice\\RepairStep\\PostMigrationTwo"
- ],
- "live-migration": [
- "OCA\\MultiTwice\\RepairStep\\LiveMigrationOne",
- "OCA\\MultiTwice\\RepairStep\\LiveMigrationTwo"
- ],
- "install": [
- "OCA\\MultiTwice\\RepairStep\\InstallOne",
- "OCA\\MultiTwice\\RepairStep\\InstallTwo"
- ],
- "uninstall": [
- "OCA\\MultiTwice\\RepairStep\\UninstallOne",
- "OCA\\MultiTwice\\RepairStep\\UninstallTwo"
- ]
- },
- "two-factor-providers": [
- "OCA\\MultiTwice\\TwoFactor\\ProviderOne",
- "OCA\\MultiTwice\\TwoFactor\\ProviderTwo"
- ],
- "commands": [
- "OCA\\MultiTwice\\Command\\MigrateOne",
- "OCA\\MultiTwice\\Command\\MigrateTwo"
- ],
- "settings": {
- "admin": [
- "OCA\\MultiTwice\\Settings\\AdminOne",
- "OCA\\MultiTwice\\Settings\\AdminTwo"
- ],
- "admin-section": [
- "OCA\\MultiTwice\\Settings\\AdminSectionOne",
- "OCA\\MultiTwice\\Settings\\AdminSectionTwo"
- ],
- "personal": [
- "OCA\\MultiTwice\\Settings\\PersonalOne",
- "OCA\\MultiTwice\\Settings\\PersonalTwo"
- ],
- "personal-section": [
- "OCA\\MultiTwice\\Settings\\PersonalSectionOne",
- "OCA\\MultiTwice\\Settings\\PersonalSectionTwo"
- ],
- "admin-delegation": [
- "OCA\\MultiTwice\\Settings\\AdminDelegationOne",
- "OCA\\MultiTwice\\Settings\\AdminDelegationTwo"
- ],
- "admin-delegation-section": [
- "OCA\\MultiTwice\\Settings\\AdminDelegationSectionOne",
- "OCA\\MultiTwice\\Settings\\AdminDelegationSectionTwo"
- ]
- },
- "activity": {
- "settings": [
- "OCA\\MultiTwice\\Activity\\SettingOne",
- "OCA\\MultiTwice\\Activity\\SettingTwo"
- ],
- "filters": [
- "OCA\\MultiTwice\\Activity\\FilterOne",
- "OCA\\MultiTwice\\Activity\\FilterTwo"
- ],
- "providers": [
- "OCA\\MultiTwice\\Activity\\ProviderOne",
- "OCA\\MultiTwice\\Activity\\ProviderTwo"
- ]
- },
- "dashboard": {
- "widget": [
- "OCA\\MultiTwice\\Dashboard\\WidgetOne",
- "OCA\\MultiTwice\\Dashboard\\WidgetTwo"
- ]
- },
- "fulltextsearch": {
- "platform": [
- "OCA\\MultiTwice\\Search\\PlatformOne",
- "OCA\\MultiTwice\\Search\\PlatformTwo"
- ],
- "provider": [
- "OCA\\MultiTwice\\Search\\ProviderOne",
- "OCA\\MultiTwice\\Search\\ProviderTwo"
- ]
- },
- "navigations": {
- "navigation": [
- {
- "name": "Multi Twice One",
- "route": "multi.twice.one",
- "icon": "multi-twice-1.svg",
- "order": "1"
- },
- {
- "name": "Multi Twice Two",
- "route": "multi.twice.two",
- "icon": "multi-twice-2.svg",
- "order": "2"
- }
- ]
- },
- "contactsmenu": {
- "provider": "OCA\\MultiTwice\\ContactsMenu\\Provider"
- },
- "collaboration": {
- "plugins": [
- {
- "@attributes": {
- "type": "collaborator-search"
- },
- "@value": "OCA\\MultiTwice\\Collaboration\\PluginOne"
- },
- {
- "@attributes": {
- "type": "autocomplete-sort"
- },
- "@value": "OCA\\MultiTwice\\Collaboration\\PluginTwo"
- }
- ]
- },
- "openmetrics": {
- "exporter": [
- "OCA\\MultiTwice\\OpenMetrics\\ExporterOne",
- "OCA\\MultiTwice\\OpenMetrics\\ExporterTwo"
- ]
- },
- "sabre": {
- "collections": {
- "collection": [
- "OCA\\MultiTwice\\Sabre\\CollectionOne",
- "OCA\\MultiTwice\\Sabre\\CollectionTwo"
- ]
- },
- "plugins": {
- "plugin": [
- "OCA\\MultiTwice\\Sabre\\PluginOne",
- "OCA\\MultiTwice\\Sabre\\PluginTwo"
- ]
- },
- "address-book-plugins": {
- "plugin": [
- "OCA\\MultiTwice\\Sabre\\AddressBookPluginOne",
- "OCA\\MultiTwice\\Sabre\\AddressBookPluginTwo"
- ]
- },
- "calendar-plugins": {
- "plugin": [
- "OCA\\MultiTwice\\Sabre\\CalendarPluginOne",
- "OCA\\MultiTwice\\Sabre\\CalendarPluginTwo"
- ]
- }
- },
- "trash": {
- "backend": [
- {
- "@attributes": {
- "for": "files"
- },
- "@value": "OCA\\MultiTwice\\Trash\\BackendOne"
- },
- {
- "@attributes": {
- "for": "files"
- },
- "@value": "OCA\\MultiTwice\\Trash\\BackendTwo"
- }
- ]
- },
- "versions": {
- "backend": [
- {
- "@attributes": {
- "for": "files"
- },
- "@value": "OCA\\MultiTwice\\Versions\\BackendOne"
- },
- {
- "@attributes": {
- "for": "files"
- },
- "@value": "OCA\\MultiTwice\\Versions\\BackendTwo"
- }
- ]
- },
- "external-app": {
- "docker-install": {
- "registry": "registry.example.test",
- "image": "multi-twice",
- "image-tag": "2.0.0"
- },
- "scopes": {
- "value": [
- "scope-one",
- "scope-two"
- ]
- },
- "system": "true",
- "environment-variables": {
- "variable": [
- {
- "name": "MULTI_TWICE_ONE",
- "display-name": "Multi Twice One",
- "description": "First variable",
- "default": "one"
- },
- {
- "name": "MULTI_TWICE_TWO",
- "display-name": "Multi Twice Two",
- "description": "Second variable",
- "default": "two"
- }
- ]
- }
- },
- "remote": [],
- "public": []
-}
--- a/tests/data/app/appinfo-multi-twice.json.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
-SPDX-License-Identifier: AGPL-3.0-or-later
\ No newline at end of file
--- a/tests/data/app/appinfo-multi-twice.xml
+++ /dev/null
@@ -1,202 +0,0 @@
-
-
-
- multi_twice
- Multi Twice
- Every repeatable element is used exactly twice.
- Fixture that exercises the list normalization path.
- 1.0.0
- agpl
- mit
- Jane Doe
- John Doe
-
-
-
-
-
- https://example.test/multi-twice/user
- https://example.test/multi-twice/admin
- https://example.test/multi-twice/developer
-
- monitoring
- social
- https://example.test/multi-twice
- https://example.test/multi-twice/discussion
- https://example.test/multi-twice/issues
- https://example.test/multi-twice.git
- https://example.test/multi-twice-1.png
- https://example.test/multi-twice-2.png
- https://example.test/donate/1
- https://example.test/donate/2
-
-
- sqlite
- mysql
- awk
- grep
- curl
- intl
-
-
- x86_64
- aarch64
- caldav
- caldav
-
-
- OCA\MultiTwice\BackgroundJob\CleanupOne
- OCA\MultiTwice\BackgroundJob\CleanupTwo
-
-
-
- OCA\MultiTwice\RepairStep\PreMigrationOne
- OCA\MultiTwice\RepairStep\PreMigrationTwo
-
-
- OCA\MultiTwice\RepairStep\PostMigrationOne
- OCA\MultiTwice\RepairStep\PostMigrationTwo
-
-
- OCA\MultiTwice\RepairStep\LiveMigrationOne
- OCA\MultiTwice\RepairStep\LiveMigrationTwo
-
-
- OCA\MultiTwice\RepairStep\InstallOne
- OCA\MultiTwice\RepairStep\InstallTwo
-
-
- OCA\MultiTwice\RepairStep\UninstallOne
- OCA\MultiTwice\RepairStep\UninstallTwo
-
-
-
- OCA\MultiTwice\TwoFactor\ProviderOne
- OCA\MultiTwice\TwoFactor\ProviderTwo
-
-
- OCA\MultiTwice\Command\MigrateOne
- OCA\MultiTwice\Command\MigrateTwo
-
-
- OCA\MultiTwice\Settings\AdminOne
- OCA\MultiTwice\Settings\AdminTwo
- OCA\MultiTwice\Settings\AdminSectionOne
- OCA\MultiTwice\Settings\AdminSectionTwo
- OCA\MultiTwice\Settings\PersonalOne
- OCA\MultiTwice\Settings\PersonalTwo
- OCA\MultiTwice\Settings\PersonalSectionOne
- OCA\MultiTwice\Settings\PersonalSectionTwo
- OCA\MultiTwice\Settings\AdminDelegationOne
- OCA\MultiTwice\Settings\AdminDelegationTwo
- OCA\MultiTwice\Settings\AdminDelegationSectionOne
- OCA\MultiTwice\Settings\AdminDelegationSectionTwo
-
-
-
- OCA\MultiTwice\Activity\SettingOne
- OCA\MultiTwice\Activity\SettingTwo
-
-
- OCA\MultiTwice\Activity\FilterOne
- OCA\MultiTwice\Activity\FilterTwo
-
-
- OCA\MultiTwice\Activity\ProviderOne
- OCA\MultiTwice\Activity\ProviderTwo
-
-
-
- OCA\MultiTwice\Dashboard\WidgetOne
- OCA\MultiTwice\Dashboard\WidgetTwo
-
-
- OCA\MultiTwice\Search\PlatformOne
- OCA\MultiTwice\Search\PlatformTwo
- OCA\MultiTwice\Search\ProviderOne
- OCA\MultiTwice\Search\ProviderTwo
-
-
-
- Multi Twice One
- multi.twice.one
- multi-twice-1.svg
- 1
-
-
- Multi Twice Two
- multi.twice.two
- multi-twice-2.svg
- 2
-
-
-
- OCA\MultiTwice\ContactsMenu\Provider
-
-
-
- OCA\MultiTwice\Collaboration\PluginOne
- OCA\MultiTwice\Collaboration\PluginTwo
-
-
-
- OCA\MultiTwice\OpenMetrics\ExporterOne
- OCA\MultiTwice\OpenMetrics\ExporterTwo
-
-
-
- OCA\MultiTwice\Sabre\CollectionOne
- OCA\MultiTwice\Sabre\CollectionTwo
-
-
- OCA\MultiTwice\Sabre\PluginOne
- OCA\MultiTwice\Sabre\PluginTwo
-
-
- OCA\MultiTwice\Sabre\AddressBookPluginOne
- OCA\MultiTwice\Sabre\AddressBookPluginTwo
-
-
- OCA\MultiTwice\Sabre\CalendarPluginOne
- OCA\MultiTwice\Sabre\CalendarPluginTwo
-
-
-
- OCA\MultiTwice\Trash\BackendOne
- OCA\MultiTwice\Trash\BackendTwo
-
-
- OCA\MultiTwice\Versions\BackendOne
- OCA\MultiTwice\Versions\BackendTwo
-
-
-
- registry.example.test
- multi-twice
- 2.0.0
-
-
- scope-one
- scope-two
-
- true
-
-
- MULTI_TWICE_ONE
- Multi Twice One
- First variable
- one
-
-
- MULTI_TWICE_TWO
- Multi Twice Two
- Second variable
- two
-
-
-
-
\ No newline at end of file
--- a/tests/data/app/expected-info.json
+++ /dev/null
@@ -1,100 +0,0 @@
-{
- "remote": [],
- "public": [],
- "id": "files_encryption",
- "name": "Server-side Encryption",
- "description": "\n\tThis application encrypts all files accessed by ownCloud at rest, wherever they are stored. As an example, with this application enabled, external cloud based Amazon S3 storage will be encrypted, protecting this data on storage outside of the control of the Admin. When this application is enabled for the first time, all files are encrypted as users log in and are prompted for their password. The recommended recovery key option enables recovery of files in case the key is lost. \n\tNote that this app encrypts all files that are touched by ownCloud, so external storage providers and applications such as SharePoint will see new files encrypted when they are accessed. Encryption is based on AES 128 or 256 bit keys. More information is available in the Encryption documentation \n\t",
- "licence": "AGPL",
- "author": [
- "Sam Tuke, Bjoern Schiessle, Florin Peter"
- ],
- "requiremin": "4",
- "shipped": "true",
- "documentation": {
- "user": "user-encryption",
- "admin": "admin-encryption"
- },
- "types": ["filesystem"],
- "dependencies": {
- "php": {
- "@attributes" : {
- "min-version": "5.4",
- "max-version": "5.5"
- }
- },
- "database": [
- {
- "@attributes" : {
- "min-version": "3.0"
- },
- "@value": "sqlite"},
- "mysql"
- ],
- "command": [
- {
- "@attributes" : {
- "os": "linux"
- },
- "@value": "grep"
- },
- {
- "@attributes" : {
- "os": "windows"
- },
- "@value": "notepad.exe"
- }
- ],
- "lib": [
- {
- "@attributes" : {
- "min-version": "1.2"
- },
- "@value": "xml"
- },
- {
- "@attributes" : {
- "max-version": "2.0"
- },
- "@value": "intl"
- },
- "curl"
- ],
- "os": "Linux",
- "owncloud": {
- "@attributes" : {
- "min-version": "7.0.1",
- "max-version": "8"
- }
- },
- "backend": [
- "caldav"
- ]
- },
- "repair-steps": {
- "install": [],
- "pre-migration": [],
- "post-migration": [],
- "live-migration": [],
- "uninstall": []
- },
- "background-jobs": [],
- "two-factor-providers": [],
- "commands": [],
- "activity": {
- "filters": [],
- "settings": [],
- "providers": []
- },
- "settings": {
- "admin": [],
- "admin-section": [],
- "personal": [],
- "personal-section": []
- },
- "openmetrics": {
- "exporter": [
- "OC\\OpenMetrics\\Exporters\\ActiveSessions",
- "OC\\OpenMetrics\\Exporters\\ActiveUsers"
- ]
- }
-}
--- a/tests/data/app/expected-info.json.license
+++ /dev/null
@@ -1,3 +0,0 @@
-SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
-SPDX-FileCopyrightText: 2016 ownCloud, Inc.
-SPDX-License-Identifier: AGPL-3.0-only
--- a/tests/data/app/invalid-info.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-files_encryption
- Server-side Encryption
-
- This application encrypts all files accessed by ownCloud at rest, wherever they are stored. As an example, with this application enabled, external cloud based Amazon S3 storage will be encrypted, protecting this data on storage outside of the control of the Admin. When this application is enabled for the first time, all files are encrypted as users log in and are prompted for their password. The recommended recovery key option enables recovery of files in case the key is lost.
- Note that this app encrypts all files that are touched by ownCloud, so external storage providers and applications such as SharePoint will see new files encrypted when they are accessed. Encryption is based on AES 128 or 256 bit keys. More information is available in the Encryption documentation
-
- AGPL
- Sam Tuke, Bjoern Schiessle, Florin Peter
- 4
- true
-
- user-encryption
- admin-encryption
-
-
-
-
-
--- a/tests/data/app/navigation-one-item.json
+++ /dev/null
@@ -1,85 +0,0 @@
-{
- "id": "activity",
- "name": "Activity",
- "summary": "This application enables users to view actions related to their files in Nextcloud.",
- "description": "\n\t\tThis application enables users to view actions related to their files in Nextcloud.\n\t\tOnce enabled, users will see a new icon \u201cActivity\u201d in their apps menu.\n\t\tWhen clicked, a new page appears for users to track the activity related to files \u2013 from new files, to deleted files, move, rename, updates and shared activity.\n\t\tThe user can configure their individual activity settings in their personal menu.\n\t\tThis sets the type of activity to record, as well as whether to the user sees their own activities,\n\t\twhether these are only available online, and whether they get an email digest on a regular basis.\n\t\tMore information is available in the Activity documentation.\n\t",
- "version": "2.9.0",
- "licence": "agpl",
- "author": [
- "Frank Karlitschek",
- "Joas Schilling"
- ],
- "default_enable": "",
- "types": [
- "filesystem"
- ],
- "documentation": {
- "admin": "https:\/\/docs.nextcloud.org\/server\/14\/admin_manual\/configuration_server\/activity_configuration.html"
- },
- "category": [
- "monitoring",
- "social"
- ],
- "website": "https:\/\/github.com\/nextcloud\/activity\/",
- "bugs": "https:\/\/github.com\/nextcloud\/activity\/issues",
- "repository": "https:\/\/github.com\/nextcloud\/activity.git",
- "dependencies": {
- "nextcloud": {
- "@attributes": {
- "min-version": "16",
- "max-version": "16"
- }
- },
- "backend": []
- },
- "background-jobs": [
- "OCA\\Activity\\BackgroundJob\\EmailNotification",
- "OCA\\Activity\\BackgroundJob\\ExpireActivities"
- ],
- "commands": {
- "command": "OCA\\Activity\\Command\\SendEmails"
- },
- "settings": {
- "admin": [
- "OCA\\Activity\\Settings\\Admin"
- ],
- "admin-section": [
- "OCA\\Activity\\Settings\\AdminSection"
- ],
- "personal": [
- "OCA\\Activity\\Settings\\Personal"
- ],
- "personal-section": [
- "OCA\\Activity\\Settings\\PersonalSection"
- ]
- },
- "activity": {
- "filters": [
- "OCA\\Activity\\Filter\\AllFilter",
- "OCA\\Activity\\Filter\\SelfFilter",
- "OCA\\Activity\\Filter\\ByFilter"
- ],
- "settings": [],
- "providers": []
- },
- "navigations": {
- "navigation": [
- {
- "name": "Activity",
- "route": "activity.Activities.showList",
- "icon": "activity.svg",
- "order": "1"
- }
- ]
- },
- "remote": [],
- "public": [],
- "repair-steps": {
- "install": [],
- "pre-migration": [],
- "post-migration": [],
- "live-migration": [],
- "uninstall": []
- },
- "two-factor-providers": []
-}
--- a/tests/data/app/navigation-one-item.json.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
-SPDX-License-Identifier: AGPL-3.0-or-later
--- a/tests/data/app/navigation-one-item.xml
+++ /dev/null
@@ -1,78 +0,0 @@
-
-
-
- activity
- Activity
- This application enables users to view actions related to their files in Nextcloud.
-
- This application enables users to view actions related to their files in Nextcloud.
- Once enabled, users will see a new icon “Activity” in their apps menu.
- When clicked, a new page appears for users to track the activity related to files – from new files, to deleted files, move, rename, updates and shared activity.
- The user can configure their individual activity settings in their personal menu.
- This sets the type of activity to record, as well as whether to the user sees their own activities,
- whether these are only available online, and whether they get an email digest on a regular basis.
- More information is available in the Activity documentation.
-
-
- 2.9.0
- agpl
- Frank Karlitschek
- Joas Schilling
-
-
-
-
-
-
-
- https://docs.nextcloud.org/server/14/admin_manual/configuration_server/activity_configuration.html
-
-
- monitoring
- social
-
- https://github.com/nextcloud/activity/
- https://github.com/nextcloud/activity/issues
- https://github.com/nextcloud/activity.git
-
-
-
-
-
-
- OCA\Activity\BackgroundJob\EmailNotification
- OCA\Activity\BackgroundJob\ExpireActivities
-
-
-
- OCA\Activity\Command\SendEmails
-
-
-
- OCA\Activity\Settings\Admin
- OCA\Activity\Settings\AdminSection
- OCA\Activity\Settings\Personal
- OCA\Activity\Settings\PersonalSection
-
-
-
-
- OCA\Activity\Filter\AllFilter
- OCA\Activity\Filter\SelfFilter
- OCA\Activity\Filter\ByFilter
-
-
-
-
-
- Activity
- activity.Activities.showList
- activity.svg
- 1
-
-
-
--- a/tests/data/app/navigation-two-items.json
+++ /dev/null
@@ -1,91 +0,0 @@
-{
- "id": "activity",
- "name": "Activity",
- "summary": "This application enables users to view actions related to their files in Nextcloud.",
- "description": "\n\t\tThis application enables users to view actions related to their files in Nextcloud.\n\t\tOnce enabled, users will see a new icon \u201cActivity\u201d in their apps menu.\n\t\tWhen clicked, a new page appears for users to track the activity related to files \u2013 from new files, to deleted files, move, rename, updates and shared activity.\n\t\tThe user can configure their individual activity settings in their personal menu.\n\t\tThis sets the type of activity to record, as well as whether to the user sees their own activities,\n\t\twhether these are only available online, and whether they get an email digest on a regular basis.\n\t\tMore information is available in the Activity documentation.\n\t",
- "version": "2.9.0",
- "licence": "agpl",
- "author": [
- "Frank Karlitschek",
- "Joas Schilling"
- ],
- "default_enable": "",
- "types": [
- "filesystem"
- ],
- "documentation": {
- "admin": "https:\/\/docs.nextcloud.org\/server\/14\/admin_manual\/configuration_server\/activity_configuration.html"
- },
- "category": [
- "monitoring",
- "social"
- ],
- "website": "https:\/\/github.com\/nextcloud\/activity\/",
- "bugs": "https:\/\/github.com\/nextcloud\/activity\/issues",
- "repository": "https:\/\/github.com\/nextcloud\/activity.git",
- "dependencies": {
- "nextcloud": {
- "@attributes": {
- "min-version": "16",
- "max-version": "16"
- }
- },
- "backend": []
- },
- "background-jobs": [
- "OCA\\Activity\\BackgroundJob\\EmailNotification",
- "OCA\\Activity\\BackgroundJob\\ExpireActivities"
- ],
- "commands": {
- "command": "OCA\\Activity\\Command\\SendEmails"
- },
- "settings": {
- "admin": [
- "OCA\\Activity\\Settings\\Admin"
- ],
- "admin-section": [
- "OCA\\Activity\\Settings\\AdminSection"
- ],
- "personal": [
- "OCA\\Activity\\Settings\\Personal"
- ],
- "personal-section": [
- "OCA\\Activity\\Settings\\PersonalSection"
- ]
- },
- "activity": {
- "filters": [
- "OCA\\Activity\\Filter\\AllFilter",
- "OCA\\Activity\\Filter\\SelfFilter",
- "OCA\\Activity\\Filter\\ByFilter"
- ],
- "settings": [],
- "providers": []
- },
- "navigations": {
- "navigation": [
- {
- "name": "Activity",
- "route": "activity.Activities.showList",
- "icon": "activity.svg",
- "order": "1"
- },
- {
- "name": "Activity-Test",
- "route": "activity.Activities.showList",
- "icon": "activity.svg",
- "order": "2"
- }
- ]
- },
- "remote": [],
- "public": [],
- "repair-steps": {
- "install": [],
- "pre-migration": [],
- "post-migration": [],
- "live-migration": [],
- "uninstall": []
- },
- "two-factor-providers": []
-}
--- a/tests/data/app/navigation-two-items.json.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
-SPDX-License-Identifier: AGPL-3.0-or-later
--- a/tests/data/app/navigation-two-items.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-
-
-
- activity
- Activity
- This application enables users to view actions related to their files in Nextcloud.
-
- This application enables users to view actions related to their files in Nextcloud.
- Once enabled, users will see a new icon “Activity” in their apps menu.
- When clicked, a new page appears for users to track the activity related to files – from new files, to deleted files, move, rename, updates and shared activity.
- The user can configure their individual activity settings in their personal menu.
- This sets the type of activity to record, as well as whether to the user sees their own activities,
- whether these are only available online, and whether they get an email digest on a regular basis.
- More information is available in the Activity documentation.
-
-
- 2.9.0
- agpl
- Frank Karlitschek
- Joas Schilling
-
-
-
-
-
-
-
- https://docs.nextcloud.org/server/14/admin_manual/configuration_server/activity_configuration.html
-
-
- monitoring
- social
-
- https://github.com/nextcloud/activity/
- https://github.com/nextcloud/activity/issues
- https://github.com/nextcloud/activity.git
-
-
-
-
-
-
- OCA\Activity\BackgroundJob\EmailNotification
- OCA\Activity\BackgroundJob\ExpireActivities
-
-
-
- OCA\Activity\Command\SendEmails
-
-
-
- OCA\Activity\Settings\Admin
- OCA\Activity\Settings\AdminSection
- OCA\Activity\Settings\Personal
- OCA\Activity\Settings\PersonalSection
-
-
-
-
- OCA\Activity\Filter\AllFilter
- OCA\Activity\Filter\SelfFilter
- OCA\Activity\Filter\ByFilter
-
-
-
-
-
- Activity
- activity.Activities.showList
- activity.svg
- 1
-
-
- Activity-Test
- activity.Activities.showList
- activity.svg
- 2
-
-
-
--- a/tests/data/app/valid-info.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
- files_encryption
- Server-side Encryption
-
- This application encrypts all files accessed by ownCloud at rest, wherever they are stored. As an example, with this application enabled, external cloud based Amazon S3 storage will be encrypted, protecting this data on storage outside of the control of the Admin. When this application is enabled for the first time, all files are encrypted as users log in and are prompted for their password. The recommended recovery key option enables recovery of files in case the key is lost.
- Note that this app encrypts all files that are touched by ownCloud, so external storage providers and applications such as SharePoint will see new files encrypted when they are accessed. Encryption is based on AES 128 or 256 bit keys. More information is available in the Encryption documentation
-
- AGPL
- Sam Tuke, Bjoern Schiessle, Florin Peter
- 4
- true
-
- user-encryption
- admin-encryption
-
-
-
-
-
-
- sqlite
- mysql
- grep
- notepad.exe
- xml
- intl
- curl
- Linux
-
- caldav
-
-
-
- OC\OpenMetrics\Exporters\ActiveSessions
- OC\OpenMetrics\Exporters\ActiveUsers
-
-
--- a/tests/data/app/various-single-item.json
+++ /dev/null
@@ -1,54 +0,0 @@
-{
- "id": "notifications",
- "name": "Notifications",
- "description": "A single screenshot should be an array",
- "version": "1.0.0",
- "licence": "agpl",
- "author": [
- "Joas Schilling"
- ],
- "dependencies": {
- "nextcloud": {
- "@attributes": {
- "min-version": "16",
- "max-version": "16"
- }
- },
- "backend": []
- },
- "screenshot": [
- "https://raw.githubusercontent.com/nextcloud/notifications/refs/heads/master/docs/screenshot.png"
- ],
- "category": [
- "monitoring"
- ],
- "background-jobs": [],
- "activity": {
- "filters": [],
- "settings": [],
- "providers": []
- },
- "commands": [],
- "remote": [],
- "public": [],
- "repair-steps": {
- "install": [],
- "pre-migration": [],
- "post-migration": [],
- "live-migration": [],
- "uninstall": []
- },
- "settings": {
- "admin": [],
- "admin-section": [],
- "personal": [],
- "personal-section": []
- },
- "two-factor-providers": [],
- "types": [],
- "openmetrics": {
- "exporter": [
- "OC\\OpenMetrics\\Exporters\\ActiveUsers"
- ]
- }
-}
--- a/tests/data/app/various-single-item.json.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
-SPDX-License-Identifier: AGPL-3.0-or-later
--- a/tests/data/app/various-single-item.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
- notifications
- Notifications
- A single screenshot should be an array
- 1.0.0
- agpl
- Joas Schilling
-
- monitoring
-
- https://raw.githubusercontent.com/nextcloud/notifications/refs/heads/master/docs/screenshot.png
-
-
-
-
-
-
- OC\OpenMetrics\Exporters\ActiveUsers
-
-
--- a/tests/data/block-aligned-plus-one.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse id hendrerit felis. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed dictum dolor nec lobortis sagittis. Aliquam blandit lobortis ullamcorper. Donec malesuada ante ante, vel cursus purus suscipit ut. Sed a eros vitae lorem tristique hendrerit a a odio. Cras interdum mi lectus, sed molestie mauris tempus sit amet. Praesent auctor neque venenatis diam tincidunt sodales. Maecenas tincidunt ligula et ipsum dignissim, et molestie dui hendrerit. Nunc lobortis mauris vel tempor tristique. Sed consectetur bibendum nunc. Pellentesque augue velit, interdum ac posuere eu, eleifend et massa. Vestibulum ac mi eu mi viverra iaculis. Sed consequat leo adipiscing dui varius, sagittis vestibulum massa vehicula.
-
-Praesent imperdiet erat vitae dui placerat, sit amet vulputate tortor viverra. In laoreet elit lorem, at luctus est tempor ac. Nam elementum scelerisque nisl vitae accumsan. Curabitur cursus rutrum est, ut adipiscing ante elementum ut. Ut lectus sem, luctus ut massa non, dignissim vestibulum ipsum. Suspendisse non convallis enim, aliquam adipiscing velit. Donec pharetra non turpis hendrerit porttitor. Aliquam erat volutpat.
-
-Cras eget arcu eu dolor faucibus accumsan non eget orci. Integer quis quam tincidunt, faucibus neque in, imperdiet lacus. Aenean dui turpis, lacinia quis ligula non, semper adipiscing tellus. Curabitur vitae dui quis nisl malesuada commodo. Nunc eleifend metus enim, eu aliquet dui semper sit amet. Sed a odio sapien. Suspendisse vitae ante id sapien semper accumsan. Sed vestibulum erat quis laoreet pellentesque. Nullam placerat ligula eu odio faucibus, eget dictum orci tristique. Quisque sit amet pulvinar velit, lacinia dictum sem.
-
-Phasellus id sagittis lacus. Vivamus facilisis convallis metus, sit amet ultricies purus gravida quis. Vivamus eget egestas arcu, a euismod risus. Fusce metus arcu, molestie ut lacinia at, commodo eu nibh. In leo tortor, feugiat aliquet semper in, malesuada ac nulla. Fusce tempor ultricies blandit. Pellentesque et lorem quam. Suspendisse eros eros, mattis ut porttitor vitae, fermentum eget augue. Aliquam vitae justo sed est dictum lobortis. Cras lacinia commodo ligula sed ornare. Donec ut eros semper, sodales tellus quis, cursus quam. Quisque elementum ullamcorper pellentesque. Integer in rutrum ante, vel condimentum metus. Aliquam erat volutpat.
-
-Quisque convallis tempus eros, sit amet mattis ipsum eleifend in. Mauris volutpat, urna ut commodo tempus, est quam scelerisque erat, eget consequat nulla arcu id eros. Nam elementum nibh quis tincidunt adipiscing. Fusce tristique pretium mollis. Nulla quis ornare felis, vel lacinia diam. In et dui et mauris vestibulum commodo. In hac habitasse platea dictumst. Vestibulum viverra pellentesque tortor, tempor vehicula orci. Praesent rutrum turpis ipsum, ac commodo nisi vehicula ac.
-
-Nulla gravida ultrices mauris. Ut congue purus nec dolor euismod, vitae auctor elit condimentum. Nullam elementum velit lectus, fringilla pharetra sem hendrerit sit amet. Cras quis lobortis nisi, id interdum massa. Proin eget porttitor sapien. Vivamus quam odio, consectetur at rhoncus vel, posuere in nisi. Donec hendrerit urna ac massa tristique, pellentesque consequat orci dapibus. Mauris mi erat, aliquet id adipiscing ultricies, ultrices nec lacus. Proin imperdiet elit magna, ut suscipit metus faucibus eget. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas at leo lorem. Donec mi nibh, blandit ut mi quis, dictum adipiscing ante. Curabitur sodales blandit odio id viverra. Fusce sagittis rutrum nibh. Mauris risus lacus, placerat at dictum in, varius volutpat quam.
-
-Ut accumsan tincidunt dui, non semper lorem sodales nec. Curabitur pellentesque velit nec erat ultrices, in convallis diam suscipit. Curabitur pellentesque convallis mi. Phasellus iaculis orci quis nisi semper auctor. Vestibulum eleifend tortor quis odio feugiat, pulvinar congue turpis blandit. Sed viverra diam risus, vel mattis orci dignissim ut. Cras pellentesque dapibus dolor. Morbi tempor eleifend magna. Ut sodales ut sapien sed elementum. In hac habitasse platea dictumst.
-
-Nullam eget sagittis tellus. Donec a tellus id ligula viverra pellentesque. Pellentesque nulla turpis, pretium vel sem vel, placerat ultricies diam. Donec felis nibh, rhoncus id ornare at, lobortis vel massa. Nullam eu luctus ipsum. Mauris consequat dictum aliquam. Suspendisse ullamcorper aliquam mauris, viverra tincidunt augue suscipit at. Sed luctus scelerisque justo ut semper. Phasellus massa mauris, molestie at nulla eget, dictum ultricies nisi. Aliquam rhoncus nisl sed urna egestas, vel luctus lectus euismod. Mauris sagittis dapibus leo. Donec vitae dignissim risus, ac ultricies augue. Ut vulputate tortor sed dui consectetur placerat. Nullam viverra non diam vel dignissim.
-
-Suspendisse potenti. Integer facilisis neque vitae euismod adipiscing. Aenean dictum leo commodo dui sodales, sit amet volutpat mauris gravida. Proin tempus convallis eros at consectetur. Proin nisl purus, dictum vel euismod ut, imperdiet quis est. Etiam sollicitudin lobortis neque eget pulvinar. Etiam venenatis vel sem ut posuere. Aliquam consectetur rhoncus facilisis. Morbi a viverra orci.
-
-Praesent ut vehicula orci, vel convallis risus. Suspendisse consectetur varius interdum. Interdum et malesuada fames ac ante ipsum primis in faucibus. Proin vel sodales enim. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur suscipit tristique justo vel dapibus. Sed tincidunt mattis massa a auctor. Donec in tincidunt elit. Curabitur interdum neque at ante fringilla tempus. In hac habitasse platea dictumst. Vivamus luctus ligula ut nisl fermentum egestas. Praesent pulvinar accumsan neque. Sed nec leo sit amet arcu vehicula vehicula non ac diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;
-
-Morbi vel arcu quis neque dictum ullamcorper. Pellentesque condimentum consequat lacinia. Vestibulum eleifend placerat erat, eu hendrerit dui pulvinar eget. Vestibulum accumsan, lectus id vehicula fringilla, erat eros dictum massa, ut tristique libero mi eu ante. In at ante nunc. Praesent sodales ullamcorper porta. Curabitur egestas odio elit. Praesent et libero malesuada, venenatis lorem vitae, eleifend nisl. Donec dapibus euismod turpis, nec porttitor turpis dapibus ac. Fusce iaculis lacus eget dictum aliquam. Sed dictum eu enim ac posuere. Ut accumsan, ipsum et laoreet consectetur, ipsum quam dapibus diam, ac molestie ligula dui id massa. Nulla aliquam mauris congue nibh vestibulum imperdiet.
-
-Aenean ultricies, orci vel consectetur suscipit, ante nisi eleifend est, vitae suscipit risus erat dictum dui. Ut nisi diam, tristique sed nisl nec, aliquam gravida orci. Vestibulum in molestie sem. Ut eu molestie mi. Suspendisse potenti. Nunc sagittis lorem ut est vehicula, vitae imperdiet leo rutrum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec pharetra urna pretium, faucibus orci cursus, sodales purus. Duis iaculis dignissim augue, non aliquet ligula elementum quis. Sed tempus a nisi et faucibus. Donec mattis ligula ac sem aliquam, sed sodales est ullamcorper. Cras vel orci est. Integer eget ultricies nisi.
-
-Quisque rhoncus, nisl vel auctor consectetur, sapien augue iaculis urna, quis suscipit arcu dolor ac est. Fusce sit amet quam lacinia, malesuada lorem vitae, dapibus justo. Mauris eget massa nec dolor volutpat tempor ut eu ante. Donec risus mi, aliquam at euismod vulputate, ullamcorper non leo. Etiam ac elit velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Vivamus sollicitudin enim sed felis pharetra, a convallis mi venenatis. Donec convallis non velit venenatis suscipit. Sed massa risus, dictum quis aliquam ut, placerat quis arcu. Donec at nisi neque. Nullam porta et mi vel ultricies. Donec vel aliquam sem. Mauris varius, ipsum interdum mattis rhoncus, nunc nisl vehicula tortor, quis condimentum nibh nisl vel ante. Sed vel pretium dui. Lorem ipsum dolor sit amet, consectetur adipisX
--- a/tests/data/block-aligned.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse id hendrerit felis. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed dictum dolor nec lobortis sagittis. Aliquam blandit lobortis ullamcorper. Donec malesuada ante ante, vel cursus purus suscipit ut. Sed a eros vitae lorem tristique hendrerit a a odio. Cras interdum mi lectus, sed molestie mauris tempus sit amet. Praesent auctor neque venenatis diam tincidunt sodales. Maecenas tincidunt ligula et ipsum dignissim, et molestie dui hendrerit. Nunc lobortis mauris vel tempor tristique. Sed consectetur bibendum nunc. Pellentesque augue velit, interdum ac posuere eu, eleifend et massa. Vestibulum ac mi eu mi viverra iaculis. Sed consequat leo adipiscing dui varius, sagittis vestibulum massa vehicula.
-
-Praesent imperdiet erat vitae dui placerat, sit amet vulputate tortor viverra. In laoreet elit lorem, at luctus est tempor ac. Nam elementum scelerisque nisl vitae accumsan. Curabitur cursus rutrum est, ut adipiscing ante elementum ut. Ut lectus sem, luctus ut massa non, dignissim vestibulum ipsum. Suspendisse non convallis enim, aliquam adipiscing velit. Donec pharetra non turpis hendrerit porttitor. Aliquam erat volutpat.
-
-Cras eget arcu eu dolor faucibus accumsan non eget orci. Integer quis quam tincidunt, faucibus neque in, imperdiet lacus. Aenean dui turpis, lacinia quis ligula non, semper adipiscing tellus. Curabitur vitae dui quis nisl malesuada commodo. Nunc eleifend metus enim, eu aliquet dui semper sit amet. Sed a odio sapien. Suspendisse vitae ante id sapien semper accumsan. Sed vestibulum erat quis laoreet pellentesque. Nullam placerat ligula eu odio faucibus, eget dictum orci tristique. Quisque sit amet pulvinar velit, lacinia dictum sem.
-
-Phasellus id sagittis lacus. Vivamus facilisis convallis metus, sit amet ultricies purus gravida quis. Vivamus eget egestas arcu, a euismod risus. Fusce metus arcu, molestie ut lacinia at, commodo eu nibh. In leo tortor, feugiat aliquet semper in, malesuada ac nulla. Fusce tempor ultricies blandit. Pellentesque et lorem quam. Suspendisse eros eros, mattis ut porttitor vitae, fermentum eget augue. Aliquam vitae justo sed est dictum lobortis. Cras lacinia commodo ligula sed ornare. Donec ut eros semper, sodales tellus quis, cursus quam. Quisque elementum ullamcorper pellentesque. Integer in rutrum ante, vel condimentum metus. Aliquam erat volutpat.
-
-Quisque convallis tempus eros, sit amet mattis ipsum eleifend in. Mauris volutpat, urna ut commodo tempus, est quam scelerisque erat, eget consequat nulla arcu id eros. Nam elementum nibh quis tincidunt adipiscing. Fusce tristique pretium mollis. Nulla quis ornare felis, vel lacinia diam. In et dui et mauris vestibulum commodo. In hac habitasse platea dictumst. Vestibulum viverra pellentesque tortor, tempor vehicula orci. Praesent rutrum turpis ipsum, ac commodo nisi vehicula ac.
-
-Nulla gravida ultrices mauris. Ut congue purus nec dolor euismod, vitae auctor elit condimentum. Nullam elementum velit lectus, fringilla pharetra sem hendrerit sit amet. Cras quis lobortis nisi, id interdum massa. Proin eget porttitor sapien. Vivamus quam odio, consectetur at rhoncus vel, posuere in nisi. Donec hendrerit urna ac massa tristique, pellentesque consequat orci dapibus. Mauris mi erat, aliquet id adipiscing ultricies, ultrices nec lacus. Proin imperdiet elit magna, ut suscipit metus faucibus eget. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas at leo lorem. Donec mi nibh, blandit ut mi quis, dictum adipiscing ante. Curabitur sodales blandit odio id viverra. Fusce sagittis rutrum nibh. Mauris risus lacus, placerat at dictum in, varius volutpat quam.
-
-Ut accumsan tincidunt dui, non semper lorem sodales nec. Curabitur pellentesque velit nec erat ultrices, in convallis diam suscipit. Curabitur pellentesque convallis mi. Phasellus iaculis orci quis nisi semper auctor. Vestibulum eleifend tortor quis odio feugiat, pulvinar congue turpis blandit. Sed viverra diam risus, vel mattis orci dignissim ut. Cras pellentesque dapibus dolor. Morbi tempor eleifend magna. Ut sodales ut sapien sed elementum. In hac habitasse platea dictumst.
-
-Nullam eget sagittis tellus. Donec a tellus id ligula viverra pellentesque. Pellentesque nulla turpis, pretium vel sem vel, placerat ultricies diam. Donec felis nibh, rhoncus id ornare at, lobortis vel massa. Nullam eu luctus ipsum. Mauris consequat dictum aliquam. Suspendisse ullamcorper aliquam mauris, viverra tincidunt augue suscipit at. Sed luctus scelerisque justo ut semper. Phasellus massa mauris, molestie at nulla eget, dictum ultricies nisi. Aliquam rhoncus nisl sed urna egestas, vel luctus lectus euismod. Mauris sagittis dapibus leo. Donec vitae dignissim risus, ac ultricies augue. Ut vulputate tortor sed dui consectetur placerat. Nullam viverra non diam vel dignissim.
-
-Suspendisse potenti. Integer facilisis neque vitae euismod adipiscing. Aenean dictum leo commodo dui sodales, sit amet volutpat mauris gravida. Proin tempus convallis eros at consectetur. Proin nisl purus, dictum vel euismod ut, imperdiet quis est. Etiam sollicitudin lobortis neque eget pulvinar. Etiam venenatis vel sem ut posuere. Aliquam consectetur rhoncus facilisis. Morbi a viverra orci.
-
-Praesent ut vehicula orci, vel convallis risus. Suspendisse consectetur varius interdum. Interdum et malesuada fames ac ante ipsum primis in faucibus. Proin vel sodales enim. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur suscipit tristique justo vel dapibus. Sed tincidunt mattis massa a auctor. Donec in tincidunt elit. Curabitur interdum neque at ante fringilla tempus. In hac habitasse platea dictumst. Vivamus luctus ligula ut nisl fermentum egestas. Praesent pulvinar accumsan neque. Sed nec leo sit amet arcu vehicula vehicula non ac diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;
-
-Morbi vel arcu quis neque dictum ullamcorper. Pellentesque condimentum consequat lacinia. Vestibulum eleifend placerat erat, eu hendrerit dui pulvinar eget. Vestibulum accumsan, lectus id vehicula fringilla, erat eros dictum massa, ut tristique libero mi eu ante. In at ante nunc. Praesent sodales ullamcorper porta. Curabitur egestas odio elit. Praesent et libero malesuada, venenatis lorem vitae, eleifend nisl. Donec dapibus euismod turpis, nec porttitor turpis dapibus ac. Fusce iaculis lacus eget dictum aliquam. Sed dictum eu enim ac posuere. Ut accumsan, ipsum et laoreet consectetur, ipsum quam dapibus diam, ac molestie ligula dui id massa. Nulla aliquam mauris congue nibh vestibulum imperdiet.
-
-Aenean ultricies, orci vel consectetur suscipit, ante nisi eleifend est, vitae suscipit risus erat dictum dui. Ut nisi diam, tristique sed nisl nec, aliquam gravida orci. Vestibulum in molestie sem. Ut eu molestie mi. Suspendisse potenti. Nunc sagittis lorem ut est vehicula, vitae imperdiet leo rutrum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec pharetra urna pretium, faucibus orci cursus, sodales purus. Duis iaculis dignissim augue, non aliquet ligula elementum quis. Sed tempus a nisi et faucibus. Donec mattis ligula ac sem aliquam, sed sodales est ullamcorper. Cras vel orci est. Integer eget ultricies nisi.
-
-Quisque rhoncus, nisl vel auctor consectetur, sapien augue iaculis urna, quis suscipit arcu dolor ac est. Fusce sit amet quam lacinia, malesuada lorem vitae, dapibus justo. Mauris eget massa nec dolor volutpat tempor ut eu ante. Donec risus mi, aliquam at euismod vulputate, ullamcorper non leo. Etiam ac elit velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Vivamus sollicitudin enim sed felis pharetra, a convallis mi venenatis. Donec convallis non velit venenatis suscipit. Sed massa risus, dictum quis aliquam ut, placerat quis arcu. Donec at nisi neque. Nullam porta et mi vel ultricies. Donec vel aliquam sem. Mauris varius, ipsum interdum mattis rhoncus, nunc nisl vehicula tortor, quis condimentum nibh nisl vel ante. Sed vel pretium dui. Lorem ipsum dolor sit amet, consectetur adipis
--- a/tests/data/certificates/Readme.md
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-# Recreating certificates
-
-Regenerate a new certificate key together with the good (Nextcloud Security) and bad (default Org name) certificates
-
-## Good Certificate
-```
-openssl req \
- -newkey rsa:2048 \
- -nodes \
- -keyout security.nextcloud.com.key \
- -x509 \
- -days 3650 \
- -out goodCertificate.crt
-```
-- Country Name: `DE`
-- State or Province Name:`Berlin`
-- Organization Name:`Nextcloud Security`
-- Common Name: `security.nextcloud.com`
-
-## Bad Certificate
-```
-openssl req \
- -key security.nextcloud.com.key \
- -new \
- -x509 \
- -days 3650 \
- -out badCertificate.crt
-```
-- Country Name: `DE`
-- State or Province Name:`Berlin`
--- a/tests/data/certificates/badCertificate.crt
+++ /dev/null
@@ -1,21 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDYzCCAkugAwIBAgIURRAxXDBFFR1X0WuUOj7oKFDCooMwDQYJKoZIhvcNAQEL
-BQAwQTELMAkGA1UEBhMCREUxDzANBgNVBAgMBkJlcmxpbjEhMB8GA1UECgwYSW50
-ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMB4XDTI1MDgyNTEwMDAxNVoXDTM1MDgyMzEw
-MDAxNVowQTELMAkGA1UEBhMCREUxDzANBgNVBAgMBkJlcmxpbjEhMB8GA1UECgwY
-SW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
-MIIBCgKCAQEAvPH9evINYYNhBg5YaZh3OUVl4SUCUbAc+xmZmiuAxoXN10avmfa/
-rpoeHua2fbPa48BOIyDPxXoofEwfqB+Q8P4CsSjEaRegsj3Djdkoyz+edPb9vTWE
-pkSCfj/K3zS2kkY2nPw6qVRVen3siP97kNv3NviU5wbvv5RW5Qjz8W6UMuX2Th4j
-7mQ5b1YdLHMFd2H6Vs3vAfiX8hY1DUc6U4Uf4XBO372k8sUyQtsSGlHxBKwPN7Gh
-KZJqkTUCXWC4JzOkShXTe179SlxFLQtOqU2azrJqlb6/0cxGtsZLcv/CI+x+Fpca
-hAQ4Qzii7iRRBlk8aDfAtXJafqhKJo1N/wIDAQABo1MwUTAdBgNVHQ4EFgQU8LRp
-H0E1ASvIgy5FCsVdX2nyYSkwHwYDVR0jBBgwFoAU8LRpH0E1ASvIgy5FCsVdX2ny
-YSkwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAZuFOU5L22cPI
-E1bMaIc6UjQn5l2QB9qhTVJuGmdo8ARLNVv3FyEHDaSCNhV1V0MEmnOwwKGKXggo
-Uxe8Ki29fOt3ftkWG2F22Fh3PsU4xIAqwQv5s+urR/9E6ylfIbqPdj6XiY/D9jID
-wnPDsNG+7ar90YFxsxisemQIVAOLzWGS2a3fglRz0hJfaoAxJdXbz///MflWzdUL
-7t9ngBsbf9M/dicltLrCcYYG+BMC+fAoZO3aRSfS/+hxBjc3hNYQ5zLzWqzLzF0y
-N+rxo/2G0fvqipowkMTgYcCIBA9DZY1YPCwgTfHGyrH3XI7yImdoHYs5Ld39Sq0J
-+fXdrZSPlQ==
------END CERTIFICATE-----
--- a/tests/data/certificates/expiredCertificate.crt
+++ /dev/null
@@ -1,13 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICATCCAWoCCQCjCIB6tCZ2sDANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB
-VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
-cyBQdHkgTHRkMB4XDTE0MDgyNzA5MTI0M1oXDTE0MDgyODA5MTI0M1owRTELMAkG
-A1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0
-IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvrMe
-x5D45HVMV2U4kqTU0mzHAihHT6r+OtO6g7S9yIlJZGGVcEet6An78Ow7aYM141eI
-Jfbvqql7OIblHXSw7mvkw4bOQ1ee5lmJYOYCgaMNJ6mBLJfpK9xwidb0ZvhWOA8P
-DLIiBKA3T5ChXCzilD5GF2+H/BXBE9lL9tuDjM0CAwEAATANBgkqhkiG9w0BAQUF
-AAOBgQBuNClmOj3wudlX86nygcZgQT2+ZS8f1iJgM9lbrrkenT6tgcT1/YjcrN9C
-BZR29Wz7htflpqverLUGZXh72K+gYercyR16Zu7zjt/NWuZldZmzJ3bUGq2HSoCX
-2sDykAEuaDxUlzdJrztlOH4vPlRaGbxUogpC2hB1BQfxA90CIA==
------END CERTIFICATE-----
\ No newline at end of file
--- a/tests/data/certificates/goodCertificate.crt
+++ /dev/null
@@ -1,22 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDuzCCAqOgAwIBAgIUGyAVECfPaXxvfEOrxo58ANU0MYEwDQYJKoZIhvcNAQEL
-BQAwbTELMAkGA1UEBhMCREUxDzANBgNVBAgMBkJlcmxpbjEPMA0GA1UEBwwGQmVy
-bGluMRswGQYDVQQKDBJOZXh0Y2xvdWQgU2VjdXJpdHkxHzAdBgNVBAMMFnNlY3Vy
-aXR5Lm5leHRjbG91ZC5jb20wHhcNMjUwODI1MDk1MzE0WhcNMzUwODIzMDk1MzE0
-WjBtMQswCQYDVQQGEwJERTEPMA0GA1UECAwGQmVybGluMQ8wDQYDVQQHDAZCZXJs
-aW4xGzAZBgNVBAoMEk5leHRjbG91ZCBTZWN1cml0eTEfMB0GA1UEAwwWc2VjdXJp
-dHkubmV4dGNsb3VkLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
-ALzx/XryDWGDYQYOWGmYdzlFZeElAlGwHPsZmZorgMaFzddGr5n2v66aHh7mtn2z
-2uPATiMgz8V6KHxMH6gfkPD+ArEoxGkXoLI9w43ZKMs/nnT2/b01hKZEgn4/yt80
-tpJGNpz8OqlUVXp97Ij/e5Db9zb4lOcG77+UVuUI8/FulDLl9k4eI+5kOW9WHSxz
-BXdh+lbN7wH4l/IWNQ1HOlOFH+FwTt+9pPLFMkLbEhpR8QSsDzexoSmSapE1Al1g
-uCczpEoV03te/UpcRS0LTqlNms6yapW+v9HMRrbGS3L/wiPsfhaXGoQEOEM4ou4k
-UQZZPGg3wLVyWn6oSiaNTf8CAwEAAaNTMFEwHQYDVR0OBBYEFPC0aR9BNQEryIMu
-RQrFXV9p8mEpMB8GA1UdIwQYMBaAFPC0aR9BNQEryIMuRQrFXV9p8mEpMA8GA1Ud
-EwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAKuoLKvlt7W63t9xDIYbYI3e
-3BeSt769lA2aBaIktSg7hVIFvX929sMMROD0sNSx81IMl7Cxm9u6C3BuKzUZDOwv
-hy7gj+CaA5e5moUmpfrFtTR4aAoacZpjDXd3H/OqMELFkx5QHhw5z2nnubrNaera
-pMQj/GOe/7+d1vjma+kNcKT6JBd5BPQqfk9+AVv5TombaLf3xc7dFR6bUQZKkZ2L
-9zCZamUai/qf66KQzdELLpUsX1O2ByCl95wVL5c0CcDObxLuVDn8AzsAnsa5EhM8
-68s38Xb/gUYDIny2F43+rEk8qQjd5OITFfd2K6ur5EcvIQrFHE7IUbKlIu9RqPs=
------END CERTIFICATE-----
--- a/tests/data/certificates/openSslTrustedCertificate.crt
+++ /dev/null
@@ -1,25 +0,0 @@
------BEGIN TRUSTED CERTIFICATE-----
-MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB
-qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
-Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
-MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
-BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw
-NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j
-LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG
-A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
-IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG
-SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs
-W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta
-3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk
-6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6
-Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J
-NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA
-MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP
-r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU
-DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz
-YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX
-xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2
-/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/
-LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7
-jVaMaDAMMAoGCCsGAQUFBwMB
------END TRUSTED CERTIFICATE-----
--- a/tests/data/desktopapp.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
--- a/tests/data/emails/new-account-email-custom-text-alternative.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-Welcome aboard - text
-
-Welcome to your Nextcloud account, you can add, protect, and share your data. - text
-
-Your username is: abc
-
-
-Set your password - text: https://example.org/resetPassword/123
-Install Client - text: https://nextcloud.com/install/#install-clients
-
-
---
-TestCloud - A safe home for your data
--- a/tests/data/emails/new-account-email-custom.html
+++ /dev/null
@@ -1,194 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Welcome aboard
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Welcome to your Nextcloud account, you can add, protect, and share your data.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Your username is: abc
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Set your password
-
-
-
-
-
-
-
-
-
-
-
-
-
- Install Client
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- TestCloud - A safe home for your data
-
-
-
-
-
-
-
-
-
-
-
--- a/tests/data/emails/new-account-email-custom.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-Welcome aboard
-
-Welcome to your Nextcloud account, you can add, protect, and share your data.
-
-Your username is: abc
-
-
-Set your password: https://example.org/resetPassword/123
-Install Client: https://nextcloud.com/install/#install-clients
-
-
---
-TestCloud - A safe home for your data
--- a/tests/data/emails/new-account-email-single-button.html
+++ /dev/null
@@ -1,167 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Welcome aboard
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Welcome to your Nextcloud account, you can add, protect, and share your data.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Your username is: abc
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Set your password
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- TestCloud - A safe home for your data
-
-
-
-
-
-
-
-
-
-
-
--- a/tests/data/emails/new-account-email-single-button.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-Welcome aboard
-
-Welcome to your Nextcloud account, you can add, protect, and share your data.
-
-Your username is: abc
-
-https://example.org/resetPassword/123
-
---
-TestCloud - A safe home for your data
--- a/tests/data/emails/new-account-email.html
+++ /dev/null
@@ -1,194 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Welcome aboard
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Welcome to your Nextcloud account, you can add, protect, and share your data.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Your username is: abc
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Set your password
-
-
-
-
-
-
-
-
-
-
-
-
-
- Install Client
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- TestCloud - A safe home for your data
This is an automatically sent email, please do not reply.
-
-
-
-
-
-
-
-
-
-
-
--- a/tests/data/emails/new-account-email.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-Welcome aboard
-
-Welcome to your Nextcloud account, you can add, protect, and share your data.
-
-Your username is: abc
-
-
-Set your password: https://example.org/resetPassword/123
-Install Client: https://nextcloud.com/install/#install-clients
-
-
---
-TestCloud - A safe home for your data
-This is an automatically sent email, please do not reply.
\ No newline at end of file
--- a/tests/data/guest_avatar_einstein_32.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
--- a/tests/data/ics/event-builder-complete.ics
+++ /dev/null
@@ -1,17 +0,0 @@
-BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Sabre//Sabre VObject 4.5.6//EN
-CALSCALE:GREGORIAN
-BEGIN:VEVENT
-UID:event-uid-123
-DTSTAMP:20250105T000000Z
-SUMMARY:My event
-DTSTART:20250105T170958Z
-DTEND:20250105T171958Z
-STATUS:CONFIRMED
-DESCRIPTION:Foo bar baz
-ORGANIZER:mailto:organizer@domain.tld
-ATTENDEE:mailto:attendee1@domain.tld
-ATTENDEE:mailto:attendee2@domain.tld
-END:VEVENT
-END:VCALENDAR
--- a/tests/data/ics/event-builder-complete.ics.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
-SPDX-License-Identifier: AGPL-3.0-or-later
--- a/tests/data/ics/event-builder-without-attendees.ics
+++ /dev/null
@@ -1,14 +0,0 @@
-BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Sabre//Sabre VObject 4.5.6//EN
-CALSCALE:GREGORIAN
-BEGIN:VEVENT
-UID:event-uid-123
-DTSTAMP:20250105T000000Z
-SUMMARY:My event
-DTSTART:20250105T170958Z
-DTEND:20250105T171958Z
-STATUS:CONFIRMED
-DESCRIPTION:Foo bar baz
-END:VEVENT
-END:VCALENDAR
--- a/tests/data/ics/event-builder-without-attendees.ics.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
-SPDX-License-Identifier: AGPL-3.0-or-later
--- a/tests/data/ics/free-busy-request.ics
+++ /dev/null
@@ -1,14 +0,0 @@
-BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Sabre//Sabre VObject 4.5.6//EN
-CALSCALE:GREGORIAN
-METHOD:REQUEST
-BEGIN:VFREEBUSY
-DTSTART:20250116T060000Z
-DTEND:20250117T060000Z
-ORGANIZER:mailto:admin@imap.localhost
-ATTENDEE:mailto:admin@imap.localhost
-ATTENDEE:mailto:user@imap.localhost
-ATTENDEE:mailto:empty@imap.localhost
-END:VFREEBUSY
-END:VCALENDAR
--- a/tests/data/ics/free-busy-request.ics.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
-SPDX-License-Identifier: AGPL-3.0-or-later
--- a/tests/data/integritycheck/SomeApp.crt
+++ /dev/null
@@ -1,28 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIEwTCCAqmgAwIBAgIUWv0iujufs5lUr0svCf/qTQvoyKAwDQYJKoZIhvcNAQEF
-BQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw
-MzIyNDk1M1oXDTE2MTEwMzIyNDk1M1owEjEQMA4GA1UEAwwHU29tZUFwcDCCAiIw
-DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK8q0x62agGSRBqeWsaeEwFfepMk
-F8cAobMMi50qHCv9IrOn/ZH9l52xBrbIkErVmRjmly0d4JhD8Ymhidsh9ONKYl/j
-+ishsZDM8eNNdp3Ew+fEYVvY1W7mR1qU24NWj0bzVsClI7hvPVIuw7AjfBDq1C5+
-A+ZSLSXYvOK2cEWjdxQfuNZwEZSjmA63DUllBIrm35IaTvfuyhU6BW9yHZxmb8+M
-w0xDv30D5UkE/2N7Pa/HQJLxCR+3zKibRK3nUyRDLSXxMkU9PnFNaPNX59VPgyj4
-GB1CFSToldJVPF4pzh7p36uGXZVxs8m3LFD4Ol8mhi7jkxDZjqFN46gzR0r23Py6
-dol9vfawGIoUwp9LvL0S7MvdRY0oazLXwClLP4OQ17zpSMAiCj7fgNT661JamPGj
-t5O7Zn2wA7I4ddDS/HDTWCu98Zwc9fHIpsJPgCZ9awoqxi4Mnf7Pk9g5nnXhszGC
-cxxIASQKM+GhdzoRxKknax2RzUCwCzcPRtCj8AQT/x/mqN3PfRmlnFBNACUw9bpZ
-SOoNq2pCF9igftDWpSIXQ38pVpKLWowjjg3DVRmVKBgivHnUnVLyzYBahHPj0vaz
-tFtUFRaqXDnt+4qyUGyrT5h5pjZaTcHIcSB4PiarYwdVvgslgwnQzOUcGAzRWBD4
-6jV2brP5vFY3g6iPAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBACTY3CCHC+Z28gCf
-FWGKQ3wAKs+k4+0yoti0qm2EKX7rSGQ0PHSas6uW79WstC4Rj+DYkDtIhGMSg8FS
-HVGZHGBCc0HwdX+BOAt3zi4p7Sf3oQef70/4imPoKxbAVCpd/cveVcFyDC19j1yB
-Bapwu87oh+muoeaZxOlqQI4UxjBlR/uRSMhOn2UGauIr3dWJgAF4pGt7TtIzt+1v
-0uA6FtN1Y4R5O8AaJPh1bIG0CVvFBE58esGzjEYLhOydgKFnEP94kVPgJD5ds9C3
-pPhEpo1dRpiXaF7WGIV1X6DI/ipWvfrF7CEy6I/kP1InY/vMDjQjeDnJ/VrXIWXO
-yZvHXVaN/m+1RlETsH7YO/QmxRue9ZHN3gvvWtmpCeA95sfpepOk7UcHxHZYyQbF
-49/au8j+5tsr4A83xzsT1JbcKRxkAaQ7WDJpOnE5O1+H0fB+BaLakTg6XX9d4Fo7
-7Gin7hVWX7pL+JIyxMzME3LhfI61+CRcqZQIrpyaafUziPQbWIPfEs7h8tCOWyvW
-UO8ZLervYCB3j44ivkrxPlcBklDCqqKKBzDP9dYOtS/P4RB1NkHA9+NTvmBpTonS
-SFXdg9fFMD7VfjDE3Vnk+8DWkVH5wBYowTAD7w9Wuzr7DumiAULexnP/Y7xwxLv7
-4B+pXTAcRK0zECDEaX3npS8xWzrB
------END CERTIFICATE-----
\ No newline at end of file
--- a/tests/data/integritycheck/SomeApp.crt.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-FileCopyrightText: 2015 ownCloud, Inc.
-SPDX-License-Identifier: AGPL-3.0-only
--- a/tests/data/integritycheck/SomeApp.key
+++ /dev/null
@@ -1,51 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIJKAIBAAKCAgEAryrTHrZqAZJEGp5axp4TAV96kyQXxwChswyLnSocK/0is6f9
-kf2XnbEGtsiQStWZGOaXLR3gmEPxiaGJ2yH040piX+P6KyGxkMzx4012ncTD58Rh
-W9jVbuZHWpTbg1aPRvNWwKUjuG89Ui7DsCN8EOrULn4D5lItJdi84rZwRaN3FB+4
-1nARlKOYDrcNSWUEiubfkhpO9+7KFToFb3IdnGZvz4zDTEO/fQPlSQT/Y3s9r8dA
-kvEJH7fMqJtEredTJEMtJfEyRT0+cU1o81fn1U+DKPgYHUIVJOiV0lU8XinOHunf
-q4ZdlXGzybcsUPg6XyaGLuOTENmOoU3jqDNHSvbc/Lp2iX299rAYihTCn0u8vRLs
-y91FjShrMtfAKUs/g5DXvOlIwCIKPt+A1PrrUlqY8aO3k7tmfbADsjh10NL8cNNY
-K73xnBz18cimwk+AJn1rCirGLgyd/s+T2DmedeGzMYJzHEgBJAoz4aF3OhHEqSdr
-HZHNQLALNw9G0KPwBBP/H+ao3c99GaWcUE0AJTD1ullI6g2rakIX2KB+0NalIhdD
-fylWkotajCOODcNVGZUoGCK8edSdUvLNgFqEc+PS9rO0W1QVFqpcOe37irJQbKtP
-mHmmNlpNwchxIHg+JqtjB1W+CyWDCdDM5RwYDNFYEPjqNXZus/m8VjeDqI8CAwEA
-AQKCAgAGEpX/GpPSOh/iTFsZR6GhCo5VS4sHex4f9u9gI3WWkNADKm+//+qhrOFu
-tMVL0tvb4SKcjcyber+E5fTBhAvZVVrTuDOUCzb8rh40oxrZnVitUEGPzZSYo6MV
-oNN7WiTdcNIxG4iBfFnD35spIBHNBFcWxYedFHw8M6dYtLpvr5sRN4hQ5tG1NXaw
-C+iKAtaFejuF9SOHtN+MnNZTZsFgCq0VpOugWTjqPJhWT7YK3NrmnSG/9ls6nkSa
-E8ftv3dCapHGHvZ/MABaLTTWOtXurzL82Jz9Zq0U+ns3L31IRmq+55y5dY8I/0gc
-Vh1TMUfUxKEiPwF6NBCdxvV9f0mZYQPEeHATnkew/SW35EDPgPSS/H3jJLORhAUJ
-7ZzDkvwdfWjnHxyzJtbHHZ8r3gqwq0rqfyd4Rz04TusUukfikdy5ooL1dMLiy3od
-V40zQxVpI44zrrLBjaJ+OItdD5NFRu82K0OF653a/1fP7x4fw3j/ATVQhW8g0EE3
-oEEdGaciqXDWNxlSwiZUmteq3chN+JRXrBWlOodV5LyJwiy5/5NMV91a5AP0wOsT
-BXzhkjnLsGJMSQuzDs1CFUVjqs1PudhyW+pgoy8HaTSfamKiAHbJlBSuSX0acR7S
-iwiGhzkChWtUv1vMVM56z19g/umwmZujwhr2jkN1Y0zZH//xAQKCAQEA3GV74emO
-IMjsb8VbJyCHycYEfcu7fZ90mLrK7yyjNxobROniqjWYT92fjse1qrTv54luxFN7
-EUF5ec9QRYo1mnyltjqTCx3jfqMlCr94LaIEdMQJ/f3vW6pEj7xY4RSY9hZX8v1Z
-mrhKcFkqGSFdWLliVD471Mxi2/7T8mKjmVhKy3P+Cm1aHardGqvT/mUTOB0sO0OT
-CJSRUWh2Tx2d3B1M6nDXZL8TTVdndEKUycj204Pf6fm6bMJcVuro/kPcwByhG+Ar
-dAKN4HdEa+XmrfqDKzl9yhLUpwXa7rI8g5if/oVj2kF/hny4mboL4U4rC+YWQZ1f
-TBklcYPCx2NgqwKCAQEAy3bioCaqa+iAsSWLjRp1Dh+93vI7WXvuPdLkAQ/Iz6yQ
-sCqjQj/4jIxm7qmhbh8X3B8VkaX2jnuaR9nUuZyjIvHg1WMCMeMYWiok5GCppkyH
-yLoEIT1mTyTe3Wjk37Zi+1UQRHPS/8noVtVj0ng95SChnmguDF3otvl4lLH+wgCz
-z1xMO9VyLTEGU3jbPgXqNeGKdqk/kEsU4C+NuwPiRMAuDuKR+s5ldB293GBPnFKu
-OqgzVoJJChLAPUvzSE+hJmWin2hoCZMktLUdSCng9rm9AhXWYpLwUrANZuvI8RfO
-9wX1NR17U+QbX2PvDlEl6B9Yt3G4R9qNxUPLrjP/rQKCAQEAyDO3kMOjw8xAWleg
-Ma6vKm6h7dN/gOGz/HjRlumpaYhhdPwwVgVRUlszcXOgZmzt8Bk7cUOT61zah/f2
-JvUhNDA+J4aVw+dmm8Z/A4BiHrGp8peRrBNbtpy4owiog+099Wzef2/8UTtPAzc7
-spBIRyw/Ud8mYms28jhNN0S677TwXFgFUFt9HK31IyEq9U/DYZm+cCc2DPlH9/c4
-YS26FBTZpazTPEUFt5/J7iX9Gj9fV0vXvqaG3fy//IRvGWlzwV9ASh4b2snnLxuo
-H4s7PJbvR/h1d3YbjY0YDvQBXFjsHTv2NHCC8xugZKRH3mYvXCOp2/ikdG/zP2Y9
-LPns+QKCAQB1M2Mx63PpusE+yajMO/xHiYM+xHvpfNjsZemOrv/2mKmzwKvQQrcy
-hsHYIoBpxaFh28n53wbaZlqlntXJoW/bdkcTw/eEsxLZBUPBBelTcOwadQRh/VNM
-ralvErgcIZx8uDApripRy4V5V2wr1bWZoaVXcR1tZD7j/2o1BR8Bs5PgE4OaR8aA
-P6gsNwbbgF68cNHorm9997HrvZi/rGoPPkCJtHtwZKnOLD+sjRHuszXHdhI0d9II
-6mowJOrbsXrbelolxud+9HKFYXqfkfgTR0SXyep3V7r1dpIRwio6roM6igUIdpYO
-6evWk+MldRsHzd61tNz5DuzxP685Bpz1AoIBAGnP0wLx3B9ri2Q5g3ENPdZhAop+
-/CMZUWR297yc0gSIoCnTBtNG3y735P887zOQlIZaFnnMzPP85mPMMMUT5Os5kFrE
-MzHaNkgpbxD2xcAB3sA1Hk/yn/c/nuTTZpTTYGv0OVE+/rQ4l2al1ngWTTz8/Bf1
-m3IDKluBTNkoD8DnS9+9CT3YWpnEr3sw8Vqzuat5hSgneJP1dtv2dCDXQ4MOUZbL
-riXuafvnlbC6tfmOGdeZ90rdTQWRIckP8zDZI0kZ1XADEwD1iTnWgfI1hoKI4MN8
-3h4JbmLC0nvaRDivwOuy2BDhpYW6wRtphSYvfshlmAptt306Na35FUvLL80=
------END RSA PRIVATE KEY-----
\ No newline at end of file
--- a/tests/data/integritycheck/SomeApp.key.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-FileCopyrightText: 2015 ownCloud, Inc.
-SPDX-License-Identifier: AGPL-3.0-only
--- a/tests/data/integritycheck/app/AnotherFile.txt
+++ /dev/null
@@ -1 +0,0 @@
-Another file with some Content.
--- a/tests/data/integritycheck/app/subfolder/file.txt
+++ /dev/null
@@ -1 +0,0 @@
-A file with some content.
--- a/tests/data/integritycheck/appWithInvalidData/AnotherFile.txt
+++ /dev/null
@@ -1 +0,0 @@
-TAMPERED FILE.
\ No newline at end of file
--- a/tests/data/integritycheck/core.crt
+++ /dev/null
@@ -1,28 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIEvjCCAqagAwIBAgIUc/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF
-BQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw
-MzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ
-KoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s
-iOf4RwPXR6SE9bWZEm/b72SfWk//J6AbrD8WiOzBuT/ODy6k5T1arEdHO+Pux0W1
-MxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr/xolP3oD+eLbShPcblhdS
-VtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0
-klnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5/2riAzIssMFSCarWCx0AKYb54+d
-xLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77
-H87KFhYW8tKFFvF1V3AHl/sFQ9tDHaxM9Y0pZ2jPp/ccdiqnmdkBxBDqsiRvHvVB
-Cn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO/wAtd2vUW8UFiq
-s2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ/zrM0
-i8nfCFwTxWRxp3H9KoECzO/zS5R5KIS7s3/wq/w9T2Ie4rcecgXwDizwnn0C/aKc
-bDIjujpL1s9HO05pcD/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ
-Q238lC+A/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2
-AvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji
-oNCXUbExC/0iCPUqdHZIVb+Lc/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd
-9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb
-H+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th/55
-cf3Fovj6JJgbb9XFxrdnsOsDOu/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX
-uVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO//+TJtXRbyNgsf
-oMRZGi8DLGU2SGEAHcRH/QZHq/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1
-0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI/XUxEWOa2F
-K2EqhErgMK/N07U1JJJay5tYZRtvkGq46oP/5kQG8hYST0MDK6VihJoPpvCmAm4E
-pEYKQ96x6A4EH9Y9mZlYozH/eqmxPbTK8n89/p7Ydun4rI+B2iiLnY8REWWy6+UQ
-V204fGUkJqW5CrKy3P3XvY9X
------END CERTIFICATE-----
\ No newline at end of file
--- a/tests/data/integritycheck/core.crt.license
+++ /dev/null
@@ -1,3 +0,0 @@
-SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
-SPDX-FileCopyrightText: 2016 ownCloud, Inc.
-SPDX-License-Identifier: AGPL-3.0-only
--- a/tests/data/integritycheck/core.key
+++ /dev/null
@@ -1,51 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIJKQIBAAKCAgEAtvoSAemQCpls7m9E7xdKHsbtcZYfDmyI5/hHA9dHpIT1tZkS
-b9vvZJ9aT/8noBusPxaI7MG5P84PLqTlPVqsR0c74+7HRbUzFgkkjiQfvgoqAykL
-RIHRG37xasypXWveGEnjp1/pev/GiU/egP54ttKE9xuWF1JW2RkSSh6/xKHovbAM
-J4cPI/HO+PXDZ1N0ZU70rO2fTEiXJ8QFqSq8Fa1zHKLcufSSWfGUzHe08NQTiSq+
-mJr2o2IHUA3Y9X3d42vn/auIDMiywwVIJqtYLHQAphvnj53EulxgXKo8nTJ0EKQX
-vwMPjlEJl63o81iR3OBuqVRYQYaiaS4OglsGDjDODM7QPvsfzsoWFhby0oUW8XVX
-cAeX+wVD20MdrEz1jSlnaM+n9xx2KqeZ2QHEEOqyJG8e9UEKfqqlvi9YYULu8c4F
-9iymYQvXMuUpdm/d7MxkRnDs71m9Q/dU7/AC13a9RbxQWKqza/VCc0s3qM2HnWht
-zCasG9aEnL29Ah20iNBsPGpZMeHXNRKvD3CuWSUIFn/OszSLyd8IXBPFZHGncf0q
-gQLM7/NLlHkohLuzf/Cr/D1PYh7itx5yBfAOLPCefQL9opxsMiO6OkvWz0c7Tmlw
-P9XfApw9nWLPKYGSYzIhsvnaJFU3kVNUd5l1c+kW6r4JNAlDbfyUL4D8o5UCAwEA
-AQKCAgBOkCKpNYqGMogF/DqB2eMWQd1zdryQ6eMCjqSXLpjxN7F0LmwvISSxdIZH
-cMunwBn94IQb+7W5gpUcNurCpCryU9CQNlbTRFDR9kz+xt3mL+EICFhxKrgI8UFg
-1M0ncogir58Sn2jVSfsJvARSKHDWNp+mpe6UxuLJRi2HK5q1J7uRroQZeLD0gv+V
-/5fNxpRkZzlBAqnyC/zyswSnNNUbDaUuN3NEWJF6EvMLs546BST6MSMyzN53GkD/
-i2KLTWa3Hf62+S5qJsYyXBM1nz41n/0jVTngfSIZzk4Fm4Z5DE+vUXVsqzjDp2HS
-AXbS/UVrq+V3yOI4CEG1nXPXXpPDS/werQcSvANGHd/LLiQ3qfcs1S/SBihDjSFQ
-CBgH3y06qDdnKxdPjpRYZpOBnkdQLHF4OwlhPXBd083Ep7jiF1WIgzwBP9o9wEWi
-dVT0Vr5vsB61MQ+4p26Us1yWm51g6AxpTu5y2RPmbuDh1IvNbheeITQMSmbtGf1R
-JZ4yqrnYpd3akja9hwko1xoWuHT15rr2pTs8g/PtHH7sNkZdThMtJscEt4YIIxoN
-CQ+VM4lGYogtySbYEiUkRNF3t06AsPhBehcH7oldUqb6UKKoi6NCZOiC5RsdpOY3
-JJX2nkCMk52264sI+kWl6kEVBpMzeLW+BM0Xi1AQRyHPIY+VZQKCAQEA4sjNh35x
-ezjiOWsq84UOUHdvei9HAm+MQMM2pEgdHWjjawhoH122gi2G/tpgNGONl/XNmZkk
-ni00jFtNRA9xDF6mv1CynxiWhKGLdEH3MELQqGyeNOE9GBQVMo2W2J2mvOj+GzC+
-cRrEBjR1MDGx+XLO8FbYyKiwVg8/OIT9hIYSlBIsu0bPwYb6X3KlfZfmdh/MZvCs
-HDthzRYnJlkVerB/2ZnfTVYflQh7XoKFipVXFMs+oG0mKCUT4HpqXWTek1Jqt8bQ
-og9235C0jEcFWjSHtp2Jhena8yMD4YKQGI7tFVFm9UkHkKPcdfIW+hoVC4vLI0fs
-LMwhzOvFof4pawKCAQEAzoyGHKUA2KG8JVV49C5LKLmJv0nBj7aT5EXcg1J9OZn7
-zP/o/BHJQpeLNI2UD5c0Ron33iRLVqNvU4sTdo597Qoc2jWsZWRmdTz2Q4VvnxHu
-VBvao0vUG4xqIbMtv4VRkuNg9EmlF4luT5+x30E6DWDMK0RhSmM9yWG7My6JG2IN
-LZ457tWvk7F1HTKNt3uFJTAOx0VqjJsbaw7Gsq7hTmObTUa+q8Ss0oK+iRkP5Obv
-9F9zUWv2UjKs/G4JYADfFhS1Ovha8pu5p+NszlGBGvG99EErRpiUPcxCTjSiUvDl
-ALn3YTDc9oSC+6b/sI7/4uQVSri5ybXLGzbtMWKm/wKCAQEAvO12M6uF1Ja1+Ams
-lYTCQQzO5OZf7MqK+CTo74FYJ/kKhE9TltXWRqqw7L12Kg7Jlc/jgVNQaynTvh4N
-x2Zp0llD5tvOgrXUJxgBek++Iwl2lOkv/3OpFtccNao5AaqMjpI3puU7sjQPG/A1
-tHmh/+LCPPzMypWlmXxIOcio/u9GqO5fL4E1cM8G4985uOCD0OJ6wUM8zqQ1vMn4
-wXyzZSuGxvvmSKI320teo4Ruxd3V1u/e830arZT98yNoWve+aNLfLszFYE0rxeHi
-V36PGe/rI5ooSFRi3+zKveKsMplXL0xKTouRbtDjx6pvs9losN6701+GhGdmvTWp
-xmNbkwKCAQEAmY94FcvG+UglbUxChKf2UOzAMGtRcNs40Lnv2+J0H2MQBbUtLlq6
-2rt4TzYDIiQ0RU1F7u3k5SDVH7OCYN5HWPfvw3usFCW01uzf2gtWlVjra7TZtBYo
-N+MI9M0V8hHYN/C8oGIwT3Npg+EiiO0hj9ircm+ANaHayeHTH5Y1cRpQ2d2NDLfp
-tVB11aNEIWm/74nvMs+1C5w1oj52E1pZP8JmL+ms0F+EbW2u4pazbmcTdweP4LT3
-iN0MJxBX//wl33C93H3QgBauzNcUib+m0LVxmCrrVa0SaW92zFXtaOSYHRYliSie
-3thd2WKrLkTikXkpK0hzODfkLPOFHPZPWQKCAQAG0Yz7eQblxIHII60ReeYIovum
-Gmn+ot0jeuPg5gYpopQygL87ygc00ER+SHgZLBjIx3uCYDbC6Q4SzEPLa/aUS3/e
-94vYBVoWYvTYUazuwgJBA1Xm7BnlqG7cQziJOQxBIJBXaX96xUptcmlKrIIYD9Jz
-qeUbbbqN4bBYjXJdNdMqU1f6t2IK7hcjFXJMpS2wJdv1AlYCUWDquQ0BUePCJAPf
-N0rKm12ffhi564NqN/6PtT7iEkSPKT2CEyqrvXwx0Lajz0ZokFzRm2iYUTxik2fI
-Lcq5zXyM4gs1hDnrasn1g0JyfeUgnPNNuWeFG0cMb8o7FeYQImhqheIgMJLP
------END RSA PRIVATE KEY-----
\ No newline at end of file
--- a/tests/data/integritycheck/core.key.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-FileCopyrightText: 2015 ownCloud, Inc.
-SPDX-License-Identifier: AGPL-3.0-only
--- a/tests/data/integritycheck/htaccessUnmodified/.htaccess
+++ /dev/null
@@ -1 +0,0 @@
-# Start of valid file
\ No newline at end of file
--- a/tests/data/integritycheck/htaccessUnmodified/subfolder/.htaccess
+++ /dev/null
@@ -1,4 +0,0 @@
-# Start of valid file
-#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####
-
-# Content that should change the hash in the root folder
\ No newline at end of file
--- a/tests/data/integritycheck/htaccessWithInvalidModifiedContent/.htaccess
+++ /dev/null
@@ -1,5 +0,0 @@
-# Start of valid file
-#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####
-# Content that should not modify the hash
-#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####
-# but it does because it is twice
\ No newline at end of file
--- a/tests/data/integritycheck/htaccessWithValidModifiedContent/.htaccess
+++ /dev/null
@@ -1,11 +0,0 @@
-# Start of valid file
-
- php_value default_charset 'UTF-8'
- php_value output_buffering 0
-
- SetEnv htaccessWorking true
-
-
-#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####
-
-# Content that should change the hash in the root folder
--- a/tests/data/integritycheck/htaccessWithValidModifiedContent/subfolder/.htaccess
+++ /dev/null
@@ -1,4 +0,0 @@
-# Start of valid file
-#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####
-
-# Content that should change the hash in the root folder
\ No newline at end of file
--- a/tests/data/integritycheck/mimetypeListModified/core/js/mimetypelist.js
+++ /dev/null
@@ -1,300 +0,0 @@
-/**
-* This file is automatically generated
-* DO NOT EDIT MANUALLY!
-*
-* You can update the list of MimeType Aliases in config/mimetypealiases.json
-* The list of files is fetched from core/img/filetypes
-* To regenerate this file run ./occ maintenance:mimetype:update-js
-*/
-OC.MimeTypeList={
- aliases: {
- "application/coreldraw": "image",
- "application/epub+zip": "text",
- "application/font-sfnt": "font",
- "application/font-woff": "font",
- "application/gpx+xml": "location",
- "application/gzip": "package/x-generic",
- "application/illustrator": "image",
- "application/internet-shortcut": "link",
- "application/javascript": "text/code",
- "application/json": "text/code",
- "application/km": "mindmap",
- "application/msaccess": "file",
- "application/msexcel": "x-office/spreadsheet",
- "application/msonenote": "x-office/document",
- "application/mspowerpoint": "x-office/presentation",
- "application/msword": "x-office/document",
- "application/octet-stream": "file",
- "application/postscript": "image",
- "application/rss+xml": "application/xml",
- "application/toml": "text/code",
- "application/vnd.android.package-archive": "package/x-generic",
- "application/vnd.excalidraw+json": "whiteboard",
- "application/vnd.garmin.tcx+xml": "location",
- "application/vnd.google-earth.kml+xml": "location",
- "application/vnd.google-earth.kmz": "location",
- "application/vnd.lotus-wordpro": "x-office/document",
- "application/vnd.ms-excel.addin.macroEnabled.12": "x-office/spreadsheet",
- "application/vnd.ms-excel.sheet.binary.macroEnabled.12": "x-office/spreadsheet",
- "application/vnd.ms-excel.sheet.macroEnabled.12": "x-office/spreadsheet",
- "application/vnd.ms-excel.template.macroEnabled.12": "x-office/spreadsheet",
- "application/vnd.ms-excel": "x-office/spreadsheet",
- "application/vnd.ms-fontobject": "font",
- "application/vnd.ms-powerpoint.addin.macroEnabled.12": "x-office/presentation",
- "application/vnd.ms-powerpoint.presentation.macroEnabled.12": "x-office/presentation",
- "application/vnd.ms-powerpoint.slideshow.macroEnabled.12": "x-office/presentation",
- "application/vnd.ms-powerpoint.template.macroEnabled.12": "x-office/presentation",
- "application/vnd.ms-powerpoint": "x-office/presentation",
- "application/vnd.ms-visio.drawing.macroEnabled.12": "application/vnd.visio",
- "application/vnd.ms-visio.drawing": "application/vnd.visio",
- "application/vnd.ms-visio.stencil.macroEnabled.12": "application/vnd.visio",
- "application/vnd.ms-visio.stencil": "application/vnd.visio",
- "application/vnd.ms-visio.template.macroEnabled.12": "application/vnd.visio",
- "application/vnd.ms-visio.template": "application/vnd.visio",
- "application/vnd.ms-word.document.macroEnabled.12": "x-office/document",
- "application/vnd.ms-word.template.macroEnabled.12": "x-office/document",
- "application/vnd.oasis.opendocument.graphics-flat-xml": "x-office/drawing",
- "application/vnd.oasis.opendocument.graphics-template": "x-office/drawing",
- "application/vnd.oasis.opendocument.graphics": "x-office/drawing",
- "application/vnd.oasis.opendocument.presentation-flat-xml": "x-office/presentation",
- "application/vnd.oasis.opendocument.presentation-template": "x-office/presentation",
- "application/vnd.oasis.opendocument.presentation": "x-office/presentation",
- "application/vnd.oasis.opendocument.spreadsheet-flat-xml": "x-office/spreadsheet",
- "application/vnd.oasis.opendocument.spreadsheet-template": "x-office/spreadsheet",
- "application/vnd.oasis.opendocument.spreadsheet": "x-office/spreadsheet",
- "application/vnd.oasis.opendocument.text-flat-xml": "x-office/document",
- "application/vnd.oasis.opendocument.text-master": "x-office/document",
- "application/vnd.oasis.opendocument.text-template": "x-office/document",
- "application/vnd.oasis.opendocument.text-web": "x-office/document",
- "application/vnd.oasis.opendocument.text": "x-office/document",
- "application/vnd.openxmlformats-officedocument.presentationml.presentation": "x-office/presentation",
- "application/vnd.openxmlformats-officedocument.presentationml.slideshow": "x-office/presentation",
- "application/vnd.openxmlformats-officedocument.presentationml.template": "x-office/presentation",
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "x-office/spreadsheet",
- "application/vnd.openxmlformats-officedocument.spreadsheetml.template": "x-office/spreadsheet",
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document.docxf": "x-office/form-template",
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document.oform": "x-office/form",
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "x-office/document",
- "application/vnd.openxmlformats-officedocument.wordprocessingml.template": "x-office/document",
- "application/vnd.visio": "x-office/document",
- "application/vnd.wordperfect": "x-office/document",
- "application/vnd.xmind.workbook": "mindmap",
- "application/x-7z-compressed": "package/x-generic",
- "application/x-bzip2": "package/x-generic",
- "application/x-cbr": "text",
- "application/x-dcraw": "image",
- "application/x-deb": "package/x-generic",
- "application/x-fictionbook+xml": "text",
- "application/x-font": "font",
- "application/x-freemind": "mindmap",
- "application/x-gimp": "image",
- "application/x-gzip": "application/gzip",
- "application/x-iwork-keynote-sffkey": "x-office/presentation",
- "application/x-iwork-numbers-sffnumbers": "x-office/spreadsheet",
- "application/x-iwork-pages-sffpages": "x-office/document",
- "application/x-mobipocket-ebook": "text",
- "application/x-openvpn-profile": "text",
- "application/x-perl": "text/code",
- "application/x-photoshop": "image",
- "application/x-php": "text/code",
- "application/x-rar-compressed": "package/x-generic",
- "application/x-tar": "package/x-generic",
- "application/x-tex": "text",
- "application/xml": "text/html",
- "application/yaml": "text/code",
- "application/zip": "package/x-generic",
- "application/zstd": "package/x-generic",
- "audio/aac": "audio",
- "audio/midi": "audio",
- "audio/mpeg": "audio",
- "audio/ogg": "audio",
- "audio/opus": "audio",
- "audio/wav": "audio",
- "audio/webm": "audio",
- "audio/x-midi": "audio",
- "database": "file",
- "httpd/unix-directory": "dir",
- "image/targa": "image/tga",
- "image/x-emf": "image/emf",
- "text/css": "text/code",
- "text/csv": "x-office/spreadsheet",
- "text/html": "text/code",
- "text/tab-separated-values": "x-office/spreadsheet",
- "text/x-c": "text/code",
- "text/x-c++src": "text/code",
- "text/x-h": "text/code",
- "text/x-java-source": "text/code",
- "text/x-ldif": "text/code",
- "text/x-nfo": "text/code",
- "text/x-python": "text/code",
- "text/x-rst": "text",
- "text/x-shellscript": "text/code",
- "video/mp4": "video",
- "video/mpeg": "video",
- "video/ogg": "video",
- "video/webm": "video",
- "web": "text/code",
- "my-custom/mimetype": "custom"
-},
- files: [
- "application",
- "application-pdf",
- "audio",
- "file",
- "folder",
- "folder-drag-accept",
- "folder-encrypted",
- "folder-external",
- "folder-public",
- "folder-shared",
- "folder-starred",
- "font",
- "image",
- "link",
- "location",
- "mindmap",
- "package-x-generic",
- "text",
- "text-calendar",
- "text-code",
- "text-vcard",
- "video",
- "whiteboard",
- "x-office-document",
- "x-office-drawing",
- "x-office-form",
- "x-office-form-template",
- "x-office-presentation",
- "x-office-spreadsheet"
-],
- themes: [],
- names: {'application/cmd': t('core', "Windows Command Script"),
-'application/epub+zip': t('core', "Electronic book document"),
-'application/font-sfnt': t('core', "TrueType Font Collection"),
-'application/font-woff': t('core', "Web Open Font Format"),
-'application/gpx+xml': t('core', "GPX geographic data"),
-'application/gzip': t('core', "Gzip archive"),
-'application/illustrator': t('core', "Adobe Illustrator document"),
-'application/java': t('core', "Java source code"),
-'application/javascript': t('core', "JavaScript source code"),
-'application/json': t('core', "JSON document"),
-'application/msaccess': t('core', "Microsoft Access database"),
-'application/msonenote': t('core', "Microsoft OneNote document"),
-'application/msword': t('core', "Microsoft Word document"),
-'application/octet-stream': t('core', "Unknown"),
-'application/pdf': t('core', "PDF document"),
-'application/postscript': t('core', "PostScript document"),
-'application/rss+xml': t('core', "RSS summary"),
-'application/toml': t('core', "TOML configuration file"),
-'application/vnd.android.package-archive': t('core', "Android package"),
-'application/vnd.google-earth.kml+xml': t('core', "KML geographic data"),
-'application/vnd.google-earth.kmz': t('core', "KML geographic compressed data"),
-'application/vnd.lotus-wordpro': t('core', "Lotus Word Pro document"),
-'application/vnd.ms-excel': t('core', "Excel spreadsheet"),
-'application/vnd.ms-excel.addin.macroEnabled.12': t('core', "Excel add-in"),
-'application/vnd.ms-excel.sheet.binary.macroEnabled.12': t('core', "Excel 2007 binary spreadsheet"),
-'application/vnd.ms-excel.sheet.macroEnabled.12': t('core', "Excel spreadsheet"),
-'application/vnd.ms-excel.template.macroEnabled.12': t('core', "Excel spreadsheet template"),
-'application/vnd.ms-outlook': t('core', "Outlook Message"),
-'application/vnd.ms-powerpoint': t('core', "PowerPoint presentation"),
-'application/vnd.ms-powerpoint.addin.macroEnabled.12': t('core', "PowerPoint add-in"),
-'application/vnd.ms-powerpoint.presentation.macroEnabled.12': t('core', "PowerPoint presentation"),
-'application/vnd.ms-powerpoint.slideshow.macroEnabled.12': t('core', "PowerPoint presentation"),
-'application/vnd.ms-powerpoint.template.macroEnabled.12': t('core', "PowerPoint presentation template"),
-'application/vnd.ms-word.document.macroEnabled.12': t('core', "Word document"),
-'application/vnd.oasis.opendocument.formula': t('core', "ODF formula"),
-'application/vnd.oasis.opendocument.graphics': t('core', "ODG drawing"),
-'application/vnd.oasis.opendocument.graphics-flat-xml': t('core', "ODG drawing (Flat XML)"),
-'application/vnd.oasis.opendocument.graphics-template': t('core', "ODG template"),
-'application/vnd.oasis.opendocument.presentation': t('core', "ODP presentation"),
-'application/vnd.oasis.opendocument.presentation-flat-xml': t('core', "ODP presentation (Flat XML)"),
-'application/vnd.oasis.opendocument.presentation-template': t('core', "ODP template"),
-'application/vnd.oasis.opendocument.spreadsheet': t('core', "ODS spreadsheet"),
-'application/vnd.oasis.opendocument.spreadsheet-flat-xml': t('core', "ODS spreadsheet (Flat XML)"),
-'application/vnd.oasis.opendocument.spreadsheet-template': t('core', "ODS template"),
-'application/vnd.oasis.opendocument.text': t('core', "ODT document"),
-'application/vnd.oasis.opendocument.text-flat-xml': t('core', "ODT document (Flat XML)"),
-'application/vnd.oasis.opendocument.text-template': t('core', "ODT template"),
-'application/vnd.openxmlformats-officedocument.presentationml.presentation': t('core', "PowerPoint 2007 presentation"),
-'application/vnd.openxmlformats-officedocument.presentationml.slideshow': t('core', "PowerPoint 2007 show"),
-'application/vnd.openxmlformats-officedocument.presentationml.template': t('core', "PowerPoint 2007 presentation template"),
-'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': t('core', "Excel 2007 spreadsheet"),
-'application/vnd.openxmlformats-officedocument.spreadsheetml.template': t('core', "Excel 2007 spreadsheet template"),
-'application/vnd.openxmlformats-officedocument.wordprocessingml.document': t('core', "Word 2007 document"),
-'application/vnd.openxmlformats-officedocument.wordprocessingml.template': t('core', "Word 2007 document template"),
-'application/vnd.visio': t('core', "Microsoft Visio document"),
-'application/vnd.wordperfect': t('core', "WordPerfect document"),
-'application/x-7z-compressed': t('core', "7-zip archive"),
-'application/x-blender': t('core', "Blender scene"),
-'application/x-bzip2': t('core', "Bzip2 archive"),
-'application/x-deb': t('core', "Debian package"),
-'application/x-fictionbook+xml': t('core', "FictionBook document"),
-'application/x-font': t('core', "Unknown font"),
-'application/x-krita': t('core', "Krita document"),
-'application/x-mobipocket-ebook': t('core', "Mobipocket e-book"),
-'application/x-msi': t('core', "Windows Installer package"),
-'application/x-openvpn-profile': t('core', "OpenVPN connection profile"),
-'application/x-perl': t('core', "Perl script"),
-'application/x-php': t('core', "PHP script"),
-'application/x-tar': t('core', "Tar archive"),
-'application/xml': t('core', "XML document"),
-'application/yaml': t('core', "YAML document"),
-'application/zip': t('core', "Zip archive"),
-'application/zstd': t('core', "Zstandard archive"),
-'audio/aac': t('core', "AAC audio"),
-'audio/flac': t('core', "FLAC audio"),
-'audio/mp4': t('core', "MPEG-4 audio"),
-'audio/mpeg': t('core', "MP3 audio"),
-'audio/ogg': t('core', "Ogg audio"),
-'audio/wav': t('core', "RIFF\/WAVe standard Audio"),
-'audio/webm': t('core', "WebM audio"),
-'audio/x-scpls': t('core', "MP3 ShoutCast playlist"),
-'image/bmp': t('core', "Windows BMP image"),
-'image/bpg': t('core', "Better Portable Graphics image"),
-'image/emf': t('core', "EMF image"),
-'image/gif': t('core', "GIF image"),
-'image/heic': t('core', "HEIC image"),
-'image/heif': t('core', "HEIF image"),
-'image/jp2': t('core', "JPEG-2000 JP2 image"),
-'image/jpeg': t('core', "JPEG image"),
-'image/png': t('core', "PNG image"),
-'image/svg+xml': t('core', "SVG image"),
-'image/tga': t('core', "Truevision Targa image"),
-'image/tiff': t('core', "TIFF image"),
-'image/webp': t('core', "WebP image"),
-'image/x-dcraw': t('core', "Digital raw image"),
-'image/x-icon': t('core', "Windows Icon"),
-'message/rfc822': t('core', "Email message"),
-'text/calendar': t('core', "VCS\/ICS calendar"),
-'text/css': t('core', "CSS stylesheet"),
-'text/csv': t('core', "CSV document"),
-'text/html': t('core', "HTML document"),
-'text/markdown': t('core', "Markdown document"),
-'text/org': t('core', "Org-mode file"),
-'text/plain': t('core', "Plain text document"),
-'text/rtf': t('core', "Rich Text document"),
-'text/tab-separated-values': t('core', "TSV document"),
-'text/vcard': t('core', "Electronic business card"),
-'text/x-c++src': t('core', "C++ source code"),
-'text/x-java-source': t('core', "Java source code"),
-'text/x-ldif': t('core', "LDIF address book"),
-'text/x-nfo': t('core', "NFO document"),
-'text/x-php': t('core', "PHP source"),
-'text/x-python': t('core', "Python script"),
-'text/x-rst': t('core', "ReStructuredText document"),
-'video/3gpp': t('core', "3GPP multimedia file"),
-'video/MP2T': t('core', "MPEG video"),
-'video/dv': t('core', "DV video"),
-'video/mp2t': t('core', "MPEG-2 transport stream"),
-'video/mp4': t('core', "MPEG-4 video"),
-'video/mpeg': t('core', "MPEG video"),
-'video/ogg': t('core', "Ogg video"),
-'video/quicktime': t('core', "QuickTime video"),
-'video/webm': t('core', "WebM video"),
-'video/x-flv': t('core', "Flash video"),
-'video/x-matroska': t('core', "Matroska video"),
-'video/x-ms-wmv': t('core', "Windows Media video"),
-'video/x-msvideo': t('core', "AVI video"),
-'my-custom/mimetype': t('core', "Custom Document"),
-},
-};
--- a/tests/data/integritycheck/mimetypeListModified/core/signature.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "hashes": {
- "core\/js\/mimetypelist.js": "25635feead9858d82354005d59b36ae965323bc651b012910c0178bef8e90c9809abb2a6aa2425268d0f08a545567d2f2a10f00818435dc04ded44b7d9e61fad"
- },
- "signature": "lrvTud+92i2m7XmQXbpT2LFSuFUk351DPjCRJ8hvPngKLB1yoSLQqUNOFTRN97fUKlkR2k2O8x\/HWAOG9pmB9H2m5bYrJ8\/mUbhbYSoVV5yC1rWtWH7A1uCvdj\/wDjuTR9NRKQXbIQa+v0DuwxyeRO6GiY9YRl+K5UYmP2jZscolECHCWKnHfLBoesrkt6VsJIqJTRk\/6l8\/MzMxmzcq8mWH5AdEvwieyEIXqmJevRi\/lVQG8h4l3yZukjtDHdFnbKdyOjezRQFK\/joJlxV3pNUVIsBl6ClGghS75c\/kO9AD+70BX6u6aRr9TPbkh84EmCyzceFaU+FzCd\/NC+fwPjDrJQlVh6EfwOEzkpNQP5KA5r8FVQZw51U9o1kJ3mdK04w0X2Eap0ZOz3RP2W2Vo2KJH7OitXA9TCNh9VoVY5jBJRYNeFi3NQewC6qZ2IXhXIEi8nyLjrsh71WfimvBl37s8LkDHODzKIQTCO14a7mDiiPZHry2BRHwF8G7znaEf6CCbygUZi4PK3VZT5Sv5OBzLfgkpTxRXIN6NxKljBRF8B2bz027J4L7A3s6ttpmBOXtj98MQekp27eWC4mb+ihUaxvpe6M3dsK7hCfUDl4VvPlVYo+vnCP8WNa1I2IcB38HN5Wb6E5JDhaXgCz6i3CcNXwghcrs1j7wXiDw8Rc=",
- "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
-}
\ No newline at end of file
--- a/tests/data/integritycheck/root.crt
+++ /dev/null
@@ -1,28 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIE1DCCArygAwIBAgIUFgEnT7tUWNgEKfbMiRTOm3Z8figwDQYJKoZIhvcNAQEF
-BQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMCAXDTE1MTEw
-MzIxMDMzMloYDzk5OTkxMjMxMjM1OTU5WjAjMSEwHwYDVQQKDBhvd25DbG91ZCBD
-b2RlIFNpZ25pbmcgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCt
-rYEwiwpuhzRNx1L2SntkL/zalA7OkwEcsBn4Ysw47nEdvp648AVohT7d+U7+DWix
-O06xvlAJUhqYTXX7EG9n+mBnG+TIMq2zlei0Jj3uq1pEZE9elfGZael2uc8gRXMZ
-YFmSlTvzexqvfK4B3DwoZaMaWJecEO9iyuUyzHMBpE8bHtGDOUGy/oTO9WASbtl9
-Rfk38VLLV6csCPwKjii6Q3YZ+AYU0YqLg22BwZlqlTexUjWAmVIqaoxmSEuKa41X
-nuAIHfP65oiob86s4IvJXVr3r7NjdF2IJ/ZrwmjGKaWgSZKcoZBPLArZJeJiIQ4Y
-KzLZATwba5dWswV0mbDj8eP2BG+02NVKxylOBbeoBESnahZeZ5nJ5XKZr+ErJAUW
-b417fEnYaQNBlNnijjkqXaDipmTktUfnv4lm30sUAgho6I3Ga7gQrFPzKg9V4j2S
-+LOTc1HmJOnR6Kfttx+yAHYLKtvV5yIMMpz+rZ2X5g/N2GdgleZj5VU9nmKzTPeG
-x6V+dBaIkqNe8/AXaVnxt9KSb03Q/+CFjKTNDtEN5fNJuXS0+h+oop6nhpktM2i7
-gCpxeLNEaQaeoxR5093VN00oOJOYBvQoVGEDftEwdG6dWbTZsIykBF7aK+p8DMy9
-tCdc2GnGMEuFlUNA9ucv2Rv7IcuPdspnK31CZoMNKwIDAQABMA0GCSqGSIb3DQEB
-BQUAA4ICAQBROWec0HOsnLPN40gkBQ62mNBUJgcAr5K2eMIEMSRFRD2ldEVOvmCO
-u1Q62umy9tiSRiFQTcG1J9k0zlOjy/hfpBl2G1Zce0OoEeuNkH7c0W/idHSloWRZ
-YlK3tVJD6DzY6s9VbO6e/ncecNsXkirkWp/cvMYquH2d4OmSl0/hW0VMdxOCLxkA
-xbW+3Dh05u7tgKVRD67/GRvLtg+xHaOJqiOh3MpMaHy+6xT5Fd5K2QC0pcGtZuqF
-EnnfdeUI/Dy76yQE8pBfjaUFf3TS1n1E6kun1Nkf0X4pvwi8W1goLsPu5sWDNNga
-1RGYj0o5OdIo27qebfmu76WX0fNNd47VabtzNV+W7Msj0yeZg+hxAtAvs0ZEyJh+
-4biWsv+ALSlqz4sSdoOVGUEBdnkUrWN19lou62ix9vTmuCrVEA3TuZA3PR0+hqqQ
-/A/DcmWwxWYKyaBgxwHc/nGo1qWrDh22P5Rp7++Zw3kOCY6QmeJkAiHFs6Crw4ub
-HKVMw3fV5H9oiUFjadPZoCU51uXKX4YRqKxWJ5djlp4r1GCEQHyxngTsmH3komnw
-kh4LsEQRqdhuT0A4sZN7CenMJfQiFqupL7RVSycJFQpgzwVFmOzjCVT4PT/W5ARv
-9YtqEkvyoRTwErwuN/FIVvhWnIP/C69Z1/T/nXyj86P2G7PMgnchIQ==
------END CERTIFICATE-----
--- a/tests/data/integritycheck/root.crt.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-FileCopyrightText: 2015 ownCloud, Inc.
-SPDX-License-Identifier: AGPL-3.0-only
--- a/tests/data/integritycheck/root.key
+++ /dev/null
@@ -1,51 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIJKgIBAAKCAgEAra2BMIsKboc0TcdS9kp7ZC/82pQOzpMBHLAZ+GLMOO5xHb6e
-uPAFaIU+3flO/g1osTtOsb5QCVIamE11+xBvZ/pgZxvkyDKts5XotCY97qtaRGRP
-XpXxmWnpdrnPIEVzGWBZkpU783sar3yuAdw8KGWjGliXnBDvYsrlMsxzAaRPGx7R
-gzlBsv6EzvVgEm7ZfUX5N/FSy1enLAj8Co4oukN2GfgGFNGKi4NtgcGZapU3sVI1
-gJlSKmqMZkhLimuNV57gCB3z+uaIqG/OrOCLyV1a96+zY3RdiCf2a8JoximloEmS
-nKGQTywK2SXiYiEOGCsy2QE8G2uXVrMFdJmw4/Hj9gRvtNjVSscpTgW3qAREp2oW
-XmeZyeVyma/hKyQFFm+Ne3xJ2GkDQZTZ4o45Kl2g4qZk5LVH57+JZt9LFAIIaOiN
-xmu4EKxT8yoPVeI9kvizk3NR5iTp0ein7bcfsgB2Cyrb1eciDDKc/q2dl+YPzdhn
-YJXmY+VVPZ5is0z3hselfnQWiJKjXvPwF2lZ8bfSkm9N0P/ghYykzQ7RDeXzSbl0
-tPofqKKep4aZLTNou4AqcXizRGkGnqMUedPd1TdNKDiTmAb0KFRhA37RMHRunVm0
-2bCMpARe2ivqfAzMvbQnXNhpxjBLhZVDQPbnL9kb+yHLj3bKZyt9QmaDDSsCAwEA
-AQKCAgEAnR6/JkRTPqTQW6D8W9YMBRoovTF+p8F0GxjxlbUDnmmQKeGeRB7YNbN9
-qWD25n0I/nVx/vj1/UiqyKgjGOvIbZ+kAQPKGJdIb5Qp/nguRTH9qqu45g/ujuSz
-EfaM/Fv4AbgZsLOTlfUDskiwPvyX68/vG1GUbtsfRhfZ+/fb/1s/OYDK99Ufq6f4
-TCbOMD7aQSvBh6upRE5a7Up/gakUDVYkjN/F2KWsmgRfWCjl+vddd+ywfFO4cqkL
-tSioNmSQbPlNIeq/I3fVn9PufJVzwMrVFgh82HeYein1E43ALa3VqcmFem/rVsS4
-V7SfNjlDP/gsuwcT8paGRigUwmScEkyXYJ7oSNEN8Xe4kWWakfwGpa1HmaYPdHx+
-O+G8coHp2kcc/tUZma+Ffo3tNRMfGcpowG+PetbCh4uSNPo5U5U5W53+vxgyZHFi
-lY7gc5HVi5JBSxmWwTa3RDcz1dByWS83NdObrxAntp3W9g8tVj2N9gfUnJS+wp3d
-m8HvO3bzIUuhQcWAtcnXqRAGsl+uc/xgdF2membV/yOHdn2Z1zXKEnjC1T6cEV06
-qocwgp0/EAMhzL4FP8xA9MvztZR7bJOyUihTIadG7Zb18XBea7yA73KvfYGrzjsg
-lmqV4CbGuo0If8SJD03xXthMqi5cBXu4V13sYAbNO8pPDjhfPIkCggEBAOUjScLN
-RoaoOBO3JUWoYauZrf9sa72/zzTOg+c8unQquExMjnsyEVJPD4+TpKGN5/sewwb5
-zksI5c1njOrdFdSKoKlgB/6owv2v70Uqe+HeVA+m5Qy6JSgMAAXZgJ5PtaCCu+Xq
-MOqTd/xInfP1oCecJF9UvqeQJBJN16fMXBib9J5+sLCRJnFNywfmlfAiIOa4MIoY
-Tz7LW+r4Zot0x2o6KcCT8AB3LJIz2seHmTf9Jjk17kw7pFJWtZf6eA1lgDlzadKJ
-iHgrdSbchDZv410B0df/ZV+gZ7PMgK/cbo3H3JOXPV1dxmTONTIIvxYj1LmOdMP4
-p5oeM367LUWQN20CggEBAMIJykbsm7IVthkMeCm/VhG/wd44iB0g5+us8q8pUO1+
-KfTzytHPjMack5a/XaEApRRiJpCAJNcBpUNbJqaOZQ1QqnX6ozlSR2aUi27noVz6
-/heG+3qZElejYfLMpJAF1crjdFJIqKucizc+E6AZ+nOixpJcDABYWby9QQw4O1Pr
-Ii3E7xyFvqizYzE7P/HG6P0gMxta9aKHKGhGHX51Uc+BUfF0T0ymXVvWLlfMg+HI
-SBPqMphW+YR0xwK21A2LvqYBM2MyaThY2wyVSZs9akjiAukRwX8l7lA5A/try0Q0
-1POyN6oo1H7iSHuraqXfDjcSpK5M/QOpXMuoPyTI//cCggEBAKCn00mwH6i+PUMl
-gA6M9p4YTDTwUcJiv+cofLcejyRv53QnoSajfh2VrTVfsWhMVMBvWxKDB674eBdC
-aT0q8elpoSfgWvqkXML+HecC2IUPGyU2QRZhVTf04fc3/sQA4zm9L/0N7GosJ05N
-o+Gu8DGVerMUefCGUaQ7y96snE3s2uBdt4i03J1Ii/foJmyNoT/jGLVaQgWnE7V1
-oIBayo6iZS/PCdFpvWhszxJi8nydE7W9KG1uy9GnVf9O7+mEpxig4StqnrKS1br/
-lfuNC37kjbrCKNOZZdxcoEWtah4iaXdZ7P6Ph1CafBWuqDvft4C6bwgQSYL3ded/
-WUiSyykCggEAePeThF3TvtUsPjd43kXriYsreLdzm/08uL+MWEkAq96gl5Y5Fk43
-LEbG+A77dvko8Skzc5h/3w6mkfRMhz90njVw37ZOddjmrHvk5VJAVfAf4lkDhG3T
-cpFn6e9MlIEexKrChN3JUZt5awonQAOSEO8krm/2B20NHM47tDuGOQ34s+H3U6fJ
-sfCL4VBX0Ao6jDu7wM0XH6j1NvSnRIQtaZjslgP1wApjX3KKV7Anc+XhkZDK1BA8
-5CfNPdLvJja9t04+VBREZp12ikSzq7VBAojsWZL5N6RVCuxQoDiWc0IglIDBlTJ5
-L1Uw7PBzv07s1MappgRXJCY8tLaCDxPEBwKCAQEA1Rxj0YpQ1D+8oFQ3ck0b4ODy
-DCSCNe6Xw+Wzv5BqGVsQCmW14uVBT+S/qij5dTrGIPXudLSHtdjs9tmoDqnkxY/o
-Nj5rx6J2brnNLTD7yo/j74kgaRwSuHafpGX1C0zge0rgIgVu8DhWHan7F/38K0cO
-T1jYJbYcTAvEcO1XXXItnaHR1ETY4p0G5FvUTLWaNQnQTU3r5ZaCkjXN9UBA2k6U
-6j6A9/JXIlNPFNoB103iAD5jvHa96AlivHSyp4UTlsiwAxec316CY2zdWrVWQCF4
-J8DspH3ygeLtvKOveEYsiaiNuJLKREC2GIRUm6O4C/RdP0s0QAODpk+yGCfukg==
------END RSA PRIVATE KEY-----
--- a/tests/data/integritycheck/root.key.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-FileCopyrightText: 2015 ownCloud, Inc.
-SPDX-License-Identifier: AGPL-3.0-only
--- a/tests/data/l10n/cs.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "translations" : {
- "_%n window_::_%n windows_" : ["%n okno", "%n okna", "%n oken"]
- },
- "pluralForm" : "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;"
-}
--- a/tests/data/l10n/de.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "translations" : {
- "_%n file_::_%n files_": ["%n Datei", "%n Dateien"],
- "Ordered placeholders one %s two %s": "Placeholder one %s two %s",
- "Reordered placeholders one %s two %s": "Placeholder two %2$s one %1$s",
- "Reordered placeholders one %1$s two %2$s": "Placeholder two %2$s one %1$s"
- },
- "pluralForm" : "nplurals=2; plural=(n != 1);"
-}
--- a/tests/data/l10n/ru.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "translations" : {
- "_%n file_::_%n files_" : ["%n файл", "%n файла", "%n файлов"]
- },
- "pluralForm" : "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"
-}
--- a/tests/data/lorem-big.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse id hendrerit felis. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed dictum dolor nec lobortis sagittis. Aliquam blandit lobortis ullamcorper. Donec malesuada ante ante, vel cursus purus suscipit ut. Sed a eros vitae lorem tristique hendrerit a a odio. Cras interdum mi lectus, sed molestie mauris tempus sit amet. Praesent auctor neque venenatis diam tincidunt sodales. Maecenas tincidunt ligula et ipsum dignissim, et molestie dui hendrerit. Nunc lobortis mauris vel tempor tristique. Sed consectetur bibendum nunc. Pellentesque augue velit, interdum ac posuere eu, eleifend et massa. Vestibulum ac mi eu mi viverra iaculis. Sed consequat leo adipiscing dui varius, sagittis vestibulum massa vehicula.
-
-Praesent imperdiet erat vitae dui placerat, sit amet vulputate tortor viverra. In laoreet elit lorem, at luctus est tempor ac. Nam elementum scelerisque nisl vitae accumsan. Curabitur cursus rutrum est, ut adipiscing ante elementum ut. Ut lectus sem, luctus ut massa non, dignissim vestibulum ipsum. Suspendisse non convallis enim, aliquam adipiscing velit. Donec pharetra non turpis hendrerit porttitor. Aliquam erat volutpat.
-
-Cras eget arcu eu dolor faucibus accumsan non eget orci. Integer quis quam tincidunt, faucibus neque in, imperdiet lacus. Aenean dui turpis, lacinia quis ligula non, semper adipiscing tellus. Curabitur vitae dui quis nisl malesuada commodo. Nunc eleifend metus enim, eu aliquet dui semper sit amet. Sed a odio sapien. Suspendisse vitae ante id sapien semper accumsan. Sed vestibulum erat quis laoreet pellentesque. Nullam placerat ligula eu odio faucibus, eget dictum orci tristique. Quisque sit amet pulvinar velit, lacinia dictum sem.
-
-Phasellus id sagittis lacus. Vivamus facilisis convallis metus, sit amet ultricies purus gravida quis. Vivamus eget egestas arcu, a euismod risus. Fusce metus arcu, molestie ut lacinia at, commodo eu nibh. In leo tortor, feugiat aliquet semper in, malesuada ac nulla. Fusce tempor ultricies blandit. Pellentesque et lorem quam. Suspendisse eros eros, mattis ut porttitor vitae, fermentum eget augue. Aliquam vitae justo sed est dictum lobortis. Cras lacinia commodo ligula sed ornare. Donec ut eros semper, sodales tellus quis, cursus quam. Quisque elementum ullamcorper pellentesque. Integer in rutrum ante, vel condimentum metus. Aliquam erat volutpat.
-
-Quisque convallis tempus eros, sit amet mattis ipsum eleifend in. Mauris volutpat, urna ut commodo tempus, est quam scelerisque erat, eget consequat nulla arcu id eros. Nam elementum nibh quis tincidunt adipiscing. Fusce tristique pretium mollis. Nulla quis ornare felis, vel lacinia diam. In et dui et mauris vestibulum commodo. In hac habitasse platea dictumst. Vestibulum viverra pellentesque tortor, tempor vehicula orci. Praesent rutrum turpis ipsum, ac commodo nisi vehicula ac.
-
-Nulla gravida ultrices mauris. Ut congue purus nec dolor euismod, vitae auctor elit condimentum. Nullam elementum velit lectus, fringilla pharetra sem hendrerit sit amet. Cras quis lobortis nisi, id interdum massa. Proin eget porttitor sapien. Vivamus quam odio, consectetur at rhoncus vel, posuere in nisi. Donec hendrerit urna ac massa tristique, pellentesque consequat orci dapibus. Mauris mi erat, aliquet id adipiscing ultricies, ultrices nec lacus. Proin imperdiet elit magna, ut suscipit metus faucibus eget. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas at leo lorem. Donec mi nibh, blandit ut mi quis, dictum adipiscing ante. Curabitur sodales blandit odio id viverra. Fusce sagittis rutrum nibh. Mauris risus lacus, placerat at dictum in, varius volutpat quam.
-
-Ut accumsan tincidunt dui, non semper lorem sodales nec. Curabitur pellentesque velit nec erat ultrices, in convallis diam suscipit. Curabitur pellentesque convallis mi. Phasellus iaculis orci quis nisi semper auctor. Vestibulum eleifend tortor quis odio feugiat, pulvinar congue turpis blandit. Sed viverra diam risus, vel mattis orci dignissim ut. Cras pellentesque dapibus dolor. Morbi tempor eleifend magna. Ut sodales ut sapien sed elementum. In hac habitasse platea dictumst.
-
-Nullam eget sagittis tellus. Donec a tellus id ligula viverra pellentesque. Pellentesque nulla turpis, pretium vel sem vel, placerat ultricies diam. Donec felis nibh, rhoncus id ornare at, lobortis vel massa. Nullam eu luctus ipsum. Mauris consequat dictum aliquam. Suspendisse ullamcorper aliquam mauris, viverra tincidunt augue suscipit at. Sed luctus scelerisque justo ut semper. Phasellus massa mauris, molestie at nulla eget, dictum ultricies nisi. Aliquam rhoncus nisl sed urna egestas, vel luctus lectus euismod. Mauris sagittis dapibus leo. Donec vitae dignissim risus, ac ultricies augue. Ut vulputate tortor sed dui consectetur placerat. Nullam viverra non diam vel dignissim.
-
-Suspendisse potenti. Integer facilisis neque vitae euismod adipiscing. Aenean dictum leo commodo dui sodales, sit amet volutpat mauris gravida. Proin tempus convallis eros at consectetur. Proin nisl purus, dictum vel euismod ut, imperdiet quis est. Etiam sollicitudin lobortis neque eget pulvinar. Etiam venenatis vel sem ut posuere. Aliquam consectetur rhoncus facilisis. Morbi a viverra orci.
-
-Praesent ut vehicula orci, vel convallis risus. Suspendisse consectetur varius interdum. Interdum et malesuada fames ac ante ipsum primis in faucibus. Proin vel sodales enim. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur suscipit tristique justo vel dapibus. Sed tincidunt mattis massa a auctor. Donec in tincidunt elit. Curabitur interdum neque at ante fringilla tempus. In hac habitasse platea dictumst. Vivamus luctus ligula ut nisl fermentum egestas. Praesent pulvinar accumsan neque. Sed nec leo sit amet arcu vehicula vehicula non ac diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;
-
-Morbi vel arcu quis neque dictum ullamcorper. Pellentesque condimentum consequat lacinia. Vestibulum eleifend placerat erat, eu hendrerit dui pulvinar eget. Vestibulum accumsan, lectus id vehicula fringilla, erat eros dictum massa, ut tristique libero mi eu ante. In at ante nunc. Praesent sodales ullamcorper porta. Curabitur egestas odio elit. Praesent et libero malesuada, venenatis lorem vitae, eleifend nisl. Donec dapibus euismod turpis, nec porttitor turpis dapibus ac. Fusce iaculis lacus eget dictum aliquam. Sed dictum eu enim ac posuere. Ut accumsan, ipsum et laoreet consectetur, ipsum quam dapibus diam, ac molestie ligula dui id massa. Nulla aliquam mauris congue nibh vestibulum imperdiet.
-
-Aenean ultricies, orci vel consectetur suscipit, ante nisi eleifend est, vitae suscipit risus erat dictum dui. Ut nisi diam, tristique sed nisl nec, aliquam gravida orci. Vestibulum in molestie sem. Ut eu molestie mi. Suspendisse potenti. Nunc sagittis lorem ut est vehicula, vitae imperdiet leo rutrum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec pharetra urna pretium, faucibus orci cursus, sodales purus. Duis iaculis dignissim augue, non aliquet ligula elementum quis. Sed tempus a nisi et faucibus. Donec mattis ligula ac sem aliquam, sed sodales est ullamcorper. Cras vel orci est. Integer eget ultricies nisi.
-
-Quisque rhoncus, nisl vel auctor consectetur, sapien augue iaculis urna, quis suscipit arcu dolor ac est. Fusce sit amet quam lacinia, malesuada lorem vitae, dapibus justo. Mauris eget massa nec dolor volutpat tempor ut eu ante. Donec risus mi, aliquam at euismod vulputate, ullamcorper non leo. Etiam ac elit velit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Vivamus sollicitudin enim sed felis pharetra, a convallis mi venenatis. Donec convallis non velit venenatis suscipit. Sed massa risus, dictum quis aliquam ut, placerat quis arcu. Donec at nisi neque. Nullam porta et mi vel ultricies. Donec vel aliquam sem. Mauris varius, ipsum interdum mattis rhoncus, nunc nisl vehicula tortor, quis condimentum nibh nisl vel ante. Sed vel pretium dui. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce pulvinar molestie neque, quis luctus ante.
-
-Interdum et malesuada fames ac ante ipsum primis in faucibus. Donec ac ipsum egestas, luctus urna aliquet, fringilla tortor. Cras eleifend sed tellus tempor congue. Integer venenatis magna dignissim pharetra cursus. Nulla volutpat, sem sit amet malesuada laoreet, odio libero consectetur orci, sit amet pellentesque neque ligula quis libero. Morbi nec interdum metus, in imperdiet justo. Fusce euismod egestas nulla et elementum. Etiam aliquam lectus quis eros semper, sed dapibus velit ultrices. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
-
-Proin sed elit vel massa placerat mattis et vel mauris. Aliquam accumsan ante ligula, at accumsan enim pellentesque sit amet. Sed eget quam vel nunc molestie iaculis vitae nec massa. Vivamus sed accumsan libero. Duis pretium quam at blandit sollicitudin. Nullam sodales sit amet elit ac adipiscing. Ut ullamcorper dignissim sapien. Nulla facilisi. Fusce sed consequat lectus, et tempus ipsum. Aliquam vestibulum lectus vitae quam mollis, et adipiscing nisl lobortis
--- a/tests/data/lorem.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
-Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
-Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
-Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
\ No newline at end of file
--- a/tests/data/scss/styles-error.scss
+++ /dev/null
@@ -1,5 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-body { error }
--- a/tests/data/scss/styles-success.scss
+++ /dev/null
@@ -1,5 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-body { background-color: $color-primary; }
--- a/tests/data/setUploadLimit/htaccess
+++ /dev/null
@@ -1,58 +0,0 @@
-# Version: 8.2.0
-
-
-
- SetEnvIfNoCase ^Authorization$ "(.+)" XAUTHORIZATION=$1
- RequestHeader set XAuthorization %{XAUTHORIZATION}e env=XAUTHORIZATION
-
-
-
-
- # Add security and privacy related headers
- Header set X-Content-Type-Options "nosniff"
- Header set X-Robots-Tag "noindex, nofollow"
- Header set X-Frame-Options "SAMEORIGIN"
- SetEnv modHeadersAvailable true
-
-
- # Add cache control for CSS and JS files
-
- Header set Cache-Control "max-age=7200, public"
-
-
-
-php_value upload_max_filesize 513M
-php_value post_max_size 513M
-php_value memory_limit 512M
-php_value default_charset 'UTF-8'
-php_value output_buffering off
-
- SetEnv htaccessWorking true
-
-
-
-RewriteEngine on
-RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
-RewriteRule ^\.well-known/host-meta /public.php?service=host-meta [QSA,L]
-RewriteRule ^\.well-known/host-meta\.json /public.php?service=host-meta-json [QSA,L]
-RewriteRule ^\.well-known/webfinger /public.php?service=webfinger [QSA,L]
-RewriteRule ^\.well-known/carddav /remote.php/carddav/ [R=301,L]
-RewriteRule ^\.well-known/caldav /remote.php/caldav/ [R=301,L]
-RewriteRule ^apps/calendar/caldav\.php remote.php/caldav/ [QSA,L]
-RewriteRule ^apps/contacts/carddav\.php remote.php/carddav/ [QSA,L]
-RewriteRule ^remote/(.*) remote.php [QSA,L]
-RewriteRule ^(build|tests|config|lib|3rdparty|templates)/.* - [R=404,L]
-RewriteRule ^(\.|autotest|occ|issue|indie|db_|console).* - [R=404,L]
-
-
-AddType image/svg+xml svg svgz
-AddEncoding gzip svgz
-
-
-DirectoryIndex index.php index.html
-
-AddDefaultCharset utf-8
-Options -Indexes
-
- ModPagespeed Off
-
--- a/tests/data/setUploadLimit/user.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-upload_max_filesize=513M
-post_max_size=513M
-memory_limit=512M
-default_charset='UTF-8'
-output_buffering=off
--- a/tests/data/svg/files-app-red.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
--- a/tests/data/svg/mixed-red.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
--- a/tests/data/svg/mixed-source.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
--- a/tests/data/svg/rect-black.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
--- a/tests/data/svg/rect-red.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
--- a/tests/data/svg/settings-admin-red.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
--- a/tests/data/testimage.eps
+++ /dev/null
@@ -1,175 +0,0 @@
-%!PS-Adobe-3.0 EPSF-3.0
-%%Creator: cairo 1.14.1 (http://cairographics.org)
-%%CreationDate: Fri May 15 16:27:38 2015
-%%Pages: 1
-%%DocumentData: Clean7Bit
-%%LanguageLevel: 2
-%%BoundingBox: 0 -1 2400 1706
-%%EndComments
-%%BeginProlog
-save
-50 dict begin
-/q { gsave } bind def
-/Q { grestore } bind def
-/cm { 6 array astore concat } bind def
-/w { setlinewidth } bind def
-/J { setlinecap } bind def
-/j { setlinejoin } bind def
-/M { setmiterlimit } bind def
-/d { setdash } bind def
-/m { moveto } bind def
-/l { lineto } bind def
-/c { curveto } bind def
-/h { closepath } bind def
-/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto
- 0 exch rlineto 0 rlineto closepath } bind def
-/S { stroke } bind def
-/f { fill } bind def
-/f* { eofill } bind def
-/n { newpath } bind def
-/W { clip } bind def
-/W* { eoclip } bind def
-/BT { } bind def
-/ET { } bind def
-/pdfmark where { pop globaldict /?pdfmark /exec load put }
- { globaldict begin /?pdfmark /pop load def /pdfmark
- /cleartomark load def end } ifelse
-/BDC { mark 3 1 roll /BDC pdfmark } bind def
-/EMC { mark /EMC pdfmark } bind def
-/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def
-/Tj { show currentpoint cairo_store_point } bind def
-/TJ {
- {
- dup
- type /stringtype eq
- { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse
- } forall
- currentpoint cairo_store_point
-} bind def
-/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore
- cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def
-/Tf { pop /cairo_font exch def /cairo_font_matrix where
- { pop cairo_selectfont } if } bind def
-/Td { matrix translate cairo_font_matrix matrix concatmatrix dup
- /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point
- /cairo_font where { pop cairo_selectfont } if } bind def
-/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def
- cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def
-/g { setgray } bind def
-/rg { setrgbcolor } bind def
-/d1 { setcachedevice } bind def
-%%EndProlog
-%%BeginSetup
-%%EndSetup
-%%Page: 1 1
-%%BeginPageSetup
-%%PageBoundingBox: 0 -1 2400 1706
-%%EndPageSetup
-q 0 -1 2400 1707 rectclip q
-0.113725 0.176471 0.266667 rg
-0 1705.263 2400 -1705.262 re f
-1 g
-1348.539 1275.525 m 1269.973 1275.525 1206.5 1212.052 1206.5 1133.486 c
- 1206.5 1101.099 1217.285 1071.294 1235.465 1047.427 c 1274.906 1093.076
- 1333.117 1122.064 1398.113 1122.064 c 1429.914 1122.064 1460.066 1114.986
- 1487.238 1102.572 c 1489.438 1112.517 1490.578 1122.865 1490.578 1133.486
- c 1490.578 1212.052 1427.105 1275.525 1348.539 1275.525 c h
-1163.055 1209.794 m 1122.137 1209.794 1089.246 1176.626 1089.246 1135.712
- c 1089.246 1122.466 1092.664 1109.951 1098.719 1099.228 c 1123.406 1113.158
- 1151.949 1121.232 1182.27 1121.232 c 1185.195 1121.232 1188.016 1121.111
- 1190.902 1120.951 c 1190.578 1125.099 1190.348 1129.255 1190.348 1133.486
- c 1190.348 1156.275 1195.297 1177.955 1203.992 1197.541 c 1192.285 1205.396
- 1178.254 1209.794 1163.055 1209.794 c h
-1513.418 1158.83 m 1510.395 1158.83 1507.48 1158.458 1504.504 1158.271
-c 1505.793 1150.154 1506.734 1141.958 1506.734 1133.486 c 1506.734 1120.294
- 1505.055 1107.568 1501.996 1095.33 c 1537.844 1075.494 1567.609 1045.81
- 1587.223 1009.826 c 1607.563 1020.416 1630.258 1027.15 1654.344 1028.767
- c 1648.137 1101.56 1587.828 1158.83 1513.418 1158.83 c h
-1398.113 1105.912 m 1288.176 1105.912 1199.258 1017.001 1199.258 907.056
- c 1199.258 797.123 1288.172 708.201 1398.113 708.201 c 1508.055 708.201
- 1596.969 797.126 1596.969 907.056 c 1596.969 1017.001 1508.051 1105.912
- 1398.113 1105.912 c h
-1182.27 1105.076 m 1096.98 1105.076 1027.977 1036.072 1027.977 950.783
-c 1027.977 900.576 1051.898 856.126 1088.969 827.962 c 1104.598 858.103
-1135.988 878.65 1172.242 878.65 c 1176.625 878.65 1180.832 878.119 1185.055
- 877.537 c 1183.73 887.181 1183.105 897.048 1183.105 907.056 c 1183.105
-954.908 1198.68 999.154 1225.16 1034.892 c 1209.309 1054.728 1197.965 1078.556
- 1193.133 1104.521 c 1189.547 1104.767 1185.918 1105.076 1182.27 1105.076
- c h
-1665.762 1013.169 m 1639.863 1013.169 1615.602 1006.556 1594.184 995.345
- c 1606.348 968.404 1613.121 938.513 1613.121 907.056 c 1613.121 848.158
- 1589.441 794.677 1551.016 755.826 c 1579.246 724.486 1620.203 704.861 1665.762
- 704.861 c 1751.051 704.861 1820.055 773.865 1820.055 859.154 c 1820.055
- 944.443 1751.051 1013.169 1665.762 1013.169 c h
-1012.938 992.837 m 934.367 992.837 870.617 929.646 870.617 851.076 c 870.617
- 772.509 934.367 708.759 1012.938 708.759 c 1042.84 708.759 1070.543 718.076
- 1093.426 733.826 c 1083.969 748.517 1078.387 766.083 1078.387 784.791 c
- 1078.387 794.501 1079.82 803.83 1082.563 812.642 c 1039.723 843.603 1011.824
- 893.986 1011.824 950.783 c 1011.824 965.212 1013.723 979.169 1017.113 992.56
- c 1015.707 992.599 1014.355 992.837 1012.938 992.837 c h
-1848.184 870.849 m 1843.992 870.849 1839.938 870.373 1835.93 869.736 c
-1836.152 866.193 1836.207 862.751 1836.207 859.154 c 1836.207 813.853 1818.332
- 772.736 1789.418 742.181 c 1803.645 725.638 1824.539 715.166 1848.184 715.166
- c 1891.254 715.166 1926.168 749.798 1926.168 792.869 c 1926.168 835.939
- 1891.254 870.849 1848.184 870.849 c h
-1172.242 862.498 m 1129.176 862.498 1094.539 827.861 1094.539 784.791 c
- 1094.539 741.724 1129.176 706.81 1172.242 706.81 c 1205.258 706.81 1233.277
- 727.408 1244.656 756.384 c 1216.891 784.662 1196.938 820.74 1188.117 860.826
- c 1182.961 861.892 1177.723 862.498 1172.242 862.498 c h
-2026.43 797.044 m 2026.43 643.029 l 1944.828 643.029 l 1891.484 643.029
- 1848.184 599.455 1848.184 546.111 c 1848.184 492.775 1891.484 449.466 1944.828
- 449.466 c 1976.578 449.466 l 1992.227 449.466 2007.852 457.068 2020.305
- 468.685 c 2032.754 480.302 2042.254 496.392 2042.582 514.361 c 2043.145
- 545.197 2042.582 634.955 2042.582 634.955 c 2042.582 797.044 l h
-1280.305 727.419 m 1280.305 514.361 l 1280.305 478.552 1309.668 449.466
- 1345.477 449.466 c 1345.477 465.623 l 1318.383 465.623 1296.457 487.263
- 1296.457 514.361 c 1296.457 717.392 l 1290.836 720.416 1285.617 723.923
- 1280.305 727.419 c h
-1118.77 707.646 m 1090.16 680.345 1072.258 641.908 1072.258 599.306 c 1072.258
- 516.642 1139.438 449.466 1222.098 449.466 c 1222.098 465.623 l 1148.152
- 465.623 1088.414 525.357 1088.414 599.306 c 1088.414 639.076 1105.773 674.822
- 1133.254 699.291 c 1128.117 701.654 1123.359 704.427 1118.77 707.646 c
-h
-928.547 643.587 m 875.203 643.587 831.906 600.291 831.906 546.947 c 831.906
- 457.544 l 848.059 457.544 l 848.059 546.947 l 848.059 591.572 883.922 627.712
- 928.547 627.712 c 973.184 627.712 1009.316 591.572 1009.316 546.947 c 1009.316
- 457.544 l 1025.469 457.544 l 1025.469 546.947 l 1025.469 600.291 981.898
- 643.587 928.547 643.587 c h
-1470.805 643.029 m 1417.457 643.029 1374.164 599.455 1374.164 546.111 c
- 1374.164 492.763 1417.457 449.189 1470.805 449.189 c 1524.152 449.189 1567.727
- 492.763 1567.727 546.111 c 1567.727 599.455 1524.152 643.029 1470.805 643.029
- c h
-392.418 642.751 m 339.078 642.751 295.496 599.451 295.496 546.111 c 295.496
- 492.767 339.078 449.466 392.418 449.466 c 445.762 449.466 489.063 492.767
- 489.063 546.111 c 489.063 599.451 445.762 642.751 392.418 642.751 c h
-537.523 635.513 m 537.523 514.361 l 537.523 478.552 566.613 449.466 602.414
- 449.466 c 626.816 449.466 648.344 462.958 659.508 482.888 c 670.59 462.958
- 691.93 449.466 716.324 449.466 c 752.133 449.466 781.496 478.552 781.496
- 514.361 c 781.496 635.513 l 765.344 635.513 l 765.344 514.361 l 765.344
- 487.267 743.418 465.623 716.324 465.623 c 689.23 465.623 667.586 487.267
- 667.586 514.361 c 667.586 635.513 l 651.434 635.513 l 651.434 514.361 l
- 651.434 487.267 629.52 465.623 602.414 465.623 c 575.328 465.623 553.676
- 487.267 553.676 514.361 c 553.676 635.513 l h
-1610.059 635.513 m 1610.059 546.111 l 1610.059 492.775 1653.359 449.466
- 1706.703 449.466 c 1760.047 449.466 1803.621 492.775 1803.621 546.111 c
- 1803.621 635.513 l 1787.469 635.513 l 1787.469 546.111 l 1787.469 501.494
- 1751.328 465.623 1706.703 465.623 c 1662.074 465.623 1626.215 501.49 1626.215
- 546.111 c 1626.215 635.513 l h
-1470.805 626.876 m 1515.438 626.876 1551.574 590.74 1551.574 546.111 c
-1551.574 501.478 1515.438 465.341 1470.805 465.341 c 1426.176 465.341 1390.316
- 501.478 1390.316 546.111 c 1390.316 590.74 1426.176 626.876 1470.805 626.876
- c h
-1944.828 626.876 m 2026.43 626.876 l 2026.492 616.205 2026.938 542.685
-2026.43 514.638 c 2026.203 502.158 2019.355 489.634 2009.441 480.38 c 1999.527
- 471.13 1986.695 465.623 1976.578 465.623 c 1944.828 465.623 l 1900.199
-465.623 1864.34 501.49 1864.34 546.111 c 1864.34 590.74 1900.199 626.876
- 1944.828 626.876 c h
-392.418 626.599 m 437.047 626.599 472.906 590.736 472.906 546.111 c 472.906
- 501.482 437.047 465.623 392.418 465.623 c 347.793 465.623 311.652 501.482
- 311.652 546.111 c 311.652 590.736 347.793 626.599 392.418 626.599 c h
-392.418 626.599 m f
-Q Q
-showpage
-%%Trailer
-end restore
-%%EOF
--- a/tests/data/testimagelarge.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
--- a/tests/databases-all-config.php
+++ /dev/null
@@ -1,16 +0,0 @@
- [
- 'sqlite',
- 'mysql',
- 'pgsql',
- 'oci',
- ],
-];
--- a/tests/docker/mariadb/oc.cnf
+++ /dev/null
@@ -1,5 +0,0 @@
-
-[mysqld]
-
-innodb_buffer_pool_size = 512M
-innodb_flush_log_at_trx_commit = 2
--- a/tests/docker/mysqlmb4.config.php
+++ /dev/null
@@ -1,11 +0,0 @@
- true,
-];
--- a/tests/docker/mysqlmb4/mb4.cnf
+++ /dev/null
@@ -1,8 +0,0 @@
-
-[mysqld]
-innodb_large_prefix=true
-innodb_file_format=barracuda
-innodb_file_per_table=true
-
-innodb_buffer_pool_size = 512M
-innodb_flush_log_at_trx_commit = 2
--- a/tests/enable_all.php
+++ /dev/null
@@ -1,31 +0,0 @@
-installApp($app);
- $appManager->enableApp($app);
- echo "Enabled application {$app}\n";
-}
-
-foreach (new \DirectoryIterator(__DIR__ . '/../apps/') as $file) {
- if ($file->isDot()) {
- continue;
- }
- if (!file_exists($file->getPathname() . '/.git')) {
- enableApp($file->getFilename());
- }
-}
--- a/tests/http-analyzer.php
+++ /dev/null
@@ -1,80 +0,0 @@
- $requests */
-$requests = [];
-foreach (file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [] as $line) {
- $request = json_decode($line, true);
- if (is_array($request)) {
- $requests[] = $request;
- }
-}
-
-if ($requests === []) {
- echo "No outgoing HTTP requests were recorded.\n";
- exit(0);
-}
-
-$totalDuration = array_sum(array_column($requests, 'duration'));
-printf("%d requests, %.1fs total\n", count($requests), $totalDuration);
-
-/** @param callable(array): string $key */
-function group(array $requests, callable $key): array {
- $groups = [];
- foreach ($requests as $request) {
- $name = $key($request);
- $groups[$name] ??= ['duration' => 0.0, 'requests' => 0];
- $groups[$name]['duration'] += $request['duration'];
- $groups[$name]['requests']++;
- }
- uasort($groups, static fn (array $a, array $b): int => $b['duration'] <=> $a['duration']);
- return $groups;
-}
-
-foreach ([
- 'host' => static fn (array $r): string => parse_url($r['uri'], PHP_URL_HOST) ?: '(unparsed)',
- 'test' => static fn (array $r): string => $r['test'],
-] as $label => $key) {
- $groups = group($requests, $key);
- printf("\nRequests by %s\n", $label);
- printf(" %9s %9s %s\n", 'sum', 'requests', $label);
- foreach (array_slice($groups, 0, $topCount, true) as $name => $stats) {
- printf(" %8.2fs %9d %s\n", $stats['duration'], $stats['requests'], $name);
- }
-}
-
-usort($requests, static fn (array $a, array $b): int => $b['duration'] <=> $a['duration']);
-printf("\nTop %d slowest requests\n", $topCount);
-foreach (array_slice($requests, 0, $topCount) as $request) {
- printf(
- " %8.2fs %-6s %-4s %s\n %s\n",
- $request['duration'],
- $request['method'],
- $request['outcome'],
- $request['uri'],
- $request['test'],
- );
-}
--- a/tests/junit-analyzer.php
+++ /dev/null
@@ -1,139 +0,0 @@
-open($file)) {
- fwrite(STDERR, "cannot open $file\n");
- exit(1);
-}
-
-/** @var list $tests in execution order */
-$tests = [];
-while ($reader->read()) {
- if ($reader->nodeType !== XMLReader::ELEMENT || $reader->name !== 'testcase') {
- continue;
- }
- $tests[] = [
- 'class' => $reader->getAttribute('class') ?: '(none)',
- 'name' => (string)$reader->getAttribute('name'),
- 'duration' => (float)$reader->getAttribute('time'),
- ];
-}
-$reader->close();
-
-$testCount = count($tests);
-if ($testCount === 0) {
- $error = libxml_get_errors()[0] ?? null;
- fwrite(STDERR, "no testcase elements found in $file"
- . ($error !== null ? ': ' . trim($error->message) : '') . "\n");
- exit(1);
-}
-
-$totalDuration = array_sum(array_column($tests, 'duration'));
-printf("%d tests, %.1fs total (%.1f min)\n\n", $testCount, $totalDuration, $totalDuration / 60);
-
-if ($totalDuration <= 0) {
- fwrite(STDERR, "no timing data to rank\n");
- exit(0);
-}
-
-$chunks = array_chunk($tests, (int)ceil($testCount / BUCKETS));
-$buckets = count($chunks);
-
-printf("Execution order, %d buckets (are later tests slower?)\n", $buckets);
-echo " bucket tests sum(s) mean(ms) median(ms) max(s) cum%\n";
-
-$durationSoFar = 0.0;
-foreach ($chunks as $bucket => $chunk) {
- $durations = array_column($chunk, 'duration');
- sort($durations);
- $inBucket = count($durations);
- $bucketDuration = array_sum($durations);
- $durationSoFar += $bucketDuration;
-
- printf(
- " %3d-%3d%% %7d %9.1f %10.2f %12.2f %9.2f %5.1f%%\n",
- $bucket * 100 / $buckets,
- ($bucket + 1) * 100 / $buckets,
- $inBucket,
- $bucketDuration,
- $bucketDuration / $inBucket * 1000,
- $durations[intdiv($inBucket, 2)] * 1000,
- max($durations),
- $durationSoFar / $totalDuration * 100,
- );
-}
-
-$slowestFirst = $tests;
-usort($slowestFirst, static fn (array $a, array $b): int => $b['duration'] <=> $a['duration']);
-
-printf("\nTop %d slowest tests\n", $topCount);
-foreach (array_slice($slowestFirst, 0, $topCount) as $test) {
- printf(" %8.2fs %s::%s\n", $test['duration'], $test['class'], $test['name']);
-}
-
-/** @var array $classes */
-$classes = [];
-foreach ($tests as $test) {
- $classes[$test['class']] ??= ['duration' => 0.0, 'tests' => 0];
- $classes[$test['class']]['duration'] += $test['duration'];
- $classes[$test['class']]['tests']++;
-}
-uasort($classes, static fn (array $a, array $b): int => $b['duration'] <=> $a['duration']);
-
-printf("\nTop %d slowest classes (sum of its tests)\n", $topCount);
-printf(" %9s %7s %10s %s\n", 'sum', 'tests', 'mean(ms)', 'class');
-foreach (array_slice($classes, 0, $topCount, true) as $class => $stats) {
- printf(
- " %8.2fs %7d %10.2f %s\n",
- $stats['duration'],
- $stats['tests'],
- $stats['duration'] / $stats['tests'] * 1000,
- $class,
- );
-}
-
-// How top-heavy is the run? A handful of tests dominating reads very differently
-// from the cost being spread evenly.
-$durationSoFar = 0.0;
-$testsInHalfTheRuntime = 0;
-foreach ($slowestFirst as $test) {
- $durationSoFar += $test['duration'];
- $testsInHalfTheRuntime++;
- if ($durationSoFar >= $totalDuration / 2) {
- break;
- }
-}
-printf(
- "\nThe slowest %d tests (%.1f%% of tests) account for 50%% of the runtime.\n",
- $testsInHalfTheRuntime,
- $testsInHalfTheRuntime / $testCount * 100,
-);
--- a/tests/kvstore-cluster.config.php
+++ /dev/null
@@ -1,23 +0,0 @@
- '\\OC\\Memcache\\KeyValueCache',
- 'memcache.distributed' => '\\OC\\Memcache\\KeyValueCache',
- 'memcache.locking' => '\\OC\\Memcache\\KeyValueCache',
- 'memcache.kvstore' => [
- 'seeds' => [
- ['host' => '127.0.0.1', 'port' => 7000],
- ['host' => '127.0.0.1', 'port' => 7001],
- ['host' => '127.0.0.1', 'port' => 7002],
- ['host' => '127.0.0.1', 'port' => 7003],
- ['host' => '127.0.0.1', 'port' => 7004],
- ['host' => '127.0.0.1', 'port' => 7005],
- ],
- ],
-];
--- a/tests/kvstore-sentinel.config.php
+++ /dev/null
@@ -1,21 +0,0 @@
- '\\OC\\Memcache\\KeyValueCache',
- 'memcache.distributed' => '\\OC\\Memcache\\KeyValueCache',
- 'memcache.locking' => '\\OC\\Memcache\\KeyValueCache',
- 'memcache.kvstore' => [
- 'sentinel' => [
- 'service' => 'mymaster',
- 'seeds' => [
- ['host' => '127.0.0.1', 'port' => 26379],
- ],
- ],
- ],
-];
--- a/tests/kvstore-single.config.php
+++ /dev/null
@@ -1,20 +0,0 @@
- '\\OC\\Memcache\\KeyValueCache',
- 'memcache.distributed' => '\\OC\\Memcache\\KeyValueCache',
- 'memcache.locking' => '\\OC\\Memcache\\KeyValueCache',
- 'memcache.kvstore' => [
- 'server' => [
- 'host' => 'localhost',
- 'port' => 6379,
- 'protocol' => 'tcp',
- ],
- ],
-];
--- a/tests/lib/Accounts/AccountManagerTest.php
+++ /dev/null
@@ -1,1065 +0,0 @@
-connection = Server::get(IDBConnection::class);
- $this->phoneNumberUtil = new PhoneNumberUtil();
-
- $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
- $this->config = $this->createMock(IConfig::class);
- $this->jobList = $this->createMock(IJobList::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->verificationToken = $this->createMock(IVerificationToken::class);
- $this->mailer = $this->createMock(IMailer::class);
- $this->defaults = $this->createMock(Defaults::class);
- $this->l10nFactory = $this->createMock(IFactory::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->crypto = $this->createMock(ICrypto::class);
- $this->clientService = $this->createMock(IClientService::class);
-
- $this->accountManager = new AccountManager(
- $this->connection,
- $this->config,
- $this->eventDispatcher,
- $this->jobList,
- $this->logger,
- $this->verificationToken,
- $this->mailer,
- $this->defaults,
- $this->l10nFactory,
- $this->urlGenerator,
- $this->crypto,
- $this->phoneNumberUtil,
- $this->clientService,
- );
- }
-
- #[\Override]
- protected function tearDown(): void {
- parent::tearDown();
- $query = $this->connection->getQueryBuilder();
- $query->delete($this->table)->executeStatement();
- }
-
- protected function makeUser(string $uid, string $name, ?string $email = null): IUser {
- $user = $this->createMock(IUser::class);
- $user->expects($this->any())
- ->method('getUid')
- ->willReturn($uid);
- $user->expects($this->any())
- ->method('getDisplayName')
- ->willReturn($name);
- if ($email !== null) {
- $user->expects($this->any())
- ->method('getEMailAddress')
- ->willReturn($email);
- }
-
- return $user;
- }
-
- protected function populateOrUpdate(): void {
- $users = [
- [
- 'user' => $this->makeUser('j.doe', 'Jane Doe', 'jane.doe@acme.com'),
- 'data' => [
- [
- 'name' => IAccountManager::PROPERTY_DISPLAYNAME,
- 'value' => 'Jane Doe',
- 'scope' => IAccountManager::SCOPE_PUBLISHED
- ],
- [
- 'name' => IAccountManager::PROPERTY_EMAIL,
- 'value' => 'jane.doe@acme.com',
- 'scope' => IAccountManager::SCOPE_LOCAL
- ],
- [
- 'name' => IAccountManager::PROPERTY_TWITTER,
- 'value' => '@sometwitter',
- 'scope' => IAccountManager::SCOPE_PUBLISHED
- ],
- [
- 'name' => IAccountManager::PROPERTY_FEDIVERSE,
- 'value' => '@someMastodon@mastodon.social',
- 'scope' => IAccountManager::SCOPE_PUBLISHED
- ],
- [
- 'name' => IAccountManager::PROPERTY_PHONE,
- 'value' => '+491601231212',
- 'scope' => IAccountManager::SCOPE_FEDERATED
- ],
- [
- 'name' => IAccountManager::PROPERTY_ADDRESS,
- 'value' => 'some street',
- 'scope' => IAccountManager::SCOPE_LOCAL
- ],
- [
- 'name' => IAccountManager::PROPERTY_WEBSITE,
- 'value' => 'https://acme.com',
- 'scope' => IAccountManager::SCOPE_PRIVATE
- ],
- [
- 'name' => IAccountManager::PROPERTY_ORGANISATION,
- 'value' => 'Some organisation',
- 'scope' => IAccountManager::SCOPE_LOCAL
- ],
- [
- 'name' => IAccountManager::PROPERTY_ROLE,
- 'value' => 'Human',
- 'scope' => IAccountManager::SCOPE_LOCAL
- ],
- [
- 'name' => IAccountManager::PROPERTY_HEADLINE,
- 'value' => 'Hi',
- 'scope' => IAccountManager::SCOPE_LOCAL
- ],
- [
- 'name' => IAccountManager::PROPERTY_BIOGRAPHY,
- 'value' => 'Biography',
- 'scope' => IAccountManager::SCOPE_LOCAL
- ],
- ],
- ],
- [
- 'user' => $this->makeUser('a.allison', 'Alice Allison', 'a.allison@example.org'),
- 'data' => [
- [
- 'name' => IAccountManager::PROPERTY_DISPLAYNAME,
- 'value' => 'Alice Allison',
- 'scope' => IAccountManager::SCOPE_LOCAL
- ],
- [
- 'name' => IAccountManager::PROPERTY_EMAIL,
- 'value' => 'a.allison@example.org',
- 'scope' => IAccountManager::SCOPE_LOCAL
- ],
- [
- 'name' => IAccountManager::PROPERTY_TWITTER,
- 'value' => '@a_alice',
- 'scope' => IAccountManager::SCOPE_FEDERATED
- ],
- [
- 'name' => IAccountManager::PROPERTY_FEDIVERSE,
- 'value' => '@a_alice@cool.social',
- 'scope' => IAccountManager::SCOPE_FEDERATED
- ],
- [
- 'name' => IAccountManager::PROPERTY_PHONE,
- 'value' => '+491602312121',
- 'scope' => IAccountManager::SCOPE_LOCAL
- ],
- [
- 'name' => IAccountManager::PROPERTY_ADDRESS,
- 'value' => 'Dundee Road 45',
- 'scope' => IAccountManager::SCOPE_LOCAL
- ],
- [
- 'name' => IAccountManager::PROPERTY_WEBSITE,
- 'value' => 'https://example.org',
- 'scope' => IAccountManager::SCOPE_LOCAL
- ],
- [
- 'name' => IAccountManager::PROPERTY_ORGANISATION,
- 'value' => 'Another organisation',
- 'scope' => IAccountManager::SCOPE_FEDERATED
- ],
- [
- 'name' => IAccountManager::PROPERTY_ROLE,
- 'value' => 'Alien',
- 'scope' => IAccountManager::SCOPE_FEDERATED
- ],
- [
- 'name' => IAccountManager::PROPERTY_HEADLINE,
- 'value' => 'Hello',
- 'scope' => IAccountManager::SCOPE_FEDERATED
- ],
- [
- 'name' => IAccountManager::PROPERTY_BIOGRAPHY,
- 'value' => 'Different biography',
- 'scope' => IAccountManager::SCOPE_FEDERATED
- ],
- ],
- ],
- [
- 'user' => $this->makeUser('b32c5a5b-1084-4380-8856-e5223b16de9f', 'Armel Oliseh', 'oliseh@example.com'),
- 'data' => [
- [
- 'name' => IAccountManager::PROPERTY_DISPLAYNAME,
- 'value' => 'Armel Oliseh',
- 'scope' => IAccountManager::SCOPE_PUBLISHED
- ],
- [
- 'name' => IAccountManager::PROPERTY_EMAIL,
- 'value' => 'oliseh@example.com',
- 'scope' => IAccountManager::SCOPE_PUBLISHED
- ],
- [
- 'name' => IAccountManager::PROPERTY_TWITTER,
- 'value' => '',
- 'scope' => IAccountManager::SCOPE_LOCAL
- ],
- [
- 'name' => IAccountManager::PROPERTY_FEDIVERSE,
- 'value' => '',
- 'scope' => IAccountManager::SCOPE_LOCAL
- ],
- [
- 'name' => IAccountManager::PROPERTY_PHONE,
- 'value' => '+491603121212',
- 'scope' => IAccountManager::SCOPE_PUBLISHED
- ],
- [
- 'name' => IAccountManager::PROPERTY_ADDRESS,
- 'value' => 'Sunflower Blvd. 77',
- 'scope' => IAccountManager::SCOPE_PUBLISHED
- ],
- [
- 'name' => IAccountManager::PROPERTY_WEBSITE,
- 'value' => 'https://example.com',
- 'scope' => IAccountManager::SCOPE_PUBLISHED
- ],
- [
- 'name' => IAccountManager::PROPERTY_ORGANISATION,
- 'value' => 'Yet another organisation',
- 'scope' => IAccountManager::SCOPE_PUBLISHED
- ],
- [
- 'name' => IAccountManager::PROPERTY_ROLE,
- 'value' => 'Being',
- 'scope' => IAccountManager::SCOPE_PUBLISHED
- ],
- [
- 'name' => IAccountManager::PROPERTY_HEADLINE,
- 'value' => 'This is a headline',
- 'scope' => IAccountManager::SCOPE_PUBLISHED
- ],
- [
- 'name' => IAccountManager::PROPERTY_BIOGRAPHY,
- 'value' => 'Some long biography',
- 'scope' => IAccountManager::SCOPE_PUBLISHED
- ],
- ],
- ],
- [
- 'user' => $this->makeUser('31b5316a-9b57-4b17-970a-315a4cbe73eb', 'K. Cheng', 'cheng@emca.com'),
- 'data' => [
- [
- 'name' => IAccountManager::PROPERTY_DISPLAYNAME,
- 'value' => 'K. Cheng',
- 'scope' => IAccountManager::SCOPE_FEDERATED
- ],
- [
- 'name' => IAccountManager::PROPERTY_EMAIL,
- 'value' => 'cheng@emca.com',
- 'scope' => IAccountManager::SCOPE_FEDERATED
- ],
- [
- 'name' => IAccountManager::PROPERTY_TWITTER,
- 'value' => '', '
- scope' => IAccountManager::SCOPE_LOCAL
- ],
- [
- 'name' => IAccountManager::PROPERTY_FEDIVERSE,
- 'value' => '', '
- scope' => IAccountManager::SCOPE_LOCAL
- ],
- [
- 'name' => IAccountManager::PROPERTY_PHONE,
- 'value' => '+71601212123',
- 'scope' => IAccountManager::SCOPE_LOCAL
- ],
- [
- 'name' => IAccountManager::PROPERTY_ADDRESS,
- 'value' => 'Pinapple Street 22',
- 'scope' => IAccountManager::SCOPE_LOCAL
- ],
- [
- 'name' => IAccountManager::PROPERTY_WEBSITE,
- 'value' => 'https://emca.com',
- 'scope' => IAccountManager::SCOPE_FEDERATED
- ],
- [
- 'name' => IAccountManager::PROPERTY_ORGANISATION,
- 'value' => 'Organisation A',
- 'scope' => IAccountManager::SCOPE_LOCAL
- ],
- [
- 'name' => IAccountManager::PROPERTY_ROLE,
- 'value' => 'Animal',
- 'scope' => IAccountManager::SCOPE_LOCAL
- ],
- [
- 'name' => IAccountManager::PROPERTY_HEADLINE,
- 'value' => 'My headline',
- 'scope' => IAccountManager::SCOPE_LOCAL
- ],
- [
- 'name' => IAccountManager::PROPERTY_BIOGRAPHY,
- 'value' => 'Short biography',
- 'scope' => IAccountManager::SCOPE_LOCAL
- ],
- [
- 'name' => IAccountManager::COLLECTION_EMAIL,
- 'value' => 'k.cheng@emca.com',
- 'scope' => IAccountManager::SCOPE_LOCAL
- ],
- [
- 'name' => IAccountManager::COLLECTION_EMAIL,
- 'value' => 'kai.cheng@emca.com',
- 'scope' => IAccountManager::SCOPE_LOCAL
- ],
- ],
- ],
- [
- 'user' => $this->makeUser('goodpal@elpmaxe.org', 'Goodpal, Kim', 'goodpal@elpmaxe.org'),
- 'data' => [
- [
- 'name' => IAccountManager::PROPERTY_DISPLAYNAME,
- 'value' => 'Goodpal, Kim',
- 'scope' => IAccountManager::SCOPE_PUBLISHED
- ],
- [
- 'name' => IAccountManager::PROPERTY_EMAIL,
- 'value' => 'goodpal@elpmaxe.org',
- 'scope' => IAccountManager::SCOPE_PUBLISHED
- ],
- [
- 'name' => IAccountManager::PROPERTY_TWITTER,
- 'value' => '',
- 'scope' => IAccountManager::SCOPE_LOCAL
- ],
- [
- 'name' => IAccountManager::PROPERTY_FEDIVERSE,
- 'value' => '',
- 'scope' => IAccountManager::SCOPE_LOCAL
- ],
- [
- 'name' => IAccountManager::PROPERTY_PHONE,
- 'value' => '+71602121231',
- 'scope' => IAccountManager::SCOPE_FEDERATED
- ],
- [
- 'name' => IAccountManager::PROPERTY_ADDRESS,
- 'value' => 'Octopus Ave 17',
- 'scope' => IAccountManager::SCOPE_FEDERATED
- ],
- [
- 'name' => IAccountManager::PROPERTY_WEBSITE,
- 'value' => 'https://elpmaxe.org',
- 'scope' => IAccountManager::SCOPE_PUBLISHED
- ],
- [
- 'name' => IAccountManager::PROPERTY_ORGANISATION,
- 'value' => 'Organisation B',
- 'scope' => IAccountManager::SCOPE_FEDERATED
- ],
- [
- 'name' => IAccountManager::PROPERTY_ROLE,
- 'value' => 'Organism',
- 'scope' => IAccountManager::SCOPE_FEDERATED
- ],
- [
- 'name' => IAccountManager::PROPERTY_HEADLINE,
- 'value' => 'Best headline',
- 'scope' => IAccountManager::SCOPE_FEDERATED
- ],
- [
- 'name' => IAccountManager::PROPERTY_BIOGRAPHY,
- 'value' => 'Autobiography',
- 'scope' => IAccountManager::SCOPE_FEDERATED
- ],
- ],
- ],
- ];
- $this->config->expects($this->exactly(count($users)))->method('getSystemValue')->with('account_manager.default_property_scope', [])->willReturn([]);
- foreach ($users as $userInfo) {
- $this->invokePrivate($this->accountManager, 'updateUser', [$userInfo['user'], $userInfo['data'], null, false]);
- }
- }
-
- /**
- * get a instance of the accountManager
- *
- * @return MockObject | AccountManager
- */
- public function getInstance(?array $mockedMethods = null) {
- return $this->getMockBuilder(AccountManager::class)
- ->setConstructorArgs([
- $this->connection,
- $this->config,
- $this->eventDispatcher,
- $this->jobList,
- $this->logger,
- $this->verificationToken,
- $this->mailer,
- $this->defaults,
- $this->l10nFactory,
- $this->urlGenerator,
- $this->crypto,
- $this->phoneNumberUtil,
- $this->clientService,
- ])
- ->onlyMethods($mockedMethods)
- ->getMock();
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTrueFalse')]
- public function testUpdateUser(array $newData, array $oldData, bool $insertNew, bool $updateExisting): void {
- $accountManager = $this->getInstance(['getUser', 'insertNewUser', 'updateExistingUser']);
- /** @var IUser $user */
- $user = $this->createMock(IUser::class);
-
- if ($updateExisting) {
- $accountManager->expects($this->once())->method('updateExistingUser')
- ->with($user, $newData);
- $accountManager->expects($this->never())->method('insertNewUser');
- }
- if ($insertNew) {
- $accountManager->expects($this->once())->method('insertNewUser')
- ->with($user, $newData);
- $accountManager->expects($this->never())->method('updateExistingUser');
- }
-
- if (!$insertNew && !$updateExisting) {
- $accountManager->expects($this->never())->method('updateExistingUser');
- $accountManager->expects($this->never())->method('insertNewUser');
- $this->eventDispatcher->expects($this->never())->method('dispatchTyped');
- } else {
- $this->eventDispatcher->expects($this->once())->method('dispatchTyped')
- ->willReturnCallback(
- function ($event) use ($user, $newData): void {
- $this->assertInstanceOf(UserUpdatedEvent::class, $event);
- $this->assertSame($user, $event->getUser());
- $this->assertSame($newData, $event->getData());
- }
- );
- }
-
- $this->invokePrivate($accountManager, 'updateUser', [$user, $newData, $oldData]);
- }
-
- public static function dataTrueFalse(): array {
- return [
- #$newData | $oldData | $insertNew | $updateExisting
- [['myProperty' => ['value' => 'newData']], ['myProperty' => ['value' => 'oldData']], false, true],
- [['myProperty' => ['value' => 'oldData']], ['myProperty' => ['value' => 'oldData']], false, false]
- ];
- }
-
- public function testAddMissingDefaults(): void {
- $user = $this->createMock(IUser::class);
-
- $this->config
- ->expects($this->once())
- ->method('getAppValue')
- ->with('settings', 'profile_enabled_by_default', '1')
- ->willReturn('1');
-
- $input = [
- [
- 'name' => IAccountManager::PROPERTY_DISPLAYNAME,
- 'value' => 'bob',
- 'verified' => IAccountManager::NOT_VERIFIED,
- ],
- [
- 'name' => IAccountManager::PROPERTY_EMAIL,
- 'value' => 'bob@bob.bob',
- ],
- ];
-
- $expected = [
- [
- 'name' => IAccountManager::PROPERTY_DISPLAYNAME,
- 'value' => 'bob',
- 'scope' => IAccountManager::SCOPE_FEDERATED,
- 'verified' => IAccountManager::NOT_VERIFIED,
- ],
-
- [
- 'name' => IAccountManager::PROPERTY_EMAIL,
- 'value' => 'bob@bob.bob',
- 'scope' => IAccountManager::SCOPE_FEDERATED,
- 'verified' => IAccountManager::NOT_VERIFIED,
- ],
-
- [
- 'name' => IAccountManager::PROPERTY_ADDRESS,
- 'value' => '',
- 'scope' => IAccountManager::SCOPE_LOCAL,
- 'verified' => IAccountManager::NOT_VERIFIED,
- ],
-
- [
- 'name' => IAccountManager::PROPERTY_WEBSITE,
- 'value' => '',
- 'scope' => IAccountManager::SCOPE_LOCAL,
- 'verified' => IAccountManager::NOT_VERIFIED,
- ],
-
- [
- 'name' => IAccountManager::PROPERTY_AVATAR,
- 'scope' => IAccountManager::SCOPE_FEDERATED
- ],
-
- [
- 'name' => IAccountManager::PROPERTY_PHONE,
- 'value' => '',
- 'scope' => IAccountManager::SCOPE_LOCAL,
- 'verified' => IAccountManager::NOT_VERIFIED,
- ],
-
- [
- 'name' => IAccountManager::PROPERTY_TWITTER,
- 'value' => '',
- 'scope' => IAccountManager::SCOPE_LOCAL,
- 'verified' => IAccountManager::NOT_VERIFIED,
- ],
-
- [
- 'name' => IAccountManager::PROPERTY_BLUESKY,
- 'value' => '',
- 'scope' => IAccountManager::SCOPE_LOCAL,
- 'verified' => IAccountManager::NOT_VERIFIED,
- ],
-
- [
- 'name' => IAccountManager::PROPERTY_FEDIVERSE,
- 'value' => '',
- 'scope' => IAccountManager::SCOPE_LOCAL,
- 'verified' => IAccountManager::NOT_VERIFIED,
- ],
-
- [
- 'name' => IAccountManager::PROPERTY_ORGANISATION,
- 'value' => '',
- 'scope' => IAccountManager::SCOPE_LOCAL,
- ],
-
- [
- 'name' => IAccountManager::PROPERTY_ROLE,
- 'value' => '',
- 'scope' => IAccountManager::SCOPE_LOCAL,
- ],
-
- [
- 'name' => IAccountManager::PROPERTY_HEADLINE,
- 'value' => '',
- 'scope' => IAccountManager::SCOPE_LOCAL,
- ],
-
- [
- 'name' => IAccountManager::PROPERTY_BIOGRAPHY,
- 'value' => '',
- 'scope' => IAccountManager::SCOPE_LOCAL,
- ],
-
- [
- 'name' => IAccountManager::PROPERTY_BIRTHDATE,
- 'value' => '',
- 'scope' => IAccountManager::SCOPE_LOCAL,
- ],
-
- [
- 'name' => IAccountManager::PROPERTY_PROFILE_ENABLED,
- 'value' => '1',
- ],
-
- [
- 'name' => IAccountManager::PROPERTY_PRONOUNS,
- 'value' => '',
- 'scope' => IAccountManager::SCOPE_FEDERATED,
- ],
- ];
- $this->config->expects($this->once())->method('getSystemValue')->with('account_manager.default_property_scope', [])->willReturn([]);
-
- $defaultUserRecord = $this->invokePrivate($this->accountManager, 'buildDefaultUserRecord', [$user]);
- $result = $this->invokePrivate($this->accountManager, 'addMissingDefaultValues', [$input, $defaultUserRecord]);
-
- $this->assertSame($expected, $result);
- }
-
- public function testGetAccount(): void {
- $accountManager = $this->getInstance(['getUser']);
- /** @var IUser $user */
- $user = $this->createMock(IUser::class);
-
- $data = [
- [
- 'value' => '@twitterhandle',
- 'scope' => IAccountManager::SCOPE_LOCAL,
- 'verified' => IAccountManager::NOT_VERIFIED,
- 'name' => IAccountManager::PROPERTY_TWITTER,
- ],
- [
- 'value' => '@mastohandle@mastodon.social',
- 'scope' => IAccountManager::SCOPE_LOCAL,
- 'verified' => IAccountManager::NOT_VERIFIED,
- 'name' => IAccountManager::PROPERTY_FEDIVERSE,
- ],
- [
- 'value' => 'test@example.com',
- 'scope' => IAccountManager::SCOPE_PUBLISHED,
- 'verified' => IAccountManager::VERIFICATION_IN_PROGRESS,
- 'name' => IAccountManager::PROPERTY_EMAIL,
- ],
- [
- 'value' => 'https://example.com',
- 'scope' => IAccountManager::SCOPE_FEDERATED,
- 'verified' => IAccountManager::VERIFIED,
- 'name' => IAccountManager::PROPERTY_WEBSITE,
- ],
- ];
- $expected = new Account($user);
- $expected->setProperty(IAccountManager::PROPERTY_TWITTER, '@twitterhandle', IAccountManager::SCOPE_LOCAL, IAccountManager::NOT_VERIFIED);
- $expected->setProperty(IAccountManager::PROPERTY_FEDIVERSE, '@mastohandle@mastodon.social', IAccountManager::SCOPE_LOCAL, IAccountManager::NOT_VERIFIED);
- $expected->setProperty(IAccountManager::PROPERTY_EMAIL, 'test@example.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::VERIFICATION_IN_PROGRESS);
- $expected->setProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::SCOPE_FEDERATED, IAccountManager::VERIFIED);
-
- $accountManager->expects($this->once())
- ->method('getUser')
- ->willReturn($data);
- $this->assertEquals($expected, $accountManager->getAccount($user));
- }
-
- public static function dataParsePhoneNumber(): array {
- return [
- ['0711 / 25 24 28-90', 'DE', '+4971125242890'],
- ['0711 / 25 24 28-90', '', null],
- ['+49 711 / 25 24 28-90', '', '+4971125242890'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataParsePhoneNumber')]
- public function testSanitizePhoneNumberOnUpdateAccount(string $phoneInput, string $defaultRegion, ?string $phoneNumber): void {
- $this->config->method('getSystemValueString')
- ->willReturn($defaultRegion);
-
- $user = $this->createMock(IUser::class);
- $account = new Account($user);
- $account->setProperty(IAccountManager::PROPERTY_PHONE, $phoneInput, IAccountManager::SCOPE_LOCAL, IAccountManager::NOT_VERIFIED);
- $manager = $this->getInstance(['getUser', 'updateUser']);
- $manager->method('getUser')
- ->with($user, false)
- ->willReturn([]);
- $manager->expects($phoneNumber === null ? self::never() : self::once())
- ->method('updateUser');
-
- if ($phoneNumber === null) {
- $this->expectException(\InvalidArgumentException::class);
- }
-
- $manager->updateAccount($account);
-
- if ($phoneNumber !== null) {
- self::assertEquals($phoneNumber, $account->getProperty(IAccountManager::PROPERTY_PHONE)->getValue());
- }
- }
-
- public static function dataSanitizeOnUpdate(): array {
- return [
- [IAccountManager::PROPERTY_WEBSITE, 'https://nextcloud.com', 'https://nextcloud.com'],
- [IAccountManager::PROPERTY_WEBSITE, 'http://nextcloud.com', 'http://nextcloud.com'],
- [IAccountManager::PROPERTY_WEBSITE, 'ftp://nextcloud.com', null],
- [IAccountManager::PROPERTY_WEBSITE, '//nextcloud.com/', null],
- [IAccountManager::PROPERTY_WEBSITE, 'https:///?query', null],
-
- [IAccountManager::PROPERTY_TWITTER, '@nextcloud', 'nextcloud'],
- [IAccountManager::PROPERTY_TWITTER, '_nextcloud', '_nextcloud'],
- [IAccountManager::PROPERTY_TWITTER, 'FooB4r', 'FooB4r'],
- [IAccountManager::PROPERTY_TWITTER, 'X', null],
- [IAccountManager::PROPERTY_TWITTER, 'next.cloud', null],
- [IAccountManager::PROPERTY_TWITTER, 'ab/cd.zip', null],
- [IAccountManager::PROPERTY_TWITTER, 'tooLongForTwitterAndX', null],
-
- [IAccountManager::PROPERTY_FEDIVERSE, 'nextcloud@mastodon.social', 'nextcloud@mastodon.social'],
- [IAccountManager::PROPERTY_FEDIVERSE, '@nextcloud@mastodon.xyz', 'nextcloud@mastodon.xyz'],
- [IAccountManager::PROPERTY_FEDIVERSE, 'l33t.h4x0r@sub.localhost.local', 'l33t.h4x0r@sub.localhost.local'],
- [IAccountManager::PROPERTY_FEDIVERSE, 'invalid/name@mastodon.social', null],
- [IAccountManager::PROPERTY_FEDIVERSE, 'name@evil.host/malware.exe', null],
- [IAccountManager::PROPERTY_FEDIVERSE, '@is-it-a-host-or-name', null],
- [IAccountManager::PROPERTY_FEDIVERSE, 'only-a-name', null],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSanitizeOnUpdate')]
- public function testSanitizingOnUpdateAccount(string $property, string $input, ?string $output): void {
-
- if ($property === IAccountManager::PROPERTY_FEDIVERSE) {
- // We do not test the server response here we do this in the `testSanitizingFediverseServer`
- $this->config
- ->method('getSystemValueBool')
- ->with('has_internet_connection', true)
- ->willReturn(false);
- }
-
- $user = $this->createMock(IUser::class);
-
- $account = new Account($user);
- $account->setProperty($property, $input, IAccountManager::SCOPE_LOCAL, IAccountManager::NOT_VERIFIED);
-
- $manager = $this->getInstance(['getUser', 'updateUser']);
- $manager->method('getUser')
- ->with($user, false)
- ->willReturn([]);
- $manager->expects($output === null ? self::never() : self::once())
- ->method('updateUser');
-
- if ($output === null) {
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage($property);
- }
-
- $manager->updateAccount($account);
-
- if ($output !== null) {
- self::assertEquals($output, $account->getProperty($property)->getValue());
- }
- }
-
- public static function dataSanitizeFediverseServer(): array {
- return [
- 'no internet' => [
- '@foo@example.com',
- 'foo@example.com',
- false,
- null,
- ],
- 'no internet - no at' => [
- 'foo@example.com',
- 'foo@example.com',
- false,
- null,
- ],
- 'valid response' => [
- '@foo@example.com',
- 'foo@example.com',
- true,
- json_encode([
- 'subject' => 'acct:foo@example.com',
- 'links' => [
- [
- 'rel' => 'self',
- 'type' => 'application/activity+json',
- 'href' => 'https://example.com/users/foo',
- ],
- ],
- ]),
- ],
- 'valid response - no at' => [
- 'foo@example.com',
- 'foo@example.com',
- true,
- json_encode([
- 'subject' => 'acct:foo@example.com',
- 'links' => [
- [
- 'rel' => 'self',
- 'type' => 'application/activity+json',
- 'href' => 'https://example.com/users/foo',
- ],
- ],
- ]),
- ],
- // failures
- 'invalid response' => [
- '@foo@example.com',
- null,
- true,
- json_encode([
- 'subject' => 'acct:foo@example.com',
- 'links' => [],
- ]),
- ],
- 'no response' => [
- '@foo@example.com',
- null,
- true,
- null,
- ],
- 'wrong user' => [
- '@foo@example.com',
- null,
- true,
- json_encode([
- 'links' => [],
- ]),
- ],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSanitizeFediverseServer')]
- public function testSanitizingFediverseServer(string $input, ?string $output, bool $hasInternet, ?string $serverResponse): void {
- $this->config->expects(self::once())
- ->method('getSystemValueBool')
- ->with('has_internet_connection', true)
- ->willReturn($hasInternet);
-
- if ($hasInternet) {
- $client = $this->createMock(IClient::class);
- if ($serverResponse !== null) {
- $response = $this->createMock(IResponse::class);
- $response->method('getBody')
- ->willReturn($serverResponse);
- $client->expects(self::once())
- ->method('get')
- ->with('https://example.com/.well-known/webfinger?resource=acct:foo@example.com')
- ->willReturn($response);
- } else {
- $client->expects(self::once())
- ->method('get')
- ->with('https://example.com/.well-known/webfinger?resource=acct:foo@example.com')
- ->willThrowException(new \Exception('404'));
- }
-
- $this->clientService
- ->expects(self::once())
- ->method('newClient')
- ->willReturn($client);
- } else {
- $this->clientService
- ->expects(self::never())
- ->method('newClient');
- }
-
- $user = $this->createMock(IUser::class);
- $account = new Account($user);
- $account->setProperty(IAccountManager::PROPERTY_FEDIVERSE, $input, IAccountManager::SCOPE_LOCAL, IAccountManager::NOT_VERIFIED);
-
- $manager = $this->getInstance(['getUser', 'updateUser']);
- $manager->method('getUser')
- ->with($user, false)
- ->willReturn([]);
- $manager->expects($output === null ? self::never() : self::once())
- ->method('updateUser');
-
- if ($output === null) {
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage(IAccountManager::PROPERTY_FEDIVERSE);
- }
-
- $manager->updateAccount($account);
-
- if ($output !== null) {
- self::assertEquals($output, $account->getProperty(IAccountManager::PROPERTY_FEDIVERSE)->getValue());
- }
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('searchDataProvider')]
- public function testSearchUsers(string $property, array $values, array $expected): void {
- $this->populateOrUpdate();
-
- $matchedUsers = $this->accountManager->searchUsers($property, $values);
- foreach ($expected as $expectedEntry) {
- $this->assertContains($expectedEntry, $matchedUsers);
- }
- if (empty($expected)) {
- $this->assertEmpty($matchedUsers);
- }
- }
-
- public static function searchDataProvider(): array {
- return [
- [ #0 Search for an existing name
- IAccountManager::PROPERTY_DISPLAYNAME,
- ['Jane Doe'],
- ['Jane Doe' => 'j.doe']
- ],
- [ #1 Search for part of a name (no result)
- IAccountManager::PROPERTY_DISPLAYNAME,
- ['Jane'],
- []
- ],
- [ #2 Search for part of a name (no result, test wildcard)
- IAccountManager::PROPERTY_DISPLAYNAME,
- ['Jane%'],
- []
- ],
- [ #3 Search for phone
- IAccountManager::PROPERTY_PHONE,
- ['+491603121212'],
- ['+491603121212' => 'b32c5a5b-1084-4380-8856-e5223b16de9f'],
- ],
- [ #4 Search for twitter handles
- IAccountManager::PROPERTY_TWITTER,
- ['@sometwitter', '@a_alice', '@unseen'],
- ['@sometwitter' => 'j.doe', '@a_alice' => 'a.allison'],
- ],
- [ #5 Search for email
- IAccountManager::PROPERTY_EMAIL,
- ['cheng@emca.com'],
- ['cheng@emca.com' => '31b5316a-9b57-4b17-970a-315a4cbe73eb'],
- ],
- [ #6 Search for email by additional email
- IAccountManager::PROPERTY_EMAIL,
- ['kai.cheng@emca.com'],
- ['kai.cheng@emca.com' => '31b5316a-9b57-4b17-970a-315a4cbe73eb'],
- ],
- [ #7 Search for additional email
- IAccountManager::COLLECTION_EMAIL,
- ['kai.cheng@emca.com', 'cheng@emca.com'],
- ['kai.cheng@emca.com' => '31b5316a-9b57-4b17-970a-315a4cbe73eb'],
- ],
- [ #8 Search for email by additional email (two valid search values, but the same user)
- IAccountManager::PROPERTY_EMAIL,
- ['kai.cheng@emca.com', 'cheng@emca.com'],
- [
- 'kai.cheng@emca.com' => '31b5316a-9b57-4b17-970a-315a4cbe73eb',
- ],
- ],
- ];
- }
-
- public static function dataCheckEmailVerification(): array {
- return [
- [['steve', 'Steve Smith', 'steve@steve.steve'], null],
- [['emma', 'Emma Morales', 'emma@emma.com'], 'emma@morales.com'],
- [['sarah@web.org', 'Sarah Foster', 'sarah@web.org'], null],
- [['cole@web.org', 'Cole Harrison', 'cole@web.org'], 'cole@example.com'],
- [['8d29e358-cf69-4849-bbf9-28076c0b908b', 'Alice McPherson', 'alice@example.com'], 'alice@mcpherson.com'],
- [['11da2744-3f4d-4c17-8c13-4c057a379237', 'James Loranger', 'james@example.com'], ''],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataCheckEmailVerification')]
- public function testCheckEmailVerification(array $userData, ?string $newEmail): void {
- $user = $this->makeUser(...$userData);
- // Once because of getAccount, once because of getUser
- $this->config->expects($this->exactly(2))->method('getSystemValue')->with('account_manager.default_property_scope', [])->willReturn([]);
- $account = $this->accountManager->getAccount($user);
- $emailUpdated = false;
-
- if (!empty($newEmail)) {
- $account->getProperty(IAccountManager::PROPERTY_EMAIL)->setValue($newEmail);
- $emailUpdated = true;
- }
-
- if ($emailUpdated) {
- $this->jobList->expects($this->once())
- ->method('add')
- ->with(VerifyUserData::class);
- } else {
- $this->jobList->expects($this->never())
- ->method('add')
- ->with(VerifyUserData::class);
- }
-
- /** @var array $oldData */
- $oldData = $this->invokePrivate($this->accountManager, 'getUser', [$user, false]);
- $this->invokePrivate($this->accountManager, 'checkEmailVerification', [$account, $oldData]);
- }
-
- public static function dataSetDefaultPropertyScopes(): array {
- return [
- [
- [],
- [
- IAccountManager::PROPERTY_DISPLAYNAME => IAccountManager::SCOPE_FEDERATED,
- IAccountManager::PROPERTY_ADDRESS => IAccountManager::SCOPE_LOCAL,
- IAccountManager::PROPERTY_EMAIL => IAccountManager::SCOPE_FEDERATED,
- IAccountManager::PROPERTY_ROLE => IAccountManager::SCOPE_LOCAL,
- ]
- ],
- [
- [
- IAccountManager::PROPERTY_DISPLAYNAME => IAccountManager::SCOPE_FEDERATED,
- IAccountManager::PROPERTY_EMAIL => IAccountManager::SCOPE_LOCAL,
- IAccountManager::PROPERTY_ROLE => IAccountManager::SCOPE_PRIVATE,
- ], [
- IAccountManager::PROPERTY_DISPLAYNAME => IAccountManager::SCOPE_FEDERATED,
- IAccountManager::PROPERTY_EMAIL => IAccountManager::SCOPE_LOCAL,
- IAccountManager::PROPERTY_ROLE => IAccountManager::SCOPE_PRIVATE,
- ]
- ],
- [
- [
- IAccountManager::PROPERTY_ADDRESS => 'invalid scope',
- 'invalid property' => IAccountManager::SCOPE_LOCAL,
- IAccountManager::PROPERTY_ROLE => IAccountManager::SCOPE_PRIVATE,
- ],
- [
- IAccountManager::PROPERTY_ADDRESS => IAccountManager::SCOPE_LOCAL,
- IAccountManager::PROPERTY_EMAIL => IAccountManager::SCOPE_FEDERATED,
- IAccountManager::PROPERTY_ROLE => IAccountManager::SCOPE_PRIVATE,
- ]
- ],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetDefaultPropertyScopes')]
- public function testSetDefaultPropertyScopes(array $propertyScopes, array $expectedResultScopes): void {
- $user = $this->makeUser('steve', 'Steve Smith', 'steve@steve.steve');
- $this->config->expects($this->once())->method('getSystemValue')->with('account_manager.default_property_scope', [])->willReturn($propertyScopes);
-
- $result = $this->invokePrivate($this->accountManager, 'buildDefaultUserRecord', [$user]);
- $resultProperties = array_column($result, 'name');
-
- $this->assertEmpty(array_diff($resultProperties, IAccountManager::ALLOWED_PROPERTIES), 'Building default user record returned non-allowed properties');
- foreach ($expectedResultScopes as $expectedResultScopeKey => $expectedResultScopeValue) {
- $resultScope = $result[array_search($expectedResultScopeKey, $resultProperties)]['scope'];
- $this->assertEquals($expectedResultScopeValue, $resultScope, "The result scope doesn't follow the value set into the config or defaults correctly.");
- }
- }
-}
--- a/tests/lib/Accounts/AccountPropertyCollectionTest.php
+++ /dev/null
@@ -1,189 +0,0 @@
-collection = new AccountPropertyCollection(self::COLLECTION_NAME);
- }
-
- protected function makePropertyMock(string $propertyName): IAccountProperty&MockObject {
- $mock = $this->createMock(IAccountProperty::class);
- $mock->expects($this->any())
- ->method('getName')
- ->willReturn($propertyName);
-
- return $mock;
- }
-
- public function testSetAndGetProperties(): void {
- $propsBefore = $this->collection->getProperties();
- $this->assertIsArray($propsBefore);
- $this->assertEmpty($propsBefore);
-
- $props = [
- $this->makePropertyMock(self::COLLECTION_NAME),
- $this->makePropertyMock(self::COLLECTION_NAME),
- $this->makePropertyMock(self::COLLECTION_NAME),
- ];
-
- $this->collection->setProperties($props);
- $propsAfter = $this->collection->getProperties();
- $this->assertIsArray($propsAfter);
- $this->assertCount(count($props), $propsAfter);
- }
-
- public function testSetPropertiesMixedInvalid(): void {
- $props = [
- $this->makePropertyMock(self::COLLECTION_NAME),
- $this->makePropertyMock('sneaky_property'),
- $this->makePropertyMock(self::COLLECTION_NAME),
- ];
-
- $this->expectException(InvalidArgumentException::class);
- $this->collection->setProperties($props);
- }
-
- public function testAddProperty(): void {
- $props = [
- $this->makePropertyMock(self::COLLECTION_NAME),
- $this->makePropertyMock(self::COLLECTION_NAME),
- $this->makePropertyMock(self::COLLECTION_NAME),
- ];
- $this->collection->setProperties($props);
-
- $additionalProperty = $this->makePropertyMock(self::COLLECTION_NAME);
- $this->collection->addProperty($additionalProperty);
-
- $propsAfter = $this->collection->getProperties();
- $this->assertCount(count($props) + 1, $propsAfter);
- $this->assertNotFalse(array_search($additionalProperty, $propsAfter, true));
- }
-
- public function testAddPropertyInvalid(): void {
- $props = [
- $this->makePropertyMock(self::COLLECTION_NAME),
- $this->makePropertyMock(self::COLLECTION_NAME),
- $this->makePropertyMock(self::COLLECTION_NAME),
- ];
- $this->collection->setProperties($props);
-
- $additionalProperty = $this->makePropertyMock('sneaky_property');
- $exceptionThrown = false;
- try {
- $this->collection->addProperty($additionalProperty);
- } catch (\InvalidArgumentException $e) {
- $exceptionThrown = true;
- } finally {
- $propsAfter = $this->collection->getProperties();
- $this->assertCount(count($props), $propsAfter);
- $this->assertFalse(array_search($additionalProperty, $propsAfter, true));
- $this->assertTrue($exceptionThrown);
- }
- }
-
- public function testRemoveProperty(): void {
- $additionalProperty = $this->makePropertyMock(self::COLLECTION_NAME);
- $props = [
- $this->makePropertyMock(self::COLLECTION_NAME),
- $this->makePropertyMock(self::COLLECTION_NAME),
- $additionalProperty,
- $this->makePropertyMock(self::COLLECTION_NAME),
- ];
- $this->collection->setProperties($props);
-
- $propsBefore = $this->collection->getProperties();
- $this->collection->removeProperty($additionalProperty);
- $propsAfter = $this->collection->getProperties();
-
- $this->assertTrue(count($propsBefore) > count($propsAfter));
- $this->assertCount(count($propsBefore) - 1, $propsAfter);
- $this->assertFalse(array_search($additionalProperty, $propsAfter, true));
- }
-
- public function testRemovePropertyNotFound(): void {
- $additionalProperty = $this->makePropertyMock(self::COLLECTION_NAME);
- $props = [
- $this->makePropertyMock(self::COLLECTION_NAME),
- $this->makePropertyMock(self::COLLECTION_NAME),
- $this->makePropertyMock(self::COLLECTION_NAME),
- ];
- $this->collection->setProperties($props);
-
- $propsBefore = $this->collection->getProperties();
- $this->collection->removeProperty($additionalProperty);
- $propsAfter = $this->collection->getProperties();
-
- // no errors, gently
- $this->assertCount(count($propsBefore), $propsAfter);
- }
-
- public function testRemovePropertyByValue(): void {
- $additionalProperty = $this->makePropertyMock(self::COLLECTION_NAME);
- $additionalProperty->expects($this->any())
- ->method('getValue')
- ->willReturn('Lorem ipsum');
-
- $additionalPropertyTwo = clone $additionalProperty;
-
- $props = [
- $this->makePropertyMock(self::COLLECTION_NAME),
- $this->makePropertyMock(self::COLLECTION_NAME),
- $additionalProperty,
- $this->makePropertyMock(self::COLLECTION_NAME),
- $additionalPropertyTwo
- ];
- $this->collection->setProperties($props);
-
- $propsBefore = $this->collection->getProperties();
- $this->collection->removePropertyByValue('Lorem ipsum');
- $propsAfter = $this->collection->getProperties();
-
- $this->assertTrue(count($propsBefore) > count($propsAfter));
- $this->assertCount(count($propsBefore) - 2, $propsAfter);
- $this->assertFalse(array_search($additionalProperty, $propsAfter, true));
- $this->assertFalse(array_search($additionalPropertyTwo, $propsAfter, true));
- }
-
- public function testRemovePropertyByValueNotFound(): void {
- $additionalProperty = $this->makePropertyMock(self::COLLECTION_NAME);
- $additionalProperty->expects($this->any())
- ->method('getValue')
- ->willReturn('Lorem ipsum');
-
- $props = [
- $this->makePropertyMock(self::COLLECTION_NAME),
- $this->makePropertyMock(self::COLLECTION_NAME),
- $this->makePropertyMock(self::COLLECTION_NAME),
- ];
- $this->collection->setProperties($props);
-
- $propsBefore = $this->collection->getProperties();
- $this->collection->removePropertyByValue('Lorem ipsum');
- $propsAfter = $this->collection->getProperties();
-
- // no errors, gently
- $this->assertCount(count($propsBefore), $propsAfter);
- }
-}
--- a/tests/lib/Accounts/AccountPropertyTest.php
+++ /dev/null
@@ -1,133 +0,0 @@
-assertEquals(IAccountManager::PROPERTY_WEBSITE, $accountProperty->getName());
- $this->assertEquals('https://example.com', $accountProperty->getValue());
- $this->assertEquals(IAccountManager::SCOPE_PUBLISHED, $accountProperty->getScope());
- $this->assertEquals(IAccountManager::VERIFIED, $accountProperty->getVerified());
- }
-
- public function testSetValue(): void {
- $accountProperty = new AccountProperty(
- IAccountManager::PROPERTY_WEBSITE,
- 'https://example.com',
- IAccountManager::SCOPE_PUBLISHED,
- IAccountManager::VERIFIED,
- ''
- );
- $actualReturn = $accountProperty->setValue('https://example.org');
- $this->assertEquals('https://example.org', $accountProperty->getValue());
- $this->assertEquals('https://example.org', $actualReturn->getValue());
- }
-
- public function testSetScope(): void {
- $accountProperty = new AccountProperty(
- IAccountManager::PROPERTY_WEBSITE,
- 'https://example.com',
- IAccountManager::SCOPE_PUBLISHED,
- IAccountManager::VERIFIED,
- ''
- );
- $actualReturn = $accountProperty->setScope(IAccountManager::SCOPE_LOCAL);
- $this->assertEquals(IAccountManager::SCOPE_LOCAL, $accountProperty->getScope());
- $this->assertEquals(IAccountManager::SCOPE_LOCAL, $actualReturn->getScope());
- }
-
- public static function scopesProvider(): array {
- return [
- // current values
- [IAccountManager::SCOPE_PRIVATE, IAccountManager::SCOPE_PRIVATE],
- [IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_LOCAL],
- [IAccountManager::SCOPE_FEDERATED, IAccountManager::SCOPE_FEDERATED],
- [IAccountManager::SCOPE_PUBLISHED, IAccountManager::SCOPE_PUBLISHED],
- // invalid values
- ['unknown', null],
- ['v2-unknown', null],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('scopesProvider')]
- public function testSetScopeMapping(string $storedScope, ?string $returnedScope): void {
- if ($returnedScope === null) {
- $this->expectException(\InvalidArgumentException::class);
- }
- $accountProperty = new AccountProperty(
- IAccountManager::PROPERTY_WEBSITE,
- 'https://example.com',
- $storedScope,
- IAccountManager::VERIFIED,
- ''
- );
- $this->assertEquals($returnedScope, $accountProperty->getScope());
- }
-
- public function testSetVerified(): void {
- $accountProperty = new AccountProperty(
- IAccountManager::PROPERTY_WEBSITE,
- 'https://example.com',
- IAccountManager::SCOPE_PUBLISHED,
- IAccountManager::VERIFIED,
- ''
- );
- $actualReturn = $accountProperty->setVerified(IAccountManager::NOT_VERIFIED);
- $this->assertEquals(IAccountManager::NOT_VERIFIED, $accountProperty->getVerified());
- $this->assertEquals(IAccountManager::NOT_VERIFIED, $actualReturn->getVerified());
- }
-
- public function testSetVerificationData(): void {
- $accountProperty = new AccountProperty(
- IAccountManager::PROPERTY_WEBSITE,
- 'https://example.com',
- IAccountManager::SCOPE_PUBLISHED,
- IAccountManager::VERIFIED,
- ''
- );
- $token = uniqid();
- $actualReturn = $accountProperty->setVerificationData($token);
- $this->assertEquals($token, $accountProperty->getVerificationData());
- $this->assertEquals($token, $actualReturn->getVerificationData());
- }
-
- public function testJsonSerialize(): void {
- $accountProperty = new AccountProperty(
- IAccountManager::PROPERTY_WEBSITE,
- 'https://example.com',
- IAccountManager::SCOPE_PUBLISHED,
- IAccountManager::VERIFIED,
- '60a7a633b74af',
- );
- $this->assertEquals([
- 'name' => IAccountManager::PROPERTY_WEBSITE,
- 'value' => 'https://example.com',
- 'scope' => IAccountManager::SCOPE_PUBLISHED,
- 'verified' => IAccountManager::VERIFIED,
- 'verificationData' => '60a7a633b74af'
- ], $accountProperty->jsonSerialize());
- }
-}
--- a/tests/lib/Accounts/AccountTest.php
+++ /dev/null
@@ -1,156 +0,0 @@
-createMock(IUser::class);
- $account = new Account($user);
- $this->assertEquals($user, $account->getUser());
- }
-
- public function testSetProperty(): void {
- $user = $this->createMock(IUser::class);
- $property = new AccountProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::NOT_VERIFIED, '');
- $account = new Account($user);
- $account->setProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::NOT_VERIFIED);
- $this->assertEquals($property, $account->getProperty(IAccountManager::PROPERTY_WEBSITE));
- }
-
- public function testGetAndGetAllProperties(): void {
- $user = $this->createMock(IUser::class);
- $properties = [
- IAccountManager::PROPERTY_WEBSITE => new AccountProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::NOT_VERIFIED, ''),
- IAccountManager::PROPERTY_EMAIL => new AccountProperty(IAccountManager::PROPERTY_EMAIL, 'user@example.com', IAccountManager::SCOPE_LOCAL, IAccountManager::VERIFIED, '')
- ];
- $account = new Account($user);
- $account->setProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::NOT_VERIFIED);
- $account->setProperty(IAccountManager::PROPERTY_EMAIL, 'user@example.com', IAccountManager::SCOPE_LOCAL, IAccountManager::VERIFIED);
-
- $col = new AccountPropertyCollection(IAccountManager::COLLECTION_EMAIL);
- $additionalProperty = new AccountProperty($col->getName(), 'second@example.org', IAccountManager::SCOPE_PUBLISHED, IAccountManager::NOT_VERIFIED, '');
- $col->addProperty($additionalProperty);
- $account->setPropertyCollection($col);
-
- $this->assertEquals($properties, $account->getProperties());
- $properties[] = $additionalProperty;
- $this->assertEquals(array_values($properties), \iterator_to_array($account->getAllProperties()));
- }
-
- public function testSetAllPropertiesFromJson(): void {
- $user = $this->createMock(IUser::class);
- $properties = [
- IAccountManager::PROPERTY_DISPLAYNAME => new AccountProperty(IAccountManager::PROPERTY_DISPLAYNAME, 'Steve', IAccountManager::SCOPE_FEDERATED, IAccountManager::NOT_VERIFIED, ''),
- IAccountManager::PROPERTY_ADDRESS => new AccountProperty(IAccountManager::PROPERTY_ADDRESS, '123 Acorn Avenue', IAccountManager::SCOPE_FEDERATED, IAccountManager::NOT_VERIFIED, ''),
- IAccountManager::PROPERTY_WEBSITE => new AccountProperty(IAccountManager::PROPERTY_WEBSITE, 'https://www.example.org', IAccountManager::SCOPE_FEDERATED, IAccountManager::VERIFIED, ''),
- IAccountManager::PROPERTY_EMAIL => new AccountProperty(IAccountManager::PROPERTY_EMAIL, 'steve@earth.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::VERIFICATION_IN_PROGRESS, ''),
- IAccountManager::PROPERTY_AVATAR => new AccountProperty(IAccountManager::PROPERTY_AVATAR, '', IAccountManager::SCOPE_PUBLISHED, IAccountManager::NOT_VERIFIED, ''),
- IAccountManager::PROPERTY_PHONE => new AccountProperty(IAccountManager::PROPERTY_PHONE, '+358407991028', IAccountManager::SCOPE_LOCAL, IAccountManager::NOT_VERIFIED, ''),
- IAccountManager::PROPERTY_TWITTER => new AccountProperty(IAccountManager::PROPERTY_TWITTER, 'therealsteve', IAccountManager::SCOPE_PRIVATE, IAccountManager::NOT_VERIFIED, ''),
- IAccountManager::PROPERTY_BLUESKY => new AccountProperty(IAccountManager::PROPERTY_BLUESKY, 'therealsteve.bsky.social', IAccountManager::SCOPE_PRIVATE, IAccountManager::NOT_VERIFIED, ''),
- IAccountManager::PROPERTY_ORGANISATION => new AccountProperty(IAccountManager::PROPERTY_ORGANISATION, 'Steve Incorporated', IAccountManager::SCOPE_FEDERATED, IAccountManager::NOT_VERIFIED, ''),
- IAccountManager::PROPERTY_ROLE => new AccountProperty(IAccountManager::PROPERTY_ROLE, 'Founder', IAccountManager::SCOPE_FEDERATED, IAccountManager::NOT_VERIFIED, ''),
- IAccountManager::PROPERTY_HEADLINE => new AccountProperty(IAccountManager::PROPERTY_HEADLINE, 'I am Steve', IAccountManager::SCOPE_PUBLISHED, IAccountManager::NOT_VERIFIED, ''),
- IAccountManager::PROPERTY_BIOGRAPHY => new AccountProperty(IAccountManager::PROPERTY_BIOGRAPHY, 'Steve is the best', IAccountManager::SCOPE_LOCAL, IAccountManager::NOT_VERIFIED, ''),
- IAccountManager::PROPERTY_PROFILE_ENABLED => new AccountProperty(IAccountManager::PROPERTY_PROFILE_ENABLED, '1', IAccountManager::SCOPE_FEDERATED, IAccountManager::NOT_VERIFIED, ''),
- IAccountManager::COLLECTION_EMAIL => [
- new AccountProperty(IAccountManager::COLLECTION_EMAIL, 'steve@mars.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::NOT_VERIFIED, ''),
- new AccountProperty(IAccountManager::COLLECTION_EMAIL, 'steve@neptune.com', IAccountManager::SCOPE_FEDERATED, IAccountManager::NOT_VERIFIED, ''),
- ],
- ];
- $account = new Account($user);
- $account->setAllPropertiesFromJson(json_decode(json_encode($properties), true));
- $this->assertEquals($properties, $account->jsonSerialize());
- }
-
- public function testGetFilteredProperties(): void {
- $user = $this->createMock(IUser::class);
- $properties = [
- IAccountManager::PROPERTY_WEBSITE => new AccountProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::NOT_VERIFIED, ''),
- IAccountManager::PROPERTY_EMAIL => new AccountProperty(IAccountManager::PROPERTY_EMAIL, 'user@example.com', IAccountManager::SCOPE_LOCAL, IAccountManager::VERIFIED, ''),
- IAccountManager::PROPERTY_PHONE => new AccountProperty(IAccountManager::PROPERTY_PHONE, '123456', IAccountManager::SCOPE_PUBLISHED, IAccountManager::VERIFIED, ''),
- ];
- $account = new Account($user);
- $account->setProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::NOT_VERIFIED);
- $account->setProperty(IAccountManager::PROPERTY_EMAIL, 'user@example.com', IAccountManager::SCOPE_LOCAL, IAccountManager::VERIFIED);
- $account->setProperty(IAccountManager::PROPERTY_PHONE, '123456', IAccountManager::SCOPE_PUBLISHED, IAccountManager::VERIFIED);
-
- $col = new AccountPropertyCollection(IAccountManager::COLLECTION_EMAIL);
- $additionalProperty1 = new AccountProperty($col->getName(), 'second@example.org', IAccountManager::SCOPE_PUBLISHED, IAccountManager::NOT_VERIFIED, '');
- $additionalProperty2 = new AccountProperty($col->getName(), 'third@example.org', IAccountManager::SCOPE_PUBLISHED, IAccountManager::VERIFIED, '');
- $col->addProperty($additionalProperty1);
- $col->addProperty($additionalProperty2);
- $account->setPropertyCollection($col);
-
- $this->assertEquals(
- [
- IAccountManager::PROPERTY_WEBSITE => $properties[IAccountManager::PROPERTY_WEBSITE],
- IAccountManager::PROPERTY_PHONE => $properties[IAccountManager::PROPERTY_PHONE],
- IAccountManager::COLLECTION_EMAIL . '#0' => $additionalProperty1,
- IAccountManager::COLLECTION_EMAIL . '#1' => $additionalProperty2,
- ],
- $account->getFilteredProperties(IAccountManager::SCOPE_PUBLISHED)
- );
- $this->assertEquals(
- [
- IAccountManager::PROPERTY_EMAIL => $properties[IAccountManager::PROPERTY_EMAIL],
- IAccountManager::PROPERTY_PHONE => $properties[IAccountManager::PROPERTY_PHONE],
- IAccountManager::COLLECTION_EMAIL . '#0' => $additionalProperty2,
- ],
- $account->getFilteredProperties(null, IAccountManager::VERIFIED)
- );
- $this->assertEquals(
- [
- IAccountManager::PROPERTY_PHONE => $properties[IAccountManager::PROPERTY_PHONE],
- IAccountManager::COLLECTION_EMAIL . '#0' => $additionalProperty2,
- ],
- $account->getFilteredProperties(IAccountManager::SCOPE_PUBLISHED, IAccountManager::VERIFIED),
- );
- }
-
- public function testJsonSerialize(): void {
- $user = $this->createMock(IUser::class);
- $properties = [
- IAccountManager::PROPERTY_WEBSITE => new AccountProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::NOT_VERIFIED, ''),
- IAccountManager::PROPERTY_EMAIL => new AccountProperty(IAccountManager::PROPERTY_EMAIL, 'user@example.com', IAccountManager::SCOPE_LOCAL, IAccountManager::VERIFIED, ''),
- IAccountManager::COLLECTION_EMAIL => [
- new AccountProperty(IAccountManager::COLLECTION_EMAIL, 'apple@orange.com', IAccountManager::SCOPE_LOCAL, IAccountManager::NOT_VERIFIED, ''),
- new AccountProperty(IAccountManager::COLLECTION_EMAIL, 'banana@orange.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::VERIFICATION_IN_PROGRESS, ''),
- new AccountProperty(IAccountManager::COLLECTION_EMAIL, 'kiwi@watermelon.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::VERIFIED, ''),
- ],
- ];
-
- $account = new Account($user);
- $account->setProperty(IAccountManager::PROPERTY_WEBSITE, 'https://example.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::NOT_VERIFIED);
- $account->setProperty(IAccountManager::PROPERTY_EMAIL, 'user@example.com', IAccountManager::SCOPE_LOCAL, IAccountManager::VERIFIED);
-
- $col = new AccountPropertyCollection(IAccountManager::COLLECTION_EMAIL);
- $col->setProperties([
- new AccountProperty($col->getName(), 'apple@orange.com', IAccountManager::SCOPE_LOCAL, IAccountManager::NOT_VERIFIED, ''),
- new AccountProperty($col->getName(), 'banana@orange.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::VERIFICATION_IN_PROGRESS, ''),
- new AccountProperty($col->getName(), 'kiwi@watermelon.com', IAccountManager::SCOPE_PUBLISHED, IAccountManager::VERIFIED, ''),
- ]);
- $account->setPropertyCollection($col);
-
- $this->assertEquals($properties, $account->jsonSerialize());
- }
-}
--- a/tests/lib/Accounts/HooksTest.php
+++ /dev/null
@@ -1,139 +0,0 @@
-logger = $this->createMock(LoggerInterface::class);
- $this->accountManager = $this->getMockBuilder(AccountManager::class)
- ->disableOriginalConstructor()->getMock();
-
- $this->hooks = new Hooks($this->logger, $this->accountManager);
- }
-
- /**
- *
- * @param $params
- * @param $data
- * @param $setEmail
- * @param $setDisplayName
- * @param $error
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestChangeUserHook')]
- public function testChangeUserHook($params, $data, $setEmail, $setDisplayName, $error): void {
- if ($error) {
- $this->accountManager->expects($this->never())->method('updateAccount');
- } else {
- $account = $this->createMock(IAccount::class);
- $this->accountManager->expects($this->atLeastOnce())->method('getAccount')->willReturn($account);
- if ($setEmail) {
- $property = $this->createMock(IAccountProperty::class);
- $property->expects($this->atLeastOnce())
- ->method('getValue')
- ->willReturn($data[IAccountManager::PROPERTY_EMAIL]['value']);
- $property->expects($this->atLeastOnce())
- ->method('setValue')
- ->with($params['value']);
-
- $account->expects($this->atLeastOnce())
- ->method('getProperty')
- ->with(IAccountManager::PROPERTY_EMAIL)
- ->willReturn($property);
-
- $this->accountManager->expects($this->once())
- ->method('updateAccount')
- ->with($account);
- } elseif ($setDisplayName) {
- $property = $this->createMock(IAccountProperty::class);
- $property->expects($this->atLeastOnce())
- ->method('getValue')
- ->willReturn($data[IAccountManager::PROPERTY_DISPLAYNAME]['value']);
- $property->expects($this->atLeastOnce())
- ->method('setValue')
- ->with($params['value']);
-
- $account->expects($this->atLeastOnce())
- ->method('getProperty')
- ->with(IAccountManager::PROPERTY_DISPLAYNAME)
- ->willReturn($property);
-
- $this->accountManager->expects($this->once())
- ->method('updateAccount')
- ->with($account);
- } else {
- $this->accountManager->expects($this->never())->method('updateAccount');
- }
- }
-
- $params['user'] = $this->createMock(IUser::class);
- $this->hooks->changeUserHook($params['user'], $params['feature'], $params['value']);
- }
-
- public static function dataTestChangeUserHook(): array {
- return [
- [
- ['feature' => '', 'value' => ''],
- [
- IAccountManager::PROPERTY_EMAIL => ['value' => ''],
- IAccountManager::PROPERTY_DISPLAYNAME => ['value' => '']
- ],
- false, false, true
- ],
- [
- ['feature' => 'foo', 'value' => 'bar'],
- [
- IAccountManager::PROPERTY_EMAIL => ['value' => 'oldMail@example.com'],
- IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'oldDisplayName']
- ],
- false, false, false
- ],
- [
- ['feature' => 'eMailAddress', 'value' => 'newMail@example.com'],
- [
- IAccountManager::PROPERTY_EMAIL => ['value' => 'oldMail@example.com'],
- IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'oldDisplayName']
- ],
- true, false, false
- ],
- [
- ['feature' => 'displayName', 'value' => 'newDisplayName'],
- [
- IAccountManager::PROPERTY_EMAIL => ['value' => 'oldMail@example.com'],
- IAccountManager::PROPERTY_DISPLAYNAME => ['value' => 'oldDisplayName']
- ],
- false, true, false
- ],
- ];
- }
-}
--- a/tests/lib/Activity/ManagerTest.php
+++ /dev/null
@@ -1,296 +0,0 @@
-request = $this->createMock(IRequest::class);
- $this->session = $this->createMock(IUserSession::class);
- $this->config = $this->createMock(IConfig::class);
- $this->validator = $this->createMock(IValidator::class);
- $this->richTextFormatter = $this->createMock(IRichTextFormatter::class);
- $this->time = $this->createMock(ITimeFactory::class);
-
- $this->activityManager = new Manager(
- $this->request,
- $this->session,
- $this->config,
- $this->validator,
- $this->richTextFormatter,
- $this->createMock(IL10N::class),
- $this->time,
- );
-
- $this->assertSame([], self::invokePrivate($this->activityManager, 'getConsumers'));
-
- $this->activityManager->registerConsumer(function () {
- return new NoOpConsumer();
- });
-
- $this->assertNotEmpty(self::invokePrivate($this->activityManager, 'getConsumers'));
- $this->assertNotEmpty(self::invokePrivate($this->activityManager, 'getConsumers'));
- }
-
- public function testGetConsumers(): void {
- $consumers = self::invokePrivate($this->activityManager, 'getConsumers');
-
- $this->assertNotEmpty($consumers);
- }
-
- public function testGetConsumersInvalidConsumer(): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $this->activityManager->registerConsumer(function () {
- return new \stdClass();
- });
-
- self::invokePrivate($this->activityManager, 'getConsumers');
- }
-
- public static function getUserFromTokenThrowInvalidTokenData(): array {
- return [
- [null, []],
- ['', []],
- ['12345678901234567890123456789', []],
- ['1234567890123456789012345678901', []],
- ['123456789012345678901234567890', []],
- ['123456789012345678901234567890', ['user1', 'user2']],
- ];
- }
-
- /**
- *
- * @param string $token
- * @param array $users
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('getUserFromTokenThrowInvalidTokenData')]
- public function testGetUserFromTokenThrowInvalidToken($token, $users): void {
- $this->expectException(\UnexpectedValueException::class);
-
- $this->mockRSSToken($token, $token, $users);
- self::invokePrivate($this->activityManager, 'getUserFromToken');
- }
-
- public static function getUserFromTokenData(): array {
- return [
- [null, '123456789012345678901234567890', 'user1'],
- ['user2', null, 'user2'],
- ['user2', '123456789012345678901234567890', 'user2'],
- ];
- }
-
- /**
- *
- * @param string $userLoggedIn
- * @param string $token
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('getUserFromTokenData')]
- public function testGetUserFromToken($userLoggedIn, $token, $expected): void {
- if ($userLoggedIn !== null) {
- $this->mockUserSession($userLoggedIn);
- }
- $this->mockRSSToken($token, '123456789012345678901234567890', ['user1']);
-
- $this->assertEquals($expected, $this->activityManager->getCurrentUserId());
- }
-
- protected function mockRSSToken($requestToken, $userToken, $users) {
- if ($requestToken !== null) {
- $this->request->expects($this->any())
- ->method('getParam')
- ->with('token', '')
- ->willReturn($requestToken);
- }
-
- $this->config->expects($this->any())
- ->method('getUsersForUserValue')
- ->with('activity', 'rsstoken', $userToken)
- ->willReturn($users);
- }
-
- protected function mockUserSession($user) {
- $mockUser = $this->getMockBuilder(IUser::class)
- ->disableOriginalConstructor()
- ->getMock();
- $mockUser->expects($this->any())
- ->method('getUID')
- ->willReturn($user);
-
- $this->session->expects($this->any())
- ->method('isLoggedIn')
- ->willReturn(true);
- $this->session->expects($this->any())
- ->method('getUser')
- ->willReturn($mockUser);
- }
-
- public function testPublishExceptionNoApp(): void {
- $this->expectException(IncompleteActivityException::class);
-
- $event = $this->activityManager->generateEvent();
- $this->activityManager->publish($event);
- }
-
- public function testPublishExceptionNoType(): void {
- $this->expectException(IncompleteActivityException::class);
-
- $event = $this->activityManager->generateEvent();
- $event->setApp('test');
- $this->activityManager->publish($event);
- }
-
- public function testPublishExceptionNoAffectedUser(): void {
- $this->expectException(IncompleteActivityException::class);
-
- $event = $this->activityManager->generateEvent();
- $event->setApp('test')
- ->setType('test_type');
- $this->activityManager->publish($event);
- }
-
- public function testPublishExceptionNoSubject(): void {
- $this->expectException(IncompleteActivityException::class);
-
- $event = $this->activityManager->generateEvent();
- $event->setApp('test')
- ->setType('test_type')
- ->setAffectedUser('test_affected');
- $this->activityManager->publish($event);
- }
-
- public static function dataPublish(): array {
- return [
- [null, ''],
- ['test_author', 'test_author'],
- ];
- }
-
- /**
- * @param string|null $author
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataPublish')]
- public function testPublish($author, $expected): void {
- if ($author !== null) {
- $authorObject = $this->getMockBuilder(IUser::class)
- ->disableOriginalConstructor()
- ->getMock();
- $authorObject->expects($this->once())
- ->method('getUID')
- ->willReturn($author);
- $this->session->expects($this->atLeastOnce())
- ->method('getUser')
- ->willReturn($authorObject);
- }
-
- $time = time();
- $this->time
- ->method('getTime')
- ->willReturn($time);
-
- $event = $this->activityManager->generateEvent();
- $event->setApp('test')
- ->setType('test_type')
- ->setSubject('test_subject', [])
- ->setAffectedUser('test_affected')
- ->setObject('file', 123);
-
- $consumer = $this->getMockBuilder('OCP\Activity\IConsumer')
- ->disableOriginalConstructor()
- ->getMock();
- $consumer->expects($this->once())
- ->method('receive')
- ->with($event)
- ->willReturnCallback(function (IEvent $event) use ($expected, $time): void {
- $this->assertEquals($time, $event->getTimestamp(), 'Timestamp not set correctly');
- $this->assertSame($expected, $event->getAuthor(), 'Author name not set correctly');
- });
- $this->activityManager->registerConsumer(function () use ($consumer) {
- return $consumer;
- });
-
- $this->activityManager->publish($event);
- }
-
- public function testPublishAllManually(): void {
- $event = $this->activityManager->generateEvent();
- $event->setApp('test_app')
- ->setType('test_type')
- ->setAffectedUser('test_affected')
- ->setAuthor('test_author')
- ->setTimestamp(1337)
- ->setSubject('test_subject', ['test_subject_param'])
- ->setMessage('test_message', ['test_message_param'])
- ->setObject('test_object_type', 42, 'test_object_name')
- ->setLink('test_link')
- ;
-
- $consumer = $this->getMockBuilder('OCP\Activity\IConsumer')
- ->disableOriginalConstructor()
- ->getMock();
- $consumer->expects($this->once())
- ->method('receive')
- ->willReturnCallback(function (IEvent $event): void {
- $this->assertSame('test_app', $event->getApp(), 'App not set correctly');
- $this->assertSame('test_type', $event->getType(), 'Type not set correctly');
- $this->assertSame('test_affected', $event->getAffectedUser(), 'Affected user not set correctly');
- $this->assertSame('test_author', $event->getAuthor(), 'Author not set correctly');
- $this->assertSame(1337, $event->getTimestamp(), 'Timestamp not set correctly');
- $this->assertSame('test_subject', $event->getSubject(), 'Subject not set correctly');
- $this->assertSame(['test_subject_param'], $event->getSubjectParameters(), 'Subject parameter not set correctly');
- $this->assertSame('test_message', $event->getMessage(), 'Message not set correctly');
- $this->assertSame(['test_message_param'], $event->getMessageParameters(), 'Message parameter not set correctly');
- $this->assertSame('test_object_type', $event->getObjectType(), 'Object type not set correctly');
- $this->assertSame(42, $event->getObjectId(), 'Object ID not set correctly');
- $this->assertSame('test_object_name', $event->getObjectName(), 'Object name not set correctly');
- $this->assertSame('test_link', $event->getLink(), 'Link not set correctly');
- });
- $this->activityManager->registerConsumer(function () use ($consumer) {
- return $consumer;
- });
-
- $this->activityManager->publish($event);
- }
-}
-
-class NoOpConsumer implements IConsumer {
- #[\Override]
- public function receive(IEvent $event) {
- }
-}
--- a/tests/lib/AllConfigTest.php
+++ /dev/null
@@ -1,464 +0,0 @@
-connection->getQueryBuilder();
- $qb->insert('preferences')
- ->values([
- 'userid' => $qb->createNamedParameter($userid),
- 'appid' => $qb->createNamedParameter($appid),
- 'configkey' => $qb->createNamedParameter($configkey),
- 'configvalue' => $qb->createNamedParameter($configvalue),
- ])
- ->executeStatement();
- }
-
- /** Return all oc_preferences rows for the given user */
- private function getPreferenceRows(string $userid): array {
- $qb = $this->connection->getQueryBuilder();
- return $qb->select('userid', 'appid', 'configkey', 'configvalue')
- ->from('preferences')
- ->where($qb->expr()->eq('userid', $qb->createNamedParameter($userid)))
- ->executeQuery()
- ->fetchAllAssociative();
- }
-
- /** Return the total number of rows in oc_preferences */
- private function countPreferenceRows(): int {
- $qb = $this->connection->getQueryBuilder();
- return (int)$qb->select($qb->func()->count('*'))
- ->from('preferences')
- ->executeQuery()
- ->fetchOne();
- }
-
- /** Truncate oc_preferences */
- private function clearPreferences(): void {
- $this->connection->getQueryBuilder()->delete('preferences')->executeStatement();
- }
-
- protected function getConfig($systemConfig = null, $connection = null) {
- if ($this->connection === null) {
- $this->connection = Server::get(IDBConnection::class);
- }
- if ($connection === null) {
- $connection = $this->connection;
- }
- if ($systemConfig === null) {
- $systemConfig = $this->getMockBuilder('\OC\SystemConfig')
- ->disableOriginalConstructor()
- ->getMock();
- }
- return new AllConfig($systemConfig, $connection);
- }
-
- public function testDeleteUserValue(): void {
- $config = $this->getConfig();
-
- // preparation - add something to the database
- $this->insertPreferenceRow('userDelete', 'appDelete', 'keyDelete', 'valueDelete');
-
- $config->deleteUserValue('userDelete', 'appDelete', 'keyDelete');
-
- $actualCount = count($this->getPreferenceRows('userDelete'));
-
- $this->assertEquals(0, $actualCount, 'There was one value in the database and after the tests there should be no entry left.');
- }
-
- public function testSetUserValue(): void {
- $config = $this->getConfig();
-
- $config->setUserValue('userSet', 'appSet', 'keySet', 'valueSet');
-
- $result = $this->getPreferenceRows('userSet');
-
- $this->assertEquals(1, count($result));
- $this->assertEquals([
- 'userid' => 'userSet',
- 'appid' => 'appSet',
- 'configkey' => 'keySet',
- 'configvalue' => 'valueSet'
- ], $result[0]);
-
- // test if the method overwrites existing database entries
- $config->setUserValue('userSet', 'appSet', 'keySet', 'valueSet2');
-
- $result = $this->getPreferenceRows('userSet');
-
- $this->assertEquals(1, count($result));
- $this->assertEquals([
- 'userid' => 'userSet',
- 'appid' => 'appSet',
- 'configkey' => 'keySet',
- 'configvalue' => 'valueSet2'
- ], $result[0]);
-
- // cleanup - it therefore relies on the successful execution of the previous test
- $config->deleteUserValue('userSet', 'appSet', 'keySet');
- }
-
- /**
- * This test needs to stay! Emails are expected to be lowercase due to performance reasons.
- * This way we can skip the expensive casing change on the database.
- */
- public function testSetUserValueSettingsEmail(): void {
- $config = $this->getConfig();
-
- $config->setUserValue('userSet', 'settings', 'email', 'mixed.CASE@domain.COM');
-
- $result = $this->getPreferenceRows('userSet');
-
- $this->assertEquals(1, count($result));
- $this->assertEquals([
- 'userid' => 'userSet',
- 'appid' => 'settings',
- 'configkey' => 'email',
- 'configvalue' => 'mixed.case@domain.com'
- ], $result[0]);
- }
-
- public function testSetUserValueWithPreCondition(): void {
- $config = $this->getConfig();
-
- $config->setUserValue('userPreCond', 'appPreCond', 'keyPreCond', 'valuePreCond');
-
- $result = $this->getPreferenceRows('userPreCond');
-
- $this->assertEquals(1, count($result));
- $this->assertEquals([
- 'userid' => 'userPreCond',
- 'appid' => 'appPreCond',
- 'configkey' => 'keyPreCond',
- 'configvalue' => 'valuePreCond'
- ], $result[0]);
-
- // test if the method overwrites existing database entries with valid precond
- $config->setUserValue('userPreCond', 'appPreCond', 'keyPreCond', 'valuePreCond2', 'valuePreCond');
-
- $result = $this->getPreferenceRows('userPreCond');
-
- $this->assertEquals(1, count($result));
- $this->assertEquals([
- 'userid' => 'userPreCond',
- 'appid' => 'appPreCond',
- 'configkey' => 'keyPreCond',
- 'configvalue' => 'valuePreCond2'
- ], $result[0]);
-
- // cleanup
- $config->deleteUserValue('userPreCond', 'appPreCond', 'keyPreCond');
- }
-
- public static function dataSetUserValueUnexpectedValue(): array {
- return [
- [true],
- [false],
- [null],
- [new \stdClass()],
- ];
- }
-
- /**
- * @param mixed $value
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetUserValueUnexpectedValue')]
- public function testSetUserValueUnexpectedValue($value): void {
- $this->expectException(\UnexpectedValueException::class);
-
- $config = $this->getConfig();
- $config->setUserValue('userSetBool', 'appSetBool', 'keySetBool', $value);
- }
-
- public function testSetUserValueWithPreConditionFailure(): void {
- $this->expectException(PreConditionNotMetException::class);
-
- $config = $this->getConfig();
-
- $config->setUserValue('userPreCond1', 'appPreCond', 'keyPreCond', 'valuePreCond');
-
- $result = $this->getPreferenceRows('userPreCond1');
-
- $this->assertEquals(1, count($result));
- $this->assertEquals([
- 'userid' => 'userPreCond1',
- 'appid' => 'appPreCond',
- 'configkey' => 'keyPreCond',
- 'configvalue' => 'valuePreCond'
- ], $result[0]);
-
- // test if the method overwrites existing database entries with valid precond
- $config->setUserValue('userPreCond1', 'appPreCond', 'keyPreCond', 'valuePreCond2', 'valuePreCond3');
-
- $result = $this->getPreferenceRows('userPreCond1');
-
- $this->assertEquals(1, count($result));
- $this->assertEquals([
- 'userid' => 'userPreCond1',
- 'appid' => 'appPreCond',
- 'configkey' => 'keyPreCond',
- 'configvalue' => 'valuePreCond'
- ], $result[0]);
-
- // cleanup
- $config->deleteUserValue('userPreCond1', 'appPreCond', 'keyPreCond');
- }
-
- public function testSetUserValueWithPreConditionFailureWhenResultStillMatches(): void {
- $this->expectException(PreConditionNotMetException::class);
-
- $config = $this->getConfig();
-
- $config->setUserValue('userPreCond1', 'appPreCond', 'keyPreCond', 'valuePreCond');
-
- $result = $this->getPreferenceRows('userPreCond1');
-
- $this->assertCount(1, $result);
- $this->assertEquals([
- 'userid' => 'userPreCond1',
- 'appid' => 'appPreCond',
- 'configkey' => 'keyPreCond',
- 'configvalue' => 'valuePreCond'
- ], $result[0]);
-
- // test if the method throws with invalid precondition when the value is the same
- $config->setUserValue('userPreCond1', 'appPreCond', 'keyPreCond', 'valuePreCond', 'valuePreCond3');
-
- $result = $this->getPreferenceRows('userPreCond1');
-
- $this->assertCount(1, $result);
- $this->assertEquals([
- 'userid' => 'userPreCond1',
- 'appid' => 'appPreCond',
- 'configkey' => 'keyPreCond',
- 'configvalue' => 'valuePreCond'
- ], $result[0]);
-
- // cleanup
- $config->deleteUserValue('userPreCond1', 'appPreCond', 'keyPreCond');
- }
-
- public function testSetUserValueUnchanged(): void {
- // TODO - FIXME until the dependency injection is handled properly (in AllConfig)
- $this->markTestSkipped('Skipped because this is just testable if database connection can be injected');
-
- $resultMock = $this->getMockBuilder('\Doctrine\DBAL\Driver\Statement')
- ->disableOriginalConstructor()->getMock();
- $resultMock->expects($this->once())
- ->method('fetchOne')
- ->willReturn('valueSetUnchanged');
-
- $connectionMock = $this->createMock(IDBConnection::class);
- $connectionMock->expects($this->once())
- ->method('executeQuery')
- ->with($this->equalTo('SELECT `configvalue` FROM `*PREFIX*preferences` '
- . 'WHERE `userid` = ? AND `appid` = ? AND `configkey` = ?'),
- $this->equalTo(['userSetUnchanged', 'appSetUnchanged', 'keySetUnchanged']))
- ->willReturn($resultMock);
- $connectionMock->expects($this->never())
- ->method('executeStatement');
-
- $config = $this->getConfig(null, $connectionMock);
-
- $config->setUserValue('userSetUnchanged', 'appSetUnchanged', 'keySetUnchanged', 'valueSetUnchanged');
- }
-
- public function testGetUserValue(): void {
- $config = $this->getConfig();
-
- // setup - it therefore relies on the successful execution of the previous test
- $config->setUserValue('userGet', 'appGet', 'keyGet', 'valueGet');
- $value = $config->getUserValue('userGet', 'appGet', 'keyGet');
-
- $this->assertEquals('valueGet', $value);
-
- $result = $this->getPreferenceRows('userGet');
-
- $this->assertEquals(1, count($result));
- $this->assertEquals([
- 'userid' => 'userGet',
- 'appid' => 'appGet',
- 'configkey' => 'keyGet',
- 'configvalue' => 'valueGet'
- ], $result[0]);
-
- // drop data from database - but the config option should be cached in the config object
- $qb = $this->connection->getQueryBuilder();
- $qb->delete('preferences')
- ->where($qb->expr()->eq('userid', $qb->createNamedParameter('userGet')))
- ->executeStatement();
-
- // testing the caching mechanism
- $value = $config->getUserValue('userGet', 'appGet', 'keyGet');
-
- $this->assertEquals('valueGet', $value);
-
- $this->assertEquals(0, count($this->getPreferenceRows('userGet')));
- }
-
- public function testGetUserKeys(): void {
- $config = $this->getConfig();
-
- // preparation - add something to the database
- $this->insertPreferenceRow('userFetch', 'appFetch1', 'keyFetch1', 'value1');
- $this->insertPreferenceRow('userFetch', 'appFetch1', 'keyFetch2', 'value2');
- $this->insertPreferenceRow('userFetch', 'appFetch2', 'keyFetch3', 'value3');
- $this->insertPreferenceRow('userFetch', 'appFetch1', 'keyFetch4', 'value4');
- $this->insertPreferenceRow('userFetch', 'appFetch4', 'keyFetch1', 'value5');
- $this->insertPreferenceRow('userFetch', 'appFetch5', 'keyFetch1', 'value6');
- $this->insertPreferenceRow('userFetch2', 'appFetch', 'keyFetch1', 'value7');
-
- $value = $config->getUserKeys('userFetch', 'appFetch1');
- $this->assertEquals(['keyFetch1', 'keyFetch2', 'keyFetch4'], $value);
-
- $value = $config->getUserKeys('userFetch2', 'appFetch');
- $this->assertEquals(['keyFetch1'], $value);
-
- // cleanup
- $this->clearPreferences();
- }
-
- public function testGetUserKeysAllInts(): void {
- $config = $this->getConfig();
-
- // preparation - add something to the database
- $this->insertPreferenceRow('userFetch8', 'appFetch1', '123', 'value');
- $this->insertPreferenceRow('userFetch8', 'appFetch1', '456', 'value');
-
- $value = $config->getUserKeys('userFetch8', 'appFetch1');
- $this->assertEquals(['123', '456'], $value);
- $this->assertIsString($value[0]);
- $this->assertIsString($value[1]);
-
- // cleanup
- $this->clearPreferences();
- }
-
- public function testGetUserValueDefault(): void {
- $config = $this->getConfig();
-
- $this->assertEquals('', $config->getUserValue('userGetUnset', 'appGetUnset', 'keyGetUnset'));
- $this->assertEquals(null, $config->getUserValue('userGetUnset', 'appGetUnset', 'keyGetUnset', null));
- $this->assertEquals('foobar', $config->getUserValue('userGetUnset', 'appGetUnset', 'keyGetUnset', 'foobar'));
- }
-
- public function testGetUserValueForUsers(): void {
- $config = $this->getConfig();
-
- // preparation - add something to the database
- $this->insertPreferenceRow('userFetch1', 'appFetch2', 'keyFetch1', 'value1');
- $this->insertPreferenceRow('userFetch2', 'appFetch2', 'keyFetch1', 'value2');
- $this->insertPreferenceRow('userFetch3', 'appFetch2', 'keyFetch1', '3');
- $this->insertPreferenceRow('userFetch4', 'appFetch2', 'keyFetch1', 'value4');
- $this->insertPreferenceRow('userFetch5', 'appFetch2', 'keyFetch1', 'value5');
- $this->insertPreferenceRow('userFetch6', 'appFetch2', 'keyFetch1', 'value6');
- $this->insertPreferenceRow('userFetch7', 'appFetch2', 'keyFetch1', 'value7');
-
- $value = $config->getUserValueForUsers('appFetch2', 'keyFetch1',
- ['userFetch1', 'userFetch2', 'userFetch3', 'userFetch5']);
- $this->assertEquals([
- 'userFetch1' => 'value1',
- 'userFetch2' => 'value2',
- 'userFetch3' => 3,
- 'userFetch5' => 'value5'
- ], $value);
-
- $value = $config->getUserValueForUsers('appFetch2', 'keyFetch1',
- ['userFetch1', 'userFetch4', 'userFetch9']);
- $this->assertEquals([
- 'userFetch1' => 'value1',
- 'userFetch4' => 'value4'
- ], $value, 'userFetch9 is an non-existent user and should not be shown.');
-
- // cleanup
- $this->clearPreferences();
- }
-
- public function testDeleteAllUserValues(): void {
- $config = $this->getConfig();
-
- // preparation - add something to the database
- $this->insertPreferenceRow('userFetch3', 'appFetch1', 'keyFetch1', 'value1');
- $this->insertPreferenceRow('userFetch3', 'appFetch1', 'keyFetch2', 'value2');
- $this->insertPreferenceRow('userFetch3', 'appFetch2', 'keyFetch3', 'value3');
- $this->insertPreferenceRow('userFetch3', 'appFetch1', 'keyFetch4', 'value4');
- $this->insertPreferenceRow('userFetch3', 'appFetch4', 'keyFetch1', 'value5');
- $this->insertPreferenceRow('userFetch3', 'appFetch5', 'keyFetch1', 'value6');
- $this->insertPreferenceRow('userFetch4', 'appFetch2', 'keyFetch1', 'value7');
-
- $config->deleteAllUserValues('userFetch3');
-
- $this->assertEquals(1, $this->countPreferenceRows(), 'After removing `userFetch3` there should be exactly 1 entry left.');
-
- // cleanup
- $this->clearPreferences();
- }
-
- public function testDeleteAppFromAllUsers(): void {
- $config = $this->getConfig();
-
- // preparation - add something to the database
- $this->insertPreferenceRow('userFetch5', 'appFetch1', 'keyFetch1', 'value1');
- $this->insertPreferenceRow('userFetch5', 'appFetch1', 'keyFetch2', 'value2');
- $this->insertPreferenceRow('userFetch5', 'appFetch2', 'keyFetch3', 'value3');
- $this->insertPreferenceRow('userFetch5', 'appFetch1', 'keyFetch4', 'value4');
- $this->insertPreferenceRow('userFetch5', 'appFetch4', 'keyFetch1', 'value5');
- $this->insertPreferenceRow('userFetch5', 'appFetch5', 'keyFetch1', 'value6');
- $this->insertPreferenceRow('userFetch6', 'appFetch2', 'keyFetch1', 'value7');
-
- $config->deleteAppFromAllUsers('appFetch1');
-
- $this->assertEquals(4, $this->countPreferenceRows(), 'After removing `appFetch1` there should be exactly 4 entries left.');
-
- $config->deleteAppFromAllUsers('appFetch2');
-
- $this->assertEquals(2, $this->countPreferenceRows(), 'After removing `appFetch2` there should be exactly 2 entries left.');
-
- // cleanup
- $this->clearPreferences();
- }
-
- public function testGetUsersForUserValue(): void {
- // mock the check for the database to run the correct SQL statements for each database type
- $systemConfig = $this->getMockBuilder('\OC\SystemConfig')
- ->disableOriginalConstructor()
- ->getMock();
- $config = $this->getConfig($systemConfig);
-
- // preparation - add something to the database
- $this->insertPreferenceRow('user1', 'appFetch9', 'keyFetch9', 'value9');
- $this->insertPreferenceRow('user2', 'appFetch9', 'keyFetch9', 'value9');
- $this->insertPreferenceRow('user3', 'appFetch9', 'keyFetch9', 'value8');
- $this->insertPreferenceRow('user4', 'appFetch9', 'keyFetch8', 'value9');
- $this->insertPreferenceRow('user5', 'appFetch8', 'keyFetch9', 'value9');
- $this->insertPreferenceRow('user6', 'appFetch9', 'keyFetch9', 'value9');
-
- $value = $config->getUsersForUserValue('appFetch9', 'keyFetch9', 'value9');
- $this->assertEquals(['user1', 'user2', 'user6'], $value);
-
- // cleanup
- $this->clearPreferences();
- }
-}
--- a/tests/lib/App/AppManagerTest.php
+++ /dev/null
@@ -1,934 +0,0 @@
-createMock(AppConfig::class);
-
- $config->expects($this->any())
- ->method('getValue')
- ->willReturnCallback(function ($app, $key, $default) use (&$appConfig) {
- return (isset($appConfig[$app]) && isset($appConfig[$app][$key]))
- ? $appConfig[$app][$key]
- : $default;
- });
- $config->expects($this->any())
- ->method('setValue')
- ->willReturnCallback(function ($app, $key, $value) use (&$appConfig): void {
- if (!isset($appConfig[$app])) {
- $appConfig[$app] = [];
- }
- $appConfig[$app][$key] = $value;
- });
- $config->expects($this->any())
- ->method('getValues')
- ->willReturnCallback(function ($app, $key) use (&$appConfig) {
- if ($app) {
- return $appConfig[$app];
- } else {
- $values = [];
- foreach ($appConfig as $appid => $appData) {
- if (isset($appData[$key])) {
- $values[$appid] = $appData[$key];
- }
- }
- return $values;
- }
- });
- $config->expects($this->any())
- ->method('searchValues')
- ->willReturnCallback(function ($key, $lazy, $type) use (&$appConfig) {
- $values = [];
- foreach ($appConfig as $appid => $appData) {
- if (isset($appData[$key])) {
- $values[$appid] = $appData[$key];
- }
- }
- return $values;
- });
-
- return $config;
- }
-
- protected IUserSession&MockObject $userSession;
- private IConfig&MockObject $config;
- protected IGroupManager&MockObject $groupManager;
- protected AppConfig&MockObject $appConfig;
- protected ICache&MockObject $cache;
- protected ICacheFactory&MockObject $cacheFactory;
- protected IEventDispatcher&MockObject $eventDispatcher;
- protected LoggerInterface&MockObject $logger;
- protected IURLGenerator&MockObject $urlGenerator;
- protected ServerVersion&MockObject $serverVersion;
- protected ConfigManager&MockObject $configManager;
-
- protected DependencyAnalyzer $dependencyAnalyzer;
-
- protected AppManager $manager;
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- $this->userSession = $this->createMock(IUserSession::class);
- $this->groupManager = $this->createMock(IGroupManager::class);
- $this->config = $this->createMock(IConfig::class);
- $this->appConfig = $this->getAppConfig();
- $this->cacheFactory = $this->createMock(ICacheFactory::class);
- $this->cache = $this->createMock(ICache::class);
- $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->serverVersion = $this->createMock(ServerVersion::class);
- $this->configManager = $this->createMock(ConfigManager::class);
- $this->dependencyAnalyzer = new DependencyAnalyzer($this->createMock(Platform::class));
-
- $this->overwriteService(AppConfig::class, $this->appConfig);
- $this->overwriteService(IURLGenerator::class, $this->urlGenerator);
-
- $this->cacheFactory->expects($this->any())
- ->method('createDistributed')
- ->with('settings')
- ->willReturn($this->cache);
-
- $this->config
- ->method('getSystemValueBool')
- ->with('installed', false)
- ->willReturn(true);
-
- $this->manager = new AppManager(
- $this->userSession,
- $this->config,
- $this->groupManager,
- $this->cacheFactory,
- $this->eventDispatcher,
- $this->logger,
- $this->serverVersion,
- $this->configManager,
- $this->dependencyAnalyzer,
- );
- }
-
- #[DataProvider('dataGetAppIcon')]
- public function testGetAppIcon($callback, ?bool $dark, ?string $expected): void {
- $this->urlGenerator->expects($this->atLeastOnce())
- ->method('imagePath')
- ->willReturnCallback($callback);
-
- if ($dark !== null) {
- $this->assertEquals($expected, $this->manager->getAppIcon('test', $dark));
- } else {
- $this->assertEquals($expected, $this->manager->getAppIcon('test'));
- }
- }
-
- public static function dataGetAppIcon(): array {
- $nothing = function ($appId): void {
- self::assertEquals('test', $appId);
- throw new \RuntimeException();
- };
-
- $createCallback = function ($workingIcons) {
- return function ($appId, $icon) use ($workingIcons) {
- self::assertEquals('test', $appId);
- if (in_array($icon, $workingIcons)) {
- return '/path/' . $icon;
- }
- throw new \RuntimeException();
- };
- };
-
- return [
- 'does not find anything' => [
- $nothing,
- false,
- null,
- ],
- 'nothing if request dark but only bright available' => [
- $createCallback(['app.svg']),
- true,
- null,
- ],
- 'nothing if request bright but only dark available' => [
- $createCallback(['app-dark.svg']),
- false,
- null,
- ],
- 'bright and only app.svg' => [
- $createCallback(['app.svg']),
- false,
- '/path/app.svg',
- ],
- 'dark and only app-dark.svg' => [
- $createCallback(['app-dark.svg']),
- true,
- '/path/app-dark.svg',
- ],
- 'dark only appname -dark.svg' => [
- $createCallback(['test-dark.svg']),
- true,
- '/path/test-dark.svg',
- ],
- 'bright and only appname.svg' => [
- $createCallback(['test.svg']),
- false,
- '/path/test.svg',
- ],
- 'priotize custom over default' => [
- $createCallback(['app.svg', 'test.svg']),
- false,
- '/path/test.svg',
- ],
- 'defaults to bright' => [
- $createCallback(['test-dark.svg', 'test.svg']),
- null,
- '/path/test.svg',
- ],
- 'no dark icon on default' => [
- $createCallback(['test-dark.svg', 'test.svg', 'app-dark.svg', 'app.svg']),
- false,
- '/path/test.svg',
- ],
- 'no bright icon on dark' => [
- $createCallback(['test-dark.svg', 'test.svg', 'app-dark.svg', 'app.svg']),
- true,
- '/path/test-dark.svg',
- ],
- ];
- }
-
- public function testEnableApp(): void {
- // making sure "files_trashbin" is disabled
- if ($this->manager->isEnabledForUser('files_trashbin')) {
- $this->manager->disableApp('files_trashbin');
- }
- $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new AppEnableEvent('files_trashbin'));
-
- $this->manager->enableApp('files_trashbin');
- $this->assertEquals('yes', $this->appConfig->getValue('files_trashbin', 'enabled', 'no'));
- }
-
- public function testDisableApp(): void {
- $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new AppDisableEvent('files_trashbin'));
-
- $this->manager->disableApp('files_trashbin');
- $this->assertEquals('no', $this->appConfig->getValue('files_trashbin', 'enabled', 'no'));
- }
-
- public function testNotEnableIfNotInstalled(): void {
- $this->expectException(AppPathNotFoundException::class);
- $this->expectExceptionMessage('Could not find path for some_random_name_which_i_hope_is_not_an_app');
- $this->appConfig->expects(self::never())
- ->method('setValue');
-
- $this->manager->enableApp('some_random_name_which_i_hope_is_not_an_app');
- }
-
- public function testEnableAppForGroups(): void {
- $group1 = $this->createMock(IGroup::class);
- $group1->method('getGID')
- ->willReturn('group1');
- $group2 = $this->createMock(IGroup::class);
- $group2->method('getGID')
- ->willReturn('group2');
-
- $groups = [$group1, $group2];
-
- /** @var AppManager|MockObject $manager */
- $manager = $this->getMockBuilder(AppManager::class)
- ->setConstructorArgs([
- $this->userSession,
- $this->config,
- $this->groupManager,
- $this->cacheFactory,
- $this->eventDispatcher,
- $this->logger,
- $this->serverVersion,
- $this->configManager,
- $this->dependencyAnalyzer,
- ])
- ->onlyMethods([
- 'getAppPath',
- ])
- ->getMock();
-
- $manager->expects($this->exactly(2))
- ->method('getAppPath')
- ->with('test')
- ->willReturn('apps/test');
-
- $this->eventDispatcher->expects($this->once())
- ->method('dispatchTyped')
- ->with(new AppEnableEvent('test', ['group1', 'group2']));
-
- $manager->enableAppForGroups('test', $groups);
- $this->assertEquals('["group1","group2"]', $this->appConfig->getValue('test', 'enabled', 'no'));
- }
-
- public static function dataEnableAppForGroupsAllowedTypes(): array {
- return [
- [[]],
- [[
- 'types' => [],
- ]],
- [[
- 'types' => ['nickvergessen'],
- ]],
- ];
- }
-
- /**
- * @param array $appInfo
- */
- #[DataProvider('dataEnableAppForGroupsAllowedTypes')]
- public function testEnableAppForGroupsAllowedTypes(array $appInfo): void {
- $group1 = $this->createMock(IGroup::class);
- $group1->method('getGID')
- ->willReturn('group1');
- $group2 = $this->createMock(IGroup::class);
- $group2->method('getGID')
- ->willReturn('group2');
-
- $groups = [$group1, $group2];
-
- /** @var AppManager|MockObject $manager */
- $manager = $this->getMockBuilder(AppManager::class)
- ->setConstructorArgs([
- $this->userSession,
- $this->config,
- $this->groupManager,
- $this->cacheFactory,
- $this->eventDispatcher,
- $this->logger,
- $this->serverVersion,
- $this->configManager,
- $this->dependencyAnalyzer,
- ])
- ->onlyMethods([
- 'getAppPath',
- 'getAppInfo',
- ])
- ->getMock();
-
- $manager->expects($this->once())
- ->method('getAppPath')
- ->with('test')
- ->willReturn('');
-
- $manager->expects($this->once())
- ->method('getAppInfo')
- ->with('test')
- ->willReturn($appInfo);
-
- $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new AppEnableEvent('test', ['group1', 'group2']));
-
- $manager->enableAppForGroups('test', $groups);
- $this->assertEquals('["group1","group2"]', $this->appConfig->getValue('test', 'enabled', 'no'));
- }
-
- public static function dataEnableAppForGroupsForbiddenTypes(): array {
- return [
- ['filesystem'],
- ['prelogin'],
- ['authentication'],
- ['logging'],
- ['prevent_group_restriction'],
- ];
- }
-
- /**
- *
- * @param string $type
- *
- */
- #[DataProvider('dataEnableAppForGroupsForbiddenTypes')]
- public function testEnableAppForGroupsForbiddenTypes($type): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('test can\'t be enabled for groups.');
-
- $group1 = $this->createMock(IGroup::class);
- $group1->method('getGID')
- ->willReturn('group1');
- $group2 = $this->createMock(IGroup::class);
- $group2->method('getGID')
- ->willReturn('group2');
-
- $groups = [$group1, $group2];
-
- /** @var AppManager|MockObject $manager */
- $manager = $this->getMockBuilder(AppManager::class)
- ->setConstructorArgs([
- $this->userSession,
- $this->config,
- $this->groupManager,
- $this->cacheFactory,
- $this->eventDispatcher,
- $this->logger,
- $this->serverVersion,
- $this->configManager,
- $this->dependencyAnalyzer,
- ])
- ->onlyMethods([
- 'getAppPath',
- 'getAppInfo',
- ])
- ->getMock();
-
- $manager->expects($this->once())
- ->method('getAppPath')
- ->with('test')
- ->willReturn('');
-
- $manager->expects($this->once())
- ->method('getAppInfo')
- ->with('test')
- ->willReturn([
- 'types' => [$type],
- ]);
-
- $this->eventDispatcher->expects($this->never())->method('dispatchTyped')->with(new AppEnableEvent('test', ['group1', 'group2']));
-
- $manager->enableAppForGroups('test', $groups);
- }
-
- public function testIsInstalledEnabled(): void {
- $this->appConfig->setValue('test', 'enabled', 'yes');
- $this->assertTrue($this->manager->isEnabledForAnyone('test'));
- }
-
- public function testIsInstalledDisabled(): void {
- $this->appConfig->setValue('test', 'enabled', 'no');
- $this->assertFalse($this->manager->isEnabledForAnyone('test'));
- }
-
- public function testIsInstalledEnabledForGroups(): void {
- $this->appConfig->setValue('test', 'enabled', '["foo"]');
- $this->assertTrue($this->manager->isEnabledForAnyone('test'));
- }
-
- private function newUser($uid) {
- $user = $this->createMock(IUser::class);
- $user->method('getUID')
- ->willReturn($uid);
-
- return $user;
- }
-
- public function testIsEnabledForUserEnabled(): void {
- $this->appConfig->setValue('test', 'enabled', 'yes');
- $user = $this->newUser('user1');
- $this->assertTrue($this->manager->isEnabledForUser('test', $user));
- }
-
- public function testIsEnabledForUserDisabled(): void {
- $this->appConfig->setValue('test', 'enabled', 'no');
- $user = $this->newUser('user1');
- $this->assertFalse($this->manager->isEnabledForUser('test', $user));
- }
-
- public function testGetAppPath(): void {
- $this->assertEquals(\OC::$SERVERROOT . '/apps/files', $this->manager->getAppPath('files'));
- }
-
- public function testGetAppPathSymlink(): void {
- $fakeAppDirname = sha1(uniqid('test', true));
- $fakeAppPath = sys_get_temp_dir() . '/' . $fakeAppDirname;
- $fakeAppLink = \OC::$SERVERROOT . '/' . $fakeAppDirname;
-
- mkdir($fakeAppPath);
- if (symlink($fakeAppPath, $fakeAppLink) === false) {
- $this->markTestSkipped('Failed to create symlink');
- }
-
- // Use the symlink as the app path
- \OC::$APPSROOTS[] = [
- 'path' => $fakeAppLink,
- 'url' => \OC::$WEBROOT . '/' . $fakeAppDirname,
- 'writable' => false,
- ];
-
- $fakeTestAppPath = $fakeAppPath . '/' . 'test_test_app';
- mkdir($fakeTestAppPath);
-
- $generatedAppPath = $this->manager->getAppPath('test_test_app');
-
- rmdir($fakeTestAppPath);
- unlink($fakeAppLink);
- rmdir($fakeAppPath);
-
- $this->assertEquals($fakeAppLink . '/test_test_app', $generatedAppPath);
- }
-
- public function testGetAppPathFail(): void {
- $this->expectException(AppPathNotFoundException::class);
- $this->manager->getAppPath('testnotexisting');
- }
-
- public function testIsEnabledForUserEnabledForGroup(): void {
- $user = $this->newUser('user1');
- $this->groupManager->expects($this->once())
- ->method('getUserGroupIds')
- ->with($user)
- ->willReturn(['foo', 'bar']);
-
- $this->appConfig->setValue('test', 'enabled', '["foo"]');
- $this->assertTrue($this->manager->isEnabledForUser('test', $user));
- }
-
- public function testIsEnabledForUserDisabledForGroup(): void {
- $user = $this->newUser('user1');
- $this->groupManager->expects($this->once())
- ->method('getUserGroupIds')
- ->with($user)
- ->willReturn(['bar']);
-
- $this->appConfig->setValue('test', 'enabled', '["foo"]');
- $this->assertFalse($this->manager->isEnabledForUser('test', $user));
- }
-
- public function testIsEnabledForUserLoggedOut(): void {
- $this->appConfig->setValue('test', 'enabled', '["foo"]');
- $this->assertFalse($this->manager->isEnabledForUser('test'));
- }
-
- public function testIsEnabledForUserLoggedIn(): void {
- $user = $this->newUser('user1');
-
- $this->userSession->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
- $this->groupManager->expects($this->once())
- ->method('getUserGroupIds')
- ->with($user)
- ->willReturn(['foo', 'bar']);
-
- $this->appConfig->setValue('test', 'enabled', '["foo"]');
- $this->assertTrue($this->manager->isEnabledForUser('test'));
- }
-
- public function testGetEnabledApps(): void {
- $this->appConfig->setValue('test1', 'enabled', 'yes');
- $this->appConfig->setValue('test2', 'enabled', 'no');
- $this->appConfig->setValue('test3', 'enabled', '["foo"]');
- $apps = [
- 'appstore',
- 'cloud_federation_api',
- 'dav',
- 'federatedfilesharing',
- 'files',
- 'lookup_server_connector',
- 'oauth2',
- 'profile',
- 'provisioning_api',
- 'settings',
- 'sharing',
- 'test1',
- 'test3',
- 'theming',
- 'twofactor_backupcodes',
- 'viewer',
- 'workflowengine',
- ];
- $this->assertEquals($apps, $this->manager->getEnabledApps());
- }
-
- public function testGetAppsForUser(): void {
- $user = $this->newUser('user1');
- $this->groupManager->expects($this->any())
- ->method('getUserGroupIds')
- ->with($user)
- ->willReturn(['foo', 'bar']);
-
- $this->appConfig->setValue('test1', 'enabled', 'yes');
- $this->appConfig->setValue('test2', 'enabled', 'no');
- $this->appConfig->setValue('test3', 'enabled', '["foo"]');
- $this->appConfig->setValue('test4', 'enabled', '["asd"]');
- $enabled = [
- 'appstore',
- 'cloud_federation_api',
- 'dav',
- 'federatedfilesharing',
- 'files',
- 'lookup_server_connector',
- 'oauth2',
- 'profile',
- 'provisioning_api',
- 'settings',
- 'sharing',
- 'test1',
- 'test3',
- 'theming',
- 'twofactor_backupcodes',
- 'viewer',
- 'workflowengine',
- ];
- $this->assertEquals($enabled, $this->manager->getEnabledAppsForUser($user));
- }
-
- public function testGetAppsNeedingUpgrade(): void {
- /** @var AppManager&MockObject $manager */
- $manager = $this->getMockBuilder(AppManager::class)
- ->setConstructorArgs([
- $this->userSession,
- $this->config,
- $this->groupManager,
- $this->cacheFactory,
- $this->eventDispatcher,
- $this->logger,
- $this->serverVersion,
- $this->configManager,
- $this->dependencyAnalyzer,
- ])
- ->onlyMethods(['getAppInfo'])
- ->getMock();
-
- $appInfos = [
- 'appstore' => ['id' => 'appstore'],
- 'cloud_federation_api' => ['id' => 'cloud_federation_api'],
- 'dav' => ['id' => 'dav'],
- 'files' => ['id' => 'files'],
- 'federatedfilesharing' => ['id' => 'federatedfilesharing'],
- 'profile' => ['id' => 'profile'],
- 'provisioning_api' => ['id' => 'provisioning_api'],
- 'lookup_server_connector' => ['id' => 'lookup_server_connector'],
- 'test1' => ['id' => 'test1', 'version' => '1.0.1', 'requiremax' => '9.0.0'],
- 'test2' => ['id' => 'test2', 'version' => '1.0.0', 'requiremin' => '8.2.0'],
- 'test3' => ['id' => 'test3', 'version' => '1.2.4', 'requiremin' => '9.0.0'],
- 'test4' => ['id' => 'test4', 'version' => '3.0.0', 'requiremin' => '8.1.0'],
- 'testnoversion' => ['id' => 'testnoversion', 'requiremin' => '8.2.0'],
- 'settings' => ['id' => 'settings'],
- 'sharing' => ['id' => 'sharing'],
- 'theming' => ['id' => 'theming'],
- 'twofactor_backupcodes' => ['id' => 'twofactor_backupcodes'],
- 'viewer' => ['id' => 'viewer'],
- 'workflowengine' => ['id' => 'workflowengine'],
- 'oauth2' => ['id' => 'oauth2'],
- ];
-
- $manager->expects($this->any())
- ->method('getAppInfo')
- ->willReturnCallback(
- function ($appId) use ($appInfos) {
- return $appInfos[$appId];
- }
- );
-
- $this->appConfig->setValue('test1', 'enabled', 'yes');
- $this->appConfig->setValue('test1', 'installed_version', '1.0.0');
- $this->appConfig->setValue('test2', 'enabled', 'yes');
- $this->appConfig->setValue('test2', 'installed_version', '1.0.0');
- $this->appConfig->setValue('test3', 'enabled', 'yes');
- $this->appConfig->setValue('test3', 'installed_version', '1.0.0');
- $this->appConfig->setValue('test4', 'enabled', 'yes');
- $this->appConfig->setValue('test4', 'installed_version', '2.4.0');
-
- $apps = $manager->getAppsNeedingUpgrade('8.2.0');
-
- $this->assertCount(2, $apps);
- $this->assertEquals('test1', $apps[0]['id']);
- $this->assertEquals('test4', $apps[1]['id']);
- }
-
- public function testGetIncompatibleApps(): void {
- /** @var AppManager|MockObject $manager */
- $manager = $this->getMockBuilder(AppManager::class)
- ->setConstructorArgs([
- $this->userSession,
- $this->config,
- $this->groupManager,
- $this->cacheFactory,
- $this->eventDispatcher,
- $this->logger,
- $this->serverVersion,
- $this->configManager,
- $this->dependencyAnalyzer,
- ])
- ->onlyMethods(['getAppInfo'])
- ->getMock();
-
- $appInfos = [
- 'appstore' => ['id' => 'appstore'],
- 'cloud_federation_api' => ['id' => 'cloud_federation_api'],
- 'dav' => ['id' => 'dav'],
- 'files' => ['id' => 'files'],
- 'federatedfilesharing' => ['id' => 'federatedfilesharing'],
- 'profile' => ['id' => 'profile'],
- 'provisioning_api' => ['id' => 'provisioning_api'],
- 'lookup_server_connector' => ['id' => 'lookup_server_connector'],
- 'test1' => ['id' => 'test1', 'version' => '1.0.1', 'requiremax' => '8.0.0'],
- 'test2' => ['id' => 'test2', 'version' => '1.0.0', 'requiremin' => '8.2.0'],
- 'test3' => ['id' => 'test3', 'version' => '1.2.4', 'requiremin' => '9.0.0'],
- 'settings' => ['id' => 'settings'],
- 'sharing' => ['id' => 'sharing'],
- 'testnoversion' => ['id' => 'testnoversion', 'requiremin' => '8.2.0'],
- 'theming' => ['id' => 'theming'],
- 'twofactor_backupcodes' => ['id' => 'twofactor_backupcodes'],
- 'workflowengine' => ['id' => 'workflowengine'],
- 'oauth2' => ['id' => 'oauth2'],
- 'viewer' => ['id' => 'viewer'],
- ];
-
- $manager->expects($this->any())
- ->method('getAppInfo')
- ->willReturnCallback(
- function ($appId) use ($appInfos) {
- return $appInfos[$appId];
- }
- );
-
- $this->appConfig->setValue('test1', 'enabled', 'yes');
- $this->appConfig->setValue('test2', 'enabled', 'yes');
- $this->appConfig->setValue('test3', 'enabled', 'yes');
-
- $apps = $manager->getIncompatibleApps('8.2.0');
-
- $this->assertCount(2, $apps);
- $this->assertEquals('test1', $apps[0]['id']);
- $this->assertEquals('test3', $apps[1]['id']);
- }
-
- public function testGetEnabledAppsForGroup(): void {
- $group = $this->createMock(IGroup::class);
- $group->expects($this->any())
- ->method('getGID')
- ->willReturn('foo');
-
- $this->appConfig->setValue('test1', 'enabled', 'yes');
- $this->appConfig->setValue('test2', 'enabled', 'no');
- $this->appConfig->setValue('test3', 'enabled', '["foo"]');
- $this->appConfig->setValue('test4', 'enabled', '["asd"]');
- $enabled = [
- 'appstore',
- 'cloud_federation_api',
- 'dav',
- 'federatedfilesharing',
- 'files',
- 'lookup_server_connector',
- 'oauth2',
- 'profile',
- 'provisioning_api',
- 'settings',
- 'sharing',
- 'test1',
- 'test3',
- 'theming',
- 'twofactor_backupcodes',
- 'viewer',
- 'workflowengine',
- ];
- $this->assertEquals($enabled, $this->manager->getEnabledAppsForGroup($group));
- }
-
- public function testGetAppRestriction(): void {
- $this->appConfig->setValue('test1', 'enabled', 'yes');
- $this->appConfig->setValue('test2', 'enabled', 'no');
- $this->appConfig->setValue('test3', 'enabled', '["foo"]');
-
- $this->assertEquals([], $this->manager->getAppRestriction('test1'));
- $this->assertEquals([], $this->manager->getAppRestriction('test2'));
- $this->assertEquals(['foo'], $this->manager->getAppRestriction('test3'));
- }
-
- public static function isBackendRequiredDataProvider(): array {
- return [
- // backend available
- [
- 'caldav',
- ['app1' => ['caldav']],
- true,
- ],
- [
- 'caldav',
- ['app1' => [], 'app2' => ['foo'], 'app3' => ['caldav']],
- true,
- ],
- // backend not available
- [
- 'caldav',
- ['app3' => [], 'app1' => ['foo'], 'app2' => ['bar', 'baz']],
- false,
- ],
- // no app available
- [
- 'caldav',
- [],
- false,
- ],
- ];
- }
-
- #[DataProvider('isBackendRequiredDataProvider')]
- public function testIsBackendRequired(
- string $backend,
- array $appBackends,
- bool $expected,
- ): void {
- $appInfoData = array_map(
- static fn (array $backends) => ['dependencies' => ['backend' => $backends]],
- $appBackends,
- );
-
- $reflection = new \ReflectionClass($this->manager);
- $property = $reflection->getProperty('appInfos');
- $property->setValue($this->manager, $appInfoData);
-
- $this->assertEquals($expected, $this->manager->isBackendRequired($backend));
- }
-
- public function testGetAppVersion() {
- $manager = $this->getMockBuilder(AppManager::class)
- ->setConstructorArgs([
- $this->userSession,
- $this->config,
- $this->groupManager,
- $this->cacheFactory,
- $this->eventDispatcher,
- $this->logger,
- $this->serverVersion,
- $this->configManager,
- $this->dependencyAnalyzer,
- ])
- ->onlyMethods([
- 'getAppInfo',
- ])
- ->getMock();
-
- $manager->expects(self::once())
- ->method('getAppInfo')
- ->with('myapp')
- ->willReturn(['version' => '99.99.99-rc.99']);
-
- $this->serverVersion
- ->expects(self::never())
- ->method('getVersionString');
-
- $this->assertEquals(
- '99.99.99-rc.99',
- $manager->getAppVersion('myapp'),
- );
- }
-
- public function testGetAppVersionCore() {
- $manager = $this->getMockBuilder(AppManager::class)
- ->setConstructorArgs([
- $this->userSession,
- $this->config,
- $this->groupManager,
- $this->cacheFactory,
- $this->eventDispatcher,
- $this->logger,
- $this->serverVersion,
- $this->configManager,
- $this->dependencyAnalyzer,
- ])
- ->onlyMethods([
- 'getAppInfo',
- ])
- ->getMock();
-
- $manager->expects(self::never())
- ->method('getAppInfo');
-
- $this->serverVersion
- ->expects(self::once())
- ->method('getVersionString')
- ->willReturn('1.2.3-beta.4');
-
- $this->assertEquals(
- '1.2.3-beta.4',
- $manager->getAppVersion('core'),
- );
- }
-
- public function testGetAppVersionUnknown() {
- $manager = $this->getMockBuilder(AppManager::class)
- ->setConstructorArgs([
- $this->userSession,
- $this->config,
- $this->groupManager,
- $this->cacheFactory,
- $this->eventDispatcher,
- $this->logger,
- $this->serverVersion,
- $this->configManager,
- $this->dependencyAnalyzer,
- ])
- ->onlyMethods([
- 'getAppInfo',
- ])
- ->getMock();
-
- $manager->expects(self::once())
- ->method('getAppInfo')
- ->with('unknown')
- ->willReturn(null);
-
- $this->serverVersion
- ->expects(self::never())
- ->method('getVersionString');
-
- $this->assertEquals(
- '0',
- $manager->getAppVersion('unknown'),
- );
- }
-
- public static function dataCleanAppId(): array {
- return [
- ['simple', 'simple'],
- ['UPPERCASEa', 'a'],
- ['MixEdCaSe', 'ixdae'],
- ['007startwithdigit', 'startwithdigit'],
- ['0-numb3rs-4ll0w3d-1n-m1ddle-0', 'numb3rs-4ll0w3d-1n-m1ddle-0'],
- ['hyphen-and_underscore_allowed', 'hyphen-and_underscore_allowed'],
- ['_but-not-at-the-end_', 'but-not-at-the-end'],
- ['-but-not-at-the-end-', 'but-not-at-the-end'],
- ['--_but-not-at-the-end___', 'but-not-at-the-end'],
- [' also remove all spaces', 'alsoremoveallspaces'],
- ['a«"«»()@+-/*=%\{}…~|&œ—<>[]^±_−÷×≠‰A', 'a-_'],
- ];
- }
-
- #[DataProvider('dataCleanAppId')]
- public function testCleanAppId(string $inputString, string $appid): void {
- $this->assertEquals($appid, $this->manager->cleanAppId($inputString));
- }
-}
--- a/tests/lib/App/AppStore/AppStoreLinkVisibilityTest.php
+++ /dev/null
@@ -1,123 +0,0 @@
-config = $this->createMock(IConfig::class);
- $this->appConfig = $this->createMock(IAppConfig::class);
- $this->registry = $this->createMock(IRegistry::class);
-
- $this->visibility = new AppStoreLinkVisibility(
- $this->config,
- $this->appConfig,
- $this->registry,
- );
- }
-
- /**
- * @param bool $stored whether an admin set the config key
- * @param bool $value the stored value, or the lexicon default when $stored is false
- */
- private function arrange(bool $stored, bool $value, bool $appStoreEnabled, bool $subscription): void {
- $this->appConfig->method('hasKey')
- ->with('core', ConfigLexicon::APPSTORE_LINK_SHOWN)
- ->willReturn($stored);
- $this->appConfig->method('getValueBool')
- ->with('core', ConfigLexicon::APPSTORE_LINK_SHOWN)
- ->willReturn($value);
- $this->config->method('getSystemValueBool')
- ->with('appstoreenabled', true)
- ->willReturn($appStoreEnabled);
- $this->registry->method('delegateHasValidSubscription')
- ->willReturn($subscription);
- }
-
- public static function dataBool(): array {
- return [
- 'shown' => [true],
- 'hidden' => [false],
- ];
- }
-
- #[DataProvider('dataBool')]
- public function testStoredValueWinsOverSubscriptionAndDisabledAppStore(bool $stored): void {
- $this->arrange(stored: true, value: $stored, appStoreEnabled: false, subscription: true);
-
- self::assertSame($stored, $this->visibility->isShownToUsers());
- }
-
- public function testHiddenWhenAppStoreIsDisabled(): void {
- $this->arrange(stored: false, value: true, appStoreEnabled: false, subscription: false);
-
- self::assertFalse($this->visibility->isShownToUsers());
- }
-
- public function testHiddenWhenSubscriptionIsAvailable(): void {
- $this->arrange(stored: false, value: true, appStoreEnabled: true, subscription: true);
-
- self::assertFalse($this->visibility->isShownToUsers());
- }
-
- #[DataProvider('dataBool')]
- public function testUnstoredKeyReturnsTheLexiconDefault(bool $default): void {
- $this->arrange(stored: false, value: $default, appStoreEnabled: true, subscription: false);
-
- self::assertSame($default, $this->visibility->isShownToUsers());
- }
-
- public static function dataLexiconPreset(): array {
- return [
- // Existing instances run without a preset and must keep the link.
- [Preset::NONE, '1'],
- [Preset::FAMILY, '1'],
- [Preset::UNIVERSITY, '0'],
- ];
- }
-
- /**
- * The lexicon default {@see AppStoreLinkVisibility} falls back to is preset
- * dependent. A fresh entry is built per case because {@see Entry::getDefault()}
- * memoizes the first default it is asked for.
- */
- #[DataProvider('dataLexiconPreset')]
- public function testLexiconPresetDefault(Preset $preset, string $expected): void {
- self::assertSame($expected, $this->lexiconEntry()->getDefault($preset));
- }
-
- private function lexiconEntry(): Entry {
- foreach ((new ConfigLexicon())->getAppConfigs() as $entry) {
- if ($entry->getKey() === ConfigLexicon::APPSTORE_LINK_SHOWN) {
- return $entry;
- }
- }
-
- self::fail('No lexicon entry for ' . ConfigLexicon::APPSTORE_LINK_SHOWN);
- }
-}
--- a/tests/lib/App/AppStore/Bundles/BundleBase.php
+++ /dev/null
@@ -1,45 +0,0 @@
-l10n = $this->createMock(IL10N::class);
- $this->l10n->method('t')
- ->willReturnCallback(function ($text, $parameters = []) {
- return vsprintf($text, $parameters);
- });
- }
-
- public function testGetIdentifier(): void {
- $this->assertSame($this->bundleIdentifier, $this->bundle->getIdentifier());
- }
-
- public function testGetName(): void {
- $this->assertSame($this->bundleName, $this->bundle->getName());
- }
-
- public function testGetAppIdentifiers(): void {
- $this->assertSame($this->bundleAppIds, $this->bundle->getAppIdentifiers());
- }
-}
--- a/tests/lib/App/AppStore/Bundles/BundleFetcherTest.php
+++ /dev/null
@@ -1,62 +0,0 @@
-l10n = $this->createMock(IL10N::class);
- $factory = $this->createMock(IFactory::class);
- $factory->method('get')->willReturn($this->l10n);
-
- $this->bundleFetcher = new BundleFetcher($factory);
- }
-
- public function testGetBundles(): void {
- $expected = [
- new EnterpriseBundle($this->l10n),
- new HubBundle($this->l10n),
- new GroupwareBundle($this->l10n),
- new SocialSharingBundle($this->l10n),
- new EducationBundle($this->l10n),
- new PublicSectorBundle($this->l10n),
- ];
- $this->assertEquals($expected, $this->bundleFetcher->getBundles());
- }
-
- public function testGetBundleByIdentifier(): void {
- $this->assertEquals(new EnterpriseBundle($this->l10n), $this->bundleFetcher->getBundleByIdentifier('EnterpriseBundle'));
- $this->assertEquals(new GroupwareBundle($this->l10n), $this->bundleFetcher->getBundleByIdentifier('GroupwareBundle'));
- }
-
- public function testGetBundleByIdentifierWithException(): void {
- $this->expectException(\BadMethodCallException::class);
- $this->expectExceptionMessage('Bundle with specified identifier does not exist');
-
- $this->bundleFetcher->getBundleByIdentifier('NotExistingBundle');
- }
-}
--- a/tests/lib/App/AppStore/Bundles/EducationBundleTest.php
+++ /dev/null
@@ -1,31 +0,0 @@
-bundle = new EducationBundle($this->l10n);
- $this->bundleIdentifier = 'EducationBundle';
- $this->bundleName = 'Education bundle';
- $this->bundleAppIds = [
- 'dashboard',
- 'circles',
- 'groupfolders',
- 'announcementcenter',
- 'quota_warning',
- 'user_saml',
- 'whiteboard',
- ];
- }
-}
--- a/tests/lib/App/AppStore/Bundles/EnterpriseBundleTest.php
+++ /dev/null
@@ -1,31 +0,0 @@
-bundle = new EnterpriseBundle($this->l10n);
- $this->bundleIdentifier = 'EnterpriseBundle';
- $this->bundleName = 'Enterprise bundle';
- $this->bundleAppIds = [
- 'admin_audit',
- 'user_ldap',
- 'files_retention',
- 'files_automatedtagging',
- 'user_saml',
- 'files_accesscontrol',
- 'terms_of_service',
- ];
- }
-}
--- a/tests/lib/App/AppStore/Bundles/GroupwareBundleTest.php
+++ /dev/null
@@ -1,28 +0,0 @@
-bundle = new GroupwareBundle($this->l10n);
- $this->bundleIdentifier = 'GroupwareBundle';
- $this->bundleName = 'Groupware bundle';
- $this->bundleAppIds = [
- 'calendar',
- 'contacts',
- 'deck',
- 'mail'
- ];
- }
-}
--- a/tests/lib/App/AppStore/Bundles/SocialSharingBundleTest.php
+++ /dev/null
@@ -1,28 +0,0 @@
-bundle = new SocialSharingBundle($this->l10n);
- $this->bundleIdentifier = 'SocialSharingBundle';
- $this->bundleName = 'Social sharing bundle';
- $this->bundleAppIds = [
- 'socialsharing_twitter',
- 'socialsharing_facebook',
- 'socialsharing_email',
- 'socialsharing_diaspora',
- ];
- }
-}
--- a/tests/lib/App/AppStore/Fetcher/AppDiscoverFetcherTest.php
+++ /dev/null
@@ -1,118 +0,0 @@
-fileName = 'discover.json';
- $this->endpoint = 'https://apps.nextcloud.com/api/v1/discover.json';
-
- $this->compareVersion = $this->createMock(CompareVersion::class);
-
- $this->fetcher = new AppDiscoverFetcher(
- $this->appDataFactory,
- $this->clientService,
- $this->timeFactory,
- $this->config,
- $this->logger,
- $this->registry,
- $this->compareVersion,
- );
- }
-
- public function testAppstoreDisabled(): void {
- $this->config
- ->method('getSystemValueBool')
- ->willReturnCallback(function ($var, $default) {
- if ($var === 'appstoreenabled') {
- return false;
- }
- return $default;
- });
- $this->appData
- ->expects($this->never())
- ->method('getFolder');
-
- $this->assertEquals([], $this->fetcher->get());
- }
-
- public function testNoInternet(): void {
- $this->config
- ->method('getSystemValueBool')
- ->willReturnCallback(function ($var, $default) {
- if ($var === 'has_internet_connection') {
- return false;
- }
- return $default;
- });
- $this->config
- ->method('getSystemValueString')
- ->willReturnCallback(function ($var, $default) {
- return $default;
- });
- $this->appData
- ->expects($this->never())
- ->method('getFolder');
-
- $this->assertEquals([], $this->fetcher->get());
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetETag')]
- public function testGetEtag(?string $expected, bool $throws, string $content = ''): void {
- $folder = $this->createMock(ISimpleFolder::class);
- if (!$throws) {
- $file = $this->createMock(ISimpleFile::class);
- $file->expects($this->once())
- ->method('getContent')
- ->willReturn($content);
- $folder->expects($this->once())
- ->method('getFile')
- ->with('discover.json')
- ->willReturn($file);
- } else {
- $folder->expects($this->once())
- ->method('getFile')
- ->with('discover.json')
- ->willThrowException(new NotFoundException(''));
- }
-
- $this->appData->expects($this->once())
- ->method('getFolder')
- ->with('/')
- ->willReturn($folder);
-
- $etag = $this->fetcher->getETag();
- $this->assertEquals($expected, $etag);
- if ($expected !== null) {
- $this->assertTrue(gettype($etag) === 'string');
- }
- }
-
- public static function dataGetETag(): array {
- return [
- 'file not found' => [null, true],
- 'empty file' => [null, false, ''],
- 'missing etag' => [null, false, '{ "foo": "bar" }'],
- 'valid etag' => ['test', false, '{ "ETag": "test" }'],
- 'numeric etag' => ['132', false, '{ "ETag": 132 }'],
- ];
- }
-}
--- a/tests/lib/App/AppStore/Fetcher/AppFetcherTest.php
+++ /dev/null
@@ -1,2252 +0,0 @@
-=9.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/juliushaertl/direct_menu/releases/download/v0.9.2/direct_menu.tar.gz","created":"2016-10-06T14:01:05.578297Z","licenses":["agpl"],"lastModified":"2016-10-06T14:01:05.643813Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=9 <=11","signature":"ERBS9G5bZ3vwCizz2Ht5DehsVJmb63bzF3aYcH7xjbDVMPagOFWdUAiLDwTeZR1n\ni4gdZ73J/IjHQQJoOPwtCjgbZgLPFqL5x13CLUO9mb/33dZe/+gqEDc/3AuJ4TlA\nXUdLxHRb1bwIlJOwuSr/E24452VG20WUhLXBoM0Zm7WcMxvJWo2zAWnuqnLX3dy9\ncPB4PX+6JU2lUMINj8OYQmM1QnqvjG8YV0cYHbBbnSicOGwXEnni7mojsC8T0cn7\nYEJ2O2iO9hh3fvFEXUzDcL7tDQ5bZqm63Oa991bsAJxFo/RbzeJRh//DcOrd8Ufn\nu2SqRhwybS8j4YvfjAL9RPdRfPLwf6X2gx/Y6QFrKHH0QMI/9J/ZFyoUQcqKbsHV\n85O+yuWoqVmza71tkp4n9PuMdprCinaVvHbHbNGUf2SIh9BWuEQuVvvnvB+ZW8XY\n+Cl+unzk3WgOgT0iY3uEmsQcrLIo4DSKhcNgD1NS13fR/JTSavvmOqBarUMFZfVC\nbkR1DTBCyDjdpNBidpa3/26675dz5IT5Zedp4BBBREQzX08cIhJx5mgqDdX3CU09\nuWtzoaLi71/1BWTFAN+Y9VyfZ8/Z3Pg3vKedRJ565mztIj0geL3riEsC5YnPS0+C\n+a3B9sDiiOa101EORzX3lrPqL7reEPdCxrIwN+hKFBQ=","translations":{}}],"screenshots":[{"url":"https://bitgrid.net/~jus/direct_menu_nc.png"}],"translations":{"en":{"name":"Direct Menu","summary":"Provide easy access to all apps in the header.","description":"Provide easy access to all apps in the header."}},"isFeatured":false,"authors":[{"name":"Julius Härtl","mail":"","homepage":""}],"ratingRecent":0.5,"ratingOverall":0.5,"ratingNumRecent":0,"ratingNumOverall":0,"certificate":"-----BEGIN CERTIFICATE-----\r\nMIIEBjCCAu4CAhADMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD\r\nVQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI\r\nMTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB\r\ndXRob3JpdHkwHhcNMTYwOTE0MTI1MDU0WhcNMjYxMjIxMTI1MDU0WjAWMRQwEgYD\r\nVQQDDAtkaXJlY3RfbWVudTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB\r\nAMkzWsAkKP/40ktvJMpnr0IJNVoPOR0hvh24igcDskL1WKiD2eiRUenj5LE0Nvn+\r\nsiGmWsAqRVpdiz+Y8ghQqQMzKi43IrRN0AxlCrHWrSqBZT3wIAUcFz4RzEoFxc1N\r\nUZzWma6ljukGnvt4V1ZyT+H/cjqxUkBhh/y9SS0jUen1a1grND6Rw54X46V2dlCu\r\nFH+pLsfPJJGw+QLeTGHn7dqdv18cYMAlWDCzPixVnNiCXHZcUtKMmstU2xU4R2e6\r\nzimp2rgkE4TNHrafpjH8xGdNi2FG5Dmokob/L5Q2r8jyNaW7UsFfrvLTRj371b3/\r\n2FhhxoGUvDzaG2An02Exwm52LJfdTVMHAMPZub5poHfy5vAEdZGPQ/m02l8ZK/Y2\r\n7yT807GlfPMXfdfjCxR6wNtmv7rvBDdrUZmIRNJfpFSdvlH/+MOTWnabyfQv2K4Q\r\nBIwltX6Elh0lh4ntvt1ZVtvFv+PL1Dc7QLV+w19+/LJA0mnsh7GIFYKFlbA65gA0\r\nc/w+uqDy0+5MxkR9WGPpd79KRA1tKWTis4Ny1lApK5y3zIsVGa3DfBHXcwqkWHbV\r\nwIpyuyyDsFtC1b9LTFONX7iU9cbNk5C5GTM331MdA2kLcD/D5k42GNTBSca7MkPx\r\nFx/ETSn0Ct167el30symf2AxvXjw+mBYPN71rVTMDwe9AgMBAAEwDQYJKoZIhvcN\r\nAQELBQADggEBAC0fJKnbEhXA8M283jA9GxABxLyTBcQyVVNnz2L/bYYNi81Y9iZv\r\n+U0S3qaIfoqNcV9FTKAutbsKvWyolnI7MRRK6feNuFfoP2jKubM1CnawpyT/RF2Q\r\ne/zxnB1EmeI2X5D2xceJDLB7Fy5W0EGrLixRIdFaSUommWFUm9E2hSIaNlziSBdc\r\n1J/mOQeNYO5zg5ouEt1rzQW4Mhh1I2uNQmGe4ip+Jl/2LAv3FZuu4NrSEcoXH3ro\r\nG2dF9Gtu4GiQ5fuaJknaxlgXHovfqeZwZJX9o4M+Ug81AqiY7XjdiaCPdh0Tthcx\r\n2OmWZo7UBREWenjKyFZZ/iKoqH5sdenBtpo=\r\n-----END CERTIFICATE-----"},{"id":"apporder","categories":["customization"],"userDocs":"","adminDocs":"","developerDocs":"","issueTracker":"https://github.com/juliushaertl/apporder/issues","website":"","created":"2016-10-01T09:16:47.111889Z","lastModified":"2016-10-12T19:50:16.038821Z","releases":[{"version":"0.3.3","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=9.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/juliushaertl/apporder/releases/download/0.3.3/apporder.tar.gz","created":"2016-10-12T19:14:10.802359Z","licenses":["agpl"],"lastModified":"2016-10-12T19:50:16.104357Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=9 <=11","signature":"nhlT9lhrmBxIsqh/e3RLm2NDw/U8ZvvoMyYQTLMM3H19DQmVcPYPYC9QWVTsowUzXblVaOXVGylbpKma9yOlOAqJtF3qyXecLl4+tA/Awb6BBhKPgHFdcLDAL5yy1K7/uei3SgEojMlJoI9vEK5I1C5YTh43yNH+//Im6MAuNYFUTlMXK426zdOe6ogpCI5GlYdXopqYANxcpT+WXWET6DDSM5Ev+MYwMcSAY4r8+tvARRU4ZAenRgjkBT6R5z6cD76emRax14tbV6vkjjwRcO+dQtM0tFPbd+5fsNInbauv50VzIMgjA6RnKTOI17gRsSdGlsV4vZn2aIxEPWauu6T/UohMvAE9HMn13vtbpPBSFwJAktj6yHASYGzupNQLprA0+OdyALoLZPpQAKNEXA42a4EVISBKu0EmduHJlUPeqhnIGkkGgVNWS8AWqzP2nFoPdXBgUWateiMcBTHxgEKDac5YmNc9lsXpzf1OxBaXHBhGYKuXPwIfyH3jTWb1OdwixJEuRe9dl63T9qOTRre8QWns/bMqKLibGfMtFhVB21ARJayBuX70eVvabG/2N7Y5t1zUlFygIKu51tvo3AVCRDdRrFWDvkAjxzIz5FIdALVZ+DReFYu/r4WF/w3V9rInFuEDSwb/OH4r8sQycs07tSlMyA74Y3FpjKTBxso=","translations":{}},{"version":"0.3.2","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=9.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/juliushaertl/apporder/releases/download/0.3.2/apporder.tar.gz","created":"2016-10-06T14:00:51.532409Z","licenses":["agpl"],"lastModified":"2016-10-06T14:00:51.598455Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=9 <=11","signature":"gRVFOtj9414ZNSdRH/qNB2SwVZUQh+gaFnNLFjjXjJ1MdRMCISzvwb+QU1qYuK/y\nuL8K0pn1+fFQf8A3VsC2pb6yaLQ5U9C3Guf886Flf4qtYw1P8UWRA9yOJ+6Md+PH\n6pTEiDIdm4xbmM0KkBhsE5kL8dvLIq4EwwcAh2Qq8fjytzAk1YiP+KrTaYrVwskM\nDmm0lgP4NVnjRBTX9myW6tr6N3w0tq2jJ/+a/vEDJv+5ozKJx8N5gbJNdrtI4k7I\nyaQNWJ7cngtAHmUREeoBggV5uJayDceu83PPQR6N9/WVyNyZjw1Q8/Q6e/NyiXT2\no8mGv5tHl3DBOVuv8v7gBQgDh6ppp12M81aiCZymn2XIgjw50VQ+K15KHnLHuFFw\nwuHZVcoQ7b6oR4K4TURSfPFUeNgGS4R9v6gjg1RUcSm1Pnryc6pYnh10AUY8dk6q\n1CZ6Upt6SScP2ZEGWsFwVjjQhY/ZJmNfnhaGoFOZ5L9CnzyNCkGXFg0rL36i6djb\naqFy/z+Brnklng5ct6XWoP7uDt5BaHznQj1NHSfHn0GUQ0N968zWm9vQvy+dyXyC\nxR7vKeu2ppZ2ydoeQ9CVwfhOEsGs5OvrpapQdh9KbUHcX7b7ql01J7/P6dFuNuHe\n+7/y4ex3sEVg5YBmDtF8iZ6d7zsHd6peL1s1EsLnenQ=","translations":{}}],"screenshots":[{"url":"https://bitgrid.net/~jus/apporder-nc.gif"}],"translations":{"en":{"name":"AppOrder","summary":"Sort apps in the menu with drag and drop","description":"\nEnable sorting for icons inside the app menu. The order will be saved for each\nuser individually. Administrators can define a custom default order.\nAppOrder works with the default owncloud menu as well as with the direct_menu\napp.\n\n## Set a default order for all new users\n\nGo to the Admin settings > Additional settings and drag the icons under App order.\n\n## Use first app as default app\n\nYou can easily let Nextcloud redirect your user to the first app in their\npersonal order by changing the following parameter in your config/config.php:\n\n'defaultapp' => 'apporder',\n\nUsers will now get redirected to the first app of the default order or to the\nfirst app of the user order.\n "}},"isFeatured":false,"authors":[{"name":"Julius Härtl","mail":"jus@bitgrid.net","homepage":""}],"ratingRecent":0.5,"ratingOverall":0.5,"ratingNumRecent":0,"ratingNumOverall":0,"certificate":"-----BEGIN CERTIFICATE-----\r\nMIIEAzCCAusCAhAEMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD\r\nVQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI\r\nMTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB\r\ndXRob3JpdHkwHhcNMTYwOTE0MTI1MjQ4WhcNMjYxMjIxMTI1MjQ4WjATMREwDwYD\r\nVQQDDAhhcHBvcmRlcjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKVK\r\nKn5jivCu+eRfe5BECjDOzNaGHlpiegb49Hf4nh0W7DqcoLHip5c1O2BcEYdH6rkw\r\n20WclvjoQpgavG5aFXzXzur6eKTT5TpgY5oZTLoWjbx4e+fKdhyDPTpqNZzs1pxz\r\nsZLDL/ElpbSErE0s+QK/pzP11WNPylAkI9AKSyDMO3Mbllg8I8Bt+bT7LJKYOO/T\r\nLhv9m0anLZ4HrdfimhVIoMiu3RpyRigk8titXZA94+F8Fbf7ZQ9f14Y/v3+rfJFQ\r\nii9cDoox5uUrjplH2LrMr5BodfCfydLu4uVpPWYkdccgvcZ1sugjvNXyCQgdzQDK\r\npOpiwVkkiQFaQJYbGSDblFWPl/cLtA/P/qS7s8tWyTQuc1rYlEpCHG/fG8ZFkSVK\r\n9eCMGxK908VB4IU2DHZHOHi7JvtOz8X/Ak6pIIFdARoW3rfKlrz6DD4T9jEgYq0n\r\nRe7YwCKEIU3liZJ+qG6LCa+rMlp/7sCzAmqBhaaaJyX4nnZCa2Q2cNZpItEAdwVc\r\nqxLYL1FiNFMSeeYhzJJoq5iMC3vp2LScUJJNoXZj9zv+uqTNGHr+bimchR2rHUBo\r\nPzDLFJmat03KdWcMYxcK5mxJNGHpgyqM7gySlbppY/cgAospE8/ygU2FlFWIC9N0\r\neDaY+T8QA1msnzsfMhYuOI8CRYigan1agGOMDgGxAgMBAAEwDQYJKoZIhvcNAQEL\r\nBQADggEBAGsECd+meXHg1rr8Wb6qrkDz/uxkY1J+pa5WxnkVcB6QrF3+HDtLMvYm\r\nTTS02ffLLyATNTOALZFSy4fh4At4SrNzl8dUaapgqk1T8f+y1FhfpZrEBsarrq+2\r\nCSKtBro2jcnxzI3BvHdQcx4RAGo8sUzaqKBmsy+JmAqpCSk8f1zHR94x4Akp7n44\r\n8Ha7u1GcHMPzSeScRMGJX/x06B45cLVGHH5GF2Bu/8JaCSEAsgETCMkc/XFMYrRd\r\nTu+WGOL2Ee5U4k4XFdzeSLODWby08iU+Gx3bXTR6WIvXCYeIVsCPKK/luvfGkiSR\r\nCpW1GUIA1cyulT4uyHf9g6BMdYVOsFQ=\r\n-----END CERTIFICATE-----"},{"id":"twofactor_totp","categories":["tools"],"userDocs":"","adminDocs":"","developerDocs":"","issueTracker":"","website":"","created":"2016-10-08T14:13:54.356716Z","lastModified":"2016-10-12T14:38:56.186269Z","releases":[{"version":"0.4.1","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":">=5.4.0 <7.1.0","platformVersionSpec":">=10.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/ChristophWurst/twofactor_totp/releases/download/0.4.1/twofactor_totp.tar.gz","created":"2016-10-12T14:38:56.174612Z","licenses":["agpl"],"lastModified":"2016-10-12T14:38:56.248223Z","isNightly":false,"rawPhpVersionSpec":">=5.4 <=7.0","rawPlatformVersionSpec":">=10 <=11","signature":"bnwWxmHEn8xkoWbtwhC1kIrJ0dQfAI3PUtU62k+Tru/BHt1G2aVxqO8bCdghojZ7\nzdFMlIJw4kekYFsVfLk8jzjUTZKVbNVKCdkHrVTQ0bUUryMAMLqGQ3PSRI5NX6D5\nFpkvwO1coYwU0XVWF8KAS0meX0ztSkT3Mv96LLrxr8F8SrB/MGmKIE4WTjt1fAIa\nZLAVEUo/3sNFTGLYBtL3wjctrkZvJltP8abeRfls9FkRHu+rN7R3uLFzk42uZn3X\nWpt5BBmlYm5ORbnJ2ApsxEkMNK+rOy8GIePaz5277ozTNrOnO04id1FXnS9mIsKD\n20nRzjekZH+nneQYoCTfnEFg2QXpW+a+zINbqCD5hivEU8utdpDAHFpNjIJdjXcS\n8MiCA/yvtwRnfqJ5Fy9BxJ6Gt05/GPUqT8DS7P1I1N+qxhsvFEdxhrm2yIOhif8o\nh7ro5ls+d3OQ8i3i4vdZm821Ytxdu/DQBHiVoOBarvFWwWAv2zd2VAvpTmk6J5yv\n3y+csRqpEJYd9fcVMPsTu7WBRRrpBsAqdAHJcZEwak2kz1kdOgSf8FIzP1z6Q71d\nMl2RKcPeutMHHSLiGIN/h7fM5aSs49wGgGZmfz28fHVd7/U0HFSMYmkT/GMq5tMP\nIyc+QZAN4qbX8G0k/QSTkK/L4lOT2hQiQqiSqmWItMk=","translations":{}}],"screenshots":[],"translations":{"en":{"name":"Two Factor TOTP Provider","summary":"A Two-Factor-Auth Provider for TOTP (e.g. Google Authenticator)","description":"A Two-Factor-Auth Provider for TOTP (e.g. Google Authenticator)"}},"isFeatured":true,"authors":[{"name":"Christoph Wurst","mail":"","homepage":""}],"ratingRecent":0.5,"ratingOverall":0.5,"ratingNumRecent":0,"ratingNumOverall":0,"certificate":"-----BEGIN CERTIFICATE-----\r\nMIIECTCCAvECAhASMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD\r\nVQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI\r\nMTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB\r\ndXRob3JpdHkwHhcNMTYxMDEyMDkzNDMxWhcNMjcwMTE4MDkzNDMxWjAZMRcwFQYD\r\nVQQDDA50d29mYWN0b3JfdG90cDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC\r\nggIBALC1K94104L/nOtmTygx7QNjUcnHs3yrn71mw4pMxTlonXOnMTpwxsfL1Hhu\r\n/5GMSgupTbQPlevSl6J86UMs455/sPShd6ifmAuhb8VFaAsjpizjs0RMaUg1sjmF\r\nuV18PD9FXLourx51V/c4MG5kpavlV+bLUrVMAjbsJY2+k30tCC/XkP5u8jUWmM/T\r\n5REChn7/obPgaeddhuJoILYhKEW3VNrR8Fm9SYiviB3FLhM7URDZ97IBnXYqbvbT\r\nZnvq+E74Zc7HgYwQwrjU/AqQAInhNpAR4ZM6CkWWWWaL96O1q3lCfKJNaxqC0Kg/\r\nkGn/pxYkl9062jtMUz60s9OPDyuisfyl68UyM68Ozyz4SMRLmDVbewOqQAwmAbtz\r\n8p9AQrX3Pr9tXhARR4pDSsQz1z+8ExEd6EKbhMyiTtHtZQ1Vm9qfoR52snpznb5N\r\ne4TcT2qHAkOWV9+a9ESXmQz2bNjgThxEl5edTVY9m4t248lK5aBTGq5ZKGULNHSQ\r\nGGpr/ftMFpII45tSvadexUvzcR/BHt3QwBAlPmA4rWtjmOMuJGDGk+mKw4pUgtT8\r\nKvUMPQpnrbXSjKctxb3V5Ppg0UGntlSG71aVdxY1raLvKSmYeoMxUTnNeS6UYAF6\r\nI3FiuPnrjVFsZa2gwZfG8NmUPVPdv1O/IvLbToXvyieo8MbZAgMBAAEwDQYJKoZI\r\nhvcNAQELBQADggEBAEb6ajdng0bnNRuqL/GbmDC2hyy3exqPoZB/P5u0nZZzDZ18\r\nLFgiWr8DOYvS+9i6kdwWscMwNJsLEUQ2rdrAi+fGr6dlazn3sCCXrskLURKn5qCU\r\nfIFZbr2bGjSg93JGnvNorfsdJkwpFW2Z9gOwMwa9tAzSkR9CsSdOeYrmdtBdodAR\r\ndIu2MkhxAZk9FZfnFkjTaAXcBHafJce7H/IEjHDEoIkFp5KnAQLHsJb4n8JeXmi9\r\nVMgQ6yUWNuzOQMZpMIV7RMOUZHvxiX/ZWUFzXNYX0GYub6p4O2uh3LJE+xXyDf77\r\nRBO7PLY3m4TXCeKesxZlkoGke+lnq7B8tkADdPI=\r\n-----END CERTIFICATE-----"},{"id":"contacts","categories":["office","organization","social"],"userDocs":"https://docs.nextcloud.com/server/11/user_manual/pim/contacts.html","adminDocs":"https://docs.nextcloud.com/server/11/admin_manual/configuration_server/occ_command.html?highlight=occ%20commands#dav-label","developerDocs":"https://github.com/nextcloud/contacts#building-the-app","issueTracker":"https://github.com/nextcloud/contacts/issues","website":"https://github.com/nextcloud/contacts#readme","created":"2016-10-30T14:00:58.922766Z","lastModified":"2016-11-22T22:08:01.904319Z","releases":[{"version":"1.5.0","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=9.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/nextcloud/contacts/releases/download/v1.5.0/contacts.tar.gz","created":"2016-11-22T22:08:01.861942Z","licenses":["agpl"],"lastModified":"2016-11-22T22:08:02.306939Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=9 <=11","signature":"ZqqhqtbHcNB+rzGCQ7FDIjjvHjit+dhAE1UhFgiXApkx3tmPP4nJOBAGNjHe+2Ao\nVcTIX2SrWEfieRrA4Gp+0k7pUPWag1Z0T1OVOwO4cmS1AVFyGIOE1bRvDhMfsWTU\n4CI4oXaKBFAY6mtnf7VJ7EeIdNqhImkohyWDQ88NiPRLM1XNkJJk6AvZBcT0fvCv\no145X4dLpbixSXsN99QFNJ/oXvK+9tBGwTd5i/WnNFY90vcNRLia8aRo7SA0YJRx\nLnxnj2HMqwTTDQEKE+1elYKWsqQ2DeqwScP97UIKe5bZXnrwOi9kH9PDmR4abtzd\nlHL8E1Wgw25ePDeHG7APrx0tVOJy1bP+g8vcarpGynWZoizDkBvYZD+xtxizpBXC\nJsDOSzczApptY6dnOtv0Vat8oh/Z/F99gBUahEu4WZ16ZgR1nj40PDK1Snl18Cgk\nMe1EZcde8SLEpTbCWYIfIw/O9Fkp5cWD/dAqoiO6g+gNxSZ/gGp57qoGfFxn7d/x\nH3aH8GljatAFjrwItw1JzR0THt0ukkOK+bw/pfCslk10sjHMitmz/GXa4qMS91DZ\nBKLUd0dSfQUQzkfwcojImbzJRvca4/DYe3mfG7+RCH0tDL6t72dKL9joB++u5R1u\nVZPgkToexlXcKWpiDB8H2/SEShKr4udAOjR5de9CYWM=","translations":{}}],"screenshots":[{"url":"https://raw.githubusercontent.com/nextcloud/screenshots/master/apps/Contacts/contacts.png"}],"translations":{"en":{"name":"Contacts","summary":"The new and improved app for your Contacts.","description":"The new and improved app for your Contacts."}},"isFeatured":true,"authors":[{"name":"Alexander Weidinger","mail":"","homepage":""},{"name":"Jan-Christoph Borchardt","mail":"","homepage":""},{"name":"Hendrik Leppelsack","mail":"","homepage":""}],"ratingRecent":0.5,"ratingOverall":0.5,"ratingNumRecent":0,"ratingNumOverall":0,"certificate":"-----BEGIN CERTIFICATE-----\r\nMIIEAzCCAusCAhATMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD\r\nVQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI\r\nMTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB\r\ndXRob3JpdHkwHhcNMTYxMDEyMjAzNzIyWhcNMjcwMTE4MjAzNzIyWjATMREwDwYD\r\nVQQDDAhjb250YWN0czCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANzx\r\n/zJF+5/s4lOJLWIlfKQgTy+UpvIpiUXCgrsHsDZTx+hjQAhIWukH88a+7NVAL7Ys\r\nkQNC0Tlm755FJi/T6EdR7edOwIRdo2ZwakOWLZXd209+6cCd2UloHL0bgnbWepTl\r\nR/4YgbLg/G+FVKCfkEiYc3PuDZ3EVrcwQFcg7h74X9ne6CHH0Z1WQLydxJuVEb2n\r\nX9I+nIRpPHcVostkSiUmzHR7C5TjTIo2PzzgnCU6GC0iBa6z6dDYfz24QEp/b8UA\r\nZaLhjkyKghVGMnoF/s9KPgH4NM8pvvtadQN8pxlOPju4wbmKPUrsXo4ujurDXbbc\r\nYkzNt8ojobGwdTXoyDogAsGZLQd2FQksWpRvY+I3zVPokBfPMdUPLllG5VcV0VA5\r\nDRK+h2ms+XmspdBvGonjF+XdbFm9hEmDoFmoi9aU6C6AdofjmG/e9+pw/20dXUWk\r\nmMorWwXQ5yLmIn5LnpRXrOuK7CS28VRhBYuVNMlsyKhzU0rophbsD9OFXxYLjr6s\r\n7UPNwZ5h+kjXZDBKD89QctBSViT8RhLe8nulRIm0iJn1sb9hca/CF63KmsFzENfK\r\nQeM6MO0H34PB84iNyz5AX1OIy+1wHD4Wrzt9O/i2LkWK6tBhL69aZiBqdLXWKffj\r\nARDCxxIfews51EZFyHzwsw65I97y46aBKxY382q7AgMBAAEwDQYJKoZIhvcNAQEL\r\nBQADggEBACLypX0spxAVAwQIS9dlC9bh1X/XdW2nAvSju2taUTBzbp074SnW6niI\r\nbnY4ihYs4yOuGvzXxnp/OlvWH7qhOIchJUq/XPcEFMa7P03XjVpcNnD3k0zQWlZb\r\ntGonX9EUOeLZKdqI4fkrCkMLScfjgJzoHGYQrm8vlIg0IVuRLCKd5+x4bS7KagbG\r\niuPit2pjkw3nWz0JRHneRXz/BNoAWBnJiV7JMF2xwBAHN4ghTM8NSJzrGTurmpMI\r\nGld7yCP47xNPaAZEC66odcClvNtJ2Clgp8739jD6uJJCqcKDejeef0VU1PG7AXId\r\n52bVrGMxJwOuL1393vKxGH0PHDzcB1M=\r\n-----END CERTIFICATE-----"},{"id":"mail","categories":["tools"],"userDocs":"","adminDocs":"https://github.com/nextcloud/mail#readme","developerDocs":"","issueTracker":"","website":"","created":"2016-10-19T19:41:41.710285Z","lastModified":"2016-10-19T19:57:33.689238Z","releases":[{"version":"0.6.0","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":">=5.4.0 <7.1.0","platformVersionSpec":">=10.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/nextcloud/mail/releases/download/v0.6.0/mail.tar.gz","created":"2016-10-19T19:57:33.676730Z","licenses":["agpl"],"lastModified":"2016-10-19T19:57:33.834580Z","isNightly":false,"rawPhpVersionSpec":">=5.4 <=7.0","rawPlatformVersionSpec":">=10 <=11","signature":"VbMsvDpt+gSPeFM8LrZXEK10rk8kkLlgCcblgqNdCSeGZeVpwDAYv3CccVSLa0+l\nlTSqQ0VIoH+OIU6vIQNBKHmSCzTplk7OrY0+L5FajXx8LnBaOh892GfGSlEt1neN\nKyM0i0uOjO/xpCP/NoUlgkz6hnmYY5XEdN6DTsJtJ/XZhDQ45IYuIkMkHE/eFehS\n0JnOagIz+PSipeBY2Ry+tV8YbRa7bC1JAvZzlod0dyI015AHZESeitRUY+MwMWkt\nN/me7g7/Kev0wggIQQZm9aYcw63GMk/1VHUPB7Y0ESW9tx2nR5+KwTDn/Jy4DGf1\nrg8h0t5I+aPhHOBLrpczH0qaZWY2lsVZWq8KWjJI9aR9P0v2f2aXixXzD/Cuz1cK\nhvhKWkOSla4D+/FxeyHGjQvdXMG8gXm0ZmTimKChCoVuCbncDd8pzkdyNoGXcvuk\nsP8OrkQFooL4E7S4BWfdSiN/a8jUITJQkuXp/OVrVGeCupLWJh7qegUw6DvoqyGy\nD4c6b+qYn68kx3CLaPPiz+tFAZQZQdj7+Kx/lohso8yTnVSiGYrMj4IvvCbpsQjg\nWF3WSqF/K/tTnPYTWb9NUPSihTbVNv6AXOfTsPEp/ba2YSS5DjvjVjkr5vhR9eg1\nikQ3Cw6lW3vaA4LVCC+hFkMRnI4N0bo5qQavP3PnZPc=","translations":{"en":{"changelog":"### Added\n- Alias support\n [#1523](https://github.com/owncloud/mail/pull/1523) @tahaalibra\n- New incoming messages are prefetched\n [#1631](https://github.com/owncloud/mail/pull/1631) @ChristophWurst\n- Custom app folder support\n [#1627](https://github.com/owncloud/mail/pull/1627) @juliushaertl\n- Improved search\n [#1609](https://github.com/owncloud/mail/pull/1609) @ChristophWurst\n- Scroll to refresh\n [#1595](https://github.com/owncloud/mail/pull/1593) @ChristophWurst\n- Shortcuts to star and mark messages as unread\n [#1590](https://github.com/owncloud/mail/pull/1590) @ChristophWurst\n- Shortcuts to select previous/next messsage\n [#1557](https://github.com/owncloud/mail/pull/1557) @ChristophWurst\n\n## Changed\n- Minimum server is Nextcloud 10/ownCloud 9.1\n [#84](https://github.com/nextcloud/mail/pull/84) @ChristophWurst\n- Use session storage instead of local storage for client-side cache\n [#1612](https://github.com/owncloud/mail/pull/1612) @ChristophWurst\n- When deleting the current message, the next one is selected immediatelly\n [#1585](https://github.com/owncloud/mail/pull/1585) @ChristophWurst\n\n## Fixed\n- Client error while composing a new message\n [#1609](https://github.com/owncloud/mail/pull/1609) @ChristophWurst\n- Delay app start until page has finished loading\n [#1634](https://github.com/owncloud/mail/pull/1634) @ChristophWurst\n- Auto-redirection of HTML mail links\n [#1603](https://github.com/owncloud/mail/pull/1603) @ChristophWurst\n- Update folder counters when reading/deleting messages\n [#1585](https://github.com/owncloud/mail/pull/1585)"}}}],"screenshots":[],"translations":{"en":{"name":"Mail","summary":"Easy to use email client which connects to your mail server via IMAP and SMTP.","description":"Easy to use email client which connects to your mail server via IMAP and SMTP."}},"isFeatured":false,"authors":[{"name":"Christoph Wurst, Thomas Müller, Jan-Christoph Borchardt, Steffen Lindner & many more …","mail":"","homepage":""}],"ratingRecent":0.5,"ratingOverall":0.5,"ratingNumRecent":0,"ratingNumOverall":0,"certificate":"-----BEGIN CERTIFICATE-----\nMIID/zCCAucCAhAVMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD\nVQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI\nMTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB\ndXRob3JpdHkwHhcNMTYxMDE5MTkzMDM0WhcNMjcwMTI1MTkzMDM0WjAPMQ0wCwYD\nVQQDDARtYWlsMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp++RuliQ\nlBeeiPtP0ecBn00OaU1UCpft/NVI5pnSiT9nU4l2kc5IvKjA8UxDB3gWfYTOeBFh\ntUHQ2P6UKCmHZT9sApHhqLu2n0V+YhlFIViuaxndSID/M414cl56xOYQusV3Pcae\no2dOSeRRzLab3tEaVHlkBSFkGmAwPZItsmTklvV3h1sUysDicYgfXPCkf7K+JgWA\nBP7vsWC8B7MDRhcB3enYv5tTcpsyvtGX7bb1oTIWVypcmKsGYfTX12VNBxKzNBIG\n8pwdb8Xo0o14TytWsWN7mSHf1XbwfwYMjDWOlMqiRc+mcoKMBH41TfM/CXslSivI\nsyvxasEaFdlj8lmKPENdzw1OfYRs43usIf4szwyt4rb8ocXfDipnY3P2hccN6YcZ\nl8y8Vsr69ASluDj2A2Pl5vH6xp6tNybZRnN5G6sghhaYaLNDU/TdMyYzz4AY33Ra\nHSaMypfcXjd76Aj8jZvcwk1BH+ZsvFqNK7ZKCb7WVcMH8KRcU1sxZ4rp9vviM2fL\nL7EVtznm3bSI9jjHXbiwq7RvNRRy+F6YRpAdWGwTU8uUkDabPFi41FikYyzNWauK\nJhlDJXl514XjKyMVBjAZYVr5gZZkO1J7C4XzLFbC5UzYNSzW5Iwx/1j5OeYJRxh6\n5rhiUwR+COT1wdVsl6khMC8MfBR4unSd338CAwEAATANBgkqhkiG9w0BAQsFAAOC\nAQEATBvpqz75PUOFPy7Tsj9bJPaKOlvBSklHH7s43fDDmQbJwswXarZi3gNdKf8D\nyO/ACZvO8ANWAWL/WahkOyQtKOYzffaABGcEIP7636jzBdKtgwSGzW3fMwDghG10\nqBr2dE6ruOEdSpuZxgMgh2EulgknZUXaHAMI2HjjtAMOBScLQVjOgUqiOHmICrXy\nZETmzhx0BXDt5enJYs8R2KMYJNIme1easQRYmWKliXogNY09W7ifT9FHtVW1HX+K\nxRS4JXbapjolkxyGSpP+iYSgItVnYzl6o9KZResR4yDsBv7G/8fpV4GQU9IS3zLD\nPiZOosVHWJdpUKCw9V4P1prGTQ==\n-----END CERTIFICATE-----"},{"id":"audioplayer","categories":["multimedia"],"userDocs":"https://github.com/rello/audioplayer/wiki#user-documentation","adminDocs":"https://github.com/rello/audioplayer/wiki#admin-documentation","developerDocs":"","issueTracker":"https://github.com/rello/audioplayer/issues","website":"https://github.com/rello/audioplayer","created":"2016-09-16T05:44:24.857567Z","lastModified":"2016-11-17T22:34:34.637028Z","releases":[{"version":"1.3.1","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":">=5.4.0","platformVersionSpec":">=9.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/Rello/audioplayer/releases/download/1.3.1/audioplayer-1.3.1.tar.gz","created":"2016-11-17T22:34:34.215350Z","licenses":["agpl"],"lastModified":"2016-11-17T22:34:34.867778Z","isNightly":false,"rawPhpVersionSpec":">=5.4","rawPlatformVersionSpec":">=9 <=11","signature":"p6Zz0IEFrxvw6y/3jHgGWWCxR6qpMzvU2HKfxcIVsK6sJnoRUhWLeAXwZ432fH2a S2llj+IGS9OvW+5VQElrXgPtEjDK1BT00DRJnp5RFCRlUv0LNoedJMzx6B6AHqPP JBufk3cG1O/CO0M0L1ITGSmSOzfKvWTRo3lxVGF792NyBaP/SyZCkH1N1TzBQzUi Ywl3+HiglPcXbHjtJm/arnKorbJWVKoaN93xFuaBapd2ozQSpi0fE0uGRsici+U7 HNa1M5WFE1rzUJoufE0E9246At07rFY1e+TdNEq8IlLgCXg5vGCKkEyuWpWno6aX LfRaIiT9x39UTAwNvuDKS0c+n4uWDYPsGfKhDx9N7CXpUrthfXVEWRzZEXG7as10 6ANvrRPJemSZH8FUSrdJhD7k12qa9R825y7mIG68Li8P71V92EOxFfo9tNXqXwBt VuDGxBqByFVPqSCj5I8hrzJzQl2Xt40g8+8ZcSF96RMg/pM+bwRMTv+mz0V+vQQ4 DWjqnWVPalaJ1PPD5/QFFErtXuNRbyxKZ6BMWxfJlLM9Kz66P75K+8fYaSwz+2KG NxY7I3svzS2K9LGH3fBLUy1t3Hl+c3zGFq/ll1MJrf9NswV4yxt2WmISfM/KDww8 yELkGs2cRzbw2tCvnmYlJJhIqRLkyFAbDzWRH/XdVx4=","translations":{"en":{"changelog":"2016-11-17\n- fix: one-click-play for wav not working\n- fix: wrong sql statement for PostgreSQL [#90](https://github.com/rello/audioplayer/issues/90)"}}},{"version":"1.3.0","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":">=5.4.0","platformVersionSpec":">=9.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/Rello/audioplayer/releases/download/1.3.0/audioplayer-1.3.0.tar.gz","created":"2016-11-15T18:11:19.539636Z","licenses":["agpl"],"lastModified":"2016-11-15T18:11:19.592881Z","isNightly":false,"rawPhpVersionSpec":">=5.4","rawPlatformVersionSpec":">=9 <=11","signature":"lbp7wd3JhMHW5mC8kVnQFvcwzf3aTIhYhq3ak/C/vfDXJDIPFuQ1odVRWtaHXEKQ XmKYIoTobV1TAU5q9G0O0Kds73T/XtHG4ATLxMZE8RsUWNSj5v3H4YDub6A0uoX6 rzyLEYV6SGEtdPFMwLcUjDExKpzAzKpgxcd9uyz2NhcHJEO8FJmirn34bm69//TO vjjiMW4zpL+dho+7LQbOX+L1SmwmdBqwypE9zzeuIuhUWDEQtImHAvjIO6Temajm lX0H5JaowJa8kvP6Jkh3KAvsHQ4sJklvWTPGcv0gboN+o6CmjWNOb+3LeSH0nhe6 BmiPloUDJcPQwq2gQejH2pY+qJEdRcULSKS09/dRbE3gOSlG36FThN0INpv6uNP4 qVIiYs3/SEHMmlS5CHvJDt2S2XN9LT9IX7QPeuS/0CMcuopaG/+cdC4KscVCq4D4 bllgew9asiBqix8iV8C4oerYOiC5vWcgBrZhGShoJT1Qq+NKz+H10dFgjFCAZuPj nVagJkbXmf2NdcvpSC7qsufhyEZyCSp+I7QEYsbo1PW3aUU35Syt47lgeVFX0hVQ jC1wMIhEW5Rm2nCkRSZkRupKDQ+N6uWuB0lykeMV2ShcDvvUZrhN3c49sooWgigB yIqKryzM4fLErjjNHdYgwCq6bbgPDLK3ic9b3B4rF3E=","translations":{"en":{"changelog":"2016-11-15\n- fix: handling of temporary scanner files [#68](https://github.com/rello/audioplayer/issues/68)\n- fix: simpler analysis of incorrect files in scanner [#57](https://github.com/rello/audioplayer/issues/57)\n- fix: album sorted correctly by artist and album [#80](https://github.com/rello/audioplayer/issues/80)\n- fix: neutral cover for unknown album [#16](https://github.com/rello/audioplayer/issues/16)\n- fix: error message from ID3 editor shown in front-end [#77](https://github.com/rello/audioplayer/issues/77)\n- enhancement: occ support for library scan and reset [#72](https://github.com/rello/audioplayer/issues/72)\n- enhancement: select a dedicated folder for scanning in personal settings [#79](https://github.com/rello/audioplayer/issues/79)\n- enhancement: exclude folders from scanning via .noaudio file [#79](https://github.com/rello/audioplayer/issues/79)\n- enhancement: significantly reduce database reads during scanning [#79](https://github.com/rello/audioplayer/issues/79)\n- enhancement: cleanup of classes; move from \\OC\\Files\\View to \\OCP\\Files\\IRootFolder [#72](https://github.com/rello/audioplayer/issues/72)"}}},{"version":"1.2.2","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":">=5.4.0","platformVersionSpec":">=9.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/Rello/audioplayer/releases/download/1.2.2/audioplayer-1.2.2.tar.gz","created":"2016-10-06T21:21:05.414691Z","licenses":["agpl"],"lastModified":"2016-10-06T21:21:05.483224Z","isNightly":false,"rawPhpVersionSpec":">=5.4","rawPlatformVersionSpec":">=9 <=11","signature":"toeS45z50Lm0djgrQokOTN7gA8a113IZtiKKiLsGUKWrCV/6AKJBmEFcSun6rhLH\nbz/RtIdFKwQql6O3E0m1Zch2y1A8aLWHzFTO+5orLCVi7y15SshrJYbb9aI5Pj3i\nSR7+kMHGS8uNx2uIn3B4mO6UYF8AzCfp+ule18DOjnpu86KWvEOGtFXsQkLtgepp\nbJhOGWW/uOVIos/T1xPP4GCmtVmgn7U3b9q0pMYRH7ONXEiNsfQxDrR66EZH3Jfo\nlVyM9UvQmMKoDSdBUlvLlhCEGJGqFOD7hFntLYloI4oXv9uGqcagaJVh3TkEysY2\nMbBZpVhch5zRJ/HGlZOvmEzZ8Inxkk3iap+JmJ5/gZTefwfKUyYHALsILlh820U2\nNA/1B5A015XH5a5uflGE/tnlPtrOeecIN4mg+1njo2RG89HJWJNHM2ZDO4SkXjSR\njgygmAS5aR5+KKifiA/pwjhLozDWPU4lNzsj3Foz3bx3Okopy7eq83LORqieT4Jp\nFvP64q/45LOSRBWIWLitYzRzZp7HYywMsnz12WpxtqxIjO7+7y/ByeWWOBNU1IJC\nK2D+035ZGWZr0CxDJte33WOISwjVoSwrcov++O3BQW8lM5IkcDNcJFyzNPKAXcQU\nPUXmQpYurHoIw6odAYcbrG6iOiSesuNOf2kQWbjV3/c=","translations":{"en":{"changelog":"2016-09-18\n- fix: icon issues with alternative apps folder [#65](https://github.com/rello/audioplayer/issues/65)"}}}],"screenshots":[{"url":"https://github.com/rello/screenshots/raw/master/audioplayer_main.png"},{"url":"https://github.com/rello/screenshots/raw/master/audioplayer_lists.png"},{"url":"https://github.com/rello/screenshots/raw/master/audioplayer_share.png"}],"translations":{"en":{"name":"Audio Player","summary":"Audio Player for ownCloud and Nextcloud","description":"Audio Player for MP3, MP4, Ogg, and Wave with a lot of flexibility for all your needs."}},"isFeatured":false,"authors":[{"name":"Marcel Scherello","mail":"","homepage":""}],"ratingRecent":0.5,"ratingOverall":0.5,"ratingNumRecent":0,"ratingNumOverall":0,"certificate":"-----BEGIN CERTIFICATE-----\r\nMIIEBjCCAu4CAhAIMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD\r\nVQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI\r\nMTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB\r\ndXRob3JpdHkwHhcNMTYwOTE1MjExMjA4WhcNMjYxMjIyMjExMjA4WjAWMRQwEgYD\r\nVQQDDAthdWRpb3BsYXllcjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB\r\nALyC+iLscLs62NeNmUXEBmg+xMuUtDmZKr+xzJWtl6SSNRz+8K1JygvUIXFJ3RIL\r\nCYA3xyq8/wyZH1gNrLKyz5eTeYawG+eT3ges/FT6MWGUbZoRrBrikVcLC94QzxTH\r\nxOl8Dn+SCV/2bhcvPTQdhK+dqtvGilOtjHa40iMrk9gSdlKVys5CK/xdlEp8uiMa\r\nkz1WENn8MVCCJV58bAUbaCupDWXR9CCoSsw8XinNsCenZ2B2XlnmbM44280w0ojs\r\n72rfQRgj3yDG+ZUUyUOuxIuodu8liXYciLf0ph6t/f/qoSmctbBdsR5Fl1Upj1Ac\r\nqeHb5Yf/B3Vi6Mn3XfDx0H2EHk1v9Dhzxay+v9BHUzyIX2iH/q+7TE0/Jzo5AwBW\r\nvFKWXvG7wXaALcHYZf5v/M93IE0iCHsv2EsZKQPBnzXVGmp4DwFSP4po1B7hcog1\r\ngAMaellAzzvUAizgCovN6Qct3qDEANYniPlvtnlcaQGonajW4N019kFQRHLIzPFR\r\njab5iUMMwSnT8FhZO2ZOWuWhJven+gXjxC8mfMVgBfZnAVgydNfx9rN+KzTc88ke\r\nobUdZ0OOeBzA7pIxGEFg9V6KTEEWZ+qH048vxXz4HI9B1I+2wQLBrZl8CvweEZ5U\r\n5ID8XrrE/UaNZ1CvLKtCgB24gj/m1Elkh7wA3gEcEo2JAgMBAAEwDQYJKoZIhvcN\r\nAQELBQADggEBACtgUp+FCmjWIkQUuWSdzKWdO+IH4v9wBIrF9mo0OLIakFyDYyM5\r\nLlkYZXbplGXd4cfn3ruIqJNzlIb4xa5CU0bM4TMbD4oOSlLMKM/EamKPHI3bzr++\r\nzi7mQDFxmAE6FWSMBgKKUb4tqLc5oBap8e12tPEZl/UR6d9iUB2ltvrm3T3vrjjl\r\n2Worm0eYBNqnagXmX5+wS11AQqeJemGqRy5e1yXRlTgB0IJhH0dCsFNwifEigutp\r\nFNvGFVBn4r5qCiChEoq+rCXHRjPi/eCfbW21XeLFDiLxapcZyc85JIcA7znUYoFe\r\nP7Y/ekMscwWhLbF91OaQlcWpRtEMyde/DaI=\r\n-----END CERTIFICATE-----"},{"id":"calendar","categories":["organization"],"userDocs":"https://docs.nextcloud.com/server/10/user_manual/pim/calendar.html","adminDocs":"","developerDocs":"","issueTracker":"https://github.com/nextcloud/calendar/issues","website":"https://github.com/nextcloud/calendar/","created":"2016-10-01T12:40:39.060903Z","lastModified":"2016-11-22T20:31:13.029921Z","releases":[{"version":"1.4.1","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=9.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/nextcloud/calendar/releases/download/v1.4.1/calendar.tar.gz","created":"2016-11-22T20:31:13.020268Z","licenses":["agpl"],"lastModified":"2016-11-22T20:31:13.087340Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=9 <=11","signature":"nThwe9CJBCan9nuDLdhfBiQyPhmum6Aa0UcYsIDdhGMw+C2acf81KhEmBJuTTWxo\nWGby6WcrcJJmeuCW+ePU91ju7Pd76RirprhVXIEceIDzSCxin+K0oZCZ1IGVIJjP\nIkVehTsLuCeTBbjvz1b3k5QFyhUhvd32Xt7k5d7VARyI4OqnqYYNBtH9vvgeRrFw\nAxsQr4o4axof6i3iykLg6WfWarYArY4dIuu5DkPuGPWf2bbgjwWEra4sQejhOs7G\nsk1xcsfYv2NpArIbpw/wnATdjiax+Gjz1URMD3NgL5ky0ecuZmNvN25QErg3nlVr\nhh1FBfA5pvCJbkJ6nr5bU4bKaffwDX1nr5h77FS5zzn0Pyd7ZIExmVmNtaeJfnfV\n5vnclapzXMNU+R6t/ATJQd1srvSJhyljQapzsqnrjNMEUojOEvqqygJp0KwNVPqs\n3g9XGSoOnO+WULWBeISW7UVOg8BOF8pwvHIU2++bSzOdpypW0Eq6p2DPWO6qL/H1\neFLKrUg3EvnTjvknbBxMB55h9jNJr0SAlkrmyEVm6+CE3BwRWpKB+cJMBuGiwPwv\nr/ASRiJrkDThbNWAUtX70rUmCqDV6/MujLXViqOc/Q2OHvcXd1oGDccJSQT92/1z\n7nonnedyYQIDqUt7u68WL8JRxp7pFsEqKLVuWSgxW3c=","translations":{}},{"version":"1.4.0","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=9.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/nextcloud/calendar/releases/download/v1.4.0/calendar.tar.gz","created":"2016-10-06T19:58:12.724588Z","licenses":["agpl"],"lastModified":"2016-10-06T19:58:12.790604Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=9 <=11","signature":"b//hJbICFMLR0Va1BGMzpLpaPREOo9QhjgfrHvDOfXVpddhvCM8ocz74X1s5hKyy\nGg67EE0pOp0dBf6RyJjduI+Dz1wQX55My7J9G1vXGCN30C/8zAcKSJoA218IWcub\nICavLkz2PkiitIOSZyBCAfEiSAeHPop/JGkq3KxQDC7QwFC78BnE9/adD9tO55c/\nDGLhvP/uTJIeH8RUifikTqVMmPH+aP3uPbZzl+AxhUezRiNCpEtZPA5QGqtQdJU4\nFc6x3d9y4IWbJV4TEIAP8jdfqtLVUQ6szFVnN8Oi1wtN9e8LIylBSYbmIZRj0+qh\nZcgntzEq6U843ZwXcAnL5jNYV0m+KNI+EkXFeWHkjvbwfCdvGPBvgFVbhc0YPzXU\nqHOe4Lvcx9X20ALG/MacV9zX69GzNnWgbBp9RnIHuaSRPFEKrNXUeXl2THuKsTyQ\nF9QtTwS5U5DcMyTO2RAN45NrRxIh8IL4stoIg5rmF7/ZaOm/Jza2gnUquOTarDE/\ntiWnNW5kWUAWyYYHvQgQix/of9qXvc2hhZaw0y623WDNrEwA+rngnjDMLA/vNv3B\nhgwQ6NbCOuHWsRK3S8DcJFpB9Kj/i7CDvDLEuJYnjSTvQ/q1XqawbJPDoRlydX43\n3/L0LvHvKVakYybv2OE5gy6bQ2Dw8e7D27DtZ6XTaBY=","translations":{}}],"screenshots":[{"url":"https://raw.githubusercontent.com/nextcloud/calendar/master/screenshots/1.png"},{"url":"https://raw.githubusercontent.com/nextcloud/calendar/master/screenshots/2.png"},{"url":"https://raw.githubusercontent.com/nextcloud/calendar/master/screenshots/3.png"},{"url":"https://raw.githubusercontent.com/nextcloud/calendar/master/screenshots/4.png"}],"translations":{"en":{"name":"Calendar","summary":"Calendar GUI for Nextcloud's CalDAV server","description":"The Nextcloud calendar app is a user interface for Nextcloud's CalDAV server.\n\nIt integrates with other apps, allows you to manage calendars and events, display external calendars and invite attendees to your events"}},"isFeatured":true,"authors":[{"name":"Georg Ehrke","mail":"","homepage":"https://georg.coffee"},{"name":"Raghu Nayyar","mail":"","homepage":"http://raghunayyar.com"},{"name":"Thomas Citharel","mail":"","homepage":"https://tcit.fr"}],"ratingRecent":0.944444444444444,"ratingOverall":0.944444444444444,"ratingNumRecent":9,"ratingNumOverall":9,"certificate":"-----BEGIN CERTIFICATE-----\r\nMIIEAzCCAusCAhARMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD\r\nVQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI\r\nMTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB\r\ndXRob3JpdHkwHhcNMTYxMDAzMTMyNjQwWhcNMjcwMTA5MTMyNjQwWjATMREwDwYD\r\nVQQDEwhjYWxlbmRhcjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm6\r\nFTeqgzCXprkU83VM4/DrZWn3kqtfaR/edkC4gYT3ug7RHa/Uv1C/S++vr7pvgpnk\r\nYzQoavl/0Qlh5sKEYX+0ud/LQDoiidwBRDckFUQ1bRfVLxAD9UAVvDRHxDqJMOx2\r\ngZArbeQ3ztdSHZp4ThzBFWq2FILsJD86weG7LwHjzhW6SWgLb/YTLbuuW6tRCDVV\r\nbtB0I/a0vCwj2u91Chw3u6pWWjPakc9DQrIDH4HCIBKQ4zVrYDxAmJDRFGDvVVWx\r\nuIAeux8sd8drqSMqAhX+XMcZPRD71NQTWbCupSwWO8kgjmZnBpIiBNpzvMQzJf3A\r\nQloZtjZ2RDXAQG88eTeT8pp8yEOCEoDLpGdraKxJrh/z2Dsk30JP3lOiNYJ9vBaB\r\nC8NJbJ3oAlG7THwUaFF9fsdAKoTwzs5Xms04TI7W/v4Z/GClOzMymnR1T4sR72Oy\r\n3WaMNHv/1QGffvQn2/TtZt23Ou3P083xWx2vn5FgTcac8+x85vRgWsVCA4hq9v6m\r\nAlktB0+UWDEXpDTKD9BdFNWM8Ig9jQf7EJrvTLNnS7FIJZMB4GK8lpvPxyvACWnh\r\nR2hQOe987Zvl3B1JZNO5RvtSeYld9Y9UfMgW1aPRweDNjSuZYAKlugx1ZoyI5HyA\r\nQjfzAwicIMwZsCJDV/P5ZO8FE+23rdWaoJczpBqDAgMBAAEwDQYJKoZIhvcNAQEL\r\nBQADggEBAHQXwvj8q5khWR/ilg3JGYpmMNBYHE9OeDaOcNArkKaGMd478SDPOXeu\r\nyW7hCvNEpiTk5g0h3g3yleZFws0xH8fPsQgZANgvQXb3RCcD61NL77d0cMTr7Xzr\r\nN3Lq/ML1YLc/WwL4uV1XvpMQMwALFL1p63BU2c0ysO31zbLOjMKAJi0hHFDYz5ZQ\r\nD3xxtc17ll3B5IqrMnMHRqmOQ39Sbe56Y7T4agaIz/sUWpseo85D5kt7UAIOR+Mr\r\nQ0Bl/QinETk72afGR46Qvc7tC1t9JjQQD3AUbEGuJdGvXjJJ9GREYu01XoODmPdT\r\njXXOI8XIOK6kxXhPHUc3iWu9b4KqGm0=\r\n-----END CERTIFICATE-----"},{"id":"gpxpod","categories":["multimedia","tools"],"userDocs":"https://gitlab.com/eneiluj/gpxpod-oc/wikis/userdoc","adminDocs":"https://gitlab.com/eneiluj/gpxpod-oc/wikis/admindoc","developerDocs":"https://gitlab.com/eneiluj/gpxpod-oc/wikis/devdoc","issueTracker":"https://gitlab.com/eneiluj/gpxpod-oc/issues","website":"https://gitlab.com/eneiluj/gpxpod-oc","created":"2016-10-31T10:57:44.387319Z","lastModified":"2016-11-23T17:27:37.793159Z","releases":[{"version":"1.0.8","phpExtensions":[],"databases":[{"id":"pgsql","versionSpec":">=9.4.0","rawVersionSpec":">=9.4"},{"id":"sqlite","versionSpec":"*","rawVersionSpec":"*"},{"id":"mysql","versionSpec":">=5.5.0","rawVersionSpec":">=5.5"}],"shellCommands":[],"phpVersionSpec":">=5.6.0","platformVersionSpec":">=9.0.0","minIntSize":32,"download":"https://gitlab.com/eneiluj/gpxpod-oc/uploads/963bbf246412bcbe8979bccadb3b8d03/gpxpod-1.0.8.tar.gz","created":"2016-11-23T17:27:37.783365Z","licenses":["agpl"],"lastModified":"2016-11-23T17:27:37.862469Z","isNightly":false,"rawPhpVersionSpec":">=5.6","rawPlatformVersionSpec":">=9.0","signature":"hqhMh1l/mnwbYf4uPzEjjLFtZWHidzgR57X471OuXv2K/s87T5WhIkTSKk+2r8sp\nS7CtrF5+Pc5AgCCHvwzawN3e2+4eO4cK0+HD9CCzygzzHZEbSjufNHMMQucVoSD8\nPqR6MV9azzUpwHa/5d8fp3cFLVAle+aG0o4v5eHky9c7eaKxVJcgfjw3pjDE73N6\ngJVdtw1jf1kOFYk5pZQxDfBKFDrO5BRo5ZfZGuOuP2u/SmTwj42oTZiT7oTVWhqd\nLvJw+2TPv7B8s0Gin+J5e9K1Rs6CEWQ6WBxM+NhS5KgWB5Ig3pwm0QvMgza2cvoh\nlwVobOotfKLUBJzg0+wR7B2YH9Ao+m94h93vg7H0OKPReoTKhlDj2UExoTyeurV8\nhJdQv8sKVAxjC7/xrVaGSjM4YxFdBpzq8Zl8z4zq1o2voH5+u4ko3c62C1loDpsC\n8KrL1t6A7QpMk/XAMrPqwEPmFqlLEdv6FhzpOGyt4IEVnv6vdMTShcYw3tPvU/mD\njPtiVwpo8gWbGVIfpmwBg4wPaTrWK8V3+/1iTahIQHZfu4Lebb5mzht80HLQIcd8\n+oB4cGDEX4Rix1WxnCmE5ZzURY8xQXcvqYN+mTrUDh/3OtxQPSm5yC945SGoFNpr\nBYxfEyQcwulZrOMBdY0Ssj1AB5NOeC9OHwjJrnVe7dQ=","translations":{"en":{"changelog":"### Added\n- save/restore options for logged user\n- option to choose picture style (popup/small/big marker)\n [#25](https://gitlab.com/eneiluj/gpxpod-oc/issues/25) @eneiluj\n- add average speed and average moving speed in comparison table\n\n### Changed\n\n### Fixed\n- bug when python PIL is not available\n- deletion of bad parameter given to getGeoPicsFromFolder() in controller\n [#20](https://gitlab.com/eneiluj/gpxpod-oc/issues/20) @eneiluj\n- bug in file cleaning, bad use of array\\_unique\n [#22](https://gitlab.com/eneiluj/gpxpod-oc/issues/22) @eneiluj\n- python script do not need to be exectuable now\n [#23](https://gitlab.com/eneiluj/gpxpod-oc/issues/23) @eneiluj\n- jquery.colorbox was brought by \"First run wizard\" app, now included\n [#21](https://gitlab.com/eneiluj/gpxpod-oc/issues/21) @eneiluj\n- avoid JS error when failed to get options values by ajax"}}},{"version":"1.0.8","phpExtensions":[],"databases":[{"id":"pgsql","versionSpec":">=9.4.0","rawVersionSpec":">=9.4"},{"id":"sqlite","versionSpec":"*","rawVersionSpec":"*"},{"id":"mysql","versionSpec":">=5.5.0","rawVersionSpec":">=5.5"}],"shellCommands":[],"phpVersionSpec":">=5.6.0","platformVersionSpec":">=9.0.0","minIntSize":32,"download":"https://pluton.cassio.pe/~julien/gpxpod-nightly.tar.gz","created":"2016-11-16T14:06:33.937534Z","licenses":["agpl"],"lastModified":"2016-11-16T14:06:33.971502Z","isNightly":true,"rawPhpVersionSpec":">=5.6","rawPlatformVersionSpec":">=9.0","signature":"JtUhKRDFGYDx9xtHjdfEUFOb0O4idexUYw6ixlBhKPP8Dn7NfyBfV6KH6MJTIVLU\nQ5Jw6tv/Nr1YDOvVikcWPG0p23mQdn1+7w8DzzIGKmknxCat9/vKr83oJZdWYxS7\nTJ4I7qTvWNlbMfK8OEdl13VJXgc6ftX+1isluLYqLjEm3aBFCS+/awYNMmXO55a1\nyG0NgJRu3pw1CBCMhDaRLsunhpRNDVLsamZj1SPmeT8qy0I/arFaG6hQnAo6JosE\ndi1XkvK6TEt9g16L6eizd+JpGE7xiWFP9ZEmMmmQSOLQYwU5Sk1YWcrW3EX4vtz5\nWnEIC0SENyyAyzBO6YJfu/EP2lLnlbNJiuc4zzKLqRw/zyz3j+imJLcXHIA78ZkQ\nuyUOBkkk3xeyBGeUcYfDuBqYQOQs+F/7+cNMsIBKJhx9ef3OPURBc7X16upk3mxV\n6GsOktbHkgUwWk3WiXRriBIqbAZocvDp0+PN++PAEZVWFEZEJzztd4Fxaeo+QSN5\n5Pz/9yXYRsoSPZv82Tlh7dx5tIPUvYb+UsANh5eGWUGufTSwgYBN0H2KT/iO35D7\nkDzNyh1qNakfBhAgPjrC2p4mBKBJJjlM0D9erDwr5D4GSTW2fp92vlRHeD0X8sqo\n3kBbwGuWnmhdJhbd7zYy0jVM6tVX/zgbhycimNALG0I=","translations":{"en":{"changelog":"### Added\n- save/restore options for logged user\n- option to choose picture style (popup/small/big marker)\n [#25](https://gitlab.com/eneiluj/gpxpod-oc/issues/25) @eneiluj\n\n### Changed\n\n### Fixed\n- bug when python PIL is not available\n- deletion of bad parameter given to getGeoPicsFromFolder() in controller\n [#20](https://gitlab.com/eneiluj/gpxpod-oc/issues/20) @eneiluj\n- bug in file cleaning, bad use of array\\_unique\n [#22](https://gitlab.com/eneiluj/gpxpod-oc/issues/22) @eneiluj\n- python script do not need to be exectuable now\n [#23](https://gitlab.com/eneiluj/gpxpod-oc/issues/23) @eneiluj\n- jquery.colorbox was brought by \"First run wizard\" app, now included\n [#21](https://gitlab.com/eneiluj/gpxpod-oc/issues/21) @eneiluj"}}},{"version":"1.0.7","phpExtensions":[],"databases":[{"id":"pgsql","versionSpec":">=9.4.0","rawVersionSpec":">=9.4"},{"id":"sqlite","versionSpec":"*","rawVersionSpec":"*"},{"id":"mysql","versionSpec":">=5.5.0","rawVersionSpec":">=5.5"}],"shellCommands":[],"phpVersionSpec":">=5.6.0","platformVersionSpec":">=9.0.0","minIntSize":32,"download":"https://pluton.cassio.pe/~julien/gpxpod-1.0.7.tar.gz","created":"2016-11-14T00:57:37.521001Z","licenses":["agpl"],"lastModified":"2016-11-14T20:35:45.363487Z","isNightly":false,"rawPhpVersionSpec":">=5.6","rawPlatformVersionSpec":">=9.0","signature":"SigBof6QJZ9IMZyFgc+B3LO2+EXaAPvnxUHjJQjIl3jLzomocpDGR6WjO6gtvB81\nzXUHjJ8+huc+P9TvgjUGRTmn9a/29HZ4IKTXnYBKIUY7wSLcJNMbJSp2Zd3OFHAG\nJwRaEdh/cIRk2X6NE1VT6dFCxB+LhTM4BXOEwuNYQvU1lZDVQgTz/r68zFLWBt6R\nqhBCNJHrVp87ecS4+XaGq/CfT4k1ihiOv+f4eX9iaPzUhxBJ71iYKF7wHpDoVmIk\nNrzWFUJH3BLBuW9oiC0PApli6Xu5RXrWUsOV7OAmxXgylRCPuTFwe09hw16JMbiS\nii8WFiUtp4qW53+7eoS7Fllm7CRi/Dg6Jvjtp3msrf1m+OiYM7dLyoKw22/S4P/a\nBIErZpSCHaCfrZ+DBXrAYcas27GWE7HizzG3yXk3aDJMa0Otcsq56bSPo01JDfNx\nm1y9iuwmlon8zKKoxsJCwxaFDnQpqazaLcUO0ATHUk8LdomTA7MCXVvNFPaO86Ea\n16iyw7Cfs0k3GrvN71+SdpvWss359CEEwBMpDwJZqwSFbLRyHtogUgbRWLIJqR4n\n5uVvJqirxWkr/EtXw6UkDWAI3ZoMhMRtjn4H4ekANP5mC8R0yp+UuFs2RkEC5uA0\nKzzh73WmxmpeUl6jcMZ49gXn3PTCC2fJNrdmSYch5Dc=","translations":{"en":{"changelog":"### Added\n- option to choose waypoint style\n- show elevation, lat, lng in waypoint popup\n- ability to display geotagged jpg pictures on the map\n- pictures slideshow with colorbox\n- pictures work in public dir link\n- use NC/OC thumbnails to display pictures on the map\n- options block hidden by default\n\n### Fixed\n- fix bug in geojson generation for waypoint-only files"}}},{"version":"1.0.6","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":">=5.6.0","platformVersionSpec":">=9.0.0","minIntSize":32,"download":"https://pluton.cassio.pe/~julien/gpxpod-1.0.6.tar.gz","created":"2016-11-07T12:11:00.619161Z","licenses":["agpl"],"lastModified":"2016-11-07T12:11:00.699921Z","isNightly":false,"rawPhpVersionSpec":">=5.6","rawPlatformVersionSpec":">=9.0","signature":"WBts2fm2rW/7LMHYjcx9a0k0WTXV6PnGRxTl+158cjfV7ruMpNvhK58iTjrox69k\nFWAoIi1wNAlLBu9Xet1j7HKi4TC9q61IEN+lPlnwBCu0uHawiqS2gqB4i8A019Ei\noLsgAPWh8ndy6+gyUtPhVLVduLH76aT6KTwAiHPknV0zPtxsUy1P6nbbNOS5A5rG\nSQBqljy0TbcjOctTudEPp1IqjJIwcd12eZ9MLG4CEIO13n53pMAsuXJf4jnKSCm0\ngimvsFOwFRjBab3ZPwtOqeVw6aIh/lYF3U3/k8YBpaDN74m30nDtkp8teXBgshSY\nVYvX3yOAYe0PIR419IX0eoHb61K0VfZYvPT4FsOqjDr0zlVB8Rjq+6SiK4vMD2+6\neGE0aHbjR9HV5jymUnFYdm/hlhcZGaKrAiQKfBY6Vh0SWKfIv7bdGfQYauePAdZt\njlsV8lIwOy7FGAeP81CcjzWWfDeBgYr+MSzfoDNoTi41MvUaT14iWPIU/s5P1/Qv\nALGxgsbmB19wEgNbdh1UtTUY3xARLpWPYdUqU7yDcsddX9vCoCG2G5wCcbYJRj8o\nC+H7wdgPJoiMY/p4Go/lyWkvmzhfXrOeXytZIFXjb3ERVd1vD9WSt1DSy/7gsFYt\nxzzOPgqMvL3BbeluNuzNv366oT872s3OuFKa1ZOYY7A=","translations":{}},{"version":"1.0.5","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":">=5.6.0","platformVersionSpec":">=9.0.0","minIntSize":32,"download":"https://pluton.cassio.pe/~julien/gpxpod-1.0.5.tar.gz","created":"2016-10-31T11:08:41.017766Z","licenses":["agpl"],"lastModified":"2016-10-31T11:08:41.068782Z","isNightly":false,"rawPhpVersionSpec":">=5.6","rawPlatformVersionSpec":">=9.0","signature":"UXeZVh5f0/WZE+r/dHWc1pu9q4qr/zACX6XraMhhIuKIp7vNCwUESeqJtVc99QZw\nw9kJp0isvib6l0zbQBRS1vI7xwKKBQyeaEhIciEs1JjsaCiato1Gyi26N+fY2N0Z\nFWlTwCsF3DdlwERXTYfeCpsOWCoxLxHKhnJIjUc0PVme/Ste4zxYj+5Su1RpadSw\n4vGnkW8zy/0tzua50NQCrOg+B4jXzH9kMWAP47w3sdP5CYalHSHl8EX0D1RjgGU5\n7vZYX3wF853FvQDbL4JXXCJupj3wZe8py8McWpQIcgz1p3KmE7A7d/rdNWExT+T+\nDxtStJ56qTRMz4aFwoSFxJrrEfgHIsE9Gv+Vo7nshCUYA8gkfHeckiaUtH1EiFTh\nVNeO6mTIqGpRosFvfUrZMKcuF5j74vGQjNM1o+M5N31gtLoPSkU605f/U4v+j2oC\n3/N1rYF2SEDFO0EgAGXaXVhB0ltSDkHJw6vZJ1L8Qz6tooUMDxaMri8vycA6LHvE\nqN+z+S6TXMfLvN/6ATYPGhicrWmkMT/k7v1az/hcnfH+zRyLZyFx94s88JWF7Jf+\nI+tgDbfqTMAIcytJUC+KfdQW1ueXh5F0owrOYM6jgBRvhqj1T8s+Twgw8orGmRPe\n9h8G9Z3wZAooQvmC0KdVhLuOeIkqt/S5krELNFFBRnk=","translations":{}}],"screenshots":[{"url":"https://gitlab.com/eneiluj/gpxpod-oc/uploads/db5af6ba7ae1cd4d22ea81d418f5c762/screen1.jpg"},{"url":"https://gitlab.com/eneiluj/gpxpod-oc/uploads/123588561a8067185572a8d1887ef906/screen2.jpg"},{"url":"https://gitlab.com/eneiluj/gpxpod-oc/uploads/427688b80bf8428dd45bd15d69b19075/screen3.jpg"}],"translations":{"en":{"name":"GpxPod","summary":"Display, analyse, compare and share gpx tracks","description":"\n# GpxPod owncloud/nextcloud application\n\nIf you want to help to translate this app in your language, take the english=>french files in \"l10n\" directory as examples.\n\nThis app's purpose is to display gpx, kml and tcx files collections,\nview elevation profiles and tracks stats, filter tracks,\n color tracks by speed, slope, elevation and compare divergent parts of similar tracks.\n\nIt's compatible with SQLite, MySQL and PostgreSQL databases.\n\nIt works with gpx/kml/tcx files anywhere in your files, files shared with you, files in folders shared with you.\nkml and tcx files will be displayed only if GpsBabel is found on the server system.\nElevations can be corrected for entire folders or specific track if SRTM.py (gpxelevations) is found.\nPersonal map tile servers can be added.\nIt works with encrypted data folder (server side encryption).\nA public link pointing to a specific track/folder can be shared if the corresponding gpx file/folder is already shared by public link.\n!!! GpxPod now uses the owncloud database to store meta-information. If you want to get rid of the .geojson, .geojson.colored and .markers produced by previous gpxpod versions, there are two buttons at the bottom of the \"Settings\" tab in user interface. !!!\nGeolocated pictures can be displayed if python PIL is installed on the server.\n\nGpxPod proudly uses Leaflet with lots of plugins to display the map.\n\nThis app is tested under Owncloud/Nextcloud 9.0/10 with Firefox and Chromium.\nThis app is under development.\n\nLink to Owncloud application website : https://apps.owncloud.com/content/show.php/GpxPod?content=174248\n\n## Install\n\nNo special installation instruction except :\n!! Server needs python2.x or 3.x \"gpxpy\" and \"geojson\" module to work !!\nThey may be installed with pip.\n\nFor example, on Debian-like systems :\n\n```\nsudo apt-get install python-pip\nsudo pip install gpxpy geojson\n```\nor on Redhat-like systems :\n```\nsudo yum install python-pip\nsudo pip install gpxpy geojson\n```\n\nThen put gpxpod directory in the Owncloud/Nextcloud apps to install.\nThere are several ways to do that.\n\n### Clone the git repository\n\n```\ncd /path/to/owncloud/apps\ngit clone https://gitlab.com/eneiluj/gpxpod-oc.git gpxpod\n```\n\n### Download from apps.owncloud.org\n\nExtract gpxpod archive you just downloaded from apps.owncloud.org :\n```\ncd /path/to/owncloud/apps\ntar xvf 174733-gpxpod-1.0.0.tar.gz\n```\n\n### Post install precautions\n\nJust in case, make python scripts executables :\n```\ncd /path/to/owncloud/apps\nchmod +x gpxpod/*.py\n```\n\n## Known issues\n\n* bad management of file names including simple or double quotes\n* _WARNING_, kml conversion will NOT work with recent kml files using the proprietary \"gx:track\" extension tag.\n\nAny feedback will be appreciated.\n\n "}},"isFeatured":false,"authors":[{"name":"Julien Veyssier","mail":"","homepage":""},{"name":"Fritz Kleinschroth","mail":"","homepage":""}],"ratingRecent":0.5,"ratingOverall":0.5,"ratingNumRecent":0,"ratingNumOverall":0,"certificate":"-----BEGIN CERTIFICATE-----\nMIIEATCCAukCAhAaMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD\nVQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI\nMTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB\ndXRob3JpdHkwHhcNMTYxMDMxMTA1MTI2WhcNMjcwMjA2MTA1MTI2WjARMQ8wDQYD\nVQQDDAZncHhwb2QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCq9p5l\nzNzR98l/xPgrplWrGQBhF6aQSLpnIyCdLAYKk+CmXn47W1pHh5CRYyCCyB8YPBBG\nTwbpP69pP1updfK2vUt+ShYmCXLxOLB3qEdhnwgqFfwpC48Ocev9d6XcacYp7jwu\nRNtv0ocMkm5o0TWWupcutQWkmqzCVq+OkrqM0xrf3mfPymuM6edEREshukoL86Ei\ngTuMMGT0XO99LikszvdceNQYEATix1MHzSVhkE7jHCNBXb95H6nQGr0v7R1MIbrI\nGFlgqxwwNNKwBFNfPMWZVnKwz9hoIwW6WOuu7ntwVcPqwB/gUsRZJTu7EjIW0trX\nnhA6xLlc4X66W1sdUCkJOxsV+Y21akz6wynI0SzIfjALLI2Ls4QWrPM8GOX8nPVM\nm+Y5WXzqLJScdWYoefFJKS7kxwUJRewREB9ykCG5OdDubV+Iu/6jh6HWx3h4p3ih\nqkDypPWoxpfLgA8VZkLD1RRKGkRa858QBGdF/RHbYT3JfLEp9l9gJVKZE/yw7HKk\nwsZ/T6CMpLyorpd1XWtp2wLX8lr3pp9ecVDOdAMSqD2thDMDsZA82JrJ/vITwkCF\nBlqtDZmT0UnpxYNYTfYBam5Cd00jsqCt+Hr+QkODNe8Yae9c/D0zE3h2Vt7g9H+W\n7Ei+rF5nDYTBAApoETxK7+aUZpycBf3THAJOcwIDAQABMA0GCSqGSIb3DQEBCwUA\nA4IBAQBbCGAEwg3M5QJDMnZgu0zNOH2f9bamAS9ksyCZqzLoeQic1W7GYYe9NqAi\n7lO5jXRZpTN4L133IUQPtxCxuDooD2vFmCne92tLxJbc7uqlSVfhL8uMVOlnrA99\nKTAhySTZU5so8/OibrngnBmcdWwbhaWoCQ671M8aXM1wg2FVYDqB2GP3RvbpW11L\nOc+4tfh4mO4TwXygf7KYMOJyJW8mNNY7PZ+XW2Qe3vSXR3DuN8H8fgMh5wppXPJf\nE0+yNs42hwFjSojtI8BCb0s5DTleaakpDo8HQGNzEXP8tBlUYudtjzdP0jxFXbFa\nsT9pcMdeJ0/t5HqJSx1EjUCLYS4y\n-----END CERTIFICATE-----"},{"id":"ownpad","categories":["tools"],"userDocs":"https://github.com/otetard/ownpad/blob/master/README.md#mimetype-detection","adminDocs":"","developerDocs":"","issueTracker":"https://github.com/otetard/ownpad/issues","website":"","created":"2016-09-29T15:58:52.814912Z","lastModified":"2016-11-19T17:37:52.278497Z","releases":[{"version":"0.5.6","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=9.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/otetard/ownpad/releases/download/v0.5.6/ownpad.tar.gz","created":"2016-11-19T17:37:52.234684Z","licenses":["agpl"],"lastModified":"2016-11-19T17:37:52.423930Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=9 <=11","signature":"dh+Txg1iVfqXr8+cxplNQuBZGErSnXUo0ewGwnybNMJqp8/EjEo72+zPpW3dVnhY\n67YCvhrm2bo+VRdFFymEfymzSJu9nWVFkGJhEwvTxPyIdAtuD5YAVrzmnR6L+H7m\n7Q1nXE63ICPCAQpHkxIfIXLh25OhWeyofBB8AVsjDUNn58FEYJ8fFkr6dCgPriZS\nsM2J+xtZMDYufy+xFMsVf/Q3WopjFuBjMC3qOecW76ZTwtREaswOC2RtpzUku2r1\nsogrfFlFer3Ii9/CWgOktnLfjB1DzbTwdEkM2xNVBRJgdMXt2VLA9FsxFFkjmr5A\nl7x9cNLWA8RLpOIpIMBbaef75u5HgRBvSvq114UsA9GCu/EYbIgD8YxEt7xuKd4t\nenksJB5gJ2IQNdHrPbsil59AsJ/dismDN6ktYgWQEk5dINzvm9EAvucueW0Gt+Jr\nqEC5WBgJucsFxSvkHh52v43M8jgPYBfHWEL/M/+377z3+mbuIh+BcQ+vcDdiqxTF\no3n0+gw3QYIhLEe75sUhxG6ynVUdW25AKKju1kVj3KJnZTBH1R8t8/zy4DnJG8d4\nuRGqyU4BXpZjEC3nVlsC7vCncWWhxl0WZQ/MWKqsfjVAU4I88E518D6NioqMnPEJ\niCZ2x+69UCDEQyfCSKajgAYT17r3OhZei8F9KSCH8Vw=","translations":{}},{"version":"0.5.5","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=9.0.0 <11.0.0","minIntSize":32,"download":"https://github.com/otetard/ownpad/releases/download/v0.5.5/ownpad.tar.gz","created":"2016-10-06T07:51:05.278637Z","licenses":["agpl"],"lastModified":"2016-10-06T07:51:05.348825Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=9 <=10","signature":"nYsQ9U5r7uXwtcquaWGm2XMJBNYCcA95aUx9gLZ/wEmjCHqId7+MzcCdBnom33+j\nat2XR2a4R96UAUP78bfSC4Yb7nPepFT51Y8CSpV3mDT85/+SgJdq500fXldq+qGY\nffXLneadAztyOfZO9TXljaCLdREYI0LJIGVENsxBQVKM/eyGIuZO7fF70cH5vbfS\ns37+BXB+fxcDTlP2Xuulra8HsNoS81bzjsdVMLM7B7QwwO6rZ1zd5c3UzQ1LmY5g\npQUBNd0KjfHfZ6+Fd64XZO6NGfgucGNmL3lgxdsfUqKiLtikvFxK39dYW5MckV8p\nvLoS2nZ7cgETQmAW9Ahn3ro7gXWcPxzL41oWtZOOHRRC2Yz5zlZ3Bky1o+bF9g5a\nYdDF13zV6utUkhlplZhWbjKaXa04rzOvmut8Iqhx/tmDtZRYtaQXJZWutVJYtPC3\nH86uJJnUHHNFHeoT560mp1Hq0dTeR+G+yWsPacPD1rTYgZOUVEtj3Y+YdbTODR2o\nOdGzeYFl+6CL/OcY4wPGRUCTFwvc31lIUd4DK5SPfN+IGtuuXhAqVhwy6lpkcKRs\ncj8sBoVXbMvEtMnt5uARBvA4tyVffUL4oyoIsUnvXz4u+q4WVt3T17swLm6HjGVC\nNVqU0srHN7EeBRhHlXP1CrKQWGQlS4k9j9Li4Iw+X8s=","translations":{}}],"screenshots":[],"translations":{"en":{"name":"Ownpad","summary":"\n Create and open Etherpad and Ethercalc documents.\n ","description":"\n Ownpad is an ownCloud application that allows to create and open\n Etherpad and Ethercalc documents.\n\n This application requires to have access to an instance of\n Etherpad and/or Ethercalc to work properly.\n "}},"isFeatured":false,"authors":[{"name":"Olivier Tétard","mail":"olivier.tetard@miskin.fr","homepage":""}],"ratingRecent":0.5,"ratingOverall":0.5,"ratingNumRecent":0,"ratingNumOverall":0,"certificate":"-----BEGIN CERTIFICATE-----\nMIIEATCCAukCAhAPMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD\nVQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI\nMTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB\ndXRob3JpdHkwHhcNMTYwOTI5MTU1NDA3WhcNMjcwMTA1MTU1NDA3WjARMQ8wDQYD\nVQQDDAZvd25wYWQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC6CY7I\nHRJTaqDu376vt+kruX+QOL864joScxRuh3IOVcQktCvxasuA0EtrX7TCAQrV1tBK\nfkqJxU9uOV54RTgyh30yH/ZtnF2bYQwViGM06Snc0riqWydFrN5fxK52dpZWs63o\nUFCNhHxrX4aUGyfXu5nQMISLm4QHoZ3LDLofk1ZsiK62fM/Jz8N2PM8qeHzf1ATo\nSKcAOd3UeaS9C8bv2DuiZM7unkSO/tjrBzkMiq8ds9sIzBBsyk6BRh2HQjHPOtmO\ned+pS9mIZmc2xhssXoHL4IfZwTqwhktpsaTl7v0ROw2dwDATz/QoKMkUpboQ5lkz\nwgLQhoIZw6uAZ1R/Qjze59I3iU8zIo9quDarHBotZNXboYCmg9FRfE4mHtegVaa8\nv1a1JvFQ5gvsWEsKSV6Bzb65GTp4KG4q7YnUrzh6HJyDCGLvLlWm5OWsFj6sNzXX\nwLOv6JLORMbF4ZIo2iybb3x7gdfCu9JxMZ4JtOUC8KSJ6+ub15C1Aia3lN68dNts\nY6KwUF1Ted0o4OQPAulq5pUc+g6dTYmIKsavIiPKhMtl86AbUK50vRTeuGdFsT7X\nav73IanPdFI9bKth+tajgvB6dxcVnvBXbrsLUyEcsxsxtBJvQcMYS4aZ6ZJYLTep\n7AdK0Zt1iMdXB8+4PCps4rcG6bYB/uJeEAVm7QIDAQABMA0GCSqGSIb3DQEBCwUA\nA4IBAQCM10O+sCYhIExnx01vGzKlnRS7MSQNx8ZMmbR5Elfz4AVJAEJ96ytS2DXH\n2c+hcD0wAenXQEFk920AEqFQBT8DP34p0FmF83aMHW08ovzFiu4MdlhcqrLnko0h\ncZTXHVyS/8JZh+o6SVm8R0/BBLF1MQQ5TqRkJehbmk6gL0+MSYxehUDKWTjJITkR\nifneTw/Ba1d0AXBOq0c0HFyGxMPIlWe4qn5LtxH5t0wyVGeSj4jyv4nvd3ZGuAgY\nEUa2uYht/z475k4+vf0YhV98iQH07GnmlfD2TDZgmOCQGKlNfJh1v88OZyLLa3dz\ngRHzGwKbAiJ8T8bbpZ3e2ozXxADr\n-----END CERTIFICATE-----"},{"id":"announcementcenter","categories":["organization"],"userDocs":"","adminDocs":"","developerDocs":"","issueTracker":"https://github.com/nextcloud/announcementcenter/issues","website":"https://github.com/nextcloud/announcementcenter","created":"2016-09-14T10:38:53.939634Z","lastModified":"2016-11-24T11:21:50.324839Z","releases":[{"version":"2.0.1","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=10.0.0 <11.0.0","minIntSize":32,"download":"https://github.com/nextcloud/announcementcenter/releases/download/v2.0.1/announcementcenter-2.0.1.tar.gz","created":"2016-11-24T11:21:50.317635Z","licenses":["agpl"],"lastModified":"2016-11-24T11:21:50.386203Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=10 <=10","signature":"lmqeE6xBqUJfhuXPbjCfuWiIP0FEB4V/SsF/OvYar6rLpvDpJVf3DJoeIoxXurRP\nE9/xCcNN44P8PreRRDnFLCa0XsKOtwoGa56Lxk7IKvtiQG6xu4J6PKM+q/tIeF9K\nakw0LQXtjZB5InPhnCDDbY5YS9jgGEBylSHsgNgrElipcW+BzOBu1Amw4FECVlQw\ncQ83bio+YPZvsnE5+v3/bAx0m6QNxfyN9Sn6rMEqRkY3jfA3vejXGQH/XkputfV+\n5hOz48KbOVg7cKxg+ieJlSwC0aYjb+RXiopjc3icCoIF1llltOOeSsVYSflOb080\nupociPgQ6qIab/VNNXa2YQ==","translations":{}},{"version":"2.0.0","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=10.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/nextcloud/announcementcenter/releases/download/v2.0.0/announcementcenter-2.0.0.tar.gz","created":"2016-10-06T12:41:56.195206Z","licenses":["agpl"],"lastModified":"2016-10-06T12:41:56.263124Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=10 <=11","signature":"NVWYz73KtuoZ7ti2sluztJO5aFUc7PzhlDcg0VWyAQd1H7sk5wjw7i0bhrjw8O7M\nLsrb+PegnsL9eMlYM2WrRom+RF1PDP482xymZf1T8vh8qcTCm3TK89xSuiSm8yoA\niWUb/Uv/ODj74wVDWqWxAFKaAG/FestCB3InOOZQqQZLzlAV0U9ziYDGNzBjFqof\n9rLNxJ2IOqZOA7hhMIKhSrpA0KkSfNhBsVf8CWClYnVkZQiq0LoYkHkHIlXmXUr3\nOfQFKEjtsx+bNLa6CkAaocHGHJXAofX3GQZ9cjBsjZqiTfbXfcVk0kRfz7pwL92L\nI1McfJYvgMxDQG5bjRpNJw==","translations":{}}],"screenshots":[{"url":"https://github.com/nextcloud/announcementcenter/raw/stable10/docs/AnnouncementCenterFrontpage.png"}],"translations":{"en":{"name":"Announcement Center","summary":"An announcement center for Nextcloud","description":"An announcement center for Nextcloud"}},"isFeatured":true,"authors":[{"name":"Joas Schilling","mail":"","homepage":""}],"ratingRecent":0.75,"ratingOverall":0.75,"ratingNumRecent":0,"ratingNumOverall":0,"certificate":"-----BEGIN CERTIFICATE-----\r\nMIIDDTCCAfUCAhABMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD\r\nVQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI\r\nMTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB\r\ndXRob3JpdHkwHhcNMTYwODIzMDkyNTQ0WhcNMjYxMTI5MDkyNTQ0WjAdMRswGQYD\r\nVQQDDBJhbm5vdW5jZW1lbnRjZW50ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw\r\nggEKAoIBAQDPx4Hp1HdBo5v7bDEiVcv2UrVjNW+fPPKS/5rwbagtPcE/1v3WDcwX\r\nvFwaXk8qCn2UpPSQ2b1rTuTDm51G1ZmEZhNiio+rBfEe9F+3tLsq9lElqIPKhkAq\r\nEUVI6dcN+jSqvLmLhuwloEoQQSYaLrX75mY3lGqTb83h1l2Pk/brVixuVf4vJW31\r\nTgeieuGKnC+keLzKOrvTHffJakU8ktwB2Nuu1o+jN5a7u1bxKkP3LjEWPjq236hk\r\nAoOcW/wi1dUEyUKUZsZQeJyvTJh1UXdLHKwYywtUu1/VLZ1IUtNyPBfiQ8ukPp3T\r\nTnSSmG3ZnvsfM6DmAvLZ8bBQkMBzEcTLAgMBAAEwDQYJKoZIhvcNAQELBQADggEB\r\nAAB3i2NgiZ4rpNag7cXYdaFxAxdDWnke1+LX2V2R3hzGmx73/W6cKLpo3JBn9+zT\r\n1aEjlqkt0yHu4aAPVYQzOa5zIV8mjP84p3ODSyV9J8lfjFNXT7wdA8+9PVx3lVki\r\n2ONoCNBh1kOxnxI4+BsMlQfF00ZbBSuGcMm3Ep3lTFWXzuUn3MQITzPwkL5LkW6a\r\nsli/yAYQRTVDsXD8A3ACYT7BG31jGxyXtIHzqCci0MhZFdKKayMYkwfjZchIUtGN\r\nJJoU8LQoHwGRtp3wutk0GlFzpEQEvSsn/Lsvvot5IfIe46tnzA6MVj5s64s5G8+Q\r\nphhXFlzXqO/VxquPdbfYjvU=\r\n-----END CERTIFICATE-----"},{"id":"ocsms","categories":["tools"],"userDocs":"","adminDocs":"","developerDocs":"","issueTracker":"https://github.com/nerzhul/ocsms/issues","website":"https://github.com/nerzhul/ocsms","created":"2016-09-19T21:56:04.745481Z","lastModified":"2016-11-11T16:29:55.864273Z","releases":[{"version":"1.10.1","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=9.0.0","minIntSize":32,"download":"https://ftp.unix-experience.fr/owncloud-sms/v1.10.1.tar.gz","created":"2016-11-11T16:29:55.856768Z","licenses":["agpl"],"lastModified":"2016-11-11T16:29:55.947926Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=9.0","signature":"hVzbkmmtJCtiOkZGe1mkWElqS3IPQ8wLtSzikVvoKmcg+Zq3YLQjpQWzy0t3UVjo\n9I/BfnL0bF+kjtGc9xF6M8IQaFqPrfJmN+lNT8WYIKLI97TTsLmJGg8Q8PAux3nY\n8/NxMjWdByMw9nVBClKo0o9eSW4+EnaUSJ62Gl/XWjq728kbB16WZm+iesk8LjJJ\nqqAgczWGwz6lkZTCN5o9n0a/YoLJTf4iT+OItHZyS609Cqaxx9CAmZPj/Xn5g1fm\n8hqO2ITAXLoBj4rYS/QsZTMcubtGkJ8fq3XYKVSv2UXZfvGsNWbbGV7puKN33uWJ\n5MrdoMlJ8XnJRPDlCBcb00LY+AB+hAMooLnNy765/Ew6ztp4KNLEPWGG+Ut8/Lkk\n0jIULl1RF/FjlW8P26NfwH36K30RCJFY06OFcWnxGBkkQaNFORDIsKcqTAxkl4x5\nnfKBkNdQZppCVfOSKOZj4NkWfWx75Ouq1S0QksmOsMZoOcjy1TbBKR8h6dt9DQub\nWpYBL0QwyQShGp0Vb1qCKkP69ZQAHVUJNzIFPz9LyoguvFyv8iZmAPLYDnFBvlf2\nnSHtA19rnJmZ4H7RJ02r6BdkstxISvEiHU7RLjNQxcb+DptIWX5C03wH87HTNIhr\nvptPorEoSY1KwW9fqUvvLE/c+vfkr5cvIEwZlyVKVXU=","translations":{}}],"screenshots":[],"translations":{"en":{"name":"ownCloud SMS","summary":"A app to sync SMS with your ownCloud","description":"A app to sync SMS with your ownCloud"}},"isFeatured":false,"authors":[{"name":"Loic Blot","mail":"","homepage":""}],"ratingRecent":0.5,"ratingOverall":0.5,"ratingNumRecent":0,"ratingNumOverall":0,"certificate":"-----BEGIN CERTIFICATE-----\r\nMIIEADCCAugCAhALMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD\r\nVQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI\r\nMTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB\r\ndXRob3JpdHkwHhcNMTYwOTE5MjE1MzU5WhcNMjYxMjI2MjE1MzU5WjAQMQ4wDAYD\r\nVQQDDAVvY3NtczCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANqZVIzM\r\nwBJuacuvgYKr2KnXuYhjKjZ58nfspSebsaGLr0ifBbo8L+NH5eaynnLCNhpegmu0\r\nO8D+KrbM1LtIkcdg1/eFpN5dTc6G2OAc9H9stmHs9nroF6MNhszgdZCz8Q8xkSoD\r\nGdSm8hdPg5GcfLrH27UilGtzdQlWJ1DralLMt3l+SfGJo152c/dc+e6SuT8+EbY2\r\nCeLdH5ImasXNpUgY+tAoEt2ZvhBrUghykBJTJVOjwL1jGLT37ybMtV4FBKo6hpeg\r\ntq/YzEk1ijBAC4pmoNWixDKCdolpVJVz0fijI9mlda3llurcp8eMhxfYJ9soXLHp\r\njvLX02YY6RfPcyy48uWVk4IEt9BvZWEVAAp7hCGA2yXrVSsR37E6sDbLsBcKav9A\r\n6dkGAgfmGkr2WT6O1/EhK/MakmnYO4WD1B+E7PnxtP/wOa+aQBmntQcd7igDiwzG\r\n6h05NYAWcRhqfZ4KWYsq0t0SezMbuHOhwzzi22q8wijC5YZbmhKSh+b3N8XwYKDi\r\nZaw+fSahPbRWaLyR3wn9zh7vKCwqrG3ugrNo6CtyoACAnmxKZ97ROFJIQTe3ndLL\r\nmv7Wy8iCZLhRYUaW/GKrF11AFwBVec9xmvkgU+PIKq2HSjwi9sCF+pFyhVjmq29C\r\nmZEPKUV7ySIpNHXpsXm8kTJJfqjSdb2ECbLfAgMBAAEwDQYJKoZIhvcNAQELBQAD\r\nggEBABvn97e8Nw8KAscf6FX/nZ99rEX+3IrZxTC8fmBgNwAvlbF2A+QZQcFI4G9/\r\n85nHK117+u7XDuwWl4QG3flWlI0hDE59Ud9Bd4AiTQ12VoXlNdYoTg/mXARxVozb\r\nKYqZ+1xRQclZKb2AqW8YiGo18okIKovn9VVRAFYPYx4O3Ve1FjgfsaMlIZLiXUFm\r\nkk+2qWo6kYsdU9FABLo6izx7RFOMbnYNre5FmDrWP1Dga/U7ErK/Dilh8g9b3HrP\r\nwP8OIZhdtFWw21wDTfyqrb9EhC/tsjPVP9u+bqyognHeiMhjbVYRbSvz5o8T7Mhj\r\nbxalCt4/LnMIfMwVyIvye7Uy2GY=\r\n-----END CERTIFICATE-----"},{"id":"rainloop","categories":["social","tools"],"userDocs":"","adminDocs":"","developerDocs":"","issueTracker":"https://github.com/RainLoop/rainloop-webmail/issues","website":"http://www.rainloop.net/","created":"2016-10-20T04:17:37.217555Z","lastModified":"2016-11-18T11:36:04.309739Z","releases":[{"version":"4.26.0","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":">=5.4.0","platformVersionSpec":">=10.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/pierre-alain-b/rainloop-nextcloud/releases/download/v4.26.0/rainloop-4.26.0.tar.gz","created":"2016-10-20T04:28:21.491747Z","licenses":["agpl"],"lastModified":"2016-11-18T11:36:04.619927Z","isNightly":false,"rawPhpVersionSpec":">=5.4","rawPlatformVersionSpec":">=10 <=11","signature":"nTYIVSB6mIwKtXIrKoVGsOGFflpLjed8jFem1VLQNtXQj4bztnNrdc4YaPIn0yzM\nyLpMSqRDNzdYNFuOeDiyKLPJPTA++MotLCNjEe7kxUekek+m+qzgnGBdcT7RQT6R\np9xWGecnVx94d6aA55uiRhgQRyHpdDMMLCOz1be+HvpwHy69DRFZ1+SPmGUt6eW0\nu5yS0vHCu1K22cbrVNXFKjxAOlGcIDm61oQuz7ycl3uAujZO4rZbWt55jilgKGak\new559A5gTp9W+j+TWKIcg6LIZ9zLRlGjcQrWJrsc+OBZQcqiYimSFyO6HhfT9TPS\nPof//I+dSsd+H0SRGGeL8DvSnK+NKZL1q5EX5pziqsv6nZFITpCDwmAN+I8AnXXL\nSNkFi53M8RZTOABpD2x7YPYP1cEvwrRweqV/C/oHcYnpfh7D2DjFeWwXsjeAXrHY\nhgFhPrg+7rf7g6UmJFOCp0dC9sBdyQ3KtJkv7bGqPr854r2cdA7xW0QHWQ2in9qQ\nLhIczc32ECi3ZVVgyF8zyT4Y/3MRS05oX3FHvHyt88mjni6bVaO78F7ZRSha8gHh\nNOAkku7AMXPvUCHaZP2iVCCoAViEso8GeR3O8xh2G42Ai61RLYwx8LB1+23EoJTr\nmfFuRYNSg+qAKCokXNnh+lDlwu4AkaQo3vtKGPXvU7A=","translations":{}}],"screenshots":[{"url":"https://raw.githubusercontent.com/pierre-alain-b/rainloop-nextcloud/master/screenshots/2016.10.20-screenshot.jpg"}],"translations":{"en":{"name":"RainLoop","summary":"RainLoop Webmail","description":"Simple, modern and fast web-based email client."}},"isFeatured":false,"authors":[{"name":"RainLoop Team","mail":"","homepage":""}],"ratingRecent":0.5,"ratingOverall":0.5,"ratingNumRecent":0,"ratingNumOverall":0,"certificate":"-----BEGIN CERTIFICATE-----\nMIIEAzCCAusCAhAXMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD\nVQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI\nMTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB\ndXRob3JpdHkwHhcNMTYxMDE5MTkzNDEwWhcNMjcwMTI1MTkzNDEwWjATMREwDwYD\nVQQDDAhyYWlubG9vcDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANqB\n5jnF9qZ/qjckt0kRjpHCOMtJumW/KiQoMeNP5nGv4ad0DS3KemOapUef8Zn7qCYb\nMnODhK7HBwPifFzI1j8bnT2hP6E0geFLb0MdN59d2NF0n4CCs1+BnepQPJ1kFbPK\n35wQRi0RDeTf/GQ+/owEVCU9a9W1P/VUXk8Z0vMoQxCXEdRqnB63SgsKl7DB9G/C\n4SYrgGor+OHVGl4ntMZhJujiM996DttrNK3iZRGkQ07L+lfUIwQ52XOhQNRdic4p\nB03lw7PpChwPGMv/EEvdR5HpCJQBJniqJbbu3Jh8bMBKTE/8fCzN3vMXICB2g3Bq\nlKkZW6fnJRGsrZ79fsQnl+WBPNSrWRLOxOfe1fyCFV1ljFB4nTH7uF3pC8ZRgJes\nkHIESHz3GJm28hn4+17ESMGHBCbs7L9FK2GY31cobU0VRntLxpSG+d9njbIAgMG1\nS7U+oKVFQhSVpdXNOaUNqhcQ3HkbQTLEP0k53A/lhLQb2+KPd8nntaELjwNyrmZg\nsVMgHj/zdlvrbguZjZFzUzDBFvkuv/5M58lNT/D1C6ufVp/R6eLsYI+nnk1ojAjz\nl7N6U8X5SXpD+Bm7+Kn1PH+bHl7cViCx8oXJXO2RhP+COXckw7BDZKtjItYHNG7M\npFwgYqWpvCu9LN6IN5a/eLqSI76dOOP3iYbaTH+NAgMBAAEwDQYJKoZIhvcNAQEL\nBQADggEBAGB0Vq0l6ndGTgNbZxSEFyBR3u3tiR3pWK81DYjsui7qBoO6P/BaGmf+\nraSwHPaBOwA9XNS8jcGLh5xdqY2p/m0dTS64xNjVL9nweWsG+FwVnPANo8C4nXdm\n9ajJ4cdg54stQK8qn1uh/xPcd23GKfYJazjYSwYmZ3pXXdzlGN9NxkeYJQxJ6B+5\npzAeVGiABI/e5URpxzz2UayRX7EE+vtpe3B84hzkLqsq0N39ZN6KLfaTyEBGLzqE\niLYeXQTV0XSRs8xVt+iyGlj7nPkv2DR0oCqRpWUFWeSBI//niDG5WxS3qg8kacSW\nfDSYhSN+IjrnIkwNtc8V9t7/GeQB5FE=\n-----END CERTIFICATE-----"},{"id":"richdocuments","categories":["integration","office"],"userDocs":"https://nextcloud.com/collaboraonline/","adminDocs":"https://nextcloud.com/collaboraonline/","developerDocs":"","issueTracker":"https://github.com/owncloud/richdocuments/issues","website":"","created":"2016-10-31T08:55:45.631429Z","lastModified":"2016-11-24T12:13:53.905352Z","releases":[{"version":"1.1.3","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=8.2.0 <9.3.0","minIntSize":32,"download":"https://github.com/owncloud/richdocuments/releases/download/1.1.3/richdocuments.tar.gz","created":"2016-10-31T09:03:40.389355Z","licenses":["agpl"],"lastModified":"2016-10-31T09:03:40.439510Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=8.2 <=9.2","signature":"s5giQeiU2zwV5X6FmxWXiG9LtNDeKBlFqK+hfvGNbGZ+nic77Y+AnXHodV4lb3Ko\n0C0ThFLuafaZRdp9rBIN2K/acCfCYKJewGuYErb7FlEl+P9J4OQbb9pva0htZJw6\niuG5eyeTufi5MKB4vuj4+jo9zhepOFAtZMa7o+ZCfJkt8vDBuq5AXxomEiZRtW+n\nf9PPUnq0z7DJVwINhHvvBZJlSLjkpJ6VIHAr+/ElWr8O/mDKq5S5ohbvpDcPqR7b\njnsBckFDLFUz1FX9dA0JCJEKMMfkcfGqZcjH17NdjKAxRW2soN5cEKluu5MkOhz9\nFEPKfshzbrfUIm5MaFGv6w==","translations":{}},{"version":"1.1.14","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=9.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/owncloud/richdocuments/releases/download/1.1.14/richdocuments.tar.gz","created":"2016-11-24T12:10:13.337165Z","licenses":["agpl"],"lastModified":"2016-11-24T12:13:53.963638Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=9 <=11","signature":"prDGlfRPxqT6LP0BsAFPwGww7P4Bngha2N4u5B6+F02N+RVOjGtTcXKqvM1KjZb1\nCo7qJvgJmjpvIvDmB+rup02i8ObfwP2ct6UdsD7ouzOWJG2sJANXK31bHyvOmQ2h\nvKu5eNcOkf+WFyFKYi51TbsfWn2+1Wge3WWujKAVcEvqtcOOz+uMWNtqzBptEupk\nE1aaRnQfTx488YB8Ubul06LIY0PNCHgGCWPgy817tOVT7JA+V0P0FFonl/PXE0dr\nWgtxRJmvGaNiFzYq+kQmdKMfayZTm3kdVgP0W52t5wp878K0i4s2KPg5lANvjTz7\nDCT+VV2IGIE52o4RpMUGyQ==","translations":{}}],"screenshots":[{"url":"https://nextcloud.com/wp-content/themes/next/assets/img/features/collabora-document.png"},{"url":"https://nextcloud.com/wp-content/themes/next/assets/img/features/collabora-app.png"},{"url":"https://nextcloud.com/wp-content/themes/next/assets/img/features/collabora-presentation.png"},{"url":"https://nextcloud.com/wp-content/themes/next/assets/img/features/collabora-spreadsheet.png"}],"translations":{"en":{"name":"Collabora Online","summary":"Edit office documents directly in your browser.","description":"Collabora Online allows you to to work with all kinds of office documents directly in your browser. This application requires Collabora Cloudsuite to be installed on one of your servers, please read the documentation to learn more about that."}},"isFeatured":false,"authors":[{"name":"Collabora Productivity based on work of Frank Karlitschek, Victor Dubiniuk","mail":"","homepage":""}],"ratingRecent":0.5,"ratingOverall":0.5,"ratingNumRecent":0,"ratingNumOverall":0,"certificate":"-----BEGIN CERTIFICATE-----\nMIIDCDCCAfACAhAZMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD\nVQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI\nMTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB\ndXRob3JpdHkwHhcNMTYxMDMxMDg1NDExWhcNMjcwMjA2MDg1NDExWjAYMRYwFAYD\nVQQDEw1yaWNoZG9jdW1lbnRzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC\nAQEA1jk29m6JykcJ2Ld0YEpjPMYh6kwxY6GysNJnfkA/th7tPWL3+vBJ9oTYyVnZ\njwAE1Cqwfa9MyBKMZ2IdfIqtT8PeWzuFP7Ib942EdxUpwwh9F3lykeGsj0h4zQwX\nF9OooiS99PfLX+JpkKm15Ujb00iLB6xQmq0+3NeOT1CTD1ziJ1ueOcxBKMwaFp2a\nPuz3F5ywqCvpmxG/OBuOs0LI3/zStXhBNbUMxBrWblr7zaVNJXl/I2JCKj8Wah/H\nXUEEGbW15fAUP1f+90eQSxpEoCZDBHXOQCTJYzySGv+BjU+qlI9/gS0QbrsiyzUf\nO5lyvi8LvUZBzpBw+yg1U75rqQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQA9jU3m\nZmD0ywO3MUsG/GLigFtcWi/p7zp2BliR+NpuY2qNFYDcsIb8ZUudmUc/cJRRctzy\nAPaLLj/d+h5RFaxjTVvim1PSe6M7urK/IMSvyUVYCeQRYpG8ZJixKTCOVIBaWHMz\nxTfc51tm9EPlpJpK6JtaWrYYoWGE3k9sINdJ4JkvKkE2CBAqVhX6ZGyEQ0bnEhtk\nRu1DXn+LW7TJ4NZ8VtLWvmW/6Kfmi7dQ1V++Kmn0lO5ntRt5altePbStCHC8bhGp\nmyBOrjhrJgLIwvgH26MYZhdiSkFzoE38nMPZdrUmUDxcPCwucWJqgzDPudguFthj\nWCVZ3TTG/2z3+tWM\n-----END CERTIFICATE-----"},{"id":"ocr","categories":["files","tools"],"userDocs":"https://janis91.github.io/ocr/","adminDocs":"https://github.com/janis91/ocr/wiki","developerDocs":"https://github.com/janis91/ocr/wiki","issueTracker":"https://github.com/janis91/ocr/issues","website":"https://janis91.github.io/ocr/","created":"2016-09-19T12:07:49.220376Z","lastModified":"2016-11-21T11:22:21.024501Z","releases":[{"version":"1.0.0","phpExtensions":[],"databases":[{"id":"pgsql","versionSpec":"*","rawVersionSpec":"*"},{"id":"mysql","versionSpec":"*","rawVersionSpec":"*"},{"id":"sqlite","versionSpec":"*","rawVersionSpec":"*"}],"shellCommands":["ocrmypdf","tesseract"],"phpVersionSpec":">=5.6.0 <8.0.0","platformVersionSpec":">=10.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/janis91/ocr/releases/download/v1.0.0/ocr.tar.gz","created":"2016-10-24T06:50:43.283900Z","licenses":["agpl"],"lastModified":"2016-11-21T11:22:21.269108Z","isNightly":false,"rawPhpVersionSpec":">=5.6 <=7","rawPlatformVersionSpec":">=10 <=11","signature":"CBJkCIiUKyf2NuWfz2zJ3grhf8p7wJes7DPV/OxUzhlxIH0Fh7K54+U5A9JOOi6f\nWPhjXG1ylkyIVY1glr/B8svWNsD4jAclpnUi1/9ZW5UPT8LnRBfTbtF9Uoj0OgNs\ntsGQYbpuREoHnjbJWTRe0kq1OsOfX44xuf8PuX43B+lpQPW4iRSSz3ZIhdPcDGq1\n7pyqQM7gdKhBQ6/tOiwd7Enyt5Hi4V6jhwhUOCYeTNiLD2V3yKL+qA9DzpXUfNNw\nLGTjcaMrifibHQIZBZWbPPMmCfMJZ7GO9oR4gWHwkhWqt0yVWAJXAHJBLd5vXC5I\njtRTXRpHO/k6Dtqem8tZCVoDE5MAC7fDZ/0XzoFiXHciP6MenVasVcXo6xJOJc5y\nGsrecNftUEhP/ngxA6lMBVkLmmdpiexVisvsavPi64i34OUA6qOuxjgNVBDwg56i\n2lOEVvHa3nn0UX7ZZoQ/Nu6Mz7J3Hx/VDlttPuWe42eeJAphyDGubT1M62gW8dVB\nD3tJOF7spnK6I3BhVLviou/zs30AIRVBDTU0Orzx78cbInwy6/vyJib2a1olAaHz\nv05SzlQRnBWM4jYBe0mA/2ds9AO6VrXGrT/iLlHemj6JYoGBM185TGewA7OJyX3a\nHSlSDqaremmi+aS3onx3AKhXykDxTRkMVarePwTzzFs=","translations":{}},{"version":"0.8.8","phpExtensions":[],"databases":[{"id":"pgsql","versionSpec":"*","rawVersionSpec":"*"},{"id":"mysql","versionSpec":"*","rawVersionSpec":"*"},{"id":"sqlite","versionSpec":"*","rawVersionSpec":"*"}],"shellCommands":["ocrmypdf","tesseract"],"phpVersionSpec":">=5.6.0 <8.0.0","platformVersionSpec":">=10.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/janis91/ocr/releases/download/v0.8.8-beta/ocr-0.8.8-beta.tar.gz","created":"2016-10-10T18:01:16.076330Z","licenses":["agpl"],"lastModified":"2016-10-10T18:01:16.169733Z","isNightly":false,"rawPhpVersionSpec":">=5.6 <=7","rawPlatformVersionSpec":">=10 <=11","signature":"uEvhHfQCrzb6z+QuOoO8rYXiMsZFkrFWEqDvTyOTSgFKvo7dVoj3EfDfaApgcKEB\nIM/SqjLSO0lNhrp8F2mST3twbvLDprKsfrDWKFE6eiH0yKl2aNB+cHWE27utARaX\n/QZBD114vbWeDnbaBa4b9OwtBXDqKJrnO1LmqSLFP8guAlVTkU1jSPkRTpmwAcAZ\nJA/aiN/D2LSGfiNp/YdeTuzU+gPINIs9dCb6+PPkyam8PCBaXUSSaW+c0lAQHln+\ntb3EXxZ5YXdjPWrpEyHvFLk1N8s/w615QoMxr5fEs1M8D29aGbcL/wu7LXH4X0Yn\noiWwIFbpfrpJQlrIFumxWZR74JXiNr9J7ijnQ7SjxdHCcrLxMdnZ2cwq4iD6PnYm\nnIojhlhPOqUIzsWYCYutLWDQbUQz9eyWbj/+7XL+5KjetUUr/MnCu6xJ27IqFbAX\nHc8KRCO+9I0/qMQ2/rCZXBMeo39MGhhkgkVl5YAKwC1IEN/jlfyVNXZwYlfcHLKj\n6aNQ4zN6gGOerWWZ8qXtOeNJN+qv0nmXUKrZdnZUAhxOdB4G9Ym+JujxJZ9yNIWV\nsiqAI9J+OIzCwv/dzZhaHadONoo/RTF+Fl6Hy56HyBtMehb8w9p8ksVediqf33yt\nFAE/tzKtNK5NiRd+8MZkq/GbocaFUv3C7Y6pLMpTE1c=","translations":{}}],"screenshots":[{"url":"https://raw.githubusercontent.com/janis91/ocr/master/screenshots/sc1.png"},{"url":"https://raw.githubusercontent.com/janis91/ocr/master/screenshots/sc2.png"},{"url":"https://raw.githubusercontent.com/janis91/ocr/master/screenshots/sc3.png"}],"translations":{"en":{"name":"OCR","summary":"Character recoginition for your images and pdf files.","description":"# Description\n\nNextcloud OCR (optical character recoginition) processing for images and PDF with tesseract-ocr and OCRmyPDF brings OCR capability to your Nextcloud 10.\nThe app uses tesseract-ocr, OCRmyPDF and a php internal message queueing service in order to process images (png, jpeg, tiff) and PDF (currently not all PDF-types are supported, for more information see [here](https://github.com/jbarlow83/OCRmyPDF)) asynchronously and save the output file to the same folder in nextcloud, so you are able to search in it.\nThe source data won't get lost. Instead:\n - in case of a PDF a copy will be saved with an extra layer of the processed text, so that you are able to search in it.\n - in case of a image the result of the OCR processing will be saved in a .txt file next to the image (same folder).\n\n**One big feature is the asynchronous ocr processing brought by the internal php message queueing system (Semaphore functions), which supports workers to handle tasks asynchronous from the rest of nextcloud.**\n\n## Prerequisites, Requirements and Dependencies\nThe OCR app has some prerequisites:\n - **[Nextcloud 10](https://nextcloud.com/)** or higher\n - **Linux** server as environment. (tested with Debian 8, Raspbian and Ubuntu 14.04 (Trusty))\n - **[OCRmyPDF](https://github.com/jbarlow83/OCRmyPDF)** >v2.x (tested with v4.1.3 (v4 is recommended))\n - **[tesseract-ocr](https://github.com/tesseract-ocr/tesseract)** >v3.02.02 with corresponding language files (e.g. tesseract-ocr-eng)\n\nFor further information see the homepage or the appropriate documentation."},"de":{"name":"OCR","summary":"Schrifterkennung für Bilder (mit Text) und PDF Dateien.","description":"# Beschreibung\n\nOCR (Automatische Texterkennung) für Bilder (mit Text) und PDF Dateien mithilfe von tesseract-ocr und OCRmyPDF ermöglicht Ihnen automatische Schrifterkennung direkt in Ihrer Nextcloud 10.\nDie App nutzt Tesseract-ocr, OCRmyPDF und den internen Message Queueing Service von PHP, um so asynchron (im Hintegrund) Bilder (PNG, JPEG, TIFF) und PDFs (aktuell werden nicht alle Typen unterstützt, näheres [hier](https://github.com/jbarlow83/OCRmyPDF)) zu verarbeiten. Das Ergebnis, welches jetzt durchsuchbar, kopierbar und ähnliches ist, wird anschließend im selben Ordner gespeichert, wie die Ursprungsdatei.\nDie Ursuprungsdatei geht dabei nicht verloren:\n - im Falle einer PDF wird eine Kopie mit einer zusätzlichen Textebene gespeichert, damit sie durchsuchbar und kopierbar wird.\n - im Falle eines Bildes wird das Resultat in einer txt-Datei gespeichert.\n\n**Ein großer Vorteil ist, dass das Ausführen und Verarbeiten asynchron im Hintergrund stattfindet. Dies geschieht mithilfe der PHP internernen Unterstützung einer Message Queue (Semaphore Funktionen). Die Aufgaben werden somit getrennt von der Nextcloud in einem eigenen Arbeits-Prozess (Worker) abgearbeitet.**\n\n## Anforderungen und Abhängigkeiten\nFür die OCR App müssen folgende Anforderungen erfüllt sein:\n - **[Nextcloud 10](https://nextcloud.com/)** oder höher\n - **Linux** server als Betriebssystem. (getestet mit Debian 8, Raspbian und Ubuntu 14.04 (Trusty))\n - **[OCRmyPDF](https://github.com/jbarlow83/OCRmyPDF)** >v2.x (getestet mit v4.1.3 (v4 empfohlen))\n - **[tesseract-ocr](https://github.com/tesseract-ocr/tesseract)** >v3.02.02 mit den dazugehörigen Übersetzungs- und Sprachdateien (z. B. tesseract-ocr-deu)\n\nFür weiter Informationen besuchen Sie die Homepage oder lesen Sie die zutreffende Dokumentation."}},"isFeatured":false,"authors":[{"name":"Janis Koehr","mail":"","homepage":""}],"ratingRecent":0.5,"ratingOverall":0.5,"ratingNumRecent":0,"ratingNumOverall":0,"certificate":"-----BEGIN CERTIFICATE-----\nMIID/jCCAuYCAhAKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD\nVQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI\nMTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB\ndXRob3JpdHkwHhcNMTYwOTE5MTEzNTAxWhcNMjYxMjI2MTEzNTAxWjAOMQwwCgYD\nVQQDDANvY3IwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDDpXiwec4f\nXAT//7YBPSb4z6ZsBJSMfBq0VTL/HagjJnQ7BL+WagzWlS69IStNDqlIlenamYRX\n4B40heJIUinzYKjTRbr5UAw6MX29HibZccm/qgrk36o1XTgIsoRhmvSxbXDVIo1k\nbDOJN8gc2Gvswa8X+uOe9pfcDgAdqGxOvFnoKW89GnB01pCNT+xakNErGAFqVLsr\n2AeademAZnbxJ1cB54tQn2Bygb/7DKKY8EmFfIq6/27n9Jbph1FG9HIlWRT4/M2H\nU2pG3cCScWMEBPsW7kpfpnzLk7Q30Oj6k/rEYjJgmNYgg6oVnn0D9uRmhBYBnGyx\nMab1ilsK53lyuzQY0pmU8V5ULqpnNFAK6DVFfofEamDUhBPO+TZXEA5cZmuULRpf\nQQXmGpUQSyV6pS9WirMIqXFp9wmQ4vtjMdhu/6CP7cmtYZdq9uOhWEHbQM0mZUkb\n8hMjeItPx9XITI7Cge1JUOI8ZIwiB3USnQXcMd3v82l++/VgqHB7s5OaKPhygsWI\nM6RCoBcGiuQB5/fEUOg5ACOpGVyJiBda0Mi57AdoxdJmfnr7Bxcf2tAWIJL9Y7T3\nE1+V2BMxJOWwvVz26Cq83F41yXK2hJS+SbfQTqNUR8Cfh50CS9POvgRxNrJK9yvI\nkKle3ITRtGVM1XU0njWjnsdGg3D3O2mmjQIDAQABMA0GCSqGSIb3DQEBCwUAA4IB\nAQAbFddMbgfPI1szT57V1FKZrOrdYqQ7qjewlIQOzshGydbMtqS/9XL5hYocJCMt\nY6w+C/i6iEzO2Jx8D/k4rcZMXoVR6y3ZvO0Ke0gzSRsU+5eYj2FK1VV+cNIQW5Iu\nCYYIVa7pVPVHdeQH2Bba680bLV0HMF6b1fI9IwkfdCAinvCYZLjyEXZlmB7YjyA8\nHR7qPCNz4uG2Va7mlUHE3UYUYnlv8JFOV3YdbVL0nxhWwIdzSri5sxFIhdlabpzY\nyA1z/MCBEyTRo80jxFmL+MpwbsdbUJi7Qxlnd56zb6HHDGrLHXZTh9LXgyVbnhWL\nkxomWjIXQh4aMHQL4QF7U4EK\n-----END CERTIFICATE-----"},{"id":"spreedme","categories":["tools"],"userDocs":"https://github.com/strukturag/nextcloud-spreedme/blob/master/README.md","adminDocs":"https://github.com/strukturag/nextcloud-spreedme/blob/master/README.md","developerDocs":"","issueTracker":"https://github.com/strukturag/nextcloud-spreedme/issues","website":"","created":"2016-09-27T08:43:07.835196Z","lastModified":"2016-11-21T16:51:23.703819Z","releases":[{"version":"0.3.4","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=9.0.0 <12.0.0","minIntSize":32,"download":"https://apps.owncloud.com/CONTENT/content-files/174436-spreedme.tar.gz","created":"2016-11-21T16:51:23.689599Z","licenses":["agpl"],"lastModified":"2016-11-21T16:51:23.826509Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=9 <=11","signature":"Mhy3hXeGWlIujx1Op39MMRdqHYOo360BCwr4FPWoTNNggH3aS0gWlh48DAfGYK9W\netNiOqIuRyA0NrVlsqR2vDILgFtODJSbKPyHd3PQn3hcGsjogjQ+dkKciLNLinw7\nOhbv6aDdRFLBeRHpX/7wOnWL5W3ko/gyn0Awvi88M9+nC5aARtqncQqPy2SxDGzH\nKlOZHSNDnEQCGMhA8hNWWKdVwNUJHod/wmBWpW5QVNSJq5DqrKZcNwpGM2UUJoql\nEqUMwDXk5uVH5r5k62Tr9kguDWoUEG1OqQSyeMY24AmA64tq/HSlAdZ+CX32bc4E\nZvm+n8poJBrdSVmWEaa4ZfYaLFdOc6Kcuid1B1Sv9kPhD9WD6T1sicdzjDzcorBK\n/MLReCuSb2E8aPTnFWRoAZ4xCUGs1IXzX5fmxI8VdzwR42R6RhGJ/rqMuZRFenZF\nbOks45K5gE1da4QpkYOUQa3GVMNPqPiT3CqjmJ8tjxq7bGpb6v+YoCLACjjPpPZL\n2Y28qLxwHVaINDFUUxD75WWdrlulRbqHwiSw8jolP9qrpXhDuLAqYam9tRwV5K5R\n8uNawnFwWkicBEYkN/WtBTouWzehOPn38tHXov6SyEyD6lkuxUBZrsGQ2ru+t33U\nk0kKCbV0GFw43I+3Ji5DiB4TUVNZYVoPG1B7Qve+UfA=","translations":{}},{"version":"0.3.3","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=9.0.0 <11.0.0","minIntSize":32,"download":"https://apps.owncloud.com/CONTENT/content-files/174436-spreedme.tar.gz","created":"2016-10-20T09:09:26.520692Z","licenses":["agpl"],"lastModified":"2016-10-20T09:09:26.666738Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=9 <=10","signature":"D62Ck7JUnrYbrfFlX7xXVaYUMZIh2acmykIKapqfemD/tuX5Bvb08GYGTeFG61MA\nQzsqcIylDfGnC1UJbf8yWEX7PbyJD5w/R4WlbFv34njDvM8rBs4HpzSjkqQoykOF\nZpYAjH2ydfKqtZadgoIRm7et5B8X2AeoGg11ec52DId5t1wAEBcDIv824CDBUt8t\n0pVY8Z8n1MUYwU7DCjCbPy23br2+EBODFCHp+cFfMBLg3F0BJI5nY3Q8ku+0tqMR\n0NDxQcscNZ2Ck/wpVDWylfhpS+ICIxSMiyq7urP593mRrK3399SUyaMqDfgl/pxo\nqTzdBxHLaAqcnKZYglbqp+Gxbyj4teqCod8TiSMlp90VaxhC72ACuVQQRWQKuTNI\nZeW3YweWB5d7VErqBNmQR1tGnX5YFFHiKo41fVDQFsrOqHx4zP6AeU3nkl2ol/r/\n3pg553so1MOxMkyLEhGYGMfrdQqVEtajNWHUdj3B73LS+M3jcjBFIdOD+AGXPtDX\njCRymt07c1znhkL+aT8yY5SHMVbKBZj9mExL49hcLjAYYc4U++60uq9MFH5r9g4T\ndph+yT6VVEM/UH2HjvKsHv2wm937sNgG3EXQdh79JU8nCXIz7cVrJ8f5/9r6n1VP\nBbjtfDAPEjmfVCXX2gmgLuZHV+GMhLBS9bTh+61AhhE=","translations":{}},{"version":"0.3.2","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=9.0.0 <11.0.0","minIntSize":32,"download":"https://apps.owncloud.com/CONTENT/content-files/174436-spreedme.tar.gz","created":"2016-10-06T08:14:05.212553Z","licenses":["agpl"],"lastModified":"2016-10-06T08:14:05.278533Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=9 <=10","signature":"X9zXDyMBPoXPMpZ+XXWK3MLufjY2MG8lJ+93LiW3rv0iq9yd8PafK4IvP9czO6o9\nf/vNilq+1mfl6mjTvL6QF5+sySlzbRGbT3uTBwYXyYL07HVYgl1ZLrwe0kxvxqaW\nxTvPem7+HzwClI3VnWc7ylQfzGrcYIaSIg7nNq1GOHocsgZVNyj/nVW/eQx24MjZ\nuLzZs9SJqYoBGq+mo63vRswhqv5OzGebo+G6dHm0hvRSOw9qsWCDWBugiSRU8zU4\nD9PQ0e8WbyrIrQhBoUvvkuijO3zCySx606S1HUyaHmbJzMc4Fbpwz6ggmi6IRBbT\nFBKB1DWJDwN/7mY4fhS4KhircVnAHDqiBVCTu5i3pSfMPrwFymcmbn9OBuFHAFuZ\nd9PZvWQg4v32w+Q/NNvZVW5lRi+l0w5DEqNREaj79hljk2reZMaB65lvtV9NCYw+\nHmwWqsGqZ1SgGZuhYkOzEIkCfJ2fF/VpyavJ4X6bHP9yYdkt1pxnSSoZ2HC8mkG4\nBnPf28mEXRcY8EJv0rEePqKSBIhAt8yfEW+joH/8nupe1qOdfPvP08ifLad5m76s\nt23UzlSljzq9kVO+d16z2uagKomN9USZaNnJcUDVblfjvCPpdiHLfRPEJnRsDZCm\nNffFWEMcz+TWmwBboZgTRO9v0bPDEuwfCCEW0zy8rT0=","translations":{}}],"screenshots":[{"url":"https://raw.githubusercontent.com/strukturag/nextcloud-spreedme/master/screenshots/appstore/conference.gif"},{"url":"https://raw.githubusercontent.com/strukturag/nextcloud-spreedme/master/screenshots/appstore/presentation.png"},{"url":"https://raw.githubusercontent.com/strukturag/nextcloud-spreedme/master/screenshots/appstore/import.png"},{"url":"https://raw.githubusercontent.com/strukturag/nextcloud-spreedme/master/screenshots/appstore/users.png"}],"translations":{"en":{"name":"Spreed.ME","summary":"Audio-, video- and text chat for your Nextcloud","description":"Securely communicate with your friends and family using rich audio-, video- and text chat, and much more right from your Nextcloud – in your browser"}},"isFeatured":false,"authors":[{"name":"struktur AG","mail":"","homepage":""}],"ratingRecent":0.5,"ratingOverall":0.5,"ratingNumRecent":0,"ratingNumOverall":0,"certificate":"-----BEGIN CERTIFICATE-----\r\nMIIEAzCCAusCAhANMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD\r\nVQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI\r\nMTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB\r\ndXRob3JpdHkwHhcNMTYwOTI2MTYxNzMzWhcNMjcwMTAyMTYxNzMzWjATMREwDwYD\r\nVQQDEwhzcHJlZWRtZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKLx\r\n2dCPBLIgX948BnOdLij0YyI2+FKD6uZOvzxMaoi3rlxNf8MJgraNMzTBWEXtxT5b\r\n7ZISNp89WEXhaQ1dwwCocodd/xow4Ek63m5nUvTZXsm+YSbMgrFbxzsBhYU7KuIE\r\nT/jhKdzYgemzErwwN/gtwkLMfPo3jkgg6c8NPPohYv6k7V4VnsqtJ0JS0kX19FqM\r\nMiNz9XkcncBHy9x0BSxy4+YnwbFcgIx/MtYKlBL8NkPuuJaB/6C1O+IPYhdEdnpX\r\n+RaIue71nSStOYOqT4YDqHAIw7EmqgA1my09mmK+0Pn92GJVEAEN7JGBSQ+F32RI\r\ndB3ivGAOVtUtVvJlepWdbHxj1xqeP+LCjWzHMLQjm0TyH8VqU4Cg/wxwAEFnBATH\r\naOaWwrggzY2d9KBo1mp0k71NArLbBdlHykFU4bgiSDWrXXMz0fZzLQVwGI0Eqcxc\r\nouf6t0kvrK8oKjrnso+FjBoT7lHV/H6ny4ufxIEDAJ/FEBV/gMizt5fDZ+DvmMw4\r\nq+a088/lXoiI/vWPoGfOa77H5BQOt3y70Pmwv2uVYp46dtU8oat+ZvyW9iMmgP1h\r\nJSEHj1WGGGlp45d10l4OghwfTB0OSuPUYwWR+lZnV8sukGvQzC9iRV1DGl/rREMC\r\ncQ5ajRAtO5NPnThvN5/Zuh4n8JoDc0GK4jEZsIivAgMBAAEwDQYJKoZIhvcNAQEL\r\nBQADggEBAGHMRbPV0WTI9r1w6m2iJRrMbZtbBb+mQr8NtOoXQwvSXWT1lXMP2N8u\r\nLQ1a8U5UaUjeg7TnoUWTEOqU05HpwA8GZtdWZqPPQpe691kMNvfqF64g0le2kzOL\r\nhuMP9kpDGzSD8pEKf1ihxvEWNUBmwewrZTC3+b4gM+MJ3BBCfb5SCzMURLirfFST\r\naxCNzc7veb2M98hS73w5ZE6vO+C/wz0GTsxuK0AoLitApT5naQnjvxSvSsjFPEGD\r\nsUNUEU2Decyp0jxLVnrrpz6Y5UupfBR0V8yAv1t5Od/mCKLc5DxHsDWiKOpsob9U\r\nJN+bdzJil2NNftihD4Dm7Ha7OS3O8W0=\r\n-----END CERTIFICATE-----"},{"id":"nextant","categories":["files","tools"],"userDocs":"","adminDocs":"https://github.com/nextcloud/nextant/wiki","developerDocs":"","issueTracker":"https://github.com/nextcloud/nextant/issues","website":"https://github.com/nextcloud/nextant/wiki","created":"2016-09-14T14:34:35.977699Z","lastModified":"2016-11-22T16:02:57.758477Z","releases":[{"version":"0.6.6","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=9.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/nextcloud/nextant/releases/download/v0.6.6/nextant-0.6.6.tar.gz","created":"2016-11-16T15:11:14.344704Z","licenses":["agpl"],"lastModified":"2016-11-16T20:39:59.030384Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=9 <=11","signature":"aOZeEeThyZ0V/vXBcn6c+Z0vyCsZcN6nfSJ8oWEea4zXh4g705Si+MFZESqix3M2\nOPCnA/U8eASwdRTAEwQJrW5ECmu1THXSIsrzQzc9kFycvyOGzCgAWtuu0ayzZD2/\nU5aDWlzpLHC1Czg9QJ5UnfZR0AfChWQ402N1YzGqMShdJv6AHXFrVE+uYnIyxuYI\noPJQBUYbQwthVUjpYwFwSxw50YU17gmx5RZ0Y0OPz3i/EiuEUrxopXtfDVYAuCML\npDw37LOTRQ2JqxSU3teALh8LcrwJbTeOP0n4bTeV+vU3jvtiaEoRrwfVrK41F701\nQymGXy1/EFG0kxPGS2dRNPBAXYLZfeoWlROl3D5BWlbsCcXKU1S+22yn0TEdS7x1\nY44x8jRKnBddDE7qkn+QoQYHNNcxOREsFFLmIoyCUpdNOdDX2PvTFUYkIqdnXaJy\noAKv2GkvWPQ0aiiBtA1i4oXuzvHW/M2wOrK7v7DCpNfILrD/sjxpljxcX082nRCd\n9P3iPd2hQ6yOM9fG21LVN74b6wggI81BzFf/xJPd4ZqYLjfeG/yqd0zaiMOzMm1W\nse+kc/a4iB3BoCNX3E942pBBzew4ya8LkCXdCHUUsuelDf1va1ikTh/G7D84ll9/\n2avNqQnUh3hgOnxFCLI/5VrbqxfSTVdO6O/LTuAmwgw=","translations":{}},{"version":"0.6.5","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=9.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/nextcloud/nextant/releases/download/v0.6.5/nextant-0.6.5.tar.gz","created":"2016-11-09T16:58:06.856332Z","licenses":["agpl"],"lastModified":"2016-11-09T16:58:07.139404Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=9 <=11","signature":"DVOIbLEVggiLkNkuPW+pXqu8WYT15unUsMoqHSw81NiU6HF0Nuf3XiwnHJJDDqo5\nyX+QyHADO4ZiQpvXhGigvwD2eS1jVLatAztyxE0tEQv5eBU/7R0jQYxI8YCnC/jE\nZDa0qs+TI58EkDek0LBzueVQqrLubKgGU9P+E9H8gbWi1JHvl/2LdY7CplPqaJ+J\nMRokobPntzgx9m4DZC1RsCoXzSON7o2gp2cmunPJiXVHPhMUfIfolGEbNGJ1/xdp\nra7Y7XkPnDx4po98a38UpUh1x/2m5goOV54em35bWbh4ShNDykiE5ttz6tOywlYN\ngxceSsStTKyqscVaOV2Xu6Ive0pY9CInvrSfRnRktIWBYDyWdbK9sJuqs/s69kqn\nKQ/SjzE2smw0zwOUMnSaz0Jzr1vdPFgNu2xDYAVQO5G03V+wQ5AhxuhBz5Xp5Fll\nLaOhymZLCC7lq0DUqkeTeQ2QCfiG23hvG2VUPsIqW7xFe2YzKHZVXi9JuH//Gwym\nDmJmcyZDMvNwNiObx3ZRKsZNH2XwbldFZ9nTpb9AafyoSR/qbwd473NewaDLRTiY\nLrOP5Wx1xx6DOkRmDF2l2iT1bSJ6eoAoWQU2I0aYRH9NAr2Ugd4f2Um4o61EJaL+\nRHT9cERRySEloU/NdgmZEOi+CG0rEu+9LC5G/jGlHE8=","translations":{}},{"version":"0.6.4","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=9.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/nextcloud/nextant/releases/download/v0.6.4/nextant-0.6.4.tar.gz","created":"2016-11-05T18:17:47.622023Z","licenses":["agpl"],"lastModified":"2016-11-05T18:17:47.678445Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=9 <=11","signature":"RdkvnhNjw+kAnT6Or3+N9FzAv9DjJ9BAlmgANMwZZcaqo1gZRFewsGD2Rx1KEb9X\numSC28tl2B5/3r/+dprVJmOnYJny/7+kDmI/d+vigKfnaQJOUZ0ya5+f72pFgow7\nth8fw9rX/3+zIBs2IeEN66cis8ioFq97BJDsnDMBDr7wl7CnFJjYe6eviWuiFTnC\n4sBXlYjHoaIRRu561nwAooV+WXmWsparYPVhj2cXdyP/CnWo5HSF5jA51WCsz7Up\n7a0URXdh85xmxEbZtnjUymTW2BIegdxj9Erbfiuy/m3ApgnP+DiEQRcM13J7pXqg\n4cgFOBSzKDZgim599WBW2uNO1ztnDYOzz47GXDfJhcdvKiZZoQpUF9W4LMRtheMz\nxD9YArO3j3d+VOInSYU2Rpvhugwo1LExhwnRdT4+cOZfEeq0VojiM7yBZLDdEjIb\nGdYUJtNWSU0F6cPab2Au8FgZymLrHL9EpGvxuA1oAwtRxHAgMElJG2O6Jp89gGl9\nh/AptypeTvzNEc9/Kg24ueBKqmSUd5a45pZ3gM2pNATJhUK7fzLb/K6cq/kEzZtj\nOPra1ZfP0/20u8VP32Rgw1cFmIjqt8DFzUmOMpMfyoJkdldtTwQtGk+yIvtN1zp6\nT2zDknAKA2N/rZ/0SJl8KxVVNLhQWtYBJ+rFAdapuxI=","translations":{}},{"version":"0.6.3","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=9.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/nextcloud/nextant/releases/download/v0.6.3/nextant-0.6.3.tar.gz","created":"2016-11-03T21:51:27.654342Z","licenses":["agpl"],"lastModified":"2016-11-04T18:25:35.697130Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=9 <=11","signature":"Hf5GB4xd+rVXzWvVpGbbF5tqmnI+DGHlNHdLYPImFLS/Z4K5wKeNp80E5sd/RkAi\nhyuEsdWHlGDVMT6s7oeCmH/ClyWqLNSz9VH4KYqyFgD4+usHZp9PrEeEKbvPDPKv\nD3eB7Ats34cWcpf4E1oR5wsPicgmdgIgb2uMXzc/1G9xUBRWzocwCstzjEEAB/VJ\nvJuHvhDTGG294P4gOb82MxKCQ8LZ4i1QXzOf/mLETOubiUbZtJgTReYvpdAo2Wct\nbdfDFw13LYZkCf71r9jIQ3PSPlIpD+0BwAlE1fi0Br9dP2JjIfiKN6CGVaki6O0v\nKR42jtcE9xXiAop0Ym1FYMWJvQUy5PFLMwYDfEd6CvfEFJl+fi+RjXNDNZcTyw00\nHa48sACoGzxwts2JknWMU57mwvi0Z4uwpE0cFp/PRzBsXmSlCzWHjJhu7+2qambE\nAabdP9nH2NvqJHUJyPsxtDSrSWCBY4CoL3wYu36UrIA4NepyudMPNe9fhKTEU0bg\n8DLclw6hYdj5p9Zj3OUuwOZLz6r85KwnooTsUjOYkBXvdUuHWkgONqqZlPMApS4i\nChRQ7ysHAinPyyzsvr0PR9g6J52CSCO/7qwSJy6yqSzuSWrbZUa4FVTcKIwWJJPu\nJ2XzB4rWVG1wWjzMM6MIvFbO2HY9upzh651OdOwsYvk=","translations":{}},{"version":"0.6.2","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=9.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/nextcloud/nextant/releases/download/v0.6.2/nextant-0.6.2.tar.gz","created":"2016-11-01T11:24:58.054892Z","licenses":["agpl"],"lastModified":"2016-11-01T11:24:58.151609Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=9 <=11","signature":"h8KgwMh2RGGIp7q/II23VSfE5Ibkha7p/C1kvIfG6QIIc2Zu/Mm3Oekynnysh5ZJ\nZuMTaeWbejbBAmlnxW+AwBWa/s2PoMhv7foVvdtg76l9/qr+9jGvUM7D1LbbBvAy\n/XW6JSrhhBZHOohdU7kwV5Xnwsn/NC/zUe0G4eZ+9fc9uSntkob9wnWvUs2daAeD\nY6Mi7Xt/XAIX65OIVTKfC6ah1SlbXgtJ2i2j4G32I9jRhmbkpt/UJEangn90fHnY\nQXNJ85OyV0aNMafNHoMSL3uLscdvjp0Hy8w4iBeavsRlCs0GiUoG1+YdwTwmC9EM\n4CjbMfRJ0DYK7u697TOmS8MQzk8O7f5THtjeokZlrom2YnV9t6gLvjnxl/+gXPdJ\nmgLixnA8P6tPsff9pquHKQZsvxjv6vVa2DVJc8VpcqJRih7yj/3V7rxesEP7MUnP\nznIOcwzTsKXHuAnvujpCwyQj3QtpQK2XJPQ5WkKgwbTdvriVJfnzPironhcHo1vC\nbuUDOdhL59aySChw2cZGD9lCWaxYR7I1BDDzWKNl9Qg0AZ2auyGUGTv8P2vi5qUB\n0CmnkzkZurR5ju6Nb9/2lcZvda7QJdIdMtm2Wnn+Ak/Z3Y4IehB5PPDP5/MMAhQY\nXE8Jqe0mGtiU/O2346L5IThkS58JlRmep4kYv+II9zE=","translations":{}},{"version":"0.6.1","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=9.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/nextcloud/nextant/releases/download/v0.6.1/nextant-0.6.1.tar.gz","created":"2016-10-27T21:16:47.066097Z","licenses":["agpl"],"lastModified":"2016-10-27T21:16:47.125641Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=9 <=11","signature":"axSX4Kz2P4PbKU676DibjPZsxk8hCIG3lLOmeMXoiBhp3ka4wJ8u5tNwTzgY8/MV\n3mjXe5DNttD66SqmiRNSPKbotYHVFFW3wFK+Dgh/++n/KTomGYUeIwt88Z9ecbuG\nNT6U46jFrfZBYzRHWzbgiJ4c7MCoid9cfmoB7HDuQSyw+E0S2vbLL8+jzMcIzF+5\nTh/sJEterXCyrWSciw/9x98F+4svNbskdEIvrOox3+K6UIhpsojqJR2+bQhG3rsM\nPerOb6J+bzHdLV1ZL/4nWEz1F30T7B08QxY/4pHD68JFQcdtzmHMIhXfCoRvWhN2\nVRnizx3IXBALY4F49Ql6bjsnr6BCp+LroM5RSQ3eupDcqejDJLbjPz8xfOMOwlx7\nz84Xd0MUyqaEkxg1ihsWLbPlYACUZ2aoDkSQUIbfZTTiov7eqTM8UBc/UqVID/LU\nyEW4gjFZzQy6kX76XRAGq1vebKFjCU63asCnVyJhF/YQVTu1hPGbFslkRKnYuh8W\ne4MeaNfbdjcSEX+7oTcPJz6V09pOPvukXL0M1m7lS9PhTisI6oGj8c33GPYp/DSK\n6SGk+ukbt1mwFuFKdTvAMxo1lk96D+pKUv4MX/ralaaoIAmwPTGsSQ04RyL454ae\nU6q8PApwrVyPHYwMBPtXGoQMyb2ZV9rylazYbKCQ8I0=","translations":{}},{"version":"0.6.0","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=9.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/nextcloud/nextant/releases/download/v0.6.0/nextant-0.6.0.tar.gz","created":"2016-10-26T01:46:48.419025Z","licenses":["agpl"],"lastModified":"2016-10-26T01:46:48.521063Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=9 <=11","signature":"W2TmbX/NbbfPPjIJLalO0kCDhhQF1pEohH/CxO9bY+yR+a5NKiTbpAhG2McqpTSj\nmgC4J8/thmwGlWGC+VW+KlpXzzjc7wCgMGMKViOpGD3pIy8p8U5MXTqVgjjrEb9g\nKgr9uErXzxJ5oDfkx8Uh1bUeBJTsAAivGJqMlhBYFGxw8BSB09sfOZytNxo4wbwZ\nNAcYP1qvkUQ8CVR0nInSTRfLvAp5+e/8xxyYZwJIWrdNgpoP0CxiwzKP/VSjBk/U\nsPU4R72UQnVZB0InRCeh/KNTwu1YiPkUKm+mNmt2hCfN7Fm6bY2rUMH7oa8KTkqn\nh52FhbaYRSAR1OPdC5RnHiQWlPh71gq+4Xqgp19Eawnl/QiVteVifSjNQZ+Ban8M\nRyw/PxHnzIWg/OAjx81Jx9mXjUIHSeUxMTJTKTp+lEqIAzjku0iHeU5UPGCfE+VM\nwmopZfnlfk2nLkUwjQXLCIcnZD1ME1m0h/H4Ad0Q/qXpBScUp47npkxg2b8zPIhk\n3aXUmNPLgwyXPWvAuaBK/WwpNefUnqWFns8t2Alpzg/EpC2PrZqciCNDcRFEycoa\nX+JsFyD7eYA7Dc9iIf09gXZX+tZ+Jinb+iPDwYrl1u/7IIBoBlUGCgo+cF7/dL9S\nc3vYeWw6MCH8Sv+ckgS2g726BfdN5EjB/8cb071b4lE=","translations":{}},{"version":"0.5.1","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=9.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/daita/nextant/releases/download/0.5.1/nextant-0.5.1.tar.gz","created":"2016-10-17T15:23:04.515057Z","licenses":["agpl"],"lastModified":"2016-10-17T15:23:04.576640Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=9 <=11","signature":"Pp3rC/9RmAYURneGpGit4HZ2t1qH9A9nwsUnGDgRuJ0akIii7CtJC+n8l1b9k73/\nhxMYnp2JOBu2HWKgFp9a3yeo1xphI5hOUdVQ1UZAWxIQyDI1FZVYDr81l7GCdkqm\n2lMxgviFADSYURCgEnAsj9Nt0NZ6LDcJZiJg3iPAjkDI0U+KnEBjtx/XRFqGUnfp\nCUZ/XLHh/hvoEitSHepTBDCMKkTNjbDEwBYfA2rAoz4zbMR5zKLU+1r1DIUgWSRe\nbk2i8TaTDVL4cbb6MhkGKwkujb+Atikvkpi45o7+fyQMs84c6fjjh/TZJaC+dMyG\n1GCikMPwwtEPjXtnLzynERAxJOd5mP4Ee4sD8ZrnzNUehpyFR88pwWU6r+dmiebb\nnsYlGkhIu2aIO2O4HZ4sUTsO5sfjZ9me7jsafhgJl6iG4IBeHa/L1MsSbhsh6mvH\nYsz4Xsluwr0QcFLmSDDQQYynyogKfJZG2xQsadM0GETWgXE44dVeRvMUsILfB4uZ\nmfKgd23SgaOhYC8m4bg5Hxnkl+xHJnyGZ6jhqg7bhuKwsoLymc18Vmjmb7a45HGV\nXbL5CTmw9yaPOBS3v7v91TnlB+0lhlzbKzZ0xIhY55qsTC76uScbTLwndPqNGaF7\n2koxRbJ3jcmf/Z2MLymdRi2BTZbZkPkxgVrSf9plaR0=","translations":{}},{"version":"0.5.0","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=9.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/daita/nextant/releases/download/0.5.0/nextant-0.5.0.tar.gz","created":"2016-10-11T11:47:46.191539Z","licenses":["agpl"],"lastModified":"2016-10-11T11:55:40.393000Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=9 <=11","signature":"qzQJSLK8nkrQedwwxUdhxL8bq3aXyppAlWJo+n3GqSvqA8uNTzl3Wsci0LsnBV0E\nDvDNW8K0enhl563F/bywQrDhgKl8jTX/CA5KCxqO9P+tvE80zAfMqiRnQayVcWuY\nSWX6RqfI/kqiWyN1SsFp2EDlas6eb+xfIoiJamlfsN0qzHYOFt5W77wmw2Bn9dB5\n9nwHHyC0z60Pf2pPduc/KuZ/971WrDFaIapL7Gm+z9XoaKSwUT575VtS+RNJkOKy\niBrwnHdc8x/62HPFOXsUYjCt2aEmLzPCQN8Ke5pd3596hm5wbAVzTHuxf2H35tb3\nljfGqAZ5AJX2Xd13d4aHXFdSEILwv6IFq2fx0hO3vkvFEoKF5oQ2t3hi++Mw/h8R\n15OKZCOC1bFH3gTwdshmnHSTSr3LxDeTF60aH16wpXcehQuJHagpb/aG8mPD1w+v\n759/mekqa4LYlqT9TLWTqX3UPeQXYIwcPijG84IvW1BDK1M4Mj2Vqsld4jXwG6CP\nORIL8xoQbA52dQI1Y19JXcU9zxIb6GaHYgpV0wejatsZRfhPv2Yd0CBShF4HY7aw\nnfcm88pqzOKNvjnLZjTFQwuJ0AUUSOsWhgZzYt8tATJ2KDZ+lxz+WAMGXJAC/ciY\ntrrglY7YxwJNLbYp+arE0sowZx+IOVaSZBvmUGHiEBY=","translations":{}},{"version":"0.4.2","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=9.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/daita/nextant/releases/download/0.4.2/nextant-0.4.2.tar.gz","created":"2016-10-06T10:31:12.482416Z","licenses":["agpl"],"lastModified":"2016-10-06T10:31:12.551117Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=9 <=11","signature":"QHJhYcBMi5HyPofshZ7EdcXsOfRrl7o48Y3BBBq8CkmYtFDbekghdJqjFbwwZa5u\n8FtgMVwssql/RSDNP6M2Zc/MpQ3K9gDm+DyxE5KRmtMEpzHB+oD+1DxP7kAoyW8/\nnBr5iiQSdLCelddMcTwbdxskLFUXIs3cFuLGhMvr8pdQOAgfxte5lolrj4/8EsJ0\n0yUImgIYG4NlgmvCygGApdpVaOcK7XVtv4oH+x43JmX9YZ3Ce0DQPYPUbFTCf4ZS\nS075j1vcnPx2cRFGxc+YpKzYVVYoy7ZdB75Hq+kaei/atxrTyV5+gcCrVPnH1RkX\n6G8rgu5l8FoGJYopi8/9dd8LsGLFx53CHMdEVob3tFR0WeK4TJAGJa403zE6S3hM\nxr86WCedmjuti0uOhSQr5AEVAoXZ/JUWQMMsPAUMuKEYVjKfmve6TlcNMC2oM5XT\nXcOf4OP3pcQq4ViN53o4Pj6NGSci6IzD6xLeAxKZUoTX37ArVKH6RHS5Najc193H\nRhYRnfE7D5YOr1u10HaZCFCVJif2MgMP0/uH2759NoRjXFowrh7Z6dW7JQG5lbHN\ne0jjJH1Y8m8H1peGGcmM0YxFiOVZ0ER7P+AxT4Cbau/cVhaS8vnPF2a2a6YFRiFS\nVH4aqazvvXrtifDr3lfvlyPCrP/570nwvOJgZGk+K/Y=","translations":{}},{"version":"0.10.0-alpha","phpExtensions":[],"databases":[],"shellCommands":[],"phpVersionSpec":"*","platformVersionSpec":">=9.0.0 <12.0.0","minIntSize":32,"download":"https://github.com/nextcloud/nextant/releases/download/0.10.0/nextant-master-0.10.0.tar.gz","created":"2016-11-22T16:02:57.740378Z","licenses":["agpl"],"lastModified":"2016-11-22T16:02:57.900805Z","isNightly":false,"rawPhpVersionSpec":"*","rawPlatformVersionSpec":">=9 <=11","signature":"kDO3xbPpoUdl2qo362tXfJIqKeiKE12M8FkMXbdKiRNzuQyvDOehQq3dErALZDOr\nLG47Tpez/Kn9Fsx1y4dQDx0w9SD3pyoqvjj1O6KkTYN6srpitQcj9EzEItCY+MZd\ngRSc7Px9MzxpFpgwtuThGVlSt5kLMd0vjWFLVcv7k07rZfWEsTwXd24INIDtlr1A\nC7hyLB777nEOGa7oAzx8nr+FJcIbmu9opVZk8AL40FOFDNBgfAy2AS9hGZreUmRV\ndV9Zh7UAR+RsEeO51xcU/CKqC8Jb0jL1gkYyUaQy16oe1HF9bRs1PWuNL5mYwsmy\nZNn0ay/a7mb7hxJbza1F3lbgBtodvbgUm7+So/QvaR29CSAqqoUXeQy6FfSBVWhZ\nYlTxdQfKcBcUPFO14BBk/O5p5iQaG8JCLJ/EZGDPDIVDMn7crGQ4oLZJv80eqTeB\n7ueDmWaD3gQ9j2XKsMk1uLSyY4unt9AaTofBylsKD1SjLhyxofGZym4jc2+M1GnQ\nyLcoEMSexnKuextu4nzrA0y5k3z9tvO07R29lwT1GFp6oGAakMLob/6XrGsm3YQY\nRQXgfXKFw8XmXBumqG8ayEIyvQ/O8nO6r4R1H8a7ooj6oWa3PhPsen+gLE0SpJPZ\nz3e2TLliwC4VtZp69H7u3px5Qn1Fc6RMDTh571edCr8=","translations":{}}],"screenshots":[{"url":"https://raw.githubusercontent.com/nextcloud/nextant/master/screenshots/displayResult.jpg"},{"url":"https://raw.githubusercontent.com/nextcloud/nextant/master/screenshots/admin.jpg"}],"translations":{"en":{"name":"Nextant","summary":"Navigate through your cloud using Solr","description":"\n\t Navigate through your cloud using Solr\n\n\n**Nextant** performs fast and concise _Full-Text Search_ within:\n\n- your own files,\n- shared files,\n- external storage,\n- bookmarks\n\n\n### Recognized file format:\n- plain text,\n- rtf,\n- pdf,\n- html,\n- openoffice,\n- microsoft office,\n- image JPEG and TIFF (will requiert Tesseract installed)\n- pdf with no text layer (will also requiert Tesseract) _[work in progress]_\n\n\n\n## Installation\n\n- [You first need to install a Solr servlet](https://github.com/nextcloud/nextant/wiki)\n- Download the .zip from the appstore, unzip and place this app in **nextcloud/apps/** (or clone the github and build the app yourself)\n- Enable the app in the app list,\n- Edit the settings in the administration page.\n- Extract the current files from your cloud using the **./occ nextant:index** commands\n- Have a look to this [explanation on how Nextant works](https://github.com/nextcloud/nextant/wiki/Extracting,-Live-Update)\n- _(Optional)_ [Installing Tesseract](https://github.com/tesseract-ocr/tesseract/wiki) ([Optical Character Recognition](https://en.wikipedia.org/wiki/Optical_character_recognition) (OCR) Engine) will allow Nextant to extract text from images and pdfs without text layer.\n\n\n\t"}},"isFeatured":false,"authors":[{"name":"Maxence Lange","mail":"","homepage":""}],"ratingRecent":0.5,"ratingOverall":0.5,"ratingNumRecent":0,"ratingNumOverall":0,"certificate":"-----BEGIN CERTIFICATE-----\nMIIEAjCCAuoCAhAFMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD\nVQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI\nMTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB\ndXRob3JpdHkwHhcNMTYwOTE0MTI1NDQwWhcNMjYxMjIxMTI1NDQwWjASMRAwDgYD\nVQQDDAduZXh0YW50MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsbnQ\n+9acjKHfcrUj4yqBpD++GmQ5z2Sp8C8uOz4ZbLyM9bUXEYHo4a4u3CdC49kGUkb3\np5MkEAEzslTWDi1eh5MZgPWpbPgItsDsXY1o55O3jtxNkzSG5/yYcPQcuKtIOm9S\n7DY0K+UQt3nK+RrXEZfARMNrzFbEzpE3b7w901Yl5n+m/B8rhW4pqg8uSfx3u04J\nwduV1fHwoHUB0Ox5HyWib4Pq1XppNh7xdc2Fg93JxshwuCPJyOOzrFTnxC7s1yzQ\nUvaqkjPW5QeQRunQjZ2XtpYH8f8v01W18bNEiHwqtFwuDEyCVx1rvEMgUDVXdPkP\ngZrlB5TzGmz0U3HzYvf6205WuzfHxz7kPj502wP51PoZBKpniggKzmuXkx6BpsZC\nZX45VpDHdiATLwRj1t2bMs3C01nzpIWO5ZwFtkepH3Y+mvwX5lDh/XDsqJC2Yo8o\nWMmniWNW7dspufYOsBUqqYGP7rkailgVT4oYk6D1j6oFZ5SSpfPF5lsyYedDSM6y\nbIGVkSF+sjLK6R9BenBijKceAKsS//WwRYCBPC+JHlsYpXKW12bL+C47Kj2/N6d4\nrYryzV6ofVSF6pwIq0oEjoyfBfNpYavf3xrRkSSmIIlPSnMY7DT1xkGD5retxSm6\n+WIfkWKRZpv2S6PhMHGLspYc4H5Dj8c48rG5Co8CAwEAATANBgkqhkiG9w0BAQsF\nAAOCAQEAOZUwyPaUi+1BOUgQJMWqYRoTVZUyBshTXSC7jSwa97b7qADV9ooA6TYF\nzgsPcE41k7jRkUbnjcY45RGtL3vqsgZbx5TjPa5fGMxlqJ6eYBOY61Q6VIHEVm3u\nxnPEO9dsMoDBijvo5D7KtE+Ccs907Rq70kCsbrdgPHkyb5tDSnCKogN1LiQrg1EP\nmy7Z1C7jG9/h57vx0+QBMDCYnTmqLsvMKqo27uHskzAiB7VXLEdSZ2FtMGHkLUQO\n0bfhnvTZ2VhMmK83t7ovo71An4ycmsolGD/MA0vNI78VrVISrdI8rRh2WntUnCBU\nEJL3BaQAQaASSsvFrcozYxrQG4VzEg==\n-----END CERTIFICATE-----"}]
-EOD;
-
- public static array $expectedResponse = [
- 'data'
- => [
- 0
- => [
- 'id' => 'direct_menu',
- 'categories'
- => [
- 0 => 'customization',
- ],
- 'userDocs' => '',
- 'adminDocs' => '',
- 'developerDocs' => '',
- 'issueTracker' => 'https://github.com/juliushaertl/direct_menu/issues',
- 'website' => '',
- 'created' => '2016-10-01T09:16:06.030994Z',
- 'lastModified' => '2016-10-06T14:01:05.584192Z',
- 'releases'
- => [
- 0
- => [
- 'version' => '0.9.2',
- 'phpExtensions'
- => [
- ],
- 'databases'
- => [
- ],
- 'shellCommands'
- => [
- ],
- 'phpVersionSpec' => '*',
- 'platformVersionSpec' => '>=9.0.0 <12.0.0',
- 'minIntSize' => 32,
- 'download' => 'https://github.com/juliushaertl/direct_menu/releases/download/v0.9.2/direct_menu.tar.gz',
- 'created' => '2016-10-06T14:01:05.578297Z',
- 'licenses'
- => [
- 0 => 'agpl',
- ],
- 'lastModified' => '2016-10-06T14:01:05.643813Z',
- 'isNightly' => false,
- 'rawPhpVersionSpec' => '*',
- 'rawPlatformVersionSpec' => '>=9 <=11',
- 'signature' => 'ERBS9G5bZ3vwCizz2Ht5DehsVJmb63bzF3aYcH7xjbDVMPagOFWdUAiLDwTeZR1n
-i4gdZ73J/IjHQQJoOPwtCjgbZgLPFqL5x13CLUO9mb/33dZe/+gqEDc/3AuJ4TlA
-XUdLxHRb1bwIlJOwuSr/E24452VG20WUhLXBoM0Zm7WcMxvJWo2zAWnuqnLX3dy9
-cPB4PX+6JU2lUMINj8OYQmM1QnqvjG8YV0cYHbBbnSicOGwXEnni7mojsC8T0cn7
-YEJ2O2iO9hh3fvFEXUzDcL7tDQ5bZqm63Oa991bsAJxFo/RbzeJRh//DcOrd8Ufn
-u2SqRhwybS8j4YvfjAL9RPdRfPLwf6X2gx/Y6QFrKHH0QMI/9J/ZFyoUQcqKbsHV
-85O+yuWoqVmza71tkp4n9PuMdprCinaVvHbHbNGUf2SIh9BWuEQuVvvnvB+ZW8XY
-+Cl+unzk3WgOgT0iY3uEmsQcrLIo4DSKhcNgD1NS13fR/JTSavvmOqBarUMFZfVC
-bkR1DTBCyDjdpNBidpa3/26675dz5IT5Zedp4BBBREQzX08cIhJx5mgqDdX3CU09
-uWtzoaLi71/1BWTFAN+Y9VyfZ8/Z3Pg3vKedRJ565mztIj0geL3riEsC5YnPS0+C
-+a3B9sDiiOa101EORzX3lrPqL7reEPdCxrIwN+hKFBQ=',
- 'translations'
- => [
- ],
- ],
- ],
- 'screenshots'
- => [
- 0
- => [
- 'url' => 'https://bitgrid.net/~jus/direct_menu_nc.png',
- ],
- ],
- 'translations'
- => [
- 'en'
- => [
- 'name' => 'Direct Menu',
- 'summary' => 'Provide easy access to all apps in the header.',
- 'description' => 'Provide easy access to all apps in the header.',
- ],
- ],
- 'isFeatured' => false,
- 'authors'
- => [
- 0
- => [
- 'name' => 'Julius Härtl',
- 'mail' => '',
- 'homepage' => '',
- ],
- ],
- 'ratingRecent' => 0.5,
- 'ratingOverall' => 0.5,
- 'ratingNumRecent' => 0,
- 'ratingNumOverall' => 0,
- 'certificate' => '-----BEGIN CERTIFICATE-----
-MIIEBjCCAu4CAhADMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
-VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
-MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
-dXRob3JpdHkwHhcNMTYwOTE0MTI1MDU0WhcNMjYxMjIxMTI1MDU0WjAWMRQwEgYD
-VQQDDAtkaXJlY3RfbWVudTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
-AMkzWsAkKP/40ktvJMpnr0IJNVoPOR0hvh24igcDskL1WKiD2eiRUenj5LE0Nvn+
-siGmWsAqRVpdiz+Y8ghQqQMzKi43IrRN0AxlCrHWrSqBZT3wIAUcFz4RzEoFxc1N
-UZzWma6ljukGnvt4V1ZyT+H/cjqxUkBhh/y9SS0jUen1a1grND6Rw54X46V2dlCu
-FH+pLsfPJJGw+QLeTGHn7dqdv18cYMAlWDCzPixVnNiCXHZcUtKMmstU2xU4R2e6
-zimp2rgkE4TNHrafpjH8xGdNi2FG5Dmokob/L5Q2r8jyNaW7UsFfrvLTRj371b3/
-2FhhxoGUvDzaG2An02Exwm52LJfdTVMHAMPZub5poHfy5vAEdZGPQ/m02l8ZK/Y2
-7yT807GlfPMXfdfjCxR6wNtmv7rvBDdrUZmIRNJfpFSdvlH/+MOTWnabyfQv2K4Q
-BIwltX6Elh0lh4ntvt1ZVtvFv+PL1Dc7QLV+w19+/LJA0mnsh7GIFYKFlbA65gA0
-c/w+uqDy0+5MxkR9WGPpd79KRA1tKWTis4Ny1lApK5y3zIsVGa3DfBHXcwqkWHbV
-wIpyuyyDsFtC1b9LTFONX7iU9cbNk5C5GTM331MdA2kLcD/D5k42GNTBSca7MkPx
-Fx/ETSn0Ct167el30symf2AxvXjw+mBYPN71rVTMDwe9AgMBAAEwDQYJKoZIhvcN
-AQELBQADggEBAC0fJKnbEhXA8M283jA9GxABxLyTBcQyVVNnz2L/bYYNi81Y9iZv
-+U0S3qaIfoqNcV9FTKAutbsKvWyolnI7MRRK6feNuFfoP2jKubM1CnawpyT/RF2Q
-e/zxnB1EmeI2X5D2xceJDLB7Fy5W0EGrLixRIdFaSUommWFUm9E2hSIaNlziSBdc
-1J/mOQeNYO5zg5ouEt1rzQW4Mhh1I2uNQmGe4ip+Jl/2LAv3FZuu4NrSEcoXH3ro
-G2dF9Gtu4GiQ5fuaJknaxlgXHovfqeZwZJX9o4M+Ug81AqiY7XjdiaCPdh0Tthcx
-2OmWZo7UBREWenjKyFZZ/iKoqH5sdenBtpo=
------END CERTIFICATE-----',
- ],
- 1
- => [
- 'id' => 'apporder',
- 'categories'
- => [
- 0 => 'customization',
- ],
- 'userDocs' => '',
- 'adminDocs' => '',
- 'developerDocs' => '',
- 'issueTracker' => 'https://github.com/juliushaertl/apporder/issues',
- 'website' => '',
- 'created' => '2016-10-01T09:16:47.111889Z',
- 'lastModified' => '2016-10-12T19:50:16.038821Z',
- 'releases'
- => [
- 0
- => [
- 'version' => '0.3.3',
- 'phpExtensions'
- => [
- ],
- 'databases'
- => [
- ],
- 'shellCommands'
- => [
- ],
- 'phpVersionSpec' => '*',
- 'platformVersionSpec' => '>=9.0.0 <12.0.0',
- 'minIntSize' => 32,
- 'download' => 'https://github.com/juliushaertl/apporder/releases/download/0.3.3/apporder.tar.gz',
- 'created' => '2016-10-12T19:14:10.802359Z',
- 'licenses'
- => [
- 0 => 'agpl',
- ],
- 'lastModified' => '2016-10-12T19:50:16.104357Z',
- 'isNightly' => false,
- 'rawPhpVersionSpec' => '*',
- 'rawPlatformVersionSpec' => '>=9 <=11',
- 'signature' => 'nhlT9lhrmBxIsqh/e3RLm2NDw/U8ZvvoMyYQTLMM3H19DQmVcPYPYC9QWVTsowUzXblVaOXVGylbpKma9yOlOAqJtF3qyXecLl4+tA/Awb6BBhKPgHFdcLDAL5yy1K7/uei3SgEojMlJoI9vEK5I1C5YTh43yNH+//Im6MAuNYFUTlMXK426zdOe6ogpCI5GlYdXopqYANxcpT+WXWET6DDSM5Ev+MYwMcSAY4r8+tvARRU4ZAenRgjkBT6R5z6cD76emRax14tbV6vkjjwRcO+dQtM0tFPbd+5fsNInbauv50VzIMgjA6RnKTOI17gRsSdGlsV4vZn2aIxEPWauu6T/UohMvAE9HMn13vtbpPBSFwJAktj6yHASYGzupNQLprA0+OdyALoLZPpQAKNEXA42a4EVISBKu0EmduHJlUPeqhnIGkkGgVNWS8AWqzP2nFoPdXBgUWateiMcBTHxgEKDac5YmNc9lsXpzf1OxBaXHBhGYKuXPwIfyH3jTWb1OdwixJEuRe9dl63T9qOTRre8QWns/bMqKLibGfMtFhVB21ARJayBuX70eVvabG/2N7Y5t1zUlFygIKu51tvo3AVCRDdRrFWDvkAjxzIz5FIdALVZ+DReFYu/r4WF/w3V9rInFuEDSwb/OH4r8sQycs07tSlMyA74Y3FpjKTBxso=',
- 'translations'
- => [
- ],
- ],
- ],
- 'screenshots'
- => [
- 0
- => [
- 'url' => 'https://bitgrid.net/~jus/apporder-nc.gif',
- ],
- ],
- 'translations'
- => [
- 'en'
- => [
- 'name' => 'AppOrder',
- 'summary' => 'Sort apps in the menu with drag and drop',
- 'description' => '
-Enable sorting for icons inside the app menu. The order will be saved for each
-user individually. Administrators can define a custom default order.
-AppOrder works with the default owncloud menu as well as with the direct_menu
-app.
-
-## Set a default order for all new users
-
-Go to the Admin settings > Additional settings and drag the icons under App order.
-
-## Use first app as default app
-
-You can easily let Nextcloud redirect your user to the first app in their
-personal order by changing the following parameter in your config/config.php:
-
-\'defaultapp\' => \'apporder\',
-
-Users will now get redirected to the first app of the default order or to the
-first app of the user order.
- ',
- ],
- ],
- 'isFeatured' => false,
- 'authors'
- => [
- 0
- => [
- 'name' => 'Julius Härtl',
- 'mail' => 'jus@bitgrid.net',
- 'homepage' => '',
- ],
- ],
- 'ratingRecent' => 0.5,
- 'ratingOverall' => 0.5,
- 'ratingNumRecent' => 0,
- 'ratingNumOverall' => 0,
- 'certificate' => '-----BEGIN CERTIFICATE-----
-MIIEAzCCAusCAhAEMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
-VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
-MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
-dXRob3JpdHkwHhcNMTYwOTE0MTI1MjQ4WhcNMjYxMjIxMTI1MjQ4WjATMREwDwYD
-VQQDDAhhcHBvcmRlcjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKVK
-Kn5jivCu+eRfe5BECjDOzNaGHlpiegb49Hf4nh0W7DqcoLHip5c1O2BcEYdH6rkw
-20WclvjoQpgavG5aFXzXzur6eKTT5TpgY5oZTLoWjbx4e+fKdhyDPTpqNZzs1pxz
-sZLDL/ElpbSErE0s+QK/pzP11WNPylAkI9AKSyDMO3Mbllg8I8Bt+bT7LJKYOO/T
-Lhv9m0anLZ4HrdfimhVIoMiu3RpyRigk8titXZA94+F8Fbf7ZQ9f14Y/v3+rfJFQ
-ii9cDoox5uUrjplH2LrMr5BodfCfydLu4uVpPWYkdccgvcZ1sugjvNXyCQgdzQDK
-pOpiwVkkiQFaQJYbGSDblFWPl/cLtA/P/qS7s8tWyTQuc1rYlEpCHG/fG8ZFkSVK
-9eCMGxK908VB4IU2DHZHOHi7JvtOz8X/Ak6pIIFdARoW3rfKlrz6DD4T9jEgYq0n
-Re7YwCKEIU3liZJ+qG6LCa+rMlp/7sCzAmqBhaaaJyX4nnZCa2Q2cNZpItEAdwVc
-qxLYL1FiNFMSeeYhzJJoq5iMC3vp2LScUJJNoXZj9zv+uqTNGHr+bimchR2rHUBo
-PzDLFJmat03KdWcMYxcK5mxJNGHpgyqM7gySlbppY/cgAospE8/ygU2FlFWIC9N0
-eDaY+T8QA1msnzsfMhYuOI8CRYigan1agGOMDgGxAgMBAAEwDQYJKoZIhvcNAQEL
-BQADggEBAGsECd+meXHg1rr8Wb6qrkDz/uxkY1J+pa5WxnkVcB6QrF3+HDtLMvYm
-TTS02ffLLyATNTOALZFSy4fh4At4SrNzl8dUaapgqk1T8f+y1FhfpZrEBsarrq+2
-CSKtBro2jcnxzI3BvHdQcx4RAGo8sUzaqKBmsy+JmAqpCSk8f1zHR94x4Akp7n44
-8Ha7u1GcHMPzSeScRMGJX/x06B45cLVGHH5GF2Bu/8JaCSEAsgETCMkc/XFMYrRd
-Tu+WGOL2Ee5U4k4XFdzeSLODWby08iU+Gx3bXTR6WIvXCYeIVsCPKK/luvfGkiSR
-CpW1GUIA1cyulT4uyHf9g6BMdYVOsFQ=
------END CERTIFICATE-----',
- ],
- 2
- => [
- 'id' => 'twofactor_totp',
- 'categories'
- => [
- 0 => 'tools',
- ],
- 'userDocs' => '',
- 'adminDocs' => '',
- 'developerDocs' => '',
- 'issueTracker' => '',
- 'website' => '',
- 'created' => '2016-10-08T14:13:54.356716Z',
- 'lastModified' => '2016-10-12T14:38:56.186269Z',
- 'releases'
- => [
- 0
- => [
- 'version' => '0.4.1',
- 'phpExtensions'
- => [
- ],
- 'databases'
- => [
- ],
- 'shellCommands'
- => [
- ],
- 'phpVersionSpec' => '>=5.4.0 <7.1.0',
- 'platformVersionSpec' => '>=10.0.0 <12.0.0',
- 'minIntSize' => 32,
- 'download' => 'https://github.com/ChristophWurst/twofactor_totp/releases/download/0.4.1/twofactor_totp.tar.gz',
- 'created' => '2016-10-12T14:38:56.174612Z',
- 'licenses'
- => [
- 0 => 'agpl',
- ],
- 'lastModified' => '2016-10-12T14:38:56.248223Z',
- 'isNightly' => false,
- 'rawPhpVersionSpec' => '>=5.4 <=7.0',
- 'rawPlatformVersionSpec' => '>=10 <=11',
- 'signature' => 'bnwWxmHEn8xkoWbtwhC1kIrJ0dQfAI3PUtU62k+Tru/BHt1G2aVxqO8bCdghojZ7
-zdFMlIJw4kekYFsVfLk8jzjUTZKVbNVKCdkHrVTQ0bUUryMAMLqGQ3PSRI5NX6D5
-FpkvwO1coYwU0XVWF8KAS0meX0ztSkT3Mv96LLrxr8F8SrB/MGmKIE4WTjt1fAIa
-ZLAVEUo/3sNFTGLYBtL3wjctrkZvJltP8abeRfls9FkRHu+rN7R3uLFzk42uZn3X
-Wpt5BBmlYm5ORbnJ2ApsxEkMNK+rOy8GIePaz5277ozTNrOnO04id1FXnS9mIsKD
-20nRzjekZH+nneQYoCTfnEFg2QXpW+a+zINbqCD5hivEU8utdpDAHFpNjIJdjXcS
-8MiCA/yvtwRnfqJ5Fy9BxJ6Gt05/GPUqT8DS7P1I1N+qxhsvFEdxhrm2yIOhif8o
-h7ro5ls+d3OQ8i3i4vdZm821Ytxdu/DQBHiVoOBarvFWwWAv2zd2VAvpTmk6J5yv
-3y+csRqpEJYd9fcVMPsTu7WBRRrpBsAqdAHJcZEwak2kz1kdOgSf8FIzP1z6Q71d
-Ml2RKcPeutMHHSLiGIN/h7fM5aSs49wGgGZmfz28fHVd7/U0HFSMYmkT/GMq5tMP
-Iyc+QZAN4qbX8G0k/QSTkK/L4lOT2hQiQqiSqmWItMk=',
- 'translations'
- => [
- ],
- ],
- ],
- 'screenshots'
- => [
- ],
- 'translations'
- => [
- 'en'
- => [
- 'name' => 'Two Factor TOTP Provider',
- 'summary' => 'A Two-Factor-Auth Provider for TOTP (e.g. Google Authenticator)',
- 'description' => 'A Two-Factor-Auth Provider for TOTP (e.g. Google Authenticator)',
- ],
- ],
- 'isFeatured' => true,
- 'authors'
- => [
- 0
- => [
- 'name' => 'Christoph Wurst',
- 'mail' => '',
- 'homepage' => '',
- ],
- ],
- 'ratingRecent' => 0.5,
- 'ratingOverall' => 0.5,
- 'ratingNumRecent' => 0,
- 'ratingNumOverall' => 0,
- 'certificate' => '-----BEGIN CERTIFICATE-----
-MIIECTCCAvECAhASMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
-VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
-MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
-dXRob3JpdHkwHhcNMTYxMDEyMDkzNDMxWhcNMjcwMTE4MDkzNDMxWjAZMRcwFQYD
-VQQDDA50d29mYWN0b3JfdG90cDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
-ggIBALC1K94104L/nOtmTygx7QNjUcnHs3yrn71mw4pMxTlonXOnMTpwxsfL1Hhu
-/5GMSgupTbQPlevSl6J86UMs455/sPShd6ifmAuhb8VFaAsjpizjs0RMaUg1sjmF
-uV18PD9FXLourx51V/c4MG5kpavlV+bLUrVMAjbsJY2+k30tCC/XkP5u8jUWmM/T
-5REChn7/obPgaeddhuJoILYhKEW3VNrR8Fm9SYiviB3FLhM7URDZ97IBnXYqbvbT
-Znvq+E74Zc7HgYwQwrjU/AqQAInhNpAR4ZM6CkWWWWaL96O1q3lCfKJNaxqC0Kg/
-kGn/pxYkl9062jtMUz60s9OPDyuisfyl68UyM68Ozyz4SMRLmDVbewOqQAwmAbtz
-8p9AQrX3Pr9tXhARR4pDSsQz1z+8ExEd6EKbhMyiTtHtZQ1Vm9qfoR52snpznb5N
-e4TcT2qHAkOWV9+a9ESXmQz2bNjgThxEl5edTVY9m4t248lK5aBTGq5ZKGULNHSQ
-GGpr/ftMFpII45tSvadexUvzcR/BHt3QwBAlPmA4rWtjmOMuJGDGk+mKw4pUgtT8
-KvUMPQpnrbXSjKctxb3V5Ppg0UGntlSG71aVdxY1raLvKSmYeoMxUTnNeS6UYAF6
-I3FiuPnrjVFsZa2gwZfG8NmUPVPdv1O/IvLbToXvyieo8MbZAgMBAAEwDQYJKoZI
-hvcNAQELBQADggEBAEb6ajdng0bnNRuqL/GbmDC2hyy3exqPoZB/P5u0nZZzDZ18
-LFgiWr8DOYvS+9i6kdwWscMwNJsLEUQ2rdrAi+fGr6dlazn3sCCXrskLURKn5qCU
-fIFZbr2bGjSg93JGnvNorfsdJkwpFW2Z9gOwMwa9tAzSkR9CsSdOeYrmdtBdodAR
-dIu2MkhxAZk9FZfnFkjTaAXcBHafJce7H/IEjHDEoIkFp5KnAQLHsJb4n8JeXmi9
-VMgQ6yUWNuzOQMZpMIV7RMOUZHvxiX/ZWUFzXNYX0GYub6p4O2uh3LJE+xXyDf77
-RBO7PLY3m4TXCeKesxZlkoGke+lnq7B8tkADdPI=
------END CERTIFICATE-----',
- ],
- 3
- => [
- 'id' => 'contacts',
- 'categories'
- => [
- 0 => 'office',
- 1 => 'organization',
- 2 => 'social',
- ],
- 'userDocs' => 'https://docs.nextcloud.com/server/11/user_manual/pim/contacts.html',
- 'adminDocs' => 'https://docs.nextcloud.com/server/11/admin_manual/configuration_server/occ_command.html?highlight=occ%20commands#dav-label',
- 'developerDocs' => 'https://github.com/nextcloud/contacts#building-the-app',
- 'issueTracker' => 'https://github.com/nextcloud/contacts/issues',
- 'website' => 'https://github.com/nextcloud/contacts#readme',
- 'created' => '2016-10-30T14:00:58.922766Z',
- 'lastModified' => '2016-11-22T22:08:01.904319Z',
- 'releases'
- => [
- 0
- => [
- 'version' => '1.5.0',
- 'phpExtensions'
- => [
- ],
- 'databases'
- => [
- ],
- 'shellCommands'
- => [
- ],
- 'phpVersionSpec' => '*',
- 'platformVersionSpec' => '>=9.0.0 <12.0.0',
- 'minIntSize' => 32,
- 'download' => 'https://github.com/nextcloud/contacts/releases/download/v1.5.0/contacts.tar.gz',
- 'created' => '2016-11-22T22:08:01.861942Z',
- 'licenses'
- => [
- 0 => 'agpl',
- ],
- 'lastModified' => '2016-11-22T22:08:02.306939Z',
- 'isNightly' => false,
- 'rawPhpVersionSpec' => '*',
- 'rawPlatformVersionSpec' => '>=9 <=11',
- 'signature' => 'ZqqhqtbHcNB+rzGCQ7FDIjjvHjit+dhAE1UhFgiXApkx3tmPP4nJOBAGNjHe+2Ao
-VcTIX2SrWEfieRrA4Gp+0k7pUPWag1Z0T1OVOwO4cmS1AVFyGIOE1bRvDhMfsWTU
-4CI4oXaKBFAY6mtnf7VJ7EeIdNqhImkohyWDQ88NiPRLM1XNkJJk6AvZBcT0fvCv
-o145X4dLpbixSXsN99QFNJ/oXvK+9tBGwTd5i/WnNFY90vcNRLia8aRo7SA0YJRx
-Lnxnj2HMqwTTDQEKE+1elYKWsqQ2DeqwScP97UIKe5bZXnrwOi9kH9PDmR4abtzd
-lHL8E1Wgw25ePDeHG7APrx0tVOJy1bP+g8vcarpGynWZoizDkBvYZD+xtxizpBXC
-JsDOSzczApptY6dnOtv0Vat8oh/Z/F99gBUahEu4WZ16ZgR1nj40PDK1Snl18Cgk
-Me1EZcde8SLEpTbCWYIfIw/O9Fkp5cWD/dAqoiO6g+gNxSZ/gGp57qoGfFxn7d/x
-H3aH8GljatAFjrwItw1JzR0THt0ukkOK+bw/pfCslk10sjHMitmz/GXa4qMS91DZ
-BKLUd0dSfQUQzkfwcojImbzJRvca4/DYe3mfG7+RCH0tDL6t72dKL9joB++u5R1u
-VZPgkToexlXcKWpiDB8H2/SEShKr4udAOjR5de9CYWM=',
- 'translations'
- => [
- ],
- ],
- ],
- 'screenshots'
- => [
- 0
- => [
- 'url' => 'https://raw.githubusercontent.com/nextcloud/screenshots/master/apps/Contacts/contacts.png',
- ],
- ],
- 'translations'
- => [
- 'en'
- => [
- 'name' => 'Contacts',
- 'summary' => 'The new and improved app for your Contacts.',
- 'description' => 'The new and improved app for your Contacts.',
- ],
- ],
- 'isFeatured' => true,
- 'authors'
- => [
- 0
- => [
- 'name' => 'Alexander Weidinger',
- 'mail' => '',
- 'homepage' => '',
- ],
- 1
- => [
- 'name' => 'Jan-Christoph Borchardt',
- 'mail' => '',
- 'homepage' => '',
- ],
- 2
- => [
- 'name' => 'Hendrik Leppelsack',
- 'mail' => '',
- 'homepage' => '',
- ],
- ],
- 'ratingRecent' => 0.5,
- 'ratingOverall' => 0.5,
- 'ratingNumRecent' => 0,
- 'ratingNumOverall' => 0,
- 'certificate' => '-----BEGIN CERTIFICATE-----
-MIIEAzCCAusCAhATMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
-VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
-MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
-dXRob3JpdHkwHhcNMTYxMDEyMjAzNzIyWhcNMjcwMTE4MjAzNzIyWjATMREwDwYD
-VQQDDAhjb250YWN0czCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANzx
-/zJF+5/s4lOJLWIlfKQgTy+UpvIpiUXCgrsHsDZTx+hjQAhIWukH88a+7NVAL7Ys
-kQNC0Tlm755FJi/T6EdR7edOwIRdo2ZwakOWLZXd209+6cCd2UloHL0bgnbWepTl
-R/4YgbLg/G+FVKCfkEiYc3PuDZ3EVrcwQFcg7h74X9ne6CHH0Z1WQLydxJuVEb2n
-X9I+nIRpPHcVostkSiUmzHR7C5TjTIo2PzzgnCU6GC0iBa6z6dDYfz24QEp/b8UA
-ZaLhjkyKghVGMnoF/s9KPgH4NM8pvvtadQN8pxlOPju4wbmKPUrsXo4ujurDXbbc
-YkzNt8ojobGwdTXoyDogAsGZLQd2FQksWpRvY+I3zVPokBfPMdUPLllG5VcV0VA5
-DRK+h2ms+XmspdBvGonjF+XdbFm9hEmDoFmoi9aU6C6AdofjmG/e9+pw/20dXUWk
-mMorWwXQ5yLmIn5LnpRXrOuK7CS28VRhBYuVNMlsyKhzU0rophbsD9OFXxYLjr6s
-7UPNwZ5h+kjXZDBKD89QctBSViT8RhLe8nulRIm0iJn1sb9hca/CF63KmsFzENfK
-QeM6MO0H34PB84iNyz5AX1OIy+1wHD4Wrzt9O/i2LkWK6tBhL69aZiBqdLXWKffj
-ARDCxxIfews51EZFyHzwsw65I97y46aBKxY382q7AgMBAAEwDQYJKoZIhvcNAQEL
-BQADggEBACLypX0spxAVAwQIS9dlC9bh1X/XdW2nAvSju2taUTBzbp074SnW6niI
-bnY4ihYs4yOuGvzXxnp/OlvWH7qhOIchJUq/XPcEFMa7P03XjVpcNnD3k0zQWlZb
-tGonX9EUOeLZKdqI4fkrCkMLScfjgJzoHGYQrm8vlIg0IVuRLCKd5+x4bS7KagbG
-iuPit2pjkw3nWz0JRHneRXz/BNoAWBnJiV7JMF2xwBAHN4ghTM8NSJzrGTurmpMI
-Gld7yCP47xNPaAZEC66odcClvNtJ2Clgp8739jD6uJJCqcKDejeef0VU1PG7AXId
-52bVrGMxJwOuL1393vKxGH0PHDzcB1M=
------END CERTIFICATE-----',
- ],
- 4
- => [
- 'id' => 'mail',
- 'categories'
- => [
- 0 => 'tools',
- ],
- 'userDocs' => '',
- 'adminDocs' => 'https://github.com/nextcloud/mail#readme',
- 'developerDocs' => '',
- 'issueTracker' => '',
- 'website' => '',
- 'created' => '2016-10-19T19:41:41.710285Z',
- 'lastModified' => '2016-10-19T19:57:33.689238Z',
- 'releases'
- => [
- 0
- => [
- 'version' => '0.6.0',
- 'phpExtensions'
- => [
- ],
- 'databases'
- => [
- ],
- 'shellCommands'
- => [
- ],
- 'phpVersionSpec' => '>=5.4.0 <7.1.0',
- 'platformVersionSpec' => '>=10.0.0 <12.0.0',
- 'minIntSize' => 32,
- 'download' => 'https://github.com/nextcloud/mail/releases/download/v0.6.0/mail.tar.gz',
- 'created' => '2016-10-19T19:57:33.676730Z',
- 'licenses'
- => [
- 0 => 'agpl',
- ],
- 'lastModified' => '2016-10-19T19:57:33.834580Z',
- 'isNightly' => false,
- 'rawPhpVersionSpec' => '>=5.4 <=7.0',
- 'rawPlatformVersionSpec' => '>=10 <=11',
- 'signature' => 'VbMsvDpt+gSPeFM8LrZXEK10rk8kkLlgCcblgqNdCSeGZeVpwDAYv3CccVSLa0+l
-lTSqQ0VIoH+OIU6vIQNBKHmSCzTplk7OrY0+L5FajXx8LnBaOh892GfGSlEt1neN
-KyM0i0uOjO/xpCP/NoUlgkz6hnmYY5XEdN6DTsJtJ/XZhDQ45IYuIkMkHE/eFehS
-0JnOagIz+PSipeBY2Ry+tV8YbRa7bC1JAvZzlod0dyI015AHZESeitRUY+MwMWkt
-N/me7g7/Kev0wggIQQZm9aYcw63GMk/1VHUPB7Y0ESW9tx2nR5+KwTDn/Jy4DGf1
-rg8h0t5I+aPhHOBLrpczH0qaZWY2lsVZWq8KWjJI9aR9P0v2f2aXixXzD/Cuz1cK
-hvhKWkOSla4D+/FxeyHGjQvdXMG8gXm0ZmTimKChCoVuCbncDd8pzkdyNoGXcvuk
-sP8OrkQFooL4E7S4BWfdSiN/a8jUITJQkuXp/OVrVGeCupLWJh7qegUw6DvoqyGy
-D4c6b+qYn68kx3CLaPPiz+tFAZQZQdj7+Kx/lohso8yTnVSiGYrMj4IvvCbpsQjg
-WF3WSqF/K/tTnPYTWb9NUPSihTbVNv6AXOfTsPEp/ba2YSS5DjvjVjkr5vhR9eg1
-ikQ3Cw6lW3vaA4LVCC+hFkMRnI4N0bo5qQavP3PnZPc=',
- 'translations'
- => [
- 'en'
- => [
- 'changelog' => '### Added
-- Alias support
- [#1523](https://github.com/owncloud/mail/pull/1523) @tahaalibra
-- New incoming messages are prefetched
- [#1631](https://github.com/owncloud/mail/pull/1631) @ChristophWurst
-- Custom app folder support
- [#1627](https://github.com/owncloud/mail/pull/1627) @juliushaertl
-- Improved search
- [#1609](https://github.com/owncloud/mail/pull/1609) @ChristophWurst
-- Scroll to refresh
- [#1595](https://github.com/owncloud/mail/pull/1593) @ChristophWurst
-- Shortcuts to star and mark messages as unread
- [#1590](https://github.com/owncloud/mail/pull/1590) @ChristophWurst
-- Shortcuts to select previous/next messsage
- [#1557](https://github.com/owncloud/mail/pull/1557) @ChristophWurst
-
-## Changed
-- Minimum server is Nextcloud 10/ownCloud 9.1
- [#84](https://github.com/nextcloud/mail/pull/84) @ChristophWurst
-- Use session storage instead of local storage for client-side cache
- [#1612](https://github.com/owncloud/mail/pull/1612) @ChristophWurst
-- When deleting the current message, the next one is selected immediatelly
- [#1585](https://github.com/owncloud/mail/pull/1585) @ChristophWurst
-
-## Fixed
-- Client error while composing a new message
- [#1609](https://github.com/owncloud/mail/pull/1609) @ChristophWurst
-- Delay app start until page has finished loading
- [#1634](https://github.com/owncloud/mail/pull/1634) @ChristophWurst
-- Auto-redirection of HTML mail links
- [#1603](https://github.com/owncloud/mail/pull/1603) @ChristophWurst
-- Update folder counters when reading/deleting messages
- [#1585](https://github.com/owncloud/mail/pull/1585)',
- ],
- ],
- ],
- ],
- 'screenshots'
- => [
- ],
- 'translations'
- => [
- 'en'
- => [
- 'name' => 'Mail',
- 'summary' => 'Easy to use email client which connects to your mail server via IMAP and SMTP.',
- 'description' => 'Easy to use email client which connects to your mail server via IMAP and SMTP.',
- ],
- ],
- 'isFeatured' => false,
- 'authors'
- => [
- 0
- => [
- 'name' => 'Christoph Wurst, Thomas Müller, Jan-Christoph Borchardt, Steffen Lindner & many more …',
- 'mail' => '',
- 'homepage' => '',
- ],
- ],
- 'ratingRecent' => 0.5,
- 'ratingOverall' => 0.5,
- 'ratingNumRecent' => 0,
- 'ratingNumOverall' => 0,
- 'certificate' => '-----BEGIN CERTIFICATE-----
-MIID/zCCAucCAhAVMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
-VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
-MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
-dXRob3JpdHkwHhcNMTYxMDE5MTkzMDM0WhcNMjcwMTI1MTkzMDM0WjAPMQ0wCwYD
-VQQDDARtYWlsMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp++RuliQ
-lBeeiPtP0ecBn00OaU1UCpft/NVI5pnSiT9nU4l2kc5IvKjA8UxDB3gWfYTOeBFh
-tUHQ2P6UKCmHZT9sApHhqLu2n0V+YhlFIViuaxndSID/M414cl56xOYQusV3Pcae
-o2dOSeRRzLab3tEaVHlkBSFkGmAwPZItsmTklvV3h1sUysDicYgfXPCkf7K+JgWA
-BP7vsWC8B7MDRhcB3enYv5tTcpsyvtGX7bb1oTIWVypcmKsGYfTX12VNBxKzNBIG
-8pwdb8Xo0o14TytWsWN7mSHf1XbwfwYMjDWOlMqiRc+mcoKMBH41TfM/CXslSivI
-syvxasEaFdlj8lmKPENdzw1OfYRs43usIf4szwyt4rb8ocXfDipnY3P2hccN6YcZ
-l8y8Vsr69ASluDj2A2Pl5vH6xp6tNybZRnN5G6sghhaYaLNDU/TdMyYzz4AY33Ra
-HSaMypfcXjd76Aj8jZvcwk1BH+ZsvFqNK7ZKCb7WVcMH8KRcU1sxZ4rp9vviM2fL
-L7EVtznm3bSI9jjHXbiwq7RvNRRy+F6YRpAdWGwTU8uUkDabPFi41FikYyzNWauK
-JhlDJXl514XjKyMVBjAZYVr5gZZkO1J7C4XzLFbC5UzYNSzW5Iwx/1j5OeYJRxh6
-5rhiUwR+COT1wdVsl6khMC8MfBR4unSd338CAwEAATANBgkqhkiG9w0BAQsFAAOC
-AQEATBvpqz75PUOFPy7Tsj9bJPaKOlvBSklHH7s43fDDmQbJwswXarZi3gNdKf8D
-yO/ACZvO8ANWAWL/WahkOyQtKOYzffaABGcEIP7636jzBdKtgwSGzW3fMwDghG10
-qBr2dE6ruOEdSpuZxgMgh2EulgknZUXaHAMI2HjjtAMOBScLQVjOgUqiOHmICrXy
-ZETmzhx0BXDt5enJYs8R2KMYJNIme1easQRYmWKliXogNY09W7ifT9FHtVW1HX+K
-xRS4JXbapjolkxyGSpP+iYSgItVnYzl6o9KZResR4yDsBv7G/8fpV4GQU9IS3zLD
-PiZOosVHWJdpUKCw9V4P1prGTQ==
------END CERTIFICATE-----',
- ],
- 5
- => [
- 'id' => 'audioplayer',
- 'categories'
- => [
- 0 => 'multimedia',
- ],
- 'userDocs' => 'https://github.com/rello/audioplayer/wiki#user-documentation',
- 'adminDocs' => 'https://github.com/rello/audioplayer/wiki#admin-documentation',
- 'developerDocs' => '',
- 'issueTracker' => 'https://github.com/rello/audioplayer/issues',
- 'website' => 'https://github.com/rello/audioplayer',
- 'created' => '2016-09-16T05:44:24.857567Z',
- 'lastModified' => '2016-11-17T22:34:34.637028Z',
- 'releases'
- => [
- 0
- => [
- 'version' => '1.3.1',
- 'phpExtensions'
- => [
- ],
- 'databases'
- => [
- ],
- 'shellCommands'
- => [
- ],
- 'phpVersionSpec' => '>=5.4.0',
- 'platformVersionSpec' => '>=9.0.0 <12.0.0',
- 'minIntSize' => 32,
- 'download' => 'https://github.com/Rello/audioplayer/releases/download/1.3.1/audioplayer-1.3.1.tar.gz',
- 'created' => '2016-11-17T22:34:34.215350Z',
- 'licenses'
- => [
- 0 => 'agpl',
- ],
- 'lastModified' => '2016-11-17T22:34:34.867778Z',
- 'isNightly' => false,
- 'rawPhpVersionSpec' => '>=5.4',
- 'rawPlatformVersionSpec' => '>=9 <=11',
- 'signature' => 'p6Zz0IEFrxvw6y/3jHgGWWCxR6qpMzvU2HKfxcIVsK6sJnoRUhWLeAXwZ432fH2a S2llj+IGS9OvW+5VQElrXgPtEjDK1BT00DRJnp5RFCRlUv0LNoedJMzx6B6AHqPP JBufk3cG1O/CO0M0L1ITGSmSOzfKvWTRo3lxVGF792NyBaP/SyZCkH1N1TzBQzUi Ywl3+HiglPcXbHjtJm/arnKorbJWVKoaN93xFuaBapd2ozQSpi0fE0uGRsici+U7 HNa1M5WFE1rzUJoufE0E9246At07rFY1e+TdNEq8IlLgCXg5vGCKkEyuWpWno6aX LfRaIiT9x39UTAwNvuDKS0c+n4uWDYPsGfKhDx9N7CXpUrthfXVEWRzZEXG7as10 6ANvrRPJemSZH8FUSrdJhD7k12qa9R825y7mIG68Li8P71V92EOxFfo9tNXqXwBt VuDGxBqByFVPqSCj5I8hrzJzQl2Xt40g8+8ZcSF96RMg/pM+bwRMTv+mz0V+vQQ4 DWjqnWVPalaJ1PPD5/QFFErtXuNRbyxKZ6BMWxfJlLM9Kz66P75K+8fYaSwz+2KG NxY7I3svzS2K9LGH3fBLUy1t3Hl+c3zGFq/ll1MJrf9NswV4yxt2WmISfM/KDww8 yELkGs2cRzbw2tCvnmYlJJhIqRLkyFAbDzWRH/XdVx4=',
- 'translations'
- => [
- 'en'
- => [
- 'changelog' => '2016-11-17
-- fix: one-click-play for wav not working
-- fix: wrong sql statement for PostgreSQL [#90](https://github.com/rello/audioplayer/issues/90)',
- ],
- ],
- ],
- ],
- 'screenshots'
- => [
- 0
- => [
- 'url' => 'https://github.com/rello/screenshots/raw/master/audioplayer_main.png',
- ],
- 1
- => [
- 'url' => 'https://github.com/rello/screenshots/raw/master/audioplayer_lists.png',
- ],
- 2
- => [
- 'url' => 'https://github.com/rello/screenshots/raw/master/audioplayer_share.png',
- ],
- ],
- 'translations'
- => [
- 'en'
- => [
- 'name' => 'Audio Player',
- 'summary' => 'Audio Player for ownCloud and Nextcloud',
- 'description' => 'Audio Player for MP3, MP4, Ogg, and Wave with a lot of flexibility for all your needs.',
- ],
- ],
- 'isFeatured' => false,
- 'authors'
- => [
- 0
- => [
- 'name' => 'Marcel Scherello',
- 'mail' => '',
- 'homepage' => '',
- ],
- ],
- 'ratingRecent' => 0.5,
- 'ratingOverall' => 0.5,
- 'ratingNumRecent' => 0,
- 'ratingNumOverall' => 0,
- 'certificate' => '-----BEGIN CERTIFICATE-----
-MIIEBjCCAu4CAhAIMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
-VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
-MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
-dXRob3JpdHkwHhcNMTYwOTE1MjExMjA4WhcNMjYxMjIyMjExMjA4WjAWMRQwEgYD
-VQQDDAthdWRpb3BsYXllcjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
-ALyC+iLscLs62NeNmUXEBmg+xMuUtDmZKr+xzJWtl6SSNRz+8K1JygvUIXFJ3RIL
-CYA3xyq8/wyZH1gNrLKyz5eTeYawG+eT3ges/FT6MWGUbZoRrBrikVcLC94QzxTH
-xOl8Dn+SCV/2bhcvPTQdhK+dqtvGilOtjHa40iMrk9gSdlKVys5CK/xdlEp8uiMa
-kz1WENn8MVCCJV58bAUbaCupDWXR9CCoSsw8XinNsCenZ2B2XlnmbM44280w0ojs
-72rfQRgj3yDG+ZUUyUOuxIuodu8liXYciLf0ph6t/f/qoSmctbBdsR5Fl1Upj1Ac
-qeHb5Yf/B3Vi6Mn3XfDx0H2EHk1v9Dhzxay+v9BHUzyIX2iH/q+7TE0/Jzo5AwBW
-vFKWXvG7wXaALcHYZf5v/M93IE0iCHsv2EsZKQPBnzXVGmp4DwFSP4po1B7hcog1
-gAMaellAzzvUAizgCovN6Qct3qDEANYniPlvtnlcaQGonajW4N019kFQRHLIzPFR
-jab5iUMMwSnT8FhZO2ZOWuWhJven+gXjxC8mfMVgBfZnAVgydNfx9rN+KzTc88ke
-obUdZ0OOeBzA7pIxGEFg9V6KTEEWZ+qH048vxXz4HI9B1I+2wQLBrZl8CvweEZ5U
-5ID8XrrE/UaNZ1CvLKtCgB24gj/m1Elkh7wA3gEcEo2JAgMBAAEwDQYJKoZIhvcN
-AQELBQADggEBACtgUp+FCmjWIkQUuWSdzKWdO+IH4v9wBIrF9mo0OLIakFyDYyM5
-LlkYZXbplGXd4cfn3ruIqJNzlIb4xa5CU0bM4TMbD4oOSlLMKM/EamKPHI3bzr++
-zi7mQDFxmAE6FWSMBgKKUb4tqLc5oBap8e12tPEZl/UR6d9iUB2ltvrm3T3vrjjl
-2Worm0eYBNqnagXmX5+wS11AQqeJemGqRy5e1yXRlTgB0IJhH0dCsFNwifEigutp
-FNvGFVBn4r5qCiChEoq+rCXHRjPi/eCfbW21XeLFDiLxapcZyc85JIcA7znUYoFe
-P7Y/ekMscwWhLbF91OaQlcWpRtEMyde/DaI=
------END CERTIFICATE-----',
- ],
- 6
- => [
- 'id' => 'calendar',
- 'categories'
- => [
- 0 => 'organization',
- ],
- 'userDocs' => 'https://docs.nextcloud.com/server/10/user_manual/pim/calendar.html',
- 'adminDocs' => '',
- 'developerDocs' => '',
- 'issueTracker' => 'https://github.com/nextcloud/calendar/issues',
- 'website' => 'https://github.com/nextcloud/calendar/',
- 'created' => '2016-10-01T12:40:39.060903Z',
- 'lastModified' => '2016-11-22T20:31:13.029921Z',
- 'releases'
- => [
- 0
- => [
- 'version' => '1.4.1',
- 'phpExtensions'
- => [
- ],
- 'databases'
- => [
- ],
- 'shellCommands'
- => [
- ],
- 'phpVersionSpec' => '*',
- 'platformVersionSpec' => '>=9.0.0 <12.0.0',
- 'minIntSize' => 32,
- 'download' => 'https://github.com/nextcloud/calendar/releases/download/v1.4.1/calendar.tar.gz',
- 'created' => '2016-11-22T20:31:13.020268Z',
- 'licenses'
- => [
- 0 => 'agpl',
- ],
- 'lastModified' => '2016-11-22T20:31:13.087340Z',
- 'isNightly' => false,
- 'rawPhpVersionSpec' => '*',
- 'rawPlatformVersionSpec' => '>=9 <=11',
- 'signature' => 'nThwe9CJBCan9nuDLdhfBiQyPhmum6Aa0UcYsIDdhGMw+C2acf81KhEmBJuTTWxo
-WGby6WcrcJJmeuCW+ePU91ju7Pd76RirprhVXIEceIDzSCxin+K0oZCZ1IGVIJjP
-IkVehTsLuCeTBbjvz1b3k5QFyhUhvd32Xt7k5d7VARyI4OqnqYYNBtH9vvgeRrFw
-AxsQr4o4axof6i3iykLg6WfWarYArY4dIuu5DkPuGPWf2bbgjwWEra4sQejhOs7G
-sk1xcsfYv2NpArIbpw/wnATdjiax+Gjz1URMD3NgL5ky0ecuZmNvN25QErg3nlVr
-hh1FBfA5pvCJbkJ6nr5bU4bKaffwDX1nr5h77FS5zzn0Pyd7ZIExmVmNtaeJfnfV
-5vnclapzXMNU+R6t/ATJQd1srvSJhyljQapzsqnrjNMEUojOEvqqygJp0KwNVPqs
-3g9XGSoOnO+WULWBeISW7UVOg8BOF8pwvHIU2++bSzOdpypW0Eq6p2DPWO6qL/H1
-eFLKrUg3EvnTjvknbBxMB55h9jNJr0SAlkrmyEVm6+CE3BwRWpKB+cJMBuGiwPwv
-r/ASRiJrkDThbNWAUtX70rUmCqDV6/MujLXViqOc/Q2OHvcXd1oGDccJSQT92/1z
-7nonnedyYQIDqUt7u68WL8JRxp7pFsEqKLVuWSgxW3c=',
- 'translations'
- => [
- ],
- ],
- ],
- 'screenshots'
- => [
- 0
- => [
- 'url' => 'https://raw.githubusercontent.com/nextcloud/calendar/master/screenshots/1.png',
- ],
- 1
- => [
- 'url' => 'https://raw.githubusercontent.com/nextcloud/calendar/master/screenshots/2.png',
- ],
- 2
- => [
- 'url' => 'https://raw.githubusercontent.com/nextcloud/calendar/master/screenshots/3.png',
- ],
- 3
- => [
- 'url' => 'https://raw.githubusercontent.com/nextcloud/calendar/master/screenshots/4.png',
- ],
- ],
- 'translations'
- => [
- 'en'
- => [
- 'name' => 'Calendar',
- 'summary' => 'Calendar GUI for Nextcloud\'s CalDAV server',
- 'description' => 'The Nextcloud calendar app is a user interface for Nextcloud\'s CalDAV server.
-
-It integrates with other apps, allows you to manage calendars and events, display external calendars and invite attendees to your events',
- ],
- ],
- 'isFeatured' => true,
- 'authors'
- => [
- 0
- => [
- 'name' => 'Georg Ehrke',
- 'mail' => '',
- 'homepage' => 'https://georg.coffee',
- ],
- 1
- => [
- 'name' => 'Raghu Nayyar',
- 'mail' => '',
- 'homepage' => 'http://raghunayyar.com',
- ],
- 2
- => [
- 'name' => 'Thomas Citharel',
- 'mail' => '',
- 'homepage' => 'https://tcit.fr',
- ],
- ],
- 'ratingRecent' => 0.94444444444444398,
- 'ratingOverall' => 0.94444444444444398,
- 'ratingNumRecent' => 9,
- 'ratingNumOverall' => 9,
- 'certificate' => '-----BEGIN CERTIFICATE-----
-MIIEAzCCAusCAhARMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
-VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
-MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
-dXRob3JpdHkwHhcNMTYxMDAzMTMyNjQwWhcNMjcwMTA5MTMyNjQwWjATMREwDwYD
-VQQDEwhjYWxlbmRhcjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMm6
-FTeqgzCXprkU83VM4/DrZWn3kqtfaR/edkC4gYT3ug7RHa/Uv1C/S++vr7pvgpnk
-YzQoavl/0Qlh5sKEYX+0ud/LQDoiidwBRDckFUQ1bRfVLxAD9UAVvDRHxDqJMOx2
-gZArbeQ3ztdSHZp4ThzBFWq2FILsJD86weG7LwHjzhW6SWgLb/YTLbuuW6tRCDVV
-btB0I/a0vCwj2u91Chw3u6pWWjPakc9DQrIDH4HCIBKQ4zVrYDxAmJDRFGDvVVWx
-uIAeux8sd8drqSMqAhX+XMcZPRD71NQTWbCupSwWO8kgjmZnBpIiBNpzvMQzJf3A
-QloZtjZ2RDXAQG88eTeT8pp8yEOCEoDLpGdraKxJrh/z2Dsk30JP3lOiNYJ9vBaB
-C8NJbJ3oAlG7THwUaFF9fsdAKoTwzs5Xms04TI7W/v4Z/GClOzMymnR1T4sR72Oy
-3WaMNHv/1QGffvQn2/TtZt23Ou3P083xWx2vn5FgTcac8+x85vRgWsVCA4hq9v6m
-AlktB0+UWDEXpDTKD9BdFNWM8Ig9jQf7EJrvTLNnS7FIJZMB4GK8lpvPxyvACWnh
-R2hQOe987Zvl3B1JZNO5RvtSeYld9Y9UfMgW1aPRweDNjSuZYAKlugx1ZoyI5HyA
-QjfzAwicIMwZsCJDV/P5ZO8FE+23rdWaoJczpBqDAgMBAAEwDQYJKoZIhvcNAQEL
-BQADggEBAHQXwvj8q5khWR/ilg3JGYpmMNBYHE9OeDaOcNArkKaGMd478SDPOXeu
-yW7hCvNEpiTk5g0h3g3yleZFws0xH8fPsQgZANgvQXb3RCcD61NL77d0cMTr7Xzr
-N3Lq/ML1YLc/WwL4uV1XvpMQMwALFL1p63BU2c0ysO31zbLOjMKAJi0hHFDYz5ZQ
-D3xxtc17ll3B5IqrMnMHRqmOQ39Sbe56Y7T4agaIz/sUWpseo85D5kt7UAIOR+Mr
-Q0Bl/QinETk72afGR46Qvc7tC1t9JjQQD3AUbEGuJdGvXjJJ9GREYu01XoODmPdT
-jXXOI8XIOK6kxXhPHUc3iWu9b4KqGm0=
------END CERTIFICATE-----',
- ],
- 8
- => [
- 'id' => 'ownpad',
- 'categories'
- => [
- 0 => 'tools',
- ],
- 'userDocs' => 'https://github.com/otetard/ownpad/blob/master/README.md#mimetype-detection',
- 'adminDocs' => '',
- 'developerDocs' => '',
- 'issueTracker' => 'https://github.com/otetard/ownpad/issues',
- 'website' => '',
- 'created' => '2016-09-29T15:58:52.814912Z',
- 'lastModified' => '2016-11-19T17:37:52.278497Z',
- 'releases'
- => [
- 0
- => [
- 'version' => '0.5.6',
- 'phpExtensions'
- => [
- ],
- 'databases'
- => [
- ],
- 'shellCommands'
- => [
- ],
- 'phpVersionSpec' => '*',
- 'platformVersionSpec' => '>=9.0.0 <12.0.0',
- 'minIntSize' => 32,
- 'download' => 'https://github.com/otetard/ownpad/releases/download/v0.5.6/ownpad.tar.gz',
- 'created' => '2016-11-19T17:37:52.234684Z',
- 'licenses'
- => [
- 0 => 'agpl',
- ],
- 'lastModified' => '2016-11-19T17:37:52.423930Z',
- 'isNightly' => false,
- 'rawPhpVersionSpec' => '*',
- 'rawPlatformVersionSpec' => '>=9 <=11',
- 'signature' => 'dh+Txg1iVfqXr8+cxplNQuBZGErSnXUo0ewGwnybNMJqp8/EjEo72+zPpW3dVnhY
-67YCvhrm2bo+VRdFFymEfymzSJu9nWVFkGJhEwvTxPyIdAtuD5YAVrzmnR6L+H7m
-7Q1nXE63ICPCAQpHkxIfIXLh25OhWeyofBB8AVsjDUNn58FEYJ8fFkr6dCgPriZS
-sM2J+xtZMDYufy+xFMsVf/Q3WopjFuBjMC3qOecW76ZTwtREaswOC2RtpzUku2r1
-sogrfFlFer3Ii9/CWgOktnLfjB1DzbTwdEkM2xNVBRJgdMXt2VLA9FsxFFkjmr5A
-l7x9cNLWA8RLpOIpIMBbaef75u5HgRBvSvq114UsA9GCu/EYbIgD8YxEt7xuKd4t
-enksJB5gJ2IQNdHrPbsil59AsJ/dismDN6ktYgWQEk5dINzvm9EAvucueW0Gt+Jr
-qEC5WBgJucsFxSvkHh52v43M8jgPYBfHWEL/M/+377z3+mbuIh+BcQ+vcDdiqxTF
-o3n0+gw3QYIhLEe75sUhxG6ynVUdW25AKKju1kVj3KJnZTBH1R8t8/zy4DnJG8d4
-uRGqyU4BXpZjEC3nVlsC7vCncWWhxl0WZQ/MWKqsfjVAU4I88E518D6NioqMnPEJ
-iCZ2x+69UCDEQyfCSKajgAYT17r3OhZei8F9KSCH8Vw=',
- 'translations'
- => [
- ],
- ],
- ],
- 'screenshots'
- => [
- ],
- 'translations'
- => [
- 'en'
- => [
- 'name' => 'Ownpad',
- 'summary' => '
- Create and open Etherpad and Ethercalc documents.
- ',
- 'description' => '
- Ownpad is an ownCloud application that allows to create and open
- Etherpad and Ethercalc documents.
-
- This application requires to have access to an instance of
- Etherpad and/or Ethercalc to work properly.
- ',
- ],
- ],
- 'isFeatured' => false,
- 'authors'
- => [
- 0
- => [
- 'name' => 'Olivier Tétard',
- 'mail' => 'olivier.tetard@miskin.fr',
- 'homepage' => '',
- ],
- ],
- 'ratingRecent' => 0.5,
- 'ratingOverall' => 0.5,
- 'ratingNumRecent' => 0,
- 'ratingNumOverall' => 0,
- 'certificate' => '-----BEGIN CERTIFICATE-----
-MIIEATCCAukCAhAPMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
-VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
-MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
-dXRob3JpdHkwHhcNMTYwOTI5MTU1NDA3WhcNMjcwMTA1MTU1NDA3WjARMQ8wDQYD
-VQQDDAZvd25wYWQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC6CY7I
-HRJTaqDu376vt+kruX+QOL864joScxRuh3IOVcQktCvxasuA0EtrX7TCAQrV1tBK
-fkqJxU9uOV54RTgyh30yH/ZtnF2bYQwViGM06Snc0riqWydFrN5fxK52dpZWs63o
-UFCNhHxrX4aUGyfXu5nQMISLm4QHoZ3LDLofk1ZsiK62fM/Jz8N2PM8qeHzf1ATo
-SKcAOd3UeaS9C8bv2DuiZM7unkSO/tjrBzkMiq8ds9sIzBBsyk6BRh2HQjHPOtmO
-ed+pS9mIZmc2xhssXoHL4IfZwTqwhktpsaTl7v0ROw2dwDATz/QoKMkUpboQ5lkz
-wgLQhoIZw6uAZ1R/Qjze59I3iU8zIo9quDarHBotZNXboYCmg9FRfE4mHtegVaa8
-v1a1JvFQ5gvsWEsKSV6Bzb65GTp4KG4q7YnUrzh6HJyDCGLvLlWm5OWsFj6sNzXX
-wLOv6JLORMbF4ZIo2iybb3x7gdfCu9JxMZ4JtOUC8KSJ6+ub15C1Aia3lN68dNts
-Y6KwUF1Ted0o4OQPAulq5pUc+g6dTYmIKsavIiPKhMtl86AbUK50vRTeuGdFsT7X
-av73IanPdFI9bKth+tajgvB6dxcVnvBXbrsLUyEcsxsxtBJvQcMYS4aZ6ZJYLTep
-7AdK0Zt1iMdXB8+4PCps4rcG6bYB/uJeEAVm7QIDAQABMA0GCSqGSIb3DQEBCwUA
-A4IBAQCM10O+sCYhIExnx01vGzKlnRS7MSQNx8ZMmbR5Elfz4AVJAEJ96ytS2DXH
-2c+hcD0wAenXQEFk920AEqFQBT8DP34p0FmF83aMHW08ovzFiu4MdlhcqrLnko0h
-cZTXHVyS/8JZh+o6SVm8R0/BBLF1MQQ5TqRkJehbmk6gL0+MSYxehUDKWTjJITkR
-ifneTw/Ba1d0AXBOq0c0HFyGxMPIlWe4qn5LtxH5t0wyVGeSj4jyv4nvd3ZGuAgY
-EUa2uYht/z475k4+vf0YhV98iQH07GnmlfD2TDZgmOCQGKlNfJh1v88OZyLLa3dz
-gRHzGwKbAiJ8T8bbpZ3e2ozXxADr
------END CERTIFICATE-----',
- ],
- 9
- => [
- 'id' => 'announcementcenter',
- 'categories'
- => [
- 0 => 'organization',
- ],
- 'userDocs' => '',
- 'adminDocs' => '',
- 'developerDocs' => '',
- 'issueTracker' => 'https://github.com/nextcloud/announcementcenter/issues',
- 'website' => 'https://github.com/nextcloud/announcementcenter',
- 'created' => '2016-09-14T10:38:53.939634Z',
- 'lastModified' => '2016-11-24T11:21:50.324839Z',
- 'releases'
- => [
- 0
- => [
- 'version' => '2.0.0',
- 'phpExtensions'
- => [
- ],
- 'databases'
- => [
- ],
- 'shellCommands'
- => [
- ],
- 'phpVersionSpec' => '*',
- 'platformVersionSpec' => '>=10.0.0 <12.0.0',
- 'minIntSize' => 32,
- 'download' => 'https://github.com/nextcloud/announcementcenter/releases/download/v2.0.0/announcementcenter-2.0.0.tar.gz',
- 'created' => '2016-10-06T12:41:56.195206Z',
- 'licenses'
- => [
- 0 => 'agpl',
- ],
- 'lastModified' => '2016-10-06T12:41:56.263124Z',
- 'isNightly' => false,
- 'rawPhpVersionSpec' => '*',
- 'rawPlatformVersionSpec' => '>=10 <=11',
- 'signature' => 'NVWYz73KtuoZ7ti2sluztJO5aFUc7PzhlDcg0VWyAQd1H7sk5wjw7i0bhrjw8O7M
-Lsrb+PegnsL9eMlYM2WrRom+RF1PDP482xymZf1T8vh8qcTCm3TK89xSuiSm8yoA
-iWUb/Uv/ODj74wVDWqWxAFKaAG/FestCB3InOOZQqQZLzlAV0U9ziYDGNzBjFqof
-9rLNxJ2IOqZOA7hhMIKhSrpA0KkSfNhBsVf8CWClYnVkZQiq0LoYkHkHIlXmXUr3
-OfQFKEjtsx+bNLa6CkAaocHGHJXAofX3GQZ9cjBsjZqiTfbXfcVk0kRfz7pwL92L
-I1McfJYvgMxDQG5bjRpNJw==',
- 'translations'
- => [
- ],
- ],
- ],
- 'screenshots'
- => [
- 0
- => [
- 'url' => 'https://github.com/nextcloud/announcementcenter/raw/stable10/docs/AnnouncementCenterFrontpage.png',
- ],
- ],
- 'translations'
- => [
- 'en'
- => [
- 'name' => 'Announcement Center',
- 'summary' => 'An announcement center for Nextcloud',
- 'description' => 'An announcement center for Nextcloud',
- ],
- ],
- 'isFeatured' => true,
- 'authors'
- => [
- 0
- => [
- 'name' => 'Joas Schilling',
- 'mail' => '',
- 'homepage' => '',
- ],
- ],
- 'ratingRecent' => 0.75,
- 'ratingOverall' => 0.75,
- 'ratingNumRecent' => 0,
- 'ratingNumOverall' => 0,
- 'certificate' => '-----BEGIN CERTIFICATE-----
-MIIDDTCCAfUCAhABMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
-VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
-MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
-dXRob3JpdHkwHhcNMTYwODIzMDkyNTQ0WhcNMjYxMTI5MDkyNTQ0WjAdMRswGQYD
-VQQDDBJhbm5vdW5jZW1lbnRjZW50ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
-ggEKAoIBAQDPx4Hp1HdBo5v7bDEiVcv2UrVjNW+fPPKS/5rwbagtPcE/1v3WDcwX
-vFwaXk8qCn2UpPSQ2b1rTuTDm51G1ZmEZhNiio+rBfEe9F+3tLsq9lElqIPKhkAq
-EUVI6dcN+jSqvLmLhuwloEoQQSYaLrX75mY3lGqTb83h1l2Pk/brVixuVf4vJW31
-TgeieuGKnC+keLzKOrvTHffJakU8ktwB2Nuu1o+jN5a7u1bxKkP3LjEWPjq236hk
-AoOcW/wi1dUEyUKUZsZQeJyvTJh1UXdLHKwYywtUu1/VLZ1IUtNyPBfiQ8ukPp3T
-TnSSmG3ZnvsfM6DmAvLZ8bBQkMBzEcTLAgMBAAEwDQYJKoZIhvcNAQELBQADggEB
-AAB3i2NgiZ4rpNag7cXYdaFxAxdDWnke1+LX2V2R3hzGmx73/W6cKLpo3JBn9+zT
-1aEjlqkt0yHu4aAPVYQzOa5zIV8mjP84p3ODSyV9J8lfjFNXT7wdA8+9PVx3lVki
-2ONoCNBh1kOxnxI4+BsMlQfF00ZbBSuGcMm3Ep3lTFWXzuUn3MQITzPwkL5LkW6a
-sli/yAYQRTVDsXD8A3ACYT7BG31jGxyXtIHzqCci0MhZFdKKayMYkwfjZchIUtGN
-JJoU8LQoHwGRtp3wutk0GlFzpEQEvSsn/Lsvvot5IfIe46tnzA6MVj5s64s5G8+Q
-phhXFlzXqO/VxquPdbfYjvU=
------END CERTIFICATE-----',
- ],
- 11
- => [
- 'id' => 'rainloop',
- 'categories'
- => [
- 0 => 'social',
- 1 => 'tools',
- ],
- 'userDocs' => '',
- 'adminDocs' => '',
- 'developerDocs' => '',
- 'issueTracker' => 'https://github.com/RainLoop/rainloop-webmail/issues',
- 'website' => 'http://www.rainloop.net/',
- 'created' => '2016-10-20T04:17:37.217555Z',
- 'lastModified' => '2016-11-18T11:36:04.309739Z',
- 'releases'
- => [
- 0
- => [
- 'version' => '4.26.0',
- 'phpExtensions'
- => [
- ],
- 'databases'
- => [
- ],
- 'shellCommands'
- => [
- ],
- 'phpVersionSpec' => '>=5.4.0',
- 'platformVersionSpec' => '>=10.0.0 <12.0.0',
- 'minIntSize' => 32,
- 'download' => 'https://github.com/pierre-alain-b/rainloop-nextcloud/releases/download/v4.26.0/rainloop-4.26.0.tar.gz',
- 'created' => '2016-10-20T04:28:21.491747Z',
- 'licenses'
- => [
- 0 => 'agpl',
- ],
- 'lastModified' => '2016-11-18T11:36:04.619927Z',
- 'isNightly' => false,
- 'rawPhpVersionSpec' => '>=5.4',
- 'rawPlatformVersionSpec' => '>=10 <=11',
- 'signature' => 'nTYIVSB6mIwKtXIrKoVGsOGFflpLjed8jFem1VLQNtXQj4bztnNrdc4YaPIn0yzM
-yLpMSqRDNzdYNFuOeDiyKLPJPTA++MotLCNjEe7kxUekek+m+qzgnGBdcT7RQT6R
-p9xWGecnVx94d6aA55uiRhgQRyHpdDMMLCOz1be+HvpwHy69DRFZ1+SPmGUt6eW0
-u5yS0vHCu1K22cbrVNXFKjxAOlGcIDm61oQuz7ycl3uAujZO4rZbWt55jilgKGak
-ew559A5gTp9W+j+TWKIcg6LIZ9zLRlGjcQrWJrsc+OBZQcqiYimSFyO6HhfT9TPS
-Pof//I+dSsd+H0SRGGeL8DvSnK+NKZL1q5EX5pziqsv6nZFITpCDwmAN+I8AnXXL
-SNkFi53M8RZTOABpD2x7YPYP1cEvwrRweqV/C/oHcYnpfh7D2DjFeWwXsjeAXrHY
-hgFhPrg+7rf7g6UmJFOCp0dC9sBdyQ3KtJkv7bGqPr854r2cdA7xW0QHWQ2in9qQ
-LhIczc32ECi3ZVVgyF8zyT4Y/3MRS05oX3FHvHyt88mjni6bVaO78F7ZRSha8gHh
-NOAkku7AMXPvUCHaZP2iVCCoAViEso8GeR3O8xh2G42Ai61RLYwx8LB1+23EoJTr
-mfFuRYNSg+qAKCokXNnh+lDlwu4AkaQo3vtKGPXvU7A=',
- 'translations'
- => [
- ],
- ],
- ],
- 'screenshots'
- => [
- 0
- => [
- 'url' => 'https://raw.githubusercontent.com/pierre-alain-b/rainloop-nextcloud/master/screenshots/2016.10.20-screenshot.jpg',
- ],
- ],
- 'translations'
- => [
- 'en'
- => [
- 'name' => 'RainLoop',
- 'summary' => 'RainLoop Webmail',
- 'description' => 'Simple, modern and fast web-based email client.',
- ],
- ],
- 'isFeatured' => false,
- 'authors'
- => [
- 0
- => [
- 'name' => 'RainLoop Team',
- 'mail' => '',
- 'homepage' => '',
- ],
- ],
- 'ratingRecent' => 0.5,
- 'ratingOverall' => 0.5,
- 'ratingNumRecent' => 0,
- 'ratingNumOverall' => 0,
- 'certificate' => '-----BEGIN CERTIFICATE-----
-MIIEAzCCAusCAhAXMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
-VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
-MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
-dXRob3JpdHkwHhcNMTYxMDE5MTkzNDEwWhcNMjcwMTI1MTkzNDEwWjATMREwDwYD
-VQQDDAhyYWlubG9vcDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANqB
-5jnF9qZ/qjckt0kRjpHCOMtJumW/KiQoMeNP5nGv4ad0DS3KemOapUef8Zn7qCYb
-MnODhK7HBwPifFzI1j8bnT2hP6E0geFLb0MdN59d2NF0n4CCs1+BnepQPJ1kFbPK
-35wQRi0RDeTf/GQ+/owEVCU9a9W1P/VUXk8Z0vMoQxCXEdRqnB63SgsKl7DB9G/C
-4SYrgGor+OHVGl4ntMZhJujiM996DttrNK3iZRGkQ07L+lfUIwQ52XOhQNRdic4p
-B03lw7PpChwPGMv/EEvdR5HpCJQBJniqJbbu3Jh8bMBKTE/8fCzN3vMXICB2g3Bq
-lKkZW6fnJRGsrZ79fsQnl+WBPNSrWRLOxOfe1fyCFV1ljFB4nTH7uF3pC8ZRgJes
-kHIESHz3GJm28hn4+17ESMGHBCbs7L9FK2GY31cobU0VRntLxpSG+d9njbIAgMG1
-S7U+oKVFQhSVpdXNOaUNqhcQ3HkbQTLEP0k53A/lhLQb2+KPd8nntaELjwNyrmZg
-sVMgHj/zdlvrbguZjZFzUzDBFvkuv/5M58lNT/D1C6ufVp/R6eLsYI+nnk1ojAjz
-l7N6U8X5SXpD+Bm7+Kn1PH+bHl7cViCx8oXJXO2RhP+COXckw7BDZKtjItYHNG7M
-pFwgYqWpvCu9LN6IN5a/eLqSI76dOOP3iYbaTH+NAgMBAAEwDQYJKoZIhvcNAQEL
-BQADggEBAGB0Vq0l6ndGTgNbZxSEFyBR3u3tiR3pWK81DYjsui7qBoO6P/BaGmf+
-raSwHPaBOwA9XNS8jcGLh5xdqY2p/m0dTS64xNjVL9nweWsG+FwVnPANo8C4nXdm
-9ajJ4cdg54stQK8qn1uh/xPcd23GKfYJazjYSwYmZ3pXXdzlGN9NxkeYJQxJ6B+5
-pzAeVGiABI/e5URpxzz2UayRX7EE+vtpe3B84hzkLqsq0N39ZN6KLfaTyEBGLzqE
-iLYeXQTV0XSRs8xVt+iyGlj7nPkv2DR0oCqRpWUFWeSBI//niDG5WxS3qg8kacSW
-fDSYhSN+IjrnIkwNtc8V9t7/GeQB5FE=
------END CERTIFICATE-----',
- ],
- 12
- => [
- 'id' => 'richdocuments',
- 'categories'
- => [
- 0 => 'integration',
- 1 => 'office',
- ],
- 'userDocs' => 'https://nextcloud.com/collaboraonline/',
- 'adminDocs' => 'https://nextcloud.com/collaboraonline/',
- 'developerDocs' => '',
- 'issueTracker' => 'https://github.com/owncloud/richdocuments/issues',
- 'website' => '',
- 'created' => '2016-10-31T08:55:45.631429Z',
- 'lastModified' => '2016-11-24T12:13:53.905352Z',
- 'releases'
- => [
- 0
- => [
- 'version' => '1.1.14',
- 'phpExtensions'
- => [
- ],
- 'databases'
- => [
- ],
- 'shellCommands'
- => [
- ],
- 'phpVersionSpec' => '*',
- 'platformVersionSpec' => '>=9.0.0 <12.0.0',
- 'minIntSize' => 32,
- 'download' => 'https://github.com/owncloud/richdocuments/releases/download/1.1.14/richdocuments.tar.gz',
- 'created' => '2016-11-24T12:10:13.337165Z',
- 'licenses'
- => [
- 0 => 'agpl',
- ],
- 'lastModified' => '2016-11-24T12:13:53.963638Z',
- 'isNightly' => false,
- 'rawPhpVersionSpec' => '*',
- 'rawPlatformVersionSpec' => '>=9 <=11',
- 'signature' => 'prDGlfRPxqT6LP0BsAFPwGww7P4Bngha2N4u5B6+F02N+RVOjGtTcXKqvM1KjZb1
-Co7qJvgJmjpvIvDmB+rup02i8ObfwP2ct6UdsD7ouzOWJG2sJANXK31bHyvOmQ2h
-vKu5eNcOkf+WFyFKYi51TbsfWn2+1Wge3WWujKAVcEvqtcOOz+uMWNtqzBptEupk
-E1aaRnQfTx488YB8Ubul06LIY0PNCHgGCWPgy817tOVT7JA+V0P0FFonl/PXE0dr
-WgtxRJmvGaNiFzYq+kQmdKMfayZTm3kdVgP0W52t5wp878K0i4s2KPg5lANvjTz7
-DCT+VV2IGIE52o4RpMUGyQ==',
- 'translations'
- => [
- ],
- ],
- ],
- 'screenshots'
- => [
- 0
- => [
- 'url' => 'https://nextcloud.com/wp-content/themes/next/assets/img/features/collabora-document.png',
- ],
- 1
- => [
- 'url' => 'https://nextcloud.com/wp-content/themes/next/assets/img/features/collabora-app.png',
- ],
- 2
- => [
- 'url' => 'https://nextcloud.com/wp-content/themes/next/assets/img/features/collabora-presentation.png',
- ],
- 3
- => [
- 'url' => 'https://nextcloud.com/wp-content/themes/next/assets/img/features/collabora-spreadsheet.png',
- ],
- ],
- 'translations'
- => [
- 'en'
- => [
- 'name' => 'Collabora Online',
- 'summary' => 'Edit office documents directly in your browser.',
- 'description' => 'Collabora Online allows you to to work with all kinds of office documents directly in your browser. This application requires Collabora Cloudsuite to be installed on one of your servers, please read the documentation to learn more about that.',
- ],
- ],
- 'isFeatured' => false,
- 'authors'
- => [
- 0
- => [
- 'name' => 'Collabora Productivity based on work of Frank Karlitschek, Victor Dubiniuk',
- 'mail' => '',
- 'homepage' => '',
- ],
- ],
- 'ratingRecent' => 0.5,
- 'ratingOverall' => 0.5,
- 'ratingNumRecent' => 0,
- 'ratingNumOverall' => 0,
- 'certificate' => '-----BEGIN CERTIFICATE-----
-MIIDCDCCAfACAhAZMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
-VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
-MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
-dXRob3JpdHkwHhcNMTYxMDMxMDg1NDExWhcNMjcwMjA2MDg1NDExWjAYMRYwFAYD
-VQQDEw1yaWNoZG9jdW1lbnRzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
-AQEA1jk29m6JykcJ2Ld0YEpjPMYh6kwxY6GysNJnfkA/th7tPWL3+vBJ9oTYyVnZ
-jwAE1Cqwfa9MyBKMZ2IdfIqtT8PeWzuFP7Ib942EdxUpwwh9F3lykeGsj0h4zQwX
-F9OooiS99PfLX+JpkKm15Ujb00iLB6xQmq0+3NeOT1CTD1ziJ1ueOcxBKMwaFp2a
-Puz3F5ywqCvpmxG/OBuOs0LI3/zStXhBNbUMxBrWblr7zaVNJXl/I2JCKj8Wah/H
-XUEEGbW15fAUP1f+90eQSxpEoCZDBHXOQCTJYzySGv+BjU+qlI9/gS0QbrsiyzUf
-O5lyvi8LvUZBzpBw+yg1U75rqQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQA9jU3m
-ZmD0ywO3MUsG/GLigFtcWi/p7zp2BliR+NpuY2qNFYDcsIb8ZUudmUc/cJRRctzy
-APaLLj/d+h5RFaxjTVvim1PSe6M7urK/IMSvyUVYCeQRYpG8ZJixKTCOVIBaWHMz
-xTfc51tm9EPlpJpK6JtaWrYYoWGE3k9sINdJ4JkvKkE2CBAqVhX6ZGyEQ0bnEhtk
-Ru1DXn+LW7TJ4NZ8VtLWvmW/6Kfmi7dQ1V++Kmn0lO5ntRt5altePbStCHC8bhGp
-myBOrjhrJgLIwvgH26MYZhdiSkFzoE38nMPZdrUmUDxcPCwucWJqgzDPudguFthj
-WCVZ3TTG/2z3+tWM
------END CERTIFICATE-----',
- ],
- 13
- => [
- 'id' => 'ocr',
- 'categories'
- => [
- 0 => 'files',
- 1 => 'tools',
- ],
- 'userDocs' => 'https://janis91.github.io/ocr/',
- 'adminDocs' => 'https://github.com/janis91/ocr/wiki',
- 'developerDocs' => 'https://github.com/janis91/ocr/wiki',
- 'issueTracker' => 'https://github.com/janis91/ocr/issues',
- 'website' => 'https://janis91.github.io/ocr/',
- 'created' => '2016-09-19T12:07:49.220376Z',
- 'lastModified' => '2016-11-21T11:22:21.024501Z',
- 'releases'
- => [
- 0
- => [
- 'version' => '1.0.0',
- 'phpExtensions'
- => [
- ],
- 'databases'
- => [
- 0
- => [
- 'id' => 'pgsql',
- 'versionSpec' => '*',
- 'rawVersionSpec' => '*',
- ],
- 1
- => [
- 'id' => 'mysql',
- 'versionSpec' => '*',
- 'rawVersionSpec' => '*',
- ],
- 2
- => [
- 'id' => 'sqlite',
- 'versionSpec' => '*',
- 'rawVersionSpec' => '*',
- ],
- ],
- 'shellCommands'
- => [
- 0 => 'ocrmypdf',
- 1 => 'tesseract',
- ],
- 'phpVersionSpec' => '>=5.6.0 <8.0.0',
- 'platformVersionSpec' => '>=10.0.0 <12.0.0',
- 'minIntSize' => 32,
- 'download' => 'https://github.com/janis91/ocr/releases/download/v1.0.0/ocr.tar.gz',
- 'created' => '2016-10-24T06:50:43.283900Z',
- 'licenses'
- => [
- 0 => 'agpl',
- ],
- 'lastModified' => '2016-11-21T11:22:21.269108Z',
- 'isNightly' => false,
- 'rawPhpVersionSpec' => '>=5.6 <=7',
- 'rawPlatformVersionSpec' => '>=10 <=11',
- 'signature' => 'CBJkCIiUKyf2NuWfz2zJ3grhf8p7wJes7DPV/OxUzhlxIH0Fh7K54+U5A9JOOi6f
-WPhjXG1ylkyIVY1glr/B8svWNsD4jAclpnUi1/9ZW5UPT8LnRBfTbtF9Uoj0OgNs
-tsGQYbpuREoHnjbJWTRe0kq1OsOfX44xuf8PuX43B+lpQPW4iRSSz3ZIhdPcDGq1
-7pyqQM7gdKhBQ6/tOiwd7Enyt5Hi4V6jhwhUOCYeTNiLD2V3yKL+qA9DzpXUfNNw
-LGTjcaMrifibHQIZBZWbPPMmCfMJZ7GO9oR4gWHwkhWqt0yVWAJXAHJBLd5vXC5I
-jtRTXRpHO/k6Dtqem8tZCVoDE5MAC7fDZ/0XzoFiXHciP6MenVasVcXo6xJOJc5y
-GsrecNftUEhP/ngxA6lMBVkLmmdpiexVisvsavPi64i34OUA6qOuxjgNVBDwg56i
-2lOEVvHa3nn0UX7ZZoQ/Nu6Mz7J3Hx/VDlttPuWe42eeJAphyDGubT1M62gW8dVB
-D3tJOF7spnK6I3BhVLviou/zs30AIRVBDTU0Orzx78cbInwy6/vyJib2a1olAaHz
-v05SzlQRnBWM4jYBe0mA/2ds9AO6VrXGrT/iLlHemj6JYoGBM185TGewA7OJyX3a
-HSlSDqaremmi+aS3onx3AKhXykDxTRkMVarePwTzzFs=',
- 'translations'
- => [
- ],
- ],
- ],
- 'screenshots'
- => [
- 0
- => [
- 'url' => 'https://raw.githubusercontent.com/janis91/ocr/master/screenshots/sc1.png',
- ],
- 1
- => [
- 'url' => 'https://raw.githubusercontent.com/janis91/ocr/master/screenshots/sc2.png',
- ],
- 2
- => [
- 'url' => 'https://raw.githubusercontent.com/janis91/ocr/master/screenshots/sc3.png',
- ],
- ],
- 'translations'
- => [
- 'en'
- => [
- 'name' => 'OCR',
- 'summary' => 'Character recoginition for your images and pdf files.',
- 'description' => '# Description
-
-Nextcloud OCR (optical character recoginition) processing for images and PDF with tesseract-ocr and OCRmyPDF brings OCR capability to your Nextcloud 10.
-The app uses tesseract-ocr, OCRmyPDF and a php internal message queueing service in order to process images (png, jpeg, tiff) and PDF (currently not all PDF-types are supported, for more information see [here](https://github.com/jbarlow83/OCRmyPDF)) asynchronously and save the output file to the same folder in nextcloud, so you are able to search in it.
-The source data won't get lost. Instead:
- - in case of a PDF a copy will be saved with an extra layer of the processed text, so that you are able to search in it.
- - in case of a image the result of the OCR processing will be saved in a .txt file next to the image (same folder).
-
-**One big feature is the asynchronous ocr processing brought by the internal php message queueing system (Semaphore functions), which supports workers to handle tasks asynchronous from the rest of nextcloud.**
-
-## Prerequisites, Requirements and Dependencies
-The OCR app has some prerequisites:
- - **[Nextcloud 10](https://nextcloud.com/)** or higher
- - **Linux** server as environment. (tested with Debian 8, Raspbian and Ubuntu 14.04 (Trusty))
- - **[OCRmyPDF](https://github.com/jbarlow83/OCRmyPDF)** >v2.x (tested with v4.1.3 (v4 is recommended))
- - **[tesseract-ocr](https://github.com/tesseract-ocr/tesseract)** >v3.02.02 with corresponding language files (e.g. tesseract-ocr-eng)
-
-For further information see the homepage or the appropriate documentation.',
- ],
- 'de'
- => [
- 'name' => 'OCR',
- 'summary' => 'Schrifterkennung für Bilder (mit Text) und PDF Dateien.',
- 'description' => '# Beschreibung
-
-OCR (Automatische Texterkennung) für Bilder (mit Text) und PDF Dateien mithilfe von tesseract-ocr und OCRmyPDF ermöglicht Ihnen automatische Schrifterkennung direkt in Ihrer Nextcloud 10.
-Die App nutzt Tesseract-ocr, OCRmyPDF und den internen Message Queueing Service von PHP, um so asynchron (im Hintegrund) Bilder (PNG, JPEG, TIFF) und PDFs (aktuell werden nicht alle Typen unterstützt, näheres [hier](https://github.com/jbarlow83/OCRmyPDF)) zu verarbeiten. Das Ergebnis, welches jetzt durchsuchbar, kopierbar und ähnliches ist, wird anschließend im selben Ordner gespeichert, wie die Ursprungsdatei.
-Die Ursuprungsdatei geht dabei nicht verloren:
- - im Falle einer PDF wird eine Kopie mit einer zusätzlichen Textebene gespeichert, damit sie durchsuchbar und kopierbar wird.
- - im Falle eines Bildes wird das Resultat in einer txt-Datei gespeichert.
-
-**Ein großer Vorteil ist, dass das Ausführen und Verarbeiten asynchron im Hintergrund stattfindet. Dies geschieht mithilfe der PHP internernen Unterstützung einer Message Queue (Semaphore Funktionen). Die Aufgaben werden somit getrennt von der Nextcloud in einem eigenen Arbeits-Prozess (Worker) abgearbeitet.**
-
-## Anforderungen und Abhängigkeiten
-Für die OCR App müssen folgende Anforderungen erfüllt sein:
- - **[Nextcloud 10](https://nextcloud.com/)** oder höher
- - **Linux** server als Betriebssystem. (getestet mit Debian 8, Raspbian und Ubuntu 14.04 (Trusty))
- - **[OCRmyPDF](https://github.com/jbarlow83/OCRmyPDF)** >v2.x (getestet mit v4.1.3 (v4 empfohlen))
- - **[tesseract-ocr](https://github.com/tesseract-ocr/tesseract)** >v3.02.02 mit den dazugehörigen Übersetzungs- und Sprachdateien (z. B. tesseract-ocr-deu)
-
-Für weiter Informationen besuchen Sie die Homepage oder lesen Sie die zutreffende Dokumentation.',
- ],
- ],
- 'isFeatured' => false,
- 'authors'
- => [
- 0
- => [
- 'name' => 'Janis Koehr',
- 'mail' => '',
- 'homepage' => '',
- ],
- ],
- 'ratingRecent' => 0.5,
- 'ratingOverall' => 0.5,
- 'ratingNumRecent' => 0,
- 'ratingNumOverall' => 0,
- 'certificate' => '-----BEGIN CERTIFICATE-----
-MIID/jCCAuYCAhAKMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
-VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
-MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
-dXRob3JpdHkwHhcNMTYwOTE5MTEzNTAxWhcNMjYxMjI2MTEzNTAxWjAOMQwwCgYD
-VQQDDANvY3IwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDDpXiwec4f
-XAT//7YBPSb4z6ZsBJSMfBq0VTL/HagjJnQ7BL+WagzWlS69IStNDqlIlenamYRX
-4B40heJIUinzYKjTRbr5UAw6MX29HibZccm/qgrk36o1XTgIsoRhmvSxbXDVIo1k
-bDOJN8gc2Gvswa8X+uOe9pfcDgAdqGxOvFnoKW89GnB01pCNT+xakNErGAFqVLsr
-2AeademAZnbxJ1cB54tQn2Bygb/7DKKY8EmFfIq6/27n9Jbph1FG9HIlWRT4/M2H
-U2pG3cCScWMEBPsW7kpfpnzLk7Q30Oj6k/rEYjJgmNYgg6oVnn0D9uRmhBYBnGyx
-Mab1ilsK53lyuzQY0pmU8V5ULqpnNFAK6DVFfofEamDUhBPO+TZXEA5cZmuULRpf
-QQXmGpUQSyV6pS9WirMIqXFp9wmQ4vtjMdhu/6CP7cmtYZdq9uOhWEHbQM0mZUkb
-8hMjeItPx9XITI7Cge1JUOI8ZIwiB3USnQXcMd3v82l++/VgqHB7s5OaKPhygsWI
-M6RCoBcGiuQB5/fEUOg5ACOpGVyJiBda0Mi57AdoxdJmfnr7Bxcf2tAWIJL9Y7T3
-E1+V2BMxJOWwvVz26Cq83F41yXK2hJS+SbfQTqNUR8Cfh50CS9POvgRxNrJK9yvI
-kKle3ITRtGVM1XU0njWjnsdGg3D3O2mmjQIDAQABMA0GCSqGSIb3DQEBCwUAA4IB
-AQAbFddMbgfPI1szT57V1FKZrOrdYqQ7qjewlIQOzshGydbMtqS/9XL5hYocJCMt
-Y6w+C/i6iEzO2Jx8D/k4rcZMXoVR6y3ZvO0Ke0gzSRsU+5eYj2FK1VV+cNIQW5Iu
-CYYIVa7pVPVHdeQH2Bba680bLV0HMF6b1fI9IwkfdCAinvCYZLjyEXZlmB7YjyA8
-HR7qPCNz4uG2Va7mlUHE3UYUYnlv8JFOV3YdbVL0nxhWwIdzSri5sxFIhdlabpzY
-yA1z/MCBEyTRo80jxFmL+MpwbsdbUJi7Qxlnd56zb6HHDGrLHXZTh9LXgyVbnhWL
-kxomWjIXQh4aMHQL4QF7U4EK
------END CERTIFICATE-----',
- ],
- 14
- => [
- 'id' => 'spreedme',
- 'categories'
- => [
- 0 => 'tools',
- ],
- 'userDocs' => 'https://github.com/strukturag/nextcloud-spreedme/blob/master/README.md',
- 'adminDocs' => 'https://github.com/strukturag/nextcloud-spreedme/blob/master/README.md',
- 'developerDocs' => '',
- 'issueTracker' => 'https://github.com/strukturag/nextcloud-spreedme/issues',
- 'website' => '',
- 'created' => '2016-09-27T08:43:07.835196Z',
- 'lastModified' => '2016-11-21T16:51:23.703819Z',
- 'releases'
- => [
- 0
- => [
- 'version' => '0.3.4',
- 'phpExtensions'
- => [
- ],
- 'databases'
- => [
- ],
- 'shellCommands'
- => [
- ],
- 'phpVersionSpec' => '*',
- 'platformVersionSpec' => '>=9.0.0 <12.0.0',
- 'minIntSize' => 32,
- 'download' => 'https://apps.owncloud.com/CONTENT/content-files/174436-spreedme.tar.gz',
- 'created' => '2016-11-21T16:51:23.689599Z',
- 'licenses'
- => [
- 0 => 'agpl',
- ],
- 'lastModified' => '2016-11-21T16:51:23.826509Z',
- 'isNightly' => false,
- 'rawPhpVersionSpec' => '*',
- 'rawPlatformVersionSpec' => '>=9 <=11',
- 'signature' => 'Mhy3hXeGWlIujx1Op39MMRdqHYOo360BCwr4FPWoTNNggH3aS0gWlh48DAfGYK9W
-etNiOqIuRyA0NrVlsqR2vDILgFtODJSbKPyHd3PQn3hcGsjogjQ+dkKciLNLinw7
-Ohbv6aDdRFLBeRHpX/7wOnWL5W3ko/gyn0Awvi88M9+nC5aARtqncQqPy2SxDGzH
-KlOZHSNDnEQCGMhA8hNWWKdVwNUJHod/wmBWpW5QVNSJq5DqrKZcNwpGM2UUJoql
-EqUMwDXk5uVH5r5k62Tr9kguDWoUEG1OqQSyeMY24AmA64tq/HSlAdZ+CX32bc4E
-Zvm+n8poJBrdSVmWEaa4ZfYaLFdOc6Kcuid1B1Sv9kPhD9WD6T1sicdzjDzcorBK
-/MLReCuSb2E8aPTnFWRoAZ4xCUGs1IXzX5fmxI8VdzwR42R6RhGJ/rqMuZRFenZF
-bOks45K5gE1da4QpkYOUQa3GVMNPqPiT3CqjmJ8tjxq7bGpb6v+YoCLACjjPpPZL
-2Y28qLxwHVaINDFUUxD75WWdrlulRbqHwiSw8jolP9qrpXhDuLAqYam9tRwV5K5R
-8uNawnFwWkicBEYkN/WtBTouWzehOPn38tHXov6SyEyD6lkuxUBZrsGQ2ru+t33U
-k0kKCbV0GFw43I+3Ji5DiB4TUVNZYVoPG1B7Qve+UfA=',
- 'translations'
- => [
- ],
- ],
- ],
- 'screenshots'
- => [
- 0
- => [
- 'url' => 'https://raw.githubusercontent.com/strukturag/nextcloud-spreedme/master/screenshots/appstore/conference.gif',
- ],
- 1
- => [
- 'url' => 'https://raw.githubusercontent.com/strukturag/nextcloud-spreedme/master/screenshots/appstore/presentation.png',
- ],
- 2
- => [
- 'url' => 'https://raw.githubusercontent.com/strukturag/nextcloud-spreedme/master/screenshots/appstore/import.png',
- ],
- 3
- => [
- 'url' => 'https://raw.githubusercontent.com/strukturag/nextcloud-spreedme/master/screenshots/appstore/users.png',
- ],
- ],
- 'translations'
- => [
- 'en'
- => [
- 'name' => 'Spreed.ME',
- 'summary' => 'Audio-, video- and text chat for your Nextcloud',
- 'description' => 'Securely communicate with your friends and family using rich audio-, video- and text chat, and much more right from your Nextcloud – in your browser',
- ],
- ],
- 'isFeatured' => false,
- 'authors'
- => [
- 0
- => [
- 'name' => 'struktur AG',
- 'mail' => '',
- 'homepage' => '',
- ],
- ],
- 'ratingRecent' => 0.5,
- 'ratingOverall' => 0.5,
- 'ratingNumRecent' => 0,
- 'ratingNumOverall' => 0,
- 'certificate' => '-----BEGIN CERTIFICATE-----
-MIIEAzCCAusCAhANMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
-VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
-MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
-dXRob3JpdHkwHhcNMTYwOTI2MTYxNzMzWhcNMjcwMTAyMTYxNzMzWjATMREwDwYD
-VQQDEwhzcHJlZWRtZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKLx
-2dCPBLIgX948BnOdLij0YyI2+FKD6uZOvzxMaoi3rlxNf8MJgraNMzTBWEXtxT5b
-7ZISNp89WEXhaQ1dwwCocodd/xow4Ek63m5nUvTZXsm+YSbMgrFbxzsBhYU7KuIE
-T/jhKdzYgemzErwwN/gtwkLMfPo3jkgg6c8NPPohYv6k7V4VnsqtJ0JS0kX19FqM
-MiNz9XkcncBHy9x0BSxy4+YnwbFcgIx/MtYKlBL8NkPuuJaB/6C1O+IPYhdEdnpX
-+RaIue71nSStOYOqT4YDqHAIw7EmqgA1my09mmK+0Pn92GJVEAEN7JGBSQ+F32RI
-dB3ivGAOVtUtVvJlepWdbHxj1xqeP+LCjWzHMLQjm0TyH8VqU4Cg/wxwAEFnBATH
-aOaWwrggzY2d9KBo1mp0k71NArLbBdlHykFU4bgiSDWrXXMz0fZzLQVwGI0Eqcxc
-ouf6t0kvrK8oKjrnso+FjBoT7lHV/H6ny4ufxIEDAJ/FEBV/gMizt5fDZ+DvmMw4
-q+a088/lXoiI/vWPoGfOa77H5BQOt3y70Pmwv2uVYp46dtU8oat+ZvyW9iMmgP1h
-JSEHj1WGGGlp45d10l4OghwfTB0OSuPUYwWR+lZnV8sukGvQzC9iRV1DGl/rREMC
-cQ5ajRAtO5NPnThvN5/Zuh4n8JoDc0GK4jEZsIivAgMBAAEwDQYJKoZIhvcNAQEL
-BQADggEBAGHMRbPV0WTI9r1w6m2iJRrMbZtbBb+mQr8NtOoXQwvSXWT1lXMP2N8u
-LQ1a8U5UaUjeg7TnoUWTEOqU05HpwA8GZtdWZqPPQpe691kMNvfqF64g0le2kzOL
-huMP9kpDGzSD8pEKf1ihxvEWNUBmwewrZTC3+b4gM+MJ3BBCfb5SCzMURLirfFST
-axCNzc7veb2M98hS73w5ZE6vO+C/wz0GTsxuK0AoLitApT5naQnjvxSvSsjFPEGD
-sUNUEU2Decyp0jxLVnrrpz6Y5UupfBR0V8yAv1t5Od/mCKLc5DxHsDWiKOpsob9U
-JN+bdzJil2NNftihD4Dm7Ha7OS3O8W0=
------END CERTIFICATE-----',
- ],
- 15
- => [
- 'id' => 'nextant',
- 'categories'
- => [
- 0 => 'files',
- 1 => 'tools',
- ],
- 'userDocs' => '',
- 'adminDocs' => 'https://github.com/nextcloud/nextant/wiki',
- 'developerDocs' => '',
- 'issueTracker' => 'https://github.com/nextcloud/nextant/issues',
- 'website' => 'https://github.com/nextcloud/nextant/wiki',
- 'created' => '2016-09-14T14:34:35.977699Z',
- 'lastModified' => '2016-11-22T16:02:57.758477Z',
- 'releases'
- => [
- 0
- => [
- 'version' => '0.6.6',
- 'phpExtensions'
- => [
- ],
- 'databases'
- => [
- ],
- 'shellCommands'
- => [
- ],
- 'phpVersionSpec' => '*',
- 'platformVersionSpec' => '>=9.0.0 <12.0.0',
- 'minIntSize' => 32,
- 'download' => 'https://github.com/nextcloud/nextant/releases/download/v0.6.6/nextant-0.6.6.tar.gz',
- 'created' => '2016-11-16T15:11:14.344704Z',
- 'licenses'
- => [
- 0 => 'agpl',
- ],
- 'lastModified' => '2016-11-16T20:39:59.030384Z',
- 'isNightly' => false,
- 'rawPhpVersionSpec' => '*',
- 'rawPlatformVersionSpec' => '>=9 <=11',
- 'signature' => 'aOZeEeThyZ0V/vXBcn6c+Z0vyCsZcN6nfSJ8oWEea4zXh4g705Si+MFZESqix3M2
-OPCnA/U8eASwdRTAEwQJrW5ECmu1THXSIsrzQzc9kFycvyOGzCgAWtuu0ayzZD2/
-U5aDWlzpLHC1Czg9QJ5UnfZR0AfChWQ402N1YzGqMShdJv6AHXFrVE+uYnIyxuYI
-oPJQBUYbQwthVUjpYwFwSxw50YU17gmx5RZ0Y0OPz3i/EiuEUrxopXtfDVYAuCML
-pDw37LOTRQ2JqxSU3teALh8LcrwJbTeOP0n4bTeV+vU3jvtiaEoRrwfVrK41F701
-QymGXy1/EFG0kxPGS2dRNPBAXYLZfeoWlROl3D5BWlbsCcXKU1S+22yn0TEdS7x1
-Y44x8jRKnBddDE7qkn+QoQYHNNcxOREsFFLmIoyCUpdNOdDX2PvTFUYkIqdnXaJy
-oAKv2GkvWPQ0aiiBtA1i4oXuzvHW/M2wOrK7v7DCpNfILrD/sjxpljxcX082nRCd
-9P3iPd2hQ6yOM9fG21LVN74b6wggI81BzFf/xJPd4ZqYLjfeG/yqd0zaiMOzMm1W
-se+kc/a4iB3BoCNX3E942pBBzew4ya8LkCXdCHUUsuelDf1va1ikTh/G7D84ll9/
-2avNqQnUh3hgOnxFCLI/5VrbqxfSTVdO6O/LTuAmwgw=',
- 'translations'
- => [
- ],
- ],
- ],
- 'screenshots'
- => [
- 0
- => [
- 'url' => 'https://raw.githubusercontent.com/nextcloud/nextant/master/screenshots/displayResult.jpg',
- ],
- 1
- => [
- 'url' => 'https://raw.githubusercontent.com/nextcloud/nextant/master/screenshots/admin.jpg',
- ],
- ],
- 'translations'
- => [
- 'en'
- => [
- 'name' => 'Nextant',
- 'summary' => 'Navigate through your cloud using Solr',
- 'description' => '
- Navigate through your cloud using Solr
-
-
-**Nextant** performs fast and concise _Full-Text Search_ within:
-
-- your own files,
-- shared files,
-- external storage,
-- bookmarks
-
-
-### Recognized file format:
-- plain text,
-- rtf,
-- pdf,
-- html,
-- openoffice,
-- microsoft office,
-- image JPEG and TIFF (will requiert Tesseract installed)
-- pdf with no text layer (will also requiert Tesseract) _[work in progress]_
-
-
-
-## Installation
-
-- [You first need to install a Solr servlet](https://github.com/nextcloud/nextant/wiki)
-- Download the .zip from the appstore, unzip and place this app in **nextcloud/apps/** (or clone the github and build the app yourself)
-- Enable the app in the app list,
-- Edit the settings in the administration page.
-- Extract the current files from your cloud using the **./occ nextant:index** commands
-- Have a look to this [explanation on how Nextant works](https://github.com/nextcloud/nextant/wiki/Extracting,-Live-Update)
-- _(Optional)_ [Installing Tesseract](https://github.com/tesseract-ocr/tesseract/wiki) ([Optical Character Recognition](https://en.wikipedia.org/wiki/Optical_character_recognition) (OCR) Engine) will allow Nextant to extract text from images and pdfs without text layer.
-
-
- ',
- ],
- ],
- 'isFeatured' => false,
- 'authors'
- => [
- 0
- => [
- 'name' => 'Maxence Lange',
- 'mail' => '',
- 'homepage' => '',
- ],
- ],
- 'ratingRecent' => 0.5,
- 'ratingOverall' => 0.5,
- 'ratingNumRecent' => 0,
- 'ratingNumOverall' => 0,
- 'certificate' => '-----BEGIN CERTIFICATE-----
-MIIEAjCCAuoCAhAFMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
-VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
-MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
-dXRob3JpdHkwHhcNMTYwOTE0MTI1NDQwWhcNMjYxMjIxMTI1NDQwWjASMRAwDgYD
-VQQDDAduZXh0YW50MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsbnQ
-+9acjKHfcrUj4yqBpD++GmQ5z2Sp8C8uOz4ZbLyM9bUXEYHo4a4u3CdC49kGUkb3
-p5MkEAEzslTWDi1eh5MZgPWpbPgItsDsXY1o55O3jtxNkzSG5/yYcPQcuKtIOm9S
-7DY0K+UQt3nK+RrXEZfARMNrzFbEzpE3b7w901Yl5n+m/B8rhW4pqg8uSfx3u04J
-wduV1fHwoHUB0Ox5HyWib4Pq1XppNh7xdc2Fg93JxshwuCPJyOOzrFTnxC7s1yzQ
-UvaqkjPW5QeQRunQjZ2XtpYH8f8v01W18bNEiHwqtFwuDEyCVx1rvEMgUDVXdPkP
-gZrlB5TzGmz0U3HzYvf6205WuzfHxz7kPj502wP51PoZBKpniggKzmuXkx6BpsZC
-ZX45VpDHdiATLwRj1t2bMs3C01nzpIWO5ZwFtkepH3Y+mvwX5lDh/XDsqJC2Yo8o
-WMmniWNW7dspufYOsBUqqYGP7rkailgVT4oYk6D1j6oFZ5SSpfPF5lsyYedDSM6y
-bIGVkSF+sjLK6R9BenBijKceAKsS//WwRYCBPC+JHlsYpXKW12bL+C47Kj2/N6d4
-rYryzV6ofVSF6pwIq0oEjoyfBfNpYavf3xrRkSSmIIlPSnMY7DT1xkGD5retxSm6
-+WIfkWKRZpv2S6PhMHGLspYc4H5Dj8c48rG5Co8CAwEAATANBgkqhkiG9w0BAQsF
-AAOCAQEAOZUwyPaUi+1BOUgQJMWqYRoTVZUyBshTXSC7jSwa97b7qADV9ooA6TYF
-zgsPcE41k7jRkUbnjcY45RGtL3vqsgZbx5TjPa5fGMxlqJ6eYBOY61Q6VIHEVm3u
-xnPEO9dsMoDBijvo5D7KtE+Ccs907Rq70kCsbrdgPHkyb5tDSnCKogN1LiQrg1EP
-my7Z1C7jG9/h57vx0+QBMDCYnTmqLsvMKqo27uHskzAiB7VXLEdSZ2FtMGHkLUQO
-0bfhnvTZ2VhMmK83t7ovo71An4ycmsolGD/MA0vNI78VrVISrdI8rRh2WntUnCBU
-EJL3BaQAQaASSsvFrcozYxrQG4VzEg==
------END CERTIFICATE-----',
- ],
- ],
- 'timestamp' => 1234,
- 'ncversion' => '11.0.0.2',
- 'ETag' => '"myETag"',
- ];
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- /** @var Factory|\PHPUnit\Framework\MockObject\MockObject $factory */
- $factory = $this->createMock(Factory::class);
- $this->appData = $this->createMock(AppData::class);
- $factory->expects($this->once())
- ->method('get')
- ->with('appstore')
- ->willReturn($this->appData);
- $this->clientService = $this->createMock(IClientService::class);
- $this->timeFactory = $this->createMock(ITimeFactory::class);
- $this->config = $this->createMock(IConfig::class);
- $this->compareVersion = new CompareVersion();
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->registry = $this->createMock(IRegistry::class);
-
- $this->fetcher = $this->getMockBuilder(AppFetcher::class)
- ->onlyMethods(['getChannel'])
- ->setConstructorArgs([
- $factory,
- $this->clientService,
- $this->timeFactory,
- $this->config,
- $this->compareVersion,
- $this->logger,
- $this->registry,
- ])
- ->getMock();
-
- $this->fetcher->method('getChannel')
- ->willReturn('stable');
- }
-
- public function testGetWithFilter(): void {
- $this->config->method('getSystemValueString')
- ->willReturnCallback(function ($key, $default) {
- if ($key === 'version') {
- return '11.0.0.2';
- } elseif ($key === 'appstoreurl' && $default === 'https://apps.nextcloud.com/api/v1') {
- return 'https://custom.appsstore.endpoint/api/v1';
- } else {
- return $default;
- }
- });
- $this->config
- ->method('getSystemValueBool')
- ->willReturnArgument(1);
-
- $file = $this->createMock(ISimpleFile::class);
- $folder = $this->createMock(ISimpleFolder::class);
- $folder
- ->expects($this->once())
- ->method('getFile')
- ->with('apps.json')
- ->willThrowException(new NotFoundException());
- $folder
- ->expects($this->once())
- ->method('newFile')
- ->with('apps.json')
- ->willReturn($file);
- $this->appData
- ->expects($this->once())
- ->method('getFolder')
- ->with('/')
- ->willReturn($folder);
- $client = $this->createMock(IClient::class);
- $this->clientService
- ->expects($this->once())
- ->method('newClient')
- ->willReturn($client);
- $response = $this->createMock(IResponse::class);
- $client
- ->method('get')
- ->with('https://custom.appsstore.endpoint/api/v1/apps.json')
- ->willReturn($response);
- $response
- ->expects($this->once())
- ->method('getBody')
- ->willReturn(self::$responseJson);
- $response->method('getHeader')
- ->with($this->equalTo('ETag'))
- ->willReturn('"myETag"');
- $this->timeFactory
- ->expects($this->once())
- ->method('getTime')
- ->willReturn(1234);
-
- $dataToPut = self::$expectedResponse;
- $dataToPut['data'] = array_values($dataToPut['data']);
- $originalArray = json_decode(self::$responseJson, true);
- $mappedOriginalArray = [];
- foreach ($originalArray as $key => $value) {
- foreach ($value as $releaseKey => $releaseValue) {
- if ($releaseKey === 'id') {
- $mappedOriginalArray[$releaseValue] = $originalArray[$key];
- }
- }
- }
- foreach ($dataToPut['data'] as $key => $appValue) {
- foreach ($appValue as $appKey => $value) {
- if ($appKey === 'certificate' || $appKey === 'description') {
- $dataToPut['data'][$key][$appKey] = $mappedOriginalArray[$appValue['id']][$appKey];
- }
- }
- }
-
- $file
- ->expects($this->once())
- ->method('putContent');
- $file
- ->method('getContent')
- ->willReturn(json_encode(self::$expectedResponse));
-
- $this->assertEquals(self::$expectedResponse['data'], $this->fetcher->get());
- }
-
- public function testAppstoreDisabled(): void {
- $this->config
- ->method('getSystemValueString')
- ->willReturnCallback(function ($var, $default) {
- if ($var === 'version') {
- return '11.0.0.2';
- }
- return $default;
- });
- $this->config
- ->method('getSystemValueBool')
- ->willReturnCallback(function ($var, $default) {
- if ($var === 'has_internet_connection') {
- return true;
- } elseif ($var === 'appstoreenabled') {
- return false;
- }
- return $default;
- });
- $this->appData
- ->expects($this->never())
- ->method('getFolder');
-
- $this->assertEquals([], $this->fetcher->get());
- }
-
- public function testNoInternet(): void {
- $this->config
- ->method('getSystemValueString')
- ->willReturnCallback(function ($var, $default) {
- if ($var === 'has_internet_connection') {
- return false;
- } elseif ($var === 'version') {
- return '11.0.0.2';
- }
- return $default;
- });
- $this->config
- ->method('getSystemValueBool')
- ->willReturnCallback(function ($var, $default) {
- if ($var === 'has_internet_connection') {
- return false;
- } elseif ($var === 'appstoreenabled') {
- return true;
- }
- return $default;
- });
- $this->appData
- ->expects($this->never())
- ->method('getFolder');
-
- $this->assertEquals([], $this->fetcher->get());
- }
-
- public function testSetVersion(): void {
- $this->config->method('getSystemValueString')
- ->willReturnCallback(function ($key, $default) {
- if ($key === 'version') {
- return '10.0.7.2';
- } elseif ($key === 'appstoreurl' && $default === 'https://apps.nextcloud.com/api/v1') {
- return 'https://custom.appsstore.endpoint/api/v1';
- } else {
- return $default;
- }
- });
- $this->config
- ->method('getSystemValueBool')
- ->willReturnArgument(1);
-
- $file = $this->createMock(ISimpleFile::class);
- $folder = $this->createMock(ISimpleFolder::class);
- $folder
- ->expects($this->once())
- ->method('getFile')
- ->with('future-apps.json')
- ->willThrowException(new NotFoundException());
- $folder
- ->expects($this->once())
- ->method('newFile')
- ->with('future-apps.json')
- ->willReturn($file);
- $this->appData
- ->expects($this->once())
- ->method('getFolder')
- ->with('/')
- ->willReturn($folder);
- $client = $this->createMock(IClient::class);
- $this->clientService
- ->expects($this->once())
- ->method('newClient')
- ->willReturn($client);
- $response = $this->createMock(IResponse::class);
- $client
- ->method('get')
- ->with('https://custom.appsstore.endpoint/api/v1/apps.json')
- ->willReturn($response);
- $response
- ->expects($this->once())
- ->method('getBody')
- ->willReturn(self::$responseJson);
- $response->method('getHeader')
- ->with($this->equalTo('ETag'))
- ->willReturn('"myETag"');
- $this->timeFactory
- ->expects($this->once())
- ->method('getTime')
- ->willReturn(1234);
-
- $dataToPut = self::$expectedResponse;
- $dataToPut['data'] = array_values($dataToPut['data']);
- $originalArray = json_decode(self::$responseJson, true);
- $mappedOriginalArray = [];
- foreach ($originalArray as $key => $value) {
- foreach ($value as $releaseKey => $releaseValue) {
- if ($releaseKey === 'id') {
- $mappedOriginalArray[$releaseValue] = $originalArray[$key];
- }
- }
- }
- foreach ($dataToPut['data'] as $key => $appValue) {
- foreach ($appValue as $appKey => $value) {
- if ($appKey === 'certificate' || $appKey === 'description') {
- $dataToPut['data'][$key][$appKey] = $mappedOriginalArray[$appValue['id']][$appKey];
- }
- }
- }
-
- $file
- ->expects($this->once())
- ->method('putContent');
- $file
- ->method('getContent')
- ->willReturn(json_encode(self::$expectedResponse));
-
- $this->fetcher->setVersion('11.0.0.2', 'future-apps.json', false);
- $this->assertEquals(self::$expectedResponse['data'], $this->fetcher->get());
- }
-
- public function testGetAppsAllowlist(): void {
- $this->config->method('getSystemValueString')
- ->willReturnCallback(function ($key, $default) {
- if ($key === 'version') {
- return '11.0.0.2';
- } else {
- return $default;
- }
- });
- $this->config->method('getSystemValue')
- ->willReturnCallback(function ($key, $default) {
- if ($key === 'appsallowlist') {
- return ['contacts'];
- }
- return $default;
- });
- $this->config->method('getAppValue')
- ->willReturnCallback(function ($app, $key, $default) {
- if ($app === 'support' && $key === 'subscription_key') {
- return 'subscription-key';
- }
- return $default;
- });
- $this->config
- ->method('getSystemValueBool')
- ->willReturnArgument(1);
-
- $file = $this->createMock(ISimpleFile::class);
- $folder = $this->createMock(ISimpleFolder::class);
- $folder
- ->expects($this->once())
- ->method('getFile')
- ->with('apps.json')
- ->willThrowException(new NotFoundException());
- $folder
- ->expects($this->once())
- ->method('newFile')
- ->with('apps.json')
- ->willReturn($file);
- $this->appData
- ->expects($this->once())
- ->method('getFolder')
- ->with('/')
- ->willReturn($folder);
- $client = $this->createMock(IClient::class);
- $this->clientService
- ->expects($this->once())
- ->method('newClient')
- ->willReturn($client);
- $response = $this->createMock(IResponse::class);
- $client
- ->expects($this->once())
- ->method('get')
- ->with('https://apps.nextcloud.com/api/v1/apps.json', [
- 'timeout' => 120,
- 'headers' => [
- 'X-NC-Subscription-Key' => 'subscription-key',
- ],
- ])
- ->willReturn($response);
- $response
- ->expects($this->once())
- ->method('getBody')
- ->willReturn(self::$responseJson);
- $response->method('getHeader')
- ->with($this->equalTo('ETag'))
- ->willReturn('"myETag"');
- $this->timeFactory
- ->expects($this->once())
- ->method('getTime')
- ->willReturn(1234);
-
- $this->registry
- ->expects($this->exactly(2))
- ->method('delegateHasValidSubscription')
- ->willReturn(true);
-
- $file
- ->expects($this->once())
- ->method('putContent');
- $file
- ->method('getContent')
- ->willReturn(json_encode(self::$expectedResponse));
-
- $apps = array_values($this->fetcher->get());
- $this->assertEquals(count($apps), 1);
- $this->assertEquals($apps[0]['id'], 'contacts');
- }
-
- public function testGetAppsAllowlistCustomAppstore(): void {
- $this->config->method('getSystemValueString')
- ->willReturnCallback(function ($key, $default) {
- if ($key === 'version') {
- return '11.0.0.2';
- } elseif ($key === 'appstoreurl' && $default === 'https://apps.nextcloud.com/api/v1') {
- return 'https://custom.appsstore.endpoint/api/v1';
- } else {
- return $default;
- }
- });
- $this->config->method('getSystemValue')
- ->willReturnCallback(function ($key, $default) {
- if ($key === 'appsallowlist') {
- return ['contacts'];
- } else {
- return $default;
- }
- });
- $this->config
- ->method('getSystemValueBool')
- ->willReturnArgument(1);
-
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['settings', 'appstore-fetcher-lastFailure', '0', '0'],
- ['settings', 'appstore-timeout', '120', '120'],
- ]);
-
- $file = $this->createMock(ISimpleFile::class);
- $folder = $this->createMock(ISimpleFolder::class);
- $folder
- ->expects($this->once())
- ->method('getFile')
- ->with('apps.json')
- ->willThrowException(new NotFoundException());
- $folder
- ->expects($this->once())
- ->method('newFile')
- ->with('apps.json')
- ->willReturn($file);
- $this->appData
- ->expects($this->once())
- ->method('getFolder')
- ->with('/')
- ->willReturn($folder);
- $client = $this->createMock(IClient::class);
- $this->clientService
- ->expects($this->once())
- ->method('newClient')
- ->willReturn($client);
- $response = $this->createMock(IResponse::class);
- $client
- ->expects($this->once())
- ->method('get')
- ->with('https://custom.appsstore.endpoint/api/v1/apps.json', [
- 'timeout' => 120,
- ])
- ->willReturn($response);
- $response
- ->expects($this->once())
- ->method('getBody')
- ->willReturn(self::$responseJson);
- $response->method('getHeader')
- ->with($this->equalTo('ETag'))
- ->willReturn('"myETag"');
- $this->timeFactory
- ->expects($this->once())
- ->method('getTime')
- ->willReturn(1234);
-
- $this->registry
- ->expects($this->exactly(1))
- ->method('delegateHasValidSubscription')
- ->willReturn(true);
-
- $file
- ->expects($this->once())
- ->method('putContent');
- $file
- ->method('getContent')
- ->willReturn(json_encode(self::$expectedResponse));
-
- $apps = array_values($this->fetcher->get());
- $this->assertEquals(count($apps), 1);
- $this->assertEquals($apps[0]['id'], 'contacts');
- }
-}
--- a/tests/lib/App/AppStore/Fetcher/CategoryFetcherTest.php
+++ /dev/null
@@ -1,67 +0,0 @@
-fileName = 'categories.json';
- $this->endpoint = 'https://apps.nextcloud.com/api/v1/categories.json';
-
- $this->fetcher = new CategoryFetcher(
- $this->appDataFactory,
- $this->clientService,
- $this->timeFactory,
- $this->config,
- $this->logger,
- $this->registry
- );
- }
-
- public function testAppstoreDisabled(): void {
- $this->config
- ->method('getSystemValueBool')
- ->willReturnCallback(function ($var, $default) {
- if ($var === 'appstoreenabled') {
- return false;
- }
- return $default;
- });
- $this->appData
- ->expects($this->never())
- ->method('getFolder');
-
- $this->assertEquals([], $this->fetcher->get());
- }
-
- public function testNoInternet(): void {
- $this->config
- ->method('getSystemValueBool')
- ->willReturnCallback(function ($var, $default) {
- if ($var === 'has_internet_connection') {
- return false;
- }
- return $default;
- });
- $this->config
- ->method('getSystemValueString')
- ->willReturnCallback(function ($var, $default) {
- return $default;
- });
- $this->appData
- ->expects($this->never())
- ->method('getFolder');
-
- $this->assertEquals([], $this->fetcher->get());
- }
-}
--- a/tests/lib/App/AppStore/Fetcher/FetcherBase.php
+++ /dev/null
@@ -1,772 +0,0 @@
-appDataFactory = $this->createMock(Factory::class);
- $this->appData = $this->createMock(AppData::class);
- $this->appDataFactory->expects($this->once())
- ->method('get')
- ->with('appstore')
- ->willReturn($this->appData);
- $this->clientService = $this->createMock(IClientService::class);
- $this->timeFactory = $this->createMock(ITimeFactory::class);
- $this->config = $this->createMock(IConfig::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->registry = $this->createMock(IRegistry::class);
- }
-
- public function testGetWithAlreadyExistingFileAndUpToDateTimestampAndVersion(): void {
- $this->config
- ->method('getSystemValueString')
- ->willReturnCallback(function ($var, $default) {
- if ($var === 'version') {
- return '11.0.0.2';
- }
- return $default;
- });
- $this->config->method('getSystemValueBool')
- ->willReturnArgument(1);
-
- $folder = $this->createMock(ISimpleFolder::class);
- $file = $this->createMock(ISimpleFile::class);
- $this->appData
- ->expects($this->once())
- ->method('getFolder')
- ->with('/')
- ->willReturn($folder);
- $folder
- ->expects($this->once())
- ->method('getFile')
- ->with($this->fileName)
- ->willReturn($file);
- $file
- ->expects($this->once())
- ->method('getContent')
- ->willReturn('{"timestamp":1200,"data":[{"id":"MyApp"}],"ncversion":"11.0.0.2"}');
- $this->timeFactory
- ->expects($this->once())
- ->method('getTime')
- ->willReturn(1499);
-
- $expected = [
- [
- 'id' => 'MyApp',
- ],
- ];
- $this->assertSame($expected, $this->fetcher->get());
- }
-
- public function testGetWithNotExistingFileAndUpToDateTimestampAndVersion(): void {
- $this->config
- ->method('getSystemValueString')
- ->willReturnCallback(function ($var, $default) {
- if ($var === 'appstoreurl') {
- return 'https://apps.nextcloud.com/api/v1';
- } elseif ($var === 'version') {
- return '11.0.0.2';
- }
- return $default;
- });
- $this->config->method('getSystemValueBool')
- ->willReturnArgument(1);
-
- $folder = $this->createMock(ISimpleFolder::class);
- $file = $this->createMock(ISimpleFile::class);
- $this->appData
- ->expects($this->once())
- ->method('getFolder')
- ->with('/')
- ->willReturn($folder);
- $folder
- ->expects($this->once())
- ->method('getFile')
- ->with($this->fileName)
- ->willThrowException(new NotFoundException());
- $folder
- ->expects($this->once())
- ->method('newFile')
- ->with($this->fileName)
- ->willReturn($file);
- $client = $this->createMock(IClient::class);
- $this->clientService
- ->expects($this->once())
- ->method('newClient')
- ->willReturn($client);
- $response = $this->createMock(IResponse::class);
- $client
- ->expects($this->once())
- ->method('get')
- ->with($this->endpoint)
- ->willReturn($response);
- $response
- ->expects($this->once())
- ->method('getBody')
- ->willReturn('[{"id":"MyNewApp", "foo": "foo"}, {"id":"bar"}]');
- $response->method('getHeader')
- ->with($this->equalTo('ETag'))
- ->willReturn('"myETag"');
- $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1502,"ncversion":"11.0.0.2","ETag":"\"myETag\""}';
- $file
- ->expects($this->once())
- ->method('putContent')
- ->with($fileData);
- $file
- ->expects($this->once())
- ->method('getContent')
- ->willReturn($fileData);
- $this->timeFactory
- ->expects($this->once())
- ->method('getTime')
- ->willReturn(1502);
-
- $expected = [
- [
- 'id' => 'MyNewApp',
- 'foo' => 'foo',
- ],
- [
- 'id' => 'bar',
- ],
- ];
- $this->assertSame($expected, $this->fetcher->get());
- }
-
- public function testGetWithAlreadyExistingFileAndOutdatedTimestamp(): void {
- $this->config->method('getSystemValueString')
- ->willReturnCallback(function ($key, $default) {
- if ($key === 'version') {
- return '11.0.0.2';
- } else {
- return $default;
- }
- });
- $this->config->method('getSystemValueBool')
- ->willReturnArgument(1);
-
- $folder = $this->createMock(ISimpleFolder::class);
- $file = $this->createMock(ISimpleFile::class);
- $this->appData
- ->expects($this->once())
- ->method('getFolder')
- ->with('/')
- ->willReturn($folder);
- $folder
- ->expects($this->once())
- ->method('getFile')
- ->with($this->fileName)
- ->willReturn($file);
- $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1502,"ncversion":"11.0.0.2","ETag":"\"myETag\""}';
- $file
- ->expects($this->once())
- ->method('putContent')
- ->with($fileData);
- $file
- ->expects($this->exactly(2))
- ->method('getContent')
- ->willReturnOnConsecutiveCalls(
- '{"timestamp":1200,"data":{"MyApp":{"id":"MyApp"}},"ncversion":"11.0.0.2"}',
- $fileData
- );
- $this->timeFactory
- ->expects($this->exactly(2))
- ->method('getTime')
- ->willReturnOnConsecutiveCalls(
- 4801,
- 1502
- );
- $client = $this->createMock(IClient::class);
- $this->clientService
- ->expects($this->once())
- ->method('newClient')
- ->willReturn($client);
- $response = $this->createMock(IResponse::class);
- $client
- ->expects($this->once())
- ->method('get')
- ->with($this->endpoint)
- ->willReturn($response);
- $response
- ->expects($this->once())
- ->method('getBody')
- ->willReturn('[{"id":"MyNewApp", "foo": "foo"}, {"id":"bar"}]');
- $response->method('getHeader')
- ->with($this->equalTo('ETag'))
- ->willReturn('"myETag"');
-
- $expected = [
- [
- 'id' => 'MyNewApp',
- 'foo' => 'foo',
- ],
- [
- 'id' => 'bar',
- ],
- ];
- $this->assertSame($expected, $this->fetcher->get());
- }
-
- public function testGetWithAlreadyExistingFileAndNoVersion(): void {
- $this->config
- ->method('getSystemValueString')
- ->willReturnCallback(function ($var, $default) {
- if ($var === 'appstoreurl') {
- return 'https://apps.nextcloud.com/api/v1';
- } elseif ($var === 'version') {
- return '11.0.0.2';
- }
- return $default;
- });
- $this->config->method('getSystemValueBool')
- ->willReturnArgument(1);
-
- $folder = $this->createMock(ISimpleFolder::class);
- $file = $this->createMock(ISimpleFile::class);
- $this->appData
- ->expects($this->once())
- ->method('getFolder')
- ->with('/')
- ->willReturn($folder);
- $folder
- ->expects($this->once())
- ->method('getFile')
- ->with($this->fileName)
- ->willReturn($file);
- $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1201,"ncversion":"11.0.0.2","ETag":"\"myETag\""}';
- $file
- ->expects($this->once())
- ->method('putContent')
- ->with($fileData);
- $file
- ->expects($this->exactly(2))
- ->method('getContent')
- ->willReturnOnConsecutiveCalls(
- '{"timestamp":1200,"data":{"MyApp":{"id":"MyApp"}}',
- $fileData
- );
- $this->timeFactory
- ->expects($this->once())
- ->method('getTime')
- ->willReturn(1201);
- $client = $this->createMock(IClient::class);
- $this->clientService
- ->expects($this->once())
- ->method('newClient')
- ->willReturn($client);
- $response = $this->createMock(IResponse::class);
- $client
- ->expects($this->once())
- ->method('get')
- ->with($this->endpoint)
- ->willReturn($response);
- $response
- ->expects($this->once())
- ->method('getBody')
- ->willReturn('[{"id":"MyNewApp", "foo": "foo"}, {"id":"bar"}]');
- $response->method('getHeader')
- ->with($this->equalTo('ETag'))
- ->willReturn('"myETag"');
-
- $expected = [
- [
- 'id' => 'MyNewApp',
- 'foo' => 'foo',
- ],
- [
- 'id' => 'bar',
- ],
- ];
- $this->assertSame($expected, $this->fetcher->get());
- }
-
- public function testGetWithAlreadyExistingFileAndOutdatedVersion(): void {
- $this->config
- ->method('getSystemValueString')
- ->willReturnCallback(function ($var, $default) {
- if ($var === 'appstoreurl') {
- return 'https://apps.nextcloud.com/api/v1';
- } elseif ($var === 'version') {
- return '11.0.0.2';
- }
- return $default;
- });
- $this->config->method('getSystemValueBool')
- ->willReturnArgument(1);
-
- $folder = $this->createMock(ISimpleFolder::class);
- $file = $this->createMock(ISimpleFile::class);
- $this->appData
- ->expects($this->once())
- ->method('getFolder')
- ->with('/')
- ->willReturn($folder);
- $folder
- ->expects($this->once())
- ->method('getFile')
- ->with($this->fileName)
- ->willReturn($file);
- $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1201,"ncversion":"11.0.0.2","ETag":"\"myETag\""}';
- $file
- ->expects($this->once())
- ->method('putContent')
- ->with($fileData);
- $file
- ->expects($this->exactly(2))
- ->method('getContent')
- ->willReturnOnConsecutiveCalls(
- '{"timestamp":1200,"data":{"MyApp":{"id":"MyApp"}},"ncversion":"11.0.0.1"',
- $fileData
- );
- $this->timeFactory
- ->method('getTime')
- ->willReturn(1201);
- $client = $this->createMock(IClient::class);
- $this->clientService
- ->expects($this->once())
- ->method('newClient')
- ->willReturn($client);
- $response = $this->createMock(IResponse::class);
- $client
- ->expects($this->once())
- ->method('get')
- ->with($this->endpoint)
- ->willReturn($response);
- $response
- ->expects($this->once())
- ->method('getBody')
- ->willReturn('[{"id":"MyNewApp", "foo": "foo"}, {"id":"bar"}]');
- $response->method('getHeader')
- ->with($this->equalTo('ETag'))
- ->willReturn('"myETag"');
-
- $expected = [
- [
- 'id' => 'MyNewApp',
- 'foo' => 'foo',
- ],
- [
- 'id' => 'bar',
- ],
- ];
- $this->assertSame($expected, $this->fetcher->get());
- }
-
- public function testGetWithExceptionInClient(): void {
- $this->config->method('getSystemValueString')
- ->willReturnArgument(1);
- $this->config->method('getSystemValueBool')
- ->willReturnArgument(1);
-
- $folder = $this->createMock(ISimpleFolder::class);
- $file = $this->createMock(ISimpleFile::class);
- $this->appData
- ->expects($this->once())
- ->method('getFolder')
- ->with('/')
- ->willReturn($folder);
- $folder
- ->expects($this->once())
- ->method('getFile')
- ->with($this->fileName)
- ->willReturn($file);
- $file
- ->expects($this->once())
- ->method('getContent')
- ->willReturn('{"timestamp":1200,"data":{"MyApp":{"id":"MyApp"}}}');
- $client = $this->createMock(IClient::class);
- $this->clientService
- ->expects($this->once())
- ->method('newClient')
- ->willReturn($client);
- $client
- ->expects($this->once())
- ->method('get')
- ->with($this->endpoint)
- ->willThrowException(new \Exception());
-
- $this->assertSame([], $this->fetcher->get());
- }
-
- public function testGetMatchingETag(): void {
- $this->config->method('getSystemValueString')
- ->willReturnCallback(function ($key, $default) {
- if ($key === 'version') {
- return '11.0.0.2';
- } else {
- return $default;
- }
- });
- $this->config->method('getSystemValueBool')
- ->willReturnArgument(1);
-
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['settings', 'appstore-fetcher-lastFailure', '0', '0'],
- ['settings', 'appstore-timeout', '120', '120'],
- ]);
-
- $folder = $this->createMock(ISimpleFolder::class);
- $file = $this->createMock(ISimpleFile::class);
- $this->appData
- ->expects($this->once())
- ->method('getFolder')
- ->with('/')
- ->willReturn($folder);
- $folder
- ->expects($this->once())
- ->method('getFile')
- ->with($this->fileName)
- ->willReturn($file);
- $origData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1200,"ncversion":"11.0.0.2","ETag":"\"myETag\""}';
-
- $newData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":4802,"ncversion":"11.0.0.2","ETag":"\"myETag\""}';
- $file
- ->expects($this->once())
- ->method('putContent')
- ->with($newData);
- $file
- ->expects($this->exactly(2))
- ->method('getContent')
- ->willReturnOnConsecutiveCalls(
- $origData,
- $newData,
- );
- $this->timeFactory
- ->expects($this->exactly(2))
- ->method('getTime')
- ->willReturnOnConsecutiveCalls(
- 4801,
- 4802
- );
- $client = $this->createMock(IClient::class);
- $this->clientService
- ->expects($this->once())
- ->method('newClient')
- ->willReturn($client);
- $response = $this->createMock(IResponse::class);
- $client
- ->expects($this->once())
- ->method('get')
- ->with(
- $this->equalTo($this->endpoint),
- $this->equalTo([
- 'timeout' => 120,
- 'headers' => [
- 'If-None-Match' => '"myETag"'
- ]
- ])
- )->willReturn($response);
- $response->method('getStatusCode')
- ->willReturn(304);
-
- $expected = [
- [
- 'id' => 'MyNewApp',
- 'foo' => 'foo',
- ],
- [
- 'id' => 'bar',
- ],
- ];
-
- $this->assertSame($expected, $this->fetcher->get());
- }
-
- public function testGetNoMatchingETag(): void {
- $this->config->method('getSystemValueString')
- ->willReturnCallback(function ($key, $default) {
- if ($key === 'version') {
- return '11.0.0.2';
- } else {
- return $default;
- }
- });
- $this->config->method('getSystemValueBool')
- ->willReturnArgument(1);
-
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['settings', 'appstore-fetcher-lastFailure', '0', '0'],
- ['settings', 'appstore-timeout', '120', '120'],
- ]);
-
- $folder = $this->createMock(ISimpleFolder::class);
- $file = $this->createMock(ISimpleFile::class);
- $this->appData
- ->expects($this->once())
- ->method('getFolder')
- ->with('/')
- ->willReturn($folder);
- $folder
- ->expects($this->once())
- ->method('getFile')
- ->with($this->fileName)
- ->willReturn($file);
- $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":4802,"ncversion":"11.0.0.2","ETag":"\"newETag\""}';
- $file
- ->expects($this->once())
- ->method('putContent')
- ->with($fileData);
- $file
- ->expects($this->exactly(2))
- ->method('getContent')
- ->willReturnOnConsecutiveCalls(
- '{"data":[{"id":"MyOldApp","abc":"def"}],"timestamp":1200,"ncversion":"11.0.0.2","ETag":"\"myETag\""}',
- $fileData,
- );
- $this->timeFactory
- ->expects($this->exactly(2))
- ->method('getTime')
- ->willReturnOnConsecutiveCalls(
- 4801,
- 4802,
- );
- $client = $this->createMock(IClient::class);
- $this->clientService
- ->expects($this->once())
- ->method('newClient')
- ->willReturn($client);
- $response = $this->createMock(IResponse::class);
- $client
- ->expects($this->once())
- ->method('get')
- ->with(
- $this->equalTo($this->endpoint),
- $this->equalTo([
- 'timeout' => 120,
- 'headers' => [
- 'If-None-Match' => '"myETag"',
- ]
- ])
- )
- ->willReturn($response);
- $response->method('getStatusCode')
- ->willReturn(200);
- $response
- ->expects($this->once())
- ->method('getBody')
- ->willReturn('[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}]');
- $response->method('getHeader')
- ->with($this->equalTo('ETag'))
- ->willReturn('"newETag"');
-
- $expected = [
- [
- 'id' => 'MyNewApp',
- 'foo' => 'foo',
- ],
- [
- 'id' => 'bar',
- ],
- ];
- $this->assertSame($expected, $this->fetcher->get());
- }
-
- public function testFetchAfterUpgradeNoETag(): void {
- $this->config->method('getSystemValueString')
- ->willReturnCallback(function ($key, $default) {
- if ($key === 'version') {
- return '11.0.0.3';
- } else {
- return $default;
- }
- });
- $this->config->method('getSystemValueBool')
- ->willReturnArgument(1);
-
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['settings', 'appstore-fetcher-lastFailure', '0', '0'],
- ['settings', 'appstore-timeout', '120', '120'],
- ]);
-
- $folder = $this->createMock(ISimpleFolder::class);
- $file = $this->createMock(ISimpleFile::class);
- $this->appData
- ->expects($this->once())
- ->method('getFolder')
- ->with('/')
- ->willReturn($folder);
- $folder
- ->expects($this->once())
- ->method('getFile')
- ->with($this->fileName)
- ->willReturn($file);
- $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1501,"ncversion":"11.0.0.3","ETag":"\"newETag\""}';
- $file
- ->expects($this->once())
- ->method('putContent')
- ->with($fileData);
- $file
- ->expects($this->exactly(2))
- ->method('getContent')
- ->willReturnOnConsecutiveCalls(
- '{"data":[{"id":"MyOldApp","abc":"def"}],"timestamp":1200,"ncversion":"11.0.0.2","ETag":"\"myETag\""}',
- $fileData
- );
- $client = $this->createMock(IClient::class);
- $this->clientService
- ->expects($this->once())
- ->method('newClient')
- ->willReturn($client);
- $response = $this->createMock(IResponse::class);
- $client
- ->expects($this->once())
- ->method('get')
- ->with(
- $this->equalTo($this->endpoint),
- $this->equalTo([
- 'timeout' => 120,
- ])
- )
- ->willReturn($response);
- $response->method('getStatusCode')
- ->willReturn(200);
- $response
- ->expects($this->once())
- ->method('getBody')
- ->willReturn('[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}]');
- $response->method('getHeader')
- ->with($this->equalTo('ETag'))
- ->willReturn('"newETag"');
- $this->timeFactory
- ->expects($this->once())
- ->method('getTime')
- ->willReturn(1501);
-
- $expected = [
- [
- 'id' => 'MyNewApp',
- 'foo' => 'foo',
- ],
- [
- 'id' => 'bar',
- ],
- ];
- $this->assertSame($expected, $this->fetcher->get());
- }
-
- public function testGetWithUnreadableCacheFileRecreatesAndFetches(): void {
- $this->config
- ->method('getSystemValueString')
- ->willReturnCallback(function ($var, $default) {
- if ($var === 'appstoreurl') {
- return 'https://apps.nextcloud.com/api/v1';
- } elseif ($var === 'version') {
- return '11.0.0.2';
- }
- return $default;
- });
- $this->config->method('getSystemValueBool')
- ->willReturn(true);
-
- $folder = $this->createMock(ISimpleFolder::class);
- $corruptedFile = $this->createMock(ISimpleFile::class);
- $freshFile = $this->createMock(ISimpleFile::class);
- $this->appData
- ->expects($this->once())
- ->method('getFolder')
- ->with('/')
- ->willReturn($folder);
- $folder
- ->expects($this->once())
- ->method('getFile')
- ->with($this->fileName)
- ->willReturn($corruptedFile);
- $corruptedFile
- ->expects($this->once())
- ->method('getContent')
- ->willThrowException(new GenericFileException());
- $corruptedFile
- ->expects($this->once())
- ->method('delete');
- $folder
- ->expects($this->once())
- ->method('newFile')
- ->with($this->fileName)
- ->willReturn($freshFile);
-
- $client = $this->createMock(IClient::class);
- $this->clientService
- ->expects($this->once())
- ->method('newClient')
- ->willReturn($client);
- $response = $this->createMock(IResponse::class);
- $client
- ->expects($this->once())
- ->method('get')
- ->with($this->endpoint)
- ->willReturn($response);
- $response
- ->expects($this->once())
- ->method('getBody')
- ->willReturn('[{"id":"MyNewApp", "foo": "foo"}, {"id":"bar"}]');
- $response->method('getHeader')
- ->with($this->equalTo('ETag'))
- ->willReturn('"myETag"');
-
- $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1502,"ncversion":"11.0.0.2","ETag":"\"myETag\""}';
- $freshFile
- ->expects($this->once())
- ->method('putContent')
- ->with($fileData);
- $freshFile
- ->expects($this->once())
- ->method('getContent')
- ->willReturn($fileData);
- $this->timeFactory
- ->expects($this->once())
- ->method('getTime')
- ->willReturn(1502);
-
- $expected = [
- [
- 'id' => 'MyNewApp',
- 'foo' => 'foo',
- ],
- [
- 'id' => 'bar',
- ],
- ];
- $this->assertSame($expected, $this->fetcher->get());
- }
-}
--- a/tests/lib/App/AppStore/Version/VersionParserTest.php
+++ /dev/null
@@ -1,85 +0,0 @@
-versionParser = new VersionParser();
- }
-
- /**
- * @return array
- */
- public static function versionProvider(): array {
- return [
- [
- '*',
- new Version('', ''),
- ],
- [
- '<=8.1.2',
- new Version('', '8.1.2'),
- ],
- [
- '<=9',
- new Version('', '9'),
- ],
- [
- '>=9.3.2',
- new Version('9.3.2', ''),
- ],
- [
- '>=8.1.2 <=9.3.2',
- new Version('8.1.2', '9.3.2'),
- ],
- [
- '>=8.2 <=9.1',
- new Version('8.2', '9.1'),
- ],
- [
- '>=9 <=11',
- new Version('9', '11'),
- ],
- ];
- }
-
- /**
- *
- * @param string $input
- * @param Version $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('versionProvider')]
- public function testGetVersion($input,
- Version $expected): void {
- $this->assertEquals($expected, $this->versionParser->getVersion($input));
- }
-
- public function testGetVersionException(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Version cannot be parsed: BogusVersion');
-
- $this->versionParser->getVersion('BogusVersion');
- }
-
- public function testGetVersionExceptionWithMultiple(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Version cannot be parsed: >=8.2 <=9.1a');
-
- $this->versionParser->getVersion('>=8.2 <=9.1a');
- }
-}
--- a/tests/lib/App/AppStore/Version/VersionTest.php
+++ /dev/null
@@ -1,25 +0,0 @@
-assertSame('9', $version->getMinimumVersion());
- }
-
- public function testGetMaximumVersion(): void {
- $version = new Version('9', '10');
- $this->assertSame('10', $version->getMaximumVersion());
- }
-}
--- a/tests/lib/App/CompareVersionTest.php
+++ /dev/null
@@ -1,79 +0,0 @@
-compare = new CompareVersion();
- }
-
- public static function comparisonData(): array {
- return [
- // Compatible versions
- ['13.0.0.3', '13.0.0', '>=', true],
- ['13.0.0.3', '13.0.0', '<=', true],
- ['13.0.0', '13.0.0', '>=', true],
- ['13.0.0', '13.0', '<=', true],
- ['13.0.0', '13', '>=', true],
- ['13.0.1', '13', '>=', true],
- ['13.0.1', '13', '<=', true],
- ['13.0.1.9', '13', '<=', true],
- ['13.0.1-beta.1', '13', '<=', true],
- ['7.4.14', '7.4', '<=', true],
- ['7.4.14-ubuntu', '7.4', '<=', true],
- ['7.4.14-ubuntu', '7.4.15', '<=', true],
- ['7.4.16-ubuntu', '7.4.15', '<=', false],
- // Incompatible major versions
- ['13.0.0.3', '13.0.0', '<', false],
- ['12.0.0', '13.0.0', '>=', false],
- ['12.0.0', '13.0', '>=', false],
- ['12.0.0', '13', '>=', false],
- ['7.4.15-ubuntu', '7.4.15', '>=', true],
- // Incompatible minor and patch versions
- ['13.0.0', '13.0.1', '>=', false],
- ['13.0.0', '13.1', '>=', false],
- // Compatible minor and patch versions
- ['13.0.1', '13.0.0', '>=', true],
- ['13.2.0', '13.1', '>=', true],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('comparisonData')]
- public function testComparison(string $actualVersion, string $requiredVersion,
- string $comparator, bool $expected): void {
- $isCompatible = $this->compare->isCompatible($actualVersion, $requiredVersion,
- $comparator);
-
- $this->assertEquals($expected, $isCompatible);
- }
-
- public function testInvalidServerVersion(): void {
- $actualVersion = '13';
- $this->expectException(InvalidArgumentException::class);
-
- $this->compare->isCompatible($actualVersion, '13.0.0');
- }
-
- public function testInvalidRequiredVersion(): void {
- $actualVersion = '13.0.0';
- $this->expectException(InvalidArgumentException::class);
-
- $this->compare->isCompatible($actualVersion, '13.0.0.9');
- }
-}
--- a/tests/lib/App/DependencyAnalyzerTest.php
+++ /dev/null
@@ -1,762 +0,0 @@
-platformMock = $this->getMockBuilder(Platform::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->platformMock->expects($this->any())
- ->method('getPhpVersion')
- ->willReturn('5.4.3');
- $this->platformMock->expects($this->any())
- ->method('getIntSize')
- ->willReturn(4);
- $this->platformMock->expects($this->any())
- ->method('getDatabase')
- ->willReturn('mysql');
- $this->platformMock->expects($this->any())
- ->method('getOS')
- ->willReturn('Linux');
- $this->platformMock->expects($this->any())
- ->method('isCommandKnown')
- ->willReturnCallback(function ($command) {
- return ($command === 'grep');
- });
- $this->platformMock->expects($this->any())
- ->method('getLibraryVersion')
- ->willReturnCallback(function ($lib) {
- if ($lib === 'curl') {
- return '2.3.4';
- }
- return null;
- });
- $this->platformMock->expects($this->any())
- ->method('getOcVersion')
- ->willReturn('8.0.2');
-
- $this->analyser = new DependencyAnalyzer($this->platformMock);
- }
-
- /**
- *
- * @param string $expectedMissing
- * @param string $minVersion
- * @param string $maxVersion
- * @param string $intSize
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providesPhpVersion')]
- public function testPhpVersion($expectedMissing, $minVersion, $maxVersion, $intSize): void {
- $app = [
- 'dependencies' => [
- 'php' => []
- ]
- ];
- if (!is_null($minVersion)) {
- $app['dependencies']['php']['@attributes']['min-version'] = $minVersion;
- }
- if (!is_null($maxVersion)) {
- $app['dependencies']['php']['@attributes']['max-version'] = $maxVersion;
- }
- if (!is_null($intSize)) {
- $app['dependencies']['php']['@attributes']['min-int-size'] = $intSize;
- }
- $missing = $this->analyser->analyze($app);
-
- $this->assertTrue(is_array($missing));
- $this->assertEquals($expectedMissing, $missing);
- }
-
- /**
- * @param $expectedMissing
- * @param $databases
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providesDatabases')]
- public function testDatabases($expectedMissing, $databases): void {
- $app = [
- 'dependencies' => [
- ]
- ];
- if (!is_null($databases)) {
- $app['dependencies']['database'] = $databases;
- }
- $missing = $this->analyser->analyze($app);
-
- $this->assertTrue(is_array($missing));
- $this->assertEquals($expectedMissing, $missing);
- }
-
- /**
- *
- * @param string $expectedMissing
- * @param string|null $commands
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providesCommands')]
- public function testCommand($expectedMissing, $commands): void {
- $app = [
- 'dependencies' => [
- ]
- ];
- if (!is_null($commands)) {
- $app['dependencies']['command'] = $commands;
- }
- $missing = $this->analyser->analyze($app);
-
- $this->assertTrue(is_array($missing));
- $this->assertEquals($expectedMissing, $missing);
- }
-
- /**
- * @param $expectedMissing
- * @param $libs
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providesLibs')]
- public function testLibs($expectedMissing, $libs): void {
- $app = [
- 'dependencies' => [
- ]
- ];
- if (!is_null($libs)) {
- $app['dependencies']['lib'] = $libs;
- }
-
- $missing = $this->analyser->analyze($app);
-
- $this->assertTrue(is_array($missing));
- $this->assertEquals($expectedMissing, $missing);
- }
-
- /**
- * @param $expectedMissing
- * @param $oss
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providesOS')]
- public function testOS($expectedMissing, $oss): void {
- $app = [
- 'dependencies' => []
- ];
- if (!is_null($oss)) {
- $app['dependencies']['os'] = $oss;
- }
-
- $missing = $this->analyser->analyze($app);
-
- $this->assertTrue(is_array($missing));
- $this->assertEquals($expectedMissing, $missing);
- }
-
- /**
- * @param $expectedMissing
- * @param $oc
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providesOC')]
- public function testOC($expectedMissing, $oc): void {
- $app = [
- 'dependencies' => []
- ];
- if (!is_null($oc)) {
- $app['dependencies'] = $oc;
- }
-
- $missing = $this->analyser->analyze($app);
-
- $this->assertTrue(is_array($missing));
- $this->assertEquals($expectedMissing, $missing);
- }
-
- /**
- * @return array
- */
- public static function providesOC(): array {
- return [
- // no version -> no missing dependency
- [
- [],
- null,
- ],
- [
- [],
- [
- 'nextcloud' => [
- '@attributes' => [
- 'min-version' => '8',
- 'max-version' => '8',
- ],
- ],
- ],
- ],
- [
- [],
- [
- 'nextcloud' => [
- '@attributes' => [
- 'min-version' => '8.0',
- 'max-version' => '8.0',
- ],
- ],
- ],
- ],
- [
- [],
- [
- 'nextcloud' => [
- '@attributes' => [
- 'min-version' => '8.0.2',
- 'max-version' => '8.0.2'
- ],
- ],
- ],
- ],
- [
- [
- 'Server version 8.0.3 or higher is required.',
- ],
- [
- 'nextcloud' => [
- '@attributes' => [
- 'min-version' => '8.0.3'
- ],
- ],
- ],
- ],
- [
- [
- 'Server version 9 or higher is required.',
- ],
- [
- 'nextcloud' => [
- '@attributes' => [
- 'min-version' => '9'
- ],
- ],
- ],
- ],
- [
- [
- 'Server version 10 or higher is required.',
- ],
- [
- 'nextcloud' => [
- '@attributes' => [
- 'min-version' => '10'
- ],
- ],
- 'owncloud' => [
- '@attributes' => [
- 'min-version' => '9'
- ],
- ],
- ],
- ],
- [
- [
- 'Server version 9.2 or higher is required.',
- ],
- [
- 'nextcloud' => [
- '@attributes' => [
- 'min-version' => '9.2',
- ],
- ],
- ],
- ],
- [
- [
- 'Server version 11 or higher is required.',
- ],
- [
- 'nextcloud' => [
- '@attributes' => [
- 'min-version' => '11',
- ],
- ],
- ],
- ],
- [
- [
- 'Server version 8.0.1 or lower is required.',
- ],
- [
- 'nextcloud' => [
- '@attributes' => [
- 'max-version' => '8.0.1',
- ],
- ],
- ],
- ],
- [
- [],
- [
- 'owncloud' => [
- '@attributes' => [
- 'min-version' => '8',
- 'max-version' => '8',
- ],
- ],
- ],
- ],
- [
- [],
- [
- 'owncloud' => [
- '@attributes' => [
- 'min-version' => '8.0',
- 'max-version' => '8.0',
- ],
- ],
- ],
- ],
- [
- [],
- [
- 'owncloud' => [
- '@attributes' => [
- 'min-version' => '8.0.2',
- 'max-version' => '8.0.2'
- ],
- ],
- ],
- ],
- [
- [
- 'Server version 8.0.3 or higher is required.',
- ],
- [
- 'owncloud' => [
- '@attributes' => [
- 'min-version' => '8.0.3'
- ],
- ],
- ],
- ],
- [
- [
- 'Server version 9 or higher is required.',
- ],
- [
- 'owncloud' => [
- '@attributes' => [
- 'min-version' => '9'
- ],
- ],
- ],
- ],
- [
- [
- 'Server version 9.2 or higher is required.',
- ],
- [
- 'owncloud' => [
- '@attributes' => [
- 'min-version' => '9.2',
- ],
- ],
- ],
- ],
- [
- [
- 'Server version 8.0.1 or lower is required.',
- ],
- [
- 'owncloud' => [
- '@attributes' => [
- 'max-version' => '8.0.1',
- ],
- ],
- ],
- ],
- ];
- }
-
- /**
- * @return array
- */
- public static function providesOS(): array {
- return [
- [[], null],
- [[], []],
- [['The following platforms are supported: ANDROID'], 'ANDROID'],
- [['The following platforms are supported: WINNT'], ['WINNT']]
- ];
- }
-
- /**
- * @return array
- */
- public static function providesLibs(): array {
- return [
- // we expect curl to exist
- [[], 'curl'],
- // we expect abcde to exist
- [['The library abcde is not available.'], ['abcde']],
- // curl in version 100.0 does not exist
- [['Library curl with a version higher than 100.0 is required - available version 2.3.4.'],
- [['@attributes' => ['min-version' => '100.0'], '@value' => 'curl']]],
- // curl in version 100.0 does not exist
- [['Library curl with a version lower than 1.0.0 is required - available version 2.3.4.'],
- [['@attributes' => ['max-version' => '1.0.0'], '@value' => 'curl']]],
- [['Library curl with a version lower than 2.3.3 is required - available version 2.3.4.'],
- [['@attributes' => ['max-version' => '2.3.3'], '@value' => 'curl']]],
- [['Library curl with a version higher than 2.3.5 is required - available version 2.3.4.'],
- [['@attributes' => ['min-version' => '2.3.5'], '@value' => 'curl']]],
- [[],
- [['@attributes' => ['min-version' => '2.3.4', 'max-version' => '2.3.4'], '@value' => 'curl']]],
- [[],
- [['@attributes' => ['min-version' => '2.3', 'max-version' => '2.3'], '@value' => 'curl']]],
- [[],
- [['@attributes' => ['min-version' => '2', 'max-version' => '2'], '@value' => 'curl']]],
- [[],
- ['@attributes' => ['min-version' => '2', 'max-version' => '2'], '@value' => 'curl']],
- ];
- }
-
- /**
- * @return array
- */
- public static function providesCommands(): array {
- return [
- [[], null],
- // grep is known on linux
- [[], [['@attributes' => ['os' => 'Linux'], '@value' => 'grep']]],
- // grepp is not known on linux
- [['The command line tool grepp could not be found'], [['@attributes' => ['os' => 'Linux'], '@value' => 'grepp']]],
- // we don't care about tools on Windows - we are on Linux
- [[], [['@attributes' => ['os' => 'Windows'], '@value' => 'grepp']]],
- // grep is known on all systems
- [[], 'grep'],
- [[], ['@attributes' => ['os' => 'Linux'], '@value' => 'grep']],
- ];
- }
-
- /**
- * @return array
- */
- public static function providesDatabases(): array {
- return [
- // non BC - in case on databases are defined -> all are supported
- [[], null],
- [[], []],
- [['The following databases are supported: mongodb'], 'mongodb'],
- [['The following databases are supported: sqlite, postgres'], ['sqlite', ['@value' => 'postgres']]],
- ];
- }
-
- /**
- * @return array
- */
- public static function providesPhpVersion(): array {
- return [
- [[], null, null, null],
- [[], '5.4', null, null],
- [[], null, '5.5', null],
- [[], '5.4', '5.5', null],
- [['PHP 5.4.4 or higher is required.'], '5.4.4', null, null],
- [['PHP with a version lower than 5.4.2 is required.'], null, '5.4.2', null],
- [['64bit or higher PHP required.'], null, null, 64],
- [[], '5.4', '5.4', null],
- ];
- }
-
- public static function appVersionsProvider(): array {
- return [
- // exact match
- [
- '6.0.0.0',
- [
- 'requiremin' => '6.0',
- 'requiremax' => '6.0',
- ],
- true
- ],
- // in-between match
- [
- '6.0.0.0',
- [
- 'requiremin' => '5.0',
- 'requiremax' => '7.0',
- ],
- true
- ],
- // app too old
- [
- '6.0.0.0',
- [
- 'requiremin' => '5.0',
- 'requiremax' => '5.0',
- ],
- false
- ],
- // app too new
- [
- '5.0.0.0',
- [
- 'requiremin' => '6.0',
- 'requiremax' => '6.0',
- ],
- false
- ],
- // only min specified
- [
- '6.0.0.0',
- [
- 'requiremin' => '6.0',
- ],
- true
- ],
- // only min specified fail
- [
- '5.0.0.0',
- [
- 'requiremin' => '6.0',
- ],
- false
- ],
- // only min specified legacy
- [
- '6.0.0.0',
- [
- 'require' => '6.0',
- ],
- true
- ],
- // only min specified legacy fail
- [
- '4.0.0.0',
- [
- 'require' => '6.0',
- ],
- false
- ],
- // only max specified
- [
- '5.0.0.0',
- [
- 'requiremax' => '6.0',
- ],
- true
- ],
- // only max specified fail
- [
- '7.0.0.0',
- [
- 'requiremax' => '6.0',
- ],
- false
- ],
- // variations of versions
- // single OC number
- [
- '4',
- [
- 'require' => '4.0',
- ],
- true
- ],
- // multiple OC number
- [
- '4.3.1',
- [
- 'require' => '4.3',
- ],
- true
- ],
- // single app number
- [
- '4',
- [
- 'require' => '4',
- ],
- true
- ],
- // single app number fail
- [
- '4.3',
- [
- 'require' => '5',
- ],
- false
- ],
- // complex
- [
- '5.0.0',
- [
- 'require' => '4.5.1',
- ],
- true
- ],
- // complex fail
- [
- '4.3.1',
- [
- 'require' => '4.3.2',
- ],
- false
- ],
- // two numbers
- [
- '4.3.1',
- [
- 'require' => '4.4',
- ],
- false
- ],
- // one number fail
- [
- '4.3.1',
- [
- 'require' => '5',
- ],
- false
- ],
- // pre-alpha app
- [
- '5.0.3',
- [
- 'require' => '4.93',
- ],
- true
- ],
- // pre-alpha OC
- [
- '6.90.0.2',
- [
- 'require' => '6.90',
- ],
- true
- ],
- // pre-alpha OC max
- [
- '6.90.0.2',
- [
- 'requiremax' => '7',
- ],
- true
- ],
- // expect same major number match
- [
- '5.0.3',
- [
- 'require' => '5',
- ],
- true
- ],
- // expect same major number match
- [
- '5.0.3',
- [
- 'requiremax' => '5',
- ],
- true
- ],
- // dependencies versions before require*
- [
- '6.0.0.0',
- [
- 'requiremin' => '5.0',
- 'requiremax' => '7.0',
- 'dependencies' => [
- 'owncloud' => [
- '@attributes' => [
- 'min-version' => '7.0',
- 'max-version' => '7.0',
- ],
- ],
- ],
- ],
- false
- ],
- [
- '6.0.0.0',
- [
- 'requiremin' => '5.0',
- 'requiremax' => '7.0',
- 'dependencies' => [
- 'owncloud' => [
- '@attributes' => [
- 'min-version' => '5.0',
- 'max-version' => '5.0',
- ],
- ],
- ],
- ],
- false
- ],
- [
- '6.0.0.0',
- [
- 'requiremin' => '5.0',
- 'requiremax' => '5.0',
- 'dependencies' => [
- 'owncloud' => [
- '@attributes' => [
- 'min-version' => '5.0',
- 'max-version' => '7.0',
- ],
- ],
- ],
- ],
- true
- ],
- [
- '9.2.0.0',
- [
- 'dependencies' => [
- 'owncloud' => [
- '@attributes' => [
- 'min-version' => '9.0',
- 'max-version' => '9.1',
- ],
- ],
- 'nextcloud' => [
- '@attributes' => [
- 'min-version' => '9.1',
- 'max-version' => '9.2',
- ],
- ],
- ],
- ],
- true
- ],
- [
- '9.2.0.0',
- [
- 'dependencies' => [
- 'nextcloud' => [
- '@attributes' => [
- 'min-version' => '9.1',
- 'max-version' => '9.2',
- ],
- ],
- ],
- ],
- true
- ],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('appVersionsProvider')]
- public function testServerVersion($ncVersion, $appInfo, $expectedResult): void {
- $this->assertEquals($expectedResult, count($this->analyser->analyzeServerVersion($ncVersion, $appInfo, false)) === 0);
- }
-}
--- a/tests/lib/App/InfoParserTest.php
+++ /dev/null
@@ -1,130 +0,0 @@
-parse(OC::$SERVERROOT . "/tests/data/app/$xmlFile");
-
- $this->assertEquals($expectedData, $data);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providesInfoXml')]
- public function testParsingValidXmlWithoutCache($expectedJson, $xmlFile): void {
- $this->parserTest($expectedJson, $xmlFile);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providesInfoXml')]
- public function testParsingValidXmlWithCache($expectedJson, $xmlFile): void {
- $this->parserTest($expectedJson, $xmlFile, self::$cache);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('appDataProvider')]
- public function testApplyL10N(array $data, array $expected, string $language): void {
- $parser = new InfoParser();
- $this->assertEqualsCanonicalizing($expected, $parser->applyL10N($data, $language));
- }
-
- public static function providesInfoXml(): array {
- return [
- 'Only one value in each list' => ['appinfo-multi-once.json', 'appinfo-multi-once.xml'],
- 'Only one value in each list with attributes' => ['appinfo-attributes-once.json', 'appinfo-attributes-once.xml'],
- 'Multiple values in each list' => ['appinfo-multi-twice.json', 'appinfo-multi-twice.xml'],
- 'Valid info' => ['expected-info.json', 'valid-info.xml'],
- 'Invalid info' => [null, 'invalid-info.xml'],
- 'Navigation one item' => ['navigation-one-item.json', 'navigation-one-item.xml'],
- 'Navigation two items' => ['navigation-two-items.json', 'navigation-two-items.xml'],
- 'Various single item' => ['various-single-item.json', 'various-single-item.xml'],
- ];
- }
-
- /**
- * Providers for the app data values
- */
- public static function appDataProvider(): array {
- $FULL_TRANSLATED = [
- 'name' => [
- ['@attributes' => ['lang' => 'en'], '@value' => 'App'],
- ['@attributes' => ['lang' => 'fr'], '@value' => 'Application']
- ],
- 'summary' => [
- 'Summary',
- ['@attributes' => ['lang' => 'fr'], '@value' => 'Résumé']
- ],
- 'description' => [
- ['@attributes' => ['lang' => 'en'], '@value' => 'Description'],
- ['@attributes' => ['lang' => 'fr'], '@value' => 'Description (fr)']
- ]
- ];
-
- return [
- // test trimming
- [
- ['description' => " \t This is a multiline \n test with \n \t \n \n some new lines "],
- ['description' => "This is a multiline \n test with \n \t \n \n some new lines", 'summary' => '', 'name' => ''],
- 'en'
- ],
- [
- ['description' => " \t This is a multiline \n test with \n \t some new lines "],
- ['description' => "This is a multiline \n test with \n \t some new lines", 'summary' => '', 'name' => ''],
- 'en'
- ],
- [
- ['description' => hex2bin('5065726d657420646520732761757468656e7469666965722064616e732070697769676f20646972656374656d656e74206176656320736573206964656e74696669616e7473206f776e636c6f75642073616e73206c65732072657461706572206574206d657420c3a0206a6f757273206365757820636920656e20636173206465206368616e67656d656e74206465206d6f742064652070617373652e0d0a0d')],
- ['description' => "Permet de s'authentifier dans piwigo directement avec ses identifiants owncloud sans les retaper et met à jours ceux ci en cas de changement de mot de passe.", 'summary' => '', 'name' => ''],
- 'fr'
- ],
- // test proper translation handling
- // just strings:
- [
- ['name' => 'App', 'summary' => 'Summary', 'description' => 'Description'],
- ['name' => 'App', 'summary' => 'Summary', 'description' => 'Description'],
- 'en'
- ],
- // translated and requesting English:
- [
- $FULL_TRANSLATED,
- ['name' => 'App', 'summary' => 'Summary', 'description' => 'Description'],
- 'en'
- ],
- // translated and requesting translation:
- [
- $FULL_TRANSLATED,
- ['name' => 'Application', 'summary' => 'Résumé', 'description' => 'Description (fr)'],
- 'fr'
- ],
- // translated but requesting non existing translation, should fallback to English:
- [
- $FULL_TRANSLATED,
- ['name' => 'App', 'summary' => 'Summary', 'description' => 'Description'],
- 'de'
- ]
- ];
- }
-}
--- a/tests/lib/App/PlatformRepositoryTest.php
+++ /dev/null
@@ -1,62 +0,0 @@
-normalizeVersion($input);
- $this->assertEquals($expected, $normalizedVersion);
- }
-
- public static function providesVersions(): array {
- return [
- 'none' => ['1.0.0', '1.0.0.0'],
- 'none/2' => ['1.2.3.4', '1.2.3.4'],
- 'parses state' => ['1.0.0RC1dev', '1.0.0.0-RC1-dev'],
- 'CI parsing' => ['1.0.0-rC15-dev', '1.0.0.0-RC15-dev'],
- 'delimiters' => ['1.0.0.RC.15-dev', '1.0.0.0-RC15-dev'],
- 'RC uppercase' => ['1.0.0-rc1', '1.0.0.0-RC1'],
- 'patch replace' => ['1.0.0.pl3-dev', '1.0.0.0-patch3-dev'],
- 'forces w.x.y.z' => ['1.0-dev', '1.0.0.0-dev'],
- 'forces w.x.y.z/2' => ['0', '0.0.0.0'],
- 'parses long' => ['10.4.13-beta', '10.4.13.0-beta'],
- 'parses long/2' => ['10.4.13beta2', '10.4.13.0-beta2'],
- 'parses long/semver' => ['10.4.13beta.2', '10.4.13.0-beta2'],
- 'expand shorthand' => ['10.4.13-b', '10.4.13.0-beta'],
- 'expand shorthand2' => ['10.4.13-b5', '10.4.13.0-beta5'],
- 'strips leading v' => ['v1.0.0', '1.0.0.0'],
- 'strips v/datetime' => ['v20100102', '20100102'],
- 'parses dates y-m' => ['2010.01', '2010-01'],
- 'parses dates w/ .' => ['2010.01.02', '2010-01-02'],
- 'parses dates w/ -' => ['2010-01-02', '2010-01-02'],
- 'parses numbers' => ['2010-01-02.5', '2010-01-02-5'],
- 'parses dates y.m.Y' => ['2010.1.555', '2010.1.555.0'],
- 'parses datetime' => ['20100102-203040', '20100102-203040'],
- 'parses dt+number' => ['20100102203040-10', '20100102203040-10'],
- 'parses dt+patch' => ['20100102-203040-p1', '20100102-203040-patch1'],
- 'parses master' => ['dev-master', '9999999-dev'],
- 'parses trunk' => ['dev-trunk', '9999999-dev'],
- // 'parses branches' => array('1.x-dev', '1.9999999.9999999.9999999-dev'),
- 'parses arbitrary' => ['dev-feature-foo', 'dev-feature-foo'],
- 'parses arbitrary2' => ['DEV-FOOBAR', 'dev-FOOBAR'],
- 'parses arbitrary3' => ['dev-feature/foo', 'dev-feature/foo'],
- 'ignores aliases' => ['dev-master as 1.0.0', '9999999-dev'],
- // 'semver metadata' => array('dev-master+foo.bar', '9999999-dev'),
- // 'semver metadata/2' => array('1.0.0-beta.5+foo', '1.0.0.0-beta5'),
- // 'semver metadata/3' => array('1.0.0+foo', '1.0.0.0'),
- // 'metadata w/ alias' => array('1.0.0+foo as 2.0', '1.0.0.0'),
- ];
- }
-}
--- a/tests/lib/AppConfigIntegrationTest.php
+++ /dev/null
@@ -1,1514 +0,0 @@
->>
- * [appId => [configKey, configValue, valueType, lazy, sensitive]]
- */
- private static array $baseStruct
- = [
- 'testapp' => [
- 'enabled' => ['enabled', 'yes'],
- 'installed_version' => ['installed_version', '1.2.3'],
- 'depends_on' => ['depends_on', 'someapp'],
- 'deletethis' => ['deletethis', 'deletethis'],
- 'key' => ['key', 'value']
- ],
- 'searchtest' => [
- 'search_key1' => ['search_key1', 'key1', IAppConfig::VALUE_STRING],
- 'search_key2' => ['search_key2', 'key2', IAppConfig::VALUE_STRING],
- 'search_key3' => ['search_key3', 'key3', IAppConfig::VALUE_STRING],
- 'searchnot_key4' => ['searchnot_key4', 'key4', IAppConfig::VALUE_STRING],
- 'search_key5_lazy' => ['search_key5_lazy', 'key5', IAppConfig::VALUE_STRING, true],
- ],
- 'someapp' => [
- 'key' => ['key', 'value'],
- 'otherkey' => ['otherkey', 'othervalue']
- ],
- '123456' => [
- 'enabled' => ['enabled', 'yes'],
- 'key' => ['key', 'value']
- ],
- 'anotherapp' => [
- 'enabled' => ['enabled', 'no'],
- 'installed_version' => ['installed_version', '3.2.1'],
- 'key' => ['key', 'value']
- ],
- 'non-sensitive-app' => [
- 'lazy-key' => ['lazy-key', 'value', IAppConfig::VALUE_STRING, true, false],
- 'non-lazy-key' => ['non-lazy-key', 'value', IAppConfig::VALUE_STRING, false, false],
- ],
- 'sensitive-app' => [
- 'lazy-key' => ['lazy-key', 'value', IAppConfig::VALUE_STRING, true, true],
- 'non-lazy-key' => ['non-lazy-key', 'value', IAppConfig::VALUE_STRING, false, true],
- ],
- 'only-lazy' => [
- 'lazy-key' => ['lazy-key', 'value', IAppConfig::VALUE_STRING, true]
- ],
- 'typed' => [
- 'mixed' => ['mixed', 'mix', IAppConfig::VALUE_MIXED],
- 'string' => ['string', 'value', IAppConfig::VALUE_STRING],
- 'int' => ['int', '42', IAppConfig::VALUE_INT],
- 'float' => ['float', '3.14', IAppConfig::VALUE_FLOAT],
- 'bool' => ['bool', '1', IAppConfig::VALUE_BOOL],
- 'array' => ['array', '{"test": 1}', IAppConfig::VALUE_ARRAY],
- ],
- 'prefix-app' => [
- 'key1' => ['key1', 'value'],
- 'prefix1' => ['prefix1', 'value'],
- 'prefix-2' => ['prefix-2', 'value'],
- 'key-2' => ['key-2', 'value'],
- ]
- ];
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- $this->connection = Server::get(IDBConnection::class);
- $this->config = Server::get(IConfig::class);
- $this->configManager = Server::get(ConfigManager::class);
- $this->presetManager = Server::get(PresetManager::class);
- $this->logger = Server::get(LoggerInterface::class);
- $this->crypto = Server::get(ICrypto::class);
- $this->cacheFactory = $this->createMock(CacheFactory::class);
- $this->cacheFactory->method('isLocalCacheAvailable')->willReturn(false);
-
- // storing current config and emptying the data table
- $sql = $this->connection->getQueryBuilder();
- $sql->select('*')
- ->from('appconfig');
- $result = $sql->executeQuery();
- $this->originalConfig = $result->fetchAllAssociative();
- $result->closeCursor();
-
- $sql = $this->connection->getQueryBuilder();
- $sql->delete('appconfig');
- $sql->executeStatement();
-
- $sql = $this->connection->getQueryBuilder();
- $sql->insert('appconfig')
- ->values(
- [
- 'appid' => $sql->createParameter('appid'),
- 'configkey' => $sql->createParameter('configkey'),
- 'configvalue' => $sql->createParameter('configvalue'),
- 'type' => $sql->createParameter('type'),
- 'lazy' => $sql->createParameter('lazy')
- ]
- );
-
- foreach (self::$baseStruct as $appId => $appData) {
- foreach ($appData as $key => $row) {
- $value = $row[1];
- $type = $row[2] ?? IAppConfig::VALUE_MIXED;
- if (($row[4] ?? false) === true) {
- $type |= IAppConfig::VALUE_SENSITIVE;
- if (!isset(self::$baseStruct[$appId][$key]['encrypted'])) {
- $value = self::invokePrivate(AppConfig::class, 'ENCRYPTION_PREFIX') . $this->crypto->encrypt($value);
- self::$baseStruct[$appId][$key]['encrypted'] = $value;
- } else {
- $value = self::$baseStruct[$appId][$key]['encrypted'];
- }
- }
-
- $sql->setParameters(
- [
- 'appid' => $appId,
- 'configkey' => $row[0],
- 'configvalue' => $value,
- 'type' => $type,
- 'lazy' => (($row[3] ?? false) === true) ? 1 : 0
- ]
- )->executeStatement();
- }
- }
- }
-
- #[\Override]
- protected function tearDown(): void {
- $sql = $this->connection->getQueryBuilder();
- $sql->delete('appconfig');
- $sql->executeStatement();
-
- $sql = $this->connection->getQueryBuilder();
- $sql->insert('appconfig')
- ->values(
- [
- 'appid' => $sql->createParameter('appid'),
- 'configkey' => $sql->createParameter('configkey'),
- 'configvalue' => $sql->createParameter('configvalue'),
- 'lazy' => $sql->createParameter('lazy'),
- 'type' => $sql->createParameter('type'),
- ]
- );
-
- foreach ($this->originalConfig as $key => $configs) {
- $sql->setParameter('appid', $configs['appid'])
- ->setParameter('configkey', $configs['configkey'])
- ->setParameter('configvalue', $configs['configvalue'])
- ->setParameter('lazy', ($configs['lazy'] === '1') ? '1' : '0')
- ->setParameter('type', $configs['type']);
- $sql->executeStatement();
- }
-
- // $this->restoreService(AppConfig::class);
- parent::tearDown();
- }
-
- /**
- * @param bool $preLoading TRUE will preload the 'fast' cache, which is the normal behavior of usual
- * IAppConfig
- *
- * @return IAppConfig
- */
- private function generateAppConfig(bool $preLoading = true): IAppConfig {
- /** @var AppConfig $config */
- $config = new AppConfig(
- $this->connection,
- $this->config,
- $this->configManager,
- $this->presetManager,
- $this->logger,
- $this->crypto,
- $this->cacheFactory,
- );
- $msg = ' generateAppConfig() failed to confirm cache status';
-
- // confirm cache status
- $status = $config->statusCache();
- $this->assertSame(false, $status['fastLoaded'], $msg);
- $this->assertSame(false, $status['lazyLoaded'], $msg);
- $this->assertSame([], $status['fastCache'], $msg);
- $this->assertSame([], $status['lazyCache'], $msg);
- if ($preLoading) {
- // simple way to initiate the load of non-lazy config values in cache
- $config->getValueString('core', 'preload', '');
-
- // confirm cache status
- $status = $config->statusCache();
- $this->assertSame(true, $status['fastLoaded'], $msg);
- $this->assertSame(false, $status['lazyLoaded'], $msg);
-
- $apps = array_values(array_diff(array_keys(self::$baseStruct), ['only-lazy']));
- $this->assertEqualsCanonicalizing($apps, array_keys($status['fastCache']), $msg);
- $this->assertSame([], array_keys($status['lazyCache']), $msg);
- }
-
- return $config;
- }
-
- public function testGetApps(): void {
- $config = $this->generateAppConfig(false);
-
- $this->assertEqualsCanonicalizing(array_keys(self::$baseStruct), $config->getApps());
- }
-
- public function testGetAppInstalledVersions(): void {
- $config = $this->generateAppConfig(false);
-
- $this->assertEquals(
- ['testapp' => '1.2.3', 'anotherapp' => '3.2.1'],
- $config->getAppInstalledVersions(false)
- );
- $this->assertEquals(
- ['testapp' => '1.2.3'],
- $config->getAppInstalledVersions(true)
- );
- }
-
- /**
- * returns list of app and their keys
- *
- * @return array ['appId' => ['key1', 'key2', ]]
- * @see testGetKeys
- */
- public static function providerGetAppKeys(): array {
- $appKeys = [];
- foreach (self::$baseStruct as $appId => $appData) {
- $keys = [];
- foreach ($appData as $row) {
- $keys[] = $row[0];
- }
- $appKeys[] = [(string)$appId, $keys];
- }
-
- return $appKeys;
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAppKeys')]
- public function testGetKeys(string $appId, array $expectedKeys): void {
- $config = $this->generateAppConfig();
- $this->assertEqualsCanonicalizing($expectedKeys, $config->getKeys($appId));
- $this->assertEqualsCanonicalizing($expectedKeys, array_keys($config->getAllValues($appId, '')));
- }
-
- public function testGetKeysOnUnknownAppShouldReturnsEmptyArray(): void {
- $config = $this->generateAppConfig();
- $this->assertEqualsCanonicalizing([], $config->getKeys('unknown-app'));
- }
-
- public function testHasKeyOnNonExistentKeyReturnsFalse(): void {
- $config = $this->generateAppConfig();
- $this->assertEquals(false, $config->hasKey(array_keys(self::$baseStruct)[0], 'inexistant-key'));
- }
-
- public function testHasKeyOnUnknownAppReturnsFalse(): void {
- $config = $this->generateAppConfig();
- $this->assertEquals(false, $config->hasKey('inexistant-app', 'inexistant-key'));
- }
-
- public function testHasKeyOnMistypedAsLazyReturnsFalse(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(false, $config->hasKey('non-sensitive-app', 'non-lazy-key', true));
- }
-
- public function testHasKeyOnMistypeAsNonLazyReturnsFalse(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(false, $config->hasKey('non-sensitive-app', 'lazy-key', false));
- }
-
- public function testHasKeyOnMistypeAsNonLazyReturnsTrueWithLazyArgumentIsNull(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(true, $config->hasKey('non-sensitive-app', 'lazy-key', null));
- }
-
- /**
- * returns list of config keys
- *
- * @return array [appId, key, value, type, lazy, sensitive]
- * @see testIsSensitiveLazyHasKey
- */
- public static function providerGetKeys(): array {
- $appKeys = [];
- foreach (self::$baseStruct as $appId => $appData) {
- foreach ($appData as $row) {
- $appKeys[] = [
- (string)$appId, $row[0], $row[1], $row[2] ?? IAppConfig::VALUE_MIXED, $row[3] ?? false,
- $row[4] ?? false
- ];
- }
- }
-
- return $appKeys;
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerGetKeys')]
- public function testIsSensitiveLazyHasKey(
- string $appId, string $configKey, string $configValue, int $type, bool $lazy, bool $sensitive,
- ): void {
- $config = $this->generateAppConfig();
-
- $this->assertEquals($sensitive, $config->isSensitive($appId, $configKey, $lazy));
-
- $this->assertEquals($lazy, $config->isLazy($appId, $configKey));
-
- $this->assertEquals(true, $config->hasKey($appId, $configKey, $lazy));
- }
-
- public function testIsSensitiveOnNonExistentKeyThrowsException(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigUnknownKeyException::class);
- $config->isSensitive(array_keys(self::$baseStruct)[0], 'inexistant-key');
- }
-
- public function testIsSensitiveOnUnknownAppThrowsException(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigUnknownKeyException::class);
- $config->isSensitive('unknown-app', 'inexistant-key');
- }
-
- public function testIsSensitiveOnSensitiveMistypedAsLazy(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(true, $config->isSensitive('sensitive-app', 'non-lazy-key', true));
- }
-
- public function testIsSensitiveOnNonSensitiveMistypedAsLazy(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(false, $config->isSensitive('non-sensitive-app', 'non-lazy-key', true));
- }
-
- public function testIsSensitiveOnSensitiveMistypedAsNonLazyThrowsException(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigUnknownKeyException::class);
- $config->isSensitive('sensitive-app', 'lazy-key', false);
- }
-
- public function testIsSensitiveOnNonSensitiveMistypedAsNonLazyThrowsException(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigUnknownKeyException::class);
- $config->isSensitive('non-sensitive-app', 'lazy-key', false);
- }
-
- public function testIsLazyOnNonExistentKeyThrowsException(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigUnknownKeyException::class);
- $config->isLazy(array_keys(self::$baseStruct)[0], 'inexistant-key');
- }
-
- public function testIsLazyOnUnknownAppThrowsException(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigUnknownKeyException::class);
- $config->isLazy('unknown-app', 'inexistant-key');
- }
-
- public function testGetAllValues(): void {
- $config = $this->generateAppConfig();
- $this->assertEquals(
- [
- 'array' => ['test' => 1],
- 'bool' => true,
- 'float' => 3.14,
- 'int' => 42,
- 'mixed' => 'mix',
- 'string' => 'value',
- ],
- $config->getAllValues('typed')
- );
- }
-
- public function testGetAllValuesWithEmptyApp(): void {
- $config = $this->generateAppConfig();
- $this->expectException(InvalidArgumentException::class);
- $config->getAllValues('');
- }
-
- public function testGetAllValuesWithPrefix(): void {
- $config = $this->generateAppConfig();
- $this->assertEqualsCanonicalizing(['prefix1', 'prefix-2'], array_keys($config->getAllValues('prefix-app', 'prefix')));
- }
-
- public function testSearchValues(): void {
- $config = $this->generateAppConfig();
- $this->assertEqualsCanonicalizing(['testapp' => 'yes', '123456' => 'yes', 'anotherapp' => 'no'], $config->searchValues('enabled'));
- }
-
- public function testGetValueString(): void {
- $config = $this->generateAppConfig();
- $this->assertSame('value', $config->getValueString('typed', 'string', ''));
- }
-
- public function testGetValueStringOnUnknownAppReturnsDefault(): void {
- $config = $this->generateAppConfig();
- $this->assertSame('default-1', $config->getValueString('typed-1', 'string', 'default-1'));
- }
-
- public function testGetValueStringOnNonExistentKeyReturnsDefault(): void {
- $config = $this->generateAppConfig();
- $this->assertSame('default-2', $config->getValueString('typed', 'string-2', 'default-2'));
- }
-
- public function testGetValueStringOnWrongType(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigTypeConflictException::class);
- $config->getValueString('typed', 'int');
- }
-
- public function testGetNonLazyValueStringAsLazy(): void {
- $config = $this->generateAppConfig();
- $this->assertSame('value', $config->getValueString('non-sensitive-app', 'non-lazy-key', 'default', lazy: true));
- }
-
- public function testGetValueInt(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(42, $config->getValueInt('typed', 'int', 0));
- }
-
- public function testGetValueIntOnUnknownAppReturnsDefault(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(1, $config->getValueInt('typed-1', 'int', 1));
- }
-
- public function testGetValueIntOnNonExistentKeyReturnsDefault(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(2, $config->getValueInt('typed', 'int-2', 2));
- }
-
- public function testGetValueIntOnWrongType(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigTypeConflictException::class);
- $config->getValueInt('typed', 'float');
- }
-
- public function testGetValueFloat(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(3.14, $config->getValueFloat('typed', 'float', 0));
- }
-
- public function testGetValueFloatOnNonUnknownAppReturnsDefault(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(1.11, $config->getValueFloat('typed-1', 'float', 1.11));
- }
-
- public function testGetValueFloatOnNonExistentKeyReturnsDefault(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(2.22, $config->getValueFloat('typed', 'float-2', 2.22));
- }
-
- public function testGetValueFloatOnWrongType(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigTypeConflictException::class);
- $config->getValueFloat('typed', 'bool');
- }
-
- public function testGetValueBool(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(true, $config->getValueBool('typed', 'bool'));
- }
-
- /**
- * Untyped values predate the typed config API and are still in the database of
- * every upgraded instance, so they have to keep resolving to a boolean. The
- * deprecated setter is used on purpose, as it is the only way to write a value
- * without a type.
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataUntypedBool')]
- public function testGetValueBoolOnUntypedValue(string $stored, bool $expected): void {
- /** @var AppConfig $config */
- $config = $this->generateAppConfig();
- $config->setValue('feed', 'untyped-bool', $stored);
-
- $this->assertSame($expected, $config->getValueBool('feed', 'untyped-bool'));
- }
-
- public static function dataUntypedBool(): array {
- return [
- 'yes' => ['yes', true],
- 'no' => ['no', false],
- 'true' => ['true', true],
- 'false' => ['false', false],
- 'on' => ['on', true],
- '1' => ['1', true],
- '0' => ['0', false],
- 'empty' => ['', false],
- ];
- }
-
- public function testGetValueBoolOnUnknownAppReturnsDefault(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(false, $config->getValueBool('typed-1', 'bool', false));
- }
-
- public function testGetValueBoolOnNonExistentKeyReturnsDefault(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(false, $config->getValueBool('typed', 'bool-2'));
- }
-
- public function testGetValueBoolOnWrongType(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigTypeConflictException::class);
- $config->getValueBool('typed', 'array');
- }
-
- public function testGetValueArray(): void {
- $config = $this->generateAppConfig();
- $this->assertEqualsCanonicalizing(['test' => 1], $config->getValueArray('typed', 'array', []));
- }
-
- public function testGetValueArrayOnUnknownAppReturnsDefault(): void {
- $config = $this->generateAppConfig();
- $this->assertSame([1], $config->getValueArray('typed-1', 'array', [1]));
- }
-
- public function testGetValueArrayOnNonExistentKeyReturnsDefault(): void {
- $config = $this->generateAppConfig();
- $this->assertSame([1, 2], $config->getValueArray('typed', 'array-2', [1, 2]));
- }
-
- public function testGetValueArrayOnWrongType(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigTypeConflictException::class);
- $config->getValueArray('typed', 'string');
- }
-
- /**
- * @return array
- * @see testGetValueType
- *
- * @see testGetValueMixed
- */
- public static function providerGetValueMixed(): array {
- return [
- // key, value, type
- ['mixed', 'mix', IAppConfig::VALUE_MIXED],
- ['string', 'value', IAppConfig::VALUE_STRING],
- ['int', '42', IAppConfig::VALUE_INT],
- ['float', '3.14', IAppConfig::VALUE_FLOAT],
- ['bool', '1', IAppConfig::VALUE_BOOL],
- ['array', '{"test": 1}', IAppConfig::VALUE_ARRAY],
- ];
- }
-
- /**
- *
- * @param string $key
- * @param string $value
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerGetValueMixed')]
- public function testGetValueMixed(string $key, string $value): void {
- $config = $this->generateAppConfig();
- $this->assertSame($value, $config->getValueMixed('typed', $key));
- }
-
- /**
- *
- * @param string $key
- * @param string $value
- * @param int $type
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerGetValueMixed')]
- public function testGetValueType(string $key, string $value, int $type): void {
- $config = $this->generateAppConfig();
- $this->assertSame($type, $config->getValueType('typed', $key));
- }
-
- public function testGetValueTypeOnUnknownApp(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigUnknownKeyException::class);
- $config->getValueType('typed-1', 'string');
- }
-
- public function testGetValueTypeOnNonExistentKey(): void {
- $config = $this->generateAppConfig();
- $this->expectException(AppConfigUnknownKeyException::class);
- $config->getValueType('typed', 'string-2');
- }
-
- public function testSetValueString(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1');
- $this->assertSame('value-1', $config->getValueString('feed', 'string', ''));
- }
-
- public function testSetValueStringCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1');
- $status = $config->statusCache();
- $this->assertSame('value-1', $status['fastCache']['feed']['string']);
- }
-
- public function testSetValueStringDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1');
- $config->clearCache();
- $this->assertSame('value-1', $config->getValueString('feed', 'string', ''));
- }
-
- public function testSetValueStringIsUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1');
- $this->assertSame(true, $config->setValueString('feed', 'string', 'value-2'));
- }
-
- public function testSetValueStringIsNotUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1');
- $this->assertSame(false, $config->setValueString('feed', 'string', 'value-1'));
- }
-
- public function testSetValueStringIsUpdatedCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1');
- $config->setValueString('feed', 'string', 'value-2');
- $status = $config->statusCache();
- $this->assertSame('value-2', $status['fastCache']['feed']['string']);
- }
-
- public function testSetValueStringIsUpdatedDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1');
- $config->setValueString('feed', 'string', 'value-2');
- $config->clearCache();
- $this->assertSame('value-2', $config->getValueString('feed', 'string', ''));
- }
-
- public function testSetValueInt(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42);
- $this->assertSame(42, $config->getValueInt('feed', 'int', 0));
- }
-
- public function testSetValueIntCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42);
- $status = $config->statusCache();
- $this->assertSame('42', $status['fastCache']['feed']['int']);
- }
-
- public function testSetValueIntDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42);
- $config->clearCache();
- $this->assertSame(42, $config->getValueInt('feed', 'int', 0));
- }
-
- public function testSetValueIntIsUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42);
- $this->assertSame(true, $config->setValueInt('feed', 'int', 17));
- }
-
- public function testSetValueIntIsNotUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42);
- $this->assertSame(false, $config->setValueInt('feed', 'int', 42));
- }
-
- public function testSetValueIntIsUpdatedCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42);
- $config->setValueInt('feed', 'int', 17);
- $status = $config->statusCache();
- $this->assertSame('17', $status['fastCache']['feed']['int']);
- }
-
- public function testSetValueIntIsUpdatedDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42);
- $config->setValueInt('feed', 'int', 17);
- $config->clearCache();
- $this->assertSame(17, $config->getValueInt('feed', 'int', 0));
- }
-
- public function testSetValueFloat(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14);
- $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0));
- }
-
- public function testSetValueFloatCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14);
- $status = $config->statusCache();
- $this->assertSame('3.14', $status['fastCache']['feed']['float']);
- }
-
- public function testSetValueFloatDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14);
- $config->clearCache();
- $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0));
- }
-
- public function testSetValueFloatIsUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14);
- $this->assertSame(true, $config->setValueFloat('feed', 'float', 1.23));
- }
-
- public function testSetValueFloatIsNotUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14);
- $this->assertSame(false, $config->setValueFloat('feed', 'float', 3.14));
- }
-
- public function testSetValueFloatIsUpdatedCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14);
- $config->setValueFloat('feed', 'float', 1.23);
- $status = $config->statusCache();
- $this->assertSame('1.23', $status['fastCache']['feed']['float']);
- }
-
- public function testSetValueFloatIsUpdatedDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14);
- $config->setValueFloat('feed', 'float', 1.23);
- $config->clearCache();
- $this->assertSame(1.23, $config->getValueFloat('feed', 'float', 0));
- }
-
- public function testSetValueBool(): void {
- $config = $this->generateAppConfig();
- $config->setValueBool('feed', 'bool', true);
- $this->assertSame(true, $config->getValueBool('feed', 'bool', false));
- }
-
- public function testSetValueBoolCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueBool('feed', 'bool', true);
- $status = $config->statusCache();
- $this->assertSame('1', $status['fastCache']['feed']['bool']);
- }
-
- public function testSetValueBoolDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueBool('feed', 'bool', true);
- $config->clearCache();
- $this->assertSame(true, $config->getValueBool('feed', 'bool', false));
- }
-
- public function testSetValueBoolIsUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueBool('feed', 'bool', true);
- $this->assertSame(true, $config->setValueBool('feed', 'bool', false));
- }
-
- public function testSetValueBoolIsNotUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueBool('feed', 'bool', true);
- $this->assertSame(false, $config->setValueBool('feed', 'bool', true));
- }
-
- public function testSetValueBoolIsUpdatedCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueBool('feed', 'bool', true);
- $config->setValueBool('feed', 'bool', false);
- $status = $config->statusCache();
- $this->assertSame('0', $status['fastCache']['feed']['bool']);
- }
-
- public function testSetValueBoolIsUpdatedDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueBool('feed', 'bool', true);
- $config->setValueBool('feed', 'bool', false);
- $config->clearCache();
- $this->assertSame(false, $config->getValueBool('feed', 'bool', true));
- }
-
- public function testSetValueArray(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1]);
- $this->assertSame(['test' => 1], $config->getValueArray('feed', 'array', []));
- }
-
- public function testSetValueArrayCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1]);
- $status = $config->statusCache();
- $this->assertSame('{"test":1}', $status['fastCache']['feed']['array']);
- }
-
- public function testSetValueArrayDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1]);
- $config->clearCache();
- $this->assertSame(['test' => 1], $config->getValueArray('feed', 'array', []));
- }
-
- public function testSetValueArrayIsUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1]);
- $this->assertSame(true, $config->setValueArray('feed', 'array', ['test' => 2]));
- }
-
- public function testSetValueArrayIsNotUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1]);
- $this->assertSame(false, $config->setValueArray('feed', 'array', ['test' => 1]));
- }
-
- public function testSetValueArrayIsUpdatedCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1]);
- $config->setValueArray('feed', 'array', ['test' => 2]);
- $status = $config->statusCache();
- $this->assertSame('{"test":2}', $status['fastCache']['feed']['array']);
- }
-
- public function testSetValueArrayIsUpdatedDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1]);
- $config->setValueArray('feed', 'array', ['test' => 2]);
- $config->clearCache();
- $this->assertSame(['test' => 2], $config->getValueArray('feed', 'array', []));
- }
-
- public function testSetLazyValueString(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1', true);
- $this->assertSame('value-1', $config->getValueString('feed', 'string', '', true));
- }
-
- public function testSetLazyValueStringCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1', true);
- $status = $config->statusCache();
- $this->assertSame('value-1', $status['lazyCache']['feed']['string']);
- }
-
- public function testSetLazyValueStringDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1', true);
- $config->clearCache();
- $this->assertSame('value-1', $config->getValueString('feed', 'string', '', true));
- }
-
- public function testSetLazyValueStringAsNonLazy(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1', true);
- $config->setValueString('feed', 'string', 'value-1', false);
- $this->assertSame('value-1', $config->getValueString('feed', 'string', ''));
- }
-
- public function testSetNonLazyValueStringAsLazy(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1', false);
- $config->setValueString('feed', 'string', 'value-1', true);
- $this->assertSame('value-1', $config->getValueString('feed', 'string', '', true));
- }
-
- public function testSetSensitiveValueString(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1', sensitive: true);
- $this->assertSame('value-1', $config->getValueString('feed', 'string', ''));
- }
-
- public function testSetSensitiveValueStringCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1', sensitive: true);
- $status = $config->statusCache();
- $this->assertStringStartsWith(self::invokePrivate(AppConfig::class, 'ENCRYPTION_PREFIX'), $status['fastCache']['feed']['string']);
- }
-
- public function testSetSensitiveValueStringDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1', sensitive: true);
- $config->clearCache();
- $this->assertSame('value-1', $config->getValueString('feed', 'string', ''));
- }
-
- public function testSetNonSensitiveValueStringAsSensitive(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1', sensitive: false);
- $config->setValueString('feed', 'string', 'value-1', sensitive: true);
- $this->assertSame(true, $config->isSensitive('feed', 'string'));
-
- $this->assertConfigValueNotEquals('feed', 'string', 'value-1');
- $this->assertConfigValueNotEquals('feed', 'string', 'value-2');
- }
-
- public function testSetSensitiveValueStringAsNonSensitiveStaysSensitive(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1', sensitive: true);
- $config->setValueString('feed', 'string', 'value-2', sensitive: false);
- $this->assertSame(true, $config->isSensitive('feed', 'string'));
-
- $this->assertConfigValueNotEquals('feed', 'string', 'value-1');
- $this->assertConfigValueNotEquals('feed', 'string', 'value-2');
- }
-
- public function testSetSensitiveValueStringAsNonSensitiveAreStillUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', 'value-1', sensitive: true);
- $config->setValueString('feed', 'string', 'value-2', sensitive: false);
- $this->assertSame('value-2', $config->getValueString('feed', 'string', ''));
-
- $this->assertConfigValueNotEquals('feed', 'string', 'value-1');
- $this->assertConfigValueNotEquals('feed', 'string', 'value-2');
- }
-
- public function testSetLazyValueInt(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42, true);
- $this->assertSame(42, $config->getValueInt('feed', 'int', 0, true));
- }
-
- public function testSetLazyValueIntCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42, true);
- $status = $config->statusCache();
- $this->assertSame('42', $status['lazyCache']['feed']['int']);
- }
-
- public function testSetLazyValueIntDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42, true);
- $config->clearCache();
- $this->assertSame(42, $config->getValueInt('feed', 'int', 0, true));
- }
-
- public function testSetLazyValueIntAsNonLazy(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42, true);
- $config->setValueInt('feed', 'int', 42, false);
- $this->assertSame(42, $config->getValueInt('feed', 'int', 0));
- }
-
- public function testSetNonLazyValueIntAsLazy(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42, false);
- $config->setValueInt('feed', 'int', 42, true);
- $this->assertSame(42, $config->getValueInt('feed', 'int', 0, true));
- }
-
- public function testSetSensitiveValueInt(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42, sensitive: true);
- $this->assertSame(42, $config->getValueInt('feed', 'int', 0));
- }
-
- public function testSetSensitiveValueIntCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42, sensitive: true);
- $status = $config->statusCache();
- $this->assertStringStartsWith(self::invokePrivate(AppConfig::class, 'ENCRYPTION_PREFIX'), $status['fastCache']['feed']['int']);
- }
-
- public function testSetSensitiveValueIntDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42, sensitive: true);
- $config->clearCache();
- $this->assertSame(42, $config->getValueInt('feed', 'int', 0));
- }
-
- public function testSetNonSensitiveValueIntAsSensitive(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42);
- $config->setValueInt('feed', 'int', 42, sensitive: true);
- $this->assertSame(true, $config->isSensitive('feed', 'int'));
- }
-
- public function testSetSensitiveValueIntAsNonSensitiveStaysSensitive(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42, sensitive: true);
- $config->setValueInt('feed', 'int', 17);
- $this->assertSame(true, $config->isSensitive('feed', 'int'));
- }
-
- public function testSetSensitiveValueIntAsNonSensitiveAreStillUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueInt('feed', 'int', 42, sensitive: true);
- $config->setValueInt('feed', 'int', 17);
- $this->assertSame(17, $config->getValueInt('feed', 'int', 0));
- }
-
- public function testSetLazyValueFloat(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14, true);
- $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0, true));
- }
-
- public function testSetLazyValueFloatCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14, true);
- $status = $config->statusCache();
- $this->assertSame('3.14', $status['lazyCache']['feed']['float']);
- }
-
- public function testSetLazyValueFloatDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14, true);
- $config->clearCache();
- $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0, true));
- }
-
- public function testSetLazyValueFloatAsNonLazy(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14, true);
- $config->setValueFloat('feed', 'float', 3.14, false);
- $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0));
- }
-
- public function testSetNonLazyValueFloatAsLazy(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14, false);
- $config->setValueFloat('feed', 'float', 3.14, true);
- $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0, true));
- }
-
- public function testSetSensitiveValueFloat(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14, sensitive: true);
- $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0));
- }
-
- public function testSetSensitiveValueFloatCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14, sensitive: true);
- $status = $config->statusCache();
- $this->assertStringStartsWith(self::invokePrivate(AppConfig::class, 'ENCRYPTION_PREFIX'), $status['fastCache']['feed']['float']);
- }
-
- public function testSetSensitiveValueFloatDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14, sensitive: true);
- $config->clearCache();
- $this->assertSame(3.14, $config->getValueFloat('feed', 'float', 0));
- }
-
- public function testSetNonSensitiveValueFloatAsSensitive(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14);
- $config->setValueFloat('feed', 'float', 3.14, sensitive: true);
- $this->assertSame(true, $config->isSensitive('feed', 'float'));
- }
-
- public function testSetSensitiveValueFloatAsNonSensitiveStaysSensitive(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14, sensitive: true);
- $config->setValueFloat('feed', 'float', 1.23);
- $this->assertSame(true, $config->isSensitive('feed', 'float'));
- }
-
- public function testSetSensitiveValueFloatAsNonSensitiveAreStillUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueFloat('feed', 'float', 3.14, sensitive: true);
- $config->setValueFloat('feed', 'float', 1.23);
- $this->assertSame(1.23, $config->getValueFloat('feed', 'float', 0));
- }
-
- public function testSetLazyValueBool(): void {
- $config = $this->generateAppConfig();
- $config->setValueBool('feed', 'bool', true, true);
- $this->assertSame(true, $config->getValueBool('feed', 'bool', false, true));
- }
-
- public function testSetLazyValueBoolCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueBool('feed', 'bool', true, true);
- $status = $config->statusCache();
- $this->assertSame('1', $status['lazyCache']['feed']['bool']);
- }
-
- public function testSetLazyValueBoolDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueBool('feed', 'bool', true, true);
- $config->clearCache();
- $this->assertSame(true, $config->getValueBool('feed', 'bool', false, true));
- }
-
- public function testSetLazyValueBoolAsNonLazy(): void {
- $config = $this->generateAppConfig();
- $config->setValueBool('feed', 'bool', true, true);
- $config->setValueBool('feed', 'bool', true, false);
- $this->assertSame(true, $config->getValueBool('feed', 'bool', false));
- }
-
- public function testSetNonLazyValueBoolAsLazy(): void {
- $config = $this->generateAppConfig();
- $config->setValueBool('feed', 'bool', true, false);
- $config->setValueBool('feed', 'bool', true, true);
- $this->assertSame(true, $config->getValueBool('feed', 'bool', false, true));
- }
-
- public function testSetLazyValueArray(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1], true);
- $this->assertSame(['test' => 1], $config->getValueArray('feed', 'array', [], true));
- }
-
- public function testSetLazyValueArrayCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1], true);
- $status = $config->statusCache();
- $this->assertSame('{"test":1}', $status['lazyCache']['feed']['array']);
- }
-
- public function testSetLazyValueArrayDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1], true);
- $config->clearCache();
- $this->assertSame(['test' => 1], $config->getValueArray('feed', 'array', [], true));
- }
-
- public function testSetLazyValueArrayAsNonLazy(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1], true);
- $config->setValueArray('feed', 'array', ['test' => 1], false);
- $this->assertSame(['test' => 1], $config->getValueArray('feed', 'array', []));
- }
-
- public function testSetNonLazyValueArrayAsLazy(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1], false);
- $config->setValueArray('feed', 'array', ['test' => 1], true);
- $this->assertSame(['test' => 1], $config->getValueArray('feed', 'array', [], true));
- }
-
- public function testSetSensitiveValueArray(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1], sensitive: true);
- $this->assertEqualsCanonicalizing(['test' => 1], $config->getValueArray('feed', 'array', []));
- }
-
- public function testSetSensitiveValueArrayCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1], sensitive: true);
- $status = $config->statusCache();
- $this->assertStringStartsWith(self::invokePrivate(AppConfig::class, 'ENCRYPTION_PREFIX'), $status['fastCache']['feed']['array']);
- }
-
- public function testSetSensitiveValueArrayDatabase(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1], sensitive: true);
- $config->clearCache();
- $this->assertEqualsCanonicalizing(['test' => 1], $config->getValueArray('feed', 'array', []));
- }
-
- public function testSetNonSensitiveValueArrayAsSensitive(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1]);
- $config->setValueArray('feed', 'array', ['test' => 1], sensitive: true);
- $this->assertSame(true, $config->isSensitive('feed', 'array'));
- }
-
- public function testSetSensitiveValueArrayAsNonSensitiveStaysSensitive(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1], sensitive: true);
- $config->setValueArray('feed', 'array', ['test' => 2]);
- $this->assertSame(true, $config->isSensitive('feed', 'array'));
- }
-
- public function testSetSensitiveValueArrayAsNonSensitiveAreStillUpdated(): void {
- $config = $this->generateAppConfig();
- $config->setValueArray('feed', 'array', ['test' => 1], sensitive: true);
- $config->setValueArray('feed', 'array', ['test' => 2]);
- $this->assertEqualsCanonicalizing(['test' => 2], $config->getValueArray('feed', 'array', []));
- }
-
- public function testUpdateNotSensitiveToSensitive(): void {
- $config = $this->generateAppConfig();
- $config->updateSensitive('non-sensitive-app', 'lazy-key', true);
- $this->assertSame(true, $config->isSensitive('non-sensitive-app', 'lazy-key', true));
- }
-
- public function testUpdateSensitiveToNotSensitive(): void {
- $config = $this->generateAppConfig();
- $config->updateSensitive('sensitive-app', 'lazy-key', false);
- $this->assertSame(false, $config->isSensitive('sensitive-app', 'lazy-key', true));
- }
-
- public function testUpdateSensitiveToSensitiveReturnsFalse(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(false, $config->updateSensitive('sensitive-app', 'lazy-key', true));
- }
-
- public function testUpdateNotSensitiveToNotSensitiveReturnsFalse(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(false, $config->updateSensitive('non-sensitive-app', 'lazy-key', false));
- }
-
- public function testUpdateSensitiveOnUnknownKeyReturnsFalse(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(false, $config->updateSensitive('non-sensitive-app', 'unknown-key', true));
- }
-
- public function testUpdateNotLazyToLazy(): void {
- $config = $this->generateAppConfig();
- $config->updateLazy('non-sensitive-app', 'non-lazy-key', true);
- $this->assertSame(true, $config->isLazy('non-sensitive-app', 'non-lazy-key'));
- }
-
- public function testUpdateLazyToNotLazy(): void {
- $config = $this->generateAppConfig();
- $config->updateLazy('non-sensitive-app', 'lazy-key', false);
- $this->assertSame(false, $config->isLazy('non-sensitive-app', 'lazy-key'));
- }
-
- public function testUpdateLazyToLazyReturnsFalse(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(false, $config->updateLazy('non-sensitive-app', 'lazy-key', true));
- }
-
- public function testUpdateNotLazyToNotLazyReturnsFalse(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(false, $config->updateLazy('non-sensitive-app', 'non-lazy-key', false));
- }
-
- public function testUpdateLazyOnUnknownKeyReturnsFalse(): void {
- $config = $this->generateAppConfig();
- $this->assertSame(false, $config->updateLazy('non-sensitive-app', 'unknown-key', true));
- }
-
- public function testGetDetails(): void {
- $config = $this->generateAppConfig();
- $this->assertEquals(
- [
- 'app' => 'non-sensitive-app',
- 'key' => 'lazy-key',
- 'value' => 'value',
- 'type' => 4,
- 'lazy' => true,
- 'typeString' => 'string',
- 'sensitive' => false,
- ],
- $config->getDetails('non-sensitive-app', 'lazy-key')
- );
- }
-
- public function testGetDetailsSensitive(): void {
- $config = $this->generateAppConfig();
- $this->assertEquals(
- [
- 'app' => 'sensitive-app',
- 'key' => 'lazy-key',
- 'value' => 'value',
- 'type' => 4,
- 'lazy' => true,
- 'typeString' => 'string',
- 'sensitive' => true,
- ],
- $config->getDetails('sensitive-app', 'lazy-key')
- );
- }
-
- public function testGetDetailsInt(): void {
- $config = $this->generateAppConfig();
- $this->assertEquals(
- [
- 'app' => 'typed',
- 'key' => 'int',
- 'value' => '42',
- 'type' => 8,
- 'lazy' => false,
- 'typeString' => 'integer',
- 'sensitive' => false
- ],
- $config->getDetails('typed', 'int')
- );
- }
-
- public function testGetDetailsFloat(): void {
- $config = $this->generateAppConfig();
- $this->assertEquals(
- [
- 'app' => 'typed',
- 'key' => 'float',
- 'value' => '3.14',
- 'type' => 16,
- 'lazy' => false,
- 'typeString' => 'float',
- 'sensitive' => false
- ],
- $config->getDetails('typed', 'float')
- );
- }
-
- public function testGetDetailsBool(): void {
- $config = $this->generateAppConfig();
- $this->assertEquals(
- [
- 'app' => 'typed',
- 'key' => 'bool',
- 'value' => '1',
- 'type' => 32,
- 'lazy' => false,
- 'typeString' => 'boolean',
- 'sensitive' => false
- ],
- $config->getDetails('typed', 'bool')
- );
- }
-
- public function testGetDetailsArray(): void {
- $config = $this->generateAppConfig();
- $this->assertEquals(
- [
- 'app' => 'typed',
- 'key' => 'array',
- 'value' => '{"test": 1}',
- 'type' => 64,
- 'lazy' => false,
- 'typeString' => 'array',
- 'sensitive' => false
- ],
- $config->getDetails('typed', 'array')
- );
- }
-
- public function testDeleteKey(): void {
- $config = $this->generateAppConfig();
- $config->deleteKey('anotherapp', 'key');
- $this->assertSame('default', $config->getValueString('anotherapp', 'key', 'default'));
- }
-
- public function testDeleteKeyCache(): void {
- $config = $this->generateAppConfig();
- $config->deleteKey('anotherapp', 'key');
- $status = $config->statusCache();
- $this->assertEqualsCanonicalizing(['enabled' => 'no', 'installed_version' => '3.2.1'], $status['fastCache']['anotherapp']);
- }
-
- public function testDeleteKeyDatabase(): void {
- $config = $this->generateAppConfig();
- $config->deleteKey('anotherapp', 'key');
- $config->clearCache();
- $this->assertSame('default', $config->getValueString('anotherapp', 'key', 'default'));
- }
-
- public function testDeleteApp(): void {
- $config = $this->generateAppConfig();
- $config->deleteApp('anotherapp');
- $this->assertSame('default', $config->getValueString('anotherapp', 'key', 'default'));
- $this->assertSame('default', $config->getValueString('anotherapp', 'enabled', 'default'));
- }
-
- public function testDeleteAppCache(): void {
- $config = $this->generateAppConfig();
- $status = $config->statusCache();
- $this->assertSame(true, isset($status['fastCache']['anotherapp']));
- $config->deleteApp('anotherapp');
- $status = $config->statusCache();
- $this->assertSame(false, isset($status['fastCache']['anotherapp']));
- }
-
- public function testDeleteAppDatabase(): void {
- $config = $this->generateAppConfig();
- $config->deleteApp('anotherapp');
- $config->clearCache();
- $this->assertSame('default', $config->getValueString('anotherapp', 'key', 'default'));
- $this->assertSame('default', $config->getValueString('anotherapp', 'enabled', 'default'));
- }
-
- public function testClearCache(): void {
- $config = $this->generateAppConfig();
- $config->setValueString('feed', 'string', '123454');
- $config->clearCache();
- $status = $config->statusCache();
- $this->assertSame([], $status['fastCache']);
- }
-
- public function testSensitiveValuesAreEncrypted(): void {
- $key = self::getUniqueID('secret');
-
- $appConfig = $this->generateAppConfig();
- $secret = md5((string)time());
- $appConfig->setValueString('testapp', $key, $secret, sensitive: true);
-
- $this->assertConfigValueNotEquals('testapp', $key, $secret);
-
- // Can get in same run
- $actualSecret = $appConfig->getValueString('testapp', $key);
- $this->assertEquals($secret, $actualSecret);
-
- // Can get freshly decrypted from DB
- $newAppConfig = $this->generateAppConfig();
- $actualSecret = $newAppConfig->getValueString('testapp', $key);
- $this->assertEquals($secret, $actualSecret);
- }
-
- public function testMigratingNonSensitiveValueToSensitiveWithSetValue(): void {
- $key = self::getUniqueID('secret');
- $appConfig = $this->generateAppConfig();
- $secret = sha1((string)time());
-
- // Unencrypted
- $appConfig->setValueString('testapp', $key, $secret);
- $this->assertConfigKey('testapp', $key, $secret);
-
- // Can get freshly decrypted from DB
- $newAppConfig = $this->generateAppConfig();
- $actualSecret = $newAppConfig->getValueString('testapp', $key);
- $this->assertEquals($secret, $actualSecret);
-
- // Encrypting on change
- $appConfig->setValueString('testapp', $key, $secret, sensitive: true);
- $this->assertConfigValueNotEquals('testapp', $key, $secret);
-
- // Can get in same run
- $actualSecret = $appConfig->getValueString('testapp', $key);
- $this->assertEquals($secret, $actualSecret);
-
- // Can get freshly decrypted from DB
- $newAppConfig = $this->generateAppConfig();
- $actualSecret = $newAppConfig->getValueString('testapp', $key);
- $this->assertEquals($secret, $actualSecret);
- }
-
- public function testUpdateSensitiveValueToNonSensitiveWithUpdateSensitive(): void {
- $key = self::getUniqueID('secret');
- $appConfig = $this->generateAppConfig();
- $secret = sha1((string)time());
-
- // Encrypted
- $appConfig->setValueString('testapp', $key, $secret, sensitive: true);
- $this->assertConfigValueNotEquals('testapp', $key, $secret);
-
- // Migrate to non-sensitive / non-encrypted
- $appConfig->updateSensitive('testapp', $key, false);
- $this->assertConfigKey('testapp', $key, $secret);
- }
-
- public function testUpdateNonSensitiveValueToSensitiveWithUpdateSensitive(): void {
- $key = self::getUniqueID('secret');
- $appConfig = $this->generateAppConfig();
- $secret = sha1((string)time());
-
- // Unencrypted
- $appConfig->setValueString('testapp', $key, $secret);
- $this->assertConfigKey('testapp', $key, $secret);
-
- // Migrate to sensitive / encrypted
- $appConfig->updateSensitive('testapp', $key, true);
- $this->assertConfigValueNotEquals('testapp', $key, $secret);
- }
-
- public function testSearchKeyNoLazyLoading(): void {
- $appConfig = $this->generateAppConfig();
- $appConfig->searchKeys('searchtest', 'search_');
- $status = $appConfig->statusCache();
- $this->assertFalse($status['lazyLoaded'], 'searchKeys() loaded lazy config');
- }
-
- public function testSearchKeyFast(): void {
- $appConfig = $this->generateAppConfig();
- $this->assertEquals(['search_key1', 'search_key2', 'search_key3'], $appConfig->searchKeys('searchtest', 'search_'));
- }
-
- public function testSearchKeyLazy(): void {
- $appConfig = $this->generateAppConfig();
- $this->assertEquals(['search_key5_lazy'], $appConfig->searchKeys('searchtest', 'search_', true));
- }
-
- protected function loadConfigValueFromDatabase(string $app, string $key): string|false {
- $sql = $this->connection->getQueryBuilder();
- $sql->select('configvalue')
- ->from('appconfig')
- ->where($sql->expr()->eq('appid', $sql->createParameter('appid')))
- ->andWhere($sql->expr()->eq('configkey', $sql->createParameter('configkey')))
- ->setParameter('appid', $app)
- ->setParameter('configkey', $key);
- $query = $sql->executeQuery();
- $actual = $query->fetchOne();
- $query->closeCursor();
-
- return $actual;
- }
-
- protected function assertConfigKey(string $app, string $key, string|false $expected): void {
- $this->assertEquals($expected, $this->loadConfigValueFromDatabase($app, $key));
- }
-
- protected function assertConfigValueNotEquals(string $app, string $key, string|false $expected): void {
- $this->assertNotEquals($expected, $this->loadConfigValueFromDatabase($app, $key));
- }
-}
--- a/tests/lib/AppConfigMigrationFallbackTest.php
+++ /dev/null
@@ -1,186 +0,0 @@
-connection = $this->createMock(IDBConnection::class);
- $this->config = $this->createMock(IConfig::class);
- $this->configManager = $this->createMock(ConfigManager::class);
- $this->presetManager = $this->createMock(PresetManager::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->crypto = $this->createMock(ICrypto::class);
- $this->cacheFactory = $this->createMock(CacheFactory::class);
- }
-
- private function getAppConfig(): AppConfig {
- $this->config->method('getSystemValueBool')
- ->with('cache_app_config', true)
- ->willReturn(true);
- $this->cacheFactory->method('isLocalCacheAvailable')->willReturn(false);
-
- return new AppConfig(
- $this->connection,
- $this->config,
- $this->configManager,
- $this->presetManager,
- $this->logger,
- $this->crypto,
- $this->cacheFactory,
- );
- }
-
- private function createInvalidFieldNameException(): DBException {
- $driverException = $this->createMock(\Doctrine\DBAL\Driver\Exception::class);
- $dbalException = new InvalidFieldNameException($driverException, null);
- return DbalException::wrap($dbalException);
- }
-
- private function createMockQueryBuilder(): IQueryBuilder&MockObject {
- $expression = $this->createMock(IExpressionBuilder::class);
- $qb = $this->createMock(IQueryBuilder::class);
- $qb->method('from')->willReturn($qb);
- $qb->method('select')->willReturn($qb);
- $qb->method('addSelect')->willReturn($qb);
- $qb->method('where')->willReturn($qb);
- $qb->method('andWhere')->willReturn($qb);
- $qb->method('set')->willReturn($qb);
- $qb->method('expr')->willReturn($expression);
- $qb->method('insert')->willReturn($qb);
- $qb->method('setValue')->willReturn($qb);
- $qb->method('createNamedParameter')->willReturn('?');
- return $qb;
- }
-
- /**
- * Test that loadConfig retries without type/lazy columns on InvalidFieldNameException.
- */
- public function testLoadConfigFallsBackOnMissingColumns(): void {
- $exception = $this->createInvalidFieldNameException();
-
- $successResult = $this->createMock(IResult::class);
- $successResult->method('fetchAllAssociative')->willReturn([
- ['appid' => 'core', 'configkey' => 'vendor', 'configvalue' => 'owncloud'],
- ['appid' => 'core', 'configkey' => 'installedat', 'configvalue' => '1234567890'],
- ]);
-
- $qb = $this->createMockQueryBuilder();
- // First call throws (columns missing), second call succeeds (fallback query)
- $qb->method('executeQuery')
- ->willReturnOnConsecutiveCalls(
- $this->throwException($exception),
- $successResult,
- );
-
- $this->connection->method('getQueryBuilder')->willReturn($qb);
-
- $appConfig = $this->getAppConfig();
-
- // getValueString triggers loadConfig internally
- $value = $appConfig->getValueString('core', 'vendor');
- $this->assertSame('owncloud', $value);
- }
-
- /**
- * Test that non-INVALID_FIELD_NAME exceptions are re-thrown, not swallowed.
- */
- public function testLoadConfigRethrowsOtherExceptions(): void {
- $driverException = $this->createMock(\Doctrine\DBAL\Driver\Exception::class);
- $dbalException = new \Doctrine\DBAL\Exception\SyntaxErrorException($driverException, null);
- $exception = DbalException::wrap($dbalException);
-
- $qb = $this->createMockQueryBuilder();
- $qb->method('executeQuery')->willThrowException($exception);
-
- $this->connection->method('getQueryBuilder')->willReturn($qb);
-
- $appConfig = $this->getAppConfig();
-
- $this->expectException(DBException::class);
- $appConfig->getValueString('core', 'vendor');
- }
-
- /**
- * Test that insert omits lazy/type columns when migration is not completed.
- */
- public function testInsertOmitsNewColumnsInFallbackMode(): void {
- $exception = $this->createInvalidFieldNameException();
-
- $loadResult = $this->createMock(IResult::class);
- $loadResult->method('fetchAllAssociative')->willReturn([]);
-
- $qb = $this->createMockQueryBuilder();
-
- $callCount = 0;
- $qb->method('executeQuery')
- ->willReturnCallback(function () use ($exception, $loadResult, &$callCount) {
- $callCount++;
- if ($callCount === 1) {
- throw $exception;
- }
- return $loadResult;
- });
-
- // Verify insert() is called (meaning we reached the insert path)
- $qb->expects(self::once())->method('insert')->with('appconfig')->willReturn($qb);
- $qb->method('executeStatement')->willReturn(1);
-
- // Track which columns are set via setValue
- $setColumns = [];
- $qb->method('setValue')
- ->willReturnCallback(function (string $column) use ($qb, &$setColumns) {
- $setColumns[] = $column;
- return $qb;
- });
-
- $this->connection->method('getQueryBuilder')->willReturn($qb);
-
- $appConfig = $this->getAppConfig();
- $appConfig->setValueString('core', 'vendor', 'owncloud');
-
- $this->assertContains('appid', $setColumns);
- $this->assertContains('configkey', $setColumns);
- $this->assertContains('configvalue', $setColumns);
- $this->assertNotContains('lazy', $setColumns, 'lazy column should be omitted in fallback mode');
- $this->assertNotContains('type', $setColumns, 'type column should be omitted in fallback mode');
- }
-}
--- a/tests/lib/AppConfigTest.php
+++ /dev/null
@@ -1,234 +0,0 @@
-connection = $this->createMock(IDBConnection::class);
- $this->config = $this->createMock(IConfig::class);
- $this->configManager = $this->createMock(ConfigManager::class);
- $this->presetManager = $this->createMock(PresetManager::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->crypto = $this->createMock(ICrypto::class);
- $this->cacheFactory = $this->createMock(CacheFactory::class);
- $this->localCache = $this->createMock(ICache::class);
- }
-
- protected function getAppConfig($cached = false): AppConfig {
- $this->config->method('getSystemValueBool')
- ->with('cache_app_config', true)
- ->willReturn(true);
- $this->cacheFactory->method('isLocalCacheAvailable')->willReturn($cached);
- if ($cached) {
- $this->cacheFactory->method('withServerVersionPrefix')->willReturnCallback(function (\Closure $closure): void {
- $closure($this->cacheFactory);
- });
- $this->cacheFactory->method('createLocal')->willReturn($this->localCache);
- }
-
- return new AppConfig(
- $this->connection,
- $this->config,
- $this->configManager,
- $this->presetManager,
- $this->logger,
- $this->crypto,
- $this->cacheFactory,
- );
- }
-
- public function testCachedRead(): void {
- $this->localCache->expects(self::once())
- ->method('get')
- ->with('OC\\AppConfig')
- ->willReturn([
- 'fastCache' => [
- 'appid' => [
- 'some-key' => 'some-value',
- 'other-key' => 'other value'
- ],
- ],
- 'valueTypes' => [
- 'appid' => [
- 'some-key' => AppConfig::VALUE_STRING,
- 'other-key' => AppConfig::VALUE_STRING,
- ],
- ],
- ]);
-
- $this->connection->expects(self::never())->method('getQueryBuilder');
- $config = $this->getAppConfig(true);
-
- $this->assertSame('some-value', $config->getValueString('appid', 'some-key'));
- $this->assertSame('other value', $config->getValueString('appid', 'other-key'));
- $this->assertSame(AppConfig::VALUE_STRING, $config->getValueType('appid', 'some-key', false));
- }
-
- public function testCachedLazyRead(): void {
- $this->localCache->expects(self::once())
- ->method('get')
- ->with('OC\\AppConfig')
- ->willReturn([
- 'fastCache' => [
- 'appid' => [
- 'fast-key' => 'fast value',
- ],
- ],
- 'lazyCache' => [
- 'appid' => [
- 'lazy-key' => 'lazy value',
- ],
- ],
- 'valueTypes' => [
- 'appid' => [
- 'some-key' => AppConfig::VALUE_STRING,
- 'lazy-key' => AppConfig::VALUE_STRING,
- ],
- ],
- ]);
-
- $this->connection->expects(self::never())->method('getQueryBuilder');
- $config = $this->getAppConfig(true);
-
- $this->assertSame('fast value', $config->getValueString('appid', 'fast-key'));
- $this->assertSame('lazy value', $config->getValueString('appid', 'lazy-key', '', true));
- }
-
- public function testOnlyFastKeyCached(): void {
- $this->localCache->expects(self::atLeastOnce())
- ->method('get')
- ->with('OC\\AppConfig')
- ->willReturn([
- 'fastCache' => [
- 'appid' => [
- 'fast-key' => 'fast value',
- ],
- ],
- 'valueTypes' => [
- 'appid' => [
- 'fast-key' => AppConfig::VALUE_STRING,
- ],
- ],
- ]);
-
- $result = $this->createMock(IResult::class);
- $result->method('fetchAllAssociative')->willReturn([
- ['lazy' => 1, 'appid' => 'appid', 'configkey' => 'lazy-key', 'configvalue' => 'lazy value'],
- ]);
- $expression = $this->createMock(IExpressionBuilder::class);
- $queryBuilder = $this->createMock(IQueryBuilder::class);
- $queryBuilder->method('from')->willReturn($queryBuilder);
- $queryBuilder->method('expr')->willReturn($expression);
- $queryBuilder->method('executeQuery')->willReturn($result);
-
- $this->connection->expects(self::once())->method('getQueryBuilder')->willReturn($queryBuilder);
- $config = $this->getAppConfig(true);
-
- $this->assertSame('fast value', $config->getValueString('appid', 'fast-key'));
- $this->assertSame('lazy value', $config->getValueString('appid', 'lazy-key', '', true));
- }
-
- public function testWritesAreCached(): void {
- $this->localCache->expects(self::atLeastOnce())
- ->method('get')
- ->with('OC\\AppConfig')
- ->willReturn([
- 'fastCache' => [
- 'appid' => [
- 'first-key' => 'first value',
- ],
- ],
- 'valueTypes' => [
- 'appid' => [
- 'first-key' => AppConfig::VALUE_STRING,
- ],
- ],
- ]);
-
- $expression = $this->createMock(IExpressionBuilder::class);
- $queryBuilder = $this->createMock(IQueryBuilder::class);
- $queryBuilder->expects(self::once())
- ->method('update')
- ->with('appconfig', null)
- ->willReturn($queryBuilder);
- $queryBuilder->method('set')->willReturn($queryBuilder);
- $queryBuilder->method('where')->willReturn($queryBuilder);
- $queryBuilder->method('andWhere')->willReturn($queryBuilder);
- $queryBuilder->method('expr')->willReturn($expression);
- $this->connection->expects(self::once())->method('getQueryBuilder')->willReturn($queryBuilder);
-
- $config = $this->getAppConfig(true);
-
- $this->assertSame('first value', $config->getValueString('appid', 'first-key'));
- $config->setValueString('appid', 'first-key', 'new value');
- $this->assertSame('new value', $config->getValueString('appid', 'first-key'));
- }
-
- public function testFilteredValuesMaskTheEuroOfficeSecret(): void {
- $this->localCache->expects(self::atLeastOnce())
- ->method('get')
- ->with('OC\\AppConfig')
- ->willReturn([
- 'fastCache' => [
- 'eurooffice' => [
- 'jwt_secret' => 'a-document-server-signing-key',
- 'jwt_header' => 'AuthorizationJwt',
- ],
- ],
- 'lazyCache' => [
- 'eurooffice' => [],
- ],
- 'valueTypes' => [
- 'eurooffice' => [
- 'jwt_secret' => AppConfig::VALUE_STRING,
- 'jwt_header' => AppConfig::VALUE_STRING,
- ],
- ],
- ]);
-
- $this->connection->expects(self::never())->method('getQueryBuilder');
- $config = $this->getAppConfig(true);
-
- $this->assertSame([
- 'jwt_secret' => IConfig::SENSITIVE_VALUE,
- 'jwt_header' => 'AuthorizationJwt',
- ], $config->getFilteredValues('eurooffice'));
- }
-}
--- a/tests/lib/AppFramework/AppTest.php
+++ /dev/null
@@ -1,214 +0,0 @@
-container = new DIContainer('test', []);
- $this->controller = $this->createMock(Controller::class);
- $this->dispatcher = $this->createMock(Dispatcher::class);
- $this->io = $this->createMock(IOutput::class);
-
- $this->headers = ['key' => 'value'];
- $this->output = 'hi';
- $this->controllerName = 'Controller';
- $this->controllerMethod = 'method';
-
- $this->container[$this->controllerName] = $this->controller;
- $this->container[Dispatcher::class] = $this->dispatcher;
- $this->container[IOutput::class] = $this->io;
- $this->container['urlParams'] = ['_route' => 'not-profiler'];
-
- $this->appPath = __DIR__ . '/../../../apps/namespacetestapp';
- $infoXmlPath = $this->appPath . '/appinfo/info.xml';
- mkdir($this->appPath . '/appinfo', 0777, true);
-
- $xml = ''
- . ''
- . 'namespacetestapp '
- . 'NameSpaceTestApp '
- . ' ';
- file_put_contents($infoXmlPath, $xml);
- }
-
- public function testControllerNameAndMethodAreBeingPassed(): void {
- $return = ['HTTP/2.0 200 OK', [], [], null, new Response()];
- $this->dispatcher->expects($this->once())
- ->method('dispatch')
- ->with($this->equalTo($this->controller),
- $this->equalTo($this->controllerMethod))
- ->willReturn($return);
-
- $this->io->expects($this->never())
- ->method('setOutput');
-
- App::main($this->controllerName, $this->controllerMethod,
- $this->container);
- }
-
- public function testBuildAppNamespace(): void {
- $ns = App::buildAppNamespace('someapp');
- $this->assertEquals('OCA\Someapp', $ns);
- }
-
- public function testBuildAppNamespaceCore(): void {
- $ns = App::buildAppNamespace('someapp', 'OC\\');
- $this->assertEquals('OC\Someapp', $ns);
- }
-
- public function testBuildAppNamespaceInfoXml(): void {
- $ns = App::buildAppNamespace('namespacetestapp', 'OCA\\');
- $this->assertEquals('OCA\NameSpaceTestApp', $ns);
- }
-
- #[\Override]
- protected function tearDown(): void {
- rrmdir($this->appPath);
- parent::tearDown();
- }
-
- public function testOutputIsPrinted(): void {
- $return = ['HTTP/2.0 200 OK', [], [], $this->output, new Response()];
- $this->dispatcher->expects($this->once())
- ->method('dispatch')
- ->with($this->equalTo($this->controller),
- $this->equalTo($this->controllerMethod))
- ->willReturn($return);
- $this->io->expects($this->once())
- ->method('setOutput')
- ->with($this->equalTo($this->output));
- App::main($this->controllerName, $this->controllerMethod, $this->container, []);
- }
-
- public static function dataNoOutput(): array {
- return [
- ['HTTP/2.0 204 No content'],
- ['HTTP/2.0 304 Not modified'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataNoOutput')]
- public function testNoOutput(string $statusCode): void {
- $return = [$statusCode, [], [], $this->output, new Response()];
- $this->dispatcher->expects($this->once())
- ->method('dispatch')
- ->with($this->equalTo($this->controller),
- $this->equalTo($this->controllerMethod))
- ->willReturn($return);
- $this->io->expects($this->once())
- ->method('setHeader')
- ->with($this->equalTo($statusCode));
- $this->io->expects($this->never())
- ->method('setOutput');
- App::main($this->controllerName, $this->controllerMethod, $this->container, []);
- }
-
- public function testCallbackIsCalled(): void {
- $mock = $this->getMockBuilder('OCP\AppFramework\Http\ICallbackResponse')
- ->getMock();
-
- $return = ['HTTP/2.0 200 OK', [], [], $this->output, $mock];
- $this->dispatcher->expects($this->once())
- ->method('dispatch')
- ->with($this->equalTo($this->controller),
- $this->equalTo($this->controllerMethod))
- ->willReturn($return);
- $mock->expects($this->once())
- ->method('callback');
- App::main($this->controllerName, $this->controllerMethod, $this->container, []);
- }
-
- public function testCoreApp(): void {
- $this->container['appName'] = 'core';
- $this->container['OC\Core\Controller\Foo'] = $this->controller;
- $this->container['urlParams'] = ['_route' => 'not-profiler'];
-
- $return = ['HTTP/2.0 200 OK', [], [], null, new Response()];
- $this->dispatcher->expects($this->once())
- ->method('dispatch')
- ->with($this->equalTo($this->controller),
- $this->equalTo($this->controllerMethod))
- ->willReturn($return);
-
- $this->io->expects($this->never())
- ->method('setOutput');
-
- App::main('Foo', $this->controllerMethod, $this->container);
- }
-
- public function testSettingsApp(): void {
- $this->container['appName'] = 'settings';
- $this->container['OCA\Settings\Controller\Foo'] = $this->controller;
- $this->container['urlParams'] = ['_route' => 'not-profiler'];
-
- $return = ['HTTP/2.0 200 OK', [], [], null, new Response()];
- $this->dispatcher->expects($this->once())
- ->method('dispatch')
- ->with($this->equalTo($this->controller),
- $this->equalTo($this->controllerMethod))
- ->willReturn($return);
-
- $this->io->expects($this->never())
- ->method('setOutput');
-
- App::main('Foo', $this->controllerMethod, $this->container);
- }
-
- public function testApp(): void {
- $this->container['appName'] = 'bar';
- $this->container['OCA\Bar\Controller\Foo'] = $this->controller;
- $this->container['urlParams'] = ['_route' => 'not-profiler'];
-
- $return = ['HTTP/2.0 200 OK', [], [], null, new Response()];
- $this->dispatcher->expects($this->once())
- ->method('dispatch')
- ->with($this->equalTo($this->controller),
- $this->equalTo($this->controllerMethod))
- ->willReturn($return);
-
- $this->io->expects($this->never())
- ->method('setOutput');
-
- App::main('Foo', $this->controllerMethod, $this->container);
- }
-}
--- a/tests/lib/AppFramework/Bootstrap/BootContextTest.php
+++ /dev/null
@@ -1,50 +0,0 @@
-appContainer = $this->createMock(IAppContainer::class);
-
- $this->context = new BootContext(
- $this->appContainer
- );
- }
-
- public function testGetAppContainer(): void {
- $container = $this->context->getAppContainer();
-
- $this->assertSame($this->appContainer, $container);
- }
-
- public function testGetServerContainer(): void {
- $serverContainer = $this->createMock(IServerContainer::class);
- $this->appContainer->method('get')
- ->with(IServerContainer::class)
- ->willReturn($serverContainer);
-
- $container = $this->context->getServerContainer();
-
- $this->assertSame($serverContainer, $container);
- }
-}
--- a/tests/lib/AppFramework/Bootstrap/CoordinatorTest.php
+++ /dev/null
@@ -1,112 +0,0 @@
-appManager = $this->createMock(AppManager::class);
- $this->serverContainer = $this->createMock(ContainerInterface::class);
- $this->crashReporterRegistry = $this->createMock(Registry::class);
- $this->dashboardManager = $this->createMock(IManager::class);
- $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
- $this->eventLogger = $this->createMock(IEventLogger::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $this->appManager->expects($this->any())
- ->method('getAppNamespace')
- ->with('settings')
- ->willReturn('OCA\\Settings');
-
- $this->coordinator = new Coordinator(
- $this->serverContainer,
- $this->crashReporterRegistry,
- $this->dashboardManager,
- $this->eventDispatcher,
- $this->eventLogger,
- $this->appManager,
- $this->logger,
- );
- }
-
- public function testBootAppNotLoadable(): void {
- $appId = 'settings';
- $this->serverContainer->expects($this->once())
- ->method('get')
- ->with(Application::class)
- ->willThrowException(new QueryException(''));
- $this->logger->expects($this->once())
- ->method('error');
-
- $this->coordinator->bootApp($appId);
- }
-
- public function testBootAppNotBootable(): void {
- $appId = 'settings';
- $mockApp = $this->createMock(Application::class);
- $this->serverContainer->expects($this->once())
- ->method('get')
- ->with(Application::class)
- ->willReturn($mockApp);
-
- $this->coordinator->bootApp($appId);
- }
-
- public function testBootApp(): void {
- $appId = 'settings';
- $mockApp = new class extends App implements IBootstrap {
- public function __construct() {
- parent::__construct('test', []);
- }
-
- #[\Override]
- public function register(IRegistrationContext $context): void {
- }
-
- #[\Override]
- public function boot(IBootContext $context): void {
- }
- };
- $this->serverContainer->expects($this->once())
- ->method('get')
- ->with(Application::class)
- ->willReturn($mockApp);
-
- $this->coordinator->bootApp($appId);
- }
-}
--- a/tests/lib/AppFramework/Bootstrap/FunctionInjectorTest.php
+++ /dev/null
@@ -1,68 +0,0 @@
-container = new SimpleContainer();
- }
-
- public function testInjectFnNotRegistered(): void {
- $this->expectException(ContainerExceptionInterface::class);
-
- (new FunctionInjector($this->container))->injectFn(static function (Foo $p1): void {
- });
- }
-
- public function testInjectFnNotRegisteredButNullable(): void {
- (new FunctionInjector($this->container))->injectFn(static function (?Foo $p1): void {
- });
-
- // Nothing to assert. No errors means everything is fine.
- $this->addToAssertionCount(1);
- }
-
- public function testInjectFnByType(): void {
- $this->container->registerService(Foo::class, function () {
- $this->addToAssertionCount(1);
- return new class implements Foo {
- };
- });
-
- (new FunctionInjector($this->container))->injectFn(static function (Foo $p1): void {
- });
-
- // Nothing to assert. No errors means everything is fine.
- $this->addToAssertionCount(1);
- }
-
- public function testInjectFnByName(): void {
- $this->container->registerParameter('test', 'abc');
-
- (new FunctionInjector($this->container))->injectFn(static function ($test): void {
- });
-
- // Nothing to assert. No errors means everything is fine.
- $this->addToAssertionCount(1);
- }
-}
--- a/tests/lib/AppFramework/Bootstrap/RegistrationContextTest.php
+++ /dev/null
@@ -1,171 +0,0 @@
-logger = $this->createMock(LoggerInterface::class);
-
- $this->context = new RegistrationContext(
- $this->logger
- );
- }
-
- public function testRegisterCapability(): void {
- $app = $this->createMock(App::class);
- $name = 'abc';
- $container = $this->createMock(DIContainer::class);
- $app->method('getContainer')
- ->willReturn($container);
- $container->expects($this->once())
- ->method('registerCapability')
- ->with($name);
- $this->logger->expects($this->never())
- ->method('error');
-
- $this->context->for('myapp')->registerCapability($name);
- $this->context->delegateCapabilityRegistrations([
- 'myapp' => $app,
- ]);
- }
-
- public function testRegisterEventListener(): void {
- $event = 'abc';
- $service = 'def';
- $dispatcher = $this->createMock(IEventDispatcher::class);
- $dispatcher->expects($this->once())
- ->method('addServiceListener')
- ->with($event, $service, 0);
- $this->logger->expects($this->never())
- ->method('error');
-
- $this->context->for('myapp')->registerEventListener($event, $service);
- $this->context->delegateEventListenerRegistrations($dispatcher);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataProvider_TrueFalse')]
- public function testRegisterService(bool $shared): void {
- $app = $this->createMock(App::class);
- $service = 'abc';
- $factory = function () {
- return 'def';
- };
- $container = $this->createMock(DIContainer::class);
- $app->method('getContainer')
- ->willReturn($container);
- $container->expects($this->once())
- ->method('registerService')
- ->with($service, $factory, $shared);
- $this->logger->expects($this->never())
- ->method('error');
-
- $this->context->for('myapp')->registerService($service, $factory, $shared);
- $this->context->delegateContainerRegistrations([
- 'myapp' => $app,
- ]);
- }
-
- public function testRegisterServiceAlias(): void {
- $app = $this->createMock(App::class);
- $alias = 'abc';
- $target = 'def';
- $container = $this->createMock(DIContainer::class);
- $app->method('getContainer')
- ->willReturn($container);
- $container->expects($this->once())
- ->method('registerAlias')
- ->with($alias, $target);
- $this->logger->expects($this->never())
- ->method('error');
-
- $this->context->for('myapp')->registerServiceAlias($alias, $target);
- $this->context->delegateContainerRegistrations([
- 'myapp' => $app,
- ]);
- }
-
- public function testRegisterParameter(): void {
- $app = $this->createMock(App::class);
- $name = 'abc';
- $value = 'def';
- $container = $this->createMock(DIContainer::class);
- $app->method('getContainer')
- ->willReturn($container);
- $container->expects($this->once())
- ->method('registerParameter')
- ->with($name, $value);
- $this->logger->expects($this->never())
- ->method('error');
-
- $this->context->for('myapp')->registerParameter($name, $value);
- $this->context->delegateContainerRegistrations([
- 'myapp' => $app,
- ]);
- }
-
- public function testRegisterUserMigrator(): void {
- $appIdA = 'myapp';
- $migratorClassA = 'OCA\App\UserMigration\AppMigrator';
-
- $appIdB = 'otherapp';
- $migratorClassB = 'OCA\OtherApp\UserMigration\OtherAppMigrator';
-
- $serviceRegistrationA = new ServiceRegistration($appIdA, $migratorClassA);
- $serviceRegistrationB = new ServiceRegistration($appIdB, $migratorClassB);
-
- $this->context
- ->for($appIdA)
- ->registerUserMigrator($migratorClassA);
- $this->context
- ->for($appIdB)
- ->registerUserMigrator($migratorClassB);
-
- $this->assertEquals(
- [
- $serviceRegistrationA,
- $serviceRegistrationB,
- ],
- $this->context->getUserMigrators(),
- );
- }
-
- public static function dataProvider_TrueFalse(): array {
- return[
- [true],
- [false]
- ];
- }
-
- public function testGetMiddlewareRegistrations(): void {
- $this->context->registerMiddleware('core', TwoFactorMiddleware::class, false);
-
- $registrations = $this->context->getMiddlewareRegistrations();
-
- self::assertNotEmpty($registrations);
- self::assertSame('core', $registrations[0]->getAppId());
- self::assertSame(TwoFactorMiddleware::class, $registrations[0]->getService());
- }
-}
--- a/tests/lib/AppFramework/Controller/ApiControllerTest.php
+++ /dev/null
@@ -1,43 +0,0 @@
- ['HTTP_ORIGIN' => 'test']],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $this->controller = new ChildApiController('app', $request, 'verbs',
- 'headers', 100);
-
- $response = $this->controller->preflightedCors();
-
- $headers = $response->getHeaders();
-
- $this->assertEquals('test', $headers['Access-Control-Allow-Origin']);
- $this->assertEquals('verbs', $headers['Access-Control-Allow-Methods']);
- $this->assertEquals('headers', $headers['Access-Control-Allow-Headers']);
- $this->assertEquals('false', $headers['Access-Control-Allow-Credentials']);
- $this->assertEquals(100, $headers['Access-Control-Max-Age']);
- }
-}
--- a/tests/lib/AppFramework/Controller/AuthPublicShareControllerTest.php
+++ /dev/null
@@ -1,137 +0,0 @@
-request = $this->createMock(IRequest::class);
- $this->session = $this->createMock(ISession::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
-
- $this->controller = $this->getMockBuilder(AuthPublicShareController::class)
- ->setConstructorArgs([
- 'app',
- $this->request,
- $this->session,
- $this->urlGenerator
- ])->onlyMethods([
- 'authFailed',
- 'getPasswordHash',
- 'isAuthenticated',
- 'isPasswordProtected',
- 'isValidToken',
- 'showShare',
- 'verifyPassword',
- 'validateIdentity',
- 'generatePassword'
- ])->getMock();
- }
-
- public function testShowAuthenticate(): void {
- $expects = new TemplateResponse('core', 'publicshareauth', [], 'guest');
-
- $this->assertEquals($expects, $this->controller->showAuthenticate());
- }
-
- public function testAuthenticateAuthenticated(): void {
- $this->controller->method('isAuthenticated')
- ->willReturn(true);
-
- $this->controller->setToken('myToken');
-
- $this->session->method('get')
- ->willReturnMap([
- ['public_link_authenticate_redirect', json_encode(['foo' => 'bar'])],
- ]);
-
- $this->urlGenerator->method('linkToRoute')
- ->willReturn('myLink!');
-
- $result = $this->controller->authenticate('password');
- $this->assertInstanceOf(RedirectResponse::class, $result);
- $this->assertSame('myLink!', $result->getRedirectURL());
- }
-
- public function testAuthenticateInvalidPassword(): void {
- $this->controller->setToken('token');
- $this->controller->method('isPasswordProtected')
- ->willReturn(true);
-
- $this->controller->method('verifyPassword')
- ->with('password')
- ->willReturn(false);
-
- $this->controller->expects($this->once())
- ->method('authFailed');
-
- $expects = new TemplateResponse('core', 'publicshareauth', ['wrongpw' => true], 'guest');
- $expects->throttle();
-
- $result = $this->controller->authenticate('password');
-
- $this->assertEquals($expects, $result);
- }
-
- public function testAuthenticateValidPassword(): void {
- $this->controller->setToken('token');
- $this->controller->method('isPasswordProtected')
- ->willReturn(true);
- $this->controller->method('verifyPassword')
- ->with('password')
- ->willReturn(true);
- $this->controller->method('getPasswordHash')
- ->willReturn('hash');
-
- $this->session->expects($this->once())
- ->method('regenerateId');
- $this->session->method('get')
- ->willReturnMap([
- ['public_link_authenticate_redirect', json_encode(['foo' => 'bar'])],
- ]);
-
- $tokenStored = false;
- $this->session
- ->method('set')
- ->willReturnCallback(function ($key, $value) use (&$tokenStored) {
- if ($key === AuthPublicShareController::DAV_AUTHENTICATED_FRONTEND) {
- $decoded = json_decode($value, true);
- if (isset($decoded['token']) && $decoded['token'] === 'hash') {
- $tokenStored = true;
- }
- return true;
- }
- return false;
- });
-
- $this->urlGenerator->method('linkToRoute')
- ->willReturn('myLink!');
-
- $result = $this->controller->authenticate('password');
- $this->assertInstanceOf(RedirectResponse::class, $result);
- $this->assertSame('myLink!', $result->getRedirectURL());
- $this->assertTrue($tokenStored);
- }
-}
--- a/tests/lib/AppFramework/Controller/ControllerTest.php
+++ /dev/null
@@ -1,128 +0,0 @@
-registerResponder('tom', function ($respone) {
- return 'hi';
- });
- }
-
- public function custom($in) {
- $this->registerResponder('json', function ($response) {
- return new JSONResponse([strlen($response)]);
- });
-
- return $in;
- }
-
- public function customDataResponse(mixed $in): DataResponse {
- $response = new DataResponse($in, 300);
- $response->addHeader('test', 'something');
- return $response;
- }
-};
-
-class ControllerTest extends \Test\TestCase {
- private Controller $controller;
- private Request $request;
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- $request = new Request(
- [
- 'get' => ['name' => 'John Q. Public', 'nickname' => 'Joey'],
- 'post' => ['name' => 'Jane Doe', 'nickname' => 'Janey'],
- 'urlParams' => ['name' => 'Johnny Weissmüller'],
- 'files' => ['file' => 'filevalue'],
- 'env' => ['PATH' => 'daheim'],
- 'session' => ['sezession' => 'kein'],
- 'method' => 'hi',
- ],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
-
- $this->controller = new ChildController('apptemplate_advanced', $request);
- $this->overwriteService(IRequest::class, $request);
- $this->request = $request;
- }
-
- public function testFormatResonseInvalidFormat(): void {
- $this->expectException(\DomainException::class);
-
- $this->controller->buildResponse(null, 'test');
- }
-
- public function testFormat(): void {
- $response = $this->controller->buildResponse(['hi'], 'json');
-
- $this->assertEquals(['hi'], $response->getData());
- }
-
- public function testFormatDataResponseJSON(): void {
- $expectedHeaders = [
- 'test' => 'something',
- 'Cache-Control' => 'no-cache, no-store, must-revalidate',
- 'Content-Type' => 'application/json; charset=utf-8',
- 'Content-Security-Policy' => "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'",
- 'Feature-Policy' => "autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone 'none';payment 'none'",
- 'X-Request-Id' => $this->request->getId(),
- 'X-Robots-Tag' => 'noindex, nofollow',
- ];
-
- $response = $this->controller->customDataResponse(['hi']);
- $response = $this->controller->buildResponse($response, 'json');
-
- $this->assertEquals(['hi'], $response->getData());
- $this->assertEquals(300, $response->getStatus());
- $this->assertEquals($expectedHeaders, $response->getHeaders());
- }
-
- public function testCustomFormatter(): void {
- $response = $this->controller->custom('hi');
- $response = $this->controller->buildResponse($response, 'json');
-
- $this->assertEquals([2], $response->getData());
- }
-
- public function testDefaultResponderToJSON(): void {
- $responder = $this->controller->getResponderByHTTPHeader('*/*');
-
- $this->assertEquals('json', $responder);
- }
-
- public function testResponderAcceptHeaderParsed(): void {
- $responder = $this->controller->getResponderByHTTPHeader(
- '*/*, application/tom, application/json'
- );
-
- $this->assertEquals('tom', $responder);
- }
-
- public function testResponderAcceptHeaderParsedUpperCase(): void {
- $responder = $this->controller->getResponderByHTTPHeader(
- '*/*, apPlication/ToM, application/json'
- );
-
- $this->assertEquals('tom', $responder);
- }
-}
--- a/tests/lib/AppFramework/Controller/OCSControllerTest.php
+++ /dev/null
@@ -1,135 +0,0 @@
- [
- 'HTTP_ORIGIN' => 'test',
- ],
- ],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $controller = new ChildOCSController('app', $request, 'verbs',
- 'headers', 100);
-
- $response = $controller->preflightedCors();
-
- $headers = $response->getHeaders();
-
- $this->assertEquals('test', $headers['Access-Control-Allow-Origin']);
- $this->assertEquals('verbs', $headers['Access-Control-Allow-Methods']);
- $this->assertEquals('headers', $headers['Access-Control-Allow-Headers']);
- $this->assertEquals('false', $headers['Access-Control-Allow-Credentials']);
- $this->assertEquals(100, $headers['Access-Control-Max-Age']);
- }
-
- public function testXML(): void {
- $controller = new ChildOCSController('app', new Request(
- [],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- ));
- $controller->setOCSVersion(1);
-
- $expected = "\n"
- . "\n"
- . " \n"
- . " ok \n"
- . " 100 \n"
- . " OK \n"
- . " \n"
- . " \n"
- . " \n"
- . " \n"
- . " hi \n"
- . " \n"
- . " \n";
-
- $params = new DataResponse(['test' => 'hi']);
-
- $response = $controller->buildResponse($params, 'xml');
- $this->assertSame(EmptyContentSecurityPolicy::class, get_class($response->getContentSecurityPolicy()));
- $this->assertEquals($expected, $response->render());
- }
-
- public function testJSON(): void {
- $controller = new ChildOCSController('app', new Request(
- [],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- ));
- $controller->setOCSVersion(1);
- $expected = '{"ocs":{"meta":{"status":"ok","statuscode":100,"message":"OK",'
- . '"totalitems":"","itemsperpage":""},"data":{"test":"hi"}}}';
- $params = new DataResponse(['test' => 'hi']);
-
- $response = $controller->buildResponse($params, 'json');
- $this->assertSame(EmptyContentSecurityPolicy::class, get_class($response->getContentSecurityPolicy()));
- $this->assertEquals($expected, $response->render());
- $this->assertEquals($expected, $response->render());
- }
-
- public function testXMLV2(): void {
- $controller = new ChildOCSController('app', new Request(
- [],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- ));
- $controller->setOCSVersion(2);
-
- $expected = "\n"
- . "\n"
- . " \n"
- . " ok \n"
- . " 200 \n"
- . " OK \n"
- . " \n"
- . " \n"
- . " hi \n"
- . " \n"
- . " \n";
-
- $params = new DataResponse(['test' => 'hi']);
-
- $response = $controller->buildResponse($params, 'xml');
- $this->assertSame(EmptyContentSecurityPolicy::class, get_class($response->getContentSecurityPolicy()));
- $this->assertEquals($expected, $response->render());
- }
-
- public function testJSONV2(): void {
- $controller = new ChildOCSController('app', new Request(
- [],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- ));
- $controller->setOCSVersion(2);
- $expected = '{"ocs":{"meta":{"status":"ok","statuscode":200,"message":"OK"},"data":{"test":"hi"}}}';
- $params = new DataResponse(['test' => 'hi']);
-
- $response = $controller->buildResponse($params, 'json');
- $this->assertSame(EmptyContentSecurityPolicy::class, get_class($response->getContentSecurityPolicy()));
- $this->assertEquals($expected, $response->render());
- }
-}
--- a/tests/lib/AppFramework/Controller/PublicShareControllerTest.php
+++ /dev/null
@@ -1,88 +0,0 @@
-hash;
- }
-
- #[\Override]
- public function isValidToken(): bool {
- return false;
- }
-
- #[\Override]
- protected function isPasswordProtected(): bool {
- return $this->isProtected;
- }
-}
-
-class PublicShareControllerTest extends \Test\TestCase {
- private IRequest&MockObject $request;
- private ISession&MockObject $session;
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- $this->request = $this->createMock(IRequest::class);
- $this->session = $this->createMock(ISession::class);
- }
-
- public function testGetToken(): void {
- $controller = new TestController('app', $this->request, $this->session, 'hash', false);
-
- $controller->setToken('test');
- $this->assertEquals('test', $controller->getToken());
- }
-
- public static function dataIsAuthenticated(): array {
- return [
- [false, 'token1', 'token1', 'hash1', 'hash1', true],
- [false, 'token1', 'token1', 'hash1', 'hash2', true],
- [false, 'token1', 'token2', 'hash1', 'hash1', true],
- [false, 'token1', 'token2', 'hash1', 'hash2', true],
- [ true, 'token1', 'token1', 'hash1', 'hash1', true],
- [ true, 'token1', 'token1', 'hash1', 'hash2', false],
- [ true, 'token1', 'token2', 'hash1', 'hash1', false],
- [ true, 'token1', 'token2', 'hash1', 'hash2', false],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataIsAuthenticated')]
- public function testIsAuthenticatedNotPasswordProtected(bool $protected, string $token1, string $token2, string $hash1, string $hash2, bool $expected): void {
- $controller = new TestController('app', $this->request, $this->session, $hash2, $protected);
-
- $this->session->method('get')
- ->with(PublicShareController::DAV_AUTHENTICATED_FRONTEND)
- ->willReturn("{\"$token1\":\"$hash1\"}");
-
- $controller->setToken($token2);
-
- $this->assertEquals($expected, $controller->isAuthenticated());
- }
-}
--- a/tests/lib/AppFramework/Db/EntityTest.php
+++ /dev/null
@@ -1,298 +0,0 @@
-addType('testId', Types::INTEGER);
- $this->addType('smallInt', Types::SMALLINT);
- $this->addType('bigInt', Types::BIGINT);
- $this->addType('anotherBool', Types::BOOLEAN);
- $this->addType('text', Types::TEXT);
- $this->addType('longText', Types::BLOB);
- $this->addType('time', Types::TIME);
- $this->addType('datetime', Types::DATETIME_IMMUTABLE);
-
- // Legacy types
- $this->addType('trueOrFalse', 'bool');
- $this->addType('legacyInt', 'int');
- $this->addType('doubleNowFloat', 'double');
- }
-
- public function setAnotherBool(bool $anotherBool): void {
- parent::setAnotherBool($anotherBool);
- }
-}
-
-class EntityTest extends \Test\TestCase {
- private TestEntity $entity;
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
- $this->entity = new TestEntity();
- }
-
- public function testResetUpdatedFields(): void {
- $entity = new TestEntity();
- $entity->setId(3);
- $entity->resetUpdatedFields();
-
- $this->assertEquals([], $entity->getUpdatedFields());
- }
-
- public function testFromRow(): void {
- $row = [
- 'pre_name' => 'john',
- 'email' => 'john@something.com',
- 'another_bool' => 1,
- ];
- $this->entity = TestEntity::fromRow($row);
-
- $this->assertEquals($row['pre_name'], $this->entity->getPreName());
- $this->assertEquals($row['email'], $this->entity->getEmail());
- $this->assertEquals($row['another_bool'], $this->entity->getAnotherBool());
- }
-
- public function testGetSetId(): void {
- $id = 3;
- $this->entity->setId(3);
-
- $this->assertEquals($id, $this->entity->getId());
- }
-
- public function testColumnToPropertyNoReplacement(): void {
- $column = 'my';
- $this->assertEquals('my',
- $this->entity->columnToProperty($column));
- }
-
- public function testColumnToProperty(): void {
- $column = 'my_attribute';
- $this->assertEquals('myAttribute',
- $this->entity->columnToProperty($column));
- }
-
- public function testPropertyToColumnNoReplacement(): void {
- $property = 'my';
- $this->assertEquals('my',
- $this->entity->propertyToColumn($property));
- }
-
- public function testSetterMarksFieldUpdated(): void {
- $this->entity->setId(3);
-
- $this->assertContains('id', array_keys($this->entity->getUpdatedFields()));
- }
-
- public function testCallShouldOnlyWorkForGetterSetter(): void {
- $this->expectException(\BadFunctionCallException::class);
-
- $this->entity->something();
- }
-
- public function testGetterShouldFailIfAttributeNotDefined(): void {
- $this->expectException(\BadFunctionCallException::class);
-
- $this->entity->getTest();
- }
-
- public function testSetterShouldFailIfAttributeNotDefined(): void {
- $this->expectException(\BadFunctionCallException::class);
-
- $this->entity->setTest();
- }
-
- public function testFromRowShouldNotAssignEmptyArray(): void {
- $row = [];
- $entity2 = new TestEntity();
-
- $this->entity = TestEntity::fromRow($row);
- $this->assertEquals($entity2, $this->entity);
- }
-
- public function testIdGetsConvertedToInt(): void {
- $row = ['id' => '4'];
-
- $this->entity = TestEntity::fromRow($row);
- $this->assertSame(4, $this->entity->getId());
- }
-
- public function testSetType(): void {
- $row = ['testId' => '4'];
-
- $this->entity = TestEntity::fromRow($row);
- $this->assertSame(4, $this->entity->getTestId());
- }
-
- public function testFromParams(): void {
- $params = [
- 'testId' => 4,
- 'email' => 'john@doe'
- ];
-
- $entity = TestEntity::fromParams($params);
-
- $this->assertEquals($params['testId'], $entity->getTestId());
- $this->assertEquals($params['email'], $entity->getEmail());
- $this->assertTrue($entity instanceof TestEntity);
- }
-
- public function testSlugify(): void {
- $entity = new TestEntity();
- $entity->setName('Slugify this!');
- $this->assertEquals('slugify-this', $entity->slugify('name'));
- $entity->setName('°!"§$%&/()=?`´ß\}][{³²#\'+~*-_.:,;<>|äöüÄÖÜSlugify this!');
- $this->assertEquals('slugify-this', $entity->slugify('name'));
- }
-
- public static function dataSetterCasts(): array {
- return [
- ['Id', '3', 3],
- ['smallInt', '3', 3],
- ['bigInt', '' . PHP_INT_MAX, PHP_INT_MAX],
- ['trueOrFalse', 0, false],
- ['trueOrFalse', 1, true],
- ['anotherBool', 0, false],
- ['anotherBool', 1, true],
- ['text', 33, '33'],
- ['longText', PHP_INT_MAX, '' . PHP_INT_MAX],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetterCasts')]
- public function testSetterCasts(string $field, mixed $in, mixed $out): void {
- $entity = new TestEntity();
- $entity->{'set' . $field}($in);
- $this->assertSame($out, $entity->{'get' . $field}());
- }
-
- public function testSetterDoesNotCastOnNull(): void {
- $entity = new TestEntity();
- $entity->setId(null);
- $this->assertSame(null, $entity->getId());
- }
-
- public function testSetterConvertsResourcesToStringProperly(): void {
- $string = 'Definitely a string';
- $stream = fopen('php://memory', 'r+');
- fwrite($stream, $string);
- rewind($stream);
-
- $entity = new TestEntity();
- $entity->setLongText($stream);
- fclose($stream);
- $this->assertSame($string, $entity->getLongText());
- }
-
- public function testSetterConvertsDatetime() {
- $entity = new TestEntity();
- $entity->setDatetime('2024-08-19 15:26:00');
- $this->assertEquals(new \DateTimeImmutable('2024-08-19 15:26:00'), $entity->getDatetime());
- }
-
- public function testSetterDoesNotConvertNullOnDatetime() {
- $entity = new TestEntity();
- $entity->setDatetime(null);
- $this->assertNull($entity->getDatetime());
- }
-
- public function testSetterConvertsTime() {
- $entity = new TestEntity();
- $entity->setTime('15:26:00');
- $this->assertEquals(new \DateTime('15:26:00'), $entity->getTime());
- }
-
- public function testGetFieldTypes(): void {
- $entity = new TestEntity();
- $this->assertEquals([
- 'id' => Types::INTEGER,
- 'testId' => Types::INTEGER,
- 'smallInt' => Types::SMALLINT,
- 'bigInt' => Types::BIGINT,
- 'anotherBool' => Types::BOOLEAN,
- 'text' => Types::TEXT,
- 'longText' => Types::BLOB,
- 'time' => Types::TIME,
- 'datetime' => Types::DATETIME_IMMUTABLE,
- 'trueOrFalse' => Types::BOOLEAN,
- 'legacyInt' => Types::INTEGER,
- 'doubleNowFloat' => Types::FLOAT,
- ], $entity->getFieldTypes());
- }
-
- public function testGetItInt(): void {
- $entity = new TestEntity();
- $entity->setId(3);
- $this->assertEquals(Types::INTEGER, gettype($entity->getId()));
- }
-
- public function testFieldsNotMarkedUpdatedIfNothingChanges(): void {
- $entity = new TestEntity('hey');
- $entity->setName('hey');
- $this->assertEquals(0, count($entity->getUpdatedFields()));
- }
-
- public function testIsGetter(): void {
- $entity = new TestEntity();
- $entity->setTrueOrFalse(false);
- $entity->setAnotherBool(false);
- $this->assertThat($entity->isTrueOrFalse(), new IsType(IsType::TYPE_BOOL));
- $this->assertThat($entity->isAnotherBool(), new IsType(IsType::TYPE_BOOL));
- }
-
- public function testIsGetterShoudFailForOtherType(): void {
- $this->expectException(\BadFunctionCallException::class);
-
- $entity = new TestEntity();
- $entity->setName('hello');
- $this->assertThat($entity->isName(), new IsType(IsType::TYPE_BOOL));
- }
-}
--- a/tests/lib/AppFramework/Db/QBMapperDBTest.php
+++ /dev/null
@@ -1,160 +0,0 @@
-addType('time', Types::TIME);
- $this->addType('datetime', Types::DATETIME_IMMUTABLE);
- }
-}
-
-/**
- * Class QBDBTestMapper
- *
- * @package Test\AppFramework\Db
- */
-class QBDBTestMapper extends QBMapper {
- public function __construct(IDBConnection $db) {
- parent::__construct($db, 'testing', QBDBTestEntity::class);
- }
-
- public function getParameterTypeForPropertyForTest(Entity $entity, string $property) {
- return parent::getParameterTypeForProperty($entity, $property);
- }
-
- public function getById(int $id): QBDBTestEntity {
- $qb = $this->db->getQueryBuilder();
- $query = $qb
- ->select('*')
- ->from($this->tableName)
- ->where(
- $qb->expr()->eq('id', $qb->createPositionalParameter($id, IQueryBuilder::PARAM_INT)),
- );
- return $this->findEntity($query);
- }
-}
-
-/**
- * Test real database handling (serialization)
- */
-#[\PHPUnit\Framework\Attributes\Group('DB')]
-class QBMapperDBTest extends TestCase {
- protected IDBConnection $connection;
- protected bool $schemaSetup = false;
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- $this->connection = Server::get(IDBConnection::class);
- $this->prepareTestingTable();
- }
-
- public function testInsertDateTime(): void {
- $mapper = new QBDBTestMapper($this->connection);
- $entity = new QBDBTestEntity();
- $entity->setTime(new \DateTime('2003-01-01 12:34:00'));
- $entity->setDatetime(new \DateTimeImmutable('2000-01-01 23:45:00'));
-
- $result = $mapper->insert($entity);
- $this->assertNotNull($result->getId());
- }
-
- public function testRetrieveDateTime(): void {
- $time = new \DateTime('2000-01-01 01:01:00');
- $datetime = new \DateTimeImmutable('2000-01-01 02:02:00');
-
- $mapper = new QBDBTestMapper($this->connection);
- $entity = new QBDBTestEntity();
- $entity->setTime($time);
- $entity->setDatetime($datetime);
-
- $result = $mapper->insert($entity);
- $this->assertNotNull($result->getId());
-
- $dbEntity = $mapper->getById($result->getId());
- $this->assertEquals($time->format('H:i:s'), $dbEntity->getTime()->format('H:i:s'));
- $this->assertEquals($datetime->format('Y-m-d H:i:s'), $dbEntity->getDatetime()->format('Y-m-d H:i:s'));
- // The date is not saved for "time"
- $this->assertNotEquals($time->format('Y'), $dbEntity->getTime()->format('Y'));
- }
-
- public function testUpdateDateTime(): void {
- $time = new \DateTime('2000-01-01 01:01:00');
- $datetime = new \DateTimeImmutable('2000-01-01 02:02:00');
-
- $mapper = new QBDBTestMapper($this->connection);
- $entity = new QBDBTestEntity();
- $entity->setTime('now');
- $entity->setDatetime('now');
-
- /** @var QBDBTestEntity */
- $entity = $mapper->insert($entity);
- $this->assertNotNull($entity->getId());
-
- // Update the values
- $entity->setTime($time);
- $entity->setDatetime($datetime);
- $mapper->update($entity);
-
- $dbEntity = $mapper->getById($entity->getId());
- $this->assertEquals($time->format('H:i:s'), $dbEntity->getTime()->format('H:i:s'));
- $this->assertEquals($datetime->format('Y-m-d H:i:s'), $dbEntity->getDatetime()->format('Y-m-d H:i:s'));
- }
-
- protected function prepareTestingTable(): void {
- if ($this->schemaSetup) {
- $this->connection->getQueryBuilder()->delete('testing')->executeStatement();
- }
-
- $prefix = Server::get(IConfig::class)->getSystemValueString('dbtableprefix', 'oc_');
- $schema = $this->connection->createSchema();
- try {
- $schema->getTable($prefix . 'testing');
- $this->connection->getQueryBuilder()->delete('testing')->executeStatement();
- } catch (SchemaException $e) {
- $this->schemaSetup = true;
- $table = $schema->createTable($prefix . 'testing');
- $table->addColumn('id', Types::BIGINT, [
- 'autoincrement' => true,
- 'notnull' => true,
- ]);
-
- $table->addColumn('time', Types::TIME, [
- 'notnull' => false,
- ]);
-
- $table->addColumn('datetime', Types::DATETIME_IMMUTABLE, [
- 'notnull' => false,
- ]);
-
- $table->setPrimaryKey(['id']);
- $this->connection->migrateToSchema($schema);
- }
- }
-}
--- a/tests/lib/AppFramework/Db/QBMapperTest.php
+++ /dev/null
@@ -1,242 +0,0 @@
-addType('intProp', 'int');
- $this->addType('boolProp', 'bool');
- $this->addType('stringProp', Types::STRING);
- $this->addType('integerProp', Types::INTEGER);
- $this->addType('booleanProp', Types::BOOLEAN);
- $this->addType('jsonProp', Types::JSON);
- $this->addType('datetimeProp', Types::DATETIME_IMMUTABLE);
- }
-}
-
-/**
- * Class QBTestMapper
- *
- * @package Test\AppFramework\Db
- */
-class QBTestMapper extends QBMapper {
- public function __construct(IDBConnection $db) {
- parent::__construct($db, 'table');
- }
-
- public function getParameterTypeForPropertyForTest(Entity $entity, string $property) {
- return parent::getParameterTypeForProperty($entity, $property);
- }
-}
-
-/**
- * Class QBMapperTest
- *
- * @package Test\AppFramework\Db
- */
-class QBMapperTest extends \Test\TestCase {
-
- protected IDBConnection&MockObject $db;
- protected IQueryBuilder&MockObject $qb;
- protected IExpressionBuilder&MockObject $expr;
- protected QBTestMapper $mapper;
-
- /**
- * @throws \ReflectionException
- */
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- $this->db = $this->getMockBuilder(IDBConnection::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->qb = $this->getMockBuilder(IQueryBuilder::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->expr = $this->getMockBuilder(IExpressionBuilder::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->qb->method('expr')->willReturn($this->expr);
- $this->db->method('getQueryBuilder')->willReturn($this->qb);
-
- $this->mapper = new QBTestMapper($this->db);
- }
-
- public function testInsertEntityParameterTypeMapping(): void {
- $datetime = new \DateTimeImmutable();
- $entity = new QBTestEntity();
- $entity->setIntProp(123);
- $entity->setBoolProp(true);
- $entity->setStringProp('string');
- $entity->setIntegerProp(456);
- $entity->setBooleanProp(false);
- $entity->setDatetimeProp($datetime);
-
- $intParam = $this->qb->createNamedParameter('int_prop', IQueryBuilder::PARAM_INT);
- $boolParam = $this->qb->createNamedParameter('bool_prop', IQueryBuilder::PARAM_BOOL);
- $stringParam = $this->qb->createNamedParameter('string_prop', IQueryBuilder::PARAM_STR);
- $integerParam = $this->qb->createNamedParameter('integer_prop', IQueryBuilder::PARAM_INT);
- $booleanParam = $this->qb->createNamedParameter('boolean_prop', IQueryBuilder::PARAM_BOOL);
- $datetimeParam = $this->qb->createNamedParameter('datetime_prop', IQueryBuilder::PARAM_DATETIME_IMMUTABLE);
-
- $createNamedParameterCalls = [
- [123, IQueryBuilder::PARAM_INT, null],
- [true, IQueryBuilder::PARAM_BOOL, null],
- ['string', IQueryBuilder::PARAM_STR, null],
- [456, IQueryBuilder::PARAM_INT, null],
- [false, IQueryBuilder::PARAM_BOOL, null],
- [$datetime, IQueryBuilder::PARAM_DATETIME_IMMUTABLE, null],
- ];
- $this->qb->expects($this->exactly(6))
- ->method('createNamedParameter')
- ->willReturnCallback(function () use (&$createNamedParameterCalls): void {
- $expected = array_shift($createNamedParameterCalls);
- $this->assertEquals($expected, func_get_args());
- });
-
- $setValueCalls = [
- ['int_prop', $intParam],
- ['bool_prop', $boolParam],
- ['string_prop', $stringParam],
- ['integer_prop', $integerParam],
- ['boolean_prop', $booleanParam],
- ['datetime_prop', $datetimeParam],
- ];
- $this->qb->expects($this->exactly(6))
- ->method('setValue')
- ->willReturnCallback(function () use (&$setValueCalls): void {
- $expected = array_shift($setValueCalls);
- $this->assertEquals($expected, func_get_args());
- });
-
- $this->mapper->insert($entity);
- }
-
- public function testUpdateEntityParameterTypeMapping(): void {
- $datetime = new \DateTimeImmutable();
- $entity = new QBTestEntity();
- $entity->setId(789);
- $entity->setIntProp(123);
- $entity->setBoolProp('true');
- $entity->setStringProp('string');
- $entity->setIntegerProp(456);
- $entity->setBooleanProp(false);
- $entity->setJsonProp(['hello' => 'world']);
- $entity->setDatetimeProp($datetime);
-
- $idParam = $this->qb->createNamedParameter('id', IQueryBuilder::PARAM_INT);
- $intParam = $this->qb->createNamedParameter('int_prop', IQueryBuilder::PARAM_INT);
- $boolParam = $this->qb->createNamedParameter('bool_prop', IQueryBuilder::PARAM_BOOL);
- $stringParam = $this->qb->createNamedParameter('string_prop', IQueryBuilder::PARAM_STR);
- $integerParam = $this->qb->createNamedParameter('integer_prop', IQueryBuilder::PARAM_INT);
- $booleanParam = $this->qb->createNamedParameter('boolean_prop', IQueryBuilder::PARAM_BOOL);
- $jsonParam = $this->qb->createNamedParameter('json_prop', IQueryBuilder::PARAM_JSON);
- $datetimeParam = $this->qb->createNamedParameter('datetime_prop', IQueryBuilder::PARAM_DATETIME_IMMUTABLE);
-
- $createNamedParameterCalls = [
- [123, IQueryBuilder::PARAM_INT, null],
- [true, IQueryBuilder::PARAM_BOOL, null],
- ['string', IQueryBuilder::PARAM_STR, null],
- [456, IQueryBuilder::PARAM_INT, null],
- [false, IQueryBuilder::PARAM_BOOL, null],
- [['hello' => 'world'], IQueryBuilder::PARAM_JSON, null],
- [$datetime, IQueryBuilder::PARAM_DATETIME_IMMUTABLE, null],
- [789, IQueryBuilder::PARAM_INT, null],
- ];
- $this->qb->expects($this->exactly(8))
- ->method('createNamedParameter')
- ->willReturnCallback(function () use (&$createNamedParameterCalls): void {
- $expected = array_shift($createNamedParameterCalls);
- $this->assertEquals($expected, func_get_args());
- });
-
- $setCalls = [
- ['int_prop', $intParam],
- ['bool_prop', $boolParam],
- ['string_prop', $stringParam],
- ['integer_prop', $integerParam],
- ['boolean_prop', $booleanParam],
- ['json_prop', $datetimeParam],
- ['datetime_prop', $datetimeParam],
- ];
- $this->qb->expects($this->exactly(7))
- ->method('set')
- ->willReturnCallback(function () use (&$setCalls): void {
- $expected = array_shift($setCalls);
- $this->assertEquals($expected, func_get_args());
- });
-
- $this->expr->expects($this->once())
- ->method('eq')
- ->with($this->equalTo('id'), $this->equalTo($idParam));
-
- $this->mapper->update($entity);
- }
-
- public function testGetParameterTypeForProperty(): void {
- $entity = new QBTestEntity();
-
- $intType = $this->mapper->getParameterTypeForPropertyForTest($entity, 'intProp');
- $this->assertEquals(IQueryBuilder::PARAM_INT, $intType, 'Int type property mapping incorrect');
-
- $integerType = $this->mapper->getParameterTypeForPropertyForTest($entity, 'integerProp');
- $this->assertEquals(IQueryBuilder::PARAM_INT, $integerType, 'Integer type property mapping incorrect');
-
- $boolType = $this->mapper->getParameterTypeForPropertyForTest($entity, 'boolProp');
- $this->assertEquals(IQueryBuilder::PARAM_BOOL, $boolType, 'Bool type property mapping incorrect');
-
- $booleanType = $this->mapper->getParameterTypeForPropertyForTest($entity, 'booleanProp');
- $this->assertEquals(IQueryBuilder::PARAM_BOOL, $booleanType, 'Boolean type property mapping incorrect');
-
- $stringType = $this->mapper->getParameterTypeForPropertyForTest($entity, 'stringProp');
- $this->assertEquals(IQueryBuilder::PARAM_STR, $stringType, 'String type property mapping incorrect');
-
- $jsonType = $this->mapper->getParameterTypeForPropertyForTest($entity, 'jsonProp');
- $this->assertEquals(IQueryBuilder::PARAM_JSON, $jsonType, 'JSON type property mapping incorrect');
-
- $datetimeType = $this->mapper->getParameterTypeForPropertyForTest($entity, 'datetimeProp');
- $this->assertEquals(IQueryBuilder::PARAM_DATETIME_IMMUTABLE, $datetimeType, 'DateTimeImmutable type property mapping incorrect');
-
- $unknownType = $this->mapper->getParameterTypeForPropertyForTest($entity, 'someProp');
- $this->assertEquals(IQueryBuilder::PARAM_STR, $unknownType, 'Unknown type property mapping incorrect');
- }
-}
--- a/tests/lib/AppFramework/Db/TransactionalTest.php
+++ /dev/null
@@ -1,79 +0,0 @@
-db = $this->createMock(IDBConnection::class);
- }
-
- public function testAtomicRollback(): void {
- $test = new class($this->db) {
- use TTransactional;
-
- public function __construct(
- private IDBConnection $db,
- ) {
- }
-
- public function fail(): void {
- $this->atomic(function (): void {
- throw new RuntimeException('nope');
- }, $this->db);
- }
- };
- $this->db->expects(self::once())
- ->method('beginTransaction');
- $this->db->expects(self::once())
- ->method('rollback');
- $this->db->expects(self::never())
- ->method('commit');
- $this->expectException(RuntimeException::class);
-
- $test->fail();
- }
-
- public function testAtomicCommit(): void {
- $test = new class($this->db) {
- use TTransactional;
-
- public function __construct(
- private IDBConnection $db,
- ) {
- }
-
- public function succeed(): int {
- return $this->atomic(function () {
- return 3;
- }, $this->db);
- }
- };
- $this->db->expects(self::once())
- ->method('beginTransaction');
- $this->db->expects(self::never())
- ->method('rollback');
- $this->db->expects(self::once())
- ->method('commit');
-
- $result = $test->succeed();
-
- self::assertEquals(3, $result);
- }
-}
--- a/tests/lib/AppFramework/DependencyInjection/DIContainerTest.php
+++ /dev/null
@@ -1,143 +0,0 @@
-container = $this->getMockBuilder(DIContainer::class)
- ->onlyMethods(['isAdminUser'])
- ->setConstructorArgs(['name'])
- ->getMock();
- }
-
- public function testProvidesRequest(): void {
- $this->assertTrue(isset($this->container['Request']));
- }
-
- public function testProvidesMiddlewareDispatcher(): void {
- $this->assertTrue(isset($this->container['MiddlewareDispatcher']));
- }
-
- public function testProvidesAppName(): void {
- $this->assertTrue(isset($this->container['AppName']));
- $this->assertTrue(isset($this->container['appName']));
- }
-
- public function testAppNameIsSetCorrectly(): void {
- $this->assertEquals('name', $this->container['AppName']);
- $this->assertEquals('name', $this->container['appName']);
- }
-
- public function testMiddlewareDispatcherIncludesSecurityMiddleware(): void {
- $this->container['Request'] = new Request(
- ['method' => 'GET'],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $dispatcher = $this->container['MiddlewareDispatcher'];
- $middlewares = $dispatcher->getMiddlewares();
-
- $found = false;
- foreach ($middlewares as $middleware) {
- if ($middleware instanceof SecurityMiddleware) {
- $found = true;
- }
- }
-
- $this->assertTrue($found);
- }
-
- public function testMiddlewareDispatcherIncludesBootstrapMiddlewares(): void {
- $coordinator = $this->createMock(Coordinator::class);
- $this->container[Coordinator::class] = $coordinator;
- $this->container['Request'] = $this->createMock(Request::class);
- $registrationContext = $this->createMock(RegistrationContext::class);
- $registrationContext->method('getMiddlewareRegistrations')
- ->willReturn([
- new MiddlewareRegistration($this->container['appName'], 'foo', false),
- new MiddlewareRegistration('otherapp', 'bar', false),
- ]);
- $this->container['foo'] = new class extends Middleware {
- };
- $this->container['bar'] = new class extends Middleware {
- };
- $coordinator->method('getRegistrationContext')->willReturn($registrationContext);
-
- $dispatcher = $this->container['MiddlewareDispatcher'];
-
- $middlewares = $dispatcher->getMiddlewares();
- self::assertNotEmpty($middlewares);
- foreach ($middlewares as $middleware) {
- if ($middleware === $this->container['bar']) {
- $this->fail('Container must not register this middleware');
- }
- if ($middleware === $this->container['foo']) {
- // It is done
- return;
- }
- }
- $this->fail('Bootstrap registered middleware not found');
- }
-
- public function testMiddlewareDispatcherIncludesGlobalBootstrapMiddlewares(): void {
- $coordinator = $this->createMock(Coordinator::class);
- $this->container[Coordinator::class] = $coordinator;
- $this->container['Request'] = $this->createMock(Request::class);
- $registrationContext = $this->createMock(RegistrationContext::class);
- $registrationContext->method('getMiddlewareRegistrations')
- ->willReturn([
- new MiddlewareRegistration('otherapp', 'foo', true),
- new MiddlewareRegistration('otherapp', 'bar', false),
- ]);
- $this->container['foo'] = new class extends Middleware {
- };
- $this->container['bar'] = new class extends Middleware {
- };
- $coordinator->method('getRegistrationContext')->willReturn($registrationContext);
-
- $dispatcher = $this->container['MiddlewareDispatcher'];
-
- $middlewares = $dispatcher->getMiddlewares();
- self::assertNotEmpty($middlewares);
- foreach ($middlewares as $middleware) {
- if ($middleware === $this->container['bar']) {
- $this->fail('Container must not register this middleware');
- }
- if ($middleware === $this->container['foo']) {
- // It is done
- return;
- }
- }
- $this->fail('Bootstrap registered middleware not found');
- }
-
- public function testInvalidAppClass(): void {
- $this->expectException(QueryException::class);
- $this->container->query('\OCA\Name\Foo');
- }
-}
--- a/tests/lib/AppFramework/DependencyInjection/DIIntergrationTests.php
+++ /dev/null
@@ -1,122 +0,0 @@
-server = new ServerContainer();
- $this->container = new DIContainer('App1', [], $this->server);
- }
-
- public function testInjectFromServer(): void {
- $this->server->registerService(Interface1::class, function () {
- return new ClassA1();
- });
-
- $this->server->registerService(ClassB::class, function (SimpleContainer $c) {
- return new ClassB(
- $c->query(Interface1::class)
- );
- });
-
- /** @var ClassB $res */
- $res = $this->container->query(ClassB::class);
- $this->assertSame(ClassA1::class, get_class($res->interface1));
- }
-
- public function testInjectDepFromServer(): void {
- $this->server->registerService(Interface1::class, function () {
- return new ClassA1();
- });
-
- $this->container->registerService(ClassB::class, function (SimpleContainer $c) {
- return new ClassB(
- $c->query(Interface1::class)
- );
- });
-
- /** @var ClassB $res */
- $res = $this->container->query(ClassB::class);
- $this->assertSame(ClassA1::class, get_class($res->interface1));
- }
-
- public function testOverwriteDepFromServer(): void {
- $this->server->registerService(Interface1::class, function () {
- return new ClassA1();
- });
-
- $this->container->registerService(Interface1::class, function () {
- return new ClassA2();
- });
-
- $this->container->registerService(ClassB::class, function (SimpleContainer $c) {
- return new ClassB(
- $c->query(Interface1::class)
- );
- });
-
- /** @var ClassB $res */
- $res = $this->container->query(ClassB::class);
- $this->assertSame(ClassA2::class, get_class($res->interface1));
- }
-
- public function testIgnoreOverwriteInServerClass(): void {
- $this->server->registerService(Interface1::class, function () {
- return new ClassA1();
- });
-
- $this->container->registerService(Interface1::class, function () {
- return new ClassA2();
- });
-
- $this->server->registerService(ClassB::class, function (SimpleContainer $c) {
- return new ClassB(
- $c->query(Interface1::class)
- );
- });
-
- /** @var ClassB $res */
- $res = $this->container->query(ClassB::class);
- $this->assertSame(ClassA1::class, get_class($res->interface1));
- }
-}
--- a/tests/lib/AppFramework/Http/ContentSecurityPolicyTest.php
+++ /dev/null
@@ -1,461 +0,0 @@
-contentSecurityPolicy = new ContentSecurityPolicy();
- }
-
- public function testGetPolicyDefault(): void {
- $defaultPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
- $this->assertSame($defaultPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyScriptDomainValid(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self' www.nextcloud.com;style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyScriptDomainValidMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self' www.nextcloud.com www.nextcloud.org;style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowScriptDomain(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowScriptDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowScriptDomainMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self' www.nextcloud.com;style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowScriptDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowScriptDomainMultipleStacked(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowScriptDomain('www.nextcloud.org')->disallowScriptDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyScriptDisallowEval(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyStyleDomainValid(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' www.nextcloud.com 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyStyleDomainValidMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' www.nextcloud.com www.nextcloud.org 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowStyleDomain(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowStyleDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowStyleDomainMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' www.nextcloud.com 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowStyleDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowStyleDomainMultipleStacked(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowStyleDomain('www.nextcloud.org')->disallowStyleDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyStyleAllowInline(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->allowInlineStyle(true);
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyStyleAllowInlineWithDomain(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' www.nextcloud.com 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyStyleDisallowInline(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->allowInlineStyle(false);
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyImageDomainValid(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob: www.nextcloud.com;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedImageDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyImageDomainValidMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob: www.nextcloud.com www.nextcloud.org;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedImageDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->addAllowedImageDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowImageDomain(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedImageDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowImageDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowImageDomainMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob: www.nextcloud.com;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedImageDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowImageDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowImageDomainMultipleStakes(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedImageDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowImageDomain('www.nextcloud.org')->disallowImageDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyFontDomainValid(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data: www.nextcloud.com;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedFontDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyFontDomainValidMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data: www.nextcloud.com www.nextcloud.org;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedFontDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->addAllowedFontDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowFontDomain(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedFontDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowFontDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowFontDomainMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data: www.nextcloud.com;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedFontDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowFontDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowFontDomainMultipleStakes(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedFontDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowFontDomain('www.nextcloud.org')->disallowFontDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyConnectDomainValid(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self' www.nextcloud.com;media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedConnectDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyConnectDomainValidMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self' www.nextcloud.com www.nextcloud.org;media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedConnectDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->addAllowedConnectDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowConnectDomain(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedConnectDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowConnectDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowConnectDomainMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self' www.nextcloud.com;media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedConnectDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowConnectDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowConnectDomainMultipleStakes(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedConnectDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowConnectDomain('www.nextcloud.org')->disallowConnectDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyMediaDomainValid(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self' www.nextcloud.com;frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedMediaDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyMediaDomainValidMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self' www.nextcloud.com www.nextcloud.org;frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedMediaDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->addAllowedMediaDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowMediaDomain(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedMediaDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowMediaDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowMediaDomainMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self' www.nextcloud.com;frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedMediaDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowMediaDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowMediaDomainMultipleStakes(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedMediaDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowMediaDomain('www.nextcloud.org')->disallowMediaDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyObjectDomainValid(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';object-src www.nextcloud.com;frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedObjectDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyObjectDomainValidMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';object-src www.nextcloud.com www.nextcloud.org;frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedObjectDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->addAllowedObjectDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowObjectDomain(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedObjectDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowObjectDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowObjectDomainMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';object-src www.nextcloud.com;frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedObjectDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowObjectDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowObjectDomainMultipleStakes(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedObjectDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowObjectDomain('www.nextcloud.org')->disallowObjectDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetAllowedFrameDomain(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-src www.nextcloud.com;frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedFrameDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyFrameDomainValidMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-src www.nextcloud.com www.nextcloud.org;frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedFrameDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->addAllowedFrameDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowFrameDomain(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedFrameDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowFrameDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowFrameDomainMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-src www.nextcloud.com;frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedFrameDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowFrameDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowFrameDomainMultipleStakes(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedFrameDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowFrameDomain('www.nextcloud.org')->disallowFrameDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetAllowedFrameAncestorDomain(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self' sub.nextcloud.com;form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedFrameAncestorDomain('sub.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyFrameAncestorValidMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self' sub.nextcloud.com foo.nextcloud.com;form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedFrameAncestorDomain('sub.nextcloud.com');
- $this->contentSecurityPolicy->addAllowedFrameAncestorDomain('foo.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowFrameAncestorDomain(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedFrameAncestorDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowFrameAncestorDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowFrameAncestorDomainMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self' www.nextcloud.com;form-action 'self'";
-
- $this->contentSecurityPolicy->addAllowedFrameAncestorDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowFrameAncestorDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyUnsafeWasmEval(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self' 'wasm-unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->allowEvalWasm(true);
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyNonce(): void {
- $nonce = base64_encode('my-nonce');
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'nonce-$nonce';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->useJsNonce($nonce);
- $this->contentSecurityPolicy->useStrictDynamicOnScripts(false);
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyNonceDefault(): void {
- $nonce = base64_encode('my-nonce');
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'nonce-$nonce';script-src-elem 'strict-dynamic' 'nonce-$nonce';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->useJsNonce($nonce);
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyNonceStrictDynamic(): void {
- $nonce = base64_encode('my-nonce');
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'strict-dynamic' 'nonce-$nonce';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->useJsNonce($nonce);
- $this->contentSecurityPolicy->useStrictDynamic(true);
- $this->contentSecurityPolicy->useStrictDynamicOnScripts(false);
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyNonceStrictDynamicDefault(): void {
- $nonce = base64_encode('my-nonce');
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'strict-dynamic' 'nonce-$nonce';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->useJsNonce($nonce);
- $this->contentSecurityPolicy->useStrictDynamic(true);
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyStrictDynamicOnScriptsOff(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->useStrictDynamicOnScripts(false);
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyStrictDynamicAndStrictDynamicOnScripts(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'";
-
- $this->contentSecurityPolicy->useStrictDynamic(true);
- $this->contentSecurityPolicy->useStrictDynamicOnScripts(true);
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-}
--- a/tests/lib/AppFramework/Http/DataResponseTest.php
+++ /dev/null
@@ -1,71 +0,0 @@
-response = new DataResponse();
- }
-
- public function testSetData(): void {
- $params = ['hi', 'yo'];
- $this->response->setData($params);
-
- $this->assertEquals(['hi', 'yo'], $this->response->getData());
- }
-
- public function testConstructorAllowsToSetData(): void {
- $data = ['hi'];
- $code = 300;
- $response = new DataResponse($data, $code);
-
- $this->assertEquals($data, $response->getData());
- $this->assertEquals($code, $response->getStatus());
- }
-
- public function testConstructorAllowsToSetHeaders(): void {
- $data = ['hi'];
- $code = 300;
- $headers = ['test' => 'something'];
- $response = new DataResponse($data, $code, $headers);
-
- $expectedHeaders = [
- 'Cache-Control' => 'no-cache, no-store, must-revalidate',
- 'Content-Security-Policy' => "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'",
- 'Feature-Policy' => "autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone 'none';payment 'none'",
- 'X-Robots-Tag' => 'noindex, nofollow',
- 'X-Request-Id' => Server::get(IRequest::class)->getId(),
- ];
- $expectedHeaders = array_merge($expectedHeaders, $headers);
-
- $this->assertEquals($data, $response->getData());
- $this->assertEquals($code, $response->getStatus());
- $this->assertEquals($expectedHeaders, $response->getHeaders());
- }
-
- public function testChainability(): void {
- $params = ['hi', 'yo'];
- $this->response->setData($params)
- ->setStatus(Http::STATUS_NOT_FOUND);
-
- $this->assertEquals(Http::STATUS_NOT_FOUND, $this->response->getStatus());
- $this->assertEquals(['hi', 'yo'], $this->response->getData());
- }
-}
--- a/tests/lib/AppFramework/Http/DispatcherTest.php
+++ /dev/null
@@ -1,813 +0,0 @@
-registerResponder('text', function ($in) {
- return new JSONResponse(['text' => $in]);
- });
- return [$int, $bool, $test, $test2];
- }
-
- /**
- * @param int $int
- * @param bool $bool
- * @param int $test
- * @param int $test2
- * @return DataResponse
- */
- public function execDataResponse($int, $bool, $test = 4, $test2 = 1) {
- return new DataResponse([
- 'text' => [$int, $bool, $test, $test2]
- ]);
- }
-
- public function test(): Response {
- return new DataResponse();
- }
-
- public function execStringBackedEnum(TestStringBackedEnum $enum) {
- return [$enum];
- }
-
- public function execIntBackedEnum(TestIntBackedEnum $enum) {
- return [$enum];
- }
-
- public function execNullableBackedEnum(?TestStringBackedEnum $enum = null) {
- return [$enum];
- }
-}
-
-/**
- * Class DispatcherTest
- *
- * @package Test\AppFramework\Http
- */
-#[\PHPUnit\Framework\Attributes\Group('DB')]
-class DispatcherTest extends \Test\TestCase {
- /** @var MiddlewareDispatcher */
- private $middlewareDispatcher;
- /** @var Dispatcher */
- private $dispatcher;
- private $controllerMethod;
- /** @var Controller|MockObject */
- private $controller;
- private $response;
- /** @var IRequest|MockObject */
- private $request;
- private $lastModified;
- private $etag;
- /** @var Http|MockObject */
- private $http;
- private $reflector;
- /** @var IConfig|MockObject */
- private $config;
- /** @var LoggerInterface|MockObject */
- private $logger;
- /** @var IEventLogger|MockObject */
- private $eventLogger;
- /** @var ContainerInterface|MockObject */
- private $container;
- /** @var IUserSession|MockObject */
- private $userSession;
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
- $this->controllerMethod = 'test';
-
- $this->config = $this->createMock(IConfig::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->eventLogger = $this->createMock(IEventLogger::class);
- $this->container = $this->createMock(ContainerInterface::class);
- $this->userSession = $this->createMock(IUserSession::class);
- $app = $this->createMock(DIContainer::class);
- $request = $this->createMock(Request::class);
- $this->http = $this->createMock(\OC\AppFramework\Http::class);
-
- $this->middlewareDispatcher = $this->createMock(MiddlewareDispatcher::class);
- $this->controller = $this->getMockBuilder(TestController::class)
- ->onlyMethods([$this->controllerMethod])
- ->setConstructorArgs([$app, $request])
- ->getMock();
-
- $this->request = $this->createMock(Request::class);
-
- $this->reflector = new ControllerMethodReflector(Server::get(LoggerInterface::class));
-
- $this->dispatcher = new Dispatcher(
- $this->http,
- $this->middlewareDispatcher,
- $this->reflector,
- $this->request,
- $this->config,
- Server::get(IDBConnection::class),
- $this->logger,
- $this->eventLogger,
- $this->container,
- $this->userSession,
- );
-
- $this->response = $this->createMock(Response::class);
-
- $this->lastModified = new \DateTime('now', new \DateTimeZone('GMT'));
- $this->etag = 'hi';
- }
-
- /**
- * @param string $out
- * @param string $httpHeaders
- */
- private function setMiddlewareExpectations($out = null,
- $httpHeaders = null, $responseHeaders = [],
- $ex = false, $catchEx = true) {
- if ($ex) {
- $exception = new \Exception();
- $this->middlewareDispatcher->expects($this->once())
- ->method('beforeController')
- ->with($this->equalTo($this->controller),
- $this->equalTo($this->controllerMethod))
- ->willThrowException($exception);
- if ($catchEx) {
- $this->middlewareDispatcher->expects($this->once())
- ->method('afterException')
- ->with($this->equalTo($this->controller),
- $this->equalTo($this->controllerMethod),
- $this->equalTo($exception))
- ->willReturn($this->response);
- } else {
- $this->middlewareDispatcher->expects($this->once())
- ->method('afterException')
- ->with($this->equalTo($this->controller),
- $this->equalTo($this->controllerMethod),
- $this->equalTo($exception))
- ->willThrowException($exception);
- return;
- }
- } else {
- $this->middlewareDispatcher->expects($this->once())
- ->method('beforeController')
- ->with($this->equalTo($this->controller),
- $this->equalTo($this->controllerMethod));
- $this->controller->expects($this->once())
- ->method($this->controllerMethod)
- ->willReturn($this->response);
- }
-
- $this->response->expects($this->once())
- ->method('render')
- ->willReturn($out);
- $this->response->expects($this->once())
- ->method('getStatus')
- ->willReturn(Http::STATUS_OK);
- $this->response->expects($this->once())
- ->method('getHeaders')
- ->willReturn($responseHeaders);
- $this->http->expects($this->once())
- ->method('getStatusHeader')
- ->with($this->equalTo(Http::STATUS_OK))
- ->willReturn($httpHeaders);
-
- $this->middlewareDispatcher->expects($this->once())
- ->method('afterController')
- ->with($this->equalTo($this->controller),
- $this->equalTo($this->controllerMethod),
- $this->equalTo($this->response))
- ->willReturn($this->response);
-
- $this->middlewareDispatcher->expects($this->once())
- ->method('afterController')
- ->with($this->equalTo($this->controller),
- $this->equalTo($this->controllerMethod),
- $this->equalTo($this->response))
- ->willReturn($this->response);
-
- $this->middlewareDispatcher->expects($this->once())
- ->method('beforeOutput')
- ->with($this->equalTo($this->controller),
- $this->equalTo($this->controllerMethod),
- $this->equalTo($out))
- ->willReturn($out);
- }
-
- public function testDispatcherReturnsArrayWith2Entries(): void {
- $this->setMiddlewareExpectations('');
-
- $response = $this->dispatcher->dispatch($this->controller, $this->controllerMethod);
- $this->assertNull($response[0]);
- $this->assertEquals([], $response[1]);
- $this->assertNull($response[2]);
- }
-
- public function testHeadersAndOutputAreReturned(): void {
- $out = 'yo';
- $httpHeaders = 'Http';
- $responseHeaders = ['hell' => 'yeah'];
- $this->setMiddlewareExpectations($out, $httpHeaders, $responseHeaders);
-
- $response = $this->dispatcher->dispatch($this->controller,
- $this->controllerMethod);
-
- $this->assertEquals($httpHeaders, $response[0]);
- $this->assertEquals($responseHeaders, $response[1]);
- $this->assertEquals($out, $response[3]);
- }
-
- public function testExceptionCallsAfterException(): void {
- $out = 'yo';
- $httpHeaders = 'Http';
- $responseHeaders = ['hell' => 'yeah'];
- $this->setMiddlewareExpectations($out, $httpHeaders, $responseHeaders, true);
-
- $response = $this->dispatcher->dispatch($this->controller,
- $this->controllerMethod);
-
- $this->assertEquals($httpHeaders, $response[0]);
- $this->assertEquals($responseHeaders, $response[1]);
- $this->assertEquals($out, $response[3]);
- }
-
- public function testExceptionThrowsIfCanNotBeHandledByAfterException(): void {
- $out = 'yo';
- $httpHeaders = 'Http';
- $responseHeaders = ['hell' => 'yeah'];
- $this->setMiddlewareExpectations($out, $httpHeaders, $responseHeaders, true, false);
-
- $this->expectException(\Exception::class);
- $this->dispatcher->dispatch(
- $this->controller,
- $this->controllerMethod
- );
- }
-
- private function dispatcherPassthrough() {
- $this->middlewareDispatcher->expects($this->once())
- ->method('beforeController');
- $this->middlewareDispatcher->expects($this->once())
- ->method('afterController')
- ->willReturnCallback(function ($a, $b, $in) {
- return $in;
- });
- $this->middlewareDispatcher->expects($this->once())
- ->method('beforeOutput')
- ->willReturnCallback(function ($a, $b, $in) {
- return $in;
- });
- }
-
- public function testControllerParametersInjected(): void {
- $this->request = new Request(
- [
- 'post' => [
- 'int' => '3',
- 'bool' => 'false',
- 'double' => 1.2,
- ],
- 'method' => 'POST'
- ],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $this->dispatcher = new Dispatcher(
- $this->http, $this->middlewareDispatcher, $this->reflector,
- $this->request,
- $this->config,
- Server::get(IDBConnection::class),
- $this->logger,
- $this->eventLogger,
- $this->container,
- $this->userSession,
- );
- $controller = new TestController('app', $this->request);
-
- // reflector is supposed to be called once
- $this->dispatcherPassthrough();
- $response = $this->dispatcher->dispatch($controller, 'exec');
-
- $this->assertEquals('[3,false,4,1]', $response[3]);
- }
-
- public function testControllerParametersInjectedStringBackedEnum(): void {
- $this->request = new Request(
- [
- 'post' => [
- 'enum' => 'foo',
- ],
- 'method' => 'POST',
- ],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $this->dispatcher = new Dispatcher(
- $this->http, $this->middlewareDispatcher, $this->reflector,
- $this->request,
- $this->config,
- Server::get(IDBConnection::class),
- $this->logger,
- $this->eventLogger,
- $this->container,
- $this->userSession,
- );
- $controller = new TestController('app', $this->request);
-
- $this->dispatcherPassthrough();
- $response = $this->dispatcher->dispatch($controller, 'execStringBackedEnum');
-
- $this->assertEquals('["foo"]', $response[3]);
- }
-
- public function testControllerParametersInjectedIntBackedEnum(): void {
- $this->request = new Request(
- [
- 'post' => [
- 'enum' => '2',
- ],
- 'method' => 'POST',
- ],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $this->dispatcher = new Dispatcher(
- $this->http, $this->middlewareDispatcher, $this->reflector,
- $this->request,
- $this->config,
- Server::get(IDBConnection::class),
- $this->logger,
- $this->eventLogger,
- $this->container,
- $this->userSession,
- );
- $controller = new TestController('app', $this->request);
-
- $this->dispatcherPassthrough();
- $response = $this->dispatcher->dispatch($controller, 'execIntBackedEnum');
-
- $this->assertEquals('[2]', $response[3]);
- }
-
- public function testControllerParametersInjectedNullableBackedEnumDefault(): void {
- $this->request = new Request(
- [
- 'post' => [],
- 'method' => 'POST',
- ],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $this->dispatcher = new Dispatcher(
- $this->http, $this->middlewareDispatcher, $this->reflector,
- $this->request,
- $this->config,
- Server::get(IDBConnection::class),
- $this->logger,
- $this->eventLogger,
- $this->container,
- $this->userSession,
- );
- $controller = new TestController('app', $this->request);
-
- $this->dispatcherPassthrough();
- $response = $this->dispatcher->dispatch($controller, 'execNullableBackedEnum');
-
- $this->assertEquals('[null]', $response[3]);
- }
-
- public function testControllerParametersInjectedDefaultOverwritten(): void {
- $this->request = new Request(
- [
- 'post' => [
- 'int' => '3',
- 'bool' => 'false',
- 'double' => 1.2,
- 'test2' => 7
- ],
- 'method' => 'POST',
- ],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $this->dispatcher = new Dispatcher(
- $this->http, $this->middlewareDispatcher, $this->reflector,
- $this->request,
- $this->config,
- Server::get(IDBConnection::class),
- $this->logger,
- $this->eventLogger,
- $this->container,
- $this->userSession,
- );
- $controller = new TestController('app', $this->request);
-
- // reflector is supposed to be called once
- $this->dispatcherPassthrough();
- $response = $this->dispatcher->dispatch($controller, 'exec');
-
- $this->assertEquals('[3,false,4,7]', $response[3]);
- }
-
- public function testResponseTransformedByUrlFormat(): void {
- $this->request = new Request(
- [
- 'post' => [
- 'int' => '3',
- 'bool' => 'false',
- 'double' => 1.2,
- ],
- 'urlParams' => [
- 'format' => 'text'
- ],
- 'method' => 'GET'
- ],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $this->dispatcher = new Dispatcher(
- $this->http, $this->middlewareDispatcher, $this->reflector,
- $this->request,
- $this->config,
- Server::get(IDBConnection::class),
- $this->logger,
- $this->eventLogger,
- $this->container,
- $this->userSession,
- );
- $controller = new TestController('app', $this->request);
-
- // reflector is supposed to be called once
- $this->dispatcherPassthrough();
- $response = $this->dispatcher->dispatch($controller, 'exec');
-
- $this->assertEquals('{"text":[3,false,4,1]}', $response[3]);
- }
-
- public function testResponseTransformsDataResponse(): void {
- $this->request = new Request(
- [
- 'post' => [
- 'int' => '3',
- 'bool' => 'false',
- 'double' => 1.2,
- ],
- 'urlParams' => [
- 'format' => 'json'
- ],
- 'method' => 'GET'
- ],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $this->dispatcher = new Dispatcher(
- $this->http, $this->middlewareDispatcher, $this->reflector,
- $this->request,
- $this->config,
- Server::get(IDBConnection::class),
- $this->logger,
- $this->eventLogger,
- $this->container,
- $this->userSession,
- );
- $controller = new TestController('app', $this->request);
-
- // reflector is supposed to be called once
- $this->dispatcherPassthrough();
- $response = $this->dispatcher->dispatch($controller, 'execDataResponse');
-
- $this->assertEquals('{"text":[3,false,4,1]}', $response[3]);
- }
-
- public function testResponseTransformedByAcceptHeader(): void {
- $this->request = new Request(
- [
- 'post' => [
- 'int' => '3',
- 'bool' => 'false',
- 'double' => 1.2,
- ],
- 'server' => [
- 'HTTP_ACCEPT' => 'application/text, test',
- 'HTTP_CONTENT_TYPE' => 'application/x-www-form-urlencoded'
- ],
- 'method' => 'PUT'
- ],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $this->dispatcher = new Dispatcher(
- $this->http, $this->middlewareDispatcher, $this->reflector,
- $this->request,
- $this->config,
- Server::get(IDBConnection::class),
- $this->logger,
- $this->eventLogger,
- $this->container,
- $this->userSession,
- );
- $controller = new TestController('app', $this->request);
-
- // reflector is supposed to be called once
- $this->dispatcherPassthrough();
- $response = $this->dispatcher->dispatch($controller, 'exec');
-
- $this->assertEquals('{"text":[3,false,4,1]}', $response[3]);
- }
-
- public function testResponseTransformedBySendingMultipartFormData(): void {
- $this->request = new Request(
- [
- 'post' => [
- 'int' => '3',
- 'bool' => 'false',
- 'double' => 1.2,
- ],
- 'server' => [
- 'HTTP_ACCEPT' => 'application/text, test',
- 'HTTP_CONTENT_TYPE' => 'multipart/form-data'
- ],
- 'method' => 'POST'
- ],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $this->dispatcher = new Dispatcher(
- $this->http, $this->middlewareDispatcher, $this->reflector,
- $this->request,
- $this->config,
- Server::get(IDBConnection::class),
- $this->logger,
- $this->eventLogger,
- $this->container,
- $this->userSession,
- );
- $controller = new TestController('app', $this->request);
-
- // reflector is supposed to be called once
- $this->dispatcherPassthrough();
- $response = $this->dispatcher->dispatch($controller, 'exec');
-
- $this->assertEquals('{"text":[3,false,4,1]}', $response[3]);
- }
-
- public function testResponsePrimarilyTransformedByParameterFormat(): void {
- $this->request = new Request(
- [
- 'post' => [
- 'int' => '3',
- 'bool' => 'false',
- 'double' => 1.2,
- ],
- 'get' => [
- 'format' => 'text'
- ],
- 'server' => [
- 'HTTP_ACCEPT' => 'application/json, test'
- ],
- 'method' => 'POST'
- ],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $this->dispatcher = new Dispatcher(
- $this->http, $this->middlewareDispatcher, $this->reflector,
- $this->request,
- $this->config,
- Server::get(IDBConnection::class),
- $this->logger,
- $this->eventLogger,
- $this->container,
- $this->userSession,
- );
- $controller = new TestController('app', $this->request);
-
- // reflector is supposed to be called once
- $this->dispatcherPassthrough();
- $response = $this->dispatcher->dispatch($controller, 'exec');
-
- $this->assertEquals('{"text":[3,false,4,1]}', $response[3]);
- }
-
- public static function rangeDataProvider(): array {
- return [
- [PHP_INT_MIN, PHP_INT_MAX, 42, false],
- [0, 12, -5, true],
- [-12, 0, 5, true],
- [1, 200, 0, true],
- [-15, -5, 0, true],
- [-15, 15, 0, false],
- [0, 200, 0, false],
- [-200, 0, 0, false],
- [7, 14, 5, true],
- [7, 14, 10, false],
- [-14, -7, -10, false],
- [null, null, -1, false],
-
- // $limit comes with default limits of self::DEFAULT_MIN (1) <= $limit <= self::DEFAULT_MAX (500)
- [null, null, -1, true, 'limit'],
- [null, null, -1, false, 'limit', -1],
- [null, null, 0, true, 'limit'],
- [null, null, 0, true, 'limit', -1],
- [null, null, 1, false, 'limit'],
- [null, null, 500, false, 'limit'],
- [null, null, 501, true, 'limit'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('rangeDataProvider')]
- public function testEnsureParameterValueSatisfiesRange(?int $min, ?int $max, int $input, bool $throw, string $param = 'myArgument', ?int $default = null): void {
- $this->reflector = $this->createMock(ControllerMethodReflector::class);
- if ($min === null && $max === null) {
- $this->reflector->expects($this->any())
- ->method('getRange')
- ->willReturn(null);
- } else {
- $this->reflector->expects($this->any())
- ->method('getRange')
- ->willReturn([
- 'min' => $min,
- 'max' => $max,
- ]);
- }
-
- $this->dispatcher = new Dispatcher(
- $this->http,
- $this->middlewareDispatcher,
- $this->reflector,
- $this->request,
- $this->config,
- Server::get(IDBConnection::class),
- $this->logger,
- $this->eventLogger,
- $this->container,
- $this->userSession,
- );
-
- if ($throw) {
- $this->expectException(ParameterOutOfRangeException::class);
- }
-
- self::invokePrivate($this->dispatcher, 'ensureParameterValueSatisfiesRange', [$param, $input, $default]);
- if (!$throw) {
- // do not mark this test risky
- $this->assertTrue(true);
- }
- }
-
- public static function stringConstraintDataProvider(): array {
- return [
- [true, null, false],
- [true, 'non-empty-string', false],
- [false, 'non-empty-string', true],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('stringConstraintDataProvider')]
- public function testEnsureParameterValueSatisfiesStringConstraint(bool $satisfies, ?string $constraint, bool $throw): void {
- $this->reflector = $this->createMock(ControllerMethodReflector::class);
- $this->reflector->expects($this->any())
- ->method('satisfiesStringConstraint')
- ->willReturn($satisfies);
- $this->reflector->expects($this->any())
- ->method('getStringConstraint')
- ->willReturn($constraint);
-
- $this->dispatcher = new Dispatcher(
- $this->http,
- $this->middlewareDispatcher,
- $this->reflector,
- $this->request,
- $this->config,
- Server::get(IDBConnection::class),
- $this->logger,
- $this->eventLogger,
- $this->container,
- $this->userSession,
- );
-
- if ($throw) {
- $this->expectException(InvalidStringParameterException::class);
- }
-
- self::invokePrivate($this->dispatcher, 'ensureParameterValueSatisfiesStringConstraint', ['myArgument', '']);
- if (!$throw) {
- // do not mark this test risky
- $this->assertTrue(true);
- }
- }
-
- public static function backedEnumDataProvider(): array {
- return [
- [TestStringBackedEnum::class, 'foo', TestStringBackedEnum::Foo],
- [TestStringBackedEnum::class, 'bar', TestStringBackedEnum::Bar],
- [TestIntBackedEnum::class, '1', TestIntBackedEnum::One],
- [TestIntBackedEnum::class, 1, TestIntBackedEnum::One],
- [TestIntBackedEnum::class, 2, TestIntBackedEnum::Two],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('backedEnumDataProvider')]
- public function testResolveBackedEnumValue(string $enumClass, string|int $input, \BackedEnum $expected): void {
- $this->reflector = $this->createMock(ControllerMethodReflector::class);
- $this->dispatcher = new Dispatcher(
- $this->http,
- $this->middlewareDispatcher,
- $this->reflector,
- $this->request,
- $this->config,
- Server::get(IDBConnection::class),
- $this->logger,
- $this->eventLogger,
- $this->container,
- $this->userSession,
- );
-
- $result = self::invokePrivate($this->dispatcher, 'resolveBackedEnumValue', ['myArgument', $enumClass, $input]);
- $this->assertSame($expected, $result);
- }
-
- public static function invalidBackedEnumDataProvider(): array {
- return [
- [TestStringBackedEnum::class, 'invalid'],
- [TestIntBackedEnum::class, 'not-a-number'],
- [TestIntBackedEnum::class, 99],
- [TestStringBackedEnum::class, ['array']],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('invalidBackedEnumDataProvider')]
- public function testResolveBackedEnumValueThrowsOnInvalidValue(string $enumClass, mixed $input): void {
- $this->reflector = $this->createMock(ControllerMethodReflector::class);
- $this->dispatcher = new Dispatcher(
- $this->http,
- $this->middlewareDispatcher,
- $this->reflector,
- $this->request,
- $this->config,
- Server::get(IDBConnection::class),
- $this->logger,
- $this->eventLogger,
- $this->container,
- $this->userSession,
- );
-
- $this->expectException(InvalidEnumParameterException::class);
-
- self::invokePrivate($this->dispatcher, 'resolveBackedEnumValue', ['myArgument', $enumClass, $input]);
- }
-}
--- a/tests/lib/AppFramework/Http/DownloadResponseTest.php
+++ /dev/null
@@ -1,59 +0,0 @@
-getHeaders();
-
- $this->assertEquals('attachment; filename=file', $headers['Content-Disposition']);
- $this->assertEquals('content', $headers['Content-Type']);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('filenameEncodingProvider')]
- public function testFilenameEncoding(string $input, string $expectedDisposition): void {
- $response = new ChildDownloadResponse($input, 'content');
- $headers = $response->getHeaders();
-
- $this->assertEquals($expectedDisposition, $headers['Content-Disposition']);
- }
-
- public static function filenameEncodingProvider(): array {
- return [
- ['TestName.txt', 'attachment; filename=TestName.txt'],
- ['A "Quoted" Filename.txt', 'attachment; filename="A \"Quoted\" Filename.txt"'],
- ['A "Quoted" Filename.txt', 'attachment; filename="A \"Quoted\" Filename.txt"'],
- ['A "Quoted" Filename With A Backslash \\.txt', 'attachment; filename="A \"Quoted\" Filename With A Backslash -.txt"'],
- ['A "Very" Weird Filename \ / & <> " >\'""""\.text', 'attachment; filename="A \"Very\" Weird Filename - - & <> \" >\'\"\"\"\"-.text"'],
- ['\\\\\\\\\\\\', 'attachment; filename=------'],
- ];
- }
-
- public function testSpecialCharactersInFilename(): void {
- $filename = 'document "draft" with; special&chars.pdf';
- $response = new ChildDownloadResponse($filename, 'application/pdf');
- $headers = $response->getHeaders();
-
- $this->assertEquals(
- 'attachment; filename="document \"draft\" with; special&chars.pdf"',
- $headers['Content-Disposition']
- );
- }
-}
--- a/tests/lib/AppFramework/Http/EmptyContentSecurityPolicyTest.php
+++ /dev/null
@@ -1,453 +0,0 @@
-contentSecurityPolicy = new EmptyContentSecurityPolicy();
- }
-
- public function testGetPolicyDefault(): void {
- $defaultPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'";
- $this->assertSame($defaultPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyScriptDomainValid(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src www.nextcloud.com;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyScriptDomainValidMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src www.nextcloud.com www.nextcloud.org;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowScriptDomain(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowScriptDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowScriptDomainMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src www.nextcloud.com;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowScriptDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowScriptDomainMultipleStacked(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowScriptDomain('www.nextcloud.org')->disallowScriptDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyScriptAllowWasmEval(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'wasm-unsafe-eval';frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->allowEvalWasm(true);
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyStyleDomainValid(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';style-src www.nextcloud.com;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyStyleDomainValidMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';style-src www.nextcloud.com www.nextcloud.org;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowStyleDomain(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowStyleDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowStyleDomainMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';style-src www.nextcloud.com;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowStyleDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowStyleDomainMultipleStacked(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowStyleDomain('www.nextcloud.org')->disallowStyleDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyStyleAllowInline(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';style-src 'unsafe-inline';frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->allowInlineStyle(true);
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyStyleAllowInlineWithDomain(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';style-src www.nextcloud.com 'unsafe-inline';frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->allowInlineStyle(true);
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyStyleDisallowInline(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->allowInlineStyle(false);
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyImageDomainValid(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';img-src www.nextcloud.com;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedImageDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyImageDomainValidMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';img-src www.nextcloud.com www.nextcloud.org;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedImageDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->addAllowedImageDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowImageDomain(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedImageDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowImageDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowImageDomainMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';img-src www.nextcloud.com;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedImageDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowImageDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowImageDomainMultipleStakes(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedImageDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowImageDomain('www.nextcloud.org')->disallowImageDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyFontDomainValid(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';font-src www.nextcloud.com;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedFontDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyFontDomainValidMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';font-src www.nextcloud.com www.nextcloud.org;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedFontDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->addAllowedFontDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowFontDomain(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedFontDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowFontDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowFontDomainMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';font-src www.nextcloud.com;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedFontDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowFontDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowFontDomainMultipleStakes(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedFontDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowFontDomain('www.nextcloud.org')->disallowFontDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyConnectDomainValid(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';connect-src www.nextcloud.com;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedConnectDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyConnectDomainValidMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';connect-src www.nextcloud.com www.nextcloud.org;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedConnectDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->addAllowedConnectDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowConnectDomain(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedConnectDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowConnectDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowConnectDomainMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';connect-src www.nextcloud.com;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedConnectDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowConnectDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowConnectDomainMultipleStakes(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedConnectDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowConnectDomain('www.nextcloud.org')->disallowConnectDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyMediaDomainValid(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';media-src www.nextcloud.com;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedMediaDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyMediaDomainValidMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';media-src www.nextcloud.com www.nextcloud.org;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedMediaDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->addAllowedMediaDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowMediaDomain(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedMediaDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowMediaDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowMediaDomainMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';media-src www.nextcloud.com;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedMediaDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowMediaDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowMediaDomainMultipleStakes(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedMediaDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowMediaDomain('www.nextcloud.org')->disallowMediaDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyObjectDomainValid(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';object-src www.nextcloud.com;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedObjectDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyObjectDomainValidMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';object-src www.nextcloud.com www.nextcloud.org;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedObjectDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->addAllowedObjectDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowObjectDomain(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedObjectDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowObjectDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowObjectDomainMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';object-src www.nextcloud.com;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedObjectDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowObjectDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowObjectDomainMultipleStakes(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedObjectDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowObjectDomain('www.nextcloud.org')->disallowObjectDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetAllowedFrameDomain(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-src www.nextcloud.com;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedFrameDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyFrameDomainValidMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-src www.nextcloud.com www.nextcloud.org;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedFrameDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->addAllowedFrameDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowFrameDomain(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedFrameDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowFrameDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowFrameDomainMultiple(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-src www.nextcloud.com;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedFrameDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowFrameDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyDisallowFrameDomainMultipleStakes(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedFrameDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->disallowFrameDomain('www.nextcloud.org')->disallowFrameDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyWithJsNonceAndScriptDomains(): void {
- $nonce = base64_encode('MyJsNonce');
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'nonce-$nonce' www.nextcloud.com www.nextcloud.org;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->useJsNonce($nonce);
- $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyWithJsNonceAndStrictDynamic(): void {
- $nonce = base64_encode('MyJsNonce');
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'strict-dynamic' 'nonce-$nonce' www.nextcloud.com;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->useStrictDynamic(true);
- $this->contentSecurityPolicy->useJsNonce($nonce);
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyWithJsNonceAndStrictDynamicAndStrictDynamicOnScripts(): void {
- $nonce = base64_encode('MyJsNonce');
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'strict-dynamic' 'nonce-$nonce' www.nextcloud.com;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->useStrictDynamic(true);
- $this->contentSecurityPolicy->useStrictDynamicOnScripts(true);
- $this->contentSecurityPolicy->useJsNonce($nonce);
- // Should be same as `testGetPolicyWithJsNonceAndStrictDynamic` because of fallback
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyWithJsNonceAndStrictDynamicOnScripts(): void {
- $nonce = base64_encode('MyJsNonce');
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'nonce-$nonce' www.nextcloud.com;script-src-elem 'strict-dynamic' 'nonce-$nonce' www.nextcloud.com;frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com');
- $this->contentSecurityPolicy->useStrictDynamicOnScripts(true);
- $this->contentSecurityPolicy->useJsNonce($nonce);
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyWithStrictDynamicOnScripts(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->useStrictDynamicOnScripts(true);
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyWithJsNonceAndSelfScriptDomain(): void {
- $nonce = base64_encode('MyJsNonce');
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'nonce-$nonce';frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->useJsNonce($nonce);
- $this->contentSecurityPolicy->addAllowedScriptDomain("'self'");
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyWithoutJsNonceAndSelfScriptDomain(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';frame-ancestors 'none'";
-
- $this->contentSecurityPolicy->addAllowedScriptDomain("'self'");
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyWithReportUri(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none';report-uri https://my-report-uri.com";
-
- $this->contentSecurityPolicy->addReportTo('https://my-report-uri.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-
- public function testGetPolicyWithMultipleReportUri(): void {
- $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none';report-uri https://my-report-uri.com https://my-other-report-uri.com";
-
- $this->contentSecurityPolicy->addReportTo('https://my-report-uri.com');
- $this->contentSecurityPolicy->addReportTo('https://my-other-report-uri.com');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
-}
--- a/tests/lib/AppFramework/Http/EmptyFeaturePolicyTest.php
+++ /dev/null
@@ -1,117 +0,0 @@
-policy = new EmptyFeaturePolicy();
- }
-
- public function testGetPolicyDefault(): void {
- $defaultPolicy = "autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone 'none';payment 'none'";
- $this->assertSame($defaultPolicy, $this->policy->buildPolicy());
- }
-
- public function testGetPolicyAutoplayDomainValid(): void {
- $expectedPolicy = "autoplay www.nextcloud.com;camera 'none';fullscreen 'none';geolocation 'none';microphone 'none';payment 'none'";
-
- $this->policy->addAllowedAutoplayDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->policy->buildPolicy());
- }
-
- public function testGetPolicyAutoplayDomainValidMultiple(): void {
- $expectedPolicy = "autoplay www.nextcloud.com www.nextcloud.org;camera 'none';fullscreen 'none';geolocation 'none';microphone 'none';payment 'none'";
-
- $this->policy->addAllowedAutoplayDomain('www.nextcloud.com');
- $this->policy->addAllowedAutoplayDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->policy->buildPolicy());
- }
-
- public function testGetPolicyCameraDomainValid(): void {
- $expectedPolicy = "autoplay 'none';camera www.nextcloud.com;fullscreen 'none';geolocation 'none';microphone 'none';payment 'none'";
-
- $this->policy->addAllowedCameraDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->policy->buildPolicy());
- }
-
- public function testGetPolicyCameraDomainValidMultiple(): void {
- $expectedPolicy = "autoplay 'none';camera www.nextcloud.com www.nextcloud.org;fullscreen 'none';geolocation 'none';microphone 'none';payment 'none'";
-
- $this->policy->addAllowedCameraDomain('www.nextcloud.com');
- $this->policy->addAllowedCameraDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->policy->buildPolicy());
- }
-
- public function testGetPolicyFullScreenDomainValid(): void {
- $expectedPolicy = "autoplay 'none';camera 'none';fullscreen www.nextcloud.com;geolocation 'none';microphone 'none';payment 'none'";
-
- $this->policy->addAllowedFullScreenDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->policy->buildPolicy());
- }
-
- public function testGetPolicyFullScreenDomainValidMultiple(): void {
- $expectedPolicy = "autoplay 'none';camera 'none';fullscreen www.nextcloud.com www.nextcloud.org;geolocation 'none';microphone 'none';payment 'none'";
-
- $this->policy->addAllowedFullScreenDomain('www.nextcloud.com');
- $this->policy->addAllowedFullScreenDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->policy->buildPolicy());
- }
-
- public function testGetPolicyGeoLocationDomainValid(): void {
- $expectedPolicy = "autoplay 'none';camera 'none';fullscreen 'none';geolocation www.nextcloud.com;microphone 'none';payment 'none'";
-
- $this->policy->addAllowedGeoLocationDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->policy->buildPolicy());
- }
-
- public function testGetPolicyGeoLocationDomainValidMultiple(): void {
- $expectedPolicy = "autoplay 'none';camera 'none';fullscreen 'none';geolocation www.nextcloud.com www.nextcloud.org;microphone 'none';payment 'none'";
-
- $this->policy->addAllowedGeoLocationDomain('www.nextcloud.com');
- $this->policy->addAllowedGeoLocationDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->policy->buildPolicy());
- }
-
- public function testGetPolicyMicrophoneDomainValid(): void {
- $expectedPolicy = "autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone www.nextcloud.com;payment 'none'";
-
- $this->policy->addAllowedMicrophoneDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->policy->buildPolicy());
- }
-
- public function testGetPolicyMicrophoneDomainValidMultiple(): void {
- $expectedPolicy = "autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone www.nextcloud.com www.nextcloud.org;payment 'none'";
-
- $this->policy->addAllowedMicrophoneDomain('www.nextcloud.com');
- $this->policy->addAllowedMicrophoneDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->policy->buildPolicy());
- }
-
- public function testGetPolicyPaymentDomainValid(): void {
- $expectedPolicy = "autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone 'none';payment www.nextcloud.com";
-
- $this->policy->addAllowedPaymentDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->policy->buildPolicy());
- }
-
- public function testGetPolicyPaymentDomainValidMultiple(): void {
- $expectedPolicy = "autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone 'none';payment www.nextcloud.com www.nextcloud.org";
-
- $this->policy->addAllowedPaymentDomain('www.nextcloud.com');
- $this->policy->addAllowedPaymentDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->policy->buildPolicy());
- }
-}
--- a/tests/lib/AppFramework/Http/FeaturePolicyTest.php
+++ /dev/null
@@ -1,117 +0,0 @@
-policy = new FeaturePolicy();
- }
-
- public function testGetPolicyDefault(): void {
- $defaultPolicy = "autoplay 'self';camera 'none';fullscreen 'self';geolocation 'none';microphone 'none';payment 'none'";
- $this->assertSame($defaultPolicy, $this->policy->buildPolicy());
- }
-
- public function testGetPolicyAutoplayDomainValid(): void {
- $expectedPolicy = "autoplay 'self' www.nextcloud.com;camera 'none';fullscreen 'self';geolocation 'none';microphone 'none';payment 'none'";
-
- $this->policy->addAllowedAutoplayDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->policy->buildPolicy());
- }
-
- public function testGetPolicyAutoplayDomainValidMultiple(): void {
- $expectedPolicy = "autoplay 'self' www.nextcloud.com www.nextcloud.org;camera 'none';fullscreen 'self';geolocation 'none';microphone 'none';payment 'none'";
-
- $this->policy->addAllowedAutoplayDomain('www.nextcloud.com');
- $this->policy->addAllowedAutoplayDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->policy->buildPolicy());
- }
-
- public function testGetPolicyCameraDomainValid(): void {
- $expectedPolicy = "autoplay 'self';camera www.nextcloud.com;fullscreen 'self';geolocation 'none';microphone 'none';payment 'none'";
-
- $this->policy->addAllowedCameraDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->policy->buildPolicy());
- }
-
- public function testGetPolicyCameraDomainValidMultiple(): void {
- $expectedPolicy = "autoplay 'self';camera www.nextcloud.com www.nextcloud.org;fullscreen 'self';geolocation 'none';microphone 'none';payment 'none'";
-
- $this->policy->addAllowedCameraDomain('www.nextcloud.com');
- $this->policy->addAllowedCameraDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->policy->buildPolicy());
- }
-
- public function testGetPolicyFullScreenDomainValid(): void {
- $expectedPolicy = "autoplay 'self';camera 'none';fullscreen 'self' www.nextcloud.com;geolocation 'none';microphone 'none';payment 'none'";
-
- $this->policy->addAllowedFullScreenDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->policy->buildPolicy());
- }
-
- public function testGetPolicyFullScreenDomainValidMultiple(): void {
- $expectedPolicy = "autoplay 'self';camera 'none';fullscreen 'self' www.nextcloud.com www.nextcloud.org;geolocation 'none';microphone 'none';payment 'none'";
-
- $this->policy->addAllowedFullScreenDomain('www.nextcloud.com');
- $this->policy->addAllowedFullScreenDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->policy->buildPolicy());
- }
-
- public function testGetPolicyGeoLocationDomainValid(): void {
- $expectedPolicy = "autoplay 'self';camera 'none';fullscreen 'self';geolocation www.nextcloud.com;microphone 'none';payment 'none'";
-
- $this->policy->addAllowedGeoLocationDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->policy->buildPolicy());
- }
-
- public function testGetPolicyGeoLocationDomainValidMultiple(): void {
- $expectedPolicy = "autoplay 'self';camera 'none';fullscreen 'self';geolocation www.nextcloud.com www.nextcloud.org;microphone 'none';payment 'none'";
-
- $this->policy->addAllowedGeoLocationDomain('www.nextcloud.com');
- $this->policy->addAllowedGeoLocationDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->policy->buildPolicy());
- }
-
- public function testGetPolicyMicrophoneDomainValid(): void {
- $expectedPolicy = "autoplay 'self';camera 'none';fullscreen 'self';geolocation 'none';microphone www.nextcloud.com;payment 'none'";
-
- $this->policy->addAllowedMicrophoneDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->policy->buildPolicy());
- }
-
- public function testGetPolicyMicrophoneDomainValidMultiple(): void {
- $expectedPolicy = "autoplay 'self';camera 'none';fullscreen 'self';geolocation 'none';microphone www.nextcloud.com www.nextcloud.org;payment 'none'";
-
- $this->policy->addAllowedMicrophoneDomain('www.nextcloud.com');
- $this->policy->addAllowedMicrophoneDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->policy->buildPolicy());
- }
-
- public function testGetPolicyPaymentDomainValid(): void {
- $expectedPolicy = "autoplay 'self';camera 'none';fullscreen 'self';geolocation 'none';microphone 'none';payment www.nextcloud.com";
-
- $this->policy->addAllowedPaymentDomain('www.nextcloud.com');
- $this->assertSame($expectedPolicy, $this->policy->buildPolicy());
- }
-
- public function testGetPolicyPaymentDomainValidMultiple(): void {
- $expectedPolicy = "autoplay 'self';camera 'none';fullscreen 'self';geolocation 'none';microphone 'none';payment www.nextcloud.com www.nextcloud.org";
-
- $this->policy->addAllowedPaymentDomain('www.nextcloud.com');
- $this->policy->addAllowedPaymentDomain('www.nextcloud.org');
- $this->assertSame($expectedPolicy, $this->policy->buildPolicy());
- }
-}
--- a/tests/lib/AppFramework/Http/FileDisplayResponseTest.php
+++ /dev/null
@@ -1,195 +0,0 @@
-file = $this->createMock(File::class);
- $this->file->expects($this->once())
- ->method('getETag')
- ->willReturn('myETag');
- $this->file->expects($this->once())
- ->method('getName')
- ->willReturn('myFileName');
- $this->file->expects($this->once())
- ->method('getMTime')
- ->willReturn(1464825600);
-
- $this->response = new FileDisplayResponse($this->file);
- }
-
- public function testHeader(): void {
- $headers = $this->response->getHeaders();
- $this->assertArrayHasKey('Content-Disposition', $headers);
- $this->assertSame('inline; filename="myFileName"', $headers['Content-Disposition']);
- }
-
- public function testETag(): void {
- $this->assertSame('myETag', $this->response->getETag());
- }
-
- public function testLastModified(): void {
- $lastModified = $this->response->getLastModified();
- $this->assertNotNull($lastModified);
- $this->assertSame(1464825600, $lastModified->getTimestamp());
- }
-
- public function test304(): void {
- $output = $this->getMockBuilder(IOutput::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $output->expects($this->any())
- ->method('getHttpResponseCode')
- ->willReturn(Http::STATUS_NOT_MODIFIED);
- $output->expects($this->never())
- ->method('setOutput');
- $this->file->expects($this->never())
- ->method('getContent');
-
- $this->response->callback($output);
- }
-
- public function testNon304(): void {
- $resource = fopen('php://memory', 'w+b');
- fwrite($resource, 'my data');
- rewind($resource);
-
- $this->file->expects($this->once())
- ->method('fopen')
- ->willReturn($resource);
- $this->file->expects($this->any())
- ->method('getSize')
- ->willReturn(7);
-
- $output = $this->getMockBuilder(IOutput::class)
- ->disableOriginalConstructor()
- ->getMock();
- $output->expects($this->once())
- ->method('getHttpResponseCode')
- ->willReturn(Http::STATUS_OK);
- $output->expects($this->once())
- ->method('setReadFile')
- ->with($this->equalTo($resource));
- $output->expects($this->once())
- ->method('setHeader')
- ->with($this->equalTo('Content-Length: 7'));
-
- $this->response->callback($output);
- }
-
- public function testFileNotFound(): void {
- $this->file->expects($this->once())
- ->method('fopen')
- ->willReturn(false);
-
- $output = $this->getMockBuilder(IOutput::class)
- ->disableOriginalConstructor()
- ->getMock();
- $output->expects($this->once())
- ->method('getHttpResponseCode')
- ->willReturn(Http::STATUS_OK);
- $output->expects($this->once())
- ->method('setHttpResponseCode')
- ->with($this->equalTo(Http::STATUS_NOT_FOUND));
- $output->expects($this->once())
- ->method('setOutput')
- ->with($this->equalTo(''));
-
- $this->response->callback($output);
- }
-
- public function testSimpleFileNotFound(): void {
- $file = $this->createMock(ISimpleFile::class);
- $file->expects($this->once())
- ->method('getETag')
- ->willReturn('myETag');
- $file->expects($this->once())
- ->method('getName')
- ->willReturn('myFileName');
- $file->expects($this->once())
- ->method('getMTime')
- ->willReturn(1464825600);
- $file->expects($this->once())
- ->method('read')
- ->willReturn(false);
-
- $response = new FileDisplayResponse($file);
-
- $output = $this->getMockBuilder(IOutput::class)
- ->disableOriginalConstructor()
- ->getMock();
- $output->expects($this->once())
- ->method('getHttpResponseCode')
- ->willReturn(Http::STATUS_OK);
- $output->expects($this->once())
- ->method('setHttpResponseCode')
- ->with($this->equalTo(Http::STATUS_NOT_FOUND));
- $output->expects($this->once())
- ->method('setOutput')
- ->with($this->equalTo(''));
-
- $response->callback($output);
- }
-
- public function testSimpleFile(): void {
- $file = $this->createMock(ISimpleFile::class);
- $file->expects($this->once())
- ->method('getETag')
- ->willReturn('myETag');
- $file->expects($this->once())
- ->method('getName')
- ->willReturn('myFileName');
- $file->expects($this->once())
- ->method('getMTime')
- ->willReturn(1464825600);
-
- $resource = fopen('php://memory', 'w+b');
- fwrite($resource, 'my data');
- rewind($resource);
-
- $file->expects($this->once())
- ->method('read')
- ->willReturn($resource);
- $file->expects($this->any())
- ->method('getSize')
- ->willReturn(7);
-
- $response = new FileDisplayResponse($file);
-
- $output = $this->getMockBuilder(IOutput::class)
- ->disableOriginalConstructor()
- ->getMock();
- $output->expects($this->once())
- ->method('getHttpResponseCode')
- ->willReturn(Http::STATUS_OK);
- $output->expects($this->once())
- ->method('setReadFile')
- ->with($this->equalTo($resource));
- $output->expects($this->once())
- ->method('setHeader')
- ->with($this->equalTo('Content-Length: 7'));
-
- $response->callback($output);
- }
-}
--- a/tests/lib/AppFramework/Http/HttpTest.php
+++ /dev/null
@@ -1,47 +0,0 @@
-server = [];
- $this->http = new Http($this->server);
- }
-
- public function testProtocol(): void {
- $header = $this->http->getStatusHeader(Http::STATUS_TEMPORARY_REDIRECT);
- $this->assertEquals('HTTP/1.1 307 Temporary Redirect', $header);
- }
-
- public function testProtocol10(): void {
- $this->http = new Http($this->server, 'HTTP/1.0');
- $header = $this->http->getStatusHeader(Http::STATUS_OK);
- $this->assertEquals('HTTP/1.0 200 OK', $header);
- }
-
- public function testTempRedirectBecomesFoundInHttp10(): void {
- $http = new Http([], 'HTTP/1.0');
-
- $header = $http->getStatusHeader(Http::STATUS_TEMPORARY_REDIRECT);
- $this->assertEquals('HTTP/1.0 302 Found', $header);
- }
- // TODO: write unittests for http codes
-}
--- a/tests/lib/AppFramework/Http/JSONResponseTest.php
+++ /dev/null
@@ -1,95 +0,0 @@
-json = new JSONResponse();
- }
-
- public function testHeader(): void {
- $headers = $this->json->getHeaders();
- $this->assertEquals('application/json; charset=utf-8', $headers['Content-Type']);
- }
-
- public function testSetData(): void {
- $params = ['hi', 'yo'];
- $this->json->setData($params);
-
- $this->assertEquals(['hi', 'yo'], $this->json->getData());
- }
-
- public function testSetRender(): void {
- $params = ['test' => 'hi'];
- $this->json->setData($params);
-
- $expected = '{"test":"hi"}';
-
- $this->assertEquals($expected, $this->json->render());
- }
-
- public static function renderDataProvider(): array {
- return [
- [
- ['test' => 'hi'], '{"test":"hi"}',
- ],
- [
- ['test' => 'hi'], '{"\u003Ch1\u003Etest":"\u003Ch1\u003Ehi"}',
- ],
- ];
- }
-
- /**
- * @param array $input
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('renderDataProvider')]
- public function testRender(array $input, $expected): void {
- $this->json->setData($input);
- $this->assertEquals($expected, $this->json->render());
- }
-
- public function testRenderWithNonUtf8Encoding(): void {
- $this->expectException(\JsonException::class);
- $this->expectExceptionMessage('Malformed UTF-8 characters, possibly incorrectly encoded');
-
- $params = ['test' => hex2bin('e9')];
- $this->json->setData($params);
- $this->json->render();
- }
-
- public function testConstructorAllowsToSetData(): void {
- $data = ['hi'];
- $code = 300;
- $response = new JSONResponse($data, $code);
-
- $expected = '["hi"]';
- $this->assertEquals($expected, $response->render());
- $this->assertEquals($code, $response->getStatus());
- }
-
- public function testChainability(): void {
- $params = ['hi', 'yo'];
- $this->json->setData($params)
- ->setStatus(Http::STATUS_NOT_FOUND);
-
- $this->assertEquals(Http::STATUS_NOT_FOUND, $this->json->getStatus());
- $this->assertEquals(['hi', 'yo'], $this->json->getData());
- }
-}
--- a/tests/lib/AppFramework/Http/OutputTest.php
+++ /dev/null
@@ -1,30 +0,0 @@
-expectOutputString('foo');
- $output = new Output('');
- $output->setOutput('foo');
- }
-
- public function testSetReadfile(): void {
- $this->expectOutputString(file_get_contents(__FILE__));
- $output = new Output('');
- $output->setReadfile(__FILE__);
- }
-
- public function testSetReadfileStream(): void {
- $this->expectOutputString(file_get_contents(__FILE__));
- $output = new Output('');
- $output->setReadfile(fopen(__FILE__, 'r'));
- }
-}
--- a/tests/lib/AppFramework/Http/PublicTemplateResponseTest.php
+++ /dev/null
@@ -1,60 +0,0 @@
- 'value']);
- $this->assertEquals(['key' => 'value'], $template->getParams());
- }
-
- public function testAdditionalElements(): void {
- $template = new PublicTemplateResponse('app', 'home', ['key' => 'value']);
- $template->setHeaderTitle('Header');
- $template->setHeaderDetails('Details');
- $this->assertEquals(['key' => 'value'], $template->getParams());
- $this->assertEquals('Header', $template->getHeaderTitle());
- $this->assertEquals('Details', $template->getHeaderDetails());
- }
-
- public function testActionSingle(): void {
- $actions = [
- new SimpleMenuAction('link', 'Download', 'download', 'downloadLink', 0)
- ];
- $template = new PublicTemplateResponse('app', 'home', ['key' => 'value']);
- $template->setHeaderActions($actions);
- $this->assertEquals(['key' => 'value'], $template->getParams());
- $this->assertEquals($actions[0], $template->getPrimaryAction());
- $this->assertEquals(1, $template->getActionCount());
- $this->assertEquals([], $template->getOtherActions());
- }
-
- public function testActionMultiple(): void {
- $actions = [
- new SimpleMenuAction('link1', 'Download1', 'download1', 'downloadLink1', 100),
- new SimpleMenuAction('link2', 'Download2', 'download2', 'downloadLink2', 20),
- new SimpleMenuAction('link3', 'Download3', 'download3', 'downloadLink3', 0)
- ];
- $template = new PublicTemplateResponse('app', 'home', ['key' => 'value']);
- $template->setHeaderActions($actions);
- $this->assertEquals(['key' => 'value'], $template->getParams());
- $this->assertEquals($actions[2], $template->getPrimaryAction());
- $this->assertEquals(3, $template->getActionCount());
- $this->assertEquals([$actions[1], $actions[0]], $template->getOtherActions());
- }
-
- public function testGetRenderAs(): void {
- $template = new PublicTemplateResponse('app', 'home', ['key' => 'value']);
- $this->assertEquals(['key' => 'value'], $template->getParams());
- $this->assertEquals('public', $template->getRenderAs());
- }
-}
--- a/tests/lib/AppFramework/Http/RedirectResponseTest.php
+++ /dev/null
@@ -1,36 +0,0 @@
-response = new RedirectResponse('/url');
- }
-
- public function testHeaders(): void {
- $headers = $this->response->getHeaders();
- $this->assertEquals('/url', $headers['Location']);
- $this->assertEquals(Http::STATUS_SEE_OTHER,
- $this->response->getStatus());
- }
-
- public function testGetRedirectUrl(): void {
- $this->assertEquals('/url', $this->response->getRedirectUrl());
- }
-}
--- a/tests/lib/AppFramework/Http/RequestIdTest.php
+++ /dev/null
@@ -1,58 +0,0 @@
-secureRandom = $this->createMock(ISecureRandom::class);
- }
-
- public function testGetIdWithModUnique(): void {
- $requestId = new RequestId(
- 'GeneratedUniqueIdByModUnique',
- $this->secureRandom
- );
-
- $this->secureRandom->expects($this->never())
- ->method('generate');
-
- $this->assertSame('GeneratedUniqueIdByModUnique', $requestId->getId());
- $this->assertSame('GeneratedUniqueIdByModUnique', $requestId->getId());
- }
-
- public function testGetIdWithoutModUnique(): void {
- $requestId = new RequestId(
- '',
- $this->secureRandom
- );
-
- $this->secureRandom->expects($this->once())
- ->method('generate')
- ->with('20')
- ->willReturn('GeneratedByNextcloudItself1');
-
- $this->assertSame('GeneratedByNextcloudItself1', $requestId->getId());
- $this->assertSame('GeneratedByNextcloudItself1', $requestId->getId());
- }
-}
--- a/tests/lib/AppFramework/Http/RequestStream.php
+++ /dev/null
@@ -1,117 +0,0 @@
-varname = $url['host'] ?? '';
- $this->position = 0;
-
- return true;
- }
-
- public function stream_read(int $count): string {
- $ret = substr($GLOBALS[$this->varname], $this->position, $count);
- $this->position += strlen($ret);
- return $ret;
- }
-
- public function stream_write(string $data): int {
- $left = substr($GLOBALS[$this->varname], 0, $this->position);
- $right = substr($GLOBALS[$this->varname], $this->position + strlen($data));
- $GLOBALS[$this->varname] = $left . $data . $right;
- $this->position += strlen($data);
- return strlen($data);
- }
-
- public function stream_tell(): int {
- return $this->position;
- }
-
- public function stream_eof(): bool {
- return $this->position >= strlen($GLOBALS[$this->varname]);
- }
-
- public function stream_seek(int $offset, int $whence = SEEK_SET): bool {
- switch ($whence) {
- case SEEK_SET:
- if ($offset < strlen($GLOBALS[$this->varname]) && $offset >= 0) {
- $this->position = $offset;
- return true;
- } else {
- return false;
- }
- break;
-
- case SEEK_CUR:
- if ($offset >= 0) {
- $this->position += $offset;
- return true;
- } else {
- return false;
- }
- break;
-
- case SEEK_END:
- if (strlen($GLOBALS[$this->varname]) + $offset >= 0) {
- $this->position = strlen($GLOBALS[$this->varname]) + $offset;
- return true;
- } else {
- return false;
- }
- break;
-
- default:
- return false;
- }
- }
-
- public function stream_stat(): array {
- $size = strlen($GLOBALS[$this->varname]);
- $time = time();
- $data = [
- 'dev' => 0,
- 'ino' => 0,
- 'mode' => 0777,
- 'nlink' => 1,
- 'uid' => 0,
- 'gid' => 0,
- 'rdev' => '',
- 'size' => $size,
- 'atime' => $time,
- 'mtime' => $time,
- 'ctime' => $time,
- 'blksize' => -1,
- 'blocks' => -1,
- ];
- return array_values($data) + $data;
- //return false;
- }
-
- public function stream_metadata(string $path, int $option, $var): bool {
- if ($option === STREAM_META_TOUCH) {
- $url = parse_url($path);
- $varname = $url['host'] ?? '';
- if (!isset($GLOBALS[$varname])) {
- $GLOBALS[$varname] = '';
- }
- return true;
- }
- return false;
- }
-}
--- a/tests/lib/AppFramework/Http/RequestTest.php
+++ /dev/null
@@ -1,2318 +0,0 @@
-requestId = $this->createMock(IRequestId::class);
- $this->config = $this->createMock(IConfig::class);
- $this->csrfTokenManager = $this->getMockBuilder(CsrfTokenManager::class)
- ->disableOriginalConstructor()
- ->getMock();
- }
-
- #[\Override]
- protected function tearDown(): void {
- stream_wrapper_unregister('fakeinput');
- parent::tearDown();
- }
-
- public function testRequestAccessors(): void {
- $vars = [
- 'get' => ['name' => 'John Q. Public', 'nickname' => 'Joey'],
- 'method' => 'GET',
- ];
-
- $request = new Request(
- $vars,
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- // Countable
- $this->assertSame(2, count($request));
- // Array access
- $this->assertSame('Joey', $request['nickname']);
- // "Magic" accessors
- $this->assertSame('Joey', $request->{'nickname'});
- $this->assertTrue(isset($request['nickname']));
- $this->assertTrue(isset($request->{'nickname'}));
- $this->assertFalse(isset($request->{'flickname'}));
- // Only testing 'get', but same approach for post, files etc.
- $this->assertSame('Joey', $request->get['nickname']);
- // Always returns null if variable not set.
- $this->assertSame(null, $request->{'flickname'});
- }
-
- // urlParams has precedence over POST which has precedence over GET
- public function testPrecedence(): void {
- $vars = [
- 'get' => ['name' => 'John Q. Public', 'nickname' => 'Joey'],
- 'post' => ['name' => 'Jane Doe', 'nickname' => 'Janey'],
- 'urlParams' => ['user' => 'jw', 'name' => 'Johnny Weissmüller'],
- 'method' => 'GET'
- ];
-
- $request = new Request(
- $vars,
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame(3, count($request));
- $this->assertSame('Janey', $request->{'nickname'});
- $this->assertSame('Johnny Weissmüller', $request->{'name'});
- }
-
- public function testImmutableArrayAccess(): void {
- $this->expectException(\RuntimeException::class);
-
- $vars = [
- 'get' => ['name' => 'John Q. Public', 'nickname' => 'Joey'],
- 'method' => 'GET'
- ];
-
- $request = new Request(
- $vars,
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $request['nickname'] = 'Janey';
- }
-
- public function testImmutableMagicAccess(): void {
- $this->expectException(\RuntimeException::class);
-
- $vars = [
- 'get' => ['name' => 'John Q. Public', 'nickname' => 'Joey'],
- 'method' => 'GET'
- ];
-
- $request = new Request(
- $vars,
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $request->{'nickname'} = 'Janey';
- }
-
- public function testGetTheMethodRight(): void {
- $this->expectException(\LogicException::class);
-
- $vars = [
- 'get' => ['name' => 'John Q. Public', 'nickname' => 'Joey'],
- 'method' => 'GET',
- ];
-
- $request = new Request(
- $vars,
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $request->post;
- }
-
- public function testTheMethodIsRight(): void {
- $vars = [
- 'get' => ['name' => 'John Q. Public', 'nickname' => 'Joey'],
- 'method' => 'GET',
- ];
-
- $request = new Request(
- $vars,
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('GET', $request->method);
- $result = $request->get;
- $this->assertSame('John Q. Public', $result['name']);
- $this->assertSame('Joey', $result['nickname']);
- }
-
- public function testJsonPost(): void {
- global $data;
- $data = '{"name": "John Q. Public", "nickname": "Joey"}';
- $vars = [
- 'method' => 'POST',
- 'server' => ['CONTENT_TYPE' => 'application/json; utf-8']
- ];
-
- $request = new Request(
- $vars,
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('POST', $request->method);
- $result = $request->post;
- $this->assertSame('John Q. Public', $result['name']);
- $this->assertSame('Joey', $result['nickname']);
- $this->assertSame('Joey', $request->params['nickname']);
- $this->assertSame('Joey', $request['nickname']);
- }
-
- public function testScimJsonPost(): void {
- global $data;
- $data = '{"userName":"testusername", "displayName":"Example User"}';
- $vars = [
- 'method' => 'POST',
- 'server' => ['CONTENT_TYPE' => 'application/scim+json; utf-8']
- ];
-
- $request = new Request(
- $vars,
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('POST', $request->method);
- $result = $request->post;
- $this->assertSame('testusername', $result['userName']);
- $this->assertSame('Example User', $result['displayName']);
- $this->assertSame('Example User', $request->params['displayName']);
- $this->assertSame('Example User', $request['displayName']);
- }
-
- public function testCustomJsonPost(): void {
- global $data;
- $data = '{"propertyA":"sometestvalue", "propertyB":"someothertestvalue"}';
-
- // Note: the content type used here is fictional and intended to check if the regex for JSON content types works fine
- $vars = [
- 'method' => 'POST',
- 'server' => ['CONTENT_TYPE' => 'application/custom-type+json; utf-8']
- ];
-
- $request = new Request(
- $vars,
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('POST', $request->method);
- $result = $request->post;
- $this->assertSame('sometestvalue', $result['propertyA']);
- $this->assertSame('someothertestvalue', $result['propertyB']);
- }
-
- public static function dataNotJsonData(): array {
- return [
- ['this is not valid json'],
- ['"just a string"'],
- ['{"just a string"}'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataNotJsonData')]
- public function testNotJsonPost(string $testData): void {
- global $data;
- $data = $testData;
- $vars = [
- 'method' => 'POST',
- 'server' => ['CONTENT_TYPE' => 'application/json; utf-8']
- ];
-
- $request = new Request(
- $vars,
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertEquals('POST', $request->method);
- $result = $request->post;
- // ensure there's no error attempting to decode the content
- }
-
- public function testNotScimJsonPost(): void {
- global $data;
- $data = 'this is not valid scim json';
- $vars = [
- 'method' => 'POST',
- 'server' => ['CONTENT_TYPE' => 'application/scim+json; utf-8']
- ];
-
- $request = new Request(
- $vars,
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertEquals('POST', $request->method);
- $result = $request->post;
- // ensure there's no error attempting to decode the content
- }
-
- public function testNotCustomJsonPost(): void {
- global $data;
- $data = 'this is not valid json';
- $vars = [
- 'method' => 'POST',
- 'server' => ['CONTENT_TYPE' => 'application/custom-type+json; utf-8']
- ];
-
- $request = new Request(
- $vars,
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertEquals('POST', $request->method);
- $result = $request->post;
- // ensure there's no error attempting to decode the content
- }
-
- public function testPatch(): void {
- global $data;
- $data = http_build_query(['name' => 'John Q. Public', 'nickname' => 'Joey'], '', '&');
-
- $vars = [
- 'method' => 'PATCH',
- 'server' => ['CONTENT_TYPE' => 'application/x-www-form-urlencoded'],
- ];
-
- $request = new Request(
- $vars,
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('PATCH', $request->method);
- $result = $request->patch;
-
- $this->assertSame('John Q. Public', $result['name']);
- $this->assertSame('Joey', $result['nickname']);
- }
-
- public function testJsonPatchAndPut(): void {
- global $data;
-
- // PUT content
- $data = '{"name": "John Q. Public", "nickname": "Joey"}';
- $vars = [
- 'method' => 'PUT',
- 'server' => ['CONTENT_TYPE' => 'application/json; utf-8'],
- ];
-
- $request = new Request(
- $vars,
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('PUT', $request->method);
- $result = $request->put;
-
- $this->assertSame('John Q. Public', $result['name']);
- $this->assertSame('Joey', $result['nickname']);
-
- // PATCH content
- $data = '{"name": "John Q. Public", "nickname": null}';
- $vars = [
- 'method' => 'PATCH',
- 'server' => ['CONTENT_TYPE' => 'application/json; utf-8'],
- ];
-
- $request = new Request(
- $vars,
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('PATCH', $request->method);
- $result = $request->patch;
-
- $this->assertSame('John Q. Public', $result['name']);
- $this->assertSame(null, $result['nickname']);
- }
-
- public function testScimJsonPatchAndPut(): void {
- global $data;
-
- // PUT content
- $data = '{"userName": "sometestusername", "displayName": "Example User"}';
- $vars = [
- 'method' => 'PUT',
- 'server' => ['CONTENT_TYPE' => 'application/scim+json; utf-8'],
- ];
-
- $request = new Request(
- $vars,
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('PUT', $request->method);
- $result = $request->put;
-
- $this->assertSame('sometestusername', $result['userName']);
- $this->assertSame('Example User', $result['displayName']);
-
- // PATCH content
- $data = '{"userName": "sometestusername", "displayName": null}';
- $vars = [
- 'method' => 'PATCH',
- 'server' => ['CONTENT_TYPE' => 'application/scim+json; utf-8'],
- ];
-
- $request = new Request(
- $vars,
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('PATCH', $request->method);
- $result = $request->patch;
-
- $this->assertSame('sometestusername', $result['userName']);
- $this->assertSame(null, $result['displayName']);
- }
-
- public function testCustomJsonPatchAndPut(): void {
- global $data;
-
- // PUT content
- $data = '{"propertyA": "sometestvalue", "propertyB": "someothertestvalue"}';
- $vars = [
- 'method' => 'PUT',
- 'server' => ['CONTENT_TYPE' => 'application/custom-type+json; utf-8'],
- ];
-
- $request = new Request(
- $vars,
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('PUT', $request->method);
- $result = $request->put;
-
- $this->assertSame('sometestvalue', $result['propertyA']);
- $this->assertSame('someothertestvalue', $result['propertyB']);
-
- // PATCH content
- $data = '{"propertyA": "sometestvalue", "propertyB": null}';
- $vars = [
- 'method' => 'PATCH',
- 'server' => ['CONTENT_TYPE' => 'application/custom-type+json; utf-8'],
- ];
-
- $request = new Request(
- $vars,
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('PATCH', $request->method);
- $result = $request->patch;
-
- $this->assertSame('sometestvalue', $result['propertyA']);
- $this->assertSame(null, $result['propertyB']);
- }
-
- public function testPutStream(): void {
- global $data;
- $data = file_get_contents(__DIR__ . '/../../../data/testimage.png');
-
- $vars = [
- 'put' => $data,
- 'method' => 'PUT',
- 'server' => [
- 'CONTENT_TYPE' => 'image/png',
- 'CONTENT_LENGTH' => (string)strlen($data)
- ],
- ];
-
- $request = new Request(
- $vars,
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('PUT', $request->method);
- $resource = $request->put;
- $contents = stream_get_contents($resource);
- $this->assertSame($data, $contents);
-
- try {
- $resource = $request->put;
- } catch (\LogicException $e) {
- return;
- }
- $this->fail('Expected LogicException.');
- }
-
- public function testSetUrlParameters(): void {
- $vars = [
- 'post' => [],
- 'method' => 'POST',
- 'urlParams' => ['id' => '2'],
- ];
-
- $request = new Request(
- $vars,
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $newParams = ['id' => '3', 'test' => 'test2'];
- $request->setUrlParameters($newParams);
- $this->assertSame('test2', $request->getParam('test'));
- $this->assertEquals('3', $request->getParam('id'));
- $this->assertEquals('3', $request->getParams()['id']);
- }
-
- public static function dataGetRemoteAddress(): array {
- return [
- 'IPv4 without trusted remote' => [
- [
- 'REMOTE_ADDR' => '10.0.0.2',
- 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4',
- 'HTTP_X_FORWARDED_FOR' => '192.168.0.233',
- ],
- [],
- [],
- '10.0.0.2',
- ],
- 'IPv4 without trusted headers' => [
- [
- 'REMOTE_ADDR' => '10.0.0.2',
- 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4',
- 'HTTP_X_FORWARDED_FOR' => '192.168.0.233',
- ],
- ['10.0.0.2'],
- [],
- '10.0.0.2',
- ],
- 'IPv4 with single trusted remote' => [
- [
- 'REMOTE_ADDR' => '10.0.0.2',
- 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4',
- 'HTTP_X_FORWARDED_FOR' => '192.168.0.233',
- ],
- ['10.0.0.2'],
- ['HTTP_X_FORWARDED'],
- '10.4.0.4',
- ],
- 'IPv6 with single trusted remote' => [
- [
- 'REMOTE_ADDR' => '2001:db8:85a3:8d3:1319:8a2e:370:7348',
- 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4',
- 'HTTP_X_FORWARDED_FOR' => '192.168.0.233',
- ],
- ['2001:db8:85a3:8d3:1319:8a2e:370:7348'],
- ['HTTP_X_FORWARDED'],
- '10.4.0.4',
- ],
- 'IPv4 with multiple trusted remotes' => [
- [
- 'REMOTE_ADDR' => '10.0.0.2',
- 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4, ::1',
- 'HTTP_X_FORWARDED_FOR' => '192.168.0.233',
- ],
- ['10.0.0.2', '::1'],
- ['HTTP_X_FORWARDED'],
- '10.4.0.4',
- ],
- 'IPv4 order of forwarded-for headers' => [
- [
- 'REMOTE_ADDR' => '10.0.0.2',
- 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4',
- 'HTTP_X_FORWARDED_FOR' => '192.168.0.233',
- ],
- ['10.0.0.2'],
- [
- 'HTTP_X_FORWARDED',
- 'HTTP_X_FORWARDED_FOR',
- 'HTTP_CLIENT_IP',
- ],
- '192.168.0.233',
- ],
- 'IPv4 order of forwarded-for headers (reversed)' => [
- [
- 'REMOTE_ADDR' => '10.0.0.2',
- 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4',
- 'HTTP_X_FORWARDED_FOR' => '192.168.0.233',
- ],
- ['10.0.0.2'],
- [
- 'HTTP_CLIENT_IP',
- 'HTTP_X_FORWARDED_FOR',
- 'HTTP_X_FORWARDED',
- ],
- '10.4.0.4',
- ],
- 'IPv6 order of forwarded-for headers' => [
- [
- 'REMOTE_ADDR' => '2001:db8:85a3:8d3:1319:8a2e:370:7348',
- 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4',
- 'HTTP_X_FORWARDED_FOR' => '192.168.0.233',
- ],
- ['2001:db8:85a3:8d3:1319:8a2e:370:7348'],
- [
- 'HTTP_X_FORWARDED',
- 'HTTP_X_FORWARDED_FOR',
- 'HTTP_CLIENT_IP',
- ],
- '192.168.0.233',
- ],
- 'IPv4 matching CIDR of trusted proxy' => [
- [
- 'REMOTE_ADDR' => '192.168.3.99',
- 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4',
- 'HTTP_X_FORWARDED_FOR' => '192.168.0.233',
- ],
- ['192.168.2.0/24'],
- ['HTTP_X_FORWARDED_FOR'],
- '192.168.3.99',
- ],
- 'IPv6 matching CIDR of trusted proxy' => [
- [
- 'REMOTE_ADDR' => '2001:db8:85a3:8d3:1319:8a21:370:7348',
- 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4',
- 'HTTP_X_FORWARDED_FOR' => '192.168.0.233',
- ],
- ['2001:db8:85a3:8d3:1319:8a20::/95'],
- ['HTTP_X_FORWARDED_FOR'],
- '192.168.0.233',
- ],
- 'IPv6 not matching CIDR of trusted proxy' => [
- [
- 'REMOTE_ADDR' => '2001:db8:85a3:8d3:1319:8a2e:370:7348',
- 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4',
- 'HTTP_X_FORWARDED_FOR' => '192.168.0.233',
- ],
- ['fd::/8'],
- [],
- '2001:db8:85a3:8d3:1319:8a2e:370:7348',
- ],
- 'IPv6 with invalid trusted proxy' => [
- [
- 'REMOTE_ADDR' => '2001:db8:85a3:8d3:1319:8a2e:370:7348',
- 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4',
- 'HTTP_X_FORWARDED_FOR' => '192.168.0.233',
- ],
- ['fx::/8'],
- [],
- '2001:db8:85a3:8d3:1319:8a2e:370:7348',
- ],
- 'IPv4 forwarded for IPv6' => [
- [
- 'REMOTE_ADDR' => '192.168.2.99',
- 'HTTP_X_FORWARDED_FOR' => '[2001:db8:85a3:8d3:1319:8a2e:370:7348]',
- ],
- ['192.168.2.0/24'],
- ['HTTP_X_FORWARDED_FOR'],
- '2001:db8:85a3:8d3:1319:8a2e:370:7348',
- ],
- 'IPv4 with port' => [
- [
- 'REMOTE_ADDR' => '2001:db8:85a3:8d3:1319:8a2e:370:7348',
- 'HTTP_X_FORWARDED_FOR' => '192.168.2.99:8080',
- ],
- ['2001:db8::/8'],
- ['HTTP_X_FORWARDED_FOR'],
- '192.168.2.99',
- ],
- 'IPv6 with port' => [
- [
- 'REMOTE_ADDR' => '192.168.2.99',
- 'HTTP_X_FORWARDED_FOR' => '[2001:db8:85a3:8d3:1319:8a2e:370:7348]:8080',
- ],
- ['192.168.2.0/24'],
- ['HTTP_X_FORWARDED_FOR'],
- '2001:db8:85a3:8d3:1319:8a2e:370:7348',
- ],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetRemoteAddress')]
- public function testGetRemoteAddress(array $headers, array $trustedProxies, array $forwardedForHeaders, string $expected): void {
- $this->config
- ->method('getSystemValue')
- ->willReturnMap([
- ['trusted_proxies', [], $trustedProxies],
- ['forwarded_for_headers', ['HTTP_X_FORWARDED_FOR'], $forwardedForHeaders],
- ]);
-
- $request = new Request(
- [
- 'server' => $headers,
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame($expected, $request->getRemoteAddress());
- }
-
- public static function dataHttpProtocol(): array {
- return [
- // Valid HTTP 1.0
- ['HTTP/1.0', 'HTTP/1.0'],
- ['http/1.0', 'HTTP/1.0'],
- ['HTTp/1.0', 'HTTP/1.0'],
-
- // Valid HTTP 1.1
- ['HTTP/1.1', 'HTTP/1.1'],
- ['http/1.1', 'HTTP/1.1'],
- ['HTTp/1.1', 'HTTP/1.1'],
-
- // Valid HTTP 2.0
- ['HTTP/2', 'HTTP/2'],
- ['http/2', 'HTTP/2'],
- ['HTTp/2', 'HTTP/2'],
-
- // Invalid
- ['HTTp/394', 'HTTP/1.1'],
- ['InvalidProvider/1.1', 'HTTP/1.1'],
- [null, 'HTTP/1.1'],
- ['', 'HTTP/1.1'],
- ];
- }
-
- /**
- *
- * @param mixed $input
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataHttpProtocol')]
- public function testGetHttpProtocol($input, $expected): void {
- $request = new Request(
- [
- 'server' => [
- 'SERVER_PROTOCOL' => $input,
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame($expected, $request->getHttpProtocol());
- }
-
- public function testGetServerProtocolWithOverrideValid(): void {
- $this->config
- ->expects($this->exactly(3))
- ->method('getSystemValueString')
- ->willReturnMap([
- ['overwriteprotocol', '', 'HTTPS'], // should be automatically lowercased
- ['overwritecondaddr', '', ''],
- ]);
-
- $request = new Request(
- [],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('https', $request->getServerProtocol());
- }
-
- public function testGetServerProtocolWithOverrideInValid(): void {
- $this->config
- ->expects($this->exactly(3))
- ->method('getSystemValueString')
- ->willReturnMap([
- ['overwriteprotocol', '', 'bogusProtocol'], // should trigger fallback to http
- ['overwritecondaddr', '', ''],
- ]);
-
- $request = new Request(
- [],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('http', $request->getServerProtocol());
- }
-
- public function testGetServerProtocolWithProtoValid(): void {
- $this->config
- ->method('getSystemValue')
- ->willReturnCallback(function ($key, $default) {
- if ($key === 'trusted_proxies') {
- return ['1.2.3.4'];
- }
-
- return $default;
- });
-
- $requestHttps = new Request(
- [
- 'server' => [
- 'HTTP_X_FORWARDED_PROTO' => 'HtTpS',
- 'REMOTE_ADDR' => '1.2.3.4',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
- $requestHttp = new Request(
- [
- 'server' => [
- 'HTTP_X_FORWARDED_PROTO' => 'HTTp',
- 'REMOTE_ADDR' => '1.2.3.4',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('https', $requestHttps->getServerProtocol());
- $this->assertSame('http', $requestHttp->getServerProtocol());
- }
-
- public function testGetServerProtocolWithHttpsServerValueOn(): void {
- $this->config
- ->method('getSystemValue')
- ->willReturnCallback(function ($key, $default) {
- return $default;
- });
-
- $request = new Request(
- [
- 'server' => [
- 'HTTPS' => 'on'
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
- $this->assertSame('https', $request->getServerProtocol());
- }
-
- public function testGetServerProtocolWithHttpsServerValueOff(): void {
- $this->config
- ->method('getSystemValue')
- ->willReturnCallback(function ($key, $default) {
- return $default;
- });
-
- $request = new Request(
- [
- 'server' => [
- 'HTTPS' => 'off'
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
- $this->assertSame('http', $request->getServerProtocol());
- }
-
- public function testGetServerProtocolWithHttpsServerValueEmpty(): void {
- $this->config
- ->method('getSystemValue')
- ->willReturnCallback(function ($key, $default) {
- return $default;
- });
-
- $request = new Request(
- [
- 'server' => [
- 'HTTPS' => ''
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
- $this->assertSame('http', $request->getServerProtocol());
- }
-
- public function testGetServerProtocolDefault(): void {
- $this->config
- ->method('getSystemValue')
- ->willReturnCallback(function ($key, $default) {
- return $default;
- });
-
- $request = new Request(
- [],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
- $this->assertSame('http', $request->getServerProtocol());
- }
-
- public function testGetServerProtocolBehindLoadBalancers(): void {
- $this->config
- ->method('getSystemValue')
- ->willReturnCallback(function ($key, $default) {
- if ($key === 'trusted_proxies') {
- return ['1.2.3.4'];
- }
-
- return $default;
- });
-
- $request = new Request(
- [
- 'server' => [
- 'HTTP_X_FORWARDED_PROTO' => 'https,http,http',
- 'REMOTE_ADDR' => '1.2.3.4',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('https', $request->getServerProtocol());
- }
-
- /**
- * @param string $testAgent
- * @param array $userAgent
- * @param bool $matches
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataUserAgent')]
- public function testUserAgent($testAgent, $userAgent, $matches): void {
- $request = new Request(
- [
- 'server' => [
- 'HTTP_USER_AGENT' => $testAgent,
- ]
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame($matches, $request->isUserAgent($userAgent));
- }
-
- /**
- * @param string $testAgent
- * @param array $userAgent
- * @param bool $matches
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataUserAgent')]
- public function testUndefinedUserAgent($testAgent, $userAgent, $matches): void {
- $request = new Request(
- [],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertFalse($request->isUserAgent($userAgent));
- }
-
- public static function dataUserAgent(): array {
- return [
- [
- 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
- [
- Request::USER_AGENT_IE
- ],
- true,
- ],
- [
- 'Mozilla/5.0 (X11; Linux i686; rv:24.0) Gecko/20100101 Firefox/24.0',
- [
- Request::USER_AGENT_IE
- ],
- false,
- ],
- [
- 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36',
- [
- Request::USER_AGENT_CHROME
- ],
- true,
- ],
- [
- 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/53.0.2785.143 Chrome/53.0.2785.143 Safari/537.36',
- [
- Request::USER_AGENT_CHROME
- ],
- true,
- ],
- [
- 'Mozilla/5.0 (Linux; Android 4.4; Nexus 4 Build/KRT16S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.59 Mobile Safari/537.36',
- [
- Request::USER_AGENT_ANDROID_MOBILE_CHROME
- ],
- true,
- ],
- [
- 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
- [
- Request::USER_AGENT_ANDROID_MOBILE_CHROME
- ],
- false,
- ],
- [
- 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
- [
- Request::USER_AGENT_IE,
- Request::USER_AGENT_ANDROID_MOBILE_CHROME,
- ],
- true,
- ],
- [
- 'Mozilla/5.0 (Linux; Android 4.4; Nexus 4 Build/KRT16S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.59 Mobile Safari/537.36',
- [
- Request::USER_AGENT_IE,
- Request::USER_AGENT_ANDROID_MOBILE_CHROME,
- ],
- true,
- ],
- [
- 'Mozilla/5.0 (X11; Linux i686; rv:24.0) Gecko/20100101 Firefox/24.0',
- [
- Request::USER_AGENT_FREEBOX
- ],
- false,
- ],
- [
- 'Mozilla/5.0',
- [
- Request::USER_AGENT_FREEBOX
- ],
- true,
- ],
- [
- 'Fake Mozilla/5.0',
- [
- Request::USER_AGENT_FREEBOX
- ],
- false,
- ],
- [
- 'Mozilla/5.0 (Android) ownCloud-android/2.0.0',
- [
- Request::USER_AGENT_CLIENT_ANDROID
- ],
- true,
- ],
- [
- 'Mozilla/5.0 (Android) Nextcloud-android/2.0.0',
- [
- Request::USER_AGENT_CLIENT_ANDROID
- ],
- true,
- ],
- [
- 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.99 Safari/537.36 Vivaldi/2.9.1705.41',
- [
- Request::USER_AGENT_CHROME
- ],
- true
- ],
- [
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.38 Safari/537.36 Brave/75',
- [
- Request::USER_AGENT_CHROME
- ],
- true
- ],
- [
- 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 OPR/50.0.2762.67',
- [
- Request::USER_AGENT_CHROME
- ],
- true
- ]
- ];
- }
-
- public static function dataMatchClientVersion(): array {
- return [
- [
- 'Mozilla/5.0 (Android) Nextcloud-android/3.24.1',
- Request::USER_AGENT_CLIENT_ANDROID,
- '3.24.1',
- ],
- [
- 'Mozilla/5.0 (iOS) Nextcloud-iOS/4.8.2',
- Request::USER_AGENT_CLIENT_IOS,
- '4.8.2',
- ],
- [
- 'Mozilla/5.0 (Windows) mirall/3.8.1',
- Request::USER_AGENT_CLIENT_DESKTOP,
- '3.8.1',
- ],
- [
- 'Mozilla/5.0 (Android) Nextcloud-Talk v17.10.0',
- Request::USER_AGENT_TALK_ANDROID,
- '17.10.0',
- ],
- [
- 'Mozilla/5.0 (iOS) Nextcloud-Talk v17.0.1',
- Request::USER_AGENT_TALK_IOS,
- '17.0.1',
- ],
- [
- 'Mozilla/5.0 (Windows) Nextcloud-Talk v0.6.0',
- Request::USER_AGENT_TALK_DESKTOP,
- '0.6.0',
- ],
- [
- 'Mozilla/5.0 (Windows) Nextcloud-Outlook v1.0.0',
- Request::USER_AGENT_OUTLOOK_ADDON,
- '1.0.0',
- ],
- [
- 'Filelink for *cloud/1.0.0',
- Request::USER_AGENT_THUNDERBIRD_ADDON,
- '1.0.0',
- ],
- ];
- }
-
- /**
- * @param string $testAgent
- * @param string $userAgent
- * @param string $version
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataMatchClientVersion')]
- public function testMatchClientVersion(string $testAgent, string $userAgent, string $version): void {
- preg_match($userAgent, $testAgent, $matches);
-
- $this->assertSame($version, $matches[1]);
- }
-
- public function testInsecureServerHostServerNameHeader(): void {
- $request = new Request(
- [
- 'server' => [
- 'SERVER_NAME' => 'from.server.name:8080',
- ]
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('from.server.name:8080', $request->getInsecureServerHost());
- }
-
- public function testInsecureServerHostHttpHostHeader(): void {
- $request = new Request(
- [
- 'server' => [
- 'SERVER_NAME' => 'from.server.name:8080',
- 'HTTP_HOST' => 'from.host.header:8080',
- ]
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('from.host.header:8080', $request->getInsecureServerHost());
- }
-
- public function testInsecureServerHostHttpFromForwardedHeaderSingle(): void {
- $this->config
- ->method('getSystemValue')
- ->willReturnCallback(function ($key, $default) {
- if ($key === 'trusted_proxies') {
- return ['1.2.3.4'];
- }
-
- return $default;
- });
-
- $request = new Request(
- [
- 'server' => [
- 'SERVER_NAME' => 'from.server.name:8080',
- 'HTTP_HOST' => 'from.host.header:8080',
- 'HTTP_X_FORWARDED_HOST' => 'from.forwarded.host:8080',
- 'REMOTE_ADDR' => '1.2.3.4',
- ]
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('from.forwarded.host:8080', $request->getInsecureServerHost());
- }
-
- public function testInsecureServerHostHttpFromForwardedHeaderStacked(): void {
- $this->config
- ->method('getSystemValue')
- ->willReturnCallback(function ($key, $default) {
- if ($key === 'trusted_proxies') {
- return ['1.2.3.4'];
- }
-
- return $default;
- });
-
- $request = new Request(
- [
- 'server' => [
- 'SERVER_NAME' => 'from.server.name:8080',
- 'HTTP_HOST' => 'from.host.header:8080',
- 'HTTP_X_FORWARDED_HOST' => 'from.forwarded.host2:8080,another.one:9000',
- 'REMOTE_ADDR' => '1.2.3.4',
- ]
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('from.forwarded.host2:8080', $request->getInsecureServerHost());
- }
-
- public function testGetServerHostWithOverwriteHost(): void {
- $this->config
- ->method('getSystemValueString')
- ->willReturnCallback(function ($key, $default) {
- if ($key === 'overwritecondaddr') {
- return '';
- } elseif ($key === 'overwritehost') {
- return 'my.overwritten.host';
- }
-
- return $default;
- });
-
- $request = new Request(
- [],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('my.overwritten.host', $request->getServerHost());
- }
-
- public function testGetServerHostWithTrustedDomain(): void {
- $this->config
- ->method('getSystemValue')
- ->willReturnCallback(function ($key, $default) {
- if ($key === 'trusted_proxies') {
- return ['1.2.3.4'];
- } elseif ($key === 'trusted_domains') {
- return ['my.trusted.host'];
- }
-
- return $default;
- });
-
- $request = new Request(
- [
- 'server' => [
- 'HTTP_X_FORWARDED_HOST' => 'my.trusted.host',
- 'REMOTE_ADDR' => '1.2.3.4',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('my.trusted.host', $request->getServerHost());
- }
-
- public function testGetServerHostWithUntrustedDomain(): void {
- $this->config
- ->method('getSystemValue')
- ->willReturnCallback(function ($key, $default) {
- if ($key === 'trusted_proxies') {
- return ['1.2.3.4'];
- } elseif ($key === 'trusted_domains') {
- return ['my.trusted.host'];
- }
-
- return $default;
- });
-
- $request = new Request(
- [
- 'server' => [
- 'HTTP_X_FORWARDED_HOST' => 'my.untrusted.host',
- 'REMOTE_ADDR' => '1.2.3.4',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('my.trusted.host', $request->getServerHost());
- }
-
- public function testGetServerHostWithNoTrustedDomain(): void {
- $this->config
- ->method('getSystemValue')
- ->willReturnCallback(function ($key, $default) {
- if ($key === 'trusted_proxies') {
- return ['1.2.3.4'];
- }
- return $default;
- });
-
- $request = new Request(
- [
- 'server' => [
- 'HTTP_X_FORWARDED_HOST' => 'my.untrusted.host',
- 'REMOTE_ADDR' => '1.2.3.4',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('', $request->getServerHost());
- }
-
- public static function dataGetServerHostTrustedDomain(): array {
- return [
- 'is array' => ['my.trusted.host', ['my.trusted.host']],
- 'is array but undefined index 0' => ['my.trusted.host', [2 => 'my.trusted.host']],
- 'is string' => ['my.trusted.host', 'my.trusted.host'],
- 'is null' => ['', null],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetServerHostTrustedDomain')]
- public function testGetServerHostTrustedDomain(string $expected, $trustedDomain): void {
- $this->config
- ->method('getSystemValue')
- ->willReturnCallback(function ($key, $default) use ($trustedDomain) {
- if ($key === 'trusted_proxies') {
- return ['1.2.3.4'];
- }
- if ($key === 'trusted_domains') {
- return $trustedDomain;
- }
- return $default;
- });
-
- $request = new Request(
- [
- 'server' => [
- 'HTTP_X_FORWARDED_HOST' => 'my.untrusted.host',
- 'REMOTE_ADDR' => '1.2.3.4',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame($expected, $request->getServerHost());
- }
-
- public function testGetOverwriteHostDefaultNull(): void {
- $this->config
- ->expects($this->once())
- ->method('getSystemValueString')
- ->with('overwritehost')
- ->willReturn('');
- $request = new Request(
- [],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertNull(self::invokePrivate($request, 'getOverwriteHost'));
- }
-
- public function testGetOverwriteHostWithOverwrite(): void {
- $this->config
- ->expects($this->exactly(3))
- ->method('getSystemValueString')
- ->willReturnMap([
- ['overwritehost', '', 'www.owncloud.org'],
- ['overwritecondaddr', '', ''],
- ]);
-
- $request = new Request(
- [],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('www.owncloud.org', self::invokePrivate($request, 'getOverwriteHost'));
- }
-
- public function testGetPathInfoNotProcessible(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('The requested uri(/foo.php) cannot be processed by the script \'/var/www/index.php\')');
-
- $request = new Request(
- [
- 'server' => [
- 'REQUEST_URI' => '/foo.php',
- 'SCRIPT_NAME' => '/var/www/index.php',
- ]
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $request->getPathInfo();
- }
-
- public function testGetRawPathInfoNotProcessible(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('The requested uri(/foo.php) cannot be processed by the script \'/var/www/index.php\')');
-
- $request = new Request(
- [
- 'server' => [
- 'REQUEST_URI' => '/foo.php',
- 'SCRIPT_NAME' => '/var/www/index.php',
- ]
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $request->getRawPathInfo();
- }
-
- /**
- * @param string $requestUri
- * @param string $scriptName
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGenericPathInfo')]
- public function testGetPathInfoWithoutSetEnvGeneric($requestUri, $scriptName, $expected): void {
- $request = new Request(
- [
- 'server' => [
- 'REQUEST_URI' => $requestUri,
- 'SCRIPT_NAME' => $scriptName,
- ]
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame($expected, $request->getPathInfo());
- }
-
- /**
- * @param string $requestUri
- * @param string $scriptName
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGenericPathInfo')]
- public function testGetRawPathInfoWithoutSetEnvGeneric($requestUri, $scriptName, $expected): void {
- $request = new Request(
- [
- 'server' => [
- 'REQUEST_URI' => $requestUri,
- 'SCRIPT_NAME' => $scriptName,
- ]
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame($expected, $request->getRawPathInfo());
- }
-
- /**
- * @param string $requestUri
- * @param string $scriptName
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataRawPathInfo')]
- public function testGetRawPathInfoWithoutSetEnv($requestUri, $scriptName, $expected): void {
- $request = new Request(
- [
- 'server' => [
- 'REQUEST_URI' => $requestUri,
- 'SCRIPT_NAME' => $scriptName,
- ]
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame($expected, $request->getRawPathInfo());
- }
-
- /**
- * @param string $requestUri
- * @param string $scriptName
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataPathInfo')]
- public function testGetPathInfoWithoutSetEnv($requestUri, $scriptName, $expected): void {
- $request = new Request(
- [
- 'server' => [
- 'REQUEST_URI' => $requestUri,
- 'SCRIPT_NAME' => $scriptName,
- ]
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame($expected, $request->getPathInfo());
- }
-
- public static function dataGenericPathInfo(): array {
- return [
- ['/core/index.php?XDEBUG_SESSION_START=14600', '/core/index.php', ''],
- ['/index.php/apps/files/', 'index.php', '/apps/files/'],
- ['/index.php/apps/files/../&/&?someQueryParameter=QueryParam', 'index.php', '/apps/files/../&/&'],
- ['/remote.php/漢字編碼方法 / 汉字编码方法', 'remote.php', '/漢字編碼方法 / 汉字编码方法'],
- ['///removeTrailin//gSlashes///', 'remote.php', '/removeTrailin/gSlashes/'],
- ['/', '/', ''],
- ['', '', ''],
- ];
- }
-
- public static function dataRawPathInfo(): array {
- return [
- ['/foo%2Fbar/subfolder', '', 'foo%2Fbar/subfolder'],
- ];
- }
-
- public static function dataPathInfo(): array {
- return [
- ['/foo%2Fbar/subfolder', '', 'foo/bar/subfolder'],
- ];
- }
-
- public function testGetRawPathInfoWithoutScriptName(): void {
- $request = new Request(
- [
- 'server' => [
- 'REQUEST_URI' => '/index.php/apps/files/',
- ]
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('index.php/apps/files/', $request->getRawPathInfo());
- }
-
- public function testGetPathInfoWithoutScriptName(): void {
- $request = new Request(
- [
- 'server' => [
- 'REQUEST_URI' => '/index.php/apps/files/',
- ]
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('index.php/apps/files/', $request->getPathInfo());
- }
-
- public function testGetRawPathInfoWithoutScriptNameRoot(): void {
- $request = new Request(
- [
- 'server' => [
- 'REQUEST_URI' => '/',
- ]
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('', $request->getRawPathInfo());
- }
-
- public function testGetRawPathInfoWithoutScriptNameOrRequestUri(): void {
- $request = new Request(
- [
- 'server' => []
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('', $request->getRawPathInfo());
- }
-
- public function testGetRequestUriWithoutOverwrite(): void {
- $this->config
- ->expects($this->once())
- ->method('getSystemValueString')
- ->with('overwritewebroot')
- ->willReturn('');
-
- $request = new Request(
- [
- 'server' => [
- 'REQUEST_URI' => '/test.php'
- ]
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- );
-
- $this->assertSame('/test.php', $request->getRequestUri());
- }
-
- public static function dataGetRequestUriWithOverwrite(): array {
- return [
- ['/scriptname.php/some/PathInfo', '/owncloud/', ''],
- ['/scriptname.php/some/PathInfo', '/owncloud/', '123', '123.123.123.123'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetRequestUriWithOverwrite')]
- public function testGetRequestUriWithOverwrite($expectedUri, $overwriteWebRoot, $overwriteCondAddr, $remoteAddr = ''): void {
- $this->config
- ->expects($this->exactly(2))
- ->method('getSystemValueString')
- ->willReturnMap([
- ['overwritewebroot', '', $overwriteWebRoot],
- ['overwritecondaddr', '', $overwriteCondAddr],
- ]);
-
- $request = $this->getMockBuilder(Request::class)
- ->onlyMethods(['getScriptName'])
- ->setConstructorArgs([
- [
- 'server' => [
- 'REQUEST_URI' => '/test.php/some/PathInfo',
- 'SCRIPT_NAME' => '/test.php',
- 'REMOTE_ADDR' => $remoteAddr
- ]
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- ])
- ->getMock();
- $request
- ->expects($this->once())
- ->method('getScriptName')
- ->willReturn('/scriptname.php');
-
- $this->assertSame($expectedUri, $request->getRequestUri());
- }
-
- public function testPassesCSRFCheckWithGet(): void {
- /** @var Request $request */
- $request = $this->getMockBuilder(Request::class)
- ->onlyMethods(['getScriptName'])
- ->setConstructorArgs([
- [
- 'get' => [
- 'requesttoken' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
- ],
- 'cookies' => [
- 'nc_sameSiteCookiestrict' => 'true',
- 'nc_sameSiteCookielax' => 'true',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- ])
- ->getMock();
- $token = new CsrfToken('AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds');
- $this->csrfTokenManager
- ->expects($this->once())
- ->method('isTokenValid')
- ->with($token)
- ->willReturn(true);
-
- $this->assertTrue($request->passesCSRFCheck());
- }
-
- public function testPassesCSRFCheckWithPost(): void {
- /** @var Request $request */
- $request = $this->getMockBuilder(Request::class)
- ->onlyMethods(['getScriptName'])
- ->setConstructorArgs([
- [
- 'post' => [
- 'requesttoken' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
- ],
- 'cookies' => [
- 'nc_sameSiteCookiestrict' => 'true',
- 'nc_sameSiteCookielax' => 'true',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- ])
- ->getMock();
- $token = new CsrfToken('AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds');
- $this->csrfTokenManager
- ->expects($this->once())
- ->method('isTokenValid')
- ->with($token)
- ->willReturn(true);
-
- $this->assertTrue($request->passesCSRFCheck());
- }
-
- public function testPassesCSRFCheckWithHeader(): void {
- /** @var Request $request */
- $request = $this->getMockBuilder(Request::class)
- ->onlyMethods(['getScriptName'])
- ->setConstructorArgs([
- [
- 'server' => [
- 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
- ],
- 'cookies' => [
- 'nc_sameSiteCookiestrict' => 'true',
- 'nc_sameSiteCookielax' => 'true',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- ])
- ->getMock();
- $token = new CsrfToken('AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds');
- $this->csrfTokenManager
- ->expects($this->once())
- ->method('isTokenValid')
- ->with($token)
- ->willReturn(true);
-
- $this->assertTrue($request->passesCSRFCheck());
- }
-
- public function testPassesCSRFCheckWithGetAndWithoutCookies(): void {
- /** @var Request $request */
- $request = $this->getMockBuilder(Request::class)
- ->onlyMethods(['getScriptName'])
- ->setConstructorArgs([
- [
- 'get' => [
- 'requesttoken' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- ])
- ->getMock();
- $this->csrfTokenManager
- ->expects($this->once())
- ->method('isTokenValid')
- ->willReturn(true);
-
- $this->assertTrue($request->passesCSRFCheck());
- }
-
- public function testPassesCSRFCheckWithPostAndWithoutCookies(): void {
- /** @var Request $request */
- $request = $this->getMockBuilder(Request::class)
- ->onlyMethods(['getScriptName'])
- ->setConstructorArgs([
- [
- 'post' => [
- 'requesttoken' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- ])
- ->getMock();
- $this->csrfTokenManager
- ->expects($this->once())
- ->method('isTokenValid')
- ->willReturn(true);
-
- $this->assertTrue($request->passesCSRFCheck());
- }
-
- public function testPassesCSRFCheckWithHeaderAndWithoutCookies(): void {
- /** @var Request $request */
- $request = $this->getMockBuilder(Request::class)
- ->onlyMethods(['getScriptName'])
- ->setConstructorArgs([
- [
- 'server' => [
- 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- ])
- ->getMock();
- $this->csrfTokenManager
- ->expects($this->once())
- ->method('isTokenValid')
- ->willReturn(true);
-
- $this->assertTrue($request->passesCSRFCheck());
- }
-
- public function testFailsCSRFCheckWithHeaderAndNotAllChecksPassing(): void {
- /** @var Request $request */
- $request = $this->getMockBuilder(Request::class)
- ->onlyMethods(['getScriptName'])
- ->setConstructorArgs([
- [
- 'server' => [
- 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
- ],
- 'cookies' => [
- session_name() => 'asdf',
- 'nc_sameSiteCookiestrict' => 'true',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- ])
- ->getMock();
- $this->csrfTokenManager
- ->expects($this->never())
- ->method('isTokenValid');
-
- $this->assertFalse($request->passesCSRFCheck());
- }
-
- public function testPassesStrictCookieCheckWithAllCookiesAndStrict(): void {
- /** @var Request $request */
- $request = $this->getMockBuilder(Request::class)
- ->onlyMethods(['getScriptName', 'getCookieParams'])
- ->setConstructorArgs([
- [
- 'server' => [
- 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
- ],
- 'cookies' => [
- session_name() => 'asdf',
- '__Host-nc_sameSiteCookiestrict' => 'true',
- '__Host-nc_sameSiteCookielax' => 'true',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- ])
- ->getMock();
- $request
- ->expects($this->any())
- ->method('getCookieParams')
- ->willReturn([
- 'secure' => true,
- 'path' => '/',
- ]);
-
- $this->assertTrue($request->passesStrictCookieCheck());
- }
-
- public function testFailsStrictCookieCheckWithAllCookiesAndMissingStrict(): void {
- /** @var Request $request */
- $request = $this->getMockBuilder(Request::class)
- ->onlyMethods(['getScriptName', 'getCookieParams'])
- ->setConstructorArgs([
- [
- 'server' => [
- 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
- ],
- 'cookies' => [
- session_name() => 'asdf',
- 'nc_sameSiteCookiestrict' => 'true',
- 'nc_sameSiteCookielax' => 'true',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- ])
- ->getMock();
- $request
- ->expects($this->any())
- ->method('getCookieParams')
- ->willReturn([
- 'secure' => true,
- 'path' => '/',
- ]);
-
- $this->assertFalse($request->passesStrictCookieCheck());
- }
-
- public function testGetCookieParams(): void {
- /** @var Request $request */
- $request = $this->getMockBuilder(Request::class)
- ->onlyMethods(['getScriptName'])
- ->setConstructorArgs([
- [],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- ])
- ->getMock();
- $actual = $request->getCookieParams();
- $this->assertSame(session_get_cookie_params(), $actual);
- }
-
- public function testPassesStrictCookieCheckWithAllCookies(): void {
- /** @var Request $request */
- $request = $this->getMockBuilder(Request::class)
- ->onlyMethods(['getScriptName'])
- ->setConstructorArgs([
- [
- 'server' => [
- 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
- ],
- 'cookies' => [
- session_name() => 'asdf',
- 'nc_sameSiteCookiestrict' => 'true',
- 'nc_sameSiteCookielax' => 'true',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- ])
- ->getMock();
-
- $this->assertTrue($request->passesStrictCookieCheck());
- }
-
- public function testPassesStrictCookieCheckWithRandomCookies(): void {
- /** @var Request $request */
- $request = $this->getMockBuilder(Request::class)
- ->onlyMethods(['getScriptName'])
- ->setConstructorArgs([
- [
- 'server' => [
- 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
- ],
- 'cookies' => [
- 'RandomCookie' => 'asdf',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- ])
- ->getMock();
-
- $this->assertTrue($request->passesStrictCookieCheck());
- }
-
- public function testFailsStrictCookieCheckWithSessionCookie(): void {
- /** @var Request $request */
- $request = $this->getMockBuilder(Request::class)
- ->onlyMethods(['getScriptName'])
- ->setConstructorArgs([
- [
- 'server' => [
- 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
- ],
- 'cookies' => [
- session_name() => 'asdf',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- ])
- ->getMock();
-
- $this->assertFalse($request->passesStrictCookieCheck());
- }
-
- public function testFailsStrictCookieCheckWithRememberMeCookie(): void {
- /** @var Request $request */
- $request = $this->getMockBuilder(Request::class)
- ->onlyMethods(['getScriptName'])
- ->setConstructorArgs([
- [
- 'server' => [
- 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
- ],
- 'cookies' => [
- 'nc_token' => 'asdf',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- ])
- ->getMock();
-
- $this->assertFalse($request->passesStrictCookieCheck());
- }
-
- public function testFailsCSRFCheckWithPostAndWithCookies(): void {
- /** @var Request $request */
- $request = $this->getMockBuilder(Request::class)
- ->onlyMethods(['getScriptName'])
- ->setConstructorArgs([
- [
- 'post' => [
- 'requesttoken' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
- ],
- 'cookies' => [
- session_name() => 'asdf',
- 'foo' => 'bar',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- ])
- ->getMock();
- $this->csrfTokenManager
- ->expects($this->never())
- ->method('isTokenValid');
-
- $this->assertFalse($request->passesCSRFCheck());
- }
-
- public function testFailStrictCookieCheckWithOnlyLaxCookie(): void {
- /** @var Request $request */
- $request = $this->getMockBuilder(Request::class)
- ->onlyMethods(['getScriptName'])
- ->setConstructorArgs([
- [
- 'server' => [
- 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
- ],
- 'cookies' => [
- session_name() => 'asdf',
- 'nc_sameSiteCookielax' => 'true',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- ])
- ->getMock();
-
- $this->assertFalse($request->passesStrictCookieCheck());
- }
-
- public function testFailStrictCookieCheckWithOnlyStrictCookie(): void {
- /** @var Request $request */
- $request = $this->getMockBuilder(Request::class)
- ->onlyMethods(['getScriptName'])
- ->setConstructorArgs([
- [
- 'server' => [
- 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
- ],
- 'cookies' => [
- session_name() => 'asdf',
- 'nc_sameSiteCookiestrict' => 'true',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- ])
- ->getMock();
-
- $this->assertFalse($request->passesStrictCookieCheck());
- }
-
- public function testPassesLaxCookieCheck(): void {
- /** @var Request $request */
- $request = $this->getMockBuilder(Request::class)
- ->onlyMethods(['getScriptName'])
- ->setConstructorArgs([
- [
- 'server' => [
- 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
- ],
- 'cookies' => [
- session_name() => 'asdf',
- 'nc_sameSiteCookielax' => 'true',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- ])
- ->getMock();
-
- $this->assertTrue($request->passesLaxCookieCheck());
- }
-
- public function testFailsLaxCookieCheckWithOnlyStrictCookie(): void {
- /** @var Request $request */
- $request = $this->getMockBuilder(Request::class)
- ->onlyMethods(['getScriptName'])
- ->setConstructorArgs([
- [
- 'server' => [
- 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
- ],
- 'cookies' => [
- session_name() => 'asdf',
- 'nc_sameSiteCookiestrict' => 'true',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- ])
- ->getMock();
-
- $this->assertFalse($request->passesLaxCookieCheck());
- }
-
- public function testSkipCookieCheckForOCSRequests(): void {
- /** @var Request $request */
- $request = $this->getMockBuilder(Request::class)
- ->onlyMethods(['getScriptName'])
- ->setConstructorArgs([
- [
- 'server' => [
- 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds',
- 'HTTP_OCS_APIREQUEST' => 'true',
- ],
- 'cookies' => [
- session_name() => 'asdf',
- 'nc_sameSiteCookiestrict' => 'false',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- ])
- ->getMock();
-
- $this->assertTrue($request->passesStrictCookieCheck());
- }
-
- public static function dataInvalidToken(): array {
- return [
- ['InvalidSentToken'],
- ['InvalidSentToken:InvalidSecret'],
- [''],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataInvalidToken')]
- public function testPassesCSRFCheckWithInvalidToken(string $invalidToken): void {
- /** @var Request $request */
- $request = $this->getMockBuilder(Request::class)
- ->onlyMethods(['getScriptName'])
- ->setConstructorArgs([
- [
- 'server' => [
- 'HTTP_REQUESTTOKEN' => $invalidToken,
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- ])
- ->getMock();
-
- $token = new CsrfToken($invalidToken);
- $this->csrfTokenManager
- ->expects($this->any())
- ->method('isTokenValid')
- ->with($token)
- ->willReturn(false);
-
- $this->assertFalse($request->passesCSRFCheck());
- }
-
- public function testPassesCSRFCheckWithoutTokenFail(): void {
- /** @var Request $request */
- $request = $this->getMockBuilder(Request::class)
- ->onlyMethods(['getScriptName'])
- ->setConstructorArgs([
- [],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- ])
- ->getMock();
-
- $this->assertFalse($request->passesCSRFCheck());
- }
-
- public function testPassesCSRFCheckWithOCSAPIRequestHeader(): void {
- /** @var Request $request */
- $request = $this->getMockBuilder(Request::class)
- ->onlyMethods(['getScriptName'])
- ->setConstructorArgs([
- [
- 'server' => [
- 'HTTP_OCS_APIREQUEST' => 'true',
- ],
- ],
- $this->requestId,
- $this->config,
- $this->csrfTokenManager,
- $this->stream
- ])
- ->getMock();
-
- $this->assertTrue($request->passesCSRFCheck());
- }
-}
--- a/tests/lib/AppFramework/Http/ResponseTest.php
+++ /dev/null
@@ -1,262 +0,0 @@
-childResponse = new Response();
- }
-
- public function testAddHeader(): void {
- $this->childResponse->addHeader(' hello ', 'world');
- $headers = $this->childResponse->getHeaders();
- $this->assertEquals('world', $headers['hello']);
- }
-
- public function testSetHeaders(): void {
- $expected = [
- 'Last-Modified' => 1,
- 'ETag' => 3,
- 'Something-Else' => 'hi',
- 'X-Robots-Tag' => 'noindex, nofollow',
- 'Cache-Control' => 'no-cache, no-store, must-revalidate',
- ];
-
- $this->childResponse->setHeaders($expected);
- $expected['Content-Security-Policy'] = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'";
- $expected['Feature-Policy'] = "autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone 'none';payment 'none'";
-
- $headers = $this->childResponse->getHeaders();
- unset($headers['X-Request-Id']);
-
- $this->assertEquals($expected, $headers);
- }
-
- public function testOverwriteCsp(): void {
- $expected = [
- 'Content-Security-Policy' => "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self' 'unsafe-inline';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self' data:;connect-src 'self';media-src 'self'",
- ];
- $policy = new ContentSecurityPolicy();
-
- $this->childResponse->setContentSecurityPolicy($policy);
- $headers = $this->childResponse->getHeaders();
-
- $this->assertEquals(array_merge($expected, $headers), $headers);
- }
-
- public function testGetCsp(): void {
- $policy = new ContentSecurityPolicy();
-
- $this->childResponse->setContentSecurityPolicy($policy);
- $this->assertEquals($policy, $this->childResponse->getContentSecurityPolicy());
- }
-
- public function testGetCspEmpty(): void {
- $this->assertEquals(new EmptyContentSecurityPolicy(), $this->childResponse->getContentSecurityPolicy());
- }
-
- public function testAddHeaderValueNullDeletesIt(): void {
- $this->childResponse->addHeader('hello', 'world');
- $this->childResponse->addHeader('hello', null);
- $this->assertEquals(5, count($this->childResponse->getHeaders()));
- }
-
- public function testCacheHeadersAreDisabledByDefault(): void {
- $headers = $this->childResponse->getHeaders();
- $this->assertEquals('no-cache, no-store, must-revalidate', $headers['Cache-Control']);
- }
-
- public function testAddCookie(): void {
- $this->childResponse->addCookie('foo', 'bar');
- $this->childResponse->addCookie('bar', 'foo', new \DateTime('1970-01-01'));
-
- $expectedResponse = [
- 'foo' => [
- 'value' => 'bar',
- 'expireDate' => null,
- 'sameSite' => 'Lax',
- ],
- 'bar' => [
- 'value' => 'foo',
- 'expireDate' => new \DateTime('1970-01-01'),
- 'sameSite' => 'Lax',
- ]
- ];
- $this->assertEquals($expectedResponse, $this->childResponse->getCookies());
- }
-
- public function testSetCookies(): void {
- $expected = [
- 'foo' => [
- 'value' => 'bar',
- 'expireDate' => null,
- ],
- 'bar' => [
- 'value' => 'foo',
- 'expireDate' => new \DateTime('1970-01-01')
- ]
- ];
-
- $this->childResponse->setCookies($expected);
- $cookies = $this->childResponse->getCookies();
-
- $this->assertEquals($expected, $cookies);
- }
-
- public function testInvalidateCookie(): void {
- $this->childResponse->addCookie('foo', 'bar');
- $this->childResponse->invalidateCookie('foo');
- $expected = [
- 'foo' => [
- 'value' => 'expired',
- 'expireDate' => new \DateTime('1971-01-01'),
- 'sameSite' => 'Lax',
- ]
- ];
-
- $cookies = $this->childResponse->getCookies();
-
- $this->assertEquals($expected, $cookies);
- }
-
- public function testInvalidateCookies(): void {
- $this->childResponse->addCookie('foo', 'bar');
- $this->childResponse->addCookie('bar', 'foo');
- $expected = [
- 'foo' => [
- 'value' => 'bar',
- 'expireDate' => null,
- 'sameSite' => 'Lax',
- ],
- 'bar' => [
- 'value' => 'foo',
- 'expireDate' => null,
- 'sameSite' => 'Lax',
- ]
- ];
- $cookies = $this->childResponse->getCookies();
- $this->assertEquals($expected, $cookies);
-
- $this->childResponse->invalidateCookies(['foo', 'bar']);
- $expected = [
- 'foo' => [
- 'value' => 'expired',
- 'expireDate' => new \DateTime('1971-01-01'),
- 'sameSite' => 'Lax',
- ],
- 'bar' => [
- 'value' => 'expired',
- 'expireDate' => new \DateTime('1971-01-01'),
- 'sameSite' => 'Lax',
- ]
- ];
-
- $cookies = $this->childResponse->getCookies();
- $this->assertEquals($expected, $cookies);
- }
-
- public function testRenderReturnNullByDefault(): void {
- $this->assertEquals(null, $this->childResponse->render());
- }
-
- public function testGetStatus(): void {
- $default = $this->childResponse->getStatus();
-
- $this->childResponse->setStatus(Http::STATUS_NOT_FOUND);
-
- $this->assertEquals(Http::STATUS_OK, $default);
- $this->assertEquals(Http::STATUS_NOT_FOUND, $this->childResponse->getStatus());
- }
-
- public function testGetEtag(): void {
- $this->childResponse->setEtag('hi');
- $this->assertSame('hi', $this->childResponse->getEtag());
- }
-
- public function testGetLastModified(): void {
- $lastModified = new \DateTime('now', new \DateTimeZone('GMT'));
- $lastModified->setTimestamp(1);
- $this->childResponse->setLastModified($lastModified);
- $this->assertEquals($lastModified, $this->childResponse->getLastModified());
- }
-
- public function testCacheSecondsZero(): void {
- $this->childResponse->cacheFor(0);
-
- $headers = $this->childResponse->getHeaders();
- $this->assertEquals('no-cache, no-store, must-revalidate', $headers['Cache-Control']);
- $this->assertFalse(isset($headers['Expires']));
- }
-
- public function testCacheSeconds(): void {
- $time = $this->createMock(ITimeFactory::class);
- $time->method('getTime')
- ->willReturn(1234567);
-
- $this->overwriteService(ITimeFactory::class, $time);
-
- $this->childResponse->cacheFor(33);
-
- $headers = $this->childResponse->getHeaders();
- $this->assertEquals('private, max-age=33, must-revalidate', $headers['Cache-Control']);
- $this->assertEquals('Thu, 15 Jan 1970 06:56:40 GMT', $headers['Expires']);
- }
-
- public function testEtagLastModifiedHeaders(): void {
- $lastModified = new \DateTime('now', new \DateTimeZone('GMT'));
- $lastModified->setTimestamp(1);
- $this->childResponse->setLastModified($lastModified);
- $headers = $this->childResponse->getHeaders();
- $this->assertEquals('Thu, 01 Jan 1970 00:00:01 GMT', $headers['Last-Modified']);
- }
-
- public function testChainability(): void {
- $lastModified = new \DateTime('now', new \DateTimeZone('GMT'));
- $lastModified->setTimestamp(1);
-
- $this->childResponse->setEtag('hi')
- ->setStatus(Http::STATUS_NOT_FOUND)
- ->setLastModified($lastModified)
- ->cacheFor(33)
- ->addHeader('hello', 'world');
-
- $headers = $this->childResponse->getHeaders();
-
- $this->assertEquals('world', $headers['hello']);
- $this->assertEquals(Http::STATUS_NOT_FOUND, $this->childResponse->getStatus());
- $this->assertEquals('hi', $this->childResponse->getEtag());
- $this->assertEquals('Thu, 01 Jan 1970 00:00:01 GMT', $headers['Last-Modified']);
- $this->assertEquals('private, max-age=33, must-revalidate',
- $headers['Cache-Control']);
- }
-
- public function testThrottle(): void {
- $this->assertFalse($this->childResponse->isThrottled());
- $this->childResponse->throttle();
- $this->assertTrue($this->childResponse->isThrottled());
- }
-
- public function testGetThrottleMetadata(): void {
- $this->childResponse->throttle(['foo' => 'bar']);
- $this->assertSame(['foo' => 'bar'], $this->childResponse->getThrottleMetadata());
- }
-}
--- a/tests/lib/AppFramework/Http/StreamResponseTest.php
+++ /dev/null
@@ -1,83 +0,0 @@
-output = $this->getMockBuilder('OCP\\AppFramework\\Http\\IOutput')
- ->disableOriginalConstructor()
- ->getMock();
- }
-
- public function testOutputNotModified(): void {
- $path = __FILE__;
- $this->output->expects($this->once())
- ->method('getHttpResponseCode')
- ->willReturn(Http::STATUS_NOT_MODIFIED);
- $this->output->expects($this->never())
- ->method('setReadfile');
- $response = new StreamResponse($path);
-
- $response->callback($this->output);
- }
-
- public function testOutputOk(): void {
- $path = __FILE__;
- $this->output->expects($this->once())
- ->method('getHttpResponseCode')
- ->willReturn(Http::STATUS_OK);
- $this->output->expects($this->once())
- ->method('setReadfile')
- ->with($this->equalTo($path))
- ->willReturn(true);
- $response = new StreamResponse($path);
-
- $response->callback($this->output);
- }
-
- public function testOutputNotFound(): void {
- $path = __FILE__ . 'test';
- $this->output->expects($this->once())
- ->method('getHttpResponseCode')
- ->willReturn(Http::STATUS_OK);
- $this->output->expects($this->never())
- ->method('setReadfile');
- $this->output->expects($this->once())
- ->method('setHttpResponseCode')
- ->with($this->equalTo(Http::STATUS_NOT_FOUND));
- $response = new StreamResponse($path);
-
- $response->callback($this->output);
- }
-
- public function testOutputReadFileError(): void {
- $path = __FILE__;
- $this->output->expects($this->once())
- ->method('getHttpResponseCode')
- ->willReturn(Http::STATUS_OK);
- $this->output->expects($this->once())
- ->method('setReadfile')
- ->willReturn(false);
- $this->output->expects($this->once())
- ->method('setHttpResponseCode')
- ->with($this->equalTo(Http::STATUS_BAD_REQUEST));
- $response = new StreamResponse($path);
-
- $response->callback($this->output);
- }
-}
--- a/tests/lib/AppFramework/Http/StreamTraversableResponseTest.php
+++ /dev/null
@@ -1,52 +0,0 @@
- 'text/plain']);
-
- $headers = $response->getHeaders();
- $this->assertEquals('text/plain', $headers['Content-Type']);
- $this->assertEquals(200, $response->getStatus());
- }
-
- public function testCallback() {
- $count = 0;
- $generator = function () use (&$count) {
- $count++;
- yield 'chunk1';
- $count++;
- yield 'chunk2';
- };
- $response = new StreamTraversableResponse($generator(), 200, ['Content-Type' => 'text/plain']);
- $output = $this->createMock(IOutput::class);
- $output->expects($this->exactly(2))
- ->method('setOutput')
- ->with($this->callback(function ($chunk) {
- return in_array($chunk, ['chunk1', 'chunk2'], true);
- }));
-
- $response->callback($output);
- $this->assertEquals($count, 2);
- }
-
-}
--- a/tests/lib/AppFramework/Http/TemplateResponseTest.php
+++ /dev/null
@@ -1,66 +0,0 @@
-tpl = new TemplateResponse('app', 'home');
- }
-
- public function testSetParamsConstructor(): void {
- $params = ['hi' => 'yo'];
- $this->tpl = new TemplateResponse('app', 'home', $params);
-
- $this->assertEquals(['hi' => 'yo'], $this->tpl->getParams());
- }
-
- public function testSetRenderAsConstructor(): void {
- $renderAs = 'myrender';
- $this->tpl = new TemplateResponse('app', 'home', [], $renderAs);
-
- $this->assertEquals($renderAs, $this->tpl->getRenderAs());
- }
-
- public function testSetParams(): void {
- $params = ['hi' => 'yo'];
- $this->tpl->setParams($params);
-
- $this->assertEquals(['hi' => 'yo'], $this->tpl->getParams());
- }
-
- public function testGetTemplateName(): void {
- $this->assertEquals('home', $this->tpl->getTemplateName());
- }
-
- public function testGetRenderAs(): void {
- $render = 'myrender';
- $this->tpl->renderAs($render);
- $this->assertEquals($render, $this->tpl->getRenderAs());
- }
-
- public function testChainability(): void {
- $params = ['hi' => 'yo'];
- $this->tpl->setParams($params)
- ->setStatus(Http::STATUS_NOT_FOUND);
-
- $this->assertEquals(Http::STATUS_NOT_FOUND, $this->tpl->getStatus());
- $this->assertEquals(['hi' => 'yo'], $this->tpl->getParams());
- }
-}
--- a/tests/lib/AppFramework/Middleware/AdditionalScriptsMiddlewareTest.php
+++ /dev/null
@@ -1,115 +0,0 @@
-userSession = $this->createMock(IUserSession::class);
- $this->dispatcher = $this->createMock(IEventDispatcher::class);
- $this->middleWare = new AdditionalScriptsMiddleware(
- $this->userSession,
- $this->dispatcher
- );
-
- $this->controller = $this->createMock(Controller::class);
- }
-
- public function testNoTemplateResponse(): void {
- $this->userSession->expects($this->never())
- ->method($this->anything());
- $this->dispatcher->expects($this->never())
- ->method($this->anything());
-
- $this->middleWare->afterController($this->controller, 'myMethod', $this->createMock(Response::class));
- }
-
- public function testPublicShareController(): void {
- $this->userSession->expects($this->never())
- ->method($this->anything());
- $this->dispatcher->expects($this->never())
- ->method($this->anything());
-
- $this->middleWare->afterController($this->createMock(PublicShareController::class), 'myMethod', $this->createMock(Response::class));
- }
-
- public function testStandaloneTemplateResponse(): void {
- $this->userSession->expects($this->never())
- ->method($this->anything());
- $this->dispatcher->expects($this->once())
- ->method('dispatchTyped')
- ->willReturnCallback(function ($event): void {
- if ($event instanceof BeforeTemplateRenderedEvent && $event->isLoggedIn() === false) {
- return;
- }
-
- $this->fail('Wrong event dispatched');
- });
-
- $this->middleWare->afterController($this->controller, 'myMethod', $this->createMock(StandaloneTemplateResponse::class));
- }
-
- public function testTemplateResponseNotLoggedIn(): void {
- $this->userSession->method('isLoggedIn')
- ->willReturn(false);
- $this->dispatcher->expects($this->once())
- ->method('dispatchTyped')
- ->willReturnCallback(function ($event): void {
- if ($event instanceof BeforeTemplateRenderedEvent && $event->isLoggedIn() === false) {
- return;
- }
-
- $this->fail('Wrong event dispatched');
- });
-
- $this->middleWare->afterController($this->controller, 'myMethod', $this->createMock(TemplateResponse::class));
- }
-
- public function testTemplateResponseLoggedIn(): void {
- $events = [];
-
- $this->userSession->method('isLoggedIn')
- ->willReturn(true);
- $this->dispatcher->expects($this->once())
- ->method('dispatchTyped')
- ->willReturnCallback(function ($event): void {
- if ($event instanceof BeforeTemplateRenderedEvent && $event->isLoggedIn() === true) {
- return;
- }
-
- $this->fail('Wrong event dispatched');
- });
-
- $this->middleWare->afterController($this->controller, 'myMethod', $this->createMock(TemplateResponse::class));
- }
-}
--- a/tests/lib/AppFramework/Middleware/CompressionMiddlewareTest.php
+++ /dev/null
@@ -1,146 +0,0 @@
-request = $this->createMock(IRequest::class);
- $this->middleWare = new CompressionMiddleware(
- $this->request
- );
-
- $this->controller = $this->createMock(Controller::class);
- }
-
- public function testGzipOCSV1(): void {
- $this->request->method('getHeader')
- ->with('Accept-Encoding')
- ->willReturn('gzip');
-
- $response = $this->createMock(V1Response::class);
- $response->expects($this->once())
- ->method('addHeader')
- ->with('Content-Encoding', 'gzip');
-
- $response->method('getStatus')
- ->willReturn(Http::STATUS_OK);
-
- $this->middleWare->beforeController($this->controller, 'myMethod');
- $this->middleWare->afterController($this->controller, 'myMethod', $response);
-
- $output = 'myoutput';
- $result = $this->middleWare->beforeOutput($this->controller, 'myMethod', $output);
-
- $this->assertSame($output, gzdecode($result));
- }
-
- public function testGzipOCSV2(): void {
- $this->request->method('getHeader')
- ->with('Accept-Encoding')
- ->willReturn('gzip');
-
- $response = $this->createMock(V2Response::class);
- $response->expects($this->once())
- ->method('addHeader')
- ->with('Content-Encoding', 'gzip');
-
- $response->method('getStatus')
- ->willReturn(Http::STATUS_OK);
-
- $this->middleWare->beforeController($this->controller, 'myMethod');
- $this->middleWare->afterController($this->controller, 'myMethod', $response);
-
- $output = 'myoutput';
- $result = $this->middleWare->beforeOutput($this->controller, 'myMethod', $output);
-
- $this->assertSame($output, gzdecode($result));
- }
-
- public function testGzipJSONResponse(): void {
- $this->request->method('getHeader')
- ->with('Accept-Encoding')
- ->willReturn('gzip');
-
- $response = $this->createMock(JSONResponse::class);
- $response->expects($this->once())
- ->method('addHeader')
- ->with('Content-Encoding', 'gzip');
-
- $response->method('getStatus')
- ->willReturn(Http::STATUS_OK);
-
- $this->middleWare->beforeController($this->controller, 'myMethod');
- $this->middleWare->afterController($this->controller, 'myMethod', $response);
-
- $output = 'myoutput';
- $result = $this->middleWare->beforeOutput($this->controller, 'myMethod', $output);
-
- $this->assertSame($output, gzdecode($result));
- }
-
- public function testNoGzipDataResponse(): void {
- $this->request->method('getHeader')
- ->with('Accept-Encoding')
- ->willReturn('gzip');
-
- $response = $this->createMock(DataResponse::class);
- $response->expects($this->never())
- ->method('addHeader');
-
- $response->method('getStatus')
- ->willReturn(Http::STATUS_OK);
- $this->middleWare->beforeController($this->controller, 'myMethod');
- $this->middleWare->afterController($this->controller, 'myMethod', $response);
-
- $output = 'myoutput';
- $result = $this->middleWare->beforeOutput($this->controller, 'myMethod', $output);
-
- $this->assertSame($output, $result);
- }
-
- public function testNoGzipNo200(): void {
- $this->request->method('getHeader')
- ->with('Accept-Encoding')
- ->willReturn('gzip');
-
- $response = $this->createMock(JSONResponse::class);
- $response->expects($this->never())
- ->method('addHeader');
-
- $response->method('getStatus')
- ->willReturn(Http::STATUS_NOT_FOUND);
-
- $this->middleWare->beforeController($this->controller, 'myMethod');
- $this->middleWare->afterController($this->controller, 'myMethod', $response);
-
- $output = 'myoutput';
- $result = $this->middleWare->beforeOutput($this->controller, 'myMethod', $output);
-
- $this->assertSame($output, $result);
- }
-}
--- a/tests/lib/AppFramework/Middleware/MiddlewareDispatcherTest.php
+++ /dev/null
@@ -1,272 +0,0 @@
-beforeControllerOrder = self::$beforeControllerCalled;
- $this->controller = $controller;
- $this->methodName = $methodName;
- if ($this->beforeControllerThrowsEx) {
- throw new \Exception();
- }
- }
-
- #[\Override]
- public function afterException(Controller $controller, string $methodName, \Exception $exception): void {
- self::$afterExceptionCalled++;
- $this->afterExceptionOrder = self::$afterExceptionCalled;
- $this->controller = $controller;
- $this->methodName = $methodName;
- $this->exception = $exception;
- parent::afterException($controller, $methodName, $exception);
- }
-
- #[\Override]
- public function afterController(Controller $controller, string $methodName, Response $response): Response {
- self::$afterControllerCalled++;
- $this->afterControllerOrder = self::$afterControllerCalled;
- $this->controller = $controller;
- $this->methodName = $methodName;
- $this->response = $response;
- return parent::afterController($controller, $methodName, $response);
- }
-
- #[\Override]
- public function beforeOutput(Controller $controller, string $methodName, string $output): string {
- self::$beforeOutputCalled++;
- $this->beforeOutputOrder = self::$beforeOutputCalled;
- $this->controller = $controller;
- $this->methodName = $methodName;
- $this->output = $output;
- return parent::beforeOutput($controller, $methodName, $output);
- }
-}
-
-class TestController extends Controller {
- public function controllerMethod(): void {
- }
-}
-
-class MiddlewareDispatcherTest extends \Test\TestCase {
- public $exception;
- public $response;
- private $out;
- private $method;
- private $controller;
-
- /**
- * @var MiddlewareDispatcher
- */
- private $dispatcher;
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- $this->dispatcher = new MiddlewareDispatcher();
- $this->controller = $this->getControllerMock();
- $this->method = 'method';
- $this->response = new Response();
- $this->out = 'hi';
- $this->exception = new \Exception();
- }
-
- private function getControllerMock() {
- return $this->getMockBuilder(TestController::class)
- ->onlyMethods(['controllerMethod'])
- ->setConstructorArgs(['app',
- new Request(
- ['controllerMethod' => 'GET'],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- )
- ])->getMock();
- }
-
- private function getMiddleware($beforeControllerThrowsEx = false) {
- $m1 = new TestMiddleware($beforeControllerThrowsEx);
- $this->dispatcher->registerMiddleware($m1);
- return $m1;
- }
-
- public function testAfterExceptionShouldReturnResponseOfMiddleware(): void {
- $response = new Response();
- $m1 = $this->getMockBuilder(Middleware::class)
- ->onlyMethods(['afterException', 'beforeController'])
- ->getMock();
- $m1->expects($this->never())
- ->method('afterException');
-
- $m2 = $this->getMockBuilder(Middleware::class)
- ->onlyMethods(['afterException', 'beforeController'])
- ->getMock();
- $m2->expects($this->once())
- ->method('afterException')
- ->willReturn($response);
-
- $this->dispatcher->registerMiddleware($m1);
- $this->dispatcher->registerMiddleware($m2);
-
- $this->dispatcher->beforeController($this->controller, $this->method);
- $this->assertEquals($response, $this->dispatcher->afterException($this->controller, $this->method, $this->exception));
- }
-
- public function testAfterExceptionShouldThrowAgainWhenNotHandled(): void {
- $m1 = new TestMiddleware(false);
- $m2 = new TestMiddleware(true);
-
- $this->dispatcher->registerMiddleware($m1);
- $this->dispatcher->registerMiddleware($m2);
-
- $this->expectException(\Exception::class);
- $this->dispatcher->beforeController($this->controller, $this->method);
- $this->dispatcher->afterException($this->controller, $this->method, $this->exception);
- }
-
- public function testBeforeControllerCorrectArguments(): void {
- $m1 = $this->getMiddleware();
- $this->dispatcher->beforeController($this->controller, $this->method);
-
- $this->assertEquals($this->controller, $m1->controller);
- $this->assertEquals($this->method, $m1->methodName);
- }
-
- public function testAfterControllerCorrectArguments(): void {
- $m1 = $this->getMiddleware();
-
- $this->dispatcher->afterController($this->controller, $this->method, $this->response);
-
- $this->assertEquals($this->controller, $m1->controller);
- $this->assertEquals($this->method, $m1->methodName);
- $this->assertEquals($this->response, $m1->response);
- }
-
- public function testAfterExceptionCorrectArguments(): void {
- $m1 = $this->getMiddleware();
-
- $this->expectException(\Exception::class);
-
- $this->dispatcher->beforeController($this->controller, $this->method);
- $this->dispatcher->afterException($this->controller, $this->method, $this->exception);
-
- $this->assertEquals($this->controller, $m1->controller);
- $this->assertEquals($this->method, $m1->methodName);
- $this->assertEquals($this->exception, $m1->exception);
- }
-
- public function testBeforeOutputCorrectArguments(): void {
- $m1 = $this->getMiddleware();
-
- $this->dispatcher->beforeOutput($this->controller, $this->method, $this->out);
-
- $this->assertEquals($this->controller, $m1->controller);
- $this->assertEquals($this->method, $m1->methodName);
- $this->assertEquals($this->out, $m1->output);
- }
-
- public function testBeforeControllerOrder(): void {
- $m1 = $this->getMiddleware();
- $m2 = $this->getMiddleware();
-
- $this->dispatcher->beforeController($this->controller, $this->method);
-
- $this->assertEquals(1, $m1->beforeControllerOrder);
- $this->assertEquals(2, $m2->beforeControllerOrder);
- }
-
- public function testAfterControllerOrder(): void {
- $m1 = $this->getMiddleware();
- $m2 = $this->getMiddleware();
-
- $this->dispatcher->afterController($this->controller, $this->method, $this->response);
-
- $this->assertEquals(2, $m1->afterControllerOrder);
- $this->assertEquals(1, $m2->afterControllerOrder);
- }
-
- public function testAfterExceptionOrder(): void {
- $m1 = $this->getMiddleware();
- $m2 = $this->getMiddleware();
-
- $this->expectException(\Exception::class);
- $this->dispatcher->beforeController($this->controller, $this->method);
- $this->dispatcher->afterException($this->controller, $this->method, $this->exception);
-
- $this->assertEquals(1, $m1->afterExceptionOrder);
- $this->assertEquals(1, $m2->afterExceptionOrder);
- }
-
- public function testBeforeOutputOrder(): void {
- $m1 = $this->getMiddleware();
- $m2 = $this->getMiddleware();
-
- $this->dispatcher->beforeOutput($this->controller, $this->method, $this->out);
-
- $this->assertEquals(2, $m1->beforeOutputOrder);
- $this->assertEquals(1, $m2->beforeOutputOrder);
- }
-
- public function testExceptionShouldRunAfterExceptionOfOnlyPreviouslyExecutedMiddlewares(): void {
- $m1 = $this->getMiddleware();
- $m2 = $this->getMiddleware(true);
- $m3 = $this->createMock(Middleware::class);
- $m3->expects($this->never())
- ->method('afterException');
- $m3->expects($this->never())
- ->method('beforeController');
- $m3->expects($this->never())
- ->method('afterController');
- $m3->method('beforeOutput')
- ->willReturnArgument(2);
-
- $this->dispatcher->registerMiddleware($m3);
-
- $this->dispatcher->beforeOutput($this->controller, $this->method, $this->out);
-
- $this->assertEquals(2, $m1->beforeOutputOrder);
- $this->assertEquals(1, $m2->beforeOutputOrder);
- }
-}
--- a/tests/lib/AppFramework/Middleware/MiddlewareTest.php
+++ /dev/null
@@ -1,74 +0,0 @@
-middleware = new ChildMiddleware();
-
- $this->api = $this->createMock(DIContainer::class);
-
- $this->controller = $this->getMockBuilder(Controller::class)
- ->setConstructorArgs([
- $this->api,
- new Request(
- [],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- )
- ])->getMock();
- $this->exception = new \Exception();
- $this->response = $this->createMock(Response::class);
- }
-
- public function testBeforeController(): void {
- $this->middleware->beforeController($this->controller, '');
- $this->assertNull(null);
- }
-
- public function testAfterExceptionRaiseAgainWhenUnhandled(): void {
- $this->expectException(\Exception::class);
- $this->middleware->afterException($this->controller, '', $this->exception);
- }
-
- public function testAfterControllerReturnResponseWhenUnhandled(): void {
- $response = $this->middleware->afterController($this->controller, '', $this->response);
-
- $this->assertEquals($this->response, $response);
- }
-
- public function testBeforeOutputReturnOutputhenUnhandled(): void {
- $output = $this->middleware->beforeOutput($this->controller, '', 'test');
-
- $this->assertEquals('test', $output);
- }
-}
--- a/tests/lib/AppFramework/Middleware/Mock/UseSessionController.php
+++ /dev/null
@@ -1,19 +0,0 @@
-request = $this->createMock(IRequest::class);
- $this->middleWare = new NotModifiedMiddleware(
- $this->request
- );
-
- $this->controller = $this->createMock(Controller::class);
- }
-
- public static function dataModified(): array {
- $now = new \DateTime();
-
- return [
- [null, '', null, '', false],
- ['etag', 'etag', null, '', false],
- ['etag', '"wrongetag"', null, '', false],
- ['etag', '', null, '', false],
- [null, '"etag"', null, '', false],
- ['etag', '"etag"', null, '', true],
-
- [null, '', $now, $now->format(Constants::DATE_RFC7231), true],
- [null, '', $now, $now->format(\DateTimeInterface::ATOM), false],
- [null, '', null, $now->format(Constants::DATE_RFC7231), false],
- [null, '', $now, '', false],
-
- ['etag', '"etag"', $now, $now->format(\DateTimeInterface::ATOM), true],
- ['etag', '"etag"', $now, $now->format(Constants::DATE_RFC7231), true],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataModified')]
- public function testMiddleware(?string $etag, string $etagHeader, ?\DateTime $lastModified, string $lastModifiedHeader, bool $notModifiedSet): void {
- $this->request->method('getHeader')
- ->willReturnCallback(function (string $name) use ($etagHeader, $lastModifiedHeader) {
- if ($name === 'IF_NONE_MATCH') {
- return $etagHeader;
- }
- if ($name === 'IF_MODIFIED_SINCE') {
- return $lastModifiedHeader;
- }
- return '';
- });
-
- $response = new Response();
- if ($etag !== null) {
- $response->setETag($etag);
- }
- if ($lastModified !== null) {
- $response->setLastModified($lastModified);
- }
- $response->setStatus(Http::STATUS_OK);
-
- $result = $this->middleWare->afterController($this->controller, 'myfunction', $response);
-
- if ($notModifiedSet) {
- $this->assertSame(Http::STATUS_NOT_MODIFIED, $result->getStatus());
- } else {
- $this->assertSame(Http::STATUS_OK, $result->getStatus());
- }
- }
-}
--- a/tests/lib/AppFramework/Middleware/OCSMiddlewareTest.php
+++ /dev/null
@@ -1,184 +0,0 @@
-request = $this->getMockBuilder(IRequest::class)
- ->getMock();
- }
-
- public static function dataAfterException(): array {
- return [
- [OCSController::class, new \Exception(), true],
- [OCSController::class, new OCSException(), false, '', Http::STATUS_INTERNAL_SERVER_ERROR],
- [OCSController::class, new OCSException('foo'), false, 'foo', Http::STATUS_INTERNAL_SERVER_ERROR],
- [OCSController::class, new OCSException('foo', Http::STATUS_IM_A_TEAPOT), false, 'foo', Http::STATUS_IM_A_TEAPOT],
- [OCSController::class, new OCSBadRequestException(), false, '', Http::STATUS_BAD_REQUEST],
- [OCSController::class, new OCSBadRequestException('foo'), false, 'foo', Http::STATUS_BAD_REQUEST],
- [OCSController::class, new OCSForbiddenException(), false, '', Http::STATUS_FORBIDDEN],
- [OCSController::class, new OCSForbiddenException('foo'), false, 'foo', Http::STATUS_FORBIDDEN],
- [OCSController::class, new OCSNotFoundException(), false, '', Http::STATUS_NOT_FOUND],
- [OCSController::class, new OCSNotFoundException('foo'), false, 'foo', Http::STATUS_NOT_FOUND],
-
- [Controller::class, new \Exception(), true],
- [Controller::class, new OCSException(), true],
- [Controller::class, new OCSException('foo'), true],
- [Controller::class, new OCSException('foo', Http::STATUS_IM_A_TEAPOT), true],
- [Controller::class, new OCSBadRequestException(), true],
- [Controller::class, new OCSBadRequestException('foo'), true],
- [Controller::class, new OCSForbiddenException(), true],
- [Controller::class, new OCSForbiddenException('foo'), true],
- [Controller::class, new OCSNotFoundException(), true],
- [Controller::class, new OCSNotFoundException('foo'), true],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataAfterException')]
- public function testAfterExceptionOCSv1(string $controller, \Exception $exception, bool $forward, string $message = '', int $code = 0): void {
- $controller = $this->createMock($controller);
- $this->request
- ->method('getScriptName')
- ->willReturn('/ocs/v1.php');
- $OCSMiddleware = new OCSMiddleware($this->request);
- $OCSMiddleware->beforeController($controller, 'method');
-
- if ($forward) {
- $this->expectException(get_class($exception));
- $this->expectExceptionMessage($exception->getMessage());
- }
-
- $result = $OCSMiddleware->afterException($controller, 'method', $exception);
-
- $this->assertInstanceOf(V1Response::class, $result);
-
- $this->assertSame($message, $this->invokePrivate($result, 'statusMessage'));
-
- if ($exception->getCode() === 0) {
- $this->assertSame(OCSController::RESPOND_UNKNOWN_ERROR, $result->getOCSStatus());
- } else {
- $this->assertSame($code, $result->getOCSStatus());
- }
-
- $this->assertSame(Http::STATUS_OK, $result->getStatus());
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataAfterException')]
- public function testAfterExceptionOCSv2(string $controller, \Exception $exception, bool $forward, string $message = '', int $code = 0): void {
- $controller = $this->createMock($controller);
- $this->request
- ->method('getScriptName')
- ->willReturn('/ocs/v2.php');
- $OCSMiddleware = new OCSMiddleware($this->request);
- $OCSMiddleware->beforeController($controller, 'method');
-
- if ($forward) {
- $this->expectException(get_class($exception));
- $this->expectExceptionMessage($exception->getMessage());
- }
-
- $result = $OCSMiddleware->afterException($controller, 'method', $exception);
-
- $this->assertInstanceOf(V2Response::class, $result);
-
- $this->assertSame($message, $this->invokePrivate($result, 'statusMessage'));
- if ($exception->getCode() === 0) {
- $this->assertSame(OCSController::RESPOND_UNKNOWN_ERROR, $result->getOCSStatus());
- } else {
- $this->assertSame($code, $result->getOCSStatus());
- }
- $this->assertSame($code, $result->getStatus());
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataAfterException')]
- public function testAfterExceptionOCSv2SubFolder(string $controller, \Exception $exception, bool $forward, string $message = '', int $code = 0): void {
- $controller = $this->createMock($controller);
- $this->request
- ->method('getScriptName')
- ->willReturn('/mysubfolder/ocs/v2.php');
- $OCSMiddleware = new OCSMiddleware($this->request);
- $OCSMiddleware->beforeController($controller, 'method');
-
- if ($forward) {
- $this->expectException($exception::class);
- $this->expectExceptionMessage($exception->getMessage());
- }
-
- $result = $OCSMiddleware->afterException($controller, 'method', $exception);
-
- $this->assertInstanceOf(V2Response::class, $result);
-
- $this->assertSame($message, $this->invokePrivate($result, 'statusMessage'));
- if ($exception->getCode() === 0) {
- $this->assertSame(OCSController::RESPOND_UNKNOWN_ERROR, $result->getOCSStatus());
- } else {
- $this->assertSame($code, $result->getOCSStatus());
- }
- $this->assertSame($code, $result->getStatus());
- }
-
- public static function dataAfterController(): array {
- return [
- [OCSController::class, new Response(), false],
- [OCSController::class, new JSONResponse(), false],
- [OCSController::class, new JSONResponse(['message' => 'foo']), false],
- [OCSController::class, new JSONResponse(['message' => 'foo'], Http::STATUS_UNAUTHORIZED), true, OCSController::RESPOND_UNAUTHORISED],
- [OCSController::class, new JSONResponse(['message' => 'foo'], Http::STATUS_FORBIDDEN), true],
-
- [Controller::class, new Response(), false],
- [Controller::class, new JSONResponse(), false],
- [Controller::class, new JSONResponse(['message' => 'foo']), false],
- [Controller::class, new JSONResponse(['message' => 'foo'], Http::STATUS_UNAUTHORIZED), false],
- [Controller::class, new JSONResponse(['message' => 'foo'], Http::STATUS_FORBIDDEN), false],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataAfterController')]
- public function testAfterController(string $controller, Response $response, bool $converted, int $convertedOCSStatus = 0): void {
- $controller = $this->createMock($controller);
- $OCSMiddleware = new OCSMiddleware($this->request);
- $newResponse = $OCSMiddleware->afterController($controller, 'foo', $response);
-
- if ($converted === false) {
- $this->assertSame($response, $newResponse);
- } else {
- $this->assertInstanceOf(BaseResponse::class, $newResponse);
- $this->assertSame($response->getData()['message'], $this->invokePrivate($newResponse, 'statusMessage'));
-
- if ($convertedOCSStatus) {
- $this->assertSame($convertedOCSStatus, $newResponse->getOCSStatus());
- } else {
- $this->assertSame($response->getStatus(), $newResponse->getOCSStatus());
- }
- $this->assertSame($response->getStatus(), $newResponse->getStatus());
- }
- }
-}
--- a/tests/lib/AppFramework/Middleware/PublicShare/PublicShareMiddlewareTest.php
+++ /dev/null
@@ -1,264 +0,0 @@
-request = $this->createMock(IRequest::class);
- $this->session = $this->createMock(ISession::class);
- $this->appConfig = $this->createMock(IAppConfig::class);
- $this->throttler = $this->createMock(IThrottler::class);
-
- $this->middleware = new PublicShareMiddleware(
- $this->request,
- $this->session,
- $this->appConfig,
- $this->throttler
- );
- }
-
- #[\PHPUnit\Framework\Attributes\DoesNotPerformAssertions]
- public function testBeforeControllerNoPublicShareController(): void {
- $controller = $this->createMock(Controller::class);
-
- $this->middleware->beforeController($controller, 'method');
- }
-
- public static function dataShareApi(): array {
- return [
- [false, false],
- [false, true],
- [true, false],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataShareApi')]
- public function testBeforeControllerShareApiDisabled(bool $shareApi, bool $shareLinks): void {
- $controller = $this->createMock(PublicShareController::class);
-
- $this->appConfig->method('getValueBool')
- ->willReturnMap([
- ['core', 'shareapi_enabled', true, $shareApi],
- ['core', 'shareapi_allow_links', true, $shareLinks],
- ]);
-
- $this->expectException(NotFoundException::class);
- $this->middleware->beforeController($controller, 'mehod');
- }
-
- public function testBeforeControllerNoTokenParam(): void {
- $controller = $this->createMock(PublicShareController::class);
-
- $this->appConfig->method('getValueBool')
- ->willReturnMap([
- ['core', 'shareapi_enabled', true, true],
- ['core', 'shareapi_allow_links', true, true],
- ]);
-
- $this->expectException(NotFoundException::class);
- $this->middleware->beforeController($controller, 'mehod');
- }
-
- public function testBeforeControllerInvalidToken(): void {
- $controller = $this->createMock(PublicShareController::class);
-
- $this->appConfig->method('getValueBool')
- ->willReturnMap([
- ['core', 'shareapi_enabled', true, true],
- ['core', 'shareapi_allow_links', true, true],
- ]);
-
- $this->request->method('getParam')
- ->with('token', null)
- ->willReturn('myToken');
-
- $controller->method('isValidToken')
- ->willReturn(false);
- $controller->expects($this->once())
- ->method('shareNotFound');
-
- $this->expectException(NotFoundException::class);
- $this->middleware->beforeController($controller, 'mehod');
- }
-
- public function testBeforeControllerValidTokenNotAuthenticated(): void {
- $controller = $this->getMockBuilder(PublicShareController::class)
- ->setConstructorArgs(['app', $this->request, $this->session])
- ->getMock();
-
- $this->appConfig->method('getValueBool')
- ->willReturnMap([
- ['core', 'shareapi_enabled', true, true],
- ['core', 'shareapi_allow_links', true, true],
- ]);
-
- $this->request->method('getParam')
- ->with('token', null)
- ->willReturn('myToken');
-
- $controller->method('isValidToken')
- ->willReturn(true);
-
- $controller->method('isPasswordProtected')
- ->willReturn(true);
-
- $this->expectException(NotFoundException::class);
- $this->middleware->beforeController($controller, 'mehod');
- }
-
- public function testBeforeControllerValidTokenAuthenticateMethod(): void {
- $controller = $this->getMockBuilder(PublicShareController::class)
- ->setConstructorArgs(['app', $this->request, $this->session])
- ->getMock();
-
- $this->appConfig->method('getValueBool')
- ->willReturnMap([
- ['core', 'shareapi_enabled', true, true],
- ['core', 'shareapi_allow_links', true, true],
- ]);
-
- $this->request->method('getParam')
- ->with('token', null)
- ->willReturn('myToken');
-
- $controller->expects($this->once())
- ->method('isValidToken')
- ->willReturn(true);
-
- $this->middleware->beforeController($controller, 'authenticate');
- }
-
- public function testBeforeControllerValidTokenShowAuthenticateMethod(): void {
- $controller = $this->getMockBuilder(PublicShareController::class)
- ->setConstructorArgs(['app', $this->request, $this->session])
- ->getMock();
-
- $this->appConfig->method('getValueBool')
- ->willReturnMap([
- ['core', 'shareapi_enabled', true, true],
- ['core', 'shareapi_allow_links', true, true],
- ]);
-
- $this->request->method('getParam')
- ->with('token', null)
- ->willReturn('myToken');
-
- $controller->expects($this->once())
- ->method('isValidToken')
- ->willReturn(true);
-
- $this->middleware->beforeController($controller, 'showAuthenticate');
- }
-
- public function testBeforeControllerAuthPublicShareController(): void {
- $controller = $this->getMockBuilder(AuthPublicShareController::class)
- ->setConstructorArgs(['app', $this->request, $this->session, $this->createMock(IURLGenerator::class)])
- ->getMock();
-
- $this->appConfig->method('getValueBool')
- ->willReturnMap([
- ['core', 'shareapi_enabled', true, true],
- ['core', 'shareapi_allow_links', true, true],
- ]);
-
- $this->request->method('getParam')
- ->with('token', null)
- ->willReturn('myToken');
-
- $controller->method('isValidToken')
- ->willReturn(true);
-
- $controller->method('isPasswordProtected')
- ->willReturn(true);
-
- $this->session->expects($this->once())
- ->method('set')
- ->with('public_link_authenticate_redirect', '[]');
-
- $this->expectException(NeedAuthenticationException::class);
- $this->middleware->beforeController($controller, 'method');
- }
-
- public function testAfterExceptionNoPublicShareController(): void {
- $controller = $this->createMock(Controller::class);
- $exception = new \Exception();
-
- try {
- $this->middleware->afterException($controller, 'method', $exception);
- } catch (\Exception $e) {
- $this->assertEquals($exception, $e);
- }
- }
-
- public function testAfterExceptionPublicShareControllerNotFoundException(): void {
- $controller = $this->createMock(PublicShareController::class);
- $exception = new NotFoundException();
-
- $result = $this->middleware->afterException($controller, 'method', $exception);
- $this->assertInstanceOf(TemplateResponse::class, $result);
- $this->assertEquals($result->getStatus(), Http::STATUS_NOT_FOUND);
- }
-
- public function testAfterExceptionPublicShareController(): void {
- $controller = $this->createMock(PublicShareController::class);
- $exception = new \Exception();
-
- try {
- $this->middleware->afterException($controller, 'method', $exception);
- } catch (\Exception $e) {
- $this->assertEquals($exception, $e);
- }
- }
-
- public function testAfterExceptionAuthPublicShareController(): void {
- $controller = $this->getMockBuilder(AuthPublicShareController::class)
- ->setConstructorArgs([
- 'app',
- $this->request,
- $this->session,
- $this->createMock(IURLGenerator::class),
- ])->getMock();
- $controller->setToken('token');
-
- $exception = new NeedAuthenticationException();
-
- $this->request->method('getParam')
- ->with('_route')
- ->willReturn('my.route');
-
- $result = $this->middleware->afterException($controller, 'method', $exception);
- $this->assertInstanceOf(RedirectResponse::class, $result);
- }
-}
--- a/tests/lib/AppFramework/Middleware/Security/BruteForceMiddlewareTest.php
+++ /dev/null
@@ -1,309 +0,0 @@
-reflector = new ControllerMethodReflector(Server::get(LoggerInterface::class));
- $this->throttler = $this->createMock(IThrottler::class);
- $this->request = $this->createMock(IRequest::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $this->bruteForceMiddleware = new BruteForceMiddleware(
- $this->reflector,
- $this->throttler,
- $this->request,
- $this->logger,
- );
- }
-
- public function testBeforeControllerWithAnnotation(): void {
- $this->request
- ->expects($this->once())
- ->method('getRemoteAddress')
- ->willReturn('127.0.0.1');
- $this->throttler
- ->expects($this->once())
- ->method('sleepDelayOrThrowOnMax')
- ->with('127.0.0.1', 'login');
-
- $controller = new BruteForceMiddlewareController('test', $this->request);
- $this->reflector->reflect($controller, 'testMethodWithAnnotation');
- $this->bruteForceMiddleware->beforeController($controller, 'testMethodWithAnnotation');
- }
-
- public function testBeforeControllerWithSingleAttribute(): void {
- $this->request
- ->expects($this->once())
- ->method('getRemoteAddress')
- ->willReturn('::1');
- $this->throttler
- ->expects($this->once())
- ->method('sleepDelayOrThrowOnMax')
- ->with('::1', 'single');
-
- $controller = new BruteForceMiddlewareController('test', $this->request);
- $this->reflector->reflect($controller, 'singleAttribute');
- $this->bruteForceMiddleware->beforeController($controller, 'singleAttribute');
- }
-
- public function testBeforeControllerWithMultipleAttributes(): void {
- $this->request
- ->expects($this->once())
- ->method('getRemoteAddress')
- ->willReturn('::1');
-
- $calls = [
- ['::1', 'first'],
- ['::1', 'second'],
- ];
- $this->throttler
- ->expects($this->exactly(2))
- ->method('sleepDelayOrThrowOnMax')
- ->willReturnCallback(function () use (&$calls) {
- $expected = array_shift($calls);
- $this->assertEquals($expected, func_get_args());
- return 0;
- });
-
- $controller = new BruteForceMiddlewareController('test', $this->request);
- $this->reflector->reflect($controller, 'multipleAttributes');
- $this->bruteForceMiddleware->beforeController($controller, 'multipleAttributes');
- }
-
- public function testBeforeControllerWithoutAnnotation(): void {
- $this->request
- ->expects($this->never())
- ->method('getRemoteAddress');
- $this->throttler
- ->expects($this->never())
- ->method('sleepDelayOrThrowOnMax');
-
- $controller = new BruteForceMiddlewareController('test', $this->request);
- $this->reflector->reflect($controller, 'testMethodWithoutAnnotation');
- $this->bruteForceMiddleware->beforeController($controller, 'testMethodWithoutAnnotation');
- }
-
- public function testAfterControllerWithAnnotationAndThrottledRequest(): void {
- /** @var Response|\PHPUnit\Framework\MockObject\MockObject $response */
- $response = $this->createMock(Response::class);
- $response
- ->expects($this->once())
- ->method('isThrottled')
- ->willReturn(true);
- $response
- ->expects($this->once())
- ->method('getThrottleMetadata')
- ->willReturn([]);
- $this->request
- ->expects($this->once())
- ->method('getRemoteAddress')
- ->willReturn('127.0.0.1');
- $this->throttler
- ->expects($this->once())
- ->method('sleepDelayOrThrowOnMax')
- ->with('127.0.0.1', 'login');
- $this->throttler
- ->expects($this->once())
- ->method('registerAttempt')
- ->with('login', '127.0.0.1');
-
- $controller = new BruteForceMiddlewareController('test', $this->request);
- $this->reflector->reflect($controller, 'testMethodWithAnnotation');
- $this->bruteForceMiddleware->afterController($controller, 'testMethodWithAnnotation', $response);
- }
-
- public function testAfterControllerWithAnnotationAndNotThrottledRequest(): void {
- /** @var Response|\PHPUnit\Framework\MockObject\MockObject $response */
- $response = $this->createMock(Response::class);
- $response
- ->expects($this->once())
- ->method('isThrottled')
- ->willReturn(false);
- $this->request
- ->expects($this->never())
- ->method('getRemoteAddress');
- $this->throttler
- ->expects($this->never())
- ->method('sleepDelayOrThrowOnMax');
- $this->throttler
- ->expects($this->never())
- ->method('registerAttempt');
-
- $controller = new BruteForceMiddlewareController('test', $this->request);
- $this->reflector->reflect($controller, 'testMethodWithAnnotation');
- $this->bruteForceMiddleware->afterController($controller, 'testMethodWithAnnotation', $response);
- }
-
- public function testAfterControllerWithSingleAttribute(): void {
- /** @var Response|\PHPUnit\Framework\MockObject\MockObject $response */
- $response = $this->createMock(Response::class);
- $response
- ->expects($this->once())
- ->method('isThrottled')
- ->willReturn(true);
- $response
- ->expects($this->once())
- ->method('getThrottleMetadata')
- ->willReturn([]);
-
- $this->request
- ->expects($this->once())
- ->method('getRemoteAddress')
- ->willReturn('::1');
- $this->throttler
- ->expects($this->once())
- ->method('sleepDelayOrThrowOnMax')
- ->with('::1', 'single');
- $this->throttler
- ->expects($this->once())
- ->method('registerAttempt')
- ->with('single', '::1');
-
- $controller = new BruteForceMiddlewareController('test', $this->request);
- $this->reflector->reflect($controller, 'singleAttribute');
- $this->bruteForceMiddleware->afterController($controller, 'singleAttribute', $response);
- }
-
- public function testAfterControllerWithMultipleAttributesGeneralMatch(): void {
- /** @var Response|\PHPUnit\Framework\MockObject\MockObject $response */
- $response = $this->createMock(Response::class);
- $response
- ->expects($this->once())
- ->method('isThrottled')
- ->willReturn(true);
- $response
- ->expects($this->once())
- ->method('getThrottleMetadata')
- ->willReturn([]);
-
- $this->request
- ->expects($this->once())
- ->method('getRemoteAddress')
- ->willReturn('::1');
-
- $sleepCalls = [
- ['::1', 'first'],
- ['::1', 'second'],
- ];
- $this->throttler
- ->expects($this->exactly(2))
- ->method('sleepDelayOrThrowOnMax')
- ->willReturnCallback(function () use (&$sleepCalls) {
- $expected = array_shift($sleepCalls);
- $this->assertEquals($expected, func_get_args());
- return 0;
- });
-
- $attemptCalls = [
- ['first', '::1', []],
- ['second', '::1', []],
- ];
- $this->throttler
- ->expects($this->exactly(2))
- ->method('registerAttempt')
- ->willReturnCallback(function () use (&$attemptCalls): void {
- $expected = array_shift($attemptCalls);
- $this->assertEquals($expected, func_get_args());
- });
-
- $controller = new BruteForceMiddlewareController('test', $this->request);
- $this->reflector->reflect($controller, 'multipleAttributes');
- $this->bruteForceMiddleware->afterController($controller, 'multipleAttributes', $response);
- }
-
- public function testAfterControllerWithMultipleAttributesSpecificMatch(): void {
- /** @var Response|\PHPUnit\Framework\MockObject\MockObject $response */
- $response = $this->createMock(Response::class);
- $response
- ->expects($this->once())
- ->method('isThrottled')
- ->willReturn(true);
- $response
- ->expects($this->once())
- ->method('getThrottleMetadata')
- ->willReturn(['action' => 'second']);
-
- $this->request
- ->expects($this->once())
- ->method('getRemoteAddress')
- ->willReturn('::1');
- $this->throttler
- ->expects($this->once())
- ->method('sleepDelayOrThrowOnMax')
- ->with('::1', 'second');
- $this->throttler
- ->expects($this->once())
- ->method('registerAttempt')
- ->with('second', '::1');
-
- $controller = new BruteForceMiddlewareController('test', $this->request);
- $this->reflector->reflect($controller, 'multipleAttributes');
- $this->bruteForceMiddleware->afterController($controller, 'multipleAttributes', $response);
- }
-
- public function testAfterControllerWithoutAnnotation(): void {
- $this->request
- ->expects($this->never())
- ->method('getRemoteAddress');
- $this->throttler
- ->expects($this->never())
- ->method('sleepDelayOrThrowOnMax');
-
- $controller = new BruteForceMiddlewareController('test', $this->request);
- $this->reflector->reflect($controller, 'testMethodWithoutAnnotation');
- /** @var Response|\PHPUnit\Framework\MockObject\MockObject $response */
- $response = $this->createMock(Response::class);
- $this->bruteForceMiddleware->afterController($controller, 'testMethodWithoutAnnotation', $response);
- }
-
- public function testAfterControllerWithThrottledResponseButUnhandled(): void {
- $this->request
- ->expects($this->never())
- ->method('getRemoteAddress');
- $this->throttler
- ->expects($this->never())
- ->method('sleepDelayOrThrowOnMax');
-
- $controller = new BruteForceMiddlewareController('test', $this->request);
- $this->reflector->reflect($controller, 'testMethodWithoutAnnotation');
- /** @var Response|\PHPUnit\Framework\MockObject\MockObject $response */
- $response = $this->createMock(Response::class);
- $response->method('isThrottled')
- ->willReturn(true);
-
- $this->logger->expects($this->once())
- ->method('debug')
- ->with('Response for Test\AppFramework\Middleware\Security\Mock\BruteForceMiddlewareController::testMethodWithoutAnnotation got bruteforce throttled but has no annotation nor attribute defined.');
-
- $this->bruteForceMiddleware->afterController($controller, 'testMethodWithoutAnnotation', $response);
- }
-}
--- a/tests/lib/AppFramework/Middleware/Security/CORSMiddlewareTest.php
+++ /dev/null
@@ -1,347 +0,0 @@
-reflector = new ControllerMethodReflector(Server::get(LoggerInterface::class));
- $this->session = $this->createMock(Session::class);
- $this->throttler = $this->createMock(IThrottler::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->controller = new CORSMiddlewareController(
- 'test',
- $this->createMock(IRequest::class)
- );
- }
-
- public static function dataSetCORSAPIHeader(): array {
- return [
- ['testSetCORSAPIHeader'],
- ['testSetCORSAPIHeaderAttribute'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetCORSAPIHeader')]
- public function testSetCORSAPIHeader(string $method): void {
- $request = new Request(
- [
- 'server' => [
- 'HTTP_ORIGIN' => 'test'
- ]
- ],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $this->reflector->reflect($this->controller, $method);
- $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler);
-
- $response = $middleware->afterController($this->controller, $method, new Response());
- $headers = $response->getHeaders();
- $this->assertEquals('test', $headers['Access-Control-Allow-Origin']);
- }
-
- public function testNoAnnotationNoCORSHEADER(): void {
- $request = new Request(
- [
- 'server' => [
- 'HTTP_ORIGIN' => 'test'
- ]
- ],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $this->reflector->reflect($this->controller, __FUNCTION__);
- $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler);
-
- $response = $middleware->afterController($this->controller, __FUNCTION__, new Response());
- $headers = $response->getHeaders();
- $this->assertFalse(array_key_exists('Access-Control-Allow-Origin', $headers));
- }
-
- public static function dataNoOriginHeaderNoCORSHEADER(): array {
- return [
- ['testNoOriginHeaderNoCORSHEADER'],
- ['testNoOriginHeaderNoCORSHEADERAttribute'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataNoOriginHeaderNoCORSHEADER')]
- public function testNoOriginHeaderNoCORSHEADER(string $method): void {
- $request = new Request(
- [],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $this->reflector->reflect($this->controller, $method);
- $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler);
-
- $response = $middleware->afterController($this->controller, $method, new Response());
- $headers = $response->getHeaders();
- $this->assertFalse(array_key_exists('Access-Control-Allow-Origin', $headers));
- }
-
- public static function dataCorsIgnoredIfWithCredentialsHeaderPresent(): array {
- return [
- ['testCorsIgnoredIfWithCredentialsHeaderPresent'],
- ['testCorsAttributeIgnoredIfWithCredentialsHeaderPresent'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataCorsIgnoredIfWithCredentialsHeaderPresent')]
- public function testCorsIgnoredIfWithCredentialsHeaderPresent(string $method): void {
- $this->expectException(SecurityException::class);
-
- $request = new Request(
- [
- 'server' => [
- 'HTTP_ORIGIN' => 'test'
- ]
- ],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $this->reflector->reflect($this->controller, $method);
- $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler, $this->logger);
-
- $response = new Response();
- $response->addHeader('AcCess-control-Allow-Credentials ', 'TRUE');
- $middleware->afterController($this->controller, $method, $response);
- }
-
- public static function dataNoCORSOnAnonymousPublicPage(): array {
- return [
- ['testNoCORSOnAnonymousPublicPage'],
- ['testNoCORSOnAnonymousPublicPageAttribute'],
- ['testNoCORSAttributeOnAnonymousPublicPage'],
- ['testNoCORSAttributeOnAnonymousPublicPageAttribute'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCORSOnAnonymousPublicPage')]
- public function testNoCORSOnAnonymousPublicPage(string $method): void {
- $request = new Request(
- [],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $this->reflector->reflect($this->controller, $method);
- $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler, $this->logger);
- $this->session->expects($this->once())
- ->method('isLoggedIn')
- ->willReturn(false);
- $this->session->expects($this->never())
- ->method('logout');
- $this->session->expects($this->never())
- ->method('logClientIn')
- ->with($this->equalTo('user'), $this->equalTo('pass'))
- ->willReturn(true);
- $this->reflector->reflect($this->controller, $method);
-
- $middleware->beforeController($this->controller, $method);
- }
-
- public static function dataCORSShouldNeverAllowCookieAuth(): array {
- return [
- ['testCORSShouldNeverAllowCookieAuth'],
- ['testCORSShouldNeverAllowCookieAuthAttribute'],
- ['testCORSAttributeShouldNeverAllowCookieAuth'],
- ['testCORSAttributeShouldNeverAllowCookieAuthAttribute'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataCORSShouldNeverAllowCookieAuth')]
- public function testCORSShouldNeverAllowCookieAuth(string $method): void {
- $request = new Request(
- [],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $this->reflector->reflect($this->controller, $method);
- $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler);
- $this->session->expects($this->once())
- ->method('isLoggedIn')
- ->willReturn(true);
- $this->session->expects($this->once())
- ->method('logout');
- $this->session->expects($this->never())
- ->method('logClientIn')
- ->with($this->equalTo('user'), $this->equalTo('pass'))
- ->willReturn(true);
-
- $this->expectException(SecurityException::class);
- $middleware->beforeController($this->controller, $method);
- }
-
- public static function dataCORSShouldRelogin(): array {
- return [
- ['testCORSShouldRelogin'],
- ['testCORSAttributeShouldRelogin'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataCORSShouldRelogin')]
- public function testCORSShouldRelogin(string $method): void {
- $request = new Request(
- ['server' => [
- 'PHP_AUTH_USER' => 'user',
- 'PHP_AUTH_PW' => 'pass'
- ]],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $this->session->expects($this->once())
- ->method('logout');
- $this->session->expects($this->once())
- ->method('logClientIn')
- ->with($this->equalTo('user'), $this->equalTo('pass'))
- ->willReturn(true);
- $this->reflector->reflect($this->controller, $method);
- $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler);
-
- $middleware->beforeController($this->controller, $method);
- }
-
- public static function dataCORSShouldFailIfPasswordLoginIsForbidden(): array {
- return [
- ['testCORSShouldFailIfPasswordLoginIsForbidden'],
- ['testCORSAttributeShouldFailIfPasswordLoginIsForbidden'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataCORSShouldFailIfPasswordLoginIsForbidden')]
- public function testCORSShouldFailIfPasswordLoginIsForbidden(string $method): void {
- $this->expectException(SecurityException::class);
-
- $request = new Request(
- ['server' => [
- 'PHP_AUTH_USER' => 'user',
- 'PHP_AUTH_PW' => 'pass'
- ]],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $this->session->expects($this->once())
- ->method('logout');
- $this->session->expects($this->once())
- ->method('logClientIn')
- ->with($this->equalTo('user'), $this->equalTo('pass'))
- ->willThrowException(new PasswordLoginForbiddenException);
- $this->reflector->reflect($this->controller, $method);
- $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler);
-
- $middleware->beforeController($this->controller, $method);
- }
-
- public static function dataCORSShouldNotAllowCookieAuth(): array {
- return [
- ['testCORSShouldNotAllowCookieAuth'],
- ['testCORSAttributeShouldNotAllowCookieAuth'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataCORSShouldNotAllowCookieAuth')]
- public function testCORSShouldNotAllowCookieAuth(string $method): void {
- $this->expectException(SecurityException::class);
-
- $request = new Request(
- ['server' => [
- 'PHP_AUTH_USER' => 'user',
- 'PHP_AUTH_PW' => 'pass'
- ]],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $this->session->expects($this->once())
- ->method('logout');
- $this->session->expects($this->once())
- ->method('logClientIn')
- ->with($this->equalTo('user'), $this->equalTo('pass'))
- ->willReturn(false);
- $this->reflector->reflect($this->controller, $method);
- $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler);
-
- $middleware->beforeController($this->controller, $method);
- }
-
- public function testAfterExceptionWithSecurityExceptionNoStatus(): void {
- $request = new Request(
- ['server' => [
- 'PHP_AUTH_USER' => 'user',
- 'PHP_AUTH_PW' => 'pass'
- ]],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $this->reflector->reflect($this->controller, __FUNCTION__);
- $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler);
- $response = $middleware->afterException($this->controller, __FUNCTION__, new SecurityException('A security exception'));
-
- $expected = new JSONResponse(['message' => 'A security exception'], 500);
- $this->assertEquals($expected, $response);
- }
-
- public function testAfterExceptionWithSecurityExceptionWithStatus(): void {
- $request = new Request(
- ['server' => [
- 'PHP_AUTH_USER' => 'user',
- 'PHP_AUTH_PW' => 'pass'
- ]],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $this->reflector->reflect($this->controller, __FUNCTION__);
- $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler);
- $response = $middleware->afterException($this->controller, __FUNCTION__, new SecurityException('A security exception', 501));
-
- $expected = new JSONResponse(['message' => 'A security exception'], 501);
- $this->assertEquals($expected, $response);
- }
-
- public function testAfterExceptionWithRegularException(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('A regular exception');
-
- $request = new Request(
- ['server' => [
- 'PHP_AUTH_USER' => 'user',
- 'PHP_AUTH_PW' => 'pass'
- ]],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $this->reflector->reflect($this->controller, __FUNCTION__);
- $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler);
- $middleware->afterException($this->controller, __FUNCTION__, new \Exception('A regular exception'));
- }
-}
--- a/tests/lib/AppFramework/Middleware/Security/CSPMiddlewareTest.php
+++ /dev/null
@@ -1,123 +0,0 @@
-controller = $this->createMock(Controller::class);
- $this->contentSecurityPolicyManager = $this->createMock(ContentSecurityPolicyManager::class);
- $this->cspNonceManager = $this->createMock(ContentSecurityPolicyNonceManager::class);
- $this->middleware = new CSPMiddleware(
- $this->contentSecurityPolicyManager,
- $this->cspNonceManager,
- );
- }
-
- public function testAfterController(): void {
- $this->cspNonceManager
- ->expects($this->once())
- ->method('browserSupportsCspV3')
- ->willReturn(false);
- $response = $this->createMock(Response::class);
- $defaultPolicy = new ContentSecurityPolicy();
- $defaultPolicy->addAllowedImageDomain('defaultpolicy');
- $currentPolicy = new ContentSecurityPolicy();
- $currentPolicy->addAllowedConnectDomain('currentPolicy');
- $mergedPolicy = new ContentSecurityPolicy();
- $mergedPolicy->addAllowedMediaDomain('mergedPolicy');
- $response
- ->expects($this->exactly(2))
- ->method('getContentSecurityPolicy')
- ->willReturn($currentPolicy);
- $this->contentSecurityPolicyManager
- ->expects($this->once())
- ->method('getDefaultPolicy')
- ->willReturn($defaultPolicy);
- $this->contentSecurityPolicyManager
- ->expects($this->once())
- ->method('mergePolicies')
- ->with($defaultPolicy, $currentPolicy)
- ->willReturn($mergedPolicy);
- $response->expects($this->once())
- ->method('setContentSecurityPolicy')
- ->with($mergedPolicy);
-
- $this->middleware->afterController($this->controller, 'test', $response);
- }
-
- public function testAfterControllerEmptyCSP(): void {
- $response = $this->createMock(Response::class);
- $emptyPolicy = new EmptyContentSecurityPolicy();
- $response->expects($this->any())
- ->method('getContentSecurityPolicy')
- ->willReturn($emptyPolicy);
- $response->expects($this->never())
- ->method('setContentSecurityPolicy');
-
- $this->middleware->afterController($this->controller, 'test', $response);
- }
-
- public function testAfterControllerWithContentSecurityPolicy3Support(): void {
- $this->cspNonceManager
- ->expects($this->once())
- ->method('browserSupportsCspV3')
- ->willReturn(true);
- $token = base64_encode('the-nonce');
- $this->cspNonceManager
- ->expects($this->once())
- ->method('getNonce')
- ->willReturn($token);
- $response = $this->createMock(Response::class);
- $defaultPolicy = new ContentSecurityPolicy();
- $defaultPolicy->addAllowedImageDomain('defaultpolicy');
- $currentPolicy = new ContentSecurityPolicy();
- $currentPolicy->addAllowedConnectDomain('currentPolicy');
- $mergedPolicy = new ContentSecurityPolicy();
- $mergedPolicy->addAllowedMediaDomain('mergedPolicy');
- $response
- ->expects($this->exactly(2))
- ->method('getContentSecurityPolicy')
- ->willReturn($currentPolicy);
- $this->contentSecurityPolicyManager
- ->expects($this->once())
- ->method('getDefaultPolicy')
- ->willReturn($defaultPolicy);
- $this->contentSecurityPolicyManager
- ->expects($this->once())
- ->method('mergePolicies')
- ->with($defaultPolicy, $currentPolicy)
- ->willReturn($mergedPolicy);
- $response->expects($this->once())
- ->method('setContentSecurityPolicy')
- ->with($mergedPolicy);
-
- $this->assertEquals($response, $this->middleware->afterController($this->controller, 'test', $response));
- }
-}
--- a/tests/lib/AppFramework/Middleware/Security/FeaturePolicyMiddlewareTest.php
+++ /dev/null
@@ -1,70 +0,0 @@
-controller = $this->createMock(Controller::class);
- $this->manager = $this->createMock(FeaturePolicyManager::class);
- $this->middleware = new FeaturePolicyMiddleware(
- $this->manager
- );
- }
-
- public function testAfterController(): void {
- $response = $this->createMock(Response::class);
- $defaultPolicy = new FeaturePolicy();
- $defaultPolicy->addAllowedCameraDomain('defaultpolicy');
- $currentPolicy = new FeaturePolicy();
- $currentPolicy->addAllowedAutoplayDomain('currentPolicy');
- $mergedPolicy = new FeaturePolicy();
- $mergedPolicy->addAllowedGeoLocationDomain('mergedPolicy');
- $response->method('getFeaturePolicy')
- ->willReturn($currentPolicy);
- $this->manager->method('getDefaultPolicy')
- ->willReturn($defaultPolicy);
- $this->manager->method('mergePolicies')
- ->with($defaultPolicy, $currentPolicy)
- ->willReturn($mergedPolicy);
- $response->expects($this->once())
- ->method('setFeaturePolicy')
- ->with($mergedPolicy);
-
- $this->middleware->afterController($this->controller, 'test', $response);
- }
-
- public function testAfterControllerEmptyCSP(): void {
- $response = $this->createMock(Response::class);
- $emptyPolicy = new EmptyFeaturePolicy();
- $response->method('getFeaturePolicy')
- ->willReturn($emptyPolicy);
- $response->expects($this->never())
- ->method('setFeaturePolicy');
-
- $this->middleware->afterController($this->controller, 'test', $response);
- }
-}
--- a/tests/lib/AppFramework/Middleware/Security/Mock/BruteForceMiddlewareController.php
+++ /dev/null
@@ -1,26 +0,0 @@
-reflector = new ControllerMethodReflector(Server::get(LoggerInterface::class));
- $this->session = $this->createMock(ISession::class);
- $this->userSession = $this->createMock(IUserSession::class);
- $this->user = $this->createMock(IUser::class);
- $this->timeFactory = $this->createMock(ITimeFactory::class);
- $this->tokenProvider = $this->createMock(IProvider::class);
- $this->request = $this->createMock(IRequest::class);
- $this->userManager = $this->createMock(Manager::class);
- $this->controller = new PasswordConfirmationMiddlewareController(
- 'test',
- $this->createMock(IRequest::class)
- );
- $this->remoteAddress = $this->createMock(IRemoteAddress::class);
-
- $this->middleware = new PasswordConfirmationMiddleware(
- $this->reflector,
- $this->session,
- $this->userSession,
- $this->timeFactory,
- $this->tokenProvider,
- $this->request,
- $this->userManager,
- $this->remoteAddress,
- );
- }
-
- public function testNoAnnotationNorAttribute(): void {
- $this->reflector->reflect($this->controller, __FUNCTION__);
- $this->session->expects($this->never())
- ->method($this->anything());
- $this->userSession->expects($this->never())
- ->method($this->anything());
-
- $this->middleware->beforeController($this->controller, __FUNCTION__);
- }
-
- public function testDifferentAnnotation(): void {
- $this->reflector->reflect($this->controller, __FUNCTION__);
- $this->session->expects($this->never())
- ->method($this->anything());
- $this->userSession->expects($this->never())
- ->method($this->anything());
-
- $this->middleware->beforeController($this->controller, __FUNCTION__);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataProvider')]
- public function testAnnotation($backend, $lastConfirm, $currentTime, $exception): void {
- $this->reflector->reflect($this->controller, __FUNCTION__);
-
- $this->user->method('getBackendClassName')
- ->willReturn($backend);
- $this->userSession->method('getUser')
- ->willReturn($this->user);
-
- $this->session->method('get')
- ->with('last-password-confirm')
- ->willReturn($lastConfirm);
-
- $this->timeFactory->method('getTime')
- ->willReturn($currentTime);
-
- $token = $this->createMock(IToken::class);
- $token->method('getScopeAsArray')
- ->willReturn([]);
- $this->tokenProvider->expects($this->once())
- ->method('getToken')
- ->willReturn($token);
-
- $thrown = false;
- try {
- $this->middleware->beforeController($this->controller, __FUNCTION__);
- } catch (NotConfirmedException $e) {
- $thrown = true;
- }
-
- $this->assertSame($exception, $thrown);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataProvider')]
- public function testAttribute($backend, $lastConfirm, $currentTime, $exception): void {
- $this->reflector->reflect($this->controller, __FUNCTION__);
-
- $this->user->method('getBackendClassName')
- ->willReturn($backend);
- $this->userSession->method('getUser')
- ->willReturn($this->user);
-
- $this->session->method('get')
- ->with('last-password-confirm')
- ->willReturn($lastConfirm);
-
- $this->timeFactory->method('getTime')
- ->willReturn($currentTime);
-
- $token = $this->createMock(IToken::class);
- $token->method('getScopeAsArray')
- ->willReturn([]);
- $this->tokenProvider->expects($this->once())
- ->method('getToken')
- ->willReturn($token);
-
- $thrown = false;
- try {
- $this->middleware->beforeController($this->controller, __FUNCTION__);
- } catch (NotConfirmedException $e) {
- $thrown = true;
- }
-
- $this->assertSame($exception, $thrown);
- }
-
- public static function dataProvider(): array {
- return [
- ['foo', 2000, 4000, true],
- ['foo', 2000, 3000, false],
- ['user_saml', 2000, 4000, false],
- ['user_saml', 2000, 3000, false],
- ['foo', 2000, 3815, false],
- ['foo', 2000, 3816, true],
- ];
- }
-
- public function testSSO(): void {
- static $sessionId = 'mySession1d';
-
- $this->reflector->reflect($this->controller, __FUNCTION__);
-
- $this->user->method('getBackendClassName')
- ->willReturn('fictional_backend');
- $this->userSession->method('getUser')
- ->willReturn($this->user);
-
- $this->session->method('get')
- ->with('last-password-confirm')
- ->willReturn(0);
- $this->session->method('getId')
- ->willReturn($sessionId);
-
- $this->timeFactory->method('getTime')
- ->willReturn(9876);
-
- $token = $this->createMock(IToken::class);
- $token->method('getScopeAsArray')
- ->willReturn([IToken::SCOPE_SKIP_PASSWORD_VALIDATION => true]);
- $this->tokenProvider->expects($this->once())
- ->method('getToken')
- ->with($sessionId)
- ->willReturn($token);
-
- $thrown = false;
- try {
- $this->middleware->beforeController($this->controller, __FUNCTION__);
- } catch (NotConfirmedException) {
- $thrown = true;
- }
-
- $this->assertSame(false, $thrown);
- }
-
- public function testAuthHeader(): void {
- $this->reflector->reflect($this->controller, __FUNCTION__);
-
- $this->user->method('getBackendClassName')
- ->willReturn('fictional_backend');
- $this->userSession->method('getUser')
- ->willReturn($this->user);
-
- $this->session->method('get')
- ->with('loginname')
- ->willReturn('user');
-
- $this->request->method('getHeader')
- ->with('PHP_AUTH_PW')
- ->willReturn('password');
-
- $this->userManager->expects($this->once())
- ->method('checkPassword')
- ->with('user', 'password');
-
- $thrown = false;
- try {
- $this->middleware->beforeController($this->controller, __FUNCTION__);
- } catch (NotConfirmedException) {
- $thrown = true;
- }
-
- $this->assertSame(false, $thrown);
- }
-}
--- a/tests/lib/AppFramework/Middleware/Security/RateLimitingMiddlewareTest.php
+++ /dev/null
@@ -1,297 +0,0 @@
-request = $this->createMock(IRequest::class);
- $this->userSession = $this->createMock(IUserSession::class);
- $this->reflector = new ControllerMethodReflector(Server::get(LoggerInterface::class));
- $this->limiter = $this->createMock(Limiter::class);
- $this->session = $this->createMock(ISession::class);
- $this->appConfig = $this->createMock(IAppConfig::class);
- $this->serverConfig = $this->createMock(IConfig::class);
- $this->bruteForceAllowList = $this->createMock(BruteforceAllowList::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $this->rateLimitingMiddleware = new RateLimitingMiddleware(
- $this->request,
- $this->userSession,
- $this->reflector,
- $this->limiter,
- $this->session,
- $this->appConfig,
- $this->serverConfig,
- $this->bruteForceAllowList,
- $this->logger
- );
- }
-
- public function testBeforeControllerWithoutAnnotationForAnon(): void {
- $this->limiter
- ->expects($this->never())
- ->method('registerUserRequest');
- $this->limiter
- ->expects($this->never())
- ->method('registerAnonRequest');
-
- $this->userSession->expects($this->once())
- ->method('isLoggedIn')
- ->willReturn(false);
-
- /** @var RateLimitingMiddlewareController|MockObject $controller */
- $controller = $this->createMock(RateLimitingMiddlewareController::class);
- $this->reflector->reflect($controller, 'testMethodWithoutAnnotation');
- $this->rateLimitingMiddleware->beforeController($controller, 'testMethodWithoutAnnotation');
- }
-
- public function testBeforeControllerWithoutAnnotationForLoggedIn(): void {
- $this->limiter
- ->expects($this->never())
- ->method('registerUserRequest');
- $this->limiter
- ->expects($this->never())
- ->method('registerAnonRequest');
-
- $this->userSession->expects($this->once())
- ->method('isLoggedIn')
- ->willReturn(true);
-
- /** @var RateLimitingMiddlewareController|MockObject $controller */
- $controller = $this->createMock(RateLimitingMiddlewareController::class);
- $this->reflector->reflect($controller, 'testMethodWithoutAnnotation');
- $this->rateLimitingMiddleware->beforeController($controller, 'testMethodWithoutAnnotation');
- }
-
- public function testBeforeControllerForAnon(): void {
- $controller = new RateLimitingMiddlewareController('test', $this->request);
-
- $this->request
- ->method('getRemoteAddress')
- ->willReturn('127.0.0.1');
-
- $this->userSession
- ->expects($this->once())
- ->method('isLoggedIn')
- ->willReturn(false);
-
- $this->limiter
- ->expects($this->never())
- ->method('registerUserRequest');
- $this->limiter
- ->expects($this->once())
- ->method('registerAnonRequest')
- ->with(get_class($controller) . '::testMethodWithAnnotation', '10', '100', '127.0.0.1');
-
- $this->reflector->reflect($controller, 'testMethodWithAnnotation');
- $this->rateLimitingMiddleware->beforeController($controller, 'testMethodWithAnnotation');
- }
-
- public function testBeforeControllerForLoggedIn(): void {
- $controller = new RateLimitingMiddlewareController('test', $this->request);
- /** @var IUser|MockObject $user */
- $user = $this->createMock(IUser::class);
-
- $this->userSession
- ->expects($this->once())
- ->method('isLoggedIn')
- ->willReturn(true);
- $this->userSession
- ->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
-
- $this->limiter
- ->expects($this->never())
- ->method('registerAnonRequest');
- $this->limiter
- ->expects($this->once())
- ->method('registerUserRequest')
- ->with(get_class($controller) . '::testMethodWithAnnotation', '20', '200', $user);
-
- $this->reflector->reflect($controller, 'testMethodWithAnnotation');
- $this->rateLimitingMiddleware->beforeController($controller, 'testMethodWithAnnotation');
- }
-
- public function testBeforeControllerAnonWithFallback(): void {
- $controller = new RateLimitingMiddlewareController('test', $this->request);
- $this->request
- ->expects($this->once())
- ->method('getRemoteAddress')
- ->willReturn('127.0.0.1');
-
- $this->userSession
- ->expects($this->once())
- ->method('isLoggedIn')
- ->willReturn(true);
-
- $this->limiter
- ->expects($this->never())
- ->method('registerUserRequest');
- $this->limiter
- ->expects($this->once())
- ->method('registerAnonRequest')
- ->with(get_class($controller) . '::testMethodWithAnnotationFallback', '10', '100', '127.0.0.1');
-
- $this->reflector->reflect($controller, 'testMethodWithAnnotationFallback');
- $this->rateLimitingMiddleware->beforeController($controller, 'testMethodWithAnnotationFallback');
- }
-
- public function testBeforeControllerAttributesForAnon(): void {
- $controller = new RateLimitingMiddlewareController('test', $this->request);
-
- $this->request
- ->method('getRemoteAddress')
- ->willReturn('127.0.0.1');
-
- $this->userSession
- ->expects($this->once())
- ->method('isLoggedIn')
- ->willReturn(false);
-
- $this->limiter
- ->expects($this->never())
- ->method('registerUserRequest');
- $this->limiter
- ->expects($this->once())
- ->method('registerAnonRequest')
- ->with(get_class($controller) . '::testMethodWithAttributes', '10', '100', '127.0.0.1');
-
- $this->reflector->reflect($controller, 'testMethodWithAttributes');
- $this->rateLimitingMiddleware->beforeController($controller, 'testMethodWithAttributes');
- }
-
- public function testBeforeControllerAttributesForLoggedIn(): void {
- $controller = new RateLimitingMiddlewareController('test', $this->request);
- /** @var IUser|MockObject $user */
- $user = $this->createMock(IUser::class);
-
- $this->userSession
- ->expects($this->once())
- ->method('isLoggedIn')
- ->willReturn(true);
- $this->userSession
- ->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
-
- $this->limiter
- ->expects($this->never())
- ->method('registerAnonRequest');
- $this->limiter
- ->expects($this->once())
- ->method('registerUserRequest')
- ->with(get_class($controller) . '::testMethodWithAttributes', '20', '200', $user);
-
- $this->reflector->reflect($controller, 'testMethodWithAttributes');
- $this->rateLimitingMiddleware->beforeController($controller, 'testMethodWithAttributes');
- }
-
- public function testBeforeControllerAttributesAnonWithFallback(): void {
- $controller = new RateLimitingMiddlewareController('test', $this->request);
- $this->request
- ->expects($this->once())
- ->method('getRemoteAddress')
- ->willReturn('127.0.0.1');
-
- $this->userSession
- ->expects($this->once())
- ->method('isLoggedIn')
- ->willReturn(true);
-
- $this->limiter
- ->expects($this->never())
- ->method('registerUserRequest');
- $this->limiter
- ->expects($this->once())
- ->method('registerAnonRequest')
- ->with(get_class($controller) . '::testMethodWithAttributesFallback', '10', '100', '127.0.0.1');
-
- $this->reflector->reflect($controller, 'testMethodWithAttributesFallback');
- $this->rateLimitingMiddleware->beforeController($controller, 'testMethodWithAttributesFallback');
- }
-
- public function testAfterExceptionWithOtherException(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('My test exception');
-
- $controller = new RateLimitingMiddlewareController('test', $this->request);
-
- $this->rateLimitingMiddleware->afterException($controller, 'testMethod', new \Exception('My test exception'));
- }
-
- public function testAfterExceptionWithJsonBody(): void {
- $controller = new RateLimitingMiddlewareController('test', $this->request);
- $this->request
- ->expects($this->once())
- ->method('getHeader')
- ->with('Accept')
- ->willReturn('JSON');
-
- $result = $this->rateLimitingMiddleware->afterException($controller, 'testMethod', new RateLimitExceededException());
- $expected = new DataResponse([], 429
- );
- $this->assertEquals($expected, $result);
- }
-
- public function testAfterExceptionWithHtmlBody(): void {
- $controller = new RateLimitingMiddlewareController('test', $this->request);
- $this->request
- ->expects($this->once())
- ->method('getHeader')
- ->with('Accept')
- ->willReturn('html');
-
- $result = $this->rateLimitingMiddleware->afterException($controller, 'testMethod', new RateLimitExceededException());
- $expected = new TemplateResponse(
- 'core',
- '429',
- [],
- TemplateResponse::RENDER_AS_GUEST
- );
- $expected->setStatus(429);
- $this->assertEquals($expected, $result);
- $this->assertIsString($result->render());
- }
-}
--- a/tests/lib/AppFramework/Middleware/Security/SameSiteCookieMiddlewareTest.php
+++ /dev/null
@@ -1,136 +0,0 @@
-request = $this->createMock(Request::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->reflector = $this->createMock(ControllerMethodReflector::class);
- $this->middleware = new SameSiteCookieMiddleware($this->request, $this->reflector);
- }
-
- #[\PHPUnit\Framework\Attributes\DoesNotPerformAssertions]
- public function testBeforeControllerNoIndex(): void {
- $this->request->method('getScriptName')
- ->willReturn('/ocs/v2.php');
-
- $this->middleware->beforeController(new NoAnnotationController('foo', $this->request), 'foo');
- }
-
- public function testBeforeControllerIndexHasAnnotation(): void {
- $this->request->method('getScriptName')
- ->willReturn('/index.php');
-
- $this->reflector->expects(self::once())
- ->method('hasAnnotationOrAttribute')
- ->with('NoSameSiteCookieRequired', NoSameSiteCookieRequired::class)
- ->willReturn(true);
-
- $this->middleware->beforeController(new HasAnnotationController('foo', $this->request), 'foo');
- }
-
- public function testBeforeControllerIndexNoAnnotationPassingCheck(): void {
- $this->request->method('getScriptName')
- ->willReturn('/index.php');
-
- $this->reflector->expects(self::once())
- ->method('hasAnnotationOrAttribute')
- ->with('NoSameSiteCookieRequired', NoSameSiteCookieRequired::class)
- ->willReturn(false);
-
- $this->request->method('passesLaxCookieCheck')
- ->willReturn(true);
-
- $this->middleware->beforeController(new NoAnnotationController('foo', $this->request), 'foo');
- }
-
- public function testBeforeControllerIndexNoAnnotationFailingCheck(): void {
- $this->expectException(LaxSameSiteCookieFailedException::class);
-
- $this->request->method('getScriptName')
- ->willReturn('/index.php');
-
- $this->reflector->expects(self::once())
- ->method('hasAnnotationOrAttribute')
- ->with('NoSameSiteCookieRequired', NoSameSiteCookieRequired::class)
- ->willReturn(false);
-
- $this->request->method('passesLaxCookieCheck')
- ->willReturn(false);
-
- $this->middleware->beforeController(new NoAnnotationController('foo', $this->request), 'foo');
- }
-
- public function testAfterExceptionNoLaxCookie(): void {
- $ex = new SecurityException();
-
- try {
- $this->middleware->afterException(new NoAnnotationController('foo', $this->request), 'foo', $ex);
- $this->fail();
- } catch (\Exception $e) {
- $this->assertSame($ex, $e);
- }
- }
-
- public function testAfterExceptionLaxCookie(): void {
- $ex = new LaxSameSiteCookieFailedException();
-
- $this->request->method('getRequestUri')
- ->willReturn('/myrequri');
-
- $middleware = $this->getMockBuilder(SameSiteCookieMiddleware::class)
- ->setConstructorArgs([$this->request, $this->reflector])
- ->onlyMethods(['setSameSiteCookie'])
- ->getMock();
-
- $middleware->expects($this->once())
- ->method('setSameSiteCookie');
-
- $resp = $middleware->afterException(new NoAnnotationController('foo', $this->request), 'foo', $ex);
-
- $this->assertSame(Http::STATUS_FOUND, $resp->getStatus());
-
- $headers = $resp->getHeaders();
- $this->assertSame('/myrequri', $headers['Location']);
- }
-}
--- a/tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php
+++ /dev/null
@@ -1,684 +0,0 @@
-authorizedGroupMapper = $this->createMock(AuthorizedGroupMapper::class);
- $this->userSession = $this->createMock(Session::class);
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('test');
- $this->userSession->method('getUser')->willReturn($user);
- $this->request = $this->createMock(IRequest::class);
- $this->controller = new SecurityMiddlewareController(
- 'test',
- $this->request
- );
- $this->reader = new ControllerMethodReflector(Server::get(LoggerInterface::class));
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->navigationManager = $this->createMock(INavigationManager::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->l10n = $this->createMock(IL10N::class);
- $this->middleware = $this->getMiddleware(true, true, false);
- $this->secAjaxException = new SecurityException('hey', true);
- }
-
- private function getMiddleware(bool $isLoggedIn, bool $isAdminUser, bool $isSubAdmin, bool $isAppEnabledForUser = true): SecurityMiddleware {
- $this->appManager = $this->createMock(IAppManager::class);
- $this->appManager->expects($this->any())
- ->method('isEnabledForUser')
- ->willReturn($isAppEnabledForUser);
- $remoteIpAddress = $this->createMock(IRemoteAddress::class);
- $remoteIpAddress->method('allowsAdminActions')->willReturn(true);
-
- $groupManager = $this->createMock(IGroupManager::class);
- $groupManager->method('isAdmin')
- ->willReturn($isAdminUser);
- $subAdminManager = $this->createMock(ISubAdmin::class);
- $subAdminManager->method('isSubAdmin')
- ->willReturn($isSubAdmin);
-
- return new SecurityMiddleware(
- $this->request,
- $this->reader,
- $this->navigationManager,
- $this->urlGenerator,
- $this->logger,
- 'files',
- $isLoggedIn,
- $groupManager,
- $subAdminManager,
- $this->appManager,
- $this->l10n,
- $this->authorizedGroupMapper,
- $this->userSession,
- $remoteIpAddress
- );
- }
-
- public static function dataNoCSRFRequiredPublicPage(): array {
- return [
- ['testAnnotationNoCSRFRequiredPublicPage'],
- ['testAnnotationNoCSRFRequiredAttributePublicPage'],
- ['testAnnotationPublicPageAttributeNoCSRFRequired'],
- ['testAttributeNoCSRFRequiredPublicPage'],
- ];
- }
-
- public static function dataPublicPage(): array {
- return [
- ['testAnnotationPublicPage'],
- ['testAttributePublicPage'],
- ];
- }
-
- public static function dataNoCSRFRequired(): array {
- return [
- ['testAnnotationNoCSRFRequired'],
- ['testAttributeNoCSRFRequired'],
- ];
- }
-
- public static function dataPublicPageStrictCookieRequired(): array {
- return [
- ['testAnnotationPublicPageStrictCookieRequired'],
- ['testAnnotationStrictCookieRequiredAttributePublicPage'],
- ['testAnnotationPublicPageAttributeStrictCookiesRequired'],
- ['testAttributePublicPageStrictCookiesRequired'],
- ];
- }
-
- public static function dataNoCSRFRequiredPublicPageStrictCookieRequired(): array {
- return [
- ['testAnnotationNoCSRFRequiredPublicPageStrictCookieRequired'],
- ['testAttributeNoCSRFRequiredPublicPageStrictCookiesRequired'],
- ];
- }
-
- public static function dataNoAdminRequiredNoCSRFRequired(): array {
- return [
- ['testAnnotationNoAdminRequiredNoCSRFRequired'],
- ['testAttributeNoAdminRequiredNoCSRFRequired'],
- ];
- }
-
- public static function dataNoAdminRequiredNoCSRFRequiredPublicPage(): array {
- return [
- ['testAnnotationNoAdminRequiredNoCSRFRequiredPublicPage'],
- ['testAttributeNoAdminRequiredNoCSRFRequiredPublicPage'],
- ];
- }
-
- public static function dataNoCSRFRequiredSubAdminRequired(): array {
- return [
- ['testAnnotationNoCSRFRequiredSubAdminRequired'],
- ['testAnnotationNoCSRFRequiredAttributeSubAdminRequired'],
- ['testAnnotationSubAdminRequiredAttributeNoCSRFRequired'],
- ['testAttributeNoCSRFRequiredSubAdminRequired'],
- ];
- }
-
- public static function dataExAppRequired(): array {
- return [
- ['testAnnotationExAppRequired'],
- ['testAttributeExAppRequired'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCSRFRequiredPublicPage')]
- public function testSetNavigationEntry(string $method): void {
- $this->navigationManager->expects($this->once())
- ->method('setActiveEntry')
- ->with($this->equalTo('files'));
-
- $this->reader->reflect($this->controller, $method);
- $this->middleware->beforeController($this->controller, $method);
- }
-
- /**
- * @param string $method
- * @param string $test
- */
- private function ajaxExceptionStatus($method, $test, $status) {
- $isLoggedIn = false;
- $isAdminUser = false;
-
- // isAdminUser requires isLoggedIn call to return true
- if ($test === 'isAdminUser') {
- $isLoggedIn = true;
- }
-
- $sec = $this->getMiddleware($isLoggedIn, $isAdminUser, false);
-
- try {
- $this->reader->reflect($this->controller, $method);
- $sec->beforeController($this->controller, $method);
- } catch (SecurityException $ex) {
- $this->assertEquals($status, $ex->getCode());
- }
-
- // add assertion if everything should work fine otherwise phpunit will
- // complain
- if ($status === 0) {
- $this->addToAssertionCount(1);
- }
- }
-
- public function testAjaxStatusLoggedInCheck(): void {
- $this->ajaxExceptionStatus(
- 'testNoAnnotationNorAttribute',
- 'isLoggedIn',
- Http::STATUS_UNAUTHORIZED
- );
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCSRFRequired')]
- public function testAjaxNotAdminCheck(string $method): void {
- $this->ajaxExceptionStatus(
- $method,
- 'isAdminUser',
- Http::STATUS_FORBIDDEN
- );
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataPublicPage')]
- public function testAjaxStatusCSRFCheck(string $method): void {
- $this->ajaxExceptionStatus(
- $method,
- 'passesCSRFCheck',
- Http::STATUS_PRECONDITION_FAILED
- );
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCSRFRequiredPublicPage')]
- public function testAjaxStatusAllGood(string $method): void {
- $this->ajaxExceptionStatus(
- $method,
- 'isLoggedIn',
- 0
- );
- $this->ajaxExceptionStatus(
- $method,
- 'isAdminUser',
- 0
- );
- $this->ajaxExceptionStatus(
- $method,
- 'passesCSRFCheck',
- 0
- );
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCSRFRequiredPublicPage')]
- public function testNoChecks(string $method): void {
- $this->request->expects($this->never())
- ->method('passesCSRFCheck')
- ->willReturn(false);
-
- $sec = $this->getMiddleware(false, false, false);
-
- $this->reader->reflect($this->controller, $method);
- $sec->beforeController($this->controller, $method);
- }
-
- /**
- * @param string $method
- * @param string $expects
- */
- private function securityCheck($method, $expects, $shouldFail = false) {
- // admin check requires login
- if ($expects === 'isAdminUser') {
- $isLoggedIn = true;
- $isAdminUser = !$shouldFail;
- } else {
- $isLoggedIn = !$shouldFail;
- $isAdminUser = false;
- }
-
- $sec = $this->getMiddleware($isLoggedIn, $isAdminUser, false);
-
- if ($shouldFail) {
- $this->expectException(SecurityException::class);
- } else {
- $this->addToAssertionCount(1);
- }
-
- $this->reader->reflect($this->controller, $method);
- $sec->beforeController($this->controller, $method);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataPublicPage')]
- public function testCsrfCheck(string $method): void {
- $this->expectException(CrossSiteRequestForgeryException::class);
-
- $this->request->expects($this->once())
- ->method('passesCSRFCheck')
- ->willReturn(false);
- $this->request->expects($this->once())
- ->method('passesStrictCookieCheck')
- ->willReturn(true);
- $this->reader->reflect($this->controller, $method);
- $this->middleware->beforeController($this->controller, $method);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCSRFRequiredPublicPage')]
- public function testNoCsrfCheck(string $method): void {
- $this->request->expects($this->never())
- ->method('passesCSRFCheck')
- ->willReturn(false);
-
- $this->reader->reflect($this->controller, $method);
- $this->middleware->beforeController($this->controller, $method);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataPublicPage')]
- public function testPassesCsrfCheck(string $method): void {
- $this->request->expects($this->once())
- ->method('passesCSRFCheck')
- ->willReturn(true);
- $this->request->expects($this->once())
- ->method('passesStrictCookieCheck')
- ->willReturn(true);
-
- $this->reader->reflect($this->controller, $method);
- $this->middleware->beforeController($this->controller, $method);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataPublicPage')]
- public function testFailCsrfCheck(string $method): void {
- $this->expectException(CrossSiteRequestForgeryException::class);
-
- $this->request->expects($this->once())
- ->method('passesCSRFCheck')
- ->willReturn(false);
- $this->request->expects($this->once())
- ->method('passesStrictCookieCheck')
- ->willReturn(true);
-
- $this->reader->reflect($this->controller, $method);
- $this->middleware->beforeController($this->controller, $method);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataPublicPageStrictCookieRequired')]
- public function testStrictCookieRequiredCheck(string $method): void {
- $this->expectException(\OC\AppFramework\Middleware\Security\Exceptions\StrictCookieMissingException::class);
-
- $this->request->expects($this->never())
- ->method('passesCSRFCheck');
- $this->request->expects($this->once())
- ->method('passesStrictCookieCheck')
- ->willReturn(false);
-
- $this->reader->reflect($this->controller, $method);
- $this->middleware->beforeController($this->controller, $method);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCSRFRequiredPublicPage')]
- public function testNoStrictCookieRequiredCheck(string $method): void {
- $this->request->expects($this->never())
- ->method('passesStrictCookieCheck')
- ->willReturn(false);
-
- $this->reader->reflect($this->controller, $method);
- $this->middleware->beforeController($this->controller, $method);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCSRFRequiredPublicPageStrictCookieRequired')]
- public function testPassesStrictCookieRequiredCheck(string $method): void {
- $this->request
- ->expects($this->once())
- ->method('passesStrictCookieCheck')
- ->willReturn(true);
-
- $this->reader->reflect($this->controller, $method);
- $this->middleware->beforeController($this->controller, $method);
- }
-
- public static function dataCsrfOcsController(): array {
- return [
- [NormalController::class, false, false, true],
- [NormalController::class, false, true, true],
- [NormalController::class, true, false, true],
- [NormalController::class, true, true, true],
-
- [OCSController::class, false, false, true],
- [OCSController::class, false, true, false],
- [OCSController::class, true, false, false],
- [OCSController::class, true, true, false],
- ];
- }
-
- /**
- * @param string $controllerClass
- * @param bool $hasOcsApiHeader
- * @param bool $hasBearerAuth
- * @param bool $exception
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataCsrfOcsController')]
- public function testCsrfOcsController(string $controllerClass, bool $hasOcsApiHeader, bool $hasBearerAuth, bool $exception): void {
- $this->request
- ->method('getHeader')
- ->willReturnCallback(function ($header) use ($hasOcsApiHeader, $hasBearerAuth) {
- if ($header === 'OCS-APIREQUEST' && $hasOcsApiHeader) {
- return 'true';
- }
- if ($header === 'Authorization' && $hasBearerAuth) {
- return 'Bearer TOKEN!';
- }
- return '';
- });
- $this->request->expects($this->once())
- ->method('passesStrictCookieCheck')
- ->willReturn(true);
-
- $controller = new $controllerClass('test', $this->request);
- $this->reader->reflect($controller, 'foo');
-
- try {
- $this->middleware->beforeController($controller, 'foo');
- $this->assertFalse($exception);
- } catch (CrossSiteRequestForgeryException $e) {
- $this->assertTrue($exception);
- }
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataNoAdminRequiredNoCSRFRequired')]
- public function testLoggedInCheck(string $method): void {
- $this->securityCheck($method, 'isLoggedIn');
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataNoAdminRequiredNoCSRFRequired')]
- public function testFailLoggedInCheck(string $method): void {
- $this->securityCheck($method, 'isLoggedIn', true);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCSRFRequired')]
- public function testIsAdminCheck(string $method): void {
- $this->securityCheck($method, 'isAdminUser');
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCSRFRequiredSubAdminRequired')]
- public function testIsNotSubAdminCheck(string $method): void {
- $this->reader->reflect($this->controller, $method);
- $sec = $this->getMiddleware(true, false, false);
-
- $this->expectException(SecurityException::class);
- $sec->beforeController($this->controller, $method);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCSRFRequiredSubAdminRequired')]
- #[\PHPUnit\Framework\Attributes\DoesNotPerformAssertions]
- public function testIsSubAdminCheck(string $method): void {
- $this->reader->reflect($this->controller, $method);
- $sec = $this->getMiddleware(true, false, true);
-
- $sec->beforeController($this->controller, $method);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCSRFRequiredSubAdminRequired')]
- #[\PHPUnit\Framework\Attributes\DoesNotPerformAssertions]
- public function testIsSubAdminAndAdminCheck(string $method): void {
- $this->reader->reflect($this->controller, $method);
- $sec = $this->getMiddleware(true, true, true);
-
- $sec->beforeController($this->controller, $method);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCSRFRequired')]
- public function testFailIsAdminCheck(string $method): void {
- $this->securityCheck($method, 'isAdminUser', true);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataNoAdminRequiredNoCSRFRequiredPublicPage')]
- public function testRestrictedAppLoggedInPublicPage(string $method): void {
- $middleware = $this->getMiddleware(true, false, false);
- $this->reader->reflect($this->controller, $method);
-
- $this->appManager->method('getAppPath')
- ->with('files')
- ->willReturn('foo');
-
- $this->appManager->method('isEnabledForUser')
- ->with('files')
- ->willReturn(false);
-
- $middleware->beforeController($this->controller, $method);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataNoAdminRequiredNoCSRFRequiredPublicPage')]
- public function testRestrictedAppNotLoggedInPublicPage(string $method): void {
- $middleware = $this->getMiddleware(false, false, false);
- $this->reader->reflect($this->controller, $method);
-
- $this->appManager->method('getAppPath')
- ->with('files')
- ->willReturn('foo');
-
- $this->appManager->method('isEnabledForUser')
- ->with('files')
- ->willReturn(false);
-
- $middleware->beforeController($this->controller, $method);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataNoAdminRequiredNoCSRFRequired')]
- public function testRestrictedAppLoggedIn(string $method): void {
- $middleware = $this->getMiddleware(true, false, false, false);
- $this->reader->reflect($this->controller, $method);
-
- $this->appManager->method('getAppPath')
- ->with('files')
- ->willReturn('foo');
-
- $this->expectException(AppNotEnabledException::class);
- $middleware->beforeController($this->controller, $method);
- }
-
- public function testAfterExceptionNotCaughtThrowsItAgain(): void {
- $ex = new \Exception();
- $this->expectException(\Exception::class);
- $this->middleware->afterException($this->controller, 'test', $ex);
- }
-
- public function testAfterExceptionReturnsRedirectForNotLoggedInUser(): void {
- $this->request = new Request(
- [
- 'server'
- => [
- 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
- 'REQUEST_URI' => 'nextcloud/index.php/apps/specialapp'
- ]
- ],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $this->middleware = $this->getMiddleware(false, false, false);
- $this->urlGenerator
- ->expects($this->once())
- ->method('linkToRoute')
- ->with(
- 'core.login.showLoginForm',
- [
- 'redirect_url' => 'nextcloud/index.php/apps/specialapp',
- ]
- )
- ->willReturn('http://localhost/nextcloud/index.php/login?redirect_url=nextcloud/index.php/apps/specialapp');
- $this->logger
- ->expects($this->once())
- ->method('debug');
- $response = $this->middleware->afterException(
- $this->controller,
- 'test',
- new NotLoggedInException()
- );
- $expected = new RedirectResponse('http://localhost/nextcloud/index.php/login?redirect_url=nextcloud/index.php/apps/specialapp');
- $this->assertEquals($expected, $response);
- }
-
- public function testAfterExceptionRedirectsToWebRootAfterStrictCookieFail(): void {
- $this->request = new Request(
- [
- 'server' => [
- 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
- 'REQUEST_URI' => 'nextcloud/index.php/apps/specialapp',
- ],
- ],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
-
- $this->middleware = $this->getMiddleware(false, false, false);
- $response = $this->middleware->afterException(
- $this->controller,
- 'test',
- new StrictCookieMissingException()
- );
-
- $expected = new RedirectResponse(\OC::$WEBROOT . '/');
- $this->assertEquals($expected, $response);
- }
-
- /**
- * @return array
- */
- public static function exceptionProvider(): array {
- return [
- [
- new AppNotEnabledException(),
- ],
- [
- new CrossSiteRequestForgeryException(),
- ],
- [
- new NotAdminException(''),
- ],
- ];
- }
-
- /**
- * @param SecurityException $exception
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('exceptionProvider')]
- public function testAfterExceptionReturnsTemplateResponse(SecurityException $exception): void {
- $this->request = new Request(
- [
- 'server'
- => [
- 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
- 'REQUEST_URI' => 'nextcloud/index.php/apps/specialapp'
- ]
- ],
- $this->createMock(IRequestId::class),
- $this->createMock(IConfig::class)
- );
- $this->middleware = $this->getMiddleware(false, false, false);
- $this->logger
- ->expects($this->once())
- ->method('debug');
- $response = $this->middleware->afterException(
- $this->controller,
- 'test',
- $exception
- );
- $expected = new TemplateResponse('core', '403', ['message' => $exception->getMessage()], 'guest');
- $expected->setStatus($exception->getCode());
- $this->assertEquals($expected, $response);
- }
-
- public function testAfterAjaxExceptionReturnsJSONError(): void {
- $response = $this->middleware->afterException($this->controller, 'test',
- $this->secAjaxException);
-
- $this->assertTrue($response instanceof JSONResponse);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataExAppRequired')]
- public function testExAppRequired(string $method): void {
- $middleware = $this->getMiddleware(true, false, false);
- $this->reader->reflect($this->controller, $method);
-
- $session = $this->createMock(ISession::class);
- $session->method('get')->with('app_api')->willReturn(true);
- $this->userSession->method('getSession')->willReturn($session);
-
- $this->request->expects($this->once())
- ->method('passesStrictCookieCheck')
- ->willReturn(true);
- $this->request->expects($this->once())
- ->method('passesCSRFCheck')
- ->willReturn(true);
-
- $middleware->beforeController($this->controller, $method);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataExAppRequired')]
- public function testExAppRequiredError(string $method): void {
- $middleware = $this->getMiddleware(true, false, false, false);
- $this->reader->reflect($this->controller, $method);
-
- $session = $this->createMock(ISession::class);
- $session->method('get')->with('app_api')->willReturn(false);
- $this->userSession->method('getSession')->willReturn($session);
-
- $this->expectException(ExAppRequiredException::class);
- $middleware->beforeController($this->controller, $method);
- }
-}
--- a/tests/lib/AppFramework/Middleware/SessionMiddlewareTest.php
+++ /dev/null
@@ -1,99 +0,0 @@
-reflector = $this->createMock(ControllerMethodReflector::class);
- $this->session = $this->createMock(ISession::class);
- $this->controller = new UseSessionController('app', $this->createMock(IRequest::class));
- $this->middleware = new SessionMiddleware(
- $this->reflector,
- $this->session,
- );
- }
-
- public function testSessionNotClosedOnBeforeController(): void {
- $this->configureSessionMock(0, 1);
- $this->reflector->expects(self::once())
- ->method('hasAnnotationOrAttribute')
- ->with('UseSession', UseSession::class)
- ->willReturn(true);
-
- $this->middleware->beforeController($this->controller, 'withAnnotation');
- }
-
- public function testSessionClosedOnAfterController(): void {
- $this->configureSessionMock(1);
- $this->reflector->expects(self::once())
- ->method('hasAnnotationOrAttribute')
- ->with('UseSession', UseSession::class)
- ->willReturn(true);
-
- $this->middleware->afterController($this->controller, 'withAnnotation', new Response());
- }
-
- public function testSessionReopenedAndClosedOnBeforeController(): void {
- $this->configureSessionMock(1, 1);
- $this->reflector->expects(self::exactly(2))
- ->method('hasAnnotationOrAttribute')
- ->with('UseSession', UseSession::class)
- ->willReturn(true);
-
- $this->middleware->beforeController($this->controller, 'withAnnotation');
- $this->middleware->afterController($this->controller, 'withAnnotation', new Response());
- }
-
- public function testSessionClosedOnBeforeController(): void {
- $this->configureSessionMock(0);
- $this->reflector->expects(self::once())
- ->method('hasAnnotationOrAttribute')
- ->with('UseSession', UseSession::class)
- ->willReturn(false);
-
- $this->middleware->beforeController($this->controller, 'without');
- }
-
- public function testSessionNotClosedOnAfterController(): void {
- $this->configureSessionMock(0);
- $this->reflector->expects(self::once())
- ->method('hasAnnotationOrAttribute')
- ->with('UseSession', UseSession::class)
- ->willReturn(false);
-
- $this->middleware->afterController($this->controller, 'without', new Response());
- }
-
- private function configureSessionMock(int $expectedCloseCount, int $expectedReopenCount = 0): void {
- $this->session->expects($this->exactly($expectedCloseCount))
- ->method('close');
- $this->session->expects($this->exactly($expectedReopenCount))
- ->method('reopen');
- }
-}
--- a/tests/lib/AppFramework/OCS/BaseResponseTest.php
+++ /dev/null
@@ -1,82 +0,0 @@
-array;
- }
-}
-
-class BaseResponseTest extends \Test\TestCase {
- public function testToXml(): void {
- /** @var BaseResponse $response */
- $response = $this->createMock(BaseResponse::class);
-
- $writer = new \XMLWriter();
- $writer->openMemory();
- $writer->setIndent(false);
- $writer->startDocument();
-
- $data = [
- 'hello' => 'hello',
- 'information' => [
- '@test' => 'some data',
- 'someElement' => 'withAttribute',
- ],
- 'value without key',
- 'object' => new \stdClass(),
- ];
-
- $this->invokePrivate($response, 'toXml', [$data, $writer]);
- $writer->endDocument();
-
- $this->assertEquals(
- "\nhello withAttribute value without key \n",
- $writer->outputMemory(true)
- );
- }
-
- public function testToXmlJsonSerializable(): void {
- /** @var BaseResponse $response */
- $response = $this->createMock(BaseResponse::class);
-
- $writer = new \XMLWriter();
- $writer->openMemory();
- $writer->setIndent(false);
- $writer->startDocument();
-
- $data = [
- 'hello' => 'hello',
- 'information' => new ArrayValue([
- '@test' => 'some data',
- 'someElement' => 'withAttribute',
- ]),
- 'value without key',
- 'object' => new \stdClass(),
- ];
-
- $this->invokePrivate($response, 'toXml', [$data, $writer]);
- $writer->endDocument();
-
- $this->assertEquals(
- "\nhello withAttribute value without key \n",
- $writer->outputMemory(true)
- );
- }
-}
--- a/tests/lib/AppFramework/OCS/V2ResponseTest.php
+++ /dev/null
@@ -1,36 +0,0 @@
-assertEquals($expected, $response->getStatus());
- }
-
- public static function providesStatusCodes(): array {
- return [
- [Http::STATUS_OK, 200],
- [Http::STATUS_BAD_REQUEST, 104],
- [Http::STATUS_BAD_REQUEST, 1000],
- [201, 201],
- [Http::STATUS_UNAUTHORIZED, OCSController::RESPOND_UNAUTHORISED],
- [Http::STATUS_INTERNAL_SERVER_ERROR, OCSController::RESPOND_SERVER_ERROR],
- [Http::STATUS_NOT_FOUND, OCSController::RESPOND_NOT_FOUND],
- [Http::STATUS_INTERNAL_SERVER_ERROR, OCSController::RESPOND_UNKNOWN_ERROR],
- ];
- }
-}
--- a/tests/lib/AppFramework/ORM/RepositoryTest.php
+++ /dev/null
@@ -1,919 +0,0 @@
- */
- public static array $entitiesClasses = [
- NoPrimaryKey::class,
- PrimaryKey::class,
- CompositeKey::class,
- Customer::class,
- Cart::class,
- CascadeParent::class,
- CascadeChild::class,
- Merchant::class,
- Order::class,
- EnumOrder::class,
- ];
-
- public static function setUpBeforeClass(): void {
- $schema = new SchemaWrapper(Server::get(Connection::class));
- $entityManager = Server::get(EntityManager::class);
- foreach (static::$entitiesClasses as $entityClass) {
- try {
- $entityManager->createTable($entityClass, $schema);
- } catch (\RuntimeException) {
- self::assertEquals(NoPrimaryKey::class, $entityClass);
- }
- }
- Server::get(Connection::class)->migrateToSchema($schema->getWrappedSchema());
- }
-
- public static function tearDownAfterClass(): void {
- $entityManager = Server::get(EntityManager::class);
- $prefix = Server::get(IConfig::class)->getSystemValueString('dbtableprefix', 'oc_');
- foreach (array_reverse(static::$entitiesClasses) as $entityClass) {
- try {
- $entityManager->dropTable($entityClass, $prefix);
- } catch (\RuntimeException) {
- self::assertEquals(NoPrimaryKey::class, $entityClass);
- }
- }
- }
-
- /**
- * @template T of object
- * @param class-string $entityClass
- * @return Repository
- */
- private function getRepository(string $entityClass): Repository {
- return Server::get(EntityManager::class)->getRepository($entityClass);
- }
-
- public function testMissingPrimaryKey(): void {
- $this->expectException(\RuntimeException::class);
-
- $repo = $this->getRepository(NoPrimaryKey::class);
- $_ = $repo->getTableName();
- }
-
- public function testPrimaryKey(): void {
- $repo = $this->getRepository(PrimaryKey::class);
- $this->assertEquals('repository_test_test2', $repo->getTableName());
- $entity = new PrimaryKey();
- $entity->name = null;
- $entity->notNullable = 'ab';
- $entity->integer = 5;
- $entity->bigInt = 5;
- $entity->float = 3.0;
- $entity->date = new \DateTime('now');
-
- $repo->insert($entity);
- $this->assertNotNull($entity->id);
-
- // Confirm only one entry in the DB
- $entities = iterator_to_array($repo->yieldAll());
- $this->assertCount(1, $entities);
- $savedEntity = $entities[0];
- $this->assertEquals($entity->name, $savedEntity->name);
- $this->assertEquals($entity->notNullable, $savedEntity->notNullable);
- $this->assertEquals($entity->integer, $savedEntity->integer);
- $this->assertEquals($entity->bigInt, $savedEntity->bigInt);
- $this->assertEquals($entity->float, $savedEntity->float);
- $this->assertEquals($entity->date->getTimestamp(), $savedEntity->date->getTimestamp());
-
- // Search by primary key
- $savedEntity = $repo->findOneBy(['id' => $entity->id]);
- $this->assertEquals($entity->name, $savedEntity->name);
- $this->assertEquals($entity->notNullable, $savedEntity->notNullable);
- $this->assertEquals($entity->integer, $savedEntity->integer);
- $this->assertEquals($entity->bigInt, $savedEntity->bigInt);
- $this->assertEquals($entity->float, $savedEntity->float);
- $this->assertEquals($entity->date->getTimestamp(), $savedEntity->date->getTimestamp());
-
- // Search by null
- $savedEntity = $repo->findOneBy(['name' => $entity->name]);
- $this->assertEquals($entity->name, $savedEntity->name);
- $this->assertEquals($entity->notNullable, $savedEntity->notNullable);
- $this->assertEquals($entity->integer, $savedEntity->integer);
- $this->assertEquals($entity->bigInt, $savedEntity->bigInt);
- $this->assertEquals($entity->float, $savedEntity->float);
- $this->assertEquals($entity->date->getTimestamp(), $savedEntity->date->getTimestamp());
-
- // Search by other fields
- $savedEntity = $repo->findOneBy([
- 'notNullable' => $entity->notNullable,
- 'integer' => $entity->integer,
- 'bigInt' => $entity->bigInt,
- ]);
- /** @psalm-assert PrimaryKey $savedEntity */
- $this->assertEquals($entity->name, $savedEntity->name);
- $this->assertEquals($entity->notNullable, $savedEntity->notNullable);
- $this->assertEquals($entity->integer, $savedEntity->integer);
- $this->assertEquals($entity->bigInt, $savedEntity->bigInt);
- $this->assertEquals($entity->float, $savedEntity->float);
- $this->assertEquals($entity->date->getTimestamp(), $savedEntity->date->getTimestamp());
-
- // update
- $entity->name = 'not null anymore';
- $repo->update($entity);
-
- $savedEntity = $repo->findOneBy(['id' => $entity->id]);
- $this->assertEquals($entity->name, $savedEntity->name);
-
- // delete
- $repo->delete($savedEntity);
- }
-
- public function testDeleteBy(): void {
- $repo = $this->getRepository(PrimaryKey::class);
-
- $entity = new PrimaryKey();
- $entity->name = null;
- $entity->notNullable = 'ab';
- $entity->integer = 5;
- $entity->bigInt = 5;
- $entity->float = 3.0;
- $entity->date = new \DateTime('now');
-
- $repo->insert($entity);
- $this->assertNotNull($entity->id);
-
- // Confirm only one entry in the DB
- $entities = iterator_to_array($repo->yieldAll());
- $this->assertCount(1, $entities);
-
- $repo->deleteBy(['id' => $entity->id]);
-
- $entities = iterator_to_array($repo->yieldAll());
- $this->assertCount(0, $entities);
- }
-
- public function testFindByAfterId(): void {
- $repo = $this->getRepository(PrimaryKey::class);
-
- $entities = [];
- for ($i = 0; $i < 5; $i++) {
- $entity = new PrimaryKey();
- $entity->name = 'entry' . $i;
- $entity->notNullable = 'testFindByAfterId';
- $entity->integer = $i;
- $entity->bigInt = $i;
- $entity->float = 1.0;
- $entity->date = new \DateTime('now');
- $repo->insert($entity);
- $entities[] = $entity;
- }
-
- // Walk through every page, seeking from the last id of the previous one, until a page
- // comes back smaller than the requested limit.
- $seenIds = [];
- $lastId = null;
- do {
- $page = iterator_to_array($repo->findByAfterId(['notNullable' => 'testFindByAfterId'], $lastId, 2));
- foreach ($page as $entity) {
- $seenIds[] = $entity->id;
- $lastId = $entity->id;
- }
- } while (count($page) === 2);
-
- $this->assertEquals(array_map(static fn (PrimaryKey $entity): ?int => $entity->id, $entities), $seenIds);
-
- foreach ($entities as $entity) {
- $repo->delete($entity);
- }
- }
-
- public function testFindByAfterIdWithCompositePrimaryKeyThrows(): void {
- $repo = $this->getRepository(CompositeKey::class);
-
- $this->expectException(\LogicException::class);
- iterator_to_array($repo->findByAfterId([], null, 10));
- }
-
- public function testFindByBeforeId(): void {
- $repo = $this->getRepository(PrimaryKey::class);
-
- $entities = [];
- for ($i = 0; $i < 5; $i++) {
- $entity = new PrimaryKey();
- $entity->name = 'entry' . $i;
- $entity->notNullable = 'testFindByBeforeId';
- $entity->integer = $i;
- $entity->bigInt = $i;
- $entity->float = 1.0;
- $entity->date = new \DateTime('now');
- $repo->insert($entity);
- $entities[] = $entity;
- }
-
- // Walk through every page, seeking from the last id of the previous one, until a page
- // comes back smaller than the requested limit.
- $seenIds = [];
- $lastId = null;
- do {
- $page = iterator_to_array($repo->findByBeforeId(['notNullable' => 'testFindByBeforeId'], $lastId, 2));
- foreach ($page as $entity) {
- $seenIds[] = $entity->id;
- $lastId = $entity->id;
- }
- } while (count($page) === 2);
-
- $this->assertEquals(array_reverse(array_map(static fn (PrimaryKey $entity): ?int => $entity->id, $entities)), $seenIds);
-
- foreach ($entities as $entity) {
- $repo->delete($entity);
- }
- }
-
- public function testFindByBeforeIdWithCompositePrimaryKeyThrows(): void {
- $repo = $this->getRepository(CompositeKey::class);
-
- $this->expectException(\LogicException::class);
- iterator_to_array($repo->findByBeforeId([], null, 10));
- }
-
- public function testCompositePrimaryKey(): void {
- $repo = $this->getRepository(CompositeKey::class);
- $this->assertEquals('repository_composite_key', $repo->getTableName());
-
- $entity = new CompositeKey();
- $entity->tenantId = 1;
- $entity->itemId = 42;
- $entity->label = 'first';
- $repo->insert($entity);
-
- // A second entity sharing the tenantId but not the itemId is a distinct row.
- $other = new CompositeKey();
- $other->tenantId = 1;
- $other->itemId = 43;
- $other->label = 'second';
- $repo->insert($other);
-
- $savedEntity = $repo->findOneBy(['tenantId' => 1, 'itemId' => 42]);
- $this->assertEquals('first', $savedEntity->label);
-
- $savedOther = $repo->findOneBy(['tenantId' => 1, 'itemId' => 43]);
- $this->assertEquals('second', $savedOther->label);
-
- // update
- $entity->label = 'updated';
- $repo->update($entity);
-
- $savedEntity = $repo->findOneBy(['tenantId' => 1, 'itemId' => 42]);
- $this->assertEquals('updated', $savedEntity->label);
- // The other row with the same tenantId is untouched.
- $savedOther = $repo->findOneBy(['tenantId' => 1, 'itemId' => 43]);
- $this->assertEquals('second', $savedOther->label);
-
- // delete
- $repo->delete($entity);
- $this->assertCount(0, iterator_to_array($repo->findBy(['tenantId' => 1, 'itemId' => 42])));
- $this->assertCount(1, iterator_to_array($repo->findBy(['tenantId' => 1, 'itemId' => 43])));
-
- $repo->delete($savedOther);
- }
-
- public function testCompositePrimaryKeyMissingPartOnInsertThrows(): void {
- $repo = $this->getRepository(CompositeKey::class);
-
- $entity = new CompositeKey();
- $entity->tenantId = 2;
- // itemId intentionally left unset: composite keys can't rely on DB autoincrement.
- $entity->label = 'incomplete';
-
- $this->expectException(\LogicException::class);
- $this->expectExceptionMessage('is part of a composite primary key and must be set before insert()');
-
- $repo->insert($entity);
- }
-
- public function testCompositePrimaryKeyMissingPartOnUpdateThrows(): void {
- $repo = $this->getRepository(CompositeKey::class);
-
- $entity = new CompositeKey();
- $entity->tenantId = 3;
- $entity->itemId = 1;
- $entity->label = 'x';
- $repo->insert($entity);
-
- $entity->itemId = null;
-
- $this->expectException(\LogicException::class);
- $this->expectExceptionMessage('Trying to update an entity with no primary key set.');
-
- try {
- $repo->update($entity);
- } finally {
- $entity->itemId = 1;
- $repo->delete($entity);
- }
- }
-
- public function testOneToOne(): void {
- $cartRepo = $this->getRepository(Cart::class);
- $customerRepo = $this->getRepository(Customer::class);
-
- $customer = new Customer();
- $customer->name = 'foo';
- $customerRepo->insert($customer);
- $this->assertNotNull($customer->id);
-
- $savedCustomer = $customerRepo->findOneBy(['id' => $customer->id]);
- $this->assertNull($savedCustomer->cart);
-
- $cart = new Cart();
- $cart->customer = $customer;
- $cartRepo->insert($cart);
- $this->assertNotNull($cart->id);
-
- // Owning side: reading the cart back resolves the customer relation
- $savedCart = $cartRepo->findOneBy(['id' => $cart->id]);
- $this->assertNotNull($savedCart->customer);
- $this->assertEquals($customer->id, $savedCart->customer->id);
- $this->assertEquals($customer->name, $savedCart->customer->name);
-
- // Inverse side: reading the customer back resolves the cart relation
- $savedCustomer = $customerRepo->findOneBy(['id' => $customer->id]);
- $this->assertNotNull($savedCustomer->cart);
- $this->assertEquals($cart->id, $savedCustomer->cart->id);
- }
-
- public function testInsertOnMappedBySideThrows(): void {
- $customerRepo = $this->getRepository(Customer::class);
-
- $cart = new Cart();
- $cart->customer = null;
-
- $customer = new Customer();
- $customer->name = 'foo';
- $customer->cart = $cart;
-
- $this->expectException(\LogicException::class);
- $this->expectExceptionMessage('is the mappedBy (inverse) side of a OneToOne relation');
-
- $customerRepo->insert($customer);
- }
-
- public function testOneToOneUniqueConstraint(): void {
- $cartRepo = $this->getRepository(Cart::class);
- $customerRepo = $this->getRepository(Customer::class);
-
- $customer = new Customer();
- $customer->name = 'shared';
- $customerRepo->insert($customer);
-
- $cart1 = new Cart();
- $cart1->customer = $customer;
- $cartRepo->insert($cart1);
-
- $cart2 = new Cart();
- $cart2->customer = $customer;
-
- try {
- $cartRepo->insert($cart2);
- $this->fail('Expected inserting a second Cart for the same Customer to violate the unique constraint.');
- } catch (\OCP\DB\Exception $e) {
- $this->assertEquals(\OCP\DB\Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION, $e->getReason());
- }
- }
-
- public function testOneToOneNullRelation(): void {
- $customerRepo = $this->getRepository(Customer::class);
-
- $customer = new Customer();
- $customer->name = 'no cart yet';
- $customerRepo->insert($customer);
-
- $savedCustomer = $customerRepo->findOneBy(['id' => $customer->id]);
- $this->assertNull($savedCustomer->cart);
- }
-
- public function testMappedByOnDeleteThrows(): void {
- $this->expectException(\RuntimeException::class);
- $this->expectExceptionMessage('sets JoinColumn::$onDelete on the mappedBy (inverse) side');
-
- Server::get(EntityManager::class)->getEntityInfo(InvalidMappedByOnDelete::class);
- }
-
- public function testTypoInMappedByThrows(): void {
- $this->expectException(\RuntimeException::class);
- $this->expectExceptionMessage('has no property named \'ownerTypo\'');
-
- Server::get(EntityManager::class)->getEntityInfo(TypoTarget::class);
- }
-
- public function testDeleteWithDanglingMappedByThrows(): void {
- $cartRepo = $this->getRepository(Cart::class);
- $customerRepo = $this->getRepository(Customer::class);
-
- $customer = new Customer();
- $customer->name = 'has a cart';
- $customerRepo->insert($customer);
-
- $cart = new Cart();
- $cart->customer = $customer;
- $cartRepo->insert($cart);
-
- $this->expectException(\LogicException::class);
- $this->expectExceptionMessage('still references it');
-
- $customerRepo->delete($customer);
- }
-
- public function testDeleteCascadesWhenConfigured(): void {
- $parentRepo = $this->getRepository(CascadeParent::class);
- $childRepo = $this->getRepository(CascadeChild::class);
-
- $parent = new CascadeParent();
- $parentRepo->insert($parent);
-
- $child = new CascadeChild();
- $child->parent = $parent;
- $childRepo->insert($child);
-
- $parentRepo->delete($parent);
-
- $entities = iterator_to_array($childRepo->yieldAll());
- $this->assertCount(0, $entities);
- }
-
- public function testManyToOne(): void {
- $merchantRepo = $this->getRepository(Merchant::class);
- $orderRepo = $this->getRepository(Order::class);
-
- $merchant = new Merchant();
- $merchant->name = 'Acme';
- $merchantRepo->insert($merchant);
-
- $order1 = new Order();
- $order1->merchant = $merchant;
- $orderRepo->insert($order1);
-
- // Unlike OneToOne, a second Order for the same Merchant must be allowed.
- $order2 = new Order();
- $order2->merchant = $merchant;
- $orderRepo->insert($order2);
-
- $savedOrder1 = $orderRepo->findOneBy(['id' => $order1->id]);
- $this->assertNotNull($savedOrder1->merchant);
- $this->assertEquals($merchant->id, $savedOrder1->merchant->id);
- $this->assertEquals($merchant->name, $savedOrder1->merchant->name);
-
- $savedOrder2 = $orderRepo->findOneBy(['id' => $order2->id]);
- $this->assertEquals($merchant->id, $savedOrder2->merchant->id);
- }
-
- public function testManyToOneNullRelation(): void {
- $orderRepo = $this->getRepository(Order::class);
-
- $order = new Order();
- $orderRepo->insert($order);
-
- $savedOrder = $orderRepo->findOneBy(['id' => $order->id]);
- $this->assertNull($savedOrder->merchant);
- }
-
- private function normalizeSql(string $sql): string {
- return str_replace(['`', '"'], '', $sql);
- }
-
- public function testOwningSideGeneratesLeftJoin(): void {
- $cartRepo = $this->getRepository(Cart::class);
-
- /** @var array{0: IQueryBuilder, 1: array} $result */
- $result = self::invokePrivate($cartRepo, 'getJoinedSelectQueryBuilder', [['id' => 1], []]);
- [$qb, $relations] = $result;
-
- $this->assertCount(1, $relations);
- $this->assertEquals(
- 'SELECT e.id AS e_id, r0.id AS r0_id, r0.name AS r0_name '
- . 'FROM *PREFIX*repository_cart e '
- . 'LEFT JOIN *PREFIX*repository_customer r0 ON e.customer_id = r0.id '
- . 'WHERE e.id = :dcValue1',
- $this->normalizeSql($qb->getSQL()),
- );
- }
-
- public function testManyToOneGeneratesLeftJoin(): void {
- $orderRepo = $this->getRepository(Order::class);
-
- /** @var array{0: IQueryBuilder, 1: array} $result */
- $result = self::invokePrivate($orderRepo, 'getJoinedSelectQueryBuilder', [['id' => 1], []]);
- [$qb, $relations] = $result;
-
- $this->assertCount(1, $relations);
- $this->assertEquals(
- 'SELECT e.id AS e_id, r0.id AS r0_id, r0.name AS r0_name '
- . 'FROM *PREFIX*repository_order e '
- . 'LEFT JOIN *PREFIX*repository_merchant r0 ON e.merchant_id = r0.id '
- . 'WHERE e.id = :dcValue1',
- $this->normalizeSql($qb->getSQL()),
- );
- }
-
- public function testInverseSideGeneratesLeftJoin(): void {
- $customerRepo = $this->getRepository(Customer::class);
-
- /** @var array{0: IQueryBuilder, 1: array} $result */
- $result = self::invokePrivate($customerRepo, 'getJoinedSelectQueryBuilder', [['id' => 1], []]);
- [$qb, $relations] = $result;
-
- $this->assertCount(1, $relations);
- $this->assertEquals(
- 'SELECT e.id AS e_id, e.name AS e_name, r0.id AS r0_id '
- . 'FROM *PREFIX*repository_customer e '
- . 'LEFT JOIN *PREFIX*repository_cart r0 ON r0.customer_id = e.id '
- . 'WHERE e.id = :dcValue1',
- $this->normalizeSql($qb->getSQL()),
- );
- }
-
- public function testEntityWithoutRelationsGeneratesNoJoin(): void {
- $repo = $this->getRepository(PrimaryKey::class);
-
- /** @var array{0: IQueryBuilder, 1: array} $result */
- $result = self::invokePrivate($repo, 'getJoinedSelectQueryBuilder', [['id' => 1], []]);
- [$qb, $relations] = $result;
-
- $this->assertCount(0, $relations);
- $this->assertEquals(
- 'SELECT e.id AS e_id, e.name AS e_name, e.not_nullable AS e_not_nullable, '
- . 'e.integer_val AS e_integer_val, e.bigint_val AS e_bigint_val, '
- . 'e.float_val AS e_float_val, e.date_val AS e_date_val '
- . 'FROM *PREFIX*repository_test_test2 e '
- . 'WHERE e.id = :dcValue1',
- $this->normalizeSql($qb->getSQL()),
- );
- }
-
- public function testEnumColumnRoundTrip(): void {
- $repo = $this->getRepository(EnumOrder::class);
-
- $order = new EnumOrder();
- $order->status = OrderStatus::Placed;
- $order->priority = Priority::High;
- $repo->insert($order);
- $this->assertNotNull($order->id);
-
- $saved = $repo->findOneBy(['id' => $order->id]);
- $this->assertSame(OrderStatus::Placed, $saved->status);
- $this->assertSame(Priority::High, $saved->priority);
- $this->assertNull($saved->previousStatus);
-
- $repo->delete($saved);
- }
-
- public function testEnumColumnDefault(): void {
- $repo = $this->getRepository(EnumOrder::class);
-
- $order = new EnumOrder();
- $order->priority = Priority::Low;
- $this->assertSame(OrderStatus::Draft, $order->status);
-
- $repo->insert($order);
- $saved = $repo->findOneBy(['id' => $order->id]);
- $this->assertSame(OrderStatus::Draft, $saved->status);
-
- $repo->delete($saved);
- }
-
- public function testEnumColumnUpdate(): void {
- $repo = $this->getRepository(EnumOrder::class);
-
- $order = new EnumOrder();
- $order->status = OrderStatus::Draft;
- $order->priority = Priority::Low;
- $repo->insert($order);
-
- $order->previousStatus = $order->status;
- $order->status = OrderStatus::Shipped;
- $repo->update($order);
-
- $saved = $repo->findOneBy(['id' => $order->id]);
- $this->assertSame(OrderStatus::Shipped, $saved->status);
- $this->assertSame(OrderStatus::Draft, $saved->previousStatus);
-
- $repo->delete($saved);
- }
-
- public function testFindByEnumCriteria(): void {
- $repo = $this->getRepository(EnumOrder::class);
-
- $placed = new EnumOrder();
- $placed->status = OrderStatus::Placed;
- $placed->priority = Priority::High;
- $repo->insert($placed);
-
- $shipped = new EnumOrder();
- $shipped->status = OrderStatus::Shipped;
- $shipped->priority = Priority::Low;
- $repo->insert($shipped);
-
- $found = $repo->findOneBy(['status' => OrderStatus::Placed]);
- $this->assertSame($placed->id, $found->id);
-
- // IN (...) form with a list of enum cases
- $both = iterator_to_array($repo->findBy(['status' => [OrderStatus::Placed, OrderStatus::Shipped]]));
- $this->assertCount(2, $both);
-
- $repo->delete($placed);
- $repo->delete($shipped);
- }
-
- public function testDeleteByEnumCriteria(): void {
- $repo = $this->getRepository(EnumOrder::class);
-
- $order = new EnumOrder();
- $order->status = OrderStatus::Draft;
- $order->priority = Priority::Low;
- $repo->insert($order);
-
- $repo->deleteBy(['status' => OrderStatus::Draft]);
-
- $this->assertCount(0, iterator_to_array($repo->findBy(['id' => $order->id])));
- }
-
- public function testEnumTypeMustExist(): void {
- $this->expectException(\RuntimeException::class);
- $this->expectExceptionMessage('that class is not an enum');
-
- Server::get(EntityManager::class)->getEntityInfo(EnumUnknownClass::class);
- }
-
- public function testEnumTypeMustBeBacked(): void {
- $this->expectException(\RuntimeException::class);
- $this->expectExceptionMessage('that enum is not backed');
-
- Server::get(EntityManager::class)->getEntityInfo(EnumNotBacked::class);
- }
-
- public function testEnumTypeMustMatchPropertyType(): void {
- $this->expectException(\RuntimeException::class);
- $this->expectExceptionMessage('the property is typed as string instead');
-
- Server::get(EntityManager::class)->getEntityInfo(EnumTypeMismatch::class);
- }
-
- public function testEnumBackingTypeMustMatchColumnType(): void {
- $this->expectException(\RuntimeException::class);
- $this->expectExceptionMessage("cannot hold a(n) int-backed enum's value");
-
- Server::get(EntityManager::class)->getEntityInfo(EnumColumnTypeMismatch::class);
- }
-}
--- a/tests/lib/AppFramework/Routing/RouteParserTest.php
+++ /dev/null
@@ -1,351 +0,0 @@
-parser = new RouteParser();
- }
-
- public function testParseRoutes(): void {
- $routes = ['routes' => [
- ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'],
- ['name' => 'folders#create', 'url' => '/{folderId}/create', 'verb' => 'POST']
- ]];
-
- $collection = $this->parser->parseDefaultRoutes($routes, 'app1');
- $this->assertArrayHasKey('app1.folders.open', $collection->all());
- $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', route: $collection->get('app1.folders.open'));
- $this->assertArrayHasKey('app1.folders.create', $collection->all());
- $this->assertSimpleRoute('/apps/app1/{folderId}/create', 'POST', 'FoldersController', 'create', route: $collection->get('app1.folders.create'));
- }
-
- public function testParseRoutesRootApps(): void {
- $routes = ['routes' => [
- ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'],
- ['name' => 'folders#create', 'url' => '/{folderId}/create', 'verb' => 'POST']
- ]];
-
- $collection = $this->parser->parseDefaultRoutes($routes, 'core');
- $this->assertArrayHasKey('core.folders.open', $collection->all());
- $this->assertSimpleRoute('/{folderId}/open', 'GET', 'FoldersController', 'open', app: 'core', route: $collection->get('core.folders.open'));
- $this->assertArrayHasKey('core.folders.create', $collection->all());
- $this->assertSimpleRoute('/{folderId}/create', 'POST', 'FoldersController', 'create', app: 'core', route: $collection->get('core.folders.create'));
- }
-
- public function testParseRoutesWithResources(): void {
- $routes = ['routes' => [
- ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'],
- ], 'resources' => [
- 'names' => ['url' => '/names'],
- 'folder_names' => ['url' => '/folder/names'],
- ]];
-
- $collection = $this->parser->parseDefaultRoutes($routes, 'app1');
- $this->assertArrayHasKey('app1.folders.open', $collection->all());
- $this->assertSimpleResource('/apps/app1/folder/names', 'folder_names', 'FolderNamesController', 'app1', $collection);
- $this->assertSimpleResource('/apps/app1/names', 'names', 'NamesController', 'app1', $collection);
- }
-
- public function testParseRoutesWithPostfix(): void {
- $routes = ['routes' => [
- ['name' => 'folders#update', 'url' => '/{folderId}/update', 'verb' => 'POST'],
- ['name' => 'folders#update', 'url' => '/{folderId}/update', 'verb' => 'PUT', 'postfix' => '-edit']
- ]];
-
- $collection = $this->parser->parseDefaultRoutes($routes, 'app1');
- $this->assertArrayHasKey('app1.folders.update', $collection->all());
- $this->assertSimpleRoute('/apps/app1/{folderId}/update', 'POST', 'FoldersController', 'update', route: $collection->get('app1.folders.update'));
- $this->assertArrayHasKey('app1.folders.update-edit', $collection->all());
- $this->assertSimpleRoute('/apps/app1/{folderId}/update', 'PUT', 'FoldersController', 'update', route: $collection->get('app1.folders.update-edit'));
- }
-
- public function testParseRoutesKebabCaseAction(): void {
- $routes = ['routes' => [
- ['name' => 'folders#open_folder', 'url' => '/{folderId}/open', 'verb' => 'GET']
- ]];
-
- $collection = $this->parser->parseDefaultRoutes($routes, 'app1');
- $this->assertArrayHasKey('app1.folders.open_folder', $collection->all());
- $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'openFolder', route: $collection->get('app1.folders.open_folder'));
- }
-
- public function testParseRoutesKebabCaseController(): void {
- $routes = ['routes' => [
- ['name' => 'my_folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET']
- ]];
-
- $collection = $this->parser->parseDefaultRoutes($routes, 'app1');
- $this->assertArrayHasKey('app1.my_folders.open', $collection->all());
- $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'MyFoldersController', 'open', route: $collection->get('app1.my_folders.open'));
- }
-
- public function testParseRoutesLowercaseVerb(): void {
- $routes = ['routes' => [
- ['name' => 'folders#delete', 'url' => '/{folderId}/delete', 'verb' => 'delete']
- ]];
-
- $collection = $this->parser->parseDefaultRoutes($routes, 'app1');
- $this->assertArrayHasKey('app1.folders.delete', $collection->all());
- $this->assertSimpleRoute('/apps/app1/{folderId}/delete', 'DELETE', 'FoldersController', 'delete', route: $collection->get('app1.folders.delete'));
- }
-
- public function testParseRoutesMissingVerb(): void {
- $routes = ['routes' => [
- ['name' => 'folders#open', 'url' => '/{folderId}/open']
- ]];
-
- $collection = $this->parser->parseDefaultRoutes($routes, 'app1');
- $this->assertArrayHasKey('app1.folders.open', $collection->all());
- $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', route: $collection->get('app1.folders.open'));
- }
-
- public function testParseRoutesWithRequirements(): void {
- $routes = ['routes' => [
- ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET', 'requirements' => ['folderId' => '\d+']]
- ]];
-
- $collection = $this->parser->parseDefaultRoutes($routes, 'app1');
- $this->assertArrayHasKey('app1.folders.open', $collection->all());
- $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', requirements: ['folderId' => '\d+'], route: $collection->get('app1.folders.open'));
- }
-
- public function testParseRoutesWithDefaults(): void {
- $routes = ['routes' => [
- ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET', 'defaults' => ['hello' => 'world']]
- ]];
-
- $collection = $this->parser->parseDefaultRoutes($routes, 'app1');
- $this->assertArrayHasKey('app1.folders.open', $collection->all());
- $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', defaults: ['hello' => 'world'], route: $collection->get('app1.folders.open'));
- }
-
- public function testParseRoutesInvalidName(): void {
- $routes = ['routes' => [
- ['name' => 'folders', 'url' => '/{folderId}/open', 'verb' => 'GET']
- ]];
-
- $this->expectException(\UnexpectedValueException::class);
- $this->parser->parseDefaultRoutes($routes, 'app1');
- }
-
- public function testParseRoutesInvalidName2(): void {
- $routes = ['routes' => [
- ['name' => 'folders#open#action', 'url' => '/{folderId}/open', 'verb' => 'GET']
- ]];
-
- $this->expectException(\UnexpectedValueException::class);
- $this->parser->parseDefaultRoutes($routes, 'app1');
- }
-
- public function testParseRoutesEmpty(): void {
- $routes = ['routes' => []];
-
- $collection = $this->parser->parseDefaultRoutes($routes, 'app1');
- $this->assertEquals(0, $collection->count());
- }
-
- // OCS routes
-
- public function testParseOcsRoutes(): void {
- $routes = ['ocs' => [
- ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'],
- ['name' => 'folders#create', 'url' => '/{folderId}/create', 'verb' => 'POST']
- ]];
-
- $collection = $this->parser->parseOCSRoutes($routes, 'app1');
- $this->assertArrayHasKey('ocs.app1.folders.open', $collection->all());
- $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', route: $collection->get('ocs.app1.folders.open'));
- $this->assertArrayHasKey('ocs.app1.folders.create', $collection->all());
- $this->assertSimpleRoute('/apps/app1/{folderId}/create', 'POST', 'FoldersController', 'create', route: $collection->get('ocs.app1.folders.create'));
- }
-
- public function testParseOcsRoutesRootApps(): void {
- $routes = ['ocs' => [
- ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'],
- ['name' => 'folders#create', 'url' => '/{folderId}/create', 'verb' => 'POST']
- ]];
-
- $collection = $this->parser->parseOCSRoutes($routes, 'core');
- $this->assertArrayHasKey('ocs.core.folders.open', $collection->all());
- $this->assertSimpleRoute('/{folderId}/open', 'GET', 'FoldersController', 'open', app: 'core', route: $collection->get('ocs.core.folders.open'));
- $this->assertArrayHasKey('ocs.core.folders.create', $collection->all());
- $this->assertSimpleRoute('/{folderId}/create', 'POST', 'FoldersController', 'create', app: 'core', route: $collection->get('ocs.core.folders.create'));
- }
-
- public function testParseOcsRoutesWithPostfix(): void {
- $routes = ['ocs' => [
- ['name' => 'folders#update', 'url' => '/{folderId}/update', 'verb' => 'POST'],
- ['name' => 'folders#update', 'url' => '/{folderId}/update', 'verb' => 'PUT', 'postfix' => '-edit']
- ]];
-
- $collection = $this->parser->parseOCSRoutes($routes, 'app1');
- $this->assertArrayHasKey('ocs.app1.folders.update', $collection->all());
- $this->assertSimpleRoute('/apps/app1/{folderId}/update', 'POST', 'FoldersController', 'update', route: $collection->get('ocs.app1.folders.update'));
- $this->assertArrayHasKey('ocs.app1.folders.update-edit', $collection->all());
- $this->assertSimpleRoute('/apps/app1/{folderId}/update', 'PUT', 'FoldersController', 'update', route: $collection->get('ocs.app1.folders.update-edit'));
- }
-
- public function testParseOcsRoutesKebabCaseAction(): void {
- $routes = ['ocs' => [
- ['name' => 'folders#open_folder', 'url' => '/{folderId}/open', 'verb' => 'GET']
- ]];
-
- $collection = $this->parser->parseOCSRoutes($routes, 'app1');
- $this->assertArrayHasKey('ocs.app1.folders.open_folder', $collection->all());
- $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'openFolder', route: $collection->get('ocs.app1.folders.open_folder'));
- }
-
- public function testParseOcsRoutesKebabCaseController(): void {
- $routes = ['ocs' => [
- ['name' => 'my_folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET']
- ]];
-
- $collection = $this->parser->parseOCSRoutes($routes, 'app1');
- $this->assertArrayHasKey('ocs.app1.my_folders.open', $collection->all());
- $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'MyFoldersController', 'open', route: $collection->get('ocs.app1.my_folders.open'));
- }
-
- public function testParseOcsRoutesLowercaseVerb(): void {
- $routes = ['ocs' => [
- ['name' => 'folders#delete', 'url' => '/{folderId}/delete', 'verb' => 'delete']
- ]];
-
- $collection = $this->parser->parseOCSRoutes($routes, 'app1');
- $this->assertArrayHasKey('ocs.app1.folders.delete', $collection->all());
- $this->assertSimpleRoute('/apps/app1/{folderId}/delete', 'DELETE', 'FoldersController', 'delete', route: $collection->get('ocs.app1.folders.delete'));
- }
-
- public function testParseOcsRoutesMissingVerb(): void {
- $routes = ['ocs' => [
- ['name' => 'folders#open', 'url' => '/{folderId}/open']
- ]];
-
- $collection = $this->parser->parseOCSRoutes($routes, 'app1');
- $this->assertArrayHasKey('ocs.app1.folders.open', $collection->all());
- $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', route: $collection->get('ocs.app1.folders.open'));
- }
-
- public function testParseOcsRoutesWithRequirements(): void {
- $routes = ['ocs' => [
- ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET', 'requirements' => ['folderId' => '\d+']]
- ]];
-
- $collection = $this->parser->parseOCSRoutes($routes, 'app1');
- $this->assertArrayHasKey('ocs.app1.folders.open', $collection->all());
- $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', requirements: ['folderId' => '\d+'], route: $collection->get('ocs.app1.folders.open'));
- }
-
- public function testParseOcsRoutesWithDefaults(): void {
- $routes = ['ocs' => [
- ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET', 'defaults' => ['hello' => 'world']]
- ]];
-
- $collection = $this->parser->parseOCSRoutes($routes, 'app1');
- $this->assertArrayHasKey('ocs.app1.folders.open', $collection->all());
- $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', defaults: ['hello' => 'world'], route: $collection->get('ocs.app1.folders.open'));
- }
-
- public function testParseOcsRoutesInvalidName(): void {
- $routes = ['ocs' => [
- ['name' => 'folders', 'url' => '/{folderId}/open', 'verb' => 'GET']
- ]];
-
- $this->expectException(\UnexpectedValueException::class);
- $this->parser->parseOCSRoutes($routes, 'app1');
- }
-
- public function testParseOcsRoutesEmpty(): void {
- $routes = ['ocs' => []];
-
- $collection = $this->parser->parseOCSRoutes($routes, 'app1');
- $this->assertEquals(0, $collection->count());
- }
-
- public function testParseOcsRoutesWithResources(): void {
- $routes = ['ocs' => [
- ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'],
- ], 'ocs-resources' => [
- 'names' => ['url' => '/names', 'root' => '/core/something'],
- 'folder_names' => ['url' => '/folder/names'],
- ]];
-
- $collection = $this->parser->parseOCSRoutes($routes, 'app1');
- $this->assertArrayHasKey('ocs.app1.folders.open', $collection->all());
- $this->assertOcsResource('/apps/app1/folder/names', 'folder_names', 'FolderNamesController', 'app1', $collection);
- $this->assertOcsResource('/core/something/names', 'names', 'NamesController', 'app1', $collection);
- }
-
- protected function assertSimpleRoute(
- string $path,
- string $method,
- string $controller,
- string $action,
- string $app = 'app1',
- array $requirements = [],
- array $defaults = [],
- ?RoutingRoute $route = null,
- ): void {
- self::assertEquals($path, $route->getPath());
- self::assertEqualsCanonicalizing([$method], $route->getMethods());
- self::assertEqualsCanonicalizing($requirements, $route->getRequirements());
- self::assertEquals([...$defaults, 'action' => null, 'caller' => [$app, $controller, $action]], $route->getDefaults());
- }
-
- protected function assertSimpleResource(
- string $path,
- string $resourceName,
- string $controller,
- string $app,
- RouteCollection $collection,
- ): void {
- self::assertArrayHasKey("$app.$resourceName.index", $collection->all());
- self::assertArrayHasKey("$app.$resourceName.show", $collection->all());
- self::assertArrayHasKey("$app.$resourceName.create", $collection->all());
- self::assertArrayHasKey("$app.$resourceName.update", $collection->all());
- self::assertArrayHasKey("$app.$resourceName.destroy", $collection->all());
-
- $this->assertSimpleRoute($path, 'GET', $controller, 'index', $app, route: $collection->get("$app.$resourceName.index"));
- $this->assertSimpleRoute($path, 'POST', $controller, 'create', $app, route: $collection->get("$app.$resourceName.create"));
- $this->assertSimpleRoute("$path/{id}", 'GET', $controller, 'show', $app, route: $collection->get("$app.$resourceName.show"));
- $this->assertSimpleRoute("$path/{id}", 'PUT', $controller, 'update', $app, route: $collection->get("$app.$resourceName.update"));
- $this->assertSimpleRoute("$path/{id}", 'DELETE', $controller, 'destroy', $app, route: $collection->get("$app.$resourceName.destroy"));
- }
-
- protected function assertOcsResource(
- string $path,
- string $resourceName,
- string $controller,
- string $app,
- RouteCollection $collection,
- ): void {
- self::assertArrayHasKey("ocs.$app.$resourceName.index", $collection->all());
- self::assertArrayHasKey("ocs.$app.$resourceName.show", $collection->all());
- self::assertArrayHasKey("ocs.$app.$resourceName.create", $collection->all());
- self::assertArrayHasKey("ocs.$app.$resourceName.update", $collection->all());
- self::assertArrayHasKey("ocs.$app.$resourceName.destroy", $collection->all());
-
- $this->assertSimpleRoute($path, 'GET', $controller, 'index', $app, route: $collection->get("ocs.$app.$resourceName.index"));
- $this->assertSimpleRoute($path, 'POST', $controller, 'create', $app, route: $collection->get("ocs.$app.$resourceName.create"));
- $this->assertSimpleRoute("$path/{id}", 'GET', $controller, 'show', $app, route: $collection->get("ocs.$app.$resourceName.show"));
- $this->assertSimpleRoute("$path/{id}", 'PUT', $controller, 'update', $app, route: $collection->get("ocs.$app.$resourceName.update"));
- $this->assertSimpleRoute("$path/{id}", 'DELETE', $controller, 'destroy', $app, route: $collection->get("ocs.$app.$resourceName.destroy"));
- }
-}
--- a/tests/lib/AppFramework/Services/AppConfigTest.php
+++ /dev/null
@@ -1,667 +0,0 @@
-config = $this->createMock(IConfig::class);
- $this->appConfigCore = $this->createMock(AppConfigCore::class);
-
- $this->appConfig = new AppConfig($this->config, $this->appConfigCore, self::TEST_APPID);
- }
-
- public function testGetAppKeys(): void {
- $expected = ['key1', 'key2', 'key3', 'key4', 'key5', 'key6', 'key7', 'test8'];
- $this->appConfigCore->expects($this->once())
- ->method('getKeys')
- ->with(self::TEST_APPID)
- ->willReturn($expected);
- $this->assertSame($expected, $this->appConfig->getAppKeys());
- }
-
- /**
- * @return array
- * @see testHasAppKey
- */
- public static function providerHasAppKey(): array {
- return [
- // lazy, expected
- [false, true],
- [true, true],
- [false, false],
- [true, false],
- ];
- }
-
- /**
- *
- * @param bool $lazy
- * @param bool $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerHasAppKey')]
- public function testHasAppKey(bool $lazy, bool $expected): void {
- $key = 'key';
- $this->appConfigCore->expects($this->once())
- ->method('hasKey')
- ->with(self::TEST_APPID, $key, $lazy)
- ->willReturn($expected);
- $this->assertSame($expected, $this->appConfig->hasAppKey($key, $lazy));
- }
-
- /**
- * @return array
- * @see testIsSensitive
- */
- public static function providerIsSensitive(): array {
- return [
- // lazy, expected
- [false, true],
- [true, true],
- [false, false],
- [true, false],
- ];
- }
-
- /**
- *
- * @param bool $lazy
- * @param bool $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerIsSensitive')]
- public function testIsSensitive(bool $lazy, bool $expected): void {
- $key = 'key';
- $this->appConfigCore->expects($this->once())
- ->method('isSensitive')
- ->with(self::TEST_APPID, $key, $lazy)
- ->willReturn($expected);
-
- $this->assertSame($expected, $this->appConfig->isSensitive($key, $lazy));
- }
-
- /**
- *
- * @param bool $lazy
- * @param bool $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerIsSensitive')]
- public function testIsSensitiveException(bool $lazy, bool $expected): void {
- $key = 'unknown-key';
- $this->appConfigCore->expects($this->once())
- ->method('isSensitive')
- ->with(self::TEST_APPID, $key, $lazy)
- ->willThrowException(new AppConfigUnknownKeyException());
-
- $this->expectException(AppConfigUnknownKeyException::class);
- $this->appConfig->isSensitive($key, $lazy);
- }
-
- /**
- * @return array
- * @see testIsLazy
- */
- public static function providerIsLazy(): array {
- return [
- // expected
- [true],
- [false],
- ];
- }
-
- /**
- * @param bool $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerIsLazy')]
- public function testIsLazy(bool $expected): void {
- $key = 'key';
- $this->appConfigCore->expects($this->once())
- ->method('isLazy')
- ->with(self::TEST_APPID, $key)
- ->willReturn($expected);
-
- $this->assertSame($expected, $this->appConfig->isLazy($key));
- }
-
- public function testIsLazyException(): void {
- $key = 'unknown-key';
- $this->appConfigCore->expects($this->once())
- ->method('isLazy')
- ->with(self::TEST_APPID, $key)
- ->willThrowException(new AppConfigUnknownKeyException());
-
- $this->expectException(AppConfigUnknownKeyException::class);
- $this->appConfig->isLazy($key);
- }
-
- /**
- * @return array
- * @see testGetAllAppValues
- */
- public static function providerGetAllAppValues(): array {
- return [
- // key, filtered
- ['', false],
- ['', true],
- ['key', false],
- ['key', true],
- ];
- }
-
- /**
- *
- * @param string $key
- * @param bool $filtered
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAllAppValues')]
- public function testGetAllAppValues(string $key, bool $filtered): void {
- $expected = [
- 'key1' => 'value1',
- 'key2' => 3,
- 'key3' => 3.14,
- 'key4' => true
- ];
-
- $this->appConfigCore->expects($this->once())
- ->method('getAllValues')
- ->with(self::TEST_APPID, $key, $filtered)
- ->willReturn($expected);
-
- $this->assertSame($expected, $this->appConfig->getAllAppValues($key, $filtered));
- }
-
- public function testSetAppValue(): void {
- $key = 'key';
- $value = 'value';
- $this->appConfigCore->expects($this->once())
- ->method('setValueMixed')
- ->with(self::TEST_APPID, $key, $value);
-
- $this->appConfig->setAppValue($key, $value);
- }
-
- /**
- * @return array
- * @see testSetAppValueString
- * @see testSetAppValueStringException
- * @see testSetAppValueInt
- * @see testSetAppValueIntException
- * @see testSetAppValueFloat
- * @see testSetAppValueFloatException
- * @see testSetAppValueArray
- * @see testSetAppValueArrayException
- */
- public static function providerSetAppValue(): array {
- return [
- // lazy, sensitive, expected
- [false, false, true],
- [false, true, true],
- [true, true, true],
- [true, false, true],
- [false, false, false],
- [false, true, false],
- [true, true, false],
- [true, false, false],
- ];
- }
-
- /**
- *
- * @param bool $lazy
- * @param bool $sensitive
- * @param bool $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerSetAppValue')]
- public function testSetAppValueString(bool $lazy, bool $sensitive, bool $expected): void {
- $key = 'key';
- $value = 'valueString';
- $this->appConfigCore->expects($this->once())
- ->method('setValueString')
- ->with(self::TEST_APPID, $key, $value, $lazy, $sensitive)
- ->willReturn($expected);
-
- $this->assertSame($expected, $this->appConfig->setAppValueString($key, $value, $lazy, $sensitive));
- }
-
- /**
- *
- * @param bool $lazy
- * @param bool $sensitive
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerSetAppValue')]
- public function testSetAppValueStringException(bool $lazy, bool $sensitive): void {
- $key = 'key';
- $value = 'valueString';
- $this->appConfigCore->expects($this->once())
- ->method('setValueString')
- ->with(self::TEST_APPID, $key, $value, $lazy, $sensitive)
- ->willThrowException(new AppConfigTypeConflictException());
-
- $this->expectException(AppConfigTypeConflictException::class);
- $this->appConfig->setAppValueString($key, $value, $lazy, $sensitive);
- }
-
- /**
- *
- * @param bool $lazy
- * @param bool $sensitive
- * @param bool $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerSetAppValue')]
- public function testSetAppValueInt(bool $lazy, bool $sensitive, bool $expected): void {
- $key = 'key';
- $value = 42;
- $this->appConfigCore->expects($this->once())
- ->method('setValueInt')
- ->with(self::TEST_APPID, $key, $value, $lazy, $sensitive)
- ->willReturn($expected);
-
- $this->assertSame($expected, $this->appConfig->setAppValueInt($key, $value, $lazy, $sensitive));
- }
-
- /**
- *
- * @param bool $lazy
- * @param bool $sensitive
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerSetAppValue')]
- public function testSetAppValueIntException(bool $lazy, bool $sensitive): void {
- $key = 'key';
- $value = 42;
- $this->appConfigCore->expects($this->once())
- ->method('setValueInt')
- ->with(self::TEST_APPID, $key, $value, $lazy, $sensitive)
- ->willThrowException(new AppConfigTypeConflictException());
-
- $this->expectException(AppConfigTypeConflictException::class);
- $this->appConfig->setAppValueInt($key, $value, $lazy, $sensitive);
- }
-
- /**
- *
- * @param bool $lazy
- * @param bool $sensitive
- * @param bool $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerSetAppValue')]
- public function testSetAppValueFloat(bool $lazy, bool $sensitive, bool $expected): void {
- $key = 'key';
- $value = 3.14;
- $this->appConfigCore->expects($this->once())
- ->method('setValueFloat')
- ->with(self::TEST_APPID, $key, $value, $lazy, $sensitive)
- ->willReturn($expected);
-
- $this->assertSame($expected, $this->appConfig->setAppValueFloat($key, $value, $lazy, $sensitive));
- }
-
- /**
- *
- * @param bool $lazy
- * @param bool $sensitive
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerSetAppValue')]
- public function testSetAppValueFloatException(bool $lazy, bool $sensitive): void {
- $key = 'key';
- $value = 3.14;
- $this->appConfigCore->expects($this->once())
- ->method('setValueFloat')
- ->with(self::TEST_APPID, $key, $value, $lazy, $sensitive)
- ->willThrowException(new AppConfigTypeConflictException());
-
- $this->expectException(AppConfigTypeConflictException::class);
- $this->appConfig->setAppValueFloat($key, $value, $lazy, $sensitive);
- }
-
- /**
- * @return array
- * @see testSetAppValueBool
- */
- public static function providerSetAppValueBool(): array {
- return [
- // lazy, expected
- [false, true],
- [false, false],
- [true, true],
- [true, false],
- ];
- }
-
- /**
- *
- * @param bool $lazy
- * @param bool $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerSetAppValueBool')]
- public function testSetAppValueBool(bool $lazy, bool $expected): void {
- $key = 'key';
- $value = true;
- $this->appConfigCore->expects($this->once())
- ->method('setValueBool')
- ->with(self::TEST_APPID, $key, $value, $lazy)
- ->willReturn($expected);
-
- $this->assertSame($expected, $this->appConfig->setAppValueBool($key, $value, $lazy));
- }
-
- /**
- * @param bool $lazy
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerSetAppValueBool')]
- public function testSetAppValueBoolException(bool $lazy): void {
- $key = 'key';
- $value = true;
- $this->appConfigCore->expects($this->once())
- ->method('setValueBool')
- ->with(self::TEST_APPID, $key, $value, $lazy)
- ->willThrowException(new AppConfigTypeConflictException());
-
- $this->expectException(AppConfigTypeConflictException::class);
- $this->appConfig->setAppValueBool($key, $value, $lazy);
- }
-
- /**
- *
- * @param bool $lazy
- * @param bool $sensitive
- * @param bool $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerSetAppValue')]
- public function testSetAppValueArray(bool $lazy, bool $sensitive, bool $expected): void {
- $key = 'key';
- $value = ['item' => true];
- $this->appConfigCore->expects($this->once())
- ->method('setValueArray')
- ->with(self::TEST_APPID, $key, $value, $lazy, $sensitive)
- ->willReturn($expected);
-
- $this->assertSame($expected, $this->appConfig->setAppValueArray($key, $value, $lazy, $sensitive));
- }
-
- /**
- *
- * @param bool $lazy
- * @param bool $sensitive
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerSetAppValue')]
- public function testSetAppValueArrayException(bool $lazy, bool $sensitive): void {
- $key = 'key';
- $value = ['item' => true];
- $this->appConfigCore->expects($this->once())
- ->method('setValueArray')
- ->with(self::TEST_APPID, $key, $value, $lazy, $sensitive)
- ->willThrowException(new AppConfigTypeConflictException());
-
- $this->expectException(AppConfigTypeConflictException::class);
- $this->appConfig->setAppValueArray($key, $value, $lazy, $sensitive);
- }
-
- public function testGetAppValue(): void {
- $key = 'key';
- $value = 'value';
- $default = 'default';
- $this->appConfigCore->expects($this->once())
- ->method('getValueMixed')
- ->with(self::TEST_APPID, $key, $default)
- ->willReturn($value);
-
- $this->assertSame($value, $this->appConfig->getAppValue($key, $default));
- }
-
- public function testGetAppValueDefault(): void {
- $key = 'key';
- $default = 'default';
- $this->appConfigCore->expects($this->once())
- ->method('getValueMixed')
- ->with(self::TEST_APPID, $key, $default)
- ->willReturn($default);
-
- $this->assertSame($default, $this->appConfig->getAppValue($key, $default));
- }
-
- /**
- * @return array
- * @see testGetAppValueString
- * @see testGetAppValueStringException
- * @see testGetAppValueInt
- * @see testGetAppValueIntException
- * @see testGetAppValueFloat
- * @see testGetAppValueFloatException
- * @see testGetAppValueBool
- * @see testGetAppValueBoolException
- * @see testGetAppValueArray
- * @see testGetAppValueArrayException
- */
- public static function providerGetAppValue(): array {
- return [
- // lazy, exist
- [false, false],
- [false, true],
- [true, true],
- [true, false]
- ];
- }
-
- /**
- *
- * @param bool $lazy
- * @param bool $exist
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAppValue')]
- public function testGetAppValueString(bool $lazy, bool $exist): void {
- $key = 'key';
- $value = 'valueString';
- $default = 'default';
-
- $expected = ($exist) ? $value : $default;
- $this->appConfigCore->expects($this->once())
- ->method('getValueString')
- ->with(self::TEST_APPID, $key, $default, $lazy)
- ->willReturn($expected);
-
- $this->assertSame($expected, $this->appConfig->getAppValueString($key, $default, $lazy));
- }
-
- /**
- * @param bool $lazy
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAppValue')]
- public function testGetAppValueStringException(bool $lazy): void {
- $key = 'key';
- $default = 'default';
-
- $this->appConfigCore->expects($this->once())
- ->method('getValueString')
- ->with(self::TEST_APPID, $key, $default, $lazy)
- ->willThrowException(new AppConfigTypeConflictException());
-
- $this->expectException(AppConfigTypeConflictException::class);
- $this->appConfig->getAppValueString($key, $default, $lazy);
- }
-
- /**
- *
- * @param bool $lazy
- * @param bool $exist
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAppValue')]
- public function testGetAppValueInt(bool $lazy, bool $exist): void {
- $key = 'key';
- $value = 42;
- $default = 17;
-
- $expected = ($exist) ? $value : $default;
- $this->appConfigCore->expects($this->once())
- ->method('getValueInt')
- ->with(self::TEST_APPID, $key, $default, $lazy)
- ->willReturn($expected);
-
- $this->assertSame($expected, $this->appConfig->getAppValueInt($key, $default, $lazy));
- }
-
- /**
- * @param bool $lazy
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAppValue')]
- public function testGetAppValueIntException(bool $lazy): void {
- $key = 'key';
- $default = 17;
-
- $this->appConfigCore->expects($this->once())
- ->method('getValueInt')
- ->with(self::TEST_APPID, $key, $default, $lazy)
- ->willThrowException(new AppConfigTypeConflictException());
-
- $this->expectException(AppConfigTypeConflictException::class);
- $this->appConfig->getAppValueInt($key, $default, $lazy);
- }
-
- /**
- *
- * @param bool $lazy
- * @param bool $exist
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAppValue')]
- public function testGetAppValueFloat(bool $lazy, bool $exist): void {
- $key = 'key';
- $value = 3.14;
- $default = 17.04;
-
- $expected = ($exist) ? $value : $default;
- $this->appConfigCore->expects($this->once())
- ->method('getValueFloat')
- ->with(self::TEST_APPID, $key, $default, $lazy)
- ->willReturn($expected);
-
- $this->assertSame($expected, $this->appConfig->getAppValueFloat($key, $default, $lazy));
- }
-
- /**
- * @param bool $lazy
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAppValue')]
- public function testGetAppValueFloatException(bool $lazy): void {
- $key = 'key';
- $default = 17.04;
-
- $this->appConfigCore->expects($this->once())
- ->method('getValueFloat')
- ->with(self::TEST_APPID, $key, $default, $lazy)
- ->willThrowException(new AppConfigTypeConflictException());
-
- $this->expectException(AppConfigTypeConflictException::class);
- $this->appConfig->getAppValueFloat($key, $default, $lazy);
- }
-
- /**
- *
- * @param bool $lazy
- * @param bool $exist
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAppValue')]
- public function testGetAppValueBool(bool $lazy, bool $exist): void {
- $key = 'key';
- $value = true;
- $default = false;
-
- $expected = ($exist) ? $value : $default; // yes, it can be simplified
- $this->appConfigCore->expects($this->once())
- ->method('getValueBool')
- ->with(self::TEST_APPID, $key, $default, $lazy)
- ->willReturn($expected);
-
- $this->assertSame($expected, $this->appConfig->getAppValueBool($key, $default, $lazy));
- }
-
- /**
- * @param bool $lazy
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAppValue')]
- public function testGetAppValueBoolException(bool $lazy): void {
- $key = 'key';
- $default = false;
-
- $this->appConfigCore->expects($this->once())
- ->method('getValueBool')
- ->with(self::TEST_APPID, $key, $default, $lazy)
- ->willThrowException(new AppConfigTypeConflictException());
-
- $this->expectException(AppConfigTypeConflictException::class);
- $this->appConfig->getAppValueBool($key, $default, $lazy);
- }
-
- /**
- *
- * @param bool $lazy
- * @param bool $exist
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAppValue')]
- public function testGetAppValueArray(bool $lazy, bool $exist): void {
- $key = 'key';
- $value = ['item' => true];
- $default = [];
-
- $expected = ($exist) ? $value : $default;
- $this->appConfigCore->expects($this->once())
- ->method('getValueArray')
- ->with(self::TEST_APPID, $key, $default, $lazy)
- ->willReturn($expected);
-
- $this->assertSame($expected, $this->appConfig->getAppValueArray($key, $default, $lazy));
- }
-
- /**
- * @param bool $lazy
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAppValue')]
- public function testGetAppValueArrayException(bool $lazy): void {
- $key = 'key';
- $default = [];
-
- $this->appConfigCore->expects($this->once())
- ->method('getValueArray')
- ->with(self::TEST_APPID, $key, $default, $lazy)
- ->willThrowException(new AppConfigTypeConflictException());
-
- $this->expectException(AppConfigTypeConflictException::class);
- $this->appConfig->getAppValueArray($key, $default, $lazy);
- }
-
- public function testDeleteAppValue(): void {
- $key = 'key';
- $this->appConfigCore->expects($this->once())
- ->method('deleteKey')
- ->with(self::TEST_APPID, $key);
-
- $this->appConfig->deleteAppValue($key);
- }
-
- public function testDeleteAppValues(): void {
- $this->appConfigCore->expects($this->once())
- ->method('deleteApp')
- ->with(self::TEST_APPID);
-
- $this->appConfig->deleteAppValues();
- }
-}
--- a/tests/lib/AppFramework/Utility/ControllerMethodReflectorTest.php
+++ /dev/null
@@ -1,395 +0,0 @@
- $rangedOne
- * @psalm-param int $rangedTwo
- * @psalm-param int<1, 6>|null $rangedThree
- * @psalm-param ?int<-70, -30> $rangedFour
- * @return void
- */
- public function test4(int $rangedOne, int $rangedTwo, ?int $rangedThree, ?int $rangedFour) {
- }
-
- /**
- * @param int<-4, 42> $rangedOne
- * @param int $rangedTwo
- * @param int<1, 6>|null $rangedThree
- * @param ?int<-70, -30> $rangedFour
- * @return void
- */
- public function test5(int $rangedOne, int $rangedTwo, ?int $rangedThree, ?int $rangedFour) {
- }
-
- /**
- * @psalm-param positive-int $positive
- * @psalm-param non-negative-int $nonNegative
- * @psalm-param negative-int $negative
- * @psalm-param non-positive-int $nonPositive
- * @psalm-param positive-int|null $positiveOrNull
- * @psalm-param ?non-negative-int $nonNegativeOrNull
- * @return void
- */
- public function test6(int $positive, int $nonNegative, int $negative, int $nonPositive, ?int $positiveOrNull, ?int $nonNegativeOrNull) {
- }
-
- /**
- * @psalm-param non-empty-string $nonEmpty
- * @psalm-param non-empty-lowercase-string $nonEmptyLowercase
- * @psalm-param lowercase-string $lowercase
- * @psalm-param non-falsy-string $nonFalsy
- * @psalm-param numeric-string $numeric
- * @psalm-param non-empty-string|null $nonEmptyOrNull
- * @psalm-param ?non-empty-string $nonEmptyOrNullPrefix
- * @psalm-param string $plain
- * @return void
- */
- public function test7(
- string $nonEmpty,
- string $nonEmptyLowercase,
- string $lowercase,
- string $nonFalsy,
- string $numeric,
- ?string $nonEmptyOrNull,
- ?string $nonEmptyOrNullPrefix,
- string $plain,
- ) {
- }
-}
-
-class EndController extends MiddleController {
-}
-
-class ControllerMethodReflectorTest extends \Test\TestCase {
- /**
- * @Annotation
- */
- public function testReadAnnotation(): void {
- $reader = new ControllerMethodReflector(Server::get(LoggerInterface::class));
- $reader->reflect(
- '\Test\AppFramework\Utility\ControllerMethodReflectorTest',
- 'testReadAnnotation'
- );
-
- $this->assertTrue($reader->hasAnnotation('Annotation'));
- }
-
- /**
- * @Annotation(parameter=value)
- */
- public function testGetAnnotationParameterSingle(): void {
- $reader = new ControllerMethodReflector(Server::get(LoggerInterface::class));
- $reader->reflect(
- self::class,
- __FUNCTION__
- );
-
- $this->assertSame('value', $reader->getAnnotationParameter('Annotation', 'parameter'));
- }
-
- /**
- * @Annotation(parameter1=value1, parameter2=value2,parameter3=value3)
- */
- public function testGetAnnotationParameterMultiple(): void {
- $reader = new ControllerMethodReflector(Server::get(LoggerInterface::class));
- $reader->reflect(
- self::class,
- __FUNCTION__
- );
-
- $this->assertSame('value1', $reader->getAnnotationParameter('Annotation', 'parameter1'));
- $this->assertSame('value2', $reader->getAnnotationParameter('Annotation', 'parameter2'));
- $this->assertSame('value3', $reader->getAnnotationParameter('Annotation', 'parameter3'));
- }
-
- /**
- * @Annotation
- * @param test
- */
- public function testReadAnnotationNoLowercase(): void {
- $reader = new ControllerMethodReflector(Server::get(LoggerInterface::class));
- $reader->reflect(
- '\Test\AppFramework\Utility\ControllerMethodReflectorTest',
- 'testReadAnnotationNoLowercase'
- );
-
- $this->assertTrue($reader->hasAnnotation('Annotation'));
- $this->assertFalse($reader->hasAnnotation('param'));
- }
-
- /**
- * @Annotation
- * @param int $test
- */
- public function testReadTypeIntAnnotations(): void {
- $reader = new ControllerMethodReflector(Server::get(LoggerInterface::class));
- $reader->reflect(
- '\Test\AppFramework\Utility\ControllerMethodReflectorTest',
- 'testReadTypeIntAnnotations'
- );
-
- $this->assertEquals('int', $reader->getType('test'));
- }
-
- /**
- * @Annotation
- * @param int $a
- * @param int $b
- */
- public function arguments3($a, float $b, int $c, $d) {
- }
-
- public function testReadTypeIntAnnotationsScalarTypes(): void {
- $reader = new ControllerMethodReflector(Server::get(LoggerInterface::class));
- $reader->reflect(
- '\Test\AppFramework\Utility\ControllerMethodReflectorTest',
- 'arguments3'
- );
-
- $this->assertEquals('int', $reader->getType('a'));
- $this->assertEquals('float', $reader->getType('b'));
- $this->assertEquals('int', $reader->getType('c'));
- $this->assertNull($reader->getType('d'));
- }
-
- /**
- * @Annotation
- * @param double $test something special
- */
- public function testReadTypeDoubleAnnotations(): void {
- $reader = new ControllerMethodReflector(Server::get(LoggerInterface::class));
- $reader->reflect(
- '\Test\AppFramework\Utility\ControllerMethodReflectorTest',
- 'testReadTypeDoubleAnnotations'
- );
-
- $this->assertEquals('double', $reader->getType('test'));
- }
-
- /**
- * @Annotation
- * @param string $foo
- */
- public function testReadTypeWhitespaceAnnotations(): void {
- $reader = new ControllerMethodReflector(Server::get(LoggerInterface::class));
- $reader->reflect(
- '\Test\AppFramework\Utility\ControllerMethodReflectorTest',
- 'testReadTypeWhitespaceAnnotations'
- );
-
- $this->assertEquals('string', $reader->getType('foo'));
- }
-
- public function arguments($arg, $arg2 = 'hi') {
- }
- public function testReflectParameters(): void {
- $reader = new ControllerMethodReflector(Server::get(LoggerInterface::class));
- $reader->reflect(
- '\Test\AppFramework\Utility\ControllerMethodReflectorTest',
- 'arguments'
- );
-
- $this->assertEquals(['arg' => null, 'arg2' => 'hi'], $reader->getParameters());
- }
-
- public function arguments2($arg) {
- }
- public function testReflectParameters2(): void {
- $reader = new ControllerMethodReflector(Server::get(LoggerInterface::class));
- $reader->reflect(
- '\Test\AppFramework\Utility\ControllerMethodReflectorTest',
- 'arguments2'
- );
-
- $this->assertEquals(['arg' => null], $reader->getParameters());
- }
-
- public function testInheritance(): void {
- $reader = new ControllerMethodReflector(Server::get(LoggerInterface::class));
- $reader->reflect('Test\AppFramework\Utility\EndController', 'test');
-
- $this->assertTrue($reader->hasAnnotation('Annotation'));
- }
-
- public function testInheritanceOverride(): void {
- $reader = new ControllerMethodReflector(Server::get(LoggerInterface::class));
- $reader->reflect('Test\AppFramework\Utility\EndController', 'test2');
-
- $this->assertTrue($reader->hasAnnotation('NoAnnotation'));
- $this->assertFalse($reader->hasAnnotation('Annotation'));
- }
-
- public function testInheritanceOverrideNoDocblock(): void {
- $reader = new ControllerMethodReflector(Server::get(LoggerInterface::class));
- $reader->reflect('Test\AppFramework\Utility\EndController', 'test3');
-
- $this->assertFalse($reader->hasAnnotation('Annotation'));
- }
-
- public function testRangeDetectionPsalm(): void {
- $reader = new ControllerMethodReflector(Server::get(LoggerInterface::class));
- $reader->reflect('Test\AppFramework\Utility\EndController', 'test4');
-
- $rangeInfo1 = $reader->getRange('rangedOne');
- $this->assertSame(-4, $rangeInfo1['min']);
- $this->assertSame(42, $rangeInfo1['max']);
-
- $rangeInfo2 = $reader->getRange('rangedTwo');
- $this->assertSame(PHP_INT_MIN, $rangeInfo2['min']);
- $this->assertSame(PHP_INT_MAX, $rangeInfo2['max']);
-
- $rangeInfo3 = $reader->getRange('rangedThree');
- $this->assertSame(1, $rangeInfo3['min']);
- $this->assertSame(6, $rangeInfo3['max']);
-
- $rangeInfo3 = $reader->getRange('rangedFour');
- $this->assertSame(-70, $rangeInfo3['min']);
- $this->assertSame(-30, $rangeInfo3['max']);
- }
-
- public function testRangeDetectionNative(): void {
- $reader = new ControllerMethodReflector(Server::get(LoggerInterface::class));
- $reader->reflect('Test\AppFramework\Utility\EndController', 'test5');
-
- $rangeInfo1 = $reader->getRange('rangedOne');
- $this->assertSame(-4, $rangeInfo1['min']);
- $this->assertSame(42, $rangeInfo1['max']);
-
- $rangeInfo2 = $reader->getRange('rangedTwo');
- $this->assertSame(PHP_INT_MIN, $rangeInfo2['min']);
- $this->assertSame(PHP_INT_MAX, $rangeInfo2['max']);
-
- $rangeInfo3 = $reader->getRange('rangedThree');
- $this->assertSame(1, $rangeInfo3['min']);
- $this->assertSame(6, $rangeInfo3['max']);
-
- $rangeInfo3 = $reader->getRange('rangedFour');
- $this->assertSame(-70, $rangeInfo3['min']);
- $this->assertSame(-30, $rangeInfo3['max']);
- }
-
- public function testRangeDetectionIntAliases(): void {
- $reader = new ControllerMethodReflector(Server::get(LoggerInterface::class));
- $reader->reflect('Test\AppFramework\Utility\EndController', 'test6');
-
- $positive = $reader->getRange('positive');
- $this->assertSame(1, $positive['min']);
- $this->assertSame(PHP_INT_MAX, $positive['max']);
-
- $nonNegative = $reader->getRange('nonNegative');
- $this->assertSame(0, $nonNegative['min']);
- $this->assertSame(PHP_INT_MAX, $nonNegative['max']);
-
- $negative = $reader->getRange('negative');
- $this->assertSame(PHP_INT_MIN, $negative['min']);
- $this->assertSame(-1, $negative['max']);
-
- $nonPositive = $reader->getRange('nonPositive');
- $this->assertSame(PHP_INT_MIN, $nonPositive['min']);
- $this->assertSame(0, $nonPositive['max']);
-
- $positiveOrNull = $reader->getRange('positiveOrNull');
- $this->assertSame(1, $positiveOrNull['min']);
- $this->assertSame(PHP_INT_MAX, $positiveOrNull['max']);
-
- $nonNegativeOrNull = $reader->getRange('nonNegativeOrNull');
- $this->assertSame(0, $nonNegativeOrNull['min']);
- $this->assertSame(PHP_INT_MAX, $nonNegativeOrNull['max']);
- }
-
- public function testStringConstraintDetection(): void {
- $reader = new ControllerMethodReflector(Server::get(LoggerInterface::class));
- $reader->reflect('Test\AppFramework\Utility\EndController', 'test7');
-
- $this->assertSame('non-empty-string', $reader->getStringConstraint('nonEmpty'));
- $this->assertSame('non-empty-lowercase-string', $reader->getStringConstraint('nonEmptyLowercase'));
- $this->assertSame('lowercase-string', $reader->getStringConstraint('lowercase'));
- $this->assertSame('non-falsy-string', $reader->getStringConstraint('nonFalsy'));
- $this->assertSame('numeric-string', $reader->getStringConstraint('numeric'));
- $this->assertSame('non-empty-string', $reader->getStringConstraint('nonEmptyOrNull'));
- $this->assertSame('non-empty-string', $reader->getStringConstraint('nonEmptyOrNullPrefix'));
- $this->assertNull($reader->getStringConstraint('plain'));
- }
-
- public static function stringConstraintDataProvider(): array {
- return [
- ['non-empty-string', '', false],
- ['non-empty-string', 'a', true],
- ['non-empty-lowercase-string', '', false],
- ['non-empty-lowercase-string', 'ABC', false],
- ['non-empty-lowercase-string', 'abc', true],
- ['lowercase-string', '', true],
- ['lowercase-string', 'ABC', false],
- ['lowercase-string', 'abc', true],
- ['non-falsy-string', '', false],
- ['non-falsy-string', '0', false],
- ['non-falsy-string', '0.0', true],
- ['non-falsy-string', 'a', true],
- ['numeric-string', 'abc', false],
- ['numeric-string', '42', true],
- ['numeric-string', '4.2', true],
- [null, '', true],
- [null, 'anything', true],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('stringConstraintDataProvider')]
- public function testSatisfiesStringConstraint(?string $constraint, string $value, bool $expected): void {
- $reader = new ControllerMethodReflector(Server::get(LoggerInterface::class));
- $reader->reflect('Test\AppFramework\Utility\EndController', 'test7');
-
- $parameter = match ($constraint) {
- 'non-empty-string' => 'nonEmpty',
- 'non-empty-lowercase-string' => 'nonEmptyLowercase',
- 'lowercase-string' => 'lowercase',
- 'non-falsy-string' => 'nonFalsy',
- 'numeric-string' => 'numeric',
- default => 'plain',
- };
-
- $this->assertSame($expected, $reader->satisfiesStringConstraint($parameter, $value));
- }
-}
--- a/tests/lib/AppFramework/Utility/SimpleContainerTest.php
+++ /dev/null
@@ -1,250 +0,0 @@
-container = new SimpleContainer();
- }
-
- public function testRegister(): void {
- $this->container->registerParameter('test', 'abc');
- $this->assertEquals('abc', $this->container->query('test'));
- }
-
- /**
- * Test querying a class that is not registered without autoload enabled
- */
- public function testNothingRegistered(): void {
- try {
- $this->container->query('something really hard', false);
- $this->fail('Expected `QueryException` exception was not thrown');
- } catch (\Throwable $exception) {
- $this->assertInstanceOf(QueryException::class, $exception);
- $this->assertInstanceOf(NotFoundExceptionInterface::class, $exception);
- }
- }
-
- /**
- * Test querying a class that is not registered with autoload enabled
- */
- public function testNothingRegistered_autoload(): void {
- try {
- $this->container->query('something really hard');
- $this->fail('Expected `QueryException` exception was not thrown');
- } catch (\Throwable $exception) {
- $this->assertInstanceOf(QueryException::class, $exception);
- $this->assertInstanceOf(NotFoundExceptionInterface::class, $exception);
- }
- }
-
- public function testNotAClass(): void {
- $this->expectException(QueryException::class);
-
- $this->container->query('Test\AppFramework\Utility\TestInterface');
- }
-
- public function testNoConstructorClass(): void {
- $object = $this->container->query('Test\AppFramework\Utility\ClassEmptyConstructor');
- $this->assertTrue($object instanceof ClassEmptyConstructor);
- }
-
- public function testInstancesOnlyOnce(): void {
- $object = $this->container->query('Test\AppFramework\Utility\ClassEmptyConstructor');
- $object2 = $this->container->query('Test\AppFramework\Utility\ClassEmptyConstructor');
- $this->assertSame($object, $object2);
- }
-
- public function testConstructorSimple(): void {
- $this->container->registerParameter('test', 'abc');
- $object = $this->container->query(
- 'Test\AppFramework\Utility\ClassSimpleConstructor'
- );
- $this->assertTrue($object instanceof ClassSimpleConstructor);
- $this->assertEquals('abc', $object->test);
- }
-
- public function testConstructorComplex(): void {
- $this->container->registerParameter('test', 'abc');
- $object = $this->container->query(
- 'Test\AppFramework\Utility\ClassComplexConstructor'
- );
- $this->assertTrue($object instanceof ClassComplexConstructor);
- $this->assertEquals('abc', $object->class->test);
- $this->assertEquals('abc', $object->test);
- }
-
- public function testConstructorComplexInterface(): void {
- $this->container->registerParameter('test', 'abc');
- $this->container->registerService(
- 'Test\AppFramework\Utility\IInterfaceConstructor', function ($c) {
- return $c->query('Test\AppFramework\Utility\ClassSimpleConstructor');
- });
- $object = $this->container->query(
- 'Test\AppFramework\Utility\ClassInterfaceConstructor'
- );
- $this->assertTrue($object instanceof ClassInterfaceConstructor);
- $this->assertEquals('abc', $object->class->test);
- $this->assertEquals('abc', $object->test);
- }
-
- public function testOverrideService(): void {
- $this->container->registerService(
- 'Test\AppFramework\Utility\IInterfaceConstructor', function ($c) {
- return $c->query('Test\AppFramework\Utility\ClassSimpleConstructor');
- });
- $this->container->registerService(
- 'Test\AppFramework\Utility\IInterfaceConstructor', function ($c) {
- return $c->query('Test\AppFramework\Utility\ClassEmptyConstructor');
- });
- $object = $this->container->query(
- 'Test\AppFramework\Utility\IInterfaceConstructor'
- );
- $this->assertTrue($object instanceof ClassEmptyConstructor);
- }
-
- public function testRegisterAliasParamter(): void {
- $this->container->registerParameter('test', 'abc');
- $this->container->registerAlias('test1', 'test');
- $this->assertEquals('abc', $this->container->query('test1'));
- }
-
- public function testRegisterAliasService(): void {
- $this->container->registerService('test', function () {
- return new \StdClass;
- }, true);
- $this->container->registerAlias('test1', 'test');
- $this->assertSame(
- $this->container->query('test'), $this->container->query('test'));
- $this->assertSame(
- $this->container->query('test1'), $this->container->query('test1'));
- $this->assertSame(
- $this->container->query('test'), $this->container->query('test1'));
- }
-
- public static function sanitizeNameProvider(): array {
- return [
- ['ABC\\Foo', 'ABC\\Foo'],
- ['\\ABC\\Foo', '\\ABC\\Foo'],
- ['\\ABC\\Foo', 'ABC\\Foo'],
- ['ABC\\Foo', '\\ABC\\Foo'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('sanitizeNameProvider')]
- public function testSanitizeName($register, $query): void {
- $this->container->registerService($register, function () {
- return 'abc';
- });
- $this->assertEquals('abc', $this->container->query($query));
- }
-
- public function testConstructorComplexNoTestParameterFound(): void {
- $this->expectException(QueryException::class);
-
- $object = $this->container->query(
- 'Test\AppFramework\Utility\ClassComplexConstructor'
- );
- /* Use the object to trigger DI on PHP >= 8.4 */
- get_object_vars($object);
- }
-
- public function testRegisterFactory(): void {
- $this->container->registerService('test', function () {
- return new \StdClass();
- }, false);
- $this->assertNotSame(
- $this->container->query('test'), $this->container->query('test'));
- }
-
- public function testRegisterAliasFactory(): void {
- $this->container->registerService('test', function () {
- return new \StdClass();
- }, false);
- $this->container->registerAlias('test1', 'test');
- $this->assertNotSame(
- $this->container->query('test'), $this->container->query('test'));
- $this->assertNotSame(
- $this->container->query('test1'), $this->container->query('test1'));
- $this->assertNotSame(
- $this->container->query('test'), $this->container->query('test1'));
- }
-
- public function testQueryUntypedNullable(): void {
- $this->expectException(QueryException::class);
-
- $object = $this->container->query(
- ClassNullableUntypedConstructorArg::class
- );
- /* Use the object to trigger DI on PHP >= 8.4 */
- get_object_vars($object);
- }
-
- public function testQueryTypedNullable(): void {
- /** @var ClassNullableTypedConstructorArg $service */
- $service = $this->container->query(ClassNullableTypedConstructorArg::class);
-
- self::assertNull($service->class);
- }
-}
--- a/tests/lib/AppFramework/Utility/TimeFactoryTest.php
+++ /dev/null
@@ -1,53 +0,0 @@
-timeFactory = new TimeFactory();
- }
-
- public function testNow(): void {
- $now = $this->timeFactory->now();
- self::assertSame('UTC', $now->getTimezone()->getName());
- }
-
- public function testNowWithTimeZone(): void {
- $timezone = new \DateTimeZone('Europe/Berlin');
- $withTimeZone = $this->timeFactory->withTimeZone($timezone);
-
- $now = $withTimeZone->now();
- self::assertSame('Europe/Berlin', $now->getTimezone()->getName());
- }
-
- public function testGetTimeZone(): void {
- $expected = new \DateTimeZone('Europe/Berlin');
- $actual = $this->timeFactory->getTimeZone('Europe/Berlin');
- self::assertEquals($expected, $actual);
- }
-
- public function testGetTimeZoneUTC(): void {
- $expected = new \DateTimeZone('UTC');
- $actual = $this->timeFactory->getTimeZone();
- self::assertEquals($expected, $actual);
- }
-
- public function testGetTimeZoneInvalid(): void {
- $this->expectException(\Exception::class);
- $this->timeFactory->getTimeZone('blubblub');
- }
-}
--- a/tests/lib/AppScriptSortTest.php
+++ /dev/null
@@ -1,125 +0,0 @@
-logger = $this->getMockBuilder(LoggerInterface::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- parent::setUp();
- }
-
- public function testSort(): void {
- $scripts = [
- 'first' => ['myFirstJSFile'],
- 'core' => [
- 'core/js/myFancyJSFile1',
- 'core/js/myFancyJSFile4',
- 'core/js/myFancyJSFile5',
- 'core/js/myFancyJSFile1',
- ],
- 'files' => ['files/js/myFancyJSFile2'],
- 'myApp5' => ['myApp5/js/myApp5JSFile'],
- 'myApp' => ['myApp/js/myFancyJSFile3'],
- 'myApp4' => ['myApp4/js/myApp4JSFile'],
- 'myApp3' => ['myApp3/js/myApp3JSFile'],
- 'myApp2' => ['myApp2/js/myApp2JSFile'],
- ];
- $scriptDeps = [
- 'first' => new AppScriptDependency('first', ['core']),
- 'core' => new AppScriptDependency('core', ['core']),
- 'files' => new AppScriptDependency('files', ['core']),
- 'myApp5' => new AppScriptDependency('myApp5', ['myApp2']),
- 'myApp' => new AppScriptDependency('myApp', ['core']),
- 'myApp4' => new AppScriptDependency('myApp4', ['myApp3']),
- 'myApp3' => new AppScriptDependency('myApp3', ['myApp2']),
- 'myApp2' => new AppScriptDependency('myApp2', ['myApp']),
- ];
-
- // No circular dependency is detected and logged as an error
- $this->logger->expects(self::never())->method('error');
-
- $scriptSort = new AppScriptSort($this->logger);
- $sortedScripts = $scriptSort->sort($scripts, $scriptDeps);
-
- $sortedScriptKeys = array_keys($sortedScripts);
-
- // Core should appear first
- $this->assertEquals(
- 0,
- array_search('core', $sortedScriptKeys, true)
- );
-
- // Dependencies should appear before their children
- $this->assertLessThan(
- array_search('files', $sortedScriptKeys, true),
- array_search('core', $sortedScriptKeys, true)
- );
- $this->assertLessThan(
- array_search('myApp2', $sortedScriptKeys, true),
- array_search('myApp', $sortedScriptKeys, true)
- );
- $this->assertLessThan(
- array_search('myApp3', $sortedScriptKeys, true),
- array_search('myApp2', $sortedScriptKeys, true)
- );
- $this->assertLessThan(
- array_search('myApp4', $sortedScriptKeys, true),
- array_search('myApp3', $sortedScriptKeys, true)
- );
- $this->assertLessThan(
- array_search('myApp5', $sortedScriptKeys, true),
- array_search('myApp2', $sortedScriptKeys, true)
- );
-
- // All apps still there
- foreach ($scripts as $app => $_) {
- $this->assertContains($app, $sortedScriptKeys);
- }
- }
-
- public function testSortCircularDependency(): void {
- $scripts = [
- 'circular' => ['circular/js/file1'],
- 'dependency' => ['dependency/js/file2'],
- ];
- $scriptDeps = [
- 'circular' => new AppScriptDependency('circular', ['dependency']),
- 'dependency' => new AppScriptDependency('dependency', ['circular']),
- ];
-
- // A circular dependency is detected and logged as an error
- $this->logger->expects(self::once())->method('error');
-
- $scriptSort = new AppScriptSort($this->logger);
- $sortedScripts = $scriptSort->sort($scripts, $scriptDeps);
-
- $sortedScriptKeys = array_keys($sortedScripts);
-
- // All apps still there
- foreach ($scripts as $app => $_) {
- $this->assertContains($app, $sortedScriptKeys);
- }
- }
-}
--- a/tests/lib/AppTest.php
+++ /dev/null
@@ -1,333 +0,0 @@
-createUser(self::TEST_USER1, 'NotAnEasyPassword123456+');
- self::$user2 = $userManager->createUser(self::TEST_USER2, 'NotAnEasyPassword123456_');
- self::$user3 = $userManager->createUser(self::TEST_USER3, 'NotAnEasyPassword123456?');
-
- self::$group1 = $groupManager->createGroup(self::TEST_GROUP1);
- self::$group1->addUser(self::$user1);
- self::$group1->addUser(self::$user3);
- self::$group2 = $groupManager->createGroup(self::TEST_GROUP2);
- self::$group2->addUser(self::$user2);
- self::$group2->addUser(self::$user3);
- }
-
- public static function tearDownAfterClass(): void {
- self::$user1->delete();
- self::$user2->delete();
- self::$user3->delete();
-
- self::$group1->delete();
- self::$group2->delete();
- }
-
- /**
- * Tests that the app order is correct
- */
- public function testGetEnabledAppsIsSorted(): void {
- $apps = \OC_App::getEnabledApps();
- // copy array
- $sortedApps = $apps;
- sort($sortedApps);
- // 'files' is always on top
- unset($sortedApps[array_search('files', $sortedApps)]);
- array_unshift($sortedApps, 'files');
- $this->assertEquals($sortedApps, $apps);
- }
-
- /**
- * Providers for the app config values
- */
- public static function appConfigValuesProvider(): array {
- return [
- // logged in user1
- [
- self::TEST_USER1,
- [
- 'files',
- 'app1',
- 'app3',
- 'appforgroup1',
- 'appforgroup12',
- 'appstore',
- 'cloud_federation_api',
- 'dav',
- 'federatedfilesharing',
- 'lookup_server_connector',
- 'oauth2',
- 'profile',
- 'provisioning_api',
- 'settings',
- 'sharing',
- 'theming',
- 'twofactor_backupcodes',
- 'viewer',
- 'workflowengine',
- ],
- false
- ],
- // logged in user2
- [
- self::TEST_USER2,
- [
- 'files',
- 'app1',
- 'app3',
- 'appforgroup12',
- 'appforgroup2',
- 'appstore',
- 'cloud_federation_api',
- 'dav',
- 'federatedfilesharing',
- 'lookup_server_connector',
- 'oauth2',
- 'profile',
- 'provisioning_api',
- 'settings',
- 'sharing',
- 'theming',
- 'twofactor_backupcodes',
- 'viewer',
- 'workflowengine',
- ],
- false
- ],
- // logged in user3
- [
- self::TEST_USER3,
- [
- 'files',
- 'app1',
- 'app3',
- 'appforgroup1',
- 'appforgroup12',
- 'appforgroup2',
- 'appstore',
- 'cloud_federation_api',
- 'dav',
- 'federatedfilesharing',
- 'lookup_server_connector',
- 'oauth2',
- 'profile',
- 'provisioning_api',
- 'settings',
- 'sharing',
- 'theming',
- 'twofactor_backupcodes',
- 'viewer',
- 'workflowengine',
- ],
- false
- ],
- // no user, returns all apps
- [
- null,
- [
- 'files',
- 'app1',
- 'app3',
- 'appforgroup1',
- 'appforgroup12',
- 'appforgroup2',
- 'appstore',
- 'cloud_federation_api',
- 'dav',
- 'federatedfilesharing',
- 'lookup_server_connector',
- 'oauth2',
- 'profile',
- 'provisioning_api',
- 'settings',
- 'sharing',
- 'theming',
- 'twofactor_backupcodes',
- 'viewer',
- 'workflowengine',
- ],
- false,
- ],
- // user given, but ask for all
- [
- self::TEST_USER1,
- [
- 'files',
- 'app1',
- 'app3',
- 'appforgroup1',
- 'appforgroup12',
- 'appforgroup2',
- 'appstore',
- 'cloud_federation_api',
- 'dav',
- 'federatedfilesharing',
- 'lookup_server_connector',
- 'oauth2',
- 'profile',
- 'provisioning_api',
- 'settings',
- 'sharing',
- 'theming',
- 'twofactor_backupcodes',
- 'viewer',
- 'workflowengine',
- ],
- true,
- ],
- ];
- }
-
- /**
- * Test enabled apps
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('appConfigValuesProvider')]
- public function testEnabledApps($userId, $expectedApps, $forceAll): void {
- $userSession = Server::get(IUserSession::class);
-
- $user = match ($userId) {
- self::TEST_USER1 => self::$user1,
- self::TEST_USER2 => self::$user2,
- self::TEST_USER3 => self::$user3,
- default => null,
- };
-
- $userSession->setUser($user);
-
- $this->setupAppConfigMock()->expects($this->once())
- ->method('searchValues')
- ->willReturn(
- [
- 'app3' => 'yes',
- 'app2' => 'no',
- 'app1' => 'yes',
- 'appforgroup1' => '["group1"]',
- 'appforgroup2' => '["group2"]',
- 'appforgroup12' => '["group2","group1"]',
- ]
- );
-
- $apps = \OC_App::getEnabledApps(false, $forceAll);
-
- $this->restoreAppConfig();
- $userSession->setUser(null);
-
- $this->assertEquals($expectedApps, $apps);
- }
-
- /**
- * Test isEnabledApps() with cache, not re-reading the list of
- * enabled apps more than once when a user is set.
- */
- public function testEnabledAppsCache(): void {
- $userSession = Server::get(IUserSession::class);
- $userSession->setUser(self::$user1);
-
- $this->setupAppConfigMock()->expects($this->once())
- ->method('searchValues')
- ->willReturn(
- [
- 'app3' => 'yes',
- 'app2' => 'no',
- ]
- );
-
- $apps = \OC_App::getEnabledApps();
- $this->assertEquals(['files', 'app3', 'appstore', 'cloud_federation_api', 'dav', 'federatedfilesharing', 'lookup_server_connector', 'oauth2', 'profile', 'provisioning_api', 'settings', 'sharing', 'theming', 'twofactor_backupcodes', 'viewer', 'workflowengine'], $apps);
-
- // mock should not be called again here
- $apps = \OC_App::getEnabledApps();
- $this->assertEquals(['files', 'app3', 'appstore', 'cloud_federation_api', 'dav', 'federatedfilesharing', 'lookup_server_connector', 'oauth2', 'profile', 'provisioning_api', 'settings', 'sharing', 'theming', 'twofactor_backupcodes', 'viewer', 'workflowengine'], $apps);
-
- $this->restoreAppConfig();
- $userSession->setUser(null);
- }
-
- private function setupAppConfigMock() {
- $appConfig = $this->getMockBuilder(AppConfig::class)
- ->onlyMethods(['searchValues'])
- ->setConstructorArgs([Server::get(IDBConnection::class)])
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->registerAppConfig($appConfig);
- return $appConfig;
- }
-
- /**
- * Register an app config mock for testing purposes.
- *
- * @param IAppConfig $appConfig app config mock
- */
- private function registerAppConfig(AppConfig $appConfig) {
- $this->overwriteService(AppConfig::class, $appConfig);
- $this->overwriteService(AppManager::class, new AppManager(
- Server::get(IUserSession::class),
- Server::get(IConfig::class),
- Server::get(IGroupManager::class),
- Server::get(ICacheFactory::class),
- Server::get(IEventDispatcher::class),
- Server::get(LoggerInterface::class),
- Server::get(ServerVersion::class),
- Server::get(ConfigManager::class),
- Server::get(DependencyAnalyzer::class),
- ));
- }
-
- /**
- * Restore the original app config service.
- */
- private function restoreAppConfig() {
- $this->restoreService(AppConfig::class);
- $this->restoreService(AppManager::class);
-
- // Remove the cache of the mocked apps list with a forceRefresh
- \OC_App::getEnabledApps();
- }
-}
--- a/tests/lib/Archive/TARTest.php
+++ /dev/null
@@ -1,26 +0,0 @@
-getTemporaryFile('.tar.gz'));
- }
-}
--- a/tests/lib/Archive/TestBase.php
+++ /dev/null
@@ -1,147 +0,0 @@
-instance = $this->getExisting();
- $allFiles = $this->instance->getFiles();
- $expected = ['lorem.txt','logo-wide.png','dir/', 'dir/lorem.txt'];
- $this->assertEquals(4, count($allFiles), 'only found ' . count($allFiles) . ' out of 4 expected files');
- foreach ($expected as $file) {
- $this->assertContains($file, $allFiles, 'cant find ' . $file . ' in archive');
- $this->assertTrue($this->instance->fileExists($file), 'file ' . $file . ' does not exist in archive');
- }
- $this->assertFalse($this->instance->fileExists('non/existing/file'));
-
- $rootContent = $this->instance->getFolder('');
- $expected = ['lorem.txt','logo-wide.png', 'dir/'];
- $this->assertEquals(3, count($rootContent));
- foreach ($expected as $file) {
- $this->assertContains($file, $rootContent, 'cant find ' . $file . ' in archive');
- }
-
- $dirContent = $this->instance->getFolder('dir/');
- $expected = ['lorem.txt'];
- $this->assertEquals(1, count($dirContent));
- foreach ($expected as $file) {
- $this->assertContains($file, $dirContent, 'cant find ' . $file . ' in archive');
- }
- }
-
- public function testContent(): void {
- $this->instance = $this->getExisting();
- $dir = \OC::$SERVERROOT . '/tests/data';
- $textFile = $dir . '/lorem.txt';
- $this->assertEquals(file_get_contents($textFile), $this->instance->getFile('lorem.txt'));
-
- $tmpFile = Server::get(ITempManager::class)->getTemporaryFile('.txt');
- $this->instance->extractFile('lorem.txt', $tmpFile);
- $this->assertEquals(file_get_contents($textFile), file_get_contents($tmpFile));
- }
-
- public function testWrite(): void {
- $dir = \OC::$SERVERROOT . '/tests/data';
- $textFile = $dir . '/lorem.txt';
- $this->instance = $this->getNew();
- $this->assertEquals(0, count($this->instance->getFiles()));
- $this->instance->addFile('lorem.txt', $textFile);
- $this->assertEquals(1, count($this->instance->getFiles()));
- $this->assertTrue($this->instance->fileExists('lorem.txt'));
- $this->assertFalse($this->instance->fileExists('lorem.txt/'));
-
- $this->assertEquals(file_get_contents($textFile), $this->instance->getFile('lorem.txt'));
- $this->instance->addFile('lorem.txt', 'foobar');
- $this->assertEquals('foobar', $this->instance->getFile('lorem.txt'));
- }
-
- public function testReadStream(): void {
- $dir = \OC::$SERVERROOT . '/tests/data';
- $this->instance = $this->getExisting();
- $fh = $this->instance->getStream('lorem.txt', 'r');
- $this->assertTrue((bool)$fh);
- $content = fread($fh, $this->instance->filesize('lorem.txt'));
- fclose($fh);
- $this->assertEquals(file_get_contents($dir . '/lorem.txt'), $content);
- }
- public function testWriteStream(): void {
- $dir = \OC::$SERVERROOT . '/tests/data';
- $this->instance = $this->getNew();
- $fh = $this->instance->getStream('lorem.txt', 'w');
- $source = fopen($dir . '/lorem.txt', 'r');
- $result = stream_copy_to_stream($source, $fh);
- $this->assertNotFalse($result);
- fclose($source);
- fclose($fh);
- $this->assertTrue($this->instance->fileExists('lorem.txt'));
- $this->assertEquals(file_get_contents($dir . '/lorem.txt'), $this->instance->getFile('lorem.txt'));
- }
- public function testFolder(): void {
- $this->instance = $this->getNew();
- $this->assertFalse($this->instance->fileExists('/test'));
- $this->assertFalse($this->instance->fileExists('/test/'));
- $this->instance->addFolder('/test');
- $this->assertTrue($this->instance->fileExists('/test'));
- $this->assertTrue($this->instance->fileExists('/test/'));
- $this->instance->remove('/test');
- $this->assertFalse($this->instance->fileExists('/test'));
- $this->assertFalse($this->instance->fileExists('/test/'));
- }
- public function testExtract(): void {
- $dir = \OC::$SERVERROOT . '/tests/data';
- $this->instance = $this->getExisting();
- $tmpDir = Server::get(ITempManager::class)->getTemporaryFolder();
- $this->instance->extract($tmpDir);
- $this->assertEquals(true, file_exists($tmpDir . 'lorem.txt'));
- $this->assertEquals(true, file_exists($tmpDir . 'dir/lorem.txt'));
- $this->assertEquals(true, file_exists($tmpDir . 'logo-wide.png'));
- $this->assertEquals(file_get_contents($dir . '/lorem.txt'), file_get_contents($tmpDir . 'lorem.txt'));
- Files::rmdirr($tmpDir);
- }
- public function testMoveRemove(): void {
- $dir = \OC::$SERVERROOT . '/tests/data';
- $textFile = $dir . '/lorem.txt';
- $this->instance = $this->getNew();
- $this->instance->addFile('lorem.txt', $textFile);
- $this->assertFalse($this->instance->fileExists('target.txt'));
- $this->instance->rename('lorem.txt', 'target.txt');
- $this->assertTrue($this->instance->fileExists('target.txt'));
- $this->assertFalse($this->instance->fileExists('lorem.txt'));
- $this->assertEquals(file_get_contents($textFile), $this->instance->getFile('target.txt'));
- $this->instance->remove('target.txt');
- $this->assertFalse($this->instance->fileExists('target.txt'));
- }
- public function testRecursive(): void {
- $dir = \OC::$SERVERROOT . '/tests/data/themes';
- $this->instance = $this->getNew();
- $this->instance->addRecursive('/dir', $dir);
- $this->assertTrue($this->instance->fileExists('/dir/abc/apps/files/l10n/zz.json'));
- }
-}
--- a/tests/lib/Archive/ZIPTest.php
+++ /dev/null
@@ -1,30 +0,0 @@
-getTempBaseDir() . '/newArchive.zip';
- if (file_exists($newZip)) {
- unlink($newZip);
- }
- return new ZIP($newZip);
- }
-}
--- a/tests/lib/Authentication/Events/RemoteWipeFinishedTest.php
+++ /dev/null
@@ -1,23 +0,0 @@
-createMock(IToken::class);
- $event = new RemoteWipeFinished($token);
-
- $this->assertSame($token, $event->getToken());
- }
-}
--- a/tests/lib/Authentication/Events/RemoteWipeStartedTest.php
+++ /dev/null
@@ -1,23 +0,0 @@
-createMock(IToken::class);
- $event = new RemoteWipeStarted($token);
-
- $this->assertSame($token, $event->getToken());
- }
-}
--- a/tests/lib/Authentication/Listeners/RemoteWipeActivityListenerTest.php
+++ /dev/null
@@ -1,138 +0,0 @@
-activityManager = $this->createMock(IActivityManager::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $this->listener = new RemoteWipeActivityListener(
- $this->activityManager,
- $this->logger
- );
- }
-
- public function testHandleUnrelated(): void {
- $event = new Event();
-
- $this->listener->handle($event);
-
- $this->addToAssertionCount(1);
- }
-
- public function testHandleRemoteWipeStarted(): void {
- /** @var IToken|MockObject $token */
- $token = $this->createMock(IToken::class);
- $event = new RemoteWipeStarted($token);
- $activityEvent = $this->createMock(IActivityEvent::class);
- $this->activityManager->expects($this->once())
- ->method('generateEvent')
- ->willReturn($activityEvent);
- $activityEvent->expects($this->once())
- ->method('setApp')
- ->with('core')
- ->willReturnSelf();
- $activityEvent->expects($this->once())
- ->method('setType')
- ->with('security')
- ->willReturnSelf();
- $token->method('getUID')->willReturn('user123');
- $activityEvent->expects($this->once())
- ->method('setAuthor')
- ->with('user123')
- ->willReturnSelf();
- $activityEvent->expects($this->once())
- ->method('setAffectedUser')
- ->with('user123')
- ->willReturnSelf();
- $token->method('getName')->willReturn('Token 1');
- $activityEvent->expects($this->once())
- ->method('setSubject')
- ->with('remote_wipe_start', ['name' => 'Token 1'])
- ->willReturnSelf();
- $this->activityManager->expects($this->once())
- ->method('publish');
-
- $this->listener->handle($event);
- }
-
- public function testHandleRemoteWipeStartedCanNotPublish(): void {
- $token = $this->createMock(IToken::class);
- $event = new RemoteWipeStarted($token);
- $this->activityManager->expects($this->once())
- ->method('generateEvent');
- $this->activityManager->expects($this->once())
- ->method('publish')
- ->willThrowException(new \BadMethodCallException());
-
- $this->listener->handle($event);
- }
-
- public function testHandleRemoteWipeFinished(): void {
- /** @var IToken|MockObject $token */
- $token = $this->createMock(IToken::class);
- $event = new RemoteWipeFinished($token);
- $activityEvent = $this->createMock(IActivityEvent::class);
- $this->activityManager->expects($this->once())
- ->method('generateEvent')
- ->willReturn($activityEvent);
- $activityEvent->expects($this->once())
- ->method('setApp')
- ->with('core')
- ->willReturnSelf();
- $activityEvent->expects($this->once())
- ->method('setType')
- ->with('security')
- ->willReturnSelf();
- $token->method('getUID')->willReturn('user123');
- $activityEvent->expects($this->once())
- ->method('setAuthor')
- ->with('user123')
- ->willReturnSelf();
- $activityEvent->expects($this->once())
- ->method('setAffectedUser')
- ->with('user123')
- ->willReturnSelf();
- $token->method('getName')->willReturn('Token 1');
- $activityEvent->expects($this->once())
- ->method('setSubject')
- ->with('remote_wipe_finish', ['name' => 'Token 1'])
- ->willReturnSelf();
- $this->activityManager->expects($this->once())
- ->method('publish');
-
- $this->listener->handle($event);
- }
-}
--- a/tests/lib/Authentication/Listeners/RemoteWipeEmailListenerTest.php
+++ /dev/null
@@ -1,225 +0,0 @@
-mailer = $this->createMock(IMailer::class);
- $this->userManager = $this->createMock(IUserManager::class);
- $this->l10nFactory = $this->createMock(IFactory::class);
- $this->l10n = $this->createMock(IL10N::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $this->l10nFactory->method('get')->with('core')->willReturn($this->l10n);
- $this->l10n->method('t')->willReturnArgument(0);
-
- $this->listener = new RemoteWipeEmailListener(
- $this->mailer,
- $this->userManager,
- $this->l10nFactory,
- $this->logger
- );
- }
-
- public function testHandleUnrelated(): void {
- $event = new Event();
- $this->mailer->expects($this->never())->method('send');
-
- $this->listener->handle($event);
- }
-
- public function testHandleRemoteWipeStartedInvalidUser(): void {
- /** @var IToken|MockObject $token */
- $token = $this->createMock(IToken::class);
- $event = new RemoteWipeStarted($token);
- $token->method('getUID')->willReturn('nope');
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('nope')
- ->willReturn(null);
- $this->mailer->expects($this->never())->method('send');
-
- $this->listener->handle($event);
- }
-
- public function testHandleRemoteWipeStartedNoEmailSet(): void {
- /** @var IToken|MockObject $token */
- $token = $this->createMock(IToken::class);
- $event = new RemoteWipeStarted($token);
- $token->method('getUID')->willReturn('nope');
- $user = $this->createMock(IUser::class);
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('nope')
- ->willReturn($user);
- $user->method('getEMailAddress')->willReturn(null);
- $this->mailer->expects($this->never())->method('send');
-
- $this->listener->handle($event);
- }
-
- public function testHandleRemoteWipeStartedTransmissionError(): void {
- /** @var IToken|MockObject $token */
- $token = $this->createMock(IToken::class);
- $event = new RemoteWipeStarted($token);
- $token->method('getUID')->willReturn('nope');
- $user = $this->createMock(IUser::class);
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('nope')
- ->willReturn($user);
- $user->method('getEMailAddress')->willReturn('user@domain.org');
- $this->mailer->expects($this->once())
- ->method('send')
- ->willThrowException(new Exception());
- $this->logger->expects($this->once())
- ->method('error');
-
- $this->listener->handle($event);
- }
-
- public function testHandleRemoteWipeStarted(): void {
- /** @var IToken|MockObject $token */
- $token = $this->createMock(IToken::class);
- $event = new RemoteWipeStarted($token);
- $token->method('getUID')->willReturn('nope');
- $user = $this->createMock(IUser::class);
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('nope')
- ->willReturn($user);
- $user->method('getEMailAddress')->willReturn('user@domain.org');
- $message = $this->createMock(IMessage::class);
- $this->mailer->expects($this->once())
- ->method('createMessage')
- ->willReturn($message);
- $message->expects($this->once())
- ->method('setTo')
- ->with($this->equalTo(['user@domain.org']));
- $this->mailer->expects($this->once())
- ->method('send')
- ->with($message);
-
- $this->listener->handle($event);
- }
-
- public function testHandleRemoteWipeFinishedInvalidUser(): void {
- /** @var IToken|MockObject $token */
- $token = $this->createMock(IToken::class);
- $event = new RemoteWipeFinished($token);
- $token->method('getUID')->willReturn('nope');
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('nope')
- ->willReturn(null);
- $this->mailer->expects($this->never())->method('send');
-
- $this->listener->handle($event);
- }
-
- public function testHandleRemoteWipeFinishedNoEmailSet(): void {
- /** @var IToken|MockObject $token */
- $token = $this->createMock(IToken::class);
- $event = new RemoteWipeFinished($token);
- $token->method('getUID')->willReturn('nope');
- $user = $this->createMock(IUser::class);
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('nope')
- ->willReturn($user);
- $user->method('getEMailAddress')->willReturn(null);
- $this->mailer->expects($this->never())->method('send');
-
- $this->listener->handle($event);
- }
-
- public function testHandleRemoteWipeFinishedTransmissionError(): void {
- /** @var IToken|MockObject $token */
- $token = $this->createMock(IToken::class);
- $event = new RemoteWipeFinished($token);
- $token->method('getUID')->willReturn('nope');
- $user = $this->createMock(IUser::class);
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('nope')
- ->willReturn($user);
- $user->method('getEMailAddress')->willReturn('user@domain.org');
- $this->mailer->expects($this->once())
- ->method('send')
- ->willThrowException(new Exception());
- $this->logger->expects($this->once())
- ->method('error');
-
- $this->listener->handle($event);
- }
-
- public function testHandleRemoteWipeFinished(): void {
- /** @var IToken|MockObject $token */
- $token = $this->createMock(IToken::class);
- $event = new RemoteWipeFinished($token);
- $token->method('getUID')->willReturn('nope');
- $user = $this->createMock(IUser::class);
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('nope')
- ->willReturn($user);
- $user->method('getEMailAddress')->willReturn('user@domain.org');
- $message = $this->createMock(IMessage::class);
- $this->mailer->expects($this->once())
- ->method('createMessage')
- ->willReturn($message);
- $message->expects($this->once())
- ->method('setTo')
- ->with($this->equalTo(['user@domain.org']));
- $this->mailer->expects($this->once())
- ->method('send')
- ->with($message);
-
- $this->listener->handle($event);
- }
-}
--- a/tests/lib/Authentication/Listeners/RemoteWipeNotificationsListenerTest.php
+++ /dev/null
@@ -1,135 +0,0 @@
-notificationManager = $this->createMock(INotificationManager::class);
- $this->timeFactory = $this->createMock(ITimeFactory::class);
-
- $this->listener = new RemoteWipeNotificationsListener(
- $this->notificationManager,
- $this->timeFactory
- );
- }
-
- public function testHandleUnrelated(): void {
- $event = new Event();
-
- $this->listener->handle($event);
-
- $this->addToAssertionCount(1);
- }
-
- public function testHandleRemoteWipeStarted(): void {
- $token = $this->createMock(IToken::class);
- $event = new RemoteWipeStarted($token);
- $notification = $this->createMock(INotification::class);
- $this->notificationManager->expects($this->once())
- ->method('createNotification')
- ->willReturn($notification);
- $notification->expects($this->once())
- ->method('setApp')
- ->with('auth')
- ->willReturnSelf();
- $token->method('getUID')->willReturn('user123');
- $notification->expects($this->once())
- ->method('setUser')
- ->with('user123')
- ->willReturnSelf();
- $now = new DateTime();
- $this->timeFactory->method('getDateTime')->willReturn($now);
- $notification->expects($this->once())
- ->method('setDateTime')
- ->with($now)
- ->willReturnSelf();
- $token->method('getId')->willReturn(123);
- $notification->expects($this->once())
- ->method('setObject')
- ->with('token', '123')
- ->willReturnSelf();
- $token->method('getName')->willReturn('Token 1');
- $notification->expects($this->once())
- ->method('setSubject')
- ->with('remote_wipe_start', [
- 'name' => 'Token 1'
- ])
- ->willReturnSelf();
- $this->notificationManager->expects($this->once())
- ->method('notify');
-
- $this->listener->handle($event);
- }
-
- public function testHandleRemoteWipeFinished(): void {
- $token = $this->createMock(IToken::class);
- $event = new RemoteWipeFinished($token);
- $notification = $this->createMock(INotification::class);
- $this->notificationManager->expects($this->once())
- ->method('createNotification')
- ->willReturn($notification);
- $notification->expects($this->once())
- ->method('setApp')
- ->with('auth')
- ->willReturnSelf();
- $token->method('getUID')->willReturn('user123');
- $notification->expects($this->once())
- ->method('setUser')
- ->with('user123')
- ->willReturnSelf();
- $now = new DateTime();
- $this->timeFactory->method('getDateTime')->willReturn($now);
- $notification->expects($this->once())
- ->method('setDateTime')
- ->with($now)
- ->willReturnSelf();
- $token->method('getId')->willReturn(123);
- $notification->expects($this->once())
- ->method('setObject')
- ->with('token', '123')
- ->willReturnSelf();
- $token->method('getName')->willReturn('Token 1');
- $notification->expects($this->once())
- ->method('setSubject')
- ->with('remote_wipe_finish', [
- 'name' => 'Token 1'
- ])
- ->willReturnSelf();
- $this->notificationManager->expects($this->once())
- ->method('notify');
-
- $this->listener->handle($event);
- }
-}
--- a/tests/lib/Authentication/Listeners/UserDeletedTokenCleanupListenerTest.php
+++ /dev/null
@@ -1,104 +0,0 @@
-manager = $this->createMock(Manager::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $this->listener = new UserDeletedTokenCleanupListener(
- $this->manager,
- $this->logger
- );
- }
-
- public function testHandleUnrelated(): void {
- $event = new Event();
- $this->manager->expects($this->never())->method('getTokenByUser');
- $this->logger->expects($this->never())->method('error');
-
- $this->listener->handle($event);
- }
-
- public function testHandleWithErrors(): void {
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('user123');
- $event = new UserDeletedEvent($user);
- $exception = new Exception('nope');
- $this->manager->expects($this->once())
- ->method('getTokenByUser')
- ->with('user123')
- ->willThrowException($exception);
- $this->logger->expects($this->once())
- ->method('error');
-
- $this->listener->handle($event);
- }
-
- public function testHandle(): void {
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('user123');
- $event = new UserDeletedEvent($user);
- $token1 = $this->createMock(IToken::class);
- $token1->method('getId')->willReturn(1);
- $token2 = $this->createMock(IToken::class);
- $token2->method('getId')->willReturn(2);
- $token3 = $this->createMock(IToken::class);
- $token3->method('getId')->willReturn(3);
- $this->manager->expects($this->once())
- ->method('getTokenByUser')
- ->with('user123')
- ->willReturn([
- $token1,
- $token2,
- $token3,
- ]);
-
- $calls = [
- ['user123', 1],
- ['user123', 2],
- ['user123', 3],
- ];
- $this->manager->expects($this->exactly(3))
- ->method('invalidateTokenById')
- ->willReturnCallback(function () use (&$calls): void {
- $expected = array_shift($calls);
- $this->assertEquals($expected, func_get_args());
- });
- $this->logger->expects($this->never())
- ->method('error');
-
- $this->listener->handle($event);
- }
-}
--- a/tests/lib/Authentication/Login/ALoginTestCommand.php
+++ /dev/null
@@ -1,107 +0,0 @@
-request = $this->createMock(IRequest::class);
- $this->user = $this->createMock(IUser::class);
- }
-
- protected function getBasicLoginData(): LoginData {
- return new LoginData(
- $this->request,
- $this->username,
- $this->password
- );
- }
-
- protected function getInvalidLoginData(): LoginData {
- return new LoginData(
- $this->request,
- $this->username,
- $this->password
- );
- }
-
- protected function getFailedLoginData(): LoginData {
- $data = new LoginData(
- $this->request,
- $this->username,
- $this->password
- );
- $data->setUser(false);
- return $data;
- }
-
- protected function getLoggedInLoginData(): LoginData {
- $basic = $this->getBasicLoginData();
- $basic->setUser($this->user);
- return $basic;
- }
-
- protected function getLoggedInLoginDataWithRedirectUrl(): LoginData {
- $data = new LoginData(
- $this->request,
- $this->username,
- $this->password,
- true,
- $this->redirectUrl
- );
- $data->setUser($this->user);
- return $data;
- }
-
- protected function getLoggedInLoginDataWithTimezone(?string $timezone = null): LoginData {
- $data = new LoginData(
- $this->request,
- $this->username,
- $this->password,
- true,
- null,
- $timezone ?? $this->timezone,
- $this->timeZoneOffset
- );
- $data->setUser($this->user);
- return $data;
- }
-}
--- a/tests/lib/Authentication/Login/ClearLostPasswordTokensCommandTest.php
+++ /dev/null
@@ -1,48 +0,0 @@
-config = $this->createMock(IConfig::class);
-
- $this->cmd = new ClearLostPasswordTokensCommand(
- $this->config
- );
- }
-
- public function testProcess(): void {
- $data = $this->getLoggedInLoginData();
- $this->user->expects($this->once())
- ->method('getUID')
- ->willReturn($this->username);
- $this->config->expects($this->once())
- ->method('deleteUserValue')
- ->with(
- $this->username,
- 'core',
- 'lostpassword'
- );
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- }
-}
--- a/tests/lib/Authentication/Login/CompleteLoginCommandTest.php
+++ /dev/null
@@ -1,49 +0,0 @@
-session = $this->createMock(Session::class);
-
- $this->cmd = new CompleteLoginCommand(
- $this->session
- );
- }
-
- public function testProcess(): void {
- $data = $this->getLoggedInLoginData();
- $this->session->expects($this->once())
- ->method('completeLogin')
- ->with(
- $this->user,
- $this->equalTo(
- [
- 'loginName' => $this->username,
- 'password' => $this->password,
- ]
- )
- );
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- }
-}
--- a/tests/lib/Authentication/Login/CreateSessionTokenCommandTest.php
+++ /dev/null
@@ -1,161 +0,0 @@
-config = $this->createMock(IConfig::class);
- $this->userSession = $this->createMock(Session::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->timeFactory = $this->createMock(ITimeFactory::class);
-
- $this->cmd = new CreateSessionTokenCommand(
- $this->config,
- $this->userSession,
- $this->urlGenerator,
- $this->timeFactory,
- );
- }
-
- public function testProcess(): void {
- // Just return the route name as path to not return an empty string
- $this->urlGenerator->expects(self::once())
- ->method('linkToRoute')
- ->willReturnArgument(0);
- $data = $this->getLoggedInLoginData();
- $this->config->expects($this->once())
- ->method('getSystemValueInt')
- ->with(
- 'remember_login_cookie_lifetime',
- 60 * 60 * 24 * 15
- )
- ->willReturn(100);
- $this->user->expects($this->any())
- ->method('getUID')
- ->willReturn($this->username);
- $this->userSession->expects($this->once())
- ->method('createSessionToken')
- ->with(
- $this->request,
- $this->username,
- $this->username,
- $this->password,
- IToken::REMEMBER,
- null
- );
- $this->userSession->expects($this->once())
- ->method('updateTokens')
- ->with(
- $this->username,
- $this->password
- );
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- }
-
- public function testProcessDoNotRemember(): void {
- // Just return the route name as path to not return an empty string
- $this->urlGenerator->expects(self::once())
- ->method('linkToRoute')
- ->willReturnArgument(0);
- $data = $this->getLoggedInLoginData();
- $this->config->expects($this->once())
- ->method('getSystemValueInt')
- ->with(
- 'remember_login_cookie_lifetime',
- 60 * 60 * 24 * 15
- )
- ->willReturn(0);
- $this->user->expects($this->any())
- ->method('getUID')
- ->willReturn($this->username);
- $this->userSession->expects($this->once())
- ->method('createSessionToken')
- ->with(
- $this->request,
- $this->username,
- $this->username,
- $this->password,
- IToken::DO_NOT_REMEMBER,
- null
- );
- $this->userSession->expects($this->once())
- ->method('updateTokens')
- ->with(
- $this->username,
- $this->password
- );
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- $this->assertFalse($data->isRememberLogin());
- }
-
- public function testLoginFlowEphemeral(): void {
- $this->redirectUrl = 'EPHEMERAL_ROUTE';
- $this->urlGenerator->expects(self::once())
- ->method('linkToRoute')
- ->willReturn($this->redirectUrl);
- $this->timeFactory->expects(self::once())
- ->method('getTime')
- ->willReturn(1000);
-
- $data = $this->getLoggedInLoginDataWithRedirectUrl();
- $this->config->expects($this->once())
- ->method('getSystemValueInt')
- ->with(
- 'remember_login_cookie_lifetime',
- 60 * 60 * 24 * 15
- )
- ->willReturn(100);
- $this->user->expects($this->any())
- ->method('getUID')
- ->willReturn($this->username);
- $this->userSession->expects($this->once())
- ->method('createSessionToken')
- ->with(
- $this->request,
- $this->username,
- $this->username,
- $this->password,
- IToken::REMEMBER,
- 1000 + 5 * 60
- );
- $this->userSession->expects($this->once())
- ->method('updateTokens')
- ->with(
- $this->username,
- $this->password
- );
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- }
-}
--- a/tests/lib/Authentication/Login/FinishRememberedLoginCommandTest.php
+++ /dev/null
@@ -1,75 +0,0 @@
-userSession = $this->createMock(Session::class);
- $this->config = $this->createMock(IConfig::class);
-
- $this->cmd = new FinishRememberedLoginCommand(
- $this->userSession,
- $this->config
- );
- }
-
- public function testProcessNotRememberedLogin(): void {
- $data = $this->getLoggedInLoginData();
- $data->setRememberLogin(false);
- $this->userSession->expects($this->never())
- ->method('createRememberMeToken');
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- }
-
- public function testProcess(): void {
- $data = $this->getLoggedInLoginData();
- $this->config->expects($this->once())
- ->method('getSystemValueBool')
- ->with('auto_logout', false)
- ->willReturn(false);
- $this->userSession->expects($this->once())
- ->method('createRememberMeToken')
- ->with($this->user);
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- }
-
- public function testProcessNotRemeberedLoginWithAutologout(): void {
- $data = $this->getLoggedInLoginData();
- $this->config->expects($this->once())
- ->method('getSystemValueBool')
- ->with('auto_logout', false)
- ->willReturn(true);
- $this->userSession->expects($this->never())
- ->method('createRememberMeToken');
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- }
-}
--- a/tests/lib/Authentication/Login/LoggedInCheckCommandTest.php
+++ /dev/null
@@ -1,56 +0,0 @@
-logger = $this->createMock(LoggerInterface::class);
- $this->dispatcher = $this->createMock(IEventDispatcher::class);
-
- $this->cmd = new LoggedInCheckCommand(
- $this->logger,
- $this->dispatcher
- );
- }
-
- public function testProcessSuccessfulLogin(): void {
- $data = $this->getLoggedInLoginData();
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- }
-
- public function testProcessFailedLogin(): void {
- $data = $this->getFailedLoginData();
- $this->logger->expects($this->once())
- ->method('warning');
-
- $result = $this->cmd->process($data);
-
- $this->assertFalse($result->isSuccess());
- $this->assertSame(LoginController::LOGIN_MSG_INVALIDPASSWORD, $result->getErrorMessage());
- }
-}
--- a/tests/lib/Authentication/Login/PreLoginHookCommandTest.php
+++ /dev/null
@@ -1,49 +0,0 @@
-userManager = $this->createMock(Manager::class);
-
- $this->cmd = new PreLoginHookCommand(
- $this->userManager
- );
- }
-
- public function testProcess(): void {
- $data = $this->getBasicLoginData();
- $this->userManager->expects($this->once())
- ->method('emit')
- ->with(
- '\OC\User',
- 'preLogin',
- [
- $this->username,
- $this->password,
- ]
- );
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- }
-}
--- a/tests/lib/Authentication/Login/RecordInteractiveLoginCommandTest.php
+++ /dev/null
@@ -1,41 +0,0 @@
-lastInteractiveLogin = $this->createMock(LastInteractiveLogin::class);
-
- $this->cmd = new RecordInteractiveLoginCommand(
- $this->lastInteractiveLogin
- );
- }
-
- public function testProcess(): void {
- $data = $this->getLoggedInLoginData();
- $this->lastInteractiveLogin->expects($this->once())
- ->method('record')
- ->with($this->user);
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- }
-}
--- a/tests/lib/Authentication/Login/SetUserTimezoneCommandTest.php
+++ /dev/null
@@ -1,146 +0,0 @@
-config = $this->createMock(IConfig::class);
- $this->session = $this->createMock(ISession::class);
-
- $this->cmd = new SetUserTimezoneCommand(
- $this->config,
- $this->session
- );
- }
-
- public function testProcessNoTimezoneSet(): void {
- $data = $this->getLoggedInLoginData();
- $this->config->expects($this->never())
- ->method('setUserValue');
- $this->session->expects($this->never())
- ->method('set');
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- }
-
- /**
- * Debian and Ubuntu ship the tz database's backward links in a separate
- * tzdata-legacy package, so pick an alias this platform actually knows
- * instead of hardcoding one.
- */
- private static function findBackwardCompatibleTimezone(): ?string {
- $aliases = array_diff(
- \DateTimeZone::listIdentifiers(\DateTimeZone::ALL_WITH_BC),
- \DateTimeZone::listIdentifiers(),
- );
- return $aliases === [] ? null : reset($aliases);
- }
-
- public static function dataAcceptedTimezone(): array {
- return [
- 'primary identifier' => ['Europe/Vienna'],
- 'backward compatible alias' => [self::findBackwardCompatibleTimezone()],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataAcceptedTimezone')]
- public function testProcess(?string $timezone): void {
- if ($timezone === null) {
- $this->markTestSkipped('No backward compatible timezone aliases in this platform\'s tz database');
- }
-
- $data = $this->getLoggedInLoginDataWithTimezone($timezone);
- $this->user->expects($this->once())
- ->method('getUID')
- ->willReturn($this->username);
- $this->config->expects($this->once())
- ->method('getUserValue')
- ->with(
- $this->username,
- 'core',
- 'timezone',
- ''
- )
- ->willReturn('');
- $this->config->expects($this->once())
- ->method('setUserValue')
- ->with(
- $this->username,
- 'core',
- 'timezone',
- $timezone
- );
- $this->session->expects($this->once())
- ->method('set')
- ->with(
- 'timezone',
- $this->timeZoneOffset
- );
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- }
-
- public function testProcessUnknownTimezone(): void {
- $data = $this->getLoggedInLoginDataWithTimezone('Mars/Olympus_Mons');
- $this->config->expects($this->never())
- ->method('setUserValue');
- $this->session->expects($this->never())
- ->method('set');
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- }
-
- public function testProcessAlreadySet(): void {
- $data = $this->getLoggedInLoginDataWithTimezone();
- $this->user->expects($this->once())
- ->method('getUID')
- ->willReturn($this->username);
- $this->config->expects($this->once())
- ->method('getUserValue')
- ->with(
- $this->username,
- 'core',
- 'timezone',
- '',
- )
- ->willReturn('Europe/Berlin');
- $this->config->expects($this->never())
- ->method('setUserValue');
- $this->session->expects($this->once())
- ->method('set')
- ->with(
- 'timezone',
- $this->timeZoneOffset
- );
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- }
-}
--- a/tests/lib/Authentication/Login/TwoFactorCommandTest.php
+++ /dev/null
@@ -1,377 +0,0 @@
-twoFactorManager = $this->createMock(Manager::class);
- $this->mandatoryTwoFactor = $this->createMock(MandatoryTwoFactor::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
-
- $this->cmd = new TwoFactorCommand(
- $this->twoFactorManager,
- $this->mandatoryTwoFactor,
- $this->urlGenerator
- );
- }
-
- public function testNotTwoFactorAuthenticated(): void {
- $data = $this->getLoggedInLoginData();
- $this->twoFactorManager->expects($this->once())
- ->method('isTwoFactorAuthenticated')
- ->willReturn(false);
- $this->twoFactorManager->expects($this->never())
- ->method('prepareTwoFactorLogin');
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- }
-
- public function testSkippedForVerifiedWebAuthnLogin(): void {
- $data = $this->getLoggedInLoginData();
- $data->setWebAuthnUserVerified(true);
- $this->twoFactorManager->expects($this->never())
- ->method('prepareTwoFactorLogin');
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- $this->assertNull($result->getRedirectUrl());
- }
-
- public function testNotSkippedForWebAuthnLoginWithoutUserVerification(): void {
- $data = $this->getLoggedInLoginData();
- $data->setWebAuthnUserVerified(false);
- $this->twoFactorManager->expects($this->once())
- ->method('isTwoFactorAuthenticated')
- ->willReturn(true);
- $this->twoFactorManager->expects($this->once())
- ->method('prepareTwoFactorLogin');
- $this->twoFactorManager->expects($this->once())
- ->method('getProviderSet')
- ->willReturn(new ProviderSet([], false));
- $this->twoFactorManager->expects($this->once())
- ->method('getLoginSetupProviders')
- ->willReturn([]);
- $this->urlGenerator->expects($this->once())
- ->method('linkToRoute')
- ->willReturn('two/factor/url');
-
- $result = $this->cmd->process($data);
-
- $this->assertEquals('two/factor/url', $result->getRedirectUrl());
- }
-
- public function testProcessOneActiveProvider(): void {
- $data = $this->getLoggedInLoginData();
- $this->twoFactorManager->expects($this->once())
- ->method('isTwoFactorAuthenticated')
- ->willReturn(true);
- $this->twoFactorManager->expects($this->once())
- ->method('prepareTwoFactorLogin')
- ->with(
- $this->user,
- $data->isRememberLogin()
- );
- $provider = $this->createMock(ITwoFactorAuthProvider::class);
- $this->twoFactorManager->expects($this->once())
- ->method('getProviderSet')
- ->willReturn(new ProviderSet([
- $provider,
- ], false));
- $this->twoFactorManager->expects($this->once())
- ->method('getLoginSetupProviders')
- ->with($this->user)
- ->willReturn([]);
- $this->mandatoryTwoFactor->expects($this->any())
- ->method('isEnforcedFor')
- ->with($this->user)
- ->willReturn(false);
- $provider->expects($this->once())
- ->method('getId')
- ->willReturn('test');
- $this->urlGenerator->expects($this->once())
- ->method('linkToRoute')
- ->with(
- 'core.TwoFactorChallenge.showChallenge',
- [
- 'challengeProviderId' => 'test'
- ]
- )
- ->willReturn('two/factor/url');
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- $this->assertEquals('two/factor/url', $result->getRedirectUrl());
- }
-
- public function testProcessMissingProviders(): void {
- $data = $this->getLoggedInLoginData();
- $this->twoFactorManager->expects($this->once())
- ->method('isTwoFactorAuthenticated')
- ->willReturn(true);
- $this->twoFactorManager->expects($this->once())
- ->method('prepareTwoFactorLogin')
- ->with(
- $this->user,
- $data->isRememberLogin()
- );
- $provider = $this->createMock(ITwoFactorAuthProvider::class);
- $provider->expects($this->once())
- ->method('getId')
- ->willReturn('test1');
- $this->twoFactorManager->expects($this->once())
- ->method('getProviderSet')
- ->willReturn(new ProviderSet([
- $provider,
- ], true));
- $this->twoFactorManager->expects($this->once())
- ->method('getLoginSetupProviders')
- ->with($this->user)
- ->willReturn([]);
- $this->mandatoryTwoFactor->expects($this->any())
- ->method('isEnforcedFor')
- ->with($this->user)
- ->willReturn(false);
- $this->urlGenerator->expects($this->once())
- ->method('linkToRoute')
- ->with(
- 'core.TwoFactorChallenge.selectChallenge'
- )
- ->willReturn('two/factor/url');
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- $this->assertEquals('two/factor/url', $result->getRedirectUrl());
- }
-
- public function testProcessTwoActiveProviders(): void {
- $data = $this->getLoggedInLoginData();
- $this->twoFactorManager->expects($this->once())
- ->method('isTwoFactorAuthenticated')
- ->willReturn(true);
- $this->twoFactorManager->expects($this->once())
- ->method('prepareTwoFactorLogin')
- ->with(
- $this->user,
- $data->isRememberLogin()
- );
- $provider1 = $this->createMock(ITwoFactorAuthProvider::class);
- $provider2 = $this->createMock(ITwoFactorAuthProvider::class);
- $provider1->expects($this->once())
- ->method('getId')
- ->willReturn('test1');
- $provider2->expects($this->once())
- ->method('getId')
- ->willReturn('test2');
- $this->twoFactorManager->expects($this->once())
- ->method('getProviderSet')
- ->willReturn(new ProviderSet([
- $provider1,
- $provider2,
- ], false));
- $this->twoFactorManager->expects($this->once())
- ->method('getLoginSetupProviders')
- ->with($this->user)
- ->willReturn([]);
- $this->mandatoryTwoFactor->expects($this->any())
- ->method('isEnforcedFor')
- ->with($this->user)
- ->willReturn(false);
- $this->urlGenerator->expects($this->once())
- ->method('linkToRoute')
- ->with(
- 'core.TwoFactorChallenge.selectChallenge'
- )
- ->willReturn('two/factor/url');
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- $this->assertEquals('two/factor/url', $result->getRedirectUrl());
- }
-
- public function testProcessFailingProviderAndEnforcedButNoSetupProviders(): void {
- $data = $this->getLoggedInLoginData();
- $this->twoFactorManager->expects($this->once())
- ->method('isTwoFactorAuthenticated')
- ->willReturn(true);
- $this->twoFactorManager->expects($this->once())
- ->method('prepareTwoFactorLogin')
- ->with(
- $this->user,
- $data->isRememberLogin()
- );
- $this->twoFactorManager->expects($this->once())
- ->method('getProviderSet')
- ->willReturn(new ProviderSet([], true));
- $this->twoFactorManager->expects($this->once())
- ->method('getLoginSetupProviders')
- ->with($this->user)
- ->willReturn([]);
- $this->mandatoryTwoFactor->expects($this->any())
- ->method('isEnforcedFor')
- ->with($this->user)
- ->willReturn(true);
- $this->urlGenerator->expects($this->once())
- ->method('linkToRoute')
- ->with(
- 'core.TwoFactorChallenge.selectChallenge'
- )
- ->willReturn('two/factor/url');
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- $this->assertEquals('two/factor/url', $result->getRedirectUrl());
- }
-
- public function testProcessFailingProviderAndEnforced(): void {
- $data = $this->getLoggedInLoginData();
- $this->twoFactorManager->expects($this->once())
- ->method('isTwoFactorAuthenticated')
- ->willReturn(true);
- $this->twoFactorManager->expects($this->once())
- ->method('prepareTwoFactorLogin')
- ->with(
- $this->user,
- $data->isRememberLogin()
- );
- $provider = $this->createMock(IActivatableAtLogin::class);
- $this->twoFactorManager->expects($this->once())
- ->method('getProviderSet')
- ->willReturn(new ProviderSet([
- $provider,
- ], true));
- $this->twoFactorManager->expects($this->once())
- ->method('getLoginSetupProviders')
- ->with($this->user)
- ->willReturn([]);
- $this->mandatoryTwoFactor->expects($this->any())
- ->method('isEnforcedFor')
- ->with($this->user)
- ->willReturn(true);
- $this->urlGenerator->expects($this->once())
- ->method('linkToRoute')
- ->with(
- 'core.TwoFactorChallenge.selectChallenge'
- )
- ->willReturn('two/factor/url');
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- $this->assertEquals('two/factor/url', $result->getRedirectUrl());
- }
-
- public function testProcessNoProvidersButEnforced(): void {
- $data = $this->getLoggedInLoginData();
- $this->twoFactorManager->expects($this->once())
- ->method('isTwoFactorAuthenticated')
- ->willReturn(true);
- $this->twoFactorManager->expects($this->once())
- ->method('prepareTwoFactorLogin')
- ->with(
- $this->user,
- $data->isRememberLogin()
- );
- $this->twoFactorManager->expects($this->once())
- ->method('getProviderSet')
- ->willReturn(new ProviderSet([], false));
- $this->twoFactorManager->expects($this->once())
- ->method('getLoginSetupProviders')
- ->with($this->user)
- ->willReturn([]);
- $this->mandatoryTwoFactor->expects($this->any())
- ->method('isEnforcedFor')
- ->with($this->user)
- ->willReturn(true);
- $this->urlGenerator->expects($this->once())
- ->method('linkToRoute')
- ->with(
- 'core.TwoFactorChallenge.selectChallenge'
- )
- ->willReturn('two/factor/url');
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- $this->assertEquals('two/factor/url', $result->getRedirectUrl());
- }
-
- public function testProcessWithRedirectUrl(): void {
- $data = $this->getLoggedInLoginDataWithRedirectUrl();
- $this->twoFactorManager->expects($this->once())
- ->method('isTwoFactorAuthenticated')
- ->willReturn(true);
- $this->twoFactorManager->expects($this->once())
- ->method('prepareTwoFactorLogin')
- ->with(
- $this->user,
- $data->isRememberLogin()
- );
- $provider = $this->createMock(ITwoFactorAuthProvider::class);
- $this->twoFactorManager->expects($this->once())
- ->method('getProviderSet')
- ->willReturn(new ProviderSet([
- $provider,
- ], false));
- $this->twoFactorManager->expects($this->once())
- ->method('getLoginSetupProviders')
- ->with($this->user)
- ->willReturn([]);
- $this->mandatoryTwoFactor->expects($this->any())
- ->method('isEnforcedFor')
- ->with($this->user)
- ->willReturn(false);
- $provider->expects($this->once())
- ->method('getId')
- ->willReturn('test');
- $this->urlGenerator->expects($this->once())
- ->method('linkToRoute')
- ->with(
- 'core.TwoFactorChallenge.showChallenge',
- [
- 'challengeProviderId' => 'test',
- 'redirect_url' => $this->redirectUrl,
- ]
- )
- ->willReturn('two/factor/url');
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- $this->assertEquals('two/factor/url', $result->getRedirectUrl());
- }
-}
--- a/tests/lib/Authentication/Login/UidLoginCommandTest.php
+++ /dev/null
@@ -1,62 +0,0 @@
-userManager = $this->createMock(Manager::class);
-
- $this->cmd = new UidLoginCommand(
- $this->userManager
- );
- }
-
- public function testProcessFailingLogin(): void {
- $data = $this->getBasicLoginData();
- $this->userManager->expects($this->once())
- ->method('checkPasswordNoLogging')
- ->with(
- $this->username,
- $this->password
- )
- ->willReturn(false);
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- $this->assertFalse($data->getUser());
- }
-
- public function testProcess(): void {
- $data = $this->getBasicLoginData();
- $this->userManager->expects($this->once())
- ->method('checkPasswordNoLogging')
- ->with(
- $this->username,
- $this->password
- )
- ->willReturn($this->user);
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- $this->assertEquals($this->user, $data->getUser());
- }
-}
--- a/tests/lib/Authentication/Login/UpdateLastPasswordConfirmCommandTest.php
+++ /dev/null
@@ -1,47 +0,0 @@
-session = $this->createMock(ISession::class);
-
- $this->cmd = new UpdateLastPasswordConfirmCommand(
- $this->session
- );
- }
-
- public function testProcess(): void {
- $data = $this->getLoggedInLoginData();
- $this->user->expects($this->once())
- ->method('getLastLogin')
- ->willReturn(1234);
- $this->session->expects($this->once())
- ->method('set')
- ->with(
- 'last-password-confirm',
- 1234
- );
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- }
-}
--- a/tests/lib/Authentication/Login/UserDisabledCheckCommandTest.php
+++ /dev/null
@@ -1,80 +0,0 @@
-userManager = $this->createMock(IUserManager::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $this->cmd = new UserDisabledCheckCommand(
- $this->userManager,
- $this->logger
- );
- }
-
- public function testProcessNonExistingUser(): void {
- $data = $this->getBasicLoginData();
- $this->userManager->expects($this->once())
- ->method('get')
- ->with($this->username)
- ->willReturn(null);
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- }
-
- public function testProcessDisabledUser(): void {
- $data = $this->getBasicLoginData();
- $this->userManager->expects($this->once())
- ->method('get')
- ->with($this->username)
- ->willReturn($this->user);
- $this->user->expects($this->once())
- ->method('isEnabled')
- ->willReturn(false);
-
- $result = $this->cmd->process($data);
-
- $this->assertFalse($result->isSuccess());
- $this->assertSame(LoginController::LOGIN_MSG_USERDISABLED, $result->getErrorMessage());
- }
-
- public function testProcess(): void {
- $data = $this->getBasicLoginData();
- $this->userManager->expects($this->once())
- ->method('get')
- ->with($this->username)
- ->willReturn($this->user);
- $this->user->expects($this->once())
- ->method('isEnabled')
- ->willReturn(true);
-
- $result = $this->cmd->process($data);
-
- $this->assertTrue($result->isSuccess());
- }
-}
--- a/tests/lib/Authentication/LoginCredentials/CredentialsTest.php
+++ /dev/null
@@ -1,48 +0,0 @@
-uid = 'user123';
- $this->user = 'User123';
- $this->password = '123456';
-
- $this->credentials = new Credentials($this->uid, $this->user, $this->password);
- }
-
- public function testGetUID(): void {
- $this->assertEquals($this->uid, $this->credentials->getUID());
- }
-
- public function testGetUserName(): void {
- $this->assertEquals($this->user, $this->credentials->getLoginName());
- }
-
- public function testGetPassword(): void {
- $this->assertEquals($this->password, $this->credentials->getPassword());
- }
-}
--- a/tests/lib/Authentication/LoginCredentials/StoreTest.php
+++ /dev/null
@@ -1,296 +0,0 @@
-session = $this->createMock(ISession::class);
- $this->tokenProvider = $this->createMock(IProvider::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->crypto = $this->createMock(ICrypto::class);
-
- $this->store = new Store($this->session, $this->logger, $this->crypto, $this->tokenProvider);
- }
-
- public function testAuthenticate(): void {
- $params = [
- 'run' => true,
- 'uid' => 'user123',
- 'password' => '123456',
- ];
-
- $this->session->expects($this->once())
- ->method('set')
- ->with($this->equalTo('login_credentials'), $this->equalTo(json_encode($params)));
- $this->crypto->expects($this->once())
- ->method('encrypt')
- ->willReturn('123456');
-
- $this->store->authenticate($params);
- }
-
- public function testSetSession(): void {
- $session = $this->createMock(ISession::class);
-
- $this->store->setSession($session);
- $this->addToAssertionCount(1);
- }
-
- public function testGetLoginCredentialsNoTokenProvider(): void {
- $this->store = new Store($this->session, $this->logger, $this->crypto, null);
-
- $this->expectException(CredentialsUnavailableException::class);
-
- $this->store->getLoginCredentials();
- }
-
- public function testGetLoginCredentials(): void {
- $uid = 'uid';
- $user = 'user123';
- $password = 'passme';
- $token = $this->createMock(IToken::class);
- $this->session->expects($this->once())
- ->method('getId')
- ->willReturn('sess2233');
- $this->tokenProvider->expects($this->once())
- ->method('getToken')
- ->with('sess2233')
- ->willReturn($token);
- $token->expects($this->once())
- ->method('getUID')
- ->willReturn($uid);
- $token->expects($this->once())
- ->method('getLoginName')
- ->willReturn($user);
- $this->tokenProvider->expects($this->once())
- ->method('getPassword')
- ->with($token, 'sess2233')
- ->willReturn($password);
- $expected = new Credentials($uid, $user, $password);
-
- $creds = $this->store->getLoginCredentials();
-
- $this->assertEquals($expected, $creds);
- }
-
- public function testGetLoginCredentialsSessionNotAvailable(): void {
- $this->session->expects($this->once())
- ->method('getId')
- ->willThrowException(new SessionNotAvailableException());
- $this->expectException(CredentialsUnavailableException::class);
-
- $this->store->getLoginCredentials();
- }
-
- public function testGetLoginCredentialsInvalidToken(): void {
- $this->session->expects($this->once())
- ->method('getId')
- ->willReturn('sess2233');
- $this->tokenProvider->expects($this->once())
- ->method('getToken')
- ->with('sess2233')
- ->willThrowException(new InvalidTokenException());
- $this->expectException(CredentialsUnavailableException::class);
-
- $this->store->getLoginCredentials();
- }
-
- public function testGetLoginCredentialsPartialCredentialsAndSessionName(): void {
- $uid = 'id987';
- $user = 'user987';
- $password = '7389374';
-
- $this->session->expects($this->once())
- ->method('getId')
- ->willReturn('sess2233');
- $this->tokenProvider->expects($this->once())
- ->method('getToken')
- ->with('sess2233')
- ->willThrowException(new InvalidTokenException());
- $this->session->expects($this->once())
- ->method('exists')
- ->with($this->equalTo('login_credentials'))
- ->willReturn(true);
- $this->crypto->expects($this->once())
- ->method('decrypt')
- ->willReturn($password);
- $this->session->expects($this->exactly(2))
- ->method('get')
- ->willReturnMap([
- [
- 'login_credentials',
- json_encode([
- 'uid' => $uid,
- 'password' => $password,
- ])
- ],
- [
- 'loginname',
- $user,
- ],
- ]);
- $expected = new Credentials($uid, $user, $password);
-
- $actual = $this->store->getLoginCredentials();
-
- $this->assertEquals($expected, $actual);
- }
-
- public function testGetLoginCredentialsPartialCredentials(): void {
- $uid = 'id987';
- $password = '7389374';
-
- $this->session->expects($this->once())
- ->method('getId')
- ->willReturn('sess2233');
- $this->tokenProvider->expects($this->once())
- ->method('getToken')
- ->with('sess2233')
- ->willThrowException(new InvalidTokenException());
- $this->session->expects($this->once())
- ->method('exists')
- ->with($this->equalTo('login_credentials'))
- ->willReturn(true);
- $this->crypto->expects($this->once())
- ->method('decrypt')
- ->willReturn($password);
- $this->session->expects($this->exactly(2))
- ->method('get')
- ->willReturnMap([
- [
- 'login_credentials',
- json_encode([
- 'uid' => $uid,
- 'password' => $password,
- ])
- ],
- [
- 'loginname',
- null,
- ],
- ]);
- $expected = new Credentials($uid, $uid, $password);
-
- $actual = $this->store->getLoginCredentials();
-
- $this->assertEquals($expected, $actual);
- }
-
- public function testGetLoginCredentialsInvalidTokenLoginCredentials(): void {
- $uid = 'id987';
- $user = 'user987';
- $password = '7389374';
-
- $this->session->expects($this->once())
- ->method('getId')
- ->willReturn('sess2233');
- $this->tokenProvider->expects($this->once())
- ->method('getToken')
- ->with('sess2233')
- ->willThrowException(new InvalidTokenException());
- $this->session->expects($this->once())
- ->method('exists')
- ->with($this->equalTo('login_credentials'))
- ->willReturn(true);
- $this->crypto->expects($this->once())
- ->method('decrypt')
- ->willReturn($password);
- $this->session->expects($this->once())
- ->method('get')
- ->with($this->equalTo('login_credentials'))
- ->willReturn('{"run":true,"uid":"id987","loginName":"user987","password":"7389374"}');
- $expected = new Credentials($uid, $user, $password);
-
- $actual = $this->store->getLoginCredentials();
-
- $this->assertEquals($expected, $actual);
- }
-
- public function testGetLoginCredentialsPasswordlessToken(): void {
- $this->session->expects($this->once())
- ->method('getId')
- ->willReturn('sess2233');
- $this->tokenProvider->expects($this->once())
- ->method('getToken')
- ->with('sess2233')
- ->willThrowException(new PasswordlessTokenException());
- $this->expectException(CredentialsUnavailableException::class);
-
- $this->store->getLoginCredentials();
- }
-
- public function testAuthenticatePasswordlessToken(): void {
- $user = 'user987';
- $password = null;
-
- $params = [
- 'run' => true,
- 'loginName' => $user,
- 'uid' => $user,
- 'password' => $password,
- ];
-
- $this->session->expects($this->once())
- ->method('set')
- ->with($this->equalTo('login_credentials'), $this->equalTo(json_encode($params)));
-
- $this->session->expects($this->once())
- ->method('getId')
- ->willReturn('sess2233');
- $this->tokenProvider->expects($this->once())
- ->method('getToken')
- ->with('sess2233')
- ->willThrowException(new PasswordlessTokenException());
-
- $this->session->expects($this->once())
- ->method('exists')
- ->with($this->equalTo('login_credentials'))
- ->willReturn(true);
- $this->session->expects($this->once())
- ->method('get')
- ->with($this->equalTo('login_credentials'))
- ->willReturn(json_encode($params));
-
- $this->store->authenticate($params);
- $actual = $this->store->getLoginCredentials();
-
- $expected = new Credentials($user, $user, $password);
- $this->assertEquals($expected, $actual);
- }
-}
--- a/tests/lib/Authentication/Token/ManagerTest.php
+++ /dev/null
@@ -1,407 +0,0 @@
-publicKeyTokenProvider = $this->createMock(PublicKeyTokenProvider::class);
- $this->manager = new Manager(
- $this->publicKeyTokenProvider
- );
- }
-
- public function testGenerateToken(): void {
- $token = new PublicKeyToken();
-
- $this->publicKeyTokenProvider->expects($this->once())
- ->method('generateToken')
- ->with(
- 'token',
- 'uid',
- 'loginName',
- 'password',
- 'name',
- IToken::TEMPORARY_TOKEN,
- IToken::REMEMBER
- )->willReturn($token);
-
- $actual = $this->manager->generateToken(
- 'token',
- 'uid',
- 'loginName',
- 'password',
- 'name',
- IToken::TEMPORARY_TOKEN,
- IToken::REMEMBER
- );
-
- $this->assertSame($token, $actual);
- }
-
- public function testGenerateConflictingToken(): void {
- /** @var MockObject|Exception $exception */
- $exception = $this->createMock(Exception::class);
- $exception->method('getReason')->willReturn(Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION);
-
- $token = new PublicKeyToken();
- $token->setUid('uid');
-
- $this->publicKeyTokenProvider->expects($this->once())
- ->method('generateToken')
- ->with(
- 'token',
- 'uid',
- 'loginName',
- 'password',
- 'name',
- IToken::TEMPORARY_TOKEN,
- IToken::REMEMBER
- )->willThrowException($exception);
- $this->publicKeyTokenProvider->expects($this->once())
- ->method('getToken')
- ->with('token')
- ->willReturn($token);
-
- $actual = $this->manager->generateToken(
- 'token',
- 'uid',
- 'loginName',
- 'password',
- 'name',
- IToken::TEMPORARY_TOKEN,
- IToken::REMEMBER
- );
-
- $this->assertSame($token, $actual);
- }
-
- public function testGenerateTokenTooLongName(): void {
- $token = $this->createMock(IToken::class);
- $token->method('getName')
- ->willReturn(str_repeat('a', 120) . '…');
-
- $this->publicKeyTokenProvider->expects($this->once())
- ->method('generateToken')
- ->with(
- 'token',
- 'uid',
- 'loginName',
- 'password',
- str_repeat('a', 120) . '…',
- IToken::TEMPORARY_TOKEN,
- IToken::REMEMBER
- )->willReturn($token);
-
- $actual = $this->manager->generateToken(
- 'token',
- 'uid',
- 'loginName',
- 'password',
- str_repeat('a', 200),
- IToken::TEMPORARY_TOKEN,
- IToken::REMEMBER
- );
-
- $this->assertSame(121, mb_strlen($actual->getName()));
- }
-
- public static function tokenData(): array {
- return [
- [new PublicKeyToken()],
- [IToken::class],
- ];
- }
-
- protected function setNoCall(IToken $token) {
- if (!($token instanceof PublicKeyToken)) {
- $this->publicKeyTokenProvider->expects($this->never())
- ->method($this->anything());
- }
- }
-
- protected function setCall(IToken $token, string $function, $return = null) {
- if ($token instanceof PublicKeyToken) {
- $this->publicKeyTokenProvider->expects($this->once())
- ->method($function)
- ->with($token)
- ->willReturn($return);
- }
- }
-
- protected function setException(IToken $token) {
- if (!($token instanceof PublicKeyToken)) {
- $this->expectException(InvalidTokenException::class);
- }
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('tokenData')]
- public function testUpdateToken(IToken|string $token): void {
- if (is_string($token)) {
- $token = $this->createMock($token);
- }
-
- $this->setNoCall($token);
- $this->setCall($token, 'updateToken');
- $this->setException($token);
-
- $this->manager->updateToken($token);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('tokenData')]
- public function testUpdateTokenActivity(IToken|string $token): void {
- if (is_string($token)) {
- $token = $this->createMock($token);
- }
-
- $this->setNoCall($token);
- $this->setCall($token, 'updateTokenActivity');
- $this->setException($token);
-
- $this->manager->updateTokenActivity($token);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('tokenData')]
- public function testGetPassword(IToken|string $token): void {
- if (is_string($token)) {
- $token = $this->createMock($token);
- }
-
- $this->setNoCall($token);
- $this->setCall($token, 'getPassword', 'password');
- $this->setException($token);
-
- $result = $this->manager->getPassword($token, 'tokenId', 'password');
-
- $this->assertSame('password', $result);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('tokenData')]
- public function testSetPassword(IToken|string $token): void {
- if (is_string($token)) {
- $token = $this->createMock($token);
- }
-
- $this->setNoCall($token);
- $this->setCall($token, 'setPassword');
- $this->setException($token);
-
- $this->manager->setPassword($token, 'tokenId', 'password');
- }
-
- public function testInvalidateTokens(): void {
- $this->publicKeyTokenProvider->expects($this->once())
- ->method('invalidateToken')
- ->with('token');
-
- $this->manager->invalidateToken('token');
- }
-
- public function testInvalidateTokenById(): void {
- $this->publicKeyTokenProvider->expects($this->once())
- ->method('invalidateTokenById')
- ->with('uid', 42);
-
- $this->manager->invalidateTokenById('uid', 42);
- }
-
- public function testInvalidateOldTokens(): void {
- $this->publicKeyTokenProvider->expects($this->once())
- ->method('invalidateOldTokens');
-
- $this->manager->invalidateOldTokens();
- }
-
- public function testInvalidateLastUsedBefore(): void {
- $this->publicKeyTokenProvider->expects($this->once())
- ->method('invalidateLastUsedBefore')
- ->with('user', 946684800);
-
- $this->manager->invalidateLastUsedBefore('user', 946684800);
- }
-
- public function testGetTokenByUser(): void {
- $t1 = new PublicKeyToken();
- $t2 = new PublicKeyToken();
-
- $this->publicKeyTokenProvider
- ->method('getTokenByUser')
- ->willReturn([$t1, $t2]);
-
- $result = $this->manager->getTokenByUser('uid');
-
- $this->assertEquals([$t1, $t2], $result);
- }
-
- public function testRenewSessionTokenPublicKey(): void {
- $this->publicKeyTokenProvider->expects($this->once())
- ->method('renewSessionToken')
- ->with('oldId', 'newId');
-
- $this->manager->renewSessionToken('oldId', 'newId');
- }
-
- public function testRenewSessionInvalid(): void {
- $this->publicKeyTokenProvider->expects($this->once())
- ->method('renewSessionToken')
- ->with('oldId', 'newId')
- ->willThrowException(new InvalidTokenException());
-
- $this->expectException(InvalidTokenException::class);
- $this->manager->renewSessionToken('oldId', 'newId');
- }
-
- public function testGetTokenByIdPublicKey(): void {
- $token = $this->createMock(IToken::class);
-
- $this->publicKeyTokenProvider->expects($this->once())
- ->method('getTokenById')
- ->with(42)
- ->willReturn($token);
-
- $this->assertSame($token, $this->manager->getTokenById(42));
- }
-
- public function testGetTokenByIdInvalid(): void {
- $this->publicKeyTokenProvider->expects($this->once())
- ->method('getTokenById')
- ->with(42)
- ->willThrowException(new InvalidTokenException());
-
- $this->expectException(InvalidTokenException::class);
- $this->manager->getTokenById(42);
- }
-
- public function testGetTokenPublicKey(): void {
- $token = new PublicKeyToken();
-
- $this->publicKeyTokenProvider
- ->method('getToken')
- ->with('tokenId')
- ->willReturn($token);
-
- $this->assertSame($token, $this->manager->getToken('tokenId'));
- }
-
- public function testGetTokenInvalid(): void {
- $this->publicKeyTokenProvider
- ->method('getToken')
- ->with('tokenId')
- ->willThrowException(new InvalidTokenException());
-
- $this->expectException(InvalidTokenException::class);
- $this->manager->getToken('tokenId');
- }
-
- public function testRotateInvalid(): void {
- $this->expectException(InvalidTokenException::class);
- $this->manager->rotate($this->createMock(IToken::class), 'oldId', 'newId');
- }
-
- public function testRotatePublicKey(): void {
- $token = new PublicKeyToken();
-
- $this->publicKeyTokenProvider
- ->method('rotate')
- ->with($token, 'oldId', 'newId')
- ->willReturn($token);
-
- $this->assertSame($token, $this->manager->rotate($token, 'oldId', 'newId'));
- }
-
- public function testMarkPasswordInvalidPublicKey(): void {
- $token = $this->createMock(PublicKeyToken::class);
-
- $this->publicKeyTokenProvider->expects($this->once())
- ->method('markPasswordInvalid')
- ->with($token, 'tokenId');
-
- $this->manager->markPasswordInvalid($token, 'tokenId');
- }
-
- public function testMarkPasswordInvalidInvalidToken(): void {
- $this->expectException(InvalidTokenException::class);
-
- $this->manager->markPasswordInvalid($this->createMock(IToken::class), 'tokenId');
- }
-
- public function testUpdatePasswords(): void {
- $this->publicKeyTokenProvider->expects($this->once())
- ->method('updatePasswords')
- ->with('uid', 'pass');
-
- $this->manager->updatePasswords('uid', 'pass');
- }
-
- public function testInvalidateTokensOfUserNoClientName(): void {
- $t1 = new PublicKeyToken();
- $t2 = new PublicKeyToken();
- $t1->setId(123);
- $t2->setId(456);
-
- $this->publicKeyTokenProvider
- ->expects($this->once())
- ->method('getTokenByUser')
- ->with('theUser')
- ->willReturn([$t1, $t2]);
-
- $calls = [
- ['theUser', 123],
- ['theUser', 456],
- ];
- $this->publicKeyTokenProvider
- ->expects($this->exactly(2))
- ->method('invalidateTokenById')
- ->willReturnCallback(function () use (&$calls): void {
- $expected = array_shift($calls);
- $this->assertEquals($expected, func_get_args());
- });
- $this->manager->invalidateTokensOfUser('theUser', null);
- }
-
- public function testInvalidateTokensOfUserClientNameGiven(): void {
- $t1 = new PublicKeyToken();
- $t2 = new PublicKeyToken();
- $t3 = new PublicKeyToken();
- $t1->setId(123);
- $t1->setName('Firefox session');
- $t2->setId(456);
- $t2->setName('My Client Name');
- $t3->setId(789);
- $t3->setName('mobile client');
-
- $this->publicKeyTokenProvider
- ->expects($this->once())
- ->method('getTokenByUser')
- ->with('theUser')
- ->willReturn([$t1, $t2, $t3]);
- $this->publicKeyTokenProvider
- ->expects($this->once())
- ->method('invalidateTokenById')
- ->with('theUser', 456);
- $this->manager->invalidateTokensOfUser('theUser', 'My Client Name');
- }
-}
--- a/tests/lib/Authentication/Token/PublicKeyTokenMapperTest.php
+++ /dev/null
@@ -1,261 +0,0 @@
-dbConnection = Server::get(IDBConnection::class);
- $this->time = time();
- $this->resetDatabase();
-
- $this->mapper = new PublicKeyTokenMapper($this->dbConnection);
- }
-
- private function resetDatabase() {
- $qb = $this->dbConnection->getQueryBuilder();
- $qb->delete('authtoken')->executeStatement();
- $qb->insert('authtoken')->values([
- 'uid' => $qb->createNamedParameter('user1'),
- 'login_name' => $qb->createNamedParameter('User1'),
- 'password' => $qb->createNamedParameter('a75c7116460c082912d8f6860a850904|3nz5qbG1nNSLLi6V|c55365a0e54cfdfac4a175bcf11a7612aea74492277bba6e5d96a24497fa9272488787cb2f3ad34d8b9b8060934fce02f008d371df3ff3848f4aa61944851ff0'),
- 'name' => $qb->createNamedParameter('Firefox on Linux'),
- 'token' => $qb->createNamedParameter('9c5a2e661482b65597408a6bb6c4a3d1af36337381872ac56e445a06cdb7fea2b1039db707545c11027a4966919918b19d875a8b774840b18c6cbb7ae56fe206'),
- 'type' => $qb->createNamedParameter(IToken::TEMPORARY_TOKEN),
- 'last_activity' => $qb->createNamedParameter($this->time - 120, IQueryBuilder::PARAM_INT), // Two minutes ago
- 'last_check' => $this->time - 60 * 10, // 10mins ago
- 'public_key' => $qb->createNamedParameter('public key'),
- 'private_key' => $qb->createNamedParameter('private key'),
- 'version' => $qb->createNamedParameter(2),
- ])->executeStatement();
- $qb->insert('authtoken')->values([
- 'uid' => $qb->createNamedParameter('user2'),
- 'login_name' => $qb->createNamedParameter('User2'),
- 'password' => $qb->createNamedParameter('971a337057853344700bbeccf836519f|UwOQwyb34sJHtqPV|036d4890f8c21d17bbc7b88072d8ef049a5c832a38e97f3e3d5f9186e896c2593aee16883f617322fa242728d0236ff32d163caeb4bd45e14ca002c57a88665f'),
- 'name' => $qb->createNamedParameter('Firefox on Android'),
- 'token' => $qb->createNamedParameter('1504445f1524fc801035448a95681a9378ba2e83930c814546c56e5d6ebde221198792fd900c88ed5ead0555780dad1ebce3370d7e154941cd5de87eb419899b'),
- 'type' => $qb->createNamedParameter(IToken::TEMPORARY_TOKEN),
- 'last_activity' => $qb->createNamedParameter($this->time - 60 * 60 * 24 * 3, IQueryBuilder::PARAM_INT), // Three days ago
- 'last_check' => $this->time - 10, // 10secs ago
- 'public_key' => $qb->createNamedParameter('public key'),
- 'private_key' => $qb->createNamedParameter('private key'),
- 'version' => $qb->createNamedParameter(2),
- ])->executeStatement();
- $qb->insert('authtoken')->values([
- 'uid' => $qb->createNamedParameter('user1'),
- 'login_name' => $qb->createNamedParameter('User1'),
- 'password' => $qb->createNamedParameter('063de945d6f6b26862d9b6f40652f2d5|DZ/z520tfdXPtd0T|395f6b89be8d9d605e409e20b9d9abe477fde1be38a3223f9e508f979bf906e50d9eaa4dca983ca4fb22a241eb696c3f98654e7775f78c4caf13108f98642b53'),
- 'name' => $qb->createNamedParameter('Iceweasel on Linux'),
- 'token' => $qb->createNamedParameter('47af8697ba590fb82579b5f1b3b6e8066773a62100abbe0db09a289a62f5d980dc300fa3d98b01d7228468d1ab05c1aa14c8d14bd5b6eee9cdf1ac14864680c3'),
- 'type' => $qb->createNamedParameter(IToken::TEMPORARY_TOKEN),
- 'last_activity' => $qb->createNamedParameter($this->time - 120, IQueryBuilder::PARAM_INT), // Two minutes ago
- 'last_check' => $this->time - 60 * 10, // 10mins ago
- 'public_key' => $qb->createNamedParameter('public key'),
- 'private_key' => $qb->createNamedParameter('private key'),
- 'version' => $qb->createNamedParameter(2),
- ])->executeStatement();
- $qb->insert('authtoken')->values([
- 'uid' => $qb->createNamedParameter('user3'),
- 'login_name' => $qb->createNamedParameter('User3'),
- 'password' => $qb->createNamedParameter('063de945d6f6b26862d9b6f40652f2d5|DZ/z520tfdXPtd0T|395f6b89be8d9d605e409e20b9d9abe477fde1be38a3223f9e508f979bf906e50d9eaa4dca983ca4fb22a241eb696c3f98654e7775f78c4caf13108f98642b53'),
- 'name' => $qb->createNamedParameter('Iceweasel on Linux'),
- 'token' => $qb->createNamedParameter('6d9a290d239d09f2cc33a03cc54cccd46f7dc71630dcc27d39214824bd3e093f1feb4e2b55eb159d204caa15dee9556c202a5aa0b9d67806c3f4ec2cde11af67'),
- 'type' => $qb->createNamedParameter(IToken::TEMPORARY_TOKEN),
- 'last_activity' => $qb->createNamedParameter($this->time - 120, IQueryBuilder::PARAM_INT), // Two minutes ago
- 'last_check' => $this->time - 60 * 10, // 10mins ago
- 'public_key' => $qb->createNamedParameter('public key'),
- 'private_key' => $qb->createNamedParameter('private key'),
- 'version' => $qb->createNamedParameter(2),
- 'password_invalid' => $qb->createNamedParameter(1),
- ])->executeStatement();
- $qb->insert('authtoken')->values([
- 'uid' => $qb->createNamedParameter('user3'),
- 'login_name' => $qb->createNamedParameter('User3'),
- 'password' => $qb->createNamedParameter('063de945d6f6b26862d9b6f40652f2d5|DZ/z520tfdXPtd0T|395f6b89be8d9d605e409e20b9d9abe477fde1be38a3223f9e508f979bf906e50d9eaa4dca983ca4fb22a241eb696c3f98654e7775f78c4caf13108f98642b53'),
- 'name' => $qb->createNamedParameter('Iceweasel on Linux'),
- 'token' => $qb->createNamedParameter('84c5808c6445b6d65b8aa5b03840f09b27de603f0fb970906fb14ea4b115b7bf5ec53fada5c093fe46afdcd7bbc9617253a4d105f7dfb32719f9973d72412f31'),
- 'type' => $qb->createNamedParameter(IToken::PERMANENT_TOKEN),
- 'last_activity' => $qb->createNamedParameter($this->time - 60 * 3, IQueryBuilder::PARAM_INT), // Three minutes ago
- 'last_check' => $this->time - 60 * 10, // 10mins ago
- 'public_key' => $qb->createNamedParameter('public key'),
- 'private_key' => $qb->createNamedParameter('private key'),
- 'version' => $qb->createNamedParameter(2),
- 'password_invalid' => $qb->createNamedParameter(1),
- ])->executeStatement();
- }
-
- private function getNumberOfTokens() {
- $qb = $this->dbConnection->getQueryBuilder();
- $result = $qb->select($qb->func()->count('*', 'count'))
- ->from('authtoken')
- ->executeQuery()
- ->fetchAssociative();
- return (int)$result['count'];
- }
-
- public function testInvalidate(): void {
- $token = '9c5a2e661482b65597408a6bb6c4a3d1af36337381872ac56e445a06cdb7fea2b1039db707545c11027a4966919918b19d875a8b774840b18c6cbb7ae56fe206';
-
- $this->mapper->invalidate($token);
-
- $this->assertSame(4, $this->getNumberOfTokens());
- }
-
- public function testInvalidateInvalid(): void {
- $token = 'youwontfindthisoneinthedatabase';
-
- $this->mapper->invalidate($token);
-
- $this->assertSame(5, $this->getNumberOfTokens());
- }
-
- public function testInvalidateOld(): void {
- $olderThan = $this->time - 60 * 60; // One hour
-
- $this->mapper->invalidateOld($olderThan);
-
- $this->assertSame(4, $this->getNumberOfTokens());
- }
-
- public function testInvalidateLastUsedBefore(): void {
- $before = $this->time - 60 * 2; // Two minutes
-
- $this->mapper->invalidateLastUsedBefore('user3', $before);
-
- $this->assertSame(4, $this->getNumberOfTokens());
- }
-
- public function testGetToken(): void {
- $token = new PublicKeyToken();
- $token->setUid('user2');
- $token->setLoginName('User2');
- $token->setPassword('971a337057853344700bbeccf836519f|UwOQwyb34sJHtqPV|036d4890f8c21d17bbc7b88072d8ef049a5c832a38e97f3e3d5f9186e896c2593aee16883f617322fa242728d0236ff32d163caeb4bd45e14ca002c57a88665f');
- $token->setName('Firefox on Android');
- $token->setToken('1504445f1524fc801035448a95681a9378ba2e83930c814546c56e5d6ebde221198792fd900c88ed5ead0555780dad1ebce3370d7e154941cd5de87eb419899b');
- $token->setType(IToken::TEMPORARY_TOKEN);
- $token->setRemember(IToken::DO_NOT_REMEMBER);
- $token->setLastActivity($this->time - 60 * 60 * 24 * 3);
- $token->setLastCheck($this->time - 10);
- $token->setPublicKey('public key');
- $token->setPrivateKey('private key');
- $token->setVersion(PublicKeyToken::VERSION);
-
- $dbToken = $this->mapper->getToken($token->getToken());
-
- $token->setId($dbToken->getId()); // We don't know the ID
- $token->resetUpdatedFields();
-
- $this->assertEquals($token, $dbToken);
- }
-
- public function testGetInvalidToken(): void {
- $this->expectException(DoesNotExistException::class);
-
- $token = 'thisisaninvalidtokenthatisnotinthedatabase';
-
- $this->mapper->getToken($token);
- }
-
- public function testGetTokenById(): void {
- $token = new PublicKeyToken();
- $token->setUid('user2');
- $token->setLoginName('User2');
- $token->setPassword('971a337057853344700bbeccf836519f|UwOQwyb34sJHtqPV|036d4890f8c21d17bbc7b88072d8ef049a5c832a38e97f3e3d5f9186e896c2593aee16883f617322fa242728d0236ff32d163caeb4bd45e14ca002c57a88665f');
- $token->setName('Firefox on Android');
- $token->setToken('1504445f1524fc801035448a95681a9378ba2e83930c814546c56e5d6ebde221198792fd900c88ed5ead0555780dad1ebce3370d7e154941cd5de87eb419899b');
- $token->setType(IToken::TEMPORARY_TOKEN);
- $token->setRemember(IToken::DO_NOT_REMEMBER);
- $token->setLastActivity($this->time - 60 * 60 * 24 * 3);
- $token->setLastCheck($this->time - 10);
- $token->setPublicKey('public key');
- $token->setPrivateKey('private key');
- $token->setVersion(PublicKeyToken::VERSION);
-
- $dbToken = $this->mapper->getToken($token->getToken());
- $token->setId($dbToken->getId()); // We don't know the ID
- $token->resetUpdatedFields();
-
- $dbToken = $this->mapper->getTokenById($token->getId());
- $this->assertEquals($token, $dbToken);
- }
-
- public function testGetTokenByIdNotFound(): void {
- $this->expectException(DoesNotExistException::class);
-
- $this->mapper->getTokenById(-1);
- }
-
- public function testGetInvalidTokenById(): void {
- $this->expectException(DoesNotExistException::class);
-
- $id = '42';
-
- $this->mapper->getToken($id);
- }
-
- public function testGetTokenByUser(): void {
- $this->assertCount(2, $this->mapper->getTokenByUser('user1'));
- }
-
- public function testGetTokenByUserNotFound(): void {
- $this->assertCount(0, $this->mapper->getTokenByUser('user1000'));
- }
-
- public function testGetById(): void {
- /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
- $user = $this->createMock(IUser::class);
- $qb = $this->dbConnection->getQueryBuilder();
- $qb->select('id')
- ->from('authtoken')
- ->where($qb->expr()->eq('token', $qb->createNamedParameter('9c5a2e661482b65597408a6bb6c4a3d1af36337381872ac56e445a06cdb7fea2b1039db707545c11027a4966919918b19d875a8b774840b18c6cbb7ae56fe206')));
- $result = $qb->executeQuery();
- $id = $result->fetchAssociative()['id'];
-
- $token = $this->mapper->getTokenById((int)$id);
- $this->assertEquals('user1', $token->getUID());
- }
-
- public function testDeleteByName(): void {
- $qb = $this->dbConnection->getQueryBuilder();
- $qb->select('name')
- ->from('authtoken')
- ->where($qb->expr()->eq('token', $qb->createNamedParameter('9c5a2e661482b65597408a6bb6c4a3d1af36337381872ac56e445a06cdb7fea2b1039db707545c11027a4966919918b19d875a8b774840b18c6cbb7ae56fe206')));
- $result = $qb->executeQuery();
- $name = $result->fetchAssociative()['name'];
- $this->mapper->deleteByName($name);
- $this->assertEquals(4, $this->getNumberOfTokens());
- }
-
- public function testHasExpiredTokens(): void {
- $this->assertFalse($this->mapper->hasExpiredTokens('user1'));
- $this->assertTrue($this->mapper->hasExpiredTokens('user3'));
- }
-}
--- a/tests/lib/Authentication/Token/PublicKeyTokenProviderTest.php
+++ /dev/null
@@ -1,645 +0,0 @@
-mapper = $this->createMock(PublicKeyTokenMapper::class);
- $this->config = $this->createMock(IConfig::class);
- $this->config->method('getSystemValue')
- ->willReturnMap([
- ['openssl', [], []],
- ]);
- $this->config->method('getSystemValueString')
- ->willReturnMap([
- ['secret', '', '1f4h9s'],
- ]);
- $this->db = $this->createMock(IDBConnection::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->timeFactory = $this->createMock(ITimeFactory::class);
- $this->time = 1313131;
- $this->timeFactory->method('getTime')
- ->willReturn($this->time);
- $this->cacheFactory = $this->createMock(ICacheFactory::class);
-
- $this->hasher = Server::get(IHasher::class);
- $this->crypto = Server::get(ICrypto::class);
- $this->eventDispatcher = Server::get(IEventDispatcher::class);
-
- $this->tokenProvider = new PublicKeyTokenProvider(
- $this->mapper,
- $this->crypto,
- $this->config,
- $this->db,
- $this->logger,
- $this->timeFactory,
- $this->hasher,
- $this->cacheFactory,
- $this->eventDispatcher,
- );
- }
-
- public function testGenerateToken(): void {
- $token = 'tokentokentokentokentoken';
- $uid = 'user';
- $user = 'User';
- $password = 'passme';
- $name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
- $type = IToken::PERMANENT_TOKEN;
-
- $this->config->method('getSystemValueBool')
- ->willReturnMap([
- ['auth.storeCryptedPassword', true, true],
- ]);
- $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
-
- $this->assertInstanceOf(PublicKeyToken::class, $actual);
- $this->assertSame($uid, $actual->getUID());
- $this->assertSame($user, $actual->getLoginName());
- $this->assertSame($name, $actual->getName());
- $this->assertSame(IToken::DO_NOT_REMEMBER, $actual->getRemember());
- $this->assertSame($password, $this->tokenProvider->getPassword($actual, $token));
- }
-
- public function testGenerateTokenNoPassword(): void {
- $token = 'tokentokentokentokentoken';
- $uid = 'user';
- $user = 'User';
- $password = 'passme';
- $name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
- $type = IToken::PERMANENT_TOKEN;
- $this->config->method('getSystemValueBool')
- ->willReturnMap([
- ['auth.storeCryptedPassword', true, false],
- ]);
- $this->expectException(PasswordlessTokenException::class);
-
- $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
-
- $this->assertInstanceOf(PublicKeyToken::class, $actual);
- $this->assertSame($uid, $actual->getUID());
- $this->assertSame($user, $actual->getLoginName());
- $this->assertSame($name, $actual->getName());
- $this->assertSame(IToken::DO_NOT_REMEMBER, $actual->getRemember());
- $this->tokenProvider->getPassword($actual, $token);
- }
-
- public function testGenerateTokenLongPassword(): void {
- $token = 'tokentokentokentokentoken';
- $uid = 'user';
- $user = 'User';
- $password = '';
- for ($i = 0; $i < 500; $i++) {
- $password .= 'e';
- }
- $name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
- $type = IToken::PERMANENT_TOKEN;
- $this->config->method('getSystemValueBool')
- ->willReturnMap([
- ['auth.storeCryptedPassword', true, true],
- ]);
- $this->expectException(\RuntimeException::class);
-
- $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
- }
-
- public function testGenerateTokenInvalidName(): void {
- $token = 'tokentokentokentokentoken';
- $uid = 'user';
- $user = 'User';
- $password = 'passme';
- $name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12'
- . 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12'
- . 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12'
- . 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
- $type = IToken::PERMANENT_TOKEN;
- $this->config->method('getSystemValueBool')
- ->willReturnMap([
- ['auth.storeCryptedPassword', true, true],
- ]);
-
- $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
-
- $this->assertInstanceOf(PublicKeyToken::class, $actual);
- $this->assertSame($uid, $actual->getUID());
- $this->assertSame($user, $actual->getLoginName());
- $this->assertSame('User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12User-Agent: Mozill…', $actual->getName());
- $this->assertSame(IToken::DO_NOT_REMEMBER, $actual->getRemember());
- $this->assertSame($password, $this->tokenProvider->getPassword($actual, $token));
- }
-
- public function testUpdateToken(): void {
- $tk = new PublicKeyToken();
- $this->mapper->expects($this->once())
- ->method('updateActivity')
- ->with($tk, $this->time);
- $tk->setLastActivity($this->time - 200);
- $tk->setToken('token');
- $this->config->method('getSystemValueBool')
- ->willReturnMap([
- ['auth.storeCryptedPassword', true, true],
- ]);
-
- $this->tokenProvider->updateTokenActivity($tk);
-
- $this->assertEquals($this->time, $tk->getLastActivity());
- }
-
- public function testUpdateTokenDebounce(): void {
- $tk = new PublicKeyToken();
- $this->config->method('getSystemValueInt')
- ->willReturnCallback(function ($value, $default) {
- return $default;
- });
- $tk->setLastActivity($this->time - 30);
-
- $this->mapper->expects($this->never())
- ->method('updateActivity')
- ->with($tk, $this->time);
-
- $this->tokenProvider->updateTokenActivity($tk);
- }
-
- public function testGetTokenByUser(): void {
- $this->mapper->expects($this->once())
- ->method('getTokenByUser')
- ->with('uid')
- ->willReturn(['token']);
-
- $this->assertEquals(['token'], $this->tokenProvider->getTokenByUser('uid'));
- }
-
- public function testGetPassword(): void {
- $token = 'tokentokentokentokentoken';
- $uid = 'user';
- $user = 'User';
- $password = 'passme';
- $name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
- $type = IToken::PERMANENT_TOKEN;
- $this->config->method('getSystemValueBool')
- ->willReturnMap([
- ['auth.storeCryptedPassword', true, true],
- ]);
-
- $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
-
- $this->assertSame($password, $this->tokenProvider->getPassword($actual, $token));
- }
-
- public function testGetPasswordPasswordLessToken(): void {
- $this->expectException(PasswordlessTokenException::class);
-
- $token = 'token1234';
- $tk = new PublicKeyToken();
- $tk->setPassword(null);
-
- $this->tokenProvider->getPassword($tk, $token);
- }
-
- public function testGetPasswordInvalidToken(): void {
- $this->expectException(InvalidTokenException::class);
-
- $token = 'tokentokentokentokentoken';
- $uid = 'user';
- $user = 'User';
- $password = 'passme';
- $name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
- $type = IToken::PERMANENT_TOKEN;
-
- $this->config->method('getSystemValueBool')
- ->willReturnMap([
- ['auth.storeCryptedPassword', true, true],
- ]);
- $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
-
- $this->tokenProvider->getPassword($actual, 'wrongtoken');
- }
-
- public function testSetPassword(): void {
- $token = 'tokentokentokentokentoken';
- $uid = 'user';
- $user = 'User';
- $password = 'passme';
- $name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
- $type = IToken::PERMANENT_TOKEN;
- $this->config->method('getSystemValueBool')
- ->willReturnMap([
- ['auth.storeCryptedPassword', true, true],
- ]);
-
- $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
-
- $this->mapper->method('getTokenByUser')
- ->with('user')
- ->willReturn([$actual]);
-
- $newpass = 'newpass';
- $this->mapper->expects($this->once())
- ->method('update')
- ->with($this->callback(function ($token) use ($newpass) {
- return $newpass === $this->tokenProvider->getPassword($token, 'tokentokentokentokentoken');
- }));
-
- $this->tokenProvider->setPassword($actual, $token, $newpass);
-
- $this->assertSame($newpass, $this->tokenProvider->getPassword($actual, 'tokentokentokentokentoken'));
- }
-
- public function testSetPasswordInvalidToken(): void {
- $this->expectException(InvalidTokenException::class);
-
- $token = $this->createMock(IToken::class);
- $tokenId = 'token123';
- $password = '123456';
-
- $this->tokenProvider->setPassword($token, $tokenId, $password);
- }
-
- public function testInvalidateToken(): void {
- $calls = [
- [hash('sha512', 'token7' . '1f4h9s')],
- [hash('sha512', 'token7')]
- ];
-
- $this->mapper->expects($this->exactly(2))
- ->method('invalidate')
- ->willReturnCallback(function () use (&$calls): void {
- $expected = array_shift($calls);
- $this->assertEquals($expected, func_get_args());
- });
-
- $this->tokenProvider->invalidateToken('token7');
- }
-
- public function testInvalidateTokenById(): void {
- $id = 123;
-
- $this->mapper->expects($this->once())
- ->method('getTokenById')
- ->with($id);
-
- $this->tokenProvider->invalidateTokenById('uid', $id);
- }
-
- public function testInvalidateOldTokens(): void {
- $defaultSessionLifetime = 60 * 60 * 24;
- $defaultRememberMeLifetime = 60 * 60 * 24 * 15;
- $wipeTokenLifetime = 60 * 60 * 24 * 60;
- $this->config->expects($this->exactly(4))
- ->method('getSystemValueInt')
- ->willReturnMap([
- ['session_lifetime', $defaultSessionLifetime, 150],
- ['remember_login_cookie_lifetime', $defaultRememberMeLifetime, 300],
- ['token_auth_wipe_token_retention', $wipeTokenLifetime, 500],
- ['token_auth_token_retention', 60 * 60 * 24 * 365, 800],
- ]);
-
- $calls = [
- [$this->time - 150, IToken::TEMPORARY_TOKEN, IToken::DO_NOT_REMEMBER],
- [$this->time - 300, IToken::TEMPORARY_TOKEN, IToken::REMEMBER],
- [$this->time - 500, IToken::WIPE_TOKEN, null],
- [$this->time - 800, IToken::PERMANENT_TOKEN, null],
- ];
- $this->mapper->expects($this->exactly(4))
- ->method('invalidateOld')
- ->willReturnCallback(function () use (&$calls): void {
- $expected = array_shift($calls);
- $this->assertEquals($expected, func_get_args());
- });
-
- $this->tokenProvider->invalidateOldTokens();
- }
-
- public function testInvalidateLastUsedBefore(): void {
- $this->mapper->expects($this->once())
- ->method('invalidateLastUsedBefore')
- ->with('user', 946684800);
-
- $this->tokenProvider->invalidateLastUsedBefore('user', 946684800);
- }
-
- public function testRenewSessionTokenWithoutPassword(): void {
- $token = 'oldIdtokentokentokentoken';
- $uid = 'user';
- $user = 'User';
- $password = null;
- $name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
- $type = IToken::PERMANENT_TOKEN;
-
- $oldToken = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
-
- $this->mapper
- ->expects($this->once())
- ->method('getToken')
- ->with(hash('sha512', 'oldIdtokentokentokentoken' . '1f4h9s'))
- ->willReturn($oldToken);
- $this->mapper
- ->expects($this->once())
- ->method('insert')
- ->with($this->callback(function (PublicKeyToken $token) use ($user, $uid, $name) {
- return $token->getUID() === $uid
- && $token->getLoginName() === $user
- && $token->getName() === $name
- && $token->getType() === IToken::DO_NOT_REMEMBER
- && $token->getLastActivity() === $this->time
- && $token->getPassword() === null;
- }));
- $this->mapper
- ->expects($this->once())
- ->method('delete')
- ->with($this->callback(function ($token) use ($oldToken) {
- return $token === $oldToken;
- }));
-
- $this->tokenProvider->renewSessionToken('oldIdtokentokentokentoken', 'newIdtokentokentokentoken');
- }
-
- public function testRenewSessionTokenWithPassword(): void {
- $token = 'oldIdtokentokentokentoken';
- $uid = 'user';
- $user = 'User';
- $password = 'password';
- $name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
- $type = IToken::PERMANENT_TOKEN;
-
- $this->config->method('getSystemValueBool')
- ->willReturnMap([
- ['auth.storeCryptedPassword', true, true],
- ]);
- $oldToken = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
-
- $this->mapper
- ->expects($this->once())
- ->method('getToken')
- ->with(hash('sha512', 'oldIdtokentokentokentoken' . '1f4h9s'))
- ->willReturn($oldToken);
- $this->mapper
- ->expects($this->once())
- ->method('insert')
- ->with($this->callback(function (PublicKeyToken $token) use ($user, $uid, $name): bool {
- return $token->getUID() === $uid
- && $token->getLoginName() === $user
- && $token->getName() === $name
- && $token->getType() === IToken::DO_NOT_REMEMBER
- && $token->getLastActivity() === $this->time
- && $token->getPassword() !== null
- && $this->tokenProvider->getPassword($token, 'newIdtokentokentokentoken') === 'password';
- }));
- $this->mapper
- ->expects($this->once())
- ->method('delete')
- ->with($this->callback(function ($token) use ($oldToken): bool {
- return $token === $oldToken;
- }));
-
- $this->tokenProvider->renewSessionToken('oldIdtokentokentokentoken', 'newIdtokentokentokentoken');
- }
-
- public function testGetToken(): void {
- $token = new PublicKeyToken();
- $token->setToken('token');
- $token->setType(IToken::TEMPORARY_TOKEN);
-
- $this->config->method('getSystemValue')
- ->with('secret')
- ->willReturn('mysecret');
-
- $this->mapper->method('getToken')
- ->with(
- $this->callback(function (string $token) {
- return hash('sha512', 'unhashedTokentokentokentokentoken' . '1f4h9s') === $token;
- })
- )->willReturn($token);
-
- $this->assertSame($token, $this->tokenProvider->getToken('unhashedTokentokentokentokentoken'));
- }
-
- public function testGetInvalidToken(): void {
- $this->expectException(InvalidTokenException::class);
-
- $calls = [
- 'unhashedTokentokentokentokentoken' . '1f4h9s',
- 'unhashedTokentokentokentokentoken',
- ];
- $this->mapper->expects($this->exactly(2))
- ->method('getToken')
- ->willReturnCallback(function (string $token) use (&$calls): void {
- $expected = array_shift($calls);
- $this->assertEquals(hash('sha512', $expected), $token);
- throw new DoesNotExistException('nope');
- });
-
- $this->tokenProvider->getToken('unhashedTokentokentokentokentoken');
- }
-
- public function testGetExpiredToken(): void {
- $token = 'tokentokentokentokentoken';
- $uid = 'user';
- $user = 'User';
- $password = 'passme';
- $name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
- $type = IToken::PERMANENT_TOKEN;
-
- $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
- $actual->setExpires(42);
-
- $this->mapper->method('getToken')
- ->with(
- $this->callback(function (string $token) {
- return hash('sha512', 'tokentokentokentokentoken' . '1f4h9s') === $token;
- })
- )->willReturn($actual);
-
- try {
- $this->tokenProvider->getToken('tokentokentokentokentoken');
- $this->fail();
- } catch (ExpiredTokenException $e) {
- $this->assertSame($actual, $e->getToken());
- }
- }
-
- public function testGetTokenById(): void {
- $token = $this->createMock(PublicKeyToken::class);
-
- $this->mapper->expects($this->once())
- ->method('getTokenById')
- ->with($this->equalTo(42))
- ->willReturn($token);
-
- $this->assertSame($token, $this->tokenProvider->getTokenById(42));
- }
-
- public function testGetInvalidTokenById(): void {
- $this->expectException(InvalidTokenException::class);
-
- $this->mapper->expects($this->once())
- ->method('getTokenById')
- ->with($this->equalTo(42))
- ->willThrowException(new DoesNotExistException('nope'));
-
- $this->tokenProvider->getTokenById(42);
- }
-
- public function testGetExpiredTokenById(): void {
- $token = new PublicKeyToken();
- $token->setExpires(42);
-
- $this->mapper->expects($this->once())
- ->method('getTokenById')
- ->with($this->equalTo(42))
- ->willReturn($token);
-
- try {
- $this->tokenProvider->getTokenById(42);
- $this->fail();
- } catch (ExpiredTokenException $e) {
- $this->assertSame($token, $e->getToken());
- }
- }
-
- public function testRotate(): void {
- $token = 'oldtokentokentokentokentoken';
- $uid = 'user';
- $user = 'User';
- $password = 'password';
- $name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
- $type = IToken::PERMANENT_TOKEN;
-
- $this->config->method('getSystemValueBool')
- ->willReturnMap([
- ['auth.storeCryptedPassword', true, true],
- ]);
- $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
-
- $new = $this->tokenProvider->rotate($actual, 'oldtokentokentokentokentoken', 'newtokentokentokentokentoken');
-
- $this->assertSame('password', $this->tokenProvider->getPassword($new, 'newtokentokentokentokentoken'));
- }
-
- public function testRotateNoPassword(): void {
- $token = 'oldtokentokentokentokentoken';
- $uid = 'user';
- $user = 'User';
- $password = null;
- $name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
- $type = IToken::PERMANENT_TOKEN;
-
- $actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
-
- $oldPrivate = $actual->getPrivateKey();
-
- $new = $this->tokenProvider->rotate($actual, 'oldtokentokentokentokentoken', 'newtokentokentokentokentoken');
-
- $newPrivate = $new->getPrivateKey();
-
- $this->assertNotSame($newPrivate, $oldPrivate);
- $this->assertNull($new->getPassword());
- }
-
- public function testMarkPasswordInvalidInvalidToken(): void {
- $token = $this->createMock(IToken::class);
-
- $this->expectException(InvalidTokenException::class);
-
- $this->tokenProvider->markPasswordInvalid($token, 'tokenId');
- }
-
- public function testMarkPasswordInvalid(): void {
- $token = $this->createMock(PublicKeyToken::class);
- $token->expects($this->once())
- ->method('getToken')
- ->willReturn('token');
-
- $token->expects($this->once())
- ->method('setPasswordInvalid')
- ->with(true);
- $this->mapper->expects($this->once())
- ->method('update')
- ->with($token);
-
- $this->tokenProvider->markPasswordInvalid($token, 'tokenId');
- }
-
- public function testUpdatePasswords(): void {
- $uid = 'myUID';
- $token1 = $this->tokenProvider->generateToken(
- 'foobetokentokentokentoken',
- $uid,
- $uid,
- 'bar',
- 'random1',
- IToken::PERMANENT_TOKEN,
- IToken::REMEMBER);
- $token2 = $this->tokenProvider->generateToken(
- 'foobartokentokentokentoken',
- $uid,
- $uid,
- 'bar',
- 'random2',
- IToken::PERMANENT_TOKEN,
- IToken::REMEMBER);
- $this->config->method('getSystemValueBool')
- ->willReturnMap([
- ['auth.storeCryptedPassword', true, true],
- ]);
-
- $this->mapper->method('hasExpiredTokens')
- ->with($uid)
- ->willReturn(true);
- $this->mapper->expects($this->once())
- ->method('getTokenByUser')
- ->with($uid)
- ->willReturn([$token1, $token2]);
- $this->mapper->expects($this->exactly(2))
- ->method('update')
- ->with($this->callback(function (PublicKeyToken $t) use ($token1, $token2) {
- return $t === $token1 || $t === $token2;
- }));
-
- $this->tokenProvider->updatePasswords($uid, 'bar2');
- }
-}
--- a/tests/lib/Authentication/Token/PublicKeyTokenTest.php
+++ /dev/null
@@ -1,29 +0,0 @@
- false];
- $token = new PublicKeyToken();
- $token->setScope($scope);
- $this->assertEquals(json_encode($scope), $token->getScope());
- $this->assertEquals($scope, $token->getScopeAsArray());
- }
-
- public function testDefaultScope(): void {
- $scope = [IToken::SCOPE_FILESYSTEM => true];
- $token = new PublicKeyToken();
- $this->assertEquals($scope, $token->getScopeAsArray());
- }
-}
--- a/tests/lib/Authentication/Token/RemoteWipeTest.php
+++ /dev/null
@@ -1,173 +0,0 @@
-tokenProvider = $this->createMock(ITokenProvider::class);
- $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $this->remoteWipe = new RemoteWipe(
- $this->tokenProvider,
- $this->eventDispatcher,
- $this->logger
- );
- }
-
- public function testMarkNonWipableTokenForWipe(): void {
- $token = $this->createMock(IToken::class);
- $result = $this->remoteWipe->markTokenForWipe($token);
- $this->assertFalse($result);
- }
-
- public function testMarkTokenForWipe(): void {
- $token = $this->createMock(IWipeableToken::class);
- $token->expects($this->once())
- ->method('wipe');
-
- $this->tokenProvider->expects($this->once())
- ->method('updateToken')
- ->with($token);
-
- $result = $this->remoteWipe->markTokenForWipe($token);
- $this->assertTrue($result);
- }
-
- public function testMarkAllTokensForWipeNoWipeableToken(): void {
- /** @var IUser|MockObject $user */
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('user123');
- $token1 = $this->createMock(IToken::class);
- $token2 = $this->createMock(IToken::class);
- $this->tokenProvider->expects($this->once())
- ->method('getTokenByUser')
- ->with('user123')
- ->willReturn([$token1, $token2]);
-
- $result = $this->remoteWipe->markAllTokensForWipe($user);
-
- $this->assertFalse($result);
- }
-
- public function testMarkAllTokensForWipe(): void {
- /** @var IUser|MockObject $user */
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('user123');
- $token1 = $this->createMock(IToken::class);
- $token2 = $this->createMock(IWipeableToken::class);
- $this->tokenProvider->expects($this->once())
- ->method('getTokenByUser')
- ->with('user123')
- ->willReturn([$token1, $token2]);
- $token2->expects($this->once())
- ->method('wipe');
- $this->tokenProvider->expects($this->once())
- ->method('updateToken')
- ->with($token2);
-
- $result = $this->remoteWipe->markAllTokensForWipe($user);
-
- $this->assertTrue($result);
- }
-
- public function testStartWipingNotAWipeToken(): void {
- $token = $this->createMock(IToken::class);
- $this->tokenProvider->expects($this->once())
- ->method('getToken')
- ->with('tk1')
- ->willReturn($token);
- $this->eventDispatcher->expects($this->never())
- ->method('dispatch');
-
- $result = $this->remoteWipe->start('tk1');
-
- $this->assertFalse($result);
- }
-
- public function testStartWiping(): void {
- $token = $this->createMock(IToken::class);
- $this->tokenProvider->expects($this->once())
- ->method('getToken')
- ->with('tk1')
- ->willThrowException(new WipeTokenException($token));
- $this->eventDispatcher->expects($this->once())
- ->method('dispatch');
- $this->eventDispatcher->expects($this->once())
- ->method('dispatch')
- ->with(RemoteWipeStarted::class, $this->equalTo(new RemoteWipeStarted($token)));
-
- $result = $this->remoteWipe->start('tk1');
-
- $this->assertTrue($result);
- }
-
- public function testFinishWipingNotAWipeToken(): void {
- $token = $this->createMock(IToken::class);
- $this->tokenProvider->expects($this->once())
- ->method('getToken')
- ->with('tk1')
- ->willReturn($token);
- $this->eventDispatcher->expects($this->never())
- ->method('dispatch');
-
- $result = $this->remoteWipe->finish('tk1');
-
- $this->assertFalse($result);
- }
-
- public function startFinishWiping() {
- $token = $this->createMock(IToken::class);
- $this->tokenProvider->expects($this->once())
- ->method('getToken')
- ->with('tk1')
- ->willThrowException(new WipeTokenException($token));
- $this->eventDispatcher->expects($this->once())
- ->method('dispatch');
- $this->tokenProvider->expects($this->once())
- ->method('invalidateToken')
- ->with($token);
- $this->eventDispatcher->expects($this->once())
- ->method('dispatch')
- ->with(RemoteWipeFinished::class, $this->equalTo(new RemoteWipeFinished($token)));
-
- $result = $this->remoteWipe->finish('tk1');
-
- $this->assertTrue($result);
- }
-}
--- a/tests/lib/Authentication/TwoFactorAuth/Db/ProviderUserAssignmentDaoTest.php
+++ /dev/null
@@ -1,158 +0,0 @@
-dbConn = Server::get(IDBConnection::class);
- $qb = $this->dbConn->getQueryBuilder();
- $q = $qb->delete(ProviderUserAssignmentDao::TABLE_NAME);
- $q->executeStatement();
-
- $this->dao = new ProviderUserAssignmentDao($this->dbConn);
- }
-
- public function testGetState(): void {
- $qb = $this->dbConn->getQueryBuilder();
- $q1 = $qb->insert(ProviderUserAssignmentDao::TABLE_NAME)->values([
- 'provider_id' => $qb->createNamedParameter('twofactor_u2f'),
- 'uid' => $qb->createNamedParameter('user123'),
- 'enabled' => $qb->createNamedParameter(1),
- ]);
- $q1->executeStatement();
- $q2 = $qb->insert(ProviderUserAssignmentDao::TABLE_NAME)->values([
- 'provider_id' => $qb->createNamedParameter('twofactor_totp'),
- 'uid' => $qb->createNamedParameter('user123'),
- 'enabled' => $qb->createNamedParameter(0),
- ]);
- $q2->executeStatement();
- $expected = [
- 'twofactor_u2f' => true,
- 'twofactor_totp' => false,
- ];
-
- $state = $this->dao->getState('user123');
-
- $this->assertEquals($expected, $state);
- }
-
- public function testPersist(): void {
- $qb = $this->dbConn->getQueryBuilder();
-
- $this->dao->persist('twofactor_totp', 'user123', 0);
-
- $q = $qb
- ->select('*')
- ->from(ProviderUserAssignmentDao::TABLE_NAME)
- ->where($qb->expr()->eq('provider_id', $qb->createNamedParameter('twofactor_totp')))
- ->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter('user123')))
- ->andWhere($qb->expr()->eq('enabled', $qb->createNamedParameter(0)));
- $res = $q->executeQuery();
- $data = $res->fetchAllAssociative();
- $res->closeCursor();
- $this->assertCount(1, $data);
- }
-
- public function testPersistTwice(): void {
- $qb = $this->dbConn->getQueryBuilder();
-
- $this->dao->persist('twofactor_totp', 'user123', 0);
- $this->dao->persist('twofactor_totp', 'user123', 1);
-
- $q = $qb
- ->select('*')
- ->from(ProviderUserAssignmentDao::TABLE_NAME)
- ->where($qb->expr()->eq('provider_id', $qb->createNamedParameter('twofactor_totp')))
- ->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter('user123')))
- ->andWhere($qb->expr()->eq('enabled', $qb->createNamedParameter(1)));
- $res = $q->executeQuery();
- $data = $res->fetchAllAssociative();
- $res->closeCursor();
-
- $this->assertCount(1, $data);
- }
-
- public function testPersistSameStateTwice(): void {
- $qb = $this->dbConn->getQueryBuilder();
-
- $this->dao->persist('twofactor_totp', 'user123', 1);
- $this->dao->persist('twofactor_totp', 'user123', 1);
-
- $q = $qb
- ->select('*')
- ->from(ProviderUserAssignmentDao::TABLE_NAME)
- ->where($qb->expr()->eq('provider_id', $qb->createNamedParameter('twofactor_totp')))
- ->andWhere($qb->expr()->eq('uid', $qb->createNamedParameter('user123')))
- ->andWhere($qb->expr()->eq('enabled', $qb->createNamedParameter(1)));
- $res = $q->executeQuery();
- $data = $res->fetchAllAssociative();
- $res->closeCursor();
-
- $this->assertCount(1, $data);
- }
-
- public function testDeleteByUser(): void {
- $this->dao->persist('twofactor_fail', 'user1', 1);
- $this->dao->persist('twofactor_u2f', 'user1', 1);
- $this->dao->persist('twofactor_fail', 'user2', 0);
- $this->dao->persist('twofactor_u2f', 'user2', 0);
-
- $deleted = $this->dao->deleteByUser('user1');
-
- $this->assertEquals(
- [
- [
- 'uid' => 'user1',
- 'provider_id' => 'twofactor_fail',
- 'enabled' => true,
- ],
- [
- 'uid' => 'user1',
- 'provider_id' => 'twofactor_u2f',
- 'enabled' => true,
- ],
- ],
- $deleted
- );
- $statesUser1 = $this->dao->getState('user1');
- $statesUser2 = $this->dao->getState('user2');
- $this->assertCount(0, $statesUser1);
- $this->assertCount(2, $statesUser2);
- }
-
- public function testDeleteAll(): void {
- $this->dao->persist('twofactor_fail', 'user1', 1);
- $this->dao->persist('twofactor_u2f', 'user1', 1);
- $this->dao->persist('twofactor_fail', 'user2', 0);
- $this->dao->persist('twofactor_u2f', 'user1', 0);
-
- $this->dao->deleteAll('twofactor_fail');
-
- $statesUser1 = $this->dao->getState('user1');
- $statesUser2 = $this->dao->getState('user2');
- $this->assertCount(1, $statesUser1);
- $this->assertCount(0, $statesUser2);
- }
-}
--- a/tests/lib/Authentication/TwoFactorAuth/EnforcementStateTest.php
+++ /dev/null
@@ -1,51 +0,0 @@
-assertTrue($state->isEnforced());
- }
-
- public function testGetEnforcedGroups(): void {
- $state = new EnforcementState(true, ['twofactorers']);
-
- $this->assertEquals(['twofactorers'], $state->getEnforcedGroups());
- }
-
- public function testGetExcludedGroups(): void {
- $state = new EnforcementState(true, [], ['yoloers']);
-
- $this->assertEquals(['yoloers'], $state->getExcludedGroups());
- }
-
- public function testJsonSerialize(): void {
- $state = new EnforcementState(true, ['twofactorers'], ['yoloers']);
- $expected = [
- 'enforced' => true,
- 'enforcedGroups' => ['twofactorers'],
- 'excludedGroups' => ['yoloers'],
- ];
-
- $json = $state->jsonSerialize();
-
- $this->assertEquals($expected, $json);
- }
-}
--- a/tests/lib/Authentication/TwoFactorAuth/ManagerTest.php
+++ /dev/null
@@ -1,760 +0,0 @@
-user = $this->createMock(IUser::class);
- $this->user->expects($this->any())
- ->method('getUID')
- ->willReturn('user-uid');
- $this->providerLoader = $this->createMock(ProviderLoader::class);
- $this->providerRegistry = $this->createMock(IRegistry::class);
- $this->mandatoryTwoFactor = $this->createMock(MandatoryTwoFactor::class);
- $this->session = $this->createMock(ISession::class);
- $this->config = $this->createMock(IConfig::class);
- $this->activityManager = $this->createMock(IManager::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->tokenProvider = $this->createMock(TokenProvider::class);
- $this->timeFactory = $this->createMock(ITimeFactory::class);
- $this->dispatcher = $this->createMock(IEventDispatcher::class);
-
- $this->manager = new Manager(
- $this->providerLoader,
- $this->providerRegistry,
- $this->mandatoryTwoFactor,
- $this->session,
- $this->config,
- $this->activityManager,
- $this->logger,
- $this->tokenProvider,
- $this->timeFactory,
- $this->dispatcher,
- );
-
- $this->fakeProvider = $this->createMock(IProvider::class);
- $this->fakeProvider->method('getId')->willReturn('email');
-
- $this->backupProvider = $this->getMockBuilder('\OCP\Authentication\TwoFactorAuth\IProvider')->getMock();
- $this->backupProvider->method('getId')->willReturn('backup_codes');
- $this->backupProvider->method('isTwoFactorAuthEnabledForUser')->willReturn(true);
- }
-
- private function prepareNoProviders() {
- $this->providerLoader->method('getProviders')
- ->with($this->user)
- ->willReturn([]);
- }
-
- private function prepareProviders() {
- $this->providerRegistry->expects($this->once())
- ->method('getProviderStates')
- ->with($this->user)
- ->willReturn([
- $this->fakeProvider->getId() => true,
- ]);
- $this->providerLoader->expects($this->once())
- ->method('getProviders')
- ->with($this->user)
- ->willReturn([$this->fakeProvider]);
- }
-
- private function prepareProvidersWitBackupProvider() {
- $this->providerLoader->method('getProviders')
- ->with($this->user)
- ->willReturn([
- $this->fakeProvider,
- $this->backupProvider,
- ]);
- }
-
- public function testIsTwoFactorAuthenticatedEnforced(): void {
- $this->mandatoryTwoFactor->expects($this->once())
- ->method('isEnforcedFor')
- ->with($this->user)
- ->willReturn(true);
-
- $enabled = $this->manager->isTwoFactorAuthenticated($this->user);
-
- $this->assertTrue($enabled);
- }
-
- public function testIsTwoFactorAuthenticatedNoProviders(): void {
- $this->mandatoryTwoFactor->expects($this->once())
- ->method('isEnforcedFor')
- ->with($this->user)
- ->willReturn(false);
- $this->providerRegistry->expects($this->once())
- ->method('getProviderStates')
- ->willReturn([]); // No providers registered
- $this->providerLoader->expects($this->once())
- ->method('getProviders')
- ->willReturn([]); // No providers loadable
-
- $this->assertFalse($this->manager->isTwoFactorAuthenticated($this->user));
- }
-
- public function testIsTwoFactorAuthenticatedOnlyBackupCodes(): void {
- $this->mandatoryTwoFactor->expects($this->once())
- ->method('isEnforcedFor')
- ->with($this->user)
- ->willReturn(false);
- $this->providerRegistry->expects($this->once())
- ->method('getProviderStates')
- ->willReturn([
- 'backup_codes' => true,
- ]);
- $backupCodesProvider = $this->createMock(IProvider::class);
- $backupCodesProvider
- ->method('getId')
- ->willReturn('backup_codes');
- $this->providerLoader->expects($this->once())
- ->method('getProviders')
- ->willReturn([
- $backupCodesProvider,
- ]);
-
- $this->assertFalse($this->manager->isTwoFactorAuthenticated($this->user));
- }
-
- public function testIsTwoFactorAuthenticatedFailingProviders(): void {
- $this->mandatoryTwoFactor->expects($this->once())
- ->method('isEnforcedFor')
- ->with($this->user)
- ->willReturn(false);
- $this->providerRegistry->expects($this->once())
- ->method('getProviderStates')
- ->willReturn([
- 'twofactor_totp' => true,
- 'twofactor_u2f' => false,
- ]); // Two providers registered, but …
- $this->providerLoader->expects($this->once())
- ->method('getProviders')
- ->willReturn([]); // … none of them is able to load, however …
-
- // … 2FA is still enforced
- $this->assertTrue($this->manager->isTwoFactorAuthenticated($this->user));
- }
-
- public static function providerStatesFixData(): array {
- return [
- [false, false],
- [true, true],
- ];
- }
-
- /**
- * If the 2FA registry has not been populated when a user logs in,
- * the 2FA manager has to first fix the state before it checks for
- * enabled providers.
- *
- * If any of these providers is active, 2FA is enabled
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('providerStatesFixData')]
- public function testIsTwoFactorAuthenticatedFixesProviderStates(bool $providerEnabled, bool $expected): void {
- $this->providerRegistry->expects($this->once())
- ->method('getProviderStates')
- ->willReturn([]); // Nothing registered yet
- $this->providerLoader->expects($this->once())
- ->method('getProviders')
- ->willReturn([
- $this->fakeProvider
- ]);
- $this->fakeProvider->expects($this->once())
- ->method('isTwoFactorAuthEnabledForUser')
- ->with($this->user)
- ->willReturn($providerEnabled);
- if ($providerEnabled) {
- $this->providerRegistry->expects($this->once())
- ->method('enableProviderFor')
- ->with(
- $this->fakeProvider,
- $this->user
- );
- } else {
- $this->providerRegistry->expects($this->once())
- ->method('disableProviderFor')
- ->with(
- $this->fakeProvider,
- $this->user
- );
- }
-
- $this->assertEquals($expected, $this->manager->isTwoFactorAuthenticated($this->user));
- }
-
- public function testGetProvider(): void {
- $this->providerRegistry->expects($this->once())
- ->method('getProviderStates')
- ->with($this->user)
- ->willReturn([
- $this->fakeProvider->getId() => true,
- ]);
- $this->providerLoader->expects($this->once())
- ->method('getProviders')
- ->with($this->user)
- ->willReturn([$this->fakeProvider]);
-
- $provider = $this->manager->getProvider($this->user, $this->fakeProvider->getId());
-
- $this->assertSame($this->fakeProvider, $provider);
- }
-
- public function testGetInvalidProvider(): void {
- $this->providerRegistry->expects($this->once())
- ->method('getProviderStates')
- ->with($this->user)
- ->willReturn([]);
- $this->providerLoader->expects($this->once())
- ->method('getProviders')
- ->with($this->user)
- ->willReturn([]);
-
- $provider = $this->manager->getProvider($this->user, 'nonexistent');
-
- $this->assertNull($provider);
- }
-
- public function testGetLoginSetupProviders(): void {
- $provider1 = $this->createMock(IProvider::class);
- $provider2 = $this->createMock(IActivatableAtLogin::class);
- $this->providerLoader->expects($this->once())
- ->method('getProviders')
- ->with($this->user)
- ->willReturn([
- $provider1,
- $provider2,
- ]);
-
- $providers = $this->manager->getLoginSetupProviders($this->user);
-
- $this->assertCount(1, $providers);
- $this->assertSame($provider2, reset($providers));
- }
-
- public function testGetProviders(): void {
- $this->providerRegistry->expects($this->once())
- ->method('getProviderStates')
- ->with($this->user)
- ->willReturn([
- $this->fakeProvider->getId() => true,
- ]);
- $this->providerLoader->expects($this->once())
- ->method('getProviders')
- ->with($this->user)
- ->willReturn([$this->fakeProvider]);
- $expectedProviders = [
- 'email' => $this->fakeProvider,
- ];
-
- $providerSet = $this->manager->getProviderSet($this->user);
- $providers = $providerSet->getProviders();
-
- $this->assertEquals($expectedProviders, $providers);
- $this->assertFalse($providerSet->isProviderMissing());
- }
-
- public function testGetProvidersOneMissing(): void {
- $this->providerRegistry->expects($this->once())
- ->method('getProviderStates')
- ->with($this->user)
- ->willReturn([
- $this->fakeProvider->getId() => true,
- ]);
- $this->providerLoader->expects($this->once())
- ->method('getProviders')
- ->with($this->user)
- ->willReturn([]);
- $expectedProviders = [
- 'email' => $this->fakeProvider,
- ];
-
- $providerSet = $this->manager->getProviderSet($this->user);
-
- $this->assertTrue($providerSet->isProviderMissing());
- }
-
- public function testVerifyChallenge(): void {
- $this->prepareProviders();
-
- $challenge = 'passme';
- $event = $this->createMock(IEvent::class);
- $this->fakeProvider->expects($this->once())
- ->method('verifyChallenge')
- ->with($this->user, $challenge)
- ->willReturn(true);
- $this->session->expects($this->once())
- ->method('get')
- ->with('two_factor_remember_login')
- ->willReturn(false);
-
- $calls = [
- ['two_factor_auth_uid'],
- ['two_factor_remember_login'],
- ];
- $this->session->expects($this->exactly(2))
- ->method('remove')
- ->willReturnCallback(function () use (&$calls): void {
- $expected = array_shift($calls);
- $this->assertEquals($expected, func_get_args());
- });
-
- $this->session->expects($this->once())
- ->method('set')
- ->with(Manager::SESSION_UID_DONE, $this->user->getUID());
- $this->session->method('getId')
- ->willReturn('mysessionid');
- $this->activityManager->expects($this->once())
- ->method('generateEvent')
- ->willReturn($event);
- $event->expects($this->once())
- ->method('setApp')
- ->with($this->equalTo('core'))
- ->willReturnSelf();
- $event->expects($this->once())
- ->method('setType')
- ->with($this->equalTo('security'))
- ->willReturnSelf();
- $event->expects($this->once())
- ->method('setAuthor')
- ->with($this->equalTo($this->user->getUID()))
- ->willReturnSelf();
- $event->expects($this->once())
- ->method('setAffectedUser')
- ->with($this->equalTo($this->user->getUID()))
- ->willReturnSelf();
- $this->fakeProvider
- ->method('getDisplayName')
- ->willReturn('Fake 2FA');
- $event->expects($this->once())
- ->method('setSubject')
- ->with($this->equalTo('twofactor_success'), $this->equalTo([
- 'provider' => 'Fake 2FA',
- ]))
- ->willReturnSelf();
- $token = $this->createMock(IToken::class);
- $this->tokenProvider->method('getToken')
- ->with('mysessionid')
- ->willReturn($token);
- $token->method('getId')
- ->willReturn(42);
- $this->config->expects($this->once())
- ->method('deleteUserValue')
- ->with($this->user->getUID(), 'login_token_2fa', '42');
-
- $result = $this->manager->verifyChallenge('email', $this->user, $challenge);
-
- $this->assertTrue($result);
- }
-
- public function testVerifyChallengeInvalidProviderId(): void {
- $this->prepareProviders();
-
- $challenge = 'passme';
- $this->fakeProvider->expects($this->never())
- ->method('verifyChallenge')
- ->with($this->user, $challenge);
- $this->session->expects($this->never())
- ->method('remove');
-
- $this->assertFalse($this->manager->verifyChallenge('dontexist', $this->user, $challenge));
- }
-
- public function testVerifyInvalidChallenge(): void {
- $this->prepareProviders();
-
- $challenge = 'dontpassme';
- $event = $this->createMock(IEvent::class);
- $this->fakeProvider->expects($this->once())
- ->method('verifyChallenge')
- ->with($this->user, $challenge)
- ->willReturn(false);
- $this->session->expects($this->never())
- ->method('remove');
- $this->activityManager->expects($this->once())
- ->method('generateEvent')
- ->willReturn($event);
- $event->expects($this->once())
- ->method('setApp')
- ->with($this->equalTo('core'))
- ->willReturnSelf();
- $event->expects($this->once())
- ->method('setType')
- ->with($this->equalTo('security'))
- ->willReturnSelf();
- $event->expects($this->once())
- ->method('setAuthor')
- ->with($this->equalTo($this->user->getUID()))
- ->willReturnSelf();
- $event->expects($this->once())
- ->method('setAffectedUser')
- ->with($this->equalTo($this->user->getUID()))
- ->willReturnSelf();
- $this->fakeProvider
- ->method('getDisplayName')
- ->willReturn('Fake 2FA');
- $event->expects($this->once())
- ->method('setSubject')
- ->with($this->equalTo('twofactor_failed'), $this->equalTo([
- 'provider' => 'Fake 2FA',
- ]))
- ->willReturnSelf();
-
- $this->assertFalse($this->manager->verifyChallenge('email', $this->user, $challenge));
- }
-
- public function testNeedsSecondFactor(): void {
- $user = $this->createMock(IUser::class);
-
- $calls = [
- ['app_password'],
- ['two_factor_auth_uid'],
- [Manager::SESSION_UID_DONE],
- ];
- $this->session->expects($this->exactly(3))
- ->method('exists')
- ->willReturnCallback(function () use (&$calls) {
- $expected = array_shift($calls);
- $this->assertEquals($expected, func_get_args());
- return false;
- });
-
- $this->session->method('getId')
- ->willReturn('mysessionid');
- $token = $this->createMock(IToken::class);
- $this->tokenProvider->method('getToken')
- ->with('mysessionid')
- ->willReturn($token);
- $token->method('getId')
- ->willReturn(42);
-
- $user->method('getUID')
- ->willReturn('user');
- $this->config->method('getUserKeys')
- ->with('user', 'login_token_2fa')
- ->willReturn([
- '42'
- ]);
-
- $manager = $this->getMockBuilder(Manager::class)
- ->setConstructorArgs([
- $this->providerLoader,
- $this->providerRegistry,
- $this->mandatoryTwoFactor,
- $this->session,
- $this->config,
- $this->activityManager,
- $this->logger,
- $this->tokenProvider,
- $this->timeFactory,
- $this->dispatcher,
- ])
- ->onlyMethods(['isTwoFactorAuthenticated'])// Do not actually load the apps
- ->getMock();
-
- $manager->method('isTwoFactorAuthenticated')
- ->with($user)
- ->willReturn(true);
-
- $this->assertTrue($manager->needsSecondFactor($user));
- }
-
- public function testNeedsSecondFactorUserIsNull(): void {
- $user = null;
- $this->session->expects($this->never())
- ->method('exists');
-
- $this->assertFalse($this->manager->needsSecondFactor($user));
- }
-
- public function testNeedsSecondFactorWithNoProviderAvailableAnymore(): void {
- $this->prepareNoProviders();
-
- $user = null;
- $this->session->expects($this->never())
- ->method('exists')
- ->with('two_factor_auth_uid')
- ->willReturn(true);
- $this->session->expects($this->never())
- ->method('remove')
- ->with('two_factor_auth_uid');
-
- $this->assertFalse($this->manager->needsSecondFactor($user));
- }
-
- public function testPrepareTwoFactorLogin(): void {
- $calls = [
- ['two_factor_auth_uid', $this->user->getUID()],
- ['two_factor_remember_login', true],
- ];
- $this->session->expects($this->exactly(2))
- ->method('set')
- ->willReturnCallback(function () use (&$calls): void {
- $expected = array_shift($calls);
- $this->assertEquals($expected, func_get_args());
- });
-
- $this->session->method('getId')
- ->willReturn('mysessionid');
- $token = $this->createMock(IToken::class);
- $this->tokenProvider->method('getToken')
- ->with('mysessionid')
- ->willReturn($token);
- $token->method('getId')
- ->willReturn(42);
-
- $this->timeFactory->method('getTime')
- ->willReturn(1337);
-
- $this->config->method('setUserValue')
- ->with($this->user->getUID(), 'login_token_2fa', '42', '1337');
-
- $this->manager->prepareTwoFactorLogin($this->user, true);
- }
-
- public function testPrepareTwoFactorLoginDontRemember(): void {
- $calls = [
- ['two_factor_auth_uid', $this->user->getUID()],
- ['two_factor_remember_login', false],
- ];
- $this->session->expects($this->exactly(2))
- ->method('set')
- ->willReturnCallback(function () use (&$calls): void {
- $expected = array_shift($calls);
- $this->assertEquals($expected, func_get_args());
- });
-
- $this->session->method('getId')
- ->willReturn('mysessionid');
- $token = $this->createMock(IToken::class);
- $this->tokenProvider->method('getToken')
- ->with('mysessionid')
- ->willReturn($token);
- $token->method('getId')
- ->willReturn(42);
-
- $this->timeFactory->method('getTime')
- ->willReturn(1337);
-
- $this->config->method('setUserValue')
- ->with($this->user->getUID(), 'login_token_2fa', '42', '1337');
-
- $this->manager->prepareTwoFactorLogin($this->user, false);
- }
-
- public function testNeedsSecondFactorSessionAuth(): void {
- $user = $this->createMock(IUser::class);
- $user->method('getUID')
- ->willReturn('user');
-
- $this->session->method('exists')
- ->willReturnCallback(function ($var) {
- if ($var === Manager::SESSION_UID_KEY) {
- return false;
- } elseif ($var === 'app_password') {
- return false;
- } elseif ($var === 'app_api') {
- return false;
- }
- return true;
- });
- $this->session->method('get')
- ->willReturnCallback(function ($var) {
- if ($var === Manager::SESSION_UID_KEY) {
- return 'user';
- } elseif ($var === 'app_api') {
- return true;
- }
- return null;
- });
- $this->session->expects($this->once())
- ->method('get')
- ->willReturnMap([
- [Manager::SESSION_UID_DONE, 'user'],
- ['app_api', true]
- ]);
-
- $this->assertFalse($this->manager->needsSecondFactor($user));
- }
-
- public function testNeedsSecondFactorSessionAuthFailDBPass(): void {
- $user = $this->createMock(IUser::class);
- $user->method('getUID')
- ->willReturn('user');
-
- $this->session->method('exists')
- ->willReturn(false);
- $this->session->method('getId')
- ->willReturn('mysessionid');
-
- $token = $this->createMock(IToken::class);
- $token->method('getId')
- ->willReturn(40);
-
- $this->tokenProvider->method('getToken')
- ->with('mysessionid')
- ->willReturn($token);
-
- $this->config->method('getUserKeys')
- ->with('user', 'login_token_2fa')
- ->willReturn([
- '42', '43', '44'
- ]);
-
- $this->session->expects($this->once())
- ->method('set')
- ->with(Manager::SESSION_UID_DONE, 'user');
-
- $this->assertFalse($this->manager->needsSecondFactor($user));
- }
-
- public function testNeedsSecondFactorInvalidToken(): void {
- $this->prepareNoProviders();
-
- $user = $this->createMock(IUser::class);
- $user->method('getUID')
- ->willReturn('user');
-
- $this->session->method('exists')
- ->willReturn(false);
- $this->session->method('getId')
- ->willReturn('mysessionid');
-
- $this->tokenProvider->method('getToken')
- ->with('mysessionid')
- ->willThrowException(new InvalidTokenException());
-
- $this->config->method('getUserKeys')->willReturn([]);
-
- $this->assertFalse($this->manager->needsSecondFactor($user));
- }
-
- public function testNeedsSecondFactorAppPassword(): void {
- $user = $this->createMock(IUser::class);
- $this->session->method('exists')
- ->willReturnMap([
- ['app_password', true],
- ['app_api', true]
- ]);
-
- $this->assertFalse($this->manager->needsSecondFactor($user));
- }
-
- public function testClearTwoFactorPending() {
- $this->config->method('getUserKeys')
- ->with('theUserId', 'login_token_2fa')
- ->willReturn([
- '42', '43', '44'
- ]);
-
- $deleteUserValueCalls = [
- ['theUserId', 'login_token_2fa', '42'],
- ['theUserId', 'login_token_2fa', '43'],
- ['theUserId', 'login_token_2fa', '44'],
- ];
- $this->config->expects($this->exactly(3))
- ->method('deleteUserValue')
- ->willReturnCallback(function () use (&$deleteUserValueCalls): void {
- $expected = array_shift($deleteUserValueCalls);
- $this->assertEquals($expected, func_get_args());
- });
-
- $invalidateCalls = [
- ['theUserId', 42],
- ['theUserId', 43],
- ['theUserId', 44],
- ];
- $this->tokenProvider->expects($this->exactly(3))
- ->method('invalidateTokenById')
- ->willReturnCallback(function () use (&$invalidateCalls): void {
- $expected = array_shift($invalidateCalls);
- $this->assertEquals($expected, func_get_args());
- });
-
- $this->manager->clearTwoFactorPending('theUserId');
- }
-
- public function testClearTwoFactorPendingTokenDoesNotExist() {
- $this->config->method('getUserKeys')
- ->with('theUserId', 'login_token_2fa')
- ->willReturn([
- '42', '43', '44'
- ]);
-
- $deleteUserValueCalls = [
- ['theUserId', 'login_token_2fa', '42'],
- ['theUserId', 'login_token_2fa', '43'],
- ['theUserId', 'login_token_2fa', '44'],
- ];
- $this->config->expects($this->exactly(3))
- ->method('deleteUserValue')
- ->willReturnCallback(function () use (&$deleteUserValueCalls): void {
- $expected = array_shift($deleteUserValueCalls);
- $this->assertEquals($expected, func_get_args());
- });
-
- $invalidateCalls = [
- ['theUserId', 42],
- ['theUserId', 43],
- ['theUserId', 44],
- ];
- $this->tokenProvider->expects($this->exactly(3))
- ->method('invalidateTokenById')
- ->willReturnCallback(function ($user, $tokenId) use (&$invalidateCalls): void {
- $expected = array_shift($invalidateCalls);
- $this->assertEquals($expected, func_get_args());
- if ($tokenId === 43) {
- throw new DoesNotExistException('token does not exist');
- }
- });
-
- $this->manager->clearTwoFactorPending('theUserId');
- }
-}
--- a/tests/lib/Authentication/TwoFactorAuth/MandatoryTwoFactorTest.php
+++ /dev/null
@@ -1,180 +0,0 @@
-config = $this->createMock(IConfig::class);
- $this->groupManager = $this->createMock(IGroupManager::class);
-
- $this->mandatoryTwoFactor = new MandatoryTwoFactor($this->config, $this->groupManager);
- }
-
- public function testIsNotEnforced(): void {
- $this->config
- ->method('getSystemValue')
- ->willReturnMap([
- ['twofactor_enforced', 'false', 'false'],
- ['twofactor_enforced_groups', [], []],
- ['twofactor_enforced_excluded_groups', [], []],
- ]);
-
- $state = $this->mandatoryTwoFactor->getState();
-
- $this->assertFalse($state->isEnforced());
- }
-
- public function testIsEnforced(): void {
- $this->config
- ->method('getSystemValue')
- ->willReturnMap([
- ['twofactor_enforced', 'false', 'true'],
- ['twofactor_enforced_groups', [], []],
- ['twofactor_enforced_excluded_groups', [], []],
- ]);
-
- $state = $this->mandatoryTwoFactor->getState();
-
- $this->assertTrue($state->isEnforced());
- }
-
- public function testIsNotEnforcedForAnybody(): void {
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('user123');
- $this->config
- ->method('getSystemValue')
- ->willReturnMap([
- ['twofactor_enforced', 'false', 'false'],
- ['twofactor_enforced_groups', [], []],
- ['twofactor_enforced_excluded_groups', [], []],
- ]);
-
- $isEnforced = $this->mandatoryTwoFactor->isEnforcedFor($user);
-
- $this->assertFalse($isEnforced);
- }
-
- public function testIsEnforcedForAGroupMember(): void {
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('user123');
- $this->config
- ->method('getSystemValue')
- ->willReturnMap([
- ['twofactor_enforced', 'false', 'true'],
- ['twofactor_enforced_groups', [], ['twofactorers']],
- ['twofactor_enforced_excluded_groups', [], []],
- ]);
- $this->groupManager->method('isInGroup')
- ->willReturnCallback(function ($user, $group) {
- return $user === 'user123' && $group === 'twofactorers';
- });
-
- $isEnforced = $this->mandatoryTwoFactor->isEnforcedFor($user);
-
- $this->assertTrue($isEnforced);
- }
-
- public function testIsEnforcedForOtherGroups(): void {
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('user123');
- $this->config
- ->method('getSystemValue')
- ->willReturnMap([
- ['twofactor_enforced', 'false', 'true'],
- ['twofactor_enforced_groups', [], ['twofactorers']],
- ['twofactor_enforced_excluded_groups', [], []],
- ]);
- $this->groupManager->method('isInGroup')
- ->willReturn(false);
-
- $isEnforced = $this->mandatoryTwoFactor->isEnforcedFor($user);
-
- $this->assertFalse($isEnforced);
- }
-
- public function testIsEnforcedButMemberOfExcludedGroup(): void {
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('user123');
- $this->config
- ->method('getSystemValue')
- ->willReturnMap([
- ['twofactor_enforced', 'false', 'true'],
- ['twofactor_enforced_groups', [], []],
- ['twofactor_enforced_excluded_groups', [], ['yoloers']],
- ]);
- $this->groupManager->method('isInGroup')
- ->willReturnCallback(function ($user, $group) {
- return $user === 'user123' && $group === 'yoloers';
- });
-
- $isEnforced = $this->mandatoryTwoFactor->isEnforcedFor($user);
-
- $this->assertFalse($isEnforced);
- }
-
- public function testSetEnforced(): void {
- $this->config
- ->expects($this->exactly(3))
- ->method('setSystemValue')
- ->willReturnMap([
- ['twofactor_enforced', 'true'],
- ['twofactor_enforced_groups', []],
- ['twofactor_enforced_excluded_groups', []],
- ]);
-
- $this->mandatoryTwoFactor->setState(new EnforcementState(true));
- }
-
- public function testSetEnforcedForGroups(): void {
- $this->config
- ->expects($this->exactly(3))
- ->method('setSystemValue')
- ->willReturnMap([
- ['twofactor_enforced', 'true'],
- ['twofactor_enforced_groups', ['twofactorers']],
- ['twofactor_enforced_excluded_groups', ['yoloers']],
- ]);
-
- $this->mandatoryTwoFactor->setState(new EnforcementState(true, ['twofactorers'], ['yoloers']));
- }
-
- public function testSetNotEnforced(): void {
- $this->config
- ->expects($this->exactly(3))
- ->method('setSystemValue')
- ->willReturnMap([
- ['twofactor_enforced', 'false'],
- ['twofactor_enforced_groups', []],
- ['twofactor_enforced_excluded_groups', []],
- ]);
-
- $this->mandatoryTwoFactor->setState(new EnforcementState(false));
- }
-}
--- a/tests/lib/Authentication/TwoFactorAuth/ProviderLoaderTest.php
+++ /dev/null
@@ -1,120 +0,0 @@
-appManager = $this->createMock(IAppManager::class);
- $this->user = $this->createMock(IUser::class);
-
- $this->registrationContext = $this->createMock(RegistrationContext::class);
- $coordinator = $this->createMock(Coordinator::class);
- $coordinator->method('getRegistrationContext')
- ->willReturn($this->registrationContext);
-
- $this->loader = new ProviderLoader($this->appManager, $coordinator);
- }
-
- public function testFailHardIfProviderCanNotBeLoaded(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Could not load two-factor auth provider \\OCA\\MyFaulty2faApp\\DoesNotExist');
-
- $this->appManager->expects($this->once())
- ->method('getEnabledAppsForUser')
- ->with($this->user)
- ->willReturn(['mail', 'twofactor_totp']);
- $this->appManager
- ->method('getAppInfo')
- ->willReturnMap([
- ['mail', false, null, []],
- ['twofactor_totp', false, null, [
- 'two-factor-providers' => [
- '\\OCA\\MyFaulty2faApp\\DoesNotExist',
- ],
- ]],
- ]);
-
- $this->loader->getProviders($this->user);
- }
-
- public function testGetProviders(): void {
- $provider = $this->createMock(IProvider::class);
- $provider->method('getId')->willReturn('test');
- \OC::$server->registerService('\\OCA\\TwoFactorTest\\Provider', function () use ($provider) {
- return $provider;
- });
- $this->appManager->expects($this->once())
- ->method('getEnabledAppsForUser')
- ->with($this->user)
- ->willReturn(['twofactor_test']);
- $this->appManager
- ->method('getAppInfo')
- ->with('twofactor_test')
- ->willReturn(['two-factor-providers' => [
- '\\OCA\\TwoFactorTest\\Provider',
- ]]);
-
- $providers = $this->loader->getProviders($this->user);
-
- $this->assertCount(1, $providers);
- $this->assertArrayHasKey('test', $providers);
- $this->assertSame($provider, $providers['test']);
- }
-
- public function testGetProvidersBootstrap(): void {
- $provider = $this->createMock(IProvider::class);
- $provider->method('getId')->willReturn('test');
-
- \OC::$server->registerService('\\OCA\\TwoFactorTest\\Provider', function () use ($provider) {
- return $provider;
- });
-
- $this->appManager->expects($this->once())
- ->method('getEnabledAppsForUser')
- ->with($this->user)
- ->willReturn([]);
-
- $this->registrationContext->method('getTwoFactorProviders')
- ->willReturn([
- new ServiceRegistration('twofactor_test', '\\OCA\\TwoFactorTest\\Provider')
- ]);
-
- $providers = $this->loader->getProviders($this->user);
-
- $this->assertCount(1, $providers);
- $this->assertArrayHasKey('test', $providers);
- $this->assertSame($provider, $providers['test']);
- }
-}
--- a/tests/lib/Authentication/TwoFactorAuth/ProviderManagerTest.php
+++ /dev/null
@@ -1,135 +0,0 @@
-providerLoader = $this->createMock(ProviderLoader::class);
- $this->registry = $this->createMock(IRegistry::class);
-
- $this->providerManager = new ProviderManager(
- $this->providerLoader,
- $this->registry
- );
- }
-
- public function testTryEnableInvalidProvider(): void {
- $this->expectException(InvalidProviderException::class);
-
- $user = $this->createMock(IUser::class);
- $this->providerManager->tryEnableProviderFor('none', $user);
- }
-
- public function testTryEnableUnsupportedProvider(): void {
- $user = $this->createMock(IUser::class);
- $provider = $this->createMock(IProvider::class);
- $this->providerLoader->expects($this->once())
- ->method('getProviders')
- ->with($user)
- ->willReturn([
- 'u2f' => $provider,
- ]);
- $this->registry->expects($this->never())
- ->method('enableProviderFor');
-
- $res = $this->providerManager->tryEnableProviderFor('u2f', $user);
-
- $this->assertFalse($res);
- }
-
- public function testTryEnableProvider(): void {
- $user = $this->createMock(IUser::class);
- $provider = $this->createMock(IActivatableByAdmin::class);
- $this->providerLoader->expects($this->once())
- ->method('getProviders')
- ->with($user)
- ->willReturn([
- 'u2f' => $provider,
- ]);
- $provider->expects($this->once())
- ->method('enableFor')
- ->with($user);
- $this->registry->expects($this->once())
- ->method('enableProviderFor')
- ->with($provider, $user);
-
- $res = $this->providerManager->tryEnableProviderFor('u2f', $user);
-
- $this->assertTrue($res);
- }
-
- public function testTryDisableInvalidProvider(): void {
- $this->expectException(InvalidProviderException::class);
-
- $user = $this->createMock(IUser::class);
- $this->providerManager->tryDisableProviderFor('none', $user);
- }
-
- public function testTryDisableUnsupportedProvider(): void {
- $user = $this->createMock(IUser::class);
- $provider = $this->createMock(IProvider::class);
- $this->providerLoader->expects($this->once())
- ->method('getProviders')
- ->with($user)
- ->willReturn([
- 'u2f' => $provider,
- ]);
- $this->registry->expects($this->never())
- ->method('disableProviderFor');
-
- $res = $this->providerManager->tryDisableProviderFor('u2f', $user);
-
- $this->assertFalse($res);
- }
-
- public function testTryDisableProvider(): void {
- $user = $this->createMock(IUser::class);
- $provider = $this->createMock(IDeactivatableByAdmin::class);
- $this->providerLoader->expects($this->once())
- ->method('getProviders')
- ->with($user)
- ->willReturn([
- 'u2f' => $provider,
- ]);
- $provider->expects($this->once())
- ->method('disableFor')
- ->with($user);
- $this->registry->expects($this->once())
- ->method('disableProviderFor')
- ->with($provider, $user);
-
- $res = $this->providerManager->tryDisableProviderFor('u2f', $user);
-
- $this->assertTrue($res);
- }
-}
--- a/tests/lib/Authentication/TwoFactorAuth/ProviderSetTest.php
+++ /dev/null
@@ -1,75 +0,0 @@
-createMock(IProvider::class);
- $p1->method('getId')->willReturn('p1');
- $p2 = $this->createMock(IProvider::class);
- $p2->method('getId')->willReturn('p2');
- $expected = [
- 'p1' => $p1,
- 'p2' => $p2,
- ];
-
- $set = new ProviderSet([$p2, $p1], false);
-
- $this->assertEquals($expected, $set->getProviders());
- }
-
- public function testGet3rdPartyProviders(): void {
- $p1 = $this->createMock(IProvider::class);
- $p1->method('getId')->willReturn('p1');
- $p2 = $this->createMock(IProvider::class);
- $p2->method('getId')->willReturn('p2');
- $p3 = $this->createMock(BackupCodesProvider::class);
- $p3->method('getId')->willReturn('p3');
- $expected = [
- 'p1' => $p1,
- 'p2' => $p2,
- ];
-
- $set = new ProviderSet([$p2, $p1], false);
-
- $this->assertEquals($expected, $set->getPrimaryProviders());
- }
-
- public function testGetProvider(): void {
- $p1 = $this->createMock(IProvider::class);
- $p1->method('getId')->willReturn('p1');
-
- $set = new ProviderSet([$p1], false);
- $provider = $set->getProvider('p1');
-
- $this->assertEquals($p1, $provider);
- }
-
- public function testGetProviderNotFound(): void {
- $set = new ProviderSet([], false);
- $provider = $set->getProvider('p1');
-
- $this->assertNull($provider);
- }
-
- public function testIsProviderMissing(): void {
- $set = new ProviderSet([], true);
-
- $this->assertTrue($set->isProviderMissing());
- }
-}
--- a/tests/lib/Authentication/TwoFactorAuth/RegistryTest.php
+++ /dev/null
@@ -1,164 +0,0 @@
-dao = $this->createMock(ProviderUserAssignmentDao::class);
- $this->dispatcher = $this->createMock(IEventDispatcher::class);
-
- $this->registry = new Registry($this->dao, $this->dispatcher);
- }
-
- public function testGetProviderStates(): void {
- $user = $this->createMock(IUser::class);
- $user->expects($this->once())->method('getUID')->willReturn('user123');
- $state = [
- 'twofactor_totp' => true,
- ];
- $this->dao->expects($this->once())->method('getState')->willReturn($state);
-
- $actual = $this->registry->getProviderStates($user);
-
- $this->assertEquals($state, $actual);
- }
-
- public function testEnableProvider(): void {
- $user = $this->createMock(IUser::class);
- $provider = $this->createMock(IProvider::class);
- $user->expects($this->once())->method('getUID')->willReturn('user123');
- $provider->expects($this->once())->method('getId')->willReturn('p1');
- $this->dao->expects($this->once())->method('persist')->with('p1', 'user123',
- true);
-
- $this->dispatcher->expects($this->once())
- ->method('dispatch')
- ->with(
- $this->equalTo(IRegistry::EVENT_PROVIDER_ENABLED),
- $this->callback(function (RegistryEvent $e) use ($user, $provider) {
- return $e->getUser() === $user && $e->getProvider() === $provider;
- })
- );
- $this->dispatcher->expects($this->once())
- ->method('dispatchTyped')
- ->with(new TwoFactorProviderForUserRegistered(
- $user,
- $provider,
- ));
-
- $this->registry->enableProviderFor($provider, $user);
- }
-
- public function testEnableStatelessProvider(): void {
- $user = $this->createMock(IUser::class);
- $provider = $this->createMock(IStatelessProvider::class);
-
- $this->dao->expects($this->never())->method('persist');
-
- $this->dispatcher->expects($this->never())->method('dispatch');
- $this->dispatcher->expects($this->never())->method('dispatchTyped');
-
- $this->registry->enableProviderFor($provider, $user);
- }
-
- public function testDisableProvider(): void {
- $user = $this->createMock(IUser::class);
- $provider = $this->createMock(IProvider::class);
- $user->expects($this->once())->method('getUID')->willReturn('user123');
- $provider->expects($this->once())->method('getId')->willReturn('p1');
- $this->dao->expects($this->once())->method('persist')->with('p1', 'user123',
- false);
-
- $this->dispatcher->expects($this->once())
- ->method('dispatch')
- ->with(
- $this->equalTo(IRegistry::EVENT_PROVIDER_DISABLED),
- $this->callback(function (RegistryEvent $e) use ($user, $provider) {
- return $e->getUser() === $user && $e->getProvider() === $provider;
- })
- );
- $this->dispatcher->expects($this->once())
- ->method('dispatchTyped')
- ->with(new TwoFactorProviderForUserUnregistered(
- $user,
- $provider,
- ));
-
- $this->registry->disableProviderFor($provider, $user);
- }
-
- public function testDisableStatelessProvider(): void {
- $user = $this->createMock(IUser::class);
- $provider = $this->createMock(IStatelessProvider::class);
-
- $this->dao->expects($this->never())->method('persist');
-
- $this->dispatcher->expects($this->never())->method('dispatch');
- $this->dispatcher->expects($this->never())->method('dispatchTyped');
-
- $this->registry->disableProviderFor($provider, $user);
- }
-
- public function testDeleteUserData(): void {
- $user = $this->createMock(IUser::class);
- $user->expects($this->once())->method('getUID')->willReturn('user123');
- $this->dao->expects($this->once())
- ->method('deleteByUser')
- ->with('user123')
- ->willReturn([
- [
- 'provider_id' => 'twofactor_u2f',
- ]
- ]);
-
- $calls = [
- [new TwoFactorProviderDisabled('twofactor_u2f')],
- [new TwoFactorProviderUserDeleted($user, 'twofactor_u2f')],
- ];
- $this->dispatcher->expects($this->exactly(2))
- ->method('dispatchTyped')
- ->willReturnCallback(function () use (&$calls): void {
- $expected = array_shift($calls);
- $this->assertEquals($expected, func_get_args());
- });
-
- $this->registry->deleteUserData($user);
- }
-
- public function testCleanUp(): void {
- $this->dao->expects($this->once())
- ->method('deleteAll')
- ->with('twofactor_u2f');
-
- $this->registry->cleanUp('twofactor_u2f');
- }
-}
--- a/tests/lib/Avatar/AvatarManagerTest.php
+++ /dev/null
@@ -1,336 +0,0 @@
-userSession = $this->createMock(IUserSession::class);
- $this->userManager = $this->createMock(Manager::class);
- $this->appData = $this->createMock(IAppData::class);
- $this->l10n = $this->createMock(IL10N::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->config = $this->createMock(IConfig::class);
- $this->accountManager = $this->createMock(IAccountManager::class);
- $this->knownUserService = $this->createMock(KnownUserService::class);
- $this->cloudIdManager = $this->createMock(ICloudIdManager::class);
-
- $this->avatarManager = new AvatarManager(
- $this->userSession,
- $this->userManager,
- $this->appData,
- $this->l10n,
- $this->logger,
- $this->config,
- $this->accountManager,
- $this->knownUserService,
- $this->cloudIdManager
- );
- }
-
- public function testGetAvatarForSelf(): void {
- $user = $this->createMock(User::class);
- $user
- ->expects($this->any())
- ->method('getUID')
- ->willReturn('valid-user');
-
- $user
- ->expects($this->any())
- ->method('isEnabled')
- ->willReturn(true);
-
- // requesting user
- $this->userSession->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
-
- $this->userManager
- ->expects($this->once())
- ->method('get')
- ->with('valid-user')
- ->willReturn($user);
-
- $account = $this->createMock(IAccount::class);
- $this->accountManager->expects($this->once())
- ->method('getAccount')
- ->with($user)
- ->willReturn($account);
-
- $property = $this->createMock(IAccountProperty::class);
- $account->expects($this->once())
- ->method('getProperty')
- ->with(IAccountManager::PROPERTY_AVATAR)
- ->willReturn($property);
-
- $property->expects($this->once())
- ->method('getScope')
- ->willReturn(IAccountManager::SCOPE_PRIVATE);
-
- $this->knownUserService->expects($this->any())
- ->method('isKnownToUser')
- ->with('valid-user', 'valid-user')
- ->willReturn(true);
-
- $folder = $this->createMock(ISimpleFolder::class);
- $this->appData
- ->expects($this->once())
- ->method('getFolder')
- ->with('valid-user')
- ->willReturn($folder);
-
- $expected = new UserAvatar($folder, $this->l10n, $user, $this->logger, $this->config);
- $this->assertEquals($expected, $this->avatarManager->getAvatar('valid-user'));
- }
-
- public function testGetAvatarValidUserDifferentCasing(): void {
- $user = $this->createMock(User::class);
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('vaLid-USER')
- ->willReturn($user);
-
- $user->expects($this->once())
- ->method('getUID')
- ->willReturn('valid-user');
-
- $user
- ->expects($this->any())
- ->method('isEnabled')
- ->willReturn(true);
-
- $this->userSession->expects($this->once())
- ->method('getUser')
- ->willReturn($user);
-
- $folder = $this->createMock(ISimpleFolder::class);
- $this->appData
- ->expects($this->once())
- ->method('getFolder')
- ->with('valid-user')
- ->willReturn($folder);
-
- $account = $this->createMock(IAccount::class);
- $this->accountManager->expects($this->once())
- ->method('getAccount')
- ->with($user)
- ->willReturn($account);
-
- $property = $this->createMock(IAccountProperty::class);
- $account->expects($this->once())
- ->method('getProperty')
- ->with(IAccountManager::PROPERTY_AVATAR)
- ->willReturn($property);
-
- $property->expects($this->once())
- ->method('getScope')
- ->willReturn(IAccountManager::SCOPE_FEDERATED);
-
- $expected = new UserAvatar($folder, $this->l10n, $user, $this->logger, $this->config);
- $this->assertEquals($expected, $this->avatarManager->getAvatar('vaLid-USER'));
- }
-
- public static function dataGetAvatarScopes(): array {
- return [
- // public access cannot see real avatar
- [IAccountManager::SCOPE_PRIVATE, true, false, true],
- // unknown users cannot see real avatar
- [IAccountManager::SCOPE_PRIVATE, false, false, true],
- // known users can see real avatar
- [IAccountManager::SCOPE_PRIVATE, false, true, false],
- [IAccountManager::SCOPE_LOCAL, false, false, false],
- [IAccountManager::SCOPE_LOCAL, true, false, false],
- [IAccountManager::SCOPE_FEDERATED, false, false, false],
- [IAccountManager::SCOPE_FEDERATED, true, false, false],
- [IAccountManager::SCOPE_PUBLISHED, false, false, false],
- [IAccountManager::SCOPE_PUBLISHED, true, false, false],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetAvatarScopes')]
- public function testGetAvatarScopes($avatarScope, $isPublicCall, $isKnownUser, $expectedPlaceholder): void {
- if ($isPublicCall) {
- $requestingUser = null;
- } else {
- $requestingUser = $this->createMock(IUser::class);
- $requestingUser->method('getUID')->willReturn('requesting-user');
- }
-
- // requesting user
- $this->userSession->expects($this->once())
- ->method('getUser')
- ->willReturn($requestingUser);
-
- $user = $this->createMock(User::class);
- $user
- ->expects($this->once())
- ->method('getUID')
- ->willReturn('valid-user');
-
- $user
- ->expects($this->any())
- ->method('isEnabled')
- ->willReturn(true);
-
- $this->userManager
- ->expects($this->once())
- ->method('get')
- ->with('valid-user')
- ->willReturn($user);
-
- $account = $this->createMock(IAccount::class);
- $this->accountManager->expects($this->once())
- ->method('getAccount')
- ->with($user)
- ->willReturn($account);
-
- $property = $this->createMock(IAccountProperty::class);
- $account->expects($this->once())
- ->method('getProperty')
- ->with(IAccountManager::PROPERTY_AVATAR)
- ->willReturn($property);
-
- $property->expects($this->once())
- ->method('getScope')
- ->willReturn($avatarScope);
-
- $folder = $this->createMock(ISimpleFolder::class);
- $this->appData
- ->expects($this->once())
- ->method('getFolder')
- ->with('valid-user')
- ->willReturn($folder);
-
- if (!$isPublicCall) {
- $this->knownUserService->expects($this->any())
- ->method('isKnownToUser')
- ->with('requesting-user', 'valid-user')
- ->willReturn($isKnownUser);
- } else {
- $this->knownUserService->expects($this->never())
- ->method('isKnownToUser');
- }
-
- if ($expectedPlaceholder) {
- $expected = new PlaceholderAvatar($folder, $user, $this->config, $this->logger);
- } else {
- $expected = new UserAvatar($folder, $this->l10n, $user, $this->logger, $this->config);
- }
- $this->assertEquals($expected, $this->avatarManager->getAvatar('valid-user'));
- }
-
- public function testGetAvatarInvalidUser(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('user does not exist');
-
- $this->userManager
- ->expects($this->once())
- ->method('get')
- ->with('invalidUser')
- ->willReturn(null);
-
- $this->avatarManager->getAvatar('invalidUser');
- }
-
- public function testGetAvatarForRemoteUser(): void {
- $cloudId = 'user@https://remote.example.com';
-
- $this->userManager
- ->expects($this->once())
- ->method('get')
- ->willReturn(null);
-
- $resolvedCloudId = $this->createMock(ICloudId::class);
- $resolvedCloudId->method('getUser')->willReturn('user');
- $resolvedCloudId->method('getRemote')->willReturn('https://remote.example.com');
- $resolvedCloudId->method('getDisplayId')->willReturn('user@remote.example.com');
-
- $this->cloudIdManager->expects($this->once())
- ->method('isValidCloudId')
- ->with($cloudId)
- ->willReturn(true);
- $this->cloudIdManager->method('resolveCloudId')
- ->with($cloudId)
- ->willReturn($resolvedCloudId);
- $this->overwriteService(ICloudIdManager::class, $this->cloudIdManager);
-
- $this->appData->expects($this->once())->method('getFolder');
- $this->accountManager->expects($this->never())->method('getAccount');
-
- $avatar = $this->avatarManager->getAvatar($cloudId);
-
- $this->assertInstanceOf(RemoteAvatar::class, $avatar);
- $this->assertTrue($avatar->exists());
- $this->assertTrue($avatar->isCustomAvatar());
- $this->assertSame('user@remote.example.com', $avatar->getDisplayName());
- }
-
- public function testGetAvatarThrowsForUnknownUserThatIsNotACloudId(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('user does not exist');
-
- $this->userManager
- ->expects($this->once())
- ->method('get')
- ->with('invalidUser')
- ->willReturn(null);
-
- $this->cloudIdManager->expects($this->once())
- ->method('isValidCloudId')
- ->with('invalidUser')
- ->willReturn(false);
-
- $this->avatarManager->getAvatar('invalidUser');
- }
-}
--- a/tests/lib/Avatar/GuestAvatarTest.php
+++ /dev/null
@@ -1,63 +0,0 @@
-createMock(LoggerInterface::class);
- $config = $this->createMock(IConfig::class);
- $this->guestAvatar = new GuestAvatar('einstein', $config, $logger);
- }
-
- /**
- * Asserts that testGet() returns the expected avatar.
- *
- * For the test a static name "einstein" is used and
- * the generated image is compared with an expected one.
- */
- public function testGet(): void {
- $avatar = $this->guestAvatar->getFile(32);
- self::assertInstanceOf(InMemoryFile::class, $avatar);
- $expectedFile = file_get_contents(
- __DIR__ . '/../../data/guest_avatar_einstein_32.png'
- );
- self::assertEquals(trim($expectedFile), trim($avatar->getContent()));
- }
-
- /**
- * Asserts that "testIsCustomAvatar" returns false for guests.
- *
- * @return void
- */
- public function testIsCustomAvatar(): void {
- self::assertFalse($this->guestAvatar->isCustomAvatar());
- }
-}
--- a/tests/lib/Avatar/RemoteAvatarTest.php
+++ /dev/null
@@ -1,145 +0,0 @@
-config = $this->createMock(IConfig::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $cloudId = $this->createMock(ICloudId::class);
- $cloudId->method('getUser')->willReturn('user');
- $cloudId->method('getRemote')->willReturn('https://remote.example.com');
- $cloudId->method('getDisplayId')->willReturn('user@remote.example.com');
-
- $this->cloudIdManager = $this->createMock(ICloudIdManager::class);
- $this->cloudIdManager->method('resolveCloudId')
- ->with(self::CLOUD_ID)
- ->willReturn($cloudId);
- $this->overwriteService(ICloudIdManager::class, $this->cloudIdManager);
-
- $this->clientService = $this->createMock(IClientService::class);
- $this->overwriteService(IClientService::class, $this->clientService);
-
- $this->folder = $this->createMock(ISimpleFolder::class);
- $this->avatar = new RemoteAvatar($this->folder, self::CLOUD_ID, $this->config, $this->logger);
- }
-
- /**
- * Stubs the client returned by IClientService::newClient() to respond to
- * a single GET request, optionally asserting the requested URL/options.
- *
- * @param string|resource|false $body
- */
- private function mockRemoteClient(string $contentType, string $body, ?string $expectedUrl = null, ?array $expectedOptions = null): void {
- $response = $this->createMock(IResponse::class);
- $response->method('getHeader')->with('Content-Type')->willReturn($contentType);
- $response->method('getBody')->willReturn($body);
-
- $client = $this->createMock(IClient::class);
- $matcher = $client->expects($this->once())->method('get');
- if ($expectedUrl !== null) {
- $matcher->with($expectedUrl, $expectedOptions ?? $this->anything());
- }
- $matcher->willReturn($response);
-
- $this->clientService->method('newClient')->willReturn($client);
- }
-
- public function testExists(): void {
- $this->assertTrue($this->avatar->exists());
- }
-
- public function testGetDisplayName(): void {
- $this->assertSame('user@remote.example.com', $this->avatar->getDisplayName());
- }
-
- public function testGetFileFetchesAvatarFromRemoteInstance(): void {
- $this->config->method('getSystemValueBool')
- ->with('sharing.federation.allowSelfSignedCertificates', false)
- ->willReturn(false);
-
- $fileContents = 'png-bytes';
-
- $this->mockRemoteClient(
- 'image/png',
- $fileContents,
- 'https://remote.example.com/index.php/avatar/user/64',
- ['verify' => true],
- );
-
- $expectedFile = $this->createMock(ISimpleFile::class);
- $expectedFile->expects($this->once())->method('getContent')->willReturn($fileContents);
-
- $this->folder->expects($this->once())->method('newFile')->willReturn($expectedFile);
-
- $file = $this->avatar->getFile(64);
- $this->assertInstanceOf(ISimpleFile::class, $file);
- $this->assertSame($fileContents, $file->getContent());
- }
-
- public function testGetFileFetchesAvatarFromCache(): void {
- $this->config->method('getSystemValueBool')
- ->with('sharing.federation.allowSelfSignedCertificates', false)
- ->willReturn(false);
-
- $fileContents = 'png-bytes';
-
- $expectedFile = $this->createMock(ISimpleFile::class);
- $expectedFile->expects($this->once())->method('getContent')->willReturn($fileContents);
- $expectedFile->expects($this->once())->method('getMTime')->willReturn(time() + (60 * 60 * 24));
- $expectedFile->expects($this->once())->method('getName')->willReturn('avatar.64.png');
- $this->folder->expects($this->once())->method('getDirectoryListing')->willReturn([$expectedFile]);
-
- $this->clientService->expects($this->never())->method('newClient');
-
- $file = $this->avatar->getFile(64);
- $this->assertInstanceOf(ISimpleFile::class, $file);
- $this->assertSame($fileContents, $file->getContent());
- }
-
- public function testGetFileThrowsOnUnexpectedContentType(): void {
- $this->mockRemoteClient('text/html', '');
-
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Unknown filetype');
-
- $this->avatar->getFile(64);
- }
-
- public function testIsCustomAvatar(): void {
- $this->assertTrue($this->avatar->isCustomAvatar());
- }
-}
--- a/tests/lib/Avatar/UserAvatarTest.php
+++ /dev/null
@@ -1,295 +0,0 @@
-folder = $this->createMock(SimpleFolder::class);
- // abcdefghi is a convenient name that our algorithm convert to our nextcloud blue 0082c9
- $this->user = $this->getUserWithDisplayName('abcdefghi');
- $this->config = $this->createMock(IConfig::class);
-
- $this->avatar = $this->getUserAvatar($this->user);
- }
-
- public static function avatarTextData(): array {
- return [
- ['', '?'],
- ['matchish', 'M'],
- ['Firstname Lastname', 'FL'],
- ['Firstname Lastname Rest', 'FL'],
- ];
- }
-
- public function testGetNoAvatar(): void {
- $file = $this->createMock(ISimpleFile::class);
- $this->folder->method('newFile')
- ->willReturn($file);
-
- $this->folder->method('getFile')
- ->willReturnCallback(function (string $path): void {
- if ($path === 'avatar.64.png') {
- throw new NotFoundException();
- }
- });
- $this->folder->method('fileExists')
- ->willReturnCallback(function ($path) {
- if ($path === 'generated') {
- return true;
- }
- return false;
- });
-
- $data = null;
- $file->method('putContent')
- ->with($this->callback(function ($d) use (&$data) {
- $data = $d;
- return true;
- }));
-
- $file->method('getContent')
- ->willReturnCallback(function () use (&$data) {
- return $data;
- });
-
- $result = $this->avatar->get();
- $this->assertTrue($result->valid());
- }
-
- public function testGetAvatarSizeMatch(): void {
- $this->folder->method('fileExists')
- ->willReturnMap([
- ['avatar.jpg', true],
- ['avatar.128.jpg', true],
- ['generated', false],
- ]);
-
- $expected = new Image();
- $expected->loadFromFile(\OC::$SERVERROOT . '/tests/data/testavatar.png');
-
- $file = $this->createMock(ISimpleFile::class);
- $file->method('getContent')->willReturn($expected->data());
- $this->folder->method('getFile')->with('avatar.128.jpg')->willReturn($file);
-
- $this->assertEquals($expected->data(), $this->avatar->get(128)->data());
- }
-
- public function testGetAvatarSizeMinusOne(): void {
- $this->folder->method('fileExists')
- ->willReturnMap([
- ['avatar.jpg', true],
- ['generated', false],
- ]);
-
- $expected = new Image();
- $expected->loadFromFile(\OC::$SERVERROOT . '/tests/data/testavatar.png');
-
- $file = $this->createMock(ISimpleFile::class);
- $file->method('getContent')->willReturn($expected->data());
- $this->folder->method('getFile')->with('avatar.jpg')->willReturn($file);
-
- $this->assertEquals($expected->data(), $this->avatar->get(-1)->data());
- }
-
- public function testGetAvatarNoSizeMatch(): void {
- $this->folder->method('fileExists')
- ->willReturnMap([
- ['avatar.jpg', false],
- ['avatar.png', true],
- ['avatar.32.png', false],
- ['generated', false],
- ]);
-
- $expected = new Image();
- $expected->loadFromFile(\OC::$SERVERROOT . '/tests/data/testavatar.png');
- $expected2 = new Image();
- $expected2->loadFromFile(\OC::$SERVERROOT . '/tests/data/testavatar.png');
- $expected2->resize(32);
-
- $file = $this->createMock(ISimpleFile::class);
- $file->method('getContent')->willReturn($expected->data());
-
- $this->folder->method('getFile')
- ->willReturnCallback(
- function ($path) use ($file) {
- if ($path === 'avatar.png') {
- return $file;
- } else {
- throw new NotFoundException;
- }
- }
- );
-
- $newFile = $this->createMock(ISimpleFile::class);
- $newFile->expects($this->once())
- ->method('putContent')
- ->with($expected2->data());
- $newFile->expects($this->once())
- ->method('getContent')
- ->willReturn($expected2->data());
- $this->folder->expects($this->once())
- ->method('newFile')
- ->with('avatar.32.png')
- ->willReturn($newFile);
-
- $this->assertEquals($expected2->data(), $this->avatar->get(32)->data());
- }
-
- public function testExistsNo(): void {
- $this->assertFalse($this->avatar->exists());
- }
-
- public function testExiststJPG(): void {
- $this->folder->method('fileExists')
- ->willReturnMap([
- ['avatar.jpg', true],
- ['avatar.png', false],
- ]);
- $this->assertTrue($this->avatar->exists());
- }
-
- public function testExistsPNG(): void {
- $this->folder->method('fileExists')
- ->willReturnMap([
- ['avatar.jpg', false],
- ['avatar.png', true],
- ]);
- $this->assertTrue($this->avatar->exists());
- }
-
- public function testSetAvatar(): void {
- $avatarFileJPG = $this->createMock(File::class);
- $avatarFileJPG->method('getName')
- ->willReturn('avatar.jpg');
- $avatarFileJPG->expects($this->once())->method('delete');
-
- $avatarFilePNG = $this->createMock(File::class);
- $avatarFilePNG->method('getName')
- ->willReturn('avatar.png');
- $avatarFilePNG->expects($this->once())->method('delete');
-
- $resizedAvatarFile = $this->createMock(File::class);
- $resizedAvatarFile->method('getName')
- ->willReturn('avatar.32.jpg');
- $resizedAvatarFile->expects($this->once())->method('delete');
-
- $this->folder->method('getDirectoryListing')
- ->willReturn([$avatarFileJPG, $avatarFilePNG, $resizedAvatarFile]);
-
- $generated = $this->createMock(ISimpleFile::class);
- $this->folder->method('getFile')
- ->with('generated')
- ->willReturn($generated);
-
- $newFile = $this->createMock(ISimpleFile::class);
- $this->folder->expects($this->once())
- ->method('newFile')
- ->with('avatar.png')
- ->willReturn($newFile);
-
- $image = new Image();
- $image->loadFromFile(\OC::$SERVERROOT . '/tests/data/testavatar.png');
- $newFile->expects($this->once())
- ->method('putContent')
- ->with($image->data());
-
- $this->config->expects($this->exactly(3))
- ->method('setUserValue');
- $this->config->expects($this->once())
- ->method('getUserValue');
-
- $this->user->expects($this->exactly(1))->method('triggerChange');
-
- $this->avatar->set($image->data());
- }
-
- public function testGenerateSvgAvatar(): void {
- $avatar = $this->invokePrivate($this->avatar, 'getAvatarVector', [$this->user->getDisplayName(), 64, false]);
-
- $svg = '
- ';
- $this->assertEquals($avatar, $svg);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('avatarTextData')]
- public function testGetAvatarText($displayName, $expectedAvatarText): void {
- $user = $this->getUserWithDisplayName($displayName);
- $avatar = $this->getUserAvatar($user);
-
- $avatarText = $this->invokePrivate($avatar, 'getAvatarText');
- $this->assertEquals($expectedAvatarText, $avatarText);
- }
-
- public function testHashToInt(): void {
- $hashToInt = $this->invokePrivate($this->avatar, 'hashToInt', ['abcdef', 18]);
- $this->assertTrue(gettype($hashToInt) === 'integer');
- }
-
- public function testMixPalette(): void {
- $colorFrom = new Color(0, 0, 0);
- $colorTo = new Color(6, 12, 18);
- $steps = 6;
- $palette = Color::mixPalette($steps, $colorFrom, $colorTo);
- foreach ($palette as $j => $color) {
- // calc increment
- $incR = (int)($colorTo->red() / $steps * $j);
- $incG = (int)($colorTo->green() / $steps * $j);
- $incB = (int)($colorTo->blue() / $steps * $j);
- // ensure everything is equal
- $this->assertEquals($color, new Color($incR, $incG, $incB));
- }
- $hashToInt = $this->invokePrivate($this->avatar, 'hashToInt', ['abcdef', 18]);
- $this->assertTrue(gettype($hashToInt) === 'integer');
- }
-
- private function getUserWithDisplayName($name) {
- $user = $this->createMock(User::class);
- $user->method('getDisplayName')->willReturn($name);
- return $user;
- }
-
- private function getUserAvatar($user) {
- /** @var IL10N|\PHPUnit\Framework\MockObject\MockObject $l */
- $l = $this->createMock(IL10N::class);
- $l->method('t')->willReturnArgument(0);
-
- return new UserAvatar(
- $this->folder,
- $l,
- $user,
- $this->createMock(LoggerInterface::class),
- $this->config
- );
- }
-}
--- a/tests/lib/BackgroundJob/DummyJob.php
+++ /dev/null
@@ -1,39 +0,0 @@
- $job
- * @param mixed $argument
- */
- #[\Override]
- public function add($job, $argument = null, ?int $firstCheck = null): void {
- if (is_string($job)) {
- /** @var IJob $job */
- $job = Server::get($job);
- }
- $job->setArgument($argument);
- $job->setId(Server::get(ISnowflakeGenerator::class)->nextId());
- if (!$this->has($job, null)) {
- $this->jobs[] = $job;
- }
- }
-
- #[\Override]
- public function scheduleAfter(string $job, int $runAfter, $argument = null): void {
- $this->add($job, $argument, $runAfter);
- }
-
- /**
- * @param IJob|string $job
- * @param mixed $argument
- */
- #[\Override]
- public function remove($job, $argument = null): void {
- foreach ($this->jobs as $index => $listJob) {
- if (get_class($job) === get_class($listJob) && $job->getArgument() == $listJob->getArgument()) {
- unset($this->jobs[$index]);
- return;
- }
- }
- }
-
- #[\Override]
- public function removeById(string $id): void {
- foreach ($this->jobs as $index => $listJob) {
- if ($listJob->getId() === $id) {
- unset($this->jobs[$index]);
- return;
- }
- }
- }
-
- /**
- * check if a job is in the list
- *
- * @param $job
- * @param mixed $argument
- * @return bool
- */
- #[\Override]
- public function has($job, $argument): bool {
- return array_search($job, $this->jobs) !== false;
- }
-
- /**
- * get all jobs in the list
- *
- * @return IJob[]
- */
- public function getAll(): array {
- return $this->jobs;
- }
-
- #[\Override]
- public function getJobsIterator($job, ?int $limit, int $offset): iterable {
- if ($job instanceof IJob) {
- $jobClass = get_class($job);
- } else {
- $jobClass = $job;
- }
-
- $jobs = array_slice(
- array_filter(
- $this->jobs,
- fn ($job) => ($jobClass === null) || (get_class($job) === $jobClass)
- ),
- $offset,
- $limit
- );
-
- return new ArrayIterator($jobs);
- }
-
- /**
- * get the next job in the list
- */
- #[\Override]
- public function getNext(bool $onlyTimeSensitive = false, ?array $jobClasses = null): ?IJob {
- if (count($this->jobs) > 0) {
- if ($this->last < (count($this->jobs) - 1)) {
- $i = $this->last + 1;
- } else {
- $i = 0;
- }
- return $this->jobs[$i];
- } else {
- return null;
- }
- }
-
- /**
- * set the job that was last ran
- *
- * @param Job $job
- */
- #[\Override]
- public function setLastJob(IJob $job): void {
- $i = array_search($job, $this->jobs);
- if ($i !== false) {
- $this->last = $i;
- } else {
- $this->last = 0;
- }
- }
-
- #[\Override]
- public function getById(string $id): ?IJob {
- foreach ($this->jobs as $job) {
- if ($job->getId() === $id) {
- return $job;
- }
- }
- return null;
- }
-
- #[\Override]
- public function getDetailsById(string $id): ?array {
- return null;
- }
-
- #[\Override]
- public function setLastRun(IJob $job): void {
- $job->setLastRun(time());
- }
-
- #[\Override]
- public function hasReservedJob(?string $className = null): bool {
- return isset($this->reserved[$className ?? '']) && $this->reserved[$className ?? ''];
- }
-
- public function setHasReservedJob(?string $className, bool $hasReserved): void {
- $this->reserved[$className ?? ''] = $hasReserved;
- }
-
- #[\Override]
- public function setExecutionTime(IJob $job, $timeTaken): void {
- }
-
- #[\Override]
- public function resetBackgroundJob(IJob $job): void {
- }
-}
--- a/tests/lib/BackgroundJob/JobClassesRegistryTest.php
+++ /dev/null
@@ -1,82 +0,0 @@
-connection = Server::get(IDBConnection::class);
- $this->snowflakeGenerator = Server::get(ISnowflakeGenerator::class);
- $this->registry = new JobClassesRegistry($this->connection, $this->snowflakeGenerator);
- }
-
- public function testResolveNonExistingClass() {
- $className = 'invalid_class_name_122278';
-
- $this->expectException(InvalidArgumentException::class);
- $this->expectExceptionMessage('Class ' . $className . ' doesn’t exists');
- $this->registry->getId($className);
- }
-
- public function testResolveInvalidClass() {
- $className = self::class;
-
- $this->expectException(InvalidArgumentException::class);
- $this->expectExceptionMessage('Class ' . $className . ' isn’t an instance of OCP\BackgroundJob\IJob');
- $this->registry->getId($className);
- }
-
- public function testResolveValidClass() {
- $className = DummyJob::class;
-
- $classId = $this->registry->getId($className);
- $this->assertIsString($classId);
- $this->assertGreaterThan(0, $classId);
-
- // Renew register. ID should stay the same
- $this->registry = new JobClassesRegistry($this->connection, $this->snowflakeGenerator);
- $newId = $this->registry->getId($className);
- $this->assertEquals($classId, $newId);
- }
-
- public function testResolveValidId() {
- $className = DummyJob::class;
-
- $classId = $this->registry->getId($className);
- $resolvedClass = $this->registry->getName($classId);
-
- $this->assertEquals($className, $resolvedClass);
- }
-
- public function testResolveInvalidId() {
- $classId = PHP_INT_MAX;
- $this->expectException(InvalidArgumentException::class);
- $this->expectExceptionMessage('Class ID ' . $classId . ' doesn’t match any class name');
- $this->registry->getName($classId);
- }
-}
--- a/tests/lib/BackgroundJob/JobListTest.php
+++ /dev/null
@@ -1,357 +0,0 @@
-connection = Server::get(IDBConnection::class);
- $this->clearJobsList();
- $this->config = $this->createMock(IConfig::class);
- $this->timeFactory = $this->createMock(ITimeFactory::class);
- $this->instance = new JobList(
- $this->connection,
- $this->config,
- $this->timeFactory,
- Server::get(LoggerInterface::class),
- Server::get(ISnowflakeGenerator::class),
- );
- }
-
- protected function clearJobsList(): void {
- $query = $this->connection->getQueryBuilder();
- $query->delete('jobs');
- $query->executeStatement();
- }
-
- protected function getAllSorted(): array {
- $iterator = $this->instance->getJobsIterator(null, null, 0);
- $jobs = [];
-
- foreach ($iterator as $job) {
- $jobs[] = clone $job;
- }
-
- return $jobs;
- }
-
- public static function argumentProvider(): array {
- return [
- [null],
- [false],
- ['foobar'],
- [12],
- [[
- 'asd' => 5,
- 'foo' => 'bar'
- ]]
- ];
- }
-
- #[DataProvider('argumentProvider')]
- public function testAddRemove(mixed $argument): void {
- $existingJobs = $this->getAllSorted();
- $job = new TestJob();
- $this->instance->add($job, $argument);
-
- $jobs = $this->getAllSorted();
-
- $this->assertCount(count($existingJobs) + 1, $jobs);
- $addedJob = $jobs[count($jobs) - 1];
- $this->assertInstanceOf('\Test\BackgroundJob\TestJob', $addedJob);
- $this->assertEquals($argument, $addedJob->getArgument());
-
- $this->instance->remove($job, $argument);
-
- $jobs = $this->getAllSorted();
- $this->assertEquals($existingJobs, $jobs);
- }
-
- public function testAddAcceptsArgumentUnderMaxLength(): void {
- $argument = str_repeat('a', $this->instance::MAX_ARGUMENT_JSON_LENGTH - 100);
- $job = new TestJob();
- $this->assertFalse($this->instance->has($job, $argument));
- $this->instance->add($job, $argument);
-
- $this->assertTrue($this->instance->has($job, $argument));
- }
-
- public function testAddRejectsArgumentAboveMaxLength(): void {
- $argument = str_repeat('a', $this->instance::MAX_ARGUMENT_JSON_LENGTH + 100);
-
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Background job arguments can\'t exceed ' . $this->instance::MAX_ARGUMENT_JSON_LENGTH . ' characters (json encoded)');
-
- $this->instance->add(new TestJob(), $argument);
- }
-
- #[DataProvider('argumentProvider')]
- public function testRemoveDifferentArgument(mixed $argument): void {
- $existingJobs = $this->getAllSorted();
- $job = new TestJob();
- $this->instance->add($job, $argument);
-
- $jobs = $this->getAllSorted();
- $this->instance->remove($job, 10);
- $jobs2 = $this->getAllSorted();
-
- $this->assertEquals($jobs, $jobs2);
-
- $this->instance->remove($job, $argument);
-
- $jobs = $this->getAllSorted();
- $this->assertEquals($existingJobs, $jobs);
- }
-
- #[DataProvider('argumentProvider')]
- public function testHas(mixed $argument): void {
- $job = new TestJob();
- $this->assertFalse($this->instance->has($job, $argument));
- $this->instance->add($job, $argument);
-
- $this->assertTrue($this->instance->has($job, $argument));
-
- $this->instance->remove($job, $argument);
-
- $this->assertFalse($this->instance->has($job, $argument));
- }
-
- #[DataProvider('argumentProvider')]
- public function testHasDifferentArgument(mixed $argument): void {
- $job = new TestJob();
- $this->instance->add($job, $argument);
-
- $this->assertFalse($this->instance->has($job, 10));
- }
-
- protected function createTempJob($class,
- $argument,
- int $reservedTime = 0,
- int $lastChecked = 0,
- int $lastRun = 0): string {
- if ($lastChecked === 0) {
- $lastChecked = time();
- }
- $id = Server::get(ISnowflakeGenerator::class)->nextId();
-
- $query = $this->connection->getQueryBuilder();
- $query->insert('jobs')
- ->values([
- 'id' => $query->createNamedParameter($id),
- 'class' => $query->createNamedParameter($class),
- 'argument' => $query->createNamedParameter($argument),
- 'last_run' => $query->createNamedParameter($lastRun, IQueryBuilder::PARAM_INT),
- 'last_checked' => $query->createNamedParameter($lastChecked, IQueryBuilder::PARAM_INT),
- 'reserved_at' => $query->createNamedParameter($reservedTime, IQueryBuilder::PARAM_INT),
- ]);
- $query->executeStatement();
- return $id;
- }
-
- public function testGetNext(): void {
- $job = new TestJob();
- $this->createTempJob(get_class($job), 1, 0, 12345);
- $this->createTempJob(get_class($job), 2, 0, 12346);
-
- $jobs = $this->getAllSorted();
- $savedJob1 = $jobs[0];
-
- $this->timeFactory->expects($this->atLeastOnce())
- ->method('getTime')
- ->willReturn(123456789);
- $nextJob = $this->instance->getNext();
-
- $this->assertEquals($savedJob1, $nextJob);
- }
-
- public function testGetNextSkipReserved(): void {
- $job = new TestJob();
- $this->createTempJob(get_class($job), 1, 123456789, 12345);
- $this->createTempJob(get_class($job), 2, 0, 12346);
-
- $this->timeFactory->expects($this->atLeastOnce())
- ->method('getTime')
- ->willReturn(123456789);
- $nextJob = $this->instance->getNext();
-
- $this->assertEquals(get_class($job), get_class($nextJob));
- $this->assertEquals(2, $nextJob->getArgument());
- }
-
- public function testGetNextSkipTimed(): void {
- $job = new TestTimedJobNew($this->timeFactory);
- $jobId = $this->createTempJob(get_class($job), 1, 123456789, 12345, 123456789 - 5);
- $this->timeFactory->expects(self::atLeastOnce())
- ->method('getTime')
- ->willReturn(123456789);
-
- $nextJob = $this->instance->getNext();
-
- self::assertNull($nextJob);
- $job = $this->instance->getById($jobId);
- self::assertInstanceOf(TestTimedJobNew::class, $job);
- self::assertEquals(123456789 - 5, $job->getLastRun());
- }
-
- public function testGetNextSkipNonExisting(): void {
- $job = new TestJob();
- $this->createTempJob('\OC\Non\Existing\Class', 1, 0, 12345);
- $this->createTempJob(get_class($job), 2, 0, 12346);
-
- $this->timeFactory->expects($this->atLeastOnce())
- ->method('getTime')
- ->willReturn(123456789);
- $nextJob = $this->instance->getNext();
-
- $this->assertEquals(get_class($job), get_class($nextJob));
- $this->assertEquals(2, $nextJob->getArgument());
- }
-
- /**
- * @param $argument
- */
- #[DataProvider('argumentProvider')]
- public function testGetById($argument): void {
- $job = new TestJob();
- $this->instance->add($job, $argument);
-
- $jobs = $this->getAllSorted();
-
- $addedJob = $jobs[count($jobs) - 1];
-
- $this->assertEquals($addedJob, $this->instance->getById($addedJob->getId()));
- }
-
- public function testSetLastRun(): void {
- $job = new TestJob();
- $this->instance->add($job);
-
- $jobs = $this->getAllSorted();
-
- $addedJob = $jobs[count($jobs) - 1];
-
- $timeStart = time();
- $this->instance->setLastRun($addedJob);
- $timeEnd = time();
-
- $addedJob = $this->instance->getById($addedJob->getId());
-
- $this->assertGreaterThanOrEqual($timeStart, $addedJob->getLastRun());
- $this->assertLessThanOrEqual($timeEnd, $addedJob->getLastRun());
- }
-
- public function testRemoveByIdWithSnowflakeId(): void {
- $this->instance->add(new TestJob(), 'remove-by-id');
- $job = $this->instance->getJobs(null, 1, 0)[0];
-
- $this->instance->removeById($job->getId());
-
- $this->assertNull($this->instance->getById($job->getId()));
- }
-
- public function testResetBackgroundJobWithSnowflakeId(): void {
- $this->instance->add(new TestJob(), 'reset');
- $job = $this->instance->getJobs(null, 1, 0)[0];
-
- $this->instance->resetBackgroundJob($job);
-
- $row = $this->instance->getDetailsById($job->getId());
- $this->assertSame('0', (string)$row['last_run']);
- $this->assertSame('0', (string)$row['reserved_at']);
- }
-
- public function testHasReservedJobs(): void {
- $this->clearJobsList();
-
- $this->timeFactory->expects($this->atLeastOnce())
- ->method('getTime')
- ->willReturn(123456789);
-
- $job = new TestJob($this->timeFactory, $this, function (): void {
- });
-
- $job2 = new TestJob($this->timeFactory, $this, function (): void {
- });
-
- $this->instance->add($job, 1);
- $this->instance->add($job2, 2);
-
- $this->assertCount(2, iterator_to_array($this->instance->getJobsIterator(null, 10, 0)));
-
- $this->assertFalse($this->instance->hasReservedJob());
- $this->assertFalse($this->instance->hasReservedJob(TestJob::class));
-
- $job = $this->instance->getNext();
- $this->assertNotNull($job);
- $this->assertTrue($this->instance->hasReservedJob());
- $this->assertTrue($this->instance->hasReservedJob(TestJob::class));
- $job = $this->instance->getNext();
- $this->assertNotNull($job);
- $this->assertTrue($this->instance->hasReservedJob());
- $this->assertTrue($this->instance->hasReservedJob(TestJob::class));
- }
-
- public function testHasReservedJobsAndParallelAwareJob(): void {
- $this->clearJobsList();
-
- $this->timeFactory->expects($this->atLeastOnce())
- ->method('getTime')
- ->willReturnCallback(function () use (&$time) {
- return time();
- });
-
- $job = new TestParallelAwareJob($this->timeFactory, $this, function (): void {
- });
-
- $job2 = new TestParallelAwareJob($this->timeFactory, $this, function (): void {
- });
-
- $this->instance->add($job, 1);
- $this->instance->add($job2, 2);
-
- $this->assertCount(2, iterator_to_array($this->instance->getJobsIterator(null, 10, 0)));
-
- $this->assertFalse($this->instance->hasReservedJob());
- $this->assertFalse($this->instance->hasReservedJob(TestParallelAwareJob::class));
-
- $job = $this->instance->getNext();
- $this->assertNotNull($job);
- $this->assertTrue($this->instance->hasReservedJob());
- $this->assertTrue($this->instance->hasReservedJob(TestParallelAwareJob::class));
- $job = $this->instance->getNext();
- $this->assertNull($job); // Job doesn't allow parallel runs
- }
-}
--- a/tests/lib/BackgroundJob/JobRunsTest.php
+++ /dev/null
@@ -1,111 +0,0 @@
-connection = Server::get(IDBConnection::class);
- $this->registry = Server::get(JobClassesRegistry::class);
- $this->runs = new JobRuns(
- $this->connection,
- Server::get(ISnowflakeGenerator::class),
- Server::get(ISnowflakeDecoder::class),
- $this->registry,
- $this->createMock(LoggerInterface::class),
- );
- }
-
- public function testJobStarted(): void {
- $myPid = 1337;
- $myClass = DummyJob::class;
-
- $runId = $this->runs->started($this->registry->getId(DummyJob::class), $myPid);
-
- $this->assertGreaterThan(0, $runId);
- }
-
- public function testJobSucceeded(): void {
- $myPid = 1337;
- $myClass = DummyJob::class;
-
- $runId = $this->runs->started($this->registry->getId(DummyJob::class), $myPid);
-
- $result = $this->runs->finished($runId, 12, 9876543);
-
- $this->assertTrue($result);
- }
-
- public function testJobFailed(): void {
- $myPid = 1337;
- $myClass = DummyJob::class;
-
- $runId = $this->runs->started($this->registry->getId(DummyJob::class), $myPid);
-
- $result = $this->runs->finished($runId, 13, 87654321, JobStatus::FAILED);
-
- $this->assertTrue($result);
- }
-
- public function testRunningJobs(): void {
- $myPid = 1337;
- $myClass = DummyJob::class;
-
- $runId = $this->runs->started($this->registry->getId(DummyJob::class), $myPid);
-
- $runningJobs = 0;
- foreach ($this->runs->runningJobs() as $job) {
- $this->assertInstanceOf(JobRun::class, $job);
- ++$runningJobs;
- }
- $this->assertGreaterThan(0, $runningJobs);
- }
-
- public function testCompletedJobs(): void {
- $myPid = 1337;
- $myClass = DummyJob::class;
-
- $runId = $this->runs->started($this->registry->getId(DummyJob::class), $myPid);
- $this->runs->finished($runId, 12345, 67890 * 1024, JobStatus::FAILED);
- $runId = $this->runs->started($this->registry->getId(DummyJob::class), $myPid);
- $this->runs->finished($runId, 12345, 67890 * 1024, JobStatus::SUCCEEDED);
- $runId = $this->runs->started($this->registry->getId(DummyJob::class), $myPid);
- $this->runs->finished($runId, 12345, 67890 * 1024, JobStatus::CRASHED);
-
- $completedJobs = 0;
- foreach ($this->runs->runningJobs() as $job) {
- $this->assertInstanceOf(JobRun::class, $job);
- ++$completedJobs;
- }
- $this->assertGreaterThan(0, $completedJobs);
- }
-}
--- a/tests/lib/BackgroundJob/JobTest.php
+++ /dev/null
@@ -1,67 +0,0 @@
-run = false;
- $this->timeFactory = Server::get(ITimeFactory::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- \OC::$server->registerService(LoggerInterface::class, fn ($c) => $this->logger);
- }
-
- public function testRemoveAfterException(): void {
- $jobList = new DummyJobList();
- $e = new \Exception();
- $job = new TestJob($this->timeFactory, $this, function () use ($e): void {
- throw $e;
- });
- $jobList->add($job);
-
- $this->logger->expects($this->once())
- ->method('error');
-
- $this->assertCount(1, $jobList->getAll());
- $job->start($jobList);
- $this->assertTrue($this->run);
- $this->assertCount(1, $jobList->getAll());
- }
-
- public function testRemoveAfterError(): void {
- $jobList = new DummyJobList();
- $job = new TestJob($this->timeFactory, $this, function (): void {
- $test = null;
- $test->someMethod();
- });
- $jobList->add($job);
-
- $this->logger->expects($this->once())
- ->method('error');
-
- $this->assertCount(1, $jobList->getAll());
- $job->start($jobList);
- $this->assertTrue($this->run);
- $this->assertCount(1, $jobList->getAll());
- }
-
- public function markRun() {
- $this->run = true;
- }
-}
--- a/tests/lib/BackgroundJob/QueuedJobTest.php
+++ /dev/null
@@ -1,43 +0,0 @@
-ran = true;
- }
-}
-
-class QueuedJobTest extends \Test\TestCase {
- private DummyJobList $jobList;
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- $this->jobList = new DummyJobList();
- }
-
- public function testJobShouldBeRemovedNew(): void {
- $job = new TestQueuedJobNew(Server::get(ITimeFactory::class));
- $job->setId(42);
- $this->jobList->add($job);
-
- $this->assertTrue($this->jobList->has($job, null));
- $job->start($this->jobList);
- $this->assertTrue($job->ran);
- }
-}
--- a/tests/lib/BackgroundJob/TestJob.php
+++ /dev/null
@@ -1,40 +0,0 @@
-callback = $callback;
- }
-
- #[\Override]
- public function run($argument) {
- $this->testCase->markRun();
- $callback = $this->callback;
- $callback($argument);
- }
-}
--- a/tests/lib/BackgroundJob/TestParallelAwareJob.php
+++ /dev/null
@@ -1,40 +0,0 @@
-setAllowParallelRuns(false);
- $this->callback = $callback;
- }
-
- #[\Override]
- public function run($argument) {
- $this->testCase->markRun();
- $callback = $this->callback;
- $callback($argument);
- }
-}
--- a/tests/lib/BackgroundJob/TestTimedJobNew.php
+++ /dev/null
@@ -1,27 +0,0 @@
-setInterval(10);
- }
-
- #[\Override]
- public function run($argument) {
- $this->ran = true;
- }
-}
--- a/tests/lib/BackgroundJob/TimedJobTest.php
+++ /dev/null
@@ -1,58 +0,0 @@
-jobList = new DummyJobList();
- $this->time = Server::get(ITimeFactory::class);
- }
-
- public function testShouldRunAfterIntervalNew(): void {
- $job = new TestTimedJobNew($this->time);
- $job->setId(42);
- $this->jobList->add($job);
-
- $job->setLastRun(time() - 12);
- $job->start($this->jobList);
- $this->assertTrue($job->ran);
- }
-
- public function testShouldNotRunWithinIntervalNew(): void {
- $job = new TestTimedJobNew($this->time);
- $job->setId(42);
- $this->jobList->add($job);
-
- $job->setLastRun(time() - 5);
- $job->start($this->jobList);
- $this->assertFalse($job->ran);
- }
-
- public function testShouldNotTwiceNew(): void {
- $job = new TestTimedJobNew($this->time);
- $job->setId(42);
- $this->jobList->add($job);
-
- $job->setLastRun(time() - 15);
- $job->start($this->jobList);
- $this->assertTrue($job->ran);
- $job->ran = false;
- $job->start($this->jobList);
- $this->assertFalse($job->ran);
- }
-}
--- a/tests/lib/BinaryFinderTest.php
+++ /dev/null
@@ -1,94 +0,0 @@
-oldEnv = getenv('PATH');
- // BinaryFinder always includes the "PATH" environment variable into the search path,
- // which we want to avoid in this test because they are not usually found in webserver
- // deployments.
- putenv('PATH=""');
- $this->cacheFactory = $this->createMock(ICacheFactory::class);
- $this->cache = new ArrayCache();
- $this->cacheFactory->method('createLocal')->with('findBinaryPath')->willReturn($this->cache);
- }
-
- #[\Override]
- protected function tearDown(): void {
- putenv('PATH=' . $this->oldEnv);
- }
-
- public function testDefaultFindsCat() {
- $config = $this->createMock(IConfig::class);
- $config
- ->method('getSystemValue')
- ->with('binary_search_paths', $this->anything())
- ->willReturnCallback(function ($key, $default) {
- return $default;
- });
- $finder = new BinaryFinder($this->cacheFactory, $config);
- $this->assertStringEndsWith('/cat', $finder->findBinaryPath('cat'));
- $this->assertStringEndsWith('/cat', $this->cache->get('cat'));
- }
-
- public function testDefaultDoesNotFindCata() {
- $config = $this->createMock(IConfig::class);
- $config
- ->method('getSystemValue')
- ->with('binary_search_paths', $this->anything())
- ->willReturnCallback(function ($key, $default) {
- return $default;
- });
- $finder = new BinaryFinder($this->cacheFactory, $config);
- $this->assertFalse($finder->findBinaryPath('cata'));
- $this->assertFalse($this->cache->get('cata'));
- }
-
- public function testCustomPathFindsCat() {
- $tmpdir = Server::get(ITempManager::class)->getTemporaryFolder();
- touch($tmpdir . '/cat');
- chmod($tmpdir . '/cat', 100);
-
- $config = $this->createMock(IConfig::class);
- $config
- ->method('getSystemValue')
- ->with('binary_search_paths', $this->anything())
- ->willReturn([$tmpdir]);
- $finder = new BinaryFinder($this->cacheFactory, $config);
- $this->assertEquals($tmpdir . '/cat', $finder->findBinaryPath('cat'));
- $this->assertEquals($tmpdir . '/cat', $this->cache->get('cat'));
- }
-
- public function testWrongCustomPathDoesNotFindCat() {
- $tmpdir = Server::get(ITempManager::class)->getTemporaryFolder();
-
- $config = $this->createMock(IConfig::class);
- $config
- ->method('getSystemValue')
- ->with('binary_search_paths')
- ->willReturn([$tmpdir]);
- $finder = new BinaryFinder($this->cacheFactory, $config);
- $this->assertFalse($finder->findBinaryPath('cat'));
- $this->assertFalse($this->cache->get('cat'));
- }
-}
--- a/tests/lib/Cache/CappedMemoryCacheTest.php
+++ /dev/null
@@ -1,67 +0,0 @@
-instance = new CappedMemoryCache();
- }
-
- public function testSetOverCap(): void {
- $instance = new CappedMemoryCache(3);
-
- $instance->set('1', 'a');
- $instance->set('2', 'b');
- $instance->set('3', 'c');
- $instance->set('4', 'd');
- $instance->set('5', 'e');
-
- $this->assertFalse($instance->hasKey('1'));
- $this->assertFalse($instance->hasKey('2'));
- $this->assertTrue($instance->hasKey('3'));
- $this->assertTrue($instance->hasKey('4'));
- $this->assertTrue($instance->hasKey('5'));
- }
-
- #[\Override]
- public function testClear(): void {
- $value = 'ipsum lorum';
- $this->instance->set('1_value1', $value);
- $this->instance->set('1_value2', $value);
- $this->instance->set('2_value1', $value);
- $this->instance->set('3_value1', $value);
-
- $this->assertTrue($this->instance->clear());
- $this->assertFalse($this->instance->hasKey('1_value1'));
- $this->assertFalse($this->instance->hasKey('1_value2'));
- $this->assertFalse($this->instance->hasKey('2_value1'));
- $this->assertFalse($this->instance->hasKey('3_value1'));
- }
-
- public function testIndirectSet(): void {
- $this->instance->set('array', []);
-
- $this->instance['array'][] = 'foo';
-
- $this->assertEquals(['foo'], $this->instance->get('array'));
-
- $this->instance['array']['bar'] = 'qwerty';
-
- $this->assertEquals(['foo', 'bar' => 'qwerty'], $this->instance->get('array'));
- }
-}
--- a/tests/lib/Cache/FileCacheTest.php
+++ /dev/null
@@ -1,163 +0,0 @@
-skipUnless(OC_User::isLoggedIn());
- }
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- //login
- $this->createUser('test', 'test');
-
- $this->user = \OC_User::getUser();
- \OC_User::setUserId('test');
-
- //clear all proxies and hooks so we can do clean testing
- \OC_Hook::clear('OC_Filesystem');
-
- /** @var IMountManager $manager */
- $manager = Server::get(IMountManager::class);
- $manager->removeMount('/test');
-
- $storage = new Temporary([]);
- Filesystem::mount($storage, [], '/test/cache');
-
- //set up the users dir
- $this->rootView = new View('');
- $this->rootView->mkdir('/test');
-
- $this->instance = new File();
-
- // forces creation of cache folder for subsequent tests
- $this->instance->set('hack', 'hack');
- }
-
- #[\Override]
- protected function tearDown(): void {
- if ($this->instance) {
- $this->instance->remove('hack', 'hack');
- }
-
- \OC_User::setUserId($this->user);
-
- if ($this->instance) {
- $this->instance->clear();
- $this->instance = null;
- }
-
- parent::tearDown();
- }
-
- private function setupMockStorage() {
- $mockStorage = $this->getMockBuilder(Local::class)
- ->onlyMethods(['filemtime', 'unlink'])
- ->setConstructorArgs([['datadir' => Server::get(ITempManager::class)->getTemporaryFolder()]])
- ->getMock();
-
- Filesystem::mount($mockStorage, [], '/test/cache');
-
- return $mockStorage;
- }
-
- public function testGarbageCollectOldKeys(): void {
- $mockStorage = $this->setupMockStorage();
-
- $mockStorage->expects($this->atLeastOnce())
- ->method('filemtime')
- ->willReturn(100);
- $mockStorage->expects($this->once())
- ->method('unlink')
- ->with('key1')
- ->willReturn(true);
-
- $this->instance->set('key1', 'value1');
- $this->instance->gc();
- }
-
- public function testGarbageCollectLeaveRecentKeys(): void {
- $mockStorage = $this->setupMockStorage();
-
- $mockStorage->expects($this->atLeastOnce())
- ->method('filemtime')
- ->willReturn(time() + 3600);
- $mockStorage->expects($this->never())
- ->method('unlink')
- ->with('key1');
- $this->instance->set('key1', 'value1');
- $this->instance->gc();
- }
-
- public static function lockExceptionProvider(): array {
- return [
- [new LockedException('key1')],
- [new LockNotAcquiredException('key1', 1)],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('lockExceptionProvider')]
- public function testGarbageCollectIgnoreLockedKeys($testException): void {
- $mockStorage = $this->setupMockStorage();
-
- $mockStorage->expects($this->atLeastOnce())
- ->method('filemtime')
- ->willReturn(100);
- $mockStorage->expects($this->atLeastOnce())
- ->method('unlink')
- ->willReturnOnConsecutiveCalls($this->throwException($testException), true);
-
- $this->instance->set('key1', 'value1');
- $this->instance->set('key2', 'value2');
-
- $this->instance->gc();
- }
-}
--- a/tests/lib/Cache/TestCache.php
+++ /dev/null
@@ -1,103 +0,0 @@
-instance) {
- $this->instance->clear();
- }
-
- parent::tearDown();
- }
-
- public function testSimple(): void {
- $this->assertNull($this->instance->get('value1'));
- $this->assertFalse($this->instance->hasKey('value1'));
-
- $value = 'foobar';
- $this->instance->set('value1', $value);
- $this->assertTrue($this->instance->hasKey('value1'));
- $received = $this->instance->get('value1');
- $this->assertEquals($value, $received, 'Value received from cache not equal to the original');
- $value = 'ipsum lorum';
- $this->instance->set('value1', $value);
- $received = $this->instance->get('value1');
- $this->assertEquals($value, $received, 'Value not overwritten by second set');
-
- $value2 = 'foobar';
- $this->instance->set('value2', $value2);
- $received2 = $this->instance->get('value2');
- $this->assertTrue($this->instance->hasKey('value1'));
- $this->assertTrue($this->instance->hasKey('value2'));
- $this->assertEquals($value, $received, 'Value changed while setting other variable');
- $this->assertEquals($value2, $received2, 'Second value not equal to original');
-
- $this->assertFalse($this->instance->hasKey('not_set'));
- $this->assertNull($this->instance->get('not_set'), 'Unset value not equal to null');
-
- $this->assertTrue($this->instance->remove('value1'));
- $this->assertFalse($this->instance->hasKey('value1'));
- }
-
- public function testClear(): void {
- $value = 'ipsum lorum';
- $this->instance->set('1_value1', $value . '1');
- $this->instance->set('1_value2', $value . '2');
- $this->instance->set('2_value1', $value . '3');
- $this->instance->set('3_value1', $value . '4');
-
- $this->assertEquals([
- '1_value1' => 'ipsum lorum1',
- '1_value2' => 'ipsum lorum2',
- '2_value1' => 'ipsum lorum3',
- '3_value1' => 'ipsum lorum4',
- ], [
- '1_value1' => $this->instance->get('1_value1'),
- '1_value2' => $this->instance->get('1_value2'),
- '2_value1' => $this->instance->get('2_value1'),
- '3_value1' => $this->instance->get('3_value1'),
- ]);
- $this->assertTrue($this->instance->clear('1_'));
-
- $this->assertEquals([
- '1_value1' => null,
- '1_value2' => null,
- '2_value1' => 'ipsum lorum3',
- '3_value1' => 'ipsum lorum4',
- ], [
- '1_value1' => $this->instance->get('1_value1'),
- '1_value2' => $this->instance->get('1_value2'),
- '2_value1' => $this->instance->get('2_value1'),
- '3_value1' => $this->instance->get('3_value1'),
- ]);
-
- $this->assertTrue($this->instance->clear());
-
- $this->assertEquals([
- '1_value1' => null,
- '1_value2' => null,
- '2_value1' => null,
- '3_value1' => null,
- ], [
- '1_value1' => $this->instance->get('1_value1'),
- '1_value2' => $this->instance->get('1_value2'),
- '2_value1' => $this->instance->get('2_value1'),
- '3_value1' => $this->instance->get('3_value1'),
- ]);
- }
-}
--- a/tests/lib/Calendar/CalendarEventBuilderTest.php
+++ /dev/null
@@ -1,152 +0,0 @@
-timeFactory = $this->createMock(ITimeFactory::class);
- $this->timeFactory->method('now')
- ->willReturn(new DateTimeImmutable('20250105T000000Z'));
-
- $this->calendarEventBuilder = new CalendarEventBuilder(
- 'event-uid-123',
- $this->timeFactory,
- );
- }
-
- public function testToIcs(): void {
- $this->calendarEventBuilder->setStartDate(new DateTimeImmutable('2025-01-05T17:09:58Z'));
- $this->calendarEventBuilder->setEndDate(new DateTimeImmutable('2025-01-05T17:19:58Z'));
- $this->calendarEventBuilder->setStatus(CalendarEventStatus::CONFIRMED);
- $this->calendarEventBuilder->setSummary('My event');
- $this->calendarEventBuilder->setDescription('Foo bar baz');
- $this->calendarEventBuilder->setOrganizer('mailto:organizer@domain.tld');
- $this->calendarEventBuilder->addAttendee('mailto:attendee1@domain.tld');
- $this->calendarEventBuilder->addAttendee('mailto:attendee2@domain.tld');
-
- $expected = file_get_contents(\OC::$SERVERROOT . '/tests/data/ics/event-builder-complete.ics');
- $actual = $this->calendarEventBuilder->toIcs();
- $this->assertEquals($expected, $actual);
- }
-
- public function testToIcsWithoutOrganizerAndAttendees(): void {
- $this->calendarEventBuilder->setStartDate(new DateTimeImmutable('2025-01-05T17:09:58Z'));
- $this->calendarEventBuilder->setEndDate(new DateTimeImmutable('2025-01-05T17:19:58Z'));
- $this->calendarEventBuilder->setStatus(CalendarEventStatus::CONFIRMED);
- $this->calendarEventBuilder->setSummary('My event');
- $this->calendarEventBuilder->setDescription('Foo bar baz');
-
- $expected = file_get_contents(\OC::$SERVERROOT . '/tests/data/ics/event-builder-without-attendees.ics');
- $actual = $this->calendarEventBuilder->toIcs();
- $this->assertEquals($expected, $actual);
- }
-
- public function testToIcsWithoutMailtoPrefix(): void {
- $this->calendarEventBuilder->setStartDate(new DateTimeImmutable('2025-01-05T17:09:58Z'));
- $this->calendarEventBuilder->setEndDate(new DateTimeImmutable('2025-01-05T17:19:58Z'));
- $this->calendarEventBuilder->setStatus(CalendarEventStatus::CONFIRMED);
- $this->calendarEventBuilder->setSummary('My event');
- $this->calendarEventBuilder->setDescription('Foo bar baz');
- $this->calendarEventBuilder->setOrganizer('organizer@domain.tld');
- $this->calendarEventBuilder->addAttendee('attendee1@domain.tld');
- $this->calendarEventBuilder->addAttendee('attendee2@domain.tld');
-
- $expected = file_get_contents(\OC::$SERVERROOT . '/tests/data/ics/event-builder-complete.ics');
- $actual = $this->calendarEventBuilder->toIcs();
- $this->assertEquals($expected, $actual);
- }
-
- public function testCreateInCalendar(): void {
- $this->calendarEventBuilder->setStartDate(new DateTimeImmutable('2025-01-05T17:09:58Z'));
- $this->calendarEventBuilder->setEndDate(new DateTimeImmutable('2025-01-05T17:19:58Z'));
- $this->calendarEventBuilder->setStatus(CalendarEventStatus::CONFIRMED);
- $this->calendarEventBuilder->setSummary('My event');
- $this->calendarEventBuilder->setDescription('Foo bar baz');
- $this->calendarEventBuilder->setOrganizer('organizer@domain.tld');
- $this->calendarEventBuilder->addAttendee('attendee1@domain.tld');
- $this->calendarEventBuilder->addAttendee('mailto:attendee2@domain.tld');
-
- $expectedIcs = file_get_contents(\OC::$SERVERROOT . '/tests/data/ics/event-builder-complete.ics');
- $calendar = $this->createMock(ICreateFromString::class);
- $calendar->expects(self::once())
- ->method('createFromString')
- ->with('event-uid-123.ics', $expectedIcs);
-
- $actual = $this->calendarEventBuilder->createInCalendar($calendar);
- $this->assertEquals('event-uid-123.ics', $actual);
- }
-
- public function testToIcsWithoutStartDate(): void {
- $this->calendarEventBuilder->setEndDate(new DateTimeImmutable('2025-01-05T17:19:58Z'));
- $this->calendarEventBuilder->setSummary('My event');
- $this->calendarEventBuilder->setDescription('Foo bar baz');
- $this->calendarEventBuilder->setOrganizer('organizer@domain.tld');
- $this->calendarEventBuilder->addAttendee('attendee1@domain.tld');
- $this->calendarEventBuilder->addAttendee('mailto:attendee2@domain.tld');
-
- $this->expectException(InvalidArgumentException::class);
- $this->expectExceptionMessageMatches('/start date/i');
- $this->calendarEventBuilder->toIcs();
- }
-
- public function testToIcsWithoutEndDate(): void {
- $this->calendarEventBuilder->setStartDate(new DateTimeImmutable('2025-01-05T17:09:58Z'));
- $this->calendarEventBuilder->setSummary('My event');
- $this->calendarEventBuilder->setDescription('Foo bar baz');
- $this->calendarEventBuilder->setOrganizer('organizer@domain.tld');
- $this->calendarEventBuilder->addAttendee('attendee1@domain.tld');
- $this->calendarEventBuilder->addAttendee('mailto:attendee2@domain.tld');
-
- $this->expectException(InvalidArgumentException::class);
- $this->expectExceptionMessageMatches('/end date/i');
- $this->calendarEventBuilder->toIcs();
- }
-
- public function testToIcsWithoutSummary(): void {
- $this->calendarEventBuilder->setStartDate(new DateTimeImmutable('2025-01-05T17:09:58Z'));
- $this->calendarEventBuilder->setEndDate(new DateTimeImmutable('2025-01-05T17:19:58Z'));
- $this->calendarEventBuilder->setDescription('Foo bar baz');
- $this->calendarEventBuilder->setOrganizer('organizer@domain.tld');
- $this->calendarEventBuilder->addAttendee('attendee1@domain.tld');
- $this->calendarEventBuilder->addAttendee('mailto:attendee2@domain.tld');
-
- $this->expectException(InvalidArgumentException::class);
- $this->expectExceptionMessageMatches('/summary/i');
- $this->calendarEventBuilder->toIcs();
- }
-
- public function testToIcsWithoutOrganizerWithAttendees(): void {
- $this->calendarEventBuilder->setStartDate(new DateTimeImmutable('2025-01-05T17:09:58Z'));
- $this->calendarEventBuilder->setEndDate(new DateTimeImmutable('2025-01-05T17:19:58Z'));
- $this->calendarEventBuilder->setSummary('My event');
- $this->calendarEventBuilder->setDescription('Foo bar baz');
- $this->calendarEventBuilder->addAttendee('attendee1@domain.tld');
- $this->calendarEventBuilder->addAttendee('mailto:attendee2@domain.tld');
-
- $this->expectException(InvalidArgumentException::class);
- $this->expectExceptionMessageMatches('/organizer/i');
- $this->calendarEventBuilder->toIcs();
- }
-}
--- a/tests/lib/Calendar/ManagerTest.php
+++ /dev/null
@@ -1,1247 +0,0 @@
-coordinator = $this->createMock(Coordinator::class);
- $this->container = $this->createMock(ContainerInterface::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->time = $this->createMock(ITimeFactory::class);
- $this->secureRandom = $this->createMock(ISecureRandom::class);
- $this->userManager = $this->createMock(IUserManager::class);
- $this->serverFactory = $this->createMock(ServerFactory::class);
- $this->propertyMapper = $this->createMock(PropertyMapper::class);
-
- $this->manager = new Manager(
- $this->coordinator,
- $this->container,
- $this->logger,
- $this->time,
- $this->secureRandom,
- $this->userManager,
- $this->serverFactory,
- $this->propertyMapper,
- );
-
- // construct calendar with a 1 hour event and same start/end time zones
- $this->vCalendar1a = new VCalendar();
- /** @var VEvent $vEvent */
- $vEvent = $this->vCalendar1a->add('VEVENT', []);
- $vEvent->UID->setValue('96a0e6b1-d886-4a55-a60d-152b31401dcc');
- $vEvent->add('DTSTART', '20240701T080000', ['TZID' => 'America/Toronto']);
- $vEvent->add('DTEND', '20240701T090000', ['TZID' => 'America/Toronto']);
- $vEvent->add('SUMMARY', 'Test Event');
- $vEvent->add('SEQUENCE', 3);
- $vEvent->add('STATUS', 'CONFIRMED');
- $vEvent->add('TRANSP', 'OPAQUE');
- $vEvent->add('ORGANIZER', 'mailto:organizer@testing.com', ['CN' => 'Organizer']);
- $vEvent->add('ATTENDEE', 'mailto:attendee1@testing.com', [
- 'CN' => 'Attendee One',
- 'CUTYPE' => 'INDIVIDUAL',
- 'PARTSTAT' => 'NEEDS-ACTION',
- 'ROLE' => 'REQ-PARTICIPANT',
- 'RSVP' => 'TRUE'
- ]);
-
- // construct calendar with a event for reply
- $this->vCalendar2a = new VCalendar();
- /** @var VEvent $vEvent */
- $vEvent = $this->vCalendar2a->add('VEVENT', []);
- $vEvent->UID->setValue('dcc733bf-b2b2-41f2-a8cf-550ae4b67aff');
- $vEvent->add('DTSTART', '20210820');
- $vEvent->add('DTEND', '20220821');
- $vEvent->add('SUMMARY', 'berry basket');
- $vEvent->add('SEQUENCE', 3);
- $vEvent->add('STATUS', 'CONFIRMED');
- $vEvent->add('TRANSP', 'OPAQUE');
- $vEvent->add('ORGANIZER', 'mailto:linus@stardew-tent-living.com', ['CN' => 'admin']);
- $vEvent->add('ATTENDEE', 'mailto:pierre@general-store.com', [
- 'CN' => 'pierre@general-store.com',
- 'CUTYPE' => 'INDIVIDUAL',
- 'ROLE' => 'REQ-PARTICIPANT',
- 'PARTSTAT' => 'ACCEPTED',
- ]);
-
- // construct calendar with a event for reply
- $this->vCalendar3a = new VCalendar();
- /** @var VEvent $vEvent */
- $vEvent = $this->vCalendar3a->add('VEVENT', []);
- $vEvent->UID->setValue('dcc733bf-b2b2-41f2-a8cf-550ae4b67aff');
- $vEvent->add('DTSTART', '20210820');
- $vEvent->add('DTEND', '20220821');
- $vEvent->add('SUMMARY', 'berry basket');
- $vEvent->add('SEQUENCE', 3);
- $vEvent->add('STATUS', 'CANCELLED');
- $vEvent->add('TRANSP', 'OPAQUE');
- $vEvent->add('ORGANIZER', 'mailto:linus@stardew-tent-living.com', ['CN' => 'admin']);
- $vEvent->add('ATTENDEE', 'mailto:pierre@general-store.com', [
- 'CN' => 'pierre@general-store.com',
- 'CUTYPE' => 'INDIVIDUAL',
- 'ROLE' => 'REQ-PARTICIPANT',
- 'PARTSTAT' => 'ACCEPTED',
- ]);
-
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('searchProvider')]
- public function testSearch($search1, $search2, $expected): void {
- /** @var ICalendar | MockObject $calendar1 */
- $calendar1 = $this->createMock(ICalendar::class);
- $calendar1->method('getKey')->willReturn('simple:1');
- $calendar1->expects($this->once())
- ->method('search')
- ->with('', [], [], null, null)
- ->willReturn($search1);
-
- /** @var ICalendar | MockObject $calendar2 */
- $calendar2 = $this->createMock(ICalendar::class);
- $calendar2->method('getKey')->willReturn('simple:2');
- $calendar2->expects($this->once())
- ->method('search')
- ->with('', [], [], null, null)
- ->willReturn($search2);
-
- $this->manager->registerCalendar($calendar1);
- $this->manager->registerCalendar($calendar2);
-
- $result = $this->manager->search('');
- $this->assertEquals($expected, $result);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('searchProvider')]
- public function testSearchOptions($search1, $search2, $expected): void {
- /** @var ICalendar | MockObject $calendar1 */
- $calendar1 = $this->createMock(ICalendar::class);
- $calendar1->method('getKey')->willReturn('simple:1');
- $calendar1->expects($this->once())
- ->method('search')
- ->with('searchTerm', ['SUMMARY', 'DESCRIPTION'],
- ['timerange' => ['start' => null, 'end' => null]], 5, 20)
- ->willReturn($search1);
-
- /** @var ICalendar | MockObject $calendar2 */
- $calendar2 = $this->createMock(ICalendar::class);
- $calendar2->method('getKey')->willReturn('simple:2');
- $calendar2->expects($this->once())
- ->method('search')
- ->with('searchTerm', ['SUMMARY', 'DESCRIPTION'],
- ['timerange' => ['start' => null, 'end' => null]], 5, 20)
- ->willReturn($search2);
-
- $this->manager->registerCalendar($calendar1);
- $this->manager->registerCalendar($calendar2);
-
- $result = $this->manager->search('searchTerm', ['SUMMARY', 'DESCRIPTION'],
- ['timerange' => ['start' => null, 'end' => null]], 5, 20);
- $this->assertEquals($expected, $result);
- }
-
- public static function searchProvider(): array {
- $search1 = [
- [
- 'id' => 1,
- 'data' => 'foobar',
- ],
- [
- 'id' => 2,
- 'data' => 'barfoo',
- ]
- ];
- $search2 = [
- [
- 'id' => 3,
- 'data' => 'blablub',
- ],
- [
- 'id' => 4,
- 'data' => 'blubbla',
- ]
- ];
-
- $expected = [
- [
- 'id' => 1,
- 'data' => 'foobar',
- 'calendar-key' => 'simple:1',
- ],
- [
- 'id' => 2,
- 'data' => 'barfoo',
- 'calendar-key' => 'simple:1',
- ],
- [
- 'id' => 3,
- 'data' => 'blablub',
- 'calendar-key' => 'simple:2',
- ],
- [
- 'id' => 4,
- 'data' => 'blubbla',
- 'calendar-key' => 'simple:2',
- ]
- ];
-
- return [
- [
- $search1,
- $search2,
- $expected
- ]
- ];
- }
-
- public function testRegisterUnregister(): void {
- /** @var ICalendar | MockObject $calendar1 */
- $calendar1 = $this->createMock(ICalendar::class);
- $calendar1->method('getKey')->willReturn('key1');
-
- /** @var ICalendar | MockObject $calendar2 */
- $calendar2 = $this->createMock(ICalendar::class);
- $calendar2->method('getKey')->willReturn('key2');
-
- $this->manager->registerCalendar($calendar1);
- $this->manager->registerCalendar($calendar2);
-
- $result = $this->manager->getCalendars();
- $this->assertCount(2, $result);
- $this->assertContains($calendar1, $result);
- $this->assertContains($calendar2, $result);
-
- $this->manager->unregisterCalendar($calendar1);
-
- $result = $this->manager->getCalendars();
- $this->assertCount(1, $result);
- $this->assertContains($calendar2, $result);
- }
-
- public function testGetCalendars(): void {
- /** @var ICalendar | MockObject $calendar1 */
- $calendar1 = $this->createMock(ICalendar::class);
- $calendar1->method('getKey')->willReturn('key1');
-
- /** @var ICalendar | MockObject $calendar2 */
- $calendar2 = $this->createMock(ICalendar::class);
- $calendar2->method('getKey')->willReturn('key2');
-
- $this->manager->registerCalendar($calendar1);
- $this->manager->registerCalendar($calendar2);
-
- $result = $this->manager->getCalendars();
- $this->assertCount(2, $result);
- $this->assertContains($calendar1, $result);
- $this->assertContains($calendar2, $result);
-
- $this->manager->clear();
-
- $result = $this->manager->getCalendars();
-
- $this->assertCount(0, $result);
- }
-
- public function testEnabledIfNot(): void {
- $isEnabled = $this->manager->isEnabled();
- $this->assertFalse($isEnabled);
- }
-
- public function testIfEnabledIfSo(): void {
- /** @var ICalendar | MockObject $calendar */
- $calendar = $this->createMock(ICalendar::class);
- $this->manager->registerCalendar($calendar);
-
- $isEnabled = $this->manager->isEnabled();
- $this->assertTrue($isEnabled);
- }
-
- public function testHandleImipWithNoCalendars(): void {
- // construct calendar manager returns
- /** @var Manager&MockObject $manager */
- $manager = $this->getMockBuilder(Manager::class)
- ->setConstructorArgs([
- $this->coordinator,
- $this->container,
- $this->logger,
- $this->time,
- $this->secureRandom,
- $this->userManager,
- $this->serverFactory,
- $this->propertyMapper,
- ])
- ->onlyMethods(['getCalendarsForPrincipal'])
- ->getMock();
- $manager->expects(self::once())
- ->method('getCalendarsForPrincipal')
- ->willReturn([]);
- // construct logger returns
- $this->logger->expects(self::once())->method('warning')
- ->with('iMip message could not be processed because user has no calendar that can process iMip messages');
- // construct parameters
- $userId = 'attendee1';
- $calendar = $this->vCalendar1a;
- $calendar->add('METHOD', 'REQUEST');
- // test method
- $result = $manager->handleIMip($userId, $calendar->serialize());
- // Assert
- $this->assertFalse($result);
- }
-
- public function testHandleImipWithNoEvent(): void {
- // construct mock user calendar
- $userCalendar = $this->createMock(ITestCalendar::class);
- $userCalendar->expects(self::once())
- ->method('isDeleted')
- ->willReturn(false);
- $userCalendar->expects(self::once())
- ->method('isWritable')
- ->willReturn(true);
- // construct mock calendar manager and returns
- /** @var Manager&MockObject $manager */
- $manager = $this->getMockBuilder(Manager::class)
- ->setConstructorArgs([
- $this->coordinator,
- $this->container,
- $this->logger,
- $this->time,
- $this->secureRandom,
- $this->userManager,
- $this->serverFactory,
- $this->propertyMapper,
- ])
- ->onlyMethods(['getCalendarsForPrincipal'])
- ->getMock();
- $manager->expects(self::once())
- ->method('getCalendarsForPrincipal')
- ->willReturn([$userCalendar]);
- // construct logger returns
- $this->logger->expects(self::once())->method('warning')
- ->with('iMip message does not contain any event(s)');
- // construct parameters
- $userId = 'attendee1';
- $calendar = $this->vCalendar1a;
- $calendar->add('METHOD', 'REQUEST');
- $calendar->remove('VEVENT');
- // Act
- $result = $manager->handleIMip($userId, $calendar->serialize());
- // Assert
- $this->assertFalse($result);
- }
-
- public function testHandleImipMissingOrganizerWithRecipient(): void {
- // construct mock user calendar
- $userCalendar = $this->createMock(ITestCalendar::class);
- $userCalendar->expects(self::once())
- ->method('isDeleted')
- ->willReturn(false);
- $userCalendar->expects(self::once())
- ->method('isWritable')
- ->willReturn(true);
- $userCalendar->expects(self::once())
- ->method('search')
- ->willReturn([['uri' => 'principals/user/attendee1/personal']]);
- // construct mock calendar manager and returns
- /** @var Manager&MockObject $manager */
- $manager = $this->getMockBuilder(Manager::class)
- ->setConstructorArgs([
- $this->coordinator,
- $this->container,
- $this->logger,
- $this->time,
- $this->secureRandom,
- $this->userManager,
- $this->serverFactory,
- $this->propertyMapper,
- ])
- ->onlyMethods(['getCalendarsForPrincipal'])
- ->getMock();
- $manager->expects(self::once())
- ->method('getCalendarsForPrincipal')
- ->willReturn([$userCalendar]);
- // construct parameters
- $userId = 'attendee1';
- $calendar = $this->vCalendar1a;
- $calendar->add('METHOD', 'REQUEST');
- $calendar->VEVENT->remove('ORGANIZER');
- // construct user calendar returns
- $userCalendar->expects(self::once())
- ->method('handleIMipMessage');
- // test method
- $result = $manager->handleIMip($userId, $calendar->serialize(), ['recipient' => 'organizer@testing.com']);
- }
-
- public function testHandleImipMissingOrganizerNoRecipient(): void {
- // construct mock user calendar
- $userCalendar = $this->createMock(ITestCalendar::class);
- $userCalendar->expects(self::once())
- ->method('isDeleted')
- ->willReturn(false);
- $userCalendar->expects(self::once())
- ->method('isWritable')
- ->willReturn(true);
- // construct mock calendar manager and returns
- /** @var Manager&MockObject $manager */
- $manager = $this->getMockBuilder(Manager::class)
- ->setConstructorArgs([
- $this->coordinator,
- $this->container,
- $this->logger,
- $this->time,
- $this->secureRandom,
- $this->userManager,
- $this->serverFactory,
- $this->propertyMapper,
- ])
- ->onlyMethods(['getCalendarsForPrincipal'])
- ->getMock();
- $manager->expects(self::once())
- ->method('getCalendarsForPrincipal')
- ->willReturn([$userCalendar]);
- // construct parameters
- $userId = 'attendee1';
- $calendar = $this->vCalendar1a;
- $calendar->add('METHOD', 'REQUEST');
- $calendar->VEVENT->remove('ORGANIZER');
- // Logger expects warning
- $this->logger->expects($this->once())
- ->method('warning')
- ->with('iMip message event does not contain an organizer and no recipient was provided');
-
- $result = $manager->handleIMip($userId, $calendar->serialize(), []);
- }
-
- public function testHandleImipWithNoUid(): void {
- // construct mock user calendar
- $userCalendar = $this->createMock(ITestCalendar::class);
- $userCalendar->expects(self::once())
- ->method('isDeleted')
- ->willReturn(false);
- $userCalendar->expects(self::once())
- ->method('isWritable')
- ->willReturn(true);
- // construct mock calendar manager and returns
- /** @var Manager&MockObject $manager */
- $manager = $this->getMockBuilder(Manager::class)
- ->setConstructorArgs([
- $this->coordinator,
- $this->container,
- $this->logger,
- $this->time,
- $this->secureRandom,
- $this->userManager,
- $this->serverFactory,
- $this->propertyMapper,
- ])
- ->onlyMethods(['getCalendarsForPrincipal'])
- ->getMock();
- $manager->expects(self::once())
- ->method('getCalendarsForPrincipal')
- ->willReturn([$userCalendar]);
- // construct logger returns
- $this->logger->expects(self::once())->method('warning')
- ->with('iMip message event does not contains a UID');
- // construct parameters
- $userId = 'attendee1';
- $calendar = $this->vCalendar1a;
- $calendar->add('METHOD', 'REQUEST');
- $calendar->VEVENT->remove('UID');
- // test method
- $result = $manager->handleIMip($userId, $calendar->serialize());
- // Assert
- $this->assertFalse($result);
- }
-
- public function testHandleImipWithNoMatch(): void {
- // construct mock user calendar
- $userCalendar = $this->createMock(ITestCalendar::class);
- $userCalendar->expects(self::once())
- ->method('isDeleted')
- ->willReturn(false);
- $userCalendar->expects(self::once())
- ->method('isWritable')
- ->willReturn(true);
- $userCalendar->expects(self::once())
- ->method('search')
- ->willReturn([]);
- // construct mock calendar manager and returns
- /** @var Manager&MockObject $manager */
- $manager = $this->getMockBuilder(Manager::class)
- ->setConstructorArgs([
- $this->coordinator,
- $this->container,
- $this->logger,
- $this->time,
- $this->secureRandom,
- $this->userManager,
- $this->serverFactory,
- $this->propertyMapper,
- ])
- ->onlyMethods(['getCalendarsForPrincipal'])
- ->getMock();
- $manager->expects(self::once())
- ->method('getCalendarsForPrincipal')
- ->willReturn([$userCalendar]);
- // construct logger returns
- $this->logger->expects(self::once())->method('warning')
- ->with('iMip message could not be processed because no corresponding event was found in any calendar');
- // construct parameters
- $userId = 'attendee1';
- $calendar = $this->vCalendar1a;
- $calendar->add('METHOD', 'REQUEST');
- // test method
- $result = $manager->handleIMip($userId, $calendar->serialize());
- // Assert
- $this->assertFalse($result);
- }
-
- public function testHandleImipWithCalendarUnableToHandleImip(): void {
- // construct mock user calendar which is writable but can not process iMip messages
- $userCalendar = $this->createMock(ITestCalendarWithoutImip::class);
- $userCalendar->expects(self::never())
- ->method('search');
- // construct mock calendar manager and returns
- /** @var Manager&MockObject $manager */
- $manager = $this->getMockBuilder(Manager::class)
- ->setConstructorArgs([
- $this->coordinator,
- $this->container,
- $this->logger,
- $this->time,
- $this->secureRandom,
- $this->userManager,
- $this->serverFactory,
- $this->propertyMapper,
- ])
- ->onlyMethods(['getCalendarsForPrincipal'])
- ->getMock();
- $manager->expects(self::once())
- ->method('getCalendarsForPrincipal')
- ->willReturn([$userCalendar]);
- // construct logger returns
- $this->logger->expects(self::once())->method('warning')
- ->with('iMip message could not be processed because user has no calendar that can process iMip messages');
- // construct parameters
- $userId = 'attendee1';
- $calendar = $this->vCalendar1a;
- $calendar->add('METHOD', 'REQUEST');
- // test method
- $result = $manager->handleIMip($userId, $calendar->serialize());
- // Assert
- $this->assertFalse($result);
- }
-
- public function testHandleImip(): void {
- // construct mock user calendar
- $userCalendar = $this->createMock(ITestCalendar::class);
- $userCalendar->expects(self::once())
- ->method('isDeleted')
- ->willReturn(false);
- $userCalendar->expects(self::once())
- ->method('isWritable')
- ->willReturn(true);
- $userCalendar->expects(self::once())
- ->method('search')
- ->willReturn([['uri' => 'principals/user/attendee1/personal']]);
- // construct mock calendar manager and returns
- /** @var Manager&MockObject $manager */
- $manager = $this->getMockBuilder(Manager::class)
- ->setConstructorArgs([
- $this->coordinator,
- $this->container,
- $this->logger,
- $this->time,
- $this->secureRandom,
- $this->userManager,
- $this->serverFactory,
- $this->propertyMapper,
- ])
- ->onlyMethods(['getCalendarsForPrincipal'])
- ->getMock();
- $manager->expects(self::once())
- ->method('getCalendarsForPrincipal')
- ->willReturn([$userCalendar]);
- // construct parameters
- $userId = 'attendee1';
- $calendar = $this->vCalendar1a;
- $calendar->add('METHOD', 'REQUEST');
- // construct user calendar returns
- $userCalendar->expects(self::once())
- ->method('handleIMipMessage');
- // test method
- $result = $manager->handleIMip($userId, $calendar->serialize());
- }
-
- public function testHandleImipWithAbsentCreateOption(): void {
- // construct mock user calendar (no matching event found)
- $userCalendar = $this->createMock(ITestCalendar::class);
- $userCalendar->expects(self::once())
- ->method('isDeleted')
- ->willReturn(false);
- $userCalendar->expects(self::once())
- ->method('isWritable')
- ->willReturn(true);
- $userCalendar->expects(self::once())
- ->method('search')
- ->willReturn([]);
- // construct mock calendar manager and returns
- /** @var Manager&MockObject $manager */
- $manager = $this->getMockBuilder(Manager::class)
- ->setConstructorArgs([
- $this->coordinator,
- $this->container,
- $this->logger,
- $this->time,
- $this->secureRandom,
- $this->userManager,
- $this->serverFactory,
- $this->propertyMapper,
- ])
- ->onlyMethods(['getCalendarsForPrincipal', 'getPrimaryCalendar'])
- ->getMock();
- $manager->expects(self::once())
- ->method('getCalendarsForPrincipal')
- ->willReturn([$userCalendar]);
- $manager->expects(self::once())
- ->method('getPrimaryCalendar')
- ->willReturn(null);
- // construct parameters
- $userId = 'attendee1';
- $calendar = $this->vCalendar1a;
- $calendar->add('METHOD', 'REQUEST');
- // construct user calendar returns - should create new event
- $userCalendar->expects(self::once())
- ->method('handleIMipMessage')
- ->with($userId, self::callback(function ($data) {
- return str_contains($data, 'STATUS:TENTATIVE');
- }));
- // test method with absent=create option
- $result = $manager->handleIMip($userId, $calendar->serialize(), [
- 'absent' => 'create',
- 'absentCreateStatus' => 'tentative',
- ]);
- // Assert
- $this->assertTrue($result);
- }
-
- public function testHandleImipWithAbsentIgnoreOption(): void {
- // construct mock user calendar (no matching event found)
- $userCalendar = $this->createMock(ITestCalendar::class);
- $userCalendar->expects(self::once())
- ->method('isDeleted')
- ->willReturn(false);
- $userCalendar->expects(self::once())
- ->method('isWritable')
- ->willReturn(true);
- $userCalendar->expects(self::once())
- ->method('search')
- ->willReturn([]);
- // construct mock calendar manager and returns
- /** @var Manager&MockObject $manager */
- $manager = $this->getMockBuilder(Manager::class)
- ->setConstructorArgs([
- $this->coordinator,
- $this->container,
- $this->logger,
- $this->time,
- $this->secureRandom,
- $this->userManager,
- $this->serverFactory,
- $this->propertyMapper,
- ])
- ->onlyMethods(['getCalendarsForPrincipal'])
- ->getMock();
- $manager->expects(self::once())
- ->method('getCalendarsForPrincipal')
- ->willReturn([$userCalendar]);
- // construct logger returns - should log warning since event not found and absent=ignore
- $this->logger->expects(self::once())->method('warning')
- ->with('iMip message could not be processed because no corresponding event was found in any calendar');
- // construct parameters
- $userId = 'attendee1';
- $calendar = $this->vCalendar1a;
- $calendar->add('METHOD', 'REQUEST');
- // test method with absent=ignore option
- $result = $manager->handleIMip($userId, $calendar->serialize(), [
- 'absent' => 'ignore',
- ]);
- // Assert
- $this->assertFalse($result);
- }
-
- public function testHandleImipWithAbsentCreateNoWritableCalendar(): void {
- // construct mock user calendar (not writable)
- $userCalendar = $this->createMock(ITestCalendar::class);
- $userCalendar->expects(self::once())
- ->method('isWritable')
- ->willReturn(false);
- $userCalendar->expects(self::never())
- ->method('search');
- // construct mock calendar manager and returns
- /** @var Manager&MockObject $manager */
- $manager = $this->getMockBuilder(Manager::class)
- ->setConstructorArgs([
- $this->coordinator,
- $this->container,
- $this->logger,
- $this->time,
- $this->secureRandom,
- $this->userManager,
- $this->serverFactory,
- $this->propertyMapper,
- ])
- ->onlyMethods(['getCalendarsForPrincipal', 'getPrimaryCalendar'])
- ->getMock();
- $manager->expects(self::once())
- ->method('getCalendarsForPrincipal')
- ->willReturn([$userCalendar]);
- $manager->expects(self::never())
- ->method('getPrimaryCalendar');
- // construct logger returns
- $this->logger->expects(self::once())->method('warning')
- ->with('iMip message could not be processed because user has no calendar that can process iMip messages');
- // construct parameters
- $userId = 'attendee1';
- $calendar = $this->vCalendar1a;
- $calendar->add('METHOD', 'REQUEST');
- // test method with absent=create option but no writable calendar
- $result = $manager->handleIMip($userId, $calendar->serialize(), [
- 'absent' => 'create',
- 'absentCreateStatus' => 'tentative',
- ]);
- // Assert
- $this->assertFalse($result);
- }
-
- public function testHandleImipWithAbsentCreateUsesPrimaryCalendar(): void {
- // construct mock user calendar (no matching event found)
- $userCalendar = $this->createMock(ITestCalendar::class);
- $userCalendar->expects(self::once())
- ->method('isDeleted')
- ->willReturn(false);
- $userCalendar->expects(self::once())
- ->method('isWritable')
- ->willReturn(true);
- $userCalendar->expects(self::once())
- ->method('search')
- ->willReturn([]);
- // construct mock primary calendar
- $primaryCalendar = $this->createMock(ITestCalendar::class);
- $primaryCalendar->expects(self::once())
- ->method('isDeleted')
- ->willReturn(false);
- $primaryCalendar->expects(self::once())
- ->method('isWritable')
- ->willReturn(true);
- // construct mock calendar manager and returns
- /** @var Manager&MockObject $manager */
- $manager = $this->getMockBuilder(Manager::class)
- ->setConstructorArgs([
- $this->coordinator,
- $this->container,
- $this->logger,
- $this->time,
- $this->secureRandom,
- $this->userManager,
- $this->serverFactory,
- $this->propertyMapper,
- ])
- ->onlyMethods(['getCalendarsForPrincipal', 'getPrimaryCalendar'])
- ->getMock();
- $manager->expects(self::once())
- ->method('getCalendarsForPrincipal')
- ->willReturn([$userCalendar]);
- $manager->expects(self::once())
- ->method('getPrimaryCalendar')
- ->willReturn($primaryCalendar);
- // construct parameters
- $userId = 'attendee1';
- $calendar = $this->vCalendar1a;
- $calendar->add('METHOD', 'REQUEST');
- // primary calendar should receive the event
- $primaryCalendar->expects(self::once())
- ->method('handleIMipMessage')
- ->with($userId, self::callback(function ($data) {
- return str_contains($data, 'STATUS:TENTATIVE');
- }));
- // test method with absent=create option
- $result = $manager->handleIMip($userId, $calendar->serialize(), [
- 'absent' => 'create',
- 'absentCreateStatus' => 'tentative',
- ]);
- // Assert
- $this->assertTrue($result);
- }
-
- public function testHandleImipWithAbsentCreateOverwritesExistingStatus(): void {
- // construct mock user calendar (no matching event found)
- $userCalendar = $this->createMock(ITestCalendar::class);
- $userCalendar->expects(self::once())
- ->method('isDeleted')
- ->willReturn(false);
- $userCalendar->expects(self::once())
- ->method('isWritable')
- ->willReturn(true);
- $userCalendar->expects(self::once())
- ->method('search')
- ->willReturn([]);
- // construct mock calendar manager and returns
- /** @var Manager&MockObject $manager */
- $manager = $this->getMockBuilder(Manager::class)
- ->setConstructorArgs([
- $this->coordinator,
- $this->container,
- $this->logger,
- $this->time,
- $this->secureRandom,
- $this->userManager,
- $this->serverFactory,
- $this->propertyMapper,
- ])
- ->onlyMethods(['getCalendarsForPrincipal', 'getPrimaryCalendar'])
- ->getMock();
- $manager->expects(self::once())
- ->method('getCalendarsForPrincipal')
- ->willReturn([$userCalendar]);
- $manager->expects(self::once())
- ->method('getPrimaryCalendar')
- ->willReturn(null);
- // construct parameters - calendar already has CONFIRMED status
- $userId = 'attendee1';
- $calendar = $this->vCalendar1a;
- $calendar->add('METHOD', 'REQUEST');
- // The original event has STATUS:CONFIRMED, but it should be overwritten to TENTATIVE
- $userCalendar->expects(self::once())
- ->method('handleIMipMessage')
- ->with($userId, self::callback(function ($data) {
- // Should contain TENTATIVE and not CONFIRMED
- return str_contains($data, 'STATUS:TENTATIVE') && !str_contains($data, 'STATUS:CONFIRMED');
- }));
- // test method with absent=create option
- $result = $manager->handleIMip($userId, $calendar->serialize(), [
- 'absent' => 'create',
- 'absentCreateStatus' => 'tentative',
- ]);
- // Assert
- $this->assertTrue($result);
- }
-
- public function testhandleIMipRequestWithInvalidPrincipal() {
- $invalidPrincipal = 'invalid-principal-uri';
- $sender = 'sender@example.com';
- $recipient = 'recipient@example.com';
- $calendarData = $this->vCalendar1a->serialize();
-
- $this->logger->expects(self::once())
- ->method('error')
- ->with('Invalid principal URI provided for iMip request');
-
- $result = $this->manager->handleIMipRequest($invalidPrincipal, $sender, $recipient, $calendarData);
- $this->assertFalse($result);
- }
-
- public function testhandleIMipRequest() {
- $principalUri = 'principals/users/attendee1';
- $sender = 'sender@example.com';
- $recipient = 'recipient@example.com';
- $calendarData = $this->vCalendar1a->serialize();
-
- /** @var Manager&MockObject $manager */
- $manager = $this->getMockBuilder(Manager::class)
- ->setConstructorArgs([
- $this->coordinator,
- $this->container,
- $this->logger,
- $this->time,
- $this->secureRandom,
- $this->userManager,
- $this->serverFactory,
- $this->propertyMapper,
- ])
- ->onlyMethods(['handleIMip'])
- ->getMock();
- $manager->expects(self::once())
- ->method('handleIMip')
- ->with('attendee1', $calendarData)
- ->willReturn(true);
-
- $result = $manager->handleIMipRequest($principalUri, $sender, $recipient, $calendarData);
- $this->assertTrue($result);
- }
-
- public function testhandleIMipReplyWithInvalidPrincipal() {
- $invalidPrincipal = 'invalid-principal-uri';
- $sender = 'sender@example.com';
- $recipient = 'recipient@example.com';
- $calendarData = $this->vCalendar2a->serialize();
-
- $this->logger->expects(self::once())
- ->method('error')
- ->with('Invalid principal URI provided for iMip reply');
-
- $result = $this->manager->handleIMipReply($invalidPrincipal, $sender, $recipient, $calendarData);
- $this->assertFalse($result);
- }
-
- public function testhandleIMipReply() {
- $principalUri = 'principals/users/attendee2';
- $sender = 'sender@example.com';
- $recipient = 'recipient@example.com';
- $calendarData = $this->vCalendar2a->serialize();
-
- /** @var Manager&MockObject $manager */
- $manager = $this->getMockBuilder(Manager::class)
- ->setConstructorArgs([
- $this->coordinator,
- $this->container,
- $this->logger,
- $this->time,
- $this->secureRandom,
- $this->userManager,
- $this->serverFactory,
- $this->propertyMapper,
- ])
- ->onlyMethods(['handleIMip'])
- ->getMock();
- $manager->expects(self::once())
- ->method('handleIMip')
- ->with('attendee2', $calendarData)
- ->willReturn(true);
-
- $result = $manager->handleIMipReply($principalUri, $sender, $recipient, $calendarData);
- $this->assertTrue($result);
- }
-
- public function testhandleIMipCancelWithInvalidPrincipal() {
- $invalidPrincipal = 'invalid-principal-uri';
- $sender = 'sender@example.com';
- $replyTo = null;
- $recipient = 'recipient@example.com';
- $calendarData = $this->vCalendar3a->serialize();
-
- $this->logger->expects(self::once())
- ->method('error')
- ->with('Invalid principal URI provided for iMip cancel');
-
- $result = $this->manager->handleIMipCancel($invalidPrincipal, $sender, $replyTo, $recipient, $calendarData);
- $this->assertFalse($result);
- }
-
- public function testhandleIMipCancel() {
- $principalUri = 'principals/users/attendee3';
- $sender = 'sender@example.com';
- $replyTo = null;
- $recipient = 'recipient@example.com';
- $calendarData = $this->vCalendar3a->serialize();
-
- /** @var Manager&MockObject $manager */
- $manager = $this->getMockBuilder(Manager::class)
- ->setConstructorArgs([
- $this->coordinator,
- $this->container,
- $this->logger,
- $this->time,
- $this->secureRandom,
- $this->userManager,
- $this->serverFactory,
- $this->propertyMapper,
- ])
- ->onlyMethods(['handleIMip'])
- ->getMock();
- $manager->expects(self::once())
- ->method('handleIMip')
- ->with('attendee3', $calendarData)
- ->willReturn(true);
-
- $result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData);
- $this->assertTrue($result);
- }
-
- private function getFreeBusyResponse(): string {
- return <<
-
-
-
- mailto:admin@imap.localhost
-
- 2.0;Success
- BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Sabre//Sabre VObject 4.5.6//EN
-CALSCALE:GREGORIAN
-METHOD:REPLY
-BEGIN:VFREEBUSY
-DTSTART:20250116T060000Z
-DTEND:20250117T060000Z
-DTSTAMP:20250111T125634Z
-FREEBUSY:20250116T060000Z/20250116T230000Z
-FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:20250116T230000Z/20250117T060000Z
-ATTENDEE:mailto:admin@imap.localhost
-UID:6099eab3-9bf1-4c7a-809e-4d46957cc372
-ORGANIZER;CN=admin:mailto:admin@imap.localhost
-END:VFREEBUSY
-END:VCALENDAR
-
-
-
-
- mailto:empty@imap.localhost
-
- 2.0;Success
- BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Sabre//Sabre VObject 4.5.6//EN
-CALSCALE:GREGORIAN
-METHOD:REPLY
-BEGIN:VFREEBUSY
-DTSTART:20250116T060000Z
-DTEND:20250117T060000Z
-DTSTAMP:20250111T125634Z
-ATTENDEE:mailto:empty@imap.localhost
-UID:6099eab3-9bf1-4c7a-809e-4d46957cc372
-ORGANIZER;CN=admin:mailto:admin@imap.localhost
-END:VFREEBUSY
-END:VCALENDAR
-
-
-
-
- mailto:user@imap.localhost
-
- 2.0;Success
- BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Sabre//Sabre VObject 4.5.6//EN
-CALSCALE:GREGORIAN
-METHOD:REPLY
-BEGIN:VFREEBUSY
-DTSTART:20250116T060000Z
-DTEND:20250117T060000Z
-DTSTAMP:20250111T125634Z
-FREEBUSY:20250116T060000Z/20250116T230000Z
-FREEBUSY;FBTYPE=BUSY-UNAVAILABLE:20250116T230000Z/20250117T060000Z
-ATTENDEE:mailto:user@imap.localhost
-UID:6099eab3-9bf1-4c7a-809e-4d46957cc372
-ORGANIZER;CN=admin:mailto:admin@imap.localhost
-END:VFREEBUSY
-END:VCALENDAR
-
-
-
-
- mailto:nouser@domain.tld
-
- 3.7;Could not find principal
-
-
-EOF;
- }
-
- public function testCheckAvailability(): void {
- $organizer = $this->createMock(IUser::class);
- $organizer->expects(self::once())
- ->method('getUID')
- ->willReturn('admin');
- $organizer->expects(self::once())
- ->method('getEMailAddress')
- ->willReturn('admin@imap.localhost');
-
- $user1 = $this->createMock(IUser::class);
- $user2 = $this->createMock(IUser::class);
-
- $this->userManager->expects(self::exactly(3))
- ->method('getByEmail')
- ->willReturnMap([
- ['user@imap.localhost', [$user1]],
- ['empty@imap.localhost', [$user2]],
- ['nouser@domain.tld', []],
- ]);
-
- $authPlugin = $this->createMock(CustomPrincipalPlugin::class);
- $authPlugin->expects(self::once())
- ->method('setCurrentPrincipal')
- ->with('principals/users/admin');
-
- $server = $this->createMock(Server::class);
- $server->expects(self::once())
- ->method('getPlugin')
- ->with('auth')
- ->willReturn($authPlugin);
- $server->expects(self::once())
- ->method('invokeMethod')
- ->willReturnCallback(function (
- RequestInterface $request,
- ResponseInterface $response,
- bool $sendResponse,
- ): void {
- $requestBody = file_get_contents(__DIR__ . '/../../data/ics/free-busy-request.ics');
- $this->assertEquals('POST', $request->getMethod());
- $this->assertEquals('calendars/admin/outbox', $request->getPath());
- $this->assertEquals('text/calendar', $request->getHeader('Content-Type'));
- $this->assertEquals('0', $request->getHeader('Depth'));
- $this->assertEquals($requestBody, $request->getBodyAsString());
- $this->assertFalse($sendResponse);
- $response->setStatus(200);
- $response->setBody($this->getFreeBusyResponse());
- });
-
- $this->serverFactory->expects(self::once())
- ->method('createAttendeeAvailabilityServer')
- ->willReturn($server);
-
- $start = new DateTimeImmutable('2025-01-16T06:00:00Z');
- $end = new DateTimeImmutable('2025-01-17T06:00:00Z');
- $actual = $this->manager->checkAvailability($start, $end, $organizer, [
- 'user@imap.localhost',
- 'empty@imap.localhost',
- 'nouser@domain.tld',
- ]);
- $expected = [
- new AvailabilityResult('admin@imap.localhost', false),
- new AvailabilityResult('empty@imap.localhost', true),
- new AvailabilityResult('user@imap.localhost', false),
- ];
- $this->assertEquals($expected, $actual);
- }
-
- public function testCheckAvailabilityWithMailtoPrefix(): void {
- $organizer = $this->createMock(IUser::class);
- $organizer->expects(self::once())
- ->method('getUID')
- ->willReturn('admin');
- $organizer->expects(self::once())
- ->method('getEMailAddress')
- ->willReturn('admin@imap.localhost');
-
- $user1 = $this->createMock(IUser::class);
- $user2 = $this->createMock(IUser::class);
-
- $this->userManager->expects(self::exactly(3))
- ->method('getByEmail')
- ->willReturnMap([
- ['user@imap.localhost', [$user1]],
- ['empty@imap.localhost', [$user2]],
- ['nouser@domain.tld', []],
- ]);
-
- $authPlugin = $this->createMock(CustomPrincipalPlugin::class);
- $authPlugin->expects(self::once())
- ->method('setCurrentPrincipal')
- ->with('principals/users/admin');
-
- $server = $this->createMock(Server::class);
- $server->expects(self::once())
- ->method('getPlugin')
- ->with('auth')
- ->willReturn($authPlugin);
- $server->expects(self::once())
- ->method('invokeMethod')
- ->willReturnCallback(function (
- RequestInterface $request,
- ResponseInterface $response,
- bool $sendResponse,
- ): void {
- $requestBody = file_get_contents(__DIR__ . '/../../data/ics/free-busy-request.ics');
- $this->assertEquals('POST', $request->getMethod());
- $this->assertEquals('calendars/admin/outbox', $request->getPath());
- $this->assertEquals('text/calendar', $request->getHeader('Content-Type'));
- $this->assertEquals('0', $request->getHeader('Depth'));
- $this->assertEquals($requestBody, $request->getBodyAsString());
- $this->assertFalse($sendResponse);
- $response->setStatus(200);
- $response->setBody($this->getFreeBusyResponse());
- });
-
- $this->serverFactory->expects(self::once())
- ->method('createAttendeeAvailabilityServer')
- ->willReturn($server);
-
- $start = new DateTimeImmutable('2025-01-16T06:00:00Z');
- $end = new DateTimeImmutable('2025-01-17T06:00:00Z');
- $actual = $this->manager->checkAvailability($start, $end, $organizer, [
- 'mailto:user@imap.localhost',
- 'mailto:empty@imap.localhost',
- 'mailto:nouser@domain.tld',
- ]);
- $expected = [
- new AvailabilityResult('admin@imap.localhost', false),
- new AvailabilityResult('empty@imap.localhost', true),
- new AvailabilityResult('user@imap.localhost', false),
- ];
- $this->assertEquals($expected, $actual);
- }
-
-}
--- a/tests/lib/Calendar/Resource/ManagerTest.php
+++ /dev/null
@@ -1,70 +0,0 @@
-coordinator = $this->createMock(Coordinator::class);
- $this->server = $this->createMock(ContainerInterface::class);
- $this->resourcesRoomsUpdater = $this->createMock(ResourcesRoomsUpdater::class);
-
- $this->manager = new Manager(
- $this->coordinator,
- $this->server,
- $this->resourcesRoomsUpdater,
- );
- }
-
- public function testGetBackendFromBootstrapRegistration(): void {
- $backendClass = '\\OCA\\CalendarResourceFoo\\Backend';
- $backend = $this->createMock(IBackend::class);
- $backend->method('getBackendIdentifier')->willReturn('from_bootstrap');
- $context = $this->createMock(RegistrationContext::class);
- $this->coordinator->expects(self::once())
- ->method('getRegistrationContext')
- ->willReturn($context);
- $context->expects(self::once())
- ->method('getCalendarResourceBackendRegistrations')
- ->willReturn([
- new ServiceRegistration('calendar_resource_foo', $backendClass)
- ]);
- $this->server->expects(self::once())
- ->method('get')
- ->with($backendClass)
- ->willReturn($backend);
-
- self::assertEquals($backend, $this->manager->getBackend('from_bootstrap'));
- }
-
- public function testUpdate(): void {
- $this->resourcesRoomsUpdater->expects(self::once())
- ->method('updateResources');
-
- $this->manager->update();
- }
-}
--- a/tests/lib/Calendar/ResourcesRoomsUpdaterTest.php
+++ /dev/null
@@ -1,430 +0,0 @@
-resourceManager = $this->createMock(Resource\Manager::class);
- $this->roomManager = $this->createMock(Room\Manager::class);
- $this->container = $this->createMock(ContainerInterface::class);
- $this->calDavBackend = $this->createMock(CalDavBackend::class);
-
- $this->container->method('get')
- ->willReturnMap([
- [Resource\Manager::class, $this->resourceManager],
- [Room\Manager::class, $this->roomManager],
- ]);
-
- $this->updater = new ResourcesRoomsUpdater(
- $this->container,
- self::$realDatabase,
- $this->calDavBackend
- );
- }
-
- #[\Override]
- protected function tearDown(): void {
- $query = self::$realDatabase->getQueryBuilder();
- $query->delete('calendar_resources')->executeStatement();
- $query->delete('calendar_resources_md')->executeStatement();
- $query->delete('calendar_rooms')->executeStatement();
- $query->delete('calendar_rooms_md')->executeStatement();
- parent::tearDown();
- }
-
- /**
- * Data in Cache:
- * resources:
- * [backend1, res1, Beamer1, {}] - []
- * [backend1, res2, TV1, {}] - []
- * [backend2, res3, Beamer2, {}] - ['meta1' => 'value1', 'meta2' => 'value2']
- * [backend2, res4, TV2, {}] - ['meta1' => 'value1', 'meta3' => 'value3-old']
- * [backend3, res5, Beamer3, {}] - []
- * [backend3, res6, Pointer, {foo, bar}] - ['meta99' => 'value99']
- *
- * Data in Backend:
- * backend1 gone
- * backend2 throws BackendTemporarilyUnavailableException
- * [backend3, res6, Pointer123, {foo, biz}] - ['meta99' => 'value99-new', 'meta123' => 'meta456']
- * [backend3, res7, Resource4, {biz}] - ['meta1' => 'value1']
- * [backend4, res8, Beamer, {}] - ['meta2' => 'value2']
- * [backend4, res9, Beamer2, {}] - []
- *
- * Expected after run:
- * [backend1, res1, Beamer1, {}] - []
- * [backend1, res2, TV1, {}] - []
- * [backend2, res3, Beamer2, {}] - ['meta1' => 'value1', 'meta2' => 'value2']
- * [backend2, res4, TV2, {}] - ['meta1' => 'value1', 'meta3' => 'value3-old']
- * [backend3, res6, Pointer123, {foo, biz}] - ['meta99' => 'value99-new', 'meta123' => 'meta456']
- * [backend3, res7, Resource4, {biz}] - ['meta1' => 'value1']
- * [backend4, res8, Beamer, {}] - ['meta2' => 'value2']
- * [backend4, res9, Beamer2, {}] - []
- */
-
- public function testUpdateBoth(): void {
- $this->createTestResourcesInCache();
-
- $backend2 = $this->createMock(IBackend::class);
- $backend3 = $this->createMock(IBackend::class);
- $backend4 = $this->createMock(IBackend::class);
-
- $res6 = $this->createMock(tmpI::class);
- $res7 = $this->createMock(tmpI::class);
- $res8 = $this->createMock(tmpI::class);
- $res9 = $this->createMock(IResource::class);
-
- $backend2->method('getBackendIdentifier')
- ->willReturn('backend2');
- $backend2->method('listAllResources')
- ->willThrowException(new BackendTemporarilyUnavailableException());
- $backend2->method('getResource')
- ->willThrowException(new BackendTemporarilyUnavailableException());
- $backend2->method('getAllResources')
- ->willThrowException(new BackendTemporarilyUnavailableException());
- $backend3->method('getBackendIdentifier')
- ->willReturn('backend3');
- $backend3->method('listAllResources')
- ->willReturn(['res6', 'res7']);
- $backend3->method('getResource')
- ->willReturnMap([
- ['res6', $res6],
- ['res7', $res7],
- ]);
- $backend4->method('getBackendIdentifier')
- ->willReturn('backend4');
- $backend4->method('listAllResources')
- ->willReturn(['res8', 'res9']);
- $backend4->method('getResource')
- ->willReturnMap([
- ['res8', $res8],
- ['res9', $res9],
- ]);
-
- $res6->method('getId')->willReturn('res6');
- $res6->method('getDisplayName')->willReturn('Pointer123');
- $res6->method('getGroupRestrictions')->willReturn(['foo', 'biz']);
- $res6->method('getEMail')->willReturn('res6@foo.bar');
- $res6->method('getBackend')->willReturn($backend3);
-
- $res6->method('getAllAvailableMetadataKeys')->willReturn(['meta99', 'meta123']);
- $res6->method('getMetadataForKey')->willReturnCallback(function ($key) {
- switch ($key) {
- case 'meta99':
- return 'value99-new';
- case 'meta123':
- return 'meta456';
- default:
- return null;
- }
- });
-
- $res7->method('getId')->willReturn('res7');
- $res7->method('getDisplayName')->willReturn('Resource4');
- $res7->method('getGroupRestrictions')->willReturn(['biz']);
- $res7->method('getEMail')->willReturn('res7@foo.bar');
- $res7->method('getBackend')->willReturn($backend3);
- $res7->method('getAllAvailableMetadataKeys')->willReturn(['meta1']);
- $res7->method('getMetadataForKey')->willReturnCallback(function ($key) {
- switch ($key) {
- case 'meta1':
- return 'value1';
- default:
- return null;
- }
- });
-
- $res8->method('getId')->willReturn('res8');
- $res8->method('getDisplayName')->willReturn('Beamer');
- $res8->method('getGroupRestrictions')->willReturn([]);
- $res8->method('getEMail')->willReturn('res8@foo.bar');
- $res8->method('getBackend')->willReturn($backend4);
- $res8->method('getAllAvailableMetadataKeys')->willReturn(['meta2']);
- $res8->method('getMetadataForKey')->willReturnCallback(function ($key) {
- switch ($key) {
- case 'meta2':
- return 'value2';
- default:
- return null;
- }
- });
-
- $res9->method('getId')->willReturn('res9');
- $res9->method('getDisplayName')->willReturn('Beamer2');
- $res9->method('getGroupRestrictions')->willReturn([]);
- $res9->method('getEMail')->willReturn('res9@foo.bar');
- $res9->method('getBackend')->willReturn($backend4);
-
- $this->resourceManager
- ->method('getBackends')
- ->willReturn([
- $backend2, $backend3, $backend4
- ]);
- $this->resourceManager
- ->method('getBackend')
- ->willReturnMap([
- ['backend2', $backend2],
- ['backend3', $backend3],
- ['backend4', $backend4],
- ]);
-
- $this->updater->updateResources();
- $this->updater->updateRooms();
-
- $query = self::$realDatabase->getQueryBuilder();
- $query->select('*')->from('calendar_resources');
-
- $rows = [];
- $ids = [];
- $stmt = $query->executeQuery();
- while ($row = $stmt->fetchAssociative()) {
- $ids[$row['backend_id'] . '::' . $row['resource_id']] = $row['id'];
- unset($row['id']);
- $rows[] = $row;
- }
-
- $this->assertEquals([
- [
- 'backend_id' => 'backend1',
- 'resource_id' => 'res1',
- 'displayname' => 'Beamer1',
- 'email' => 'res1@foo.bar',
- 'group_restrictions' => '[]',
- ],
- [
- 'backend_id' => 'backend1',
- 'resource_id' => 'res2',
- 'displayname' => 'TV1',
- 'email' => 'res2@foo.bar',
- 'group_restrictions' => '[]',
- ],
- [
- 'backend_id' => 'backend2',
- 'resource_id' => 'res3',
- 'displayname' => 'Beamer2',
- 'email' => 'res3@foo.bar',
- 'group_restrictions' => '[]',
- ],
- [
- 'backend_id' => 'backend2',
- 'resource_id' => 'res4',
- 'displayname' => 'TV2',
- 'email' => 'res4@foo.bar',
- 'group_restrictions' => '[]',
- ],
- [
- 'backend_id' => 'backend3',
- 'resource_id' => 'res6',
- 'displayname' => 'Pointer123',
- 'email' => 'res6@foo.bar',
- 'group_restrictions' => '["foo","biz"]',
- ],
- [
- 'backend_id' => 'backend3',
- 'resource_id' => 'res7',
- 'displayname' => 'Resource4',
- 'email' => 'res7@foo.bar',
- 'group_restrictions' => '["biz"]',
- ],
- [
- 'backend_id' => 'backend4',
- 'resource_id' => 'res8',
- 'displayname' => 'Beamer',
- 'email' => 'res8@foo.bar',
- 'group_restrictions' => '[]',
- ],
- [
- 'backend_id' => 'backend4',
- 'resource_id' => 'res9',
- 'displayname' => 'Beamer2',
- 'email' => 'res9@foo.bar',
- 'group_restrictions' => '[]',
- ],
- ], $rows);
-
- $query2 = self::$realDatabase->getQueryBuilder();
- $query2->select('*')->from('calendar_resources_md');
-
- $rows2 = [];
- $stmt = $query2->executeQuery();
- while ($row = $stmt->fetchAssociative()) {
- unset($row['id']);
- $rows2[] = $row;
- }
-
- $this->assertEquals([
- [
- 'resource_id' => $ids['backend2::res3'],
- 'key' => 'meta1',
- 'value' => 'value1',
- ],
- [
- 'resource_id' => $ids['backend2::res3'],
- 'key' => 'meta2',
- 'value' => 'value2',
- ],
- [
- 'resource_id' => $ids['backend2::res4'],
- 'key' => 'meta1',
- 'value' => 'value1',
- ],
- [
- 'resource_id' => $ids['backend2::res4'],
- 'key' => 'meta3',
- 'value' => 'value3-old',
- ],
- [
- 'resource_id' => $ids['backend3::res6'],
- 'key' => 'meta99',
- 'value' => 'value99-new',
- ],
- [
- 'resource_id' => $ids['backend3::res7'],
- 'key' => 'meta1',
- 'value' => 'value1',
- ],
- [
- 'resource_id' => $ids['backend3::res6'],
- 'key' => 'meta123',
- 'value' => 'meta456',
- ],
- [
- 'resource_id' => $ids['backend4::res8'],
- 'key' => 'meta2',
- 'value' => 'value2',
- ]
- ], $rows2);
- }
-
- protected function createTestResourcesInCache() {
- $query = self::$realDatabase->getQueryBuilder();
- $query->insert('calendar_resources')
- ->values([
- 'backend_id' => $query->createNamedParameter('backend1'),
- 'resource_id' => $query->createNamedParameter('res1'),
- 'email' => $query->createNamedParameter('res1@foo.bar'),
- 'displayname' => $query->createNamedParameter('Beamer1'),
- 'group_restrictions' => $query->createNamedParameter('[]'),
- ])
- ->executeStatement();
-
- $query->insert('calendar_resources')
- ->values([
- 'backend_id' => $query->createNamedParameter('backend1'),
- 'resource_id' => $query->createNamedParameter('res2'),
- 'email' => $query->createNamedParameter('res2@foo.bar'),
- 'displayname' => $query->createNamedParameter('TV1'),
- 'group_restrictions' => $query->createNamedParameter('[]'),
- ])
- ->executeStatement();
-
- $query->insert('calendar_resources')
- ->values([
- 'backend_id' => $query->createNamedParameter('backend2'),
- 'resource_id' => $query->createNamedParameter('res3'),
- 'email' => $query->createNamedParameter('res3@foo.bar'),
- 'displayname' => $query->createNamedParameter('Beamer2'),
- 'group_restrictions' => $query->createNamedParameter('[]'),
- ])
- ->executeStatement();
- $id3 = $query->getLastInsertId();
-
- $query->insert('calendar_resources')
- ->values([
- 'backend_id' => $query->createNamedParameter('backend2'),
- 'resource_id' => $query->createNamedParameter('res4'),
- 'email' => $query->createNamedParameter('res4@foo.bar'),
- 'displayname' => $query->createNamedParameter('TV2'),
- 'group_restrictions' => $query->createNamedParameter('[]'),
- ])
- ->executeStatement();
- $id4 = $query->getLastInsertId();
-
- $query->insert('calendar_resources')
- ->values([
- 'backend_id' => $query->createNamedParameter('backend3'),
- 'resource_id' => $query->createNamedParameter('res5'),
- 'email' => $query->createNamedParameter('res5@foo.bar'),
- 'displayname' => $query->createNamedParameter('Beamer3'),
- 'group_restrictions' => $query->createNamedParameter('[]'),
- ])
- ->executeStatement();
-
- $query->insert('calendar_resources')
- ->values([
- 'backend_id' => $query->createNamedParameter('backend3'),
- 'resource_id' => $query->createNamedParameter('res6'),
- 'email' => $query->createNamedParameter('res6@foo.bar'),
- 'displayname' => $query->createNamedParameter('Pointer'),
- 'group_restrictions' => $query->createNamedParameter('["foo", "bar"]'),
- ])
- ->executeStatement();
- $id6 = $query->getLastInsertId();
-
- $query->insert('calendar_resources_md')
- ->values([
- 'resource_id' => $query->createNamedParameter($id3),
- 'key' => $query->createNamedParameter('meta1'),
- 'value' => $query->createNamedParameter('value1')
- ])
- ->executeStatement();
- $query->insert('calendar_resources_md')
- ->values([
- 'resource_id' => $query->createNamedParameter($id3),
- 'key' => $query->createNamedParameter('meta2'),
- 'value' => $query->createNamedParameter('value2')
- ])
- ->executeStatement();
- $query->insert('calendar_resources_md')
- ->values([
- 'resource_id' => $query->createNamedParameter($id4),
- 'key' => $query->createNamedParameter('meta1'),
- 'value' => $query->createNamedParameter('value1')
- ])
- ->executeStatement();
- $query->insert('calendar_resources_md')
- ->values([
- 'resource_id' => $query->createNamedParameter($id4),
- 'key' => $query->createNamedParameter('meta3'),
- 'value' => $query->createNamedParameter('value3-old')
- ])
- ->executeStatement();
- $query->insert('calendar_resources_md')
- ->values([
- 'resource_id' => $query->createNamedParameter($id6),
- 'key' => $query->createNamedParameter('meta99'),
- 'value' => $query->createNamedParameter('value99')
- ])
- ->executeStatement();
- }
-}
--- a/tests/lib/Calendar/Room/ManagerTest.php
+++ /dev/null
@@ -1,70 +0,0 @@
-coordinator = $this->createMock(Coordinator::class);
- $this->server = $this->createMock(ContainerInterface::class);
- $this->resourcesRoomsUpdater = $this->createMock(ResourcesRoomsUpdater::class);
-
- $this->manager = new Manager(
- $this->coordinator,
- $this->server,
- $this->resourcesRoomsUpdater,
- );
- }
-
- public function testGetBackendFromBootstrapRegistration(): void {
- $backendClass = '\\OCA\\CalendarRoomFoo\\Backend';
- $backend = $this->createMock(IBackend::class);
- $backend->method('getBackendIdentifier')->willReturn('from_bootstrap');
- $context = $this->createMock(RegistrationContext::class);
- $this->coordinator->expects(self::once())
- ->method('getRegistrationContext')
- ->willReturn($context);
- $context->expects(self::once())
- ->method('getCalendarRoomBackendRegistrations')
- ->willReturn([
- new ServiceRegistration('calendar_room_foo', $backendClass)
- ]);
- $this->server->expects(self::once())
- ->method('get')
- ->with($backendClass)
- ->willReturn($backend);
-
- self::assertEquals($backend, $this->manager->getBackend('from_bootstrap'));
- }
-
- public function testUpdate(): void {
- $this->resourcesRoomsUpdater->expects(self::once())
- ->method('updateRooms');
-
- $this->manager->update();
- }
-}
--- a/tests/lib/CapabilitiesManagerTest.php
+++ /dev/null
@@ -1,202 +0,0 @@
-logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
- $this->manager = new CapabilitiesManager($this->logger);
- }
-
- /**
- * Test no capabilities
- */
- public function testNoCapabilities(): void {
- $res = $this->manager->getCapabilities();
- $this->assertEmpty($res);
- }
-
- /**
- * Test a valid capabilitie
- */
- public function testValidCapability(): void {
- $this->manager->registerCapability(function () {
- return new SimpleCapability();
- });
-
- $res = $this->manager->getCapabilities();
- $this->assertEquals(['foo' => 1], $res);
- }
-
- /**
- * Test a public capabilitie
- */
- public function testPublicCapability(): void {
- $this->manager->registerCapability(function () {
- return new PublicSimpleCapability1();
- });
- $this->manager->registerCapability(function () {
- return new SimpleCapability2();
- });
- $this->manager->registerCapability(function () {
- return new SimpleCapability3();
- });
-
- $res = $this->manager->getCapabilities(true);
- $this->assertEquals(['foo' => 1], $res);
- }
-
- /**
- * Test that we need something that implents ICapability
- */
- public function testNoICapability(): void {
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('The given Capability (Test\\NoCapability) does not implement the ICapability interface');
-
- $this->manager->registerCapability(function () {
- return new NoCapability();
- });
-
- $res = $this->manager->getCapabilities();
- $this->assertEquals([], $res);
- }
-
- /**
- * Test a bunch of merged Capabilities
- */
- public function testMergedCapabilities(): void {
- $this->manager->registerCapability(function () {
- return new SimpleCapability();
- });
- $this->manager->registerCapability(function () {
- return new SimpleCapability2();
- });
- $this->manager->registerCapability(function () {
- return new SimpleCapability3();
- });
-
- $res = $this->manager->getCapabilities();
- $expected = [
- 'foo' => 1,
- 'bar' => [
- 'x' => 1,
- 'y' => 2
- ]
- ];
-
- $this->assertEquals($expected, $res);
- }
-
- /**
- * Test deep identical capabilities
- */
- public function testDeepIdenticalCapabilities(): void {
- $this->manager->registerCapability(function () {
- return new DeepCapability();
- });
- $this->manager->registerCapability(function () {
- return new DeepCapability();
- });
-
- $res = $this->manager->getCapabilities();
- $expected = [
- 'foo' => [
- 'bar' => [
- 'baz' => true
- ]
- ]
- ];
-
- $this->assertEquals($expected, $res);
- }
-
- public function testInvalidCapability(): void {
- $this->manager->registerCapability(function (): void {
- throw new QueryException();
- });
-
- $this->logger->expects($this->once())
- ->method('error');
-
- $res = $this->manager->getCapabilities();
-
- $this->assertEquals([], $res);
- }
-}
-
-class SimpleCapability implements ICapability {
- #[\Override]
- public function getCapabilities() {
- return [
- 'foo' => 1
- ];
- }
-}
-
-class SimpleCapability2 implements ICapability {
- #[\Override]
- public function getCapabilities() {
- return [
- 'bar' => ['x' => 1]
- ];
- }
-}
-
-class SimpleCapability3 implements ICapability {
- #[\Override]
- public function getCapabilities() {
- return [
- 'bar' => ['y' => 2]
- ];
- }
-}
-
-class PublicSimpleCapability1 implements IPublicCapability {
- #[\Override]
- public function getCapabilities() {
- return [
- 'foo' => 1
- ];
- }
-}
-
-class NoCapability {
- public function getCapabilities() {
- return [
- 'baz' => 'z'
- ];
- }
-}
-
-class DeepCapability implements ICapability {
- #[\Override]
- public function getCapabilities() {
- return [
- 'foo' => [
- 'bar' => [
- 'baz' => true
- ]
- ]
- ];
- }
-}
--- a/tests/lib/Collaboration/Collaborators/GroupPluginTest.php
+++ /dev/null
@@ -1,489 +0,0 @@
-config = $this->createMock(IConfig::class);
-
- $this->groupManager = $this->createMock(IGroupManager::class);
-
- $this->session = $this->createMock(IUserSession::class);
-
- $this->searchResult = new SearchResult();
-
- $this->user = $this->getUserMock('admin', 'Administrator');
- }
-
- public function instantiatePlugin(): void {
- // cannot be done within setUp, because dependent mocks needs to be set
- // up with configuration etc. first
- $this->plugin = new GroupPlugin(
- $this->config,
- $this->groupManager,
- $this->session
- );
- }
-
- public function getUserMock(string $uid, string $displayName): IUser&MockObject {
- $user = $this->createMock(IUser::class);
-
- $user->expects($this->any())
- ->method('getUID')
- ->willReturn($uid);
-
- $user->expects($this->any())
- ->method('getDisplayName')
- ->willReturn($displayName);
-
- return $user;
- }
-
- protected function getGroupMock(string $gid, ?string $displayName = null, bool $hide = false): IGroup&MockObject {
- $group = $this->createMock(IGroup::class);
-
- $group->expects($this->any())
- ->method('getGID')
- ->willReturn($gid);
-
- if (is_null($displayName)) {
- // note: this is how the Group class behaves
- $displayName = $gid;
- }
-
- $group->expects($this->any())
- ->method('getDisplayName')
- ->willReturn($displayName);
-
- $group->method('hideFromCollaboration')
- ->willReturn($hide);
-
- return $group;
- }
-
- public static function dataGetGroups(): array {
- return [
- ['test', false, true, false, [], [], [], [], true, false],
- ['test', false, false, false, [], [], [], [], true, false],
- // group sharing disabled
- ['test', false, true, true, [], [], [], [], false, false],
- // group without display name
- [
- 'test', false, true, false,
- [['test1']],
- [],
- [],
- [['label' => 'test1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test1']]],
- true,
- false,
- ],
- // group with display name, search by id
- [
- 'test', false, true, false,
- [['test1', 'Test One']],
- [],
- [],
- [['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test1']]],
- true,
- false,
- ],
- // group with display name, search by display name
- [
- 'one', false, true, false,
- [['test1', 'Test One']],
- [],
- [],
- [['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test1']]],
- true,
- false,
- ],
- // group with display name, search by display name, exact expected
- [
- 'Test One', false, true, false,
- [['test1', 'Test One']],
- [],
- [['label' => 'Test One', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test1']]],
- [],
- true,
- false,
- ],
- [
- 'test', false, false, false,
- [['test1']],
- [],
- [],
- [],
- true,
- false,
- ],
- [
- 'test', false, true, false,
- [
- ['test'],
- ['test1'],
- ],
- [],
- [['label' => 'test', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test']]],
- [['label' => 'test1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test1']]],
- false,
- false,
- ],
- [
- 'test', false, false, false,
- [
- ['test'],
- ['test1'],
- ],
- [],
- [['label' => 'test', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test']]],
- [],
- true,
- false,
- ],
- [
- 'test', false, true, false,
- [
- ['test0'],
- ['test1'],
- ],
- [],
- [],
- [
- ['label' => 'test0', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test0']],
- ['label' => 'test1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test1']],
- ],
- false,
- false,
- ],
- [
- 'test', false, false, false,
- [
- ['test0'],
- ['test1'],
- ],
- [],
- [],
- [],
- true,
- false,
- ],
- [
- 'test', false, true, false,
- [
- ['test0'],
- ['test1'],
- ],
- [],
- [
- ['label' => 'test', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test']],
- ],
- [
- ['label' => 'test0', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test0']],
- ['label' => 'test1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test1']],
- ],
- false,
- ['test'],
- ],
- [
- 'test', false, false, false,
- [
- ['test0'],
- ['test1'],
- ],
- [],
- [
- ['label' => 'test', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test']],
- ],
- [],
- true,
- ['test'],
- ],
- ['test', true, true, false, [], [], [], [], true, false],
- ['test', true, false, false, [], [], [], [], true, false],
- [
- 'test', true, true, false,
- [
- ['test1'],
- ['test2'],
- ],
- [['test1']],
- [],
- [['label' => 'test1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test1']]],
- false,
- false,
- ],
- [
- 'test', true, false, false,
- [
- ['test1'],
- ['test2'],
- ],
- [['test1']],
- [],
- [],
- true,
- false,
- ],
- [
- 'test', true, true, false,
- [
- ['test'],
- ['test1'],
- ],
- [['test']],
- [['label' => 'test', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test']]],
- [],
- false,
- false,
- ],
- [
- 'test', true, false, false,
- [
- ['test'],
- ['test1'],
- ],
- [['test']],
- [['label' => 'test', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test']]],
- [],
- true,
- false,
- ],
- [
- 'test', true, true, false,
- [
- ['test'],
- ['test1'],
- ],
- [['test1']],
- [],
- [['label' => 'test1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test1']]],
- false,
- false,
- ],
- [
- 'test', true, false, false,
- [
- ['test'],
- ['test1'],
- ],
- [['test1']],
- [],
- [],
- true,
- false,
- ],
- [
- 'test', true, true, false,
- [
- ['test'],
- ['test1'],
- ],
- [['test'], ['test0'], ['test1']],
- [['label' => 'test', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test']]],
- [['label' => 'test1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test1']]],
- false,
- false,
- ],
- [
- 'test', true, false, false,
- [
- ['test'],
- ['test1'],
- ],
- [['test'], ['test0'], ['test1']],
- [['label' => 'test', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test']]],
- [],
- true,
- false,
- ],
- [
- 'test', true, true, false,
- [
- ['test0'],
- ['test1'],
- ],
- [['test'], ['test0'], ['test1']],
- [],
- [
- ['label' => 'test0', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test0']],
- ['label' => 'test1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test1']],
- ],
- false,
- false,
- ],
- [
- 'test', true, false, false,
- [
- ['test0'],
- ['test1'],
- ],
- [['test'], ['test0'], ['test1']],
- [],
- [],
- true,
- false,
- ],
- [
- 'test', true, true, false,
- [
- ['test0'],
- ['test1'],
- ],
- [['test'], ['test0'], ['test1']],
- [
- ['label' => 'test', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test']],
- ],
- [
- ['label' => 'test0', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test0']],
- ['label' => 'test1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test1']],
- ],
- false,
- ['test'],
- ],
- [
- 'test', true, false, false,
- [
- ['test0'],
- ['test1'],
- ],
- [['test'], ['test0'], ['test1']],
- [
- ['label' => 'test', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'test']],
- ],
- [],
- true,
- ['test'],
- ],
- [
- 'test', false, false, false,
- [
- ['test', null, true],
- ['test1'],
- ],
- [],
- [],
- [],
- true,
- false,
- ],
- ];
- }
-
- #[DataProvider('dataGetGroups')]
- public function testSearch(
- string $searchTerm,
- bool $shareWithGroupOnly,
- bool $shareeEnumeration,
- bool $groupSharingDisabled,
- array $groupResponse,
- array $userGroupsResponse,
- array $exactExpected,
- array $expected,
- bool $reachedEnd,
- array|false $singleGroup,
- ): void {
- $groupResponse = array_map(
- fn ($args) => $this->getGroupMock(...$args),
- $groupResponse
- );
- if (is_array($singleGroup)) {
- $singleGroup = $this->getGroupMock(...$singleGroup);
- }
- $userGroupsResponse = array_map(
- fn ($args) => $this->getGroupMock(...$args),
- $userGroupsResponse
- );
- $this->config->expects($this->any())
- ->method('getAppValue')
- ->willReturnCallback(
- function ($appName, $key, $default) use ($shareWithGroupOnly, $shareeEnumeration, $groupSharingDisabled) {
- if ($appName !== 'core') {
- return $default;
- }
- switch ($key) {
- case 'shareapi_only_share_with_group_members':
- return $shareWithGroupOnly ? 'yes' : 'no';
- case 'shareapi_allow_share_dialog_user_enumeration':
- return $shareeEnumeration ? 'yes' : 'no';
- case 'shareapi_allow_group_sharing':
- return $groupSharingDisabled ? 'no' : 'yes';
- default:
- return $default;
- }
- }
- );
-
- $this->instantiatePlugin();
-
- if (!$groupSharingDisabled) {
- $this->groupManager->expects($this->once())
- ->method('search')
- ->with($searchTerm, $this->limit, $this->offset)
- ->willReturn($groupResponse);
- }
-
- if ($singleGroup !== false) {
- $this->groupManager->expects($this->once())
- ->method('get')
- ->with($searchTerm)
- ->willReturn($singleGroup);
- }
-
- if ($shareWithGroupOnly) {
- $this->session->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
-
- $numGetUserGroupsCalls = empty($groupResponse) ? 0 : 1;
- $this->groupManager->expects($this->exactly($numGetUserGroupsCalls))
- ->method('getUserGroups')
- ->with($this->user)
- ->willReturn($userGroupsResponse);
- }
-
- $moreResults = $this->plugin->search($searchTerm, $this->limit, $this->offset, $this->searchResult);
- $result = $this->searchResult->asArray();
-
- if (!$groupSharingDisabled) {
- $this->assertEquals($exactExpected, $result['exact']['groups']);
- $this->assertEquals($expected, $result['groups']);
- }
- $this->assertSame($reachedEnd, $moreResults);
- }
-}
--- a/tests/lib/Collaboration/Collaborators/LookupPluginTest.php
+++ /dev/null
@@ -1,539 +0,0 @@
-userSession = $this->createMock(IUserSession::class);
- $this->cloudIdManager = $this->createMock(ICloudIdManager::class);
- $this->config = $this->createMock(IConfig::class);
- $this->globalScaleConfig = $this->createMock(GlobalScaleConfig::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->clientService = $this->createMock(IClientService::class);
- $cloudId = $this->createMock(ICloudId::class);
- $cloudId->expects($this->any())->method('getRemote')->willReturn('myNextcloud.net');
- $user = $this->createMock(IUser::class);
- $user->expects($this->any())->method('getCloudId')->willReturn('user@myNextcloud.net');
- $this->userSession->expects($this->any())->method('getUser')
- ->willReturn($user);
- $this->cloudIdManager->expects($this->any())->method('resolveCloudId')
- ->willReturnCallback(function ($cloudId) {
- if ($cloudId === 'user@myNextcloud.net') {
- return new CloudId('user@myNextcloud.net', 'user', 'myNextcloud.net');
- }
- if (str_contains($cloudId, '@enceladus.moon')) {
- [$user, $remote] = explode('@', $cloudId, 2);
- return new CloudId($cloudId, $user, $remote);
- }
- return new CloudId('user@someNextcloud.net', 'user', 'someNextcloud.net');
- });
-
- $this->plugin = new LookupPlugin(
- $this->config,
- $this->clientService,
- $this->userSession,
- $this->cloudIdManager,
- $this->logger,
- null,
- $this->globalScaleConfig,
- );
- }
-
- public function testSearchNoLookupServerURI(): void {
- $this->config->expects($this->once())
- ->method('getAppValue')
- ->with('files_sharing', 'lookupServerEnabled', 'no')
- ->willReturn('yes');
- $this->config->expects($this->once())
- ->method('getSystemValueBool')
- ->willReturnMap([
- ['has_internet_connection', true, true],
- ]);
-
- $this->globalScaleConfig->expects($this->once())
- ->method('isGlobalScaleEnabled')
- ->willReturn(true);
-
- $this->config->expects($this->once())
- ->method('getSystemValueString')
- ->with('lookup_server', 'https://lookup.nextcloud.com')
- ->willReturn('');
-
- $this->clientService->expects($this->never())
- ->method('newClient');
-
- /** @var ISearchResult|MockObject $searchResult */
- $searchResult = $this->createMock(ISearchResult::class);
-
- $this->plugin->search('foobar', 10, 0, $searchResult);
- }
-
- public function testSearchNoInternet(): void {
- $this->config->expects($this->once())
- ->method('getAppValue')
- ->with('files_sharing', 'lookupServerEnabled', 'no')
- ->willReturn('yes');
- $this->config->expects($this->exactly(1))
- ->method('getSystemValueBool')
- ->willReturnMap([
- ['has_internet_connection', true, false],
- ]);
-
- $this->globalScaleConfig->expects($this->exactly(1))
- ->method('isGlobalScaleEnabled')
- ->willReturn(false);
-
- $this->clientService->expects($this->never())
- ->method('newClient');
-
- /** @var ISearchResult|MockObject $searchResult */
- $searchResult = $this->createMock(ISearchResult::class);
-
- $this->plugin->search('foobar', 10, 0, $searchResult);
- }
-
- /**
- * @param array $searchParams
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('searchDataProvider')]
- public function testSearch(array $searchParams): void {
- $type = new SearchResultType('lookup');
-
- /** @var ISearchResult|MockObject $searchResult */
- $searchResult = $this->createMock(ISearchResult::class);
- $searchResult->expects($this->once())
- ->method('addResultSet')
- ->with($type, $searchParams['expectedResult'], []);
-
- $this->config->expects($this->once())
- ->method('getAppValue')
- ->with('files_sharing', 'lookupServerEnabled', 'no')
- ->willReturn('yes');
- $this->config->expects($this->exactly(2))
- ->method('getSystemValueBool')
- ->willReturnMap([
- ['has_internet_connection', true, true],
- ['gss.selfsigned.allow', false, false],
- ]);
-
- $this->globalScaleConfig->expects($this->once())
- ->method('isGlobalScaleEnabled')
- ->willReturn(true);
-
- $this->config->expects($this->once())
- ->method('getSystemValueString')
- ->with('lookup_server', 'https://lookup.nextcloud.com')
- ->willReturn($searchParams['server']);
-
- $response = $this->createMock(IResponse::class);
- $response->expects($this->once())
- ->method('getBody')
- ->willReturn(json_encode($searchParams['resultBody']));
-
- $client = $this->createMock(IClient::class);
- $client->expects($this->once())
- ->method('get')
- ->willReturnCallback(function ($url) use ($searchParams, $response) {
- $this->assertSame(strpos($url, $searchParams['server'] . '/users?search='), 0);
- $this->assertNotFalse(strpos($url, urlencode($searchParams['search'])));
- return $response;
- });
-
- $this->clientService->expects($this->once())
- ->method('newClient')
- ->willReturn($client);
-
- $moreResults = $this->plugin->search(
- $searchParams['search'],
- $searchParams['limit'],
- $searchParams['offset'],
- $searchResult
- );
-
- $this->assertFalse($moreResults);
- }
-
- /**
- * @param array $searchParams
- * @param bool $GSEnabled
- * @param bool $LookupEnabled
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSearchEnableDisableLookupServer')]
- public function testSearchEnableDisableLookupServer(array $searchParams, $GSEnabled, $LookupEnabled): void {
- $type = new SearchResultType('lookup');
-
- /** @var ISearchResult|MockObject $searchResult */
- $searchResult = $this->createMock(ISearchResult::class);
-
- $this->config->expects($this->once())
- ->method('getAppValue')
- ->with('files_sharing', 'lookupServerEnabled', 'no')
- ->willReturn($LookupEnabled ? 'yes' : 'no');
- if ($GSEnabled) {
- $searchResult->expects($this->once())
- ->method('addResultSet')
- ->with($type, $searchParams['expectedResult'], []);
-
- $this->config->expects($this->exactly(2))
- ->method('getSystemValueBool')
- ->willReturnMap([
- ['has_internet_connection', true, true],
- ['gss.selfsigned.allow', false, false],
- ]);
-
- $this->globalScaleConfig->expects($this->once())
- ->method('isGlobalScaleEnabled')
- ->willReturn($GSEnabled);
- $this->config->expects($this->once())
- ->method('getSystemValueString')
- ->with('lookup_server', 'https://lookup.nextcloud.com')
- ->willReturn($searchParams['server']);
-
- $response = $this->createMock(IResponse::class);
- $response->expects($this->once())
- ->method('getBody')
- ->willReturn(json_encode($searchParams['resultBody']));
-
- $client = $this->createMock(IClient::class);
- $client->expects($this->once())
- ->method('get')
- ->willReturnCallback(function ($url) use ($searchParams, $response) {
- $this->assertSame(strpos($url, $searchParams['server'] . '/users?search='), 0);
- $this->assertNotFalse(strpos($url, urlencode($searchParams['search'])));
- return $response;
- });
-
- $this->clientService->expects($this->once())
- ->method('newClient')
- ->willReturn($client);
- } else {
- $searchResult->expects($this->never())->method('addResultSet');
- $this->config->expects($this->once())
- ->method('getSystemValueBool')
- ->willReturnMap([
- ['has_internet_connection', true, true],
- ]);
-
- $this->globalScaleConfig->expects($this->once())
- ->method('isGlobalScaleEnabled')
- ->willReturn($GSEnabled);
- }
- $moreResults = $this->plugin->search(
- $searchParams['search'],
- $searchParams['limit'],
- $searchParams['offset'],
- $searchResult
- );
-
- $this->assertFalse($moreResults);
- }
-
- public function testSearchGSDisabled(): void {
- $this->config->expects($this->once())
- ->method('getSystemValueBool')
- ->willReturnMap([
- ['has_internet_connection', true, true],
- ]);
-
- $this->globalScaleConfig->expects($this->once())
- ->method('isGlobalScaleEnabled')
- ->willReturn(false);
-
- /** @var ISearchResult|MockObject $searchResult */
- $searchResult = $this->createMock(ISearchResult::class);
- $searchResult->expects($this->never())
- ->method('addResultSet');
- $searchResult->expects($this->never())
- ->method('markExactIdMatch');
-
- $this->assertFalse($this->plugin->search('irr', 10, 0, $searchResult));
- }
-
- public static function dataSearchEnableDisableLookupServer(): array {
- $fedIDs = [
- 'foo@enceladus.moon',
- 'foobar@enceladus.moon',
- 'foongus@enceladus.moon',
- ];
-
- return [
- [[
- 'search' => 'foo',
- 'limit' => 10,
- 'offset' => 0,
- 'server' => 'https://lookup.example.io',
- 'resultBody' => [
- ['federationId' => $fedIDs[0]],
- ['federationId' => $fedIDs[1]],
- ['federationId' => $fedIDs[2]],
- ],
- 'expectedResult' => [
- [
- 'label' => $fedIDs[0],
- 'value' => [
- 'shareType' => IShare::TYPE_REMOTE,
- 'globalScale' => true,
- 'shareWith' => $fedIDs[0],
- 'server' => 'enceladus.moon',
- 'isTrustedServer' => false,
- ],
- 'extra' => ['federationId' => $fedIDs[0]],
- ],
- [
- 'label' => $fedIDs[1],
- 'value' => [
- 'shareType' => IShare::TYPE_REMOTE,
- 'globalScale' => true,
- 'shareWith' => $fedIDs[1],
- 'server' => 'enceladus.moon',
- 'isTrustedServer' => false,
- ],
- 'extra' => ['federationId' => $fedIDs[1]],
- ],
- [
- 'label' => $fedIDs[2],
- 'value' => [
- 'shareType' => IShare::TYPE_REMOTE,
- 'globalScale' => true,
- 'shareWith' => $fedIDs[2],
- 'server' => 'enceladus.moon',
- 'isTrustedServer' => false,
- ],
- 'extra' => ['federationId' => $fedIDs[2]],
- ],
- ]
- ],// GS , Lookup
- true, true
- ],
- [[
- 'search' => 'foo',
- 'limit' => 10,
- 'offset' => 0,
- 'server' => 'https://lookup.example.io',
- 'resultBody' => [
- ['federationId' => $fedIDs[0]],
- ['federationId' => $fedIDs[1]],
- ['federationId' => $fedIDs[2]],
- ],
- 'expectedResult' => [
- [
- 'label' => $fedIDs[0],
- 'value' => [
- 'shareType' => IShare::TYPE_REMOTE,
- 'globalScale' => true,
- 'shareWith' => $fedIDs[0],
- 'server' => 'enceladus.moon',
- 'isTrustedServer' => false,
- ],
- 'extra' => ['federationId' => $fedIDs[0]],
- ],
- [
- 'label' => $fedIDs[1],
- 'value' => [
- 'shareType' => IShare::TYPE_REMOTE,
- 'globalScale' => true,
- 'shareWith' => $fedIDs[1],
- 'server' => 'enceladus.moon',
- 'isTrustedServer' => false,
- ],
- 'extra' => ['federationId' => $fedIDs[1]],
- ],
- [
- 'label' => $fedIDs[2],
- 'value' => [
- 'shareType' => IShare::TYPE_REMOTE,
- 'globalScale' => true,
- 'shareWith' => $fedIDs[2],
- 'server' => 'enceladus.moon',
- 'isTrustedServer' => false,
- ],
- 'extra' => ['federationId' => $fedIDs[2]],
- ],
- ]
- ],// GS , Lookup
- true, false
- ],
- [[
- 'search' => 'foo',
- 'limit' => 10,
- 'offset' => 0,
- 'server' => 'https://lookup.example.io',
- 'resultBody' => [
- ['federationId' => $fedIDs[0]],
- ['federationId' => $fedIDs[1]],
- ['federationId' => $fedIDs[2]],
- ],
- 'expectedResult' => [
- [
- 'label' => $fedIDs[0],
- 'value' => [
- 'shareType' => IShare::TYPE_REMOTE,
- 'shareWith' => $fedIDs[0]
- ],
- 'extra' => ['federationId' => $fedIDs[0]],
- ],
- [
- 'label' => $fedIDs[1],
- 'value' => [
- 'shareType' => IShare::TYPE_REMOTE,
- 'shareWith' => $fedIDs[1]
- ],
- 'extra' => ['federationId' => $fedIDs[1]],
- ],
- [
- 'label' => $fedIDs[2],
- 'value' => [
- 'shareType' => IShare::TYPE_REMOTE,
- 'shareWith' => $fedIDs[2]
- ],
- 'extra' => ['federationId' => $fedIDs[2]],
- ],
- ]
- ],// GS , Lookup
- false, true
- ],
- [[
- 'search' => 'foo',
- 'limit' => 10,
- 'offset' => 0,
- 'server' => 'https://lookup.example.io',
- 'resultBody' => [
- ['federationId' => $fedIDs[0]],
- ['federationId' => $fedIDs[1]],
- ['federationId' => $fedIDs[2]],
- ],
- 'expectedResult' => [
- [
- 'label' => $fedIDs[0],
- 'value' => [
- 'shareType' => IShare::TYPE_REMOTE,
- 'shareWith' => $fedIDs[0]
- ],
- 'extra' => ['federationId' => $fedIDs[0]],
- ],
- [
- 'label' => $fedIDs[1],
- 'value' => [
- 'shareType' => IShare::TYPE_REMOTE,
- 'shareWith' => $fedIDs[1]
- ],
- 'extra' => ['federationId' => $fedIDs[1]],
- ],
- [
- 'label' => $fedIDs[2],
- 'value' => [
- 'shareType' => IShare::TYPE_REMOTE,
- 'shareWith' => $fedIDs[2]
- ],
- 'extra' => ['federationId' => $fedIDs[2]],
- ],
- ]
- ],// GS , Lookup
- false, false
- ],
- ];
- }
-
- public static function searchDataProvider(): array {
- $fedIDs = [
- 'foo@enceladus.moon',
- 'foobar@enceladus.moon',
- 'foongus@enceladus.moon',
- ];
-
- return [
- // #0, standard search with results
- [[
- 'search' => 'foo',
- 'limit' => 10,
- 'offset' => 0,
- 'server' => 'https://lookup.example.io',
- 'resultBody' => [
- ['federationId' => $fedIDs[0]],
- ['federationId' => $fedIDs[1]],
- ['federationId' => $fedIDs[2]],
- ],
- 'expectedResult' => [
- [
- 'label' => $fedIDs[0],
- 'value' => [
- 'shareType' => IShare::TYPE_REMOTE,
- 'globalScale' => true,
- 'shareWith' => $fedIDs[0],
- 'server' => 'enceladus.moon',
- 'isTrustedServer' => false,
- ],
- 'extra' => ['federationId' => $fedIDs[0]],
- ],
- [
- 'label' => $fedIDs[1],
- 'value' => [
- 'shareType' => IShare::TYPE_REMOTE,
- 'globalScale' => true,
- 'shareWith' => $fedIDs[1],
- 'server' => 'enceladus.moon',
- 'isTrustedServer' => false,
- ],
- 'extra' => ['federationId' => $fedIDs[1]],
- ],
- [
- 'label' => $fedIDs[2],
- 'value' => [
- 'shareType' => IShare::TYPE_REMOTE,
- 'globalScale' => true,
- 'shareWith' => $fedIDs[2],
- 'server' => 'enceladus.moon',
- 'isTrustedServer' => false,
- ],
- 'extra' => ['federationId' => $fedIDs[2]],
- ],
- ]
- ]],
- // #1, search without results
- [[
- 'search' => 'foo',
- 'limit' => 10,
- 'offset' => 0,
- 'server' => 'https://lookup.example.io',
- 'resultBody' => [],
- 'expectedResult' => [],
- ]],
- ];
- }
-}
--- a/tests/lib/Collaboration/Collaborators/MailPluginTest.php
+++ /dev/null
@@ -1,1078 +0,0 @@
-config = $this->createMock(IConfig::class);
- $this->contactsManager = $this->createMock(IManager::class);
- $this->groupManager = $this->createMock(IGroupManager::class);
- $this->knownUserService = $this->createMock(KnownUserService::class);
- $this->userSession = $this->createMock(IUserSession::class);
- $this->userManager = $this->createMock(IUserManager::class);
- $this->userManager
- ->method('get')
- ->willReturnCallback(function (string $uid): IUser {
- $user = $this->createMock(IUser::class);
- $user
- ->method('getUID')
- ->willReturn($uid);
-
- return $user;
- });
- $this->cloudIdManager = new CloudIdManager(
- $this->createMock(ICacheFactory::class),
- $this->createMock(IEventDispatcher::class),
- $this->contactsManager,
- $this->createMock(IURLGenerator::class),
- $this->createMock(IUserManager::class),
- );
-
- $this->searchResult = new SearchResult();
- }
-
- public function instantiatePlugin(int $shareType) {
- $this->plugin = new MailPlugin(
- $this->contactsManager,
- $this->cloudIdManager,
- $this->config,
- $this->groupManager,
- $this->knownUserService,
- $this->userSession,
- $this->getEmailValidatorWithStrictEmailCheck(),
- $this->userManager,
- [],
- $shareType,
- );
- }
-
- #[DataProvider('dataSearchEmail')]
- public function testSearchEmail(string $searchTerm, array $contacts, bool $shareeEnumeration, array $expectedResult, bool $expectedExactIdMatch, bool $expectedMoreResults, bool $validEmail): void {
- $this->config->expects($this->any())
- ->method('getAppValue')
- ->willReturnCallback(
- function ($appName, $key, $default) use ($shareeEnumeration) {
- if ($appName === 'core' && $key === 'shareapi_allow_share_dialog_user_enumeration') {
- return $shareeEnumeration ? 'yes' : 'no';
- }
- return $default;
- }
- );
-
- $this->instantiatePlugin(IShare::TYPE_EMAIL);
-
- $currentUser = $this->createMock(IUser::class);
- $currentUser->method('getUID')
- ->willReturn('current');
- $this->userSession->method('getUser')
- ->willReturn($currentUser);
-
- $this->contactsManager->expects($this->any())
- ->method('search')
- ->willReturnCallback(function ($search, $searchAttributes) use ($searchTerm, $contacts) {
- if ($search === $searchTerm) {
- return $contacts;
- }
- return [];
- });
-
- $moreResults = $this->plugin->search($searchTerm, 2, 0, $this->searchResult);
- $result = $this->searchResult->asArray();
-
- $this->assertSame($expectedExactIdMatch, $this->searchResult->hasExactIdMatch(new SearchResultType('emails')));
- $this->assertEquals($expectedResult, $result);
- $this->assertSame($expectedMoreResults, $moreResults);
- }
-
- public static function dataSearchEmail(): array {
- return [
- // data set 0
- ['test', [], true, ['emails' => [], 'exact' => ['emails' => []]], false, false, false],
- // data set 1
- ['test', [], false, ['emails' => [], 'exact' => ['emails' => []]], false, false, false],
- // data set 2
- [
- 'test@remote.com',
- [],
- true,
- ['emails' => [], 'exact' => ['emails' => [['uuid' => 'test@remote.com', 'label' => 'test@remote.com', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'test@remote.com']]]]],
- false,
- false,
- true,
- ],
- // data set 3
- [ // no valid email address
- 'test@remote',
- [],
- true,
- ['emails' => [], 'exact' => ['emails' => []]],
- false,
- false,
- false,
- ],
- // data set 4
- [
- 'test@remote.com',
- [],
- false,
- ['emails' => [], 'exact' => ['emails' => [['uuid' => 'test@remote.com', 'label' => 'test@remote.com', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'test@remote.com']]]]],
- false,
- false,
- true,
- ],
- // data set 5
- [
- 'test',
- [
- [
- 'UID' => 'uid3',
- 'FN' => 'User3 @ Localhost',
- ],
- [
- 'UID' => 'uid2',
- 'FN' => 'User2 @ Localhost',
- 'EMAIL' => [
- ],
- ],
- [
- 'UID' => 'uid1',
- 'FN' => 'User @ Localhost',
- 'EMAIL' => [
- 'username@example.com',
- ],
- ],
- ],
- true,
- ['emails' => [['uuid' => 'uid1', 'name' => 'User @ Localhost', 'type' => '', 'label' => 'User @ Localhost (username@example.com)', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'username@example.com']]], 'exact' => ['emails' => []]],
- false,
- false,
- false,
- ],
- // data set 6
- [
- 'test',
- [
- [
- 'UID' => 'uid3',
- 'FN' => 'User3 @ Localhost',
- ],
- [
- 'UID' => 'uid2',
- 'FN' => 'User2 @ Localhost',
- 'EMAIL' => [
- ],
- ],
- [
- 'isLocalSystemBook' => true,
- 'UID' => 'uid1',
- 'FN' => 'User @ Localhost',
- 'EMAIL' => [
- 'username@localhost',
- ],
- ],
- ],
- false,
- ['emails' => [], 'exact' => ['emails' => []]],
- false,
- false,
- false,
- ],
- // data set 7
- [
- 'test@remote.com',
- [
- [
- 'UID' => 'uid3',
- 'FN' => 'User3 @ example.com',
- ],
- [
- 'UID' => 'uid2',
- 'FN' => 'User2 @ example.com',
- 'EMAIL' => [
- ],
- ],
- [
- 'UID' => 'uid1',
- 'FN' => 'User @ example.com',
- 'EMAIL' => [
- 'username@example.com',
- ],
- ],
- ],
- true,
- ['emails' => [['uuid' => 'uid1', 'name' => 'User @ example.com', 'type' => '', 'label' => 'User @ example.com (username@example.com)', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'username@example.com']]], 'exact' => ['emails' => [['label' => 'test@remote.com', 'uuid' => 'test@remote.com', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'test@remote.com']]]]],
- false,
- false,
- true,
- ],
- // data set 8
- [
- 'test@remote.com',
- [
- [
- 'UID' => 'uid3',
- 'FN' => 'User3 @ Localhost',
- ],
- [
- 'UID' => 'uid2',
- 'FN' => 'User2 @ Localhost',
- 'EMAIL' => [
- ],
- ],
- [
- 'isLocalSystemBook' => true,
- 'UID' => 'uid1',
- 'FN' => 'User @ Localhost',
- 'EMAIL' => [
- 'username@localhost',
- ],
- ],
- ],
- false,
- ['emails' => [], 'exact' => ['emails' => [['label' => 'test@remote.com', 'uuid' => 'test@remote.com', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'test@remote.com']]]]],
- false,
- false,
- true,
- ],
- // data set 9
- [
- 'username@example.com',
- [
- [
- 'UID' => 'uid3',
- 'FN' => 'User3 @ example.com',
- ],
- [
- 'UID' => 'uid2',
- 'FN' => 'User2 @ example.com',
- 'EMAIL' => [
- ],
- ],
- [
- 'UID' => 'uid1',
- 'FN' => 'User @ example.com',
- 'EMAIL' => [
- 'username@example.com',
- ],
- ],
- ],
- true,
- ['emails' => [], 'exact' => ['emails' => [['name' => 'User @ example.com', 'uuid' => 'uid1', 'type' => '', 'label' => 'User @ example.com (username@example.com)', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'username@example.com']]]]],
- true,
- false,
- false,
- ],
- // data set 10
- [
- 'username@example.com',
- [
- [
- 'UID' => 'uid1',
- 'FN' => 'User3 @ example.com',
- ],
- [
- 'UID' => 'uid2',
- 'FN' => 'User2 @ example.com',
- 'EMAIL' => [
- ],
- ],
- [
- 'UID' => 'uid1',
- 'FN' => 'User @ example.com',
- 'EMAIL' => [
- 'username@example.com',
- ],
- ],
- ],
- false,
- ['emails' => [], 'exact' => ['emails' => [['name' => 'User @ example.com', 'uuid' => 'uid1', 'type' => '', 'label' => 'User @ example.com (username@example.com)', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'username@example.com']]]]],
- true,
- false,
- false,
- ],
- // data set 11
- // contact with space
- [
- 'user name@localhost',
- [
- [
- 'UID' => 'uid3',
- 'FN' => 'User3 @ Localhost',
- ],
- [
- 'UID' => 'uid2',
- 'FN' => 'User2 @ Localhost',
- 'EMAIL' => [
- ],
- ],
- [
- 'UID' => 'uid1',
- 'FN' => 'User Name @ Localhost',
- 'EMAIL' => [
- 'user name@localhost',
- ],
- ],
- ],
- false,
- ['emails' => [], 'exact' => ['emails' => []]],
- false,
- false,
- false,
- ],
- // data set 12
- // remote with space, no contact
- [
- 'user space@remote.com',
- [
- [
- 'UID' => 'uid3',
- 'FN' => 'User3 @ Localhost',
- ],
- [
- 'UID' => 'uid2',
- 'FN' => 'User2 @ Localhost',
- 'EMAIL' => [
- ],
- ],
- [
- 'isLocalSystemBook' => true,
- 'UID' => 'uid1',
- 'FN' => 'User @ Localhost',
- 'EMAIL' => [
- 'username@localhost',
- ],
- ],
- ],
- false,
- ['emails' => [], 'exact' => ['emails' => []]],
- false,
- false,
- false,
- ],
- // data set 13
- // Local user found by email => no result
- [
- 'test@example.com',
- [
- [
- 'UID' => 'uid1',
- 'FN' => 'User',
- 'EMAIL' => ['test@example.com'],
- 'CLOUD' => ['test@localhost'],
- 'isLocalSystemBook' => true,
- ]
- ],
- false,
- ['exact' => []],
- false,
- false,
- true,
- ],
- // data set 14
- // Current local user found by email => no result
- [
- 'test@example.com',
- [
- [
- 'UID' => 'uid1',
- 'FN' => 'User',
- 'EMAIL' => ['test@example.com'],
- 'CLOUD' => ['current@localhost'],
- 'isLocalSystemBook' => true,
- ]
- ],
- true,
- ['exact' => []],
- false,
- false,
- true,
- ],
- // data set 15
- // Several local users found by email => no result nor pagination
- [
- 'test@example',
- [
- [
- 'UID' => 'uid1',
- 'FN' => 'User1',
- 'EMAIL' => ['test@example.com'],
- 'CLOUD' => ['test1@localhost'],
- 'isLocalSystemBook' => true,
- ],
- [
- 'UID' => 'uid2',
- 'FN' => 'User2',
- 'EMAIL' => ['test@example.de'],
- 'CLOUD' => ['test2@localhost'],
- 'isLocalSystemBook' => true,
- ],
- [
- 'UID' => 'uid3',
- 'FN' => 'User3',
- 'EMAIL' => ['test@example.org'],
- 'CLOUD' => ['test3@localhost'],
- 'isLocalSystemBook' => true,
- ],
- [
- 'UID' => 'uid4',
- 'FN' => 'User4',
- 'EMAIL' => ['test@example.net'],
- 'CLOUD' => ['test4@localhost'],
- 'isLocalSystemBook' => true,
- ],
- ],
- true,
- ['emails' => [], 'exact' => ['emails' => []]],
- false,
- false,
- false,
- ],
- // data set 16
- // Pagination and "more results" for normal emails
- [
- 'test@example',
- [
- [
- 'UID' => 'uid1',
- 'FN' => 'User1',
- 'EMAIL' => ['test@example.com'],
- 'CLOUD' => ['test1@localhost'],
- ],
- [
- 'UID' => 'uid2',
- 'FN' => 'User2',
- 'EMAIL' => ['test@example.de'],
- 'CLOUD' => ['test2@localhost'],
- ],
- [
- 'UID' => 'uid3',
- 'FN' => 'User3',
- 'EMAIL' => ['test@example.org'],
- 'CLOUD' => ['test3@localhost'],
- ],
- [
- 'UID' => 'uid4',
- 'FN' => 'User4',
- 'EMAIL' => ['test@example.net'],
- 'CLOUD' => ['test4@localhost'],
- ],
- ],
- true,
- ['emails' => [
- ['uuid' => 'uid1', 'name' => 'User1', 'type' => '', 'label' => 'User1 (test@example.com)', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'test@example.com']],
- ['uuid' => 'uid2', 'name' => 'User2', 'type' => '', 'label' => 'User2 (test@example.de)', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'test@example.de']],
- ], 'exact' => ['emails' => []]],
- false,
- true,
- false,
- ],
- // data set 17
- // multiple email addresses with type
- [
- 'User Name',
- [
- [
- 'UID' => 'uid3',
- 'FN' => 'User3',
- ],
- [
- 'UID' => 'uid2',
- 'FN' => 'User2',
- 'EMAIL' => [
- ],
- ],
- [
- 'UID' => 'uid1',
- 'FN' => 'User Name',
- 'EMAIL' => [
- ['type' => 'HOME', 'value' => 'username@example.com'],
- ['type' => 'WORK', 'value' => 'other@example.com'],
- ],
- ],
- ],
- false,
- ['emails' => [
- ], 'exact' => ['emails' => [
- ['name' => 'User Name', 'uuid' => 'uid1', 'type' => 'HOME', 'label' => 'User Name (username@example.com)', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'username@example.com']],
- ['name' => 'User Name', 'uuid' => 'uid1', 'type' => 'WORK', 'label' => 'User Name (other@example.com)', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'other@example.com']]
- ]]],
- false,
- false,
- false,
- ],
- // data set 18
- // idn email
- [
- 'test@lölölölölölölöl.com',
- [],
- true,
- ['emails' => [], 'exact' => ['emails' => [['uuid' => 'test@lölölölölölölöl.com', 'label' => 'test@lölölölölölölöl.com', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'test@lölölölölölölöl.com']]]]],
- false,
- false,
- true,
- ],
- ];
- }
-
- #[DataProvider('dataSearchUser')]
- public function testSearchUser(string $searchTerm, array $contacts, bool $shareeEnumeration, array $expectedResult, bool $expectedExactIdMatch, bool $expectedMoreResults): void {
- $this->config->expects($this->any())
- ->method('getAppValue')
- ->willReturnCallback(
- function ($appName, $key, $default) use ($shareeEnumeration) {
- if ($appName === 'core' && $key === 'shareapi_allow_share_dialog_user_enumeration') {
- return $shareeEnumeration ? 'yes' : 'no';
- }
- return $default;
- }
- );
-
- $this->instantiatePlugin(IShare::TYPE_USER);
-
- $currentUser = $this->createMock(IUser::class);
- $currentUser->method('getUID')
- ->willReturn('current');
- $this->userSession->method('getUser')
- ->willReturn($currentUser);
-
- $this->contactsManager->expects($this->any())
- ->method('search')
- ->willReturnCallback(function ($search, $searchAttributes) use ($searchTerm, $contacts) {
- if ($search === $searchTerm) {
- return $contacts;
- }
- return [];
- });
-
- $moreResults = $this->plugin->search($searchTerm, 2, 0, $this->searchResult);
- $result = $this->searchResult->asArray();
-
- $this->assertSame($expectedExactIdMatch, $this->searchResult->hasExactIdMatch(new SearchResultType('users')));
- $this->assertEquals($expectedResult, $result);
- $this->assertSame($expectedMoreResults, $moreResults);
- }
-
- public static function dataSearchUser(): array {
- return [
- // data set 0
- ['test', [], true, ['users' => [], 'exact' => ['users' => [],]], false, false],
- // data set 1
- ['test', [], false, ['users' => [], 'exact' => ['users' => [],]], false, false],
- // data set 2
- [
- 'test@remote.com',
- [],
- true,
- ['users' => [], 'exact' => ['users' => [],]],
- false,
- false,
- ],
- // data set 3
- [
- 'test@remote.com',
- [],
- false,
- ['users' => [], 'exact' => ['users' => [],]],
- false,
- false,
- ],
- // data set 4
- [
- 'test',
- [
- [
- 'UID' => 'uid3',
- 'FN' => 'User3 @ Localhost',
- ],
- [
- 'UID' => 'uid2',
- 'FN' => 'User2 @ Localhost',
- 'EMAIL' => [
- ],
- ],
- [
- 'UID' => 'uid1',
- 'FN' => 'User @ Localhost',
- 'EMAIL' => [
- 'username@localhost',
- ],
- ],
- ],
- true,
- ['users' => [], 'exact' => ['users' => [],]],
- false,
- false,
- ],
- // data set 5
- [
- 'test',
- [
- [
- 'UID' => 'uid3',
- 'FN' => 'User3 @ Localhost',
- ],
- [
- 'UID' => 'uid2',
- 'FN' => 'User2 @ Localhost',
- 'EMAIL' => [
- ],
- ],
- [
- 'isLocalSystemBook' => true,
- 'UID' => 'uid1',
- 'FN' => 'User @ Localhost',
- 'EMAIL' => [
- 'username@localhost',
- ],
- ],
- ],
- false,
- ['users' => [], 'exact' => ['users' => [],]],
- false,
- false,
- ],
- // data set 6
- [
- 'test@remote.com',
- [
- [
- 'UID' => 'uid3',
- 'FN' => 'User3 @ Localhost',
- ],
- [
- 'UID' => 'uid2',
- 'FN' => 'User2 @ Localhost',
- 'EMAIL' => [
- ],
- ],
- [
- 'UID' => 'uid1',
- 'FN' => 'User @ Localhost',
- 'EMAIL' => [
- 'username@localhost',
- ],
- ],
- ],
- true,
- ['users' => [], 'exact' => ['users' => [],]],
- false,
- false,
- ],
- // data set 7
- [
- 'username@localhost',
- [
- [
- 'UID' => 'uid3',
- 'FN' => 'User3 @ Localhost',
- ],
- [
- 'UID' => 'uid2',
- 'FN' => 'User2 @ Localhost',
- 'EMAIL' => [
- ],
- ],
- [
- 'UID' => 'uid1',
- 'FN' => 'User @ Localhost',
- 'EMAIL' => [
- 'username@localhost',
- ],
- ],
- ],
- true,
- ['users' => [], 'exact' => ['users' => [],]],
- false,
- false,
- ],
- // data set 8
- // Local user found by email
- [
- 'test@example.com',
- [
- [
- 'UID' => 'uid1',
- 'FN' => 'User',
- 'EMAIL' => ['test@example.com'],
- 'CLOUD' => ['test@localhost'],
- 'isLocalSystemBook' => true,
- ]
- ],
- false,
- ['users' => [], 'exact' => ['users' => [['uuid' => 'uid1', 'name' => 'User', 'label' => 'User (test@example.com)', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test'], 'shareWithDisplayNameUnique' => 'test@example.com']]]],
- true,
- false,
- ],
- // data set 9
- // Current local user found by email => no result
- [
- 'test@example.com',
- [
- [
- 'UID' => 'uid1',
- 'FN' => 'User',
- 'EMAIL' => ['test@example.com'],
- 'CLOUD' => ['current@localhost'],
- 'isLocalSystemBook' => true,
- ]
- ],
- true,
- ['exact' => []],
- false,
- false,
- ],
- // data set 10
- // Pagination and "more results" for user matches by emails
- [
- 'test@example',
- [
- [
- 'UID' => 'uid1',
- 'FN' => 'User1',
- 'EMAIL' => ['test@example.com'],
- 'CLOUD' => ['test1@localhost'],
- 'isLocalSystemBook' => true,
- ],
- [
- 'UID' => 'uid2',
- 'FN' => 'User2',
- 'EMAIL' => ['test@example.de'],
- 'CLOUD' => ['test2@localhost'],
- 'isLocalSystemBook' => true,
- ],
- [
- 'UID' => 'uid3',
- 'FN' => 'User3',
- 'EMAIL' => ['test@example.org'],
- 'CLOUD' => ['test3@localhost'],
- 'isLocalSystemBook' => true,
- ],
- [
- 'UID' => 'uid4',
- 'FN' => 'User4',
- 'EMAIL' => ['test@example.net'],
- 'CLOUD' => ['test4@localhost'],
- 'isLocalSystemBook' => true,
- ],
- ],
- true,
- ['users' => [
- ['uuid' => 'uid1', 'name' => 'User1', 'label' => 'User1 (test@example.com)', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1'], 'shareWithDisplayNameUnique' => 'test@example.com'],
- ['uuid' => 'uid2', 'name' => 'User2', 'label' => 'User2 (test@example.de)', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test2'], 'shareWithDisplayNameUnique' => 'test@example.de'],
- ], 'exact' => ['users' => []]],
- false,
- true,
- ],
- // data set 11
- // Pagination and "more results" for normal emails
- [
- 'test@example',
- [
- [
- 'UID' => 'uid1',
- 'FN' => 'User1',
- 'EMAIL' => ['test@example.com'],
- 'CLOUD' => ['test1@localhost'],
- ],
- [
- 'UID' => 'uid2',
- 'FN' => 'User2',
- 'EMAIL' => ['test@example.de'],
- 'CLOUD' => ['test2@localhost'],
- ],
- [
- 'UID' => 'uid3',
- 'FN' => 'User3',
- 'EMAIL' => ['test@example.org'],
- 'CLOUD' => ['test3@localhost'],
- ],
- [
- 'UID' => 'uid4',
- 'FN' => 'User4',
- 'EMAIL' => ['test@example.net'],
- 'CLOUD' => ['test4@localhost'],
- ],
- ],
- true,
- ['users' => [], 'exact' => ['users' => [],]],
- false,
- false,
- ],
- ];
- }
-
- #[DataProvider('dataSearchEmailGroupsOnly')]
- public function testSearchEmailGroupsOnly(string $searchTerm, array $contacts, array $expectedResult, bool $expectedExactIdMatch, bool $expectedMoreResults, array $userToGroupMapping, bool $validEmail): void {
- $this->config->expects($this->any())
- ->method('getAppValue')
- ->willReturnCallback(
- function ($appName, $key, $default) {
- if ($appName === 'core' && $key === 'shareapi_allow_share_dialog_user_enumeration') {
- return 'yes';
- } elseif ($appName === 'core' && $key === 'shareapi_only_share_with_group_members') {
- return 'yes';
- }
- return $default;
- }
- );
-
- $this->instantiatePlugin(IShare::TYPE_EMAIL);
-
- /** @var IUser|\PHPUnit\Framework\MockObject\MockObject */
- $currentUser = $this->createMock('\OCP\IUser');
-
- $currentUser->expects($this->any())
- ->method('getUID')
- ->willReturn('currentUser');
-
- $this->contactsManager->expects($this->any())
- ->method('search')
- ->willReturnCallback(function ($search, $searchAttributes) use ($searchTerm, $contacts) {
- if ($search === $searchTerm) {
- return $contacts;
- }
- return [];
- });
-
- $this->userSession->expects($this->any())
- ->method('getUser')
- ->willReturn($currentUser);
-
- $this->groupManager->expects($this->any())
- ->method('getUserGroupIds')
- ->willReturnCallback(function (IUser $user) use ($userToGroupMapping) {
- return $userToGroupMapping[$user->getUID()];
- });
-
- $moreResults = $this->plugin->search($searchTerm, 2, 0, $this->searchResult);
- $result = $this->searchResult->asArray();
-
- $this->assertSame($expectedExactIdMatch, $this->searchResult->hasExactIdMatch(new SearchResultType('emails')));
- $this->assertEquals($expectedResult, $result);
- $this->assertSame($expectedMoreResults, $moreResults);
- }
-
- public static function dataSearchEmailGroupsOnly(): array {
- return [
- // The user `User` can share with the current user
- [
- 'test',
- [
- [
- 'FN' => 'User',
- 'EMAIL' => ['test@example.com'],
- 'CLOUD' => ['test@localhost'],
- 'isLocalSystemBook' => true,
- 'UID' => 'User',
- ]
- ],
- ['emails' => [], 'exact' => ['emails' => []]],
- false,
- false,
- [
- 'currentUser' => ['group1'],
- 'User' => ['group1'],
- ],
- false,
- ],
- // The user `User` cannot share with the current user
- [
- 'test',
- [
- [
- 'FN' => 'User',
- 'EMAIL' => ['test@example.com'],
- 'CLOUD' => ['test@localhost'],
- 'isLocalSystemBook' => true,
- 'UID' => 'User',
- ]
- ],
- ['emails' => [], 'exact' => ['emails' => []]],
- false,
- false,
- [
- 'currentUser' => ['group1'],
- 'User' => ['group2'],
- ],
- false,
- ],
- // The user `User` cannot share with the current user, but there is an exact match on the e-mail address -> share by e-mail
- [
- 'test@example.com',
- [
- [
- 'FN' => 'User',
- 'EMAIL' => ['test@example.com'],
- 'CLOUD' => ['test@localhost'],
- 'isLocalSystemBook' => true,
- 'UID' => 'User',
- ]
- ],
- ['emails' => [], 'exact' => ['emails' => [['label' => 'test@example.com', 'uuid' => 'test@example.com', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'test@example.com']]]]],
- false,
- false,
- [
- 'currentUser' => ['group1'],
- 'User' => ['group2'],
- ],
- true,
- ]
- ];
- }
-
- #[DataProvider('dataSearchUserGroupsOnly')]
- public function testSearchUserGroupsOnly(string $searchTerm, array $contacts, array $expectedResult, bool $expectedExactIdMatch, bool $expectedMoreResults, array $userToGroupMapping): void {
- $this->config->expects($this->any())
- ->method('getAppValue')
- ->willReturnCallback(
- function ($appName, $key, $default) {
- if ($appName === 'core' && $key === 'shareapi_allow_share_dialog_user_enumeration') {
- return 'yes';
- } elseif ($appName === 'core' && $key === 'shareapi_only_share_with_group_members') {
- return 'yes';
- }
- return $default;
- }
- );
-
- $this->instantiatePlugin(IShare::TYPE_USER);
-
- $currentUser = $this->createMock(IUser::class);
-
- $currentUser->expects($this->any())
- ->method('getUID')
- ->willReturn('currentUser');
-
- $this->contactsManager->expects($this->any())
- ->method('search')
- ->willReturnCallback(function ($search, $searchAttributes) use ($searchTerm, $contacts) {
- if ($search === $searchTerm) {
- return $contacts;
- }
- return [];
- });
-
- $this->userSession->expects($this->any())
- ->method('getUser')
- ->willReturn($currentUser);
-
- $this->groupManager->expects($this->any())
- ->method('getUserGroupIds')
- ->willReturnCallback(function (IUser $user) use ($userToGroupMapping) {
- return $userToGroupMapping[$user->getUID()];
- });
-
- $moreResults = $this->plugin->search($searchTerm, 2, 0, $this->searchResult);
- $result = $this->searchResult->asArray();
-
- $this->assertSame($expectedExactIdMatch, $this->searchResult->hasExactIdMatch(new SearchResultType('emails')));
- $this->assertEquals($expectedResult, $result);
- $this->assertSame($expectedMoreResults, $moreResults);
- }
-
- public static function dataSearchUserGroupsOnly(): array {
- return [
- // The user `User` can share with the current user
- [
- 'test',
- [
- [
- 'FN' => 'User',
- 'EMAIL' => ['test@example.com'],
- 'CLOUD' => ['test@localhost'],
- 'isLocalSystemBook' => true,
- 'UID' => 'User',
- ]
- ],
- ['users' => [['label' => 'User (test@example.com)', 'uuid' => 'User', 'name' => 'User', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test'], 'shareWithDisplayNameUnique' => 'test@example.com',]], 'exact' => ['users' => []]],
- false,
- false,
- [
- 'currentUser' => ['group1'],
- 'User' => ['group1'],
- ],
- ],
- // The user `User` cannot share with the current user
- [
- 'test',
- [
- [
- 'FN' => 'User',
- 'EMAIL' => ['test@example.com'],
- 'CLOUD' => ['test@localhost'],
- 'isLocalSystemBook' => true,
- 'UID' => 'User',
- ]
- ],
- ['users' => [], 'exact' => ['users' => [],]],
- false,
- false,
- [
- 'currentUser' => ['group1'],
- 'User' => ['group2'],
- ],
- ],
- // The user `User` cannot share with the current user, but there is an exact match on the e-mail address -> share by e-mail
- [
- 'test@example.com',
- [
- [
- 'FN' => 'User',
- 'EMAIL' => ['test@example.com'],
- 'CLOUD' => ['test@localhost'],
- 'isLocalSystemBook' => true,
- 'UID' => 'User',
- ]
- ],
- ['users' => [], 'exact' => ['users' => [],]],
- false,
- false,
- [
- 'currentUser' => ['group1'],
- 'User' => ['group2'],
- ],
- ]
- ];
- }
-}
--- a/tests/lib/Collaboration/Collaborators/RemotePluginTest.php
+++ /dev/null
@@ -1,510 +0,0 @@
-userManager = $this->createMock(IUserManager::class);
- $this->config = $this->createMock(IConfig::class);
- $this->contactsManager = $this->createMock(IManager::class);
- $this->cloudIdManager = new CloudIdManager(
- $this->createMock(ICacheFactory::class),
- $this->createMock(IEventDispatcher::class),
- $this->contactsManager,
- $this->createMock(IURLGenerator::class),
- $this->createMock(IUserManager::class),
- );
- $this->appConfig = $this->createMock(IAppConfig::class);
- $this->trustedServers = $this->createMock(TrustedServers::class);
- $this->searchResult = new SearchResult();
- }
-
- public function instantiatePlugin() {
- $user = $this->createMock(IUser::class);
- $user->expects($this->any())
- ->method('getUID')
- ->willReturn('admin');
- $userSession = $this->createMock(IUserSession::class);
- $userSession->expects($this->any())
- ->method('getUser')
- ->willReturn($user);
- $this->plugin = new RemotePlugin($this->contactsManager, $this->cloudIdManager, $this->config, $this->userManager, $userSession, $this->appConfig, $this->trustedServers);
- }
-
- /**
- *
- * @param string $searchTerm
- * @param array $contacts
- * @param bool $shareeEnumeration
- * @param array $expected
- * @param bool $exactIdMatch
- * @param bool $reachedEnd
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetRemote')]
- public function testSearch($searchTerm, array $contacts, $shareeEnumeration, array $expected, $exactIdMatch, $reachedEnd): void {
- $this->config->expects($this->any())
- ->method('getAppValue')
- ->willReturnCallback(
- function ($appName, $key, $default) use ($shareeEnumeration) {
- if ($appName === 'core' && $key === 'shareapi_allow_share_dialog_user_enumeration') {
- return $shareeEnumeration ? 'yes' : 'no';
- }
- return $default;
- }
- );
-
- $this->instantiatePlugin();
-
- $this->contactsManager->expects($this->any())
- ->method('search')
- ->willReturnCallback(function ($search, $searchAttributes) use ($searchTerm, $contacts) {
- if ($search === $searchTerm) {
- return $contacts;
- }
- return [];
- });
-
- $moreResults = $this->plugin->search($searchTerm, 2, 0, $this->searchResult);
- $result = $this->searchResult->asArray();
-
- $this->assertSame($exactIdMatch, $this->searchResult->hasExactIdMatch(new SearchResultType('remotes')));
- $this->assertEquals($expected, $result);
- $this->assertSame($reachedEnd, $moreResults);
- }
-
- /**
- *
- * @param string $remote
- * @param string $expectedUser
- * @param string $expectedUrl
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestSplitUserRemote')]
- public function testSplitUserRemote($remote, $expectedUser, $expectedUrl): void {
- $this->instantiatePlugin();
-
- $this->contactsManager->expects($this->any())
- ->method('search')
- ->willReturn([]);
-
- [$remoteUser, $remoteUrl] = $this->plugin->splitUserRemote($remote);
- $this->assertSame($expectedUser, $remoteUser);
- $this->assertSame($expectedUrl, $remoteUrl);
- }
-
- /**
- * @param string $id
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestSplitUserRemoteError')]
- public function testSplitUserRemoteError($id): void {
- $this->expectException(\Exception::class);
-
- $this->instantiatePlugin();
- $this->plugin->splitUserRemote($id);
- }
-
- public function testTrustedServerMetadata(): void {
- $this->config->expects($this->any())
- ->method('getAppValue')
- ->willReturnCallback(
- function ($appName, $key, $default) {
- if ($appName === 'core' && $key === 'shareapi_allow_share_dialog_user_enumeration') {
- return 'yes';
- }
- return $default;
- }
- );
-
- $this->trustedServers->expects($this->any())
- ->method('isTrustedServer')
- ->willReturnCallback(function ($serverUrl) {
- return $serverUrl === 'trustedserver.com';
- });
-
- $this->instantiatePlugin();
-
- $this->contactsManager->expects($this->any())
- ->method('search')
- ->willReturn([]);
-
- $this->plugin->search('test@trustedserver.com', 2, 0, $this->searchResult);
- $result = $this->searchResult->asArray();
-
- $this->assertNotEmpty($result['exact']['remotes']);
- $this->assertTrue($result['exact']['remotes'][0]['value']['isTrustedServer']);
- }
-
- public function testEmailSearchInContacts(): void {
- $this->config->expects($this->any())
- ->method('getAppValue')
- ->willReturnCallback(
- function ($appName, $key, $default) {
- if ($appName === 'core' && $key === 'shareapi_allow_share_dialog_user_enumeration') {
- return 'yes';
- }
- return $default;
- }
- );
-
- $this->trustedServers->expects($this->any())
- ->method('isTrustedServer')
- ->willReturnCallback(function ($serverUrl) {
- return $serverUrl === 'trustedserver.com';
- });
-
- $this->instantiatePlugin();
-
- $this->contactsManager->expects($this->once())
- ->method('search')
- ->with('john@gmail.com', ['CLOUD', 'FN', 'EMAIL'])
- ->willReturn([
- [
- 'FN' => 'John Doe',
- 'EMAIL' => 'john@gmail.com',
- 'CLOUD' => 'john@trustedserver.com',
- 'UID' => 'john-contact-id'
- ]
- ]);
-
- $this->plugin->search('john@gmail.com', 2, 0, $this->searchResult);
- $result = $this->searchResult->asArray();
-
- $this->assertNotEmpty($result['exact']['remotes']);
- $this->assertEquals('john@trustedserver.com', $result['exact']['remotes'][0]['value']['shareWith']);
- $this->assertTrue($result['exact']['remotes'][0]['value']['isTrustedServer']);
- }
-
- public static function dataGetRemote() {
- return [
- ['test', [], true, ['remotes' => [], 'exact' => ['remotes' => []]], false, true],
- ['test', [], false, ['remotes' => [], 'exact' => ['remotes' => []]], false, true],
- [
- 'test@remote',
- [],
- true,
- ['remotes' => [], 'exact' => ['remotes' => [['label' => 'test (remote)', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'test@remote', 'server' => 'remote', 'isTrustedServer' => false], 'uuid' => 'test', 'name' => 'test']]]],
- false,
- true,
- ],
- [
- 'test@remote',
- [],
- false,
- ['remotes' => [], 'exact' => ['remotes' => [['label' => 'test (remote)', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'test@remote', 'server' => 'remote', 'isTrustedServer' => false], 'uuid' => 'test', 'name' => 'test']]]],
- false,
- true,
- ],
- [
- 'test',
- [
- [
- 'UID' => 'uid',
- 'FN' => 'User3 @ Localhost',
- ],
- [
- 'UID' => 'uid',
- 'FN' => 'User2 @ Localhost',
- 'CLOUD' => [
- ],
- ],
- [
- 'UID' => 'uid1',
- 'FN' => 'User @ Localhost',
- 'CLOUD' => [
- 'username@localhost',
- ],
- ],
- ],
- true,
- ['remotes' => [['name' => 'User @ Localhost', 'label' => 'User @ Localhost (username@localhost)', 'uuid' => 'uid1', 'type' => '', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost', 'isTrustedServer' => false]]], 'exact' => ['remotes' => []]],
- false,
- true,
- ],
- [
- 'test',
- [
- [
- 'UID' => 'uid',
- 'FN' => 'User3 @ Localhost',
- ],
- [
- 'UID' => 'uid',
- 'FN' => 'User2 @ Localhost',
- 'CLOUD' => [
- ],
- ],
- [
- 'UID' => 'uid',
- 'FN' => 'User @ Localhost',
- 'CLOUD' => [
- 'username@localhost',
- ],
- ],
- ],
- false,
- ['remotes' => [], 'exact' => ['remotes' => []]],
- false,
- true,
- ],
- [
- 'test@remote',
- [
- [
- 'UID' => 'uid',
- 'FN' => 'User3 @ Localhost',
- ],
- [
- 'UID' => 'uid',
- 'FN' => 'User2 @ Localhost',
- 'CLOUD' => [
- ],
- ],
- [
- 'UID' => 'uid',
- 'FN' => 'User @ Localhost',
- 'CLOUD' => [
- 'username@localhost',
- ],
- ],
- ],
- true,
- ['remotes' => [['name' => 'User @ Localhost', 'label' => 'User @ Localhost (username@localhost)', 'uuid' => 'uid', 'type' => '', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost', 'isTrustedServer' => false]]], 'exact' => ['remotes' => [['label' => 'test (remote)', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'test@remote', 'server' => 'remote', 'isTrustedServer' => false], 'uuid' => 'test', 'name' => 'test']]]],
- false,
- true,
- ],
- [
- 'test@remote',
- [
- [
- 'UID' => 'uid',
- 'FN' => 'User3 @ Localhost',
- ],
- [
- 'UID' => 'uid',
- 'FN' => 'User2 @ Localhost',
- 'CLOUD' => [
- ],
- ],
- [
- 'UID' => 'uid',
- 'FN' => 'User @ Localhost',
- 'CLOUD' => [
- 'username@localhost',
- ],
- ],
- ],
- false,
- ['remotes' => [], 'exact' => ['remotes' => [['label' => 'test (remote)', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'test@remote', 'server' => 'remote', 'isTrustedServer' => false], 'uuid' => 'test', 'name' => 'test']]]],
- false,
- true,
- ],
- [
- 'username@localhost',
- [
- [
- 'UID' => 'uid3',
- 'FN' => 'User3 @ Localhost',
- ],
- [
- 'UID' => '2',
- 'FN' => 'User2 @ Localhost',
- 'CLOUD' => [
- ],
- ],
- [
- 'UID' => 'uid1',
- 'FN' => 'User @ Localhost',
- 'CLOUD' => [
- 'username@localhost',
- ],
- ],
- ],
- true,
- ['remotes' => [], 'exact' => ['remotes' => [['name' => 'User @ Localhost', 'label' => 'User @ Localhost (username@localhost)', 'uuid' => 'uid1', 'type' => '', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost', 'isTrustedServer' => false]]]]],
- true,
- true,
- ],
- [
- 'username@localhost',
- [
- [
- 'UID' => 'uid3',
- 'FN' => 'User3 @ Localhost',
- ],
- [
- 'UID' => 'uid2',
- 'FN' => 'User2 @ Localhost',
- 'CLOUD' => [
- ],
- ],
- [
- 'UID' => 'uid1',
- 'FN' => 'User @ Localhost',
- 'CLOUD' => [
- 'username@localhost',
- ],
- ],
- ],
- false,
- ['remotes' => [], 'exact' => ['remotes' => [['name' => 'User @ Localhost', 'label' => 'User @ Localhost (username@localhost)', 'uuid' => 'uid1', 'type' => '', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost', 'isTrustedServer' => false]]]]],
- true,
- true,
- ],
- // contact with space
- [
- 'user name@localhost',
- [
- [
- 'UID' => 'uid1',
- 'FN' => 'User3 @ Localhost',
- ],
- [
- 'UID' => 'uid2',
- 'FN' => 'User2 @ Localhost',
- 'CLOUD' => [
- ],
- ],
- [
- 'UID' => 'uid3',
- 'FN' => 'User Name @ Localhost',
- 'CLOUD' => [
- 'user name@localhost',
- ],
- ],
- ],
- false,
- ['remotes' => [], 'exact' => ['remotes' => [['name' => 'User Name @ Localhost', 'label' => 'User Name @ Localhost (user name@localhost)', 'uuid' => 'uid3', 'type' => '', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'user name@localhost', 'server' => 'localhost', 'isTrustedServer' => false]]]]],
- true,
- true,
- ],
- // remote with space, no contact
- [
- 'user space@remote',
- [
- [
- 'UID' => 'uid3',
- 'FN' => 'User3 @ Localhost',
- ],
- [
- 'UID' => 'uid2',
- 'FN' => 'User2 @ Localhost',
- 'CLOUD' => [
- ],
- ],
- [
- 'UID' => 'uid1',
- 'FN' => 'User @ Localhost',
- 'CLOUD' => [
- 'username@localhost',
- ],
- ],
- ],
- false,
- ['remotes' => [], 'exact' => ['remotes' => [['label' => 'user space (remote)', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'user space@remote', 'server' => 'remote', 'isTrustedServer' => false], 'uuid' => 'user space', 'name' => 'user space']]]],
- false,
- true,
- ],
- ];
- }
-
- public static function dataTestSplitUserRemote(): array {
- $userPrefix = ['user@name', 'username'];
- $protocols = ['', 'http://', 'https://'];
- $remotes = [
- 'localhost',
- 'local.host',
- 'dev.local.host',
- 'dev.local.host/path',
- 'dev.local.host/at@inpath',
- '127.0.0.1',
- '::1',
- '::192.0.2.128',
- '::192.0.2.128/at@inpath',
- ];
-
- $testCases = [];
- foreach ($userPrefix as $user) {
- foreach ($remotes as $remote) {
- foreach ($protocols as $protocol) {
- $baseUrl = $user . '@' . $protocol . $remote;
-
- if ($protocol === 'https://') {
- // https:// protocol is not expected in the final result
- $protocol = '';
- }
-
- $testCases[] = [$baseUrl, $user, $protocol . $remote];
- $testCases[] = [$baseUrl . '/', $user, $protocol . $remote];
- $testCases[] = [$baseUrl . '/index.php', $user, $protocol . $remote];
- $testCases[] = [$baseUrl . '/index.php/s/token', $user, $protocol . $remote];
- }
- }
- }
- return $testCases;
- }
-
- public static function dataTestSplitUserRemoteError(): array {
- return [
- // Invalid path
- ['user@'],
-
- // Invalid user
- ['@server'],
- ['us/er@server'],
- ['us:er@server'],
-
- // Invalid splitting
- ['user'],
- [''],
- ['us/erserver'],
- ['us:erserver'],
- ];
- }
-}
--- a/tests/lib/Collaboration/Collaborators/SearchResultTest.php
+++ /dev/null
@@ -1,86 +0,0 @@
-container = $this->createMock(IContainer::class);
-
- $this->search = new Search($this->container, $this->createMock(IEventDispatcher::class));
- }
-
- public static function dataAddResultSet(): array {
- return [
- [[], ['exact' => []]],
- [['users' => ['exact' => null, 'loose' => []]], ['exact' => ['users' => []], 'users' => []]],
- [['groups' => ['exact' => null, 'loose' => ['l1']]], ['exact' => ['groups' => []], 'groups' => ['l1']]],
- [['users' => ['exact' => ['e1'], 'loose' => []]], ['exact' => ['users' => ['e1']], 'users' => []]],
- ];
- }
-
- /**
- * @param array $toAdd
- * @param array $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataAddResultSet')]
- public function testAddResultSet(array $toAdd, array $expected): void {
- $result = new SearchResult();
-
- foreach ($toAdd as $type => $results) {
- $result->addResultSet(new SearchResultType($type), $results['loose'], $results['exact']);
- }
-
- $this->assertEquals($expected, $result->asArray());
- }
-
- public static function dataHasResult(): array {
- $result = ['value' => ['shareWith' => 'l1']];
- return [
- [[],'users', 'n1', false],
- [['users' => ['exact' => null, 'loose' => [$result]]], 'users', 'l1', true],
- [['users' => ['exact' => null, 'loose' => [$result]]], 'users', 'l2', false],
- [['users' => ['exact' => null, 'loose' => [$result]]], 'groups', 'l1', false],
- [['users' => ['exact' => [$result], 'loose' => []]], 'users', 'l1', true],
- [['users' => ['exact' => [$result], 'loose' => []]], 'users', 'l2', false],
- [['users' => ['exact' => [$result], 'loose' => []]], 'groups', 'l1', false],
- ];
- }
-
- /**
- * @param array $toAdd
- * @param string $type
- * @param string $id
- * @param bool $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataHasResult')]
- public function testHasResult(array $toAdd, $type, $id, $expected): void {
- $result = new SearchResult();
-
- foreach ($toAdd as $addType => $results) {
- $result->addResultSet(new SearchResultType($addType), $results['loose'], $results['exact']);
- }
-
- $this->assertSame($expected, $result->hasResult(new SearchResultType($type), $id));
- }
-}
--- a/tests/lib/Collaboration/Collaborators/SearchTest.php
+++ /dev/null
@@ -1,265 +0,0 @@
-container = $this->createMock(IContainer::class);
- $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
-
- $this->search = new Search($this->container, $this->eventDispatcher);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSearchSharees')]
- public function testSearch(
- string $searchTerm,
- array $shareTypes,
- int $page,
- int $perPage,
- array $mockedUserResult,
- array $mockedGroupsResult,
- array $mockedRemotesResult,
- array $mockedMailResult,
- array $expected,
- bool $expectedMoreResults,
- ): void {
- $searchResult = new SearchResult();
-
- $userPlugin = $this->createMock(ISearchPlugin::class);
- $userPlugin->expects($this->any())
- ->method('search')
- ->willReturnCallback(function () use ($searchResult, $mockedUserResult, $expectedMoreResults) {
- $type = new SearchResultType('users');
- $searchResult->addResultSet($type, $mockedUserResult);
- return $expectedMoreResults;
- });
-
- $groupPlugin = $this->createMock(ISearchPlugin::class);
- $groupPlugin->expects($this->any())
- ->method('search')
- ->willReturnCallback(function () use ($searchResult, $mockedGroupsResult, $expectedMoreResults) {
- $type = new SearchResultType('groups');
- $searchResult->addResultSet($type, $mockedGroupsResult);
- return $expectedMoreResults;
- });
-
- $remotePlugin = $this->createMock(ISearchPlugin::class);
- $remotePlugin->expects($this->any())
- ->method('search')
- ->willReturnCallback(function () use ($searchResult, $mockedRemotesResult, $expectedMoreResults) {
- if ($mockedRemotesResult !== null) {
- $type = new SearchResultType('remotes');
- $searchResult->addResultSet($type, $mockedRemotesResult['results'], $mockedRemotesResult['exact']);
- if ($mockedRemotesResult['exactIdMatch'] === true) {
- $searchResult->markExactIdMatch($type);
- }
- }
- return $expectedMoreResults;
- });
-
- $mailPlugin = $this->createMock(ISearchPlugin::class);
- $mailPlugin->expects($this->any())
- ->method('search')
- ->willReturnCallback(function () use ($searchResult, $mockedMailResult, $expectedMoreResults) {
- $type = new SearchResultType('emails');
- $searchResult->addResultSet($type, $mockedMailResult);
- return $expectedMoreResults;
- });
-
- $this->container->expects($this->any())
- ->method('resolve')
- ->willReturnCallback(function ($class) use ($searchResult) {
- if ($class === SearchResult::class) {
- return $searchResult;
- }
- return null;
- });
-
- $this->container->expects($this->any())
- ->method('get')
- ->willReturnCallback(function ($class) use ($userPlugin, $groupPlugin, $remotePlugin, $mailPlugin) {
- if ($class === 'user') {
- return $userPlugin;
- } elseif ($class === 'group') {
- return $groupPlugin;
- } elseif ($class === 'remote') {
- return $remotePlugin;
- } elseif ($class === 'mail') {
- return $mailPlugin;
- }
- return null;
- });
-
- $this->search->registerPlugin(['shareType' => 'SHARE_TYPE_USER', 'class' => 'user']);
- $this->search->registerPlugin(['shareType' => 'SHARE_TYPE_GROUP', 'class' => 'group']);
- $this->search->registerPlugin(['shareType' => 'SHARE_TYPE_REMOTE', 'class' => 'remote']);
- $this->search->registerPlugin(['shareType' => 'SHARE_TYPE_EMAIL', 'class' => 'mail']);
-
- [$results, $moreResults] = $this->search->search($searchTerm, $shareTypes, false, $perPage, $perPage * ($page - 1));
-
- $this->assertEquals($expected, $results);
- $this->assertSame($expectedMoreResults, $moreResults);
- }
-
- public static function dataSearchSharees(): array {
- return [
- // #0
- [
- 'test', [IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_REMOTE], 1, 2, [], [], ['results' => [], 'exact' => [], 'exactIdMatch' => false],
- [],
- [
- 'exact' => ['users' => [], 'groups' => [], 'remotes' => []],
- 'users' => [],
- 'groups' => [],
- 'remotes' => [],
- ],
- false
- ],
- // #1
- [
- 'test', [IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_REMOTE], 1, 2, [], [], ['results' => [], 'exact' => [], 'exactIdMatch' => false],
- [],
- [
- 'exact' => ['users' => [], 'groups' => [], 'remotes' => []],
- 'users' => [],
- 'groups' => [],
- 'remotes' => [],
- ],
- false
- ],
- // #2
- [
- 'test', [IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_REMOTE], 1, 2, [
- ['label' => 'test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1']],
- ], [
- ['label' => 'testgroup1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'testgroup1']],
- ], [
- 'results' => [['label' => 'testz@remote', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'testz@remote']]], 'exact' => [], 'exactIdMatch' => false,
- ],
- [],
- [
- 'exact' => ['users' => [], 'groups' => [], 'remotes' => []],
- 'users' => [
- ['label' => 'test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1']],
- ],
- 'groups' => [
- ['label' => 'testgroup1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'testgroup1']],
- ],
- 'remotes' => [
- ['label' => 'testz@remote', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'testz@remote']],
- ],
- ], true,
- ],
- // #3 No groups requested
- [
- 'test', [IShare::TYPE_USER, IShare::TYPE_REMOTE], 1, 2, [
- ['label' => 'test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1']],
- ], [], [
- 'results' => [['label' => 'testz@remote', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'testz@remote']]], 'exact' => [], 'exactIdMatch' => false
- ],
- [],
- [
- 'exact' => ['users' => [], 'remotes' => []],
- 'users' => [
- ['label' => 'test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1']],
- ],
- 'remotes' => [
- ['label' => 'testz@remote', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'testz@remote']],
- ],
- ], false,
- ],
- // #4 Share type restricted to user - Only one user
- [
- 'test', [IShare::TYPE_USER], 1, 2, [
- ['label' => 'test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1']],
- ], [], [], [],
- [
- 'exact' => ['users' => []],
- 'users' => [
- ['label' => 'test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1']],
- ],
- ], false,
- ],
- // #5 Share type restricted to user - Multipage result
- [
- 'test', [IShare::TYPE_USER], 1, 2, [
- ['label' => 'test 1', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1']],
- ['label' => 'test 2', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test2']],
- ], [], [], [],
- [
- 'exact' => ['users' => []],
- 'users' => [
- ['label' => 'test 1', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1']],
- ['label' => 'test 2', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test2']],
- ],
- ], true,
- ],
- // #6 Mail shares filtered out in favor of remote shares
- [
- 'test', // search term
- [IShare::TYPE_USER, IShare::TYPE_GROUP, IShare::TYPE_REMOTE, IShare::TYPE_EMAIL], // plugins
- 1, // page
- 10, // per page
- [ // user result
- ['label' => 'test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1']],
- ],
- [ // group result
- ['label' => 'testgroup1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'testgroup1']],
- ],
- [ // remote result
- 'results' => [
- ['label' => 'testz@remote.tld', 'uuid' => 'f3d78140-abcc-46df-b58d-c7cc1176aadf','value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'testz@remote.tld']]
- ],
- 'exact' => [],
- 'exactIdMatch' => false,
- ],
- [ // mail result
- ['label' => 'test Two', 'uuid' => 'b2321e9e-31af-43ac-a406-583fb26d1964', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'test2@remote.tld']],
- ['label' => 'test One', 'uuid' => 'f3d78140-abcc-46df-b58d-c7cc1176aadf', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'testz@remote.tld']],
- ],
- [ // expected result
- 'exact' => ['users' => [], 'groups' => [], 'remotes' => [], 'emails' => []],
- 'users' => [
- ['label' => 'test One', 'value' => ['shareType' => IShare::TYPE_USER, 'shareWith' => 'test1']],
- ],
- 'groups' => [
- ['label' => 'testgroup1', 'value' => ['shareType' => IShare::TYPE_GROUP, 'shareWith' => 'testgroup1']],
- ],
- 'remotes' => [
- ['label' => 'testz@remote.tld', 'uuid' => 'f3d78140-abcc-46df-b58d-c7cc1176aadf', 'value' => ['shareType' => IShare::TYPE_REMOTE, 'shareWith' => 'testz@remote.tld']],
- ],
- 'emails' => [
- // one passes, another is filtered out
- ['label' => 'test Two', 'uuid' => 'b2321e9e-31af-43ac-a406-583fb26d1964', 'value' => ['shareType' => IShare::TYPE_EMAIL, 'shareWith' => 'test2@remote.tld']]
- ]
- ],
- false, // expected more results indicator
- ],
- ];
- }
-}
--- a/tests/lib/Collaboration/Resources/ProviderManagerTest.php
+++ /dev/null
@@ -1,99 +0,0 @@
-serverContainer = $this->createMock(ContainerInterface::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $this->providerManager = new class($this->serverContainer, $this->logger) extends ProviderManager {
- public function countProviders(): int {
- return count($this->providers);
- }
- };
- }
-
- public function testRegisterResourceProvider(): void {
- $this->providerManager->registerResourceProvider('AwesomeResourceProvider');
- $this->assertSame(1, $this->providerManager->countProviders());
- }
-
- public function testGetResourceProvidersNoProvider(): void {
- $this->assertCount(0, $this->providerManager->getResourceProviders());
- }
-
- public function testGetResourceProvidersValidProvider(): void {
- $this->serverContainer->expects($this->once())
- ->method('get')
- ->with($this->equalTo(ResourceProvider::class))
- ->willReturn($this->createMock(ResourceProvider::class));
-
- $this->providerManager->registerResourceProvider(ResourceProvider::class);
- $resourceProviders = $this->providerManager->getResourceProviders();
-
- $this->assertCount(1, $resourceProviders);
- $this->assertInstanceOf(ResourceProvider::class, $resourceProviders[0]);
- }
-
- public function testGetResourceProvidersInvalidProvider(): void {
- $this->serverContainer->expects($this->once())
- ->method('get')
- ->with($this->equalTo('InvalidResourceProvider'))
- ->willThrowException(new QueryException('A meaningful error message'));
-
- $this->logger->expects($this->once())
- ->method('error');
-
- $this->providerManager->registerResourceProvider('InvalidResourceProvider');
- $resourceProviders = $this->providerManager->getResourceProviders();
-
- $this->assertCount(0, $resourceProviders);
- }
-
- public function testGetResourceProvidersValidAndInvalidProvider(): void {
- $this->serverContainer->expects($this->exactly(2))
- ->method('get')
- ->willReturnCallback(function (string $service) {
- if ($service === 'InvalidResourceProvider') {
- throw new QueryException('A meaningful error message');
- }
- if ($service === ResourceProvider::class) {
- return $this->createMock(ResourceProvider::class);
- }
- });
-
- $this->logger->expects($this->once())
- ->method('error');
-
- $this->providerManager->registerResourceProvider('InvalidResourceProvider');
- $this->providerManager->registerResourceProvider(ResourceProvider::class);
- $resourceProviders = $this->providerManager->getResourceProviders();
-
- $this->assertCount(1, $resourceProviders);
- }
-}
--- a/tests/lib/Command/AsyncBusTestCase.php
+++ /dev/null
@@ -1,114 +0,0 @@
-state;
- }
-}
-
-class FilesystemCommand implements ICommand {
- use FileAccess;
-
- #[\Override]
- public function handle() {
- AsyncBusTestCase::$lastCommand = 'FileAccess';
- }
-}
-
-abstract class AsyncBusTestCase extends TestCase {
- /**
- * Basic way to check output from a command
- *
- * @var string
- */
- public static $lastCommand;
-
- /**
- * @var IBus
- */
- private $bus;
-
- public static function DummyCommand() {
- self::$lastCommand = 'static';
- }
-
- /**
- * @return IBus
- */
- protected function getBus() {
- if (!$this->bus instanceof IBus) {
- $this->bus = $this->createBus();
- }
- return $this->bus;
- }
-
- /**
- * @return IBus
- */
- abstract protected function createBus();
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- self::$lastCommand = '';
- }
-
- public function testSimpleCommand(): void {
- $command = new SimpleCommand();
- $this->getBus()->push($command);
- $this->runJobs();
- $this->assertEquals('SimpleCommand', self::$lastCommand);
- }
-
- public function testStateFullCommand(): void {
- $command = new StateFullCommand('foo');
- $this->getBus()->push($command);
- $this->runJobs();
- $this->assertEquals('foo', self::$lastCommand);
- }
-
- public function testFileFileAccessCommand(): void {
- $this->getBus()->push(new FilesystemCommand());
- $this->assertEquals('', self::$lastCommand);
- $this->runJobs();
- $this->assertEquals('FileAccess', self::$lastCommand);
- }
-
- public function testFileFileAccessCommandSync(): void {
- $this->getBus()->requireSync('\OC\Command\FileAccess');
- $this->getBus()->push(new FilesystemCommand());
- $this->assertEquals('FileAccess', self::$lastCommand);
- self::$lastCommand = '';
- $this->runJobs();
- $this->assertEquals('', self::$lastCommand);
- }
-
- abstract protected function runJobs();
-}
--- a/tests/lib/Command/BackgroundModeTest.php
+++ /dev/null
@@ -1,62 +0,0 @@
-
- * SPDX-License-Identifier: MIT
- */
-
-namespace Test\Command;
-
-use OC\Core\Command\Background\Mode;
-use OCP\IAppConfig;
-use Symfony\Component\Console\Input\InputArgument;
-use Symfony\Component\Console\Input\InputDefinition;
-use Symfony\Component\Console\Tester\CommandTester;
-use Test\TestCase;
-
-#[\PHPUnit\Framework\Attributes\Group('DB')]
-class BackgroundModeTest extends TestCase {
- private IAppConfig $appConfig;
-
- private Mode $command;
-
- #[\Override]
- public function setUp(): void {
- parent::setUp();
-
- $this->appConfig = $this->createMock(IAppConfig::class);
-
- $inputDefinition = new InputDefinition([
- new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
- ]);
-
- $this->command = new Mode($this->appConfig);
- $this->command->setDefinition($inputDefinition);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataModeCommand')]
- public function testModeCommand(string $mode): void {
- $this->appConfig->expects($this->once())
- ->method('setValueString')
- ->with('core', 'backgroundjobs_mode', $mode);
-
- $commandTester = new CommandTester($this->command);
- $commandTester->execute(['command' => 'background:' . $mode]);
-
- $commandTester->assertCommandIsSuccessful();
-
- $output = $commandTester->getDisplay();
- $this->assertStringContainsString($mode, $output);
- }
-
- public static function dataModeCommand(): array {
- return [
- 'ajax' => ['ajax'],
- 'cron' => ['cron'],
- 'webcron' => ['webcron'],
- ];
- }
-}
--- a/tests/lib/Command/CronBusTest.php
+++ /dev/null
@@ -1,40 +0,0 @@
-jobList = new DummyJobList();
- }
-
- #[\Override]
- protected function createBus() {
- return new CronBus($this->jobList);
- }
-
- #[\Override]
- protected function runJobs() {
- $jobs = $this->jobList->getAll();
- foreach ($jobs as $job) {
- $job->start($this->jobList);
- }
- }
-}
--- a/tests/lib/Command/Integrity/SignAppTest.php
+++ /dev/null
@@ -1,272 +0,0 @@
-checker = $this->createMock(Checker::class);
- $this->fileAccessHelper = $this->createMock(FileAccessHelper::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->signApp = new SignApp(
- $this->checker,
- $this->fileAccessHelper,
- $this->urlGenerator
- );
- }
-
- public function testExecuteWithMissingPath(): void {
- $inputInterface = $this->createMock(InputInterface::class);
- $outputInterface = $this->createMock(OutputInterface::class);
-
- $inputInterface
- ->expects($this->exactly(3))
- ->method('getOption')
- ->willReturnMap([
- ['path', null],
- ['privateKey', 'PrivateKey'],
- ['certificate', 'Certificate'],
- ]);
-
- $calls = [
- 'This command requires the --path, --privateKey and --certificate.',
- '*',
- '*',
- ];
- $outputInterface
- ->expects($this->any())
- ->method('writeln')
- ->willReturnCallback(function (string $message) use (&$calls): void {
- $expected = array_shift($calls);
- if ($expected === '*') {
- $this->assertNotEmpty($message);
- } else {
- $this->assertEquals($expected, $message);
- }
- });
-
- $this->assertSame(1, self::invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]));
- }
-
- public function testExecuteWithMissingPrivateKey(): void {
- $inputInterface = $this->createMock(InputInterface::class);
- $outputInterface = $this->createMock(OutputInterface::class);
-
- $inputInterface
- ->expects($this->exactly(3))
- ->method('getOption')
- ->willReturnMap([
- ['path', 'AppId'],
- ['privateKey', null],
- ['certificate', 'Certificate'],
- ]);
-
- $calls = [
- 'This command requires the --path, --privateKey and --certificate.',
- '*',
- '*',
- ];
- $outputInterface
- ->expects($this->any())
- ->method('writeln')
- ->willReturnCallback(function (string $message) use (&$calls): void {
- $expected = array_shift($calls);
- if ($expected === '*') {
- $this->assertNotEmpty($message);
- } else {
- $this->assertEquals($expected, $message);
- }
- });
-
- $this->assertSame(1, self::invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]));
- }
-
- public function testExecuteWithMissingCertificate(): void {
- $inputInterface = $this->createMock(InputInterface::class);
- $outputInterface = $this->createMock(OutputInterface::class);
-
- $inputInterface
- ->expects($this->exactly(3))
- ->method('getOption')
- ->willReturnMap([
- ['path', 'AppId'],
- ['privateKey', 'PrivateKey'],
- ['certificate', null],
- ]);
-
- $calls = [
- 'This command requires the --path, --privateKey and --certificate.',
- '*',
- '*',
- ];
- $outputInterface
- ->expects($this->any())
- ->method('writeln')
- ->willReturnCallback(function (string $message) use (&$calls): void {
- $expected = array_shift($calls);
- if ($expected === '*') {
- $this->assertNotEmpty($message);
- } else {
- $this->assertEquals($expected, $message);
- }
- });
-
- $this->assertSame(1, self::invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]));
- }
-
- public function testExecuteWithNotExistingPrivateKey(): void {
- $inputInterface = $this->createMock(InputInterface::class);
- $outputInterface = $this->createMock(OutputInterface::class);
-
- $inputInterface
- ->expects($this->exactly(3))
- ->method('getOption')
- ->willReturnMap([
- ['path', 'AppId'],
- ['privateKey', 'privateKey'],
- ['certificate', 'certificate'],
- ]);
-
- $this->fileAccessHelper
- ->expects($this->any())
- ->method('file_get_contents')
- ->willReturnMap([
- ['privateKey', false],
- ]);
-
- $outputInterface
- ->expects($this->any())
- ->method('writeln')
- ->willReturnCallback(function (string $message): void {
- $this->assertEquals('Private key "privateKey" does not exists.', $message);
- });
-
- $this->assertSame(1, self::invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]));
- }
-
- public function testExecuteWithNotExistingCertificate(): void {
- $inputInterface = $this->createMock(InputInterface::class);
- $outputInterface = $this->createMock(OutputInterface::class);
-
- $inputInterface
- ->expects($this->exactly(3))
- ->method('getOption')
- ->willReturnMap([
- ['path', 'AppId'],
- ['privateKey', 'privateKey'],
- ['certificate', 'certificate'],
- ]);
-
- $this->fileAccessHelper
- ->expects($this->any())
- ->method('file_get_contents')
- ->willReturnMap([
- ['privateKey', file_get_contents(\OC::$SERVERROOT . '/tests/data/integritycheck/core.key')],
- ['certificate', false],
- ]);
-
- $outputInterface
- ->expects($this->any())
- ->method('writeln')
- ->willReturnCallback(function (string $message): void {
- $this->assertEquals('Certificate "certificate" does not exists.', $message);
- });
-
- $this->assertSame(1, self::invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]));
- }
-
- public function testExecuteWithException(): void {
- $inputInterface = $this->createMock(InputInterface::class);
- $outputInterface = $this->createMock(OutputInterface::class);
-
- $inputInterface
- ->expects($this->exactly(3))
- ->method('getOption')
- ->willReturnMap([
- ['path', 'AppId'],
- ['privateKey', 'privateKey'],
- ['certificate', 'certificate'],
- ]);
-
- $this->fileAccessHelper
- ->expects($this->any())
- ->method('file_get_contents')
- ->willReturnMap([
- ['privateKey', file_get_contents(\OC::$SERVERROOT . '/tests/data/integritycheck/core.key')],
- ['certificate', \OC::$SERVERROOT . '/tests/data/integritycheck/core.crt'],
- ]);
-
- $this->checker
- ->expects($this->once())
- ->method('writeAppSignature')
- ->willThrowException(new \Exception('My error message'));
-
- $outputInterface
- ->expects($this->any())
- ->method('writeln')
- ->willReturnCallback(function (string $message): void {
- $this->assertEquals('Error: My error message', $message);
- });
-
- $this->assertSame(1, self::invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]));
- }
-
- public function testExecute(): void {
- $inputInterface = $this->createMock(InputInterface::class);
- $outputInterface = $this->createMock(OutputInterface::class);
-
- $inputInterface
- ->expects($this->exactly(3))
- ->method('getOption')
- ->willReturnMap([
- ['path', 'AppId'],
- ['privateKey', 'privateKey'],
- ['certificate', 'certificate'],
- ]);
-
- $this->fileAccessHelper
- ->expects($this->any())
- ->method('file_get_contents')
- ->willReturnMap([
- ['privateKey', file_get_contents(\OC::$SERVERROOT . '/tests/data/integritycheck/core.key')],
- ['certificate', \OC::$SERVERROOT . '/tests/data/integritycheck/core.crt'],
- ]);
-
- $this->checker
- ->expects($this->once())
- ->method('writeAppSignature');
-
- $outputInterface
- ->expects($this->any())
- ->method('writeln')
- ->willReturnCallback(function (string $message): void {
- $this->assertEquals('Successfully signed "AppId"', $message);
- });
-
- $this->assertSame(0, self::invokePrivate($this->signApp, 'execute', [$inputInterface, $outputInterface]));
- }
-}
--- a/tests/lib/Command/Integrity/SignCoreTest.php
+++ /dev/null
@@ -1,213 +0,0 @@
-checker = $this->createMock(Checker::class);
- $this->fileAccessHelper = $this->createMock(FileAccessHelper::class);
- $this->signCore = new SignCore(
- $this->checker,
- $this->fileAccessHelper
- );
- }
-
- public function testExecuteWithMissingPrivateKey(): void {
- $inputInterface = $this->createMock(InputInterface::class);
- $outputInterface = $this->createMock(OutputInterface::class);
-
- $inputInterface
- ->expects($this->exactly(3))
- ->method('getOption')
- ->willReturnMap([
- ['privateKey', null],
- ['certificate', 'certificate'],
- ['path', 'certificate'],
- ]);
-
- $outputInterface
- ->expects($this->any())
- ->method('writeln')
- ->willReturnCallback(function (string $message): void {
- $this->assertEquals('--privateKey, --certificate and --path are required.', $message);
- });
-
- $this->assertSame(1, self::invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface]));
- }
-
- public function testExecuteWithMissingCertificate(): void {
- $inputInterface = $this->createMock(InputInterface::class);
- $outputInterface = $this->createMock(OutputInterface::class);
-
- $inputInterface
- ->expects($this->exactly(3))
- ->method('getOption')
- ->willReturnMap([
- ['privateKey', 'privateKey'],
- ['certificate', null],
- ['path', 'certificate'],
- ]);
-
- $outputInterface
- ->expects($this->any())
- ->method('writeln')
- ->willReturnCallback(function (string $message): void {
- $this->assertEquals('--privateKey, --certificate and --path are required.', $message);
- });
-
- $this->assertSame(1, self::invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface]));
- }
-
- public function testExecuteWithNotExistingPrivateKey(): void {
- $inputInterface = $this->createMock(InputInterface::class);
- $outputInterface = $this->createMock(OutputInterface::class);
-
- $inputInterface
- ->expects($this->exactly(3))
- ->method('getOption')
- ->willReturnMap([
- ['privateKey', 'privateKey'],
- ['certificate', 'certificate'],
- ['path', 'certificate'],
- ]);
-
- $this->fileAccessHelper
- ->method('file_get_contents')
- ->willReturnMap([
- ['privateKey', false],
- ]);
-
- $outputInterface
- ->expects($this->any())
- ->method('writeln')
- ->willReturnCallback(function (string $message): void {
- $this->assertEquals('Private key "privateKey" does not exists.', $message);
- });
-
- $this->assertSame(1, self::invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface]));
- }
-
- public function testExecuteWithNotExistingCertificate(): void {
- $inputInterface = $this->createMock(InputInterface::class);
- $outputInterface = $this->createMock(OutputInterface::class);
-
- $inputInterface
- ->expects($this->exactly(3))
- ->method('getOption')
- ->willReturnMap([
- ['privateKey', 'privateKey'],
- ['certificate', 'certificate'],
- ['path', 'certificate'],
- ]);
-
- $this->fileAccessHelper
- ->expects($this->any())
- ->method('file_get_contents')
- ->willReturnMap([
- ['privateKey', file_get_contents(\OC::$SERVERROOT . '/tests/data/integritycheck/core.key')],
- ['certificate', false],
- ]);
-
- $outputInterface
- ->expects($this->any())
- ->method('writeln')
- ->willReturnCallback(function (string $message): void {
- $this->assertEquals('Certificate "certificate" does not exists.', $message);
- });
-
- $this->assertSame(1, self::invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface]));
- }
-
- public function testExecuteWithException(): void {
- $inputInterface = $this->createMock(InputInterface::class);
- $outputInterface = $this->createMock(OutputInterface::class);
-
- $inputInterface
- ->expects($this->exactly(3))
- ->method('getOption')
- ->willReturnMap([
- ['privateKey', 'privateKey'],
- ['certificate', 'certificate'],
- ['path', 'certificate'],
- ]);
-
- $this->fileAccessHelper
- ->expects($this->any())
- ->method('file_get_contents')
- ->willReturnMap([
- ['privateKey', file_get_contents(\OC::$SERVERROOT . '/tests/data/integritycheck/core.key')],
- ['certificate', file_get_contents(\OC::$SERVERROOT . '/tests/data/integritycheck/core.crt')],
- ]);
-
- $this->checker
- ->expects($this->once())
- ->method('writeCoreSignature')
- ->willThrowException(new \Exception('My exception message'));
-
- $outputInterface
- ->expects($this->any())
- ->method('writeln')
- ->willReturnCallback(function (string $message): void {
- $this->assertEquals('Error: My exception message', $message);
- });
-
- $this->assertEquals(1, self::invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface]));
- }
-
- public function testExecute(): void {
- $inputInterface = $this->createMock(InputInterface::class);
- $outputInterface = $this->createMock(OutputInterface::class);
-
- $inputInterface
- ->expects($this->exactly(3))
- ->method('getOption')
- ->willReturnMap([
- ['privateKey', 'privateKey'],
- ['certificate', 'certificate'],
- ['path', 'certificate'],
- ]);
-
- $this->fileAccessHelper
- ->expects($this->any())
- ->method('file_get_contents')
- ->willReturnMap([
- ['privateKey', file_get_contents(\OC::$SERVERROOT . '/tests/data/integritycheck/core.key')],
- ['certificate', file_get_contents(\OC::$SERVERROOT . '/tests/data/integritycheck/core.crt')],
- ]);
-
- $this->checker
- ->expects($this->once())
- ->method('writeCoreSignature');
-
- $outputInterface
- ->expects($this->any())
- ->method('writeln')
- ->willReturnCallback(function (string $message): void {
- $this->assertEquals('Successfully signed "core"', $message);
- });
-
- $this->assertEquals(0, self::invokePrivate($this->signCore, 'execute', [$inputInterface, $outputInterface]));
- }
-}
--- a/tests/lib/Comments/CommentTest.php
+++ /dev/null
@@ -1,267 +0,0 @@
- 'users', 'id' => 'alice'];
- $creationDT = new \DateTime();
- $latestChildDT = new \DateTime('yesterday');
- $object = ['type' => 'files', 'id' => 'file64'];
- $referenceId = sha1('referenceId');
- $metaData = ['last_edit_actor_id' => 'admin'];
-
- $comment
- ->setId($id)
- ->setParentId($parentId)
- ->setTopmostParentId($topMostParentId)
- ->setChildrenCount($childrenCount)
- ->setMessage($message)
- ->setVerb($verb)
- ->setActor($actor['type'], $actor['id'])
- ->setCreationDateTime($creationDT)
- ->setLatestChildDateTime($latestChildDT)
- ->setObject($object['type'], $object['id'])
- ->setReferenceId($referenceId)
- ->setMetaData($metaData);
-
- $this->assertSame($id, $comment->getId());
- $this->assertSame($parentId, $comment->getParentId());
- $this->assertSame($topMostParentId, $comment->getTopmostParentId());
- $this->assertSame($childrenCount, $comment->getChildrenCount());
- $this->assertSame($message, $comment->getMessage());
- $this->assertSame($verb, $comment->getVerb());
- $this->assertSame($actor['type'], $comment->getActorType());
- $this->assertSame($actor['id'], $comment->getActorId());
- $this->assertSame($creationDT, $comment->getCreationDateTime());
- $this->assertSame($latestChildDT, $comment->getLatestChildDateTime());
- $this->assertSame($object['type'], $comment->getObjectType());
- $this->assertSame($object['id'], $comment->getObjectId());
- $this->assertSame($referenceId, $comment->getReferenceId());
- $this->assertSame($metaData, $comment->getMetaData());
- }
-
- public function testSetIdIllegalInput(): void {
- $this->expectException(IllegalIDChangeException::class);
-
- $comment = new Comment();
-
- $comment->setId('c23');
- $comment->setId('c17');
- }
-
- /**
- * @throws IllegalIDChangeException
- */
- public function testResetId(): void {
- $comment = new Comment();
- $comment->setId('c23');
- $comment->setId('');
-
- $this->assertSame('', $comment->getId());
- }
-
- public static function simpleSetterProvider(): array {
- return [
- ['Id', true],
- ['TopmostParentId', true],
- ['ParentId', true],
- ['Message', true],
- ['Verb', true],
- ['Verb', ''],
- ['ChildrenCount', true],
- ];
- }
-
- #[DataProvider(methodName: 'simpleSetterProvider')]
- public function testSimpleSetterInvalidInput($field, $input): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $comment = new Comment();
- $setter = 'set' . $field;
-
- $comment->$setter($input);
- }
-
- public static function roleSetterProvider(): array {
- return [
- ['Actor', true, true],
- ['Actor', 'users', true],
- ['Actor', true, 'alice'],
- ['Actor', ' ', ' '],
- ['Object', true, true],
- ['Object', 'files', true],
- ['Object', true, 'file64'],
- ['Object', ' ', ' '],
- ];
- }
-
- #[DataProvider(methodName: 'roleSetterProvider')]
- public function testSetRoleInvalidInput($role, $type, $id): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $comment = new Comment();
- $setter = 'set' . $role;
- $comment->$setter($type, $id);
- }
-
- public function testSetUberlongMessage(): void {
- $this->expectException(MessageTooLongException::class);
-
- $comment = new Comment();
- $msg = str_pad('', IComment::MAX_MESSAGE_LENGTH + 1, 'x');
- $comment->setMessage($msg);
- }
-
- public static function mentionsProvider(): array {
- return [
- [
- '@alice @bob look look, a cook!',
- [['type' => 'user', 'id' => 'alice'], ['type' => 'user', 'id' => 'bob']],
- ],
- [
- 'no mentions in this message',
- []
- ],
- [
- '@alice @bob look look, a duplication @alice test @bob!',
- [['type' => 'user', 'id' => 'alice'], ['type' => 'user', 'id' => 'bob']],
- ],
- [
- '@alice is the author, notify @bob, nevertheless mention her!',
- [['type' => 'user', 'id' => 'alice'], ['type' => 'user', 'id' => 'bob']],
- /* author: */ 'alice'
- ],
- [
- '@foobar and @barfoo you should know, @foo@bar.com is valid'
- . ' and so is @bar@foo.org@foobar.io I hope that clarifies everything.'
- . ' cc @23452-4333-54353-2342 @yolo!'
- . ' however the most important thing to know is that www.croissant.com/@oil is not valid'
- . ' and won\'t match anything at all',
- [
- ['type' => 'user', 'id' => 'bar@foo.org@foobar.io'],
- ['type' => 'user', 'id' => '23452-4333-54353-2342'],
- ['type' => 'user', 'id' => 'foo@bar.com'],
- ['type' => 'user', 'id' => 'foobar'],
- ['type' => 'user', 'id' => 'barfoo'],
- ['type' => 'user', 'id' => 'yolo'],
- ],
- ],
- [
- '@@chef is also a valid mention, no matter how strange it looks',
- [['type' => 'user', 'id' => '@chef']],
- ],
- [
- 'Also @"user with spaces" are now supported',
- [['type' => 'user', 'id' => 'user with spaces']],
- ],
- [
- 'Also @"guest/0123456789abcdef" are now supported',
- [['type' => 'guest', 'id' => 'guest/0123456789abcdef']],
- ],
- [
- 'Also @"group/My Group ID 321" are now supported',
- [['type' => 'group', 'id' => 'My Group ID 321']],
- ],
- [
- 'Welcome federation @"federated_group/My Group ID 321" @"federated_team/Former Cirle" @"federated_user/cloudId@http://example.tld:8080/nextcloud"! Now freshly supported',
- [
- ['type' => 'federated_user', 'id' => 'cloudId@http://example.tld:8080/nextcloud'],
- ['type' => 'federated_group', 'id' => 'My Group ID 321'],
- ['type' => 'federated_team', 'id' => 'Former Cirle'],
- ],
- ],
- [
- 'Emails are supported since 30.0.2 right? @"email/aa23d315de327cfc330f0401ea061005b2b0cdd45ec8346f12664dd1f34cb886"',
- [
- ['type' => 'email', 'id' => 'aa23d315de327cfc330f0401ea061005b2b0cdd45ec8346f12664dd1f34cb886'],
- ],
- ],
- [
- 'Mention @alice but not `@bob` inside inline code',
- [['type' => 'user', 'id' => 'alice']],
- ],
- [
- 'Mention @alice but not `Hello @bob there` inside inline code',
- [['type' => 'user', 'id' => 'alice']],
- ],
- [
- "Mention ` user @alice\nAs it's just 2 ` accents",
- [['type' => 'user', 'id' => 'alice']],
- ],
- [
- 'Mention @alice and @bob but not `Hello @bob there` inside inline code',
- [['type' => 'user', 'id' => 'alice'], ['type' => 'user', 'id' => 'bob']],
- ],
- [
- "Mention @alice but not in fenced code block\n```\n@bob @charlie\n```\nend",
- [['type' => 'user', 'id' => 'alice']],
- ],
- [
- "Mention @alice but not in tilde code block\n~~~\n@bob\n~~~\nend",
- [['type' => 'user', 'id' => 'alice']],
- ],
- [
- 'No mentions at all in `@alice` and `@bob`',
- [],
- ],
- [
- "@alice\n```\n@bob\n```\n@charlie",
- [['type' => 'user', 'id' => 'charlie'], ['type' => 'user', 'id' => 'alice']],
- ],
- [
- "@alice\n```\n@bob\n```\n@charlie",
- [['type' => 'user', 'id' => 'charlie'], ['type' => 'user', 'id' => 'alice'], ['type' => 'user', 'id' => 'bob']],
- null,
- false,
- ],
- [
- 'Mention @alice and `also @bob as end of text only applies to code blocks',
- [['type' => 'user', 'id' => 'alice'], ['type' => 'user', 'id' => 'bob']],
- ],
- [
- "Mention @alice but not in unclosed fenced code block\n```\n@bob\n@charlie",
- [['type' => 'user', 'id' => 'alice']],
- ],
- [
- "Mention @alice but not in unclosed tilde code block\n~~~\n@bob",
- [['type' => 'user', 'id' => 'alice']],
- ],
- ];
- }
-
- #[DataProvider(methodName: 'mentionsProvider')]
- public function testMentions(string $message, array $expectedMentions, ?string $author = null, bool $markdown = true): void {
- $comment = new Comment();
- $comment->setMessage($message);
- if (!is_null($author)) {
- $comment->setActor('user', $author);
- }
- $mentions = $comment->getMentions($markdown);
- $this->assertSame($expectedMentions, $mentions);
- }
-}
--- a/tests/lib/Comments/FakeFactory.php
+++ /dev/null
@@ -1,25 +0,0 @@
- new Comment(), 'replies' => []];
- }
-
- #[\Override]
- public function getForObject(
- $objectType,
- $objectId,
- $limit = 0,
- $offset = 0,
- ?\DateTime $notOlderThan = null,
- ): array {
- return [];
- }
-
- #[\Override]
- public function getForObjectSince(
- string $objectType,
- string $objectId,
- int $lastKnownCommentId,
- string $sortDirection = 'asc',
- int $limit = 30,
- bool $includeLastKnown = false,
- string $topmostParentId = '',
- ): array {
- return [];
- }
-
- #[\Override]
- public function getCommentsWithVerbForObjectSinceComment(
- string $objectType,
- string $objectId,
- array $verbs,
- int $lastKnownCommentId,
- string $sortDirection = 'asc',
- int $limit = 30,
- bool $includeLastKnown = false,
- string $topmostParentId = '',
- ): array {
- return [];
- }
-
- #[\Override]
- public function getNumberOfCommentsForObject($objectType, $objectId, ?\DateTime $notOlderThan = null, $verb = ''): int {
- return 0;
- }
-
- #[\Override]
- public function getNumberOfCommentsForObjects(string $objectType, array $objectIds, ?\DateTime $notOlderThan = null, string $verb = ''): array {
- return array_fill_keys($objectIds, 0);
- }
-
- #[\Override]
- public function search(string $search, string $objectType, string $objectId, string $verb, int $offset, int $limit = 50): array {
- return [];
- }
-
- #[\Override]
- public function create($actorType, $actorId, $objectType, $objectId): IComment {
- return new Comment();
- }
-
- #[\Override]
- public function delete($id): bool {
- return false;
- }
-
- #[\Override]
- public function getReactionComment(int $parentId, string $actorType, string $actorId, string $reaction): IComment {
- return new Comment();
- }
-
- #[\Override]
- public function retrieveAllReactions(int $parentId): array {
- return [];
- }
-
- #[\Override]
- public function retrieveAllReactionsWithSpecificReaction(int $parentId, string $reaction): array {
- return [];
- }
-
- #[\Override]
- public function supportReactions(): bool {
- return false;
- }
-
- #[\Override]
- public function save(IComment $comment): bool {
- return false;
- }
-
- #[\Override]
- public function deleteReferencesOfActor($actorType, $actorId): bool {
- return false;
- }
-
- #[\Override]
- public function deleteCommentsAtObject($objectType, $objectId): bool {
- return false;
- }
-
- #[\Override]
- public function setReadMark($objectType, $objectId, \DateTime $dateTime, IUser $user): bool {
- return false;
- }
-
- #[\Override]
- public function getReadMark($objectType, $objectId, IUser $user): bool {
- return false;
- }
-
- #[\Override]
- public function deleteReadMarksFromUser(IUser $user): bool {
- return false;
- }
-
- #[\Override]
- public function deleteReadMarksOnObject($objectType, $objectId): bool {
- return false;
- }
-
- #[\Override]
- public function registerEventHandler(\Closure $closure): void {
- }
-
- #[\Override]
- public function registerDisplayNameResolver($type, \Closure $closure): void {
- }
-
- #[\Override]
- public function resolveDisplayName($type, $id): string {
- return '';
- }
-
- #[\Override]
- public function getNumberOfUnreadCommentsForFolder($folderId, IUser $user): array {
- return [];
- }
-
- #[\Override]
- public function getNumberOfUnreadCommentsForObjects(string $objectType, array $objectIds, IUser $user, $verb = ''): array {
- return [];
- }
-
- #[\Override]
- public function load(): void {
- }
-
- #[\Override]
- public function searchForObjects(string $search, string $objectType, array $objectIds, string $verb, int $offset, int $limit = 50): array {
- return [];
- }
-
- #[\Override]
- public function getNumberOfCommentsForObjectSinceComment(string $objectType, string $objectId, int $lastRead, string $verb = ''): int {
- return 0;
- }
-
- #[\Override]
- public function getNumberOfCommentsWithVerbsForObjectSinceComment(string $objectType, string $objectId, int $lastRead, array $verbs): int {
- return 0;
- }
-
- #[\Override]
- public function getLastCommentBeforeDate(string $objectType, string $objectId, \DateTime $beforeDate, string $verb = ''): int {
- return 0;
- }
-
- #[\Override]
- public function getLastCommentDateByActor(string $objectType, string $objectId, string $verb, string $actorType, array $actors): array {
- return [];
- }
-
- #[\Override]
- public function deleteCommentsExpiredAtObject(string $objectType, string $objectId = ''): bool {
- return true;
- }
-}
--- a/tests/lib/Comments/ManagerTest.php
+++ /dev/null
@@ -1,1385 +0,0 @@
-connection = Server::get(IDBConnection::class);
- $this->rootFolder = $this->createMock(IRootFolder::class);
-
- /** @psalm-suppress DeprecatedMethod */
- $sql = $this->connection->getDatabasePlatform()->getTruncateTableSQL('`*PREFIX*comments`');
- $this->connection->prepare($sql)->execute();
- /** @psalm-suppress DeprecatedMethod */
- $sql = $this->connection->getDatabasePlatform()->getTruncateTableSQL('`*PREFIX*reactions`');
- $this->connection->prepare($sql)->execute();
- }
-
- protected function addDatabaseEntry(?string $parentId, ?string $topmostParentId, ?\DateTimeInterface $creationDT = null, ?\DateTimeInterface $latestChildDT = null, $objectId = null, ?\DateTimeInterface $expireDate = null): string {
- $creationDT ??= new \DateTime();
- $latestChildDT ??= new \DateTime('yesterday');
- $objectId ??= 'file64';
-
- $qb = $this->connection->getQueryBuilder();
- $qb
- ->insert('comments')
- ->values([
- 'parent_id' => $qb->createNamedParameter($parentId),
- 'topmost_parent_id' => $qb->createNamedParameter($topmostParentId),
- 'children_count' => $qb->createNamedParameter(2),
- 'actor_type' => $qb->createNamedParameter('users'),
- 'actor_id' => $qb->createNamedParameter('alice'),
- 'message' => $qb->createNamedParameter('nice one'),
- 'verb' => $qb->createNamedParameter('comment'),
- 'creation_timestamp' => $qb->createNamedParameter($creationDT, IQueryBuilder::PARAM_DATETIME_MUTABLE),
- 'latest_child_timestamp' => $qb->createNamedParameter($latestChildDT, IQueryBuilder::PARAM_DATETIME_MUTABLE),
- 'object_type' => $qb->createNamedParameter('files'),
- 'object_id' => $qb->createNamedParameter($objectId),
- 'expire_date' => $qb->createNamedParameter($expireDate, IQueryBuilder::PARAM_DATETIME_MUTABLE),
- 'reference_id' => $qb->createNamedParameter('referenceId'),
- 'meta_data' => $qb->createNamedParameter(json_encode(['last_edit_actor_id' => 'admin'])),
- ])
- ->executeStatement();
-
- return (string)$qb->getLastInsertId();
- }
-
- protected function getManager(): Manager {
- /** @psalm-suppress DeprecatedInterface No way around at the moment */
- return new Manager(
- $this->connection,
- $this->createMock(LoggerInterface::class),
- $this->createMock(IConfig::class),
- $this->createMock(ITimeFactory::class),
- new EmojiHelper($this->connection),
- $this->createMock(IInitialStateService::class),
- $this->rootFolder,
- $this->createMock(IEventDispatcher::class),
- );
- }
-
- public function testGetCommentNotFound(): void {
- $this->expectException(NotFoundException::class);
-
- $manager = $this->getManager();
- $manager->get('22');
- }
-
- public function testGetCommentNotFoundInvalidInput(): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $manager = $this->getManager();
- $manager->get('unexisting22');
- }
-
- public function testGetComment(): void {
- $manager = $this->getManager();
-
- $creationDT = new \DateTime('yesterday');
- $latestChildDT = new \DateTime();
-
- $qb = $this->connection->getQueryBuilder();
- $qb
- ->insert('comments')
- ->values([
- 'parent_id' => $qb->createNamedParameter('2'),
- 'topmost_parent_id' => $qb->createNamedParameter('1'),
- 'children_count' => $qb->createNamedParameter(2),
- 'actor_type' => $qb->createNamedParameter('users'),
- 'actor_id' => $qb->createNamedParameter('alice'),
- 'message' => $qb->createNamedParameter('nice one'),
- 'verb' => $qb->createNamedParameter('comment'),
- 'creation_timestamp' => $qb->createNamedParameter($creationDT, 'datetime'),
- 'latest_child_timestamp' => $qb->createNamedParameter($latestChildDT, 'datetime'),
- 'object_type' => $qb->createNamedParameter('files'),
- 'object_id' => $qb->createNamedParameter('file64'),
- 'reference_id' => $qb->createNamedParameter('referenceId'),
- 'meta_data' => $qb->createNamedParameter(json_encode(['last_edit_actor_id' => 'admin'])),
- ])
- ->executeStatement();
-
- $id = (string)$qb->getLastInsertId();
-
- $comment = $manager->get($id);
- $this->assertInstanceOf(IComment::class, $comment);
- $this->assertSame($id, $comment->getId());
- $this->assertSame('2', $comment->getParentId());
- $this->assertSame('1', $comment->getTopmostParentId());
- $this->assertSame(2, $comment->getChildrenCount());
- $this->assertSame('users', $comment->getActorType());
- $this->assertSame('alice', $comment->getActorId());
- $this->assertSame('nice one', $comment->getMessage());
- $this->assertSame('comment', $comment->getVerb());
- $this->assertSame('files', $comment->getObjectType());
- $this->assertSame('file64', $comment->getObjectId());
- $this->assertEquals($creationDT->getTimestamp(), $comment->getCreationDateTime()->getTimestamp());
- $this->assertEquals($latestChildDT->getTimestamp(), $comment->getLatestChildDateTime()->getTimestamp());
- $this->assertEquals('referenceId', $comment->getReferenceId());
- $this->assertEquals(['last_edit_actor_id' => 'admin'], $comment->getMetaData());
- }
-
- public function testGetTreeNotFound(): void {
- $this->expectException(NotFoundException::class);
-
- $manager = $this->getManager();
- $manager->getTree('22');
- }
-
- public function testGetTreeNotFoundInvalidIpnut(): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $manager = $this->getManager();
- $manager->getTree('unexisting22');
- }
-
- public function testGetTree(): void {
- $headId = $this->addDatabaseEntry('0', '0');
-
- $this->addDatabaseEntry($headId, $headId, new \DateTime('-3 hours'));
- $this->addDatabaseEntry($headId, $headId, new \DateTime('-2 hours'));
- $id = $this->addDatabaseEntry($headId, $headId, new \DateTime('-1 hour'));
-
- $manager = $this->getManager();
- $tree = $manager->getTree($headId);
-
- // Verifying the root comment
- $this->assertArrayHasKey('comment', $tree);
- $this->assertInstanceOf(IComment::class, $tree['comment']);
- $this->assertSame($headId, $tree['comment']->getId());
- $this->assertArrayHasKey('replies', $tree);
- $this->assertCount(3, $tree['replies']);
-
- // one level deep
- foreach ($tree['replies'] as $reply) {
- $this->assertInstanceOf(IComment::class, $reply['comment']);
- $this->assertSame((string)$id, $reply['comment']->getId());
- $this->assertCount(0, $reply['replies']);
- $id--;
- }
- }
-
- public function testGetTreeNoReplies(): void {
- $id = $this->addDatabaseEntry('0', '0');
-
- $manager = $this->getManager();
- $tree = $manager->getTree($id);
-
- // Verifying the root comment
- $this->assertArrayHasKey('comment', $tree);
- $this->assertInstanceOf(IComment::class, $tree['comment']);
- $this->assertSame($id, $tree['comment']->getId());
- $this->assertArrayHasKey('replies', $tree);
- $this->assertCount(0, $tree['replies']);
- }
-
- public function testGetTreeWithLimitAndOffset(): void {
- $headId = $this->addDatabaseEntry('0', '0');
-
- $this->addDatabaseEntry($headId, $headId, new \DateTime('-3 hours'));
- $this->addDatabaseEntry($headId, $headId, new \DateTime('-2 hours'));
- $this->addDatabaseEntry($headId, $headId, new \DateTime('-1 hour'));
- $idToVerify = $this->addDatabaseEntry($headId, $headId, new \DateTime());
-
- $manager = $this->getManager();
-
- for ($offset = 0; $offset < 3; $offset += 2) {
- $tree = $manager->getTree($headId, 2, $offset);
-
- // Verifying the root comment
- $this->assertArrayHasKey('comment', $tree);
- $this->assertInstanceOf(IComment::class, $tree['comment']);
- $this->assertSame($headId, $tree['comment']->getId());
- $this->assertArrayHasKey('replies', $tree);
- $this->assertCount(2, $tree['replies']);
-
- // one level deep
- foreach ($tree['replies'] as $reply) {
- $this->assertInstanceOf(IComment::class, $reply['comment']);
- $this->assertSame((string)$idToVerify, (string)$reply['comment']->getId());
- $this->assertCount(0, $reply['replies']);
- $idToVerify--;
- }
- }
- }
-
- public function testGetForObject(): void {
- $this->addDatabaseEntry('0', '0');
-
- $manager = $this->getManager();
- $comments = $manager->getForObject('files', 'file64');
-
- $this->assertIsArray($comments);
- $this->assertCount(1, $comments);
- $this->assertInstanceOf(IComment::class, $comments[0]);
- $this->assertSame('nice one', $comments[0]->getMessage());
- }
-
- public function testGetForObjectWithLimitAndOffset(): void {
- $this->addDatabaseEntry('0', '0', new \DateTime('-6 hours'));
- $this->addDatabaseEntry('0', '0', new \DateTime('-5 hours'));
- $this->addDatabaseEntry('1', '1', new \DateTime('-4 hours'));
- $this->addDatabaseEntry('0', '0', new \DateTime('-3 hours'));
- $this->addDatabaseEntry('2', '2', new \DateTime('-2 hours'));
- $this->addDatabaseEntry('2', '2', new \DateTime('-1 hours'));
- $idToVerify = $this->addDatabaseEntry('3', '1', new \DateTime());
-
- $manager = $this->getManager();
- $offset = 0;
- do {
- $comments = $manager->getForObject('files', 'file64', 3, $offset);
-
- $this->assertIsArray($comments);
- foreach ($comments as $key => $comment) {
- $this->assertInstanceOf(IComment::class, $comment);
- $this->assertSame('nice one', $comment->getMessage());
- $this->assertSame((string)$idToVerify, $comment->getId(), 'ID wrong for comment ' . $key . ' on offset: ' . $offset);
- $idToVerify--;
- }
- $offset += 3;
- } while (count($comments) > 0);
- }
-
- public function testGetForObjectWithDateTimeConstraint(): void {
- $this->addDatabaseEntry('0', '0', new \DateTime('-6 hours'));
- $this->addDatabaseEntry('0', '0', new \DateTime('-5 hours'));
- $id1 = $this->addDatabaseEntry('0', '0', new \DateTime('-3 hours'));
- $id2 = $this->addDatabaseEntry('2', '2', new \DateTime('-2 hours'));
-
- $manager = $this->getManager();
- $comments = $manager->getForObject('files', 'file64', 0, 0, new \DateTime('-4 hours'));
-
- $this->assertCount(2, $comments);
- $this->assertSame($id2, $comments[0]->getId());
- $this->assertSame($id1, $comments[1]->getId());
- }
-
- public function testGetForObjectWithLimitAndOffsetAndDateTimeConstraint(): void {
- $this->addDatabaseEntry('0', '0', new \DateTime('-7 hours'));
- $this->addDatabaseEntry('0', '0', new \DateTime('-6 hours'));
- $this->addDatabaseEntry('1', '1', new \DateTime('-5 hours'));
- $this->addDatabaseEntry('0', '0', new \DateTime('-3 hours'));
- $this->addDatabaseEntry('2', '2', new \DateTime('-2 hours'));
- $this->addDatabaseEntry('2', '2', new \DateTime('-1 hours'));
- $idToVerify = $this->addDatabaseEntry('3', '1', new \DateTime());
-
- $manager = $this->getManager();
- $offset = 0;
- do {
- $comments = $manager->getForObject('files', 'file64', 3, $offset, new \DateTime('-4 hours'));
-
- $this->assertIsArray($comments);
- foreach ($comments as $comment) {
- $this->assertInstanceOf(IComment::class, $comment);
- $this->assertSame('nice one', $comment->getMessage());
- $this->assertSame((string)$idToVerify, $comment->getId());
- $this->assertGreaterThanOrEqual(4, $comment->getId());
- $idToVerify--;
- }
- $offset += 3;
- } while (count($comments) > 0);
- }
-
- public function testGetNumberOfCommentsForObject(): void {
- for ($i = 1; $i < 5; $i++) {
- $this->addDatabaseEntry('0', '0');
- }
-
- $manager = $this->getManager();
-
- $amount = $manager->getNumberOfCommentsForObject('untype', '00');
- $this->assertSame(0, $amount);
-
- $amount = $manager->getNumberOfCommentsForObject('files', 'file64');
- $this->assertSame(4, $amount);
- }
-
- public function testGetNumberOfUnreadCommentsForFolder(): void {
- $folder = $this->createMock(Folder::class);
- $fileIds = range(1111, 1114);
- $children = array_map(function (int $id) {
- $file = $this->createMock(Folder::class);
- $file->method('getId')
- ->willReturn($id);
- return $file;
- }, $fileIds);
- $folder->method('getId')->willReturn(1000);
- $folder->method('getDirectoryListing')->willReturn($children);
- $this->rootFolder->method('getFirstNodeById')
- ->with($folder->getId())
- ->willReturn($folder);
-
- // 2 comment for 1111 with 1 before read marker
- // 2 comments for 1112 with no read marker
- // 1 comment for 1113 before read marker
- // 1 comment for 1114 with no read marker
- $this->addDatabaseEntry('0', '0', null, null, $fileIds[1]);
- for ($i = 0; $i < 4; $i++) {
- $this->addDatabaseEntry('0', '0', null, null, $fileIds[$i]);
- }
- $this->addDatabaseEntry('0', '0', (new \DateTime())->modify('-2 days'), null, $fileIds[0]);
- /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
- $user = $this->createMock(IUser::class);
- $user->expects($this->any())
- ->method('getUID')
- ->willReturn('comment_test');
-
- $manager = $this->getManager();
-
- $manager->setReadMark('files', (string)$fileIds[0], (new \DateTime())->modify('-1 days'), $user);
- $manager->setReadMark('files', (string)$fileIds[2], (new \DateTime()), $user);
-
- $amount = $manager->getNumberOfUnreadCommentsForFolder($folder->getId(), $user);
- $this->assertEquals([
- $fileIds[0] => 1,
- $fileIds[1] => 2,
- $fileIds[3] => 1,
- ], $amount);
- }
-
- #[DataProvider(methodName: 'dataGetForObjectSince')]
- public function testGetForObjectSince(?int $lastKnown, string $order, int $limit, int $resultFrom, int $resultTo): void {
- $ids = [];
- $ids[] = $this->addDatabaseEntry('0', '0');
- $ids[] = $this->addDatabaseEntry('0', '0');
- $ids[] = $this->addDatabaseEntry('0', '0');
- $ids[] = $this->addDatabaseEntry('0', '0');
- $ids[] = $this->addDatabaseEntry('0', '0');
-
- $manager = $this->getManager();
- $comments = $manager->getForObjectSince('files', 'file64', ($lastKnown === null ? 0 : (int)$ids[$lastKnown]), $order, $limit);
-
- $expected = array_slice($ids, $resultFrom, $resultTo - $resultFrom + 1);
- if ($order === 'desc') {
- $expected = array_reverse($expected);
- }
-
- $this->assertSame($expected, array_map(static fn (IComment $c): string => $c->getId(), $comments));
- }
-
- public static function dataGetForObjectSince(): array {
- return [
- [null, 'asc', 20, 0, 4],
- [null, 'asc', 2, 0, 1],
- [null, 'desc', 20, 0, 4],
- [null, 'desc', 2, 3, 4],
- [1, 'asc', 20, 2, 4],
- [1, 'asc', 2, 2, 3],
- [3, 'desc', 20, 0, 2],
- [3, 'desc', 2, 1, 2],
- ];
- }
-
- public static function invalidCreateArgsProvider(): array {
- return [
- ['', 'aId-1', 'oType-1', 'oId-1'],
- ['aType-1', '', 'oType-1', 'oId-1'],
- ['aType-1', 'aId-1', '', 'oId-1'],
- ['aType-1', 'aId-1', 'oType-1', ''],
- [1, 'aId-1', 'oType-1', 'oId-1'],
- ['aType-1', 1, 'oType-1', 'oId-1'],
- ['aType-1', 'aId-1', 1, 'oId-1'],
- ['aType-1', 'aId-1', 'oType-1', 1],
- ];
- }
-
- #[DataProvider(methodName: 'invalidCreateArgsProvider')]
- public function testCreateCommentInvalidArguments(string|int $aType, string|int $aId, string|int $oType, string|int $oId): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $manager = $this->getManager();
- $manager->create($aType, $aId, $oType, $oId);
- }
-
- public function testCreateComment(): void {
- $actorType = 'bot';
- $actorId = 'bob';
- $objectType = 'weather';
- $objectId = 'bielefeld';
-
- $comment = $this->getManager()->create($actorType, $actorId, $objectType, $objectId);
- $this->assertInstanceOf(IComment::class, $comment);
- $this->assertSame($actorType, $comment->getActorType());
- $this->assertSame($actorId, $comment->getActorId());
- $this->assertSame($objectType, $comment->getObjectType());
- $this->assertSame($objectId, $comment->getObjectId());
- }
-
- public function testDelete(): void {
- $this->expectException(NotFoundException::class);
-
- $manager = $this->getManager();
-
- $done = $manager->delete('404');
- $this->assertFalse($done);
-
- $done = $manager->delete('%');
- $this->assertFalse($done);
-
- $done = $manager->delete('');
- $this->assertFalse($done);
-
- $id = $this->addDatabaseEntry('0', '0');
- $comment = $manager->get($id);
- $this->assertInstanceOf(IComment::class, $comment);
- $done = $manager->delete($id);
- $this->assertTrue($done);
- $manager->get($id);
- }
-
- #[DataProvider(methodName: 'providerTestSave')]
- public function testSave(string $message, string $actorId, string $verb, ?string $parentId, ?string $id = ''): IComment {
- $manager = $this->getManager();
- $comment = new Comment();
- $comment
- ->setId($id)
- ->setActor('users', $actorId)
- ->setObject('files', 'file64')
- ->setMessage($message)
- ->setVerb($verb);
- if ($parentId) {
- $comment->setParentId($parentId);
- }
-
- $saveSuccessful = $manager->save($comment);
- $this->assertTrue($saveSuccessful, 'Comment saving was not successful');
- $this->assertNotEquals('', $comment->getId(), 'Comment ID should not be empty');
- $this->assertNotEquals('0', $comment->getId(), 'Comment ID should not be string \'0\'');
- $this->assertNotNull($comment->getCreationDateTime(), 'Comment creation date should not be null');
-
- $loadedComment = $manager->get($comment->getId());
- $this->assertSame($comment->getMessage(), $loadedComment->getMessage(), 'Comment message should match');
- $this->assertEquals($comment->getCreationDateTime()->getTimestamp(), $loadedComment->getCreationDateTime()->getTimestamp(), 'Comment creation date should match');
- return $comment;
- }
-
- public static function providerTestSave(): array {
- return [
- ['very beautiful, I am impressed!', 'alice', 'comment', null],
- ];
- }
-
- public function testSaveUpdate(): void {
- $manager = $this->getManager();
- $comment = new Comment();
- $comment
- ->setActor('users', 'alice')
- ->setObject('files', 'file64')
- ->setMessage('very beautiful, I am impressed!')
- ->setVerb('comment')
- ->setExpireDate(new \DateTime('+2 hours'));
-
- $manager->save($comment);
-
- $loadedComment = $manager->get($comment->getId());
- // Compare current object with database values
- $this->assertSame($comment->getMessage(), $loadedComment->getMessage());
- $this->assertSame(
- $comment->getExpireDate()->format('Y-m-d H:i:s'),
- $loadedComment->getExpireDate()->format('Y-m-d H:i:s')
- );
-
- // Preserve the original comment to compare after update
- $original = clone $comment;
-
- // Update values
- $comment->setMessage('very beautiful, I am really so much impressed!')
- ->setExpireDate(new \DateTime('+1 hours'));
- $manager->save($comment);
-
- $loadedComment = $manager->get($comment->getId());
- // Compare current object with database values
- $this->assertSame($comment->getMessage(), $loadedComment->getMessage());
- $this->assertSame(
- $comment->getExpireDate()->format('Y-m-d H:i:s'),
- $loadedComment->getExpireDate()->format('Y-m-d H:i:s')
- );
-
- // Compare original object with database values
- $this->assertNotSame($original->getMessage(), $loadedComment->getMessage());
- $this->assertNotSame(
- $original->getExpireDate()->format('Y-m-d H:i:s'),
- $loadedComment->getExpireDate()->format('Y-m-d H:i:s')
- );
- }
-
- public function testSaveUpdateException(): void {
- $manager = $this->getManager();
- $comment = new Comment();
- $comment
- ->setActor('users', 'alice')
- ->setObject('files', 'file64')
- ->setMessage('very beautiful, I am impressed!')
- ->setVerb('comment');
-
- $manager->save($comment);
-
- $manager->delete($comment->getId());
-
- $comment->setMessage('very beautiful, I am really so much impressed!');
- $this->expectException(NotFoundException::class);
- $manager->save($comment);
- }
-
- public function testSaveIncomplete(): void {
-
- $manager = $this->getManager();
- $comment = new Comment();
- $comment->setMessage('from no one to nothing');
-
- $this->expectException(\UnexpectedValueException::class);
- $manager->save($comment);
- }
-
- public function testSaveAsChild(): void {
- $id = $this->addDatabaseEntry('0', '0');
-
- $manager = $this->getManager();
-
- for ($i = 0; $i < 3; $i++) {
- $comment = new Comment();
- $comment
- ->setActor('users', 'alice')
- ->setObject('files', 'file64')
- ->setParentId($id)
- ->setMessage('full ack')
- ->setVerb('comment')
- // setting the creation time avoids using sleep() while making sure to test with different timestamps
- ->setCreationDateTime(new \DateTime('+' . $i . ' minutes'));
-
- $manager->save($comment);
-
- $this->assertSame($id, $comment->getTopmostParentId());
- $parentComment = $manager->get($id);
- $this->assertSame($i + 1, $parentComment->getChildrenCount());
- $this->assertEquals($comment->getCreationDateTime()->getTimestamp(), $parentComment->getLatestChildDateTime()->getTimestamp());
- }
- }
-
- public static function invalidActorArgsProvider(): array {
- return
- [
- ['', ''],
- [1, 'alice'],
- ['users', 1],
- ];
- }
-
- #[DataProvider(methodName: 'invalidActorArgsProvider')]
- public function testDeleteReferencesOfActorInvalidInput(string|int $type, string|int $id): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $manager = $this->getManager();
- $manager->deleteReferencesOfActor($type, $id);
- }
-
- public function testDeleteReferencesOfActor(): void {
- $ids = [];
- $ids[] = $this->addDatabaseEntry('0', '0');
- $ids[] = $this->addDatabaseEntry('0', '0');
- $ids[] = $this->addDatabaseEntry('0', '0');
-
- $manager = $this->getManager();
-
- // just to make sure they are really set, with correct actor data
- $comment = $manager->get($ids[1]);
- $this->assertSame('users', $comment->getActorType());
- $this->assertSame('alice', $comment->getActorId());
-
- $wasSuccessful = $manager->deleteReferencesOfActor('users', 'alice');
- $this->assertTrue($wasSuccessful);
-
- foreach ($ids as $id) {
- $comment = $manager->get($id);
- $this->assertSame(ICommentsManager::DELETED_USER, $comment->getActorType());
- $this->assertSame(ICommentsManager::DELETED_USER, $comment->getActorId());
- }
-
- // actor info is gone from DB, but when database interaction is alright,
- // we still expect to get true back
- $wasSuccessful = $manager->deleteReferencesOfActor('users', 'alice');
- $this->assertTrue($wasSuccessful);
- }
-
- public function testDeleteReferencesOfActorWithUserManagement(): void {
- $user = Server::get(IUserManager::class)->createUser('xenia', 'NotAnEasyPassword123456+');
- $this->assertInstanceOf(IUser::class, $user);
-
- $manager = Server::get(ICommentsManager::class);
- $comment = $manager->create('users', $user->getUID(), 'files', 'file64');
- $comment
- ->setMessage('Most important comment I ever left on the Internet.')
- ->setVerb('comment');
- $status = $manager->save($comment);
- $this->assertTrue($status);
-
- $commentID = $comment->getId();
- $user->delete();
-
- $comment = $manager->get($commentID);
- $this->assertSame(ICommentsManager::DELETED_USER, $comment->getActorType());
- $this->assertSame(ICommentsManager::DELETED_USER, $comment->getActorId());
- }
-
- public static function invalidObjectArgsProvider(): array {
- return
- [
- ['', ''],
- [1, 'file64'],
- ['files', 1],
- ];
- }
-
- #[DataProvider(methodName: 'invalidObjectArgsProvider')]
- public function testDeleteCommentsAtObjectInvalidInput(string|int $type, string|int $id): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $manager = $this->getManager();
- $manager->deleteCommentsAtObject($type, $id);
- }
-
- public function testDeleteCommentsAtObject(): void {
- $ids = [];
- $ids[] = $this->addDatabaseEntry('0', '0');
- $ids[] = $this->addDatabaseEntry('0', '0');
- $ids[] = $this->addDatabaseEntry('0', '0');
-
- $manager = $this->getManager();
-
- // just to make sure they are really set, with correct actor data
- $comment = $manager->get($ids[1]);
- $this->assertSame('files', $comment->getObjectType());
- $this->assertSame('file64', $comment->getObjectId());
-
- $wasSuccessful = $manager->deleteCommentsAtObject('files', 'file64');
- $this->assertTrue($wasSuccessful);
-
- $verified = 0;
- foreach ($ids as $id) {
- try {
- $manager->get($id);
- } catch (NotFoundException) {
- $verified++;
- }
- }
- $this->assertSame(3, $verified);
-
- // actor info is gone from DB, but when database interaction is alright,
- // we still expect to get true back
- $wasSuccessful = $manager->deleteCommentsAtObject('files', 'file64');
- $this->assertTrue($wasSuccessful);
- }
-
- public function testDeleteCommentsExpiredAtObjectTypeAndId(): void {
- $ids = [];
- $ids[] = $this->addDatabaseEntry('0', '0', null, null, null, new \DateTime('+2 hours'));
- $ids[] = $this->addDatabaseEntry('0', '0', null, null, null, new \DateTime('+2 hours'));
- $ids[] = $this->addDatabaseEntry('0', '0', null, null, null, new \DateTime('+2 hours'));
- $ids[] = $this->addDatabaseEntry('0', '0', null, null, null, new \DateTime('-2 hours'));
- $ids[] = $this->addDatabaseEntry('0', '0', null, null, null, new \DateTime('-2 hours'));
- $ids[] = $this->addDatabaseEntry('0', '0', null, null, null, new \DateTime('-2 hours'));
-
- /** @psalm-suppress DeprecatedInterface No way around at the moment */
- $manager = new Manager(
- $this->connection,
- $this->createMock(LoggerInterface::class),
- $this->createMock(IConfig::class),
- Server::get(ITimeFactory::class),
- new EmojiHelper($this->connection),
- $this->createMock(IInitialStateService::class),
- $this->rootFolder,
- $this->createMock(IEventDispatcher::class)
- );
-
- // just to make sure they are really set, with correct actor data
- $comment = $manager->get($ids[1]);
- $this->assertSame('files', $comment->getObjectType());
- $this->assertSame('file64', $comment->getObjectId());
-
- $deleted = $manager->deleteCommentsExpiredAtObject('files', 'file64');
- $this->assertTrue($deleted);
-
- $deleted = 0;
- $exists = 0;
- foreach ($ids as $id) {
- try {
- $manager->get($id);
- $exists++;
- } catch (NotFoundException) {
- $deleted++;
- }
- }
- $this->assertSame(3, $exists);
- $this->assertSame(3, $deleted);
-
- // actor info is gone from DB, but when database interaction is alright,
- // we still expect to get true back
- $deleted = $manager->deleteCommentsExpiredAtObject('files', 'file64');
- $this->assertFalse($deleted);
- }
-
- public function testDeleteCommentsExpiredAtObjectType(): void {
- $ids = [];
- $ids[] = $this->addDatabaseEntry('0', '0', null, null, 'file1', new \DateTime('-2 hours'));
- $ids[] = $this->addDatabaseEntry('0', '0', null, null, 'file2', new \DateTime('-2 hours'));
- $ids[] = $this->addDatabaseEntry('0', '0', null, null, 'file3', new \DateTime('-2 hours'));
- $ids[] = $this->addDatabaseEntry('0', '0', null, null, 'file3', new \DateTime());
- $ids[] = $this->addDatabaseEntry('0', '0', null, null, 'file3', new \DateTime());
- $ids[] = $this->addDatabaseEntry('0', '0', null, null, 'file3', new \DateTime());
-
- /** @psalm-suppress DeprecatedInterface No way around at the moment */
- $manager = new Manager(
- $this->connection,
- $this->createMock(LoggerInterface::class),
- $this->createMock(IConfig::class),
- Server::get(ITimeFactory::class),
- new EmojiHelper($this->connection),
- $this->createMock(IInitialStateService::class),
- $this->rootFolder,
- $this->createMock(IEventDispatcher::class)
- );
-
- $deleted = $manager->deleteCommentsExpiredAtObject('files');
- $this->assertTrue($deleted);
-
- $deleted = 0;
- $exists = 0;
- foreach ($ids as $id) {
- try {
- $manager->get($id);
- $exists++;
- } catch (NotFoundException) {
- $deleted++;
- }
- }
- $this->assertSame(0, $exists);
- $this->assertSame(6, $deleted);
-
- // actor info is gone from DB, but when database interaction is alright,
- // we still expect to get true back
- $deleted = $manager->deleteCommentsExpiredAtObject('files');
- $this->assertFalse($deleted);
- }
-
- public function testSetMarkRead(): void {
- /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
- $user = $this->createMock(IUser::class);
- $user->expects($this->any())
- ->method('getUID')
- ->willReturn('alice');
-
- $dateTimeSet = new \DateTime();
-
- $manager = $this->getManager();
- $manager->setReadMark('robot', '36', $dateTimeSet, $user);
-
- $dateTimeGet = $manager->getReadMark('robot', '36', $user);
-
- $this->assertEquals($dateTimeSet->getTimestamp(), $dateTimeGet->getTimestamp());
- }
-
- public function testSetMarkReadUpdate(): void {
- /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
- $user = $this->createMock(IUser::class);
- $user->expects($this->any())
- ->method('getUID')
- ->willReturn('alice');
-
- $dateTimeSet = new \DateTime('yesterday');
-
- $manager = $this->getManager();
- $manager->setReadMark('robot', '36', $dateTimeSet, $user);
-
- $dateTimeSet = new \DateTime('today');
- $manager->setReadMark('robot', '36', $dateTimeSet, $user);
-
- $dateTimeGet = $manager->getReadMark('robot', '36', $user);
-
- $this->assertEquals($dateTimeSet, $dateTimeGet);
- }
-
- public function testReadMarkDeleteUser(): void {
- $user = $this->createMock(IUser::class);
- $user->expects($this->any())
- ->method('getUID')
- ->willReturn('alice');
-
- $dateTimeSet = new \DateTime();
-
- $manager = $this->getManager();
- $manager->setReadMark('robot', '36', $dateTimeSet, $user);
-
- $manager->deleteReadMarksFromUser($user);
- $dateTimeGet = $manager->getReadMark('robot', '36', $user);
-
- $this->assertNull($dateTimeGet);
- }
-
- public function testReadMarkDeleteObject(): void {
- $user = $this->createMock(IUser::class);
- $user->expects($this->any())
- ->method('getUID')
- ->willReturn('alice');
-
- $dateTimeSet = new \DateTime();
-
- $manager = $this->getManager();
- $manager->setReadMark('robot', '36', $dateTimeSet, $user);
-
- $manager->deleteReadMarksOnObject('robot', '36');
- $dateTimeGet = $manager->getReadMark('robot', '36', $user);
-
- $this->assertNull($dateTimeGet);
- }
-
- public function testSendEvent(): void {
- /** @psalm-suppress DeprecatedInterface Test for deprecated interface */
- $handler1 = $this->createMock(ICommentsEventHandler::class);
- $handler1->expects($this->exactly(4))
- ->method('handle');
-
- /** @psalm-suppress DeprecatedInterface Test for deprecated interface */
- $handler2 = $this->createMock(ICommentsEventHandler::class);
- $handler1->expects($this->exactly(4))
- ->method('handle');
-
- $manager = $this->getManager();
- $manager->registerEventHandler(function () use ($handler1) {
- return $handler1;
- });
- $manager->registerEventHandler(function () use ($handler2) {
- return $handler2;
- });
-
- $comment = new Comment();
- $comment
- ->setActor('users', 'alice')
- ->setObject('files', 'file64')
- ->setMessage('very beautiful, I am impressed!')
- ->setVerb('comment');
-
- // Add event
- $manager->save($comment);
-
- // Update event
- $comment->setMessage('Different topic');
- $manager->save($comment);
-
- // Delete event
- $manager->delete($comment->getId());
- }
-
- public function testResolveDisplayName(): void {
- $manager = $this->getManager();
-
- $planetClosure = function ($name) {
- return ucfirst($name);
- };
-
- $galaxyClosure = function ($name) {
- return strtoupper($name);
- };
-
- $manager->registerDisplayNameResolver('planet', $planetClosure);
- $manager->registerDisplayNameResolver('galaxy', $galaxyClosure);
-
- $this->assertSame('Neptune', $manager->resolveDisplayName('planet', 'neptune'));
- $this->assertSame('SOMBRERO', $manager->resolveDisplayName('galaxy', 'sombrero'));
- }
-
- public function testRegisterResolverDuplicate(): void {
- $this->expectException(\OutOfBoundsException::class);
-
- $manager = $this->getManager();
-
- $planetClosure = static fn (string $name): string => ucfirst($name);
- $manager->registerDisplayNameResolver('planet', $planetClosure);
- $manager->registerDisplayNameResolver('planet', $planetClosure);
- }
-
- public function testResolveDisplayNameUnregisteredType(): void {
- $this->expectException(\OutOfBoundsException::class);
-
- $manager = $this->getManager();
-
- $planetClosure = static fn (string $name): string => ucfirst($name);
- $manager->registerDisplayNameResolver('planet', $planetClosure);
- $manager->resolveDisplayName('galaxy', 'sombrero');
- }
-
- public function testResolveDisplayNameDirtyResolver(): void {
- $manager = $this->getManager();
-
- $planetClosure = static fn (): null => null;
- $manager->registerDisplayNameResolver('planet', $planetClosure);
- $this->assertIsString($manager->resolveDisplayName('planet', 'neptune'));
- }
-
- private function skipIfNotSupport4ByteUTF(): void {
- if (!$this->getManager()->supportReactions()) {
- $this->markTestSkipped('MySQL doesn\'t support 4 byte UTF-8');
- }
- }
-
- #[DataProvider(methodName: 'providerTestReactionAddAndDelete')]
- public function testReactionAddAndDelete(array $comments, array $reactionsExpected): void {
- $this->skipIfNotSupport4ByteUTF();
- $manager = $this->getManager();
-
- $processedComments = $this->proccessComments($comments);
- $comment = end($processedComments);
- if ($comment->getParentId()) {
- $parent = $manager->get($comment->getParentId());
- $this->assertEqualsCanonicalizing($reactionsExpected, $parent->getReactions());
- }
- }
-
- public static function providerTestReactionAddAndDelete(): array {
- return[
- [
- [
- ['message', 'alice', 'comment', null],
- ], [],
- ],
- [
- [
- ['message', 'alice', 'comment', null],
- ['👍', 'alice', 'reaction', 'message#alice'],
- ], ['👍' => 1],
- ],
- [
- [
- ['message', 'alice', 'comment', null],
- ['👍', 'alice', 'reaction', 'message#alice'],
- ['👍', 'alice', 'reaction', 'message#alice'],
- ], ['👍' => 1],
- ],
- [
- [
- ['message', 'alice', 'comment', null],
- ['👍', 'alice', 'reaction', 'message#alice'],
- ['👍', 'frank', 'reaction', 'message#alice'],
- ], ['👍' => 2],
- ],
- [
- [
- ['message', 'alice', 'comment', null],
- ['👍', 'alice', 'reaction', 'message#alice'],
- ['👍', 'frank', 'reaction', 'message#alice'],
- ['👍', 'frank', 'reaction_deleted', 'message#alice'],
- ], ['👍' => 1],
- ],
- [
- [
- ['message', 'alice', 'comment', null],
- ['👍', 'alice', 'reaction', 'message#alice'],
- ['👍', 'frank', 'reaction', 'message#alice'],
- ['👍', 'alice', 'reaction_deleted', 'message#alice'],
- ['👍', 'frank', 'reaction_deleted', 'message#alice'],
- ], [],
- ],
- ];
- }
-
- /**
- * @param array $data
- * @return array
- */
- private function proccessComments(array $data): array {
- $this->connection->beginTransaction();
- /** @var array $comments */
- $comments = [];
- foreach ($data as $comment) {
- [$message, $actorId, $verb, $parentText] = $comment;
- $parentId = null;
- if ($parentText) {
- $parentId = $comments[$parentText]->getId();
- }
- $id = '';
- if ($verb === 'reaction_deleted') {
- $id = $comments[$message . '#' . $actorId]->getId();
- }
- $comment = $this->testSave($message, $actorId, $verb, $parentId, $id);
- $comments[$comment->getMessage() . '#' . $comment->getActorId()] = $comment;
- }
- $this->connection->commit();
- return $comments;
- }
-
- #[DataProvider(methodName: 'providerTestRetrieveAllReactions')]
- public function testRetrieveAllReactions(array $comments, array $expected): void {
- $this->skipIfNotSupport4ByteUTF();
- $manager = $this->getManager();
-
- $processedComments = $this->proccessComments($comments);
- $comment = reset($processedComments);
- $this->invokePrivate($this, 'chunkSize', [50]);
- $all = $manager->retrieveAllReactions((int)$comment->getId());
- $actual = array_map(static function (IComment $row): array {
- return [
- $row->getActorId(),
- $row->getMessage(),
- ];
- }, $all);
-
- usort($actual, static fn (array $a, array $b): int => $a[1] <=> $b[1]);
- usort($expected, static fn (array $a, array $b): int => $a[1] <=> $b[1]);
-
- $this->assertEqualsCanonicalizing($expected, $actual);
- }
-
- public static function providerTestRetrieveAllReactions(): array {
- return [
- [
- [
- ['message', 'alice', 'comment', null],
- ],
- [],
- ],
- [
- [
- ['message', 'alice', 'comment', null],
- ['👍', 'alice', 'reaction', 'message#alice'],
- ['👍', 'frank', 'reaction', 'message#alice'],
- ],
- [
- ['👍', 'alice'],
- ['👍', 'frank'],
- ],
- ],
- [
- [
- ['message', 'alice', 'comment', null],
- ['👍', 'alice', 'reaction', 'message#alice'],
- ['👍', 'alice', 'reaction', 'message#alice'],
- ['👍', 'frank', 'reaction', 'message#alice'],
- ],
- [
- ['👍', 'alice'],
- ['👍', 'frank'],
- ],
- ],
- [# 60 reactions to cover chunk size when retrieve comments of reactions.
- [
- ['message', 'alice', 'comment', null],
- ['😀', 'alice', 'reaction', 'message#alice'],
- ['😃', 'alice', 'reaction', 'message#alice'],
- ['😄', 'alice', 'reaction', 'message#alice'],
- ['😁', 'alice', 'reaction', 'message#alice'],
- ['😆', 'alice', 'reaction', 'message#alice'],
- ['😅', 'alice', 'reaction', 'message#alice'],
- ['😂', 'alice', 'reaction', 'message#alice'],
- ['🤣', 'alice', 'reaction', 'message#alice'],
- ['🥲', 'alice', 'reaction', 'message#alice'],
- ['🥹', 'alice', 'reaction', 'message#alice'],
- ['☺️', 'alice', 'reaction', 'message#alice'],
- ['😊', 'alice', 'reaction', 'message#alice'],
- ['😇', 'alice', 'reaction', 'message#alice'],
- ['🙂', 'alice', 'reaction', 'message#alice'],
- ['🙃', 'alice', 'reaction', 'message#alice'],
- ['😉', 'alice', 'reaction', 'message#alice'],
- ['😌', 'alice', 'reaction', 'message#alice'],
- ['😍', 'alice', 'reaction', 'message#alice'],
- ['🥰', 'alice', 'reaction', 'message#alice'],
- ['😘', 'alice', 'reaction', 'message#alice'],
- ['😗', 'alice', 'reaction', 'message#alice'],
- ['😙', 'alice', 'reaction', 'message#alice'],
- ['😚', 'alice', 'reaction', 'message#alice'],
- ['😋', 'alice', 'reaction', 'message#alice'],
- ['😛', 'alice', 'reaction', 'message#alice'],
- ['😝', 'alice', 'reaction', 'message#alice'],
- ['😜', 'alice', 'reaction', 'message#alice'],
- ['🤪', 'alice', 'reaction', 'message#alice'],
- ['🤨', 'alice', 'reaction', 'message#alice'],
- ['🧐', 'alice', 'reaction', 'message#alice'],
- ],
- [
- ['😀', 'alice'],
- ['😃', 'alice'],
- ['😄', 'alice'],
- ['😁', 'alice'],
- ['😆', 'alice'],
- ['😅', 'alice'],
- ['😂', 'alice'],
- ['🤣', 'alice'],
- ['🥲', 'alice'],
- ['🥹', 'alice'],
- ['☺️', 'alice'],
- ['😊', 'alice'],
- ['😇', 'alice'],
- ['🙂', 'alice'],
- ['🙃', 'alice'],
- ['😉', 'alice'],
- ['😌', 'alice'],
- ['😍', 'alice'],
- ['🥰', 'alice'],
- ['😘', 'alice'],
- ['😗', 'alice'],
- ['😙', 'alice'],
- ['😚', 'alice'],
- ['😋', 'alice'],
- ['😛', 'alice'],
- ['😝', 'alice'],
- ['😜', 'alice'],
- ['🤪', 'alice'],
- ['🤨', 'alice'],
- ['🧐', 'alice'],
- ],
- ],
- ];
- }
-
- #[DataProvider(methodName: 'providerTestRetrieveAllReactionsWithSpecificReaction')]
- public function testRetrieveAllReactionsWithSpecificReaction(array $comments, string $reaction, array $expected): void {
- $this->skipIfNotSupport4ByteUTF();
- $manager = $this->getManager();
-
- $processedComments = $this->proccessComments($comments);
- $comment = reset($processedComments);
- $all = $manager->retrieveAllReactionsWithSpecificReaction((int)$comment->getId(), $reaction);
- $actual = array_map(static function (IComment $row): array {
- return [
- $row->getActorId(),
- $row->getMessage(),
- ];
- }, $all);
- $this->assertEqualsCanonicalizing($expected, $actual);
- }
-
- public static function providerTestRetrieveAllReactionsWithSpecificReaction(): array {
- return [
- [
- [
- ['message', 'alice', 'comment', null],
- ],
- '👎',
- [],
- ],
- [
- [
- ['message', 'alice', 'comment', null],
- ['👍', 'alice', 'reaction', 'message#alice'],
- ['👍', 'frank', 'reaction', 'message#alice'],
- ],
- '👍',
- [
- ['👍', 'alice'],
- ['👍', 'frank'],
- ],
- ],
- [
- [
- ['message', 'alice', 'comment', null],
- ['👍', 'alice', 'reaction', 'message#alice'],
- ['👎', 'alice', 'reaction', 'message#alice'],
- ['👍', 'frank', 'reaction', 'message#alice'],
- ],
- '👎',
- [
- ['👎', 'alice'],
- ],
- ],
- ];
- }
-
- #[DataProvider(methodName: 'providerTestGetReactionComment')]
- public function testGetReactionComment(array $comments, array $expected, bool $notFound): void {
- $this->skipIfNotSupport4ByteUTF();
- $manager = $this->getManager();
-
- $processedComments = $this->proccessComments($comments);
-
- $keys = ['message', 'actorId', 'verb', 'parent'];
- $expected = array_combine($keys, $expected);
-
- if ($notFound) {
- $this->expectException(NotFoundException::class);
- }
- $comment = $processedComments[$expected['message'] . '#' . $expected['actorId']];
- $actual = $manager->getReactionComment((int)$comment->getParentId(), $comment->getActorType(), $comment->getActorId(), $comment->getMessage());
- if (!$notFound) {
- $this->assertEquals($expected['message'], $actual->getMessage());
- $this->assertEquals($expected['actorId'], $actual->getActorId());
- $this->assertEquals($expected['verb'], $actual->getVerb());
- $this->assertEquals($processedComments[$expected['parent']]->getId(), $actual->getParentId());
- }
- }
-
- public static function providerTestGetReactionComment(): array {
- return [
- [
- [
- ['message', 'Matthew', 'comment', null],
- ['👍', 'Matthew', 'reaction', 'message#Matthew'],
- ['👍', 'Mark', 'reaction', 'message#Matthew'],
- ['👍', 'Luke', 'reaction', 'message#Matthew'],
- ['👍', 'John', 'reaction', 'message#Matthew'],
- ],
- ['👍', 'Matthew', 'reaction', 'message#Matthew'],
- false,
- ],
- [
- [
- ['message', 'Matthew', 'comment', null],
- ['👍', 'Matthew', 'reaction', 'message#Matthew'],
- ['👍', 'Mark', 'reaction', 'message#Matthew'],
- ['👍', 'Luke', 'reaction', 'message#Matthew'],
- ['👍', 'John', 'reaction', 'message#Matthew'],
- ],
- ['👍', 'Mark', 'reaction', 'message#Matthew'],
- false,
- ],
- [
- [
- ['message', 'Matthew', 'comment', null],
- ['👎', 'Matthew', 'reaction', 'message#Matthew'],
- ],
- ['👎', 'Matthew', 'reaction', 'message#Matthew'],
- false,
- ],
- [
- [
- ['message', 'Matthew', 'comment', null],
- ['👎', 'Matthew', 'reaction', 'message#Matthew'],
- ['👎', 'Matthew', 'reaction_deleted', 'message#Matthew'],
- ],
- ['👎', 'Matthew', 'reaction', 'message#Matthew'],
- true,
- ],
- ];
- }
-
- #[DataProvider(methodName: 'providerTestReactionMessageSize')]
- public function testReactionMessageSize(string $reactionString, bool $valid): void {
- $this->skipIfNotSupport4ByteUTF();
- if (!$valid) {
- $this->expectException(\UnexpectedValueException::class);
- }
-
- $manager = $this->getManager();
- $comment = new Comment();
- $comment->setMessage($reactionString)
- ->setVerb('reaction')
- ->setActor('users', 'alice')
- ->setObject('files', 'file64');
- $status = $manager->save($comment);
- $this->assertTrue($status);
- }
-
- public static function providerTestReactionMessageSize(): array {
- return [
- ['a', false],
- ['1', false],
- ['👍', true],
- ['👍👍', false],
- ['👍🏽', true],
- ['👨🏽💻', true],
- ['👨🏽💻👍', false],
- ];
- }
-
- #[DataProvider(methodName: 'providerTestReactionsSummarizeOrdered')]
- public function testReactionsSummarizeOrdered(array $comments, array $expected, bool $isFullMatch): void {
- $this->skipIfNotSupport4ByteUTF();
- $manager = $this->getManager();
-
- $processedComments = $this->proccessComments($comments);
- $comment = end($processedComments);
- $actual = $manager->get($comment->getParentId());
-
- if ($isFullMatch) {
- $this->assertSame($expected, $actual->getReactions());
- } else {
- $subResult = array_slice($actual->getReactions(), 0, count($expected));
- $this->assertSame($expected, $subResult);
- }
- }
-
- public static function providerTestReactionsSummarizeOrdered(): array {
- return [
- [
- [
- ['message', 'alice', 'comment', null],
- ['👍', 'alice', 'reaction', 'message#alice'],
- ],
- ['👍' => 1],
- true,
- ],
- [
- [
- ['message', 'alice', 'comment', null],
- ['👎', 'John', 'reaction', 'message#alice'],
- ['💼', 'Luke', 'reaction', 'message#alice'],
- ['📋', 'Luke', 'reaction', 'message#alice'],
- ['🚀', 'Luke', 'reaction', 'message#alice'],
- ['🖤', 'Luke', 'reaction', 'message#alice'],
- ['😜', 'Luke', 'reaction', 'message#alice'],
- ['🌖', 'Luke', 'reaction', 'message#alice'],
- ['💖', 'Luke', 'reaction', 'message#alice'],
- ['📥', 'Luke', 'reaction', 'message#alice'],
- ['🐉', 'Luke', 'reaction', 'message#alice'],
- ['☕', 'Luke', 'reaction', 'message#alice'],
- ['🐄', 'Luke', 'reaction', 'message#alice'],
- ['🐕', 'Luke', 'reaction', 'message#alice'],
- ['🐈', 'Luke', 'reaction', 'message#alice'],
- ['🛂', 'Luke', 'reaction', 'message#alice'],
- ['🕸', 'Luke', 'reaction', 'message#alice'],
- ['🏰', 'Luke', 'reaction', 'message#alice'],
- ['⚙️', 'Luke', 'reaction', 'message#alice'],
- ['🚨', 'Luke', 'reaction', 'message#alice'],
- ['👥', 'Luke', 'reaction', 'message#alice'],
- ['👍', 'Paul', 'reaction', 'message#alice'],
- ['👍', 'Peter', 'reaction', 'message#alice'],
- ['💜', 'Matthew', 'reaction', 'message#alice'],
- ['💜', 'Mark', 'reaction', 'message#alice'],
- ['💜', 'Luke', 'reaction', 'message#alice'],
- ],
- [
- '💜' => 3,
- '👍' => 2,
- ],
- false,
- ],
- ];
- }
-}
--- a/tests/lib/Config/LexiconTest.php
+++ /dev/null
@@ -1,262 +0,0 @@
-getRegistrationContext()?->registerConfigLexicon(TestConfigLexicon_I::APPID, TestConfigLexicon_I::class);
- $bootstrapCoordinator->getRegistrationContext()?->registerConfigLexicon(TestLexicon_N::APPID, TestLexicon_N::class);
- $bootstrapCoordinator->getRegistrationContext()?->registerConfigLexicon(TestLexicon_W::APPID, TestLexicon_W::class);
- $bootstrapCoordinator->getRegistrationContext()?->registerConfigLexicon(TestLexicon_E::APPID, TestLexicon_E::class);
-
- $this->appConfig = Server::get(IAppConfig::class);
- $this->userConfig = Server::get(IUserConfig::class);
- $this->configManager = Server::get(ConfigManager::class);
- $this->appManager = Server::get(IAppManager::class);
- }
-
- #[\Override]
- protected function tearDown(): void {
- parent::tearDown();
-
- $this->appConfig->deleteApp(TestConfigLexicon_I::APPID);
- $this->appConfig->deleteApp(TestLexicon_N::APPID);
- $this->appConfig->deleteApp(TestLexicon_W::APPID);
- $this->appConfig->deleteApp(TestLexicon_E::APPID);
- $this->appConfig->deleteApp(TestLexicon_UserIndexed::APPID);
- $this->appConfig->deleteApp(TestLexicon_UserIndexedRemove::APPID);
-
- $this->userConfig->deleteApp(TestConfigLexicon_I::APPID);
- $this->userConfig->deleteApp(TestLexicon_N::APPID);
- $this->userConfig->deleteApp(TestLexicon_W::APPID);
- $this->userConfig->deleteApp(TestLexicon_E::APPID);
- $this->userConfig->deleteApp(TestLexicon_UserIndexed::APPID);
- $this->userConfig->deleteApp(TestLexicon_UserIndexedRemove::APPID);
- }
-
- public function testAppLexiconSetCorrect() {
- $this->assertSame(true, $this->appConfig->setValueString(TestLexicon_E::APPID, 'key1', 'new_value'));
- $this->assertSame(true, $this->appConfig->isLazy(TestLexicon_E::APPID, 'key1'));
- $this->assertSame(true, $this->appConfig->isSensitive(TestLexicon_E::APPID, 'key1'));
- $this->appConfig->deleteKey(TestLexicon_E::APPID, 'key1');
- }
-
- public function testAppLexiconGetCorrect() {
- $this->assertSame('abcde', $this->appConfig->getValueString(TestLexicon_E::APPID, 'key1', 'default'));
- }
-
- public function testAppLexiconSetIncorrectValueType() {
- $this->expectException(AppConfigTypeConflictException::class);
- $this->appConfig->setValueInt(TestLexicon_E::APPID, 'key1', -1);
- }
-
- public function testAppLexiconGetIncorrectValueType() {
- $this->expectException(AppConfigTypeConflictException::class);
- $this->appConfig->getValueInt(TestLexicon_E::APPID, 'key1');
- }
-
- public function testAppLexiconIgnore() {
- $this->appConfig->setValueString(TestConfigLexicon_I::APPID, 'key_ignore', 'new_value');
- $this->assertSame('new_value', $this->appConfig->getValueString(TestConfigLexicon_I::APPID, 'key_ignore', ''));
- }
-
- public function testAppLexiconNotice() {
- $this->appConfig->setValueString(TestLexicon_N::APPID, 'key_notice', 'new_value');
- $this->assertSame('new_value', $this->appConfig->getValueString(TestLexicon_N::APPID, 'key_notice', ''));
- }
-
- public function testAppLexiconWarning() {
- $this->appConfig->setValueString(TestLexicon_W::APPID, 'key_warning', 'new_value');
- $this->assertSame('', $this->appConfig->getValueString(TestLexicon_W::APPID, 'key_warning', ''));
- }
-
- public function testAppLexiconSetException() {
- $this->expectException(AppConfigUnknownKeyException::class);
- $this->appConfig->setValueString(TestLexicon_E::APPID, 'key_exception', 'new_value');
- $this->assertSame('', $this->appConfig->getValueString(TestLexicon_E::APPID, 'key3', ''));
- }
-
- public function testAppLexiconGetException() {
- $this->expectException(AppConfigUnknownKeyException::class);
- $this->appConfig->getValueString(TestLexicon_E::APPID, 'key_exception');
- }
-
- public function testUserLexiconSetCorrect() {
- $this->assertSame(true, $this->userConfig->setValueString('user1', TestLexicon_E::APPID, 'key1', 'new_value'));
- $this->assertSame(true, $this->userConfig->isLazy('user1', TestLexicon_E::APPID, 'key1'));
- $this->assertSame(true, $this->userConfig->isSensitive('user1', TestLexicon_E::APPID, 'key1'));
- $this->userConfig->deleteKey(TestLexicon_E::APPID, 'key1');
- }
-
- public function testUserLexiconGetCorrect() {
- $this->assertSame('abcde', $this->userConfig->getValueString('user1', TestLexicon_E::APPID, 'key1', 'default'));
- }
-
- public function testUserLexiconSetIncorrectValueType() {
- $this->expectException(TypeConflictException::class);
- $this->userConfig->setValueInt('user1', TestLexicon_E::APPID, 'key1', -1);
- }
-
- public function testUserLexiconGetIncorrectValueType() {
- $this->expectException(TypeConflictException::class);
- $this->userConfig->getValueInt('user1', TestLexicon_E::APPID, 'key1');
- }
-
- public function testUserLexiconIgnore() {
- $this->userConfig->setValueString('user1', TestConfigLexicon_I::APPID, 'key_ignore', 'new_value');
- $this->assertSame('new_value', $this->userConfig->getValueString('user1', TestConfigLexicon_I::APPID, 'key_ignore', ''));
- }
-
- public function testUserLexiconNotice() {
- $this->userConfig->setValueString('user1', TestLexicon_N::APPID, 'key_notice', 'new_value');
- $this->assertSame('new_value', $this->userConfig->getValueString('user1', TestLexicon_N::APPID, 'key_notice', ''));
- }
-
- public function testUserLexiconWarning() {
- $this->userConfig->setValueString('user1', TestLexicon_W::APPID, 'key_warning', 'new_value');
- $this->assertSame('', $this->userConfig->getValueString('user1', TestLexicon_W::APPID, 'key_warning', ''));
- }
-
- public function testUserLexiconSetException() {
- $this->expectException(UnknownKeyException::class);
- $this->userConfig->setValueString('user1', TestLexicon_E::APPID, 'key_exception', 'new_value');
- $this->assertSame('', $this->userConfig->getValueString('user1', TestLexicon_E::APPID, 'key5', ''));
- }
-
- public function testUserLexiconGetException() {
- $this->expectException(UnknownKeyException::class);
- $this->userConfig->getValueString('user1', TestLexicon_E::APPID, 'key_exception');
- }
-
- public function testAppConfigLexiconRenameSetNewValue() {
- $this->assertSame(12345, $this->appConfig->getValueInt(TestConfigLexicon_I::APPID, 'key3', 123));
- $this->appConfig->setValueInt(TestConfigLexicon_I::APPID, 'old_key3', 994);
- $this->assertSame(994, $this->appConfig->getValueInt(TestConfigLexicon_I::APPID, 'key3', 123));
- }
-
- public function testAppConfigLexiconRenameSetOldValuePreMigration() {
- $this->appConfig->ignoreLexiconAliases(true);
- $this->appConfig->setValueInt(TestConfigLexicon_I::APPID, 'old_key3', 993);
- $this->appConfig->ignoreLexiconAliases(false);
- $this->assertSame(12345, $this->appConfig->getValueInt(TestConfigLexicon_I::APPID, 'key3', 123));
- }
-
- public function testAppConfigLexiconRenameSetOldValuePostMigration() {
- $this->appConfig->ignoreLexiconAliases(true);
- $this->appConfig->setValueInt(TestConfigLexicon_I::APPID, 'old_key3', 994);
- $this->appConfig->ignoreLexiconAliases(false);
- $this->configManager->migrateConfigLexiconKeys(TestConfigLexicon_I::APPID);
- $this->assertSame(994, $this->appConfig->getValueInt(TestConfigLexicon_I::APPID, 'key3', 123));
- }
-
- public function testAppConfigLexiconRenameGetNewValue() {
- $this->appConfig->setValueInt(TestConfigLexicon_I::APPID, 'key3', 981);
- $this->assertSame(981, $this->appConfig->getValueInt(TestConfigLexicon_I::APPID, 'old_key3', 123));
- }
-
- public function testAppConfigLexiconRenameGetOldValuePreMigration() {
- $this->appConfig->ignoreLexiconAliases(true);
- $this->appConfig->setValueInt(TestConfigLexicon_I::APPID, 'key3', 984);
- $this->assertSame(123, $this->appConfig->getValueInt(TestConfigLexicon_I::APPID, 'old_key3', 123));
- $this->appConfig->ignoreLexiconAliases(false);
- }
-
- public function testAppConfigLexiconRenameGetOldValuePostMigration() {
- $this->appConfig->ignoreLexiconAliases(true);
- $this->appConfig->setValueInt(TestConfigLexicon_I::APPID, 'key3', 987);
- $this->appConfig->ignoreLexiconAliases(false);
- $this->configManager->migrateConfigLexiconKeys(TestConfigLexicon_I::APPID);
- $this->assertSame(987, $this->appConfig->getValueInt(TestConfigLexicon_I::APPID, 'old_key3', 123));
- }
-
- public function testAppConfigLexiconRenameInvertBoolean() {
- $this->appConfig->ignoreLexiconAliases(true);
- $this->appConfig->setValueBool(TestConfigLexicon_I::APPID, 'old_key4', true);
- $this->appConfig->ignoreLexiconAliases(false);
- $this->assertSame(true, $this->appConfig->getValueBool(TestConfigLexicon_I::APPID, 'key4'));
- $this->configManager->migrateConfigLexiconKeys(TestConfigLexicon_I::APPID);
- $this->assertSame(false, $this->appConfig->getValueBool(TestConfigLexicon_I::APPID, 'key4'));
- }
-
- public function testLexiconIndexedUpdate() {
- $this->userConfig->setValueString('user1', TestLexicon_UserIndexed::APPID, 'key1', 'abcd');
- $this->userConfig->setValueString('user2', TestLexicon_UserIndexed::APPID, 'key1', '1234', flags: 64);
- $this->userConfig->setValueString('user3', TestLexicon_UserIndexed::APPID, 'key1', 'qwer', flags: IUserConfig::FLAG_INDEXED);
- $this->userConfig->setValueString('user4', TestLexicon_UserIndexed::APPID, 'key1', 'uiop', flags: 64 | IUserConfig::FLAG_INDEXED);
-
- $bootstrapCoordinator = Server::get(Coordinator::class);
- $bootstrapCoordinator->getRegistrationContext()?->registerConfigLexicon(TestLexicon_UserIndexed::APPID, TestLexicon_UserIndexed::class);
- $this->userConfig->clearCacheAll();
-
- $this->configManager->updateLexiconEntries(TestLexicon_UserIndexed::APPID);
-
- $this->assertTrue($this->userConfig->isIndexed('user1', TestLexicon_UserIndexed::APPID, 'key1'));
- $this->assertTrue($this->userConfig->isIndexed('user2', TestLexicon_UserIndexed::APPID, 'key1'));
- $this->assertTrue($this->userConfig->isIndexed('user3', TestLexicon_UserIndexed::APPID, 'key1'));
- $this->assertTrue($this->userConfig->isIndexed('user4', TestLexicon_UserIndexed::APPID, 'key1'));
-
- $this->assertSame(2, $this->userConfig->getValueFlags('user1', TestLexicon_UserIndexed::APPID, 'key1'));
- $this->assertSame(66, $this->userConfig->getValueFlags('user2', TestLexicon_UserIndexed::APPID, 'key1'));
- $this->assertSame(2, $this->userConfig->getValueFlags('user3', TestLexicon_UserIndexed::APPID, 'key1'));
- $this->assertSame(66, $this->userConfig->getValueFlags('user4', TestLexicon_UserIndexed::APPID, 'key1'));
- }
-
- public function testLexiconIndexedUpdateRemove() {
- $this->userConfig->setValueString('user1', TestLexicon_UserIndexedRemove::APPID, 'key1', 'abcd');
- $this->userConfig->setValueString('user2', TestLexicon_UserIndexedRemove::APPID, 'key1', '1234', flags: 64);
- $this->userConfig->setValueString('user3', TestLexicon_UserIndexedRemove::APPID, 'key1', 'qwer', flags: IUserConfig::FLAG_INDEXED);
- $this->userConfig->setValueString('user4', TestLexicon_UserIndexedRemove::APPID, 'key1', 'uiop', flags: 64 | IUserConfig::FLAG_INDEXED);
-
- $bootstrapCoordinator = Server::get(Coordinator::class);
- $bootstrapCoordinator->getRegistrationContext()?->registerConfigLexicon(TestLexicon_UserIndexedRemove::APPID, TestLexicon_UserIndexedRemove::class);
- $this->userConfig->clearCacheAll();
-
- $this->configManager->updateLexiconEntries(TestLexicon_UserIndexedRemove::APPID);
-
- $this->assertFalse($this->userConfig->isIndexed('user1', TestLexicon_UserIndexedRemove::APPID, 'key1'));
- $this->assertFalse($this->userConfig->isIndexed('user2', TestLexicon_UserIndexedRemove::APPID, 'key1'));
- $this->assertFalse($this->userConfig->isIndexed('user3', TestLexicon_UserIndexedRemove::APPID, 'key1'));
- $this->assertFalse($this->userConfig->isIndexed('user4', TestLexicon_UserIndexedRemove::APPID, 'key1'));
-
- $this->assertSame(0, $this->userConfig->getValueFlags('user1', TestLexicon_UserIndexedRemove::APPID, 'key1'));
- $this->assertSame(64, $this->userConfig->getValueFlags('user2', TestLexicon_UserIndexedRemove::APPID, 'key1'));
- $this->assertSame(0, $this->userConfig->getValueFlags('user3', TestLexicon_UserIndexedRemove::APPID, 'key1'));
- $this->assertSame(64, $this->userConfig->getValueFlags('user4', TestLexicon_UserIndexedRemove::APPID, 'key1'));
- }
-}
--- a/tests/lib/Config/TestConfigLexicon_I.php
+++ /dev/null
@@ -1,44 +0,0 @@
- match ($p) {
- Preset::FAMILY => 'family',
- Preset::CLUB, Preset::MEDIUM => 'club+medium',
- default => 'none',
- }, 'test key'),
- ];
- }
-
- #[\Override]
- public function getUserConfigs(): array {
- return [
- new Entry('key1', ValueType::STRING, 'abcde', 'test key', true, IUserConfig::FLAG_SENSITIVE),
- new Entry('key2', ValueType::INT, 12345, 'test key', false),
- new Entry('key3', ValueType::STRING, fn (Preset $p): string => match ($p) {
- Preset::FAMILY => 'family',
- Preset::CLUB, Preset::MEDIUM => 'club+medium',
- default => 'none',
- }, 'test key'),
- ];
- }
-}
--- a/tests/lib/Config/TestLexicon_N.php
+++ /dev/null
@@ -1,42 +0,0 @@
-connection = $this->createMock(IDBConnection::class);
- $this->config = $this->createMock(IConfig::class);
- $this->configManager = $this->createMock(ConfigManager::class);
- $this->presetManager = $this->createMock(PresetManager::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->crypto = $this->createMock(ICrypto::class);
- $this->dispatcher = $this->createMock(IEventDispatcher::class);
- }
-
- private function getUserConfig(): UserConfig {
- return new UserConfig(
- $this->connection,
- $this->config,
- $this->configManager,
- $this->presetManager,
- $this->logger,
- $this->crypto,
- $this->dispatcher,
- );
- }
-
- private function createInvalidFieldNameException(): DBException {
- $driverException = $this->createMock(\Doctrine\DBAL\Driver\Exception::class);
- $dbalException = new InvalidFieldNameException($driverException, null);
- return DbalException::wrap($dbalException);
- }
-
- private function createMockQueryBuilder(): IQueryBuilder&MockObject {
- $expression = $this->createMock(IExpressionBuilder::class);
- $qb = $this->createMock(IQueryBuilder::class);
- $qb->method('from')->willReturn($qb);
- $qb->method('select')->willReturn($qb);
- $qb->method('addSelect')->willReturn($qb);
- $qb->method('where')->willReturn($qb);
- $qb->method('andWhere')->willReturn($qb);
- $qb->method('set')->willReturn($qb);
- $qb->method('expr')->willReturn($expression);
- $qb->method('insert')->willReturn($qb);
- $qb->method('setValue')->willReturn($qb);
- $qb->method('createNamedParameter')->willReturn('?');
- return $qb;
- }
-
- /**
- * Test that loadConfig retries without new columns on InvalidFieldNameException.
- */
- public function testLoadConfigFallsBackOnMissingColumns(): void {
- $exception = $this->createInvalidFieldNameException();
-
- $successResult = $this->createMock(IResult::class);
- $successResult->method('fetchAllAssociative')->willReturn([
- ['appid' => 'settings', 'configkey' => 'email', 'configvalue' => 'user@example.com'],
- ]);
-
- $qb = $this->createMockQueryBuilder();
- $qb->method('executeQuery')
- ->willReturnOnConsecutiveCalls(
- $this->throwException($exception),
- $successResult,
- );
-
- $this->connection->method('getQueryBuilder')->willReturn($qb);
-
- $userConfig = $this->getUserConfig();
-
- $value = $userConfig->getValueString('user1', 'settings', 'email');
- $this->assertSame('user@example.com', $value);
- }
-
- /**
- * Test that non-INVALID_FIELD_NAME exceptions are re-thrown.
- */
- public function testLoadConfigRethrowsOtherExceptions(): void {
- $driverException = $this->createMock(\Doctrine\DBAL\Driver\Exception::class);
- $dbalException = new \Doctrine\DBAL\Exception\SyntaxErrorException($driverException, null);
- $exception = DbalException::wrap($dbalException);
-
- $qb = $this->createMockQueryBuilder();
- $qb->method('executeQuery')->willThrowException($exception);
-
- $this->connection->method('getQueryBuilder')->willReturn($qb);
-
- $userConfig = $this->getUserConfig();
-
- $this->expectException(DBException::class);
- $userConfig->getValueString('user1', 'settings', 'email');
- }
-
- /**
- * Test that insert omits new columns when migration is not completed.
- */
- public function testInsertOmitsNewColumnsInFallbackMode(): void {
- $exception = $this->createInvalidFieldNameException();
-
- $loadResult = $this->createMock(IResult::class);
- $loadResult->method('fetchAllAssociative')->willReturn([]);
-
- $qb = $this->createMockQueryBuilder();
-
- $callCount = 0;
- $qb->method('executeQuery')
- ->willReturnCallback(function () use ($exception, $loadResult, &$callCount) {
- $callCount++;
- if ($callCount === 1) {
- throw $exception;
- }
- return $loadResult;
- });
-
- $qb->expects(self::once())->method('insert')->with('preferences')->willReturn($qb);
- $qb->method('executeStatement')->willReturn(1);
-
- $setColumns = [];
- $qb->method('setValue')
- ->willReturnCallback(function (string $column) use ($qb, &$setColumns) {
- $setColumns[] = $column;
- return $qb;
- });
-
- $this->connection->method('getQueryBuilder')->willReturn($qb);
-
- $userConfig = $this->getUserConfig();
- $userConfig->setValueString('user1', 'settings', 'email', 'user@example.com');
-
- $this->assertContains('userid', $setColumns);
- $this->assertContains('appid', $setColumns);
- $this->assertContains('configkey', $setColumns);
- $this->assertContains('configvalue', $setColumns);
- $this->assertNotContains('lazy', $setColumns, 'lazy column should be omitted in fallback mode');
- $this->assertNotContains('type', $setColumns, 'type column should be omitted in fallback mode');
- $this->assertNotContains('flags', $setColumns, 'flags column should be omitted in fallback mode');
- $this->assertNotContains('indexed', $setColumns, 'indexed column should be omitted in fallback mode');
- }
-}
--- a/tests/lib/Config/UserConfigTest.php
+++ /dev/null
@@ -1,1696 +0,0 @@
->> [userId => [appId => prefKey, prefValue, valueType, lazy, sensitive]]]
- */
- private array $basePreferences
- = [
- 'user1'
- => [
- 'app1' => [
- 'key1' => ['key1', 'value1'],
- 'key22' => ['key22', '31'],
- 'fast_string' => ['fast_string', 'f_value', ValueType::STRING],
- 'lazy_string' => ['lazy_string', 'l_value', ValueType::STRING, true],
- 'fast_string_sensitive' => [
- 'fast_string_sensitive', 'fs_value', ValueType::STRING, false, UserConfig::FLAG_SENSITIVE
- ],
- 'lazy_string_sensitive' => [
- 'lazy_string_sensitive', 'ls_value', ValueType::STRING, true, UserConfig::FLAG_SENSITIVE
- ],
- 'fast_int' => ['fast_int', 11, ValueType::INT],
- 'lazy_int' => ['lazy_int', 12, ValueType::INT, true],
- 'fast_int_sensitive' => ['fast_int_sensitive', 2024, ValueType::INT, false, UserConfig::FLAG_SENSITIVE],
- 'lazy_int_sensitive' => ['lazy_int_sensitive', 2048, ValueType::INT, true, UserConfig::FLAG_SENSITIVE],
- 'fast_float' => ['fast_float', 3.14, ValueType::FLOAT],
- 'lazy_float' => ['lazy_float', 3.14159, ValueType::FLOAT, true],
- 'fast_float_sensitive' => [
- 'fast_float_sensitive', 1.41, ValueType::FLOAT, false, UserConfig::FLAG_SENSITIVE
- ],
- 'lazy_float_sensitive' => [
- 'lazy_float_sensitive', 1.4142, ValueType::FLOAT, true, UserConfig::FLAG_SENSITIVE
- ],
- 'fast_array' => ['fast_array', ['year' => 2024], ValueType::ARRAY],
- 'lazy_array' => ['lazy_array', ['month' => 'October'], ValueType::ARRAY, true],
- 'fast_array_sensitive' => [
- 'fast_array_sensitive', ['password' => 'pwd'], ValueType::ARRAY, false, UserConfig::FLAG_SENSITIVE
- ],
- 'lazy_array_sensitive' => [
- 'lazy_array_sensitive', ['password' => 'qwerty'], ValueType::ARRAY, true, UserConfig::FLAG_SENSITIVE
- ],
- 'fast_boolean' => ['fast_boolean', true, ValueType::BOOL],
- 'fast_boolean_0' => ['fast_boolean_0', false, ValueType::BOOL],
- 'lazy_boolean' => ['lazy_boolean', true, ValueType::BOOL, true],
- 'lazy_boolean_0' => ['lazy_boolean_0', false, ValueType::BOOL, true],
- ],
- 'app2' => [
- 'key2' => ['key2', 'value2a', ValueType::STRING, false, 0, true],
- 'key3' => ['key3', 'value3', ValueType::STRING, true],
- 'key4' => ['key4', 'value4', ValueType::STRING, false, UserConfig::FLAG_SENSITIVE],
- 'key8' => ['key8', 11, ValueType::INT, false, 0, true],
- 'key9' => ['key9', 'value9a', ValueType::STRING],
- ],
- 'app3' => [
- 'key1' => ['key1', 'value123'],
- 'key3' => ['key3', 'value3'],
- 'key8' => ['key8', 12, ValueType::INT, false, UserConfig::FLAG_SENSITIVE, true],
- 'key9' => ['key9', 'value9b', ValueType::STRING, false, UserConfig::FLAG_SENSITIVE],
- 'key10' => ['key10', true, ValueType::BOOL, false, 0, true],
- ],
- 'only-lazy' => [
- 'key1' => ['key1', 'value456', ValueType::STRING, true, 0, true],
- 'key2' => ['key2', 'value2c', ValueType::STRING, true, UserConfig::FLAG_SENSITIVE],
- 'key3' => ['key3', 42, ValueType::INT, true],
- 'key4' => ['key4', 17.42, ValueType::FLOAT, true],
- 'key5' => ['key5', true, ValueType::BOOL, true],
- ]
- ],
- 'user2'
- => [
- 'app1' => [
- '1' => ['1', 'value1'],
- '2' => ['2', 'value2', ValueType::STRING, true, UserConfig::FLAG_SENSITIVE],
- '3' => ['3', 17, ValueType::INT, true],
- '4' => ['4', 42, ValueType::INT, false, UserConfig::FLAG_SENSITIVE],
- '5' => ['5', 17.42, ValueType::FLOAT, false],
- '6' => ['6', true, ValueType::BOOL, false],
- ],
- 'app2' => [
- 'key2' => ['key2', 'value2b', ValueType::STRING, false, 0, true],
- 'key3' => ['key3', 'value3', ValueType::STRING, true],
- 'key4' => ['key4', 'value4', ValueType::STRING, false, UserConfig::FLAG_SENSITIVE],
- 'key8' => ['key8', 12, ValueType::INT, false, 0, true],
- ],
- 'app3' => [
- 'key10' => ['key10', false, ValueType::BOOL, false, 0, true],
- ],
- 'only-lazy' => [
- 'key1' => ['key1', 'value1', ValueType::STRING, true, 0, true]
- ]
- ],
- 'user3'
- => [
- 'app2' => [
- 'key2' => ['key2', 'value2c', ValueType::MIXED, false, 0, true],
- 'key3' => ['key3', 'value3', ValueType::STRING, true, ],
- 'key4' => ['key4', 'value4', ValueType::STRING, false, UserConfig::FLAG_SENSITIVE],
- 'fast_string_sensitive' => [
- 'fast_string_sensitive', 'fs_value', ValueType::STRING, false, UserConfig::FLAG_SENSITIVE
- ],
- 'lazy_string_sensitive' => [
- 'lazy_string_sensitive', 'ls_value', ValueType::STRING, true, UserConfig::FLAG_SENSITIVE
- ],
- ],
- 'only-lazy' => [
- 'key3' => ['key3', 'value3', ValueType::STRING, true]
- ]
- ],
- 'user4'
- => [
- 'app2' => [
- 'key1' => ['key1', 'value1'],
- 'key2' => ['key2', 'value2A', ValueType::MIXED, false, 0, true],
- 'key3' => ['key3', 'value3', ValueType::STRING, true,],
- 'key4' => ['key4', 'value4', ValueType::STRING, false, UserConfig::FLAG_SENSITIVE],
- ],
- 'app3' => [
- 'key10' => ['key10', true, ValueType::BOOL, false, 0, true],
- ],
- 'only-lazy' => [
- 'key1' => ['key1', 123, ValueType::INT, true, 0, true]
- ]
- ],
- 'user5'
- => [
- 'app1' => [
- 'key1' => ['key1', 'value1']
- ],
- 'app2' => [
- 'key8' => ['key8', 12, ValueType::INT, false, 0, true]
- ],
- 'only-lazy' => [
- 'key1' => ['key1', 'value1', ValueType::STRING, true, 0, true]
- ]
- ],
- ];
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- $this->connection = Server::get(IDBConnection::class);
- $this->config = Server::get(IConfig::class);
- $this->configManager = Server::get(ConfigManager::class);
- $this->presetManager = Server::get(PresetManager::class);
- $this->logger = Server::get(LoggerInterface::class);
- $this->crypto = Server::get(ICrypto::class);
- $this->dispatcher = Server::get(IEventDispatcher::class);
-
- // storing current preferences and emptying the data table
- $sql = $this->connection->getQueryBuilder();
- $sql->select('*')
- ->from('preferences');
- $result = $sql->executeQuery();
- $this->originalPreferences = $result->fetchAllAssociative();
- $result->closeCursor();
-
- $sql = $this->connection->getQueryBuilder();
- $sql->delete('preferences');
- $sql->executeStatement();
-
- $sql = $this->connection->getQueryBuilder();
- $sql->insert('preferences')
- ->values(
- [
- 'userid' => $sql->createParameter('userid'),
- 'appid' => $sql->createParameter('appid'),
- 'configkey' => $sql->createParameter('configkey'),
- 'configvalue' => $sql->createParameter('configvalue'),
- 'type' => $sql->createParameter('type'),
- 'lazy' => $sql->createParameter('lazy'),
- 'flags' => $sql->createParameter('flags'),
- 'indexed' => $sql->createParameter('indexed')
- ]
- );
-
- foreach ($this->basePreferences as $userId => $userData) {
- foreach ($userData as $appId => $appData) {
- foreach ($appData as $key => $row) {
- $value = $row[1];
- $type = ($row[2] ?? ValueType::MIXED)->value;
-
- if ($type === ValueType::ARRAY->value) {
- $value = json_encode($value);
- }
-
- if ($type === ValueType::BOOL->value && $value === false) {
- $value = '0';
- }
-
- $flags = $row[4] ?? 0;
- if ((UserConfig::FLAG_SENSITIVE & $flags) !== 0) {
- if (!isset($this->basePreferences[$userId][$appId][$key]['encrypted'])) {
- $value = self::invokePrivate(UserConfigEntry::class, 'ENCRYPTION_PREFIX')
- . $this->crypto->encrypt((string)$value);
- $this->basePreferences[$userId][$appId][$key]['encrypted'] = $value;
- } else {
- $value = $this->basePreferences[$userId][$appId][$key]['encrypted'];
- }
- } else {
- $indexed = (($row[5] ?? false) === true) ? $value : '';
- }
-
- $sql->setParameters(
- [
- 'userid' => $userId,
- 'appid' => $appId,
- 'configkey' => $row[0],
- 'configvalue' => $value,
- 'type' => $type,
- 'lazy' => (($row[3] ?? false) === true) ? 1 : 0,
- 'flags' => $flags,
- 'indexed' => $indexed ?? ''
- ]
- )->executeStatement();
- }
- }
- }
- }
-
- #[\Override]
- protected function tearDown(): void {
- $sql = $this->connection->getQueryBuilder();
- $sql->delete('preferences');
- $sql->executeStatement();
-
- $sql = $this->connection->getQueryBuilder();
- $sql->insert('preferences')
- ->values(
- [
- 'userid' => $sql->createParameter('userid'),
- 'appid' => $sql->createParameter('appid'),
- 'configkey' => $sql->createParameter('configkey'),
- 'configvalue' => $sql->createParameter('configvalue'),
- 'lazy' => $sql->createParameter('lazy'),
- 'type' => $sql->createParameter('type'),
- ]
- );
-
- foreach ($this->originalPreferences as $key => $configs) {
- $sql->setParameter('userid', $configs['userid'])
- ->setParameter('appid', $configs['appid'])
- ->setParameter('configkey', $configs['configkey'])
- ->setParameter('configvalue', $configs['configvalue'])
- ->setParameter('lazy', ($configs['lazy'] === '1') ? '1' : '0')
- ->setParameter('type', $configs['type']);
- $sql->executeStatement();
- }
-
- parent::tearDown();
- }
-
- /**
- * @param array $preLoading preload the 'fast' cache for a list of users)
- *
- * @return IUserConfig
- */
- private function generateUserConfig(array $preLoading = []): IUserConfig {
- $userConfig = new UserConfig(
- $this->connection,
- $this->config,
- $this->configManager,
- $this->presetManager,
- $this->logger,
- $this->crypto,
- $this->dispatcher
- );
- $msg = ' generateUserConfig() failed to confirm cache status';
-
- // confirm cache status
- $status = $userConfig->statusCache();
- $this->assertSame([], $status['fastCache'], $msg);
- $this->assertSame([], $status['lazyCache'], $msg);
- foreach ($preLoading as $preLoadUser) {
- // simple way to initiate the load of non-lazy preferences values in cache
- $userConfig->getValueString($preLoadUser, 'core', 'preload');
-
- // confirm cache status
- $status = $userConfig->statusCache();
- $this->assertTrue(isset($status['fastCache'][$preLoadUser]), $msg);
- $this->assertFalse(isset($status['lazyCache'][$preLoadUser]), $msg);
-
- $apps = array_values(array_diff(array_keys($this->basePreferences[$preLoadUser]), ['only-lazy']));
- $this->assertEqualsCanonicalizing($apps, array_keys($status['fastCache'][$preLoadUser]), $msg);
- $this->assertSame([], $status['lazyCache'][$preLoadUser] ?? [], $msg);
- }
-
- return $userConfig;
- }
-
- public function testGetUserIdsEmpty(): void {
- $userConfig = $this->generateUserConfig();
- $this->assertEqualsCanonicalizing(array_keys($this->basePreferences), $userConfig->getUserIds());
- }
-
- public function testGetUserIds(): void {
- $userConfig = $this->generateUserConfig();
- $this->assertEqualsCanonicalizing(['user1', 'user2', 'user5'], $userConfig->getUserIds('app1'));
- }
-
- public function testGetApps(): void {
- $userConfig = $this->generateUserConfig();
- $this->assertEqualsCanonicalizing(
- array_keys($this->basePreferences['user1']), $userConfig->getApps('user1')
- );
- }
-
- public function testGetKeys(): void {
- $userConfig = $this->generateUserConfig(['user1']);
- $this->assertEqualsCanonicalizing(
- array_keys($this->basePreferences['user1']['app1']), $userConfig->getKeys('user1', 'app1')
- );
- }
-
- public static function providerHasKey(): array {
- return [
- ['user1', 'app1', 'key1', false, true],
- ['user0', 'app1', 'key1', false, false],
- ['user1', 'app1', 'key1', true, false],
- ['user1', 'app1', 'key0', false, false],
- ['user1', 'app1', 'key0', true, false],
- ['user1', 'app1', 'fast_string_sensitive', false, true],
- ['user1', 'app1', 'lazy_string_sensitive', true, true],
- ['user2', 'only-lazy', 'key1', false, false],
- ['user2', 'only-lazy', 'key1', true, true],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerHasKey')]
- public function testHasKey(string $userId, string $appId, string $key, ?bool $lazy, bool $result): void {
- $userConfig = $this->generateUserConfig();
- $this->assertEquals($result, $userConfig->hasKey($userId, $appId, $key, $lazy));
- }
-
- public static function providerIsSensitive(): array {
- return [
- ['user1', 'app1', 'key1', false, false, false],
- ['user0', 'app1', 'key1', false, false, true],
- ['user1', 'app1', 'key1', true, false, true],
- ['user1', 'app1', 'key1', null, false, false],
- ['user1', 'app1', 'key0', false, false, true],
- ['user1', 'app1', 'key0', true, false, true],
- ['user1', 'app1', 'fast_string_sensitive', false, true, false],
- ['user1', 'app1', 'lazy_string_sensitive', true, true, false],
- ['user1', 'app1', 'fast_string_sensitive', true, true, true],
- ['user1', 'app1', 'lazy_string_sensitive', false, true, true],
- ['user1', 'app1', 'lazy_string_sensitive', null, true, false],
- ['user2', 'only-lazy', 'key1', false, false, true],
- ['user2', 'only-lazy', 'key1', true, false, false],
- ['user2', 'only-lazy', 'key1', null, false, false],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerIsSensitive')]
- public function testIsSensitive(
- string $userId,
- string $appId,
- string $key,
- ?bool $lazy,
- bool $result,
- bool $exception,
- ): void {
- $userConfig = $this->generateUserConfig();
- if ($exception) {
- $this->expectException(UnknownKeyException::class);
- }
-
- $this->assertEquals($result, $userConfig->isSensitive($userId, $appId, $key, $lazy));
- }
-
- public static function providerIsLazy(): array {
- return [
- ['user1', 'app1', 'key1', false, false],
- ['user0', 'app1', 'key1', false, true],
- ['user1', 'app1', 'key0', false, true],
- ['user1', 'app1', 'key0', false, true],
- ['user1', 'app1', 'fast_string_sensitive', false, false],
- ['user1', 'app1', 'lazy_string_sensitive', true, false],
- ['user2', 'only-lazy', 'key1', true, false],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerIsLazy')]
- public function testIsLazy(
- string $userId,
- string $appId,
- string $key,
- bool $result,
- bool $exception,
- ): void {
- $userConfig = $this->generateUserConfig();
- if ($exception) {
- $this->expectException(UnknownKeyException::class);
- }
-
- $this->assertEquals($result, $userConfig->isLazy($userId, $appId, $key));
- }
-
- public static function providerGetValues(): array {
- return [
- [
- 'user1', 'app1', '', true,
- [
- 'fast_array' => ['year' => 2024],
- 'fast_array_sensitive' => '***REMOVED SENSITIVE VALUE***',
- 'fast_boolean' => true,
- 'fast_boolean_0' => false,
- 'fast_float' => 3.14,
- 'fast_float_sensitive' => '***REMOVED SENSITIVE VALUE***',
- 'fast_int' => 11,
- 'fast_int_sensitive' => '***REMOVED SENSITIVE VALUE***',
- 'fast_string' => 'f_value',
- 'fast_string_sensitive' => '***REMOVED SENSITIVE VALUE***',
- 'key1' => 'value1',
- 'key22' => '31',
- 'lazy_array' => ['month' => 'October'],
- 'lazy_array_sensitive' => '***REMOVED SENSITIVE VALUE***',
- 'lazy_boolean' => true,
- 'lazy_boolean_0' => false,
- 'lazy_float' => 3.14159,
- 'lazy_float_sensitive' => '***REMOVED SENSITIVE VALUE***',
- 'lazy_int' => 12,
- 'lazy_int_sensitive' => '***REMOVED SENSITIVE VALUE***',
- 'lazy_string' => 'l_value',
- 'lazy_string_sensitive' => '***REMOVED SENSITIVE VALUE***',
- ]
- ],
- [
- 'user1', 'app1', '', false,
- [
- 'fast_array' => ['year' => 2024],
- 'fast_array_sensitive' => ['password' => 'pwd'],
- 'fast_boolean' => true,
- 'fast_boolean_0' => false,
- 'fast_float' => 3.14,
- 'fast_float_sensitive' => 1.41,
- 'fast_int' => 11,
- 'fast_int_sensitive' => 2024,
- 'fast_string' => 'f_value',
- 'fast_string_sensitive' => 'fs_value',
- 'key1' => 'value1',
- 'key22' => '31',
- 'lazy_array' => ['month' => 'October'],
- 'lazy_array_sensitive' => ['password' => 'qwerty'],
- 'lazy_boolean' => true,
- 'lazy_boolean_0' => false,
- 'lazy_float' => 3.14159,
- 'lazy_float_sensitive' => 1.4142,
- 'lazy_int' => 12,
- 'lazy_int_sensitive' => 2048,
- 'lazy_string' => 'l_value',
- 'lazy_string_sensitive' => 'ls_value'
- ]
- ],
- [
- 'user1', 'app1', 'fast_', true,
- [
- 'fast_array' => ['year' => 2024],
- 'fast_array_sensitive' => '***REMOVED SENSITIVE VALUE***',
- 'fast_boolean' => true,
- 'fast_boolean_0' => false,
- 'fast_float' => 3.14,
- 'fast_float_sensitive' => '***REMOVED SENSITIVE VALUE***',
- 'fast_int' => 11,
- 'fast_int_sensitive' => '***REMOVED SENSITIVE VALUE***',
- 'fast_string' => 'f_value',
- 'fast_string_sensitive' => '***REMOVED SENSITIVE VALUE***',
- ]
- ],
- [
- 'user1', 'app1', 'fast_', false,
- [
- 'fast_array' => ['year' => 2024],
- 'fast_array_sensitive' => ['password' => 'pwd'],
- 'fast_boolean' => true,
- 'fast_boolean_0' => false,
- 'fast_float' => 3.14,
- 'fast_float_sensitive' => 1.41,
- 'fast_int' => 11,
- 'fast_int_sensitive' => 2024,
- 'fast_string' => 'f_value',
- 'fast_string_sensitive' => 'fs_value',
- ]
- ],
- [
- 'user1', 'app1', 'key1', true,
- [
- 'key1' => 'value1',
- ]
- ],
- [
- 'user2', 'app1', '', false,
- [
- '1' => 'value1',
- '4' => 42,
- '5' => 17.42,
- '6' => true,
- '2' => 'value2',
- '3' => 17,
- ]
- ],
- [
- 'user2', 'app1', '', true,
- [
- '1' => 'value1',
- '4' => '***REMOVED SENSITIVE VALUE***',
- '5' => 17.42,
- '6' => true,
- '2' => '***REMOVED SENSITIVE VALUE***',
- '3' => 17,
- ]
- ],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerGetValues')]
- public function testGetValues(
- string $userId,
- string $appId,
- string $prefix,
- bool $filtered,
- array $result,
- ): void {
- $userConfig = $this->generateUserConfig();
- $this->assertJsonStringEqualsJsonString(
- json_encode($result), json_encode($userConfig->getValues($userId, $appId, $prefix, $filtered))
- );
- }
-
- public static function providerGetAllValues(): array {
- return [
- [
- 'user2', false,
- [
- 'app1' => [
- '1' => 'value1',
- '4' => 42,
- '5' => 17.42,
- '6' => true,
- '2' => 'value2',
- '3' => 17,
- ],
- 'app2' => [
- 'key2' => 'value2b',
- 'key3' => 'value3',
- 'key4' => 'value4',
- 'key8' => 12,
- ],
- 'app3' => [
- 'key10' => false,
- ],
- 'only-lazy' => [
- 'key1' => 'value1',
- ]
- ],
- ],
- [
- 'user2', true,
- [
- 'app1' => [
- '1' => 'value1',
- '4' => '***REMOVED SENSITIVE VALUE***',
- '5' => 17.42,
- '6' => true,
- '2' => '***REMOVED SENSITIVE VALUE***',
- '3' => 17,
- ],
- 'app2' => [
- 'key2' => 'value2b',
- 'key3' => 'value3',
- 'key4' => '***REMOVED SENSITIVE VALUE***',
- 'key8' => 12,
- ],
- 'app3' => [
- 'key10' => false,
- ],
- 'only-lazy' => [
- 'key1' => 'value1',
- ]
- ],
- ],
- [
- 'user3', true,
- [
- 'app2' => [
- 'key2' => 'value2c',
- 'key3' => 'value3',
- 'key4' => '***REMOVED SENSITIVE VALUE***',
- 'fast_string_sensitive' => '***REMOVED SENSITIVE VALUE***',
- 'lazy_string_sensitive' => '***REMOVED SENSITIVE VALUE***',
- ],
- 'only-lazy' => [
- 'key3' => 'value3',
- ]
- ],
- ],
- [
- 'user3', false,
- [
- 'app2' => [
- 'key2' => 'value2c',
- 'key3' => 'value3',
- 'key4' => 'value4',
- 'fast_string_sensitive' => 'fs_value',
- 'lazy_string_sensitive' => 'ls_value',
- ],
- 'only-lazy' => [
- 'key3' => 'value3',
- ]
- ],
- ],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAllValues')]
- public function testGetAllValues(
- string $userId,
- bool $filtered,
- array $result,
- ): void {
- $userConfig = $this->generateUserConfig();
- $this->assertEqualsCanonicalizing($result, $userConfig->getAllValues($userId, $filtered));
- }
-
- public static function providerSearchValuesByApps(): array {
- return [
- [
- 'user1', 'key1', false, null,
- [
- 'app1' => 'value1',
- 'app3' => 'value123'
- ]
- ],
- [
- 'user1', 'key1', true, null,
- [
- 'only-lazy' => 'value456'
- ]
- ],
- [
- 'user1', 'key8', false, null,
- [
- 'app2' => 11,
- 'app3' => 12,
- ]
- ],
- [
- 'user1', 'key9', false, ValueType::INT,
- [
- 'app2' => 0,
- 'app3' => 0,
- ]
- ]
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerSearchValuesByApps')]
- public function testSearchValuesByApps(
- string $userId,
- string $key,
- bool $lazy,
- ?ValueType $typedAs,
- array $result,
- ): void {
- $userConfig = $this->generateUserConfig();
- $this->assertEquals($result, $userConfig->getValuesByApps($userId, $key, $lazy, $typedAs));
- }
-
- public static function providerSearchValuesByUsers(): array {
- return [
- [
- 'app2', 'key2', null, null,
- [
- 'user1' => 'value2a',
- 'user2' => 'value2b',
- 'user3' => 'value2c',
- 'user4' => 'value2A'
- ]
- ],
- [
- 'app2', 'key2', null, ['user1', 'user3'],
- [
- 'user1' => 'value2a',
- 'user3' => 'value2c',
- ]
- ],
- [
- 'app2', 'key2', ValueType::INT, ['user1', 'user3'],
- [
- 'user1' => 0,
- 'user3' => 0,
- ]
- ],
- [
- 'app2', 'key8', ValueType::INT, null,
- [
- 'user1' => 11,
- 'user2' => 12,
- 'user5' => 12,
- ]
- ],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerSearchValuesByUsers')]
- public function testSearchValuesByUsers(
- string $app,
- string $key,
- ?ValueType $typedAs,
- ?array $userIds,
- array $result,
- ): void {
- $userConfig = $this->generateUserConfig();
- $this->assertEqualsCanonicalizing(
- $result, $userConfig->getValuesByUsers($app, $key, $typedAs, $userIds)
- );
- }
-
- public static function providerSearchValuesByValueString(): array {
- return [
- ['app2', 'key2', 'value2a', false, ['user1']],
- ['app2', 'key2', 'value2A', false, ['user4']],
- ['app2', 'key2', 'value2A', true, ['user1', 'user4']]
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerSearchValuesByValueString')]
- public function testSearchUsersByValueString(
- string $app,
- string $key,
- string|array $value,
- bool $ci,
- array $result,
- ): void {
- $userConfig = $this->generateUserConfig();
- $this->assertEqualsCanonicalizing($result, iterator_to_array($userConfig->searchUsersByValueString($app, $key, $value, $ci)));
- }
-
- public static function providerSearchValuesByValueInt(): array {
- return [
- ['app3', 'key8', 12, []], // sensitive value, cannot search
- ['app2', 'key8', 12, ['user2', 'user5']], // sensitive value, cannot search
- ['only-lazy', 'key1', 123, ['user4']],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerSearchValuesByValueInt')]
- public function testSearchUsersByValueInt(
- string $app,
- string $key,
- int $value,
- array $result,
- ): void {
- $userConfig = $this->generateUserConfig();
- $this->assertEqualsCanonicalizing($result, iterator_to_array($userConfig->searchUsersByValueInt($app, $key, $value)));
- }
-
- public static function providerSearchValuesByValues(): array {
- return [
- ['app2', 'key2', ['value2a', 'value2b'], ['user1', 'user2']],
- ['app2', 'key2', ['value2a', 'value2c'], ['user1', 'user3']],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerSearchValuesByValues')]
- public function testSearchUsersByValues(
- string $app,
- string $key,
- array $values,
- array $result,
- ): void {
- $userConfig = $this->generateUserConfig();
- $this->assertEqualsCanonicalizing($result, iterator_to_array($userConfig->searchUsersByValues($app, $key, $values)));
- }
-
- public static function providerSearchValuesByValueBool(): array {
- return [
- ['app3', 'key10', true, ['user1', 'user4']],
- ['app3', 'key10', false, ['user2']],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerSearchValuesByValueBool')]
- public function testSearchUsersByValueBool(
- string $app,
- string $key,
- bool $value,
- array $result,
- ): void {
- $userConfig = $this->generateUserConfig();
- $this->assertEqualsCanonicalizing($result, iterator_to_array($userConfig->searchUsersByValueBool($app, $key, $value)));
- }
-
- private array $getValueMixed = [
- [['user1'], 'user1', 'app1', 'key0', 'default_because_unknown_key', true, 'default_because_unknown_key'],
- [['user1'], 'user1', 'app1', 'key0', 'default_because_unknown_key', false, 'default_because_unknown_key'],
- [['user1'], 'user1', 'app1', 'fast_string', 'default_because_unknown_key', false, 'f_value'],
- [['user1'], 'user1', 'app1', 'fast_string', 'default_because_unknown_key', true, 'f_value'],
- [['user1'], 'user1', 'app1', 'lazy_string', 'default_because_unknown_key', false, 'default_because_unknown_key'],
- [['user1'], 'user1', 'app1', 'lazy_string', 'default_because_unknown_key', true, 'l_value'],
- [['user1'], 'user1', 'app1', 'fast_string_sensitive', 'default_because_unknown_key', false, 'fs_value'],
- [['user1'], 'user1', 'app1', 'fast_string_sensitive', 'default_because_unknown_key', true, 'fs_value'],
- [['user1'], 'user1', 'app1', 'lazy_string_sensitive', 'default_because_unknown_key', false, 'default_because_unknown_key'],
- [['user1'], 'user1', 'app1', 'lazy_string_sensitive', 'default_because_unknown_key', true, 'ls_value'],
-
- [null, 'user1', 'app1', 'key0', 'default_because_unknown_key', true, 'default_because_unknown_key'],
- [null, 'user1', 'app1', 'key0', 'default_because_unknown_key', false, 'default_because_unknown_key'],
- [null, 'user1', 'app1', 'fast_string', 'default_because_unknown_key', false, 'f_value'],
- // because non-lazy are already loaded
- [null, 'user1', 'app1', 'fast_string', 'default_because_unknown_key', true, 'default_because_unknown_key'],
- [null, 'user1', 'app1', 'lazy_string', 'default_because_unknown_key', false, 'default_because_unknown_key'],
- [null, 'user1', 'app1', 'lazy_string', 'default_because_unknown_key', true, 'l_value'],
- [null, 'user1', 'app1', 'fast_string_sensitive', 'default_because_unknown_key', false, 'fs_value'],
- [null, 'user1', 'app1', 'fast_string_sensitive', 'default_because_unknown_key', true, 'default_because_unknown_key'],
- [null, 'user1', 'app1', 'lazy_string_sensitive', 'default_because_unknown_key', false, 'default_because_unknown_key'],
- [null, 'user1', 'app1', 'lazy_string_sensitive', 'default_because_unknown_key', true, 'ls_value'],
- ];
-
- public function testGetValueMixed(): void {
- foreach ($this->getValueMixed as $value) {
- [$preload, $userId, $app, $key, $default, $lazy, $result] = $value;
- $userConfig = $this->generateUserConfig($preload ?? []);
- $this->assertEquals($result, $userConfig->getValueMixed($userId, $app, $key, $default, $lazy));
- }
- }
-
- public function testGetValueString(): void {
- foreach ($this->getValueMixed as $value) {
- [$preload, $userId, $app, $key, $default, $lazy, $result] = $value;
- $userConfig = $this->generateUserConfig($preload ?? []);
- $this->assertEquals($result, $userConfig->getValueString($userId, $app, $key, $default, $lazy));
- }
- }
-
- private array $getValueInt = [
- [['user1'], 'user1', 'app1', 'key0', 54321, true, 54321],
- [['user1'], 'user1', 'app1', 'key0', 54321, false, 54321],
- [['user1'], 'user1', 'app1', 'fast_int', 54321, false, 11],
- [['user1'], 'user1', 'app1', 'fast_int', 54321, true, 11],
- [['user1'], 'user1', 'app1', 'fast_int_sensitive', 54321, false, 2024],
- [['user1'], 'user1', 'app1', 'fast_int_sensitive', 54321, true, 2024],
- [['user1'], 'user1', 'app1', 'lazy_int', 54321, false, 54321],
- [['user1'], 'user1', 'app1', 'lazy_int', 54321, true, 12],
- [['user1'], 'user1', 'app1', 'lazy_int_sensitive', 54321, false, 54321],
- [['user1'], 'user1', 'app1', 'lazy_int_sensitive', 54321, true, 2048],
- [null, 'user1', 'app1', 'key0', 54321, true, 54321],
- [null, 'user1', 'app1', 'key0', 54321, false, 54321],
- [null, 'user1', 'app1', 'key22', 54321, false, 31],
- [null, 'user1', 'app1', 'fast_int', 54321, false, 11],
- [null, 'user1', 'app1', 'fast_int', 54321, true, 54321],
- [null, 'user1', 'app1', 'fast_int_sensitive', 54321, false, 2024],
- [null, 'user1', 'app1', 'fast_int_sensitive', 54321, true, 54321],
- [null, 'user1', 'app1', 'lazy_int', 54321, false, 54321],
- [null, 'user1', 'app1', 'lazy_int', 54321, true, 12],
- [null, 'user1', 'app1', 'lazy_int_sensitive', 54321, false, 54321],
- [null, 'user1', 'app1', 'lazy_int_sensitive', 54321, true, 2048],
- ];
-
- public function testGetValueInt(): void {
- foreach ($this->getValueInt as $value) {
- [$preload, $userId, $app, $key, $default, $lazy, $result] = $value;
- $userConfig = $this->generateUserConfig($preload ?? []);
- $this->assertEquals($result, $userConfig->getValueInt($userId, $app, $key, $default, $lazy));
- }
- }
-
- private array $getValueFloat = [
- [['user1'], 'user1', 'app1', 'key0', 54.321, true, 54.321],
- [null, 'user1', 'app1', 'key0', 54.321, true, 54.321],
- [['user1'], 'user1', 'app1', 'key0', 54.321, false, 54.321],
- [null, 'user1', 'app1', 'key0', 54.321, false, 54.321],
- [['user1'], 'user1', 'app1', 'fast_float', 54.321, false, 3.14],
- [null, 'user1', 'app1', 'fast_float', 54.321, false, 3.14],
- [['user1'], 'user1', 'app1', 'fast_float', 54.321, true, 3.14],
- [null, 'user1', 'app1', 'fast_float', 54.321, true, 54.321],
- [['user1'], 'user1', 'app1', 'fast_float_sensitive', 54.321, false, 1.41],
- [null, 'user1', 'app1', 'fast_float_sensitive', 54.321, false, 1.41],
- [['user1'], 'user1', 'app1', 'fast_float_sensitive', 54.321, true, 1.41],
- [null, 'user1', 'app1', 'fast_float_sensitive', 54.321, true, 54.321],
- [['user1'], 'user1', 'app1', 'lazy_float', 54.321, false, 54.321],
- [null, 'user1', 'app1', 'lazy_float', 54.321, false, 54.321],
- [['user1'], 'user1', 'app1', 'lazy_float', 54.321, true, 3.14159],
- [null, 'user1', 'app1', 'lazy_float', 54.321, true, 3.14159],
- [['user1'], 'user1', 'app1', 'lazy_float_sensitive', 54.321, false, 54.321],
- [null, 'user1', 'app1', 'lazy_float_sensitive', 54.321, false, 54.321],
- [['user1'], 'user1', 'app1', 'lazy_float_sensitive', 54.321, true, 1.4142],
- [null, 'user1', 'app1', 'lazy_float_sensitive', 54.321, true, 1.4142],
- ];
-
- public function testGetValueFloat(): void {
- foreach ($this->getValueFloat as $value) {
- [$preload, $userId, $app, $key, $default, $lazy, $result] = $value;
- $userConfig = $this->generateUserConfig($preload ?? []);
- $this->assertEquals($result, $userConfig->getValueFloat($userId, $app, $key, $default, $lazy));
- }
- }
-
- private array $getValueBool = [
- [['user1'], 'user1', 'app1', 'key0', false, true, false],
- [null, 'user1', 'app1', 'key0', false, true, false],
- [['user1'], 'user1', 'app1', 'key0', true, true, true],
- [null, 'user1', 'app1', 'key0', true, true, true],
- [['user1'], 'user1', 'app1', 'key0', false, false, false],
- [null, 'user1', 'app1', 'key0', false, false, false],
- [['user1'], 'user1', 'app1', 'key0', true, false, true],
- [null, 'user1', 'app1', 'key0', true, false, true],
- [['user1'], 'user1', 'app1', 'fast_boolean', false, false, true],
- [null, 'user1', 'app1', 'fast_boolean', false, false, true],
- [['user1'], 'user1', 'app1', 'fast_boolean_0', false, false, false],
- [null, 'user1', 'app1', 'fast_boolean_0', false, false, false],
- [['user1'], 'user1', 'app1', 'fast_boolean', true, false, true],
- [null, 'user1', 'app1', 'fast_boolean', true, false, true],
- [['user1'], 'user1', 'app1', 'fast_boolean_0', true, false, false],
- [null, 'user1', 'app1', 'fast_boolean_0', true, false, false],
- [['user1'], 'user1', 'app1', 'fast_boolean', false, true, true],
- [null, 'user1', 'app1', 'fast_boolean', false, true, false],
- [['user1'], 'user1', 'app1', 'fast_boolean_0', false, true, false],
- [null, 'user1', 'app1', 'fast_boolean_0', false, true, false],
- [['user1'], 'user1', 'app1', 'fast_boolean', true, true, true],
- [null, 'user1', 'app1', 'fast_boolean', true, true, true],
- [['user1'], 'user1', 'app1', 'fast_boolean_0', true, true, false],
- [null, 'user1', 'app1', 'fast_boolean_0', true, true, true],
- [['user1'], 'user1', 'app1', 'lazy_boolean', false, false, false],
- [null, 'user1', 'app1', 'lazy_boolean', false, false, false],
- [['user1'], 'user1', 'app1', 'lazy_boolean_0', false, false, false],
- [null, 'user1', 'app1', 'lazy_boolean_0', false, false, false],
- [['user1'], 'user1', 'app1', 'lazy_boolean', true, false, true],
- [null, 'user1', 'app1', 'lazy_boolean', true, false, true],
- [['user1'], 'user1', 'app1', 'lazy_boolean_0', true, false, true],
- [null, 'user1', 'app1', 'lazy_boolean_0', true, false, true],
- [['user1'], 'user1', 'app1', 'lazy_boolean', false, true, true],
- [null, 'user1', 'app1', 'lazy_boolean', false, true, true],
- [['user1'], 'user1', 'app1', 'lazy_boolean_0', false, true, false],
- [null, 'user1', 'app1', 'lazy_boolean_0', false, true, false],
- [['user1'], 'user1', 'app1', 'lazy_boolean', true, true, true],
- [null, 'user1', 'app1', 'lazy_boolean', true, true, true],
- [['user1'], 'user1', 'app1', 'lazy_boolean_0', true, true, false],
- [null, 'user1', 'app1', 'lazy_boolean_0', true, true, false],
- ];
-
- public function testGetValueBool(): void {
- foreach ($this->getValueBool as $value) {
- [$preload, $userId, $app, $key, $default, $lazy, $result] = $value;
- $userConfig = $this->generateUserConfig($preload ?? []);
- $this->assertEquals($result, $userConfig->getValueBool($userId, $app, $key, $default, $lazy));
- }
- }
-
- private array $getValueArray = [
- [['user1'], 'user1', 'app1', 'key0', ['default_because_unknown_key'], true, ['default_because_unknown_key']],
- [null, 'user1', 'app1', 'key0', ['default_because_unknown_key'], true, ['default_because_unknown_key']],
- [['user1'], 'user1', 'app1', 'key0', ['default_because_unknown_key'], false, ['default_because_unknown_key']],
- [null, 'user1', 'app1', 'key0', ['default_because_unknown_key'], false, ['default_because_unknown_key']],
- ];
-
- public function testGetValueArray(): void {
- foreach ($this->getValueArray as $value) {
- [$preload, $userId, $app, $key, $default, $lazy, $result] = $value;
- $userConfig = $this->generateUserConfig($preload ?? []);
- $this->assertEquals($result, $userConfig->getValueArray($userId, $app, $key, $default, $lazy));
- }
- }
-
- public function testGetValueType(): void {
- $getValueType = [
- [null, 'user1', 'app1', 'key1', false, ValueType::MIXED],
- [null, 'user1', 'app1', 'key1', true, null, UnknownKeyException::class],
- [null, 'user1', 'app1', 'fast_string', true, ValueType::STRING, UnknownKeyException::class],
- [['user1'], 'user1', 'app1', 'fast_string', true, ValueType::STRING],
- [null, 'user1', 'app1', 'fast_string', false, ValueType::STRING],
- [null, 'user1', 'app1', 'lazy_string', true, ValueType::STRING],
- [null, 'user1', 'app1', 'lazy_string', false, ValueType::STRING, UnknownKeyException::class],
- [
- null, 'user1', 'app1', 'fast_string_sensitive', true, ValueType::STRING,
- UnknownKeyException::class
- ],
- [['user1'], 'user1', 'app1', 'fast_string_sensitive', true, ValueType::STRING],
- [null, 'user1', 'app1', 'fast_string_sensitive', false, ValueType::STRING],
- [null, 'user1', 'app1', 'lazy_string_sensitive', true, ValueType::STRING],
- [
- null, 'user1', 'app1', 'lazy_string_sensitive', false, ValueType::STRING,
- UnknownKeyException::class
- ],
- [null, 'user1', 'app1', 'fast_int', true, ValueType::INT, UnknownKeyException::class],
- [['user1'], 'user1', 'app1', 'fast_int', true, ValueType::INT],
- [null, 'user1', 'app1', 'fast_int', false, ValueType::INT],
- [null, 'user1', 'app1', 'lazy_int', true, ValueType::INT],
- [null, 'user1', 'app1', 'lazy_int', false, ValueType::INT, UnknownKeyException::class],
- [null, 'user1', 'app1', 'fast_float', true, ValueType::FLOAT, UnknownKeyException::class],
- [['user1'], 'user1', 'app1', 'fast_float', true, ValueType::FLOAT],
- [null, 'user1', 'app1', 'fast_float', false, ValueType::FLOAT],
- [null, 'user1', 'app1', 'lazy_float', true, ValueType::FLOAT],
- [null, 'user1', 'app1', 'lazy_float', false, ValueType::FLOAT, UnknownKeyException::class],
- [null, 'user1', 'app1', 'fast_boolean', true, ValueType::BOOL, UnknownKeyException::class],
- [['user1'], 'user1', 'app1', 'fast_boolean', true, ValueType::BOOL],
- [null, 'user1', 'app1', 'fast_boolean', false, ValueType::BOOL],
- [null, 'user1', 'app1', 'lazy_boolean', true, ValueType::BOOL],
- [null, 'user1', 'app1', 'lazy_boolean', false, ValueType::BOOL, UnknownKeyException::class],
- ];
-
- foreach ($getValueType as $value) {
- [$preload, $userId, $app, $key, $lazy, $result] = $value;
- $exception = $value[6] ?? null;
- $userConfig = $this->generateUserConfig($preload ?? []);
- if ($exception !== null) {
- $this->expectException($exception);
- }
-
- $type = $userConfig->getValueType($userId, $app, $key, $lazy);
- if ($exception === null) {
- $this->assertEquals($result->value, $type->value);
- }
- }
- }
-
- public static function providerSetValueMixed(): array {
- return [
- [null, 'user1', 'app1', 'key1', 'value', false, false, true],
- [null, 'user1', 'app1', 'key1', '12345', true, false, true],
- [null, 'user1', 'app1', 'key1', '12345', true, true, true],
- [null, 'user1', 'app1', 'key1', 'value1', false, false, false],
- [null, 'user1', 'app1', 'key1', 'value1', true, false, true],
- [null, 'user1', 'app1', 'key1', 'value1', false, true, true],
- [
- null, 'user1', 'app1', 'fast_string', 'f_value_2', false, false, true,
- TypeConflictException::class
- ],
- [
- null, 'user1', 'app1', 'fast_string', 'f_value', true, false, true,
- TypeConflictException::class
- ],
- [null, 'user1', 'app1', 'fast_string', 'f_value', true, true, true, TypeConflictException::class],
- [null, 'user1', 'app1', 'fast_int', 'n_value', false, false, true, TypeConflictException::class],
- [
- null, 'user1', 'app1', 'fast_float', 'n_value', false, false, true,
- TypeConflictException::class
- ],
- [
- null, 'user1', 'app1', 'lazy_string', 'l_value_2', false, false, true,
- TypeConflictException::class
- ],
- [null, 'user1', 'app1', 'lazy_string', 'l_value', true, false, false],
- [null, 'user1', 'app1', 'lazy_string', 'l_value', true, true, true, TypeConflictException::class],
- [null, 'user1', 'app1', 'lazy_int', 'l_value', false, false, true, TypeConflictException::class],
- [
- null, 'user1', 'app1', 'lazy_float', 'l_value', false, false, true,
- TypeConflictException::class
- ],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerSetValueMixed')]
- public function testSetValueMixed(
- ?array $preload,
- string $userId,
- string $app,
- string $key,
- string $value,
- bool $lazy,
- bool $sensitive,
- bool $result,
- ?string $exception = null,
- ): void {
- $userConfig = $this->generateUserConfig($preload ?? []);
- if ($exception !== null) {
- $this->expectException($exception);
- }
-
- $edited = $userConfig->setValueMixed($userId, $app, $key, $value, $lazy, ($sensitive) ? 1 : 0);
-
- if ($exception === null) {
- $this->assertEquals($result, $edited);
- }
- }
-
- /**
- * This test needs to stay! Emails are expected to be lowercase due to performance reasons.
- * This way we can skip the expensive casing change on the database.
- */
- public function testSetValueMixedWithSettingsEmail(): void {
- $userConfig = $this->generateUserConfig();
-
- $edited = $userConfig->setValueMixed('user1', 'settings', 'email', 'mixed.CASE@Nextcloud.com');
- $this->assertTrue($edited);
-
- $actual = $userConfig->getValueMixed('user1', 'settings', 'email');
- $this->assertEquals('mixed.case@nextcloud.com', $actual);
- }
-
- public static function providerSetValueString(): array {
- return [
- [null, 'user1', 'app1', 'key1', 'value', false, false, true],
- [null, 'user1', 'app1', 'key1', '12345', true, false, true],
- [null, 'user1', 'app1', 'key1', '12345', true, true, true],
- [null, 'user1', 'app1', 'key1', 'value1', false, false, false],
- [null, 'user1', 'app1', 'key1', 'value1', true, false, true],
- [null, 'user1', 'app1', 'key1', 'value1', false, true, true],
- [null, 'user1', 'app1', 'fast_string', 'f_value_2', false, false, true],
- [null, 'user1', 'app1', 'fast_string', 'f_value', false, false, false],
- [null, 'user1', 'app1', 'fast_string', 'f_value', true, false, true],
- [null, 'user1', 'app1', 'fast_string', 'f_value', true, true, true],
- [null, 'user1', 'app1', 'lazy_string', 'l_value_2', false, false, true],
- [null, 'user1', 'app1', 'lazy_string', 'l_value', true, false, false],
- [null, 'user1', 'app1', 'lazy_string', 'l_value', true, true, true],
- [null, 'user1', 'app1', 'fast_string_sensitive', 'fs_value', false, true, false],
- [null, 'user1', 'app1', 'fast_string_sensitive', 'fs_value', true, true, true],
- [null, 'user1', 'app1', 'fast_string_sensitive', 'fs_value', true, false, true],
- [null, 'user1', 'app1', 'lazy_string_sensitive', 'ls_value', false, true, true],
- [null, 'user1', 'app1', 'lazy_string_sensitive', 'ls_value', true, true, false],
- [null, 'user1', 'app1', 'lazy_string_sensitive', 'ls_value', true, false, false],
- [null, 'user1', 'app1', 'lazy_string_sensitive', 'ls_value_2', true, false, true],
- [null, 'user1', 'app1', 'fast_int', 'n_value', false, false, true, TypeConflictException::class],
- [
- null, 'user1', 'app1', 'fast_float', 'n_value', false, false, true,
- TypeConflictException::class
- ],
- [
- null, 'user1', 'app1', 'fast_float', 'n_value', false, false, true,
- TypeConflictException::class
- ],
- [null, 'user1', 'app1', 'lazy_int', 'n_value', false, false, true, TypeConflictException::class],
- [
- null, 'user1', 'app1', 'lazy_boolean', 'n_value', false, false, true,
- TypeConflictException::class
- ],
- [
- null, 'user1', 'app1', 'lazy_float', 'n_value', false, false, true,
- TypeConflictException::class
- ],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerSetValueString')]
- public function testSetValueString(
- ?array $preload,
- string $userId,
- string $app,
- string $key,
- string $value,
- bool $lazy,
- bool $sensitive,
- bool $result,
- ?string $exception = null,
- ): void {
- $userConfig = $this->generateUserConfig($preload ?? []);
- if ($exception !== null) {
- $this->expectException($exception);
- }
-
- $edited = $userConfig->setValueString($userId, $app, $key, $value, $lazy, ($sensitive) ? 1 : 0);
- if ($exception !== null) {
- return;
- }
-
- $this->assertEquals($result, $edited);
- if ($result) {
- $this->assertEquals($value, $userConfig->getValueString($userId, $app, $key, $value, $lazy));
- $userConfig = $this->generateUserConfig($preload ?? []);
- $this->assertEquals($value, $userConfig->getValueString($userId, $app, $key, $value, $lazy));
- }
- }
-
- public static function providerSetValueInt(): array {
- return [
- [null, 'user1', 'app1', 'key1', 12345, false, false, true],
- [null, 'user1', 'app1', 'key1', 12345, true, false, true],
- [null, 'user1', 'app1', 'key1', 12345, true, true, true],
- [null, 'user1', 'app1', 'fast_int', 11, false, false, false],
- [null, 'user1', 'app1', 'fast_int', 111, false, false, true],
- [null, 'user1', 'app1', 'fast_int', 111, true, false, true],
- [null, 'user1', 'app1', 'fast_int', 111, false, true, true],
- [null, 'user1', 'app1', 'fast_int', 11, true, false, true],
- [null, 'user1', 'app1', 'fast_int', 11, false, true, true],
- [null, 'user1', 'app1', 'lazy_int', 12, false, false, true],
- [null, 'user1', 'app1', 'lazy_int', 121, false, false, true],
- [null, 'user1', 'app1', 'lazy_int', 121, true, false, true],
- [null, 'user1', 'app1', 'lazy_int', 121, false, true, true],
- [null, 'user1', 'app1', 'lazy_int', 12, true, false, false],
- [null, 'user1', 'app1', 'lazy_int', 12, false, true, true],
- [null, 'user1', 'app1', 'fast_string', 12345, false, false, true, TypeConflictException::class],
- [null, 'user1', 'app1', 'fast_string', 12345, false, false, false, TypeConflictException::class],
- [null, 'user1', 'app1', 'fast_string', 12345, true, false, true, TypeConflictException::class],
- [null, 'user1', 'app1', 'fast_string', 12345, true, true, true, TypeConflictException::class],
- [null, 'user1', 'app1', 'lazy_string', 12345, false, false, true, TypeConflictException::class],
- [null, 'user1', 'app1', 'lazy_string', 12345, true, false, false, TypeConflictException::class],
- [null, 'user1', 'app1', 'lazy_string', 12345, true, true, true, TypeConflictException::class],
- [null, 'user1', 'app1', 'fast_float', 12345, false, false, true, TypeConflictException::class],
- [null, 'user1', 'app1', 'fast_float', 12345, false, false, true, TypeConflictException::class],
- [null, 'user1', 'app1', 'lazy_boolean', 12345, false, false, true, TypeConflictException::class],
- [null, 'user1', 'app1', 'lazy_float', 12345, false, false, true, TypeConflictException::class],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerSetValueInt')]
- public function testSetValueInt(
- ?array $preload,
- string $userId,
- string $app,
- string $key,
- int $value,
- bool $lazy,
- bool $sensitive,
- bool $result,
- ?string $exception = null,
- ): void {
- $userConfig = $this->generateUserConfig($preload ?? []);
- if ($exception !== null) {
- $this->expectException($exception);
- }
-
- $edited = $userConfig->setValueInt($userId, $app, $key, $value, $lazy, ($sensitive) ? 1 : 0);
-
- if ($exception !== null) {
- return;
- }
-
- $this->assertEquals($result, $edited);
- if ($result) {
- $this->assertEquals($value, $userConfig->getValueInt($userId, $app, $key, $value, $lazy));
- $userConfig = $this->generateUserConfig($preload ?? []);
- $this->assertEquals($value, $userConfig->getValueInt($userId, $app, $key, $value, $lazy));
- }
- }
-
- public static function providerSetValueFloat(): array {
- return [
- [null, 'user1', 'app1', 'key1', 12.345, false, false, true],
- [null, 'user1', 'app1', 'key1', 12.345, true, false, true],
- [null, 'user1', 'app1', 'key1', 12.345, true, true, true],
- [null, 'user1', 'app1', 'fast_float', 3.14, false, false, false],
- [null, 'user1', 'app1', 'fast_float', 3.15, false, false, true],
- [null, 'user1', 'app1', 'fast_float', 3.15, true, false, true],
- [null, 'user1', 'app1', 'fast_float', 3.15, false, true, true],
- [null, 'user1', 'app1', 'fast_float', 3.14, true, false, true],
- [null, 'user1', 'app1', 'fast_float', 3.14, false, true, true],
- [null, 'user1', 'app1', 'lazy_float', 3.14159, false, false, true],
- [null, 'user1', 'app1', 'lazy_float', 3.14158, false, false, true],
- [null, 'user1', 'app1', 'lazy_float', 3.14158, true, false, true],
- [null, 'user1', 'app1', 'lazy_float', 3.14158, false, true, true],
- [null, 'user1', 'app1', 'lazy_float', 3.14159, true, false, false],
- [null, 'user1', 'app1', 'lazy_float', 3.14159, false, true, true],
- [null, 'user1', 'app1', 'fast_string', 12.345, false, false, true, TypeConflictException::class],
- [null, 'user1', 'app1', 'fast_string', 12.345, false, false, false, TypeConflictException::class],
- [null, 'user1', 'app1', 'fast_string', 12.345, true, false, true, TypeConflictException::class],
- [null, 'user1', 'app1', 'fast_string', 12.345, true, true, true, TypeConflictException::class],
- [null, 'user1', 'app1', 'lazy_string', 12.345, false, false, true, TypeConflictException::class],
- [null, 'user1', 'app1', 'lazy_string', 12.345, true, false, false, TypeConflictException::class],
- [null, 'user1', 'app1', 'fast_array', 12.345, true, true, true, TypeConflictException::class],
- [null, 'user1', 'app1', 'fast_int', 12.345, false, false, true, TypeConflictException::class],
- [null, 'user1', 'app1', 'fast_int', 12.345, false, false, true, TypeConflictException::class],
- [null, 'user1', 'app1', 'lazy_boolean', 12.345, false, false, true, TypeConflictException::class],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerSetValueFloat')]
- public function testSetValueFloat(
- ?array $preload,
- string $userId,
- string $app,
- string $key,
- float $value,
- bool $lazy,
- bool $sensitive,
- bool $result,
- ?string $exception = null,
- ): void {
- $userConfig = $this->generateUserConfig($preload ?? []);
- if ($exception !== null) {
- $this->expectException($exception);
- }
-
- $edited = $userConfig->setValueFloat($userId, $app, $key, $value, $lazy, ($sensitive) ? 1 : 0);
-
- if ($exception !== null) {
- return;
- }
-
- $this->assertEquals($result, $edited);
- if ($result) {
- $this->assertEquals($value, $userConfig->getValueFloat($userId, $app, $key, $value, $lazy));
- $userConfig = $this->generateUserConfig($preload ?? []);
- $this->assertEquals($value, $userConfig->getValueFloat($userId, $app, $key, $value, $lazy));
- }
- }
-
- public static function providerSetValueArray(): array {
- return [
- [null, 'user1', 'app1', 'key1', [], false, false, true],
- [null, 'user1', 'app1', 'key1', [], true, false, true],
- [null, 'user1', 'app1', 'key1', [], true, true, true],
- [null, 'user1', 'app1', 'fast_array', ['year' => 2024], false, false, false],
- [null, 'user1', 'app1', 'fast_array', [], false, false, true],
- [null, 'user1', 'app1', 'fast_array', [], true, false, true],
- [null, 'user1', 'app1', 'fast_array', [], false, true, true],
- [null, 'user1', 'app1', 'fast_array', ['year' => 2024], true, false, true],
- [null, 'user1', 'app1', 'fast_array', ['year' => 2024], false, true, true],
- [null, 'user1', 'app1', 'lazy_array', ['month' => 'October'], false, false, true],
- [null, 'user1', 'app1', 'lazy_array', ['month' => 'September'], false, false, true],
- [null, 'user1', 'app1', 'lazy_array', ['month' => 'September'], true, false, true],
- [null, 'user1', 'app1', 'lazy_array', ['month' => 'September'], false, true, true],
- [null, 'user1', 'app1', 'lazy_array', ['month' => 'October'], true, false, false],
- [null, 'user1', 'app1', 'lazy_array', ['month' => 'October'], false, true, true],
- [null, 'user1', 'app1', 'fast_string', [], false, false, true, TypeConflictException::class],
- [null, 'user1', 'app1', 'fast_string', [], false, false, false, TypeConflictException::class],
- [null, 'user1', 'app1', 'fast_string', [], true, false, true, TypeConflictException::class],
- [null, 'user1', 'app1', 'fast_string', [], true, true, true, TypeConflictException::class],
- [null, 'user1', 'app1', 'lazy_string', [], false, false, true, TypeConflictException::class],
- [null, 'user1', 'app1', 'lazy_string', [], true, false, false, TypeConflictException::class],
- [null, 'user1', 'app1', 'lazy_string', [], true, true, true, TypeConflictException::class],
- [null, 'user1', 'app1', 'fast_int', [], false, false, true, TypeConflictException::class],
- [null, 'user1', 'app1', 'fast_int', [], false, false, true, TypeConflictException::class],
- [null, 'user1', 'app1', 'lazy_boolean', [], false, false, true, TypeConflictException::class],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerSetValueArray')]
- public function testSetValueArray(
- ?array $preload,
- string $userId,
- string $app,
- string $key,
- array $value,
- bool $lazy,
- bool $sensitive,
- bool $result,
- ?string $exception = null,
- ): void {
- $userConfig = $this->generateUserConfig($preload ?? []);
- if ($exception !== null) {
- $this->expectException($exception);
- }
-
- $edited = $userConfig->setValueArray($userId, $app, $key, $value, $lazy, ($sensitive) ? 1 : 0);
-
- if ($exception !== null) {
- return;
- }
-
- $this->assertEquals($result, $edited);
- if ($result) {
- $this->assertEqualsCanonicalizing(
- $value, $userConfig->getValueArray($userId, $app, $key, $value, $lazy)
- );
- $userConfig = $this->generateUserConfig($preload ?? []);
- $this->assertEqualsCanonicalizing(
- $value, $userConfig->getValueArray($userId, $app, $key, $value, $lazy)
- );
- }
- }
-
- public static function providerUpdateSensitive(): array {
- return [
- [null, 'user1', 'app1', 'key1', false, false],
- [['user1'], 'user1', 'app1', 'key1', false, false],
- [null, 'user1', 'app1', 'key1', true, true],
- [['user1'], 'user1', 'app1', 'key1', true, true],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerUpdateSensitive')]
- public function testUpdateSensitive(
- ?array $preload,
- string $userId,
- string $app,
- string $key,
- bool $sensitive,
- bool $result,
- ?string $exception = null,
- ): void {
- $userConfig = $this->generateUserConfig($preload ?? []);
- if ($exception !== null) {
- $this->expectException($exception);
- }
-
- $edited = $userConfig->updateSensitive($userId, $app, $key, $sensitive);
- if ($exception !== null) {
- return;
- }
-
- $this->assertEquals($result, $edited);
- if ($result) {
- $this->assertEquals($sensitive, $userConfig->isSensitive($userId, $app, $key));
- $userConfig = $this->generateUserConfig($preload ?? []);
- $this->assertEquals($sensitive, $userConfig->isSensitive($userId, $app, $key));
- if ($sensitive) {
- $statusCache = $userConfig->statusCache();
- $this->assertEquals(
- true,
- str_starts_with(
- ($statusCache['fastCache'][$userId][$app][$key]
- ?? $statusCache['lazyCache'][$userId][$app][$key])->getRawValue(),
- '$UserConfigEncryption$')
- );
- }
- }
- }
-
- public static function providerUpdateGlobalSensitive(): array {
- return [[true], [false]];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerUpdateGlobalSensitive')]
- public function testUpdateGlobalSensitive(bool $sensitive): void {
- $userConfig = $this->generateUserConfig($preload ?? []);
- $app = 'app2';
- if ($sensitive) {
- $key = 'key2';
- $value = 'value2a';
- } else {
- $key = 'key4';
- $value = 'value4';
- }
-
- $this->assertEquals($value, $userConfig->getValueString('user1', $app, $key));
- foreach (['user1', 'user2', 'user3', 'user4'] as $userId) {
- $userConfig->getValueString($userId, $app, $key); // cache loading for userId
- $statusCache = $userConfig->statusCache();
- $this->assertEquals(
- !$sensitive, str_starts_with(
- ($statusCache['fastCache'][$userId][$app][$key]
- ?? $statusCache['lazyCache'][$userId][$app][$key])->getRawValue(),
- '$UserConfigEncryption$'
- )
- );
- }
-
- $userConfig->updateGlobalSensitive($app, $key, $sensitive);
-
- $this->assertEquals($value, $userConfig->getValueString('user1', $app, $key));
- foreach (['user1', 'user2', 'user3', 'user4'] as $userId) {
- $this->assertEquals($sensitive, $userConfig->isSensitive($userId, $app, $key));
- $statusCache = $userConfig->statusCache();
- // should only work if updateGlobalSensitive drop cache
- $this->assertEquals($sensitive, str_starts_with(
- ($statusCache['fastCache'][$userId][$app][$key]
- ?? $statusCache['lazyCache'][$userId][$app][$key])->getRawValue(),
- '$UserConfigEncryption$')
- );
- }
- }
-
- public static function providerUpdateLazy(): array {
- return [
- [null, 'user1', 'app1', 'key1', false, false],
- [['user1'], 'user1', 'app1', 'key1', false, false],
- [null, 'user1', 'app1', 'key1', true, true],
- [['user1'], 'user1', 'app1', 'key1', true, true],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerUpdateLazy')]
- public function testUpdateLazy(
- ?array $preload,
- string $userId,
- string $app,
- string $key,
- bool $lazy,
- bool $result,
- ?string $exception = null,
- ): void {
- $userConfig = $this->generateUserConfig($preload ?? []);
- if ($exception !== null) {
- $this->expectException($exception);
- }
-
- $edited = $userConfig->updateLazy($userId, $app, $key, $lazy);
- if ($exception !== null) {
- return;
- }
-
- $this->assertEquals($result, $edited);
- if ($result) {
- $this->assertEquals($lazy, $userConfig->isLazy($userId, $app, $key));
- $userConfig = $this->generateUserConfig($preload ?? []);
- $this->assertEquals($lazy, $userConfig->isLazy($userId, $app, $key));
- }
- }
-
- public static function providerUpdateGlobalLazy(): array {
- return [[true], [false]];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerUpdateGlobalLazy')]
- public function testUpdateGlobalLazy(bool $lazy): void {
- $userConfig = $this->generateUserConfig($preload ?? []);
- $app = 'app2';
- if ($lazy) {
- $key = 'key4';
- $value = 'value4';
- } else {
- $key = 'key3';
- $value = 'value3';
- }
-
- $this->assertEquals($value, $userConfig->getValueString('user1', $app, $key, '', !$lazy));
- foreach (['user1', 'user2', 'user3', 'user4'] as $userId) {
- $this->assertEquals(!$lazy, $userConfig->isLazy($userId, $app, $key));
- }
-
- $userConfig->updateGlobalLazy($app, $key, $lazy);
- $this->assertEquals($value, $userConfig->getValueString('user1', $app, $key, '', $lazy));
- foreach (['user1', 'user2', 'user3', 'user4'] as $userId) {
- $this->assertEquals($lazy, $userConfig->isLazy($userId, $app, $key));
- }
- }
-
- public static function providerGetDetails(): array {
- return [
- [
- 'user3', 'app2', 'key2',
- [
- 'userId' => 'user3',
- 'app' => 'app2',
- 'key' => 'key2',
- 'value' => 'value2c',
- 'type' => 0,
- 'lazy' => false,
- 'typeString' => 'mixed',
- 'sensitive' => false
- ]
- ],
- [
- 'user1', 'app1', 'lazy_int',
- [
- 'userId' => 'user1',
- 'app' => 'app1',
- 'key' => 'lazy_int',
- 'value' => 12,
- 'type' => 2,
- 'lazy' => true,
- 'typeString' => 'int',
- 'sensitive' => false
- ]
- ],
- [
- 'user1', 'app1', 'fast_float_sensitive',
- [
- 'userId' => 'user1',
- 'app' => 'app1',
- 'key' => 'fast_float_sensitive',
- 'value' => 1.41,
- 'type' => 3,
- 'lazy' => false,
- 'typeString' => 'float',
- 'sensitive' => true
- ]
- ],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerGetDetails')]
- public function testGetDetails(string $userId, string $app, string $key, array $result): void {
- $userConfig = $this->generateUserConfig($preload ?? []);
- $this->assertEqualsCanonicalizing($result, $userConfig->getDetails($userId, $app, $key));
- }
-
- public static function providerDeletePreference(): array {
- return [
- [null, 'user1', 'app1', 'key22'],
- [['user1'], 'user1', 'app1', 'fast_string_sensitive'],
- [null, 'user1', 'app1', 'lazy_array_sensitive'],
- [['user2'], 'user1', 'app1', 'lazy_array_sensitive'],
- [null, 'user2', 'only-lazy', 'key1'],
- [['user2'], 'user2', 'only-lazy', 'key1'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerDeletePreference')]
- public function testDeletePreference(
- ?array $preload,
- string $userId,
- string $app,
- string $key,
- ): void {
- $userConfig = $this->generateUserConfig($preload ?? []);
- $lazy = $userConfig->isLazy($userId, $app, $key);
-
- $userConfig = $this->generateUserConfig($preload ?? []);
- $this->assertEquals(true, $userConfig->hasKey($userId, $app, $key, $lazy));
- $userConfig->deleteUserConfig($userId, $app, $key);
- $this->assertEquals(false, $userConfig->hasKey($userId, $app, $key, $lazy));
- $userConfig = $this->generateUserConfig($preload ?? []);
- $this->assertEquals(false, $userConfig->hasKey($userId, $app, $key, $lazy));
- }
-
- public static function providerDeleteKey(): array {
- return [
- [null, 'app2', 'key3'],
- [['user1'], 'app2', 'key3'],
- [null, 'only-lazy', 'key1'],
- [['user2'], 'only-lazy', 'key1'],
- [null, 'app2', 'lazy_string_sensitive'],
- [['user3', 'user1'], 'app2', 'lazy_string_sensitive'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerDeleteKey')]
- public function testDeleteKey(
- ?array $preload,
- string $app,
- string $key,
- ): void {
- $userConfig = $this->generateUserConfig($preload ?? []);
- $userConfig->deleteKey($app, $key);
-
- foreach (['user1', 'user2', 'user3', 'user4'] as $userId) {
- $this->assertEquals(false, $userConfig->hasKey($userId, $app, $key, null));
- $userConfigTemp = $this->generateUserConfig($preload ?? []);
- $this->assertEquals(false, $userConfigTemp->hasKey($userId, $app, $key, null));
- }
- }
-
- public function testDeleteApp(): void {
- $userConfig = $this->generateUserConfig();
- $userConfig->deleteApp('only-lazy');
-
- foreach (['user1', 'user2', 'user3', 'user4'] as $userId) {
- $this->assertEquals(false, in_array('only-lazy', $userConfig->getApps($userId)));
- $userConfigTemp = $this->generateUserConfig();
- $this->assertEquals(false, in_array('only-lazy', $userConfigTemp->getApps($userId)));
- }
- }
-
- public function testDeleteAllPreferences(): void {
- $userConfig = $this->generateUserConfig();
- $userConfig->deleteAllUserConfig('user1');
-
- $this->assertEqualsCanonicalizing([], $userConfig->getApps('user1'));
- $userConfig = $this->generateUserConfig();
- $this->assertEqualsCanonicalizing([], $userConfig->getApps('user1'));
- }
-
- public function testClearCache(): void {
- $userConfig = $this->generateUserConfig(['user1', 'user2']);
- $userConfig->clearCache('user1');
- $status = $userConfig->statusCache();
- $this->assertTrue(isset($status['fastCache']['user2']));
- $this->assertFalse(isset($status['fastCache']['user1']));
- $this->assertEquals('value2a', $userConfig->getValueString('user1', 'app2', 'key2'));
- $status = $userConfig->statusCache();
- $this->assertFalse(isset($status['lazyCache']['user1']));
- $this->assertTrue(isset($status['fastCache']['user1']));
- }
-
- public function testClearCacheAll(): void {
- $userConfig = $this->generateUserConfig(['user1', 'user2']);
- $userConfig->clearCacheAll();
- $this->assertEqualsCanonicalizing(
- [
- 'fastCache' => [],
- 'lazyCache' => [],
- ],
- $userConfig->statusCache()
- );
- }
-}
--- a/tests/lib/ConfigTest.php
+++ /dev/null
@@ -1,193 +0,0 @@
-"bar", "beers" => array("Appenzeller", "Guinness", "Kölsch"), "alcohol_free" => false);';
-
- /** @var array */
- private $initialConfig = ['foo' => 'bar', 'beers' => ['Appenzeller', 'Guinness', 'Kölsch'], 'alcohol_free' => false];
- /** @var string */
- private $configFile;
- /** @var string */
- private $randomTmpDir;
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- $this->randomTmpDir = Server::get(ITempManager::class)->getTemporaryFolder();
- $this->configFile = $this->randomTmpDir . 'testconfig.php';
- file_put_contents($this->configFile, self::TESTCONTENT);
- }
-
- #[\Override]
- protected function tearDown(): void {
- unlink($this->configFile);
- parent::tearDown();
- }
-
- private function getConfig(): Config {
- return new Config($this->randomTmpDir, 'testconfig.php');
- }
-
- public function testGetKeys(): void {
- $expectedConfig = ['foo', 'beers', 'alcohol_free'];
- $this->assertSame($expectedConfig, $this->getConfig()->getKeys());
- }
-
- public function testGetKeysReturnsEnvironmentKeysIfSet() {
- $expectedConfig = ['foo', 'beers', 'alcohol_free', 'taste'];
- putenv('NC_taste=great');
- $this->assertSame($expectedConfig, $this->getConfig()->getKeys());
- putenv('NC_taste');
- }
-
- public function testGetValue(): void {
- $config = $this->getConfig();
- $this->assertSame('bar', $config->getValue('foo'));
- $this->assertSame(null, $config->getValue('bar'));
- $this->assertSame('moo', $config->getValue('bar', 'moo'));
- $this->assertSame(false, $config->getValue('alcohol_free', 'someBogusValue'));
- $this->assertSame(['Appenzeller', 'Guinness', 'Kölsch'], $config->getValue('beers', 'someBogusValue'));
- $this->assertSame(['Appenzeller', 'Guinness', 'Kölsch'], $config->getValue('beers'));
- }
-
- public function testGetValueReturnsEnvironmentValueIfSet(): void {
- $config = $this->getConfig();
- $this->assertEquals('bar', $config->getValue('foo'));
-
- putenv('NC_foo=baz');
- $config = $this->getConfig();
- $this->assertEquals('baz', $config->getValue('foo'));
- putenv('NC_foo'); // unset the env variable
- }
-
- public function testGetValueReturnsEnvironmentValueIfSetToZero(): void {
- $config = $this->getConfig();
- $this->assertEquals('bar', $config->getValue('foo'));
-
- putenv('NC_foo=0');
- $config = $this->getConfig();
- $this->assertEquals('0', $config->getValue('foo'));
- putenv('NC_foo'); // unset the env variable
- }
-
- public function testGetValueReturnsEnvironmentValueIfSetToFalse(): void {
- $config = $this->getConfig();
- $this->assertEquals('bar', $config->getValue('foo'));
-
- putenv('NC_foo=false');
- $config = $this->getConfig();
- $this->assertEquals('false', $config->getValue('foo'));
- putenv('NC_foo'); // unset the env variable
- }
-
- public function testSetValue(): void {
- $config = $this->getConfig();
- $config->setValue('foo', 'moo');
- $this->assertSame('moo', $config->getValue('foo'));
-
- $content = file_get_contents($this->configFile);
- $expected = " 'moo',\n 'beers' => \n array (\n 0 => 'Appenzeller',\n "
- . " 1 => 'Guinness',\n 2 => 'Kölsch',\n ),\n 'alcohol_free' => false,\n);\n";
- $this->assertEquals($expected, $content);
-
- $config->setValue('bar', 'red');
- $config->setValue('apps', ['files', 'gallery']);
- $this->assertSame('red', $config->getValue('bar'));
- $this->assertSame(['files', 'gallery'], $config->getValue('apps'));
-
- $content = file_get_contents($this->configFile);
-
- $expected = " 'moo',\n 'beers' => \n array (\n 0 => 'Appenzeller',\n "
- . " 1 => 'Guinness',\n 2 => 'Kölsch',\n ),\n 'alcohol_free' => false,\n 'bar' => 'red',\n 'apps' => \n "
- . " array (\n 0 => 'files',\n 1 => 'gallery',\n ),\n);\n";
- $this->assertEquals($expected, $content);
- }
-
- public function testSetValues(): void {
- $config = $this->getConfig();
- $content = file_get_contents($this->configFile);
- $this->assertEquals(self::TESTCONTENT, $content);
-
- // Changing configs to existing values and deleting non-existing once
- // should not rewrite the config.php
- $config->setValues([
- 'foo' => 'bar',
- 'not_exists' => null,
- ]);
-
- $this->assertSame('bar', $config->getValue('foo'));
- $this->assertSame(null, $config->getValue('not_exists'));
- $content = file_get_contents($this->configFile);
- $this->assertEquals(self::TESTCONTENT, $content);
-
- $config->setValues([
- 'foo' => 'moo',
- 'alcohol_free' => null,
- ]);
- $this->assertSame('moo', $config->getValue('foo'));
- $this->assertSame(null, $config->getValue('not_exists'));
-
- $content = file_get_contents($this->configFile);
- $expected = " 'moo',\n 'beers' => \n array (\n 0 => 'Appenzeller',\n "
- . " 1 => 'Guinness',\n 2 => 'Kölsch',\n ),\n);\n";
- $this->assertEquals($expected, $content);
- }
-
- public function testDeleteKey(): void {
- $config = $this->getConfig();
- $config->deleteKey('foo');
- $this->assertSame('this_was_clearly_not_set_before', $config->getValue('foo', 'this_was_clearly_not_set_before'));
- $content = file_get_contents($this->configFile);
-
- $expected = " \n array (\n 0 => 'Appenzeller',\n "
- . " 1 => 'Guinness',\n 2 => 'Kölsch',\n ),\n 'alcohol_free' => false,\n);\n";
- $this->assertEquals($expected, $content);
- }
-
- public function testConfigMerge(): void {
- // Create additional config
- $additionalConfig = '"totallyOutdated");';
- $additionalConfigPath = $this->randomTmpDir . 'additionalConfig.testconfig.php';
- file_put_contents($additionalConfigPath, $additionalConfig);
-
- // Reinstantiate the config to force a read-in of the additional configs
- $config = new Config($this->randomTmpDir, 'testconfig.php');
-
- // Ensure that the config value can be read and the config has not been modified
- $this->assertSame('totallyOutdated', $config->getValue('php53', 'bogusValue'));
- $this->assertEquals(self::TESTCONTENT, file_get_contents($this->configFile));
-
- // Write a new value to the config
- $config->setValue('CoolWebsites', ['demo.owncloud.org', 'owncloud.org', 'owncloud.com']);
- $expected = " 'bar',\n 'beers' => \n array (\n 0 => 'Appenzeller',\n "
- . " 1 => 'Guinness',\n 2 => 'Kölsch',\n ),\n 'alcohol_free' => false,\n 'php53' => 'totallyOutdated',\n 'CoolWebsites' => \n array (\n "
- . " 0 => 'demo.owncloud.org',\n 1 => 'owncloud.org',\n 2 => 'owncloud.com',\n ),\n);\n";
- $this->assertEquals($expected, file_get_contents($this->configFile));
-
- // Cleanup
- unlink($additionalConfigPath);
- }
-}
--- a/tests/lib/Contacts/ContactsMenu/ActionFactoryTest.php
+++ /dev/null
@@ -1,48 +0,0 @@
-actionFactory = new ActionFactory();
- }
-
- public function testNewLinkAction(): void {
- $icon = 'icon-test';
- $name = 'Test';
- $href = 'some/url';
-
- $action = $this->actionFactory->newLinkAction($icon, $name, $href);
-
- $this->assertInstanceOf(IAction::class, $action);
- $this->assertEquals($name, $action->getName());
- $this->assertEquals(10, $action->getPriority());
- }
-
- public function testNewEMailAction(): void {
- $icon = 'icon-test';
- $name = 'Test';
- $href = 'user@example.com';
-
- $action = $this->actionFactory->newEMailAction($icon, $name, $href);
-
- $this->assertInstanceOf(IAction::class, $action);
- $this->assertEquals($name, $action->getName());
- $this->assertEquals(10, $action->getPriority());
- $this->assertEquals('mailto:user@example.com', $action->getHref());
- }
-}
--- a/tests/lib/Contacts/ContactsMenu/ActionProviderStoreTest.php
+++ /dev/null
@@ -1,120 +0,0 @@
-serverContainer = $this->createMock(ContainerInterface::class);
- $this->appManager = $this->createMock(AppManager::class);
- $logger = $this->createMock(LoggerInterface::class);
-
- $this->actionProviderStore = new ActionProviderStore($this->serverContainer, $this->appManager, $logger);
- }
-
- public function testGetProviders(): void {
- $user = $this->createMock(IUser::class);
- $provider1 = $this->createMock(ProfileProvider::class);
- $provider2 = $this->createMock(LocalTimeProvider::class);
- $provider3 = $this->createMock(EMailProvider::class);
- $provider4 = $this->createMock(IProvider::class);
-
- $this->appManager->expects($this->once())
- ->method('getEnabledAppsForUser')
- ->with($user)
- ->willReturn(['contacts']);
- $this->appManager->expects($this->once())
- ->method('getAppInfo')
- ->with('contacts')
- ->willReturn([
- 'contactsmenu' => [
- 'OCA\Contacts\Provider1',
- ],
- ]);
- $this->serverContainer->expects($this->exactly(4))
- ->method('get')
- ->willReturnMap([
- [ProfileProvider::class, $provider1],
- [LocalTimeProvider::class, $provider2],
- [EMailProvider::class, $provider3],
- ['OCA\Contacts\Provider1', $provider4]
- ]);
-
- $providers = $this->actionProviderStore->getProviders($user);
-
- $this->assertCount(4, $providers);
- $this->assertInstanceOf(ProfileProvider::class, $providers[0]);
- $this->assertInstanceOf(LocalTimeProvider::class, $providers[1]);
- $this->assertInstanceOf(EMailProvider::class, $providers[2]);
- }
-
- public function testGetProvidersOfAppWithIncompleInfo(): void {
- $user = $this->createMock(IUser::class);
- $provider1 = $this->createMock(ProfileProvider::class);
- $provider2 = $this->createMock(LocalTimeProvider::class);
- $provider3 = $this->createMock(EMailProvider::class);
-
- $this->appManager->expects($this->once())
- ->method('getEnabledAppsForUser')
- ->with($user)
- ->willReturn(['contacts']);
- $this->appManager->expects($this->once())
- ->method('getAppInfo')
- ->with('contacts')
- ->willReturn([/* Empty info.xml */]);
- $this->serverContainer->expects($this->exactly(3))
- ->method('get')
- ->willReturnMap([
- [ProfileProvider::class, $provider1],
- [LocalTimeProvider::class, $provider2],
- [EMailProvider::class, $provider3],
- ]);
-
- $providers = $this->actionProviderStore->getProviders($user);
-
- $this->assertCount(3, $providers);
- $this->assertInstanceOf(ProfileProvider::class, $providers[0]);
- $this->assertInstanceOf(LocalTimeProvider::class, $providers[1]);
- $this->assertInstanceOf(EMailProvider::class, $providers[2]);
- }
-
- public function testGetProvidersWithQueryException(): void {
- $this->expectException(\Exception::class);
-
- $user = $this->createMock(IUser::class);
- $this->appManager->expects($this->once())
- ->method('getEnabledAppsForUser')
- ->with($user)
- ->willReturn([]);
- $this->serverContainer->expects($this->once())
- ->method('get')
- ->willThrowException(new QueryException());
-
- $this->actionProviderStore->getProviders($user);
- }
-}
--- a/tests/lib/Contacts/ContactsMenu/Actions/LinkActionTest.php
+++ /dev/null
@@ -1,91 +0,0 @@
-action = new LinkAction();
- }
-
- public function testSetIcon(): void {
- $icon = 'icon-test';
-
- $this->action->setIcon($icon);
- $json = $this->action->jsonSerialize();
-
- $this->assertArrayHasKey('icon', $json);
- $this->assertEquals($json['icon'], $icon);
- }
-
- public function testGetSetName(): void {
- $name = 'Jane Doe';
-
- $this->assertEmpty($this->action->getName());
- $this->action->setName($name);
- $this->assertEquals($name, $this->action->getName());
- }
-
- public function testGetSetPriority(): void {
- $prio = 50;
-
- $this->assertEquals(10, $this->action->getPriority());
- $this->action->setPriority($prio);
- $this->assertEquals($prio, $this->action->getPriority());
- }
-
- public function testSetHref(): void {
- $this->action->setHref('/some/url');
-
- $json = $this->action->jsonSerialize();
- $this->assertArrayHasKey('hyperlink', $json);
- $this->assertEquals('/some/url', $json['hyperlink']);
- }
-
- public function testJsonSerialize(): void {
- $this->action->setIcon('icon-contacts');
- $this->action->setName('Nickie Works');
- $this->action->setPriority(33);
- $this->action->setHref('example.com');
- $this->action->setAppId('contacts');
- $expected = [
- 'title' => 'Nickie Works',
- 'icon' => 'icon-contacts',
- 'hyperlink' => 'example.com',
- 'appId' => 'contacts',
- ];
-
- $json = $this->action->jsonSerialize();
-
- $this->assertEquals($expected, $json);
- }
-
- public function testJsonSerializeNoAppName(): void {
- $this->action->setIcon('icon-contacts');
- $this->action->setName('Nickie Works');
- $this->action->setPriority(33);
- $this->action->setHref('example.com');
- $expected = [
- 'title' => 'Nickie Works',
- 'icon' => 'icon-contacts',
- 'hyperlink' => 'example.com',
- 'appId' => '',
- ];
-
- $json = $this->action->jsonSerialize();
-
- $this->assertEquals($expected, $json);
- }
-}
--- a/tests/lib/Contacts/ContactsMenu/ContactsStoreTest.php
+++ /dev/null
@@ -1,1163 +0,0 @@
-contactsManager = $this->createMock(IManager::class);
- $this->statusService = $this->createMock(StatusService::class);
- $this->userManager = $this->createMock(IUserManager::class);
- $this->profileManager = $this->createMock(ProfileManager::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->groupManager = $this->createMock(IGroupManager::class);
- $this->config = $this->createMock(IConfig::class);
- $this->knownUserService = $this->createMock(KnownUserService::class);
- $this->l10nFactory = $this->createMock(IL10NFactory::class);
- $this->contactsStore = new ContactsStore(
- $this->contactsManager,
- $this->statusService,
- $this->config,
- $this->profileManager,
- $this->userManager,
- $this->urlGenerator,
- $this->groupManager,
- $this->knownUserService,
- $this->l10nFactory,
- new ShareDisableChecker($this->config, $this->userManager, $this->groupManager),
- );
- }
-
- public function testGetContactsWithoutFilter(): void {
- /** @var IUser|MockObject $user */
- $user = $this->createMock(IUser::class);
- $this->contactsManager->expects($this->once())
- ->method('search')
- ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
- ->willReturn([
- [
- 'UID' => 123,
- ],
- [
- 'UID' => 567,
- 'FN' => 'Darren Roner',
- 'EMAIL' => [
- 'darren@roner.au'
- ],
- ],
- ]);
- $user->expects($this->exactly(3))
- ->method('getUID')
- ->willReturn('user123');
-
- $entries = $this->contactsStore->getContacts($user, '');
-
- $this->assertCount(2, $entries);
- $this->assertEquals([
- 'darren@roner.au'
- ], $entries[1]->getEMailAddresses());
- }
-
- public function testGetContactsHidesOwnEntry(): void {
- /** @var IUser|MockObject $user */
- $user = $this->createMock(IUser::class);
- $this->contactsManager->expects($this->once())
- ->method('search')
- ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
- ->willReturn([
- [
- 'UID' => 'user123',
- ],
- [
- 'UID' => 567,
- 'FN' => 'Darren Roner',
- 'EMAIL' => [
- 'darren@roner.au'
- ],
- ],
- ]);
- $user->expects($this->exactly(3))
- ->method('getUID')
- ->willReturn('user123');
-
- $entries = $this->contactsStore->getContacts($user, '');
-
- $this->assertCount(1, $entries);
- }
-
- public function testGetContactsWithoutBinaryImage(): void {
- /** @var IUser|MockObject $user */
- $user = $this->createMock(IUser::class);
- $this->urlGenerator->expects($this->any())
- ->method('linkToRouteAbsolute')
- ->with('core.GuestAvatar.getAvatar', $this->anything())
- ->willReturn('https://urlToNcAvatar.test');
- $this->contactsManager->expects($this->once())
- ->method('search')
- ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
- ->willReturn([
- [
- 'UID' => 123,
- ],
- [
- 'UID' => 567,
- 'FN' => 'Darren Roner',
- 'EMAIL' => [
- 'darren@roner.au'
- ],
- 'PHOTO' => base64_encode('photophotophoto'),
- ],
- ]);
- $user->expects($this->exactly(3))
- ->method('getUID')
- ->willReturn('user123');
-
- $entries = $this->contactsStore->getContacts($user, '');
-
- $this->assertCount(2, $entries);
- $this->assertSame('https://urlToNcAvatar.test', $entries[1]->getAvatar());
- }
-
- public function testGetContactsWithoutAvatarURI(): void {
- /** @var IUser|MockObject $user */
- $user = $this->createMock(IUser::class);
- $this->contactsManager->expects($this->once())
- ->method('search')
- ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
- ->willReturn([
- [
- 'UID' => 123,
- ],
- [
- 'UID' => 567,
- 'FN' => 'Darren Roner',
- 'EMAIL' => [
- 'darren@roner.au'
- ],
- 'PHOTO' => 'VALUE=uri:https://photo',
- ],
- ]);
- $user->expects($this->exactly(3))
- ->method('getUID')
- ->willReturn('user123');
-
- $entries = $this->contactsStore->getContacts($user, '');
-
- $this->assertCount(2, $entries);
- $this->assertEquals('https://photo', $entries[1]->getAvatar());
- }
-
- public static function dataGetContactsWhenUserIsInExcludeGroups(): array {
- return [
- ['yes', '[]', [], ['user123', 'user12345']],
- ['yes', '["excludedGroup1"]', [], ['user123', 'user12345']],
- ['yes', '["excludedGroup1"]', ['anotherGroup1'], ['user123', 'user12345']],
- ['yes', '["excludedGroup1"]', ['anotherGroup1', 'anotherGroup2', 'anotherGroup3'], ['user123', 'user12345']],
- ['yes', '["excludedGroup1"]', ['excludedGroup1'], []],
- ['yes', '["excludedGroup1"]', ['anotherGroup1', 'excludedGroup1'], []],
- ['yes', '["excludedGroup1"]', ['excludedGroup1', 'anotherGroup1', 'anotherGroup2', 'anotherGroup3'], []],
- ['yes', '["excludedGroup1", "excludedGroup2", "excludedGroup3"]', [], ['user123', 'user12345']],
- ['yes', '["excludedGroup1", "excludedGroup2", "excludedGroup3"]', ['anotherGroup1'], ['user123', 'user12345']],
- ['yes', '["excludedGroup1", "excludedGroup2", "excludedGroup3"]', ['anotherGroup1', 'anotherGroup2', 'anotherGroup3'], ['user123', 'user12345']],
- ['yes', '["excludedGroup1", "excludedGroup2", "excludedGroup3"]', ['excludedGroup1'], []],
- ['yes', '["excludedGroup1", "excludedGroup2", "excludedGroup3"]', ['excludedGroup2'], []],
- ['yes', '["excludedGroup1", "excludedGroup2", "excludedGroup3"]', ['excludedGroup3'], []],
- ['yes', '["excludedGroup1", "excludedGroup2", "excludedGroup3"]', ['excludedGroup1', 'excludedGroup2', 'excludedGroup3'], []],
- ['yes', '["excludedGroup1", "excludedGroup2", "excludedGroup3"]', ['anotherGroup1', 'excludedGroup1'], []],
- ['yes', '["excludedGroup1", "excludedGroup2", "excludedGroup3"]', ['anotherGroup1', 'excludedGroup2', 'anotherGroup2', 'anotherGroup3'], []],
- ['yes', '["excludedGroup1", "excludedGroup2", "excludedGroup3"]', ['excludedGroup3', 'anotherGroup1', 'anotherGroup2', 'anotherGroup3'], []],
- ['allow', '[]', [], []],
- ['allow', '["allowedGroup1"]', [], []],
- ['allow', '["allowedGroup1"]', ['anotherGroup1'], []],
- ['allow', '["allowedGroup1"]', ['anotherGroup1', 'anotherGroup2', 'anotherGroup3'], []],
- ['allow', '["allowedGroup1"]', ['allowedGroup1'], ['user123', 'user12345']],
- ['allow', '["allowedGroup1"]', ['anotherGroup1', 'allowedGroup1'], ['user123', 'user12345']],
- ['allow', '["allowedGroup1"]', ['allowedGroup1', 'anotherGroup1', 'anotherGroup2', 'anotherGroup3'], ['user123', 'user12345']],
- ['allow', '["allowedGroup1", "allowedGroup2", "allowedGroup3"]', [], []],
- ['allow', '["allowedGroup1", "allowedGroup2", "allowedGroup3"]', ['anotherGroup1'], []],
- ['allow', '["allowedGroup1", "allowedGroup2", "allowedGroup3"]', ['anotherGroup1', 'anotherGroup2', 'anotherGroup3'], []],
- ['allow', '["allowedGroup1", "allowedGroup2", "allowedGroup3"]', ['allowedGroup1'], ['user123', 'user12345']],
- ['allow', '["allowedGroup1", "allowedGroup2", "allowedGroup3"]', ['allowedGroup2'], ['user123', 'user12345']],
- ['allow', '["allowedGroup1", "allowedGroup2", "allowedGroup3"]', ['allowedGroup3'], ['user123', 'user12345']],
- ['allow', '["allowedGroup1", "allowedGroup2", "allowedGroup3"]', ['allowedGroup1', 'allowedGroup2', 'allowedGroup3'], ['user123', 'user12345']],
- ['allow', '["allowedGroup1", "allowedGroup2", "allowedGroup3"]', ['anotherGroup1', 'allowedGroup1'], ['user123', 'user12345']],
- ['allow', '["allowedGroup1", "allowedGroup2", "allowedGroup3"]', ['anotherGroup1', 'allowedGroup2', 'anotherGroup2', 'anotherGroup3'], ['user123', 'user12345']],
- ['allow', '["allowedGroup1", "allowedGroup2", "allowedGroup3"]', ['allowedGroup3', 'anotherGroup1', 'anotherGroup2', 'anotherGroup3'], ['user123', 'user12345']],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetContactsWhenUserIsInExcludeGroups')]
- public function testGetContactsWhenUserIsInExcludeGroups(string $excludeGroups, string $excludeGroupsList, array $currentUserGroupIds, array $expectedUids): void {
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
- ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
- ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
- ['core', 'shareapi_exclude_groups', 'no', $excludeGroups],
- ['core', 'shareapi_only_share_with_group_members', 'no', 'no'],
- ['core', 'shareapi_exclude_groups_list', '', $excludeGroupsList],
- ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
- ]);
-
- /** @var IUser|MockObject $currentUser */
- $currentUser = $this->createMock(IUser::class);
- $currentUser->expects($this->exactly(3))
- ->method('getUID')
- ->willReturn('user001');
-
- $this->userManager->method('get')
- ->with('user001')
- ->willReturn($currentUser);
-
- $this->groupManager->expects($this->exactly(2))
- ->method('getUserGroupIds')
- ->with($this->equalTo($currentUser))
- ->willReturn($currentUserGroupIds);
-
- $this->contactsManager->expects($this->once())
- ->method('search')
- ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
- ->willReturn([
- [
- 'UID' => 'user123',
- 'isLocalSystemBook' => true
- ],
- [
- 'UID' => 'user12345',
- 'isLocalSystemBook' => true
- ],
- ]);
-
- $entries = $this->contactsStore->getContacts($currentUser, '');
-
- $this->assertCount(count($expectedUids), $entries);
- for ($i = 0; $i < count($expectedUids); $i++) {
- $this->assertEquals($expectedUids[$i], $entries[$i]->getProperty('UID'));
- }
- }
-
- public function testGetContactsOnlyShareIfInTheSameGroupWhenUserIsInExcludeGroups(): void {
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
- ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
- ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
- ['core', 'shareapi_exclude_groups', 'no', 'yes'],
- ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
- ['core', 'shareapi_exclude_groups_list', '', '["group1", "group5", "group6"]'],
- ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
- ]);
-
- /** @var IUser|MockObject $currentUser */
- $currentUser = $this->createMock(IUser::class);
- $currentUser->expects($this->exactly(3))
- ->method('getUID')
- ->willReturn('user001');
-
- $this->userManager->method('get')
- ->with('user001')
- ->willReturn($currentUser);
-
- $this->groupManager->expects($this->exactly(2))
- ->method('getUserGroupIds')
- ->with($this->equalTo($currentUser))
- ->willReturn(['group1', 'group2', 'group3']);
-
- $this->contactsManager->expects($this->once())
- ->method('search')
- ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
- ->willReturn([
- [
- 'UID' => 'user123',
- 'isLocalSystemBook' => true
- ],
- [
- 'UID' => 'user12345',
- 'isLocalSystemBook' => true
- ],
- ]);
-
- $entries = $this->contactsStore->getContacts($currentUser, '');
-
- $this->assertCount(0, $entries);
- }
-
- public function testGetContactsOnlyShareIfInTheSameGroup(): void {
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
- ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
- ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
- ['core', 'shareapi_exclude_groups', 'no', 'no'],
- ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
- ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
- ]);
-
- /** @var IUser|MockObject $currentUser */
- $currentUser = $this->createMock(IUser::class);
- $currentUser->expects($this->exactly(3))
- ->method('getUID')
- ->willReturn('user001');
-
- $user1 = $this->createMock(IUser::class);
- $user2 = $this->createMock(IUser::class);
- $user3 = $this->createMock(IUser::class);
-
- $calls = [
- [[$currentUser], ['group1', 'group2', 'group3']],
- [[$user1], ['group1']],
- [[$user2], ['group2', 'group3']],
- [[$user3], ['group8', 'group9']],
- ];
- $this->groupManager->expects($this->exactly(4))
- ->method('getUserGroupIds')
- ->willReturnCallback(function () use (&$calls): array {
- $expected = array_shift($calls);
- $this->assertEquals($expected[0], func_get_args());
- return $expected[1];
- });
-
- $this->userManager->expects($this->exactly(3))
- ->method('get')
- ->willReturnMap([
- ['user1', $user1],
- ['user2', $user2],
- ['user3', $user3],
- ]);
-
- $this->contactsManager->expects($this->once())
- ->method('search')
- ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
- ->willReturn([
- [
- 'UID' => 'user1',
- 'isLocalSystemBook' => true
- ],
- [
- 'UID' => 'user2',
- 'isLocalSystemBook' => true
- ],
- [
- 'UID' => 'user3',
- 'isLocalSystemBook' => true
- ],
- [
- 'UID' => 'contact',
- ],
- ]);
-
- $entries = $this->contactsStore->getContacts($currentUser, '');
-
- $this->assertCount(3, $entries);
- $this->assertEquals('user1', $entries[0]->getProperty('UID'));
- $this->assertEquals('user2', $entries[1]->getProperty('UID'));
- $this->assertEquals('contact', $entries[2]->getProperty('UID'));
- }
-
- public function testGetContactsOnlyEnumerateIfInTheSameGroup(): void {
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
- ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'yes'],
- ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
- ['core', 'shareapi_exclude_groups', 'no', 'no'],
- ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
- ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
- ]);
-
- /** @var IUser|MockObject $currentUser */
- $currentUser = $this->createMock(IUser::class);
- $currentUser->expects($this->exactly(3))
- ->method('getUID')
- ->willReturn('user001');
-
- $user1 = $this->createMock(IUser::class);
- $user2 = $this->createMock(IUser::class);
- $user3 = $this->createMock(IUser::class);
-
- $calls = [
- [[$currentUser], ['group1', 'group2', 'group3']],
- [[$user1], ['group1']],
- [[$user2], ['group2', 'group3']],
- [[$user3], ['group8', 'group9']],
- ];
- $this->groupManager->expects($this->exactly(4))
- ->method('getUserGroupIds')
- ->willReturnCallback(function () use (&$calls): array {
- $expected = array_shift($calls);
- $this->assertEquals($expected[0], func_get_args());
- return $expected[1];
- });
-
- $this->userManager->expects($this->exactly(3))
- ->method('get')
- ->willReturnMap([
- ['user1', $user1],
- ['user2', $user2],
- ['user3', $user3],
- ]);
-
- $this->contactsManager->expects($this->once())
- ->method('search')
- ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
- ->willReturn([
- [
- 'UID' => 'user1',
- 'isLocalSystemBook' => true
- ],
- [
- 'UID' => 'user2',
- 'isLocalSystemBook' => true
- ],
- [
- 'UID' => 'user3',
- 'isLocalSystemBook' => true
- ],
- [
- 'UID' => 'contact',
- ],
- ]);
-
- $entries = $this->contactsStore->getContacts($currentUser, '');
-
- $this->assertCount(3, $entries);
- $this->assertEquals('user1', $entries[0]->getProperty('UID'));
- $this->assertEquals('user2', $entries[1]->getProperty('UID'));
- $this->assertEquals('contact', $entries[2]->getProperty('UID'));
- }
-
- public function testGetContactsOnlyEnumerateIfPhoneBookMatch(): void {
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
- ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
- ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'yes'],
- ['core', 'shareapi_exclude_groups', 'no', 'no'],
- ['core', 'shareapi_only_share_with_group_members', 'no', 'no'],
- ]);
-
- /** @var IUser|MockObject $currentUser */
- $currentUser = $this->createMock(IUser::class);
- $currentUser->expects($this->exactly(3))
- ->method('getUID')
- ->willReturn('user001');
-
- $this->groupManager->expects($this->once())
- ->method('getUserGroupIds')
- ->with($this->equalTo($currentUser))
- ->willReturn(['group1', 'group2', 'group3']);
-
- $this->knownUserService->method('isKnownToUser')
- ->willReturnMap([
- ['user001', 'user1', true],
- ['user001', 'user2', true],
- ['user001', 'user3', false],
- ]);
-
- $this->contactsManager->expects($this->once())
- ->method('search')
- ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
- ->willReturn([
- [
- 'UID' => 'user1',
- 'isLocalSystemBook' => true
- ],
- [
- 'UID' => 'user2',
- 'isLocalSystemBook' => true
- ],
- [
- 'UID' => 'user3',
- 'isLocalSystemBook' => true
- ],
- [
- 'UID' => 'contact',
- ],
- ]);
-
- $entries = $this->contactsStore->getContacts($currentUser, '');
-
- $this->assertCount(3, $entries);
- $this->assertEquals('user1', $entries[0]->getProperty('UID'));
- $this->assertEquals('user2', $entries[1]->getProperty('UID'));
- $this->assertEquals('contact', $entries[2]->getProperty('UID'));
- }
-
- public function testGetContactsOnlyEnumerateIfPhoneBookMatchWithOwnGroupsOnly(): void {
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
- ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
- ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'yes'],
- ['core', 'shareapi_exclude_groups', 'no', 'no'],
- ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
- ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
- ]);
-
- /** @var IUser|MockObject $currentUser */
- $currentUser = $this->createMock(IUser::class);
- $currentUser->expects($this->exactly(3))
- ->method('getUID')
- ->willReturn('user001');
-
- $user1 = $this->createMock(IUser::class);
- $user2 = $this->createMock(IUser::class);
- $user3 = $this->createMock(IUser::class);
-
- $calls = [
- [[$currentUser], ['group1', 'group2', 'group3']],
- [[$user1], ['group1']],
- [[$user2], ['group2', 'group3']],
- [[$user3], ['group8', 'group9']],
- ];
- $this->groupManager->expects($this->exactly(4))
- ->method('getUserGroupIds')
- ->willReturnCallback(function () use (&$calls): array {
- $expected = array_shift($calls);
- $this->assertEquals($expected[0], func_get_args());
- return $expected[1];
- });
-
- $this->userManager->expects($this->exactly(3))
- ->method('get')
- ->willReturnMap([
- ['user1', $user1],
- ['user2', $user2],
- ['user3', $user3],
- ]);
-
- $this->knownUserService->method('isKnownToUser')
- ->willReturnMap([
- ['user001', 'user1', true],
- ['user001', 'user2', true],
- ['user001', 'user3', true],
- ]);
-
- $this->contactsManager->expects($this->once())
- ->method('search')
- ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
- ->willReturn([
- [
- 'UID' => 'user1',
- 'isLocalSystemBook' => true
- ],
- [
- 'UID' => 'user2',
- 'isLocalSystemBook' => true
- ],
- [
- 'UID' => 'user3',
- 'isLocalSystemBook' => true
- ],
- [
- 'UID' => 'contact',
- ],
- ]);
-
- $entries = $this->contactsStore->getContacts($currentUser, '');
-
- $this->assertCount(3, $entries);
- $this->assertEquals('user1', $entries[0]->getProperty('UID'));
- $this->assertEquals('user2', $entries[1]->getProperty('UID'));
- $this->assertEquals('contact', $entries[2]->getProperty('UID'));
- }
-
- public function testGetContactsOnlyEnumerateIfPhoneBookOrSameGroup(): void {
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
- ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'yes'],
- ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'yes'],
- ['core', 'shareapi_exclude_groups', 'no', 'no'],
- ['core', 'shareapi_only_share_with_group_members', 'no', 'no'],
- ]);
-
- /** @var IUser|MockObject $currentUser */
- $currentUser = $this->createMock(IUser::class);
- $currentUser->expects($this->exactly(3))
- ->method('getUID')
- ->willReturn('user001');
-
- $user1 = $this->createMock(IUser::class);
-
- $calls = [
- [[$currentUser], ['group1', 'group2', 'group3']],
- [[$user1], ['group1']],
- ];
- $this->groupManager->expects($this->exactly(2))
- ->method('getUserGroupIds')
- ->willReturnCallback(function () use (&$calls): array {
- $expected = array_shift($calls);
- $this->assertEquals($expected[0], func_get_args());
- return $expected[1];
- });
-
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('user1')
- ->willReturn($user1);
-
- $this->knownUserService->method('isKnownToUser')
- ->willReturnMap([
- ['user001', 'user1', false],
- ['user001', 'user2', true],
- ['user001', 'user3', true],
- ]);
-
- $this->contactsManager->expects($this->once())
- ->method('search')
- ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
- ->willReturn([
- [
- 'UID' => 'user1',
- 'isLocalSystemBook' => true
- ],
- [
- 'UID' => 'user2',
- 'isLocalSystemBook' => true
- ],
- [
- 'UID' => 'user3',
- 'isLocalSystemBook' => true
- ],
- [
- 'UID' => 'contact',
- ],
- ]);
-
- $entries = $this->contactsStore->getContacts($currentUser, '');
-
- $this->assertCount(4, $entries);
- $this->assertEquals('user1', $entries[0]->getProperty('UID'));
- $this->assertEquals('user2', $entries[1]->getProperty('UID'));
- $this->assertEquals('user3', $entries[2]->getProperty('UID'));
- $this->assertEquals('contact', $entries[3]->getProperty('UID'));
- }
-
- public function testGetContactsOnlyEnumerateIfPhoneBookOrSameGroupInOwnGroupsOnly(): void {
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
- ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'yes'],
- ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'yes'],
- ['core', 'shareapi_exclude_groups', 'no', 'no'],
- ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
- ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
- ]);
-
- /** @var IUser|MockObject $currentUser */
- $currentUser = $this->createMock(IUser::class);
- $currentUser->expects($this->exactly(3))
- ->method('getUID')
- ->willReturn('user001');
-
- $user1 = $this->createMock(IUser::class);
- $user2 = $this->createMock(IUser::class);
- $user3 = $this->createMock(IUser::class);
-
- $calls = [
- [[$currentUser], ['group1', 'group2', 'group3']],
- [[$user1], ['group1']],
- [[$user2], ['group2', 'group3']],
- [[$user3], ['group8', 'group9']],
- ];
- $this->groupManager->expects($this->exactly(4))
- ->method('getUserGroupIds')
- ->willReturnCallback(function () use (&$calls): array {
- $expected = array_shift($calls);
- $this->assertEquals($expected[0], func_get_args());
- return $expected[1];
- });
-
- $this->userManager->expects($this->exactly(3))
- ->method('get')
- ->willReturnMap([
- ['user1', $user1],
- ['user2', $user2],
- ['user3', $user3],
- ]);
-
- $this->knownUserService->method('isKnownToUser')
- ->willReturnMap([
- ['user001', 'user1', false],
- ['user001', 'user2', true],
- ['user001', 'user3', true],
- ]);
-
- $this->contactsManager->expects($this->once())
- ->method('search')
- ->with($this->equalTo(''), $this->equalTo(['FN', 'EMAIL']))
- ->willReturn([
- [
- 'UID' => 'user1',
- 'isLocalSystemBook' => true
- ],
- [
- 'UID' => 'user2',
- 'isLocalSystemBook' => true
- ],
- [
- 'UID' => 'user3',
- 'isLocalSystemBook' => true
- ],
- [
- 'UID' => 'contact',
- ],
- ]);
-
- $entries = $this->contactsStore->getContacts($currentUser, '');
-
- $this->assertCount(3, $entries);
- $this->assertEquals('user1', $entries[0]->getProperty('UID'));
- $this->assertEquals('user2', $entries[1]->getProperty('UID'));
- $this->assertEquals('contact', $entries[2]->getProperty('UID'));
- }
-
- public function testGetContactsWithFilter(): void {
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'no'],
- ['core', 'shareapi_restrict_user_enumeration_full_match', 'yes', 'yes'],
- ]);
-
- /** @var IUser|MockObject $user */
- $user = $this->createMock(IUser::class);
- $this->contactsManager->expects($this->any())
- ->method('search')
- ->willReturn([
- [
- 'UID' => 'a567',
- 'FN' => 'Darren Roner',
- 'EMAIL' => [
- 'darren@roner.au',
- ],
- 'isLocalSystemBook' => true,
- ],
- [
- 'UID' => 'john',
- 'FN' => 'John Doe',
- 'EMAIL' => [
- 'john@example.com',
- ],
- 'isLocalSystemBook' => true,
- ],
- [
- 'FN' => 'Anne D',
- 'EMAIL' => [
- 'anne@example.com',
- ],
- 'isLocalSystemBook' => false,
- ],
- ]);
- $user->expects($this->any())
- ->method('getUID')
- ->willReturn('user123');
-
- // Complete match on UID should match
- $entry = $this->contactsStore->getContacts($user, 'a567');
- $this->assertSame(2, count($entry));
- $this->assertEquals([
- 'darren@roner.au'
- ], $entry[0]->getEMailAddresses());
-
- // Partial match on UID should not match
- $entry = $this->contactsStore->getContacts($user, 'a56');
- $this->assertSame(1, count($entry));
- $this->assertEquals([
- 'anne@example.com'
- ], $entry[0]->getEMailAddresses());
-
- // Complete match on email should match
- $entry = $this->contactsStore->getContacts($user, 'john@example.com');
- $this->assertSame(2, count($entry));
- $this->assertEquals([
- 'john@example.com'
- ], $entry[0]->getEMailAddresses());
- $this->assertEquals([
- 'anne@example.com'
- ], $entry[1]->getEMailAddresses());
-
- // Partial match on email should not match
- $entry = $this->contactsStore->getContacts($user, 'john@example.co');
- $this->assertSame(1, count($entry));
- $this->assertEquals([
- 'anne@example.com'
- ], $entry[0]->getEMailAddresses());
-
- // Match on FN should not match
- $entry = $this->contactsStore->getContacts($user, 'Darren Roner');
- $this->assertSame(1, count($entry));
- $this->assertEquals([
- 'anne@example.com'
- ], $entry[0]->getEMailAddresses());
-
- // Don't filter users in local addressbook
- $entry = $this->contactsStore->getContacts($user, 'Anne D');
- $this->assertSame(1, count($entry));
- $this->assertEquals([
- 'anne@example.com'
- ], $entry[0]->getEMailAddresses());
- }
-
- public function testGetContactsWithFilterWithoutFullMatch(): void {
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'no'],
- ['core', 'shareapi_restrict_user_enumeration_full_match', 'yes', 'no'],
- ]);
-
- /** @var IUser|MockObject $user */
- $user = $this->createMock(IUser::class);
- $this->contactsManager->expects($this->any())
- ->method('search')
- ->willReturn([
- [
- 'UID' => 'a567',
- 'FN' => 'Darren Roner',
- 'EMAIL' => [
- 'darren@roner.au',
- ],
- 'isLocalSystemBook' => true,
- ],
- [
- 'UID' => 'john',
- 'FN' => 'John Doe',
- 'EMAIL' => [
- 'john@example.com',
- ],
- 'isLocalSystemBook' => true,
- ],
- [
- 'FN' => 'Anne D',
- 'EMAIL' => [
- 'anne@example.com',
- ],
- 'isLocalSystemBook' => false,
- ],
- ]);
- $user->expects($this->any())
- ->method('getUID')
- ->willReturn('user123');
-
- // Complete match on UID should not match
- $entry = $this->contactsStore->getContacts($user, 'a567');
- $this->assertSame(1, count($entry));
- $this->assertEquals([
- 'anne@example.com'
- ], $entry[0]->getEMailAddresses());
-
- // Partial match on UID should not match
- $entry = $this->contactsStore->getContacts($user, 'a56');
- $this->assertSame(1, count($entry));
- $this->assertEquals([
- 'anne@example.com'
- ], $entry[0]->getEMailAddresses());
-
- // Complete match on email should not match
- $entry = $this->contactsStore->getContacts($user, 'john@example.com');
- $this->assertSame(1, count($entry));
- $this->assertEquals([
- 'anne@example.com'
- ], $entry[0]->getEMailAddresses());
-
- // Partial match on email should not match
- $entry = $this->contactsStore->getContacts($user, 'john@example.co');
- $this->assertSame(1, count($entry));
- $this->assertEquals([
- 'anne@example.com'
- ], $entry[0]->getEMailAddresses());
-
- // Match on FN should not match
- $entry = $this->contactsStore->getContacts($user, 'Darren Roner');
- $this->assertSame(1, count($entry));
- $this->assertEquals([
- 'anne@example.com'
- ], $entry[0]->getEMailAddresses());
-
- // Don't filter users in local addressbook
- $entry = $this->contactsStore->getContacts($user, 'Anne D');
- $this->assertSame(1, count($entry));
- $this->assertEquals([
- 'anne@example.com'
- ], $entry[0]->getEMailAddresses());
- }
-
- public function testFindOneUser(): void {
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_allow_share_dialog_user_enumeration', 'yes', 'yes'],
- ['core', 'shareapi_restrict_user_enumeration_to_group', 'no', 'no'],
- ['core', 'shareapi_restrict_user_enumeration_to_phone', 'no', 'no'],
- ['core', 'shareapi_restrict_user_enumeration_full_match', 'yes', 'yes'],
- ['core', 'shareapi_exclude_groups', 'no', 'yes'],
- ['core', 'shareapi_exclude_groups_list', '', ''],
- ['core', 'shareapi_only_share_with_group_members', 'no', 'no'],
- ]);
-
- /** @var IUser|MockObject $user */
- $user = $this->createMock(IUser::class);
- $this->contactsManager->expects($this->once())
- ->method('search')
- ->with($this->equalTo('a567'), $this->equalTo(['UID']))
- ->willReturn([
- [
- 'UID' => 123,
- 'isLocalSystemBook' => false
- ],
- [
- 'UID' => 'a567',
- 'FN' => 'Darren Roner',
- 'EMAIL' => [
- 'darren@roner.au'
- ],
- 'isLocalSystemBook' => true
- ],
- ]);
- $user->expects($this->any())
- ->method('getUID')
- ->willReturn('user123');
-
- $entry = $this->contactsStore->findOne($user, 0, 'a567');
-
- $this->assertEquals([
- 'darren@roner.au'
- ], $entry->getEMailAddresses());
- }
-
- public function testFindOneEMail(): void {
- /** @var IUser|MockObject $user */
- $user = $this->createMock(IUser::class);
- $this->contactsManager->expects($this->once())
- ->method('search')
- ->with($this->equalTo('darren@roner.au'), $this->equalTo(['EMAIL']))
- ->willReturn([
- [
- 'UID' => 123,
- 'isLocalSystemBook' => false
- ],
- [
- 'UID' => 'a567',
- 'FN' => 'Darren Roner',
- 'EMAIL' => [
- 'darren@roner.au'
- ],
- 'isLocalSystemBook' => false
- ],
- ]);
- $user->expects($this->any())
- ->method('getUID')
- ->willReturn('user123');
-
- $entry = $this->contactsStore->findOne($user, 4, 'darren@roner.au');
-
- $this->assertEquals([
- 'darren@roner.au'
- ], $entry->getEMailAddresses());
- }
-
- public function testFindOneNotSupportedType(): void {
- /** @var IUser|MockObject $user */
- $user = $this->createMock(IUser::class);
-
- $entry = $this->contactsStore->findOne($user, 42, 'darren@roner.au');
-
- $this->assertEquals(null, $entry);
- }
-
- public function testFindOneNoMatches(): void {
- /** @var IUser|MockObject $user */
- $user = $this->createMock(IUser::class);
- $this->contactsManager->expects($this->once())
- ->method('search')
- ->with($this->equalTo('a567'), $this->equalTo(['UID']))
- ->willReturn([
- [
- 'UID' => 123,
- 'isLocalSystemBook' => false
- ],
- [
- 'UID' => 'a567',
- 'FN' => 'Darren Roner',
- 'EMAIL' => [
- 'darren@roner.au123'
- ],
- 'isLocalSystemBook' => false
- ],
- ]);
- $user->expects($this->never())
- ->method('getUID');
-
- $entry = $this->contactsStore->findOne($user, 0, 'a567');
-
- $this->assertEquals(null, $entry);
- }
-
- public function testGetRecentStatusFirst(): void {
- $user = $this->createMock(IUser::class);
- $status1 = new UserStatus();
- $status1->setUserId('user1');
- $status2 = new UserStatus();
- $status2->setUserId('user2');
- $this->statusService->expects(self::once())
- ->method('findAllRecentStatusChanges')
- ->willReturn([
- $status1,
- $status2,
- ]);
- $user1 = $this->createMock(IUser::class);
- $user1->method('getCloudId')->willReturn('user1@localcloud');
- $user2 = $this->createMock(IUser::class);
- $user2->method('getCloudId')->willReturn('user2@localcloud');
- $this->userManager->expects(self::exactly(2))
- ->method('get')
- ->willReturnCallback(function ($uid) use ($user1, $user2) {
- return match ($uid) {
- 'user1' => $user1,
- 'user2' => $user2,
- };
- });
- $this->contactsManager
- ->expects(self::exactly(3))
- ->method('search')
- ->willReturnCallback(function ($uid, $searchProps, $options) {
- return match ([$uid, $options['limit'] ?? null]) {
- ['user1@localcloud', 1] => [
- [
- 'UID' => 'user1',
- 'URI' => 'user1.vcf',
- ],
- ],
- ['user2@localcloud' => [], 1], // Simulate not found
- ['', 4] => [
- [
- 'UID' => 'contact1',
- 'URI' => 'contact1.vcf',
- ],
- [
- 'UID' => 'contact2',
- 'URI' => 'contact2.vcf',
- ],
- ],
- default => [],
- };
- });
-
- $contacts = $this->contactsStore->getContacts(
- $user,
- null,
- 5,
- );
-
- self::assertCount(3, $contacts);
- self::assertEquals('user1', $contacts[0]->getProperty('UID'));
- self::assertEquals('contact1', $contacts[1]->getProperty('UID'));
- self::assertEquals('contact2', $contacts[2]->getProperty('UID'));
- }
-
- public function testPaginateRecentStatus(): void {
- $user = $this->createMock(IUser::class);
- $status1 = new UserStatus();
- $status1->setUserId('user1');
- $status2 = new UserStatus();
- $status2->setUserId('user2');
- $status3 = new UserStatus();
- $status3->setUserId('user3');
- $this->statusService->expects(self::never())
- ->method('findAllRecentStatusChanges');
- $this->contactsManager
- ->expects(self::exactly(2))
- ->method('search')
- ->willReturnCallback(function ($uid, $searchProps, $options) {
- return match ([$uid, $options['limit'] ?? null, $options['offset'] ?? null]) {
- ['', 2, 0] => [
- [
- 'UID' => 'contact1',
- 'URI' => 'contact1.vcf',
- ],
- [
- 'UID' => 'contact2',
- 'URI' => 'contact2.vcf',
- ],
- ],
- ['', 2, 3] => [
- [
- 'UID' => 'contact3',
- 'URI' => 'contact3.vcf',
- ],
- ],
- default => [],
- };
- });
-
- $page1 = $this->contactsStore->getContacts(
- $user,
- null,
- 2,
- 0,
- );
- $page2 = $this->contactsStore->getContacts(
- $user,
- null,
- 2,
- 3,
- );
-
- self::assertCount(2, $page1);
- self::assertCount(1, $page2);
- }
-}
--- a/tests/lib/Contacts/ContactsMenu/EntryTest.php
+++ /dev/null
@@ -1,105 +0,0 @@
-entry = new Entry();
- }
-
- public function testSetId(): void {
- $this->entry->setId(123);
- $this->addToAssertionCount(1);
- }
-
- public function testSetGetFullName(): void {
- $fn = 'Danette Chaille';
- $this->assertEquals('', $this->entry->getFullName());
- $this->entry->setFullName($fn);
- $this->assertEquals($fn, $this->entry->getFullName());
- }
-
- public function testAddGetEMailAddresses(): void {
- $this->assertEmpty($this->entry->getEMailAddresses());
- $this->entry->addEMailAddress('user@example.com');
- $this->assertEquals(['user@example.com'], $this->entry->getEMailAddresses());
- }
-
- public function testAddAndSortAction(): void {
- // Three actions, two with equal priority
- $action1 = new LinkAction();
- $action2 = new LinkAction();
- $action3 = new LinkAction();
- $action1->setPriority(10);
- $action1->setName('Bravo');
-
- $action2->setPriority(0);
- $action2->setName('Batman');
-
- $action3->setPriority(10);
- $action3->setName('Alfa');
-
- $this->entry->addAction($action1);
- $this->entry->addAction($action2);
- $this->entry->addAction($action3);
- $sorted = $this->entry->getActions();
-
- $this->assertSame($action3, $sorted[0]);
- $this->assertSame($action1, $sorted[1]);
- $this->assertSame($action2, $sorted[2]);
- }
-
- public function testSetGetProperties(): void {
- $props = [
- 'prop1' => 123,
- 'prop2' => 'string',
- ];
-
- $this->entry->setProperties($props);
-
- $this->assertNull($this->entry->getProperty('doesntexist'));
- $this->assertEquals(123, $this->entry->getProperty('prop1'));
- $this->assertEquals('string', $this->entry->getProperty('prop2'));
- }
-
- public function testJsonSerialize(): void {
- $expectedJson = [
- 'id' => '123',
- 'fullName' => 'Guadalupe Frisbey',
- 'topAction' => null,
- 'actions' => [],
- 'lastMessage' => '',
- 'avatar' => null,
- 'emailAddresses' => ['user@example.com'],
- 'profileTitle' => null,
- 'profileUrl' => null,
- 'status' => null,
- 'statusMessage' => null,
- 'statusMessageTimestamp' => null,
- 'statusIcon' => null,
- 'isUser' => false,
- 'uid' => null,
- ];
-
- $this->entry->setId(123);
- $this->entry->setFullName('Guadalupe Frisbey');
- $this->entry->addEMailAddress('user@example.com');
- $json = $this->entry->jsonSerialize();
-
- $this->assertEquals($expectedJson, $json);
- }
-}
--- a/tests/lib/Contacts/ContactsMenu/ManagerTest.php
+++ /dev/null
@@ -1,202 +0,0 @@
-contactsStore = $this->createMock(ContactsStore::class);
- $this->actionProviderStore = $this->createMock(ActionProviderStore::class);
- $this->appManager = $this->createMock(IAppManager::class);
- $this->config = $this->createMock(IConfig::class);
-
- $this->manager = new Manager($this->contactsStore, $this->actionProviderStore, $this->appManager, $this->config);
- }
-
- private function generateTestEntries(): array {
- $entries = [];
- foreach (range('Z', 'A') as $char) {
- $entry = $this->createMock(Entry::class);
- $entry->expects($this->any())
- ->method('getFullName')
- ->willReturn('Contact ' . $char);
- $entries[] = $entry;
- }
- return $entries;
- }
-
- public function testGetFilteredEntries(): void {
- $filter = 'con';
- $user = $this->createMock(IUser::class);
- $entries = $this->generateTestEntries();
- $provider = $this->createMock(IProvider::class);
-
- $this->config->expects($this->exactly(2))
- ->method('getSystemValueInt')
- ->willReturnMap([
- ['sharing.maxAutocompleteResults', Constants::SHARING_MAX_AUTOCOMPLETE_RESULTS_DEFAULT, 25],
- ['sharing.minSearchStringLength', 0, 0],
- ]);
- $this->contactsStore->expects($this->once())
- ->method('getContacts')
- ->with($user, $filter)
- ->willReturn($entries);
- $this->actionProviderStore->expects($this->once())
- ->method('getProviders')
- ->with($user)
- ->willReturn([$provider]);
- $provider->expects($this->exactly(25))
- ->method('process');
- $this->appManager->expects($this->once())
- ->method('isEnabledForUser')
- ->with($this->equalTo('contacts'), $user)
- ->willReturn(false);
- $expected = [
- 'contacts' => array_slice($entries, 0, 25),
- 'contactsAppEnabled' => false,
- ];
-
- $data = $this->manager->getEntries($user, $filter);
-
- $this->assertEquals($expected, $data);
- }
-
- public function testGetFilteredEntriesLimit(): void {
- $filter = 'con';
- $user = $this->createMock(IUser::class);
- $entries = $this->generateTestEntries();
- $provider = $this->createMock(IProvider::class);
-
- $this->config->expects($this->exactly(2))
- ->method('getSystemValueInt')
- ->willReturnMap([
- ['sharing.maxAutocompleteResults', Constants::SHARING_MAX_AUTOCOMPLETE_RESULTS_DEFAULT, 3],
- ['sharing.minSearchStringLength', 0, 0],
- ]);
- $this->contactsStore->expects($this->once())
- ->method('getContacts')
- ->with($user, $filter)
- ->willReturn($entries);
- $this->actionProviderStore->expects($this->once())
- ->method('getProviders')
- ->with($user)
- ->willReturn([$provider]);
- $provider->expects($this->exactly(3))
- ->method('process');
- $this->appManager->expects($this->once())
- ->method('isEnabledForUser')
- ->with($this->equalTo('contacts'), $user)
- ->willReturn(false);
- $expected = [
- 'contacts' => array_slice($entries, 0, 3),
- 'contactsAppEnabled' => false,
- ];
-
- $data = $this->manager->getEntries($user, $filter);
-
- $this->assertEquals($expected, $data);
- }
-
- public function testGetFilteredEntriesMinSearchStringLength(): void {
- $filter = 'con';
- $user = $this->createMock(IUser::class);
- $provider = $this->createMock(IProvider::class);
-
- $this->config->expects($this->exactly(2))
- ->method('getSystemValueInt')
- ->willReturnMap([
- ['sharing.maxAutocompleteResults', Constants::SHARING_MAX_AUTOCOMPLETE_RESULTS_DEFAULT, 3],
- ['sharing.minSearchStringLength', 0, 4],
- ]);
- $this->appManager->expects($this->once())
- ->method('isEnabledForUser')
- ->with($this->equalTo('contacts'), $user)
- ->willReturn(false);
- $expected = [
- 'contacts' => [],
- 'contactsAppEnabled' => false,
- ];
-
- $data = $this->manager->getEntries($user, $filter);
-
- $this->assertEquals($expected, $data);
- }
-
- public function testFindOne(): void {
- $shareTypeFilter = 42;
- $shareWithFilter = 'foobar';
-
- $user = $this->createMock(IUser::class);
- $entry = current($this->generateTestEntries());
- $provider = $this->createMock(IProvider::class);
- $this->contactsStore->expects($this->once())
- ->method('findOne')
- ->with($user, $shareTypeFilter, $shareWithFilter)
- ->willReturn($entry);
- $this->actionProviderStore->expects($this->once())
- ->method('getProviders')
- ->with($user)
- ->willReturn([$provider]);
- $provider->expects($this->once())
- ->method('process');
-
- $data = $this->manager->findOne($user, $shareTypeFilter, $shareWithFilter);
-
- $this->assertEquals($entry, $data);
- }
-
- public function testFindOne404(): void {
- $shareTypeFilter = 42;
- $shareWithFilter = 'foobar';
-
- $user = $this->createMock(IUser::class);
- $provider = $this->createMock(IProvider::class);
- $this->contactsStore->expects($this->once())
- ->method('findOne')
- ->with($user, $shareTypeFilter, $shareWithFilter)
- ->willReturn(null);
- $this->actionProviderStore->expects($this->never())
- ->method('getProviders')
- ->with($user)
- ->willReturn([$provider]);
- $provider->expects($this->never())
- ->method('process');
-
- $data = $this->manager->findOne($user, $shareTypeFilter, $shareWithFilter);
-
- $this->assertEquals(null, $data);
- }
-}
--- a/tests/lib/Contacts/ContactsMenu/Providers/EMailproviderTest.php
+++ /dev/null
@@ -1,86 +0,0 @@
-actionFactory = $this->createMock(IActionFactory::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
-
- $this->provider = new EMailProvider($this->actionFactory, $this->urlGenerator);
- }
-
- public function testProcess(): void {
- $entry = $this->createMock(IEntry::class);
- $action = $this->createMock(ILinkAction::class);
- $iconUrl = 'https://example.com/img/actions/icon.svg';
- $this->urlGenerator->expects($this->once())
- ->method('imagePath')
- ->willReturn('img/actions/icon.svg');
- $this->urlGenerator->expects($this->once())
- ->method('getAbsoluteURL')
- ->with('img/actions/icon.svg')
- ->willReturn($iconUrl);
- $entry->expects($this->once())
- ->method('getEMailAddresses')
- ->willReturn([
- 'user@example.com',
- ]);
- $this->actionFactory->expects($this->once())
- ->method('newEMailAction')
- ->with($this->equalTo($iconUrl), $this->equalTo('user@example.com'), $this->equalTo('user@example.com'))
- ->willReturn($action);
- $entry->expects($this->once())
- ->method('addAction')
- ->with($action);
-
- $this->provider->process($entry);
- }
-
- public function testProcessEmptyAddress(): void {
- $entry = $this->createMock(IEntry::class);
- $iconUrl = 'https://example.com/img/actions/icon.svg';
- $this->urlGenerator->expects($this->once())
- ->method('imagePath')
- ->willReturn('img/actions/icon.svg');
- $this->urlGenerator->expects($this->once())
- ->method('getAbsoluteURL')
- ->with('img/actions/icon.svg')
- ->willReturn($iconUrl);
- $entry->expects($this->once())
- ->method('getEMailAddresses')
- ->willReturn([
- '',
- ]);
- $this->actionFactory->expects($this->never())
- ->method('newEMailAction');
- $entry->expects($this->never())
- ->method('addAction');
-
- $this->provider->process($entry);
- }
-}
--- a/tests/lib/Contacts/ContactsMenu/Providers/LocalTimeProviderTest.php
+++ /dev/null
@@ -1,194 +0,0 @@
-actionFactory = $this->createMock(IActionFactory::class);
- $this->l10nFactory = $this->createMock(IL10NFactory::class);
- $this->l = $this->createMock(IL10N::class);
- $this->l->expects($this->any())
- ->method('t')
- ->willReturnCallback(function ($text, $parameters = []) {
- return vsprintf($text, $parameters);
- });
- $this->l->expects($this->any())
- ->method('n')
- ->willReturnCallback(function ($text, $textPlural, $n, $parameters = []) {
- $formatted = str_replace('%n', (string)$n, $n === 1 ? $text : $textPlural);
- return vsprintf($formatted, $parameters);
- });
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->userManager = $this->createMock(IUserManager::class);
- $this->timeFactory = $this->createMock(ITimeFactory::class);
- $this->dateTimeFormatter = $this->createMock(IDateTimeFormatter::class);
- $this->config = $this->createMock(IConfig::class);
- $this->userSession = $this->createMock(IUserSession::class);
-
- $this->provider = new LocalTimeProvider(
- $this->actionFactory,
- $this->l10nFactory,
- $this->urlGenerator,
- $this->userManager,
- $this->timeFactory,
- $this->dateTimeFormatter,
- $this->config,
- $this->userSession,
- );
- }
-
- public static function dataTestProcess(): array {
- return [
- 'no current user' => [
- false,
- null,
- null,
- 'Local time: 10:24',
- ],
- 'both UTC' => [
- true,
- null,
- null,
- '10:24 • same time',
- ],
- 'both same time zone' => [
- true,
- 'Europe/Berlin',
- 'Europe/Berlin',
- '11:24 • same time',
- ],
- '1h behind' => [
- true,
- 'Europe/Berlin',
- 'Europe/London',
- '10:24 • 1h behind',
- ],
- '4:45h ahead' => [
- true,
- 'Europe/Berlin',
- 'Asia/Kathmandu',
- '16:09 • 4h45m ahead',
- ],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestProcess')]
- public function testProcess(bool $hasCurrentUser, ?string $currentUserTZ, ?string $targetUserTZ, string $expected): void {
- $entry = $this->createMock(IEntry::class);
- $entry->expects($this->once())
- ->method('getProperty')
- ->with('UID')
- ->willReturn('user1');
-
- $user = $this->createMock(IUser::class);
- $user->method('getUID')
- ->willReturn('user1');
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('user1')
- ->willReturn($user);
-
- $this->l10nFactory->method('get')
- ->with('lib')
- ->willReturn($this->l);
-
- $this->config->method('getSystemValueString')
- ->with('default_timezone', 'UTC')
- ->willReturn('UTC');
- $this->config
- ->method('getUserValue')
- ->willReturnMap([
- ['user1', 'core', 'timezone', '', $targetUserTZ],
- ['currentUser', 'core', 'timezone', '', $currentUserTZ],
- ]);
-
- if ($hasCurrentUser) {
- $currentUser = $this->createMock(IUser::class);
- $currentUser->method('getUID')
- ->willReturn('currentUser');
- $this->userSession->method('getUser')
- ->willReturn($currentUser);
- }
-
- $this->timeFactory->method('getDateTime')
- ->willReturnCallback(fn ($time, $tz) => (new \DateTime('2023-01-04 10:24:43', new \DateTimeZone('UTC')))->setTimezone($tz));
-
- $this->dateTimeFormatter->method('formatTime')
- ->willReturnCallback(fn (\DateTime $time) => $time->format('H:i'));
-
- $this->urlGenerator->method('imagePath')
- ->willReturn('actions/recent.svg');
- $this->urlGenerator->method('getAbsoluteURL')
- ->with('actions/recent.svg')
- ->willReturn('https://localhost/actions/recent.svg');
-
- $action = $this->createMock(ILinkAction::class);
- $this->actionFactory->expects($this->once())
- ->method('newLinkAction')
- ->with(
- 'https://localhost/actions/recent.svg',
- $expected,
- '#',
- 'timezone'
- )
- ->willReturn($action);
-
- $entry->expects($this->once())
- ->method('addAction')
- ->with($action);
-
- $this->provider->process($entry);
- }
-
- public function testProcessNoUser(): void {
- $entry = $this->createMock(IEntry::class);
- $entry->expects($this->once())
- ->method('getProperty')
- ->with('UID')
- ->willReturn('user1');
-
- $entry->expects($this->never())
- ->method('addAction');
-
- $this->provider->process($entry);
- }
-}
--- a/tests/lib/ContactsManagerTest.php
+++ /dev/null
@@ -1,314 +0,0 @@
-cm = new ContactsManager();
- }
-
- public static function searchProvider(): array {
- $search1 = [
- 0 => [
- 'N' => [0 => '', 1 => 'Jan', 2 => 'Jansen', 3 => '', 4 => '',],
- 'UID' => '04ada7f5-01f9-4309-9c82-6b555b2170ed',
- 'FN' => 'Jan Jansen',
- 'id' => '1',
- 'addressbook-key' => 'simple:1',
- ],
- 0 => [
- 'N' => [0 => '', 1 => 'Tom', 2 => 'Peeters', 3 => '', 4 => '',],
- 'UID' => '04ada7f5-01f9-4309-9c82-2345-2345--6b555b2170ed',
- 'FN' => 'Tom Peeters',
- 'id' => '2',
- 'addressbook-key' => 'simple:1',
- ],
- ];
-
- $search2 = [
- 0 => [
- 'N' => [0 => '', 1 => 'fg', 2 => '', 3 => '', 4 => '',],
- 'UID' => '04ada234h5jh357f5-01f9-4309-9c82-6b555b2170ed',
- 'FN' => 'Jan Rompuy',
- 'id' => '1',
- 'addressbook-key' => 'simple:2',
- ],
- 0 => [
- 'N' => [0 => '', 1 => 'fg', 2 => '', 3 => '', 4 => '',],
- 'UID' => '04ada7f5-01f9-4309-345kj345j9c82-2345-2345--6b555b2170ed',
- 'FN' => 'Tim Peeters',
- 'id' => '2',
- 'addressbook-key' => 'simple:2',
- ],
- ];
-
- $expectedResult = array_merge($search1, $search2);
- return [
- [
- $search1,
- $search2,
- $expectedResult
- ]
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('searchProvider')]
- public function testSearch($search1, $search2, $expectedResult): void {
- /** @var \PHPUnit\Framework\MockObject\MockObject|IAddressBook $addressbook */
- $addressbook1 = $this->getMockBuilder('\OCP\IAddressBookEnabled')
- ->disableOriginalConstructor()
- ->getMock();
-
- $addressbook1->expects($this->once())
- ->method('isEnabled')
- ->willReturn(true);
-
- $addressbook1->expects($this->once())
- ->method('search')
- ->willReturn($search1);
-
- $addressbook1->expects($this->any())
- ->method('getKey')
- ->willReturn('simple:1');
-
- $addressbook2 = $this->getMockBuilder('\OCP\IAddressBookEnabled')
- ->disableOriginalConstructor()
- ->getMock();
-
- $addressbook2->expects($this->once())
- ->method('isEnabled')
- ->willReturn(true);
-
- $addressbook2->expects($this->once())
- ->method('search')
- ->willReturn($search2);
-
- $addressbook2->expects($this->any())
- ->method('getKey')
- ->willReturn('simple:2');
-
- $this->cm->registerAddressBook($addressbook1);
- $this->cm->registerAddressBook($addressbook2);
- $result = $this->cm->search('');
- $this->assertEquals($expectedResult, $result);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('searchProvider')]
- public function testSearchDisabledAb($search1): void {
- /** @var \PHPUnit\Framework\MockObject\MockObject|IAddressBookEnabled $addressbook */
- $addressbook1 = $this->getMockBuilder('\OCP\IAddressBookEnabled')
- ->disableOriginalConstructor()
- ->getMock();
-
- $addressbook1->expects($this->once())
- ->method('isEnabled')
- ->willReturn(true);
-
- $addressbook1->expects($this->once())
- ->method('search')
- ->willReturn($search1);
-
- $addressbook1->expects($this->any())
- ->method('getKey')
- ->willReturn('simple:1');
-
- $addressbook2 = $this->getMockBuilder('\OCP\IAddressBookEnabled')
- ->disableOriginalConstructor()
- ->getMock();
-
- $addressbook2->expects($this->once())
- ->method('isEnabled')
- ->willReturn(false);
-
- $addressbook2->expects($this->never())
- ->method('search');
-
- $this->cm->registerAddressBook($addressbook1);
- $this->cm->registerAddressBook($addressbook2);
- $result = $this->cm->search('');
- $this->assertEquals($search1, $result);
- }
-
- public function testDeleteHavePermission(): void {
- /** @var \PHPUnit\Framework\MockObject\MockObject|IAddressBookEnabled $addressbook */
- $addressbook = $this->getMockBuilder('\OCP\IAddressBookEnabled')
- ->disableOriginalConstructor()
- ->getMock();
-
- $addressbook->expects($this->any())
- ->method('getPermissions')
- ->willReturn(Constants::PERMISSION_ALL);
-
- $addressbook->expects($this->once())
- ->method('delete')
- ->willReturn('returnMe');
-
- $addressbook->expects($this->any())
- ->method('getKey')
- ->willReturn('addressbookKey');
-
- $this->cm->registerAddressBook($addressbook);
- $result = $this->cm->delete(1, $addressbook->getKey());
- $this->assertEquals($result, 'returnMe');
- }
-
- public function testDeleteNoPermission(): void {
- /** @var \PHPUnit\Framework\MockObject\MockObject|IAddressBookEnabled $addressbook */
- $addressbook = $this->getMockBuilder('\OCP\IAddressBookEnabled')
- ->disableOriginalConstructor()
- ->getMock();
-
- $addressbook->expects($this->any())
- ->method('getPermissions')
- ->willReturn(Constants::PERMISSION_READ);
-
- $addressbook->expects($this->never())
- ->method('delete');
-
- $addressbook->expects($this->any())
- ->method('getKey')
- ->willReturn('addressbookKey');
-
- $this->cm->registerAddressBook($addressbook);
- $result = $this->cm->delete(1, $addressbook->getKey());
- $this->assertEquals($result, null);
- }
-
- public function testDeleteNoAddressbook(): void {
- /** @var \PHPUnit\Framework\MockObject\MockObject|IAddressBookEnabled $addressbook */
- $addressbook = $this->getMockBuilder('\OCP\IAddressBookEnabled')
- ->disableOriginalConstructor()
- ->getMock();
-
- $addressbook->expects($this->never())
- ->method('delete');
-
- $addressbook->expects($this->any())
- ->method('getKey')
- ->willReturn('addressbookKey');
-
- $this->cm->registerAddressBook($addressbook);
- $result = $this->cm->delete(1, 'noaddressbook');
- $this->assertEquals($result, null);
- }
-
- public function testCreateOrUpdateHavePermission(): void {
- /** @var \PHPUnit\Framework\MockObject\MockObject|IAddressBookEnabled $addressbook */
- $addressbook = $this->getMockBuilder('\OCP\IAddressBookEnabled')
- ->disableOriginalConstructor()
- ->getMock();
-
- $addressbook->expects($this->any())
- ->method('getPermissions')
- ->willReturn(Constants::PERMISSION_ALL);
-
- $addressbook->expects($this->once())
- ->method('createOrUpdate')
- ->willReturn('returnMe');
-
- $addressbook->expects($this->any())
- ->method('getKey')
- ->willReturn('addressbookKey');
-
- $this->cm->registerAddressBook($addressbook);
- $result = $this->cm->createOrUpdate([], $addressbook->getKey());
- $this->assertEquals($result, 'returnMe');
- }
-
- public function testCreateOrUpdateNoPermission(): void {
- /** @var \PHPUnit\Framework\MockObject\MockObject|IAddressBookEnabled $addressbook */
- $addressbook = $this->getMockBuilder('\OCP\IAddressBookEnabled')
- ->disableOriginalConstructor()
- ->getMock();
-
- $addressbook->expects($this->any())
- ->method('getPermissions')
- ->willReturn(Constants::PERMISSION_READ);
-
- $addressbook->expects($this->never())
- ->method('createOrUpdate');
-
- $addressbook->expects($this->any())
- ->method('getKey')
- ->willReturn('addressbookKey');
-
- $this->cm->registerAddressBook($addressbook);
- $result = $this->cm->createOrUpdate([], $addressbook->getKey());
- $this->assertEquals($result, null);
- }
-
- public function testCreateOrUpdateNOAdressbook(): void {
- /** @var \PHPUnit\Framework\MockObject\MockObject|IAddressBookEnabled $addressbook */
- $addressbook = $this->getMockBuilder('\OCP\IAddressBookEnabled')
- ->disableOriginalConstructor()
- ->getMock();
-
- $addressbook->expects($this->never())
- ->method('createOrUpdate');
-
- $addressbook->expects($this->any())
- ->method('getKey')
- ->willReturn('addressbookKey');
-
- $this->cm->registerAddressBook($addressbook);
- $result = $this->cm->createOrUpdate([], 'noaddressbook');
- $this->assertEquals($result, null);
- }
-
- public function testIsEnabledIfNot(): void {
- $result = $this->cm->isEnabled();
- $this->assertFalse($result);
- }
-
- public function testIsEnabledIfSo(): void {
- /** @var \PHPUnit\Framework\MockObject\MockObject|IAddressBookEnabled $addressbook */
- $addressbook = $this->getMockBuilder('\OCP\IAddressBookEnabled')
- ->disableOriginalConstructor()
- ->getMock();
-
- $addressbook->expects($this->any())
- ->method('getKey')
- ->willReturn('addressbookKey');
-
- $this->cm->registerAddressBook($addressbook);
- $result = $this->cm->isEnabled();
- $this->assertTrue($result);
- }
-
- public function testAddressBookEnumeration(): void {
- // create mock for the addressbook
- /** @var \PHPUnit\Framework\MockObject\MockObject|IAddressBookEnabled $addressbook */
- $addressbook = $this->getMockBuilder('\OCP\IAddressBookEnabled')
- ->disableOriginalConstructor()
- ->getMock();
-
- // setup return for method calls
- $addressbook->expects($this->any())
- ->method('getKey')
- ->willReturn('SIMPLE_ADDRESS_BOOK');
- $addressbook->expects($this->any())
- ->method('getDisplayName')
- ->willReturn('A very simple Addressbook');
-
- // register the address book
- $this->cm->registerAddressBook($addressbook);
- $all_books = $this->cm->getUserAddressBooks();
-
- $this->assertEquals(1, count($all_books));
- $this->assertEquals($addressbook, $all_books['SIMPLE_ADDRESS_BOOK']);
- }
-}
--- a/tests/lib/DB/AdapterTest.php
+++ /dev/null
@@ -1,211 +0,0 @@
-connection = Server::get(IDBConnection::class);
- $this->appId = substr(uniqid('test_db_adapter', true), 0, 32);
- }
-
- #[\Override]
- public function tearDown(): void {
- $qb = $this->connection->getQueryBuilder();
-
- $qb->delete('appconfig')
- ->from('appconfig')
- ->where($qb->expr()->eq('appid', $qb->createNamedParameter($this->appId)))
- ->executeStatement();
- parent::tearDown();
- }
-
- public function testInsertIgnoreOnConflictDuplicate(): void {
- $configKey = uniqid('key', true);
- $expected = [
- [
- 'configkey' => $configKey,
- 'configvalue' => '1',
- ]
- ];
- $result = $this->connection->insertIgnoreConflict('appconfig', [
- 'appid' => $this->appId,
- 'configkey' => $configKey,
- 'configvalue' => '1',
- ]);
- $this->assertEquals(1, $result);
- $rows = $this->getRows($configKey);
- $this->assertSame($expected, $rows);
-
- $result = $this->connection->insertIgnoreConflict('appconfig', [
- 'appid' => $this->appId,
- 'configkey' => $configKey,
- 'configvalue' => '2',
- ]);
- $this->assertEquals(0, $result);
- $rows = $this->getRows($configKey);
- $this->assertSame($expected, $rows);
- }
-
- private function getRows(string $configKey): array {
- $qb = $this->connection->getQueryBuilder();
- return $qb->select(['configkey', 'configvalue'])
- ->from('appconfig')
- ->where($qb->expr()->eq('appid', $qb->createNamedParameter($this->appId)))
- ->andWhere($qb->expr()->eq('configkey', $qb->createNamedParameter($configKey)))
- ->executeQuery()
- ->fetchAllAssociative();
- }
-
- public function fetchAssociative(): void {
- $insert = $this->connection->getQueryBuilder();
- $insert->insert('appconfig')
- ->values([
- 'appid' => $this->appId,
- 'configkey' => 'test',
- 'configvalue' => '1',
- ])
- ->executeStatement();
-
- // fetch all associative
- $qb = $this->connection->getQueryBuilder();
- $result = $qb->select(['configkey', 'configvalue', 'appid'])
- ->from('appconfig')
- ->executeQuery();
-
- $rows = $result->fetchAllAssociative();
- $this->assertEquals([
- [
- 'appid' => $this->appId,
- 'configkey' => 'test',
- 'configvalue' => '1',
- ]
- ], $rows);
-
- // fetch associative
- $qb = $this->connection->getQueryBuilder();
- $result = $qb->select(['configkey', 'configvalue', 'appid'])
- ->from('appconfig')
- ->executeQuery();
- $row = $result->fetchAssociative();
- $this->assertEquals([
- 'appid' => $this->appId,
- 'configkey' => 'test',
- 'configvalue' => '1',
- ], $row);
-
- // iterate associative
- $qb = $this->connection->getQueryBuilder();
- $result = $qb->select(['configkey', 'configvalue', 'appid'])
- ->from('appconfig')
- ->executeQuery();
- $row = iterator_to_array($result->iterateAssociative());
- $this->assertEquals([
- 'appid' => $this->appId,
- 'configkey' => 'test',
- 'configvalue' => '1',
- ], $row);
- }
-
- public function fetchNumeric(): void {
- $insert = $this->connection->getQueryBuilder();
- $insert->insert('appconfig')
- ->values([
- 'appid' => $this->appId,
- 'configkey' => 'test',
- 'configvalue' => '1',
- ])
- ->executeStatement();
-
- // fetch all associative
- $qb = $this->connection->getQueryBuilder();
- $result = $qb->select(['configkey', 'configvalue', 'appid'])
- ->from('appconfig')
- ->executeQuery();
-
- $rows = $result->fetchAllNumeric();
- $this->assertEquals([
- [
- 0 => $this->appId,
- 1 => 'test',
- 2 => '1',
- ]
- ], $rows);
-
- // fetch associative
- $qb = $this->connection->getQueryBuilder();
- $result = $qb->select(['configkey', 'configvalue', 'appid'])
- ->from('appconfig')
- ->executeQuery();
- $row = $result->fetchNumeric();
- $this->assertEquals([
- 0 => $this->appId,
- 1 => 'test',
- 2 => '1',
- ], $row);
-
- // iterate associative
- $qb = $this->connection->getQueryBuilder();
- $result = $qb->select(['configkey', 'configvalue', 'appid'])
- ->from('appconfig')
- ->executeQuery();
- $row = iterator_to_array($result->iterateNumeric());
- $this->assertEquals([
- 0 => $this->appId,
- 1 => 'test',
- 2 => '1',
- ], $row);
- }
-
- public function fetchOne(): void {
- $insert = $this->connection->getQueryBuilder();
- $insert->insert('appconfig')
- ->values([
- 'appid' => $this->appId,
- 'configkey' => 'test',
- 'configvalue' => '1',
- ])
- ->executeStatement();
-
- // fetch all associative
- $qb = $this->connection->getQueryBuilder();
- $result = $qb->select(['configkey', 'configvalue', 'appid'])
- ->from('appconfig')
- ->executeQuery();
-
- $rows = $result->fetchFirstColumn();
- $this->assertEquals($this->appId, $rows);
-
- // fetch associative
- $qb = $this->connection->getQueryBuilder();
- $result = $qb->select(['configkey', 'configvalue', 'appid'])
- ->from('appconfig')
- ->executeQuery();
- $row = $result->fetchFirstColumn();
- $this->assertEquals($this->appId, $row);
-
- // iterate associative
- $qb = $this->connection->getQueryBuilder();
- $result = $qb->select(['configkey', 'configvalue', 'appid'])
- ->from('appconfig')
- ->executeQuery();
- $rows = iterator_to_array($result->iterateNumeric());
- $this->assertEquals([$this->appId], $rows);
- }
-}
--- a/tests/lib/DB/ConnectionFactoryTest.php
+++ /dev/null
@@ -1,127 +0,0 @@
- '127.0.0.1']],
- ['db.example.org', ['host' => 'db.example.org']],
- ['unix', ['host' => 'unix']],
- ['[::1]', ['host' => '[::1]']],
- ['127.0.0.1:3306', ['host' => '127.0.0.1', 'port' => 3306]],
- ['db.example.org:3306', ['host' => 'db.example.org', 'port' => 3306]],
- ['unix:3306', ['host' => 'unix', 'port' => 3306]],
- ['[::1]:3306', ['host' => '[::1]', 'port' => 3306]],
- ['unix:/socket', ['host' => 'unix', 'unix_socket' => '/socket']],
- ];
- }
-
- /**
- * @param string $host
- * @param array $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('splitHostFromPortAndSocketData')]
- public function testSplitHostFromPortAndSocket($host, array $expected): void {
- /** @var SystemConfig $config */
- $config = $this->createMock(SystemConfig::class);
- $cacheFactory = $this->createMock(ICacheFactory::class);
- $factory = new ConnectionFactory($config, $cacheFactory);
-
- $this->assertEquals($expected, self::invokePrivate($factory, 'splitHostFromPortAndSocket', [$host]));
- }
-
- /**
- * The numeric value of a PDO MySQL attribute, e.g. `SSL_CA`.
- *
- * The values are not stable across PHP versions, so they must never be hardcoded.
- * Since PHP 8.5 the `PDO::MYSQL_ATTR_*` constants are deprecated in favor of
- * `Pdo\Mysql::ATTR_*`, and either only exists with the MySQL driver installed.
- */
- private function mysqlAttribute(string $name): int {
- if (!extension_loaded('pdo_mysql')) {
- $this->markTestSkipped('The pdo_mysql extension is required to resolve the PDO attribute values');
- }
- if (PHP_VERSION_ID >= 80500 && class_exists(\Pdo\Mysql::class)) {
- return (int)constant('Pdo\Mysql::ATTR_' . $name);
- }
- return (int)constant('PDO::MYSQL_ATTR_' . $name);
- }
-
- public function testMysqlSslConnection(): void {
- /** @var SystemConfig|\PHPUnit\Framework\MockObject\MockObject $config */
- $config = $this->createMock(SystemConfig::class);
- $config->method('getValue')
- ->willReturnCallback(function ($key, $default) {
- return match ($key) {
- 'dbdriveroptions' => [
- $this->mysqlAttribute('SSL_CA') => 'rootCA.crt',
- $this->mysqlAttribute('SSL_CERT') => 'client.crt',
- $this->mysqlAttribute('SSL_KEY') => 'client.key',
- $this->mysqlAttribute('SSL_VERIFY_SERVER_CERT') => true,
- ],
- 'dbtype' => 'mysql',
- default => $default,
- };
- });
- $factory = new ConnectionFactory($config);
-
- $params = $factory->createConnectionParams();
-
- $this->assertEquals('pdo_mysql', $params['driver']);
- $this->assertEquals([
- $this->mysqlAttribute('FOUND_ROWS') => true,
- $this->mysqlAttribute('SSL_CA') => 'rootCA.crt',
- $this->mysqlAttribute('SSL_CERT') => 'client.crt',
- $this->mysqlAttribute('SSL_KEY') => 'client.key',
- $this->mysqlAttribute('SSL_VERIFY_SERVER_CERT') => true,
- ], $params['driverOptions']);
- }
-
- public function testPgsqlSslConnection(): void {
- /** @var SystemConfig|\PHPUnit\Framework\MockObject\MockObject $config */
- $config = $this->createMock(SystemConfig::class);
- $config->method('getValue')
- ->willReturnCallback(function ($key, $default) {
- return match ($key) {
- 'dbtype' => 'pgsql',
- 'dbdriveroptions' => [
- 1 => 'foo',
- 3 => 'bar',
- ],
- 'pgsql_ssl' => [
- 'mode' => 'verify-full',
- 'cert' => 'client.crt',
- 'key' => 'client.key',
- 'crl' => 'client.crl',
- 'rootcert' => 'rootCA.crt',
- ],
- default => $default,
- };
- });
- $factory = new ConnectionFactory($config);
-
- $params = $factory->createConnectionParams();
-
- $this->assertEquals('pdo_pgsql', $params['driver']);
- $this->assertEquals('verify-full', $params['sslmode']);
- $this->assertEquals('rootCA.crt', $params['sslrootcert']);
- $this->assertEquals('client.crt', $params['sslcert']);
- $this->assertEquals('client.key', $params['sslkey']);
- $this->assertEquals('client.crl', $params['sslcrl']);
- $this->assertEquals([
- 1 => 'foo',
- 3 => 'bar',
- ], $params['driverOptions']);
- }
-}
--- a/tests/lib/DB/ConnectionTest.php
+++ /dev/null
@@ -1,154 +0,0 @@
- 'test',
- 'password' => 'topsecret',
- 'host' => 'test',
- ];
- $adapter = $this->createMock(Adapter::class);
- $driver = $this->createMock(Driver::class);
- $configuration = $this->createMock(Configuration::class);
- $connection = $this->getMockBuilder(Connection::class)
- ->onlyMethods(['connectTo'])
- ->setConstructorArgs([
- [
- 'adapter' => $adapter,
- 'platform' => new MySQLPlatform(),
- 'tablePrefix' => 'nctest',
- 'primary' => $connectionParams,
- 'replica' => [
- $connectionParams,
- ],
- ],
- $driver,
- $configuration,
- ])
- ->getMock();
- $driverConnection = $this->createMock(DriverConnection::class);
- $connection->expects(self::once())
- ->method('connectTo')
- ->with('primary')
- ->willReturn($driverConnection);
-
- $connection->ensureConnectedToReplica();
- $connection->ensureConnectedToPrimary();
- $connection->ensureConnectedToReplica();
- }
-
- public function testClusterConnectsToPrimaryAndReplica(): void {
- $connectionParamsPrimary = [
- 'user' => 'test',
- 'password' => 'topsecret',
- 'host' => 'testprimary',
- ];
- $connectionParamsReplica = [
- 'user' => 'test',
- 'password' => 'topsecret',
- 'host' => 'testreplica',
- ];
- $adapter = $this->createMock(Adapter::class);
- $driver = $this->createMock(Driver::class);
- $configuration = $this->createMock(Configuration::class);
- $connection = $this->getMockBuilder(Connection::class)
- ->onlyMethods(['connectTo'])
- ->setConstructorArgs([
- [
- 'adapter' => $adapter,
- 'platform' => new MySQLPlatform(),
- 'tablePrefix' => 'nctest',
- 'primary' => $connectionParamsPrimary,
- 'replica' => [
- $connectionParamsReplica,
- ],
- ],
- $driver,
- $configuration,
- ])
- ->getMock();
- $driverConnection = $this->createMock(DriverConnection::class);
- $connection->expects(self::exactly(2))
- ->method('connectTo')
- ->willReturn($driverConnection);
-
- $connection->ensureConnectedToReplica();
- $connection->ensureConnectedToPrimary();
- $connection->ensureConnectedToReplica();
- }
-
- public function testSuccessfulQueryResetsConnectivityCheckTimer(): void {
- $inner = $this->getInnerConnection();
-
- // Ensure the connection is established before touching the timer
- $qb = $inner->getQueryBuilder();
- $qb->select('configvalue')->from('appconfig')->setMaxResults(1);
- $qb->executeQuery()->closeCursor();
-
- $property = $this->backdateLastConnectionCheck($inner);
- $before = time();
-
- $qb->executeQuery()->closeCursor();
-
- // A connectivity probe firing between adjacent operations would reset
- // the driver level last insert id on MySQL
- self::assertGreaterThanOrEqual($before, max($property->getValue($inner)));
- }
-
- public function testPreparedStatementExecutionResetsConnectivityCheckTimer(): void {
- $inner = $this->getInnerConnection();
-
- $statement = $inner->prepare('SELECT `configvalue` FROM `*PREFIX*appconfig`', 1);
-
- $property = $this->backdateLastConnectionCheck($inner);
- $before = time();
-
- $statement->executeQuery()->free();
-
- self::assertGreaterThanOrEqual($before, max($property->getValue($inner)));
- }
-
- private function getInnerConnection(): Connection {
- $connection = Server::get(IDBConnection::class);
- if (!$connection instanceof ConnectionAdapter) {
- self::markTestSkipped('Test requires the real database connection');
- }
-
- return $connection->getInner();
- }
-
- /**
- * Make the connectivity check timer stale, but by less than the check
- * interval: the probe must not fire, so only actual query activity can
- * refresh the timer.
- */
- private function backdateLastConnectionCheck(Connection $connection): \ReflectionProperty {
- $property = new \ReflectionProperty(Connection::class, 'lastConnectionCheck');
- $property->setValue($connection, ['primary' => time() - 20, 'replica' => time() - 20]);
-
- return $property;
- }
-
-}
--- a/tests/lib/DB/Exception/DbalExceptionTest.php
+++ /dev/null
@@ -1,76 +0,0 @@
-driverException = $this->createMock(TheDriverException::class);
- }
-
- /**
- * @param string $class
- * @param int $reason
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataDriverException')]
- public function testDriverException(string $class, int $reason): void {
- $result = DbalException::wrap(new $class($this->driverException, null));
- $this->assertSame($reason, $result->getReason());
- }
-
- public static function dataDriverException(): array {
- return [
- [LockWaitTimeoutException::class, DbalException::REASON_LOCK_WAIT_TIMEOUT],
- [ForeignKeyConstraintViolationException::class, DbalException::REASON_FOREIGN_KEY_VIOLATION],
- [NotNullConstraintViolationException::class, DbalException::REASON_NOT_NULL_CONSTRAINT_VIOLATION],
- [UniqueConstraintViolationException::class, DbalException::REASON_UNIQUE_CONSTRAINT_VIOLATION],
- [ConstraintViolationException::class, DbalException::REASON_CONSTRAINT_VIOLATION],
- [DatabaseObjectExistsException::class, DbalException::REASON_DATABASE_OBJECT_EXISTS],
- [DatabaseObjectNotFoundException::class, DbalException::REASON_DATABASE_OBJECT_NOT_FOUND],
- [DeadlockException::class, DbalException::REASON_DEADLOCK],
- [InvalidFieldNameException::class, DbalException::REASON_INVALID_FIELD_NAME],
- [NonUniqueFieldNameException::class, DbalException::REASON_NON_UNIQUE_FIELD_NAME],
- [SyntaxErrorException::class, DbalException::REASON_SYNTAX_ERROR],
- [ServerException::class, DbalException::REASON_SERVER],
- [DriverException::class, DbalException::REASON_DRIVER],
- ];
- }
-
- public function testConnectionException(): void {
- $result = DbalException::wrap(ConnectionException::noActiveTransaction());
- $this->assertSame(DbalException::REASON_CONNECTION_LOST, $result->getReason());
- }
-
- public function testInvalidArgumentException(): void {
- $result = DbalException::wrap(InvalidArgumentException::fromEmptyCriteria());
- $this->assertSame(DbalException::REASON_INVALID_ARGUMENT, $result->getReason());
- }
-}
--- a/tests/lib/DB/Middleware/UtcTimezoneMiddlewareDriverTest.php
+++ /dev/null
@@ -1,109 +0,0 @@
-connection = $this->getRootDbConnection();
- if ($this->connection === null) {
- $this->markTestSkipped('No root database credentials provided (DB_ROOT_USER, DB_ROOT_PASSWORD), cannot run test');
- return;
- }
-
- $provider = $this->connection->getDatabaseProvider();
- if ($provider === IDBConnection::PLATFORM_MARIADB || $provider === IDBConnection::PLATFORM_MYSQL) {
- $this->connection->executeStatement("SET GLOBAL time_zone = 'America/New_York'");
- } else {
- $this->markTestSkipped('This test only works with MySQL/MariaDB');
- }
- }
-
- #[\Override]
- protected function tearDown(): void {
- if ($this->connection !== null) {
- $provider = $this->connection->getDatabaseProvider();
- if ($provider === IDBConnection::PLATFORM_MARIADB || $provider === IDBConnection::PLATFORM_MYSQL) {
- $this->connection->executeStatement("SET GLOBAL time_zone = 'SYSTEM'");
- }
- $this->connection->close();
- }
-
- parent::tearDown();
- }
-
- public function testSqlNowIsInUtc() {
- $connection = $this->getDbConnection();
- $result = $connection->executeQuery('SELECT NOW()');
- $data = $result->fetchOne();
- $connection->close();
-
- self::assertIsString($data, 'Expected a string from the database');
-
- $expected = new \DateTimeImmutable('now', new \DateTimeZone('UTC'));
- $received = \DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $data, new \DateTimeZone('UTC'));
-
- $diff = abs($received->getTimestamp() - $expected->getTimestamp());
- self::assertLessThan(15 * 60, $diff); // allow up to 15 minutes of difference, to account for slow test environments and time sync issues.
- }
-
- /**
- * Get a new database connection.
- * This is needed because the setup is changing the global timezone setting,
- * but its only applied for new connections.
- */
- private function getDbConnection(array $overrides = []): Connection {
- $config = Server::get(IConfig::class);
- $cf = Server::get(ConnectionFactory::class);
- return $cf->getConnection(
- $config->getSystemValue('dbtype'),
- [
- 'host' => $config->getSystemValue('dbhost'),
- 'user' => $config->getSystemValue('dbuser'),
- 'password' => $config->getSystemValue('dbpassword'),
- 'tablePrefix' => $config->getSystemValue('dbtableprefix'),
- 'dbname' => $config->getSystemValue('dbname'),
- ...$overrides,
- ],
- );
- }
- /**
- * Get the database connection as root user,
- * so that we can change the global timezone setting.
- */
- private function getRootDbConnection(): ?Connection {
- $rootUser = getenv('DB_ROOT_USER') ?: '';
- $rootPassword = getenv('DB_ROOT_PASSWORD') ?: '';
- if ($rootPassword === '' || $rootUser === '') {
- return null;
- }
-
- return $this->getDbConnection([
- 'user' => $rootUser,
- 'password' => $rootPassword,
- ]);
- }
-}
--- a/tests/lib/DB/Middleware/UtcTimezoneMiddlewareTest.php
+++ /dev/null
@@ -1,24 +0,0 @@
-createMock(\Doctrine\DBAL\Driver::class);
- $middleware = new UtcTimezoneMiddleware();
- $wrappedDriver = $middleware->wrap($driver);
-
- $this->assertInstanceOf(UtcTimezoneMiddlewareDriver::class, $wrappedDriver);
- }
-}
--- a/tests/lib/DB/MigrationServiceTest.php
+++ /dev/null
@@ -1,1022 +0,0 @@
-db = $this->createMock(Connection::class);
- $this->db
- ->expects($this->any())
- ->method('getPrefix')
- ->willReturn('test_oc_');
-
- $this->migrationService = new MigrationService('testing', $this->db);
- }
-
- public function testGetters(): void {
- $this->assertEquals('testing', $this->migrationService->getApp());
- $this->assertEquals(\OC::$SERVERROOT . '/apps/testing/lib/Migration', $this->migrationService->getMigrationsDirectory());
- $this->assertEquals('OCA\Testing\Migration', $this->migrationService->getMigrationsNamespace());
- $this->assertEquals('test_oc_migrations', $this->migrationService->getMigrationsTableName());
- }
-
- public function testCore(): void {
- $migrationService = new MigrationService('core', $this->db);
-
- $this->assertEquals('core', $migrationService->getApp());
- $this->assertEquals(\OC::$SERVERROOT . '/core/Migrations', $migrationService->getMigrationsDirectory());
- $this->assertEquals('OC\Core\Migrations', $migrationService->getMigrationsNamespace());
- $this->assertEquals('test_oc_migrations', $migrationService->getMigrationsTableName());
- }
-
- public function testExecuteUnknownStep(): void {
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Version 20170130180000 is unknown.');
-
- $this->migrationService->executeStep('20170130180000');
- }
-
- public function testUnknownApp(): void {
- $this->expectException(AppPathNotFoundException::class);
- $this->expectExceptionMessage('Could not find path for unknown_bloody_app');
-
- new MigrationService('unknown_bloody_app', $this->db);
- }
-
- public function testExecuteStepWithUnknownClass(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Migration step \'X\' is unknown');
-
- $migrationService = $this->getMockBuilder(MigrationService::class)
- ->onlyMethods(['findMigrations'])
- ->setConstructorArgs(['testing', $this->db])
- ->getMock();
- $migrationService->expects($this->any())->method('findMigrations')->willReturn(
- ['20170130180000' => 'X', '20170130180001' => 'Y', '20170130180002' => 'Z', '20170130180003' => 'A']
- );
- $migrationService->executeStep('20170130180000');
- }
-
- public function testExecuteStepWithSchemaChange(): void {
- $schema = $this->createMock(Schema::class);
- $this->db->expects($this->any())
- ->method('createSchema')
- ->willReturn($schema);
-
- $this->db->expects($this->once())
- ->method('migrateToSchema');
-
- $wrappedSchema = $this->createMock(Schema::class);
- $wrappedSchema->expects($this->atLeast(2))
- ->method('getTables')
- ->willReturn([]);
- $wrappedSchema->expects($this->atLeast(2))
- ->method('getSequences')
- ->willReturn([]);
-
- $schemaResult = $this->createMock(SchemaWrapper::class);
- $schemaResult->expects($this->once())
- ->method('getWrappedSchema')
- ->willReturn($wrappedSchema);
-
- $step = $this->createMock(IMigrationStep::class);
- $step->expects($this->once())
- ->method('preSchemaChange');
- $step->expects($this->once())
- ->method('changeSchema')
- ->willReturn($schemaResult);
- $step->expects($this->once())
- ->method('postSchemaChange');
-
- $migrationService = $this->getMockBuilder(MigrationService::class)
- ->onlyMethods(['createInstance'])
- ->setConstructorArgs(['testing', $this->db])
- ->getMock();
-
- $migrationService->expects($this->any())
- ->method('createInstance')
- ->with('20170130180000')
- ->willReturn($step);
-
- $migrationService->executeStep('20170130180000');
- }
-
- public function testExecuteStepWithoutSchemaChange(): void {
- $schema = $this->createMock(Schema::class);
- $this->db->expects($this->any())
- ->method('createSchema')
- ->willReturn($schema);
-
- $this->db->expects($this->never())
- ->method('migrateToSchema');
-
- $step = $this->createMock(IMigrationStep::class);
- $step->expects($this->once())
- ->method('preSchemaChange');
- $step->expects($this->once())
- ->method('changeSchema')
- ->willReturn(null);
- $step->expects($this->once())
- ->method('postSchemaChange');
-
- $migrationService = $this->getMockBuilder(MigrationService::class)
- ->onlyMethods(['createInstance'])
- ->setConstructorArgs(['testing', $this->db])
- ->getMock();
-
- $migrationService->expects($this->any())
- ->method('createInstance')
- ->with('20170130180000')
- ->willReturn($step);
-
- $migrationService->executeStep('20170130180000');
- }
-
- public static function dataGetMigration(): array {
- return [
- ['current', '10000Date20200819121721'],
- ['prev', '8000Date20200407115318'],
- ['next', '20000Date20240717180417'],
- ['latest', '20000Date20240718031959'],
- ];
- }
-
- /**
- * @param string $alias
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetMigration')]
- public function testGetMigration($alias, $expected): void {
- $migrationService = $this->getMockBuilder(MigrationService::class)
- ->onlyMethods(['getMigratedVersions', 'findMigrations'])
- ->setConstructorArgs(['testing', $this->db])
- ->getMock();
-
- $migrationService->expects($this->any())->method('getMigratedVersions')->willReturn(
- [
- '8000Date20200407115318',
- '10000Date20200819121721',
- ]
- );
- $migrationService->expects($this->any())->method('findMigrations')->willReturn(
- [
- '20000Date20240718031959' => 'D',
- '10000Date20200819121721' => 'B',
- '8000Date20200407115318' => 'A',
- '20000Date20240717180417' => 'C',
- ]
- );
-
- $this->assertSame([
- '8000Date20200407115318',
- '10000Date20200819121721',
- '20000Date20240717180417',
- '20000Date20240718031959',
- ], $migrationService->getAvailableVersions());
-
- $migration = $migrationService->getMigration($alias);
- $this->assertSame($expected, $migration);
- }
-
- #[Group('DB')]
- public function testGetMigratedVersionsSortsByVersionThenDate(): void {
- /** @var Connection $db */
- $db = Server::get(Connection::class);
- $appId = 'migration_sort_' . bin2hex(random_bytes(8));
-
- $migrationService = new class('testing', $db, $appId) extends MigrationService {
- public function __construct(
- string $appName,
- Connection $connection,
- private string $migrationApp,
- ) {
- parent::__construct($appName, $connection);
- }
-
- #[\Override]
- public function getApp(): string {
- return $this->migrationApp;
- }
- };
-
- // Ensure the migrations table exists before inserting the fixtures.
- self::assertSame([], $migrationService->getMigratedVersions());
-
- $versions = [
- '20000Date20240718031959',
- '10000Date20200819121721',
- '8000Date20200407115318',
- '20000Date20240717180417',
- ];
-
- try {
- foreach ($versions as $version) {
- $db->insertIfNotExist('*PREFIX*migrations', [
- 'app' => $appId,
- 'version' => $version,
- ]);
- }
-
- self::assertSame([
- '8000Date20200407115318',
- '10000Date20200819121721',
- '20000Date20240717180417',
- '20000Date20240718031959',
- ], $migrationService->getMigratedVersions());
- } finally {
- $qb = $db->getQueryBuilder();
- $qb->delete('migrations')
- ->where($qb->expr()->eq(
- 'app',
- $qb->createNamedParameter($appId),
- ))
- ->executeStatement();
- }
- }
-
- public function testMigrate(): void {
- $migrationService = $this->getMockBuilder(MigrationService::class)
- ->onlyMethods(['getMigratedVersions', 'findMigrations', 'executeStep'])
- ->setConstructorArgs(['testing', $this->db])
- ->getMock();
-
- $migrationService->expects($this->any())->method('getMigratedVersions')->willReturn(
- [
- '8000Date20200407115318',
- '10000Date20200819121721',
- ]
- );
- $migrationService->expects($this->any())->method('findMigrations')->willReturn(
- [
- '20000Date20240718031959' => 'D',
- '10000Date20200819121721' => 'B',
- '8000Date20200407115318' => 'A',
- '20000Date20240717180417' => 'C',
- ]
- );
-
- $this->assertSame([
- '8000Date20200407115318',
- '10000Date20200819121721',
- '20000Date20240717180417',
- '20000Date20240718031959',
- ], $migrationService->getAvailableVersions());
-
- $calls = [];
- $migrationService
- ->expects($this->exactly(2))
- ->method('executeStep')
- ->willReturnCallback(function (string $migration) use (&$calls): void {
- $calls[] = $migration;
- });
-
- $migrationService->migrate();
- self::assertSame([
- '20000Date20240717180417',
- '20000Date20240718031959',
- ], $calls);
- }
-
- #[DataProvider('dataEnsureNamingConstraintsTableName')]
- public function testEnsureNamingConstraintsTableName(string $name, int $prefixLength, bool $tableExists, bool $throws): void {
- if ($throws) {
- $this->expectException(\InvalidArgumentException::class);
- }
-
- $table = $this->createMock(Table::class);
- $table->expects($this->atLeastOnce())
- ->method('getName')
- ->willReturn($name);
- $table->expects($this->any())
- ->method('getColumns')
- ->willReturn([]);
- $table->expects($this->any())
- ->method('getIndexes')
- ->willReturn([]);
- $table->expects($this->any())
- ->method('getForeignKeys')
- ->willReturn([]);
-
- $schema = $this->createMock(Schema::class);
- $schema->expects($this->once())
- ->method('getTables')
- ->willReturn([$table]);
- $schema->expects(self::once())
- ->method('getSequences')
- ->willReturn([]);
-
- $sourceSchema = $this->createMock(Schema::class);
- $sourceSchema->expects($this->any())
- ->method('getTable')
- ->willReturnCallback(fn () => match($tableExists) {
- false => throw new SchemaException(),
- true => $table,
- });
- $sourceSchema->expects($this->any())
- ->method('hasSequence')
- ->willReturn(false);
-
- $this->migrationService->ensureNamingConstraints($sourceSchema, $schema, $prefixLength);
- }
-
- public static function dataEnsureNamingConstraintsTableName(): array {
- return [
- 'valid name' => [
- \str_repeat('x', 60), // table name
- 3, // prefix length
- false, // has this table
- false, // throws
- ],
- 'valid name - long prefix' => [
- \str_repeat('x', 55),
- 8,
- false,
- false,
- ],
- 'too long but not a new table' => [
- \str_repeat('x', 61),
- 3,
- true,
- false,
- ],
- 'too long' => [
- \str_repeat('x', 61),
- 3,
- false,
- true,
- ],
- 'too long with prefix' => [
- \str_repeat('x', 60),
- 4,
- false,
- true,
- ],
- ];
- }
-
- #[DataProvider('dataEnsureNamingConstraintsPrimaryDefaultKey')]
- public function testEnsureNamingConstraintsPrimaryDefaultKey(string $tableName, int $prefixLength, string $platform, bool $throws): void {
- if ($throws) {
- $this->expectException(\InvalidArgumentException::class);
- }
-
- $this->db->expects(self::atLeastOnce())
- ->method('getDatabaseProvider')
- ->willReturn($platform);
-
- $defaultName = match ($platform) {
- IDBConnection::PLATFORM_POSTGRES => $tableName . '_pkey',
- IDBConnection::PLATFORM_ORACLE => $tableName . '_seq',
- default => 'PRIMARY',
- };
-
- $index = $this->createMock(Index::class);
- $index->expects($this->any())
- ->method('getName')
- ->willReturn($defaultName);
- $index->expects($this->any())
- ->method('getColumns')
- ->willReturn([]);
-
- $table = $this->createMock(Table::class);
- $table->expects($this->any())
- ->method('getName')
- ->willReturn($tableName);
-
- $table->expects($this->once())
- ->method('getColumns')
- ->willReturn([]);
- $table->expects($this->once())
- ->method('getIndexes')
- ->willReturn([]);
- $table->expects($this->once())
- ->method('getForeignKeys')
- ->willReturn([]);
- $table->expects($this->once())
- ->method('getPrimaryKey')
- ->willReturn($index);
-
- $schema = $this->createMock(Schema::class);
- $schema->expects($this->once())
- ->method('getTables')
- ->willReturn([$table]);
- $schema->expects($this->atMost(1))
- ->method('getSequences')
- ->willReturn([]);
-
- $sourceSchema = $this->createMock(Schema::class);
- $sourceSchema->expects($this->any())
- ->method('getTable')
- ->willThrowException(new SchemaException());
- $sourceSchema->expects($this->any())
- ->method('hasSequence')
- ->willReturn(false);
-
- $this->migrationService->ensureNamingConstraints($sourceSchema, $schema, $prefixLength);
- }
-
- public static function dataEnsureNamingConstraintsPrimaryDefaultKey(): array {
- foreach ([IDBConnection::PLATFORM_MYSQL, IDBConnection::PLATFORM_ORACLE, IDBConnection::PLATFORM_POSTGRES, IDBConnection::PLATFORM_SQLITE] as $engine) {
- $testcases["$engine valid"] = [
- str_repeat('x', 55),
- 3,
- $engine,
- false,
- ];
- $testcases["$engine too long"] = [
- str_repeat('x', 56), // 56 (name) + 3 (prefix) + 5 ('_pkey')= 64 > 63
- 3,
- $engine,
- true,
- ];
- $testcases["$engine too long prefix"] = [
- str_repeat('x', 55),
- 4,
- $engine,
- true,
- ];
- }
- return $testcases;
- }
-
- #[DataProvider('dataEnsureNamingConstraintsPrimaryCustomKey')]
- public function testEnsureNamingConstraintsPrimaryCustomKey(string $name, int $prefixLength, bool $newIndex, bool $throws): void {
- if ($throws) {
- $this->expectException(\InvalidArgumentException::class);
- }
-
- $index = $this->createMock(Index::class);
- $index->expects($this->any())
- ->method('getName')
- ->willReturn($name);
-
- $table = $this->createMock(Table::class);
- $table->expects($this->any())
- ->method('getName')
- ->willReturn('tablename');
-
- $table->expects($this->any())
- ->method('getColumns')
- ->willReturn([]);
- $table->expects($this->any())
- ->method('getIndexes')
- ->willReturn([]);
- $table->expects($this->any())
- ->method('getForeignKeys')
- ->willReturn([]);
- $table->expects($this->atLeastOnce())
- ->method('getPrimaryKey')
- ->willReturn($index);
-
- $schema = $this->createMock(Schema::class);
- $schema->expects($this->once())
- ->method('getTables')
- ->willReturn([$table]);
- $schema->expects($this->once())
- ->method('getSequences')
- ->willReturn([]);
-
- $sourceSchema = $this->createMock(Schema::class);
- $sourceSchema->expects($this->any())
- ->method('getTable')
- ->willReturnCallback(fn () => match($newIndex) {
- true => throw new SchemaException(),
- false => $table,
- });
- $sourceSchema->expects($this->any())
- ->method('hasSequence')
- ->willReturn(false);
-
- $this->migrationService->ensureNamingConstraints($sourceSchema, $schema, $prefixLength);
- }
-
- public static function dataEnsureNamingConstraintsPrimaryCustomKey(): array {
- return [
- 'valid name' => [
- str_repeat('x', 60),
- 3,
- true,
- false,
- ],
- 'valid name - prefix does not matter' => [
- str_repeat('x', 63),
- 3,
- true,
- false,
- ],
- 'invalid name - but not new' => [
- str_repeat('x', 64),
- 3,
- false,
- false,
- ],
- 'too long name' => [
- str_repeat('x', 64),
- 3,
- true,
- true,
- ],
- ];
- }
-
- #[DataProvider('dataEnsureNamingConstraints')]
- public function testEnsureNamingConstraintsColumnName(string $name, bool $throws): void {
- if ($throws) {
- $this->expectException(\InvalidArgumentException::class);
- }
-
- $column = $this->createMock(Column::class);
- $column->expects(self::atLeastOnce())
- ->method('getName')
- ->willReturn($name);
-
- $table = $this->createMock(Table::class);
- $table->expects(self::any())
- ->method('getName')
- ->willReturn('valid');
-
- $table->expects(self::once())
- ->method('getColumns')
- ->willReturn([$column]);
- $table->expects(self::atMost(1))
- ->method('getIndexes')
- ->willReturn([]);
- $table->expects(self::atMost(1))
- ->method('getForeignKeys')
- ->willReturn([]);
-
- $schema = $this->createMock(Schema::class);
- $schema->expects(self::once())
- ->method('getTables')
- ->willReturn([$table]);
- $schema->expects(self::once())
- ->method('getSequences')
- ->willReturn([]);
-
- $sourceSchema = $this->createMock(Schema::class);
- $sourceSchema->expects(self::any())
- ->method('getTable')
- ->willThrowException(new SchemaException());
- $sourceSchema->expects(self::any())
- ->method('hasSequence')
- ->willReturn(false);
-
- $this->migrationService->ensureNamingConstraints($sourceSchema, $schema, 3);
- }
-
- #[DataProvider('dataEnsureNamingConstraints')]
- public function testEnsureNamingConstraintsIndexName(string $name, bool $throws): void {
- if ($throws) {
- $this->expectException(\InvalidArgumentException::class);
- }
-
- $index = $this->createMock(Index::class);
- $index->expects(self::atLeastOnce())
- ->method('getName')
- ->willReturn($name);
-
- $table = $this->createMock(Table::class);
- $table->expects(self::any())
- ->method('getName')
- ->willReturn('valid');
-
- $table->expects(self::atMost(1))
- ->method('getColumns')
- ->willReturn([]);
- $table->expects(self::once())
- ->method('getIndexes')
- ->willReturn([$index]);
- $table->expects(self::atMost(1))
- ->method('getForeignKeys')
- ->willReturn([]);
-
- $schema = $this->createMock(Schema::class);
- $schema->expects(self::once())
- ->method('getTables')
- ->willReturn([$table]);
- $schema->expects(self::once())
- ->method('getSequences')
- ->willReturn([]);
-
- $sourceSchema = $this->createMock(Schema::class);
- $sourceSchema->expects(self::any())
- ->method('getTable')
- ->willThrowException(new SchemaException());
- $sourceSchema->expects(self::any())
- ->method('hasSequence')
- ->willReturn(false);
-
- $this->migrationService->ensureNamingConstraints($sourceSchema, $schema, 3);
- }
-
- #[DataProvider('dataEnsureNamingConstraints')]
- public function testEnsureNamingConstraintsForeignKeyName(string $name, bool $throws): void {
- if ($throws) {
- $this->expectException(\InvalidArgumentException::class);
- }
-
- $foreignKey = $this->createMock(ForeignKeyConstraint::class);
- $foreignKey->expects(self::any())
- ->method('getName')
- ->willReturn($name);
-
- $table = $this->createMock(Table::class);
- $table->expects(self::any())
- ->method('getName')
- ->willReturn('valid');
-
- $table->expects(self::once())
- ->method('getColumns')
- ->willReturn([]);
- $table->expects(self::once())
- ->method('getIndexes')
- ->willReturn([]);
- $table->expects(self::once())
- ->method('getForeignKeys')
- ->willReturn([$foreignKey]);
-
- $schema = $this->createMock(Schema::class);
- $schema->expects(self::once())
- ->method('getTables')
- ->willReturn([$table]);
- $schema->expects(self::once())
- ->method('getSequences')
- ->willReturn([]);
-
- $sourceSchema = $this->createMock(Schema::class);
- $sourceSchema->expects(self::any())
- ->method('getTable')
- ->willThrowException(new SchemaException());
- $sourceSchema->expects(self::any())
- ->method('hasSequence')
- ->willReturn(false);
-
- $this->migrationService->ensureNamingConstraints($sourceSchema, $schema, 3);
- }
-
- #[DataProvider('dataEnsureNamingConstraints')]
- public function testEnsureNamingConstraintsSequenceName(string $name, bool $throws): void {
- if ($throws) {
- $this->expectException(\InvalidArgumentException::class);
- }
-
- $sequence = $this->createMock(Sequence::class);
- $sequence->expects($this->any())
- ->method('getName')
- ->willReturn($name);
-
- $schema = $this->createMock(Schema::class);
- $schema->expects($this->once())
- ->method('getTables')
- ->willReturn([]);
- $schema->expects($this->once())
- ->method('getSequences')
- ->willReturn([$sequence]);
-
- $sourceSchema = $this->createMock(Schema::class);
- $sourceSchema->expects($this->any())
- ->method('getTable')
- ->willThrowException(new SchemaException());
- $sourceSchema->expects($this->any())
- ->method('hasSequence')
- ->willReturn(false);
-
- $this->migrationService->ensureNamingConstraints($sourceSchema, $schema, 3);
- }
-
- public static function dataEnsureNamingConstraints(): array {
- return [
- 'valid length' => [\str_repeat('x', 63), false],
- 'too long' => [\str_repeat('x', 64), true],
- ];
- }
-
- public function testEnsureOracleConstraintsValid(): void {
- $table = $this->createMock(Table::class);
- $table->expects($this->atLeastOnce())
- ->method('getName')
- ->willReturn('tablename');
-
- $primaryKey = $this->createMock(Index::class);
- $primaryKey->expects($this->once())
- ->method('getName')
- ->willReturn('primary_key');
-
- $column = $this->createMock(Column::class);
- $table->expects($this->once())
- ->method('getColumns')
- ->willReturn([$column]);
- $table->expects($this->once())
- ->method('getPrimaryKey')
- ->willReturn($primaryKey);
-
- $sequence = $this->createMock(Sequence::class);
- $schema = $this->createMock(Schema::class);
- $schema->expects($this->once())
- ->method('getTables')
- ->willReturn([$table]);
- $schema->expects($this->once())
- ->method('getSequences')
- ->willReturn([$sequence]);
-
- $sourceSchema = $this->createMock(Schema::class);
- $sourceSchema->expects($this->any())
- ->method('getTable')
- ->willThrowException(new SchemaException());
- $sourceSchema->expects($this->any())
- ->method('hasSequence')
- ->willReturn(false);
-
- $this->migrationService->ensureOracleConstraints($sourceSchema, $schema);
- }
-
- public function testEnsureOracleConstraintsValidWithPrimaryKey(): void {
- $index = $this->createMock(Index::class);
- $index->expects($this->any())
- ->method('getName')
- ->willReturn(\str_repeat('a', 30));
-
- $table = $this->createMock(Table::class);
- $table->expects($this->any())
- ->method('getName')
- ->willReturn(\str_repeat('a', 26));
-
- $table->expects($this->once())
- ->method('getColumns')
- ->willReturn([]);
- $table->expects($this->once())
- ->method('getPrimaryKey')
- ->willReturn($index);
-
- $schema = $this->createMock(Schema::class);
- $schema->expects($this->once())
- ->method('getTables')
- ->willReturn([$table]);
- $schema->expects($this->once())
- ->method('getSequences')
- ->willReturn([]);
-
- $sourceSchema = $this->createMock(Schema::class);
- $sourceSchema->expects($this->any())
- ->method('getTable')
- ->willThrowException(new SchemaException());
- $sourceSchema->expects($this->any())
- ->method('hasSequence')
- ->willReturn(false);
-
- $this->migrationService->ensureOracleConstraints($sourceSchema, $schema);
- }
-
- public function testEnsureOracleConstraintsValidWithPrimaryKeyDefault(): void {
- $defaultName = 'PRIMARY';
- if ($this->db->getDatabaseProvider() === IDBConnection::PLATFORM_POSTGRES) {
- $defaultName = \str_repeat('a', 26) . '_' . \str_repeat('b', 30) . '_seq';
- } elseif ($this->db->getDatabaseProvider() === IDBConnection::PLATFORM_ORACLE) {
- $defaultName = \str_repeat('a', 26) . '_seq';
- }
-
- $index = $this->createMock(Index::class);
- $index->expects($this->any())
- ->method('getName')
- ->willReturn($defaultName);
- $index->expects($this->any())
- ->method('getColumns')
- ->willReturn([\str_repeat('b', 30)]);
-
- $table = $this->createMock(Table::class);
- $table->expects($this->any())
- ->method('getName')
- ->willReturn(\str_repeat('a', 25));
-
- $table->expects($this->once())
- ->method('getColumns')
- ->willReturn([]);
- $table->expects($this->once())
- ->method('getPrimaryKey')
- ->willReturn($index);
-
- $schema = $this->createMock(Schema::class);
- $schema->expects($this->once())
- ->method('getTables')
- ->willReturn([$table]);
- $schema->expects($this->once())
- ->method('getSequences')
- ->willReturn([]);
-
- $sourceSchema = $this->createMock(Schema::class);
- $sourceSchema->expects($this->any())
- ->method('getTable')
- ->willThrowException(new SchemaException());
- $sourceSchema->expects($this->any())
- ->method('hasSequence')
- ->willReturn(false);
-
- $this->migrationService->ensureOracleConstraints($sourceSchema, $schema);
- }
-
- public function testEnsureOracleConstraintsNoPrimaryKey(): void {
- $this->markTestSkipped('Test disabled for now due to multiple reasons, see https://github.com/nextcloud/server/pull/31580#issuecomment-1069182234 for details.');
- $this->expectException(\InvalidArgumentException::class);
-
- $table = $this->createMock(Table::class);
- $table->expects($this->atLeastOnce())
- ->method('getName')
- ->willReturn('tablename');
- $table->expects($this->once())
- ->method('getColumns')
- ->willReturn([]);
- $table->expects($this->once())
- ->method('getPrimaryKey')
- ->willReturn(null);
-
- $schema = $this->createMock(Schema::class);
- $schema->expects($this->once())
- ->method('getTables')
- ->willReturn([$table]);
- $schema->expects($this->once())
- ->method('getSequences')
- ->willReturn([]);
-
- $sourceSchema = $this->createMock(Schema::class);
- $sourceSchema->expects($this->any())
- ->method('getTable')
- ->willThrowException(new SchemaException());
- $sourceSchema->expects($this->any())
- ->method('hasSequence')
- ->willReturn(false);
-
- $this->migrationService->ensureOracleConstraints($sourceSchema, $schema);
- }
-
- /**
- * Alternative for testEnsureOracleConstraintsNoPrimaryKey until we enforce it.
- */
- public function testEnsureOracleConstraintsNoPrimaryKeyLogging(): void {
- $table = $this->createMock(Table::class);
- $table->expects($this->atLeastOnce())
- ->method('getName')
- ->willReturn('tablename');
- $table->expects($this->once())
- ->method('getColumns')
- ->willReturn([]);
- $table->expects($this->once())
- ->method('getPrimaryKey')
- ->willReturn(null);
-
- $schema = $this->createMock(Schema::class);
- $schema->expects($this->once())
- ->method('getTables')
- ->willReturn([$table]);
- $schema->expects($this->once())
- ->method('getSequences')
- ->willReturn([]);
-
- $sourceSchema = $this->createMock(Schema::class);
- $sourceSchema->expects($this->any())
- ->method('getTable')
- ->willThrowException(new SchemaException());
- $sourceSchema->expects($this->any())
- ->method('hasSequence')
- ->willReturn(false);
-
- $logger = $this->createMock(LoggerInterface::class);
- $logger->expects(self::once())
- ->method('error');
- $this->overwriteService(LoggerInterface::class, $logger);
-
- $this->migrationService->ensureOracleConstraints($sourceSchema, $schema);
- }
-
- #[TestWith([true])]
- #[TestWith([false])]
- public function testEnsureOracleConstraintsBooleanNotNull(bool $isOracle): void {
- $this->db->method('getDatabaseProvider')
- ->willReturn($isOracle ? IDBConnection::PLATFORM_ORACLE : IDBConnection::PLATFORM_MARIADB);
-
- $column = $this->createMock(Column::class);
- $column->expects($this->any())
- ->method('getName')
- ->willReturn('aaaa');
- $column->expects($this->any())
- ->method('getType')
- ->willReturn(Type::getType('boolean'));
- $column->expects($this->any())
- ->method('getNotnull')
- ->willReturn(true);
-
- $table = $this->createMock(Table::class);
- $table->expects($this->any())
- ->method('getName')
- ->willReturn('tablename');
- $table->method('getIndexes')->willReturn([]);
- $table->method('getForeignKeys')->willReturn([]);
-
- $table->expects($this->once())
- ->method('getColumns')
- ->willReturn([$column]);
-
- $schema = $this->createMock(Schema::class);
- $schema->expects($this->once())
- ->method('getTables')
- ->willReturn([$table]);
- $schema->method('getSequences')->willReturn([]);
-
- $sourceSchema = $this->createMock(Schema::class);
- $sourceSchema->expects($this->any())
- ->method('getTable')
- ->willThrowException(new SchemaException());
- $sourceSchema->expects($this->any())
- ->method('hasSequence')
- ->willReturn(false);
-
- if ($isOracle) {
- $column->expects($this->once())
- ->method('setNotnull')
- ->with(false);
- } else {
- $column->expects($this->never())
- ->method('setNotnull');
- }
-
- $this->migrationService->ensureOracleConstraints($sourceSchema, $schema);
- }
-
- public function testEnsureOracleConstraintsStringLength4000(): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $column = $this->createMock(Column::class);
- $column->expects($this->any())
- ->method('getName')
- ->willReturn('aaaa');
- $column->expects($this->any())
- ->method('getType')
- ->willReturn(Type::getType('string'));
- $column->expects($this->any())
- ->method('getLength')
- ->willReturn(4001);
-
- $table = $this->createMock(Table::class);
- $table->expects($this->any())
- ->method('getName')
- ->willReturn('tablename');
-
- $table->expects($this->once())
- ->method('getColumns')
- ->willReturn([$column]);
-
- $schema = $this->createMock(Schema::class);
- $schema->expects($this->once())
- ->method('getTables')
- ->willReturn([$table]);
-
- $sourceSchema = $this->createMock(Schema::class);
- $sourceSchema->expects($this->any())
- ->method('getTable')
- ->willThrowException(new SchemaException());
- $sourceSchema->expects($this->any())
- ->method('hasSequence')
- ->willReturn(false);
-
- $this->migrationService->ensureOracleConstraints($sourceSchema, $schema);
- }
-}
--- a/tests/lib/DB/MigratorTest.php
+++ /dev/null
@@ -1,314 +0,0 @@
-config = Server::get(IConfig::class);
- $this->connection = Server::get(\OC\DB\Connection::class);
-
- $this->tableName = $this->getUniqueTableName();
- $this->tableNameTmp = $this->getUniqueTableName();
- }
-
- private function getMigrator(): Migrator {
- $dispatcher = Server::get(IEventDispatcher::class);
- if ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_SQLITE) {
- return new SQLiteMigrator($this->connection, $this->config, $dispatcher);
- } elseif ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_ORACLE) {
- return new OracleMigrator($this->connection, $this->config, $dispatcher);
- }
- return new Migrator($this->connection, $this->config, $dispatcher);
- }
-
- private function getUniqueTableName() {
- return strtolower($this->getUniqueID($this->config->getSystemValueString('dbtableprefix', 'oc_') . 'test_'));
- }
-
- #[\Override]
- protected function tearDown(): void {
- // Try to delete if exists (IF EXISTS NOT SUPPORTED IN ORACLE)
- try {
- $this->connection->exec('DROP TABLE ' . $this->connection->quoteIdentifier($this->tableNameTmp));
- } catch (Exception $e) {
- }
-
- try {
- $this->connection->exec('DROP TABLE ' . $this->connection->quoteIdentifier($this->tableName));
- } catch (Exception $e) {
- }
- parent::tearDown();
- }
-
- /**
- * @return \Doctrine\DBAL\Schema\Schema[]
- */
- private function getDuplicateKeySchemas() {
- $startSchema = new Schema([], [], $this->getSchemaConfig());
- $table = $startSchema->createTable($this->tableName);
- $table->addColumn('id', 'integer');
- $table->addColumn('name', 'string');
- $table->addIndex(['id'], $this->tableName . '_id');
-
- $endSchema = new Schema([], [], $this->getSchemaConfig());
- $table = $endSchema->createTable($this->tableName);
- $table->addColumn('id', 'integer');
- $table->addColumn('name', 'string');
- $table->addUniqueIndex(['id'], $this->tableName . '_id');
-
- return [$startSchema, $endSchema];
- }
-
- /**
- * @return \Doctrine\DBAL\Schema\Schema[]
- */
- private function getChangedTypeSchema($from, $to) {
- $startSchema = new Schema([], [], $this->getSchemaConfig());
- $table = $startSchema->createTable($this->tableName);
- $table->addColumn('id', $from);
- $table->addColumn('name', 'string');
- $table->addIndex(['id'], $this->tableName . '_id');
-
- $endSchema = new Schema([], [], $this->getSchemaConfig());
- $table = $endSchema->createTable($this->tableName);
- $table->addColumn('id', $to);
- $table->addColumn('name', 'string');
- $table->addIndex(['id'], $this->tableName . '_id');
-
- return [$startSchema, $endSchema];
- }
-
- private function getSchemaConfig() {
- $config = new SchemaConfig();
- $config->setName($this->connection->getDatabase());
- return $config;
- }
-
- public function testUpgrade(): void {
- [$startSchema, $endSchema] = $this->getDuplicateKeySchemas();
- $migrator = $this->getMigrator();
- $migrator->migrate($startSchema);
-
- $this->connection->insert($this->tableName, ['id' => 1, 'name' => 'foo']);
- $this->connection->insert($this->tableName, ['id' => 2, 'name' => 'bar']);
- $this->connection->insert($this->tableName, ['id' => 3, 'name' => 'qwerty']);
-
- $migrator->migrate($endSchema);
- $this->addToAssertionCount(1);
- }
-
- public function testUpgradeDifferentPrefix(): void {
- $oldTablePrefix = $this->config->getSystemValueString('dbtableprefix', 'oc_');
-
- $this->config->setSystemValue('dbtableprefix', 'ownc_');
- $this->tableName = strtolower($this->getUniqueID($this->config->getSystemValueString('dbtableprefix') . 'test_'));
-
- [$startSchema, $endSchema] = $this->getDuplicateKeySchemas();
- $migrator = $this->getMigrator();
- $migrator->migrate($startSchema);
-
- $this->connection->insert($this->tableName, ['id' => 1, 'name' => 'foo']);
- $this->connection->insert($this->tableName, ['id' => 2, 'name' => 'bar']);
- $this->connection->insert($this->tableName, ['id' => 3, 'name' => 'qwerty']);
-
- $migrator->migrate($endSchema);
- $this->addToAssertionCount(1);
-
- $this->config->setSystemValue('dbtableprefix', $oldTablePrefix);
- }
-
- public function testInsertAfterUpgrade(): void {
- [$startSchema, $endSchema] = $this->getDuplicateKeySchemas();
- $migrator = $this->getMigrator();
- $migrator->migrate($startSchema);
-
- $migrator->migrate($endSchema);
-
- $this->connection->insert($this->tableName, ['id' => 1, 'name' => 'foo']);
- $this->connection->insert($this->tableName, ['id' => 2, 'name' => 'bar']);
- try {
- $this->connection->insert($this->tableName, ['id' => 2, 'name' => 'qwerty']);
- $this->fail('Expected duplicate key insert to fail');
- } catch (Exception $e) {
- $this->addToAssertionCount(1);
- }
- }
-
- public function testAddingPrimaryKeyWithAutoIncrement(): void {
- $startSchema = new Schema([], [], $this->getSchemaConfig());
- $table = $startSchema->createTable($this->tableName);
- $table->addColumn('id', 'integer');
- $table->addColumn('name', 'string');
-
- $endSchema = new Schema([], [], $this->getSchemaConfig());
- $table = $endSchema->createTable($this->tableName);
- $table->addColumn('id', 'integer', ['autoincrement' => true]);
- $table->addColumn('name', 'string');
- $table->setPrimaryKey(['id']);
-
- $migrator = $this->getMigrator();
- $migrator->migrate($startSchema);
-
- $migrator->migrate($endSchema);
-
- $this->addToAssertionCount(1);
- }
-
- public function testReservedKeywords(): void {
- $startSchema = new Schema([], [], $this->getSchemaConfig());
- $table = $startSchema->createTable($this->tableName);
- $table->addColumn('id', 'integer', ['autoincrement' => true]);
- $table->addColumn('user', 'string', ['length' => 255]);
- $table->setPrimaryKey(['id']);
-
- $endSchema = new Schema([], [], $this->getSchemaConfig());
- $table = $endSchema->createTable($this->tableName);
- $table->addColumn('id', 'integer', ['autoincrement' => true]);
- $table->addColumn('user', 'string', ['length' => 64]);
- $table->setPrimaryKey(['id']);
-
- $migrator = $this->getMigrator();
- $migrator->migrate($startSchema);
-
- $migrator->migrate($endSchema);
-
- $this->addToAssertionCount(1);
- }
-
- /**
- * Test for nextcloud/server#36803
- */
- public function testColumnCommentsInUpdate(): void {
- $startSchema = new Schema([], [], $this->getSchemaConfig());
- $table = $startSchema->createTable($this->tableName);
- $table->addColumn('id', 'integer', ['autoincrement' => true, 'comment' => 'foo']);
- $table->setPrimaryKey(['id']);
-
- $endSchema = new Schema([], [], $this->getSchemaConfig());
- $table = $endSchema->createTable($this->tableName);
- $table->addColumn('id', 'integer', ['autoincrement' => true, 'comment' => 'foo']);
- // Assert adding comments on existing tables work (or at least does not throw)
- $table->addColumn('time', 'integer', ['comment' => 'unix-timestamp', 'notnull' => false]);
- $table->setPrimaryKey(['id']);
-
- $migrator = $this->getMigrator();
- $migrator->migrate($startSchema);
-
- $migrator->migrate($endSchema);
-
- $this->addToAssertionCount(1);
- }
-
- public function testAddingForeignKey(): void {
- $startSchema = new Schema([], [], $this->getSchemaConfig());
- $table = $startSchema->createTable($this->tableName);
- $table->addColumn('id', 'integer', ['autoincrement' => true]);
- $table->addColumn('name', 'string');
- $table->setPrimaryKey(['id']);
-
- $fkName = 'fkc';
- $tableFk = $startSchema->createTable($this->tableNameTmp);
- $tableFk->addColumn('fk_id', 'integer');
- $tableFk->addColumn('name', 'string');
- $tableFk->addForeignKeyConstraint($this->tableName, ['fk_id'], ['id'], [], $fkName);
-
- $migrator = $this->getMigrator();
- $migrator->migrate($startSchema);
-
- $this->assertTrue($startSchema->getTable($this->tableNameTmp)->hasForeignKey($fkName));
- }
-
- public static function dataNotNullEmptyValuesFailOracle(): array {
- return [
- [ParameterType::BOOLEAN, true, Types::BOOLEAN, false],
- [ParameterType::BOOLEAN, false, Types::BOOLEAN, true],
-
- [ParameterType::STRING, 'foo', Types::STRING, false],
- [ParameterType::STRING, '', Types::STRING, true],
-
- [ParameterType::INTEGER, 1234, Types::INTEGER, false],
- [ParameterType::INTEGER, 0, Types::INTEGER, false], // Integer 0 is not stored as Null and therefor works
-
- [ParameterType::STRING, '{"a": 2}', Types::JSON, false],
- ];
- }
-
- /**
- *
- * @param int $parameterType
- * @param bool|int|string $value
- * @param string $columnType
- * @param bool $oracleThrows
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataNotNullEmptyValuesFailOracle')]
- public function testNotNullEmptyValuesFailOracle(int $parameterType, $value, string $columnType, bool $oracleThrows): void {
- $startSchema = new Schema([], [], $this->getSchemaConfig());
- $table = $startSchema->createTable($this->tableName);
- $table->addColumn('id', Types::BIGINT);
- $table->addColumn('will_it_blend', $columnType, [
- 'notnull' => true,
- ]);
- $table->addIndex(['id'], $this->tableName . '_id');
-
- $migrator = $this->getMigrator();
- $migrator->migrate($startSchema);
-
- if ($oracleThrows && $this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_ORACLE) {
- // Oracle can not store false|empty string in notnull columns
- $this->expectException(\Doctrine\DBAL\Exception\NotNullConstraintViolationException::class);
- }
-
- $this->connection->insert(
- $this->tableName,
- ['id' => 1, 'will_it_blend' => $value],
- ['id' => ParameterType::INTEGER, 'will_it_blend' => $parameterType],
- );
-
- $this->addToAssertionCount(1);
- }
-}
--- a/tests/lib/DB/OCPostgreSqlPlatformTest.php
+++ /dev/null
@@ -1,60 +0,0 @@
-createTableAndColumn($sourceSchema, Types::INTEGER);
- $this->createTableAndColumn($targetSchema, Types::BIGINT);
-
- $comparator = new Comparator();
- $diff = $comparator->compare($sourceSchema, $targetSchema);
- $sqlStatements = $diff->toSql($platform);
- $this->assertContains(
- 'ALTER TABLE poor_yorick ALTER id TYPE BIGINT',
- $sqlStatements,
- true
- );
-
- $this->assertNotContains(
- 'ALTER TABLE poor_yorick ALTER id DROP DEFAULT',
- $sqlStatements,
- true
- );
- }
-
- protected function createTableAndColumn($schema, $type) {
- $table = $schema->createTable('poor_yorick');
- $table->addColumn('id', $type, [
- 'autoincrement' => true,
- 'unsigned' => true,
- 'notnull' => true,
- 'length' => 11,
- ]);
- }
-}
--- a/tests/lib/DB/QueryBuilder/ExpressionBuilderDBTest.php
+++ /dev/null
@@ -1,369 +0,0 @@
-connection = Server::get(IDBConnection::class);
- $this->prepareTestingTable();
- }
-
- public static function likeProvider(): array {
- $connection = Server::get(IDBConnection::class);
-
- return [
- ['foo', 'bar', false],
- ['foo', 'foo', true],
- ['foo', 'f%', true],
- ['foo', '%o', true],
- ['foo', '%', true],
- ['foo', 'fo_', true],
- ['foo', 'foo_', false],
- ['foo', $connection->escapeLikeParameter('fo_'), false],
- ['foo', $connection->escapeLikeParameter('f%'), false],
- ];
- }
-
- /**
- *
- * @param string $param1
- * @param string $param2
- * @param boolean $match
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('likeProvider')]
- public function testLike($param1, $param2, $match): void {
- $query = $this->connection->getQueryBuilder();
-
- $query->select(new Literal('1'))
- ->from('users')
- ->where($query->expr()->like($query->createNamedParameter($param1), $query->createNamedParameter($param2)));
-
- $result = $query->executeQuery();
- $column = $result->fetchOne();
- $result->closeCursor();
- $this->assertEquals($match, $column);
- }
-
- public static function ilikeProvider(): array {
- $connection = Server::get(IDBConnection::class);
-
- return [
- ['foo', 'bar', false],
- ['foo', 'foo', true],
- ['foo', 'Foo', true],
- ['foo', 'f%', true],
- ['foo', '%o', true],
- ['foo', '%', true],
- ['foo', 'fo_', true],
- ['foo', 'foo_', false],
- ['foo', $connection->escapeLikeParameter('fo_'), false],
- ['foo', $connection->escapeLikeParameter('f%'), false],
- ];
- }
-
- /**
- *
- * @param string $param1
- * @param string $param2
- * @param boolean $match
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('ilikeProvider')]
- public function testILike($param1, $param2, $match): void {
- $query = $this->connection->getQueryBuilder();
-
- $query->select(new Literal('1'))
- ->from('users')
- ->where($query->expr()->iLike($query->createNamedParameter($param1), $query->createNamedParameter($param2)));
-
- $result = $query->executeQuery();
- $column = $result->fetchOne();
- $result->closeCursor();
- $this->assertEquals($match, $column);
- }
-
- public static function notILikeProvider(): array {
- $connection = Server::get(IDBConnection::class);
-
- return [
- ['foo', 'bar', true],
- ['foo', 'foo', false],
- ['foo', 'Foo', false],
- ['foo', 'f%', false],
- ['foo', '%o', false],
- ['foo', '%', false],
- ['foo', 'fo_', false],
- ['foo', 'foo_', true],
- ['foo', $connection->escapeLikeParameter('fo_'), true],
- ['foo', $connection->escapeLikeParameter('f%'), true],
- ];
- }
-
- /**
- *
- * @param string $param1
- * @param string $param2
- * @param boolean $match
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('notILikeProvider')]
- public function testNotILike($param1, $param2, $match): void {
- $query = $this->connection->getQueryBuilder();
-
- $query->select(new Literal('1'))
- ->from('users')
- ->where($query->expr()->notILike($query->createNamedParameter($param1), $query->createNamedParameter($param2)));
-
- $result = $query->executeQuery();
- $column = $result->fetchOne();
- $result->closeCursor();
- $this->assertEquals($match, $column);
- }
-
- public function testCastColumn(): void {
- $appId = $this->getUniqueID('testing');
- $this->createConfig($appId, '1', '4');
-
- $query = $this->connection->getQueryBuilder();
- $query->update('appconfig')
- ->set('configvalue',
- $query->expr()->castColumn(
- $query->createFunction(
- '(' . $query->expr()->castColumn('configvalue', IQueryBuilder::PARAM_INT)
- . ' + 1)'
- ), IQueryBuilder::PARAM_STR
- )
- )
- ->where($query->expr()->eq('appid', $query->createNamedParameter($appId)))
- ->andWhere($query->expr()->eq('configkey', $query->createNamedParameter('1')));
-
- $result = $query->executeStatement();
- $this->assertEquals(1, $result);
- }
-
- public function testLongText(): void {
- $appId = $this->getUniqueID('testing');
- $this->createConfig($appId, 'mykey', 'myvalue');
-
- $query = $this->connection->getQueryBuilder();
- $query->select('*')
- ->from('appconfig')
- ->where($query->expr()->eq('appid', $query->createNamedParameter($appId)))
- ->andWhere($query->expr()->eq('configkey', $query->createNamedParameter('mykey')))
- ->andWhere($query->expr()->eq('configvalue', $query->createNamedParameter('myvalue', IQueryBuilder::PARAM_STR), IQueryBuilder::PARAM_STR));
-
- $result = $query->executeQuery();
- $entries = $result->fetchAllAssociative();
- $result->closeCursor();
- self::assertCount(1, $entries);
- self::assertEquals('myvalue', $entries[0]['configvalue']);
- }
-
- public function testJson(): void {
- $appId = $this->getUniqueID('testing');
- $query = $this->connection->getQueryBuilder();
- $query->insert('share')
- ->values([
- 'uid_owner' => $query->createNamedParameter('uid_owner'),
- 'item_type' => $query->createNamedParameter('item_type'),
- 'permissions' => $query->createNamedParameter(0),
- 'stime' => $query->createNamedParameter(0),
- 'accepted' => $query->createNamedParameter(0),
- 'mail_send' => $query->createNamedParameter(0),
- 'share_type' => $query->createNamedParameter(0),
- 'share_with' => $query->createNamedParameter($appId),
- 'attributes' => $query->createNamedParameter('[["permissions","before"]]'),
- ])
- ->executeStatement();
-
- $query = $this->connection->getQueryBuilder();
- $query->update('share')
- ->set('attributes', $query->createNamedParameter('[["permissions","after"]]'));
- if ($this->connection->getDatabaseProvider(true) === IDBConnection::PLATFORM_MYSQL) {
- $query->where($query->expr()->eq('attributes', $query->createFunction("JSON_ARRAY(JSON_ARRAY('permissions','before'))"), IQueryBuilder::PARAM_JSON));
- } else {
- $query->where($query->expr()->eq('attributes', $query->createNamedParameter('[["permissions","before"]]'), IQueryBuilder::PARAM_JSON));
- }
- $query->executeStatement();
-
- $query = $this->connection->getQueryBuilder();
- $query->select('attributes')
- ->from('share')
- ->where($query->expr()->eq('share_with', $query->createNamedParameter($appId)));
-
- $result = $query->executeQuery();
- $entries = $result->fetchAll();
- $result->closeCursor();
- self::assertCount(1, $entries);
- self::assertEquals([['permissions','after']], json_decode($entries[0]['attributes'], true));
- }
-
- public function testDateTimeEquals(): void {
- $dateTime = new \DateTime('2023-01-01');
- $insert = $this->connection->getQueryBuilder();
- $insert->insert('testing')
- ->values(['datetime' => $insert->createNamedParameter($dateTime, IQueryBuilder::PARAM_DATETIME_MUTABLE)])
- ->executeStatement();
-
- $query = $this->connection->getQueryBuilder();
- $result = $query->select('*')
- ->from('testing')
- ->where($query->expr()->eq('datetime', $query->createNamedParameter($dateTime, IQueryBuilder::PARAM_DATETIME_MUTABLE)))
- ->executeQuery();
- $entries = $result->fetchAllAssociative();
- $result->closeCursor();
- self::assertCount(1, $entries);
- }
-
- public function testDateTimeLess(): void {
- $dateTime = new \DateTime('2022-01-01');
- $dateTimeCompare = new \DateTime('2022-01-02');
- $insert = $this->connection->getQueryBuilder();
- $insert->insert('testing')
- ->values(['datetime' => $insert->createNamedParameter($dateTime, IQueryBuilder::PARAM_DATETIME_MUTABLE)])
- ->executeStatement();
-
- $query = $this->connection->getQueryBuilder();
- $result = $query->select('*')
- ->from('testing')
- ->where($query->expr()->lt('datetime', $query->createNamedParameter($dateTimeCompare, IQueryBuilder::PARAM_DATETIME_MUTABLE)))
- ->executeQuery();
- $entries = $result->fetchAllAssociative();
- $result->closeCursor();
- self::assertCount(1, $entries);
- }
-
- public function testDateTimeGreater(): void {
- $dateTime = new \DateTime('2023-01-02');
- $dateTimeCompare = new \DateTime('2023-01-01');
- $insert = $this->connection->getQueryBuilder();
- $insert->insert('testing')
- ->values(['datetime' => $insert->createNamedParameter($dateTime, IQueryBuilder::PARAM_DATETIME_MUTABLE)])
- ->executeStatement();
-
- $query = $this->connection->getQueryBuilder();
- $result = $query->select('*')
- ->from('testing')
- ->where($query->expr()->gt('datetime', $query->createNamedParameter($dateTimeCompare, IQueryBuilder::PARAM_DATETIME_MUTABLE)))
- ->executeQuery();
- $entries = $result->fetchAllAssociative();
- $result->closeCursor();
- self::assertCount(1, $entries);
- }
-
- public static function dataExists(): array {
- $connection = Server::get(IDBConnection::class);
-
- $query1 = $connection->getQueryBuilder()
- ->select(new Literal('1'))
- ->from('users');
-
- $query2 = $connection->getQueryBuilder();
- $query2->select(new Literal('1'))
- ->from('users')
- ->where($query2->expr()->eq($query2->createNamedParameter('a'), $query2->createNamedParameter('b')));
- return [
- [$query1, true],
- [$query2, false],
- ];
- }
-
- #[DataProvider('dataExists')]
- public function testExists(IQueryBuilder $subquery, bool $match): void {
- $query = $this->connection->getQueryBuilder();
-
- foreach ($subquery->getParameters() as $key => $value) {
- $query->setParameter($key, $value);
- }
-
- $query->select(new Literal('1'))
- ->from('users')
- ->where($query->expr()->exists($subquery));
-
- $result = $query->executeQuery();
- $column = $result->fetchOne();
- $result->closeCursor();
- $this->assertEquals($match, $column);
- }
-
- public static function dataNotExists(): array {
- return array_map(fn (array $data) => [$data[0], !$data[1]], self::dataExists());
- }
-
- #[DataProvider('dataNotExists')]
- public function testNotExists(IQueryBuilder $subquery, bool $match): void {
- $query = $this->connection->getQueryBuilder();
-
- foreach ($subquery->getParameters() as $key => $value) {
- $query->setParameter($key, $value);
- }
-
- $query->select(new Literal('1'))
- ->from('users')
- ->where($query->expr()->notExists($subquery));
-
- $result = $query->executeQuery();
- $column = $result->fetchOne();
- $result->closeCursor();
- $this->assertEquals($match, $column);
- }
-
- protected function createConfig($appId, $key, $value) {
- $query = $this->connection->getQueryBuilder();
- $query->insert('appconfig')
- ->values([
- 'appid' => $query->createNamedParameter($appId),
- 'configkey' => $query->createNamedParameter((string)$key),
- 'configvalue' => $query->createNamedParameter((string)$value),
- ])
- ->executeStatement();
- }
-
- protected function prepareTestingTable(): void {
- if ($this->schemaSetup) {
- $this->connection->getQueryBuilder()->delete('testing')->executeStatement();
- }
-
- $prefix = Server::get(IConfig::class)->getSystemValueString('dbtableprefix', 'oc_');
- $schema = $this->connection->createSchema();
- try {
- $schema->getTable($prefix . 'testing');
- $this->connection->getQueryBuilder()->delete('testing')->executeStatement();
- } catch (SchemaException $e) {
- $this->schemaSetup = true;
- $table = $schema->createTable($prefix . 'testing');
- $table->addColumn('id', Types::BIGINT, [
- 'autoincrement' => true,
- 'notnull' => true,
- ]);
-
- $table->addColumn('datetime', Types::DATETIME, [
- 'notnull' => false,
- ]);
-
- $table->setPrimaryKey(['id']);
- $this->connection->migrateToSchema($schema);
- }
- }
-}
--- a/tests/lib/DB/QueryBuilder/ExpressionBuilderTest.php
+++ /dev/null
@@ -1,366 +0,0 @@
-connection = Server::get(IDBConnection::class);
- $this->internalConnection = Server::get(Connection::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $queryBuilder = $this->createMock(IQueryBuilder::class);
- $queryBuilder->method('func')
- ->willReturn($this->createMock(IFunctionBuilder::class));
-
- $this->expressionBuilder = new ExpressionBuilder($this->connection, $queryBuilder, $this->logger);
-
- $this->doctrineExpressionBuilder = new DoctrineExpressionBuilder($this->internalConnection);
- }
-
- public static function dataComparison(): array {
- $valueSets = self::dataComparisons();
- $comparisonOperators = ['=', '<>', '<', '>', '<=', '>='];
-
- $testSets = [];
- foreach ($comparisonOperators as $operator) {
- foreach ($valueSets as $values) {
- $testSets[] = array_merge([$operator], $values);
- }
- }
- return $testSets;
- }
-
- #[DataProvider('dataComparison')]
- public function testComparison(string $comparison, string $input1, bool $isInput1Literal, string $input2, bool $isInput2Literal): void {
- [$doctrineInput1, $ocInput1] = $this->helpWithLiteral($input1, $isInput1Literal);
- [$doctrineInput2, $ocInput2] = $this->helpWithLiteral($input2, $isInput2Literal);
-
- $this->assertEquals(
- $this->doctrineExpressionBuilder->comparison($doctrineInput1, $comparison, $doctrineInput2),
- $this->expressionBuilder->comparison($ocInput1, $comparison, $ocInput2)
- );
- }
-
- public static function dataComparisons(): array {
- return [
- ['value', false, 'value', false],
- ['value', false, 'value', true],
- ['value', true, 'value', false],
- ['value', true, 'value', true],
- ];
- }
-
- #[DataProvider('dataComparisons')]
- public function testEquals(string $input1, bool $isInput1Literal, string $input2, bool $isInput2Literal): void {
- [$doctrineInput1, $ocInput1] = $this->helpWithLiteral($input1, $isInput1Literal);
- [$doctrineInput2, $ocInput2] = $this->helpWithLiteral($input2, $isInput2Literal);
-
- $this->assertEquals(
- $this->doctrineExpressionBuilder->eq($doctrineInput1, $doctrineInput2),
- $this->expressionBuilder->eq($ocInput1, $ocInput2)
- );
- }
-
- #[DataProvider('dataComparisons')]
- public function testNotEquals(string $input1, bool $isInput1Literal, string $input2, bool $isInput2Literal): void {
- [$doctrineInput1, $ocInput1] = $this->helpWithLiteral($input1, $isInput1Literal);
- [$doctrineInput2, $ocInput2] = $this->helpWithLiteral($input2, $isInput2Literal);
-
- $this->assertEquals(
- $this->doctrineExpressionBuilder->neq($doctrineInput1, $doctrineInput2),
- $this->expressionBuilder->neq($ocInput1, $ocInput2)
- );
- }
-
- #[DataProvider('dataComparisons')]
- public function testLowerThan(string $input1, bool $isInput1Literal, string $input2, bool $isInput2Literal): void {
- [$doctrineInput1, $ocInput1] = $this->helpWithLiteral($input1, $isInput1Literal);
- [$doctrineInput2, $ocInput2] = $this->helpWithLiteral($input2, $isInput2Literal);
-
- $this->assertEquals(
- $this->doctrineExpressionBuilder->lt($doctrineInput1, $doctrineInput2),
- $this->expressionBuilder->lt($ocInput1, $ocInput2)
- );
- }
-
- #[DataProvider('dataComparisons')]
- public function testLowerThanEquals(string $input1, bool $isInput1Literal, string $input2, bool $isInput2Literal): void {
- [$doctrineInput1, $ocInput1] = $this->helpWithLiteral($input1, $isInput1Literal);
- [$doctrineInput2, $ocInput2] = $this->helpWithLiteral($input2, $isInput2Literal);
-
- $this->assertEquals(
- $this->doctrineExpressionBuilder->lte($doctrineInput1, $doctrineInput2),
- $this->expressionBuilder->lte($ocInput1, $ocInput2)
- );
- }
-
- #[DataProvider('dataComparisons')]
- public function testGreaterThan(string $input1, bool $isInput1Literal, string $input2, bool $isInput2Literal): void {
- [$doctrineInput1, $ocInput1] = $this->helpWithLiteral($input1, $isInput1Literal);
- [$doctrineInput2, $ocInput2] = $this->helpWithLiteral($input2, $isInput2Literal);
-
- $this->assertEquals(
- $this->doctrineExpressionBuilder->gt($doctrineInput1, $doctrineInput2),
- $this->expressionBuilder->gt($ocInput1, $ocInput2)
- );
- }
-
- #[DataProvider('dataComparisons')]
- public function testGreaterThanEquals(string $input1, bool $isInput1Literal, string $input2, bool $isInput2Literal): void {
- [$doctrineInput1, $ocInput1] = $this->helpWithLiteral($input1, $isInput1Literal);
- [$doctrineInput2, $ocInput2] = $this->helpWithLiteral($input2, $isInput2Literal);
-
- $this->assertEquals(
- $this->doctrineExpressionBuilder->gte($doctrineInput1, $doctrineInput2),
- $this->expressionBuilder->gte($ocInput1, $ocInput2)
- );
- }
-
- public function testIsNull(): void {
- $this->assertEquals(
- $this->doctrineExpressionBuilder->isNull('`test`'),
- $this->expressionBuilder->isNull('test')
- );
- }
-
- public function testIsNotNull(): void {
- $this->assertEquals(
- $this->doctrineExpressionBuilder->isNotNull('`test`'),
- $this->expressionBuilder->isNotNull('test')
- );
- }
-
- public static function dataLike(): array {
- return [
- ['value', false],
- ['value', true],
- ];
- }
-
- #[DataProvider('dataLike')]
- public function testLike(string $input, bool $isLiteral): void {
- [$doctrineInput, $ocInput] = $this->helpWithLiteral($input, $isLiteral);
-
- $this->assertEquals(
- $this->doctrineExpressionBuilder->like('`test`', $doctrineInput),
- $this->expressionBuilder->like('test', $ocInput)
- );
- }
-
- #[DataProvider('dataLike')]
- public function testNotLike(string $input, bool $isLiteral): void {
- [$doctrineInput, $ocInput] = $this->helpWithLiteral($input, $isLiteral);
-
- $this->assertEquals(
- $this->doctrineExpressionBuilder->notLike('`test`', $doctrineInput),
- $this->expressionBuilder->notLike('test', $ocInput)
- );
- }
-
- public function testILike(): void {
- // iLike is implemented using lower() on both sides, so we just verify it returns a string
- $result = $this->expressionBuilder->iLike('test', 'value');
- $this->assertIsString($result);
- }
-
- public function testNotILike(): void {
- // notILike is implemented using notLike with lower() on both sides, so we just verify it returns a string
- $result = $this->expressionBuilder->notILike('test', 'value');
- $this->assertIsString($result);
- }
-
- public static function dataIn(): array {
- return [
- ['value', false],
- ['value', true],
- [['value'], false],
- [['value'], true],
- ];
- }
-
- #[DataProvider('dataIn')]
- public function testIn(string|array $input, bool $isLiteral): void {
- [$doctrineInput, $ocInput] = $this->helpWithLiteral($input, $isLiteral);
-
- $this->assertEquals(
- $this->doctrineExpressionBuilder->in('`test`', $doctrineInput),
- $this->expressionBuilder->in('test', $ocInput)
- );
- }
-
- #[DataProvider('dataIn')]
- public function testNotIn(string|array $input, bool $isLiteral): void {
- [$doctrineInput, $ocInput] = $this->helpWithLiteral($input, $isLiteral);
-
- $this->assertEquals(
- $this->doctrineExpressionBuilder->notIn('`test`', $doctrineInput),
- $this->expressionBuilder->notIn('test', $ocInput)
- );
- }
-
- protected function helpWithLiteral(string|array $input, bool $isLiteral) {
- if ($isLiteral) {
- if (is_array($input)) {
- $doctrineInput = array_map(function ($ident) {
- return $this->doctrineExpressionBuilder->literal($ident);
- }, $input);
- $ocInput = array_map(function ($ident) {
- return $this->expressionBuilder->literal($ident);
- }, $input);
- } else {
- $doctrineInput = $this->doctrineExpressionBuilder->literal($input);
- $ocInput = $this->expressionBuilder->literal($input);
- }
- } else {
- if (is_array($input)) {
- $doctrineInput = array_map(function ($input) {
- return '`' . $input . '`';
- }, $input);
- $ocInput = $input;
- } else {
- $doctrineInput = '`' . $input . '`';
- $ocInput = $input;
- }
- }
-
- return [$doctrineInput, $ocInput];
- }
-
- public static function dataLiteral(): array {
- return [
- ['value', null],
- ['1', null],
- [1, null],
- [1, 'string'],
- [1, 'integer'],
- [1, IQueryBuilder::PARAM_INT],
- ];
- }
-
- #[DataProvider('dataLiteral')]
- public function testLiteral(string|int $input, string|int|null $type): void {
- /** @var Literal $actual */
- $actual = $this->expressionBuilder->literal($input, $type);
-
- $this->assertInstanceOf('\OC\DB\QueryBuilder\Literal', $actual);
- $this->assertEquals(
- $this->doctrineExpressionBuilder->literal($input, $type),
- $actual->__toString()
- );
- }
-
- public static function dataClobComparisons(): array {
- return [
- ['eq', '5', IQueryBuilder::PARAM_STR, false, 3],
- ['eq', '5', IQueryBuilder::PARAM_STR, true, 1],
- ['neq', '5', IQueryBuilder::PARAM_STR, false, 8],
- ['neq', '5', IQueryBuilder::PARAM_STR, true, 6],
- ['lt', '5', IQueryBuilder::PARAM_STR, false, 3],
- ['lt', '5', IQueryBuilder::PARAM_STR, true, 1],
- ['lte', '5', IQueryBuilder::PARAM_STR, false, 6],
- ['lte', '5', IQueryBuilder::PARAM_STR, true, 4],
- ['gt', '5', IQueryBuilder::PARAM_STR, false, 5],
- ['gt', '5', IQueryBuilder::PARAM_STR, true, 1],
- ['gte', '5', IQueryBuilder::PARAM_STR, false, 8],
- ['gte', '5', IQueryBuilder::PARAM_STR, true, 4],
- ['like', '%5%', IQueryBuilder::PARAM_STR, false, 3],
- ['like', '%5%', IQueryBuilder::PARAM_STR, true, 1],
- ['like', 'under_%', IQueryBuilder::PARAM_STR, false, 2],
- ['like', 'under\_%', IQueryBuilder::PARAM_STR, false, 1],
- ['notLike', '%5%', IQueryBuilder::PARAM_STR, false, 8],
- ['notLike', '%5%', IQueryBuilder::PARAM_STR, true, 6],
- ['iLike', '%5%', IQueryBuilder::PARAM_STR, false, 3],
- ['iLike', '%5%', IQueryBuilder::PARAM_STR, true, 1],
- ['notILike', '%5%', IQueryBuilder::PARAM_STR, false, 8],
- ['notILike', '%5%', IQueryBuilder::PARAM_STR, true, 6],
- ['in', ['5'], IQueryBuilder::PARAM_STR_ARRAY, false, 3],
- ['in', ['5'], IQueryBuilder::PARAM_STR_ARRAY, true, 1],
- ['notIn', ['5'], IQueryBuilder::PARAM_STR_ARRAY, false, 8],
- ['notIn', ['5'], IQueryBuilder::PARAM_STR_ARRAY, true, 6],
- ];
- }
-
- #[DataProvider('dataClobComparisons')]
- public function testClobComparisons(string $function, string|array $value, int $type, bool $compareKeyToValue, int $expected): void {
- $appId = $this->getUniqueID('testing');
- $this->createConfig($appId, 1, 4);
- $this->createConfig($appId, 2, 5);
- $this->createConfig($appId, 3, 6);
- $this->createConfig($appId, 4, 4);
- $this->createConfig($appId, 5, 5);
- $this->createConfig($appId, 6, 6);
- $this->createConfig($appId, 7, 4);
- $this->createConfig($appId, 8, 5);
- $this->createConfig($appId, 9, 6);
- $this->createConfig($appId, 10, 'under_score');
- $this->createConfig($appId, 11, 'underscore');
-
- $query = $this->connection->getQueryBuilder();
- $query->select($query->func()->count('*', 'count'))
- ->from('appconfig')
- ->where($query->expr()->eq('appid', $query->createNamedParameter($appId)))
- ->andWhere(call_user_func([$query->expr(), $function], 'configvalue', $query->createNamedParameter($value, $type), IQueryBuilder::PARAM_STR));
-
- if ($compareKeyToValue) {
- $query->andWhere(call_user_func([$query->expr(), $function], 'configkey', 'configvalue', IQueryBuilder::PARAM_STR));
- }
-
- $result = $query->executeQuery();
-
- $this->assertEquals(['count' => $expected], $result->fetchAssociative());
- $result->closeCursor();
-
- $query = $this->connection->getQueryBuilder();
- $query->delete('appconfig')
- ->where($query->expr()->eq('appid', $query->createNamedParameter($appId)))
- ->executeStatement();
- }
-
- protected function createConfig(string $appId, int $key, int|string $value) {
- $query = $this->connection->getQueryBuilder();
- $query->insert('appconfig')
- ->values([
- 'appid' => $query->createNamedParameter($appId),
- 'configkey' => $query->createNamedParameter((string)$key),
- 'configvalue' => $query->createNamedParameter((string)$value),
- ])
- ->executeStatement();
- }
-}
--- a/tests/lib/DB/QueryBuilder/FunctionBuilderTest.php
+++ /dev/null
@@ -1,518 +0,0 @@
-connection = Server::get(IDBConnection::class);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providerTestConcatString')]
- public function testConcatString($closure): void {
- $query = $this->connection->getQueryBuilder();
- [$real, $arguments, $return] = $closure($query);
- if ($real) {
- $this->addDummyData();
- $query->where($query->expr()->eq('appid', $query->createNamedParameter('group_concat')));
- $query->orderBy('configkey', 'asc');
- }
-
- $query->select($query->func()->concat(...$arguments));
- $query->from('appconfig')
- ->setMaxResults(1);
-
- $result = $query->executeQuery();
- $column = $result->fetchOne();
- $result->closeCursor();
- $this->assertEquals($return, $column);
- }
-
- public static function providerTestConcatString(): array {
- return [
- '1 column: string param unicode'
- => [function ($q) {
- return [false, [$q->createNamedParameter('👍')], '👍'];
- }],
- '2 columns: string param and string param'
- => [function ($q) {
- return [false, [$q->createNamedParameter('foo'), $q->createNamedParameter('bar')], 'foobar'];
- }],
- '2 columns: string param and int literal'
- => [function ($q) {
- return [false, [$q->createNamedParameter('foo'), $q->expr()->literal(1)], 'foo1'];
- }],
- '2 columns: string param and string literal'
- => [function ($q) {
- return [false, [$q->createNamedParameter('foo'), $q->expr()->literal('bar')], 'foobar'];
- }],
- '2 columns: string real and int literal'
- => [function ($q) {
- return [true, ['configkey', $q->expr()->literal(2)], '12'];
- }],
- '4 columns: string literal'
- => [function ($q) {
- return [false, [$q->expr()->literal('foo'), $q->expr()->literal('bar'), $q->expr()->literal('foo'), $q->expr()->literal('bar')], 'foobarfoobar'];
- }],
- '4 columns: int literal'
- => [function ($q) {
- return [false, [$q->expr()->literal(1), $q->expr()->literal(2), $q->expr()->literal(3), $q->expr()->literal(4)], '1234'];
- }],
- '5 columns: string param with special chars used in the function'
- => [function ($q) {
- return [false, [$q->createNamedParameter('b'), $q->createNamedParameter("'"), $q->createNamedParameter('||'), $q->createNamedParameter(','), $q->createNamedParameter('a')], "b'||,a"];
- }],
- ];
- }
-
- protected function clearDummyData(): void {
- $delete = $this->connection->getQueryBuilder();
-
- $delete->delete('appconfig')
- ->where($delete->expr()->eq('appid', $delete->createNamedParameter('group_concat')));
- $delete->executeStatement();
- }
-
- protected function addDummyData(): void {
- $this->clearDummyData();
- $insert = $this->connection->getQueryBuilder();
-
- $insert->insert('appconfig')
- ->setValue('appid', $insert->createNamedParameter('group_concat'))
- ->setValue('configvalue', $insert->createNamedParameter('unittest'))
- ->setValue('configkey', $insert->createParameter('value'));
-
- $insert->setParameter('value', '1');
- $insert->executeStatement();
- $insert->setParameter('value', '3');
- $insert->executeStatement();
- $insert->setParameter('value', '2');
- $insert->executeStatement();
- }
-
- public function testGroupConcatWithoutSeparator(): void {
- $this->addDummyData();
- $query = $this->connection->getQueryBuilder();
-
- $query->select($query->func()->groupConcat('configkey'))
- ->from('appconfig')
- ->where($query->expr()->eq('appid', $query->createNamedParameter('group_concat')));
-
- $result = $query->executeQuery();
- $column = $result->fetchOne();
- $result->closeCursor();
- $this->assertStringContainsString(',', $column);
- $actual = explode(',', $column);
- $this->assertEqualsCanonicalizing([1,2,3], $actual);
- }
-
- public function testGroupConcatWithSeparator(): void {
- $this->addDummyData();
- $query = $this->connection->getQueryBuilder();
-
- $query->select($query->func()->groupConcat('configkey', '#'))
- ->from('appconfig')
- ->where($query->expr()->eq('appid', $query->createNamedParameter('group_concat')));
-
- $result = $query->executeQuery();
- $column = $result->fetchOne();
- $result->closeCursor();
- $this->assertStringContainsString('#', $column);
- $actual = explode('#', $column);
- $this->assertEqualsCanonicalizing([1,2,3], $actual);
- }
-
- public function testGroupConcatWithSingleQuoteSeparator(): void {
- $this->addDummyData();
- $query = $this->connection->getQueryBuilder();
-
- $query->select($query->func()->groupConcat('configkey', '\''))
- ->from('appconfig')
- ->where($query->expr()->eq('appid', $query->createNamedParameter('group_concat')));
-
- $result = $query->executeQuery();
- $column = $result->fetchOne();
- $result->closeCursor();
- $this->assertStringContainsString("'", $column);
- $actual = explode("'", $column);
- $this->assertEqualsCanonicalizing([1,2,3], $actual);
- }
-
- public function testGroupConcatWithDoubleQuoteSeparator(): void {
- $this->addDummyData();
- $query = $this->connection->getQueryBuilder();
-
- $query->select($query->func()->groupConcat('configkey', '"'))
- ->from('appconfig')
- ->where($query->expr()->eq('appid', $query->createNamedParameter('group_concat')));
-
- $result = $query->executeQuery();
- $column = $result->fetchOne();
- $result->closeCursor();
- $this->assertStringContainsString('"', $column);
- $actual = explode('"', $column);
- $this->assertEqualsCanonicalizing([1,2,3], $actual);
- }
-
- protected function clearIntDummyData(): void {
- $delete = $this->connection->getQueryBuilder();
-
- $delete->delete('systemtag')
- ->where($delete->expr()->eq('name', $delete->createNamedParameter('group_concat')));
- $delete->executeStatement();
- }
-
- protected function addIntDummyData(): void {
- $this->clearIntDummyData();
- $insert = $this->connection->getQueryBuilder();
-
- $insert->insert('systemtag')
- ->setValue('name', $insert->createNamedParameter('group_concat'))
- ->setValue('visibility', $insert->createNamedParameter(1))
- ->setValue('editable', $insert->createParameter('value'));
-
- $insert->setParameter('value', 1);
- $insert->executeStatement();
- $insert->setParameter('value', 2);
- $insert->executeStatement();
- $insert->setParameter('value', 3);
- $insert->executeStatement();
- }
-
- public function testIntGroupConcatWithoutSeparator(): void {
- $this->addIntDummyData();
- $query = $this->connection->getQueryBuilder();
-
- $query->select($query->func()->groupConcat('editable'))
- ->from('systemtag')
- ->where($query->expr()->eq('name', $query->createNamedParameter('group_concat')));
-
- $result = $query->executeQuery();
- $column = $result->fetchOne();
- $result->closeCursor();
- $this->assertStringContainsString(',', $column);
- $actual = explode(',', $column);
- $this->assertEqualsCanonicalizing([1,2,3], $actual);
- }
-
- public function testIntGroupConcatWithSeparator(): void {
- $this->addIntDummyData();
- $query = $this->connection->getQueryBuilder();
-
- $query->select($query->func()->groupConcat('editable', '#'))
- ->from('systemtag')
- ->where($query->expr()->eq('name', $query->createNamedParameter('group_concat')));
-
- $result = $query->executeQuery();
- $column = $result->fetchOne();
- $result->closeCursor();
- $this->assertStringContainsString('#', $column);
- $actual = explode('#', $column);
- $this->assertEqualsCanonicalizing([1,2,3], $actual);
- }
-
- public function testMd5(): void {
- $query = $this->connection->getQueryBuilder();
-
- $query->select($query->func()->md5($query->createNamedParameter('foobar')));
- $query->from('appconfig')
- ->setMaxResults(1);
-
- $result = $query->executeQuery();
- $column = $result->fetchOne();
- $result->closeCursor();
- $this->assertEquals(md5('foobar'), $column);
- }
-
- public function testSubstring(): void {
- $query = $this->connection->getQueryBuilder();
-
- $query->select($query->func()->substring($query->createNamedParameter('foobar'), new Literal(2), $query->createNamedParameter(2)));
- $query->from('appconfig')
- ->setMaxResults(1);
-
- $result = $query->executeQuery();
- $column = $result->fetchOne();
- $result->closeCursor();
- $this->assertEquals('oo', $column);
- }
-
- public function testSubstringNoLength(): void {
- $query = $this->connection->getQueryBuilder();
-
- $query->select($query->func()->substring($query->createNamedParameter('foobar'), new Literal(2)));
- $query->from('appconfig')
- ->setMaxResults(1);
-
- $result = $query->executeQuery();
- $column = $result->fetchOne();
- $result->closeCursor();
- $this->assertEquals('oobar', $column);
- }
-
- public function testLower(): void {
- $query = $this->connection->getQueryBuilder();
-
- $query->select($query->func()->lower($query->createNamedParameter('FooBar')));
- $query->from('appconfig')
- ->setMaxResults(1);
-
- $result = $query->executeQuery();
- $column = $result->fetchOne();
- $result->closeCursor();
- $this->assertEquals('foobar', $column);
- }
-
- public function testAdd(): void {
- $query = $this->connection->getQueryBuilder();
-
- $query->select($query->func()->add($query->createNamedParameter(2, IQueryBuilder::PARAM_INT), new Literal(1)));
- $query->from('appconfig')
- ->setMaxResults(1);
-
- $result = $query->executeQuery();
- $column = $result->fetchOne();
- $result->closeCursor();
- $this->assertEquals(3, $column);
- }
-
- public function testSubtract(): void {
- $query = $this->connection->getQueryBuilder();
-
- $query->select($query->func()->subtract($query->createNamedParameter(2, IQueryBuilder::PARAM_INT), new Literal(1)));
- $query->from('appconfig')
- ->setMaxResults(1);
-
- $result = $query->executeQuery();
- $column = $result->fetchOne();
- $result->closeCursor();
- $this->assertEquals(1, $column);
- }
-
- public function testCount(): void {
- $query = $this->connection->getQueryBuilder();
-
- $query->select($query->func()->count('appid'));
- $query->from('appconfig')
- ->setMaxResults(1);
-
- $result = $query->executeQuery();
- $column = $result->fetchOne();
- $result->closeCursor();
- $this->assertGreaterThan(1, $column);
- }
-
- public static function octetLengthProvider(): array {
- return [
- ['', 0],
- ['foobar', 6],
- ['fé', 3],
- ['šđčćž', 10],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('octetLengthProvider')]
- public function testOctetLength(string $str, int $bytes): void {
- $query = $this->connection->getQueryBuilder();
-
- $query->select($query->func()->octetLength($query->createNamedParameter($str, IQueryBuilder::PARAM_STR)));
- $query->from('appconfig')
- ->setMaxResults(1);
-
- $result = $query->executeQuery();
- $column = $result->fetchOne();
- $result->closeCursor();
- $this->assertEquals($bytes, $column);
- }
-
- public static function charLengthProvider(): array {
- return [
- ['', 0],
- ['foobar', 6],
- ['fé', 2],
- ['šđčćž', 5],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('charLengthProvider')]
- public function testCharLength(string $str, int $bytes): void {
- $query = $this->connection->getQueryBuilder();
-
- $query->select($query->func()->charLength($query->createNamedParameter($str, IQueryBuilder::PARAM_STR)));
- $query->from('appconfig')
- ->setMaxResults(1);
-
- $result = $query->executeQuery();
- $column = $result->fetchOne();
- $result->closeCursor();
- $this->assertNotNull($column);
- $this->assertEquals($bytes, $column);
- }
-
- private function setUpMinMax($value) {
- $query = $this->connection->getQueryBuilder();
-
- $query->insert('appconfig')
- ->values([
- 'appid' => $query->createNamedParameter('minmax'),
- 'configkey' => $query->createNamedParameter(uniqid()),
- 'configvalue' => $query->createNamedParameter((string)$value),
- ]);
- $query->executeStatement();
- }
-
- private function clearMinMax() {
- $query = $this->connection->getQueryBuilder();
-
- $query->delete('appconfig')
- ->where($query->expr()->eq('appid', $query->createNamedParameter('minmax')));
- $query->executeStatement();
- }
-
- public function testMaxEmpty(): void {
- $this->clearMinMax();
-
- $query = $this->connection->getQueryBuilder();
-
- $query->select($query->func()->max($query->expr()->castColumn('configvalue', IQueryBuilder::PARAM_INT)));
- $query->from('appconfig')
- ->where($query->expr()->eq('appid', $query->createNamedParameter('minmax')))
- ->setMaxResults(1);
-
- $result = $query->executeQuery();
- $row = $result->fetchOne();
- $result->closeCursor();
- $this->assertEquals(null, $row);
- }
-
- public function testMinEmpty(): void {
- $this->clearMinMax();
-
- $query = $this->connection->getQueryBuilder();
-
- $query->select($query->func()->min($query->expr()->castColumn('configvalue', IQueryBuilder::PARAM_INT)));
- $query->from('appconfig')
- ->where($query->expr()->eq('appid', $query->createNamedParameter('minmax')))
- ->setMaxResults(1);
-
- $result = $query->executeQuery();
- $row = $result->fetchOne();
- $result->closeCursor();
- $this->assertEquals(null, $row);
- }
-
- public function testMax(): void {
- $this->clearMinMax();
- $this->setUpMinMax(10);
- $this->setUpMinMax(11);
- $this->setUpMinMax(20);
-
- $query = $this->connection->getQueryBuilder();
-
- $query->select($query->func()->max($query->expr()->castColumn('configvalue', IQueryBuilder::PARAM_INT)));
- $query->from('appconfig')
- ->where($query->expr()->eq('appid', $query->createNamedParameter('minmax')))
- ->setMaxResults(1);
-
- $result = $query->executeQuery();
- $row = $result->fetchOne();
- $result->closeCursor();
- $this->assertEquals(20, $row);
- }
-
- public function testMin(): void {
- $this->clearMinMax();
- $this->setUpMinMax(10);
- $this->setUpMinMax(11);
- $this->setUpMinMax(20);
-
- $query = $this->connection->getQueryBuilder();
-
- $query->select($query->func()->min($query->expr()->castColumn('configvalue', IQueryBuilder::PARAM_INT)));
- $query->from('appconfig')
- ->where($query->expr()->eq('appid', $query->createNamedParameter('minmax')))
- ->setMaxResults(1);
-
- $result = $query->executeQuery();
- $row = $result->fetchOne();
- $result->closeCursor();
- $this->assertEquals(10, $row);
- }
-
- public function testGreatest(): void {
- $query = $this->connection->getQueryBuilder();
-
- $query->select($query->func()->greatest($query->createNamedParameter(2, IQueryBuilder::PARAM_INT), new Literal(1)));
- $query->from('appconfig')
- ->setMaxResults(1);
-
- $result = $query->executeQuery();
- $row = $result->fetchOne();
- $result->closeCursor();
- $this->assertEquals(2, $row);
- }
-
- public function testLeast(): void {
- $query = $this->connection->getQueryBuilder();
-
- $query->select($query->func()->least($query->createNamedParameter(2, IQueryBuilder::PARAM_INT), new Literal(1)));
- $query->from('appconfig')
- ->setMaxResults(1);
-
- $result = $query->executeQuery();
- $row = $result->fetchOne();
- $result->closeCursor();
- $this->assertEquals(1, $row);
- }
-
- public static function regexSubstringData(): array {
- return [
- ['foobar', 'foo', 'foo'],
- ['foobar', 'b.+$', 'bar'],
- ['foo#bar', 'ba.+r$', null],
- ['foo#bar', 'o#.', 'o#b'],
- ['a/file/path', '[^/]+$', 'path'],
- ];
- }
-
- #[DataProvider('regexSubstringData')]
- public function testRegexSubstring(string $input, string $pattern, ?string $expected): void {
- $query = $this->connection->getQueryBuilder();
-
- $query->select($query->func()->regexSubstring(
- $query->createNamedParameter($input),
- $query->createNamedParameter($pattern),
- ));
- $query->from('appconfig')
- ->setMaxResults(1);
-
- $result = $query->executeQuery();
- $row = $result->fetchOne();
- $result->closeCursor();
- $this->assertEquals($expected, $row);
- }
-}
--- a/tests/lib/DB/QueryBuilder/Partitioned/JoinConditionTest.php
+++ /dev/null
@@ -1,75 +0,0 @@
-
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-namespace lib\DB\QueryBuilder\Partitioned;
-
-use OC\DB\ConnectionAdapter;
-use OC\DB\QueryBuilder\Partitioned\JoinCondition;
-use OC\DB\QueryBuilder\QueryBuilder;
-use OC\SystemConfig;
-use OCP\DB\QueryBuilder\IQueryBuilder;
-use OCP\IDBConnection;
-use Psr\Log\LoggerInterface;
-use Test\TestCase;
-
-class JoinConditionTest extends TestCase {
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
- }
-
- public static function platformProvider(): array {
- return [
- [IDBConnection::PLATFORM_SQLITE],
- [IDBConnection::PLATFORM_POSTGRES],
- [IDBConnection::PLATFORM_MYSQL],
- [IDBConnection::PLATFORM_ORACLE],
- ];
- }
-
- private function getBuilder(string $platform): IQueryBuilder {
- $connection = $this->createMock(ConnectionAdapter::class);
- $connection->method('getDatabaseProvider')->willReturn($platform);
- return new QueryBuilder(
- $connection,
- $this->createMock(SystemConfig::class),
- $this->createMock(LoggerInterface::class)
- );
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('platformProvider')]
- public function testParseCondition(string $platform): void {
- $query = $this->getBuilder($platform);
- $param1 = $query->createNamedParameter('files');
- $param2 = $query->createNamedParameter('test');
- $condition = $query->expr()->andX(
- $query->expr()->eq('tagmap.categoryid', 'tag.id'),
- $query->expr()->eq('tag.type', $param1),
- $query->expr()->eq('tag.uid', $param2)
- );
- $parsed = JoinCondition::parse($condition, 'vcategory', 'tag', 'tagmap');
- $this->assertEquals('tagmap.categoryid', $parsed->fromColumn);
- $this->assertEquals('tag.id', $parsed->toColumn);
- $this->assertEquals([], $parsed->fromConditions);
- $this->assertEquals([
- $query->expr()->eq('tag.type', $param1),
- $query->expr()->eq('tag.uid', $param2),
- ], $parsed->toConditions);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('platformProvider')]
- public function testParseCastCondition(string $platform): void {
- $query = $this->getBuilder($platform);
-
- $condition = $query->expr()->eq($query->expr()->castColumn('m.objectid', IQueryBuilder::PARAM_INT), 'f.fileid');
- $parsed = JoinCondition::parse($condition, 'filecache', 'f', 'm');
- $this->assertEquals('m.objectid', $parsed->fromColumn);
- $this->assertEquals('f.fileid', $parsed->toColumn);
- $this->assertEquals([], $parsed->fromConditions);
- }
-}
--- a/tests/lib/DB/QueryBuilder/Partitioned/PartitionedQueryBuilderTest.php
+++ /dev/null
@@ -1,234 +0,0 @@
-markTestSkipped('Test requires 64bit');
- return;
- }
- $this->connection = Server::get(IDBConnection::class);
- $this->shardConnectionManager = Server::get(ShardConnectionManager::class);
- $this->autoIncrementHandler = Server::get(AutoIncrementHandler::class);
-
- $this->setupFileCache();
- }
-
- #[\Override]
- protected function tearDown(): void {
- // PHP unit also runs tearDown when the test is skipped, but we only initialized when using 64bit
- // see https://github.com/sebastianbergmann/phpunit/issues/6394
- if (PHP_INT_SIZE >= 8) {
- $this->cleanupDb();
- }
- parent::tearDown();
- }
-
- private function getQueryBuilder(): PartitionedQueryBuilder {
- $builder = $this->connection->getQueryBuilder();
- if ($builder instanceof PartitionedQueryBuilder) {
- return $builder;
- } else {
- return new PartitionedQueryBuilder($builder, [], $this->shardConnectionManager, $this->autoIncrementHandler);
- }
- }
-
- private function setupFileCache(): void {
- $this->cleanupDb();
- $query = $this->getQueryBuilder();
- $query->insert('storages')
- ->values([
- 'numeric_id' => $query->createNamedParameter(1001001, IQueryBuilder::PARAM_INT),
- 'id' => $query->createNamedParameter('test1'),
- ]);
- $query->executeStatement();
-
- $query = $this->getQueryBuilder();
- $query->insert('filecache')
- ->values([
- 'storage' => $query->createNamedParameter(1001001, IQueryBuilder::PARAM_INT),
- 'path' => $query->createNamedParameter('file1'),
- 'path_hash' => $query->createNamedParameter(md5('file1')),
- ]);
- $query->executeStatement();
- $fileId = $query->getLastInsertId();
-
- $query = $this->getQueryBuilder();
- $query->insert('filecache_extended')
- ->hintShardKey('storage', 1001001)
- ->values([
- 'fileid' => $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT),
- 'upload_time' => $query->createNamedParameter(1234, IQueryBuilder::PARAM_INT),
- ]);
- $query->executeStatement();
-
- $query = $this->getQueryBuilder();
- $query->insert('mounts')
- ->values([
- 'storage_id' => $query->createNamedParameter(1001001, IQueryBuilder::PARAM_INT),
- 'user_id' => $query->createNamedParameter('partitioned_test'),
- 'mount_point' => $query->createNamedParameter('/mount/point'),
- 'mount_point_hash' => $query->createNamedParameter(hash('xxh128', '/mount/point')),
- 'mount_provider_class' => $query->createNamedParameter('test'),
- 'root_id' => $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT),
- ]);
- $query->executeStatement();
- }
-
- private function cleanupDb(): void {
- $query = $this->getQueryBuilder();
- $query->delete('storages')
- ->where($query->expr()->gt('numeric_id', $query->createNamedParameter(1000000, IQueryBuilder::PARAM_INT)));
- $query->executeStatement();
-
- $query = $this->getQueryBuilder();
- $query->delete('filecache')
- ->where($query->expr()->gt('storage', $query->createNamedParameter(1000000, IQueryBuilder::PARAM_INT)))
- ->runAcrossAllShards();
- $query->executeStatement();
-
- $query = $this->getQueryBuilder();
- $query->delete('filecache_extended')
- ->runAcrossAllShards();
- $query->executeStatement();
-
- $query = $this->getQueryBuilder();
- $query->delete('mounts')
- ->where($query->expr()->like('user_id', $query->createNamedParameter('partitioned_%')));
- $query->executeStatement();
- }
-
- public function testSimpleOnlyPartitionQuery(): void {
- $builder = $this->getQueryBuilder();
- $builder->addPartition(new PartitionSplit('filecache', ['filecache']));
-
- // query borrowed from UserMountCache
- $query = $builder->select('path')
- ->from('filecache')
- ->where($builder->expr()->eq('storage', $builder->createNamedParameter(1001001, IQueryBuilder::PARAM_INT)));
-
- $results = $query->executeQuery()->fetchAll();
- $this->assertCount(1, $results);
- $this->assertEquals($results[0]['path'], 'file1');
- }
-
- public function testSimplePartitionedQuery(): void {
- $builder = $this->getQueryBuilder();
- $builder->addPartition(new PartitionSplit('filecache', ['filecache']));
-
- // query borrowed from UserMountCache
- $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_point_hash', 'mount_id', 'f.path', 'mount_provider_class')
- ->from('mounts', 'm')
- ->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid'))
- ->where($builder->expr()->eq('storage_id', $builder->createNamedParameter(1001001, IQueryBuilder::PARAM_INT)));
-
- $query->andWhere($builder->expr()->eq('user_id', $builder->createNamedParameter('partitioned_test')));
-
- $this->assertEquals(2, $query->getPartitionCount());
-
- $results = $query->executeQuery()->fetchAll();
- $this->assertCount(1, $results);
- $this->assertEquals($results[0]['user_id'], 'partitioned_test');
- $this->assertEquals($results[0]['mount_point'], '/mount/point');
- $this->assertEquals($results[0]['mount_point_hash'], hash('xxh128', '/mount/point'));
- $this->assertEquals($results[0]['mount_provider_class'], 'test');
- $this->assertEquals($results[0]['path'], 'file1');
- }
-
- public function testMultiTablePartitionedQuery(): void {
- $builder = $this->getQueryBuilder();
- $builder->addPartition(new PartitionSplit('filecache', ['filecache', 'filecache_extended']));
-
- $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_point_hash', 'mount_id', 'f.path', 'mount_provider_class', 'fe.upload_time')
- ->from('mounts', 'm')
- ->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid'))
- ->innerJoin('f', 'filecache_extended', 'fe', $builder->expr()->eq('f.fileid', 'fe.fileid'))
- ->where($builder->expr()->eq('storage_id', $builder->createNamedParameter(1001001, IQueryBuilder::PARAM_INT)));
-
- $query->andWhere($builder->expr()->eq('user_id', $builder->createNamedParameter('partitioned_test')));
-
- $this->assertEquals(2, $query->getPartitionCount());
-
- $results = $query->executeQuery()->fetchAll();
- $this->assertCount(1, $results);
- $this->assertEquals($results[0]['user_id'], 'partitioned_test');
- $this->assertEquals($results[0]['mount_point'], '/mount/point');
- $this->assertEquals($results[0]['mount_point_hash'], hash('xxh128', '/mount/point'));
- $this->assertEquals($results[0]['mount_provider_class'], 'test');
- $this->assertEquals($results[0]['path'], 'file1');
- $this->assertEquals($results[0]['upload_time'], 1234);
- }
-
- public function testPartitionedQueryFromSplit(): void {
- $builder = $this->getQueryBuilder();
- $builder->addPartition(new PartitionSplit('filecache', ['filecache']));
-
- $query = $builder->select('storage', 'm.root_id', 'm.user_id', 'm.mount_point', 'm.mount_point_hash', 'm.mount_id', 'path', 'm.mount_provider_class')
- ->from('filecache', 'f')
- ->innerJoin('f', 'mounts', 'm', $builder->expr()->eq('m.root_id', 'f.fileid'));
- $query->where($builder->expr()->eq('storage', $builder->createNamedParameter(1001001, IQueryBuilder::PARAM_INT)));
-
- $query->andWhere($builder->expr()->eq('m.user_id', $builder->createNamedParameter('partitioned_test')));
-
- $this->assertEquals(2, $query->getPartitionCount());
-
- $results = $query->executeQuery()->fetchAll();
- $this->assertCount(1, $results);
- $this->assertEquals($results[0]['user_id'], 'partitioned_test');
- $this->assertEquals($results[0]['mount_point'], '/mount/point');
- $this->assertEquals($results[0]['mount_point_hash'], hash('xxh128', '/mount/point'));
- $this->assertEquals($results[0]['mount_provider_class'], 'test');
- $this->assertEquals($results[0]['path'], 'file1');
- }
-
- public function testMultiJoinPartitionedQuery(): void {
- $builder = $this->getQueryBuilder();
- $builder->addPartition(new PartitionSplit('filecache', ['filecache']));
-
- // query borrowed from UserMountCache
- $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_point_hash', 'mount_id', 'f.path', 'mount_provider_class')
- ->selectAlias('s.id', 'storage_string_id')
- ->from('mounts', 'm')
- ->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid'))
- ->innerJoin('f', 'storages', 's', $builder->expr()->eq('f.storage', 's.numeric_id'))
- ->where($builder->expr()->eq('storage_id', $builder->createNamedParameter(1001001, IQueryBuilder::PARAM_INT)));
-
- $query->andWhere($builder->expr()->eq('user_id', $builder->createNamedParameter('partitioned_test')));
-
- $this->assertEquals(3, $query->getPartitionCount());
-
- $results = $query->executeQuery()->fetchAll();
- $this->assertCount(1, $results);
- $this->assertEquals($results[0]['user_id'], 'partitioned_test');
- $this->assertEquals($results[0]['mount_point'], '/mount/point');
- $this->assertEquals($results[0]['mount_point_hash'], hash('xxh128', '/mount/point'));
- $this->assertEquals($results[0]['mount_provider_class'], 'test');
- $this->assertEquals($results[0]['path'], 'file1');
- $this->assertEquals($results[0]['storage_string_id'], 'test1');
- }
-}
--- a/tests/lib/DB/QueryBuilder/QueryBuilderTest.php
+++ /dev/null
@@ -1,1452 +0,0 @@
-connection = Server::get(IDBConnection::class);
- $this->config = $this->createMock(SystemConfig::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->queryBuilder = new QueryBuilder($this->connection, $this->config, $this->logger);
- }
-
- protected function createTestingRows($appId = 'testFirstResult') {
- $qB = $this->connection->getQueryBuilder();
- for ($i = 1; $i < 10; $i++) {
- $qB->insert('*PREFIX*appconfig')
- ->values([
- 'appid' => $qB->expr()->literal($appId),
- 'configkey' => $qB->expr()->literal('testing' . $i),
- 'configvalue' => $qB->expr()->literal(100 - $i),
- ])
- ->executeStatement();
- }
- }
-
- protected function getTestingRows(QueryBuilder $queryBuilder) {
- $queryBuilder->select('configvalue')
- ->from('*PREFIX*appconfig')
- ->where($queryBuilder->expr()->eq(
- 'appid',
- $queryBuilder->expr()->literal('testFirstResult')
- ))
- ->orderBy('configkey', 'ASC');
-
- $query = $queryBuilder->executeQuery();
- $rows = [];
- while ($row = $query->fetch()) {
- $rows[] = $row['configvalue'];
- }
- $query->closeCursor();
-
- return $rows;
- }
-
- protected function deleteTestingRows($appId = 'testFirstResult') {
- $qB = $this->connection->getQueryBuilder();
-
- $qB->delete('*PREFIX*appconfig')
- ->where($qB->expr()->eq('appid', $qB->expr()->literal($appId)))
- ->executeStatement();
- }
-
- public static function dataFirstResult(): array {
- return [
- [0, [99, 98, 97, 96, 95, 94, 93, 92, 91]],
- [0, [99, 98, 97, 96, 95, 94, 93, 92, 91]],
- [1, [98, 97, 96, 95, 94, 93, 92, 91]],
- [5, [94, 93, 92, 91]],
- ];
- }
-
- /**
- *
- * @param int|null $firstResult
- * @param array $expectedSet
- */
- #[DataProvider('dataFirstResult')]
- public function testFirstResult($firstResult, $expectedSet): void {
- $this->deleteTestingRows();
- $this->createTestingRows();
-
- if ($firstResult !== null) {
- $this->queryBuilder->setFirstResult($firstResult);
- }
-
- $this->assertSame(
- $firstResult ?? 0,
- $this->queryBuilder->getFirstResult()
- );
-
- $rows = $this->getTestingRows($this->queryBuilder);
-
- $this->assertCount(sizeof($expectedSet), $rows);
- $this->assertEquals($expectedSet, $rows);
-
- $this->deleteTestingRows();
- }
-
- public static function dataMaxResults(): array {
- return [
- [null, [99, 98, 97, 96, 95, 94, 93, 92, 91]],
- // Limit 0 gives mixed results: either all entries or none is returned
- //[0, []],
- [1, [99]],
- [5, [99, 98, 97, 96, 95]],
- ];
- }
-
- /**
- *
- * @param int $maxResult
- * @param array $expectedSet
- */
- #[DataProvider('dataMaxResults')]
- public function testMaxResults($maxResult, $expectedSet): void {
- $this->deleteTestingRows();
- $this->createTestingRows();
-
- if ($maxResult !== null) {
- $this->queryBuilder->setMaxResults($maxResult);
- }
-
- $this->assertSame(
- $maxResult,
- $this->queryBuilder->getMaxResults()
- );
-
- $rows = $this->getTestingRows($this->queryBuilder);
-
- $this->assertCount(sizeof($expectedSet), $rows);
- $this->assertEquals($expectedSet, $rows);
-
- $this->deleteTestingRows();
- }
-
- public static function dataSelect(): array {
- return [
- // select('column1')
- [['configvalue'], ['configvalue' => '99']],
-
- // select('column1', 'column2')
- [['configvalue', 'configkey'], ['configvalue' => '99', 'configkey' => 'testing1']],
-
- // select(['column1', 'column2'])
- [[['configvalue', 'configkey']], ['configvalue' => '99', 'configkey' => 'testing1']],
-
- // select(new Literal('column1'))
- [['l::column1'], [], 'column1'],
-
- // select(new Literal('column1'), 'column2')
- [['l::column1', 'configkey'], ['configkey' => 'testing1'], 'column1'],
-
- // select([new Literal('column1'), 'column2'])
- [[['l::column1', 'configkey']], ['configkey' => 'testing1'], 'column1'],
- ];
- }
-
- #[DataProvider('dataSelect')]
- public function testSelect(array $selectArguments, array $expected, string $expectedLiteral = ''): void {
- $this->deleteTestingRows();
- $this->createTestingRows();
-
- array_walk_recursive(
- $selectArguments,
- function (string &$arg): void {
- if (\str_starts_with($arg, 'l::')) {
- $arg = $this->queryBuilder->expr()->literal(substr($arg, 3));
- }
- },
- );
- $this->queryBuilder->select(...$selectArguments);
-
- $this->queryBuilder->from('*PREFIX*appconfig')
- ->where($this->queryBuilder->expr()->eq(
- 'appid',
- $this->queryBuilder->expr()->literal('testFirstResult')
- ))
- ->orderBy('configkey', 'ASC')
- ->setMaxResults(1);
-
- $query = $this->queryBuilder->executeQuery();
- $row = $query->fetch();
- $query->closeCursor();
-
- foreach ($expected as $key => $value) {
- $this->assertArrayHasKey($key, $row);
- $this->assertEquals($value, $row[$key]);
- unset($row[$key]);
- }
-
- if ($expectedLiteral) {
- $this->assertEquals([$expectedLiteral], array_values($row));
- } else {
- $this->assertEmpty($row);
- }
-
- $this->deleteTestingRows();
- }
-
- public static function dataSelectAlias(): array {
- return [
- ['configvalue', 'cv', ['cv' => '99']],
- ['l::column1', 'thing', ['thing' => 'column1']],
- ];
- }
-
- #[DataProvider('dataSelectAlias')]
- public function testSelectAlias(string $select, string $alias, array $expected): void {
- if (str_starts_with($select, 'l::')) {
- $select = $this->queryBuilder->expr()->literal(substr($select, 3));
- }
-
- $this->deleteTestingRows();
- $this->createTestingRows();
-
- $this->queryBuilder->selectAlias($select, $alias);
-
- $this->queryBuilder->from('*PREFIX*appconfig')
- ->where($this->queryBuilder->expr()->eq(
- 'appid',
- $this->queryBuilder->expr()->literal('testFirstResult')
- ))
- ->orderBy('configkey', 'ASC')
- ->setMaxResults(1);
-
- $query = $this->queryBuilder->executeQuery();
- $row = $query->fetch();
- $query->closeCursor();
-
- $this->assertEquals(
- $expected,
- $row
- );
-
- $this->deleteTestingRows();
- }
-
- public function testSelectDistinct(): void {
- $this->deleteTestingRows('testFirstResult1');
- $this->deleteTestingRows('testFirstResult2');
- $this->createTestingRows('testFirstResult1');
- $this->createTestingRows('testFirstResult2');
-
- $this->queryBuilder->selectDistinct('appid');
-
- $this->queryBuilder->from('*PREFIX*appconfig')
- ->where($this->queryBuilder->expr()->in(
- 'appid',
- [$this->queryBuilder->expr()->literal('testFirstResult1'), $this->queryBuilder->expr()->literal('testFirstResult2')]
- ))
- ->orderBy('appid', 'DESC');
-
- $query = $this->queryBuilder->executeQuery();
- $rows = $query->fetchAll();
- $query->closeCursor();
-
- $this->assertEquals(
- [['appid' => 'testFirstResult2'], ['appid' => 'testFirstResult1']],
- $rows
- );
-
- $this->deleteTestingRows('testFirstResult1');
- $this->deleteTestingRows('testFirstResult2');
- }
-
- public function testSelectDistinctMultiple(): void {
- $this->deleteTestingRows('testFirstResult1');
- $this->deleteTestingRows('testFirstResult2');
- $this->createTestingRows('testFirstResult1');
- $this->createTestingRows('testFirstResult2');
-
- $this->queryBuilder->selectDistinct(['appid', 'configkey']);
-
- $this->queryBuilder->from('*PREFIX*appconfig')
- ->where($this->queryBuilder->expr()->eq(
- 'appid',
- $this->queryBuilder->expr()->literal('testFirstResult1')
- ))
- ->orderBy('configkey', 'ASC');
-
- $query = $this->queryBuilder->executeQuery();
- $rows = $query->fetchAll();
- $query->closeCursor();
-
- $this->assertEquals(
- [
- ['appid' => 'testFirstResult1', 'configkey' => 'testing1'],
- ['appid' => 'testFirstResult1', 'configkey' => 'testing2'],
- ['appid' => 'testFirstResult1', 'configkey' => 'testing3'],
- ['appid' => 'testFirstResult1', 'configkey' => 'testing4'],
- ['appid' => 'testFirstResult1', 'configkey' => 'testing5'],
- ['appid' => 'testFirstResult1', 'configkey' => 'testing6'],
- ['appid' => 'testFirstResult1', 'configkey' => 'testing7'],
- ['appid' => 'testFirstResult1', 'configkey' => 'testing8'],
- ['appid' => 'testFirstResult1', 'configkey' => 'testing9'],
- ],
- $rows
- );
-
- $this->deleteTestingRows('testFirstResult1');
- $this->deleteTestingRows('testFirstResult2');
- }
-
- public static function dataAddSelect(): array {
- return [
- // addSelect('column1')
- [['configvalue'], ['appid' => 'testFirstResult', 'configvalue' => '99']],
-
- // addSelect('column1', 'column2')
- [['configvalue', 'configkey'], ['appid' => 'testFirstResult', 'configvalue' => '99', 'configkey' => 'testing1']],
-
- // addSelect(['column1', 'column2'])
- [[['configvalue', 'configkey']], ['appid' => 'testFirstResult', 'configvalue' => '99', 'configkey' => 'testing1']],
-
- // select(new Literal('column1'))
- [['l::column1'], ['appid' => 'testFirstResult'], 'column1'],
-
- // select(new Literal('column1'), 'column2')
- [['l::column1', 'configkey'], ['appid' => 'testFirstResult', 'configkey' => 'testing1'], 'column1'],
-
- // select([new Literal('column1'), 'column2'])
- [[['l::column1', 'configkey']], ['appid' => 'testFirstResult', 'configkey' => 'testing1'], 'column1'],
- ];
- }
-
- #[DataProvider('dataAddSelect')]
- public function testAddSelect(array $selectArguments, array $expected, string $expectedLiteral = ''): void {
- $this->deleteTestingRows();
- $this->createTestingRows();
-
- array_walk_recursive(
- $selectArguments,
- function (string &$arg): void {
- if (\str_starts_with($arg, 'l::')) {
- $arg = $this->queryBuilder->expr()->literal(substr($arg, 3));
- }
- },
- );
-
- $this->queryBuilder->select('appid');
-
- call_user_func_array(
- [$this->queryBuilder, 'addSelect'],
- $selectArguments
- );
-
- $this->queryBuilder->from('*PREFIX*appconfig')
- ->where($this->queryBuilder->expr()->eq(
- 'appid',
- $this->queryBuilder->expr()->literal('testFirstResult')
- ))
- ->orderBy('configkey', 'ASC')
- ->setMaxResults(1);
-
- $query = $this->queryBuilder->executeQuery();
- $row = $query->fetch();
- $query->closeCursor();
-
- foreach ($expected as $key => $value) {
- $this->assertArrayHasKey($key, $row);
- $this->assertEquals($value, $row[$key]);
- unset($row[$key]);
- }
-
- if ($expectedLiteral) {
- $this->assertEquals([$expectedLiteral], array_values($row));
- } else {
- $this->assertEmpty($row);
- }
-
- $this->deleteTestingRows();
- }
-
- public static function dataDelete(): array {
- return [
- ['data', null, ['table' => '`*PREFIX*data`', 'alias' => null], '`*PREFIX*data`'],
- ['data', 't', ['table' => '`*PREFIX*data`', 'alias' => 't'], '`*PREFIX*data` t'],
- ];
- }
-
- /**
- *
- * @param string $tableName
- * @param string $tableAlias
- * @param array $expectedQueryPart
- * @param string $expectedQuery
- */
- #[DataProvider('dataDelete')]
- public function testDelete($tableName, $tableAlias, $expectedQueryPart, $expectedQuery): void {
- $this->queryBuilder->delete($tableName, $tableAlias);
-
- $this->assertSame(
- $expectedQueryPart,
- $this->queryBuilder->getQueryPart('from')
- );
-
- $this->assertSame(
- 'DELETE FROM ' . $expectedQuery,
- $this->queryBuilder->getSQL()
- );
- }
-
- public static function dataUpdate(): array {
- return [
- ['data', null, ['table' => '`*PREFIX*data`', 'alias' => null], '`*PREFIX*data`'],
- ['data', 't', ['table' => '`*PREFIX*data`', 'alias' => 't'], '`*PREFIX*data` t'],
- ];
- }
-
- /**
- *
- * @param string $tableName
- * @param string $tableAlias
- * @param array $expectedQueryPart
- * @param string $expectedQuery
- */
- #[DataProvider('dataUpdate')]
- public function testUpdate($tableName, $tableAlias, $expectedQueryPart, $expectedQuery): void {
- $this->queryBuilder->update($tableName, $tableAlias);
-
- $this->assertSame(
- $expectedQueryPart,
- $this->queryBuilder->getQueryPart('from')
- );
-
- $this->assertSame(
- 'UPDATE ' . $expectedQuery . ' SET ',
- $this->queryBuilder->getSQL()
- );
- }
-
- public static function dataInsert(): array {
- return [
- ['data', ['table' => '`*PREFIX*data`'], '`*PREFIX*data`'],
- ];
- }
-
- /**
- *
- * @param string $tableName
- * @param array $expectedQueryPart
- * @param string $expectedQuery
- */
- #[DataProvider('dataInsert')]
- public function testInsert($tableName, $expectedQueryPart, $expectedQuery): void {
- $this->queryBuilder->insert($tableName);
-
- $this->assertSame(
- $expectedQueryPart,
- $this->queryBuilder->getQueryPart('from')
- );
-
- $this->assertSame(
- 'INSERT INTO ' . $expectedQuery . ' () VALUES()',
- $this->queryBuilder->getSQL()
- );
- }
-
- public static function dataFrom(): array {
- return [
- ['function', 'q', null, null, [
- ['table' => '(SELECT * FROM `*PREFIX*test`)', 'alias' => '`q`']
- ], '(SELECT * FROM `*PREFIX*test`) `q`'],
- ['data', null, null, null, [['table' => '`*PREFIX*data`', 'alias' => null]], '`*PREFIX*data`'],
- ['data', 't', null, null, [['table' => '`*PREFIX*data`', 'alias' => '`t`']], '`*PREFIX*data` `t`'],
- ['data1', null, 'data2', null, [
- ['table' => '`*PREFIX*data1`', 'alias' => null],
- ['table' => '`*PREFIX*data2`', 'alias' => null]
- ], '`*PREFIX*data1`, `*PREFIX*data2`'],
- ['data', 't1', 'data', 't2', [
- ['table' => '`*PREFIX*data`', 'alias' => '`t1`'],
- ['table' => '`*PREFIX*data`', 'alias' => '`t2`']
- ], '`*PREFIX*data` `t1`, `*PREFIX*data` `t2`'],
- ];
- }
-
- #[DataProvider('dataFrom')]
- public function testFrom(string $table1Name, ?string $table1Alias, ?string $table2Name, ?string $table2Alias, array $expectedQueryPart, string $expectedQuery): void {
- $config = $this->createMock(SystemConfig::class);
- $logger = $this->createMock(LoggerInterface::class);
- $queryBuilder = new QueryBuilder(Server::get(IDBConnection::class), $config, $logger);
-
- if ($table1Name === 'function') {
- $table1Name = $queryBuilder->createFunction('(' . $queryBuilder->select('*')->from('test')->getSQL() . ')');
- }
- $this->queryBuilder->from($table1Name, $table1Alias);
- if ($table2Name !== null) {
- $this->queryBuilder->from($table2Name, $table2Alias);
- }
-
- $this->assertSame(
- $expectedQueryPart,
- $this->queryBuilder->getQueryPart('from')
- );
-
- $this->assertSame(
- 'SELECT FROM ' . $expectedQuery,
- $this->queryBuilder->getSQL()
- );
- }
-
- public static function dataJoin(): array {
- return [
- [
- 'd1', 'data2', '', null,
- ['`d1`' => [['joinType' => 'inner', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => '', 'joinCondition' => null]]],
- '`*PREFIX*data1` `d1` INNER JOIN `*PREFIX*data2` '
- ],
- [
- 'd1', 'data2', 'd2', null,
- ['`d1`' => [['joinType' => 'inner', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => '`d2`', 'joinCondition' => null]]],
- '`*PREFIX*data1` `d1` INNER JOIN `*PREFIX*data2` `d2`'
- ],
- [
- 'd1', 'data2', 'd2', '`d1`.`field1` = `d2`.`field2`',
- ['`d1`' => [['joinType' => 'inner', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => '`d2`', 'joinCondition' => '`d1`.`field1` = `d2`.`field2`']]],
- '`*PREFIX*data1` `d1` INNER JOIN `*PREFIX*data2` `d2` ON `d1`.`field1` = `d2`.`field2`'
- ],
- ];
- }
-
- /**
- *
- * @param string $fromAlias
- * @param string $tableName
- * @param string $tableAlias
- * @param string $condition
- * @param array $expectedQueryPart
- * @param string $expectedQuery
- */
- #[DataProvider('dataJoin')]
- public function testJoin($fromAlias, $tableName, $tableAlias, $condition, $expectedQueryPart, $expectedQuery): void {
- $this->queryBuilder->from('data1', 'd1');
- $this->queryBuilder->join(
- $fromAlias,
- $tableName,
- $tableAlias,
- $condition
- );
-
- $this->assertSame(
- $expectedQueryPart,
- $this->queryBuilder->getQueryPart('join')
- );
-
- $this->assertSame(
- 'SELECT FROM ' . $expectedQuery,
- $this->queryBuilder->getSQL()
- );
- }
-
- /**
- *
- * @param string $fromAlias
- * @param string $tableName
- * @param string $tableAlias
- * @param string $condition
- * @param array $expectedQueryPart
- * @param string $expectedQuery
- */
- #[DataProvider('dataJoin')]
- public function testInnerJoin($fromAlias, $tableName, $tableAlias, $condition, $expectedQueryPart, $expectedQuery): void {
- $this->queryBuilder->from('data1', 'd1');
- $this->queryBuilder->innerJoin(
- $fromAlias,
- $tableName,
- $tableAlias,
- $condition
- );
-
- $this->assertSame(
- $expectedQueryPart,
- $this->queryBuilder->getQueryPart('join')
- );
-
- $this->assertSame(
- 'SELECT FROM ' . $expectedQuery,
- $this->queryBuilder->getSQL()
- );
- }
-
- public static function dataLeftJoin(): array {
- return [
- [
- 'd1', 'data2', '', null,
- ['`d1`' => [['joinType' => 'left', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => '', 'joinCondition' => null]]],
- '`*PREFIX*data1` `d1` LEFT JOIN `*PREFIX*data2` '
- ],
- [
- 'd1', 'data2', 'd2', null,
- ['`d1`' => [['joinType' => 'left', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => '`d2`', 'joinCondition' => null]]],
- '`*PREFIX*data1` `d1` LEFT JOIN `*PREFIX*data2` `d2`'
- ],
- [
- 'd1', 'data2', 'd2', '`d1`.`field1` = `d2`.`field2`',
- ['`d1`' => [['joinType' => 'left', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => '`d2`', 'joinCondition' => '`d1`.`field1` = `d2`.`field2`']]],
- '`*PREFIX*data1` `d1` LEFT JOIN `*PREFIX*data2` `d2` ON `d1`.`field1` = `d2`.`field2`'
- ],
- ];
- }
-
- /**
- *
- * @param string $fromAlias
- * @param string $tableName
- * @param string $tableAlias
- * @param string $condition
- * @param array $expectedQueryPart
- * @param string $expectedQuery
- */
- #[DataProvider('dataLeftJoin')]
- public function testLeftJoin($fromAlias, $tableName, $tableAlias, $condition, $expectedQueryPart, $expectedQuery): void {
- $this->queryBuilder->from('data1', 'd1');
- $this->queryBuilder->leftJoin(
- $fromAlias,
- $tableName,
- $tableAlias,
- $condition
- );
-
- $this->assertSame(
- $expectedQueryPart,
- $this->queryBuilder->getQueryPart('join')
- );
-
- $this->assertSame(
- 'SELECT FROM ' . $expectedQuery,
- $this->queryBuilder->getSQL()
- );
- }
-
- public static function dataRightJoin(): array {
- return [
- [
- 'd1', 'data2', '', null,
- ['`d1`' => [['joinType' => 'right', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => '', 'joinCondition' => null]]],
- '`*PREFIX*data1` `d1` RIGHT JOIN `*PREFIX*data2` '
- ],
- [
- 'd1', 'data2', 'd2', null,
- ['`d1`' => [['joinType' => 'right', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => '`d2`', 'joinCondition' => null]]],
- '`*PREFIX*data1` `d1` RIGHT JOIN `*PREFIX*data2` `d2`'
- ],
- [
- 'd1', 'data2', 'd2', '`d1`.`field1` = `d2`.`field2`',
- ['`d1`' => [['joinType' => 'right', 'joinTable' => '`*PREFIX*data2`', 'joinAlias' => '`d2`', 'joinCondition' => '`d1`.`field1` = `d2`.`field2`']]],
- '`*PREFIX*data1` `d1` RIGHT JOIN `*PREFIX*data2` `d2` ON `d1`.`field1` = `d2`.`field2`'
- ],
- ];
- }
-
- #[DataProvider('dataRightJoin')]
- public function testRightJoin(
- string $fromAlias,
- string $tableName,
- string $tableAlias,
- ?string $condition,
- array $expectedQueryPart,
- string $expectedQuery,
- ): void {
- $this->queryBuilder->from('data1', 'd1');
- $this->queryBuilder->rightJoin(
- $fromAlias,
- $tableName,
- $tableAlias,
- $condition
- );
-
- $this->assertSame(
- $expectedQueryPart,
- $this->queryBuilder->getQueryPart('join')
- );
-
- $this->assertSame(
- 'SELECT FROM ' . $expectedQuery,
- $this->queryBuilder->getSQL()
- );
- }
-
- public static function dataSet(): array {
- return [
- ['column1', new Literal('value'), null, null, ['`column1` = value'], '`column1` = value'],
- ['column1', new Parameter(':param'), null, null, ['`column1` = :param'], '`column1` = :param'],
- ['column1', 'column2', null, null, ['`column1` = `column2`'], '`column1` = `column2`'],
- ['column1', 'column2', 'column3', new Literal('value'), ['`column1` = `column2`', '`column3` = value'], '`column1` = `column2`, `column3` = value'],
- ];
- }
-
- /**
- *
- * @param string $partOne1
- * @param string $partOne2
- * @param string $partTwo1
- * @param string $partTwo2
- * @param array $expectedQueryPart
- * @param string $expectedQuery
- */
- #[DataProvider('dataSet')]
- public function testSet($partOne1, $partOne2, $partTwo1, $partTwo2, $expectedQueryPart, $expectedQuery): void {
- $this->queryBuilder->update('data');
- $this->queryBuilder->set($partOne1, $partOne2);
- if ($partTwo1 !== null) {
- $this->queryBuilder->set($partTwo1, $partTwo2);
- }
-
- $this->assertSame(
- $expectedQueryPart,
- $this->queryBuilder->getQueryPart('set')
- );
-
- $this->assertSame(
- 'UPDATE `*PREFIX*data` SET ' . $expectedQuery,
- $this->queryBuilder->getSQL()
- );
- }
-
- public static function dataWhere(): array {
- return [
- [['where1'], new CompositeExpression('AND', ['where1']), 'where1'],
- [['where1', 'where2'], new CompositeExpression('AND', ['where1', 'where2']), '(where1) AND (where2)'],
- ];
- }
-
- /**
- *
- * @param array $whereArguments
- * @param array $expectedQueryPart
- * @param string $expectedQuery
- */
- #[DataProvider('dataWhere')]
- public function testWhere($whereArguments, $expectedQueryPart, $expectedQuery): void {
- $this->queryBuilder->select('column');
- call_user_func_array(
- [$this->queryBuilder, 'where'],
- $whereArguments
- );
-
- $this->assertEquals(
- $expectedQueryPart,
- $this->queryBuilder->getQueryPart('where')
- );
-
- $this->assertSame(
- 'SELECT `column` WHERE ' . $expectedQuery,
- $this->queryBuilder->getSQL()
- );
- }
-
- /**
- *
- * @param array $whereArguments
- * @param array $expectedQueryPart
- * @param string $expectedQuery
- */
- #[DataProvider('dataWhere')]
- public function testAndWhere($whereArguments, $expectedQueryPart, $expectedQuery): void {
- $this->queryBuilder->select('column');
- call_user_func_array(
- [$this->queryBuilder, 'andWhere'],
- $whereArguments
- );
-
- $this->assertEquals(
- $expectedQueryPart,
- $this->queryBuilder->getQueryPart('where')
- );
-
- $this->assertSame(
- 'SELECT `column` WHERE ' . $expectedQuery,
- $this->queryBuilder->getSQL()
- );
- }
-
- public static function dataOrWhere(): array {
- return [
- [['where1'], new CompositeExpression('OR', ['where1']), 'where1'],
- [['where1', 'where2'], new CompositeExpression('OR', ['where1', 'where2']), '(where1) OR (where2)'],
- ];
- }
-
- /**
- *
- * @param array $whereArguments
- * @param array $expectedQueryPart
- * @param string $expectedQuery
- */
- #[DataProvider('dataOrWhere')]
- public function testOrWhere($whereArguments, $expectedQueryPart, $expectedQuery): void {
- $this->queryBuilder->select('column');
- call_user_func_array(
- [$this->queryBuilder, 'orWhere'],
- $whereArguments
- );
-
- $this->assertEquals(
- $expectedQueryPart,
- $this->queryBuilder->getQueryPart('where')
- );
-
- $this->assertSame(
- 'SELECT `column` WHERE ' . $expectedQuery,
- $this->queryBuilder->getSQL()
- );
- }
-
- public static function dataGroupBy(): array {
- return [
- [['column1'], ['`column1`'], '`column1`'],
- [['column1', 'column2'], ['`column1`', '`column2`'], '`column1`, `column2`'],
- ];
- }
-
- /**
- *
- * @param array $groupByArguments
- * @param array $expectedQueryPart
- * @param string $expectedQuery
- */
- #[DataProvider('dataGroupBy')]
- public function testGroupBy($groupByArguments, $expectedQueryPart, $expectedQuery): void {
- $this->queryBuilder->select('column');
- call_user_func_array(
- [$this->queryBuilder, 'groupBy'],
- $groupByArguments
- );
-
- $this->assertEquals(
- $expectedQueryPart,
- $this->queryBuilder->getQueryPart('groupBy')
- );
-
- $this->assertSame(
- 'SELECT `column` GROUP BY ' . $expectedQuery,
- $this->queryBuilder->getSQL()
- );
- }
-
- public static function dataAddGroupBy(): array {
- return [
- [['column2'], ['`column1`', '`column2`'], '`column1`, `column2`'],
- [['column2', 'column3'], ['`column1`', '`column2`', '`column3`'], '`column1`, `column2`, `column3`'],
- ];
- }
-
- /**
- *
- * @param array $groupByArguments
- * @param array $expectedQueryPart
- * @param string $expectedQuery
- */
- #[DataProvider('dataAddGroupBy')]
- public function testAddGroupBy($groupByArguments, $expectedQueryPart, $expectedQuery): void {
- $this->queryBuilder->select('column');
- $this->queryBuilder->groupBy('column1');
- call_user_func_array(
- [$this->queryBuilder, 'addGroupBy'],
- $groupByArguments
- );
-
- $this->assertEquals(
- $expectedQueryPart,
- $this->queryBuilder->getQueryPart('groupBy')
- );
-
- $this->assertSame(
- 'SELECT `column` GROUP BY ' . $expectedQuery,
- $this->queryBuilder->getSQL()
- );
- }
-
- public static function dataSetValue(): array {
- return [
- ['column', 'value', ['`column`' => 'value'], '(`column`) VALUES(value)'],
- ];
- }
-
- /**
- *
- * @param string $column
- * @param string $value
- * @param array $expectedQueryPart
- * @param string $expectedQuery
- */
- #[DataProvider('dataSetValue')]
- public function testSetValue($column, $value, $expectedQueryPart, $expectedQuery): void {
- $this->queryBuilder->insert('data');
- $this->queryBuilder->setValue($column, $value);
-
- $this->assertEquals(
- $expectedQueryPart,
- $this->queryBuilder->getQueryPart('values')
- );
-
- $this->assertSame(
- 'INSERT INTO `*PREFIX*data` ' . $expectedQuery,
- $this->queryBuilder->getSQL()
- );
- }
-
- /**
- *
- * @param string $column
- * @param string $value
- * @param array $expectedQueryPart
- * @param string $expectedQuery
- */
- #[DataProvider('dataSetValue')]
- public function testValues($column, $value, $expectedQueryPart, $expectedQuery): void {
- $this->queryBuilder->insert('data');
- $this->queryBuilder->values([
- $column => $value,
- ]);
-
- $this->assertEquals(
- $expectedQueryPart,
- $this->queryBuilder->getQueryPart('values')
- );
-
- $this->assertSame(
- 'INSERT INTO `*PREFIX*data` ' . $expectedQuery,
- $this->queryBuilder->getSQL()
- );
- }
-
- public static function dataHaving(): array {
- return [
- [['condition1'], new CompositeExpression('AND', ['condition1']), 'HAVING condition1'],
- [['condition1', 'condition2'], new CompositeExpression('AND', ['condition1', 'condition2']), 'HAVING (condition1) AND (condition2)'],
- [
- [new CompositeExpression('OR', ['condition1', 'condition2'])],
- new CompositeExpression('OR', ['condition1', 'condition2']),
- 'HAVING (condition1) OR (condition2)'
- ],
- [
- [new CompositeExpression('AND', ['condition1', 'condition2'])],
- new CompositeExpression('AND', ['condition1', 'condition2']),
- 'HAVING (condition1) AND (condition2)'
- ],
- ];
- }
-
- /**
- *
- * @param array $havingArguments
- * @param array $expectedQueryPart
- * @param string $expectedQuery
- */
- #[DataProvider('dataHaving')]
- public function testHaving($havingArguments, $expectedQueryPart, $expectedQuery): void {
- call_user_func_array(
- [$this->queryBuilder, 'having'],
- $havingArguments
- );
-
- $this->assertEquals(
- $expectedQueryPart,
- $this->queryBuilder->getQueryPart('having')
- );
-
- $this->assertSame(
- 'SELECT ' . $expectedQuery,
- $this->queryBuilder->getSQL()
- );
- }
-
- public static function dataAndHaving(): array {
- return [
- [['condition2'], new CompositeExpression('AND', ['condition1', 'condition2']), 'HAVING (condition1) AND (condition2)'],
- [['condition2', 'condition3'], new CompositeExpression('AND', ['condition1', 'condition2', 'condition3']), 'HAVING (condition1) AND (condition2) AND (condition3)'],
- [
- [new CompositeExpression('OR', ['condition2', 'condition3'])],
- new CompositeExpression('AND', ['condition1', new CompositeExpression('OR', ['condition2', 'condition3'])]),
- 'HAVING (condition1) AND ((condition2) OR (condition3))'
- ],
- [
- [new CompositeExpression('AND', ['condition2', 'condition3'])],
- new CompositeExpression('AND', ['condition1', new CompositeExpression('AND', ['condition2', 'condition3'])]),
- 'HAVING (condition1) AND ((condition2) AND (condition3))'
- ],
- ];
- }
-
- /**
- *
- * @param array $havingArguments
- * @param array $expectedQueryPart
- * @param string $expectedQuery
- */
- #[DataProvider('dataAndHaving')]
- public function testAndHaving($havingArguments, $expectedQueryPart, $expectedQuery): void {
- $this->queryBuilder->having('condition1');
- call_user_func_array(
- [$this->queryBuilder, 'andHaving'],
- $havingArguments
- );
-
- $this->assertEquals(
- $expectedQueryPart,
- $this->queryBuilder->getQueryPart('having')
- );
-
- $this->assertSame(
- 'SELECT ' . $expectedQuery,
- $this->queryBuilder->getSQL()
- );
- }
-
- public static function dataOrHaving(): array {
- return [
- [['condition2'], new CompositeExpression('OR', ['condition1', 'condition2']), 'HAVING (condition1) OR (condition2)'],
- [['condition2', 'condition3'], new CompositeExpression('OR', ['condition1', 'condition2', 'condition3']), 'HAVING (condition1) OR (condition2) OR (condition3)'],
- [
- [new CompositeExpression('OR', ['condition2', 'condition3'])],
- new CompositeExpression('OR', ['condition1', new CompositeExpression('OR', ['condition2', 'condition3'])]),
- 'HAVING (condition1) OR ((condition2) OR (condition3))'
- ],
- [
- [new CompositeExpression('AND', ['condition2', 'condition3'])],
- new CompositeExpression('OR', ['condition1', new CompositeExpression('AND', ['condition2', 'condition3'])]),
- 'HAVING (condition1) OR ((condition2) AND (condition3))'
- ],
- ];
- }
-
- /**
- *
- * @param array $havingArguments
- * @param array $expectedQueryPart
- * @param string $expectedQuery
- */
- #[DataProvider('dataOrHaving')]
- public function testOrHaving($havingArguments, $expectedQueryPart, $expectedQuery): void {
- $this->queryBuilder->having('condition1');
- call_user_func_array(
- [$this->queryBuilder, 'orHaving'],
- $havingArguments
- );
-
- $this->assertEquals(
- $expectedQueryPart,
- $this->queryBuilder->getQueryPart('having')
- );
-
- $this->assertSame(
- 'SELECT ' . $expectedQuery,
- $this->queryBuilder->getSQL()
- );
- }
-
- public static function dataOrderBy(): array {
- return [
- ['column', null, ['`column` ASC'], 'ORDER BY `column` ASC'],
- ['column', 'ASC', ['`column` ASC'], 'ORDER BY `column` ASC'],
- ['column', 'DESC', ['`column` DESC'], 'ORDER BY `column` DESC'],
- ];
- }
-
- /**
- *
- * @param string $sort
- * @param string $order
- * @param array $expectedQueryPart
- * @param string $expectedQuery
- */
- #[DataProvider('dataOrderBy')]
- public function testOrderBy($sort, $order, $expectedQueryPart, $expectedQuery): void {
- $this->queryBuilder->orderBy($sort, $order);
-
- $this->assertEquals(
- $expectedQueryPart,
- $this->queryBuilder->getQueryPart('orderBy')
- );
-
- $this->assertSame(
- 'SELECT ' . $expectedQuery,
- $this->queryBuilder->getSQL()
- );
- }
-
- public static function dataAddOrderBy(): array {
- return [
- ['column2', null, null, ['`column1` ASC', '`column2` ASC'], 'ORDER BY `column1` ASC, `column2` ASC'],
- ['column2', null, 'ASC', ['`column1` ASC', '`column2` ASC'], 'ORDER BY `column1` ASC, `column2` ASC'],
- ['column2', null, 'DESC', ['`column1` DESC', '`column2` ASC'], 'ORDER BY `column1` DESC, `column2` ASC'],
- ['column2', 'ASC', null, ['`column1` ASC', '`column2` ASC'], 'ORDER BY `column1` ASC, `column2` ASC'],
- ['column2', 'ASC', 'ASC', ['`column1` ASC', '`column2` ASC'], 'ORDER BY `column1` ASC, `column2` ASC'],
- ['column2', 'ASC', 'DESC', ['`column1` DESC', '`column2` ASC'], 'ORDER BY `column1` DESC, `column2` ASC'],
- ['column2', 'DESC', null, ['`column1` ASC', '`column2` DESC'], 'ORDER BY `column1` ASC, `column2` DESC'],
- ['column2', 'DESC', 'ASC', ['`column1` ASC', '`column2` DESC'], 'ORDER BY `column1` ASC, `column2` DESC'],
- ['column2', 'DESC', 'DESC', ['`column1` DESC', '`column2` DESC'], 'ORDER BY `column1` DESC, `column2` DESC'],
- ];
- }
-
- /**
- *
- * @param string $sort2
- * @param string $order2
- * @param string $order1
- * @param array $expectedQueryPart
- * @param string $expectedQuery
- */
- #[DataProvider('dataAddOrderBy')]
- public function testAddOrderBy($sort2, $order2, $order1, $expectedQueryPart, $expectedQuery): void {
- $this->queryBuilder->orderBy('column1', $order1);
- $this->queryBuilder->addOrderBy($sort2, $order2);
-
- $this->assertEquals(
- $expectedQueryPart,
- $this->queryBuilder->getQueryPart('orderBy')
- );
-
- $this->assertSame(
- 'SELECT ' . $expectedQuery,
- $this->queryBuilder->getSQL()
- );
- }
-
- public function testGetLastInsertId(): void {
- $qB = $this->connection->getQueryBuilder();
-
- try {
- $qB->getLastInsertId();
- $this->fail('getLastInsertId() should throw an exception, when being called before insert()');
- } catch (\BadMethodCallException $e) {
- $this->addToAssertionCount(1);
- }
-
- $qB->insert('properties')
- ->values([
- 'userid' => $qB->expr()->literal('testFirstResult'),
- 'propertypath' => $qB->expr()->literal('testing'),
- 'propertyname' => $qB->expr()->literal('testing'),
- 'propertyvalue' => $qB->expr()->literal('testing'),
- ])
- ->executeStatement();
-
- $actual = $qB->getLastInsertId();
-
- $this->assertNotNull($actual);
- $this->assertIsInt($actual);
- $this->assertEquals($this->connection->lastInsertId('*PREFIX*properties'), $actual);
-
- $qB->delete('properties')
- ->where($qB->expr()->eq('userid', $qB->expr()->literal('testFirstResult')))
- ->executeStatement();
-
- try {
- $qB->getLastInsertId();
- $this->fail('getLastInsertId() should throw an exception, when being called after delete()');
- } catch (\BadMethodCallException $e) {
- $this->addToAssertionCount(1);
- }
- }
-
- public static function dataGetTableName(): array {
- return [
- ['*PREFIX*table', null, '`*PREFIX*table`'],
- ['*PREFIX*table', true, '`*PREFIX*table`'],
- ['*PREFIX*table', false, '`*PREFIX*table`'],
-
- ['table', null, '`*PREFIX*table`'],
- ['table', true, '`*PREFIX*table`'],
- ['table', false, '`table`'],
-
- ['function', null, '(SELECT * FROM `*PREFIX*table`)'],
- ['function', true, '(SELECT * FROM `*PREFIX*table`)'],
- ['function', false, '(SELECT * FROM `*PREFIX*table`)'],
- ];
- }
-
- #[DataProvider('dataGetTableName')]
- public function testGetTableName(string $tableName, ?bool $automatic, string $expected): void {
- if ($tableName === 'function') {
- $tableName = $this->queryBuilder->createFunction('(' . $this->queryBuilder->select('*')->from('table')->getSQL() . ')');
- }
-
- if ($automatic !== null) {
- $this->queryBuilder->automaticTablePrefix($automatic);
- }
-
- $this->assertSame(
- $expected,
- $this->queryBuilder->getTableName($tableName)
- );
- }
-
- public static function dataGetColumnName(): array {
- return [
- ['column', '', '`column`'],
- ['column', 'a', '`a`.`column`'],
- ];
- }
-
- #[DataProvider('dataGetColumnName')]
- public function testGetColumnName(string $column, string $prefix, string $expected): void {
- $this->assertSame(
- $expected,
- $this->queryBuilder->getColumnName($column, $prefix)
- );
- }
-
- private function getConnection(): MockObject&ConnectionAdapter {
- $connection = $this->createMock(ConnectionAdapter::class);
- $connection->method('executeStatement')
- ->willReturn(3);
- $connection->method('executeQuery')
- ->willReturn($this->createMock(IResult::class));
- return $connection;
- }
-
- public function testExecuteWithoutLogger(): void {
- $queryBuilder = $this->createMock(\Doctrine\DBAL\Query\QueryBuilder::class);
- $queryBuilder
- ->method('getSQL')
- ->willReturn('');
- $queryBuilder
- ->method('getParameters')
- ->willReturn([]);
- $queryBuilder
- ->method('getParameterTypes')
- ->willReturn([]);
- $this->logger
- ->expects($this->never())
- ->method('debug');
- $this->config
- ->expects($this->once())
- ->method('getValue')
- ->with('log_query', false)
- ->willReturn(false);
-
- $this->invokePrivate($this->queryBuilder, 'queryBuilder', [$queryBuilder]);
- $this->invokePrivate($this->queryBuilder, 'connection', [$this->getConnection()]);
- $this->assertEquals(3, $this->queryBuilder->executeStatement());
- }
-
- public function testExecuteWithLoggerAndNamedArray(): void {
- $queryBuilder = $this->createMock(\Doctrine\DBAL\Query\QueryBuilder::class);
- $queryBuilder
- ->expects($this->any())
- ->method('getParameters')
- ->willReturn([
- 'foo' => 'bar',
- 'key' => 'value',
- ]);
- $queryBuilder
- ->method('getParameterTypes')
- ->willReturn([
- 'foo' => IQueryBuilder::PARAM_STR,
- 'key' => IQueryBuilder::PARAM_STR,
- ]);
- $queryBuilder
- ->expects($this->any())
- ->method('getSQL')
- ->willReturn('UPDATE FOO SET bar = 1 WHERE BAR = ?');
- $this->logger
- ->expects($this->once())
- ->method('debug')
- ->with(
- 'DB QueryBuilder: \'{query}\' with parameters: {params}',
- [
- 'query' => 'UPDATE FOO SET bar = 1 WHERE BAR = ?',
- 'params' => 'foo => \'bar\', key => \'value\'',
- 'app' => 'core',
- ]
- );
- $this->config
- ->expects($this->once())
- ->method('getValue')
- ->with('log_query', false)
- ->willReturn(true);
-
- $this->invokePrivate($this->queryBuilder, 'queryBuilder', [$queryBuilder]);
- $this->invokePrivate($this->queryBuilder, 'connection', [$this->getConnection()]);
- $this->assertEquals(3, $this->queryBuilder->executeStatement());
- }
-
- public function testExecuteWithLoggerAndUnnamedArray(): void {
- $queryBuilder = $this->createMock(\Doctrine\DBAL\Query\QueryBuilder::class);
- $queryBuilder
- ->expects($this->any())
- ->method('getParameters')
- ->willReturn(['Bar']);
- $queryBuilder
- ->method('getParameterTypes')
- ->willReturn([IQueryBuilder::PARAM_STR]);
- $queryBuilder
- ->expects($this->any())
- ->method('getSQL')
- ->willReturn('UPDATE FOO SET bar = false WHERE BAR = ?');
- $this->logger
- ->expects($this->once())
- ->method('debug')
- ->with(
- 'DB QueryBuilder: \'{query}\' with parameters: {params}',
- [
- 'query' => 'UPDATE FOO SET bar = false WHERE BAR = ?',
- 'params' => '0 => \'Bar\'',
- 'app' => 'core',
- ]
- );
- $this->config
- ->expects($this->once())
- ->method('getValue')
- ->with('log_query', false)
- ->willReturn(true);
-
- $this->invokePrivate($this->queryBuilder, 'queryBuilder', [$queryBuilder]);
- $this->invokePrivate($this->queryBuilder, 'connection', [$this->getConnection()]);
- $this->assertEquals(3, $this->queryBuilder->executeStatement());
- }
-
- public function testExecuteWithLoggerAndNoParams(): void {
- $queryBuilder = $this->createMock(\Doctrine\DBAL\Query\QueryBuilder::class);
- $queryBuilder
- ->expects($this->any())
- ->method('getParameters')
- ->willReturn([]);
- $queryBuilder
- ->method('getParameterTypes')
- ->willReturn([]);
- $queryBuilder
- ->expects($this->any())
- ->method('getSQL')
- ->willReturn('UPDATE FOO SET bar = false WHERE BAR = ?');
- $this->logger
- ->expects($this->once())
- ->method('debug')
- ->with(
- 'DB QueryBuilder: \'{query}\'',
- [
- 'query' => 'UPDATE FOO SET bar = false WHERE BAR = ?',
- 'app' => 'core',
- ]
- );
- $this->config
- ->expects($this->once())
- ->method('getValue')
- ->with('log_query', false)
- ->willReturn(true);
-
- $this->invokePrivate($this->queryBuilder, 'queryBuilder', [$queryBuilder]);
- $this->invokePrivate($this->queryBuilder, 'connection', [$this->getConnection()]);
- $this->assertEquals(3, $this->queryBuilder->executeStatement());
- }
-
- public function testExecuteWithParameterTooLarge(): void {
- $queryBuilder = $this->createMock(\Doctrine\DBAL\Query\QueryBuilder::class);
- $p = array_fill(0, 1001, 'foo');
- $queryBuilder
- ->expects($this->any())
- ->method('getParameters')
- ->willReturn([$p]);
- $queryBuilder
- ->method('getParameterTypes')
- ->willReturn([IQueryBuilder::PARAM_STR_ARRAY]);
- $queryBuilder
- ->expects($this->any())
- ->method('getType')
- ->willReturn(\Doctrine\DBAL\Query\QueryBuilder::SELECT);
- $queryBuilder
- ->expects($this->any())
- ->method('getSQL')
- ->willReturn('SELECT * FROM FOO WHERE BAR IN (?)');
- $this->logger
- ->expects($this->once())
- ->method('error')
- ->willReturnCallback(function ($message, $parameters): void {
- $this->assertInstanceOf(QueryException::class, $parameters['exception']);
- $this->assertSame(
- 'More than 1000 expressions in a list are not allowed on Oracle.',
- $message
- );
- });
- $this->config
- ->expects($this->once())
- ->method('getValue')
- ->with('log_query', false)
- ->willReturn(false);
-
- $this->invokePrivate($this->queryBuilder, 'queryBuilder', [$queryBuilder]);
- $this->invokePrivate($this->queryBuilder, 'connection', [$this->getConnection()]);
- $this->queryBuilder->executeQuery();
- }
-
- public function testExecuteWithParametersTooMany(): void {
- $queryBuilder = $this->createMock(\Doctrine\DBAL\Query\QueryBuilder::class);
- $p = array_fill(0, 999, 'foo');
- $queryBuilder
- ->expects($this->any())
- ->method('getParameters')
- ->willReturn(array_fill(0, 66, $p));
- $queryBuilder
- ->method('getParameterTypes')
- ->willReturn([IQueryBuilder::PARAM_STR_ARRAY]);
- $queryBuilder
- ->expects($this->any())
- ->method('getType')
- ->willReturn(\Doctrine\DBAL\Query\QueryBuilder::SELECT);
- $queryBuilder
- ->expects($this->any())
- ->method('getSQL')
- ->willReturn('SELECT * FROM FOO WHERE BAR IN (?) OR BAR IN (?)');
- $this->logger
- ->expects($this->once())
- ->method('error')
- ->willReturnCallback(function ($message, $parameters): void {
- $this->assertInstanceOf(QueryException::class, $parameters['exception']);
- $this->assertSame(
- 'The number of parameters must not exceed 65535. Restriction by PostgreSQL.',
- $message
- );
- });
- $this->config
- ->expects($this->once())
- ->method('getValue')
- ->with('log_query', false)
- ->willReturn(false);
-
- $this->invokePrivate($this->queryBuilder, 'queryBuilder', [$queryBuilder]);
- $this->invokePrivate($this->queryBuilder, 'connection', [$this->getConnection()]);
- $this->queryBuilder->executeQuery();
- }
-}
--- a/tests/lib/DB/QueryBuilder/QuoteHelperTest.php
+++ /dev/null
@@ -1,133 +0,0 @@
-helper = new QuoteHelper();
- }
-
- public static function dataQuoteColumnName(): array {
- return [
- ['column', '`column`'],
- [new Literal('literal'), 'literal'],
- [new Literal(1), '1'],
- [new Parameter(':param'), ':param'],
-
- // (string) 'null' is Doctrines way to set columns to null
- // See https://github.com/owncloud/core/issues/19314
- ['null', 'null'],
- ];
- }
-
- /**
- * @param mixed $input
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataQuoteColumnName')]
- public function testQuoteColumnName($input, $expected): void {
- $this->assertSame(
- $expected,
- $this->helper->quoteColumnName($input)
- );
- }
-
- public static function dataQuoteColumnNames(): array {
- return [
- // Single case
- ['d.column', '`d`.`column`'],
- ['column', '`column`'],
- [new Literal('literal'), 'literal'],
- [new Literal(1), '1'],
- [new Parameter(':param'), ':param'],
-
- // Array case
- [['column'], ['`column`']],
- [[new Literal('literal')], ['literal']],
- [[new Literal(1)], ['1']],
- [[new Parameter(':param')], [':param']],
-
- // Array mixed cases
- [['column1', 'column2'], ['`column1`', '`column2`']],
- [['column', new Literal('literal')], ['`column`', 'literal']],
- [['column', new Literal(1)], ['`column`', '1']],
- [['column', new Parameter(':param')], ['`column`', ':param']],
- ];
- }
-
- /**
- * @param mixed $input
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataQuoteColumnNames')]
- public function testQuoteColumnNames($input, $expected): void {
- $this->assertSame(
- $expected,
- $this->helper->quoteColumnNames($input)
- );
- }
-
- /**
- * @param array|string|ILiteral|IParameter $strings string, Literal or Parameter
- * @return array|string
- */
- public function quoteColumnNames($strings) {
- if (!is_array($strings)) {
- return $this->quoteColumnName($strings);
- }
-
- $return = [];
- foreach ($strings as $string) {
- $return[] = $this->quoteColumnName($string);
- }
-
- return $return;
- }
-
- /**
- * @param string|ILiteral|IParameter $string string, Literal or Parameter
- * @return string
- */
- public function quoteColumnName($string) {
- if ($string instanceof IParameter) {
- return $string->getName();
- }
-
- if ($string instanceof ILiteral) {
- return $string->getLiteral();
- }
-
- if ($string === null) {
- return $string;
- }
-
- if (!is_string($string)) {
- throw new \InvalidArgumentException('Only strings, Literals and Parameters are allowed');
- }
-
- if (substr_count($string, '.')) {
- [$alias, $columnName] = explode('.', $string);
- return '`' . $alias . '`.`' . $columnName . '`';
- }
-
- return '`' . $string . '`';
- }
-}
--- a/tests/lib/DB/QueryBuilder/Sharded/SharedQueryBuilderTest.php
+++ /dev/null
@@ -1,128 +0,0 @@
-
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-namespace Test\DB\QueryBuilder\Sharded;
-
-use OC\DB\QueryBuilder\Sharded\AutoIncrementHandler;
-use OC\DB\QueryBuilder\Sharded\InvalidShardedQueryException;
-use OC\DB\QueryBuilder\Sharded\RoundRobinShardMapper;
-use OC\DB\QueryBuilder\Sharded\ShardConnectionManager;
-use OC\DB\QueryBuilder\Sharded\ShardDefinition;
-use OC\DB\QueryBuilder\Sharded\ShardedQueryBuilder;
-use OCP\DB\QueryBuilder\IQueryBuilder;
-use OCP\IDBConnection;
-use OCP\Server;
-use Test\TestCase;
-
-#[\PHPUnit\Framework\Attributes\Group('DB')]
-class SharedQueryBuilderTest extends TestCase {
- private IDBConnection $connection;
- private AutoIncrementHandler $autoIncrementHandler;
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- if (PHP_INT_SIZE < 8) {
- $this->markTestSkipped('Test requires 64bit');
- }
- $this->connection = Server::get(IDBConnection::class);
- $this->autoIncrementHandler = Server::get(AutoIncrementHandler::class);
- }
-
- private function getQueryBuilder(string $table, string $shardColumn, string $primaryColumn, array $companionTables = []): ShardedQueryBuilder {
- return new ShardedQueryBuilder(
- $this->connection->getQueryBuilder(),
- [
- new ShardDefinition($table, $primaryColumn, [], $shardColumn, new RoundRobinShardMapper(), $companionTables, [], 0, 0),
- ],
- $this->createMock(ShardConnectionManager::class),
- $this->autoIncrementHandler,
- );
- }
-
- public function testGetShardKeySingleParam(): void {
- $query = $this->getQueryBuilder('filecache', 'storage', 'fileid');
- $query->select('fileid', 'path')
- ->from('filecache')
- ->where($query->expr()->eq('storage', $query->createNamedParameter(10, IQueryBuilder::PARAM_INT)));
-
- $this->assertEquals([], $query->getPrimaryKeys());
- $this->assertEquals([10], $query->getShardKeys());
- }
-
- public function testGetPrimaryKeyParam(): void {
- $query = $this->getQueryBuilder('filecache', 'storage', 'fileid');
- $query->select('fileid', 'path')
- ->from('filecache')
- ->where($query->expr()->in('fileid', $query->createNamedParameter([10, 11], IQueryBuilder::PARAM_INT)));
-
- $this->assertEquals([10, 11], $query->getPrimaryKeys());
- $this->assertEquals([], $query->getShardKeys());
- }
-
- #[\PHPUnit\Framework\Attributes\DoesNotPerformAssertions]
- public function testValidateWithShardKey(): void {
- $query = $this->getQueryBuilder('filecache', 'storage', 'fileid');
- $query->select('fileid', 'path')
- ->from('filecache')
- ->where($query->expr()->eq('storage', $query->createNamedParameter(10)));
-
- $query->validate();
- }
-
- #[\PHPUnit\Framework\Attributes\DoesNotPerformAssertions]
- public function testValidateWithPrimaryKey(): void {
- $query = $this->getQueryBuilder('filecache', 'storage', 'fileid');
- $query->select('fileid', 'path')
- ->from('filecache')
- ->where($query->expr()->in('fileid', $query->createNamedParameter([10, 11], IQueryBuilder::PARAM_INT)));
-
- $query->validate();
- }
-
- public function testValidateWithNoKey(): void {
- $query = $this->getQueryBuilder('filecache', 'storage', 'fileid');
- $query->select('fileid', 'path')
- ->from('filecache')
- ->where($query->expr()->lt('size', $query->createNamedParameter(0)));
-
- $this->expectException(InvalidShardedQueryException::class);
- $query->validate();
- $this->fail('exception expected');
- }
-
- #[\PHPUnit\Framework\Attributes\DoesNotPerformAssertions]
- public function testValidateNonSharedTable(): void {
- $query = $this->getQueryBuilder('filecache', 'storage', 'fileid');
- $query->select('configvalue')
- ->from('appconfig')
- ->where($query->expr()->eq('configkey', $query->createNamedParameter('test')));
-
- $query->validate();
- }
-
- public function testGetShardKeyMultipleSingleParam(): void {
- $query = $this->getQueryBuilder('filecache', 'storage', 'fileid');
- $query->select('fileid', 'path')
- ->from('filecache')
- ->where($query->expr()->andX(
- $query->expr()->gt('mtime', $query->createNamedParameter(0), IQueryBuilder::PARAM_INT),
- $query->expr()->orX(
- $query->expr()->eq('storage', $query->createNamedParameter(10, IQueryBuilder::PARAM_INT)),
- $query->expr()->andX(
- $query->expr()->eq('storage', $query->createNamedParameter(11, IQueryBuilder::PARAM_INT)),
- $query->expr()->like('path', $query->createNamedParameter('foo/%'))
- )
- )
- ));
-
- $this->assertEquals([], $query->getPrimaryKeys());
- $this->assertEquals([10, 11], $query->getShardKeys());
- }
-}
--- a/tests/lib/DB/SchemaCheckerTest.php
+++ /dev/null
@@ -1,164 +0,0 @@
-connection = $this->createMock(Connection::class);
- $this->appConfig = $this->createMock(IAppConfig::class);
- $this->appManager = $this->createMock(IAppManager::class);
- $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $this->schemaChecker = new SchemaChecker(
- $this->connection,
- $this->appConfig,
- $this->appManager,
- $this->eventDispatcher,
- $this->logger,
- );
- }
-
- public static function dataFormatFinding(): array {
- return [
- 'missing_table' => [['table' => 'oc_foo', 'type' => 'missing_table'], "missing table 'oc_foo'"],
- 'unexpected_table' => [['table' => 'oc_foo', 'type' => 'unexpected_table'], "unexpected table 'oc_foo'"],
- 'missing_column' => [['table' => 'oc_foo', 'type' => 'missing_column', 'name' => 'bar'], "oc_foo: missing column 'bar'"],
- 'unexpected_column' => [['table' => 'oc_foo', 'type' => 'unexpected_column', 'name' => 'bar'], "oc_foo: unexpected column 'bar'"],
- 'modified_column' => [['table' => 'oc_foo', 'type' => 'modified_column', 'name' => 'bar', 'changes' => ['type', 'default']], "oc_foo: column 'bar' differs in: type, default"],
- 'missing_index' => [['table' => 'oc_foo', 'type' => 'missing_index', 'name' => 'bar_idx'], "oc_foo: missing index 'bar_idx'"],
- 'unexpected_index' => [['table' => 'oc_foo', 'type' => 'unexpected_index', 'name' => 'bar_idx'], "oc_foo: unexpected index 'bar_idx'"],
- 'unknown' => [['table' => 'oc_foo', 'type' => 'something_else'], "oc_foo: unknown finding 'something_else'"],
- ];
- }
-
- #[DataProvider('dataFormatFinding')]
- public function testFormatFinding(array $finding, string $expected): void {
- $this->assertSame($expected, $this->schemaChecker->formatFinding($finding));
- }
-
- public function testPartitionFindingsSplitsBlockingAndDisabled(): void {
- $blockingFinding = ['table' => 'oc_foo', 'type' => 'missing_column', 'name' => 'a', 'app' => 'core', 'enabled' => true];
- $disabledAppFinding = ['table' => 'oc_bar', 'type' => 'missing_column', 'name' => 'b', 'app' => 'files', 'enabled' => false];
- $unattributedFinding = ['table' => 'oc_baz', 'type' => 'unexpected_table', 'app' => null, 'enabled' => false];
-
- $result = $this->schemaChecker->partitionFindings([
- $blockingFinding,
- $disabledAppFinding,
- $unattributedFinding,
- ]);
-
- $this->assertSame([$blockingFinding], $result['blocking']);
- $this->assertSame(['files' => [$disabledAppFinding]], array_intersect_key($result['byDisabledApp'], ['files' => true]));
- $this->assertSame(['(unknown app)' => [$unattributedFinding]], array_intersect_key($result['byDisabledApp'], ['(unknown app)' => true]));
- }
-
- public function testNormalizeLongStringColumnsRewritesOnlyColumnsOverTheLimit(): void {
- $schema = new Schema();
- $table = $schema->createTable('oc_test');
- $table->addColumn('short_col', Types::STRING, ['length' => 255]);
- $table->addColumn('long_col', Types::STRING, ['length' => 4001]);
-
- self::invokePrivate($this->schemaChecker, 'normalizeLongStringColumns', [$schema]);
-
- $this->assertSame(Types::STRING, Type::getTypeRegistry()->lookupName($table->getColumn('short_col')->getType()));
- $this->assertSame(255, $table->getColumn('short_col')->getLength());
-
- $this->assertInstanceOf(TextType::class, $table->getColumn('long_col')->getType());
- $this->assertNull($table->getColumn('long_col')->getLength());
- }
-
- public static function dataIsIgnorableTextDefaultDiff(): array {
- return [
- 'mysql text' => [IDBConnection::PLATFORM_MYSQL, Types::TEXT, true],
- 'mariadb blob' => [IDBConnection::PLATFORM_MARIADB, Types::BLOB, true],
- 'mysql string is not ignorable' => [IDBConnection::PLATFORM_MYSQL, Types::STRING, false],
- 'sqlite text is not ignorable' => [IDBConnection::PLATFORM_SQLITE, Types::TEXT, false],
- ];
- }
-
- #[DataProvider('dataIsIgnorableTextDefaultDiff')]
- public function testIsIgnorableTextDefaultDiff(string $provider, string $typeName, bool $expected): void {
- $this->connection->method('getDatabaseProvider')->willReturn($provider);
-
- $column = new Column('some_col', Type::getType($typeName));
- $columnDiff = new ColumnDiff('some_col', $column, ['default'], $column);
-
- $result = self::invokePrivate($this->schemaChecker, 'isIgnorableTextDefaultDiff', [$columnDiff]);
-
- $this->assertSame($expected, $result);
- }
-
- public function testGetOptionalIndexNamesCollectsMissingAndReplacedIndices(): void {
- $this->connection->method('getPrefix')->willReturn('oc_');
- $this->eventDispatcher->method('dispatchTyped')
- ->willReturnCallback(function (AddMissingIndicesEvent $event): void {
- $event->addMissingIndex('foo', 'foo_idx', ['col']);
- $event->replaceIndex('bar', ['old_idx'], 'new_idx', ['col'], false);
- });
-
- $names = self::invokePrivate($this->schemaChecker, 'getOptionalIndexNames');
-
- $this->assertSame([
- 'oc_foo' => ['foo_idx' => true],
- 'oc_bar' => ['new_idx' => true, 'old_idx' => true],
- ], $names);
- }
-
- public static function dataIsOptionalIndexFinding(): array {
- $optionalIndexNames = ['oc_foo' => ['foo_idx' => true]];
-
- return [
- 'matching missing_index' => [['table' => 'oc_foo', 'type' => 'missing_index', 'name' => 'foo_idx'], $optionalIndexNames, true],
- 'matching unexpected_index' => [['table' => 'oc_foo', 'type' => 'unexpected_index', 'name' => 'foo_idx'], $optionalIndexNames, true],
- 'different index name' => [['table' => 'oc_foo', 'type' => 'missing_index', 'name' => 'other_idx'], $optionalIndexNames, false],
- 'different table' => [['table' => 'oc_bar', 'type' => 'missing_index', 'name' => 'foo_idx'], $optionalIndexNames, false],
- 'non-index finding type' => [['table' => 'oc_foo', 'type' => 'missing_column', 'name' => 'foo_idx'], $optionalIndexNames, false],
- ];
- }
-
- /**
- * Optional-index findings must be filtered out entirely (never reach
- * partitionFindings() or --output=json), not just hidden from plain-text
- * output - Settings already has a dedicated admin-overview surface for them.
- */
- #[DataProvider('dataIsOptionalIndexFinding')]
- public function testIsOptionalIndexFinding(array $finding, array $optionalIndexNames, bool $expected): void {
- $result = self::invokePrivate($this->schemaChecker, 'isOptionalIndexFinding', [$finding, $optionalIndexNames]);
-
- $this->assertSame($expected, $result);
- }
-}
--- a/tests/lib/DateTimeFormatterTest.php
+++ /dev/null
@@ -1,179 +0,0 @@
-formatter = new DateTimeFormatter(new \DateTimeZone('UTC'), Util::getL10N('lib', 'en'));
- }
-
- protected static function getTimestampAgo($time, $seconds = 0, $minutes = 0, $hours = 0, $days = 0, $years = 0) {
- return $time - $seconds - $minutes * 60 - $hours * 3600 - $days * 24 * 3600 - $years * 365 * 24 * 3600;
- }
-
- public static function formatTimeSpanData(): array {
- $time = 1416916800; // Use a fixed timestamp so we don't switch days/years with the getTimestampAgo
- $deL10N = Util::getL10N('lib', 'de');
- return [
- ['seconds ago', $time, $time],
- ['in a few seconds', $time + 5 , $time],
- ['1 minute ago', self::getTimestampAgo($time, 30, 1), $time],
- ['15 minutes ago', self::getTimestampAgo($time, 30, 15), $time],
- ['in 15 minutes', $time, self::getTimestampAgo($time, 30, 15)],
- ['1 hour ago', self::getTimestampAgo($time, 30, 15, 1), $time],
- ['3 hours ago', self::getTimestampAgo($time, 30, 15, 3), $time],
- ['in 3 hours', $time, self::getTimestampAgo($time, 30, 15, 3)],
- ['4 days ago', self::getTimestampAgo($time, 30, 15, 3, 4), $time],
-
- ['seconds ago', new \DateTime('Wed, 02 Oct 2013 23:59:58 +0000'), new \DateTime('Wed, 02 Oct 2013 23:59:59 +0000')],
- ['seconds ago', new \DateTime('Wed, 02 Oct 2013 23:59:00 +0000'), new \DateTime('Wed, 02 Oct 2013 23:59:59 +0000')],
- ['1 minute ago', new \DateTime('Wed, 02 Oct 2013 23:58:30 +0000'), new \DateTime('Wed, 02 Oct 2013 23:59:59 +0000')],
- ['3 minutes ago', new \DateTime('Wed, 02 Oct 2013 23:56:30 +0000'), new \DateTime('Wed, 02 Oct 2013 23:59:59 +0000')],
- ['59 minutes ago', new \DateTime('Wed, 02 Oct 2013 23:00:00 +0000'), new \DateTime('Wed, 02 Oct 2013 23:59:59 +0000')],
- ['1 hour ago', new \DateTime('Wed, 02 Oct 2013 22:59:59 +0000'), new \DateTime('Wed, 02 Oct 2013 23:59:59 +0000')],
- ['3 hours ago', new \DateTime('Wed, 02 Oct 2013 20:39:59 +0000'), new \DateTime('Wed, 02 Oct 2013 23:59:59 +0000')],
- ['yesterday', new \DateTime('Tue, 01 Oct 2013 20:39:59 +0000'), new \DateTime('Wed, 02 Oct 2013 23:59:59 +0000')],
- ['2 days ago', new \DateTime('Mon, 30 Sep 2013 20:39:59 +0000'), new \DateTime('Wed, 02 Oct 2013 23:59:59 +0000')],
-
- [$deL10N->t('seconds ago'), new \DateTime('Wed, 02 Oct 2013 23:59:58 +0000'), new \DateTime('Wed, 02 Oct 2013 23:59:59 +0000'), $deL10N],
- [$deL10N->n('%n minute ago', '%n minutes ago', 1), new \DateTime('Wed, 02 Oct 2013 23:58:30 +0000'), new \DateTime('Wed, 02 Oct 2013 23:59:59 +0000'), $deL10N],
- [$deL10N->n('%n minute ago', '%n minutes ago', 3), new \DateTime('Wed, 02 Oct 2013 23:56:30 +0000'), new \DateTime('Wed, 02 Oct 2013 23:59:59 +0000'), $deL10N],
- [$deL10N->n('%n hour ago', '%n hours ago', 1), new \DateTime('Wed, 02 Oct 2013 22:59:59 +0000'), new \DateTime('Wed, 02 Oct 2013 23:59:59 +0000'), $deL10N],
- [$deL10N->n('%n hour ago', '%n hours ago', 3), new \DateTime('Wed, 02 Oct 2013 20:39:59 +0000'), new \DateTime('Wed, 02 Oct 2013 23:59:59 +0000'), $deL10N],
- [$deL10N->n('%n day ago', '%n days ago', 2), new \DateTime('Mon, 30 Sep 2013 20:39:59 +0000'), new \DateTime('Wed, 02 Oct 2013 23:59:59 +0000'), $deL10N],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('formatTimeSpanData')]
- public function testFormatTimeSpan($expected, $timestamp, $compare, $locale = null): void {
- $this->assertEquals((string)$expected, (string)$this->formatter->formatTimeSpan($timestamp, $compare, $locale));
- }
-
- public static function formatDateSpanData(): array {
- $time = 1416916800; // Use a fixed timestamp so we don't switch days/years with the getTimestampAgo
- $deL10N = Util::getL10N('lib', 'de');
- return [
- // Normal testing
- ['today', self::getTimestampAgo($time, 30, 15), $time],
- ['yesterday', self::getTimestampAgo($time, 0, 0, 0, 1), $time],
- ['tomorrow', $time, self::getTimestampAgo($time, 0, 0, 0, 1)],
- ['4 days ago', self::getTimestampAgo($time, 0, 0, 0, 4), $time],
- ['in 4 days', $time, self::getTimestampAgo($time, 0, 0, 0, 4)],
- ['5 months ago', self::getTimestampAgo($time, 0, 0, 0, 155), $time],
- ['next month', $time, self::getTimestampAgo($time, 0, 0, 0, 32)],
- ['in 5 months', $time, self::getTimestampAgo($time, 0, 0, 0, 155)],
- ['2 years ago', self::getTimestampAgo($time, 0, 0, 0, 0, 2), $time],
- ['next year', $time, self::getTimestampAgo($time, 0, 0, 0, 0, 1)],
- ['in 2 years', $time, self::getTimestampAgo($time, 0, 0, 0, 0, 2)],
-
- // Test with compare timestamp
- ['today', self::getTimestampAgo($time, 0, 0, 0, 0, 1), self::getTimestampAgo($time, 0, 0, 0, 0, 1)],
- ['yesterday', self::getTimestampAgo($time, 30, 15, 3, 1, 1), self::getTimestampAgo($time, 0, 0, 0, 0, 1)],
- ['4 days ago', self::getTimestampAgo($time, 30, 15, 3, 4, 1), self::getTimestampAgo($time, 0, 0, 0, 0, 1)],
- ['5 months ago', self::getTimestampAgo($time, 30, 15, 3, 155, 1), self::getTimestampAgo($time, 0, 0, 0, 0, 1)],
- ['2 years ago', self::getTimestampAgo($time, 30, 15, 3, 35, 3), self::getTimestampAgo($time, 0, 0, 0, 0, 1)],
-
- // Test translations
- [$deL10N->t('today'), new \DateTime('Wed, 02 Oct 2013 12:00:00 +0000'), new \DateTime('Wed, 02 Oct 2013 23:59:59 +0000'), $deL10N],
- [$deL10N->t('yesterday'), new \DateTime('Tue, 01 Oct 2013 00:00:00 +0000'), new \DateTime('Wed, 02 Oct 2013 00:00:00 +0000'), $deL10N],
- [$deL10N->n('%n day ago', '%n days ago', 2), new \DateTime('Mon, 30 Sep 2013 00:00:00 +0000'), new \DateTime('Wed, 02 Oct 2013 00:00:00 +0000'), $deL10N],
- [$deL10N->n('%n month ago', '%n months ago', 9), new \DateTime('Tue, 31 Dec 2013 00:00:00 +0000'), new \DateTime('Thu, 02 Oct 2014 00:00:00 +0000'), $deL10N],
- [$deL10N->n('%n year ago', '%n years ago', 2), new \DateTime('Sun, 01 Jan 2012 00:00:00 +0000'), new \DateTime('Thu, 02 Oct 2014 00:00:00 +0000'), $deL10N],
-
- // Test time
- ['today', new \DateTime('Wed, 02 Oct 2013 00:00:00 +0000'), new \DateTime('Wed, 02 Oct 2013 23:59:59 +0000')],
- ['today', new \DateTime('Wed, 02 Oct 2013 12:00:00 +0000'), new \DateTime('Wed, 02 Oct 2013 23:59:59 +0000')],
- ['today', new \DateTime('Wed, 02 Oct 2013 23:59:58 +0000'), new \DateTime('Wed, 02 Oct 2013 23:59:59 +0000')],
-
- // Test some special yesterdays
- ['yesterday', new \DateTime('Tue, 01 Oct 2013 00:00:00 +0000'), new \DateTime('Wed, 02 Oct 2013 00:00:00 +0000')],
- ['yesterday', new \DateTime('Tue, 01 Oct 2013 00:00:00 +0000'), new \DateTime('Wed, 02 Oct 2013 23:59:59 +0000')],
- ['yesterday', new \DateTime('Tue, 01 Oct 2013 23:59:58 +0000'), new \DateTime('Wed, 02 Oct 2013 00:00:00 +0000')],
- ['yesterday', new \DateTime('Tue, 01 Oct 2013 23:59:58 +0000'), new \DateTime('Wed, 02 Oct 2013 23:59:59 +0000')],
- ['yesterday', new \DateTime('Mon, 30 Sep 2013 00:00:00 +0000'), new \DateTime('Tue, 01 Oct 2013 00:00:00 +0000')],
- ['yesterday', new \DateTime('Mon, 31 Dec 2012 00:00:00 +0000'), new \DateTime('Tue, 01 Jan 2013 00:00:00 +0000')],
-
- // Test last month
- ['2 days ago', new \DateTime('Mon, 30 Sep 2013 00:00:00 +0000'), new \DateTime('Wed, 02 Oct 2013 00:00:00 +0000')],
- ['last month', new \DateTime('Mon, 30 Sep 2013 00:00:00 +0000'), new \DateTime('Tue, 31 Oct 2013 00:00:00 +0000')],
- ['last month', new \DateTime('Sun, 01 Sep 2013 00:00:00 +0000'), new \DateTime('Tue, 01 Oct 2013 00:00:00 +0000')],
- ['last month', new \DateTime('Sun, 01 Sep 2013 00:00:00 +0000'), new \DateTime('Thu, 31 Oct 2013 00:00:00 +0000')],
-
- // Test last year
- ['9 months ago', new \DateTime('Tue, 31 Dec 2013 00:00:00 +0000'), new \DateTime('Thu, 02 Oct 2014 00:00:00 +0000')],
- ['11 months ago', new \DateTime('Thu, 03 Oct 2013 00:00:00 +0000'), new \DateTime('Thu, 02 Oct 2014 00:00:00 +0000')],
- ['last year', new \DateTime('Wed, 02 Oct 2013 00:00:00 +0000'), new \DateTime('Thu, 02 Oct 2014 00:00:00 +0000')],
- ['last year', new \DateTime('Tue, 01 Jan 2013 00:00:00 +0000'), new \DateTime('Thu, 02 Oct 2014 00:00:00 +0000')],
- ['2 years ago', new \DateTime('Sun, 01 Jan 2012 00:00:00 +0000'), new \DateTime('Thu, 02 Oct 2014 00:00:00 +0000')],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('formatDateSpanData')]
- public function testFormatDateSpan($expected, $timestamp, $compare = null, $locale = null): void {
- $this->assertEquals((string)$expected, (string)$this->formatter->formatDateSpan($timestamp, $compare, $locale));
- }
-
- public static function formatDateData(): array {
- return [
- [1102831200, 'December 12, 2004'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('formatDateData')]
- public function testFormatDate($timestamp, $expected): void {
- $this->assertEquals($expected, (string)$this->formatter->formatDate($timestamp));
- }
-
- public static function formatDateTimeData(): array {
- return [
- [1350129205, null, "October 13, 2012, 11:53:25\xE2\x80\xAFAM UTC"],
- [1350129205, new \DateTimeZone('Europe/Berlin'), "October 13, 2012, 1:53:25\xE2\x80\xAFPM GMT+2"],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('formatDateTimeData')]
- public function testFormatDateTime($timestamp, $timeZone, $expected): void {
- $this->assertEquals($expected, (string)$this->formatter->formatDateTime($timestamp, 'long', 'long', $timeZone));
- }
-
- public function testFormatDateWithInvalidTZ(): void {
- $this->expectException(\Exception::class);
-
- $this->formatter->formatDate(1350129205, 'long', new \DateTimeZone('Mordor/Barad-dûr'));
- }
-}
--- a/tests/lib/Diagnostics/EventLoggerTest.php
+++ /dev/null
@@ -1,58 +0,0 @@
-logger = new EventLogger(
- $this->createMock(SystemConfig::class),
- $this->createMock(LoggerInterface::class),
- $this->createMock(Log::class)
- );
- }
-
- public function testQueryLogger(): void {
- // Module is not activated and this should not be logged
- $this->logger->start('test1', 'testevent1');
- $this->logger->end('test1');
- $this->logger->log('test2', 'testevent2', microtime(true), microtime(true));
- $events = $this->logger->getEvents();
- $this->assertSame(0, sizeof($events));
-
- // Activate module and log some query
- $this->logger->activate();
-
- // start one event
- $this->logger->start('test3', 'testevent3');
-
- // force log of another event
- $this->logger->log('test4', 'testevent4', microtime(true), microtime(true));
-
- // log started event
- $this->logger->end('test3');
-
- $events = $this->logger->getEvents();
- $this->assertSame('test4', $events['test4']->getId());
- $this->assertSame('testevent4', $events['test4']->getDescription());
- $this->assertSame('test3', $events['test3']->getId());
- $this->assertSame('testevent3', $events['test3']->getDescription());
- $this->assertSame(2, sizeof($events));
- }
-}
--- a/tests/lib/Diagnostics/QueryLoggerTest.php
+++ /dev/null
@@ -1,43 +0,0 @@
-logger = new QueryLogger(
- Server::get(SystemConfig::class)
- );
- }
-
- public function testQueryLogger(): void {
- // Module is not activated and this should not be logged
- $this->logger->startQuery('SELECT', ['testuser', 'count'], ['string', 'int']);
- $this->logger->stopQuery();
- $queries = $this->logger->getQueries();
- $this->assertSame(0, sizeof($queries));
-
- // Activate module and log some query
- $this->logger->activate();
- $this->logger->startQuery('SELECT', ['testuser', 'count'], ['string', 'int']);
- $this->logger->stopQuery();
-
- $queries = $this->logger->getQueries();
- $this->assertSame(1, sizeof($queries));
- }
-}
--- a/tests/lib/DirectEditing/ManagerTest.php
+++ /dev/null
@@ -1,324 +0,0 @@
-editor = new Editor();
-
- $this->random = $this->createMock(ISecureRandom::class);
- $this->connection = Server::get(IDBConnection::class);
- $this->userSession = $this->createMock(IUserSession::class);
- $this->rootFolder = $this->createMock(IRootFolder::class);
- $this->userFolder = $this->createMock(Folder::class);
- $this->l10n = $this->createMock(IL10N::class);
- $this->encryptionManager = $this->createMock(IManager::class);
-
- $l10nFactory = $this->createMock(IFactory::class);
- $l10nFactory->expects($this->once())
- ->method('get')
- ->willReturn($this->l10n);
-
- $this->rootFolder->expects($this->any())
- ->method('getUserFolder')
- ->willReturn($this->userFolder);
-
- $user = $this->createMock(IUser::class);
- $user->expects(self::any())
- ->method('getUID')
- ->willReturn('admin');
- $this->userSession->expects(self::any())
- ->method('getUser')
- ->willReturn($user);
-
- $this->manager = new Manager(
- $this->random, $this->connection, $this->userSession, $this->rootFolder, $l10nFactory, $this->encryptionManager
- );
-
- $this->manager->registerDirectEditor($this->editor);
- }
-
- public function testEditorRegistration(): void {
- $this->assertEquals($this->manager->getEditors(), ['testeditor' => $this->editor]);
- }
-
- public function testCreateToken(): void {
- $expectedToken = 'TOKEN' . time();
- $file = $this->createMock(File::class);
- $file->expects($this->any())
- ->method('getId')
- ->willReturn(123);
- $this->random->expects($this->once())
- ->method('generate')
- ->willReturn($expectedToken);
- $folder = $this->createMock(Folder::class);
- $this->userFolder
- ->method('nodeExists')
- ->willReturnMap([
- ['/File.txt', false],
- ['/', true],
- ]);
- $this->userFolder
- ->method('get')
- ->with('/')
- ->willReturn($folder);
- $folder->expects($this->once())
- ->method('newFile')
- ->willReturn($file);
- $token = $this->manager->create('/File.txt', 'testeditor', 'createEmpty');
- $this->assertEquals($token, $expectedToken);
- }
-
- public function testCreateTokenAccess(): void {
- $expectedToken = 'TOKEN' . time();
- $file = $this->createMock(File::class);
- $file->expects($this->any())
- ->method('getId')
- ->willReturn(123);
- $this->random->expects($this->once())
- ->method('generate')
- ->willReturn($expectedToken);
- $folder = $this->createMock(Folder::class);
- $this->userFolder
- ->method('nodeExists')
- ->willReturnMap([
- ['/File.txt', false],
- ['/', true],
- ]);
- $this->userFolder
- ->method('get')
- ->with('/')
- ->willReturn($folder);
- $folder->expects($this->once())
- ->method('newFile')
- ->willReturn($file);
- $this->manager->create('/File.txt', 'testeditor', 'createEmpty');
- $firstResult = $this->manager->edit($expectedToken);
- $secondResult = $this->manager->edit($expectedToken);
- $this->assertInstanceOf(DataResponse::class, $firstResult);
- $this->assertInstanceOf(NotFoundResponse::class, $secondResult);
- }
-
- public function testOpenByPath(): void {
- $expectedToken = 'TOKEN' . time();
- $file = $this->createMock(File::class);
- $file->expects($this->any())
- ->method('getId')
- ->willReturn(123);
- $file->expects($this->any())
- ->method('getPath')
- ->willReturn('/admin/files/File.txt');
- $this->random->expects($this->once())
- ->method('generate')
- ->willReturn($expectedToken);
- $this->userFolder
- ->method('nodeExists')
- ->willReturnMap([
- ['/File.txt', false],
- ['/', true],
- ]);
- $this->userFolder
- ->method('get')
- ->with('/File.txt')
- ->willReturn($file);
- $this->userFolder
- ->method('getRelativePath')
- ->willReturn('/File.txt');
- $this->manager->open('/File.txt', 'testeditor');
- $firstResult = $this->manager->edit($expectedToken);
- $secondResult = $this->manager->edit($expectedToken);
- $this->assertInstanceOf(DataResponse::class, $firstResult);
- $this->assertInstanceOf(NotFoundResponse::class, $secondResult);
- }
-
- public function testOpenById(): void {
- $expectedToken = 'TOKEN' . time();
- $fileRead = $this->createMock(File::class);
- $fileRead->method('getPermissions')
- ->willReturn(1);
- $fileRead->expects($this->any())
- ->method('getId')
- ->willReturn(123);
- $fileRead->expects($this->any())
- ->method('getPath')
- ->willReturn('/admin/files/shared_file.txt');
- $file = $this->createMock(File::class);
- $file->method('getPermissions')
- ->willReturn(1);
- $file->expects($this->any())
- ->method('getId')
- ->willReturn(123);
- $file->expects($this->any())
- ->method('getPath')
- ->willReturn('/admin/files/File.txt');
- $this->random->expects($this->once())
- ->method('generate')
- ->willReturn($expectedToken);
- $folder = $this->createMock(Folder::class);
- $folder->expects($this->any())
- ->method('getById')
- ->willReturn([
- $fileRead,
- $file
- ]);
- $this->userFolder
- ->method('nodeExists')
- ->willReturnMap([
- ['/File.txt', false],
- ['/', true],
- ]);
- $this->userFolder
- ->method('get')
- ->with('/')
- ->willReturn($folder);
- $this->userFolder
- ->method('getRelativePath')
- ->willReturn('/File.txt');
-
- $this->manager->open('/', 'testeditor', 123);
- $firstResult = $this->manager->edit($expectedToken);
- $secondResult = $this->manager->edit($expectedToken);
- $this->assertInstanceOf(DataResponse::class, $firstResult);
- $this->assertInstanceOf(NotFoundResponse::class, $secondResult);
- }
-
- public function testCreateFileAlreadyExists(): void {
- $this->expectException(\RuntimeException::class);
- $this->userFolder
- ->method('nodeExists')
- ->with('/File.txt')
- ->willReturn(true);
-
- $this->manager->create('/File.txt', 'testeditor', 'createEmpty');
- }
-}
--- a/tests/lib/EmojiHelperTest.php
+++ /dev/null
@@ -1,3155 +0,0 @@
-db = $this->createMock(IDBConnection::class);
- $this->helper = new EmojiHelper($this->db);
- }
-
- /**
- * @param bool $supports4ByteText
- * @param bool $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('doesPlatformSupportEmojiDataProvider')]
- public function testDoesPlatformSupportEmoji(bool $supports4ByteText, bool $expected): void {
- $this->db->expects($this->once())
- ->method('supports4ByteText')
- ->willReturn($supports4ByteText);
-
- $this->assertEquals($expected, $this->helper->doesPlatformSupportEmoji());
- }
-
- public static function doesPlatformSupportEmojiDataProvider(): array {
- return [
- [true, true],
- [false, false],
- ];
- }
-
- /**
- * @param string $emoji
- * @param bool $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('isValidSingleEmojiDataProvider')]
- public function testIsValidSingleEmoji(string $emoji, bool $expected): void {
- $actual = $this->helper->isValidSingleEmoji($emoji);
-
- $this->assertEquals($expected, $actual);
- }
-
- public static function isValidSingleEmojiDataProvider(): array {
- return [
- ['📱📠', false],
- ['a', false],
- ['0', false],
- ['$', false],
- ['$$', false],
- // Cyrillic
- ['а', false],
- ['Б', false],
- ['ж', false],
- ['Я', false],
- // Greek
- ['α', false],
- ['Ω', false],
- ['λ', false],
- ['Σ', false],
- // Extracted from spreed/node_modules/emojis-list/index.js
- // https://github.com/Kikobeats/emojis-list/blob/master/index.js
- ['🀄️', true],
- ['🃏', true],
- ['🅰️', true],
- ['🅱️', true],
- ['🅾️', true],
- ['🅿️', true],
- ['🆎', true],
- ['🆑', true],
- ['🆒', true],
- ['🆓', true],
- ['🆔', true],
- ['🆕', true],
- ['🆖', true],
- ['🆗', true],
- ['🆘', true],
- ['🆙', true],
- ['🆚', true],
- ['🇦🇨', true],
- ['🇦🇩', true],
- ['🇦🇪', true],
- ['🇦🇫', true],
- ['🇦🇬', true],
- ['🇦🇮', true],
- ['🇦🇱', true],
- ['🇦🇲', true],
- ['🇦🇴', true],
- ['🇦🇶', true],
- ['🇦🇷', true],
- ['🇦🇸', true],
- ['🇦🇹', true],
- ['🇦🇺', true],
- ['🇦🇼', true],
- ['🇦🇽', true],
- ['🇦🇿', true],
- ['🇦', true],
- ['🇧🇦', true],
- ['🇧🇧', true],
- ['🇧🇩', true],
- ['🇧🇪', true],
- ['🇧🇫', true],
- ['🇧🇬', true],
- ['🇧🇭', true],
- ['🇧🇮', true],
- ['🇧🇯', true],
- ['🇧🇱', true],
- ['🇧🇲', true],
- ['🇧🇳', true],
- ['🇧🇴', true],
- ['🇧🇶', true],
- ['🇧🇷', true],
- ['🇧🇸', true],
- ['🇧🇹', true],
- ['🇧🇻', true],
- ['🇧🇼', true],
- ['🇧🇾', true],
- ['🇧🇿', true],
- ['🇧', true],
- ['🇨🇦', true],
- ['🇨🇨', true],
- ['🇨🇩', true],
- ['🇨🇫', true],
- ['🇨🇬', true],
- ['🇨🇭', true],
- ['🇨🇮', true],
- ['🇨🇰', true],
- ['🇨🇱', true],
- ['🇨🇲', true],
- ['🇨🇳', true],
- ['🇨🇴', true],
- ['🇨🇵', true],
- ['🇨🇷', true],
- ['🇨🇺', true],
- ['🇨🇻', true],
- ['🇨🇼', true],
- ['🇨🇽', true],
- ['🇨🇾', true],
- ['🇨🇿', true],
- ['🇨', true],
- ['🇩🇪', true],
- ['🇩🇬', true],
- ['🇩🇯', true],
- ['🇩🇰', true],
- ['🇩🇲', true],
- ['🇩🇴', true],
- ['🇩🇿', true],
- ['🇩', true],
- ['🇪🇦', true],
- ['🇪🇨', true],
- ['🇪🇪', true],
- ['🇪🇬', true],
- ['🇪🇭', true],
- ['🇪🇷', true],
- ['🇪🇸', true],
- ['🇪🇹', true],
- ['🇪🇺', true],
- ['🇪', true],
- ['🇫🇮', true],
- ['🇫🇯', true],
- ['🇫🇰', true],
- ['🇫🇲', true],
- ['🇫🇴', true],
- ['🇫🇷', true],
- ['🇫', true],
- ['🇬🇦', true],
- ['🇬🇧', true],
- ['🇬🇩', true],
- ['🇬🇪', true],
- ['🇬🇫', true],
- ['🇬🇬', true],
- ['🇬🇭', true],
- ['🇬🇮', true],
- ['🇬🇱', true],
- ['🇬🇲', true],
- ['🇬🇳', true],
- ['🇬🇵', true],
- ['🇬🇶', true],
- ['🇬🇷', true],
- ['🇬🇸', true],
- ['🇬🇹', true],
- ['🇬🇺', true],
- ['🇬🇼', true],
- ['🇬🇾', true],
- ['🇬', true],
- ['🇭🇰', true],
- ['🇭🇲', true],
- ['🇭🇳', true],
- ['🇭🇷', true],
- ['🇭🇹', true],
- ['🇭🇺', true],
- ['🇭', true],
- ['🇮🇨', true],
- ['🇮🇩', true],
- ['🇮🇪', true],
- ['🇮🇱', true],
- ['🇮🇲', true],
- ['🇮🇳', true],
- ['🇮🇴', true],
- ['🇮🇶', true],
- ['🇮🇷', true],
- ['🇮🇸', true],
- ['🇮🇹', true],
- ['🇮', true],
- ['🇯🇪', true],
- ['🇯🇲', true],
- ['🇯🇴', true],
- ['🇯🇵', true],
- ['🇯', true],
- ['🇰🇪', true],
- ['🇰🇬', true],
- ['🇰🇭', true],
- ['🇰🇮', true],
- ['🇰🇲', true],
- ['🇰🇳', true],
- ['🇰🇵', true],
- ['🇰🇷', true],
- ['🇰🇼', true],
- ['🇰🇾', true],
- ['🇰🇿', true],
- ['🇰', true],
- ['🇱🇦', true],
- ['🇱🇧', true],
- ['🇱🇨', true],
- ['🇱🇮', true],
- ['🇱🇰', true],
- ['🇱🇷', true],
- ['🇱🇸', true],
- ['🇱🇹', true],
- ['🇱🇺', true],
- ['🇱🇻', true],
- ['🇱🇾', true],
- ['🇱', true],
- ['🇲🇦', true],
- ['🇲🇨', true],
- ['🇲🇩', true],
- ['🇲🇪', true],
- ['🇲🇫', true],
- ['🇲🇬', true],
- ['🇲🇭', true],
- ['🇲🇰', true],
- ['🇲🇱', true],
- ['🇲🇲', true],
- ['🇲🇳', true],
- ['🇲🇴', true],
- ['🇲🇵', true],
- ['🇲🇶', true],
- ['🇲🇷', true],
- ['🇲🇸', true],
- ['🇲🇹', true],
- ['🇲🇺', true],
- ['🇲🇻', true],
- ['🇲🇼', true],
- ['🇲🇽', true],
- ['🇲🇾', true],
- ['🇲🇿', true],
- ['🇲', true],
- ['🇳🇦', true],
- ['🇳🇨', true],
- ['🇳🇪', true],
- ['🇳🇫', true],
- ['🇳🇬', true],
- ['🇳🇮', true],
- ['🇳🇱', true],
- ['🇳🇴', true],
- ['🇳🇵', true],
- ['🇳🇷', true],
- ['🇳🇺', true],
- ['🇳🇿', true],
- ['🇳', true],
- ['🇴🇲', true],
- ['🇴', true],
- ['🇵🇦', true],
- ['🇵🇪', true],
- ['🇵🇫', true],
- ['🇵🇬', true],
- ['🇵🇭', true],
- ['🇵🇰', true],
- ['🇵🇱', true],
- ['🇵🇲', true],
- ['🇵🇳', true],
- ['🇵🇷', true],
- ['🇵🇸', true],
- ['🇵🇹', true],
- ['🇵🇼', true],
- ['🇵🇾', true],
- ['🇵', true],
- ['🇶🇦', true],
- ['🇶', true],
- ['🇷🇪', true],
- ['🇷🇴', true],
- ['🇷🇸', true],
- ['🇷🇺', true],
- ['🇷🇼', true],
- ['🇷', true],
- ['🇸🇦', true],
- ['🇸🇧', true],
- ['🇸🇨', true],
- ['🇸🇩', true],
- ['🇸🇪', true],
- ['🇸🇬', true],
- ['🇸🇭', true],
- ['🇸🇮', true],
- ['🇸🇯', true],
- ['🇸🇰', true],
- ['🇸🇱', true],
- ['🇸🇲', true],
- ['🇸🇳', true],
- ['🇸🇴', true],
- ['🇸🇷', true],
- ['🇸🇸', true],
- ['🇸🇹', true],
- ['🇸🇻', true],
- ['🇸🇽', true],
- ['🇸🇾', true],
- ['🇸🇿', true],
- ['🇸', true],
- ['🇹🇦', true],
- ['🇹🇨', true],
- ['🇹🇩', true],
- ['🇹🇫', true],
- ['🇹🇬', true],
- ['🇹🇭', true],
- ['🇹🇯', true],
- ['🇹🇰', true],
- ['🇹🇱', true],
- ['🇹🇲', true],
- ['🇹🇳', true],
- ['🇹🇴', true],
- ['🇹🇷', true],
- ['🇹🇹', true],
- ['🇹🇻', true],
- ['🇹🇼', true],
- ['🇹🇿', true],
- ['🇹', true],
- ['🇺🇦', true],
- ['🇺🇬', true],
- ['🇺🇲', true],
- ['🇺🇳', true],
- ['🇺🇸', true],
- ['🇺🇾', true],
- ['🇺🇿', true],
- ['🇺', true],
- ['🇻🇦', true],
- ['🇻🇨', true],
- ['🇻🇪', true],
- ['🇻🇬', true],
- ['🇻🇮', true],
- ['🇻🇳', true],
- ['🇻🇺', true],
- ['🇻', true],
- ['🇼🇫', true],
- ['🇼🇸', true],
- ['🇼', true],
- ['🇽🇰', true],
- ['🇽', true],
- ['🇾🇪', true],
- ['🇾🇹', true],
- ['🇾', true],
- ['🇿🇦', true],
- ['🇿🇲', true],
- ['🇿🇼', true],
- ['🇿', true],
- ['🈁', true],
- ['🈂️', true],
- ['🈚️', true],
- ['🈯️', true],
- ['🈲', true],
- ['🈳', true],
- ['🈴', true],
- ['🈵', true],
- ['🈶', true],
- ['🈷️', true],
- ['🈸', true],
- ['🈹', true],
- ['🈺', true],
- ['🉐', true],
- ['🉑', true],
- ['🌀', true],
- ['🌁', true],
- ['🌂', true],
- ['🌃', true],
- ['🌄', true],
- ['🌅', true],
- ['🌆', true],
- ['🌇', true],
- ['🌈', true],
- ['🌉', true],
- ['🌊', true],
- ['🌋', true],
- ['🌌', true],
- ['🌍', true],
- ['🌎', true],
- ['🌏', true],
- ['🌐', true],
- ['🌑', true],
- ['🌒', true],
- ['🌓', true],
- ['🌔', true],
- ['🌕', true],
- ['🌖', true],
- ['🌗', true],
- ['🌘', true],
- ['🌙', true],
- ['🌚', true],
- ['🌛', true],
- ['🌜', true],
- ['🌝', true],
- ['🌞', true],
- ['🌟', true],
- ['🌠', true],
- ['🌡️', true],
- ['🌤️', true],
- ['🌥️', true],
- ['🌦️', true],
- ['🌧️', true],
- ['🌨️', true],
- ['🌩️', true],
- ['🌪️', true],
- ['🌫️', true],
- ['🌬️', true],
- ['🌭', true],
- ['🌮', true],
- ['🌯', true],
- ['🌰', true],
- ['🌱', true],
- ['🌲', true],
- ['🌳', true],
- ['🌴', true],
- ['🌵', true],
- ['🌶️', true],
- ['🌷', true],
- ['🌸', true],
- ['🌹', true],
- ['🌺', true],
- ['🌻', true],
- ['🌼', true],
- ['🌽', true],
- ['🌾', true],
- ['🌿', true],
- ['🍀', true],
- ['🍁', true],
- ['🍂', true],
- ['🍃', true],
- ['🍄', true],
- ['🍅', true],
- ['🍆', true],
- ['🍇', true],
- ['🍈', true],
- ['🍉', true],
- ['🍊', true],
- ['🍋', true],
- ['🍌', true],
- ['🍍', true],
- ['🍎', true],
- ['🍏', true],
- ['🍐', true],
- ['🍑', true],
- ['🍒', true],
- ['🍓', true],
- ['🍔', true],
- ['🍕', true],
- ['🍖', true],
- ['🍗', true],
- ['🍘', true],
- ['🍙', true],
- ['🍚', true],
- ['🍛', true],
- ['🍜', true],
- ['🍝', true],
- ['🍞', true],
- ['🍟', true],
- ['🍠', true],
- ['🍡', true],
- ['🍢', true],
- ['🍣', true],
- ['🍤', true],
- ['🍥', true],
- ['🍦', true],
- ['🍧', true],
- ['🍨', true],
- ['🍩', true],
- ['🍪', true],
- ['🍫', true],
- ['🍬', true],
- ['🍭', true],
- ['🍮', true],
- ['🍯', true],
- ['🍰', true],
- ['🍱', true],
- ['🍲', true],
- ['🍳', true],
- ['🍴', true],
- ['🍵', true],
- ['🍶', true],
- ['🍷', true],
- ['🍸', true],
- ['🍹', true],
- ['🍺', true],
- ['🍻', true],
- ['🍼', true],
- ['🍽️', true],
- ['🍾', true],
- ['🍿', true],
- ['🎀', true],
- ['🎁', true],
- ['🎂', true],
- ['🎃', true],
- ['🎄', true],
- ['🎅🏻', true],
- ['🎅🏼', true],
- ['🎅🏽', true],
- ['🎅🏾', true],
- ['🎅🏿', true],
- ['🎅', true],
- ['🎆', true],
- ['🎇', true],
- ['🎈', true],
- ['🎉', true],
- ['🎊', true],
- ['🎋', true],
- ['🎌', true],
- ['🎍', true],
- ['🎎', true],
- ['🎏', true],
- ['🎐', true],
- ['🎑', true],
- ['🎒', true],
- ['🎓', true],
- ['🎖️', true],
- ['🎗️', true],
- ['🎙️', true],
- ['🎚️', true],
- ['🎛️', true],
- ['🎞️', true],
- ['🎟️', true],
- ['🎠', true],
- ['🎡', true],
- ['🎢', true],
- ['🎣', true],
- ['🎤', true],
- ['🎥', true],
- ['🎦', true],
- ['🎧', true],
- ['🎨', true],
- ['🎩', true],
- ['🎪', true],
- ['🎫', true],
- ['🎬', true],
- ['🎭', true],
- ['🎮', true],
- ['🎯', true],
- ['🎰', true],
- ['🎱', true],
- ['🎲', true],
- ['🎳', true],
- ['🎴', true],
- ['🎵', true],
- ['🎶', true],
- ['🎷', true],
- ['🎸', true],
- ['🎹', true],
- ['🎺', true],
- ['🎻', true],
- ['🎼', true],
- ['🎽', true],
- ['🎾', true],
- ['🎿', true],
- ['🏀', true],
- ['🏁', true],
- ['🏂🏻', true],
- ['🏂🏼', true],
- ['🏂🏽', true],
- ['🏂🏾', true],
- ['🏂🏿', true],
- ['🏂', true],
- ['🏃🏻♀️', true],
- ['🏃🏻♂️', true],
- ['🏃🏻', true],
- ['🏃🏼♀️', true],
- ['🏃🏼♂️', true],
- ['🏃🏼', true],
- ['🏃🏽♀️', true],
- ['🏃🏽♂️', true],
- ['🏃🏽', true],
- ['🏃🏾♀️', true],
- ['🏃🏾♂️', true],
- ['🏃🏾', true],
- ['🏃🏿♀️', true],
- ['🏃🏿♂️', true],
- ['🏃🏿', true],
- ['🏃♀️', true],
- ['🏃♂️', true],
- ['🏃', true],
- ['🏄🏻♀️', true],
- ['🏄🏻♂️', true],
- ['🏄🏻', true],
- ['🏄🏼♀️', true],
- ['🏄🏼♂️', true],
- ['🏄🏼', true],
- ['🏄🏽♀️', true],
- ['🏄🏽♂️', true],
- ['🏄🏽', true],
- ['🏄🏾♀️', true],
- ['🏄🏾♂️', true],
- ['🏄🏾', true],
- ['🏄🏿♀️', true],
- ['🏄🏿♂️', true],
- ['🏄🏿', true],
- ['🏄♀️', true],
- ['🏄♂️', true],
- ['🏄', true],
- ['🏅', true],
- ['🏆', true],
- ['🏇🏻', true],
- ['🏇🏼', true],
- ['🏇🏽', true],
- ['🏇🏾', true],
- ['🏇🏿', true],
- ['🏇', true],
- ['🏈', true],
- ['🏉', true],
- ['🏊🏻♀️', true],
- ['🏊🏻♂️', true],
- ['🏊🏻', true],
- ['🏊🏼♀️', true],
- ['🏊🏼♂️', true],
- ['🏊🏼', true],
- ['🏊🏽♀️', true],
- ['🏊🏽♂️', true],
- ['🏊🏽', true],
- ['🏊🏾♀️', true],
- ['🏊🏾♂️', true],
- ['🏊🏾', true],
- ['🏊🏿♀️', true],
- ['🏊🏿♂️', true],
- ['🏊🏿', true],
- ['🏊♀️', true],
- ['🏊♂️', true],
- ['🏊', true],
- ['🏋🏻♀️', true],
- ['🏋🏻♂️', true],
- ['🏋🏻', true],
- ['🏋🏼♀️', true],
- ['🏋🏼♂️', true],
- ['🏋🏼', true],
- ['🏋🏽♀️', true],
- ['🏋🏽♂️', true],
- ['🏋🏽', true],
- ['🏋🏾♀️', true],
- ['🏋🏾♂️', true],
- ['🏋🏾', true],
- ['🏋🏿♀️', true],
- ['🏋🏿♂️', true],
- ['🏋🏿', true],
- ['🏋️♀️', true],
- ['🏋️♂️', true],
- ['🏋️', true],
- ['🏌🏻♀️', true],
- ['🏌🏻♂️', true],
- ['🏌🏻', true],
- ['🏌🏼♀️', true],
- ['🏌🏼♂️', true],
- ['🏌🏼', true],
- ['🏌🏽♀️', true],
- ['🏌🏽♂️', true],
- ['🏌🏽', true],
- ['🏌🏾♀️', true],
- ['🏌🏾♂️', true],
- ['🏌🏾', true],
- ['🏌🏿♀️', true],
- ['🏌🏿♂️', true],
- ['🏌🏿', true],
- ['🏌️♀️', true],
- ['🏌️♂️', true],
- ['🏌️', true],
- ['🏍️', true],
- ['🏎️', true],
- ['🏏', true],
- ['🏐', true],
- ['🏑', true],
- ['🏒', true],
- ['🏓', true],
- ['🏔️', true],
- ['🏕️', true],
- ['🏖️', true],
- ['🏗️', true],
- ['🏘️', true],
- ['🏙️', true],
- ['🏚️', true],
- ['🏛️', true],
- ['🏜️', true],
- ['🏝️', true],
- ['🏞️', true],
- ['🏟️', true],
- ['🏠', true],
- ['🏡', true],
- ['🏢', true],
- ['🏣', true],
- ['🏤', true],
- ['🏥', true],
- ['🏦', true],
- ['🏧', true],
- ['🏨', true],
- ['🏩', true],
- ['🏪', true],
- ['🏫', true],
- ['🏬', true],
- ['🏭', true],
- ['🏮', true],
- ['🏯', true],
- ['🏰', true],
- ['🏳️🌈', true],
- ['🏳️', true],
- ['🏴☠️', true],
- ['🏴', true],
- ['🏴', true],
- ['🏴', true],
- ['🏴', true],
- ['🏵️', true],
- ['🏷️', true],
- ['🏸', true],
- ['🏹', true],
- ['🏺', true],
- ['🏻', true],
- ['🏼', true],
- ['🏽', true],
- ['🏾', true],
- ['🏿', true],
- ['🐀', true],
- ['🐁', true],
- ['🐂', true],
- ['🐃', true],
- ['🐄', true],
- ['🐅', true],
- ['🐆', true],
- ['🐇', true],
- ['🐈', true],
- ['🐉', true],
- ['🐊', true],
- ['🐋', true],
- ['🐌', true],
- ['🐍', true],
- ['🐎', true],
- ['🐏', true],
- ['🐐', true],
- ['🐑', true],
- ['🐒', true],
- ['🐓', true],
- ['🐔', true],
- ['🐕🦺', true],
- ['🐕', true],
- ['🐖', true],
- ['🐗', true],
- ['🐘', true],
- ['🐙', true],
- ['🐚', true],
- ['🐛', true],
- ['🐜', true],
- ['🐝', true],
- ['🐞', true],
- ['🐟', true],
- ['🐠', true],
- ['🐡', true],
- ['🐢', true],
- ['🐣', true],
- ['🐤', true],
- ['🐥', true],
- ['🐦', true],
- ['🐧', true],
- ['🐨', true],
- ['🐩', true],
- ['🐪', true],
- ['🐫', true],
- ['🐬', true],
- ['🐭', true],
- ['🐮', true],
- ['🐯', true],
- ['🐰', true],
- ['🐱', true],
- ['🐲', true],
- ['🐳', true],
- ['🐴', true],
- ['🐵', true],
- ['🐶', true],
- ['🐷', true],
- ['🐸', true],
- ['🐹', true],
- ['🐺', true],
- ['🐻', true],
- ['🐼', true],
- ['🐽', true],
- ['🐾', true],
- ['🐿️', true],
- ['👀', true],
- ['👁🗨', true],
- ['👁️', true],
- ['👂🏻', true],
- ['👂🏼', true],
- ['👂🏽', true],
- ['👂🏾', true],
- ['👂🏿', true],
- ['👂', true],
- ['👃🏻', true],
- ['👃🏼', true],
- ['👃🏽', true],
- ['👃🏾', true],
- ['👃🏿', true],
- ['👃', true],
- ['👄', true],
- ['👅', true],
- ['👆🏻', true],
- ['👆🏼', true],
- ['👆🏽', true],
- ['👆🏾', true],
- ['👆🏿', true],
- ['👆', true],
- ['👇🏻', true],
- ['👇🏼', true],
- ['👇🏽', true],
- ['👇🏾', true],
- ['👇🏿', true],
- ['👇', true],
- ['👈🏻', true],
- ['👈🏼', true],
- ['👈🏽', true],
- ['👈🏾', true],
- ['👈🏿', true],
- ['👈', true],
- ['👉🏻', true],
- ['👉🏼', true],
- ['👉🏽', true],
- ['👉🏾', true],
- ['👉🏿', true],
- ['👉', true],
- ['👊🏻', true],
- ['👊🏼', true],
- ['👊🏽', true],
- ['👊🏾', true],
- ['👊🏿', true],
- ['👊', true],
- ['👋🏻', true],
- ['👋🏼', true],
- ['👋🏽', true],
- ['👋🏾', true],
- ['👋🏿', true],
- ['👋', true],
- ['👌🏻', true],
- ['👌🏼', true],
- ['👌🏽', true],
- ['👌🏾', true],
- ['👌🏿', true],
- ['👌', true],
- ['👍🏻', true],
- ['👍🏼', true],
- ['👍🏽', true],
- ['👍🏾', true],
- ['👍🏿', true],
- ['👍', true],
- ['👎🏻', true],
- ['👎🏼', true],
- ['👎🏽', true],
- ['👎🏾', true],
- ['👎🏿', true],
- ['👎', true],
- ['👏🏻', true],
- ['👏🏼', true],
- ['👏🏽', true],
- ['👏🏾', true],
- ['👏🏿', true],
- ['👏', true],
- ['👐🏻', true],
- ['👐🏼', true],
- ['👐🏽', true],
- ['👐🏾', true],
- ['👐🏿', true],
- ['👐', true],
- ['👑', true],
- ['👒', true],
- ['👓', true],
- ['👔', true],
- ['👕', true],
- ['👖', true],
- ['👗', true],
- ['👘', true],
- ['👙', true],
- ['👚', true],
- ['👛', true],
- ['👜', true],
- ['👝', true],
- ['👞', true],
- ['👟', true],
- ['👠', true],
- ['👡', true],
- ['👢', true],
- ['👣', true],
- ['👤', true],
- ['👥', true],
- ['👦🏻', true],
- ['👦🏼', true],
- ['👦🏽', true],
- ['👦🏾', true],
- ['👦🏿', true],
- ['👦', true],
- ['👧🏻', true],
- ['👧🏼', true],
- ['👧🏽', true],
- ['👧🏾', true],
- ['👧🏿', true],
- ['👧', true],
- ['👨🏻🌾', true],
- ['👨🏻🍳', true],
- ['👨🏻🎓', true],
- ['👨🏻🎤', true],
- ['👨🏻🎨', true],
- ['👨🏻🏫', true],
- ['👨🏻🏭', true],
- ['👨🏻💻', true],
- ['👨🏻💼', true],
- ['👨🏻🔧', true],
- ['👨🏻🔬', true],
- ['👨🏻🚀', true],
- ['👨🏻🚒', true],
- ['👨🏻🦯', true],
- ['👨🏻🦰', true],
- ['👨🏻🦱', true],
- ['👨🏻🦲', true],
- ['👨🏻🦳', true],
- ['👨🏻🦼', true],
- ['👨🏻🦽', true],
- ['👨🏻⚕️', true],
- ['👨🏻⚖️', true],
- ['👨🏻✈️', true],
- ['👨🏻', true],
- ['👨🏼🌾', true],
- ['👨🏼🍳', true],
- ['👨🏼🎓', true],
- ['👨🏼🎤', true],
- ['👨🏼🎨', true],
- ['👨🏼🏫', true],
- ['👨🏼🏭', true],
- ['👨🏼💻', true],
- ['👨🏼💼', true],
- ['👨🏼🔧', true],
- ['👨🏼🔬', true],
- ['👨🏼🚀', true],
- ['👨🏼🚒', true],
- ['👨🏼🤝👨🏻', true],
- ['👨🏼🦯', true],
- ['👨🏼🦰', true],
- ['👨🏼🦱', true],
- ['👨🏼🦲', true],
- ['👨🏼🦳', true],
- ['👨🏼🦼', true],
- ['👨🏼🦽', true],
- ['👨🏼⚕️', true],
- ['👨🏼⚖️', true],
- ['👨🏼✈️', true],
- ['👨🏼', true],
- ['👨🏽🌾', true],
- ['👨🏽🍳', true],
- ['👨🏽🎓', true],
- ['👨🏽🎤', true],
- ['👨🏽🎨', true],
- ['👨🏽🏫', true],
- ['👨🏽🏭', true],
- ['👨🏽💻', true],
- ['👨🏽💼', true],
- ['👨🏽🔧', true],
- ['👨🏽🔬', true],
- ['👨🏽🚀', true],
- ['👨🏽🚒', true],
- ['👨🏽🤝👨🏻', true],
- ['👨🏽🤝👨🏼', true],
- ['👨🏽🦯', true],
- ['👨🏽🦰', true],
- ['👨🏽🦱', true],
- ['👨🏽🦲', true],
- ['👨🏽🦳', true],
- ['👨🏽🦼', true],
- ['👨🏽🦽', true],
- ['👨🏽⚕️', true],
- ['👨🏽⚖️', true],
- ['👨🏽✈️', true],
- ['👨🏽', true],
- ['👨🏾🌾', true],
- ['👨🏾🍳', true],
- ['👨🏾🎓', true],
- ['👨🏾🎤', true],
- ['👨🏾🎨', true],
- ['👨🏾🏫', true],
- ['👨🏾🏭', true],
- ['👨🏾💻', true],
- ['👨🏾💼', true],
- ['👨🏾🔧', true],
- ['👨🏾🔬', true],
- ['👨🏾🚀', true],
- ['👨🏾🚒', true],
- ['👨🏾🤝👨🏻', true],
- ['👨🏾🤝👨🏼', true],
- ['👨🏾🤝👨🏽', true],
- ['👨🏾🦯', true],
- ['👨🏾🦰', true],
- ['👨🏾🦱', true],
- ['👨🏾🦲', true],
- ['👨🏾🦳', true],
- ['👨🏾🦼', true],
- ['👨🏾🦽', true],
- ['👨🏾⚕️', true],
- ['👨🏾⚖️', true],
- ['👨🏾✈️', true],
- ['👨🏾', true],
- ['👨🏿🌾', true],
- ['👨🏿🍳', true],
- ['👨🏿🎓', true],
- ['👨🏿🎤', true],
- ['👨🏿🎨', true],
- ['👨🏿🏫', true],
- ['👨🏿🏭', true],
- ['👨🏿💻', true],
- ['👨🏿💼', true],
- ['👨🏿🔧', true],
- ['👨🏿🔬', true],
- ['👨🏿🚀', true],
- ['👨🏿🚒', true],
- ['👨🏿🤝👨🏻', true],
- ['👨🏿🤝👨🏼', true],
- ['👨🏿🤝👨🏽', true],
- ['👨🏿🤝👨🏾', true],
- ['👨🏿🦯', true],
- ['👨🏿🦰', true],
- ['👨🏿🦱', true],
- ['👨🏿🦲', true],
- ['👨🏿🦳', true],
- ['👨🏿🦼', true],
- ['👨🏿🦽', true],
- ['👨🏿⚕️', true],
- ['👨🏿⚖️', true],
- ['👨🏿✈️', true],
- ['👨🏿', true],
- ['👨🌾', true],
- ['👨🍳', true],
- ['👨🎓', true],
- ['👨🎤', true],
- ['👨🎨', true],
- ['👨🏫', true],
- ['👨🏭', true],
- ['👨👦👦', true],
- ['👨👦', true],
- ['👨👧👦', true],
- ['👨👧👧', true],
- ['👨👧', true],
- ['👨👨👦👦', true],
- ['👨👨👦', true],
- ['👨👨👧👦', true],
- ['👨👨👧👧', true],
- ['👨👨👧', true],
- ['👨👩👦👦', true],
- ['👨👩👦', true],
- ['👨👩👧👦', true],
- ['👨👩👧👧', true],
- ['👨👩👧', true],
- ['👨💻', true],
- ['👨💼', true],
- ['👨🔧', true],
- ['👨🔬', true],
- ['👨🚀', true],
- ['👨🚒', true],
- ['👨🦯', true],
- ['👨🦰', true],
- ['👨🦱', true],
- ['👨🦲', true],
- ['👨🦳', true],
- ['👨🦼', true],
- ['👨🦽', true],
- ['👨⚕️', true],
- ['👨⚖️', true],
- ['👨✈️', true],
- ['👨❤️👨', true],
- ['👨❤️💋👨', true],
- ['👨', true],
- ['👩🏻🌾', true],
- ['👩🏻🍳', true],
- ['👩🏻🎓', true],
- ['👩🏻🎤', true],
- ['👩🏻🎨', true],
- ['👩🏻🏫', true],
- ['👩🏻🏭', true],
- ['👩🏻💻', true],
- ['👩🏻💼', true],
- ['👩🏻🔧', true],
- ['👩🏻🔬', true],
- ['👩🏻🚀', true],
- ['👩🏻🚒', true],
- ['👩🏻🤝👨🏼', true],
- ['👩🏻🤝👨🏽', true],
- ['👩🏻🤝👨🏾', true],
- ['👩🏻🤝👨🏿', true],
- ['👩🏻🦯', true],
- ['👩🏻🦰', true],
- ['👩🏻🦱', true],
- ['👩🏻🦲', true],
- ['👩🏻🦳', true],
- ['👩🏻🦼', true],
- ['👩🏻🦽', true],
- ['👩🏻⚕️', true],
- ['👩🏻⚖️', true],
- ['👩🏻✈️', true],
- ['👩🏻', true],
- ['👩🏼🌾', true],
- ['👩🏼🍳', true],
- ['👩🏼🎓', true],
- ['👩🏼🎤', true],
- ['👩🏼🎨', true],
- ['👩🏼🏫', true],
- ['👩🏼🏭', true],
- ['👩🏼💻', true],
- ['👩🏼💼', true],
- ['👩🏼🔧', true],
- ['👩🏼🔬', true],
- ['👩🏼🚀', true],
- ['👩🏼🚒', true],
- ['👩🏼🤝👨🏻', true],
- ['👩🏼🤝👨🏽', true],
- ['👩🏼🤝👨🏾', true],
- ['👩🏼🤝👨🏿', true],
- ['👩🏼🤝👩🏻', true],
- ['👩🏼🦯', true],
- ['👩🏼🦰', true],
- ['👩🏼🦱', true],
- ['👩🏼🦲', true],
- ['👩🏼🦳', true],
- ['👩🏼🦼', true],
- ['👩🏼🦽', true],
- ['👩🏼⚕️', true],
- ['👩🏼⚖️', true],
- ['👩🏼✈️', true],
- ['👩🏼', true],
- ['👩🏽🌾', true],
- ['👩🏽🍳', true],
- ['👩🏽🎓', true],
- ['👩🏽🎤', true],
- ['👩🏽🎨', true],
- ['👩🏽🏫', true],
- ['👩🏽🏭', true],
- ['👩🏽💻', true],
- ['👩🏽💼', true],
- ['👩🏽🔧', true],
- ['👩🏽🔬', true],
- ['👩🏽🚀', true],
- ['👩🏽🚒', true],
- ['👩🏽🤝👨🏻', true],
- ['👩🏽🤝👨🏼', true],
- ['👩🏽🤝👨🏾', true],
- ['👩🏽🤝👨🏿', true],
- ['👩🏽🤝👩🏻', true],
- ['👩🏽🤝👩🏼', true],
- ['👩🏽🦯', true],
- ['👩🏽🦰', true],
- ['👩🏽🦱', true],
- ['👩🏽🦲', true],
- ['👩🏽🦳', true],
- ['👩🏽🦼', true],
- ['👩🏽🦽', true],
- ['👩🏽⚕️', true],
- ['👩🏽⚖️', true],
- ['👩🏽✈️', true],
- ['👩🏽', true],
- ['👩🏾🌾', true],
- ['👩🏾🍳', true],
- ['👩🏾🎓', true],
- ['👩🏾🎤', true],
- ['👩🏾🎨', true],
- ['👩🏾🏫', true],
- ['👩🏾🏭', true],
- ['👩🏾💻', true],
- ['👩🏾💼', true],
- ['👩🏾🔧', true],
- ['👩🏾🔬', true],
- ['👩🏾🚀', true],
- ['👩🏾🚒', true],
- ['👩🏾🤝👨🏻', true],
- ['👩🏾🤝👨🏼', true],
- ['👩🏾🤝👨🏽', true],
- ['👩🏾🤝👨🏿', true],
- ['👩🏾🤝👩🏻', true],
- ['👩🏾🤝👩🏼', true],
- ['👩🏾🤝👩🏽', true],
- ['👩🏾🦯', true],
- ['👩🏾🦰', true],
- ['👩🏾🦱', true],
- ['👩🏾🦲', true],
- ['👩🏾🦳', true],
- ['👩🏾🦼', true],
- ['👩🏾🦽', true],
- ['👩🏾⚕️', true],
- ['👩🏾⚖️', true],
- ['👩🏾✈️', true],
- ['👩🏾', true],
- ['👩🏿🌾', true],
- ['👩🏿🍳', true],
- ['👩🏿🎓', true],
- ['👩🏿🎤', true],
- ['👩🏿🎨', true],
- ['👩🏿🏫', true],
- ['👩🏿🏭', true],
- ['👩🏿💻', true],
- ['👩🏿💼', true],
- ['👩🏿🔧', true],
- ['👩🏿🔬', true],
- ['👩🏿🚀', true],
- ['👩🏿🚒', true],
- ['👩🏿🤝👨🏻', true],
- ['👩🏿🤝👨🏼', true],
- ['👩🏿🤝👨🏽', true],
- ['👩🏿🤝👨🏾', true],
- ['👩🏿🤝👩🏻', true],
- ['👩🏿🤝👩🏼', true],
- ['👩🏿🤝👩🏽', true],
- ['👩🏿🤝👩🏾', true],
- ['👩🏿🦯', true],
- ['👩🏿🦰', true],
- ['👩🏿🦱', true],
- ['👩🏿🦲', true],
- ['👩🏿🦳', true],
- ['👩🏿🦼', true],
- ['👩🏿🦽', true],
- ['👩🏿⚕️', true],
- ['👩🏿⚖️', true],
- ['👩🏿✈️', true],
- ['👩🏿', true],
- ['👩🌾', true],
- ['👩🍳', true],
- ['👩🎓', true],
- ['👩🎤', true],
- ['👩🎨', true],
- ['👩🏫', true],
- ['👩🏭', true],
- ['👩👦👦', true],
- ['👩👦', true],
- ['👩👧👦', true],
- ['👩👧👧', true],
- ['👩👧', true],
- ['👩👩👦👦', true],
- ['👩👩👦', true],
- ['👩👩👧👦', true],
- ['👩👩👧👧', true],
- ['👩👩👧', true],
- ['👩💻', true],
- ['👩💼', true],
- ['👩🔧', true],
- ['👩🔬', true],
- ['👩🚀', true],
- ['👩🚒', true],
- ['👩🦯', true],
- ['👩🦰', true],
- ['👩🦱', true],
- ['👩🦲', true],
- ['👩🦳', true],
- ['👩🦼', true],
- ['👩🦽', true],
- ['👩⚕️', true],
- ['👩⚖️', true],
- ['👩✈️', true],
- ['👩❤️👨', true],
- ['👩❤️👩', true],
- ['👩❤️💋👨', true],
- ['👩❤️💋👩', true],
- ['👩', true],
- ['👪', true],
- ['👫🏻', true],
- ['👫🏼', true],
- ['👫🏽', true],
- ['👫🏾', true],
- ['👫🏿', true],
- ['👫', true],
- ['👬🏻', true],
- ['👬🏼', true],
- ['👬🏽', true],
- ['👬🏾', true],
- ['👬🏿', true],
- ['👬', true],
- ['👭🏻', true],
- ['👭🏼', true],
- ['👭🏽', true],
- ['👭🏾', true],
- ['👭🏿', true],
- ['👭', true],
- ['👮🏻♀️', true],
- ['👮🏻♂️', true],
- ['👮🏻', true],
- ['👮🏼♀️', true],
- ['👮🏼♂️', true],
- ['👮🏼', true],
- ['👮🏽♀️', true],
- ['👮🏽♂️', true],
- ['👮🏽', true],
- ['👮🏾♀️', true],
- ['👮🏾♂️', true],
- ['👮🏾', true],
- ['👮🏿♀️', true],
- ['👮🏿♂️', true],
- ['👮🏿', true],
- ['👮♀️', true],
- ['👮♂️', true],
- ['👮', true],
- ['👯♀️', true],
- ['👯♂️', true],
- ['👯', true],
- ['👰🏻', true],
- ['👰🏼', true],
- ['👰🏽', true],
- ['👰🏾', true],
- ['👰🏿', true],
- ['👰', true],
- ['👱🏻♀️', true],
- ['👱🏻♂️', true],
- ['👱🏻', true],
- ['👱🏼♀️', true],
- ['👱🏼♂️', true],
- ['👱🏼', true],
- ['👱🏽♀️', true],
- ['👱🏽♂️', true],
- ['👱🏽', true],
- ['👱🏾♀️', true],
- ['👱🏾♂️', true],
- ['👱🏾', true],
- ['👱🏿♀️', true],
- ['👱🏿♂️', true],
- ['👱🏿', true],
- ['👱♀️', true],
- ['👱♂️', true],
- ['👱', true],
- ['👲🏻', true],
- ['👲🏼', true],
- ['👲🏽', true],
- ['👲🏾', true],
- ['👲🏿', true],
- ['👲', true],
- ['👳🏻♀️', true],
- ['👳🏻♂️', true],
- ['👳🏻', true],
- ['👳🏼♀️', true],
- ['👳🏼♂️', true],
- ['👳🏼', true],
- ['👳🏽♀️', true],
- ['👳🏽♂️', true],
- ['👳🏽', true],
- ['👳🏾♀️', true],
- ['👳🏾♂️', true],
- ['👳🏾', true],
- ['👳🏿♀️', true],
- ['👳🏿♂️', true],
- ['👳🏿', true],
- ['👳♀️', true],
- ['👳♂️', true],
- ['👳', true],
- ['👴🏻', true],
- ['👴🏼', true],
- ['👴🏽', true],
- ['👴🏾', true],
- ['👴🏿', true],
- ['👴', true],
- ['👵🏻', true],
- ['👵🏼', true],
- ['👵🏽', true],
- ['👵🏾', true],
- ['👵🏿', true],
- ['👵', true],
- ['👶🏻', true],
- ['👶🏼', true],
- ['👶🏽', true],
- ['👶🏾', true],
- ['👶🏿', true],
- ['👶', true],
- ['👷🏻♀️', true],
- ['👷🏻♂️', true],
- ['👷🏻', true],
- ['👷🏼♀️', true],
- ['👷🏼♂️', true],
- ['👷🏼', true],
- ['👷🏽♀️', true],
- ['👷🏽♂️', true],
- ['👷🏽', true],
- ['👷🏾♀️', true],
- ['👷🏾♂️', true],
- ['👷🏾', true],
- ['👷🏿♀️', true],
- ['👷🏿♂️', true],
- ['👷🏿', true],
- ['👷♀️', true],
- ['👷♂️', true],
- ['👷', true],
- ['👸🏻', true],
- ['👸🏼', true],
- ['👸🏽', true],
- ['👸🏾', true],
- ['👸🏿', true],
- ['👸', true],
- ['👹', true],
- ['👺', true],
- ['👻', true],
- ['👼🏻', true],
- ['👼🏼', true],
- ['👼🏽', true],
- ['👼🏾', true],
- ['👼🏿', true],
- ['👼', true],
- ['👽', true],
- ['👾', true],
- ['👿', true],
- ['💀', true],
- ['💁🏻♀️', true],
- ['💁🏻♂️', true],
- ['💁🏻', true],
- ['💁🏼♀️', true],
- ['💁🏼♂️', true],
- ['💁🏼', true],
- ['💁🏽♀️', true],
- ['💁🏽♂️', true],
- ['💁🏽', true],
- ['💁🏾♀️', true],
- ['💁🏾♂️', true],
- ['💁🏾', true],
- ['💁🏿♀️', true],
- ['💁🏿♂️', true],
- ['💁🏿', true],
- ['💁♀️', true],
- ['💁♂️', true],
- ['💁', true],
- ['💂🏻♀️', true],
- ['💂🏻♂️', true],
- ['💂🏻', true],
- ['💂🏼♀️', true],
- ['💂🏼♂️', true],
- ['💂🏼', true],
- ['💂🏽♀️', true],
- ['💂🏽♂️', true],
- ['💂🏽', true],
- ['💂🏾♀️', true],
- ['💂🏾♂️', true],
- ['💂🏾', true],
- ['💂🏿♀️', true],
- ['💂🏿♂️', true],
- ['💂🏿', true],
- ['💂♀️', true],
- ['💂♂️', true],
- ['💂', true],
- ['💃🏻', true],
- ['💃🏼', true],
- ['💃🏽', true],
- ['💃🏾', true],
- ['💃🏿', true],
- ['💃', true],
- ['💄', true],
- ['💅🏻', true],
- ['💅🏼', true],
- ['💅🏽', true],
- ['💅🏾', true],
- ['💅🏿', true],
- ['💅', true],
- ['💆🏻♀️', true],
- ['💆🏻♂️', true],
- ['💆🏻', true],
- ['💆🏼♀️', true],
- ['💆🏼♂️', true],
- ['💆🏼', true],
- ['💆🏽♀️', true],
- ['💆🏽♂️', true],
- ['💆🏽', true],
- ['💆🏾♀️', true],
- ['💆🏾♂️', true],
- ['💆🏾', true],
- ['💆🏿♀️', true],
- ['💆🏿♂️', true],
- ['💆🏿', true],
- ['💆♀️', true],
- ['💆♂️', true],
- ['💆', true],
- ['💇🏻♀️', true],
- ['💇🏻♂️', true],
- ['💇🏻', true],
- ['💇🏼♀️', true],
- ['💇🏼♂️', true],
- ['💇🏼', true],
- ['💇🏽♀️', true],
- ['💇🏽♂️', true],
- ['💇🏽', true],
- ['💇🏾♀️', true],
- ['💇🏾♂️', true],
- ['💇🏾', true],
- ['💇🏿♀️', true],
- ['💇🏿♂️', true],
- ['💇🏿', true],
- ['💇♀️', true],
- ['💇♂️', true],
- ['💇', true],
- ['💈', true],
- ['💉', true],
- ['💊', true],
- ['💋', true],
- ['💌', true],
- ['💍', true],
- ['💎', true],
- ['💏', true],
- ['💐', true],
- ['💑', true],
- ['💒', true],
- ['💓', true],
- ['💔', true],
- ['💕', true],
- ['💖', true],
- ['💗', true],
- ['💘', true],
- ['💙', true],
- ['💚', true],
- ['💛', true],
- ['💜', true],
- ['💝', true],
- ['💞', true],
- ['💟', true],
- ['💠', true],
- ['💡', true],
- ['💢', true],
- ['💣', true],
- ['💤', true],
- ['💥', true],
- ['💦', true],
- ['💧', true],
- ['💨', true],
- ['💩', true],
- ['💪🏻', true],
- ['💪🏼', true],
- ['💪🏽', true],
- ['💪🏾', true],
- ['💪🏿', true],
- ['💪', true],
- ['💫', true],
- ['💬', true],
- ['💭', true],
- ['💮', true],
- ['💯', true],
- ['💰', true],
- ['💱', true],
- ['💲', true],
- ['💳', true],
- ['💴', true],
- ['💵', true],
- ['💶', true],
- ['💷', true],
- ['💸', true],
- ['💹', true],
- ['💺', true],
- ['💻', true],
- ['💼', true],
- ['💽', true],
- ['💾', true],
- ['💿', true],
- ['📀', true],
- ['📁', true],
- ['📂', true],
- ['📃', true],
- ['📄', true],
- ['📅', true],
- ['📆', true],
- ['📇', true],
- ['📈', true],
- ['📉', true],
- ['📊', true],
- ['📋', true],
- ['📌', true],
- ['📍', true],
- ['📎', true],
- ['📏', true],
- ['📐', true],
- ['📑', true],
- ['📒', true],
- ['📓', true],
- ['📔', true],
- ['📕', true],
- ['📖', true],
- ['📗', true],
- ['📘', true],
- ['📙', true],
- ['📚', true],
- ['📛', true],
- ['📜', true],
- ['📝', true],
- ['📞', true],
- ['📟', true],
- ['📠', true],
- ['📡', true],
- ['📢', true],
- ['📣', true],
- ['📤', true],
- ['📥', true],
- ['📦', true],
- ['📧', true],
- ['📨', true],
- ['📩', true],
- ['📪', true],
- ['📫', true],
- ['📬', true],
- ['📭', true],
- ['📮', true],
- ['📯', true],
- ['📰', true],
- ['📱', true],
- ['📲', true],
- ['📳', true],
- ['📴', true],
- ['📵', true],
- ['📶', true],
- ['📷', true],
- ['📸', true],
- ['📹', true],
- ['📺', true],
- ['📻', true],
- ['📼', true],
- ['📽️', true],
- ['📿', true],
- ['🔀', true],
- ['🔁', true],
- ['🔂', true],
- ['🔃', true],
- ['🔄', true],
- ['🔅', true],
- ['🔆', true],
- ['🔇', true],
- ['🔈', true],
- ['🔉', true],
- ['🔊', true],
- ['🔋', true],
- ['🔌', true],
- ['🔍', true],
- ['🔎', true],
- ['🔏', true],
- ['🔐', true],
- ['🔑', true],
- ['🔒', true],
- ['🔓', true],
- ['🔔', true],
- ['🔕', true],
- ['🔖', true],
- ['🔗', true],
- ['🔘', true],
- ['🔙', true],
- ['🔚', true],
- ['🔛', true],
- ['🔜', true],
- ['🔝', true],
- ['🔞', true],
- ['🔟', true],
- ['🔠', true],
- ['🔡', true],
- ['🔢', true],
- ['🔣', true],
- ['🔤', true],
- ['🔥', true],
- ['🔦', true],
- ['🔧', true],
- ['🔨', true],
- ['🔩', true],
- ['🔪', true],
- ['🔫', true],
- ['🔬', true],
- ['🔭', true],
- ['🔮', true],
- ['🔯', true],
- ['🔰', true],
- ['🔱', true],
- ['🔲', true],
- ['🔳', true],
- ['🔴', true],
- ['🔵', true],
- ['🔶', true],
- ['🔷', true],
- ['🔸', true],
- ['🔹', true],
- ['🔺', true],
- ['🔻', true],
- ['🔼', true],
- ['🔽', true],
- ['🕉️', true],
- ['🕊️', true],
- ['🕋', true],
- ['🕌', true],
- ['🕍', true],
- ['🕎', true],
- ['🕐', true],
- ['🕑', true],
- ['🕒', true],
- ['🕓', true],
- ['🕔', true],
- ['🕕', true],
- ['🕖', true],
- ['🕗', true],
- ['🕘', true],
- ['🕙', true],
- ['🕚', true],
- ['🕛', true],
- ['🕜', true],
- ['🕝', true],
- ['🕞', true],
- ['🕟', true],
- ['🕠', true],
- ['🕡', true],
- ['🕢', true],
- ['🕣', true],
- ['🕤', true],
- ['🕥', true],
- ['🕦', true],
- ['🕧', true],
- ['🕯️', true],
- ['🕰️', true],
- ['🕳️', true],
- ['🕴🏻♀️', true],
- ['🕴🏻♂️', true],
- ['🕴🏻', true],
- ['🕴🏼♀️', true],
- ['🕴🏼♂️', true],
- ['🕴🏼', true],
- ['🕴🏽♀️', true],
- ['🕴🏽♂️', true],
- ['🕴🏽', true],
- ['🕴🏾♀️', true],
- ['🕴🏾♂️', true],
- ['🕴🏾', true],
- ['🕴🏿♀️', true],
- ['🕴🏿♂️', true],
- ['🕴🏿', true],
- ['🕴️♀️', true],
- ['🕴️♂️', true],
- ['🕴️', true],
- ['🕵🏻♀️', true],
- ['🕵🏻♂️', true],
- ['🕵🏻', true],
- ['🕵🏼♀️', true],
- ['🕵🏼♂️', true],
- ['🕵🏼', true],
- ['🕵🏽♀️', true],
- ['🕵🏽♂️', true],
- ['🕵🏽', true],
- ['🕵🏾♀️', true],
- ['🕵🏾♂️', true],
- ['🕵🏾', true],
- ['🕵🏿♀️', true],
- ['🕵🏿♂️', true],
- ['🕵🏿', true],
- ['🕵️♀️', true],
- ['🕵️♂️', true],
- ['🕵️', true],
- ['🕶️', true],
- ['🕷️', true],
- ['🕸️', true],
- ['🕹️', true],
- ['🕺🏻', true],
- ['🕺🏼', true],
- ['🕺🏽', true],
- ['🕺🏾', true],
- ['🕺🏿', true],
- ['🕺', true],
- ['🖇️', true],
- ['🖊️', true],
- ['🖋️', true],
- ['🖌️', true],
- ['🖍️', true],
- ['🖐🏻', true],
- ['🖐🏼', true],
- ['🖐🏽', true],
- ['🖐🏾', true],
- ['🖐🏿', true],
- ['🖐️', true],
- ['🖕🏻', true],
- ['🖕🏼', true],
- ['🖕🏽', true],
- ['🖕🏾', true],
- ['🖕🏿', true],
- ['🖕', true],
- ['🖖🏻', true],
- ['🖖🏼', true],
- ['🖖🏽', true],
- ['🖖🏾', true],
- ['🖖🏿', true],
- ['🖖', true],
- ['🖤', true],
- ['🖥️', true],
- ['🖨️', true],
- ['🖱️', true],
- ['🖲️', true],
- ['🖼️', true],
- ['🗂️', true],
- ['🗃️', true],
- ['🗄️', true],
- ['🗑️', true],
- ['🗒️', true],
- ['🗓️', true],
- ['🗜️', true],
- ['🗝️', true],
- ['🗞️', true],
- ['🗡️', true],
- ['🗣️', true],
- ['🗨️', true],
- ['🗯️', true],
- ['🗳️', true],
- ['🗺️', true],
- ['🗻', true],
- ['🗼', true],
- ['🗽', true],
- ['🗾', true],
- ['🗿', true],
- ['😀', true],
- ['😁', true],
- ['😂', true],
- ['😃', true],
- ['😄', true],
- ['😅', true],
- ['😆', true],
- ['😇', true],
- ['😈', true],
- ['😉', true],
- ['😊', true],
- ['😋', true],
- ['😌', true],
- ['😍', true],
- ['😎', true],
- ['😏', true],
- ['😐', true],
- ['😑', true],
- ['😒', true],
- ['😓', true],
- ['😔', true],
- ['😕', true],
- ['😖', true],
- ['😗', true],
- ['😘', true],
- ['😙', true],
- ['😚', true],
- ['😛', true],
- ['😜', true],
- ['😝', true],
- ['😞', true],
- ['😟', true],
- ['😠', true],
- ['😡', true],
- ['😢', true],
- ['😣', true],
- ['😤', true],
- ['😥', true],
- ['😦', true],
- ['😧', true],
- ['😨', true],
- ['😩', true],
- ['😪', true],
- ['😫', true],
- ['😬', true],
- ['😭', true],
- ['😮', true],
- ['😯', true],
- ['😰', true],
- ['😱', true],
- ['😲', true],
- ['😳', true],
- ['😴', true],
- ['😵', true],
- ['😶', true],
- ['😷', true],
- ['😸', true],
- ['😹', true],
- ['😺', true],
- ['😻', true],
- ['😼', true],
- ['😽', true],
- ['😾', true],
- ['😿', true],
- ['🙀', true],
- ['🙁', true],
- ['🙂', true],
- ['🙃', true],
- ['🙄', true],
- ['🙅🏻♀️', true],
- ['🙅🏻♂️', true],
- ['🙅🏻', true],
- ['🙅🏼♀️', true],
- ['🙅🏼♂️', true],
- ['🙅🏼', true],
- ['🙅🏽♀️', true],
- ['🙅🏽♂️', true],
- ['🙅🏽', true],
- ['🙅🏾♀️', true],
- ['🙅🏾♂️', true],
- ['🙅🏾', true],
- ['🙅🏿♀️', true],
- ['🙅🏿♂️', true],
- ['🙅🏿', true],
- ['🙅♀️', true],
- ['🙅♂️', true],
- ['🙅', true],
- ['🙆🏻♀️', true],
- ['🙆🏻♂️', true],
- ['🙆🏻', true],
- ['🙆🏼♀️', true],
- ['🙆🏼♂️', true],
- ['🙆🏼', true],
- ['🙆🏽♀️', true],
- ['🙆🏽♂️', true],
- ['🙆🏽', true],
- ['🙆🏾♀️', true],
- ['🙆🏾♂️', true],
- ['🙆🏾', true],
- ['🙆🏿♀️', true],
- ['🙆🏿♂️', true],
- ['🙆🏿', true],
- ['🙆♀️', true],
- ['🙆♂️', true],
- ['🙆', true],
- ['🙇🏻♀️', true],
- ['🙇🏻♂️', true],
- ['🙇🏻', true],
- ['🙇🏼♀️', true],
- ['🙇🏼♂️', true],
- ['🙇🏼', true],
- ['🙇🏽♀️', true],
- ['🙇🏽♂️', true],
- ['🙇🏽', true],
- ['🙇🏾♀️', true],
- ['🙇🏾♂️', true],
- ['🙇🏾', true],
- ['🙇🏿♀️', true],
- ['🙇🏿♂️', true],
- ['🙇🏿', true],
- ['🙇♀️', true],
- ['🙇♂️', true],
- ['🙇', true],
- ['🙈', true],
- ['🙉', true],
- ['🙊', true],
- ['🙋🏻♀️', true],
- ['🙋🏻♂️', true],
- ['🙋🏻', true],
- ['🙋🏼♀️', true],
- ['🙋🏼♂️', true],
- ['🙋🏼', true],
- ['🙋🏽♀️', true],
- ['🙋🏽♂️', true],
- ['🙋🏽', true],
- ['🙋🏾♀️', true],
- ['🙋🏾♂️', true],
- ['🙋🏾', true],
- ['🙋🏿♀️', true],
- ['🙋🏿♂️', true],
- ['🙋🏿', true],
- ['🙋♀️', true],
- ['🙋♂️', true],
- ['🙋', true],
- ['🙌🏻', true],
- ['🙌🏼', true],
- ['🙌🏽', true],
- ['🙌🏾', true],
- ['🙌🏿', true],
- ['🙌', true],
- ['🙍🏻♀️', true],
- ['🙍🏻♂️', true],
- ['🙍🏻', true],
- ['🙍🏼♀️', true],
- ['🙍🏼♂️', true],
- ['🙍🏼', true],
- ['🙍🏽♀️', true],
- ['🙍🏽♂️', true],
- ['🙍🏽', true],
- ['🙍🏾♀️', true],
- ['🙍🏾♂️', true],
- ['🙍🏾', true],
- ['🙍🏿♀️', true],
- ['🙍🏿♂️', true],
- ['🙍🏿', true],
- ['🙍♀️', true],
- ['🙍♂️', true],
- ['🙍', true],
- ['🙎🏻♀️', true],
- ['🙎🏻♂️', true],
- ['🙎🏻', true],
- ['🙎🏼♀️', true],
- ['🙎🏼♂️', true],
- ['🙎🏼', true],
- ['🙎🏽♀️', true],
- ['🙎🏽♂️', true],
- ['🙎🏽', true],
- ['🙎🏾♀️', true],
- ['🙎🏾♂️', true],
- ['🙎🏾', true],
- ['🙎🏿♀️', true],
- ['🙎🏿♂️', true],
- ['🙎🏿', true],
- ['🙎♀️', true],
- ['🙎♂️', true],
- ['🙎', true],
- ['🙏🏻', true],
- ['🙏🏼', true],
- ['🙏🏽', true],
- ['🙏🏾', true],
- ['🙏🏿', true],
- ['🙏', true],
- ['🚀', true],
- ['🚁', true],
- ['🚂', true],
- ['🚃', true],
- ['🚄', true],
- ['🚅', true],
- ['🚆', true],
- ['🚇', true],
- ['🚈', true],
- ['🚉', true],
- ['🚊', true],
- ['🚋', true],
- ['🚌', true],
- ['🚍', true],
- ['🚎', true],
- ['🚏', true],
- ['🚐', true],
- ['🚑', true],
- ['🚒', true],
- ['🚓', true],
- ['🚔', true],
- ['🚕', true],
- ['🚖', true],
- ['🚗', true],
- ['🚘', true],
- ['🚙', true],
- ['🚚', true],
- ['🚛', true],
- ['🚜', true],
- ['🚝', true],
- ['🚞', true],
- ['🚟', true],
- ['🚠', true],
- ['🚡', true],
- ['🚢', true],
- ['🚣🏻♀️', true],
- ['🚣🏻♂️', true],
- ['🚣🏻', true],
- ['🚣🏼♀️', true],
- ['🚣🏼♂️', true],
- ['🚣🏼', true],
- ['🚣🏽♀️', true],
- ['🚣🏽♂️', true],
- ['🚣🏽', true],
- ['🚣🏾♀️', true],
- ['🚣🏾♂️', true],
- ['🚣🏾', true],
- ['🚣🏿♀️', true],
- ['🚣🏿♂️', true],
- ['🚣🏿', true],
- ['🚣♀️', true],
- ['🚣♂️', true],
- ['🚣', true],
- ['🚤', true],
- ['🚥', true],
- ['🚦', true],
- ['🚧', true],
- ['🚨', true],
- ['🚩', true],
- ['🚪', true],
- ['🚫', true],
- ['🚬', true],
- ['🚭', true],
- ['🚮', true],
- ['🚯', true],
- ['🚰', true],
- ['🚱', true],
- ['🚲', true],
- ['🚳', true],
- ['🚴🏻♀️', true],
- ['🚴🏻♂️', true],
- ['🚴🏻', true],
- ['🚴🏼♀️', true],
- ['🚴🏼♂️', true],
- ['🚴🏼', true],
- ['🚴🏽♀️', true],
- ['🚴🏽♂️', true],
- ['🚴🏽', true],
- ['🚴🏾♀️', true],
- ['🚴🏾♂️', true],
- ['🚴🏾', true],
- ['🚴🏿♀️', true],
- ['🚴🏿♂️', true],
- ['🚴🏿', true],
- ['🚴♀️', true],
- ['🚴♂️', true],
- ['🚴', true],
- ['🚵🏻♀️', true],
- ['🚵🏻♂️', true],
- ['🚵🏻', true],
- ['🚵🏼♀️', true],
- ['🚵🏼♂️', true],
- ['🚵🏼', true],
- ['🚵🏽♀️', true],
- ['🚵🏽♂️', true],
- ['🚵🏽', true],
- ['🚵🏾♀️', true],
- ['🚵🏾♂️', true],
- ['🚵🏾', true],
- ['🚵🏿♀️', true],
- ['🚵🏿♂️', true],
- ['🚵🏿', true],
- ['🚵♀️', true],
- ['🚵♂️', true],
- ['🚵', true],
- ['🚶🏻♀️', true],
- ['🚶🏻♂️', true],
- ['🚶🏻', true],
- ['🚶🏼♀️', true],
- ['🚶🏼♂️', true],
- ['🚶🏼', true],
- ['🚶🏽♀️', true],
- ['🚶🏽♂️', true],
- ['🚶🏽', true],
- ['🚶🏾♀️', true],
- ['🚶🏾♂️', true],
- ['🚶🏾', true],
- ['🚶🏿♀️', true],
- ['🚶🏿♂️', true],
- ['🚶🏿', true],
- ['🚶♀️', true],
- ['🚶♂️', true],
- ['🚶', true],
- ['🚷', true],
- ['🚸', true],
- ['🚹', true],
- ['🚺', true],
- ['🚻', true],
- ['🚼', true],
- ['🚽', true],
- ['🚾', true],
- ['🚿', true],
- ['🛀🏻', true],
- ['🛀🏼', true],
- ['🛀🏽', true],
- ['🛀🏾', true],
- ['🛀🏿', true],
- ['🛀', true],
- ['🛁', true],
- ['🛂', true],
- ['🛃', true],
- ['🛄', true],
- ['🛅', true],
- ['🛋️', true],
- ['🛌🏻', true],
- ['🛌🏼', true],
- ['🛌🏽', true],
- ['🛌🏾', true],
- ['🛌🏿', true],
- ['🛌', true],
- ['🛍️', true],
- ['🛎️', true],
- ['🛏️', true],
- ['🛐', true],
- ['🛑', true],
- ['🛒', true],
- ['🛕', true],
- ['🛠️', true],
- ['🛡️', true],
- ['🛢️', true],
- ['🛣️', true],
- ['🛤️', true],
- ['🛥️', true],
- ['🛩️', true],
- ['🛫', true],
- ['🛬', true],
- ['🛰️', true],
- ['🛳️', true],
- ['🛴', true],
- ['🛵', true],
- ['🛶', true],
- ['🛷', true],
- ['🛸', true],
- ['🛹', true],
- ['🛺', true],
- ['🟠', true],
- ['🟡', true],
- ['🟢', true],
- ['🟣', true],
- ['🟤', true],
- ['🟥', true],
- ['🟦', true],
- ['🟧', true],
- ['🟨', true],
- ['🟩', true],
- ['🟪', true],
- ['🟫', true],
- ['🤍', true],
- ['🤎', true],
- ['🤏🏻', true],
- ['🤏🏼', true],
- ['🤏🏽', true],
- ['🤏🏾', true],
- ['🤏🏿', true],
- ['🤏', true],
- ['🤐', true],
- ['🤑', true],
- ['🤒', true],
- ['🤓', true],
- ['🤔', true],
- ['🤕', true],
- ['🤖', true],
- ['🤗', true],
- ['🤘🏻', true],
- ['🤘🏼', true],
- ['🤘🏽', true],
- ['🤘🏾', true],
- ['🤘🏿', true],
- ['🤘', true],
- ['🤙🏻', true],
- ['🤙🏼', true],
- ['🤙🏽', true],
- ['🤙🏾', true],
- ['🤙🏿', true],
- ['🤙', true],
- ['🤚🏻', true],
- ['🤚🏼', true],
- ['🤚🏽', true],
- ['🤚🏾', true],
- ['🤚🏿', true],
- ['🤚', true],
- ['🤛🏻', true],
- ['🤛🏼', true],
- ['🤛🏽', true],
- ['🤛🏾', true],
- ['🤛🏿', true],
- ['🤛', true],
- ['🤜🏻', true],
- ['🤜🏼', true],
- ['🤜🏽', true],
- ['🤜🏾', true],
- ['🤜🏿', true],
- ['🤜', true],
- ['🤝', true],
- ['🤞🏻', true],
- ['🤞🏼', true],
- ['🤞🏽', true],
- ['🤞🏾', true],
- ['🤞🏿', true],
- ['🤞', true],
- ['🤟🏻', true],
- ['🤟🏼', true],
- ['🤟🏽', true],
- ['🤟🏾', true],
- ['🤟🏿', true],
- ['🤟', true],
- ['🤠', true],
- ['🤡', true],
- ['🤢', true],
- ['🤣', true],
- ['🤤', true],
- ['🤥', true],
- ['🤦🏻♀️', true],
- ['🤦🏻♂️', true],
- ['🤦🏻', true],
- ['🤦🏼♀️', true],
- ['🤦🏼♂️', true],
- ['🤦🏼', true],
- ['🤦🏽♀️', true],
- ['🤦🏽♂️', true],
- ['🤦🏽', true],
- ['🤦🏾♀️', true],
- ['🤦🏾♂️', true],
- ['🤦🏾', true],
- ['🤦🏿♀️', true],
- ['🤦🏿♂️', true],
- ['🤦🏿', true],
- ['🤦♀️', true],
- ['🤦♂️', true],
- ['🤦', true],
- ['🤧', true],
- ['🤨', true],
- ['🤩', true],
- ['🤪', true],
- ['🤫', true],
- ['🤬', true],
- ['🤭', true],
- ['🤮', true],
- ['🤯', true],
- ['🤰🏻', true],
- ['🤰🏼', true],
- ['🤰🏽', true],
- ['🤰🏾', true],
- ['🤰🏿', true],
- ['🤰', true],
- ['🤱🏻', true],
- ['🤱🏼', true],
- ['🤱🏽', true],
- ['🤱🏾', true],
- ['🤱🏿', true],
- ['🤱', true],
- ['🤲🏻', true],
- ['🤲🏼', true],
- ['🤲🏽', true],
- ['🤲🏾', true],
- ['🤲🏿', true],
- ['🤲', true],
- ['🤳🏻', true],
- ['🤳🏼', true],
- ['🤳🏽', true],
- ['🤳🏾', true],
- ['🤳🏿', true],
- ['🤳', true],
- ['🤴🏻', true],
- ['🤴🏼', true],
- ['🤴🏽', true],
- ['🤴🏾', true],
- ['🤴🏿', true],
- ['🤴', true],
- ['🤵🏻♀️', true],
- ['🤵🏻♂️', true],
- ['🤵🏻', true],
- ['🤵🏼♀️', true],
- ['🤵🏼♂️', true],
- ['🤵🏼', true],
- ['🤵🏽♀️', true],
- ['🤵🏽♂️', true],
- ['🤵🏽', true],
- ['🤵🏾♀️', true],
- ['🤵🏾♂️', true],
- ['🤵🏾', true],
- ['🤵🏿♀️', true],
- ['🤵🏿♂️', true],
- ['🤵🏿', true],
- ['🤵♀️', true],
- ['🤵♂️', true],
- ['🤵', true],
- ['🤶🏻', true],
- ['🤶🏼', true],
- ['🤶🏽', true],
- ['🤶🏾', true],
- ['🤶🏿', true],
- ['🤶', true],
- ['🤷🏻♀️', true],
- ['🤷🏻♂️', true],
- ['🤷🏻', true],
- ['🤷🏼♀️', true],
- ['🤷🏼♂️', true],
- ['🤷🏼', true],
- ['🤷🏽♀️', true],
- ['🤷🏽♂️', true],
- ['🤷🏽', true],
- ['🤷🏾♀️', true],
- ['🤷🏾♂️', true],
- ['🤷🏾', true],
- ['🤷🏿♀️', true],
- ['🤷🏿♂️', true],
- ['🤷🏿', true],
- ['🤷♀️', true],
- ['🤷♂️', true],
- ['🤷', true],
- ['🤸🏻♀️', true],
- ['🤸🏻♂️', true],
- ['🤸🏻', true],
- ['🤸🏼♀️', true],
- ['🤸🏼♂️', true],
- ['🤸🏼', true],
- ['🤸🏽♀️', true],
- ['🤸🏽♂️', true],
- ['🤸🏽', true],
- ['🤸🏾♀️', true],
- ['🤸🏾♂️', true],
- ['🤸🏾', true],
- ['🤸🏿♀️', true],
- ['🤸🏿♂️', true],
- ['🤸🏿', true],
- ['🤸♀️', true],
- ['🤸♂️', true],
- ['🤸', true],
- ['🤹🏻♀️', true],
- ['🤹🏻♂️', true],
- ['🤹🏻', true],
- ['🤹🏼♀️', true],
- ['🤹🏼♂️', true],
- ['🤹🏼', true],
- ['🤹🏽♀️', true],
- ['🤹🏽♂️', true],
- ['🤹🏽', true],
- ['🤹🏾♀️', true],
- ['🤹🏾♂️', true],
- ['🤹🏾', true],
- ['🤹🏿♀️', true],
- ['🤹🏿♂️', true],
- ['🤹🏿', true],
- ['🤹♀️', true],
- ['🤹♂️', true],
- ['🤹', true],
- ['🤺', true],
- ['🤼♀️', true],
- ['🤼♂️', true],
- ['🤼', true],
- ['🤽🏻♀️', true],
- ['🤽🏻♂️', true],
- ['🤽🏻', true],
- ['🤽🏼♀️', true],
- ['🤽🏼♂️', true],
- ['🤽🏼', true],
- ['🤽🏽♀️', true],
- ['🤽🏽♂️', true],
- ['🤽🏽', true],
- ['🤽🏾♀️', true],
- ['🤽🏾♂️', true],
- ['🤽🏾', true],
- ['🤽🏿♀️', true],
- ['🤽🏿♂️', true],
- ['🤽🏿', true],
- ['🤽♀️', true],
- ['🤽♂️', true],
- ['🤽', true],
- ['🤾🏻♀️', true],
- ['🤾🏻♂️', true],
- ['🤾🏻', true],
- ['🤾🏼♀️', true],
- ['🤾🏼♂️', true],
- ['🤾🏼', true],
- ['🤾🏽♀️', true],
- ['🤾🏽♂️', true],
- ['🤾🏽', true],
- ['🤾🏾♀️', true],
- ['🤾🏾♂️', true],
- ['🤾🏾', true],
- ['🤾🏿♀️', true],
- ['🤾🏿♂️', true],
- ['🤾🏿', true],
- ['🤾♀️', true],
- ['🤾♂️', true],
- ['🤾', true],
- ['🤿', true],
- ['🥀', true],
- ['🥁', true],
- ['🥂', true],
- ['🥃', true],
- ['🥄', true],
- ['🥅', true],
- ['🥇', true],
- ['🥈', true],
- ['🥉', true],
- ['🥊', true],
- ['🥋', true],
- ['🥌', true],
- ['🥍', true],
- ['🥎', true],
- ['🥏', true],
- ['🥐', true],
- ['🥑', true],
- ['🥒', true],
- ['🥓', true],
- ['🥔', true],
- ['🥕', true],
- ['🥖', true],
- ['🥗', true],
- ['🥘', true],
- ['🥙', true],
- ['🥚', true],
- ['🥛', true],
- ['🥜', true],
- ['🥝', true],
- ['🥞', true],
- ['🥟', true],
- ['🥠', true],
- ['🥡', true],
- ['🥢', true],
- ['🥣', true],
- ['🥤', true],
- ['🥥', true],
- ['🥦', true],
- ['🥧', true],
- ['🥨', true],
- ['🥩', true],
- ['🥪', true],
- ['🥫', true],
- ['🥬', true],
- ['🥭', true],
- ['🥮', true],
- ['🥯', true],
- ['🥰', true],
- ['🥱', true],
- ['🥳', true],
- ['🥴', true],
- ['🥵', true],
- ['🥶', true],
- ['🥺', true],
- ['🥻', true],
- ['🥼', true],
- ['🥽', true],
- ['🥾', true],
- ['🥿', true],
- ['🦀', true],
- ['🦁', true],
- ['🦂', true],
- ['🦃', true],
- ['🦄', true],
- ['🦅', true],
- ['🦆', true],
- ['🦇', true],
- ['🦈', true],
- ['🦉', true],
- ['🦊', true],
- ['🦋', true],
- ['🦌', true],
- ['🦍', true],
- ['🦎', true],
- ['🦏', true],
- ['🦐', true],
- ['🦑', true],
- ['🦒', true],
- ['🦓', true],
- ['🦔', true],
- ['🦕', true],
- ['🦖', true],
- ['🦗', true],
- ['🦘', true],
- ['🦙', true],
- ['🦚', true],
- ['🦛', true],
- ['🦜', true],
- ['🦝', true],
- ['🦞', true],
- ['🦟', true],
- ['🦠', true],
- ['🦡', true],
- ['🦢', true],
- ['🦥', true],
- ['🦦', true],
- ['🦧', true],
- ['🦨', true],
- ['🦩', true],
- ['🦪', true],
- ['🦮', true],
- ['🦯', true],
- ['🦰', true],
- ['🦱', true],
- ['🦲', true],
- ['🦳', true],
- ['🦴', true],
- ['🦵🏻', true],
- ['🦵🏼', true],
- ['🦵🏽', true],
- ['🦵🏾', true],
- ['🦵🏿', true],
- ['🦵', true],
- ['🦶🏻', true],
- ['🦶🏼', true],
- ['🦶🏽', true],
- ['🦶🏾', true],
- ['🦶🏿', true],
- ['🦶', true],
- ['🦷', true],
- ['🦸🏻♀️', true],
- ['🦸🏻♂️', true],
- ['🦸🏻', true],
- ['🦸🏼♀️', true],
- ['🦸🏼♂️', true],
- ['🦸🏼', true],
- ['🦸🏽♀️', true],
- ['🦸🏽♂️', true],
- ['🦸🏽', true],
- ['🦸🏾♀️', true],
- ['🦸🏾♂️', true],
- ['🦸🏾', true],
- ['🦸🏿♀️', true],
- ['🦸🏿♂️', true],
- ['🦸🏿', true],
- ['🦸♀️', true],
- ['🦸♂️', true],
- ['🦸', true],
- ['🦹🏻♀️', true],
- ['🦹🏻♂️', true],
- ['🦹🏻', true],
- ['🦹🏼♀️', true],
- ['🦹🏼♂️', true],
- ['🦹🏼', true],
- ['🦹🏽♀️', true],
- ['🦹🏽♂️', true],
- ['🦹🏽', true],
- ['🦹🏾♀️', true],
- ['🦹🏾♂️', true],
- ['🦹🏾', true],
- ['🦹🏿♀️', true],
- ['🦹🏿♂️', true],
- ['🦹🏿', true],
- ['🦹♀️', true],
- ['🦹♂️', true],
- ['🦹', true],
- ['🦺', true],
- ['🦻🏻', true],
- ['🦻🏼', true],
- ['🦻🏽', true],
- ['🦻🏾', true],
- ['🦻🏿', true],
- ['🦻', true],
- ['🦼', true],
- ['🦽', true],
- ['🦾', true],
- ['🦿', true],
- ['🧀', true],
- ['🧁', true],
- ['🧂', true],
- ['🧃', true],
- ['🧄', true],
- ['🧅', true],
- ['🧆', true],
- ['🧇', true],
- ['🧈', true],
- ['🧉', true],
- ['🧊', true],
- ['🧍🏻♀️', true],
- ['🧍🏻♂️', true],
- ['🧍🏻', true],
- ['🧍🏼♀️', true],
- ['🧍🏼♂️', true],
- ['🧍🏼', true],
- ['🧍🏽♀️', true],
- ['🧍🏽♂️', true],
- ['🧍🏽', true],
- ['🧍🏾♀️', true],
- ['🧍🏾♂️', true],
- ['🧍🏾', true],
- ['🧍🏿♀️', true],
- ['🧍🏿♂️', true],
- ['🧍🏿', true],
- ['🧍♀️', true],
- ['🧍♂️', true],
- ['🧍', true],
- ['🧎🏻♀️', true],
- ['🧎🏻♂️', true],
- ['🧎🏻', true],
- ['🧎🏼♀️', true],
- ['🧎🏼♂️', true],
- ['🧎🏼', true],
- ['🧎🏽♀️', true],
- ['🧎🏽♂️', true],
- ['🧎🏽', true],
- ['🧎🏾♀️', true],
- ['🧎🏾♂️', true],
- ['🧎🏾', true],
- ['🧎🏿♀️', true],
- ['🧎🏿♂️', true],
- ['🧎🏿', true],
- ['🧎♀️', true],
- ['🧎♂️', true],
- ['🧎', true],
- ['🧏🏻♀️', true],
- ['🧏🏻♂️', true],
- ['🧏🏻', true],
- ['🧏🏼♀️', true],
- ['🧏🏼♂️', true],
- ['🧏🏼', true],
- ['🧏🏽♀️', true],
- ['🧏🏽♂️', true],
- ['🧏🏽', true],
- ['🧏🏾♀️', true],
- ['🧏🏾♂️', true],
- ['🧏🏾', true],
- ['🧏🏿♀️', true],
- ['🧏🏿♂️', true],
- ['🧏🏿', true],
- ['🧏♀️', true],
- ['🧏♂️', true],
- ['🧏', true],
- ['🧐', true],
- ['🧑🏻🤝🧑🏻', true],
- ['🧑🏻', true],
- ['🧑🏼🤝🧑🏻', true],
- ['🧑🏼🤝🧑🏼', true],
- ['🧑🏼', true],
- ['🧑🏽🤝🧑🏻', true],
- ['🧑🏽🤝🧑🏼', true],
- ['🧑🏽🤝🧑🏽', true],
- ['🧑🏽', true],
- ['🧑🏾🤝🧑🏻', true],
- ['🧑🏾🤝🧑🏼', true],
- ['🧑🏾🤝🧑🏽', true],
- ['🧑🏾🤝🧑🏾', true],
- ['🧑🏾', true],
- ['🧑🏿🤝🧑🏻', true],
- ['🧑🏿🤝🧑🏼', true],
- ['🧑🏿🤝🧑🏽', true],
- ['🧑🏿🤝🧑🏾', true],
- ['🧑🏿🤝🧑🏿', true],
- ['🧑🏿', true],
- ['🧑🤝🧑', true],
- ['🧑', true],
- ['🧒🏻', true],
- ['🧒🏼', true],
- ['🧒🏽', true],
- ['🧒🏾', true],
- ['🧒🏿', true],
- ['🧒', true],
- ['🧓🏻', true],
- ['🧓🏼', true],
- ['🧓🏽', true],
- ['🧓🏾', true],
- ['🧓🏿', true],
- ['🧓', true],
- ['🧔🏻', true],
- ['🧔🏼', true],
- ['🧔🏽', true],
- ['🧔🏾', true],
- ['🧔🏿', true],
- ['🧔', true],
- ['🧕🏻', true],
- ['🧕🏼', true],
- ['🧕🏽', true],
- ['🧕🏾', true],
- ['🧕🏿', true],
- ['🧕', true],
- ['🧖🏻♀️', true],
- ['🧖🏻♂️', true],
- ['🧖🏻', true],
- ['🧖🏼♀️', true],
- ['🧖🏼♂️', true],
- ['🧖🏼', true],
- ['🧖🏽♀️', true],
- ['🧖🏽♂️', true],
- ['🧖🏽', true],
- ['🧖🏾♀️', true],
- ['🧖🏾♂️', true],
- ['🧖🏾', true],
- ['🧖🏿♀️', true],
- ['🧖🏿♂️', true],
- ['🧖🏿', true],
- ['🧖♀️', true],
- ['🧖♂️', true],
- ['🧖', true],
- ['🧗🏻♀️', true],
- ['🧗🏻♂️', true],
- ['🧗🏻', true],
- ['🧗🏼♀️', true],
- ['🧗🏼♂️', true],
- ['🧗🏼', true],
- ['🧗🏽♀️', true],
- ['🧗🏽♂️', true],
- ['🧗🏽', true],
- ['🧗🏾♀️', true],
- ['🧗🏾♂️', true],
- ['🧗🏾', true],
- ['🧗🏿♀️', true],
- ['🧗🏿♂️', true],
- ['🧗🏿', true],
- ['🧗♀️', true],
- ['🧗♂️', true],
- ['🧗', true],
- ['🧘🏻♀️', true],
- ['🧘🏻♂️', true],
- ['🧘🏻', true],
- ['🧘🏼♀️', true],
- ['🧘🏼♂️', true],
- ['🧘🏼', true],
- ['🧘🏽♀️', true],
- ['🧘🏽♂️', true],
- ['🧘🏽', true],
- ['🧘🏾♀️', true],
- ['🧘🏾♂️', true],
- ['🧘🏾', true],
- ['🧘🏿♀️', true],
- ['🧘🏿♂️', true],
- ['🧘🏿', true],
- ['🧘♀️', true],
- ['🧘♂️', true],
- ['🧘', true],
- ['🧙🏻♀️', true],
- ['🧙🏻♂️', true],
- ['🧙🏻', true],
- ['🧙🏼♀️', true],
- ['🧙🏼♂️', true],
- ['🧙🏼', true],
- ['🧙🏽♀️', true],
- ['🧙🏽♂️', true],
- ['🧙🏽', true],
- ['🧙🏾♀️', true],
- ['🧙🏾♂️', true],
- ['🧙🏾', true],
- ['🧙🏿♀️', true],
- ['🧙🏿♂️', true],
- ['🧙🏿', true],
- ['🧙♀️', true],
- ['🧙♂️', true],
- ['🧙', true],
- ['🧚🏻♀️', true],
- ['🧚🏻♂️', true],
- ['🧚🏻', true],
- ['🧚🏼♀️', true],
- ['🧚🏼♂️', true],
- ['🧚🏼', true],
- ['🧚🏽♀️', true],
- ['🧚🏽♂️', true],
- ['🧚🏽', true],
- ['🧚🏾♀️', true],
- ['🧚🏾♂️', true],
- ['🧚🏾', true],
- ['🧚🏿♀️', true],
- ['🧚🏿♂️', true],
- ['🧚🏿', true],
- ['🧚♀️', true],
- ['🧚♂️', true],
- ['🧚', true],
- ['🧛🏻♀️', true],
- ['🧛🏻♂️', true],
- ['🧛🏻', true],
- ['🧛🏼♀️', true],
- ['🧛🏼♂️', true],
- ['🧛🏼', true],
- ['🧛🏽♀️', true],
- ['🧛🏽♂️', true],
- ['🧛🏽', true],
- ['🧛🏾♀️', true],
- ['🧛🏾♂️', true],
- ['🧛🏾', true],
- ['🧛🏿♀️', true],
- ['🧛🏿♂️', true],
- ['🧛🏿', true],
- ['🧛♀️', true],
- ['🧛♂️', true],
- ['🧛', true],
- ['🧜🏻♀️', true],
- ['🧜🏻♂️', true],
- ['🧜🏻', true],
- ['🧜🏼♀️', true],
- ['🧜🏼♂️', true],
- ['🧜🏼', true],
- ['🧜🏽♀️', true],
- ['🧜🏽♂️', true],
- ['🧜🏽', true],
- ['🧜🏾♀️', true],
- ['🧜🏾♂️', true],
- ['🧜🏾', true],
- ['🧜🏿♀️', true],
- ['🧜🏿♂️', true],
- ['🧜🏿', true],
- ['🧜♀️', true],
- ['🧜♂️', true],
- ['🧜', true],
- ['🧝🏻♀️', true],
- ['🧝🏻♂️', true],
- ['🧝🏻', true],
- ['🧝🏼♀️', true],
- ['🧝🏼♂️', true],
- ['🧝🏼', true],
- ['🧝🏽♀️', true],
- ['🧝🏽♂️', true],
- ['🧝🏽', true],
- ['🧝🏾♀️', true],
- ['🧝🏾♂️', true],
- ['🧝🏾', true],
- ['🧝🏿♀️', true],
- ['🧝🏿♂️', true],
- ['🧝🏿', true],
- ['🧝♀️', true],
- ['🧝♂️', true],
- ['🧝', true],
- ['🧞♀️', true],
- ['🧞♂️', true],
- ['🧞', true],
- ['🧟♀️', true],
- ['🧟♂️', true],
- ['🧟', true],
- ['🧠', true],
- ['🧡', true],
- ['🧢', true],
- ['🧣', true],
- ['🧤', true],
- ['🧥', true],
- ['🧦', true],
- ['🧧', true],
- ['🧨', true],
- ['🧩', true],
- ['🧪', true],
- ['🧫', true],
- ['🧬', true],
- ['🧭', true],
- ['🧮', true],
- ['🧯', true],
- ['🧰', true],
- ['🧱', true],
- ['🧲', true],
- ['🧳', true],
- ['🧴', true],
- ['🧵', true],
- ['🧶', true],
- ['🧷', true],
- ['🧸', true],
- ['🧹', true],
- ['🧺', true],
- ['🧻', true],
- ['🧼', true],
- ['🧽', true],
- ['🧾', true],
- ['🧿', true],
- ['🩰', true],
- ['🩱', true],
- ['🩲', true],
- ['🩳', true],
- ['🩸', true],
- ['🩹', true],
- ['🩺', true],
- ['🪀', true],
- ['🪁', true],
- ['🪂', true],
- ['🪐', true],
- ['🪑', true],
- ['🪒', true],
- ['🪓', true],
- ['🪔', true],
- ['🪕', true],
- ['‼️', true],
- ['⁉️', true],
- ['™️', true],
- ['ℹ️', true],
- ['↔️', true],
- ['↕️', true],
- ['↖️', true],
- ['↗️', true],
- ['↘️', true],
- ['↙️', true],
- ['↩️', true],
- ['↪️', true],
- ['#⃣', true],
- ['⌚️', true],
- ['⌛️', true],
- ['⌨️', true],
- ['⏏️', true],
- ['⏩', true],
- ['⏪', true],
- ['⏫', true],
- ['⏬', true],
- ['⏭️', true],
- ['⏮️', true],
- ['⏯️', true],
- ['⏰', true],
- ['⏱️', true],
- ['⏲️', true],
- ['⏳', true],
- ['⏸️', true],
- ['⏹️', true],
- ['⏺️', true],
- ['Ⓜ️', true],
- ['▪️', true],
- ['▫️', true],
- ['▶️', true],
- ['◀️', true],
- ['◻️', true],
- ['◼️', true],
- ['◽️', true],
- ['◾️', true],
- ['☀️', true],
- ['☁️', true],
- ['☂️', true],
- ['☃️', true],
- ['☄️', true],
- ['☎️', true],
- ['☑️', true],
- ['☔️', true],
- ['☕️', true],
- ['☘️', true],
- ['☝🏻', true],
- ['☝🏼', true],
- ['☝🏽', true],
- ['☝🏾', true],
- ['☝🏿', true],
- ['☝️', true],
- ['☠️', true],
- ['☢️', true],
- ['☣️', true],
- ['☦️', true],
- ['☪️', true],
- ['☮️', true],
- ['☯️', true],
- ['☸️', true],
- ['☹️', true],
- ['☺️', true],
- ['♀️', true],
- ['♂️', true],
- ['♈️', true],
- ['♉️', true],
- ['♊️', true],
- ['♋️', true],
- ['♌️', true],
- ['♍️', true],
- ['♎️', true],
- ['♏️', true],
- ['♐️', true],
- ['♑️', true],
- ['♒️', true],
- ['♓️', true],
- ['♟️', true],
- ['♠️', true],
- ['♣️', true],
- ['♥️', true],
- ['♦️', true],
- ['♨️', true],
- ['♻️', true],
- ['♾', true],
- ['♿️', true],
- ['⚒️', true],
- ['⚓️', true],
- ['⚔️', true],
- ['⚕️', true],
- ['⚖️', true],
- ['⚗️', true],
- ['⚙️', true],
- ['⚛️', true],
- ['⚜️', true],
- ['⚠️', true],
- ['⚡️', true],
- ['⚪️', true],
- ['⚫️', true],
- ['⚰️', true],
- ['⚱️', true],
- ['⚽️', true],
- ['⚾️', true],
- ['⛄️', true],
- ['⛅️', true],
- ['⛈️', true],
- ['⛎', true],
- ['⛏️', true],
- ['⛑️', true],
- ['⛓️', true],
- ['⛔️', true],
- ['⛩️', true],
- ['⛪️', true],
- ['⛰️', true],
- ['⛱️', true],
- ['⛲️', true],
- ['⛳️', true],
- ['⛴️', true],
- ['⛵️', true],
- ['⛷🏻', true],
- ['⛷🏼', true],
- ['⛷🏽', true],
- ['⛷🏾', true],
- ['⛷🏿', true],
- ['⛷️', true],
- ['⛸️', true],
- ['⛹🏻♀️', true],
- ['⛹🏻♂️', true],
- ['⛹🏻', true],
- ['⛹🏼♀️', true],
- ['⛹🏼♂️', true],
- ['⛹🏼', true],
- ['⛹🏽♀️', true],
- ['⛹🏽♂️', true],
- ['⛹🏽', true],
- ['⛹🏾♀️', true],
- ['⛹🏾♂️', true],
- ['⛹🏾', true],
- ['⛹🏿♀️', true],
- ['⛹🏿♂️', true],
- ['⛹🏿', true],
- ['⛹️♀️', true],
- ['⛹️♂️', true],
- ['⛹️', true],
- ['⛺️', true],
- ['⛽️', true],
- ['✂️', true],
- ['✅', true],
- ['✈️', true],
- ['✉️', true],
- ['✊🏻', true],
- ['✊🏼', true],
- ['✊🏽', true],
- ['✊🏾', true],
- ['✊🏿', true],
- ['✊', true],
- ['✋🏻', true],
- ['✋🏼', true],
- ['✋🏽', true],
- ['✋🏾', true],
- ['✋🏿', true],
- ['✋', true],
- ['✌🏻', true],
- ['✌🏼', true],
- ['✌🏽', true],
- ['✌🏾', true],
- ['✌🏿', true],
- ['✌️', true],
- ['✍🏻', true],
- ['✍🏼', true],
- ['✍🏽', true],
- ['✍🏾', true],
- ['✍🏿', true],
- ['✍️', true],
- ['✏️', true],
- ['✒️', true],
- ['✔️', true],
- ['✖️', true],
- ['✝️', true],
- ['✡️', true],
- ['✨', true],
- ['✳️', true],
- ['✴️', true],
- ['❄️', true],
- ['❇️', true],
- ['❌', true],
- ['❎', true],
- ['❓', true],
- ['❔', true],
- ['❕', true],
- ['❗️', true],
- ['❣️', true],
- ['❤️', true],
- ['➕', true],
- ['➖', true],
- ['➗', true],
- ['➡️', true],
- ['➰', true],
- ['➿', true],
- ['⤴️', true],
- ['⤵️', true],
- ['*⃣', true],
- ['⬅️', true],
- ['⬆️', true],
- ['⬇️', true],
- ['⬛️', true],
- ['⬜️', true],
- ['⭐️', true],
- ['⭕️', true],
- ['0⃣', true],
- ['〰️', true],
- ['〽️', true],
- ['1⃣', true],
- ['2⃣', true],
- ['㊗️', true],
- ['㊙️', true],
- ['3⃣', true],
- ['4⃣', true],
- ['5⃣', true],
- ['6⃣', true],
- ['7⃣', true],
- ['8⃣', true],
- ['9⃣', true],
- ['©️', true],
- ['®️', true],
- ];
- }
-}
--- a/tests/lib/Encryption/DecryptAllTest.php
+++ /dev/null
@@ -1,386 +0,0 @@
-userManager = $this->getMockBuilder(IUserManager::class)
- ->disableOriginalConstructor()->getMock();
- $this->encryptionManager = $this->getMockBuilder('OC\Encryption\Manager')
- ->disableOriginalConstructor()->getMock();
- $this->view = $this->getMockBuilder(View::class)
- ->disableOriginalConstructor()->getMock();
- $this->inputInterface = $this->getMockBuilder(InputInterface::class)
- ->disableOriginalConstructor()->getMock();
- $this->outputInterface = $this->getMockBuilder(OutputInterface::class)
- ->disableOriginalConstructor()->getMock();
- $this->outputInterface->expects($this->any())->method('isDecorated')
- ->willReturn(false);
- $this->userInterface = $this->getMockBuilder(UserInterface::class)
- ->disableOriginalConstructor()->getMock();
-
- /* We need format method to return a string */
- $outputFormatter = $this->createMock(OutputFormatterInterface::class);
- $outputFormatter->method('format')->willReturn('foo');
- $outputFormatter->method('isDecorated')->willReturn(false);
-
- $this->outputInterface->expects($this->any())->method('getFormatter')
- ->willReturn($outputFormatter);
-
- $this->instance = new DecryptAll($this->encryptionManager, $this->userManager, $this->view);
-
- $this->invokePrivate($this->instance, 'input', [$this->inputInterface]);
- $this->invokePrivate($this->instance, 'output', [$this->outputInterface]);
- }
-
- public static function dataDecryptAll(): array {
- return [
- [true, 'user1', true],
- [false, 'user1', true],
- [true, '0', true],
- [false, '0', true],
- [true, '', false],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataDecryptAll')]
- public function testDecryptAll(bool $prepareResult, string $user, bool $userExistsChecked): void {
- if ($userExistsChecked) {
- $this->userManager->expects($this->once())->method('userExists')->willReturn(true);
- } else {
- $this->userManager->expects($this->never())->method('userExists');
- }
- /** @var DecryptAll&MockObject $instance */
- $instance = $this->getMockBuilder('OC\Encryption\DecryptAll')
- ->setConstructorArgs(
- [
- $this->encryptionManager,
- $this->userManager,
- $this->view
- ]
- )
- ->onlyMethods(['prepareEncryptionModules', 'decryptAllUsersFiles'])
- ->getMock();
-
- $instance->expects($this->once())
- ->method('prepareEncryptionModules')
- ->with($this->inputInterface, $this->outputInterface, $user)
- ->willReturn($prepareResult);
-
- if ($prepareResult) {
- $instance->expects($this->once())
- ->method('decryptAllUsersFiles')
- ->with($this->outputInterface, $user);
- } else {
- $instance->expects($this->never())->method('decryptAllUsersFiles');
- }
-
- $instance->decryptAll($this->inputInterface, $this->outputInterface, $user);
- }
-
- /**
- * test decrypt all call with a user who doesn't exists
- */
- public function testDecryptAllWrongUser(): void {
- $this->userManager->expects($this->once())->method('userExists')->willReturn(false);
- $this->outputInterface->expects($this->once())->method('writeln')
- ->with('User "user1" does not exist. Please check the username and try again');
-
- $this->assertFalse(
- $this->instance->decryptAll($this->inputInterface, $this->outputInterface, 'user1')
- );
- }
-
- public static function dataTrueFalse(): array {
- return [
- [true],
- [false],
- ];
- }
-
- /**
- * @param bool $success
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTrueFalse')]
- public function testPrepareEncryptionModules($success): void {
- $user = 'user1';
-
- $dummyEncryptionModule = $this->getMockBuilder('OCP\Encryption\IEncryptionModule')
- ->disableOriginalConstructor()->getMock();
-
- $dummyEncryptionModule->expects($this->once())
- ->method('prepareDecryptAll')
- ->with($this->inputInterface, $this->outputInterface, $user)
- ->willReturn($success);
-
- $callback = function () use ($dummyEncryptionModule) {
- return $dummyEncryptionModule;
- };
- $moduleDescription = [
- 'id' => 'id',
- 'displayName' => 'displayName',
- 'callback' => $callback
- ];
-
- $this->encryptionManager->expects($this->once())
- ->method('getEncryptionModules')
- ->willReturn([$moduleDescription]);
-
- $this->assertSame($success,
- $this->invokePrivate($this->instance, 'prepareEncryptionModules', [$this->inputInterface, $this->outputInterface, $user])
- );
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestDecryptAllUsersFiles')]
- public function testDecryptAllUsersFiles($user): void {
- /** @var DecryptAll&MockObject $instance */
- $instance = $this->getMockBuilder('OC\Encryption\DecryptAll')
- ->setConstructorArgs(
- [
- $this->encryptionManager,
- $this->userManager,
- $this->view
- ]
- )
- ->onlyMethods(['decryptUsersFiles'])
- ->getMock();
-
- if (empty($user)) {
- $this->userManager->expects($this->once())
- ->method('getBackends')
- ->willReturn([$this->userInterface]);
- $this->userInterface->expects($this->any())
- ->method('getUsers')
- ->willReturn(['user1', 'user2']);
- $calls = [
- 'user1',
- 'user2',
- ];
- $instance->expects($this->exactly(2))
- ->method('decryptUsersFiles')
- ->willReturnCallback(function ($user) use (&$calls): void {
- $expected = array_shift($calls);
- $this->assertEquals($expected, $user);
- });
- } else {
- $instance->expects($this->once())
- ->method('decryptUsersFiles')
- ->with($user);
- }
-
- $this->invokePrivate($instance, 'decryptAllUsersFiles', [$this->outputInterface, $user]);
- }
-
- public static function dataTestDecryptAllUsersFiles(): array {
- return [
- ['user1'],
- ['']
- ];
- }
-
- public function testDecryptUsersFiles(): void {
- /** @var DecryptAll | \PHPUnit\Framework\MockObject\MockObject $instance */
- $instance = $this->getMockBuilder('OC\Encryption\DecryptAll')
- ->setConstructorArgs(
- [
- $this->encryptionManager,
- $this->userManager,
- $this->view
- ]
- )
- ->onlyMethods(['decryptFile'])
- ->getMock();
-
- $storage = $this->getMockBuilder(IStorage::class)
- ->disableOriginalConstructor()->getMock();
-
- $sharedStorage = $this->getMockBuilder(IStorage::class)
- ->disableOriginalConstructor()->getMock();
-
- $sharedStorage->expects($this->once())
- ->method('instanceOfStorage')
- ->with('OCA\Files_Sharing\SharedStorage')
- ->willReturn(true);
-
- $this->view->expects($this->exactly(2))
- ->method('getDirectoryContent')
- ->willReturnMap([
- [
- '/user1/files', null, null,
- [
- new FileInfo('path', $storage, 'intPath', ['name' => 'foo', 'type' => 'dir'], null),
- new FileInfo('path', $storage, 'intPath', ['name' => 'bar', 'type' => 'file', 'encrypted' => true], null),
- new FileInfo('path', $sharedStorage, 'intPath', ['name' => 'shared', 'type' => 'file', 'encrypted' => true], null),
- ],
- ],
- [
- '/user1/files/foo', null, null,
- [
- new FileInfo('path', $storage, 'intPath', ['name' => 'subfile', 'type' => 'file', 'encrypted' => true], null)
- ],
- ],
- ]);
-
- $this->view->expects($this->any())->method('is_dir')
- ->willReturnCallback(
- function ($path) {
- if ($path === '/user1/files/foo') {
- return true;
- }
- return false;
- }
- );
-
- $calls = [
- '/user1/files/bar',
- '/user1/files/foo/subfile',
- ];
- $instance->expects($this->exactly(2))
- ->method('decryptFile')
- ->willReturnCallback(function ($path) use (&$calls): bool {
- $expected = array_shift($calls);
- $this->assertEquals($expected, $path);
- return true;
- });
-
- /* We need format method to return a string */
- $outputFormatter = $this->createMock(OutputFormatterInterface::class);
- $outputFormatter->method('isDecorated')->willReturn(false);
- $outputFormatter->method('format')->willReturn('foo');
-
- $output = $this->createMock(OutputInterface::class);
- $output->expects($this->any())
- ->method('getFormatter')
- ->willReturn($outputFormatter);
- $progressBar = new ProgressBar($output);
-
- $this->invokePrivate($instance, 'decryptUsersFiles', ['user1', $progressBar, '']);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTrueFalse')]
- public function testDecryptFile($isEncrypted): void {
- $path = 'test.txt';
-
- /** @var DecryptAll&MockObject $instance */
- $instance = $this->getMockBuilder('OC\Encryption\DecryptAll')
- ->setConstructorArgs(
- [
- $this->encryptionManager,
- $this->userManager,
- $this->view
- ]
- )
- ->onlyMethods(['getTimestamp'])
- ->getMock();
-
- $fileInfo = $this->createMock(FileInfo::class);
- $fileInfo->expects($this->any())->method('isEncrypted')
- ->willReturn($isEncrypted);
- $this->view->expects($this->any())->method('getFileInfo')
- ->willReturn($fileInfo);
-
- if ($isEncrypted) {
- $instance->expects($this->any())->method('getTimestamp')->willReturn(42);
-
- $this->view->expects($this->once())
- ->method('copy')
- ->with($path, $path . '.decrypted.42');
- $this->view->expects($this->once())
- ->method('rename')
- ->with($path . '.decrypted.42', $path);
- } else {
- $instance->expects($this->never())->method('getTimestamp');
- $this->view->expects($this->never())->method('copy');
- $this->view->expects($this->never())->method('rename');
- }
- $this->assertTrue(
- $this->invokePrivate($instance, 'decryptFile', [$path])
- );
- }
-
- public function testDecryptFileFailure(): void {
- $path = 'test.txt';
-
- /** @var DecryptAll&MockObject $instance */
- $instance = $this->getMockBuilder('OC\Encryption\DecryptAll')
- ->setConstructorArgs(
- [
- $this->encryptionManager,
- $this->userManager,
- $this->view
- ]
- )
- ->onlyMethods(['getTimestamp'])
- ->getMock();
-
- $fileInfo = $this->createMock(FileInfo::class);
- $fileInfo->expects($this->any())->method('isEncrypted')
- ->willReturn(true);
- $this->view->expects($this->any())->method('getFileInfo')
- ->willReturn($fileInfo);
-
- $instance->expects($this->any())->method('getTimestamp')->willReturn(42);
-
- $this->view->expects($this->once())
- ->method('copy')
- ->with($path, $path . '.decrypted.42')
- ->willReturnCallback(function (): void {
- throw new DecryptionFailedException();
- });
-
- $this->view->expects($this->never())->method('rename');
- $this->view->expects($this->once())
- ->method('file_exists')
- ->with($path . '.decrypted.42')
- ->willReturn(true);
- $this->view->expects($this->once())
- ->method('unlink')
- ->with($path . '.decrypted.42');
-
- $this->assertFalse(
- $this->invokePrivate($instance, 'decryptFile', [$path])
- );
- }
-}
--- a/tests/lib/Encryption/EncryptionWrapperTest.php
+++ /dev/null
@@ -1,78 +0,0 @@
-arrayCache = $this->createMock(ArrayCache::class);
- $this->manager = $this->createMock(Manager::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $this->instance = new EncryptionWrapper($this->arrayCache, $this->manager, $this->logger);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('provideWrapStorage')]
- public function testWrapStorage($expectedWrapped, $wrappedStorages): void {
- $storage = $this->getMockBuilder(Storage::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $storage->expects($this->any())
- ->method('instanceOfStorage')
- ->willReturnCallback(fn (string $storage): bool => in_array($storage, $wrappedStorages, true));
-
- $mount = $this->getMockBuilder(IMountPoint::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $returnedStorage = $this->instance->wrapStorage('mountPoint', $storage, $mount);
-
- $this->assertEquals(
- $expectedWrapped,
- $returnedStorage->instanceOfStorage(Encryption::class),
- 'Asserted that the storage is (not) wrapped with encryption'
- );
- }
-
- public static function provideWrapStorage(): array {
- return [
- // Wrap when not wrapped or not wrapped with storage
- [true, []],
- [true, [Storage::class]],
-
- // Do not wrap shared storages
- [false, [IDisableEncryptionStorage::class]],
- ];
- }
-}
--- a/tests/lib/Encryption/FileTest.php
+++ /dev/null
@@ -1,156 +0,0 @@
-util = $this->createMock(Util::class);
- $this->shareManager = $this->createMock(IManager::class);
- $this->userFolder = $this->createMock(Folder::class);
-
- $rootFolder = $this->createMock(IRootFolder::class);
- $rootFolder->method('getUserFolder')
- ->with('user1')
- ->willReturn($this->userFolder);
-
- $appManager = $this->createMock(IAppManager::class);
- $appManager->method('isEnabledForUser')
- ->willReturn(false);
-
- $this->file = $this->getMockBuilder(File::class)
- ->setConstructorArgs([$this->util, $rootFolder, $this->shareManager])
- ->onlyMethods(['getAppManager'])
- ->getMock();
- $this->file->method('getAppManager')
- ->willReturn($appManager);
- }
-
- /**
- * Let the util mock resolve every path to $owner and $ownerPath
- */
- private function mockUtil(string $owner, string $ownerPath): void {
- $this->util->method('getUidAndFilename')
- ->willReturn([$owner, $ownerPath]);
- $this->util->method('isFile')
- ->willReturn(true);
- $this->util->method('stripPartialFileExtension')
- ->willReturnArgument(0);
- }
-
- public function testGetAccessList(): void {
- $this->mockUtil('user1', '/files/folder/file.txt');
-
- $node = $this->createMock(Node::class);
- $parentNode = $this->createMock(Node::class);
- $this->userFolder->method('get')
- ->willReturnMap([
- ['/folder/file.txt', $node],
- ['/folder', $parentNode],
- ]);
-
- $this->shareManager->method('getAccessList')
- ->willReturnCallback(fn (Node $requested) => match ($requested) {
- $parentNode => ['users' => ['user2'], 'public' => false, 'remote' => false],
- $node => ['users' => ['user3'], 'public' => true, 'remote' => false],
- });
-
- $this->assertSame(
- ['users' => ['user1', 'user2', 'user3'], 'public' => true],
- $this->file->getAccessList('/user1/files/folder/file.txt')
- );
- }
-
- /**
- * Copying a folder creates the target directories on the storage before their
- * cache entries exist, so the parent of a file written into it can not be resolved.
- */
- public function testGetAccessListWithUncachedParent(): void {
- $this->mockUtil('user1', '/files/target/sub');
-
- $rootNode = $this->createMock(Node::class);
- $this->userFolder->method('get')
- ->willReturnCallback(function (string $path) use ($rootNode) {
- if ($path !== '/') {
- throw new NotFoundException($path);
- }
- return $rootNode;
- });
-
- $this->shareManager->expects($this->once())
- ->method('getAccessList')
- ->with($rootNode)
- ->willReturn(['users' => ['user2'], 'public' => false, 'remote' => false]);
-
- $this->assertSame(
- ['users' => ['user1', 'user2'], 'public' => false],
- $this->file->getAccessList('/user1/files/target/sub')
- );
- }
-
- public function testGetAccessListWithoutAnyResolvablePath(): void {
- $this->mockUtil('user1', '/files/target/sub');
-
- $this->userFolder->method('get')
- ->willThrowException(new NotFoundException());
-
- $this->shareManager->expects($this->never())
- ->method('getAccessList');
-
- $this->assertSame(
- ['users' => ['user1'], 'public' => false],
- $this->file->getAccessList('/user1/files/target/sub')
- );
- }
-
- public function testGetAccessListCachesTheParentResult(): void {
- $this->util->method('getUidAndFilename')
- ->willReturnCallback(fn (string $path) => ['user1', substr($path, strlen('/user1'))]);
- $this->util->method('isFile')
- ->willReturn(true);
- $this->util->method('stripPartialFileExtension')
- ->willReturnArgument(0);
-
- $parentNode = $this->createMock(Node::class);
- $this->userFolder->method('get')
- ->willReturnCallback(function (string $path) use ($parentNode) {
- if ($path !== '/folder') {
- throw new NotFoundException($path);
- }
- return $parentNode;
- });
-
- $this->shareManager->expects($this->once())
- ->method('getAccessList')
- ->with($parentNode)
- ->willReturn(['users' => ['user2'], 'public' => false, 'remote' => false]);
-
- $expected = ['users' => ['user1', 'user2'], 'public' => false];
- $this->assertSame($expected, $this->file->getAccessList('/user1/files/folder/first.txt'));
- $this->assertSame($expected, $this->file->getAccessList('/user1/files/folder/second.txt'));
- }
-}
--- a/tests/lib/Encryption/Keys/StorageTest.php
+++ /dev/null
@@ -1,769 +0,0 @@
-util = $this->getMockBuilder(Util::class)
- ->disableOriginalConstructor()
- ->onlyMethods(array_diff(get_class_methods(Util::class), ['getFileKeyDir']))
- ->getMock();
-
- $this->view = $this->getMockBuilder(View::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->crypto = $this->createMock(ICrypto::class);
- $this->crypto->method('encrypt')
- ->willReturnCallback(function ($data, $pass) {
- return $data;
- });
- $this->crypto->method('decrypt')
- ->willReturnCallback(function ($data, $pass) {
- return $data;
- });
-
- $this->config = $this->getMockBuilder(IConfig::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->storage = new Storage($this->view, $this->util, $this->crypto, $this->config);
- }
-
- public function testSetFileKey(): void {
- $this->config->method('getSystemValueString')
- ->with('version')
- ->willReturn('20.0.0.2');
- $this->util->expects($this->any())
- ->method('getUidAndFilename')
- ->willReturn(['user1', '/files/foo.txt']);
- $this->util->expects($this->any())
- ->method('stripPartialFileExtension')
- ->willReturnArgument(0);
- $this->util->expects($this->any())
- ->method('isSystemWideMountPoint')
- ->willReturn(false);
-
- $data = json_encode(['key' => base64_encode('key')]);
- $this->view->expects($this->once())
- ->method('file_put_contents')
- ->with($this->equalTo('/user1/files_encryption/keys/files/foo.txt/encModule/fileKey'),
- $this->equalTo($data))
- ->willReturn(strlen($data));
-
- $this->assertTrue(
- $this->storage->setFileKey('user1/files/foo.txt', 'fileKey', 'key', 'encModule')
- );
- }
-
- public function testSetFileOld(): void {
- $this->config->method('getSystemValueString')
- ->with('version')
- ->willReturn('20.0.0.0');
- $this->util->expects($this->any())
- ->method('getUidAndFilename')
- ->willReturn(['user1', '/files/foo.txt']);
- $this->util->expects($this->any())
- ->method('stripPartialFileExtension')
- ->willReturnArgument(0);
- $this->util->expects($this->any())
- ->method('isSystemWideMountPoint')
- ->willReturn(false);
- $this->crypto->expects($this->never())
- ->method('encrypt');
- $this->view->expects($this->once())
- ->method('file_put_contents')
- ->with($this->equalTo('/user1/files_encryption/keys/files/foo.txt/encModule/fileKey'),
- $this->equalTo('key'))
- ->willReturn(strlen('key'));
-
- $this->assertTrue(
- $this->storage->setFileKey('user1/files/foo.txt', 'fileKey', 'key', 'encModule')
- );
- }
-
- public static function dataTestGetFileKey() {
- return [
- ['/files/foo.txt', '/files/foo.txt', true, 'key'],
- ['/files/foo.txt.ocTransferId2111130212.part', '/files/foo.txt', true, 'key'],
- ['/files/foo.txt.ocTransferId2111130212.part', '/files/foo.txt', false, 'key2'],
- ];
- }
-
- /**
- *
- * @param string $path
- * @param string $strippedPartialName
- * @param bool $originalKeyExists
- * @param string $expectedKeyContent
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetFileKey')]
- public function testGetFileKey($path, $strippedPartialName, $originalKeyExists, $expectedKeyContent): void {
- $this->config->method('getSystemValueString')
- ->with('version')
- ->willReturn('20.0.0.2');
- $this->util->expects($this->any())
- ->method('getUidAndFilename')
- ->willReturnMap([
- ['user1/files/foo.txt', ['user1', '/files/foo.txt']],
- ['user1/files/foo.txt.ocTransferId2111130212.part', ['user1', '/files/foo.txt.ocTransferId2111130212.part']],
- ]);
- // we need to strip away the part file extension in order to reuse a
- // existing key if it exists, otherwise versions will break
- $this->util->expects($this->once())
- ->method('stripPartialFileExtension')
- ->willReturn('user1' . $strippedPartialName);
- $this->util->expects($this->any())
- ->method('isSystemWideMountPoint')
- ->willReturn(false);
-
- $this->crypto->method('decrypt')
- ->willReturnCallback(function ($data, $pass) {
- return $data;
- });
-
- if (!$originalKeyExists) {
- $this->view->expects($this->exactly(2))
- ->method('file_exists')
- ->willReturnMap([
- ['/user1/files_encryption/keys' . $strippedPartialName . '/encModule/fileKey', $originalKeyExists],
- ['/user1/files_encryption/keys' . $path . '/encModule/fileKey', true],
- ]);
-
- $this->view->expects($this->once())
- ->method('file_get_contents')
- ->with($this->equalTo('/user1/files_encryption/keys' . $path . '/encModule/fileKey'))
- ->willReturn(json_encode(['key' => base64_encode('key2')]));
- } else {
- $this->view->expects($this->once())
- ->method('file_exists')
- ->with($this->equalTo('/user1/files_encryption/keys' . $strippedPartialName . '/encModule/fileKey'))
- ->willReturn($originalKeyExists);
-
- $this->view->expects($this->once())
- ->method('file_get_contents')
- ->with($this->equalTo('/user1/files_encryption/keys' . $strippedPartialName . '/encModule/fileKey'))
- ->willReturn(json_encode(['key' => base64_encode('key')]));
- }
-
- $this->assertSame($expectedKeyContent,
- $this->storage->getFileKey('user1' . $path, 'fileKey', 'encModule')
- );
- }
-
- public function testSetFileKeySystemWide(): void {
- $this->config->method('getSystemValueString')
- ->with('version')
- ->willReturn('20.0.0.2');
-
- $this->util->expects($this->any())
- ->method('getUidAndFilename')
- ->willReturn(['user1', '/files/foo.txt']);
- $this->util->expects($this->any())
- ->method('isSystemWideMountPoint')
- ->willReturn(true);
- $this->util->expects($this->any())
- ->method('stripPartialFileExtension')
- ->willReturnArgument(0);
-
- $this->crypto->method('encrypt')
- ->willReturnCallback(function ($data, $pass) {
- return $data;
- });
-
- $data = json_encode(['key' => base64_encode('key')]);
- $this->view->expects($this->once())
- ->method('file_put_contents')
- ->with($this->equalTo('/files_encryption/keys/files/foo.txt/encModule/fileKey'),
- $this->equalTo($data))
- ->willReturn(strlen($data));
-
- $this->assertTrue(
- $this->storage->setFileKey('user1/files/foo.txt', 'fileKey', 'key', 'encModule')
- );
- }
-
- public function testGetFileKeySystemWide(): void {
- $this->config->method('getSystemValueString')
- ->with('version')
- ->willReturn('20.0.0.2');
-
- $this->util->expects($this->any())
- ->method('getUidAndFilename')
- ->willReturn(['user1', '/files/foo.txt']);
- $this->util->expects($this->any())
- ->method('stripPartialFileExtension')
- ->willReturnArgument(0);
- $this->util->expects($this->any())
- ->method('isSystemWideMountPoint')
- ->willReturn(true);
- $this->view->expects($this->once())
- ->method('file_get_contents')
- ->with($this->equalTo('/files_encryption/keys/files/foo.txt/encModule/fileKey'))
- ->willReturn(json_encode(['key' => base64_encode('key')]));
- $this->view->expects($this->once())
- ->method('file_exists')
- ->with($this->equalTo('/files_encryption/keys/files/foo.txt/encModule/fileKey'))
- ->willReturn(true);
-
- $this->assertSame('key',
- $this->storage->getFileKey('user1/files/foo.txt', 'fileKey', 'encModule')
- );
- }
-
- public function testSetSystemUserKey(): void {
- $this->config->method('getSystemValueString')
- ->with('version')
- ->willReturn('20.0.0.2');
-
- $data = json_encode([
- 'key' => base64_encode('key'),
- 'uid' => null]
- );
- $this->view->expects($this->once())
- ->method('file_put_contents')
- ->with($this->equalTo('/files_encryption/encModule/shareKey_56884'),
- $this->equalTo($data))
- ->willReturn(strlen($data));
-
- $this->assertTrue(
- $this->storage->setSystemUserKey('shareKey_56884', 'key', 'encModule')
- );
- }
-
- public function testSetUserKey(): void {
- $this->config->method('getSystemValueString')
- ->with('version')
- ->willReturn('20.0.0.2');
-
- $data = json_encode([
- 'key' => base64_encode('key'),
- 'uid' => 'user1']
- );
- $this->view->expects($this->once())
- ->method('file_put_contents')
- ->with($this->equalTo('/user1/files_encryption/encModule/user1.publicKey'),
- $this->equalTo($data))
- ->willReturn(strlen($data));
-
- $this->assertTrue(
- $this->storage->setUserKey('user1', 'publicKey', 'key', 'encModule')
- );
- }
-
- public function testGetSystemUserKey(): void {
- $this->config->method('getSystemValueString')
- ->with('version')
- ->willReturn('20.0.0.2');
-
- $data = json_encode([
- 'key' => base64_encode('key'),
- 'uid' => null]
- );
- $this->view->expects($this->once())
- ->method('file_get_contents')
- ->with($this->equalTo('/files_encryption/encModule/shareKey_56884'))
- ->willReturn($data);
- $this->view->expects($this->once())
- ->method('file_exists')
- ->with($this->equalTo('/files_encryption/encModule/shareKey_56884'))
- ->willReturn(true);
-
- $this->assertSame('key',
- $this->storage->getSystemUserKey('shareKey_56884', 'encModule')
- );
- }
-
- public function testGetUserKey(): void {
- $this->config->method('getSystemValueString')
- ->with('version')
- ->willReturn('20.0.0.2');
-
- $data = json_encode([
- 'key' => base64_encode('key'),
- 'uid' => 'user1']
- );
- $this->view->expects($this->once())
- ->method('file_get_contents')
- ->with($this->equalTo('/user1/files_encryption/encModule/user1.publicKey'))
- ->willReturn($data);
- $this->view->expects($this->once())
- ->method('file_exists')
- ->with($this->equalTo('/user1/files_encryption/encModule/user1.publicKey'))
- ->willReturn(true);
-
- $this->assertSame('key',
- $this->storage->getUserKey('user1', 'publicKey', 'encModule')
- );
- }
-
- public function testDeleteUserKey(): void {
- $this->view->expects($this->once())
- ->method('file_exists')
- ->with($this->equalTo('/user1/files_encryption/encModule/user1.publicKey'))
- ->willReturn(true);
- $this->view->expects($this->once())
- ->method('unlink')
- ->with($this->equalTo('/user1/files_encryption/encModule/user1.publicKey'))
- ->willReturn(true);
-
- $this->assertTrue(
- $this->storage->deleteUserKey('user1', 'publicKey', 'encModule')
- );
- }
-
- public function testDeleteSystemUserKey(): void {
- $this->view->expects($this->once())
- ->method('file_exists')
- ->with($this->equalTo('/files_encryption/encModule/shareKey_56884'))
- ->willReturn(true);
- $this->view->expects($this->once())
- ->method('unlink')
- ->with($this->equalTo('/files_encryption/encModule/shareKey_56884'))
- ->willReturn(true);
-
- $this->assertTrue(
- $this->storage->deleteSystemUserKey('shareKey_56884', 'encModule')
- );
- }
-
- public function testDeleteFileKeySystemWide(): void {
- $this->util->expects($this->any())
- ->method('getUidAndFilename')
- ->willReturn(['user1', '/files/foo.txt']);
- $this->util->expects($this->any())
- ->method('stripPartialFileExtension')
- ->willReturnArgument(0);
- $this->util->expects($this->any())
- ->method('isSystemWideMountPoint')
- ->willReturn(true);
- $this->view->expects($this->once())
- ->method('file_exists')
- ->with($this->equalTo('/files_encryption/keys/files/foo.txt/encModule/fileKey'))
- ->willReturn(true);
- $this->view->expects($this->once())
- ->method('unlink')
- ->with($this->equalTo('/files_encryption/keys/files/foo.txt/encModule/fileKey'))
- ->willReturn(true);
-
- $this->assertTrue(
- $this->storage->deleteFileKey('user1/files/foo.txt', 'fileKey', 'encModule')
- );
- }
-
- public function testDeleteFileKey(): void {
- $this->util->expects($this->any())
- ->method('getUidAndFilename')
- ->willReturn(['user1', '/files/foo.txt']);
- $this->util->expects($this->any())
- ->method('stripPartialFileExtension')
- ->willReturnArgument(0);
- $this->util->expects($this->any())
- ->method('isSystemWideMountPoint')
- ->willReturn(false);
- $this->view->expects($this->once())
- ->method('file_exists')
- ->with($this->equalTo('/user1/files_encryption/keys/files/foo.txt/encModule/fileKey'))
- ->willReturn(true);
- $this->view->expects($this->once())
- ->method('unlink')
- ->with($this->equalTo('/user1/files_encryption/keys/files/foo.txt/encModule/fileKey'))
- ->willReturn(true);
-
- $this->assertTrue(
- $this->storage->deleteFileKey('user1/files/foo.txt', 'fileKey', 'encModule')
- );
- }
-
- /**
- * Set up the mocks needed to read file keys for arbitrary paths
- */
- private function mockFileKeyEnvironment(): void {
- $this->config->method('getSystemValueString')
- ->with('version')
- ->willReturn('20.0.0.2');
- $this->config->method('getSystemValueBool')
- ->willReturn(true);
- $this->util->method('getUidAndFilename')
- ->willReturnCallback([$this, 'getUidAndFilenameCallback']);
- $this->util->method('stripPartialFileExtension')
- ->willReturnArgument(0);
- $this->util->method('isSystemWideMountPoint')
- ->willReturn(false);
- $this->view->method('file_exists')
- ->willReturn(true);
- $this->view->method('is_dir')
- ->willReturn(true);
- }
-
- /**
- * Make the view return a dummy key for every path and collect the read paths
- *
- * @param string[] $reads
- */
- private function trackKeyReads(array &$reads, ?string $uid = null): void {
- $this->view->method('file_get_contents')
- ->willReturnCallback(function (string $path) use (&$reads, $uid): string {
- $reads[] = $path;
- return json_encode(['key' => base64_encode('key'), 'uid' => $uid]);
- });
- }
-
- public function testGetFileKeyIsCached(): void {
- $this->mockFileKeyEnvironment();
- $reads = [];
- $this->trackKeyReads($reads);
-
- $this->storage->getFileKey('/user1/files/foo.txt', 'fileKey', 'encModule');
- $this->storage->getFileKey('/user1/files/foo.txt', 'fileKey', 'encModule');
-
- $this->assertSame(['/user1/files_encryption/keys/files/foo.txt/encModule/fileKey'], $reads);
- }
-
- public function testKeyCacheIsCapped(): void {
- $this->mockFileKeyEnvironment();
- $reads = [];
- $this->trackKeyReads($reads);
-
- for ($i = 0; $i < 600; $i++) {
- $this->storage->getFileKey('/user1/files/foo' . $i . '.txt', 'fileKey', 'encModule');
- }
-
- /** @var CappedMemoryCache $keyCache */
- $keyCache = self::invokePrivate($this->storage, 'keyCache');
- $this->assertCount(512, $keyCache->getData());
- }
-
- public function testDeleteFileKeyInvalidatesCache(): void {
- $this->mockFileKeyEnvironment();
- $this->view->method('unlink')->willReturn(true);
- $reads = [];
- $this->trackKeyReads($reads);
-
- $this->storage->getFileKey('/user1/files/foo.txt', 'fileKey', 'encModule');
- $this->assertTrue($this->storage->deleteFileKey('/user1/files/foo.txt', 'fileKey', 'encModule'));
- $reads = [];
-
- $this->storage->getFileKey('/user1/files/foo.txt', 'fileKey', 'encModule');
-
- $this->assertSame(['/user1/files_encryption/keys/files/foo.txt/encModule/fileKey'], $reads);
- }
-
- public function testDeleteAllFileKeysInvalidatesCache(): void {
- $this->mockFileKeyEnvironment();
- $this->view->method('deleteAll')->willReturn(true);
- $reads = [];
- $this->trackKeyReads($reads);
-
- $this->storage->getFileKey('/user1/files/foo.txt', 'fileKey', 'encModule');
- $this->storage->getFileKey('/user1/files/foo.txt', 'otherKey', 'encModule');
- // a sibling sharing the name prefix must stay cached
- $this->storage->getFileKey('/user1/files/foobar.txt', 'fileKey', 'encModule');
- $this->assertTrue($this->storage->deleteAllFileKeys('/user1/files/foo.txt'));
- $reads = [];
-
- $this->storage->getFileKey('/user1/files/foo.txt', 'fileKey', 'encModule');
- $this->storage->getFileKey('/user1/files/foo.txt', 'otherKey', 'encModule');
- $this->storage->getFileKey('/user1/files/foobar.txt', 'fileKey', 'encModule');
-
- $this->assertSame([
- '/user1/files_encryption/keys/files/foo.txt/encModule/fileKey',
- '/user1/files_encryption/keys/files/foo.txt/encModule/otherKey',
- ], $reads);
- }
-
- public function testRenameKeysInvalidatesCache(): void {
- $this->mockFileKeyEnvironment();
- $this->view->method('rename')->willReturn(true);
- $reads = [];
- $this->trackKeyReads($reads);
-
- $this->storage->getFileKey('/user1/files/source.txt', 'fileKey', 'encModule');
- $this->storage->getFileKey('/user1/files/target.txt', 'fileKey', 'encModule');
- $this->assertTrue($this->storage->renameKeys('/user1/files/source.txt', '/user1/files/target.txt'));
- $reads = [];
-
- $this->storage->getFileKey('/user1/files/source.txt', 'fileKey', 'encModule');
- $this->storage->getFileKey('/user1/files/target.txt', 'fileKey', 'encModule');
-
- $this->assertSame([
- '/user1/files_encryption/keys/files/source.txt/encModule/fileKey',
- '/user1/files_encryption/keys/files/target.txt/encModule/fileKey',
- ], $reads);
- }
-
- public function testCopyKeysInvalidatesTargetCache(): void {
- $this->mockFileKeyEnvironment();
- $this->view->method('copy')->willReturn(true);
- $reads = [];
- $this->trackKeyReads($reads);
-
- $this->storage->getFileKey('/user1/files/source.txt', 'fileKey', 'encModule');
- $this->storage->getFileKey('/user1/files/target.txt', 'fileKey', 'encModule');
- $this->assertTrue($this->storage->copyKeys('/user1/files/source.txt', '/user1/files/target.txt'));
- $reads = [];
-
- $this->storage->getFileKey('/user1/files/source.txt', 'fileKey', 'encModule');
- $this->storage->getFileKey('/user1/files/target.txt', 'fileKey', 'encModule');
-
- $this->assertSame([
- '/user1/files_encryption/keys/files/target.txt/encModule/fileKey',
- ], $reads);
- }
-
- public function testDeleteUserKeyInvalidatesCache(): void {
- $this->mockFileKeyEnvironment();
- $this->view->method('unlink')->willReturn(true);
- $reads = [];
- $this->trackKeyReads($reads, 'user1');
-
- $this->storage->getUserKey('user1', 'publicKey', 'encModule');
- $this->assertTrue($this->storage->deleteUserKey('user1', 'publicKey', 'encModule'));
- $reads = [];
-
- $this->storage->getUserKey('user1', 'publicKey', 'encModule');
-
- $this->assertSame(['/user1/files_encryption/encModule/user1.publicKey'], $reads);
- }
-
- public function testDeleteSystemUserKeyInvalidatesCache(): void {
- $this->mockFileKeyEnvironment();
- $this->view->method('unlink')->willReturn(true);
- $reads = [];
- $this->trackKeyReads($reads);
-
- $this->storage->getSystemUserKey('shareKey_56884', 'encModule');
- $this->assertTrue($this->storage->deleteSystemUserKey('shareKey_56884', 'encModule'));
- $reads = [];
-
- $this->storage->getSystemUserKey('shareKey_56884', 'encModule');
-
- $this->assertSame(['/files_encryption/encModule/shareKey_56884'], $reads);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataProviderCopyRename')]
- public function testRenameKeys($source, $target, $systemWideMountSource, $systemWideMountTarget, $expectedSource, $expectedTarget): void {
- $this->view->expects($this->any())
- ->method('file_exists')
- ->willReturn(true);
- $this->view->expects($this->any())
- ->method('is_dir')
- ->willReturn(true);
- $this->view->expects($this->once())
- ->method('rename')
- ->with(
- $this->equalTo($expectedSource),
- $this->equalTo($expectedTarget))
- ->willReturn(true);
- $this->util->expects($this->any())
- ->method('getUidAndFilename')
- ->willReturnCallback([$this, 'getUidAndFilenameCallback']);
- $this->util->expects($this->any())
- ->method('isSystemWideMountPoint')
- ->willReturnCallback(function ($path, $owner) use ($systemWideMountSource, $systemWideMountTarget) {
- if (strpos($path, 'source.txt') !== false) {
- return $systemWideMountSource;
- }
- return $systemWideMountTarget;
- });
-
- $this->storage->renameKeys($source, $target);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataProviderCopyRename')]
- public function testCopyKeys($source, $target, $systemWideMountSource, $systemWideMountTarget, $expectedSource, $expectedTarget): void {
- $this->view->expects($this->any())
- ->method('file_exists')
- ->willReturn(true);
- $this->view->expects($this->any())
- ->method('is_dir')
- ->willReturn(true);
- $this->view->expects($this->once())
- ->method('copy')
- ->with(
- $this->equalTo($expectedSource),
- $this->equalTo($expectedTarget))
- ->willReturn(true);
- $this->util->expects($this->any())
- ->method('getUidAndFilename')
- ->willReturnCallback([$this, 'getUidAndFilenameCallback']);
- $this->util->expects($this->any())
- ->method('isSystemWideMountPoint')
- ->willReturnCallback(function ($path, $owner) use ($systemWideMountSource, $systemWideMountTarget) {
- if (strpos($path, 'source.txt') !== false) {
- return $systemWideMountSource;
- }
- return $systemWideMountTarget;
- });
-
- $this->storage->copyKeys($source, $target);
- }
-
- public function getUidAndFilenameCallback() {
- $args = func_get_args();
-
- $path = $args[0];
- $parts = explode('/', $path);
-
- return [$parts[1], '/' . implode('/', array_slice($parts, 2))];
- }
-
- public static function dataProviderCopyRename() {
- return [
- ['/user1/files/source.txt', '/user1/files/target.txt', false, false,
- '/user1/files_encryption/keys/files/source.txt/', '/user1/files_encryption/keys/files/target.txt/'],
- ['/user1/files/foo/source.txt', '/user1/files/target.txt', false, false,
- '/user1/files_encryption/keys/files/foo/source.txt/', '/user1/files_encryption/keys/files/target.txt/'],
- ['/user1/files/source.txt', '/user1/files/foo/target.txt', false, false,
- '/user1/files_encryption/keys/files/source.txt/', '/user1/files_encryption/keys/files/foo/target.txt/'],
- ['/user1/files/source.txt', '/user1/files/foo/target.txt', true, true,
- '/files_encryption/keys/files/source.txt/', '/files_encryption/keys/files/foo/target.txt/'],
- ['/user1/files/source.txt', '/user1/files/target.txt', false, true,
- '/user1/files_encryption/keys/files/source.txt/', '/files_encryption/keys/files/target.txt/'],
- ['/user1/files/source.txt', '/user1/files/target.txt', true, false,
- '/files_encryption/keys/files/source.txt/', '/user1/files_encryption/keys/files/target.txt/'],
-
- ['/user2/files/source.txt', '/user1/files/target.txt', false, false,
- '/user2/files_encryption/keys/files/source.txt/', '/user1/files_encryption/keys/files/target.txt/'],
- ['/user2/files/foo/source.txt', '/user1/files/target.txt', false, false,
- '/user2/files_encryption/keys/files/foo/source.txt/', '/user1/files_encryption/keys/files/target.txt/'],
- ['/user2/files/source.txt', '/user1/files/foo/target.txt', false, false,
- '/user2/files_encryption/keys/files/source.txt/', '/user1/files_encryption/keys/files/foo/target.txt/'],
- ['/user2/files/source.txt', '/user1/files/foo/target.txt', true, true,
- '/files_encryption/keys/files/source.txt/', '/files_encryption/keys/files/foo/target.txt/'],
- ['/user2/files/source.txt', '/user1/files/target.txt', false, true,
- '/user2/files_encryption/keys/files/source.txt/', '/files_encryption/keys/files/target.txt/'],
- ['/user2/files/source.txt', '/user1/files/target.txt', true, false,
- '/files_encryption/keys/files/source.txt/', '/user1/files_encryption/keys/files/target.txt/'],
- ];
- }
-
- /**
- *
- * @param string $path
- * @param boolean $systemWideMountPoint
- * @param string $storageRoot
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetPathToKeys')]
- public function testGetPathToKeys($path, $systemWideMountPoint, $storageRoot, $expected): void {
- $this->invokePrivate($this->storage, 'root_dir', [$storageRoot]);
-
- $this->util->expects($this->any())
- ->method('getUidAndFilename')
- ->willReturnCallback([$this, 'getUidAndFilenameCallback']);
- $this->util->expects($this->any())
- ->method('isSystemWideMountPoint')
- ->willReturn($systemWideMountPoint);
-
- $this->assertSame($expected,
- self::invokePrivate($this->storage, 'getPathToKeys', [$path])
- );
- }
-
- public static function dataTestGetPathToKeys() {
- return [
- ['/user1/files/source.txt', false, '', '/user1/files_encryption/keys/files/source.txt/'],
- ['/user1/files/source.txt', true, '', '/files_encryption/keys/files/source.txt/'],
- ['/user1/files/source.txt', false, 'storageRoot', '/storageRoot/user1/files_encryption/keys/files/source.txt/'],
- ['/user1/files/source.txt', true, 'storageRoot', '/storageRoot/files_encryption/keys/files/source.txt/'],
- ];
- }
-
- public function testKeySetPreparation(): void {
- $this->view->expects($this->any())
- ->method('file_exists')
- ->willReturn(false);
- $this->view->expects($this->any())
- ->method('is_dir')
- ->willReturn(false);
- $this->view->expects($this->any())
- ->method('mkdir')
- ->willReturnCallback([$this, 'mkdirCallback']);
-
- $this->mkdirStack = [
- '/user1/files_encryption/keys/foo',
- '/user1/files_encryption/keys',
- '/user1/files_encryption',
- '/user1'];
-
- self::invokePrivate($this->storage, 'keySetPreparation', ['/user1/files_encryption/keys/foo']);
- }
-
- public function mkdirCallback() {
- $args = func_get_args();
- $expected = array_pop($this->mkdirStack);
- $this->assertSame($expected, $args[0]);
- }
-
- /**
- * @param bool $createBackupDir
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestBackupUserKeys')]
- public function testBackupUserKeys($createBackupDir): void {
- $storage = $this->getMockBuilder('OC\Encryption\Keys\Storage')
- ->setConstructorArgs([$this->view, $this->util, $this->crypto, $this->config])
- ->onlyMethods(['getTimestamp'])
- ->getMock();
-
- $storage->expects($this->any())->method('getTimestamp')->willReturn('1234567');
-
- $this->view->expects($this->once())->method('file_exists')
- ->with('user1/files_encryption/backup')->willReturn(!$createBackupDir);
-
- if ($createBackupDir) {
- $calls = [
- 'user1/files_encryption/backup',
- 'user1/files_encryption/backup/test.encryptionModule.1234567',
- ];
- $this->view->expects($this->exactly(2))->method('mkdir')
- ->willReturnCallback(function ($path) use (&$calls): void {
- $expected = array_shift($calls);
- $this->assertEquals($expected, $path);
- });
- } else {
- $this->view->expects($this->once())->method('mkdir')
- ->with('user1/files_encryption/backup/test.encryptionModule.1234567');
- }
-
- $this->view->expects($this->once())->method('copy')
- ->with(
- 'user1/files_encryption/encryptionModule',
- 'user1/files_encryption/backup/test.encryptionModule.1234567'
- )->willReturn(true);
-
- $this->assertTrue($storage->backupUserKeys('encryptionModule', 'test', 'user1'));
- }
-
- public static function dataTestBackupUserKeys() {
- return [
- [true], [false]
- ];
- }
-}
--- a/tests/lib/Encryption/ManagerTest.php
+++ /dev/null
@@ -1,281 +0,0 @@
-config = $this->createMock(IConfig::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->l10n = $this->createMock(IL10N::class);
- $this->view = $this->createMock(View::class);
- $this->util = $this->createMock(Util::class);
- $this->arrayCache = $this->createMock(ArrayCache::class);
- $this->manager = new Manager($this->config, $this->logger, $this->l10n, $this->view, $this->util, $this->arrayCache);
- }
-
- public function testManagerIsDisabled(): void {
- $this->assertFalse($this->manager->isEnabled());
- }
-
- public function testManagerIsDisabledIfEnabledButNoModules(): void {
- $this->config->expects($this->any())->method('getAppValue')->willReturn(true);
- $this->assertFalse($this->manager->isEnabled());
- }
-
- public function testManagerIsDisabledIfDisabledButModules(): void {
- $this->config->expects($this->any())->method('getAppValue')->willReturn(false);
- $em = $this->createMock(IEncryptionModule::class);
- $em->expects($this->any())->method('getId')->willReturn('id');
- $em->expects($this->any())->method('getDisplayName')->willReturn('TestDummyModule0');
- $this->manager->registerEncryptionModule('id', 'TestDummyModule0', function () use ($em) {
- return $em;
- });
- $this->assertFalse($this->manager->isEnabled());
- }
-
- #[Group(name: 'DB')]
- public function testManagerIsEnabled(): void {
- $appConfig = Server::get(IAppConfig::class);
- $appConfig->setValueBool('core', 'encryption_enabled', true);
-
- $this->config->expects($this->any())->method('getSystemValueBool')->willReturn(true);
- $result = $this->manager->isEnabled();
-
- $appConfig->deleteKey('core', 'encryption_enabled');
- $this->assertTrue($result);
- }
-
- public function testModuleRegistration() {
- $this->config->expects($this->any())->method('getAppValue')->willReturn('yes');
-
- $this->addNewEncryptionModule($this->manager, 0);
- $this->assertCount(1, $this->manager->getEncryptionModules());
-
- return $this->manager;
- }
-
- #[\PHPUnit\Framework\Attributes\Depends('testModuleRegistration')]
- public function testModuleReRegistration($manager): void {
- $this->expectException(ModuleAlreadyExistsException::class);
- $this->expectExceptionMessage('Id "ID0" already used by encryption module "TestDummyModule0"');
-
- $this->addNewEncryptionModule($manager, 0);
- }
-
- public function testModuleUnRegistration(): void {
- $this->config->expects($this->any())->method('getAppValue')->willReturn(true);
- $this->addNewEncryptionModule($this->manager, 0);
- $this->assertCount(1, $this->manager->getEncryptionModules());
-
- $this->manager->unregisterEncryptionModule('ID0');
- $this->assertEmpty($this->manager->getEncryptionModules());
- }
-
- public function testGetEncryptionModuleUnknown(): void {
- $this->expectException(ModuleDoesNotExistsException::class);
- $this->expectExceptionMessage('Module with ID: unknown does not exist.');
-
- $this->config->expects($this->any())->method('getAppValue')->willReturn(true);
- $this->addNewEncryptionModule($this->manager, 0);
- $this->assertCount(1, $this->manager->getEncryptionModules());
- $this->manager->getEncryptionModule('unknown');
- }
-
- public function testGetEncryptionModuleEmpty(): void {
- global $defaultId;
- $defaultId = null;
-
- $this->config->expects($this->any())
- ->method('getAppValue')
- ->with('core', 'default_encryption_module')
- ->willReturnCallback(function () {
- global $defaultId;
- return $defaultId;
- });
-
- $this->addNewEncryptionModule($this->manager, 0);
- $this->assertCount(1, $this->manager->getEncryptionModules());
- $this->addNewEncryptionModule($this->manager, 1);
- $this->assertCount(2, $this->manager->getEncryptionModules());
-
- // Should return the default module
- $defaultId = 'ID0';
- $this->assertEquals('ID0', $this->manager->getEncryptionModule()->getId());
- $defaultId = 'ID1';
- $this->assertEquals('ID1', $this->manager->getEncryptionModule()->getId());
- }
-
- public function testGetEncryptionModule(): void {
- global $defaultId;
- $defaultId = null;
-
- $this->config->expects($this->any())
- ->method('getAppValue')
- ->with('core', 'default_encryption_module')
- ->willReturnCallback(function () {
- global $defaultId;
- return $defaultId;
- });
-
- $this->addNewEncryptionModule($this->manager, 0);
- $defaultId = 'ID0';
- $this->assertCount(1, $this->manager->getEncryptionModules());
-
- $en0 = $this->manager->getEncryptionModule('ID0');
- $this->assertEquals('ID0', $en0->getId());
-
- $en0 = self::invokePrivate($this->manager, 'getDefaultEncryptionModule');
- $this->assertEquals('ID0', $en0->getId());
-
- $this->assertEquals('ID0', $this->manager->getDefaultEncryptionModuleId());
- }
-
- public function testSetDefaultEncryptionModule(): void {
- global $defaultId;
- $defaultId = null;
-
- $this->config->expects($this->any())
- ->method('getAppValue')
- ->with('core', 'default_encryption_module')
- ->willReturnCallback(function () {
- global $defaultId;
- return $defaultId;
- });
-
- $this->addNewEncryptionModule($this->manager, 0);
- $this->assertCount(1, $this->manager->getEncryptionModules());
- $this->addNewEncryptionModule($this->manager, 1);
- $this->assertCount(2, $this->manager->getEncryptionModules());
-
- // Default module is the first we set
- $defaultId = 'ID0';
- $this->assertEquals('ID0', $this->manager->getDefaultEncryptionModuleId());
-
- // Set to an existing module
- $this->config->expects($this->once())
- ->method('setAppValue')
- ->with('core', 'default_encryption_module', 'ID1');
- $this->assertTrue($this->manager->setDefaultEncryptionModule('ID1'));
- $defaultId = 'ID1';
- $this->assertEquals('ID1', $this->manager->getDefaultEncryptionModuleId());
-
- // Set to an unexisting module
- $this->assertFalse($this->manager->setDefaultEncryptionModule('ID2'));
- $this->assertEquals('ID1', $this->manager->getDefaultEncryptionModuleId());
- }
-
- // /**
- // * @expectedException \OC\Encryption\Exceptions\ModuleAlreadyExistsException
- // * @expectedExceptionMessage Id "0" already used by encryption module "TestDummyModule0"
- // */
- // public function testModuleRegistration() {
- // $config = $this->createMock(IConfig::class);
- // $config->expects($this->any())->method('getSystemValueBool')->willReturn(true);
- // $em = $this->createMock(IEncryptionModule::class);
- // $em->expects($this->any())->method('getId')->willReturn(0);
- // $em->expects($this->any())->method('getDisplayName')->willReturn('TestDummyModule0');
- // $m = new Manager($config);
- // $m->registerEncryptionModule($em);
- // $this->assertTrue($m->isEnabled());
- // $m->registerEncryptionModule($em);
- // }
- //
- // public function testModuleUnRegistration() {
- // $config = $this->createMock(IConfig::class);
- // $config->expects($this->any())->method('getSystemValueBool')->willReturn(true);
- // $em = $this->createMock(IEncryptionModule::class);
- // $em->expects($this->any())->method('getId')->willReturn(0);
- // $em->expects($this->any())->method('getDisplayName')->willReturn('TestDummyModule0');
- // $m = new Manager($config);
- // $m->registerEncryptionModule($em);
- // $this->assertTrue($m->isEnabled());
- // $m->unregisterEncryptionModule($em);
- // $this->assertFalse($m->isEnabled());
- // }
- //
- // /**
- // * @expectedException \OC\Encryption\Exceptions\ModuleDoesNotExistsException
- // * @expectedExceptionMessage Module with ID: unknown does not exist.
- // */
- // public function testGetEncryptionModuleUnknown() {
- // $config = $this->createMock(IConfig::class);
- // $config->expects($this->any())->method('getSystemValueBool')->willReturn(true);
- // $em = $this->createMock(IEncryptionModule::class);
- // $em->expects($this->any())->method('getId')->willReturn(0);
- // $em->expects($this->any())->method('getDisplayName')->willReturn('TestDummyModule0');
- // $m = new Manager($config);
- // $m->registerEncryptionModule($em);
- // $this->assertTrue($m->isEnabled());
- // $m->getEncryptionModule('unknown');
- // }
- //
- // public function testGetEncryptionModule() {
- // $config = $this->createMock(IConfig::class);
- // $config->expects($this->any())->method('getSystemValueBool')->willReturn(true);
- // $em = $this->createMock(IEncryptionModule::class);
- // $em->expects($this->any())->method('getId')->willReturn(0);
- // $em->expects($this->any())->method('getDisplayName')->willReturn('TestDummyModule0');
- // $m = new Manager($config);
- // $m->registerEncryptionModule($em);
- // $this->assertTrue($m->isEnabled());
- // $en0 = $m->getEncryptionModule(0);
- // $this->assertEquals(0, $en0->getId());
- // }
-
- protected function addNewEncryptionModule(Manager $manager, $id) {
- $encryptionModule = $this->createMock(IEncryptionModule::class);
- $encryptionModule->expects($this->any())
- ->method('getId')
- ->willReturn('ID' . $id);
- $encryptionModule->expects($this->any())
- ->method('getDisplayName')
- ->willReturn('TestDummyModule' . $id);
- /** @var IEncryptionModule $encryptionModule */
- $manager->registerEncryptionModule('ID' . $id, 'TestDummyModule' . $id, function () use ($encryptionModule) {
- return $encryptionModule;
- });
- }
-}
--- a/tests/lib/Encryption/UpdateTest.php
+++ /dev/null
@@ -1,191 +0,0 @@
-view = $this->createMock(View::class);
- $this->util = $this->createMock(Util::class);
- $this->encryptionManager = $this->createMock(\OC\Encryption\Manager::class);
- $this->fileHelper = $this->createMock(File::class);
- $this->encryptionModule = $this->createMock(IEncryptionModule::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $this->uid = 'testUser1';
- }
-
- private function getUserMock(string $uid): IUser&MockObject {
- $user = $this->createMock(IUser::class);
- $user->expects(self::any())
- ->method('getUID')
- ->willReturn($uid);
- return $user;
- }
-
- private function getFileMock(string $path, string $owner): OCPFile&MockObject {
- $node = $this->createMock(OCPFile::class);
- $node->expects(self::atLeastOnce())
- ->method('getPath')
- ->willReturn($path);
- $node->expects(self::any())
- ->method('getOwner')
- ->willReturn($this->getUserMock($owner));
-
- return $node;
- }
-
- private function getFolderMock(string $path, string $owner): Folder&MockObject {
- $node = $this->createMock(Folder::class);
- $node->expects(self::atLeastOnce())
- ->method('getPath')
- ->willReturn($path);
- $node->expects(self::any())
- ->method('getOwner')
- ->willReturn($this->getUserMock($owner));
-
- return $node;
- }
-
- /**
- *
- * @param string $path
- * @param boolean $isDir
- * @param array $allFiles
- * @param integer $numberOfFiles
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestUpdate')]
- public function testUpdate($path, $isDir, $allFiles, $numberOfFiles): void {
- $updateMock = $this->getUpdateMock(['getOwnerPath']);
- $updateMock->expects($this->once())->method('getOwnerPath')
- ->willReturnCallback(fn (OCPFile|Folder $node) => '/user/' . $node->getPath());
-
- $this->encryptionManager->expects($this->once())
- ->method('getEncryptionModule')
- ->willReturn($this->encryptionModule);
-
- if ($isDir) {
- $this->util->expects($this->once())
- ->method('getAllFiles')
- ->willReturn($allFiles);
- $node = $this->getFolderMock($path, 'user');
- } else {
- $node = $this->getFileMock($path, 'user');
- }
-
- $this->fileHelper->expects($this->exactly($numberOfFiles))
- ->method('getAccessList')
- ->willReturn(['users' => [], 'public' => false]);
-
- $this->encryptionModule->expects($this->exactly($numberOfFiles))
- ->method('update')
- ->willReturn(true);
-
- $updateMock->update($node);
- }
-
- public static function dataTestUpdate(): array {
- return [
- ['/user/files/foo', true, ['/user/files/foo/file1.txt', '/user/files/foo/file1.txt'], 2],
- ['/user/files/test.txt', false, [], 1],
- ];
- }
-
- /**
- *
- * @param string $source
- * @param string $target
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestPostRename')]
- public function testPostRename($source, $target): void {
- $updateMock = $this->getUpdateMock(['update','getOwnerPath']);
-
- $sourceNode = $this->getFileMock($source, 'user');
- $targetNode = $this->getFileMock($target, 'user');
-
- if (dirname($source) === dirname($target)) {
- $updateMock->expects($this->never())->method('getOwnerPath');
- $updateMock->expects($this->never())->method('update');
- } else {
- $updateMock->expects($this->once())->method('update')
- ->willReturnCallback(fn (OCPFile|Folder $node) => $this->assertSame(
- $target,
- $node->getPath(),
- 'update needs to be executed for the target destination'
- ));
- }
-
- $updateMock->postRename($sourceNode, $targetNode);
- }
-
- public static function dataTestPostRename(): array {
- return [
- ['/test.txt', '/testNew.txt'],
- ['/folder/test.txt', '/testNew.txt'],
- ['/test.txt', '/folder/testNew.txt'],
- ];
- }
-
- public function testPostRestore(): void {
- $updateMock = $this->getUpdateMock(['update']);
-
- $updateMock->expects($this->once())->method('update')
- ->willReturnCallback(fn (OCPFile|Folder $node) => $this->assertSame(
- '/folder/test.txt',
- $node->getPath(),
- 'update needs to be executed for the target destination'
- ));
-
- $updateMock->postRestore($this->getFileMock('/folder/test.txt', 'user'));
- }
-
- /**
- * create mock of the update method
- *
- * @param array $methods methods which should be set
- */
- protected function getUpdateMock(array $methods): Update&MockObject {
- return $this->getMockBuilder(Update::class)
- ->setConstructorArgs(
- [
- $this->util,
- $this->encryptionManager,
- $this->fileHelper,
- $this->logger,
- $this->uid
- ]
- )
- ->onlyMethods($methods)
- ->getMock();
- }
-}
--- a/tests/lib/Encryption/UtilTest.php
+++ /dev/null
@@ -1,247 +0,0 @@
-view = $this->getMockBuilder(View::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->userManager = $this->createMock(IUserManager::class);
- $this->groupManager = $this->createMock(IGroupManager::class);
- $this->config = $this->createMock(IConfig::class);
-
- $this->util = new Util(
- $this->view,
- $this->userManager,
- $this->groupManager,
- $this->config
- );
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providesHeadersForEncryptionModule')]
- public function testGetEncryptionModuleId($expected, $header): void {
- $id = $this->util->getEncryptionModuleId($header);
- $this->assertEquals($expected, $id);
- }
-
- public static function providesHeadersForEncryptionModule(): array {
- return [
- ['', []],
- ['', ['1']],
- [2, ['oc_encryption_module' => 2]],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providesHeaders')]
- public function testCreateHeader($expected, $header, $moduleId): void {
- $em = $this->createMock(IEncryptionModule::class);
- $em->expects($this->any())->method('getId')->willReturn($moduleId);
-
- $result = $this->util->createHeader($header, $em);
- $this->assertEquals($expected, $result);
- }
-
- public static function providesHeaders(): array {
- return [
- [str_pad('HBEGIN:oc_encryption_module:0:HEND', self::$headerSize, '-', STR_PAD_RIGHT)
- , [], '0'],
- [str_pad('HBEGIN:oc_encryption_module:0:custom_header:foo:HEND', self::$headerSize, '-', STR_PAD_RIGHT)
- , ['custom_header' => 'foo'], '0'],
- ];
- }
-
- public function testCreateHeaderFailed(): void {
- $this->expectException(EncryptionHeaderKeyExistsException::class);
-
- $header = ['header1' => 1, 'header2' => 2, 'oc_encryption_module' => 'foo'];
-
- $em = $this->createMock(IEncryptionModule::class);
- $em->expects($this->any())->method('getId')->willReturn('moduleId');
-
- $this->util->createHeader($header, $em);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providePathsForTestIsExcluded')]
- public function testIsExcluded($path, $keyStorageRoot, $expected): void {
- $this->config->expects($this->once())
- ->method('getAppValue')
- ->with('core', 'encryption_key_storage_root', '')
- ->willReturn($keyStorageRoot);
- $this->userManager
- ->expects($this->any())
- ->method('userExists')
- ->willReturnCallback([$this, 'isExcludedCallback']);
-
- $this->assertSame($expected,
- $this->util->isExcluded($path)
- );
- }
-
- public static function providePathsForTestIsExcluded(): array {
- return [
- ['/files_encryption', '', true],
- ['files_encryption/foo.txt', '', true],
- ['test/foo.txt', '', false],
- ['/user1/files_encryption/foo.txt', '', true],
- ['/user1/files/foo.txt', '', false],
- ['/keyStorage/user1/files/foo.txt', 'keyStorage', true],
- ['/keyStorage/files_encryption', '/keyStorage', true],
- ['keyStorage/user1/files_encryption', '/keyStorage/', true],
- ];
- }
-
- public function isExcludedCallback() {
- $args = func_get_args();
- if ($args[0] === 'user1') {
- return true;
- }
-
- return false;
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestIsFile')]
- public function testIsFile($path, $expected): void {
- $this->assertSame($expected,
- $this->util->isFile($path)
- );
- }
-
- public static function dataTestIsFile(): array {
- return [
- ['/user/files/test.txt', true],
- ['/user/files', true],
- ['/user/files_versions/test.txt', false],
- ['/user/foo/files/test.txt', false],
- ['/files/foo/files/test.txt', false],
- ['/user', false],
- ['/user/test.txt', false],
- ];
- }
-
- /**
- *
- * @param string $path
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestStripPartialFileExtension')]
- public function testStripPartialFileExtension($path, $expected): void {
- $this->assertSame($expected,
- $this->util->stripPartialFileExtension($path));
- }
-
- public static function dataTestStripPartialFileExtension(): array {
- return [
- ['/foo/test.txt', '/foo/test.txt'],
- ['/foo/test.txt.part', '/foo/test.txt'],
- ['/foo/test.txt.ocTransferId7567846853.part', '/foo/test.txt'],
- ['/foo/test.txt.ocTransferId7567.part', '/foo/test.txt'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestParseRawHeader')]
- public function testParseRawHeader($rawHeader, $expected): void {
- $result = $this->util->parseRawHeader($rawHeader);
- $this->assertSameSize($expected, $result);
- foreach ($result as $key => $value) {
- $this->assertArrayHasKey($key, $expected);
- $this->assertSame($expected[$key], $value);
- }
- }
-
- public static function dataTestParseRawHeader(): array {
- return [
- [str_pad('HBEGIN:oc_encryption_module:0:HEND', self::$headerSize, '-', STR_PAD_RIGHT)
- , [Util::HEADER_ENCRYPTION_MODULE_KEY => '0']],
- [str_pad('HBEGIN:oc_encryption_module:0:custom_header:foo:HEND', self::$headerSize, '-', STR_PAD_RIGHT)
- , ['custom_header' => 'foo', Util::HEADER_ENCRYPTION_MODULE_KEY => '0']],
- [str_pad('HelloWorld', self::$headerSize, '-', STR_PAD_RIGHT), []],
- ['', []],
- [str_pad('HBEGIN:oc_encryption_module:0', self::$headerSize, '-', STR_PAD_RIGHT)
- , []],
- [str_pad('oc_encryption_module:0:HEND', self::$headerSize, '-', STR_PAD_RIGHT)
- , []],
- ];
- }
-
- /**
- *
- * @param bool $isSystemWideMountPoint
- * @param string $storageRoot
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetFileKeyDir')]
- public function testGetFileKeyDir($isSystemWideMountPoint, $storageRoot, $expected): void {
- $path = '/user1/files/foo/bar.txt';
- $owner = 'user1';
- $relativePath = '/foo/bar.txt';
-
- $util = $this->getMockBuilder(Util::class)
- ->onlyMethods(['isSystemWideMountPoint', 'getUidAndFilename', 'getKeyStorageRoot'])
- ->setConstructorArgs([
- $this->view,
- $this->userManager,
- $this->groupManager,
- $this->config
- ])
- ->getMock();
-
- $util->expects($this->once())->method('getKeyStorageRoot')
- ->willReturn($storageRoot);
- $util->expects($this->once())->method('isSystemWideMountPoint')
- ->willReturn($isSystemWideMountPoint);
- $util->expects($this->once())->method('getUidAndFilename')
- ->with($path)->willReturn([$owner, $relativePath]);
-
- $this->assertSame($expected,
- $util->getFileKeyDir('OC_DEFAULT_MODULE', $path)
- );
- }
-
- public static function dataTestGetFileKeyDir(): array {
- return [
- [false, '', '/user1/files_encryption/keys/foo/bar.txt/OC_DEFAULT_MODULE/'],
- [true, '', '/files_encryption/keys/foo/bar.txt/OC_DEFAULT_MODULE/'],
- [false, 'newStorageRoot', '/newStorageRoot/user1/files_encryption/keys/foo/bar.txt/OC_DEFAULT_MODULE/'],
- [true, 'newStorageRoot', '/newStorageRoot/files_encryption/keys/foo/bar.txt/OC_DEFAULT_MODULE/'],
- ];
- }
-}
--- a/tests/lib/ErrorHandlerTest.php
+++ /dev/null
@@ -1,79 +0,0 @@
-logger = $this->createMock(LoggerInterface::class);
- $this->errorHandler = new ErrorHandler(
- $this->logger
- );
-
- $this->errorReporting = error_reporting(E_ALL);
- }
-
- #[\Override]
- protected function tearDown(): void {
- error_reporting($this->errorReporting);
- parent::tearDown();
- }
-
- /**
- * provide username, password combinations for testRemovePassword
- * @return array
- */
- public static function passwordProvider(): array {
- return [
- ['us:er', 'pass@word'],
- ['us:er', 'password'],
- ['user', '-C:R,w)@6*}'],
- ['user', 'pass:word'],
- ['user', 'pass@word'],
- ['user', 'password'],
- ['user:test@cloud', 'password'],
- ['user@owncloud.org', 'password'],
- ['user@test@owncloud.org', 'password'],
- ];
- }
-
- /**
- * @param string $username
- * @param string $password
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('passwordProvider')]
- public function testRemovePasswordFromError($username, $password): void {
- $url = 'http://' . $username . ':' . $password . '@owncloud.org';
- $expectedResult = 'http://xxx:xxx@owncloud.org';
- $this->logger->expects(self::once())
- ->method('log')
- ->with(
- ILogger::ERROR,
- 'Could not reach ' . $expectedResult . ' at file#4',
- ['app' => 'PHP'],
- );
-
- $result = $this->errorHandler->onError(E_USER_ERROR, 'Could not reach ' . $url, 'file', 4);
-
- self::assertTrue($result);
- }
-}
--- a/tests/lib/EventSourceFactoryTest.php
+++ /dev/null
@@ -1,22 +0,0 @@
-createMock(IRequest::class);
- $factory = new EventSourceFactory($request);
-
- $instance = $factory->create();
- $this->assertInstanceOf(IEventSource::class, $instance);
- }
-}
--- a/tests/lib/Federation/CloudIdManagerTest.php
+++ /dev/null
@@ -1,173 +0,0 @@
-contactsManager = $this->createMock(IManager::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->userManager = $this->createMock(IUserManager::class);
-
- $this->cacheFactory = $this->createMock(ICacheFactory::class);
- $this->cacheFactory->method('createDistributed')
- ->willReturn(new ArrayCache(''));
-
- $this->cloudIdManager = new CloudIdManager(
- $this->cacheFactory,
- $this->createMock(IEventDispatcher::class),
- $this->contactsManager,
- $this->urlGenerator,
- $this->userManager,
- );
- $this->overwriteService(ICloudIdManager::class, $this->cloudIdManager);
- }
-
- public static function dataGetDisplayNameFromContact(): array {
- return [
- ['test1@example.tld', 'test', 'test'],
- ['test2@example.tld', null, null],
- ['test3@example.tld', 'test3@example', 'test3@example'],
- ['test4@example.tld', 'test4@example.tld', null],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetDisplayNameFromContact')]
- public function testGetDisplayNameFromContact(string $cloudId, ?string $displayName, ?string $expected): void {
- $returnedContact = [
- 'CLOUD' => [$cloudId],
- 'FN' => $expected,
- ];
- if ($displayName === null) {
- unset($returnedContact['FN']);
- }
- $this->contactsManager->method('search')
- ->with($cloudId, ['CLOUD'])
- ->willReturn([$returnedContact]);
-
- $this->assertEquals($expected, $this->cloudIdManager->getDisplayNameFromContact($cloudId));
- $this->assertEquals($expected, $this->cloudIdManager->getDisplayNameFromContact($cloudId));
- }
-
- public static function cloudIdProvider(): array {
- return [
- ['test@example.com', 'test', 'example.com', 'test@example.com'],
- ['test@example.com/cloud', 'test', 'example.com/cloud', 'test@example.com/cloud'],
- ['test@example.com/cloud/', 'test', 'example.com/cloud', 'test@example.com/cloud'],
- ['test@example.com/cloud/index.php', 'test', 'example.com/cloud', 'test@example.com/cloud'],
- ['test@example.com@example.com', 'test@example.com', 'example.com', 'test@example.com@example.com'],
-
- // Equal signs are not valid on Nextcloud side, but can be used by other federated OCM compatible servers
- ['test==@example.com', 'test==', 'example.com', 'test==@example.com'],
- ['==@example.com', '==', 'example.com', '==@example.com'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('cloudIdProvider')]
- public function testResolveCloudId(string $cloudId, string $user, string $noProtocolRemote, string $cleanId): void {
- $displayName = 'Ample Ex';
-
- $this->contactsManager->expects($this->any())
- ->method('search')
- ->with($cleanId, ['CLOUD'])
- ->willReturn([
- [
- 'CLOUD' => [$cleanId],
- 'FN' => $displayName,
- ]
- ]);
-
- $cloudId = $this->cloudIdManager->resolveCloudId($cloudId);
-
- $this->assertEquals($user, $cloudId->getUser());
- $this->assertEquals('https://' . $noProtocolRemote, $cloudId->getRemote());
- $this->assertEquals($cleanId, $cloudId->getId());
- $this->assertEquals($displayName . '@' . $noProtocolRemote, $cloudId->getDisplayId());
- }
-
- public static function invalidCloudIdProvider(): array {
- return [
- ['example.com'],
- ['test:foo@example.com'],
- ['test/foo@example.com']
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('invalidCloudIdProvider')]
- public function testInvalidCloudId(string $cloudId): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $this->contactsManager->expects($this->never())
- ->method('search');
-
- $this->cloudIdManager->resolveCloudId($cloudId);
- }
-
- public static function getCloudIdProvider(): array {
- return [
- ['test', 'example.com', 'test@example.com', null, 'https://example.com', 'https://example.com'],
- ['test', 'http://example.com', 'test@http://example.com', 'test@example.com'],
- ['test', null, 'test@http://example.com', 'test@example.com', 'http://example.com', 'http://example.com'],
- ['test@example.com', 'example.com', 'test@example.com@example.com', null, 'https://example.com', 'https://example.com'],
- ['test@example.com', 'https://example.com', 'test@example.com@example.com'],
- ['test@example.com', null, 'test@example.com@example.com', null, 'https://example.com', 'https://example.com'],
- ['test@example.com', 'https://example.com/index.php/s/shareToken', 'test@example.com@example.com', null, 'https://example.com', 'https://example.com'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('getCloudIdProvider')]
- public function testGetCloudId(string $user, ?string $remote, string $id, ?string $searchCloudId = null, ?string $localHost = 'https://example.com', ?string $expectedRemoteId = null): void {
- if ($remote !== null) {
- $this->contactsManager->expects($this->any())
- ->method('search')
- ->with($searchCloudId ?? $id, ['CLOUD'])
- ->willReturn([
- [
- 'CLOUD' => [$searchCloudId ?? $id],
- 'FN' => 'Ample Ex',
- ]
- ]);
- } else {
- $this->urlGenerator->expects(self::once())
- ->method('getAbsoluteUrl')
- ->willReturn($localHost);
- }
- $expectedRemoteId ??= $remote;
-
- $cloudId = $this->cloudIdManager->getCloudId($user, $remote);
-
- $this->assertEquals($id, $cloudId->getId(), 'Cloud ID');
- $this->assertEquals($expectedRemoteId, $cloudId->getRemote(), 'Remote URL');
- }
-}
--- a/tests/lib/Federation/CloudIdTest.php
+++ /dev/null
@@ -1,48 +0,0 @@
-cloudIdManager = $this->createMock(CloudIdManager::class);
- $this->overwriteService(ICloudIdManager::class, $this->cloudIdManager);
- }
-
- public static function dataGetDisplayCloudId(): array {
- return [
- ['test@example.com', 'test', 'example.com', 'test@example.com'],
- ['test@http://example.com', 'test', 'http://example.com', 'test@example.com'],
- ['test@https://example.com', 'test', 'https://example.com', 'test@example.com'],
- ['test@https://example.com', 'test', 'https://example.com', 'Beloved Amy@example.com', 'Beloved Amy'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetDisplayCloudId')]
- public function testGetDisplayCloudId(string $id, string $user, string $remote, string $display, ?string $addressbookName = null): void {
- $this->cloudIdManager->expects($this->once())
- ->method('getDisplayNameFromContact')
- ->willReturn($addressbookName);
-
- $cloudId = new CloudId($id, $user, $remote);
- $this->assertEquals($display, $cloudId->getDisplayId());
- }
-}
--- a/tests/lib/Files/AppData/AppDataTest.php
+++ /dev/null
@@ -1,96 +0,0 @@
-rootFolder = $this->createMock(IRootFolder::class);
- $this->systemConfig = $this->createMock(SystemConfig::class);
- $this->appData = new AppData($this->rootFolder, $this->systemConfig, 'myApp');
-
- $this->systemConfig->expects($this->any())
- ->method('getValue')
- ->with('instanceid', null)
- ->willReturn('iid');
- }
-
- private function setupAppFolder() {
- $appFolder = $this->createMock(Folder::class);
-
- $this->rootFolder->expects($this->any())
- ->method('get')
- ->with($this->equalTo('appdata_iid/myApp'))
- ->willReturn($appFolder);
-
- return $appFolder;
- }
-
- public function testGetFolder(): void {
- $folder = $this->createMock(Folder::class);
-
- $this->rootFolder->expects($this->once())
- ->method('get')
- ->with($this->equalTo('appdata_iid/myApp/folder'))
- ->willReturn($folder);
-
- $result = $this->appData->getFolder('folder');
- $this->assertInstanceOf(ISimpleFolder::class, $result);
- }
-
- public function testNewFolder(): void {
- $appFolder = $this->setupAppFolder();
-
- $folder = $this->createMock(Folder::class);
-
- $appFolder->expects($this->once())
- ->method('newFolder')
- ->with($this->equalTo('folder'))
- ->willReturn($folder);
-
- $result = $this->appData->newFolder('folder');
- $this->assertInstanceOf(ISimpleFolder::class, $result);
- }
-
- public function testGetDirectoryListing(): void {
- $appFolder = $this->setupAppFolder();
-
- $file = $this->createMock(File::class);
- $folder = $this->createMock(Folder::class);
- $node = $this->createMock(Node::class);
-
- $appFolder->expects($this->once())
- ->method('getDirectoryListing')
- ->willReturn([$file, $folder, $node]);
-
- $result = $this->appData->getDirectoryListing();
-
- $this->assertCount(1, $result);
- $this->assertInstanceOf(ISimpleFolder::class, $result[0]);
- }
-}
--- a/tests/lib/Files/AppData/FactoryTest.php
+++ /dev/null
@@ -1,41 +0,0 @@
-rootFolder = $this->createMock(IRootFolder::class);
- $this->systemConfig = $this->createMock(SystemConfig::class);
- $this->factory = new Factory($this->rootFolder, $this->systemConfig);
- }
-
- public function testGet(): void {
- $this->rootFolder->expects($this->never())
- ->method($this->anything());
- $this->systemConfig->expects($this->never())
- ->method($this->anything());
-
- $this->factory->get('foo');
- }
-}
--- a/tests/lib/Files/Cache/CacheTest.php
+++ /dev/null
@@ -1,1086 +0,0 @@
-storage = new Temporary([]);
- $this->storage2 = new Temporary([]);
- $this->cache = new Cache($this->storage);
- $this->cache2 = new Cache($this->storage2);
- $this->cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $this->cache2->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- }
-
- #[\Override]
- protected function tearDown(): void {
- if ($this->cache) {
- $this->cache->clear();
- }
-
- parent::tearDown();
- }
-
- public function testGetNumericId(): void {
- $this->assertNotNull($this->cache->getNumericStorageId());
- }
-
- public function testSimple(): void {
- $file1 = 'foo';
- $file2 = 'foo/bar';
- $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder'];
- $data2 = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'];
-
- $this->assertFalse($this->cache->inCache($file1));
- $this->assertEquals($this->cache->get($file1), null);
-
- $id1 = $this->cache->put($file1, $data1);
- $this->assertTrue($this->cache->inCache($file1));
- $cacheData1 = $this->cache->get($file1);
- foreach ($data1 as $key => $value) {
- $this->assertEquals($value, $cacheData1[$key]);
- }
- $this->assertEquals($cacheData1['mimepart'], 'foo');
- $this->assertEquals($cacheData1['fileid'], $id1);
- $this->assertEquals($id1, $this->cache->getId($file1));
-
- $this->assertFalse($this->cache->inCache($file2));
- $id2 = $this->cache->put($file2, $data2);
- $this->assertTrue($this->cache->inCache($file2));
- $cacheData2 = $this->cache->get($file2);
- foreach ($data2 as $key => $value) {
- $this->assertEquals($value, $cacheData2[$key]);
- }
- $this->assertEquals($cacheData1['fileid'], $cacheData2['parent']);
- $this->assertEquals($cacheData2['fileid'], $id2);
- $this->assertEquals($id2, $this->cache->getId($file2));
- $this->assertEquals($id1, $this->cache->getParentId($file2));
-
- $newSize = 1050;
- $newId2 = $this->cache->put($file2, ['size' => $newSize]);
- $cacheData2 = $this->cache->get($file2);
- $this->assertEquals($newId2, $id2);
- $this->assertEquals($cacheData2['size'], $newSize);
- $this->assertEquals($cacheData1, $this->cache->get($file1));
-
- $this->cache->remove($file2);
- $this->assertFalse($this->cache->inCache($file2));
- $this->assertEquals($this->cache->get($file2), null);
- $this->assertTrue($this->cache->inCache($file1));
-
- $this->assertEquals($cacheData1, $this->cache->get($id1));
- }
-
- public function testCacheEntryGetters(): void {
- $file1 = 'foo';
- $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/file'];
-
- $id1 = $this->cache->put($file1, $data1);
- $entry = $this->cache->get($file1);
-
- $this->assertEquals($entry->getId(), $id1);
- $this->assertEquals($entry->getStorageId(), $this->cache->getNumericStorageId());
- $this->assertEquals($entry->getPath(), 'foo');
- $this->assertEquals($entry->getName(), 'foo');
- $this->assertEquals($entry->getMimeType(), 'foo/file');
- $this->assertEquals($entry->getMimePart(), 'foo');
- $this->assertEquals($entry->getSize(), 100);
- $this->assertEquals($entry->getMTime(), 50);
- $this->assertEquals($entry->getStorageMTime(), 50);
- $this->assertEquals($entry->getEtag(), null);
- $this->assertEquals($entry->getPermissions(), 0);
- $this->assertEquals($entry->isEncrypted(), false);
- $this->assertEquals($entry->getMetadataEtag(), null);
- $this->assertEquals($entry->getCreationTime(), null);
- $this->assertEquals($entry->getUploadTime(), null);
- $this->assertEquals($entry->getUnencryptedSize(), 100);
- }
-
- public function testGetUnencryptedSizeEncryptedZeroByte(): void {
- $file1 = 'encrypted_zero';
- $this->cache->put($file1, ['size' => 8192, 'mtime' => 50, 'mimetype' => 'application/octet-stream', 'encrypted' => 1, 'unencrypted_size' => 0]);
- $entry = $this->cache->get($file1);
-
- // getUnencryptedSize() must return 0 (the true plaintext size), not 8192 (the encrypted on-disk size)
- $this->assertEquals(0, $entry->getUnencryptedSize());
- $this->assertTrue($entry->isEncrypted());
- }
-
- public function testPartial(): void {
- $file1 = 'foo';
-
- $this->cache->put($file1, ['size' => 10]);
- $this->assertEquals(new CacheEntry(['size' => 10]), $this->cache->get($file1));
-
- $this->cache->put($file1, ['mtime' => 15]);
- $this->assertEquals(new CacheEntry(['size' => 10, 'mtime' => 15]), $this->cache->get($file1));
-
- $this->cache->put($file1, ['size' => 12]);
- $this->assertEquals(new CacheEntry(['size' => 12, 'mtime' => 15]), $this->cache->get($file1));
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('folderDataProvider')]
- public function testFolder($folder): void {
- if (strpos($folder, 'F09F9890')) {
- // 4 byte UTF doesn't work on mysql
- $params = Server::get(\OC\DB\Connection::class)->getParams();
- if (Server::get(IDBConnection::class)->getDatabaseProvider() === IDBConnection::PLATFORM_MYSQL && $params['charset'] !== 'utf8mb4') {
- $this->markTestSkipped('MySQL doesn\'t support 4 byte UTF-8');
- }
- }
- $file2 = $folder . '/bar';
- $file3 = $folder . '/foo';
- $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
- $fileData = [];
- $fileData['bar'] = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'];
- $fileData['foo'] = ['size' => 20, 'mtime' => 25, 'mimetype' => 'foo/file'];
-
- $this->cache->put($folder, $data1);
- $this->cache->put($file2, $fileData['bar']);
- $this->cache->put($file3, $fileData['foo']);
-
- $content = $this->cache->getFolderContents($folder);
- $this->assertEquals(count($content), 2);
- foreach ($content as $cachedData) {
- $data = $fileData[$cachedData['name']];
- foreach ($data as $name => $value) {
- $this->assertEquals($value, $cachedData[$name]);
- }
- }
-
- $file4 = $folder . '/unkownSize';
- $fileData['unkownSize'] = ['size' => -1, 'mtime' => 25, 'mimetype' => 'foo/file'];
- $this->cache->put($file4, $fileData['unkownSize']);
-
- $this->assertEquals(-1, $this->cache->calculateFolderSize($folder));
-
- $fileData['unkownSize'] = ['size' => 5, 'mtime' => 25, 'mimetype' => 'foo/file'];
- $this->cache->put($file4, $fileData['unkownSize']);
-
- $this->assertEquals(1025, $this->cache->calculateFolderSize($folder));
-
- $this->cache->remove($file2);
- $this->cache->remove($file3);
- $this->cache->remove($file4);
- $this->assertEquals(0, $this->cache->calculateFolderSize($folder));
-
- $this->cache->remove($folder);
- $this->assertFalse($this->cache->inCache($folder . '/foo'));
- $this->assertFalse($this->cache->inCache($folder . '/bar'));
- }
-
- public function testRemoveRecursive(): void {
- $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
- $fileData = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'text/plain'];
- $folders = ['folder', 'folder/subfolder', 'folder/sub2', 'folder/sub2/sub3'];
- $files = ['folder/foo.txt', 'folder/bar.txt', 'folder/subfolder/asd.txt', 'folder/sub2/qwerty.txt', 'folder/sub2/sub3/foo.txt'];
-
- foreach ($folders as $folder) {
- $this->cache->put($folder, $folderData);
- }
- foreach ($files as $file) {
- $this->cache->put($file, $fileData);
- }
-
- $this->cache->remove('folder');
- foreach ($files as $file) {
- $this->assertFalse($this->cache->inCache($file));
- }
- }
-
- /**
- * @return array{0: Cache, 1: \Closure(): list}
- */
- private function cacheWithRecordedEvents(IStorage $storage): array {
- $events = [];
- $dispatcher = $this->createMock(IEventDispatcher::class);
- $dispatcher->method('dispatchTyped')
- ->willReturnCallback(function (object $event) use (&$events): void {
- if ($event instanceof CacheEntriesRemovedEvent) {
- $events[] = $event;
- }
- });
-
- $dependencies = new CacheDependencies(
- Server::get(IMimeTypeLoader::class),
- Server::get(IDBConnection::class),
- $dispatcher,
- Server::get(QuerySearchHelper::class),
- Server::get(SystemConfig::class),
- Server::get(LoggerInterface::class),
- Server::get(IFilesMetadataManager::class),
- Server::get(DisplayNameCache::class),
- );
-
- return [new Cache($storage, $dependencies), function () use (&$events): array {
- return $events;
- }];
- }
-
- /**
- * @param list $events
- * @return list
- */
- private function announcedFileIds(array $events): array {
- $fileIds = [];
- foreach ($events as $event) {
- foreach ($event->getCacheEntryRemovedEvents() as $removed) {
- $fileIds[] = $removed->getFileId();
- }
- }
- return $fileIds;
- }
-
- public function testRemoveSingleFileAnnouncesBatchEvent(): void {
- $storage = new Temporary([]);
- [$cache, $recorded] = $this->cacheWithRecordedEvents($storage);
- $cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $fileId = $cache->put('foo.txt', ['size' => 1, 'mtime' => 20, 'mimetype' => 'text/plain']);
-
- $cache->remove('foo.txt');
-
- $this->assertEquals([$fileId], $this->announcedFileIds($recorded()));
- }
-
- public function testRemoveRecursiveAnnouncesRealFileIds(): void {
- $storage = new Temporary([]);
- [$cache, $recorded] = $this->cacheWithRecordedEvents($storage);
- $cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
- $fileData = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'text/plain'];
-
- $expected = [$cache->put('folder', $folderData)];
- $expected[] = $cache->put('folder/sub', $folderData);
- $expected[] = $cache->put('folder/foo.txt', $fileData);
- $expected[] = $cache->put('folder/sub/bar.txt', $fileData);
-
- $cache->remove('folder');
-
- $announced = $this->announcedFileIds($recorded());
- sort($expected);
- sort($announced);
- $this->assertEquals($expected, $announced);
- }
-
- public function testRemoveRecursiveAnnouncesEveryChildExactlyOnce(): void {
- $storage = new Temporary([]);
- [$cache, $recorded] = $this->cacheWithRecordedEvents($storage);
- $cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $fileData = ['size' => 1, 'mtime' => 20, 'mimetype' => 'text/plain'];
-
- $expected = [$cache->put('folder', ['size' => 0, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE])];
- for ($i = 0; $i < IQueryBuilder::MAX_IN_PARAMETERS + 1; $i++) {
- $expected[] = $cache->insert("folder/child$i.txt", $fileData);
- }
-
- $cache->remove('folder');
-
- $announced = $this->announcedFileIds($recorded());
- $this->assertCount(count($expected), $announced, 'every removed entry is announced exactly once');
- sort($expected);
- sort($announced);
- $this->assertEquals($expected, $announced);
- }
-
- public function testClearRemovesExtendedAndMetadataEntries(): void {
- $cache = new Cache($this->storage);
- $fileId = $cache->put('foo', ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain', 'upload_time' => 30]);
-
- $metadataManager = Server::get(IFilesMetadataManager::class);
- $metadata = $metadataManager->getMetadata($fileId, true);
- $metadata->setString('test-key', 'value', true);
- $metadataManager->saveMetadata($metadata);
-
- $cache->clear();
-
- $this->assertFalse($cache->inCache('foo'));
- $connection = Server::get(IDBConnection::class);
- foreach (['filecache_extended' => 'fileid', 'files_metadata' => 'file_id', 'files_metadata_index' => 'file_id'] as $table => $column) {
- $query = $connection->getQueryBuilder();
- $query->select($column)
- ->from($table)
- ->where($query->expr()->eq($column, $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
- $this->assertSame([], $query->executeQuery()->fetchFirstColumn(), "$table rows of the cleared storage remain");
- }
- }
-
- public static function folderDataProvider(): array {
- return [
- ['folder'],
- // that was too easy, try something harder
- ['☺, WHITE SMILING FACE, UTF-8 hex E298BA'],
- // what about 4 byte utf-8
- ['😐, NEUTRAL_FACE, UTF-8 hex F09F9890'],
- // now the crazy stuff
- [', UNASSIGNED PRIVATE USE, UTF-8 hex EF9890'],
- // and my favorite
- ['w͢͢͝h͡o͢͡ ̸͢k̵͟n̴͘ǫw̸̛s͘ ̀́w͘͢ḩ̵a҉̡͢t ̧̕h́o̵r͏̵rors̡ ̶͡͠lį̶e͟͟ ̶͝in͢ ͏t̕h̷̡͟e ͟͟d̛a͜r̕͡k̢̨ ͡h̴e͏a̷̢̡rt́͏ ̴̷͠ò̵̶f̸ u̧͘ní̛͜c͢͏o̷͏d̸͢e̡͝']
- ];
- }
-
- public function testEncryptedFolder(): void {
- $file1 = 'folder';
- $file2 = 'folder/bar';
- $file3 = 'folder/foo';
- $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
- $fileData = [];
- $fileData['bar'] = ['size' => 1000, 'encrypted' => 1, 'mtime' => 20, 'mimetype' => 'foo/file'];
- $fileData['foo'] = ['size' => 20, 'encrypted' => 1, 'mtime' => 25, 'mimetype' => 'foo/file'];
-
- $this->cache->put($file1, $data1);
- $this->cache->put($file2, $fileData['bar']);
- $this->cache->put($file3, $fileData['foo']);
-
- $content = $this->cache->getFolderContents($file1);
- $this->assertEquals(count($content), 2);
- foreach ($content as $cachedData) {
- $data = $fileData[$cachedData['name']];
- }
-
- $file4 = 'folder/unkownSize';
- $fileData['unkownSize'] = ['size' => -1, 'mtime' => 25, 'mimetype' => 'foo/file'];
- $this->cache->put($file4, $fileData['unkownSize']);
-
- $this->assertEquals(-1, $this->cache->calculateFolderSize($file1));
-
- $fileData['unkownSize'] = ['size' => 5, 'mtime' => 25, 'mimetype' => 'foo/file'];
- $this->cache->put($file4, $fileData['unkownSize']);
-
- $this->assertEquals(1025, $this->cache->calculateFolderSize($file1));
- // direct cache entry retrieval returns the original values
- $entry = $this->cache->get($file1);
- $this->assertEquals(1025, $entry['size']);
-
- $this->cache->remove($file2);
- $this->cache->remove($file3);
- $this->cache->remove($file4);
- $this->assertEquals(0, $this->cache->calculateFolderSize($file1));
-
- $this->cache->remove('folder');
- $this->assertFalse($this->cache->inCache('folder/foo'));
- $this->assertFalse($this->cache->inCache('folder/bar'));
- }
-
- public function testCalculateFolderSizeWithEncryptedZeroByte(): void {
- $folder = 'enc_folder';
- $this->cache->put($folder, ['size' => -1, 'mtime' => 20, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
-
- // Child 1: zero-byte encrypted file — on-disk 8192 (header only), plaintext 0
- $child1 = $folder . '/empty.enc';
- $this->cache->put($child1, [
- 'size' => 8192,
- 'mtime' => 20,
- 'mimetype' => 'application/octet-stream',
- 'encrypted' => 1,
- 'unencrypted_size' => 0,
- ]);
-
- // Child 2: non-zero encrypted file — opens the write-back gate ($unencryptedMax > 0)
- $child2 = $folder . '/small.enc';
- $this->cache->put($child2, [
- 'size' => 8292,
- 'mtime' => 20,
- 'mimetype' => 'application/octet-stream',
- 'encrypted' => 1,
- 'unencrypted_size' => 100,
- ]);
-
- $this->cache->calculateFolderSize($folder);
-
- $entry = $this->cache->get($folder);
- // Must sum plaintext sizes (0 + 100 = 100), not fall back to on-disk size for
- // the zero-byte child (8192 + 100 = 8292 with the old buggy code)
- $this->assertEquals(100, $entry['unencrypted_size'], 'Folder unencrypted_size should sum plaintext sizes');
- $this->assertEquals(16484, $entry['size'], 'Folder size should sum on-disk sizes (8192 + 8292)');
- }
-
- public function testRootFolderSizeForNonHomeStorage(): void {
- $dir1 = 'knownsize';
- $dir2 = 'unknownsize';
- $fileData = [];
- $fileData[''] = ['size' => -1, 'mtime' => 20, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
- $fileData[$dir1] = ['size' => 1000, 'mtime' => 20, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
- $fileData[$dir2] = ['size' => -1, 'mtime' => 25, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
-
- $this->cache->put($dir1, $fileData[$dir1]);
- $this->cache->put($dir2, $fileData[$dir2]);
-
- $this->assertTrue($this->cache->inCache($dir1));
- $this->assertTrue($this->cache->inCache($dir2));
-
- // check that root size ignored the unknown sizes
- $this->assertEquals(-1, $this->cache->calculateFolderSize(''));
-
- // clean up
- $this->cache->remove('');
- $this->cache->remove($dir1);
- $this->cache->remove($dir2);
-
- $this->assertFalse($this->cache->inCache($dir1));
- $this->assertFalse($this->cache->inCache($dir2));
- }
-
- public function testStatus(): void {
- $this->assertEquals(Cache::NOT_FOUND, $this->cache->getStatus('foo'));
- $this->cache->put('foo', ['size' => -1]);
- $this->assertEquals(Cache::PARTIAL, $this->cache->getStatus('foo'));
- $this->cache->put('foo', ['size' => -1, 'mtime' => 20, 'mimetype' => 'foo/file']);
- $this->assertEquals(Cache::SHALLOW, $this->cache->getStatus('foo'));
- $this->cache->put('foo', ['size' => 10]);
- $this->assertEquals(Cache::COMPLETE, $this->cache->getStatus('foo'));
- }
-
- public static function putWithAllKindOfQuotesData(): array {
- return [
- ['`backtick`'],
- ['´forward´'],
- ['\'single\''],
- ];
- }
-
- /**
- * @param $fileName
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('putWithAllKindOfQuotesData')]
- public function testPutWithAllKindOfQuotes($fileName): void {
- $this->assertEquals(Cache::NOT_FOUND, $this->cache->get($fileName));
- $this->cache->put($fileName, ['size' => 20, 'mtime' => 25, 'mimetype' => 'foo/file', 'etag' => $fileName]);
-
- $cacheEntry = $this->cache->get($fileName);
- $this->assertEquals($fileName, $cacheEntry['etag']);
- $this->assertEquals($fileName, $cacheEntry['path']);
- }
-
- public function testSearch(): void {
- $file1 = 'folder';
- $file2 = 'folder/foobar';
- $file3 = 'folder/foo';
- $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder'];
- $fileData = [];
- $fileData['foobar'] = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'];
- $fileData['foo'] = ['size' => 20, 'mtime' => 25, 'mimetype' => 'foo/file'];
-
- $this->cache->put($file1, $data1);
- $this->cache->put($file2, $fileData['foobar']);
- $this->cache->put($file3, $fileData['foo']);
-
- $this->assertEquals(2, count($this->cache->search('%foo%')));
- $this->assertEquals(1, count($this->cache->search('foo')));
- $this->assertEquals(1, count($this->cache->search('%folder%')));
- $this->assertEquals(1, count($this->cache->search('folder%')));
-
- // case insensitive search should match the same files
- $this->assertEquals(2, count($this->cache->search('%Foo%')));
- $this->assertEquals(1, count($this->cache->search('Foo')));
- $this->assertEquals(1, count($this->cache->search('%Folder%')));
- $this->assertEquals(1, count($this->cache->search('Folder%')));
-
- $this->assertEquals(3, count($this->cache->searchByMime('foo')));
- $this->assertEquals(2, count($this->cache->searchByMime('foo/file')));
- }
-
- public function testSearchQueryByTag(): void {
- $userId = static::getUniqueID('user');
- Server::get(IUserManager::class)->createUser($userId, $userId);
- static::loginAsUser($userId);
- $user = new User($userId, null, Server::get(IEventDispatcher::class));
-
- $file1 = 'folder';
- $file2 = 'folder/foobar';
- $file3 = 'folder/foo';
- $file4 = 'folder/foo2';
- $file5 = 'folder/foo3';
- $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder'];
- $fileData = [];
- $fileData['foobar'] = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'];
- $fileData['foo'] = ['size' => 20, 'mtime' => 25, 'mimetype' => 'foo/file'];
- $fileData['foo2'] = ['size' => 25, 'mtime' => 28, 'mimetype' => 'foo/file'];
- $fileData['foo3'] = ['size' => 88, 'mtime' => 34, 'mimetype' => 'foo/file'];
-
- $id1 = $this->cache->put($file1, $data1);
- $id2 = $this->cache->put($file2, $fileData['foobar']);
- $id3 = $this->cache->put($file3, $fileData['foo']);
- $id4 = $this->cache->put($file4, $fileData['foo2']);
- $id5 = $this->cache->put($file5, $fileData['foo3']);
-
- $tagManager = Server::get(ITagManager::class)->load('files', [], false, $userId);
- $this->assertTrue($tagManager->tagAs($id1, 'tag1'));
- $this->assertTrue($tagManager->tagAs($id1, 'tag2'));
- $this->assertTrue($tagManager->tagAs($id2, 'tag2'));
- $this->assertTrue($tagManager->tagAs($id3, 'tag1'));
- $this->assertTrue($tagManager->tagAs($id4, 'tag2'));
-
- $results = $this->cache->searchQuery(new SearchQuery(
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'tagname', 'tag2'),
- 0, 0, [], $user
- ));
- $this->assertEquals(3, count($results));
-
- usort($results, function ($value1, $value2) {
- return $value1['name'] <=> $value2['name'];
- });
-
- $this->assertEquals('folder', $results[0]['name']);
- $this->assertEquals('foo2', $results[1]['name']);
- $this->assertEquals('foobar', $results[2]['name']);
-
- $tagManager->delete('tag1');
- $tagManager->delete('tag2');
-
- static::logout();
- $user = Server::get(IUserManager::class)->get($userId);
- if ($user !== null) {
- try {
- $user->delete();
- } catch (\Exception $e) {
- }
- }
- }
-
- public function testSearchByQuery(): void {
- $file1 = 'folder';
- $file2 = 'folder/foobar';
- $file3 = 'folder/foo';
- $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder'];
- $fileData = [];
- $fileData['foobar'] = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'];
- $fileData['foo'] = ['size' => 20, 'mtime' => 25, 'mimetype' => 'foo/file'];
-
- $this->cache->put($file1, $data1);
- $this->cache->put($file2, $fileData['foobar']);
- $this->cache->put($file3, $fileData['foo']);
- /** @var IUser $user */
- $user = $this->createMock(IUser::class);
-
- $this->assertCount(1, $this->cache->searchQuery(new SearchQuery(
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'name', 'foo'), 10, 0, [], $user)));
- $this->assertCount(2, $this->cache->searchQuery(new SearchQuery(
- new SearchComparison(ISearchComparison::COMPARE_LIKE, 'name', 'foo%'), 10, 0, [], $user)));
- $this->assertCount(2, $this->cache->searchQuery(new SearchQuery(
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', 'foo/file'), 10, 0, [], $user)));
- $this->assertCount(3, $this->cache->searchQuery(new SearchQuery(
- new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mimetype', 'foo/%'), 10, 0, [], $user)));
- $this->assertCount(1, $this->cache->searchQuery(new SearchQuery(
- new SearchComparison(ISearchComparison::COMPARE_GREATER_THAN, 'size', 100), 10, 0, [], $user)));
- $this->assertCount(2, $this->cache->searchQuery(new SearchQuery(
- new SearchComparison(ISearchComparison::COMPARE_GREATER_THAN_EQUAL, 'size', 100), 10, 0, [], $user)));
- }
-
- public static function movePathProvider(): array {
- return [
- ['folder/foo', 'folder/foobar', ['1', '2']],
- ['folder/foo', 'foo', ['1', '2']],
- ['files/Индустрия_Инженерные системы ЦОД', 'files/Индустрия_Инженерные системы ЦОД1', ['1', '2']],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('movePathProvider')]
- public function testMove($sourceFolder, $targetFolder, $children): void {
- $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/bar'];
- $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
-
- // create folders
- foreach ([$sourceFolder, $targetFolder] as $current) {
- while (strpos($current, '/') > 0) {
- $current = dirname($current);
- $this->cache->put($current, $folderData);
- $this->cache2->put($current, $folderData);
- }
- }
-
- $this->cache->put($sourceFolder, $folderData);
- $this->cache2->put($sourceFolder, $folderData);
- foreach ($children as $child) {
- $this->cache->put($sourceFolder . '/' . $child, $data);
- $this->cache2->put($sourceFolder . '/' . $child, $data);
- }
-
- $this->cache->move($sourceFolder, $targetFolder);
-
- $this->assertFalse($this->cache->inCache($sourceFolder));
- $this->assertTrue($this->cache2->inCache($sourceFolder));
- $this->assertTrue($this->cache->inCache($targetFolder));
- $this->assertFalse($this->cache2->inCache($targetFolder));
- foreach ($children as $child) {
- $this->assertFalse($this->cache->inCache($sourceFolder . '/' . $child));
- $this->assertTrue($this->cache2->inCache($sourceFolder . '/' . $child));
- $this->assertTrue($this->cache->inCache($targetFolder . '/' . $child));
- $this->assertFalse($this->cache2->inCache($targetFolder . '/' . $child));
- }
- }
-
- public function testMoveFromCache(): void {
- $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/bar'];
- $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
-
- $this->cache2->put('folder', $folderData);
- $this->cache2->put('folder/sub', $data);
-
- $this->cache->moveFromCache($this->cache2, 'folder', 'targetfolder');
-
- $this->assertFalse($this->cache2->inCache('folder'));
- $this->assertFalse($this->cache2->inCache('folder/sub'));
-
- $this->assertTrue($this->cache->inCache('targetfolder'));
- $this->assertTrue($this->cache->inCache('targetfolder/sub'));
- }
-
- public function testMoveFromCacheJail(): void {
- $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/bar'];
- $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
-
- $this->cache2->put('folder', $folderData);
- $this->cache2->put('folder/sub', $data);
-
- $jail = new CacheJail($this->cache2, 'folder');
-
- $this->cache->moveFromCache($jail, 'sub', 'targetsub');
-
- $this->assertTrue($this->cache2->inCache('folder'));
- $this->assertFalse($this->cache2->inCache('folder/sub'));
-
- $this->assertTrue($this->cache->inCache('targetsub'));
- $this->assertEquals($this->cache->getId(''), $this->cache->get('targetsub')->getParentId());
- }
-
- public function testCopyFromCachePreservesEncryptedVersion(): void {
- $data = [
- 'size' => 100, 'mtime' => 50, 'mimetype' => 'foo/bar',
- 'encrypted' => true, 'encryptedVersion' => 3,
- ];
- $this->cache->put('source', $data);
- $sourceEntry = $this->cache->get('source');
- $this->assertSame(3, $sourceEntry['encryptedVersion']);
-
- $this->cache->copyFromCache($this->cache, $sourceEntry, 'target');
-
- $targetEntry = $this->cache->get('target');
- $this->assertTrue($targetEntry->isEncrypted());
- $this->assertSame(3, $targetEntry['encryptedVersion']);
- }
-
- public function testCopyFromCachePreservesUnencryptedSize(): void {
- $data = [
- 'size' => 128, 'mtime' => 50, 'mimetype' => 'foo/bar',
- 'encrypted' => true, 'encryptedVersion' => 3, 'unencrypted_size' => 100,
- ];
- $this->cache->put('source', $data);
- $sourceEntry = $this->cache->get('source');
- $this->assertEquals(100, $sourceEntry->getUnencryptedSize());
-
- $this->cache->copyFromCache($this->cache, $sourceEntry, 'target');
-
- $targetEntry = $this->cache->get('target');
- $this->assertEquals(100, $targetEntry->getUnencryptedSize());
- }
-
- public function testCopyFromCacheClearsEncryptedVersionWhenCopyingToNonEncryptedStorage(): void {
- $data = [
- 'size' => 128, 'mtime' => 50, 'mimetype' => 'foo/bar',
- 'encrypted' => true, 'encryptedVersion' => 3, 'unencrypted_size' => 100,
- ];
- $this->cache2->put('source', $data);
- $sourceEntry = $this->cache2->get('source');
-
- $sourceCache = $this->getMockBuilder(Cache::class)
- ->setConstructorArgs([$this->storage2])
- ->onlyMethods(['hasEncryptionWrapper'])
- ->getMock();
- $sourceCache->method('hasEncryptionWrapper')->willReturn(true);
-
- $targetCache = $this->getMockBuilder(Cache::class)
- ->setConstructorArgs([$this->storage])
- ->onlyMethods(['shouldEncrypt'])
- ->getMock();
- $targetCache->method('shouldEncrypt')->willReturn(false);
-
- $targetCache->copyFromCache($sourceCache, $sourceEntry, 'target');
-
- $targetEntry = $targetCache->get('target');
- $this->assertFalse($targetEntry->isEncrypted());
- $this->assertSame(0, $targetEntry['encryptedVersion']);
- // the target is not marked as encrypted, so its size is read from `size`
- $this->assertEquals(0, $targetEntry['unencrypted_size']);
- $this->assertEquals(128, $targetEntry->getSize());
- }
-
- public function testGetIncomplete(): void {
- $file1 = 'folder1';
- $file2 = 'folder2';
- $file3 = 'folder3';
- $file4 = 'folder4';
- $data = ['size' => 10, 'mtime' => 50, 'mimetype' => 'foo/bar'];
-
- $this->cache->put($file1, $data);
- $data['size'] = -1;
- $this->cache->put($file2, $data);
- $this->cache->put($file3, $data);
- $data['size'] = 12;
- $this->cache->put($file4, $data);
-
- $this->assertEquals($file3, $this->cache->getIncomplete());
- }
-
- public function testNonExisting(): void {
- $this->assertFalse($this->cache->get('foo.txt'));
- $this->assertFalse($this->cache->get(-1));
- $this->assertEquals([], $this->cache->getFolderContents('foo'));
- }
-
- public function testGetById(): void {
- $storageId = $this->storage->getId();
- $data = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'];
- $id = $this->cache->put('foo', $data);
-
- if (strlen($storageId) > 64) {
- $storageId = md5($storageId);
- }
- $this->assertEquals([$storageId, 'foo'], Cache::getById($id));
- }
-
- public function testStorageMTime(): void {
- $data = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'];
- $this->cache->put('foo', $data);
- $cachedData = $this->cache->get('foo');
- $this->assertEquals($data['mtime'], $cachedData['storage_mtime']); //if no storage_mtime is saved, mtime should be used
-
- $this->cache->put('foo', ['storage_mtime' => 30]); //when setting storage_mtime, mtime is also set
- $cachedData = $this->cache->get('foo');
- $this->assertEquals(30, $cachedData['storage_mtime']);
- $this->assertEquals(30, $cachedData['mtime']);
-
- $this->cache->put('foo', ['mtime' => 25]); //setting mtime does not change storage_mtime
- $cachedData = $this->cache->get('foo');
- $this->assertEquals(30, $cachedData['storage_mtime']);
- $this->assertEquals(25, $cachedData['mtime']);
- }
-
- public function testLongId(): void {
- $storage = new LongId([]);
- $cache = $storage->getCache();
- $cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $storageId = $storage->getId();
- $data = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'];
- $id = $cache->put('foo', $data);
- $this->assertEquals([md5($storageId), 'foo'], Cache::getById($id));
- }
-
- /**
- * this test show the bug resulting if we have no normalizer installed
- */
- public function testWithoutNormalizer(): void {
- // folder name "Schön" with U+00F6 (normalized)
- $folderWith00F6 = "\x53\x63\x68\xc3\xb6\x6e";
-
- // folder name "Schön" with U+0308 (un-normalized)
- $folderWith0308 = "\x53\x63\x68\x6f\xcc\x88\x6e";
-
- /**
- * @var Cache|\PHPUnit\Framework\MockObject\MockObject $cacheMock
- */
- $cacheMock = $this->getMockBuilder(Cache::class)
- ->onlyMethods(['normalize'])
- ->setConstructorArgs([$this->storage])
- ->getMock();
-
- $cacheMock->expects($this->any())
- ->method('normalize')
- ->willReturnArgument(0);
-
- $data = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
-
- // put root folder
- $this->assertFalse($cacheMock->get('folder'));
- $this->assertGreaterThan(0, $cacheMock->put('folder', $data));
-
- // put un-normalized folder
- $this->assertFalse($cacheMock->get('folder/' . $folderWith0308));
- $this->assertGreaterThan(0, $cacheMock->put('folder/' . $folderWith0308, $data));
-
- // get un-normalized folder by name
- $unNormalizedFolderName = $cacheMock->get('folder/' . $folderWith0308);
-
- // check if database layer normalized the folder name (this should not happen)
- $this->assertEquals($folderWith0308, $unNormalizedFolderName['name']);
-
- // put normalized folder
- $this->assertFalse($cacheMock->get('folder/' . $folderWith00F6));
- $this->assertGreaterThan(0, $cacheMock->put('folder/' . $folderWith00F6, $data));
-
- // this is our bug, we have two different hashes with the same name (Schön)
- $this->assertEquals(2, count($cacheMock->getFolderContents('folder')));
- }
-
- /**
- * this test shows that there is no bug if we use the normalizer
- */
- public function testWithNormalizer(): void {
- if (!class_exists('Patchwork\PHP\Shim\Normalizer')) {
- $this->markTestSkipped('The 3rdparty Normalizer extension is not available.');
- return;
- }
-
- // folder name "Schön" with U+00F6 (normalized)
- $folderWith00F6 = "\x53\x63\x68\xc3\xb6\x6e";
-
- // folder name "Schön" with U+0308 (un-normalized)
- $folderWith0308 = "\x53\x63\x68\x6f\xcc\x88\x6e";
-
- $data = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
-
- // put root folder
- $this->assertFalse($this->cache->get('folder'));
- $this->assertGreaterThan(0, $this->cache->put('folder', $data));
-
- // put un-normalized folder
- $this->assertFalse($this->cache->get('folder/' . $folderWith0308));
- $this->assertGreaterThan(0, $this->cache->put('folder/' . $folderWith0308, $data));
-
- // get un-normalized folder by name
- $unNormalizedFolderName = $this->cache->get('folder/' . $folderWith0308);
-
- // check if folder name was normalized
- $this->assertEquals($folderWith00F6, $unNormalizedFolderName['name']);
-
- // put normalized folder
- $this->assertInstanceOf('\OCP\Files\Cache\ICacheEntry', $this->cache->get('folder/' . $folderWith00F6));
- $this->assertGreaterThan(0, $this->cache->put('folder/' . $folderWith00F6, $data));
-
- // at this point we should have only one folder named "Schön"
- $this->assertEquals(1, count($this->cache->getFolderContents('folder')));
- }
-
- public static function bogusPathNamesProvider(): array {
- return [
- ['/bogus.txt', 'bogus.txt'],
- ['//bogus.txt', 'bogus.txt'],
- ['bogus/', 'bogus'],
- ['bogus//', 'bogus'],
- ];
- }
-
- /**
- * Test bogus paths with leading or doubled slashes
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('bogusPathNamesProvider')]
- public function testBogusPaths($bogusPath, $fixedBogusPath): void {
- $data = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
- $parentId = $this->cache->getId('');
-
- $this->assertGreaterThan(0, $this->cache->put($bogusPath, $data));
-
- $newData = $this->cache->get($fixedBogusPath);
- $this->assertNotFalse($newData);
-
- $this->assertEquals($fixedBogusPath, $newData['path']);
- // parent is the correct one, resolved properly (they used to not be)
- $this->assertEquals($parentId, $newData['parent']);
-
- $newDataFromBogus = $this->cache->get($bogusPath);
- // same entry
- $this->assertEquals($newData, $newDataFromBogus);
- }
-
- public function testNoReuseOfFileId(): void {
- $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain'];
- $this->cache->put('somefile.txt', $data1);
- $info = $this->cache->get('somefile.txt');
- $fileId = $info['fileid'];
- $this->cache->remove('somefile.txt');
- $data2 = ['size' => 200, 'mtime' => 100, 'mimetype' => 'text/plain'];
- $this->cache->put('anotherfile.txt', $data2);
- $info2 = $this->cache->get('anotherfile.txt');
- $fileId2 = $info2['fileid'];
- $this->assertNotEquals($fileId, $fileId2);
- }
-
- public static function escapingProvider(): array {
- return [
- ['foo'],
- ['o%'],
- ['oth_r'],
- ];
- }
-
- /**
- * @param string $name
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('escapingProvider')]
- public function testEscaping($name): void {
- $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain'];
- $this->cache->put($name, $data);
- $this->assertTrue($this->cache->inCache($name));
- $retrievedData = $this->cache->get($name);
- foreach ($data as $key => $value) {
- $this->assertEquals($value, $retrievedData[$key]);
- }
- $this->cache->move($name, $name . 'asd');
- $this->assertFalse($this->cache->inCache($name));
- $this->assertTrue($this->cache->inCache($name . 'asd'));
- $this->cache->remove($name . 'asd');
- $this->assertFalse($this->cache->inCache($name . 'asd'));
- $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
- $this->cache->put($name, $folderData);
- $this->cache->put('other', $folderData);
- $childs = ['asd', 'bar', 'foo', 'sub/folder'];
- $this->cache->put($name . '/sub', $folderData);
- $this->cache->put('other/sub', $folderData);
- foreach ($childs as $child) {
- $this->cache->put($name . '/' . $child, $data);
- $this->cache->put('other/' . $child, $data);
- $this->assertTrue($this->cache->inCache($name . '/' . $child));
- }
- $this->cache->move($name, $name . 'asd');
- foreach ($childs as $child) {
- $this->assertTrue($this->cache->inCache($name . 'asd/' . $child));
- $this->assertTrue($this->cache->inCache('other/' . $child));
- }
- foreach ($childs as $child) {
- $this->cache->remove($name . 'asd/' . $child);
- $this->assertFalse($this->cache->inCache($name . 'asd/' . $child));
- $this->assertTrue($this->cache->inCache('other/' . $child));
- }
- }
-
- public function testExtended(): void {
- $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
-
- $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain', 'creation_time' => 20];
- $id1 = $this->cache->put('foo1', $data);
- $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain', 'upload_time' => 30];
- $this->cache->put('foo2', $data);
- $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain', 'metadata_etag' => 'foo'];
- $this->cache->put('foo3', $data);
- $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain'];
- $id4 = $this->cache->put('foo4', $data);
-
- $entry = $this->cache->get($id1);
- $this->assertEquals(20, $entry->getCreationTime());
- $this->assertEquals(0, $entry->getUploadTime());
- $this->assertEquals(null, $entry->getMetadataEtag());
-
- $entries = $this->cache->getFolderContents('');
- $this->assertCount(4, $entries);
-
- $entriesByName = [];
- foreach ($entries as $entry) {
- $entriesByName[$entry->getName()] = $entry;
- }
-
- $this->assertArrayHasKey('foo1', $entriesByName);
- $this->assertArrayHasKey('foo2', $entriesByName);
- $this->assertArrayHasKey('foo3', $entriesByName);
- $this->assertArrayHasKey('foo4', $entriesByName);
-
- $this->assertEquals(20, $entriesByName['foo1']->getCreationTime());
- $this->assertEquals(0, $entriesByName['foo1']->getUploadTime());
- $this->assertEquals(null, $entriesByName['foo1']->getMetadataEtag());
-
- $this->assertEquals(0, $entriesByName['foo2']->getCreationTime());
- $this->assertEquals(30, $entriesByName['foo2']->getUploadTime());
- $this->assertEquals(null, $entriesByName['foo2']->getMetadataEtag());
-
- $this->assertEquals(0, $entriesByName['foo3']->getCreationTime());
- $this->assertEquals(0, $entriesByName['foo3']->getUploadTime());
- $this->assertEquals('foo', $entriesByName['foo3']->getMetadataEtag());
-
- $this->assertEquals(0, $entriesByName['foo4']->getCreationTime());
- $this->assertEquals(0, $entriesByName['foo4']->getUploadTime());
- $this->assertEquals(null, $entriesByName['foo4']->getMetadataEtag());
-
- $this->cache->update($id1, ['upload_time' => 25]);
-
- $entry = $this->cache->get($id1);
- $this->assertEquals(20, $entry->getCreationTime());
- $this->assertEquals(25, $entry->getUploadTime());
- $this->assertEquals(null, $entry->getMetadataEtag());
-
- $this->cache->put('sub', $folderData);
-
- $this->cache->move('foo1', 'sub/foo1');
-
- $entries = $this->cache->getFolderContents('sub');
- $this->assertCount(1, $entries);
-
- $this->assertEquals('foo1', $entries[0]->getName());
- $this->assertEquals(20, $entries[0]->getCreationTime());
- $this->assertEquals(25, $entries[0]->getUploadTime());
- $this->assertEquals(null, $entries[0]->getMetadataEtag());
-
- $this->cache->update($id4, ['upload_time' => 25]);
-
- $entry = $this->cache->get($id4);
- $this->assertEquals(0, $entry->getCreationTime());
- $this->assertEquals(25, $entry->getUploadTime());
- $this->assertEquals(null, $entry->getMetadataEtag());
-
- $this->cache->remove('sub');
- }
-}
--- a/tests/lib/Files/Cache/FileAccessTest.php
+++ /dev/null
@@ -1,453 +0,0 @@
-dbConnection = Server::get(IDBConnection::class);
-
- // Ensure FileAccess is instantiated with the real connection
- $this->fileAccess = new FileAccess(
- $this->dbConnection,
- Server::get(SystemConfig::class),
- Server::get(LoggerInterface::class),
- Server::get(FilesMetadataManager::class),
- Server::get(IMimeTypeLoader::class)
- );
-
- // Clear and prepare `filecache` table for tests
- $queryBuilder = $this->dbConnection->getQueryBuilder()->runAcrossAllShards();
- $queryBuilder->delete('filecache')->executeStatement();
-
- // Clean up potential leftovers from other tests
- $queryBuilder = $this->dbConnection->getQueryBuilder();
- $queryBuilder->delete('mounts')->executeStatement();
-
- $this->setUpTestDatabaseForGetDistinctMounts();
- $this->setUpTestDatabaseForGetByAncestorInStorage();
- }
-
- private function setUpTestDatabaseForGetDistinctMounts(): void {
- $queryBuilder = $this->dbConnection->getQueryBuilder();
-
- // Insert test data
- $queryBuilder->insert('mounts')
- ->values([
- 'storage_id' => $queryBuilder->createNamedParameter(1, IQueryBuilder::PARAM_INT),
- 'root_id' => $queryBuilder->createNamedParameter(10, IQueryBuilder::PARAM_INT),
- 'mount_provider_class' => $queryBuilder->createNamedParameter('TestProviderClass1'),
- 'mount_point' => $queryBuilder->createNamedParameter('/files'),
- 'mount_point_hash' => $queryBuilder->createNamedParameter(hash('xxh128', '/files')),
- 'user_id' => $queryBuilder->createNamedParameter('test'),
- ])
- ->executeStatement();
-
- $queryBuilder->insert('mounts')
- ->values([
- 'storage_id' => $queryBuilder->createNamedParameter(3, IQueryBuilder::PARAM_INT),
- 'root_id' => $queryBuilder->createNamedParameter(30, IQueryBuilder::PARAM_INT),
- 'mount_provider_class' => $queryBuilder->createNamedParameter('TestProviderClass1'),
- 'mount_point' => $queryBuilder->createNamedParameter('/documents'),
- 'mount_point_hash' => $queryBuilder->createNamedParameter(hash('xxh128', '/documents')),
- 'user_id' => $queryBuilder->createNamedParameter('test'),
- ])
- ->executeStatement();
-
- $queryBuilder->insert('mounts')
- ->values([
- 'storage_id' => $queryBuilder->createNamedParameter(4, IQueryBuilder::PARAM_INT),
- 'root_id' => $queryBuilder->createNamedParameter(31, IQueryBuilder::PARAM_INT),
- 'mount_provider_class' => $queryBuilder->createNamedParameter('TestProviderClass2'),
- 'mount_point' => $queryBuilder->createNamedParameter('/foobar'),
- 'mount_point_hash' => $queryBuilder->createNamedParameter(hash('xxh128', '/foobar')),
- 'user_id' => $queryBuilder->createNamedParameter('test'),
- ])
- ->executeStatement();
- }
-
- /**
- * Test that getDistinctMounts returns all mounts without filters
- */
- public function testGetDistinctMountsWithoutFilters(): void {
- $result = iterator_to_array($this->fileAccess->getDistinctMounts([], false));
-
- $this->assertCount(3, $result);
-
- $this->assertEquals([
- 'storage_id' => 1,
- 'root_id' => 10,
- 'overridden_root' => 10,
- ], $result[0]);
-
- $this->assertEquals([
- 'storage_id' => 3,
- 'root_id' => 30,
- 'overridden_root' => 30,
- ], $result[1]);
-
- $this->assertEquals([
- 'storage_id' => 4,
- 'root_id' => 31,
- 'overridden_root' => 31,
- ], $result[2]);
- }
-
- /**
- * Test that getDistinctMounts applies filtering by mount providers
- */
- public function testGetDistinctMountsWithMountProviderFilter(): void {
- $result = iterator_to_array($this->fileAccess->getDistinctMounts(['TestProviderClass1'], false));
-
- $this->assertCount(2, $result);
-
- $this->assertEquals([
- 'storage_id' => 1,
- 'root_id' => 10,
- 'overridden_root' => 10,
- ], $result[0]);
-
- $this->assertEquals([
- 'storage_id' => 3,
- 'root_id' => 30,
- 'overridden_root' => 30,
- ], $result[1]);
- }
-
- /**
- * Test that getDistinctMounts rewrites home directory paths
- */
- public function testGetDistinctMountsWithRewriteHomeDirectories(): void {
- // Add additional test data for a home directory mount
- $queryBuilder = $this->dbConnection->getQueryBuilder();
- $queryBuilder->insert('mounts')
- ->values([
- 'storage_id' => $queryBuilder->createNamedParameter(4, IQueryBuilder::PARAM_INT),
- 'root_id' => $queryBuilder->createNamedParameter(40, IQueryBuilder::PARAM_INT),
- 'mount_provider_class' => $queryBuilder->createNamedParameter(LocalHomeMountProvider::class),
- 'mount_point' => $queryBuilder->createNamedParameter('/home/user'),
- 'mount_point_hash' => $queryBuilder->createNamedParameter(hash('xxh128', '/home/user')),
- 'user_id' => $queryBuilder->createNamedParameter('test'),
- ])
- ->executeStatement();
-
- // Add a mount that is mounted in the home directory
- $queryBuilder = $this->dbConnection->getQueryBuilder();
- $queryBuilder->insert('mounts')
- ->values([
- 'storage_id' => $queryBuilder->createNamedParameter(5, IQueryBuilder::PARAM_INT),
- 'root_id' => $queryBuilder->createNamedParameter(41, IQueryBuilder::PARAM_INT),
- 'mount_provider_class' => $queryBuilder->createNamedParameter('TestMountProvider3'),
- 'mount_point' => $queryBuilder->createNamedParameter('/test/files/foobar'),
- 'mount_point_hash' => $queryBuilder->createNamedParameter(hash('xxh128', '/test/files/foobar')),
- 'user_id' => $queryBuilder->createNamedParameter('test'),
- ])
- ->executeStatement();
-
- // Simulate adding a "files" directory to the filecache table
- $queryBuilder = $this->dbConnection->getQueryBuilder()->runAcrossAllShards();
- $queryBuilder->delete('filecache')->executeStatement();
- $queryBuilder = $this->dbConnection->getQueryBuilder();
- $queryBuilder->insert('filecache')
- ->values([
- 'fileid' => $queryBuilder->createNamedParameter(99, IQueryBuilder::PARAM_INT),
- 'storage' => $queryBuilder->createNamedParameter(4, IQueryBuilder::PARAM_INT),
- 'parent' => $queryBuilder->createNamedParameter(40),
- 'name' => $queryBuilder->createNamedParameter('files'),
- 'path' => $queryBuilder->createNamedParameter('files'),
- 'path_hash' => $queryBuilder->createNamedParameter(md5('files')),
- ])
- ->executeStatement();
-
- $result = iterator_to_array($this->fileAccess->getDistinctMounts());
-
- $this->assertCount(2, $result);
-
- $this->assertEquals([
- 'storage_id' => 4,
- 'root_id' => 40,
- 'overridden_root' => 99,
- ], $result[0]);
-
- $this->assertEquals([
- 'storage_id' => 5,
- 'root_id' => 41,
- 'overridden_root' => 41,
- ], $result[1]);
- }
-
- private function setUpTestDatabaseForGetByAncestorInStorage(): void {
- // prepare `filecache` table for tests
- $queryBuilder = $this->dbConnection->getQueryBuilder();
-
- $queryBuilder->insert('filecache')
- ->values([
- 'fileid' => 1,
- 'parent' => 0,
- 'path' => $queryBuilder->createNamedParameter('files'),
- 'path_hash' => $queryBuilder->createNamedParameter(md5('files')),
- 'storage' => $queryBuilder->createNamedParameter(1),
- 'name' => $queryBuilder->createNamedParameter('files'),
- 'mimetype' => 1,
- 'encrypted' => 0,
- 'size' => 1,
- ])
- ->executeStatement();
-
- $queryBuilder->insert('filecache')
- ->values([
- 'fileid' => 2,
- 'parent' => 1,
- 'path' => $queryBuilder->createNamedParameter('files/documents'),
- 'path_hash' => $queryBuilder->createNamedParameter(md5('files/documents')),
- 'storage' => $queryBuilder->createNamedParameter(1),
- 'name' => $queryBuilder->createNamedParameter('documents'),
- 'mimetype' => 2,
- 'encrypted' => 1,
- 'size' => 1,
- ])
- ->executeStatement();
-
- $queryBuilder->insert('filecache')
- ->values([
- 'fileid' => 3,
- 'parent' => 1,
- 'path' => $queryBuilder->createNamedParameter('files/photos'),
- 'path_hash' => $queryBuilder->createNamedParameter(md5('files/photos')),
- 'storage' => $queryBuilder->createNamedParameter(1),
- 'name' => $queryBuilder->createNamedParameter('photos'),
- 'mimetype' => 3,
- 'encrypted' => 1,
- 'size' => 1,
- ])
- ->executeStatement();
-
- $queryBuilder->insert('filecache')
- ->values([
- 'fileid' => 4,
- 'parent' => 3,
- 'path' => $queryBuilder->createNamedParameter('files/photos/endtoendencrypted'),
- 'path_hash' => $queryBuilder->createNamedParameter(md5('files/photos/endtoendencrypted')),
- 'storage' => $queryBuilder->createNamedParameter(1),
- 'name' => $queryBuilder->createNamedParameter('endtoendencrypted'),
- 'mimetype' => 4,
- 'encrypted' => 0,
- 'size' => 1,
- ])
- ->executeStatement();
-
- $queryBuilder->insert('filecache')
- ->values([
- 'fileid' => 5,
- 'parent' => 1,
- 'path' => $queryBuilder->createNamedParameter('files/serversideencrypted'),
- 'path_hash' => $queryBuilder->createNamedParameter(md5('files/serversideencrypted')),
- 'storage' => $queryBuilder->createNamedParameter(1),
- 'name' => $queryBuilder->createNamedParameter('serversideencrypted'),
- 'mimetype' => 4,
- 'encrypted' => 1,
- 'size' => 1,
- ])
- ->executeStatement();
-
- $queryBuilder->insert('filecache')
- ->values([
- 'fileid' => 6,
- 'parent' => 0,
- 'path' => $queryBuilder->createNamedParameter('files/storage2'),
- 'path_hash' => $queryBuilder->createNamedParameter(md5('files/storage2')),
- 'storage' => $queryBuilder->createNamedParameter(2),
- 'name' => $queryBuilder->createNamedParameter('storage2'),
- 'mimetype' => 5,
- 'encrypted' => 0,
- 'size' => 1,
- ])
- ->executeStatement();
-
- $queryBuilder->insert('filecache')
- ->values([
- 'fileid' => 7,
- 'parent' => 6,
- 'path' => $queryBuilder->createNamedParameter('files/storage2/file'),
- 'path_hash' => $queryBuilder->createNamedParameter(md5('files/storage2/file')),
- 'storage' => $queryBuilder->createNamedParameter(2),
- 'name' => $queryBuilder->createNamedParameter('file'),
- 'mimetype' => 6,
- 'encrypted' => 0,
- 'size' => 1,
- ])
- ->executeStatement();
- }
-
- /**
- * Test fetching files by ancestor in storage.
- */
- public function testGetByAncestorInStorage(): void {
- $generator = $this->fileAccess->getByAncestorInStorage(
- 1, // storageId
- 1, // rootId
- 0, // lastFileId
- 10, // maxResults
- [], // mimeTypes
- true, // include end-to-end encrypted files
- true, // include server-side encrypted files
- );
-
- $result = iterator_to_array($generator);
-
- $this->assertCount(4, $result);
-
- $paths = array_map(fn (CacheEntry $entry) => $entry->getPath(), $result);
- $this->assertEquals([
- 'files/documents',
- 'files/photos',
- 'files/photos/endtoendencrypted',
- 'files/serversideencrypted',
- ], $paths);
- }
-
- /**
- * Test filtering by mime types.
- */
- public function testGetByAncestorInStorageWithMimeTypes(): void {
- $generator = $this->fileAccess->getByAncestorInStorage(
- 1,
- 1,
- 0,
- 10,
- [2], // Only include documents (mimetype=2)
- true,
- true,
- );
-
- $result = iterator_to_array($generator);
-
- $this->assertCount(1, $result);
- $this->assertEquals('files/documents', $result[0]->getPath());
- }
-
- /**
- * Test excluding end-to-end encrypted files.
- */
- public function testGetByAncestorInStorageWithoutEndToEndEncrypted(): void {
- $generator = $this->fileAccess->getByAncestorInStorage(
- 1,
- 1,
- 0,
- 10,
- [],
- false, // exclude end-to-end encrypted files
- true,
- );
-
- $result = iterator_to_array($generator);
-
- $this->assertCount(3, $result);
- $paths = array_map(fn (CacheEntry $entry) => $entry->getPath(), $result);
- $this->assertEquals(['files/documents', 'files/photos', 'files/serversideencrypted'], $paths);
- }
-
- /**
- * Test excluding server-side encrypted files.
- */
- public function testGetByAncestorInStorageWithoutServerSideEncrypted(): void {
- $generator = $this->fileAccess->getByAncestorInStorage(
- 1,
- 1,
- 0,
- 10,
- [],
- true,
- false, // exclude server-side encrypted files
- );
-
- $result = iterator_to_array($generator);
-
- $this->assertCount(1, $result);
- $this->assertEquals('files/photos/endtoendencrypted', $result[0]->getPath());
- }
-
- /**
- * Test max result limits.
- */
- public function testGetByAncestorInStorageWithMaxResults(): void {
- $generator = $this->fileAccess->getByAncestorInStorage(
- 1,
- 1,
- 0,
- 1, // Limit to 1 result
- [],
- true,
- true,
- );
-
- $result = iterator_to_array($generator);
-
- $this->assertCount(1, $result);
- $this->assertEquals('files/documents', $result[0]->getPath());
- }
-
- /**
- * Test rootId filter
- */
- public function testGetByAncestorInStorageWithRootIdFilter(): void {
- $generator = $this->fileAccess->getByAncestorInStorage(
- 1,
- 3, // Filter by rootId
- 0,
- 10,
- [],
- true,
- true,
- );
-
- $result = iterator_to_array($generator);
-
- $this->assertCount(1, $result);
- $this->assertEquals('files/photos/endtoendencrypted', $result[0]->getPath());
- }
-
- /**
- * Test rootId filter
- */
- public function testGetByAncestorInStorageWithStorageFilter(): void {
- $generator = $this->fileAccess->getByAncestorInStorage(
- 2, // Filter by storage
- 6, // and by rootId
- 0,
- 10,
- [],
- true,
- true,
- );
-
- $result = iterator_to_array($generator);
-
- $this->assertCount(1, $result);
- $this->assertEquals('files/storage2/file', $result[0]->getPath());
- }
-}
--- a/tests/lib/Files/Cache/HomeCacheTest.php
+++ /dev/null
@@ -1,117 +0,0 @@
-home;
- }
-
- /**
- * @return string
- */
- #[\Override]
- public function getUID(): string {
- return $this->uid;
- }
-}
-
-#[Group('DB')]
-class HomeCacheTest extends TestCase {
- private Home $storage;
- private ICache $cache;
- private User $user;
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- $this->user = new DummyUser('foo', Server::get(ITempManager::class)->getTemporaryFolder());
- $this->storage = new Home(['user' => $this->user]);
- $this->cache = $this->storage->getCache();
- }
-
- /**
- * Tests that the root and files folder size calculation ignores the subdirs
- * that have an unknown size. This makes sure that quota calculation still
- * works as it's based on the "files" folder size.
- */
- public function testRootFolderSizeIgnoresUnknownUpdate(): void {
- $dir1 = 'files/knownsize';
- $dir2 = 'files/unknownsize';
- $fileData = [];
- $fileData[''] = ['size' => -1, 'mtime' => 20, 'mimetype' => 'httpd/unix-directory'];
- $fileData['files'] = ['size' => -1, 'mtime' => 20, 'mimetype' => 'httpd/unix-directory'];
- $fileData[$dir1] = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'httpd/unix-directory'];
- $fileData[$dir2] = ['size' => -1, 'mtime' => 25, 'mimetype' => 'httpd/unix-directory'];
-
- $this->cache->put('', $fileData['']);
- $this->cache->put('files', $fileData['files']);
- $this->cache->put($dir1, $fileData[$dir1]);
- $this->cache->put($dir2, $fileData[$dir2]);
-
- $this->assertTrue($this->cache->inCache('files'));
- $this->assertTrue($this->cache->inCache($dir1));
- $this->assertTrue($this->cache->inCache($dir2));
-
- // check that files and root size ignored the unknown sizes
- $this->assertEquals(1000, $this->cache->calculateFolderSize('files'));
-
- // clean up
- $this->cache->remove('');
- $this->cache->remove('files');
- $this->cache->remove($dir1);
- $this->cache->remove($dir2);
-
- $this->assertFalse($this->cache->inCache('files'));
- $this->assertFalse($this->cache->inCache($dir1));
- $this->assertFalse($this->cache->inCache($dir2));
- }
-
- public function testRootFolderSizeIsFilesSize(): void {
- $dir1 = 'files';
- $afile = 'test.txt';
- $fileData = [];
- $fileData[''] = ['size' => 1500, 'mtime' => 20, 'mimetype' => 'httpd/unix-directory'];
- $fileData[$dir1] = ['size' => 1000, 'mtime' => 20, 'mimetype' => 'httpd/unix-directory'];
- $fileData[$afile] = ['size' => 500, 'mtime' => 20];
-
- $this->cache->put('', $fileData['']);
- $this->cache->put($dir1, $fileData[$dir1]);
-
- $this->assertTrue($this->cache->inCache($dir1));
-
- // check that root size ignored the unknown sizes
- $data = $this->cache->get('files');
- $this->assertEquals(1000, $data['size']);
- $data = $this->cache->get('');
- $this->assertEquals(1000, $data['size']);
-
- // clean up
- $this->cache->remove('');
- $this->cache->remove($dir1);
-
- $this->assertFalse($this->cache->inCache($dir1));
- }
-}
--- a/tests/lib/Files/Cache/LocalRootScannerTest.php
+++ /dev/null
@@ -1,45 +0,0 @@
-getTemporaryFolder();
- $this->storage = new LocalRootStorage(['datadir' => $folder]);
- }
-
- public function testDontScanUsers(): void {
- $this->storage->mkdir('foo');
- $this->storage->mkdir('foo/bar');
-
- $this->storage->getScanner()->scan('');
- $this->assertFalse($this->storage->getCache()->inCache('foo'));
- }
-
- public function testDoScanAppData(): void {
- $this->storage->mkdir('appdata_foo');
- $this->storage->mkdir('appdata_foo/bar');
-
- $this->storage->getScanner()->scan('');
- $this->assertTrue($this->storage->getCache()->inCache('appdata_foo'));
- $this->assertTrue($this->storage->getCache()->inCache('appdata_foo/bar'));
- }
-}
--- a/tests/lib/Files/Cache/MoveFromCacheTraitTest.php
+++ /dev/null
@@ -1,39 +0,0 @@
-storage = new Temporary([]);
- $this->storage2 = new Temporary([]);
- $this->cache = new FallBackCrossCacheMoveCache($this->storage);
- $this->cache2 = new FallBackCrossCacheMoveCache($this->storage2);
-
- $this->cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $this->cache2->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- }
-}
--- a/tests/lib/Files/Cache/PropagatorTest.php
+++ /dev/null
@@ -1,134 +0,0 @@
-storage = new Temporary();
- $this->storage->mkdir('foo/bar');
- $this->storage->file_put_contents('foo/bar/file.txt', 'bar');
- $this->storage->getScanner()->scan('');
- }
-
- /**
- * @param $paths
- * @return ICacheEntry[]
- */
- private function getFileInfos($paths) {
- $values = array_map(function ($path) {
- return $this->storage->getCache()->get($path);
- }, $paths);
- return array_combine($paths, $values);
- }
-
- public function testEtagPropagation(): void {
- $paths = ['', 'foo', 'foo/bar'];
- $oldInfos = $this->getFileInfos($paths);
- $this->storage->getPropagator()->propagateChange('foo/bar/file.txt', time());
- $newInfos = $this->getFileInfos($paths);
-
- foreach ($oldInfos as $i => $oldInfo) {
- $this->assertNotEquals($oldInfo->getEtag(), $newInfos[$i]->getEtag());
- }
- }
-
- public function testTimePropagation(): void {
- $paths = ['', 'foo', 'foo/bar'];
- $oldTime = time() - 200;
- $targetTime = time() - 100;
- $now = time();
- $cache = $this->storage->getCache();
- $cache->put('', ['mtime' => $now]);
- $cache->put('foo', ['mtime' => $now]);
- $cache->put('foo/bar', ['mtime' => $oldTime]);
- $cache->put('foo/bar/file.txt', ['mtime' => $oldTime]);
- $this->storage->getPropagator()->propagateChange('foo/bar/file.txt', $targetTime);
- $newInfos = $this->getFileInfos($paths);
-
- $this->assertEquals($targetTime, $newInfos['foo/bar']->getMTime());
-
- // dont lower mtimes
- $this->assertEquals($now, $newInfos['foo']->getMTime());
- $this->assertEquals($now, $newInfos['']->getMTime());
- }
-
- public function testSizePropagation(): void {
- $paths = ['', 'foo', 'foo/bar'];
- $oldInfos = $this->getFileInfos($paths);
- $this->storage->getPropagator()->propagateChange('foo/bar/file.txt', time(), 10);
- $newInfos = $this->getFileInfos($paths);
-
- foreach ($oldInfos as $i => $oldInfo) {
- $this->assertEquals($oldInfo->getSize() + 10, $newInfos[$i]->getSize());
- }
- }
-
- public function testSizePropagationNoNegative(): void {
- $paths = ['', 'foo', 'foo/bar'];
- $oldInfos = $this->getFileInfos($paths);
- $this->storage->getPropagator()->propagateChange('foo/bar/file.txt', time(), -100);
- $newInfos = $this->getFileInfos($paths);
-
- foreach ($oldInfos as $i => $oldInfo) {
- $this->assertEquals(-1, $newInfos[$i]->getSize());
- }
- }
-
- public function testBatchedPropagation(): void {
- $this->storage->mkdir('foo/baz');
- $this->storage->mkdir('asd');
- $this->storage->file_put_contents('asd/file.txt', 'bar');
- $this->storage->file_put_contents('foo/baz/file.txt', 'bar');
- $this->storage->getScanner()->scan('');
-
- $paths = ['', 'foo', 'foo/bar', 'asd', 'foo/baz'];
-
- $oldInfos = $this->getFileInfos($paths);
- $propagator = $this->storage->getPropagator();
-
- $propagator->beginBatch();
- $propagator->propagateChange('asd/file.txt', time(), 10);
- $propagator->propagateChange('foo/bar/file.txt', time(), 2);
-
- $newInfos = $this->getFileInfos($paths);
-
- // no changes until we finish the batch
- foreach ($oldInfos as $i => $oldInfo) {
- $this->assertEquals($oldInfo->getSize(), $newInfos[$i]->getSize());
- $this->assertEquals($oldInfo->getEtag(), $newInfos[$i]->getEtag());
- $this->assertEquals($oldInfo->getMTime(), $newInfos[$i]->getMTime());
- }
-
- $propagator->commitBatch();
-
- $newInfos = $this->getFileInfos($paths);
-
- foreach ($oldInfos as $i => $oldInfo) {
- if ($oldInfo->getPath() !== 'foo/baz') {
- $this->assertNotEquals($oldInfo->getEtag(), $newInfos[$i]->getEtag(), "etag for {$oldInfo->getPath()} not updated");
- }
- }
-
- $this->assertEquals($oldInfos['']->getSize() + 12, $newInfos['']->getSize());
- $this->assertEquals($oldInfos['asd']->getSize() + 10, $newInfos['asd']->getSize());
- $this->assertEquals($oldInfos['foo']->getSize() + 2, $newInfos['foo']->getSize());
- $this->assertEquals($oldInfos['foo/bar']->getSize() + 2, $newInfos['foo/bar']->getSize());
- $this->assertEquals($oldInfos['foo/baz']->getSize(), $newInfos['foo/baz']->getSize());
- }
-}
--- a/tests/lib/Files/Cache/ScannerTest.php
+++ /dev/null
@@ -1,455 +0,0 @@
-storage = new Temporary([]);
- $this->scanner = new Scanner($this->storage);
- $this->cache = new Cache($this->storage);
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->cache->clear();
-
- parent::tearDown();
- }
-
- public function testFile(): void {
- $data = "dummy file data\n";
- $this->storage->file_put_contents('foo.txt', $data);
- $this->scanner->scanFile('foo.txt');
-
- $this->assertEquals($this->cache->inCache('foo.txt'), true);
- $cachedData = $this->cache->get('foo.txt');
- $this->assertEquals($cachedData['size'], strlen($data));
- $this->assertEquals($cachedData['mimetype'], 'text/plain');
- $this->assertNotEquals($cachedData['parent'], -1); //parent folders should be scanned automatically
-
- $data = file_get_contents(OC::$SERVERROOT . '/core/img/logo/logo.png');
- $this->storage->file_put_contents('foo.png', $data);
- $this->scanner->scanFile('foo.png');
-
- $this->assertEquals($this->cache->inCache('foo.png'), true);
- $cachedData = $this->cache->get('foo.png');
- $this->assertEquals($cachedData['size'], strlen($data));
- $this->assertEquals($cachedData['mimetype'], 'image/png');
- }
-
- public function testFile4Byte(): void {
- $data = "dummy file data\n";
- $this->storage->file_put_contents('foo🙈.txt', $data);
-
- if (Server::get(IDBConnection::class)->supports4ByteText()) {
- $this->assertNotNull($this->scanner->scanFile('foo🙈.txt'));
- $this->assertTrue($this->cache->inCache('foo🙈.txt'), true);
-
- $cachedData = $this->cache->get('foo🙈.txt');
- $this->assertEquals(strlen($data), $cachedData['size']);
- $this->assertEquals('text/plain', $cachedData['mimetype']);
- $this->assertNotEquals(-1, $cachedData['parent']); //parent folders should be scanned automatically
- } else {
- $this->assertNull($this->scanner->scanFile('foo🙈.txt'));
- $this->assertFalse($this->cache->inCache('foo🙈.txt'), true);
- }
- }
-
- public function testFileInvalidChars(): void {
- $data = "dummy file data\n";
- $this->storage->file_put_contents("foo\nbar.txt", $data);
-
- $this->assertNull($this->scanner->scanFile("foo\nbar.txt"));
- $this->assertFalse($this->cache->inCache("foo\nbar.txt"), true);
- }
-
- private function fillTestFolders() {
- $textData = "dummy file data\n";
- $imgData = file_get_contents(OC::$SERVERROOT . '/core/img/logo/logo.png');
- $this->storage->mkdir('folder');
- $this->storage->file_put_contents('foo.txt', $textData);
- $this->storage->file_put_contents('foo.png', $imgData);
- $this->storage->file_put_contents('folder/bar.txt', $textData);
- }
-
- public function testFolder(): void {
- $this->fillTestFolders();
-
- $this->scanner->scan('');
- $this->assertEquals($this->cache->inCache(''), true);
- $this->assertEquals($this->cache->inCache('foo.txt'), true);
- $this->assertEquals($this->cache->inCache('foo.png'), true);
- $this->assertEquals($this->cache->inCache('folder'), true);
- $this->assertEquals($this->cache->inCache('folder/bar.txt'), true);
-
- $cachedDataText = $this->cache->get('foo.txt');
- $cachedDataText2 = $this->cache->get('foo.txt');
- $cachedDataImage = $this->cache->get('foo.png');
- $cachedDataFolder = $this->cache->get('');
- $cachedDataFolder2 = $this->cache->get('folder');
-
- $this->assertEquals($cachedDataImage['parent'], $cachedDataText['parent']);
- $this->assertEquals($cachedDataFolder['fileid'], $cachedDataImage['parent']);
- $this->assertEquals($cachedDataFolder['size'], $cachedDataImage['size'] + $cachedDataText['size'] + $cachedDataText2['size']);
- $this->assertEquals($cachedDataFolder2['size'], $cachedDataText2['size']);
- }
-
- public function testShallow(): void {
- $this->fillTestFolders();
-
- $this->scanner->scan('', IScanner::SCAN_SHALLOW);
- $this->assertEquals($this->cache->inCache(''), true);
- $this->assertEquals($this->cache->inCache('foo.txt'), true);
- $this->assertEquals($this->cache->inCache('foo.png'), true);
- $this->assertEquals($this->cache->inCache('folder'), true);
- $this->assertEquals($this->cache->inCache('folder/bar.txt'), false);
-
- $cachedDataFolder = $this->cache->get('');
- $cachedDataFolder2 = $this->cache->get('folder');
-
- $this->assertEquals(-1, $cachedDataFolder['size']);
- $this->assertEquals(-1, $cachedDataFolder2['size']);
-
- $this->scanner->scan('folder', IScanner::SCAN_SHALLOW);
-
- $cachedDataFolder2 = $this->cache->get('folder');
-
- $this->assertNotEquals($cachedDataFolder2['size'], -1);
-
- $this->cache->correctFolderSize('folder');
-
- $cachedDataFolder = $this->cache->get('');
- $this->assertNotEquals($cachedDataFolder['size'], -1);
- }
-
- public function testBackgroundScan(): void {
- $this->fillTestFolders();
- $this->storage->mkdir('folder2');
- $this->storage->file_put_contents('folder2/bar.txt', 'foobar');
-
- $this->scanner->scan('', IScanner::SCAN_SHALLOW);
- $this->assertFalse($this->cache->inCache('folder/bar.txt'));
- $this->assertFalse($this->cache->inCache('folder/2bar.txt'));
- $cachedData = $this->cache->get('');
- $this->assertEquals(-1, $cachedData['size']);
-
- $this->scanner->backgroundScan();
-
- $this->assertTrue($this->cache->inCache('folder/bar.txt'));
- $this->assertTrue($this->cache->inCache('folder/bar.txt'));
-
- $cachedData = $this->cache->get('');
- $this->assertnotEquals(-1, $cachedData['size']);
-
- $this->assertFalse($this->cache->getIncomplete());
- }
-
- public function testBackgroundScanOnlyRecurseIncomplete(): void {
- $this->fillTestFolders();
- $this->storage->mkdir('folder2');
- $this->storage->file_put_contents('folder2/bar.txt', 'foobar');
-
- $this->scanner->scan('', IScanner::SCAN_SHALLOW);
- $this->assertFalse($this->cache->inCache('folder/bar.txt'));
- $this->assertFalse($this->cache->inCache('folder/2bar.txt'));
- $this->assertFalse($this->cache->inCache('folder2/bar.txt'));
- $this->cache->put('folder2', ['size' => 1]); // mark as complete
-
- $cachedData = $this->cache->get('');
- $this->assertEquals(-1, $cachedData['size']);
-
- $this->scanner->scan('', IScanner::SCAN_RECURSIVE_INCOMPLETE, IScanner::REUSE_ETAG | IScanner::REUSE_SIZE);
-
- $this->assertTrue($this->cache->inCache('folder/bar.txt'));
- $this->assertTrue($this->cache->inCache('folder/bar.txt'));
- $this->assertFalse($this->cache->inCache('folder2/bar.txt'));
-
- $cachedData = $this->cache->get('');
- $this->assertNotEquals(-1, $cachedData['size']);
-
- $this->assertFalse($this->cache->getIncomplete());
- }
-
- public function testBackgroundScanNestedIncompleteFolders(): void {
- $this->storage->mkdir('folder');
- $this->scanner->backgroundScan();
-
- $this->storage->mkdir('folder/subfolder1');
- $this->storage->mkdir('folder/subfolder2');
-
- $this->storage->mkdir('folder/subfolder1/subfolder3');
- $this->cache->put('folder', ['size' => -1]);
- $this->cache->put('folder/subfolder1', ['size' => -1]);
-
- // do a scan to get the folders into the cache.
- $this->scanner->backgroundScan();
-
- $this->assertTrue($this->cache->inCache('folder/subfolder1/subfolder3'));
-
- $this->storage->file_put_contents('folder/subfolder1/bar1.txt', 'foobar');
- $this->storage->file_put_contents('folder/subfolder1/subfolder3/bar3.txt', 'foobar');
- $this->storage->file_put_contents('folder/subfolder2/bar2.txt', 'foobar');
-
- //mark folders as incomplete.
- $this->cache->put('folder/subfolder1', ['size' => -1]);
- $this->cache->put('folder/subfolder2', ['size' => -1]);
- $this->cache->put('folder/subfolder1/subfolder3', ['size' => -1]);
-
- $this->scanner->backgroundScan();
-
- $this->assertTrue($this->cache->inCache('folder/subfolder1/bar1.txt'));
- $this->assertTrue($this->cache->inCache('folder/subfolder2/bar2.txt'));
- $this->assertTrue($this->cache->inCache('folder/subfolder1/subfolder3/bar3.txt'));
-
- //check if folder sizes are correct.
- $this->assertEquals(18, $this->cache->get('folder')['size']);
- $this->assertEquals(12, $this->cache->get('folder/subfolder1')['size']);
- $this->assertEquals(6, $this->cache->get('folder/subfolder1/subfolder3')['size']);
- $this->assertEquals(6, $this->cache->get('folder/subfolder2')['size']);
- }
-
- public function testReuseExisting(): void {
- $this->fillTestFolders();
-
- $this->scanner->scan('');
- $oldData = $this->cache->get('');
- $this->storage->unlink('folder/bar.txt');
- $this->cache->put('folder', ['mtime' => $this->storage->filemtime('folder'), 'storage_mtime' => $this->storage->filemtime('folder')]);
- $this->scanner->scan('', IScanner::SCAN_SHALLOW, IScanner::REUSE_SIZE);
- $newData = $this->cache->get('');
- $this->assertIsString($oldData['etag']);
- $this->assertIsString($newData['etag']);
- $this->assertNotSame($oldData['etag'], $newData['etag']);
- $this->assertEquals($oldData['size'], $newData['size']);
-
- $oldData = $newData;
- $this->scanner->scan('', IScanner::SCAN_SHALLOW, IScanner::REUSE_ETAG);
- $newData = $this->cache->get('');
- $this->assertSame($oldData['etag'], $newData['etag']);
- $this->assertEquals(-1, $newData['size']);
-
- $this->scanner->scan('', IScanner::SCAN_RECURSIVE);
- $oldData = $this->cache->get('');
- $this->assertNotEquals(-1, $oldData['size']);
- $this->scanner->scanFile('', IScanner::REUSE_ETAG + IScanner::REUSE_SIZE);
- $newData = $this->cache->get('');
- $this->assertSame($oldData['etag'], $newData['etag']);
- $this->assertEquals($oldData['size'], $newData['size']);
-
- $this->scanner->scan('', IScanner::SCAN_RECURSIVE, IScanner::REUSE_ETAG + IScanner::REUSE_SIZE);
- $newData = $this->cache->get('');
- $this->assertSame($oldData['etag'], $newData['etag']);
- $this->assertEquals($oldData['size'], $newData['size']);
-
- $this->scanner->scan('', IScanner::SCAN_SHALLOW, IScanner::REUSE_ETAG + IScanner::REUSE_SIZE);
- $newData = $this->cache->get('');
- $this->assertSame($oldData['etag'], $newData['etag']);
- $this->assertEquals($oldData['size'], $newData['size']);
- }
-
- public function testRemovedFile(): void {
- $this->fillTestFolders();
-
- $this->scanner->scan('');
- $this->assertTrue($this->cache->inCache('foo.txt'));
- $this->storage->unlink('foo.txt');
- $this->scanner->scan('', IScanner::SCAN_SHALLOW);
- $this->assertFalse($this->cache->inCache('foo.txt'));
- }
-
- public function testRemovedFolder(): void {
- $this->fillTestFolders();
-
- $this->scanner->scan('');
- $this->assertTrue($this->cache->inCache('folder/bar.txt'));
- $this->storage->rmdir('/folder');
- $this->scanner->scan('', IScanner::SCAN_SHALLOW);
- $this->assertFalse($this->cache->inCache('folder'));
- $this->assertFalse($this->cache->inCache('folder/bar.txt'));
- }
-
- public function testScanRemovedFile(): void {
- $this->fillTestFolders();
-
- $this->scanner->scan('');
- $this->assertTrue($this->cache->inCache('folder/bar.txt'));
- $this->storage->unlink('folder/bar.txt');
- $this->scanner->scanFile('folder/bar.txt');
- $this->assertFalse($this->cache->inCache('folder/bar.txt'));
- }
-
- public function testETagRecreation(): void {
- $this->fillTestFolders();
-
- $this->scanner->scan('folder/bar.txt');
-
- // manipulate etag to simulate an empty etag
- $this->scanner->scan('', IScanner::SCAN_SHALLOW, IScanner::REUSE_ETAG);
- /** @var CacheEntry $data0 */
- $data0 = $this->cache->get('folder/bar.txt');
- $this->assertIsString($data0['etag']);
- $data1 = $this->cache->get('folder');
- $this->assertIsString($data1['etag']);
- $data2 = $this->cache->get('');
- $this->assertIsString($data2['etag']);
- $data0['etag'] = '';
- $this->cache->put('folder/bar.txt', $data0->getData());
-
- // rescan
- $this->scanner->scan('folder/bar.txt', IScanner::SCAN_SHALLOW, IScanner::REUSE_ETAG);
-
- // verify cache content
- $newData0 = $this->cache->get('folder/bar.txt');
- $this->assertIsString($newData0['etag']);
- $this->assertNotEmpty($newData0['etag']);
- }
-
- public function testRepairParent(): void {
- $this->fillTestFolders();
- $this->scanner->scan('');
- $this->assertTrue($this->cache->inCache('folder/bar.txt'));
- $oldFolderId = $this->cache->getId('folder');
-
- // delete the folder without removing the children
- $query = Server::get(IDBConnection::class)->getQueryBuilder();
- $query->delete('filecache')
- ->where($query->expr()->eq('fileid', $query->createNamedParameter($oldFolderId)));
- $query->executeStatement();
-
- $cachedData = $this->cache->get('folder/bar.txt');
- $this->assertEquals($oldFolderId, $cachedData['parent']);
- $this->assertFalse($this->cache->inCache('folder'));
-
- $this->scanner->scan('');
-
- $this->assertTrue($this->cache->inCache('folder'));
- $newFolderId = $this->cache->getId('folder');
- $this->assertNotEquals($oldFolderId, $newFolderId);
-
- $cachedData = $this->cache->get('folder/bar.txt');
- $this->assertEquals($newFolderId, $cachedData['parent']);
- }
-
- public function testRepairParentShallow(): void {
- $this->fillTestFolders();
- $this->scanner->scan('');
- $this->assertTrue($this->cache->inCache('folder/bar.txt'));
- $oldFolderId = $this->cache->getId('folder');
-
- // delete the folder without removing the children
- $query = Server::get(IDBConnection::class)->getQueryBuilder();
- $query->delete('filecache')
- ->where($query->expr()->eq('fileid', $query->createNamedParameter($oldFolderId)));
- $query->executeStatement();
-
- $cachedData = $this->cache->get('folder/bar.txt');
- $this->assertEquals($oldFolderId, $cachedData['parent']);
- $this->assertFalse($this->cache->inCache('folder'));
-
- $this->scanner->scan('folder', IScanner::SCAN_SHALLOW);
-
- $this->assertTrue($this->cache->inCache('folder'));
- $newFolderId = $this->cache->getId('folder');
- $this->assertNotEquals($oldFolderId, $newFolderId);
-
- $cachedData = $this->cache->get('folder/bar.txt');
- $this->assertEquals($newFolderId, $cachedData['parent']);
- }
-
- /**
- *
- * @param string $path
- * @param bool $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestIsPartialFile')]
- public function testIsPartialFile($path, $expected): void {
- $this->assertSame($expected,
- $this->scanner->isPartialFile($path)
- );
- }
-
- public static function dataTestIsPartialFile(): array {
- return [
- ['foo.txt.part', true],
- ['/sub/folder/foo.txt.part', true],
- ['/sub/folder.part/foo.txt', true],
- ['foo.txt', false],
- ['/sub/folder/foo.txt', false],
- ];
- }
-
- public function testNoETagUnscannedFolder(): void {
- $this->fillTestFolders();
-
- $this->scanner->scan('');
-
- $oldFolderEntry = $this->cache->get('folder');
- // create a new file in a folder by keeping the mtime unchanged, but mark the folder as unscanned
- $this->storage->file_put_contents('folder/new.txt', 'foo');
- $this->storage->touch('folder', $oldFolderEntry->getMTime());
- $this->cache->update($oldFolderEntry->getId(), ['size' => -1]);
-
- $this->scanner->scan('');
-
- $this->cache->inCache('folder/new.txt');
-
- $newFolderEntry = $this->cache->get('folder');
- $this->assertNotEquals($newFolderEntry->getEtag(), $oldFolderEntry->getEtag());
- }
-
- public function testNoETagUnscannedSubFolder(): void {
- $this->fillTestFolders();
- $this->storage->mkdir('folder/sub');
-
- $this->scanner->scan('');
-
- $oldFolderEntry1 = $this->cache->get('folder');
- $oldFolderEntry2 = $this->cache->get('folder/sub');
- // create a new file in a folder by keeping the mtime unchanged, but mark the folder as unscanned
- $this->storage->file_put_contents('folder/sub/new.txt', 'foo');
- $this->storage->touch('folder/sub', $oldFolderEntry1->getMTime());
-
- // we only mark the direct parent as unscanned, which is the current "notify" behavior
- $this->cache->update($oldFolderEntry2->getId(), ['size' => -1]);
-
- $this->scanner->scan('');
-
- $this->cache->inCache('folder/new.txt');
-
- $newFolderEntry1 = $this->cache->get('folder');
- $this->assertNotEquals($newFolderEntry1->getEtag(), $oldFolderEntry1->getEtag());
- $newFolderEntry2 = $this->cache->get('folder/sub');
- $this->assertNotEquals($newFolderEntry2->getEtag(), $oldFolderEntry2->getEtag());
- }
-}
--- a/tests/lib/Files/Cache/SearchBuilderTest.php
+++ /dev/null
@@ -1,212 +0,0 @@
-builder = Server::get(IDBConnection::class)->getQueryBuilder();
- $this->mimetypeLoader = $this->createMock(IMimeTypeLoader::class);
- $this->filesMetadataManager = $this->createMock(IFilesMetadataManager::class);
-
- $this->mimetypeLoader->expects($this->any())
- ->method('getId')
- ->willReturnMap([
- ['text', 1],
- ['text/plain', 2],
- ['text/xml', 3],
- ['image/jpg', 4],
- ['image/png', 5],
- ['image', 6],
- ]);
-
- $this->mimetypeLoader->expects($this->any())
- ->method('getMimetypeById')
- ->willReturnMap([
- [1, 'text'],
- [2, 'text/plain'],
- [3, 'text/xml'],
- [4, 'image/jpg'],
- [5, 'image/png'],
- [6, 'image']
- ]);
-
- $this->searchBuilder = new SearchBuilder($this->mimetypeLoader, $this->filesMetadataManager);
- $this->numericStorageId = 10000;
-
- $this->builder->select(['fileid'])
- ->from('filecache', 'file') // alias needed for QuerySearchHelper#getOperatorFieldAndValue
- ->where($this->builder->expr()->eq('storage', new Literal($this->numericStorageId)));
- }
-
- #[\Override]
- protected function tearDown(): void {
- parent::tearDown();
-
- $builder = Server::get(IDBConnection::class)->getQueryBuilder();
-
- $builder->delete('filecache')
- ->where($builder->expr()->eq('storage', $builder->createNamedParameter($this->numericStorageId, IQueryBuilder::PARAM_INT)));
-
- $builder->executeStatement();
- }
-
- private function addCacheEntry(array $data) {
- $data['storage'] = $this->numericStorageId;
- $data['etag'] = 'unimportant';
- $data['storage_mtime'] = $data['mtime'];
- if (!isset($data['path'])) {
- $data['path'] = 'random/' . $this->getUniqueID();
- }
- $data['path_hash'] = md5($data['path']);
- if (!isset($data['mtime'])) {
- $data['mtime'] = 100;
- }
- if (!isset($data['size'])) {
- $data['size'] = 100;
- }
- $data['name'] = basename($data['path']);
- $data['parent'] = -1;
- if (isset($data['mimetype'])) {
- [$mimepart,] = explode('/', $data['mimetype']);
- $data['mimepart'] = $this->mimetypeLoader->getId($mimepart);
- $data['mimetype'] = $this->mimetypeLoader->getId($data['mimetype']);
- } else {
- $data['mimepart'] = 1;
- $data['mimetype'] = 1;
- }
-
- $builder = Server::get(IDBConnection::class)->getQueryBuilder();
-
- $values = [];
- foreach ($data as $key => $value) {
- $values[$key] = $builder->createNamedParameter($value);
- }
-
- $builder->insert('filecache')
- ->values($values)
- ->executeStatement();
-
- return $builder->getLastInsertId();
- }
-
- private function search(ISearchOperator $operator) {
- $dbOperator = $this->searchBuilder->searchOperatorToDBExpr($this->builder, $operator);
- $this->builder->andWhere($dbOperator);
-
- $result = $this->builder->executeQuery();
- $rows = $result->fetchFirstColumn();
- $result->closeCursor();
-
- return $rows;
- }
-
- public static function comparisonProvider(): array {
- return [
- [new SearchComparison(ISearchComparison::COMPARE_GREATER_THAN, 'mtime', 125), [1]],
- [new SearchComparison(ISearchComparison::COMPARE_LESS_THAN, 'mtime', 125), [0]],
- [new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'size', 125), []],
- [new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'size', 50), [0, 1]],
- [new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'name', 'foobar'), [0]],
- [new SearchComparison(ISearchComparison::COMPARE_LIKE, 'name', 'foo%'), [0, 1]],
- [new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', 'image/jpg'), [0]],
- [new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mimetype', 'image/%'), [0, 1]],
- [new SearchComparison(ISearchComparison::COMPARE_IN, 'mimetype', ['image/jpg', 'image/png']), [0, 1]],
- [new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'size', 50),
- new SearchComparison(ISearchComparison::COMPARE_LESS_THAN, 'mtime', 125)
- ]), [0]],
- [new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'size', 50),
- new SearchComparison(ISearchComparison::COMPARE_LESS_THAN, 'mtime', 125),
- new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mimetype', 'text/%')
- ]), []],
- [new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mtime', 100),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mtime', 150),
- ]), [0, 1]],
- [new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_NOT, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mtime', 150),
- ]), [0]],
- [new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_NOT, [
- new SearchComparison(ISearchComparison::COMPARE_GREATER_THAN, 'mtime', 125),
- ]), [0]],
- [new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_NOT, [
- new SearchComparison(ISearchComparison::COMPARE_LESS_THAN, 'mtime', 125),
- ]), [1]],
- [new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_NOT, [
- new SearchComparison(ISearchComparison::COMPARE_LIKE, 'name', '%bar'),
- ]), [1]],
- ];
- }
-
- /**
- *
- * @param ISearchOperator $operator
- * @param array $fileIds
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('comparisonProvider')]
- public function testComparison(ISearchOperator $operator, array $fileIds): void {
- $fileId = [];
- $fileId[] = $this->addCacheEntry([
- 'path' => 'foobar',
- 'mtime' => 100,
- 'size' => 50,
- 'mimetype' => 'image/jpg'
- ]);
-
- $fileId[] = $this->addCacheEntry([
- 'path' => 'fooasd',
- 'mtime' => 150,
- 'size' => 50,
- 'mimetype' => 'image/png'
- ]);
-
- $fileIds = array_map(function ($i) use ($fileId) {
- return $fileId[$i];
- }, $fileIds);
-
- $results = $this->search($operator);
-
- sort($fileIds);
- sort($results);
-
- $this->assertEquals($fileIds, $results);
- }
-}
--- a/tests/lib/Files/Cache/StorageTest.php
+++ /dev/null
@@ -1,67 +0,0 @@
-getCache();
- $rootId = $cache->put('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $fileId = $cache->put('foo.txt', ['size' => 1, 'mtime' => 1, 'mimetype' => 'text/plain', 'upload_time' => 25]);
-
- $metadataManager = Server::get(IFilesMetadataManager::class);
- $metadata = $metadataManager->getMetadata($fileId, true);
- $metadata->setString('test-key', 'value', true);
- $metadataManager->saveMetadata($metadata);
-
- $mountId = random_int(100000000, 999999999);
- $mountPoint = '/' . $this->getUniqueID('user') . '/files/mount/';
- $qb = Server::get(IDBConnection::class)->getQueryBuilder();
- $qb->insert('mounts')
- ->values([
- 'storage_id' => $qb->createNamedParameter($cache->getNumericStorageId(), IQueryBuilder::PARAM_INT),
- 'root_id' => $qb->createNamedParameter($rootId, IQueryBuilder::PARAM_INT),
- 'user_id' => $qb->createNamedParameter('test'),
- 'mount_point' => $qb->createNamedParameter($mountPoint),
- 'mount_point_hash' => $qb->createNamedParameter(hash('xxh128', $mountPoint)),
- 'mount_id' => $qb->createNamedParameter($mountId, IQueryBuilder::PARAM_INT),
- ])
- ->executeStatement();
-
- $this->assertSame(1, $this->countRows('filecache_extended', 'fileid', $fileId));
- $this->assertSame(1, $this->countRows('files_metadata', 'file_id', $fileId));
- $this->assertSame(1, $this->countRows('files_metadata_index', 'file_id', $fileId));
-
- Storage::cleanByMountId($mountId);
-
- $this->assertSame(0, $this->countRows('filecache', 'fileid', $fileId));
- $this->assertSame(0, $this->countRows('filecache_extended', 'fileid', $fileId));
- $this->assertSame(0, $this->countRows('files_metadata', 'file_id', $fileId));
- $this->assertSame(0, $this->countRows('files_metadata_index', 'file_id', $fileId));
- }
-
- private function countRows(string $table, string $column, int $fileId): int {
- $qb = Server::get(IDBConnection::class)->getQueryBuilder();
- $qb->select($qb->func()->count())
- ->from($table)
- ->where($qb->expr()->eq($column, $qb->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)));
- return (int)$qb->executeQuery()->fetchOne();
- }
-}
--- a/tests/lib/Files/Cache/UpdaterLegacyTest.php
+++ /dev/null
@@ -1,321 +0,0 @@
-storage = new Temporary([]);
- $textData = "dummy file data\n";
- $imgData = file_get_contents(\OC::$SERVERROOT . '/core/img/logo/logo.png');
- $this->storage->mkdir('folder');
- $this->storage->file_put_contents('foo.txt', $textData);
- $this->storage->file_put_contents('foo.png', $imgData);
- $this->storage->file_put_contents('folder/bar.txt', $textData);
- $this->storage->file_put_contents('folder/bar2.txt', $textData);
-
- $this->scanner = $this->storage->getScanner();
- $this->scanner->scan('');
- $this->cache = $this->storage->getCache();
-
- if (!self::$user) {
- self::$user = $this->getUniqueID();
- }
-
- Server::get(IUserManager::class)->createUser(self::$user, 'NotAnEasyPassword123456+');
- $this->loginAsUser(self::$user);
-
- Filesystem::init(self::$user, '/' . self::$user . '/files');
-
- /** @var IMountManager $manager */
- $manager = Server::get(IMountManager::class);
- $manager->removeMount('/' . self::$user);
-
- Filesystem::mount($this->storage, [], '/' . self::$user . '/files');
-
- \OC_Hook::clear('OC_Filesystem');
- }
-
- #[\Override]
- protected function tearDown(): void {
- if ($this->cache) {
- $this->cache->clear();
- }
-
- $result = false;
- $user = Server::get(IUserManager::class)->get(self::$user);
- if ($user !== null) {
- $result = $user->delete();
- }
- $this->assertTrue($result);
-
- $this->logout();
- parent::tearDown();
- }
-
- public function testWrite(): void {
- $textSize = strlen("dummy file data\n");
- $imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo/logo.png');
- $this->cache->put('foo.txt', ['mtime' => 100, 'storage_mtime' => 150]);
- $rootCachedData = $this->cache->get('');
- $this->assertEquals(3 * $textSize + $imageSize, $rootCachedData['size']);
-
- $fooCachedData = $this->cache->get('foo.txt');
- Filesystem::file_put_contents('foo.txt', 'asd');
- $cachedData = $this->cache->get('foo.txt');
- $this->assertEquals(3, $cachedData['size']);
- $this->assertIsString($fooCachedData['etag']);
- $this->assertIsString($cachedData['etag']);
- $this->assertNotSame($fooCachedData['etag'], $cachedData['etag']);
- $cachedData = $this->cache->get('');
- $this->assertEquals(2 * $textSize + $imageSize + 3, $cachedData['size']);
- $this->assertIsString($rootCachedData['etag']);
- $this->assertIsString($cachedData['etag']);
- $this->assertNotSame($rootCachedData['etag'], $cachedData['etag']);
- $rootCachedData = $cachedData;
-
- $this->assertFalse($this->cache->inCache('bar.txt'));
- Filesystem::file_put_contents('bar.txt', 'asd');
- $this->assertTrue($this->cache->inCache('bar.txt'));
- $cachedData = $this->cache->get('bar.txt');
- $this->assertEquals(3, $cachedData['size']);
- $mtime = $cachedData['mtime'];
- $cachedData = $this->cache->get('');
- $this->assertEquals(2 * $textSize + $imageSize + 2 * 3, $cachedData['size']);
- $this->assertIsString($rootCachedData['etag']);
- $this->assertIsString($cachedData['etag']);
- $this->assertNotSame($rootCachedData['etag'], $cachedData['etag']);
- $this->assertGreaterThanOrEqual($rootCachedData['mtime'], $mtime);
- }
-
- public function testWriteWithMountPoints(): void {
- $storage2 = new Temporary([]);
- $storage2->getScanner()->scan(''); //initialize etags
- $cache2 = $storage2->getCache();
- Filesystem::mount($storage2, [], '/' . self::$user . '/files/folder/substorage');
- $view = new View('/' . self::$user . '/files');
- $folderCachedData = $view->getFileInfo('folder');
- $substorageCachedData = $cache2->get('');
- Filesystem::file_put_contents('folder/substorage/foo.txt', 'asd');
- $this->assertTrue($cache2->inCache('foo.txt'));
- $cachedData = $cache2->get('foo.txt');
- $oldEtag = $substorageCachedData['etag'];
- $this->assertEquals(3, $cachedData['size']);
- $mtime = $cachedData['mtime'];
-
- $cachedData = $cache2->get('');
- $this->assertIsString($substorageCachedData['etag']);
- $this->assertIsString($cachedData['etag']);
- $this->assertNotSame($oldEtag, $cachedData['etag']);
-
- $cachedData = $view->getFileInfo('folder');
- $this->assertIsString($folderCachedData['etag']);
- $this->assertIsString($cachedData['etag']);
- $this->assertNotSame($oldEtag, $cachedData['etag']);
- }
-
- public function testDelete(): void {
- $textSize = strlen("dummy file data\n");
- $imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo/logo.png');
- $rootCachedData = $this->cache->get('');
- $oldEtag = $rootCachedData['etag'];
- $this->assertEquals(3 * $textSize + $imageSize, $rootCachedData['size']);
-
- $this->assertTrue($this->cache->inCache('foo.txt'));
- Filesystem::unlink('foo.txt');
- $this->assertFalse($this->cache->inCache('foo.txt'));
- $cachedData = $this->cache->get('');
- $this->assertEquals(2 * $textSize + $imageSize, $cachedData['size']);
- $this->assertIsString($rootCachedData['etag']);
- $this->assertIsString($cachedData['etag']);
- $this->assertNotSame($oldEtag, $cachedData['etag']);
- $this->assertGreaterThanOrEqual($rootCachedData['mtime'], $cachedData['mtime']);
- $rootCachedData = $cachedData;
-
- Filesystem::mkdir('bar_folder');
- $this->assertTrue($this->cache->inCache('bar_folder'));
- $cachedData = $this->cache->get('');
- $this->assertIsString($rootCachedData['etag']);
- $this->assertIsString($cachedData['etag']);
- $this->assertNotSame($oldEtag, $cachedData['etag']);
- $rootCachedData = $cachedData;
- $oldEtag = $rootCachedData['etag'];
- Filesystem::rmdir('bar_folder');
- $this->assertFalse($this->cache->inCache('bar_folder'));
- $cachedData = $this->cache->get('');
- $this->assertIsString($rootCachedData['etag']);
- $this->assertIsString($cachedData['etag']);
- $this->assertNotSame($oldEtag, $cachedData['etag']);
- $this->assertGreaterThanOrEqual($rootCachedData['mtime'], $cachedData['mtime']);
- }
-
- public function testDeleteWithMountPoints(): void {
- $storage2 = new Temporary([]);
- $cache2 = $storage2->getCache();
- Filesystem::mount($storage2, [], '/' . self::$user . '/files/folder/substorage');
- Filesystem::file_put_contents('folder/substorage/foo.txt', 'asd');
- $view = new View('/' . self::$user . '/files');
- $this->assertTrue($cache2->inCache('foo.txt'));
- $folderCachedData = $view->getFileInfo('folder');
- $substorageCachedData = $cache2->get('');
- $oldEtag = $folderCachedData['etag'];
- Filesystem::unlink('folder/substorage/foo.txt');
- $this->assertFalse($cache2->inCache('foo.txt'));
-
- $cachedData = $cache2->get('');
- $this->assertIsString($substorageCachedData['etag']);
- $this->assertIsString($cachedData['etag']);
- $this->assertNotSame($substorageCachedData, $cachedData['etag']);
- $this->assertGreaterThanOrEqual($substorageCachedData['mtime'], $cachedData['mtime']);
-
- $cachedData = $view->getFileInfo('folder');
- $this->assertIsString($folderCachedData['etag']);
- $this->assertIsString($cachedData['etag']);
- $this->assertNotSame($oldEtag, $cachedData['etag']);
- $this->assertGreaterThanOrEqual($folderCachedData['mtime'], $cachedData['mtime']);
- }
-
- public function testRename(): void {
- $textSize = strlen("dummy file data\n");
- $imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo/logo.png');
- $rootCachedData = $this->cache->get('');
- $this->assertEquals(3 * $textSize + $imageSize, $rootCachedData['size']);
-
- $this->assertTrue($this->cache->inCache('foo.txt'));
- $fooCachedData = $this->cache->get('foo.txt');
- $this->assertFalse($this->cache->inCache('bar.txt'));
- Filesystem::rename('foo.txt', 'bar.txt');
- $this->assertFalse($this->cache->inCache('foo.txt'));
- $this->assertTrue($this->cache->inCache('bar.txt'));
- $cachedData = $this->cache->get('bar.txt');
- $this->assertEquals($fooCachedData['fileid'], $cachedData['fileid']);
- $mtime = $cachedData['mtime'];
- $cachedData = $this->cache->get('');
- $this->assertEquals(3 * $textSize + $imageSize, $cachedData['size']);
- $this->assertIsString($rootCachedData['etag']);
- $this->assertIsString($cachedData['etag']);
- $this->assertNotSame($rootCachedData['etag'], $cachedData['etag']);
- }
-
- public function testRenameExtension(): void {
- $fooCachedData = $this->cache->get('foo.txt');
- $this->assertEquals('text/plain', $fooCachedData['mimetype']);
- Filesystem::rename('foo.txt', 'foo.abcd');
- $fooCachedData = $this->cache->get('foo.abcd');
- $this->assertEquals('application/octet-stream', $fooCachedData['mimetype']);
- }
-
- public function testRenameWithMountPoints(): void {
- $storage2 = new Temporary([]);
- $cache2 = $storage2->getCache();
- Filesystem::mount($storage2, [], '/' . self::$user . '/files/folder/substorage');
- Filesystem::file_put_contents('folder/substorage/foo.txt', 'asd');
- $view = new View('/' . self::$user . '/files');
- $this->assertTrue($cache2->inCache('foo.txt'));
- $folderCachedData = $view->getFileInfo('folder');
- $oldEtag = $folderCachedData['etag'];
- $substorageCachedData = $cache2->get('');
- $fooCachedData = $cache2->get('foo.txt');
- Filesystem::rename('folder/substorage/foo.txt', 'folder/substorage/bar.txt');
- $this->assertFalse($cache2->inCache('foo.txt'));
- $this->assertTrue($cache2->inCache('bar.txt'));
- $cachedData = $cache2->get('bar.txt');
- $this->assertEquals($fooCachedData['fileid'], $cachedData['fileid']);
- $mtime = $cachedData['mtime'];
-
- $cachedData = $cache2->get('');
- $this->assertIsString($substorageCachedData['etag']);
- $this->assertIsString($cachedData['etag']);
- $this->assertNotSame($oldEtag, $cachedData['etag']);
- // rename can cause mtime change - invalid assert
- // $this->assertEquals($mtime, $cachedData['mtime']);
-
- $cachedData = $view->getFileInfo('folder');
- $this->assertIsString($folderCachedData['etag']);
- $this->assertIsString($cachedData['etag']);
- $this->assertNotSame($oldEtag, $cachedData['etag']);
- // rename can cause mtime change - invalid assert
- // $this->assertEquals($mtime, $cachedData['mtime']);
- }
-
- public function testTouch(): void {
- $rootCachedData = $this->cache->get('');
- $fooCachedData = $this->cache->get('foo.txt');
- Filesystem::touch('foo.txt');
- $cachedData = $this->cache->get('foo.txt');
- $this->assertIsString($fooCachedData['etag']);
- $this->assertIsString($cachedData['etag']);
- $this->assertGreaterThanOrEqual($fooCachedData['mtime'], $cachedData['mtime']);
-
- $cachedData = $this->cache->get('');
- $this->assertIsString($rootCachedData['etag']);
- $this->assertIsString($cachedData['etag']);
- $this->assertNotSame($rootCachedData['etag'], $cachedData['etag']);
- $this->assertGreaterThanOrEqual($rootCachedData['mtime'], $cachedData['mtime']);
- $rootCachedData = $cachedData;
-
- $time = 1371006070;
- $barCachedData = $this->cache->get('folder/bar.txt');
- $folderCachedData = $this->cache->get('folder');
- $this->cache->put('', ['mtime' => $time - 100]);
- Filesystem::touch('folder/bar.txt', $time);
- $cachedData = $this->cache->get('folder/bar.txt');
- $this->assertIsString($barCachedData['etag']);
- $this->assertIsString($cachedData['etag']);
- $this->assertNotSame($barCachedData['etag'], $cachedData['etag']);
- $this->assertEquals($time, $cachedData['mtime']);
-
- $cachedData = $this->cache->get('folder');
- $this->assertIsString($folderCachedData['etag']);
- $this->assertIsString($cachedData['etag']);
- $this->assertNotSame($folderCachedData['etag'], $cachedData['etag']);
-
- $cachedData = $this->cache->get('');
- $this->assertIsString($rootCachedData['etag']);
- $this->assertIsString($cachedData['etag']);
- $this->assertNotSame($rootCachedData['etag'], $cachedData['etag']);
- $this->assertEquals($time, $cachedData['mtime']);
- }
-}
--- a/tests/lib/Files/Cache/UpdaterTest.php
+++ /dev/null
@@ -1,386 +0,0 @@
-loginAsUser();
-
- $this->storage = new Temporary([]);
- $this->updater = $this->storage->getUpdater();
- $this->cache = $this->storage->getCache();
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->logout();
- parent::tearDown();
- }
-
- public function testNewFile(): void {
- $this->storage->file_put_contents('foo.txt', 'bar');
- $this->assertFalse($this->cache->inCache('foo.txt'));
-
- $this->updater->update('foo.txt');
-
- $this->assertTrue($this->cache->inCache('foo.txt'));
- $cached = $this->cache->get('foo.txt');
- $this->assertEquals(3, $cached['size']);
- $this->assertEquals('text/plain', $cached['mimetype']);
- }
-
- public function testUpdatedFile(): void {
- $this->storage->file_put_contents('foo.txt', 'bar');
- $this->updater->update('foo.txt');
-
- $cached = $this->cache->get('foo.txt');
- $this->assertEquals(3, $cached['size']);
- $this->assertEquals('text/plain', $cached['mimetype']);
-
- $this->storage->file_put_contents('foo.txt', 'qwerty');
-
- $cached = $this->cache->get('foo.txt');
- $this->assertEquals(3, $cached['size']);
-
- $this->updater->update('/foo.txt');
-
- $cached = $this->cache->get('foo.txt');
- $this->assertEquals(6, $cached['size']);
- }
-
- public function testParentSize(): void {
- $this->storage->getScanner()->scan('');
-
- $parentCached = $this->cache->get('');
- $this->assertEquals(0, $parentCached['size']);
-
- $this->storage->file_put_contents('foo.txt', 'bar');
-
- $parentCached = $this->cache->get('');
- $this->assertEquals(0, $parentCached['size']);
-
- $this->updater->update('foo.txt');
-
- $parentCached = $this->cache->get('');
- $this->assertEquals(3, $parentCached['size']);
-
- $this->storage->file_put_contents('foo.txt', 'qwerty');
-
- $parentCached = $this->cache->get('');
- $this->assertEquals(3, $parentCached['size']);
-
- $this->updater->update('foo.txt');
-
- $parentCached = $this->cache->get('');
- $this->assertEquals(6, $parentCached['size']);
-
- $this->storage->unlink('foo.txt');
-
- $parentCached = $this->cache->get('');
- $this->assertEquals(6, $parentCached['size']);
-
- $this->updater->remove('foo.txt');
-
- $parentCached = $this->cache->get('');
- $this->assertEquals(0, $parentCached['size']);
- }
-
- public function testMove(): void {
- $this->storage->file_put_contents('foo.txt', 'qwerty');
- $this->updater->update('foo.txt');
-
- $this->assertTrue($this->cache->inCache('foo.txt'));
- $this->assertFalse($this->cache->inCache('bar.txt'));
- $cached = $this->cache->get('foo.txt');
-
- $this->storage->rename('foo.txt', 'bar.txt');
-
- $this->assertTrue($this->cache->inCache('foo.txt'));
- $this->assertFalse($this->cache->inCache('bar.txt'));
-
- $this->updater->renameFromStorage($this->storage, 'foo.txt', 'bar.txt');
-
- $this->assertFalse($this->cache->inCache('foo.txt'));
- $this->assertTrue($this->cache->inCache('bar.txt'));
-
- $cachedTarget = $this->cache->get('bar.txt');
- $this->assertEquals($cached['etag'], $cachedTarget['etag']);
- $this->assertEquals($cached['mtime'], $cachedTarget['mtime']);
- $this->assertEquals($cached['size'], $cachedTarget['size']);
- $this->assertEquals($cached['fileid'], $cachedTarget['fileid']);
- }
-
- public function testMoveNonExistingOverwrite(): void {
- $this->storage->file_put_contents('bar.txt', 'qwerty');
- $this->updater->update('bar.txt');
-
- $cached = $this->cache->get('bar.txt');
-
- $this->updater->renameFromStorage($this->storage, 'foo.txt', 'bar.txt');
-
- $this->assertFalse($this->cache->inCache('foo.txt'));
- $this->assertTrue($this->cache->inCache('bar.txt'));
-
- $cachedTarget = $this->cache->get('bar.txt');
- $this->assertEquals($cached['etag'], $cachedTarget['etag']);
- $this->assertEquals($cached['mtime'], $cachedTarget['mtime']);
- $this->assertEquals($cached['size'], $cachedTarget['size']);
- $this->assertEquals($cached['fileid'], $cachedTarget['fileid']);
- }
-
- public function testUpdateStorageMTime(): void {
- $this->storage->mkdir('sub');
- $this->storage->mkdir('sub2');
- $this->storage->file_put_contents('sub/foo.txt', 'qwerty');
-
- $this->updater->update('sub');
- $this->updater->update('sub/foo.txt');
- $this->updater->update('sub2');
-
- $cachedSourceParent = $this->cache->get('sub');
- $cachedSource = $this->cache->get('sub/foo.txt');
-
- $this->storage->rename('sub/foo.txt', 'sub2/bar.txt');
-
- // simulate storage having a different mtime
- $testmtime = 1433323578;
-
- // source storage mtime change
- $this->storage->touch('sub', $testmtime);
-
- // target storage mtime change
- $this->storage->touch('sub2', $testmtime);
- // some storages (like Dropbox) change storage mtime on rename
- $this->storage->touch('sub2/bar.txt', $testmtime);
-
- $this->updater->renameFromStorage($this->storage, 'sub/foo.txt', 'sub2/bar.txt');
-
- $cachedTargetParent = $this->cache->get('sub2');
- $cachedTarget = $this->cache->get('sub2/bar.txt');
-
- $this->assertEquals($cachedSource['mtime'], $cachedTarget['mtime'], 'file mtime preserved');
-
- $this->assertNotEquals($cachedTarget['storage_mtime'], $cachedTarget['mtime'], 'mtime is not storage_mtime for moved file');
-
- $this->assertEquals($testmtime, $cachedTarget['storage_mtime'], 'target file storage_mtime propagated');
- $this->assertNotEquals($testmtime, $cachedTarget['mtime'], 'target file mtime changed, not from storage');
-
- $this->assertEquals($testmtime, $cachedTargetParent['storage_mtime'], 'target parent storage_mtime propagated');
- $this->assertNotEquals($testmtime, $cachedTargetParent['mtime'], 'target folder mtime changed, not from storage');
- }
-
- public function testNewFileDisabled(): void {
- $this->storage->file_put_contents('foo.txt', 'bar');
- $this->assertFalse($this->cache->inCache('foo.txt'));
-
- $this->updater->disable();
- $this->updater->update('/foo.txt');
-
- $this->assertFalse($this->cache->inCache('foo.txt'));
- }
-
- public function testMoveCrossStorage(): void {
- $storage2 = new Temporary([]);
- $cache2 = $storage2->getCache();
- Filesystem::mount($storage2, [], '/bar');
- $this->storage->file_put_contents('foo.txt', 'qwerty');
-
- $this->updater->update('foo.txt');
-
- $this->assertTrue($this->cache->inCache('foo.txt'));
- $this->assertFalse($cache2->inCache('bar.txt'));
- $cached = $this->cache->get('foo.txt');
-
- // "rename"
- $storage2->file_put_contents('bar.txt', 'qwerty');
- $this->storage->unlink('foo.txt');
-
- $this->assertTrue($this->cache->inCache('foo.txt'));
- $this->assertFalse($cache2->inCache('bar.txt'));
-
- $storage2->getUpdater()->renameFromStorage($this->storage, 'foo.txt', 'bar.txt');
-
- $this->assertFalse($this->cache->inCache('foo.txt'));
- $this->assertTrue($cache2->inCache('bar.txt'));
-
- $cachedTarget = $cache2->get('bar.txt');
- $this->assertEquals($cached['mtime'], $cachedTarget['mtime']);
- $this->assertEquals($cached['size'], $cachedTarget['size']);
- $this->assertEquals($cached['etag'], $cachedTarget['etag']);
- $this->assertEquals($cached['fileid'], $cachedTarget['fileid']);
- }
-
- public function testMoveCrossStorageMissingTargetParent(): void {
- $storage2 = new Temporary([]);
- $cache2 = $storage2->getCache();
- Filesystem::mount($storage2, [], '/bar');
- $this->storage->file_put_contents('foo.txt', 'qwerty');
- $this->updater->update('foo.txt');
- $cached = $this->cache->get('foo.txt');
-
- // the target parent exists on disk but is missing from the cache
- $storage2->mkdir('sub');
- $this->assertFalse($cache2->inCache('sub'));
-
- $storage2->moveFromStorage($this->storage, 'foo.txt', 'sub/bar.txt');
- $storage2->getUpdater()->renameFromStorage($this->storage, 'foo.txt', 'sub/bar.txt');
-
- $this->assertFalse($this->cache->inCache('foo.txt'));
- $this->assertTrue($cache2->inCache('sub'));
- $this->assertTrue($cache2->inCache('sub/bar.txt'));
-
- // the moved entry is attached to the scanned parent instead of being orphaned
- $cachedTarget = $cache2->get('sub/bar.txt');
- $this->assertEquals($cache2->getId('sub'), $cachedTarget['parent']);
- $this->assertEquals($cached['fileid'], $cachedTarget['fileid']);
- }
-
- public function testMoveCrossStorageSourceNotInCache(): void {
- $storage2 = new Temporary([]);
- $cache2 = $storage2->getCache();
- Filesystem::mount($storage2, [], '/bar');
- $this->storage->file_put_contents('foo.txt', 'qwerty');
- // the source was never scanned into the cache
- $this->assertFalse($this->cache->inCache('foo.txt'));
-
- $storage2->moveFromStorage($this->storage, 'foo.txt', 'bar.txt');
- $storage2->getUpdater()->renameFromStorage($this->storage, 'foo.txt', 'bar.txt');
-
- // the target still gets a cache entry
- $this->assertTrue($cache2->inCache('bar.txt'));
- $cachedTarget = $cache2->get('bar.txt');
- $this->assertEquals(6, $cachedTarget['size']);
- }
-
- public function testMoveFolderCrossStorage(): void {
- $storage2 = new Temporary([]);
- $cache2 = $storage2->getCache();
- Filesystem::mount($storage2, [], '/bar');
- $this->storage->mkdir('foo');
- $this->storage->mkdir('foo/bar');
- $this->storage->file_put_contents('foo/foo.txt', 'qwerty');
- $this->storage->file_put_contents('foo/bar.txt', 'foo');
- $this->storage->file_put_contents('foo/bar/bar.txt', 'qwertyuiop');
-
- $this->storage->getScanner()->scan('');
-
- $this->assertTrue($this->cache->inCache('foo'));
- $this->assertTrue($this->cache->inCache('foo/foo.txt'));
- $this->assertTrue($this->cache->inCache('foo/bar.txt'));
- $this->assertTrue($this->cache->inCache('foo/bar'));
- $this->assertTrue($this->cache->inCache('foo/bar/bar.txt'));
- $cached = [];
- $cached[] = $this->cache->get('foo');
- $cached[] = $this->cache->get('foo/foo.txt');
- $cached[] = $this->cache->get('foo/bar.txt');
- $cached[] = $this->cache->get('foo/bar');
- $cached[] = $this->cache->get('foo/bar/bar.txt');
-
- // add extension to trigger the possible mimetype change
- $storage2->moveFromStorage($this->storage, 'foo', 'foo.b');
- $storage2->getUpdater()->renameFromStorage($this->storage, 'foo', 'foo.b');
-
- $this->assertFalse($this->cache->inCache('foo'));
- $this->assertFalse($this->cache->inCache('foo/foo.txt'));
- $this->assertFalse($this->cache->inCache('foo/bar.txt'));
- $this->assertFalse($this->cache->inCache('foo/bar'));
- $this->assertFalse($this->cache->inCache('foo/bar/bar.txt'));
- $this->assertTrue($cache2->inCache('foo.b'));
- $this->assertTrue($cache2->inCache('foo.b/foo.txt'));
- $this->assertTrue($cache2->inCache('foo.b/bar.txt'));
- $this->assertTrue($cache2->inCache('foo.b/bar'));
- $this->assertTrue($cache2->inCache('foo.b/bar/bar.txt'));
-
- $cachedTarget = [];
- $cachedTarget[] = $cache2->get('foo.b');
- $cachedTarget[] = $cache2->get('foo.b/foo.txt');
- $cachedTarget[] = $cache2->get('foo.b/bar.txt');
- $cachedTarget[] = $cache2->get('foo.b/bar');
- $cachedTarget[] = $cache2->get('foo.b/bar/bar.txt');
-
- foreach ($cached as $i => $old) {
- $new = $cachedTarget[$i];
- $this->assertEquals($old['mtime'], $new['mtime']);
- $this->assertEquals($old['size'], $new['size']);
- $this->assertEquals($old['etag'], $new['etag']);
- $this->assertEquals($old['fileid'], $new['fileid']);
- $this->assertEquals($old['mimetype'], $new['mimetype']);
- }
- }
-
- public static function changeExtensionProvider(): array {
- return [
- [new Temporary()],
- [new ObjectStoreStorage(['objectstore' => new StorageObjectStore(new Temporary())])]
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('changeExtensionProvider')]
- public function testChangeExtension(IStorage $storage) {
- $updater = $storage->getUpdater();
- $cache = $storage->getCache();
- $storage->file_put_contents('foo', 'qwerty');
- $updater->update('foo');
-
- $bareCached = $cache->get('foo');
- $this->assertEquals('application/octet-stream', $bareCached->getMimeType());
-
- $storage->rename('foo', 'foo.txt');
- $updater->renameFromStorage($storage, 'foo', 'foo.txt');
-
- $cached = $cache->get('foo.txt');
- $this->assertEquals('text/plain', $cached->getMimeType());
-
- $storage->rename('foo.txt', 'foo.md');
- $updater->renameFromStorage($storage, 'foo.txt', 'foo.md');
-
- $cachedTarget = $cache->get('foo.md');
- $this->assertEquals('text/markdown', $cachedTarget->getMimeType());
- }
-}
--- a/tests/lib/Files/Cache/WatcherTest.php
+++ /dev/null
@@ -1,238 +0,0 @@
-loginAsUser();
- }
-
- #[\Override]
- protected function tearDown(): void {
- foreach ($this->storages as $storage) {
- $cache = $storage->getCache();
- $ids = $cache->getAll();
- $cache->clear();
- }
-
- $this->logout();
- parent::tearDown();
- }
-
- public function testWatcher(): void {
- $storage = $this->getTestStorage();
- $cache = $storage->getCache();
- $updater = $storage->getWatcher();
- $updater->setPolicy(Watcher::CHECK_ONCE);
-
- //set the mtime to the past so it can detect an mtime change
- $cache->put('', ['storage_mtime' => 10]);
-
- $this->assertTrue($cache->inCache('folder/bar.txt'));
- $this->assertTrue($cache->inCache('folder/bar2.txt'));
-
- $this->assertFalse($cache->inCache('bar.test'));
- $storage->file_put_contents('bar.test', 'foo');
- $updater->checkUpdate('');
- $this->assertTrue($cache->inCache('bar.test'));
- $cachedData = $cache->get('bar.test');
- $this->assertEquals(3, $cachedData['size']);
-
- $cache->put('bar.test', ['storage_mtime' => 10]);
- $storage->file_put_contents('bar.test', 'test data');
-
- // make sure that PHP can read the new size correctly
- clearstatcache();
-
- $updater->checkUpdate('bar.test');
- $cachedData = $cache->get('bar.test');
- $this->assertEquals(9, $cachedData['size']);
-
- $cache->put('folder', ['storage_mtime' => 10]);
-
- $storage->unlink('folder/bar2.txt');
- $updater->checkUpdate('folder');
-
- $this->assertTrue($cache->inCache('folder/bar.txt'));
- $this->assertFalse($cache->inCache('folder/bar2.txt'));
- }
-
- public function testFileToFolder(): void {
- $storage = $this->getTestStorage();
- $cache = $storage->getCache();
- $updater = $storage->getWatcher();
- $updater->setPolicy(Watcher::CHECK_ONCE);
-
- //set the mtime to the past so it can detect an mtime change
- $cache->put('', ['storage_mtime' => 10]);
-
- $storage->unlink('foo.txt');
- $storage->rename('folder', 'foo.txt');
- $updater->checkUpdate('');
-
- $entry = $cache->get('foo.txt');
- $this->assertEquals('httpd/unix-directory', $entry['mimetype']);
- $this->assertFalse($cache->inCache('folder'));
- $this->assertFalse($cache->inCache('folder/bar.txt'));
-
- $storage = $this->getTestStorage();
- $cache = $storage->getCache();
- $updater = $storage->getWatcher();
- $updater->setPolicy(Watcher::CHECK_ONCE);
-
- //set the mtime to the past so it can detect an mtime change
- $cache->put('foo.txt', ['storage_mtime' => 10]);
-
- $storage->unlink('foo.txt');
- $storage->rename('folder', 'foo.txt');
- $updater->checkUpdate('foo.txt');
-
- $entry = $cache->get('foo.txt');
- $this->assertEquals('httpd/unix-directory', $entry['mimetype']);
- $this->assertTrue($cache->inCache('foo.txt/bar.txt'));
- }
-
- public function testPolicyNever(): void {
- $storage = $this->getTestStorage();
- $cache = $storage->getCache();
- $updater = $storage->getWatcher();
-
- //set the mtime to the past so it can detect an mtime change
- $cache->put('foo.txt', ['storage_mtime' => 10]);
-
- $updater->setPolicy(Watcher::CHECK_NEVER);
-
- $storage->file_put_contents('foo.txt', 'q');
- $this->assertFalse($updater->checkUpdate('foo.txt'));
-
- $cache->put('foo.txt', ['storage_mtime' => 20]);
- $storage->file_put_contents('foo.txt', 'w');
- $this->assertFalse($updater->checkUpdate('foo.txt'));
- }
-
- public function testPolicyOnce(): void {
- $storage = $this->getTestStorage();
- $cache = $storage->getCache();
- $updater = $storage->getWatcher();
-
- //set the mtime to the past so it can detect an mtime change
- $cache->put('foo.txt', ['storage_mtime' => 10]);
-
- $updater->setPolicy(Watcher::CHECK_ONCE);
-
- $storage->file_put_contents('foo.txt', 'q');
- $this->assertTrue($updater->checkUpdate('foo.txt'));
-
- $cache->put('foo.txt', ['storage_mtime' => 20]);
- $storage->file_put_contents('foo.txt', 'w');
- $this->assertFalse($updater->checkUpdate('foo.txt'));
- }
-
- public function testPolicyAlways(): void {
- $storage = $this->getTestStorage();
- $cache = $storage->getCache();
- $updater = $storage->getWatcher();
-
- //set the mtime to the past so it can detect an mtime change
- $cache->put('foo.txt', ['storage_mtime' => 10]);
-
- $updater->setPolicy(Watcher::CHECK_ALWAYS);
-
- $storage->file_put_contents('foo.txt', 'q');
- $this->assertTrue($updater->checkUpdate('foo.txt'));
-
- $cache->put('foo.txt', ['storage_mtime' => 20]);
- $storage->file_put_contents('foo.txt', 'w');
- $this->assertTrue($updater->checkUpdate('foo.txt'));
- }
-
- private function getTestStorage(bool $scan = true): IStorage {
- $storage = new Temporary([]);
- $textData = "dummy file data\n";
- $imgData = file_get_contents(\OC::$SERVERROOT . '/core/img/logo/logo.png');
- $storage->mkdir('folder');
- $storage->file_put_contents('foo.txt', $textData);
- $storage->file_put_contents('foo.png', $imgData);
- $storage->file_put_contents('folder/bar.txt', $textData);
- $storage->file_put_contents('folder/bar2.txt', $textData);
-
- if ($scan) {
- $scanner = $storage->getScanner();
- $scanner->scan('');
- }
- $this->storages[] = $storage;
- return $storage;
- }
-
- public static function checkFilterProvider(): array {
- return [
- [null, [
- '' => true,
- 'foo' => true,
- 'foo.txt' => true,
- ]],
- ['/^.+$/', [
- '' => false,
- 'foo' => true,
- 'foo.txt' => true,
- ]],
- ['/^.+\..+$/', [
- '' => false,
- 'foo' => false,
- 'foo.txt' => true,
- ]]
- ];
- }
-
- #[DataProvider(methodName: 'checkFilterProvider')]
- public function testCheckFilter(?string $filter, array $paths): void {
- $storage = $this->createMock(IStorage::class);
- $storage->method('hasUpdated')
- ->willReturn(true);
- $storage->method('getCache')
- ->willReturn($this->createMock(ICache::class));
- $storage->method('getScanner')
- ->willReturn($this->createMock(IScanner::class));
- $watcher = new Watcher($storage);
- $watcher->setPolicy(IWatcher::CHECK_ALWAYS);
-
- $watcher->setCheckFilter($filter);
-
- $entry = new CacheEntry([
- 'storage_mtime' => 0,
- ]);
-
- foreach ($paths as $patch => $shouldUpdate) {
- $this->assertEquals($shouldUpdate, $watcher->needsUpdate($patch, $entry));
- }
- }
-}
--- a/tests/lib/Files/Cache/Wrapper/CacheJailTest.php
+++ /dev/null
@@ -1,270 +0,0 @@
-storage->mkdir('jail');
- $this->sourceCache = $this->cache;
- $this->cache = new CacheJail($this->sourceCache, 'jail');
- $this->cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- }
-
- public function testSearchOutsideJail(): void {
- $this->storage->getScanner()->scan('');
- $file1 = 'jail/foobar';
- $file2 = 'folder/foobar';
- $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder'];
-
- $this->sourceCache->insert('folder', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $this->sourceCache->put($file1, $data1);
- $this->sourceCache->put($file2, $data1);
-
- $this->assertCount(2, $this->sourceCache->search('%foobar'));
-
- $result = $this->cache->search('%foobar%');
- $this->assertCount(1, $result);
- $this->assertEquals('foobar', $result[0]['path']);
-
- $result = $this->cache->search('%foo%');
- $this->assertCount(1, $result);
- usort($result, function ($a, $b) {
- return $a['path'] <=> $b['path'];
- });
- $this->assertEquals('foobar', $result[0]['path']);
- }
-
- public function testSearchMimeOutsideJail(): void {
- $this->storage->getScanner()->scan('');
- $file1 = 'jail/foobar';
- $file2 = 'folder/foobar';
- $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder'];
-
- $this->sourceCache->insert('folder', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $this->sourceCache->put($file1, $data1);
- $this->sourceCache->put($file2, $data1);
-
- $this->assertCount(2, $this->sourceCache->searchByMime('foo/folder'));
-
- $result = $this->cache->search('%foobar%');
- $this->assertCount(1, $result);
- $this->assertEquals('foobar', $result[0]['path']);
- }
-
- public function testSearchQueryOutsideJail(): void {
- $this->storage->getScanner()->scan('');
- $file1 = 'jail/foobar';
- $file2 = 'folder/foobar';
- $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder'];
-
- $this->sourceCache->insert('folder', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $this->sourceCache->put($file1, $data1);
- $this->sourceCache->put($file2, $data1);
-
- $user = new User('foo', null, $this->createMock(IEventDispatcher::class));
- $query = new SearchQuery(new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'name', 'foobar'), 10, 0, [], $user);
- $result = $this->cache->searchQuery($query);
-
- $this->assertCount(1, $result);
- $this->assertEquals('foobar', $result[0]['path']);
-
- $query = new SearchQuery(new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'name', 'jail'), 10, 0, [], $user);
- $result = $this->cache->searchQuery($query);
- $this->assertCount(1, $result);
- $this->assertEquals('', $result[0]['path']);
- }
-
- public function testClearKeepEntriesOutsideJail(): void {
- $file1 = 'jail/foobar';
- $file2 = 'jail/foobar/asd';
- $file3 = 'folder/foobar';
- $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
-
- $this->sourceCache->put('folder', $data1);
- $this->sourceCache->put($file1, $data1);
- $this->sourceCache->put($file2, $data1);
- $this->sourceCache->put($file3, $data1);
-
- $this->cache->clear();
-
- $this->assertFalse($this->cache->inCache('foobar'));
- $this->assertTrue($this->sourceCache->inCache('folder/foobar'));
- }
-
- #[\Override]
- public function testGetById(): void {
- $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
- $id = $this->sourceCache->put('jail/bar', $data1);
-
- // path from jailed foo of foo/bar is bar
- $path = $this->cache->getPathById($id);
- $this->assertEquals('bar', $path);
-
- // path from jailed '' of foo/bar is foo/bar
- $this->cache = new CacheJail($this->sourceCache, '');
- $path = $this->cache->getPathById($id);
- $this->assertEquals('jail/bar', $path);
- }
-
- #[\Override]
- public function testGetIncomplete(): void {
- //not supported
- $this->addToAssertionCount(1);
- }
-
- public function testMoveFromJail(): void {
- $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
-
- $this->sourceCache->put('source', $folderData);
- $this->sourceCache->put('source/foo', $folderData);
- $this->sourceCache->put('source/foo/bar', $folderData);
- $this->sourceCache->put('target', $folderData);
-
- $jail = new CacheJail($this->sourceCache, 'source');
-
- $this->sourceCache->moveFromCache($jail, 'foo', 'target/foo');
-
- $this->assertTrue($this->sourceCache->inCache('target/foo'));
- $this->assertTrue($this->sourceCache->inCache('target/foo/bar'));
- }
-
- public function testMoveToJail(): void {
- $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
-
- $this->sourceCache->put('source', $folderData);
- $this->sourceCache->put('source/foo', $folderData);
- $this->sourceCache->put('source/foo/bar', $folderData);
- $this->sourceCache->put('target', $folderData);
-
- $jail = new CacheJail($this->sourceCache, 'target');
-
- $jail->moveFromCache($this->sourceCache, 'source/foo', 'foo');
-
- $this->assertTrue($this->sourceCache->inCache('target/foo'));
- $this->assertTrue($this->sourceCache->inCache('target/foo/bar'));
- }
-
- public function testMoveBetweenJail(): void {
- $folderData = ['size' => 100, 'mtime' => 50, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE];
-
- $this->sourceCache->put('source', $folderData);
- $this->sourceCache->put('source/foo', $folderData);
- $this->sourceCache->put('source/foo/bar', $folderData);
- $this->sourceCache->put('target', $folderData);
-
- $jail = new CacheJail($this->sourceCache, 'target');
- $sourceJail = new CacheJail($this->sourceCache, 'source');
-
- $jail->moveFromCache($sourceJail, 'foo', 'foo');
-
- $this->assertTrue($this->sourceCache->inCache('target/foo'));
- $this->assertTrue($this->sourceCache->inCache('target/foo/bar'));
- }
-
- public function testSearchNested(): void {
- $this->storage->getScanner()->scan('');
- $file1 = 'jail';
- $file2 = 'jail/bar';
- $file3 = 'jail/bar/asd';
- $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder'];
-
- $this->sourceCache->put($file1, $data1);
- $this->sourceCache->put($file2, $data1);
- $this->sourceCache->put($file3, $data1);
-
- $nested = new CacheJail($this->cache, 'bar');
-
- $result = $nested->search('%asd%');
- $this->assertCount(1, $result);
- $this->assertEquals('asd', $result[0]['path']);
- }
-
- public function testRootJail(): void {
- $this->storage->getScanner()->scan('');
- $file1 = 'jail';
- $file2 = 'jail/bar';
- $file3 = 'jail/bar/asd';
- $data1 = ['size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder'];
-
- $this->sourceCache->put($file1, $data1);
- $this->sourceCache->put($file2, $data1);
- $this->sourceCache->put($file3, $data1);
-
- $nested = new CacheJail($this->sourceCache, '');
-
- $result = $nested->search('%asd%');
- $this->assertCount(1, $result);
- $this->assertEquals('jail/bar/asd', $result[0]['path']);
- }
-
- public function testWatcher(): void {
- $storage = new Jail([
- 'storage' => $this->storage,
- 'root' => 'jail'
- ]);
- $storage->getScanner()->scan('');
- $storage->file_put_contents('bar', 'asd');
-
- $this->assertFalse($this->cache->inCache('bar'));
- $storage->getWatcher()->update('bar', ['mimetype' => 'text/plain']);
- $this->assertTrue($this->cache->inCache('bar'));
- }
-
- public function testWatcherAfterInnerWatcher(): void {
- $storage = new Jail([
- 'storage' => $this->storage,
- 'root' => 'jail'
- ]);
- $storage->getScanner()->scan('');
- $storage->file_put_contents('bar', 'asd');
-
- // let the underlying storage create it's watcher first
- $this->storage->getWatcher();
-
- $this->assertFalse($this->cache->inCache('bar'));
- $storage->getWatcher()->update('bar', ['mimetype' => 'text/plain']);
- $this->assertTrue($this->cache->inCache('bar'));
- }
-
- public function testUnJailedRoot(): void {
- $jail1 = new CacheJail($this->sourceCache, 'foo');
- $jail2 = new CacheJail($jail1, 'bar');
- $this->assertEquals('foo/bar', $jail2->getGetUnjailedRoot());
-
- $middleWrapper = new CacheWrapper($jail1);
- $jail3 = new CacheJail($middleWrapper, 'bar');
- $this->assertEquals('foo/bar', $jail3->getGetUnjailedRoot());
- }
-}
--- a/tests/lib/Files/Cache/Wrapper/CachePermissionsMaskTest.php
+++ /dev/null
@@ -1,104 +0,0 @@
-storage->mkdir('foo');
- $this->sourceCache = $this->cache;
- $this->cache = $this->getMaskedCached(Constants::PERMISSION_ALL);
- }
-
- protected function getMaskedCached($mask) {
- return new CachePermissionsMask($this->sourceCache, $mask);
- }
-
- public static function maskProvider(): array {
- return [
- [Constants::PERMISSION_ALL],
- [Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE],
- [Constants::PERMISSION_ALL - Constants::PERMISSION_UPDATE],
- [Constants::PERMISSION_READ]
- ];
- }
-
- /**
- * @param int $mask
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('maskProvider')]
- public function testGetMasked($mask): void {
- $cache = $this->getMaskedCached($mask);
- $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain', 'permissions' => Constants::PERMISSION_ALL];
- $this->sourceCache->put('foo', $data);
- $result = $cache->get('foo');
- $this->assertEquals($mask, $result['permissions']);
-
- $data = ['size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain', 'permissions' => Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE];
- $this->sourceCache->put('bar', $data);
- $result = $cache->get('bar');
- $this->assertEquals($mask & ~Constants::PERMISSION_DELETE, $result['permissions']);
- }
-
- /**
- * @param int $mask
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('maskProvider')]
- public function testGetFolderContentMasked($mask): void {
- $this->storage->mkdir('foo');
- $this->storage->file_put_contents('foo/bar', 'asd');
- $this->storage->file_put_contents('foo/asd', 'bar');
- $this->storage->getScanner()->scan('');
-
- $cache = $this->getMaskedCached($mask);
- $files = $cache->getFolderContents('foo');
- $this->assertCount(2, $files);
-
- foreach ($files as $file) {
- $this->assertEquals($mask & ~Constants::PERMISSION_CREATE, $file['permissions']);
- }
- }
-
- /**
- * @param int $mask
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('maskProvider')]
- public function testSearchMasked($mask): void {
- $this->storage->mkdir('foo');
- $this->storage->file_put_contents('foo/bar', 'asd');
- $this->storage->file_put_contents('foo/foobar', 'bar');
- $this->storage->getScanner()->scan('');
-
- $cache = $this->getMaskedCached($mask);
- $files = $cache->search('%bar');
- $this->assertCount(2, $files);
-
- foreach ($files as $file) {
- $this->assertEquals($mask & ~Constants::PERMISSION_CREATE, $file['permissions']);
- }
- }
-}
--- a/tests/lib/Files/Config/UserMountCacheTest.php
+++ /dev/null
@@ -1,675 +0,0 @@
-fileIds = [];
-
- $this->connection = Server::get(IDBConnection::class);
-
- $config = $this->getMockBuilder(IConfig::class)
- ->disableOriginalConstructor()
- ->getMock();
- $config
- ->expects($this->any())
- ->method('getUserValue')
- ->willReturnArgument(3);
- $config
- ->expects($this->any())
- ->method('getAppValue')
- ->willReturnArgument(2);
-
- $this->userManager = new Manager($config, $this->createMock(ICacheFactory::class), $this->createMock(IEventDispatcher::class), $this->createMock(LoggerInterface::class));
- $userBackend = new Dummy();
- $userBackend->createUser('u1', '');
- $userBackend->createUser('u2', '');
- $userBackend->createUser('u3', '');
- $this->userManager->registerBackend($userBackend);
-
- $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
-
- $this->cache = new UserMountCache($this->connection,
- $this->userManager,
- $this->createMock(LoggerInterface::class),
- $this->createMock(IEventLogger::class),
- $this->eventDispatcher,
- );
- }
-
- #[\Override]
- protected function tearDown(): void {
- $builder = $this->connection->getQueryBuilder();
-
- $builder->delete('mounts')->executeStatement();
-
- $builder = $this->connection->getQueryBuilder();
-
- foreach ($this->fileIds as $fileId) {
- $builder->delete('filecache')
- ->where($builder->expr()->eq('fileid', new Literal($fileId)))
- ->executeStatement();
- }
- parent::tearDown();
- }
-
- private function getStorage($storageId, $rootInternalPath = '') {
- $rootId = $this->createCacheEntry($rootInternalPath, $storageId);
-
- $storageCache = $this->createMock(\OC\Files\Cache\Storage::class);
- $storageCache->expects($this->any())
- ->method('getNumericId')
- ->willReturn($storageId);
-
- $cache = $this->createMock(Cache::class);
- $cache->expects($this->any())
- ->method('getId')
- ->willReturn($rootId);
- $cache->method('getNumericStorageId')
- ->willReturn($storageId);
-
- $storage = $this->createMock(Storage::class);
- $storage->expects($this->any())
- ->method('getStorageCache')
- ->willReturn($storageCache);
- $storage->expects($this->any())
- ->method('getCache')
- ->willReturn($cache);
-
- return [$storage, $rootId];
- }
-
- private function clearCache() {
- $this->invokePrivate($this->cache, 'mountsForUsers', [new CappedMemoryCache()]);
- }
-
- private function keyForMount(MountPoint $mount): string {
- return $mount->getStorageRootId() . '::' . $mount->getMountPoint();
- }
-
- public function testNewMounts(): void {
- $this->eventDispatcher
- ->expects($this->once())
- ->method('dispatchTyped')
- ->with($this->callback(fn (UserMountAddedEvent $event) => $event->mountPoint->getMountPoint() === '/asd/'));
-
- $user = $this->userManager->get('u1');
-
- [$storage] = $this->getStorage(10);
- $mount = new MountPoint($storage, '/asd/');
-
- $this->cache->registerMounts($user, [$mount]);
-
- $this->clearCache();
-
- $cachedMounts = $this->cache->getMountsForUser($user);
-
- $this->assertCount(1, $cachedMounts);
- $cachedMount = $cachedMounts[$this->keyForMount($mount)];
- $this->assertEquals('/asd/', $cachedMount->getMountPoint());
- $this->assertEquals($user->getUID(), $cachedMount->getUser()->getUID());
- $this->assertEquals($storage->getCache()->getId(''), $cachedMount->getRootId());
- $this->assertEquals($storage->getStorageCache()->getNumericId(), $cachedMount->getStorageId());
- }
-
- public function testSameMounts(): void {
- $this->eventDispatcher
- ->expects($this->once())
- ->method('dispatchTyped')
- ->with($this->callback(fn (UserMountAddedEvent $event) => $event->mountPoint->getMountPoint() === '/asd/'));
-
- $user = $this->userManager->get('u1');
-
- [$storage] = $this->getStorage(10);
- $mount = new MountPoint($storage, '/asd/');
-
- $this->cache->registerMounts($user, [$mount]);
-
- $this->clearCache();
-
- $this->cache->registerMounts($user, [$mount]);
-
- $this->clearCache();
-
- $cachedMounts = $this->cache->getMountsForUser($user);
-
- $this->assertCount(1, $cachedMounts);
- $cachedMount = $cachedMounts[$this->keyForMount($mount)];
- $this->assertEquals('/asd/', $cachedMount->getMountPoint());
- $this->assertEquals($user->getUID(), $cachedMount->getUser()->getUID());
- $this->assertEquals($storage->getCache()->getId(''), $cachedMount->getRootId());
- $this->assertEquals($storage->getStorageCache()->getNumericId(), $cachedMount->getStorageId());
- }
-
- public function testRemoveMounts(): void {
- $operation = 0;
- $this->eventDispatcher
- ->expects($this->exactly(2))
- ->method('dispatchTyped')
- ->with($this->callback(function (
- UserMountAddedEvent|UserMountRemovedEvent $event,
- ) use (&$operation) {
- return match (++$operation) {
- 1 => $event instanceof UserMountAddedEvent && $event->mountPoint->getMountPoint() === '/asd/',
- 2 => $event instanceof UserMountRemovedEvent && $event->mountPoint->getMountPoint() === '/asd/',
- default => false,
- };
- }));
-
- $user = $this->userManager->get('u1');
-
- [$storage] = $this->getStorage(10);
- $mount = new MountPoint($storage, '/asd/');
-
- $this->cache->registerMounts($user, [$mount]);
-
- $this->clearCache();
-
- $this->cache->registerMounts($user, []);
-
- $this->clearCache();
-
- $cachedMounts = $this->cache->getMountsForUser($user);
-
- $this->assertCount(0, $cachedMounts);
- }
-
- public function testChangeMounts(): void {
- $operation = 0;
- $this->eventDispatcher
- ->expects($this->exactly(3))
- ->method('dispatchTyped')
- ->with($this->callback(function (
- UserMountAddedEvent|UserMountRemovedEvent $event,
- ) use (&$operation) {
- return match (++$operation) {
- 1 => $event instanceof UserMountAddedEvent && $event->mountPoint->getMountPoint() === '/bar/',
- 2 => $event instanceof UserMountAddedEvent && $event->mountPoint->getMountPoint() === '/foo/',
- 3 => $event instanceof UserMountRemovedEvent && $event->mountPoint->getMountPoint() === '/bar/',
- default => false,
- };
- }));
-
- $user = $this->userManager->get('u1');
-
- [$storage] = $this->getStorage(10);
- $mount = new MountPoint($storage, '/bar/');
-
- $this->cache->registerMounts($user, [$mount]);
-
- $this->clearCache();
-
- $mount = new MountPoint($storage, '/foo/');
-
- $this->cache->registerMounts($user, [$mount]);
-
- $this->clearCache();
-
- $cachedMounts = $this->cache->getMountsForUser($user);
-
- $this->assertCount(1, $cachedMounts);
- $cachedMount = $cachedMounts[$this->keyForMount($mount)];
- $this->assertEquals('/foo/', $cachedMount->getMountPoint());
- }
-
- public function testChangeMountId(): void {
- $operation = 0;
- $this->eventDispatcher
- ->expects($this->exactly(2))
- ->method('dispatchTyped')
- ->with($this->callback(function (
- UserMountAddedEvent|UserMountUpdatedEvent $event,
- ) use (&$operation) {
- return match (++$operation) {
- 1 => $event instanceof UserMountAddedEvent && $event->mountPoint->getMountPoint() === '/foo/',
- 2 => $event instanceof UserMountUpdatedEvent && $event->oldMountPoint->getMountId() === null && $event->newMountPoint->getMountId() === 1,
- default => false,
- };
- }));
-
- $user = $this->userManager->get('u1');
-
- [$storage] = $this->getStorage(10);
- $mount = new MountPoint($storage, '/foo/', null, null, null, null);
-
- $this->cache->registerMounts($user, [$mount]);
-
- $this->clearCache();
-
- $mount = new MountPoint($storage, '/foo/', null, null, null, 1);
-
- $this->cache->registerMounts($user, [$mount]);
-
- $this->clearCache();
-
- $cachedMounts = $this->cache->getMountsForUser($user);
-
- $this->assertCount(1, $cachedMounts);
- $cachedMount = $cachedMounts[$this->keyForMount($mount)];
- $this->assertEquals(1, $cachedMount->getMountId());
- }
-
- public function testGetMountsForUser(): void {
- $user1 = $this->userManager->get('u1');
- $user2 = $this->userManager->get('u2');
- $user3 = $this->userManager->get('u3');
-
- [$storage1, $id1] = $this->getStorage(1);
- [$storage2, $id2] = $this->getStorage(2, 'foo/bar');
- $mount1 = new MountPoint($storage1, '/foo/');
- $mount2 = new MountPoint($storage2, '/bar/');
-
- $this->cache->registerMounts($user1, [$mount1, $mount2]);
- $this->cache->registerMounts($user2, [$mount2]);
- $this->cache->registerMounts($user3, [$mount2]);
-
- $this->clearCache();
-
- $user3->delete();
-
- $cachedMounts = $this->cache->getMountsForUser($user1);
-
- $this->assertCount(2, $cachedMounts);
- $this->assertEquals('/foo/', $cachedMounts[$this->keyForMount($mount1)]->getMountPoint());
- $this->assertEquals($user1->getUID(), $cachedMounts[$this->keyForMount($mount1)]->getUser()->getUID());
- $this->assertEquals($id1, $cachedMounts[$this->keyForMount($mount1)]->getRootId());
- $this->assertEquals(1, $cachedMounts[$this->keyForMount($mount1)]->getStorageId());
- $this->assertEquals('', $cachedMounts[$this->keyForMount($mount1)]->getRootInternalPath());
-
- $this->assertEquals('/bar/', $cachedMounts[$this->keyForMount($mount2)]->getMountPoint());
- $this->assertEquals($user1->getUID(), $cachedMounts[$this->keyForMount($mount2)]->getUser()->getUID());
- $this->assertEquals($id2, $cachedMounts[$this->keyForMount($mount2)]->getRootId());
- $this->assertEquals(2, $cachedMounts[$this->keyForMount($mount2)]->getStorageId());
- $this->assertEquals('foo/bar', $cachedMounts[$this->keyForMount($mount2)]->getRootInternalPath());
-
- $cachedMounts = $this->cache->getMountsForUser($user3);
- $this->assertEmpty($cachedMounts);
- }
-
- public function testGetMountsByStorageId(): void {
- $user1 = $this->userManager->get('u1');
- $user2 = $this->userManager->get('u2');
-
- [$storage1, $id1] = $this->getStorage(1);
- [$storage2, $id2] = $this->getStorage(2);
- $mount1 = new MountPoint($storage1, '/foo/');
- $mount2 = new MountPoint($storage2, '/bar/');
-
- $this->cache->registerMounts($user1, [$mount1, $mount2]);
- $this->cache->registerMounts($user2, [$mount2]);
-
- $this->clearCache();
-
- $cachedMounts = $this->cache->getMountsForStorageId(2);
- $this->sortMounts($cachedMounts);
-
- $this->assertCount(2, $cachedMounts);
-
- $this->assertEquals('/bar/', $cachedMounts[0]->getMountPoint());
- $this->assertEquals($user1->getUID(), $cachedMounts[0]->getUser()->getUID());
- $this->assertEquals($id2, $cachedMounts[0]->getRootId());
- $this->assertEquals(2, $cachedMounts[0]->getStorageId());
-
- $this->assertEquals('/bar/', $cachedMounts[1]->getMountPoint());
- $this->assertEquals($user2->getUID(), $cachedMounts[1]->getUser()->getUID());
- $this->assertEquals($id2, $cachedMounts[1]->getRootId());
- $this->assertEquals(2, $cachedMounts[1]->getStorageId());
- }
-
- public function testGetMountsByRootId(): void {
- $user1 = $this->userManager->get('u1');
- $user2 = $this->userManager->get('u2');
-
- [$storage1, $id1] = $this->getStorage(1);
- [$storage2, $id2] = $this->getStorage(2);
- $mount1 = new MountPoint($storage1, '/foo/');
- $mount2 = new MountPoint($storage2, '/bar/');
-
- $this->cache->registerMounts($user1, [$mount1, $mount2]);
- $this->cache->registerMounts($user2, [$mount2]);
-
- $this->clearCache();
-
- $cachedMounts = $this->cache->getMountsForRootId($id2);
- $this->sortMounts($cachedMounts);
-
- $this->assertCount(2, $cachedMounts);
-
- $this->assertEquals('/bar/', $cachedMounts[0]->getMountPoint());
- $this->assertEquals($user1->getUID(), $cachedMounts[0]->getUser()->getUID());
- $this->assertEquals($id2, $cachedMounts[0]->getRootId());
- $this->assertEquals(2, $cachedMounts[0]->getStorageId());
-
- $this->assertEquals('/bar/', $cachedMounts[1]->getMountPoint());
- $this->assertEquals($user2->getUID(), $cachedMounts[1]->getUser()->getUID());
- $this->assertEquals($id2, $cachedMounts[1]->getRootId());
- $this->assertEquals(2, $cachedMounts[1]->getStorageId());
- }
-
- private function sortMounts(&$mounts) {
- usort($mounts, function (ICachedMountInfo $a, ICachedMountInfo $b) {
- return strcmp($a->getUser()->getUID(), $b->getUser()->getUID());
- });
- }
-
- private function createCacheEntry($internalPath, $storageId, $size = 0) {
- $internalPath = trim($internalPath, '/');
- try {
- $query = $this->connection->getQueryBuilder();
- $query->insert('filecache')
- ->values([
- 'storage' => $query->createNamedParameter($storageId),
- 'path' => $query->createNamedParameter($internalPath),
- 'path_hash' => $query->createNamedParameter(md5($internalPath)),
- 'parent' => $query->createNamedParameter(-1, IQueryBuilder::PARAM_INT),
- 'name' => $query->createNamedParameter(basename($internalPath)),
- 'mimetype' => $query->createNamedParameter(0, IQueryBuilder::PARAM_INT),
- 'mimepart' => $query->createNamedParameter(0, IQueryBuilder::PARAM_INT),
- 'size' => $query->createNamedParameter($size),
- 'storage_mtime' => $query->createNamedParameter(0, IQueryBuilder::PARAM_INT),
- 'encrypted' => $query->createNamedParameter(0, IQueryBuilder::PARAM_INT),
- 'unencrypted_size' => $query->createNamedParameter(0, IQueryBuilder::PARAM_INT),
- 'etag' => $query->createNamedParameter(''),
- 'permissions' => $query->createNamedParameter(31, IQueryBuilder::PARAM_INT),
- ]);
- $query->executeStatement();
- $id = $query->getLastInsertId();
- $this->fileIds[] = $id;
- } catch (DbalException $e) {
- if ($e->getReason() === DbalException::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
- $query = $this->connection->getQueryBuilder();
- $query->select('fileid')
- ->from('filecache')
- ->where($query->expr()->eq('storage', $query->createNamedParameter($storageId)))
- ->andWhere($query->expr()->eq('path_hash', $query->createNamedParameter(md5($internalPath))));
- $id = (int)$query->executeQuery()->fetchOne();
- } else {
- throw $e;
- }
- }
- return $id;
- }
-
- public function testGetMountsForFileIdRootId(): void {
- $user1 = $this->userManager->get('u1');
-
- [$storage1, $rootId] = $this->getStorage(2);
- $mount1 = new MountPoint($storage1, '/foo/');
-
- $this->cache->registerMounts($user1, [$mount1]);
-
- $this->clearCache();
-
- $cachedMounts = $this->cache->getMountsForFileId($rootId);
-
- $this->assertCount(1, $cachedMounts);
-
- $this->assertEquals('/foo/', $cachedMounts[0]->getMountPoint());
- $this->assertEquals($user1->getUID(), $cachedMounts[0]->getUser()->getUID());
- $this->assertEquals($rootId, $cachedMounts[0]->getRootId());
- $this->assertEquals(2, $cachedMounts[0]->getStorageId());
- }
-
- public function testGetMountsForFileIdSubFolder(): void {
- $user1 = $this->userManager->get('u1');
-
- $fileId = $this->createCacheEntry('/foo/bar', 2);
-
- [$storage1, $rootId] = $this->getStorage(2);
- $mount1 = new MountPoint($storage1, '/foo/');
-
- $this->cache->registerMounts($user1, [$mount1]);
-
- $this->clearCache();
-
- $cachedMounts = $this->cache->getMountsForFileId($fileId);
-
- $this->assertCount(1, $cachedMounts);
-
- $this->assertEquals('/foo/', $cachedMounts[0]->getMountPoint());
- $this->assertEquals($user1->getUID(), $cachedMounts[0]->getUser()->getUID());
- $this->assertEquals($rootId, $cachedMounts[0]->getRootId());
- $this->assertEquals(2, $cachedMounts[0]->getStorageId());
- $this->assertEquals('foo/bar', $cachedMounts[0]->getInternalPath());
- $this->assertEquals('/foo/foo/bar', $cachedMounts[0]->getPath());
- }
-
- public function testGetMountsForFileIdSubFolderMount(): void {
- $user1 = $this->userManager->get('u1');
-
- [$storage1, $rootId] = $this->getStorage(2);
- $folderId = $this->createCacheEntry('/foo', 2);
- $fileId = $this->createCacheEntry('/foo/bar', 2);
-
- $mount1 = $this->getMockBuilder(MountPoint::class)
- ->setConstructorArgs([$storage1, '/'])
- ->onlyMethods(['getStorageRootId'])
- ->getMock();
-
- $mount1->expects($this->any())
- ->method('getStorageRootId')
- ->willReturn($folderId);
-
- $this->cache->registerMounts($user1, [$mount1]);
-
- $this->clearCache();
-
- $cachedMounts = $this->cache->getMountsForFileId($fileId);
-
- $this->assertCount(1, $cachedMounts);
-
- $this->assertEquals('/', $cachedMounts[0]->getMountPoint());
- $this->assertEquals($user1->getUID(), $cachedMounts[0]->getUser()->getUID());
- $this->assertEquals($folderId, $cachedMounts[0]->getRootId());
- $this->assertEquals(2, $cachedMounts[0]->getStorageId());
- $this->assertEquals('foo', $cachedMounts[0]->getRootInternalPath());
- $this->assertEquals('bar', $cachedMounts[0]->getInternalPath());
- $this->assertEquals('/bar', $cachedMounts[0]->getPath());
- }
-
- public function testGetMountsForFileIdSubFolderMountOutside(): void {
- $user1 = $this->userManager->get('u1');
-
- [$storage1, $rootId] = $this->getStorage(2);
- $folderId = $this->createCacheEntry('/foo', 2);
- $fileId = $this->createCacheEntry('/bar/asd', 2);
-
- $mount1 = $this->getMockBuilder(MountPoint::class)
- ->setConstructorArgs([$storage1, '/foo/'])
- ->onlyMethods(['getStorageRootId'])
- ->getMock();
-
- $mount1->expects($this->any())
- ->method('getStorageRootId')
- ->willReturn($folderId);
-
- $this->cache->registerMounts($user1, [$mount1]);
-
- $this->cache->registerMounts($user1, [$mount1]);
-
- $this->clearCache();
-
- $cachedMounts = $this->cache->getMountsForFileId($fileId);
-
- $this->assertCount(0, $cachedMounts);
- }
-
- public function testGetMountsForFileIdDeletedUser(): void {
- $user1 = $this->userManager->get('u1');
-
- [$storage1, $rootId] = $this->getStorage(2);
- $rootId = $this->createCacheEntry('', 2);
- $mount1 = new MountPoint($storage1, '/foo/');
- $this->cache->registerMounts($user1, [$mount1]);
-
- $user1->delete();
- $this->clearCache();
-
- $cachedMounts = $this->cache->getMountsForFileId($rootId);
- $this->assertEmpty($cachedMounts);
- }
-
- public function testGetUsedSpaceForUsers(): void {
- $user1 = $this->userManager->get('u1');
- $user2 = $this->userManager->get('u2');
-
- /** @var Storage $storage1 */
- [$storage1, $rootId] = $this->getStorage(2);
- $folderId = $this->createCacheEntry('files', 2, 100);
- $fileId = $this->createCacheEntry('files/foo', 2, 7);
- $storage1->getCache()->put($folderId, ['size' => 100]);
- $storage1->getCache()->update($fileId, ['size' => 70]);
-
- $mount1 = $this->getMockBuilder(MountPoint::class)
- ->setConstructorArgs([$storage1, '/u1/'])
- ->onlyMethods(['getStorageRootId', 'getNumericStorageId'])
- ->getMock();
-
- $mount1->expects($this->any())
- ->method('getStorageRootId')
- ->willReturn($rootId);
-
- $mount1->expects($this->any())
- ->method('getNumericStorageId')
- ->willReturn(2);
-
- $this->cache->registerMounts($user1, [$mount1]);
-
- $result = $this->cache->getUsedSpaceForUsers([$user1, $user2]);
- $this->assertEquals(['u1' => 100], $result);
- }
-
- public function testMigrateMountProvider(): void {
- $user1 = $this->userManager->get('u1');
-
- [$storage1, $rootId] = $this->getStorage(2);
- $rootId = $this->createCacheEntry('', 2);
- $mount1 = new MountPoint($storage1, '/foo/');
- $this->cache->registerMounts($user1, [$mount1]);
-
- $this->clearCache();
-
- $cachedMounts = $this->cache->getMountsForUser($user1);
- $this->assertCount(1, $cachedMounts);
- $this->assertEquals('', $cachedMounts[$this->keyForMount($mount1)]->getMountProvider());
-
- $mount1 = new MountPoint($storage1, '/foo/', null, null, null, null, 'dummy');
- $this->cache->registerMounts($user1, [$mount1], ['dummy']);
-
- $this->clearCache();
-
- $cachedMounts = $this->cache->getMountsForUser($user1);
- $this->assertCount(1, $cachedMounts);
- $this->assertEquals('dummy', $cachedMounts[$this->keyForMount($mount1)]->getMountProvider());
- }
-
- public function testChangedSameRootId(): void {
- $user = $this->userManager->get('u1');
-
- [$storage] = $this->getStorage(10);
- $mount1 = new MountPoint($storage, '/asd/');
- $mount2 = new MountPoint($storage, '/asd2/');
-
- $this->cache->registerMounts($user, [$mount1, $mount2]);
-
- $mount2 = new MountPoint($storage, '/asd2/', null, null, null, 1);
- $this->cache->registerMounts($user, [$mount1, $mount2]);
-
- $this->cache->flush();
- $cached = $this->cache->getMountsForUser($user);
- usort($cached, fn (ICachedMountInfo $a, ICachedMountInfo $b,
- ) => $a->getMountPoint() <=> $b->getMountPoint());
-
- $mountPoints = array_map(fn (ICachedMountInfo $mountInfo,
- ) => $mountInfo->getMountPoint(), $cached);
- $this->assertEquals(['/asd/', '/asd2/'], $mountPoints);
-
- $mountIds = array_map(fn (ICachedMountInfo $mountInfo,
- ) => $mountInfo->getMountId(), $cached);
- $this->assertEquals([null, 1], $mountIds);
- }
-
- public function testGetMountForPath(): void {
- $user = $this->userManager->get('u1');
-
- [$storage] = $this->getStorage(10);
- $mount1 = new MountPoint($storage, '/asd/');
- $mount2 = new MountPoint($storage, '/asd/foo');
-
- $this->cache->registerMounts($user, [$mount1, $mount2]);
- $this->cache->flush();
-
- $this->assertEquals('/asd/', $this->cache->getMountForPath($user, '/asd/bar/')->getMountPoint());
- $this->assertEquals('/asd/', $this->cache->getMountForPath($user, '/asd/')->getMountPoint());
- $this->assertEquals('/asd/foo/', $this->cache->getMountForPath($user, '/asd/foo/bar/')->getMountPoint());
- $this->assertEquals('/asd/foo/', $this->cache->getMountForPath($user, '/asd/foo/')->getMountPoint());
- }
-
- public function testGetMountsInPath(): void {
- $user = $this->userManager->get('u1');
-
- [$storage] = $this->getStorage(10);
- $mount1 = new MountPoint($storage, '/asd/');
- $mount2 = new MountPoint($storage, '/asd/foo/');
- $mount3 = new MountPoint($storage, '/asd/foo/bar/');
-
- $this->cache->registerMounts($user, [$mount1, $mount2, $mount3]);
- $this->cache->flush();
-
- $getMountPaths = function (string $path) use ($user) {
- $mountPoints = array_values(
- array_map(
- fn (ICachedMountInfo $mount) => $mount->getMountPoint(),
- $this->cache->getMountsInPath($user, $path)
- )
- );
- sort($mountPoints);
- return $mountPoints;
- };
-
- $this->assertEquals(['/asd/foo/', '/asd/foo/bar/'], $getMountPaths('/asd/'));
- $this->assertEquals([], $getMountPaths('/asd/foo/bar/'));
- $this->assertEquals(['/asd/foo/bar/'], $getMountPaths('/asd/foo/'));
- $this->assertEquals([], $getMountPaths('/asd/bar/'));
- }
-}
--- a/tests/lib/Files/EtagTest.php
+++ /dev/null
@@ -1,106 +0,0 @@
-datadir = $config->getSystemValueString('datadirectory');
- $this->tmpDir = Server::get(ITempManager::class)->getTemporaryFolder();
- $config->setSystemValue('datadirectory', $this->tmpDir);
-
- $this->userBackend = new \Test\Util\User\Dummy();
- Server::get(IUserManager::class)->registerBackend($this->userBackend);
- }
-
- #[\Override]
- protected function tearDown(): void {
- Server::get(IConfig::class)->setSystemValue('datadirectory', $this->datadir);
-
- $this->logout();
- parent::tearDown();
- }
-
- public function testNewUser(): void {
- $user1 = $this->getUniqueID('user_');
- $this->userBackend->createUser($user1, '');
-
- $this->loginAsUser($user1);
- Filesystem::mkdir('/folder');
- Filesystem::mkdir('/folder/subfolder');
- Filesystem::file_put_contents('/foo.txt', 'asd');
- Filesystem::file_put_contents('/folder/bar.txt', 'fgh');
- Filesystem::file_put_contents('/folder/subfolder/qwerty.txt', 'jkl');
-
- $files = ['/foo.txt', '/folder/bar.txt', '/folder/subfolder', '/folder/subfolder/qwerty.txt'];
- $originalEtags = $this->getEtags($files);
-
- $scanner = new Scanner(
- Server::get(IUserManager::class)->get($user1),
- Server::get(IDBConnection::class),
- Server::get(IEventDispatcher::class),
- Server::get(LoggerInterface::class),
- Server::get(SetupManager::class),
- );
- $scanner->backgroundScan('/');
-
- $newEtags = $this->getEtags($files);
- // loop over array and use assertSame over assertEquals to prevent false positives
- foreach ($originalEtags as $file => $originalEtag) {
- $this->assertSame($originalEtag, $newEtags[$file]);
- }
- }
-
- /**
- * @param string[] $files
- */
- private function getEtags($files) {
- $etags = [];
- foreach ($files as $file) {
- $info = Filesystem::getFileInfo($file);
- $etags[$file] = $info['etag'];
- }
- return $etags;
- }
-}
--- a/tests/lib/Files/FileInfoTest.php
+++ /dev/null
@@ -1,109 +0,0 @@
-createUser('foo', 'foo');
- $this->config = $this->getMockBuilder(IConfig::class)->getMock();
- }
-
- private function makeFileInfo(array $data): FileInfo {
- $storage = new Temporary();
- return new FileInfo('', $storage, '', $data, new MountPoint($storage, '/foo/files'));
- }
-
- public function testGetSizeEncryptedZeroByte(): void {
- $info = $this->makeFileInfo(['encrypted' => true, 'size' => 8192, 'unencrypted_size' => 0]);
- // Both paths must report the true plaintext size (0), not the on-disk encrypted size (8192)
- $this->assertSame(0, $info->getSize(true));
- $this->assertSame(0, $info->getSize(false));
- }
-
- public function testGetSizeEncryptedNonZero(): void {
- $info = $this->makeFileInfo(['encrypted' => true, 'size' => 16384, 'unencrypted_size' => 5000]);
- $this->assertSame(5000, $info->getSize(true));
- $this->assertSame(5000, $info->getSize(false));
- }
-
- public function testGetSizeEncryptedFolderWithoutPlaintextSize(): void {
- // End-to-end encrypted folders carry the `encrypted` flag but never a plaintext size,
- // so the propagated `size` has to be reported instead of the unset `unencrypted_size`
- $info = $this->makeFileInfo([
- 'encrypted' => true,
- 'mimetype' => FileInfo::MIMETYPE_FOLDER,
- 'size' => 8192,
- 'unencrypted_size' => 0,
- ]);
- $this->assertSame(8192, $info->getSize(true));
- $this->assertSame(8192, $info->getSize(false));
- }
-
- public function testGetSizeEncryptedFolderWithPlaintextSize(): void {
- $info = $this->makeFileInfo([
- 'encrypted' => true,
- 'mimetype' => FileInfo::MIMETYPE_FOLDER,
- 'size' => 8192,
- 'unencrypted_size' => 5000,
- ]);
- $this->assertSame(5000, $info->getSize(true));
- $this->assertSame(5000, $info->getSize(false));
- }
-
- public function testGetSizeNonEncrypted(): void {
- $info = $this->makeFileInfo(['encrypted' => false, 'size' => 100]);
- $this->assertSame(100, $info->getSize(true));
- $this->assertSame(100, $info->getSize(false));
- }
-
- public function testIsMountedHomeStorage(): void {
- $user = $this->createMock(IUser::class);
- $user->method('getUID')
- ->willReturn('foo');
- $user->method('getHome')
- ->willReturn('foo');
- $storage = new Home(['user' => $user]);
-
- $fileInfo = new FileInfo(
- '',
- $storage,
- '',
- [],
- new HomeMountPoint($user, $storage, '/foo/files')
- );
- $this->assertFalse($fileInfo->isMounted());
- }
-
- public function testIsMountedNonHomeStorage(): void {
- $storage = new Temporary();
- $fileInfo = new FileInfo(
- '',
- $storage,
- '',
- [],
- new MountPoint($storage, '/foo/files/bar')
- );
- $this->assertTrue($fileInfo->isMounted());
- }
-}
--- a/tests/lib/Files/FilenameValidatorTest.php
+++ /dev/null
@@ -1,490 +0,0 @@
-createMock(IL10N::class);
- $l10n->method('t')
- ->willReturnCallback(fn ($string, $params) => sprintf($string, ...$params));
- $this->l10n = $this->createMock(IFactory::class);
- $this->l10n
- ->method('get')
- ->willReturn($l10n);
-
- $this->config = $this->createMock(IConfig::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->database = $this->createMock(IDBConnection::class);
- $this->database->method('supports4ByteText')->willReturn(true);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataValidateFilename')]
- public function testValidateFilename(
- string $filename,
- array $forbiddenNames,
- array $forbiddenBasenames,
- array $forbiddenExtensions,
- array $forbiddenCharacters,
- ?string $exception,
- ): void {
- /** @var FilenameValidator&MockObject */
- $validator = $this->getMockBuilder(FilenameValidator::class)
- ->onlyMethods([
- 'getForbiddenBasenames',
- 'getForbiddenCharacters',
- 'getForbiddenExtensions',
- 'getForbiddenFilenames',
- ])
- ->setConstructorArgs([$this->l10n, $this->database, $this->config, $this->logger])
- ->getMock();
-
- $validator->method('getForbiddenBasenames')
- ->willReturn($forbiddenBasenames);
- $validator->method('getForbiddenCharacters')
- ->willReturn($forbiddenCharacters);
- $validator->method('getForbiddenExtensions')
- ->willReturn($forbiddenExtensions);
- $validator->method('getForbiddenFilenames')
- ->willReturn($forbiddenNames);
-
- if ($exception !== null) {
- $this->expectException($exception);
- } else {
- $this->expectNotToPerformAssertions();
- }
- $validator->validateFilename($filename);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataValidateFilename')]
- public function testIsFilenameValid(
- string $filename,
- array $forbiddenNames,
- array $forbiddenBasenames,
- array $forbiddenExtensions,
- array $forbiddenCharacters,
- ?string $exception,
- ): void {
- /** @var FilenameValidator&MockObject */
- $validator = $this->getMockBuilder(FilenameValidator::class)
- ->onlyMethods([
- 'getForbiddenBasenames',
- 'getForbiddenExtensions',
- 'getForbiddenFilenames',
- 'getForbiddenCharacters',
- ])
- ->setConstructorArgs([$this->l10n, $this->database, $this->config, $this->logger])
- ->getMock();
-
- $validator->method('getForbiddenBasenames')
- ->willReturn($forbiddenBasenames);
- $validator->method('getForbiddenCharacters')
- ->willReturn($forbiddenCharacters);
- $validator->method('getForbiddenExtensions')
- ->willReturn($forbiddenExtensions);
- $validator->method('getForbiddenFilenames')
- ->willReturn($forbiddenNames);
-
- $this->assertEquals($exception === null, $validator->isFilenameValid($filename));
- }
-
- public static function dataValidateFilename(): array {
- return [
- 'valid name' => [
- 'a: b.txt', ['.htaccess'], [], [], [], null
- ],
- 'forbidden name in the middle is ok' => [
- 'a.htaccess.txt', ['.htaccess'], [], [], [], null
- ],
- 'valid name with some more parameters' => [
- 'a: b.txt', ['.htaccess'], [], ['exe'], ['~'], null
- ],
- 'valid name checks only the full name' => [
- '.htaccess.sample', ['.htaccess'], [], [], [], null
- ],
- 'forbidden name' => [
- '.htaccess', ['.htaccess'], [], [], [], ReservedWordException::class
- ],
- 'forbidden name - name is case insensitive' => [
- 'COM1', ['.htaccess', 'com1'], [], [], [], ReservedWordException::class
- ],
- 'forbidden basename' => [
- // needed for Windows namespaces
- 'com1.suffix', ['.htaccess'], ['com1'], [], [], ReservedWordException::class
- ],
- 'forbidden basename case insensitive' => [
- // needed for Windows namespaces
- 'COM1.suffix', ['.htaccess'], ['com1'], [], [], ReservedWordException::class
- ],
- 'forbidden basename for hidden files' => [
- // needed for Windows namespaces
- '.thumbs.db', ['.htaccess'], ['.thumbs'], [], [], ReservedWordException::class
- ],
- 'invalid character' => [
- 'a: b.txt', ['.htaccess'], [], [], [':'], InvalidCharacterInPathException::class
- ],
- 'invalid path' => [
- '../../foo.bar', ['.htaccess'], [], [], ['/', '\\'], InvalidCharacterInPathException::class,
- ],
- 'invalid extension' => [
- 'a: b.txt', ['.htaccess'], [], ['.txt'], [], InvalidPathException::class
- ],
- 'invalid extension case insensitive' => [
- 'a: b.TXT', ['.htaccess'], [], ['.txt'], [], InvalidPathException::class
- ],
- 'empty filename' => [
- '', [], [], [], [], EmptyFileNameException::class
- ],
- 'reserved unix name "."' => [
- '.', [], [], [], [], InvalidDirectoryException::class
- ],
- 'reserved unix name ".."' => [
- '..', [], [], [], [], InvalidDirectoryException::class
- ],
- 'weird but valid tripple dot name' => [
- '...', [], [], [], [], null // is valid
- ],
- 'too long filename "."' => [
- str_repeat('a', 251), [], [], [], [], FileNameTooLongException::class
- ],
- // make sure to not split the list entries as they migh contain Unicode sequences
- // in this example the "face in clouds" emoji contains the clouds emoji so only having clouds is ok
- ['🌫️.txt', ['.htaccess'], [], [], ['😶🌫️'], null],
- // This is the reverse: clouds are forbidden -> so is also the face in the clouds emoji
- ['😶🌫️.txt', ['.htaccess'], [], [], ['🌫️'], InvalidCharacterInPathException::class],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('data4ByteUnicode')]
- public function testDatabaseDoesNotSupport4ByteText($filename): void {
- $database = $this->createMock(IDBConnection::class);
- $database->expects($this->once())
- ->method('supports4ByteText')
- ->willReturn(false);
- $this->expectException(InvalidCharacterInPathException::class);
- $validator = new FilenameValidator($this->l10n, $database, $this->config, $this->logger);
- $validator->validateFilename($filename);
- }
-
- public static function data4ByteUnicode(): array {
- return [
- ['plane 1 𐪅'],
- ['emoji 😶🌫️'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataInvalidAsciiCharacters')]
- public function testInvalidAsciiCharactersAreAlwaysForbidden(string $filename): void {
- $this->expectException(InvalidPathException::class);
- $validator = new FilenameValidator($this->l10n, $this->database, $this->config, $this->logger);
- $validator->validateFilename($filename);
- }
-
- public static function dataInvalidAsciiCharacters(): array {
- return [
- [\chr(0)],
- [\chr(1)],
- [\chr(2)],
- [\chr(3)],
- [\chr(4)],
- [\chr(5)],
- [\chr(6)],
- [\chr(7)],
- [\chr(8)],
- [\chr(9)],
- [\chr(10)],
- [\chr(11)],
- [\chr(12)],
- [\chr(13)],
- [\chr(14)],
- [\chr(15)],
- [\chr(16)],
- [\chr(17)],
- [\chr(18)],
- [\chr(19)],
- [\chr(20)],
- [\chr(21)],
- [\chr(22)],
- [\chr(23)],
- [\chr(24)],
- [\chr(25)],
- [\chr(26)],
- [\chr(27)],
- [\chr(28)],
- [\chr(29)],
- [\chr(30)],
- [\chr(31)],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataIsForbidden')]
- public function testIsForbidden(string $filename, array $forbiddenNames, bool $expected): void {
- /** @var FilenameValidator&MockObject */
- $validator = $this->getMockBuilder(FilenameValidator::class)
- ->onlyMethods(['getForbiddenFilenames'])
- ->setConstructorArgs([$this->l10n, $this->database, $this->config, $this->logger])
- ->getMock();
-
- $validator->method('getForbiddenFilenames')
- ->willReturn($forbiddenNames);
-
- $this->assertEquals($expected, $validator->isForbidden($filename));
- }
-
- public static function dataIsForbidden(): array {
- return [
- 'valid name' => [
- 'a: b.txt', ['.htaccess'], false
- ],
- 'valid name with some more parameters' => [
- 'a: b.txt', ['.htaccess'], false
- ],
- 'valid name as only full forbidden should be matched' => [
- '.htaccess.sample', ['.htaccess'], false,
- ],
- 'forbidden name' => [
- '.htaccess', ['.htaccess'], true
- ],
- 'forbidden name - name is case insensitive' => [
- 'COM1', ['.htaccess', 'com1'], true,
- ],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetForbiddenExtensions')]
- public function testGetForbiddenExtensions(array $configValue, array $expectedValue): void {
- $validator = new FilenameValidator($this->l10n, $this->database, $this->config, $this->logger);
- $this->config
- // only once - then cached
- ->expects(self::once())
- ->method('getSystemValue')
- ->with('forbidden_filename_extensions', ['.filepart'])
- ->willReturn($configValue);
-
- self::assertEqualsCanonicalizing($expectedValue, $validator->getForbiddenExtensions());
- }
-
- public static function dataGetForbiddenExtensions(): array {
- return [
- // default
- [['.filepart'], ['.filepart', '.part']],
- // always include .part
- [[], ['.part']],
- // handle case insensitivity
- [['.TXT'], ['.txt', '.part']],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetForbiddenFilenames')]
- public function testGetForbiddenFilenames(array $configValue, array $legacyValue, array $expectedValue): void {
- $validator = new FilenameValidator($this->l10n, $this->database, $this->config, $this->logger);
- $this->config
- // only once - then cached
- ->expects(self::exactly(2))
- ->method('getSystemValue')
- ->willReturnMap([
- ['forbidden_filenames', ['.htaccess'], $configValue],
- ['blacklisted_files', [], $legacyValue],
- ]);
-
- $this->logger
- ->expects(empty($legacyValue) ? self::never() : self::once())
- ->method('warning');
-
- self::assertEqualsCanonicalizing($expectedValue, $validator->getForbiddenFilenames());
- }
-
- public static function dataGetForbiddenFilenames(): array {
- return [
- // default
- [['.htaccess'], [], ['.htaccess']],
- // with legacy values
- [['.htaccess'], ['legacy'], ['.htaccess', 'legacy']],
- // handle case insensitivity
- [['FileName', '.htaccess'], ['LegAcy'], ['.htaccess', 'filename', 'legacy']],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetForbiddenBasenames')]
- public function testGetForbiddenBasenames(array $configValue, array $expectedValue): void {
- $validator = new FilenameValidator($this->l10n, $this->database, $this->config, $this->logger);
- $this->config
- // only once - then cached
- ->expects(self::once())
- ->method('getSystemValue')
- ->with('forbidden_filename_basenames', [])
- ->willReturn($configValue);
-
- self::assertEqualsCanonicalizing($expectedValue, $validator->getForbiddenBasenames());
- }
-
- public static function dataGetForbiddenBasenames(): array {
- return [
- // default
- [[], []],
- // with values
- [['aux', 'com0'], ['aux', 'com0']],
- // handle case insensitivity
- [['AuX', 'COM1'], ['aux', 'com1']],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSanitizeFilename')]
- public function testSanitizeFilename(
- string $filename,
- array $forbiddenNames,
- array $forbiddenBasenames,
- array $forbiddenExtensions,
- array $forbiddenCharacters,
- string $expected,
- ): void {
- /** @var FilenameValidator&MockObject */
- $validator = $this->getMockBuilder(FilenameValidator::class)
- ->onlyMethods([
- 'getForbiddenBasenames',
- 'getForbiddenExtensions',
- 'getForbiddenFilenames',
- 'getForbiddenCharacters',
- ])
- ->setConstructorArgs([$this->l10n, $this->database, $this->config, $this->logger])
- ->getMock();
-
- $validator->method('getForbiddenBasenames')
- ->willReturn($forbiddenBasenames);
- $validator->method('getForbiddenCharacters')
- ->willReturn($forbiddenCharacters);
- $validator->method('getForbiddenExtensions')
- ->willReturn($forbiddenExtensions);
- $validator->method('getForbiddenFilenames')
- ->willReturn($forbiddenNames);
-
- $this->assertEquals($expected, $validator->sanitizeFilename($filename));
- }
-
- public static function dataSanitizeFilename(): array {
- return [
- 'valid name' => [
- 'a * b.txt', ['.htaccess'], [], [], [], 'a * b.txt'
- ],
- 'forbidden name in the middle is ok' => [
- 'a.htaccess.txt', ['.htaccess'], [], [], [], 'a.htaccess.txt'
- ],
- 'forbidden name on the beginning' => [
- '.htaccess.sample', ['.htaccess'], [], [], [], '.htaccess.sample'
- ],
- 'forbidden name' => [
- '.htaccess', ['.htaccess'], [], [], [], '.htaccess (renamed)'
- ],
- 'forbidden name - name is case insensitive' => [
- 'COM1', ['.htaccess', 'com1'], [], [], [], 'COM1 (renamed)'
- ],
- 'forbidden basename' => [
- 'com1.suffix', ['.htaccess'], ['com1'], [], [], 'com1 (renamed).suffix'
- ],
- 'forbidden basename case insensitive' => [
- // needed for Windows namespaces
- 'COM1.suffix', ['.htaccess'], ['com1'], [], [], 'COM1 (renamed).suffix'
- ],
- 'forbidden basename for hidden files' => [
- // needed for Windows namespaces
- '.thumbs.db', ['.htaccess'], ['.thumbs'], [], [], '.thumbs (renamed).db'
- ],
- 'invalid character' => [
- 'a: b.txt', ['.htaccess'], [], [], [':'], 'a_ b.txt',
- ],
- 'invalid extension' => [
- 'a: b.txt', ['.htaccess'], [], ['.txt'], [], 'a: b'
- ],
- 'invalid extension case insensitive' => [
- 'a: b.TXT', ['.htaccess'], [], ['.txt'], [], 'a: b'
- ],
- 'empty filename' => [
- '', [], [], [], [], 'renamed file'
- ],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSanitizeFilenameCharacterReplacement')]
- public function testSanitizeFilenameCharacterReplacement(
- string $filename,
- array $forbiddenCharacters,
- ?string $characterReplacement,
- ?string $expected,
- ): void {
- /** @var FilenameValidator&MockObject */
- $validator = $this->getMockBuilder(FilenameValidator::class)
- ->onlyMethods([
- 'getForbiddenBasenames',
- 'getForbiddenExtensions',
- 'getForbiddenFilenames',
- 'getForbiddenCharacters',
- ])
- ->setConstructorArgs([$this->l10n, $this->database, $this->config, $this->logger])
- ->getMock();
-
- $validator->method('getForbiddenBasenames')
- ->willReturn([]);
- $validator->method('getForbiddenCharacters')
- ->willReturn($forbiddenCharacters);
- $validator->method('getForbiddenExtensions')
- ->willReturn([]);
- $validator->method('getForbiddenFilenames')
- ->willReturn([]);
-
- if ($expected === null) {
- $this->expectException(\InvalidArgumentException::class);
- $validator->sanitizeFilename($filename, $characterReplacement);
- } else {
- $this->assertEquals($expected, $validator->sanitizeFilename($filename, $characterReplacement));
- }
- }
-
- public static function dataSanitizeFilenameCharacterReplacement(): array {
- return [
- 'default' => [
- 'foo*bar', ['*'], null, 'foo_bar'
- ],
- 'default - underscore not allowed' => [
- 'foo*bar', ['*', '_'], null, 'foo-bar'
- ],
- 'default - dash and underscore not allowed' => [
- 'foo*bar', ['*', '-', '_'], null, 'foo bar'
- ],
- 'default - no replacement' => [
- 'foo*bar', ['*', ' ', '_', '-'], null, null
- ],
- 'custom replacement' => [
- 'foo*bar', ['*'], 'x', 'fooxbar'
- ],
- ];
- }
-}
--- a/tests/lib/Files/FilesystemTest.php
+++ /dev/null
@@ -1,480 +0,0 @@
-mounts[$user->getUID()]) ? $this->mounts[$user->getUID()] : [];
- }
-}
-
-/**
- * Class FilesystemTest
- *
- *
- * @package Test\Files
- */
-#[\PHPUnit\Framework\Attributes\Group('DB')]
-class FilesystemTest extends \Test\TestCase {
- public const TEST_FILESYSTEM_USER1 = 'test-filesystem-user1';
- public const TEST_FILESYSTEM_USER2 = 'test-filesystem-user1';
-
- /**
- * @var array tmpDirs
- */
- private $tmpDirs = [];
-
- /**
- * @return array
- */
- private function getStorageData() {
- $dir = Server::get(ITempManager::class)->getTemporaryFolder();
- $this->tmpDirs[] = $dir;
- return ['datadir' => $dir];
- }
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
- $userBackend = new \Test\Util\User\Dummy();
- $userBackend->createUser(self::TEST_FILESYSTEM_USER1, self::TEST_FILESYSTEM_USER1);
- $userBackend->createUser(self::TEST_FILESYSTEM_USER2, self::TEST_FILESYSTEM_USER2);
- Server::get(IUserManager::class)->registerBackend($userBackend);
- $this->loginAsUser();
- }
-
- #[\Override]
- protected function tearDown(): void {
- foreach ($this->tmpDirs as $dir) {
- Files::rmdirr($dir);
- }
-
- $this->logout();
- $this->invokePrivate('\OC\Files\Filesystem', 'normalizedPathCache', [null]);
- parent::tearDown();
- }
-
- public function testMount(): void {
- Filesystem::mount('\OC\Files\Storage\Local', self::getStorageData(), '/');
- $this->assertEquals('/', Filesystem::getMountPoint('/'));
- $this->assertEquals('/', Filesystem::getMountPoint('/some/folder'));
- [, $internalPath] = Filesystem::resolvePath('/');
- $this->assertEquals('', $internalPath);
- [, $internalPath] = Filesystem::resolvePath('/some/folder');
- $this->assertEquals('some/folder', $internalPath);
-
- Filesystem::mount('\OC\Files\Storage\Local', self::getStorageData(), '/some');
- $this->assertEquals('/', Filesystem::getMountPoint('/'));
- $this->assertEquals('/some/', Filesystem::getMountPoint('/some/folder'));
- $this->assertEquals('/some/', Filesystem::getMountPoint('/some/'));
- $this->assertEquals('/some/', Filesystem::getMountPoint('/some'));
- [, $internalPath] = Filesystem::resolvePath('/some/folder');
- $this->assertEquals('folder', $internalPath);
- }
-
- public static function normalizePathData(): array {
- return [
- ['/', ''],
- ['/', '/'],
- ['/', '//'],
- ['/', '/', false],
- ['/', '//', false],
-
- ['/path', '/path/'],
- ['/path/', '/path/', false],
- ['/path', 'path'],
-
- ['/foo/bar', '/foo//bar/'],
- ['/foo/bar/', '/foo//bar/', false],
- ['/foo/bar', '/foo////bar'],
- ['/foo/bar', '/foo/////bar'],
- ['/foo/bar', '/foo/bar/.'],
- ['/foo/bar', '/foo/bar/./'],
- ['/foo/bar/', '/foo/bar/./', false],
- ['/foo/bar', '/foo/bar/./.'],
- ['/foo/bar', '/foo/bar/././'],
- ['/foo/bar/', '/foo/bar/././', false],
- ['/foo/bar', '/foo/./bar/'],
- ['/foo/bar/', '/foo/./bar/', false],
- ['/foo/.bar', '/foo/.bar/'],
- ['/foo/.bar/', '/foo/.bar/', false],
- ['/foo/.bar/tee', '/foo/.bar/tee'],
- ['/foo/bar.', '/foo/bar./'],
- ['/foo/bar./', '/foo/bar./', false],
- ['/foo/bar./tee', '/foo/bar./tee'],
- ['/foo/.bar.', '/foo/.bar./'],
- ['/foo/.bar./', '/foo/.bar./', false],
- ['/foo/.bar./tee', '/foo/.bar./tee'],
-
- ['/foo/bar', '/.////././//./foo/.///././//./bar/././/./.'],
- ['/foo/bar/', '/.////././//./foo/.///././//./bar/./././.', false],
- ['/foo/bar', '/.////././//./foo/.///././//./bar/././/././'],
- ['/foo/bar/', '/.////././//./foo/.///././//./bar/././/././', false],
- ['/foo/.bar', '/.////././//./foo/./././/./.bar/././/././'],
- ['/foo/.bar/', '/.////././//./foo/./././/./.bar/././/././', false],
- ['/foo/.bar/tee./', '/.////././//./foo/./././/./.bar/tee././/././', false],
- ['/foo/bar.', '/.////././//./foo/./././/./bar./././/././'],
- ['/foo/bar./', '/.////././//./foo/./././/./bar./././/././', false],
- ['/foo/bar./tee./', '/.////././//./foo/./././/./bar./tee././/././', false],
- ['/foo/.bar.', '/.////././//./foo/./././/./.bar./././/././'],
- ['/foo/.bar./', '/.////././//./foo/./././/./.bar./././././', false],
- ['/foo/.bar./tee./', '/.////././//./foo/./././/./.bar./tee././././', false],
-
- // Windows paths
- ['/', ''],
- ['/', '\\'],
- ['/', '\\', false],
- ['/', '\\\\'],
- ['/', '\\\\', false],
-
- ['/path', '\\path'],
- ['/path', '\\path', false],
- ['/path', '\\path\\'],
- ['/path/', '\\path\\', false],
-
- ['/foo/bar', '\\foo\\\\bar\\'],
- ['/foo/bar/', '\\foo\\\\bar\\', false],
- ['/foo/bar', '\\foo\\\\\\\\bar'],
- ['/foo/bar', '\\foo\\\\\\\\\\bar'],
- ['/foo/bar', '\\foo\\bar\\.'],
- ['/foo/bar', '\\foo\\bar\\.\\'],
- ['/foo/bar/', '\\foo\\bar\\.\\', false],
- ['/foo/bar', '\\foo\\bar\\.\\.'],
- ['/foo/bar', '\\foo\\bar\\.\\.\\'],
- ['/foo/bar/', '\\foo\\bar\\.\\.\\', false],
- ['/foo/bar', '\\foo\\.\\bar\\'],
- ['/foo/bar/', '\\foo\\.\\bar\\', false],
- ['/foo/.bar', '\\foo\\.bar\\'],
- ['/foo/.bar/', '\\foo\\.bar\\', false],
- ['/foo/.bar/tee', '\\foo\\.bar\\tee'],
-
- // Absolute windows paths NOT marked as absolute
- ['/C:', 'C:\\'],
- ['/C:/', 'C:\\', false],
- ['/C:/tests', 'C:\\tests'],
- ['/C:/tests', 'C:\\tests', false],
- ['/C:/tests', 'C:\\tests\\'],
- ['/C:/tests/', 'C:\\tests\\', false],
- ['/C:/tests/bar', 'C:\\tests\\.\\.\\bar'],
- ['/C:/tests/bar/', 'C:\\tests\\.\\.\\bar\\.\\', false],
-
- // normalize does not resolve '..' (by design)
- ['/foo/..', '/foo/../'],
- ['/foo/../bar', '/foo/../bar/.'],
- ['/foo/..', '\\foo\\..\\'],
- ['/foo/../bar', '\\foo\\..\\bar'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('normalizePathData')]
- public function testNormalizePath($expected, $path, $stripTrailingSlash = true): void {
- $this->assertEquals($expected, Filesystem::normalizePath($path, $stripTrailingSlash));
- }
-
- public static function normalizePathKeepUnicodeData(): array {
- $nfdName = 'ümlaut';
- $nfcName = 'ümlaut';
- return [
- ['/' . $nfcName, $nfcName, true],
- ['/' . $nfcName, $nfcName, false],
- ['/' . $nfdName, $nfdName, true],
- ['/' . $nfcName, $nfdName, false],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('normalizePathKeepUnicodeData')]
- public function testNormalizePathKeepUnicode($expected, $path, $keepUnicode = false): void {
- $this->assertEquals($expected, Filesystem::normalizePath($path, true, false, $keepUnicode));
- }
-
- public function testNormalizePathKeepUnicodeCache(): void {
- $nfdName = 'ümlaut';
- $nfcName = 'ümlaut';
- // call in succession due to cache
- $this->assertEquals('/' . $nfcName, Filesystem::normalizePath($nfdName, true, false, false));
- $this->assertEquals('/' . $nfdName, Filesystem::normalizePath($nfdName, true, false, true));
- }
-
- public static function isValidPathData(): array {
- return [
- ['/', true],
- ['/path', true],
- ['/foo/bar', true],
- ['/foo//bar/', true],
- ['/foo////bar', true],
- ['/foo//\///bar', true],
- ['/foo/bar/.', true],
- ['/foo/bar/./', true],
- ['/foo/bar/./.', true],
- ['/foo/bar/././', true],
- ['/foo/bar/././..bar', true],
- ['/foo/bar/././..bar/a', true],
- ['/foo/bar/././..', false],
- ['/foo/bar/././../', false],
- ['/foo/bar/.././', false],
- ['/foo/bar/../../', false],
- ['/foo/bar/../..\\', false],
- ['..', false],
- ['../', false],
- ['../foo/bar', false],
- ['..\foo/bar', false],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('isValidPathData')]
- public function testIsValidPath($path, $expected): void {
- $this->assertSame($expected, Filesystem::isValidPath($path));
- }
-
- public static function isFileBlacklistedData(): array {
- return [
- ['/etc/foo/bar/foo.txt', false],
- ['\etc\foo/bar\foo.txt', false],
- ['.htaccess', true],
- ['.htaccess/', true],
- ['.htaccess\\', true],
- ['/etc/foo\bar/.htaccess\\', true],
- ['/etc/foo\bar/.htaccess/', true],
- ['/etc/foo\bar/.htaccess/foo', false],
- ['//foo//bar/\.htaccess/', true],
- ['\foo\bar\.HTAccess', true],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('isFileBlacklistedData')]
- public function testIsFileBlacklisted($path, $expected): void {
- $this->assertSame($expected, Filesystem::isFileBlacklisted($path));
- }
-
- public function testNormalizePathUTF8(): void {
- if (!class_exists('Patchwork\PHP\Shim\Normalizer')) {
- $this->markTestSkipped('UTF8 normalizer Patchwork was not found');
- }
-
- $this->assertEquals("/foo/bar\xC3\xBC", Filesystem::normalizePath("/foo/baru\xCC\x88"));
- $this->assertEquals("/foo/bar\xC3\xBC", Filesystem::normalizePath("\\foo\\baru\xCC\x88"));
- }
-
- public function testHooks(): void {
- if (Filesystem::getView()) {
- $user = \OC_User::getUser();
- } else {
- $user = self::TEST_FILESYSTEM_USER1;
- $backend = new \Test\Util\User\Dummy();
- Server::get(IUserManager::class)->registerBackend($backend);
- $backend->createUser($user, $user);
- $userObj = Server::get(IUserManager::class)->get($user);
- Server::get(IUserSession::class)->setUser($userObj);
- Filesystem::init($user, '/' . $user . '/files');
- }
- \OC_Hook::clear('OC_Filesystem');
- \OC_Hook::connect('OC_Filesystem', 'post_write', $this, 'dummyHook');
-
- Filesystem::mount('OC\Files\Storage\Temporary', [], '/');
-
- $rootView = new View('');
- $rootView->mkdir('/' . $user);
- $rootView->mkdir('/' . $user . '/files');
-
- // \OC\Files\Filesystem::file_put_contents('/foo', 'foo');
- Filesystem::mkdir('/bar');
- // \OC\Files\Filesystem::file_put_contents('/bar//foo', 'foo');
-
- $tmpFile = Server::get(ITempManager::class)->getTemporaryFile();
- file_put_contents($tmpFile, 'foo');
- $fh = fopen($tmpFile, 'r');
- // \OC\Files\Filesystem::file_put_contents('/bar//foo', $fh);
- }
-
- /**
- * Tests that an exception is thrown when passed user does not exist.
- *
- */
- public function testLocalMountWhenUserDoesNotExist(): void {
- $this->expectException(UserNotFoundException::class);
-
- $userId = $this->getUniqueID('user_');
-
- Filesystem::initMountPoints($userId);
- }
-
- public function testNullUserThrows(): void {
- $this->expectException(UserNotFoundException::class);
-
- Filesystem::initMountPoints(null);
- }
-
- public function testNullUserThrowsTwice(): void {
- $thrown = 0;
- try {
- Filesystem::initMountPoints(null);
- } catch (UserNotFoundException $e) {
- $thrown++;
- }
- try {
- Filesystem::initMountPoints(null);
- } catch (UserNotFoundException $e) {
- $thrown++;
- }
- $this->assertEquals(2, $thrown);
- }
-
- /**
- * Tests that an exception is thrown when passed user does not exist.
- */
- public function testLocalMountWhenUserDoesNotExistTwice(): void {
- $thrown = 0;
- $userId = $this->getUniqueID('user_');
-
- try {
- Filesystem::initMountPoints($userId);
- } catch (UserNotFoundException $e) {
- $thrown++;
- }
-
- try {
- Filesystem::initMountPoints($userId);
- } catch (UserNotFoundException $e) {
- $thrown++;
- }
-
- $this->assertEquals(2, $thrown);
- }
-
- /**
- * Tests that the home storage is used for the user's mount point
- */
- public function testHomeMount(): void {
- $userId = $this->getUniqueID('user_');
-
- Server::get(IUserManager::class)->createUser($userId, $userId);
-
- Filesystem::initMountPoints($userId);
-
- $homeMount = Filesystem::getStorage('/' . $userId . '/');
-
- $this->assertTrue($homeMount->instanceOfStorage('\OCP\Files\IHomeStorage'));
- if ($homeMount->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage')) {
- $this->assertEquals('object::user:' . $userId, $homeMount->getId());
- } elseif ($homeMount->instanceOfStorage('\OC\Files\Storage\Home')) {
- $this->assertEquals('home::' . $userId, $homeMount->getId());
- }
-
- $user = Server::get(IUserManager::class)->get($userId);
- if ($user !== null) {
- $user->delete();
- }
- }
-
- public function dummyHook($arguments) {
- $path = $arguments['path'];
- $this->assertEquals($path, Filesystem::normalizePath($path)); //the path passed to the hook should already be normalized
- }
-
- /**
- * Test that the default cache dir is part of the user's home
- */
- public function testMountDefaultCacheDir(): void {
- $userId = $this->getUniqueID('user_');
- $config = Server::get(IConfig::class);
- $oldCachePath = $config->getSystemValueString('cache_path', '');
- // no cache path configured
- $config->setSystemValue('cache_path', '');
-
- Server::get(IUserManager::class)->createUser($userId, $userId);
- Filesystem::initMountPoints($userId);
-
- $this->assertEquals(
- '/' . $userId . '/',
- Filesystem::getMountPoint('/' . $userId . '/cache')
- );
- [$storage, $internalPath] = Filesystem::resolvePath('/' . $userId . '/cache');
- $this->assertTrue($storage->instanceOfStorage('\OCP\Files\IHomeStorage'));
- $this->assertEquals('cache', $internalPath);
- $user = Server::get(IUserManager::class)->get($userId);
- if ($user !== null) {
- $user->delete();
- }
-
- $config->setSystemValue('cache_path', $oldCachePath);
- }
-
- /**
- * Test that an external cache is mounted into
- * the user's home
- */
- public function testMountExternalCacheDir(): void {
- $userId = $this->getUniqueID('user_');
-
- $config = Server::get(IConfig::class);
- $oldCachePath = $config->getSystemValueString('cache_path', '');
- // set cache path to temp dir
- $cachePath = Server::get(ITempManager::class)->getTemporaryFolder() . '/extcache';
- $config->setSystemValue('cache_path', $cachePath);
-
- Server::get(IUserManager::class)->createUser($userId, $userId);
- Filesystem::initMountPoints($userId);
-
- $this->assertEquals(
- '/' . $userId . '/cache/',
- Filesystem::getMountPoint('/' . $userId . '/cache')
- );
- [$storage, $internalPath] = Filesystem::resolvePath('/' . $userId . '/cache');
- $this->assertTrue($storage->instanceOfStorage('\OC\Files\Storage\Local'));
- $this->assertEquals('', $internalPath);
- $user = Server::get(IUserManager::class)->get($userId);
- if ($user !== null) {
- $user->delete();
- }
-
- $config->setSystemValue('cache_path', $oldCachePath);
- }
-
- public function testRegisterMountProviderAfterSetup(): void {
- Filesystem::initMountPoints(self::TEST_FILESYSTEM_USER2);
- $this->assertEquals('/', Filesystem::getMountPoint('/foo/bar'));
- $mount = new MountPoint(new Temporary([]), '/foo/bar');
- $mountProvider = new DummyMountProvider([self::TEST_FILESYSTEM_USER2 => [$mount]]);
- Server::get(IMountProviderCollection::class)->registerProvider($mountProvider);
- $this->assertEquals('/foo/bar/', Filesystem::getMountPoint('/foo/bar'));
- }
-}
--- a/tests/lib/Files/Mount/CacheMountProviderTest.php
+++ /dev/null
@@ -1,94 +0,0 @@
-config = $this->createMock(IConfig::class);
- $this->user = $this->createMock(IUser::class);
- $this->storageFactory = new StorageFactory();
- stream_wrapper_register('cachemountprovidertest', CacheMountProviderTestStream::class);
- }
-
- #[\Override]
- protected function tearDown(): void {
- stream_wrapper_unregister('cachemountprovidertest');
- }
-
- public function testGetMountsForUser(): void {
- $provider = new CacheMountProvider($this->config);
-
- $this->assertCount(0, $provider->getMountsForUser($this->user, $this->storageFactory));
- }
-
- public function testGetMountsForUserCacheDir(): void {
- $this->config->expects($this->exactly(1))
- ->method('getSystemValueString')
- ->willReturnMap([
- ['cache_path', '', 'cachemountprovidertest:////cache_path'],
- ]);
- $this->user->method('getUID')
- ->willReturn('bob');
-
- $provider = new CacheMountProvider($this->config);
- $mounts = $provider->getMountsForUser($this->user, $this->storageFactory);
-
- $this->assertCount(2, $mounts);
- $this->assertEquals(1, CacheMountProviderTestStream::$statCounter);
- $this->assertEquals(2, CacheMountProviderTestStream::$mkdirCounter);
-
- $cacheMountProvider = $mounts[0];
- $this->assertEquals('/bob/cache/', $cacheMountProvider->getMountPoint());
-
- $cacheStorage = $cacheMountProvider->getStorage();
- $this->assertEquals('local::cachemountprovidertest://cache_path/bob/', $cacheStorage->getId());
-
- $uploadsMountProvider = $mounts[1];
- $this->assertEquals('/bob/uploads/', $uploadsMountProvider->getMountPoint());
-
- $uploadsStorage = $uploadsMountProvider->getStorage();
- $this->assertEquals('local::cachemountprovidertest://cache_path/bob/uploads/', $uploadsStorage->getId());
-
- $cacheStorage->mkdir('foobar');
- $this->assertEquals(3, CacheMountProviderTestStream::$mkdirCounter);
-
- $uploadsStorage->mkdir('foobar');
- $this->assertEquals(4, CacheMountProviderTestStream::$mkdirCounter);
- }
-}
--- a/tests/lib/Files/Mount/ManagerTest.php
+++ /dev/null
@@ -1,88 +0,0 @@
-manager = new \OC\Files\Mount\Manager($this->createMock(SetupManagerFactory::class));
- }
-
- public function testFind(): void {
- $rootMount = new MountPoint(new Temporary([]), '/');
- $this->manager->addMount($rootMount);
- $this->assertEquals($rootMount, $this->manager->find('/'));
- $this->assertEquals($rootMount, $this->manager->find('/foo/bar'));
-
- $storage = new Temporary([]);
- $mount1 = new MountPoint($storage, '/foo');
- $this->manager->addMount($mount1);
- $this->assertEquals($rootMount, $this->manager->find('/'));
- $this->assertEquals($mount1, $this->manager->find('/foo/bar'));
-
- $this->assertEquals(1, count($this->manager->findIn('/')));
- $mount2 = new MountPoint(new Temporary([]), '/bar');
- $this->manager->addMount($mount2);
- $this->assertEquals(2, count($this->manager->findIn('/')));
-
- $id = $mount1->getStorageId();
- $this->assertEquals([$mount1], $this->manager->findByStorageId($id));
-
- $mount3 = new MountPoint($storage, '/foo/bar');
- $this->manager->addMount($mount3);
- $this->assertEquals([$mount1, $mount3], $this->manager->findByStorageId($id));
- }
-
- public function testBinarySearch(): void {
- $sortedArray = [
- 'a' => false,
- 'aa' => false,
- 'b' => false,
- 'bb' => false,
- 'bba' => true,
- 'bbb' => true,
- 'bdc' => false,
- ];
- $sortedKey = array_keys($sortedArray);
- $result = $this->invokePrivate($this->manager, 'binarySearch', [$sortedArray, $sortedKey, 'bb']);
- $this->assertEquals(2, count($result));
- foreach ($result as $entry) {
- $this->assertTrue($entry);
- }
- }
-
- public function testLong(): void {
- $storage = new LongId([]);
- $mount = new MountPoint($storage, '/foo');
- $this->manager->addMount($mount);
-
- $id = $mount->getStorageId();
- $storageId = $storage->getId();
- $this->assertEquals([$mount], $this->manager->findByStorageId($id));
- $this->assertEquals([$mount], $this->manager->findByStorageId($storageId));
- $this->assertEquals([$mount], $this->manager->findByStorageId(md5($storageId)));
- }
-}
--- a/tests/lib/Files/Mount/MountPointTest.php
+++ /dev/null
@@ -1,77 +0,0 @@
-createMock(IStorage::class);
- $storage->expects($this->once())
- ->method('getId')
- ->willReturn(123);
-
- $loader = $this->createMock(StorageFactory::class);
- $loader->expects($this->once())
- ->method('wrap')
- ->willReturn($storage);
-
- $mountPoint = new MountPoint(
- // just use this because a real class is needed
- NullStorage::class,
- '/mountpoint',
- null,
- $loader
- );
-
- $this->assertEquals($storage, $mountPoint->getStorage());
- $this->assertEquals(123, $mountPoint->getStorageId());
- $this->assertEquals('/mountpoint/', $mountPoint->getMountPoint());
-
- $mountPoint->setMountPoint('another');
- $this->assertEquals('/another/', $mountPoint->getMountPoint());
- }
-
- public function testInvalidStorage(): void {
- $loader = $this->createMock(StorageFactory::class);
- $loader->expects($this->once())
- ->method('wrap')
- ->willThrowException(new \Exception('Test storage init exception'));
-
- $called = false;
- $wrapper = function ($mountPoint, $storage) use ($called): void {
- $called = true;
- };
-
- $mountPoint = new MountPoint(
- // just use this because a real class is needed
- NullStorage::class,
- '/mountpoint',
- null,
- $loader
- );
-
- $this->assertNull($mountPoint->getStorage());
- // call it again to make sure the init code only ran once
- $this->assertNull($mountPoint->getStorage());
-
- $this->assertNull($mountPoint->getStorageId());
-
- // wrapping doesn't fail
- $mountPoint->wrapStorage($wrapper);
-
- $this->assertNull($mountPoint->getStorage());
-
- // storage wrapper never called
- $this->assertFalse($called);
- }
-}
--- a/tests/lib/Files/Mount/MountTest.php
+++ /dev/null
@@ -1,46 +0,0 @@
-getMockBuilder('\OC\Files\Storage\Temporary')
- ->disableOriginalConstructor()
- ->getMock();
- $mount = new MountPoint($storage, '/foo');
- $this->assertInstanceOf('\OC\Files\Storage\Temporary', $mount->getStorage());
- }
-
- public function testFromStorageClassname(): void {
- $mount = new MountPoint('\OC\Files\Storage\Temporary', '/foo');
- $this->assertInstanceOf('\OC\Files\Storage\Temporary', $mount->getStorage());
- }
-
- public function testWrapper(): void {
- $test = $this;
- $wrapper = function ($mountPoint, $storage) use (&$test) {
- $test->assertEquals('/foo/', $mountPoint);
- $test->assertInstanceOf('\OC\Files\Storage\Storage', $storage);
- return new Wrapper(['storage' => $storage]);
- };
-
- $loader = new StorageFactory();
- $loader->addStorageWrapper('test_wrapper', $wrapper);
-
- $storage = $this->getMockBuilder('\OC\Files\Storage\Temporary')
- ->disableOriginalConstructor()
- ->getMock();
- $mount = new MountPoint($storage, '/foo', [], $loader);
- $this->assertInstanceOf('\OC\Files\Storage\Wrapper\Wrapper', $mount->getStorage());
- }
-}
--- a/tests/lib/Files/Mount/ObjectHomeMountProviderTest.php
+++ /dev/null
@@ -1,284 +0,0 @@
-config = $this->createMock(IConfig::class);
- $this->user = $this->createMock(IUser::class);
- $this->loader = $this->createMock(IStorageFactory::class);
-
- $objectStoreConfig = new PrimaryObjectStoreConfig($this->config, $this->createMock(IAppManager::class));
- $this->provider = new ObjectHomeMountProvider($objectStoreConfig);
- }
-
- public function testSingleBucket(): void {
- $this->config->method('getSystemValue')
- ->willReturnCallback(function ($key, $default) {
- if ($key === 'objectstore') {
- return [
- 'class' => 'Test\Files\Mount\FakeObjectStore',
- 'arguments' => [
- 'foo' => 'bar'
- ],
- ];
- } else {
- return $default;
- }
- });
-
- $mount = $this->provider->getHomeMountForUser($this->user, $this->loader);
- $arguments = $this->invokePrivate($mount, 'arguments');
-
- $objectStore = $arguments['objectstore'];
- $this->assertInstanceOf(FakeObjectStore::class, $objectStore);
- $this->assertEquals(['foo' => 'bar', 'multibucket' => false], $objectStore->getArguments());
- }
-
- public function testMultiBucket(): void {
- $this->config->method('getSystemValue')
- ->willReturnCallback(function ($key, $default) {
- if ($key === 'objectstore_multibucket') {
- return [
- 'class' => 'Test\Files\Mount\FakeObjectStore',
- 'arguments' => [
- 'foo' => 'bar'
- ],
- ];
- } else {
- return $default;
- }
- });
-
- $this->user->method('getUID')
- ->willReturn('uid');
- $this->loader->expects($this->never())->method($this->anything());
-
- $this->config->method('getUserValue')
- ->willReturn(null);
-
- $this->config
- ->method('setUserValue')
- ->with(
- $this->equalTo('uid'),
- $this->equalTo('homeobjectstore'),
- $this->equalTo('bucket'),
- $this->equalTo('49'),
- $this->equalTo(null)
- );
-
- $mount = $this->provider->getHomeMountForUser($this->user, $this->loader);
- $arguments = $this->invokePrivate($mount, 'arguments');
-
- $objectStore = $arguments['objectstore'];
- $this->assertInstanceOf(FakeObjectStore::class, $objectStore);
- $this->assertEquals(['foo' => 'bar', 'bucket' => 49, 'multibucket' => true], $objectStore->getArguments());
- }
-
- public function testMultiBucketWithPrefix(): void {
- $this->config->method('getSystemValue')
- ->willReturnCallback(function ($key, $default) {
- if ($key === 'objectstore_multibucket') {
- return [
- 'class' => 'Test\Files\Mount\FakeObjectStore',
- 'arguments' => [
- 'foo' => 'bar',
- 'bucket' => 'myBucketPrefix',
- ],
- ];
- } else {
- return $default;
- }
- });
-
- $this->user->method('getUID')
- ->willReturn('uid');
- $this->loader->expects($this->never())->method($this->anything());
-
- $this->config
- ->method('getUserValue')
- ->willReturn(null);
-
- $this->config->expects($this->once())
- ->method('setUserValue')
- ->with(
- $this->equalTo('uid'),
- $this->equalTo('homeobjectstore'),
- $this->equalTo('bucket'),
- $this->equalTo('myBucketPrefix49'),
- $this->equalTo(null)
- );
-
- $mount = $this->provider->getHomeMountForUser($this->user, $this->loader);
- $arguments = $this->invokePrivate($mount, 'arguments');
-
- $objectStore = $arguments['objectstore'];
- $this->assertInstanceOf(FakeObjectStore::class, $objectStore);
- $this->assertEquals(['foo' => 'bar', 'bucket' => 'myBucketPrefix49', 'multibucket' => true], $objectStore->getArguments());
- }
-
- public function testMultiBucketBucketAlreadySet(): void {
- $this->config->method('getSystemValue')
- ->willReturnCallback(function ($key, $default) {
- if ($key === 'objectstore_multibucket') {
- return [
- 'class' => 'Test\Files\Mount\FakeObjectStore',
- 'arguments' => [
- 'foo' => 'bar',
- 'bucket' => 'myBucketPrefix',
- ],
- ];
- } else {
- return $default;
- }
- });
-
- $this->user->method('getUID')
- ->willReturn('uid');
- $this->loader->expects($this->never())->method($this->anything());
-
- $this->config
- ->method('getUserValue')
- ->willReturnCallback(function ($uid, $app, $key, $default) {
- if ($uid === 'uid' && $app === 'homeobjectstore' && $key === 'bucket') {
- return 'awesomeBucket1';
- } else {
- return $default;
- }
- });
-
- $this->config->expects($this->never())
- ->method('setUserValue');
-
- $mount = $this->provider->getHomeMountForUser($this->user, $this->loader);
- $arguments = $this->invokePrivate($mount, 'arguments');
-
- $objectStore = $arguments['objectstore'];
- $this->assertInstanceOf(FakeObjectStore::class, $objectStore);
- $this->assertEquals(['foo' => 'bar', 'bucket' => 'awesomeBucket1', 'multibucket' => true], $objectStore->getArguments());
- }
-
- public function testMultiBucketConfigFirst(): void {
- $this->config->method('getSystemValue')
- ->willReturnCallback(function ($key, $default) {
- if ($key === 'objectstore_multibucket') {
- return [
- 'class' => 'Test\Files\Mount\FakeObjectStore',
- 'arguments' => [
- 'foo' => 'bar',
- 'bucket' => 'myBucketPrefix',
- ],
- ];
- } else {
- return $default;
- }
- });
-
- $this->user->method('getUID')
- ->willReturn('uid');
- $this->loader->expects($this->never())->method($this->anything());
-
- $mount = $this->provider->getHomeMountForUser($this->user, $this->loader);
- $this->assertInstanceOf('OC\Files\Mount\MountPoint', $mount);
- }
-
- public function testMultiBucketConfigFirstFallBackSingle(): void {
- $this->config
- ->method('getSystemValue')->willReturnMap([
- ['objectstore_multibucket', null, null],
- ['objectstore', null, [
- 'class' => 'Test\Files\Mount\FakeObjectStore',
- 'arguments' => [
- 'foo' => 'bar',
- 'bucket' => 'myBucketPrefix',
- ],
- ]],
- ]);
-
- $this->user->method('getUID')
- ->willReturn('uid');
- $this->loader->expects($this->never())->method($this->anything());
-
- $mount = $this->provider->getHomeMountForUser($this->user, $this->loader);
- $this->assertInstanceOf('OC\Files\Mount\MountPoint', $mount);
- }
-
- public function testNoObjectStore(): void {
- $this->config->method('getSystemValue')
- ->willReturnCallback(function ($key, $default) {
- return $default;
- });
-
- $mount = $this->provider->getHomeMountForUser($this->user, $this->loader);
- $this->assertNull($mount);
- }
-}
-
-class FakeObjectStore implements IObjectStore {
- public function __construct(
- private array $arguments,
- ) {
- }
-
- public function getArguments() {
- return $this->arguments;
- }
-
- #[\Override]
- public function getStorageId() {
- }
-
- #[\Override]
- public function readObject($urn) {
- }
-
- #[\Override]
- public function writeObject($urn, $stream, ?string $mimetype = null) {
- }
-
- #[\Override]
- public function deleteObject($urn) {
- }
-
- #[\Override]
- public function objectExists($urn) {
- }
-
- #[\Override]
- public function copyObject($from, $to) {
- }
-
- #[\Override]
- public function preSignedUrl(string $urn, \DateTimeInterface $expiration): ?string {
- return null;
- }
-}
--- a/tests/lib/Files/Mount/RootMountProviderTest.php
+++ /dev/null
@@ -1,124 +0,0 @@
-loader = new StorageFactory();
- }
-
- private function getConfig(array $systemConfig): IConfig {
- $config = $this->createMock(IConfig::class);
- $config->method('getSystemValue')
- ->willReturnCallback(function (string $key, $default) use ($systemConfig) {
- return $systemConfig[$key] ?? $default;
- });
- return $config;
- }
-
- private function getProvider(array $systemConfig): RootMountProvider {
- $config = $this->getConfig($systemConfig);
- $objectStoreConfig = new PrimaryObjectStoreConfig($config, $this->createMock(IAppManager::class));
- return new RootMountProvider($objectStoreConfig, $config);
- }
-
- public function testLocal(): void {
- $provider = $this->getProvider([
- 'datadirectory' => '/data',
- ]);
- $mounts = $provider->getRootMounts($this->loader);
- $this->assertCount(1, $mounts);
- $mount = $mounts[0];
- $this->assertEquals('/', $mount->getMountPoint());
- /** @var LocalRootStorage $storage */
- $storage = $mount->getStorage();
- $this->assertInstanceOf(LocalRootStorage::class, $storage);
- $this->assertEquals('/data/', $storage->getSourcePath(''));
- }
-
- public function testObjectStore(): void {
- $provider = $this->getProvider([
- 'objectstore' => [
- 'class' => "OC\Files\ObjectStore\S3",
- 'arguments' => [
- 'bucket' => 'nextcloud',
- 'autocreate' => true,
- 'key' => 'minio',
- 'secret' => 'minio123',
- 'hostname' => 'localhost',
- 'port' => 9000,
- 'use_ssl' => false,
- 'use_path_style' => true,
- 'uploadPartSize' => 52428800,
- ],
- ],
- ]);
- $mounts = $provider->getRootMounts($this->loader);
- $this->assertCount(1, $mounts);
- $mount = $mounts[0];
- $this->assertEquals('/', $mount->getMountPoint());
- /** @var ObjectStoreStorage $storage */
- $storage = $mount->getStorage();
- $this->assertInstanceOf(ObjectStoreStorage::class, $storage);
-
- $class = new \ReflectionClass($storage);
- $prop = $class->getProperty('objectStore');
- /** @var S3 $objectStore */
- $objectStore = $prop->getValue($storage);
- $this->assertEquals('nextcloud', $objectStore->getBucket());
- }
-
- public function testObjectStoreMultiBucket(): void {
- $provider = $this->getProvider([
- 'objectstore_multibucket' => [
- 'class' => "OC\Files\ObjectStore\S3",
- 'arguments' => [
- 'bucket' => 'nextcloud',
- 'autocreate' => true,
- 'key' => 'minio',
- 'secret' => 'minio123',
- 'hostname' => 'localhost',
- 'port' => 9000,
- 'use_ssl' => false,
- 'use_path_style' => true,
- 'uploadPartSize' => 52428800,
- ],
- ],
- ]);
- $mounts = $provider->getRootMounts($this->loader);
- $this->assertCount(1, $mounts);
- $mount = $mounts[0];
- $this->assertEquals('/', $mount->getMountPoint());
- /** @var ObjectStoreStorage $storage */
- $storage = $mount->getStorage();
- $this->assertInstanceOf(ObjectStoreStorage::class, $storage);
-
- $class = new \ReflectionClass($storage);
- $prop = $class->getProperty('objectStore');
- /** @var S3 $objectStore */
- $objectStore = $prop->getValue($storage);
- $this->assertEquals('nextcloud0', $objectStore->getBucket());
- }
-}
--- a/tests/lib/Files/Node/FileTest.php
+++ /dev/null
@@ -1,318 +0,0 @@
-getFileInfo($data, $internalPath, $storage));
- } else {
- return new File($root, $view, $path);
- }
- }
-
- #[\Override]
- protected function getNodeClass(): string {
- return File::class;
- }
-
- #[\Override]
- protected function getNonExistingNodeClass(): string {
- return NonExistingFile::class;
- }
-
- #[\Override]
- protected function getViewDeleteMethod(): string {
- return 'unlink';
- }
-
- public function testGetContent(): void {
- /** @var Root|\PHPUnit\Framework\MockObject\MockObject $root */
- $root = $this->getMockBuilder(Root::class)
- ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
-
- $hook = function ($file): void {
- throw new \Exception('Hooks are not supposed to be called');
- };
-
- $root->listen('\OC\Files', 'preWrite', $hook);
- $root->listen('\OC\Files', 'postWrite', $hook);
-
- $this->view->expects($this->once())
- ->method('file_get_contents')
- ->with('/bar/foo')
- ->willReturn('bar');
-
- $this->view->expects($this->once())
- ->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ]));
-
- $node = new File($root, $this->view, '/bar/foo');
- $this->assertEquals('bar', $node->getContent());
- }
-
- public function testGetContentNotPermitted(): void {
- $this->expectException(NotPermittedException::class);
-
- /** @var Root|\PHPUnit\Framework\MockObject\MockObject $root */
- $root = $this->getMockBuilder(Root::class)
- ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
-
- $root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
-
- $this->view->expects($this->once())
- ->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($this->getFileInfo(['permissions' => 0]));
-
- $node = new File($root, $this->view, '/bar/foo');
- $node->getContent();
- }
-
- public function testPutContent(): void {
- /** @var Root|\PHPUnit\Framework\MockObject\MockObject $root */
- $root = $this->getMockBuilder(Root::class)
- ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
-
- $root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
-
- $this->view->expects($this->once())
- ->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL]));
-
- $this->view->expects($this->once())
- ->method('file_put_contents')
- ->with('/bar/foo', 'bar')
- ->willReturn(true);
-
- $node = new File($root, $this->view, '/bar/foo');
- $node->putContent('bar');
- }
-
- public function testPutContentNotPermitted(): void {
- $this->expectException(NotPermittedException::class);
-
- /** @var Root|\PHPUnit\Framework\MockObject\MockObject $root */
- $root = $this->getMockBuilder(Root::class)
- ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
-
- $this->view->expects($this->once())
- ->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ]));
-
- $node = new File($root, $this->view, '/bar/foo');
- $node->putContent('bar');
- }
-
- public function testGetMimeType(): void {
- /** @var Root|\PHPUnit\Framework\MockObject\MockObject $root */
- $root = $this->getMockBuilder(Root::class)
- ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
-
- $this->view->expects($this->once())
- ->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($this->getFileInfo(['mimetype' => 'text/plain']));
-
- $node = new File($root, $this->view, '/bar/foo');
- $this->assertEquals('text/plain', $node->getMimeType());
- }
-
- public function testFOpenRead(): void {
- $stream = fopen('php://memory', 'w+');
- fwrite($stream, 'bar');
- rewind($stream);
-
- $root = new Root(
- $this->manager,
- $this->view,
- $this->user,
- $this->userMountCache,
- $this->logger,
- $this->userManager,
- $this->eventDispatcher,
- $this->cacheFactory,
- $this->appConfig,
- );
-
- $hook = function ($file): void {
- throw new \Exception('Hooks are not supposed to be called');
- };
-
- $root->listen('\OC\Files', 'preWrite', $hook);
- $root->listen('\OC\Files', 'postWrite', $hook);
-
- $this->view->expects($this->once())
- ->method('fopen')
- ->with('/bar/foo', 'r')
- ->willReturn($stream);
-
- $this->view->expects($this->once())
- ->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL]));
-
- $node = new File($root, $this->view, '/bar/foo');
- $fh = $node->fopen('r');
- $this->assertEquals($stream, $fh);
- $this->assertEquals('bar', fread($fh, 3));
- }
-
- public function testFOpenWrite(): void {
- $stream = fopen('php://memory', 'w+');
-
- $root = new Root(
- $this->manager,
- $this->view,
- $this->user,
- $this->userMountCache,
- $this->logger,
- $this->userManager,
- $this->eventDispatcher,
- $this->cacheFactory,
- $this->appConfig,
- );
- $hooksCalled = 0;
- $hook = function ($file) use (&$hooksCalled): void {
- $hooksCalled++;
- };
-
- $root->listen('\OC\Files', 'preWrite', $hook);
- $root->listen('\OC\Files', 'postWrite', $hook);
-
- $this->view->expects($this->once())
- ->method('fopen')
- ->with('/bar/foo', 'w')
- ->willReturn($stream);
-
- $this->view->expects($this->once())
- ->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL]));
-
- $node = new File($root, $this->view, '/bar/foo');
- $fh = $node->fopen('w');
- $this->assertEquals($stream, $fh);
- fwrite($fh, 'bar');
- rewind($fh);
- $this->assertEquals('bar', fread($stream, 3));
- $this->assertEquals(2, $hooksCalled);
- }
-
- public function testFOpenReadNotPermitted(): void {
- $this->expectException(NotPermittedException::class);
-
- $root = new Root(
- $this->manager,
- $this->view,
- $this->user,
- $this->userMountCache,
- $this->logger,
- $this->userManager,
- $this->eventDispatcher,
- $this->cacheFactory,
- $this->appConfig,
- );
- $hook = function ($file): void {
- throw new \Exception('Hooks are not supposed to be called');
- };
-
- $this->view->expects($this->once())
- ->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($this->getFileInfo(['permissions' => 0]));
-
- $node = new File($root, $this->view, '/bar/foo');
- $node->fopen('r');
- }
-
- public function testFOpenReadWriteNoReadPermissions(): void {
- $this->expectException(NotPermittedException::class);
-
- $root = new Root(
- $this->manager,
- $this->view,
- $this->user,
- $this->userMountCache,
- $this->logger,
- $this->userManager,
- $this->eventDispatcher,
- $this->cacheFactory,
- $this->appConfig,
- );
- $hook = function (): void {
- throw new \Exception('Hooks are not supposed to be called');
- };
-
- $this->view->expects($this->once())
- ->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_UPDATE]));
-
- $node = new File($root, $this->view, '/bar/foo');
- $node->fopen('w');
- }
-
- public function testFOpenReadWriteNoWritePermissions(): void {
- $this->expectException(NotPermittedException::class);
-
- $root = new Root(
- $this->manager,
- $this->view,
- $this->user,
- $this->userMountCache,
- $this->logger,
- $this->userManager,
- $this->eventDispatcher,
- $this->cacheFactory,
- $this->appConfig,
- );
- $hook = function (): void {
- throw new \Exception('Hooks are not supposed to be called');
- };
-
- $this->view->expects($this->once())
- ->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ]));
-
- $node = new File($root, $this->view, '/bar/foo');
- $node->fopen('w');
- }
-}
--- a/tests/lib/Files/Node/FolderTest.php
+++ /dev/null
@@ -1,1253 +0,0 @@
-createUser('bar', 'bar');
- }
-
- #[\Override]
- protected function createTestNode(IRootFolder $root, View&MockObject $view, string $path, array $data = [], string $internalPath = '', ?IStorage $storage = null): Folder {
- $view->expects($this->any())
- ->method('getRoot')
- ->willReturn('');
- if ($data || $internalPath || $storage) {
- return new Folder($root, $view, $path, $this->getFileInfo($data, $internalPath, $storage));
- } else {
- return new Folder($root, $view, $path);
- }
- }
-
- #[\Override]
- protected function getNodeClass(): string {
- return Folder::class;
- }
-
- #[\Override]
- protected function getNonExistingNodeClass(): string {
- return NonExistingFolder::class;
- }
-
- #[\Override]
- protected function getViewDeleteMethod(): string {
- return 'rmdir';
- }
-
- public function testGetDirectoryContent(): void {
- $manager = $this->createMock(Manager::class);
- $root = $this->getMockBuilder(Root::class)
- ->setConstructorArgs([$manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
- $root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
-
- $this->view->expects($this->any())
- ->method('getDirectoryContent')
- ->with('/bar/foo')
- ->willReturn([
- new FileInfo('/bar/foo/asd', null, 'foo/asd', ['fileid' => 2, 'path' => '/bar/foo/asd', 'name' => 'asd', 'size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain'], null),
- new FileInfo('/bar/foo/qwerty', null, 'foo/qwerty', ['fileid' => 3, 'path' => '/bar/foo/qwerty', 'name' => 'qwerty', 'size' => 200, 'mtime' => 55, 'mimetype' => 'httpd/unix-directory'], null),
- ]);
- $this->view->method('getFileInfo')
- ->willReturn($this->createMock(FileInfo::class));
- $this->view->method('getRelativePath')
- ->willReturn('/bar/foo');
-
- $node = new Folder($root, $this->view, '/bar/foo');
- $children = $node->getDirectoryListing();
- $this->assertEquals(2, count($children));
- $this->assertInstanceOf('\OC\Files\Node\File', $children[0]);
- $this->assertInstanceOf('\OC\Files\Node\Folder', $children[1]);
- $this->assertEquals('asd', $children[0]->getName());
- $this->assertEquals('qwerty', $children[1]->getName());
- $this->assertEquals(2, $children[0]->getId());
- $this->assertEquals(3, $children[1]->getId());
- }
-
- public function testGet(): void {
- $manager = $this->createMock(Manager::class);
- $view = $this->getRootViewMock();
- $root = $this->getMockBuilder(Root::class)
- ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
- $root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
-
- $node = new File($root, $view, '/bar/foo/asd');
- $root->method('get')
- ->with('/bar/foo/asd')
- ->willReturn($node);
-
- $parentNode = new Folder($root, $view, '/bar/foo');
- self::assertEquals($node, $parentNode->get('asd'));
- }
-
- public function testNodeExists(): void {
- $manager = $this->createMock(Manager::class);
- $view = $this->getRootViewMock();
- $root = $this->getMockBuilder(Root::class)
- ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
- $root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
-
- $child = new Folder($root, $view, '/bar/foo/asd');
-
- $root->method('get')
- ->with('/bar/foo/asd')
- ->willReturn($child);
-
- $node = new Folder($root, $view, '/bar/foo');
- $this->assertTrue($node->nodeExists('asd'));
- }
-
- public function testNodeExistsNotExists(): void {
- $manager = $this->createMock(Manager::class);
- $view = $this->getRootViewMock();
- $root = $this->getMockBuilder(Root::class)
- ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
- $root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
-
- $root->method('get')
- ->with('/bar/foo/asd')
- ->willThrowException(new NotFoundException());
-
- $node = new Folder($root, $view, '/bar/foo');
- $this->assertFalse($node->nodeExists('asd'));
- }
-
- public function testNewFolder(): void {
- $manager = $this->createMock(Manager::class);
- $view = $this->getRootViewMock();
- $root = $this->getMockBuilder(Root::class)
- ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
- $root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
-
- $view->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL]));
-
- $view->method('mkdir')
- ->with('/bar/foo/asd')
- ->willReturn(true);
-
- $node = new Folder($root, $view, '/bar/foo');
- $child = new Folder($root, $view, '/bar/foo/asd', null, $node);
- $result = $node->newFolder('asd');
- $this->assertEquals($child, $result);
- }
-
- public function testNewFolderDeepParent(): void {
- $manager = $this->createMock(Manager::class);
- $view = $this->getRootViewMock();
- $root = $this->getMockBuilder(Root::class)
- ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
- $root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
-
- $view->method('getFileInfo')
- ->with('/foobar')
- ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL]));
-
- $view->method('mkdir')
- ->with('/foobar/asd/sdf')
- ->willReturn(true);
-
- $node = new Folder($root, $view, '/foobar');
- $child = new Folder($root, $view, '/foobar/asd/sdf', null, null);
- $result = $node->newFolder('asd/sdf');
- $this->assertEquals($child, $result);
- }
-
- public function testNewFolderNotPermitted(): void {
- $this->expectException(NotPermittedException::class);
-
- $manager = $this->createMock(Manager::class);
- $view = $this->getRootViewMock();
- $root = $this->getMockBuilder(Root::class)
- ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
- $root->method('getUser')
- ->willReturn($this->user);
-
- $view->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ]));
-
- $node = new Folder($root, $view, '/bar/foo');
- $node->newFolder('asd');
- }
-
- public function testNewFile(): void {
- $manager = $this->createMock(Manager::class);
- $view = $this->getRootViewMock();
- $root = $this->getMockBuilder(Root::class)
- ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
- $root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
-
- $view->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL]));
-
- $view->method('touch')
- ->with('/bar/foo/asd')
- ->willReturn(true);
-
- $node = new Folder($root, $view, '/bar/foo');
- $child = new File($root, $view, '/bar/foo/asd', null, $node);
- $result = $node->newFile('asd');
- $this->assertEquals($child, $result);
- }
-
- public function testNewFileNotPermitted(): void {
- $this->expectException(NotPermittedException::class);
-
- $manager = $this->createMock(Manager::class);
- $view = $this->getRootViewMock();
- $root = $this->getMockBuilder(Root::class)
- ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
- $root->method('getUser')
- ->willReturn($this->user);
-
- $view->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ]));
-
- $node = new Folder($root, $view, '/bar/foo');
- $node->newFile('asd');
- }
-
- public function testGetFreeSpace(): void {
- $manager = $this->createMock(Manager::class);
- $view = $this->getRootViewMock();
- $root = $this->getMockBuilder(Root::class)
- ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
- $root->method('getUser')
- ->willReturn($this->user);
-
- $view->method('free_space')
- ->with('/bar/foo')
- ->willReturn(100);
-
- $node = new Folder($root, $view, '/bar/foo');
- $this->assertEquals(100, $node->getFreeSpace());
- }
-
- public function testSearch(): void {
- $manager = $this->createMock(Manager::class);
- $view = $this->getRootViewMock();
- $root = $this->getMockBuilder(Root::class)
- ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
- $root->method('getUser')
- ->willReturn($this->user);
- $storage = $this->createMock(IStorage::class);
- $storage->method('getId')->willReturn('test::1');
- $cache = new Cache($storage);
-
- $storage->method('getCache')
- ->willReturn($cache);
-
- $storage->expects($this->atLeastOnce())
- ->method('getOwner')
- ->with('qwerty')
- ->willReturn(false);
-
- $mount = $this->createMock(IMountPoint::class);
- $mount->expects($this->atLeastOnce())
- ->method('getStorage')
- ->willReturn($storage);
- $mount->expects($this->atLeastOnce())
- ->method('getInternalPath')
- ->willReturn('foo');
-
- $cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $cache->insert('foo', ['size' => 200, 'mtime' => 55, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $cache->insert('foo/qwerty', ['size' => 200, 'mtime' => 55, 'mimetype' => 'text/plain']);
-
- $root->method('getMountsIn')
- ->with('/bar/foo')
- ->willReturn([]);
-
- $root->method('getMount')
- ->with('/bar/foo')
- ->willReturn($mount);
-
- $node = new Folder($root, $view, '/bar/foo');
- $result = $node->search('qw');
- $cache->clear();
- $this->assertEquals(1, count($result));
- $this->assertEquals('/bar/foo/qwerty', $result[0]->getPath());
- }
-
- public function testSearchInRoot(): void {
- $manager = $this->createMock(Manager::class);
- $view = $this->getRootViewMock();
- $root = $this->getMockBuilder(Root::class)
- ->onlyMethods(['getUser', 'getMountsIn', 'getMount'])
- ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
- $root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
- $storage = $this->createMock(IStorage::class);
- $storage->method('getId')->willReturn('test::2');
- $cache = new Cache($storage);
-
- $mount = $this->createMock(IMountPoint::class);
- $mount->method('getStorage')
- ->willReturn($storage);
- $mount->method('getInternalPath')
- ->willReturn('files');
-
- $storage->method('getCache')
- ->willReturn($cache);
- $storage->method('getOwner')
- ->willReturn('owner');
-
- $cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $cache->insert('files', ['size' => 200, 'mtime' => 55, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $cache->insert('files/foo', ['size' => 200, 'mtime' => 55, 'mimetype' => 'text/plain']);
-
- $root->method('getMountsIn')
- ->with('')
- ->willReturn([]);
-
- $root->method('getMount')
- ->with('')
- ->willReturn($mount);
-
- $result = $root->search('foo');
- $cache->clear();
- $this->assertEquals(1, count($result));
- $this->assertEquals('/foo', $result[0]->getPath());
- }
-
- public function testSearchInStorageRoot(): void {
- $manager = $this->createMock(Manager::class);
- $view = $this->getRootViewMock();
- $root = $this->getMockBuilder(Root::class)
- ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
- $root->method('getUser')
- ->willReturn($this->user);
- $storage = $this->createMock(IStorage::class);
- $storage->method('getId')->willReturn('test::1');
- $cache = new Cache($storage);
-
- $mount = $this->createMock(IMountPoint::class);
- $mount->method('getStorage')
- ->willReturn($storage);
- $mount->method('getInternalPath')
- ->willReturn('');
-
- $storage->method('getCache')
- ->willReturn($cache);
- $storage->method('getOwner')
- ->willReturn('owner');
-
- $cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $cache->insert('foo', ['size' => 200, 'mtime' => 55, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $cache->insert('foo/qwerty', ['size' => 200, 'mtime' => 55, 'mimetype' => 'text/plain']);
-
- $root->method('getMountsIn')
- ->with('/bar')
- ->willReturn([]);
-
- $root->method('getMount')
- ->with('/bar')
- ->willReturn($mount);
-
- $node = new Folder($root, $view, '/bar');
- $result = $node->search('qw');
- $cache->clear();
- $this->assertEquals(1, count($result));
- $this->assertEquals('/bar/foo/qwerty', $result[0]->getPath());
- }
-
- public function testSearchSubStorages(): void {
- $manager = $this->createMock(Manager::class);
- $view = $this->getRootViewMock();
- $root = $this->getMockBuilder(Root::class)
- ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
- $root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
- $storage = $this->createMock(IStorage::class);
- $storage->method('getId')->willReturn('test::1');
- $cache = new Cache($storage);
- $subStorage = $this->createMock(IStorage::class);
- $subStorage->method('getId')->willReturn('test::2');
- $subCache = new Cache($subStorage);
- $subMount = $this->getMockBuilder(MountPoint::class)->setConstructorArgs([Temporary::class, ''])->getMock();
-
- $mount = $this->createMock(IMountPoint::class);
- $mount->method('getStorage')
- ->willReturn($storage);
- $mount->method('getInternalPath')
- ->willReturn('foo');
-
- $subMount->method('getStorage')
- ->willReturn($subStorage);
-
- $subMount->method('getMountPoint')
- ->willReturn('/bar/foo/bar/');
-
- $storage->method('getCache')
- ->willReturn($cache);
- $storage->method('getOwner')
- ->willReturn('owner');
-
- $subStorage->method('getCache')
- ->willReturn($subCache);
- $subStorage->method('getOwner')
- ->willReturn('owner');
-
- $cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $cache->insert('foo', ['size' => 200, 'mtime' => 55, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $cache->insert('foo/qwerty', ['size' => 200, 'mtime' => 55, 'mimetype' => 'text/plain']);
-
- $subCache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $subCache->insert('asd', ['size' => 200, 'mtime' => 55, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $subCache->insert('asd/qwerty', ['size' => 200, 'mtime' => 55, 'mimetype' => 'text/plain']);
-
- $root->method('getMountsIn')
- ->with('/bar/foo')
- ->willReturn([$subMount]);
-
- $root->method('getMount')
- ->with('/bar/foo')
- ->willReturn($mount);
-
- $node = new Folder($root, $view, '/bar/foo');
- $result = $node->search('qw');
- $cache->clear();
- $subCache->clear();
- $this->assertEquals(2, count($result));
- }
-
- public function testIsSubNode(): void {
- $rootFolderMock = $this->createMock(IRootFolder::class);
- $file = new Node($rootFolderMock, $this->view, '/foo/bar');
- $folder = new Folder($rootFolderMock, $this->view, '/foo');
- $this->assertTrue($folder->isSubNode($file));
- $this->assertFalse($folder->isSubNode($folder));
-
- $file = new Node($rootFolderMock, $this->view, '/foobar');
- $this->assertFalse($folder->isSubNode($file));
- }
-
- public function testGetById(): void {
- $manager = $this->createMock(Manager::class);
- $view = $this->getRootViewMock();
- $root = $this->getMockBuilder(Root::class)
- ->onlyMethods(['getMountsIn', 'getMount'])
- ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
- $storage = $this->createMock(Storage::class);
- $mount = new MountPoint($storage, '/bar');
- $storage->method('getId')->willReturn('');
- $cache = $this->getMockBuilder(Cache::class)->setConstructorArgs([$storage])->getMock();
-
- $fileInfo = new CacheEntry(['path' => 'foo/qwerty', 'mimetype' => 'text/plain'], null);
-
- $storage->method('getCache')
- ->willReturn($cache);
- $storage->method('getOwner')
- ->willReturn('owner');
-
- $this->userMountCache->expects($this->any())
- ->method('getMountsForFileId')
- ->with(1)
- ->willReturn([new CachedMountInfo(
- $this->user,
- 1,
- 0,
- '/bar/',
- 'test',
- 1,
- ''
- )]);
-
- $cache->method('get')
- ->with(1)
- ->willReturn($fileInfo);
-
- $root->method('getMountsIn')
- ->with('/bar/foo')
- ->willReturn([]);
-
- $manager->method('getMountFromMountInfo')
- ->willReturn($mount);
- $manager->method('getMountsByMountProvider')
- ->willReturn([$mount]);
-
- $node = new Folder($root, $view, '/bar/foo');
- $result = $node->getById(1);
- $this->assertEquals(1, count($result));
- $this->assertEquals('/bar/foo/qwerty', $result[0]->getPath());
- }
-
- public function testGetByIdMountRoot(): void {
- $manager = $this->createMock(Manager::class);
- $view = $this->getRootViewMock();
- $root = $this->getMockBuilder(Root::class)
- ->onlyMethods(['getMountsIn', 'getMount'])
- ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
- $storage = $this->createMock(Storage::class);
- $mount = new MountPoint($storage, '/bar');
- $storage->method('getId')->willReturn('');
- $cache = $this->getMockBuilder(Cache::class)->setConstructorArgs([$storage])->getMock();
-
- $fileInfo = new CacheEntry(['path' => '', 'mimetype' => 'text/plain'], null);
-
- $storage->method('getCache')
- ->willReturn($cache);
- $storage->method('getOwner')
- ->willReturn('owner');
-
- $this->userMountCache->expects($this->any())
- ->method('getMountsForFileId')
- ->with(1)
- ->willReturn([new CachedMountInfo(
- $this->user,
- 1,
- 0,
- '/bar/',
- 'test',
- 1,
- ''
- )]);
-
- $cache->method('get')
- ->with(1)
- ->willReturn($fileInfo);
-
- $manager->method('getMountFromMountInfo')
- ->willReturn($mount);
- $manager->method('getMountsByMountProvider')
- ->willReturn([$mount]);
-
- $node = new Folder($root, $view, '/bar');
- $result = $node->getById(1);
- $this->assertEquals(1, count($result));
- $this->assertEquals('/bar', $result[0]->getPath());
- }
-
- public function testGetByIdOutsideFolder(): void {
- $manager = $this->createMock(Manager::class);
- $view = $this->getRootViewMock();
- $root = $this->getMockBuilder(Root::class)
- ->onlyMethods(['getMountsIn', 'getMount'])
- ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
- $storage = $this->createMock(Storage::class);
- $mount = new MountPoint($storage, '/bar');
- $storage->method('getId')->willReturn('');
- $cache = $this->getMockBuilder(Cache::class)->setConstructorArgs([$storage])->getMock();
-
- $fileInfo = new CacheEntry(['path' => 'foobar', 'mimetype' => 'text/plain'], null);
-
- $storage->method('getCache')
- ->willReturn($cache);
- $storage->method('getOwner')
- ->willReturn('owner');
-
- $this->userMountCache->expects($this->any())
- ->method('getMountsForFileId')
- ->with(1)
- ->willReturn([new CachedMountInfo(
- $this->user,
- 1,
- 0,
- '/bar/',
- 'test',
- 1,
- ''
- )]);
-
- $cache->method('get')
- ->with(1)
- ->willReturn($fileInfo);
-
- $manager->method('getMountFromMountInfo')
- ->willReturn($mount);
- $manager->method('getMountsByMountProvider')
- ->willReturn([$mount]);
-
- $node = new Folder($root, $view, '/bar/foo');
- $result = $node->getById(1);
- $this->assertEquals(0, count($result));
- }
-
- public function testGetByIdMultipleStorages(): void {
- $manager = $this->createMock(Manager::class);
- $view = $this->getRootViewMock();
- $root = $this->getMockBuilder(Root::class)
- ->onlyMethods(['getMountsIn', 'getMount'])
- ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
- $storage = $this->createMock(Storage::class);
- $mount1 = new MountPoint($storage, '/bar');
- $mount2 = new MountPoint($storage, '/bar/foo/asd');
- $storage->method('getId')->willReturn('');
- $cache = $this->getMockBuilder(Cache::class)->setConstructorArgs([$storage])->getMock();
-
- $fileInfo = new CacheEntry(['path' => 'foo/qwerty', 'mimetype' => 'text/plain'], null);
-
- $storage->method('getCache')
- ->willReturn($cache);
- $storage->method('getOwner')
- ->willReturn('owner');
-
- $this->userMountCache->method('getMountsForFileId')
- ->with(1)
- ->willReturn([
- new CachedMountInfo(
- $this->user,
- 1,
- 0,
- '/bar/',
- 'test',
- 1,
- ''
- ),
- new CachedMountInfo(
- $this->user,
- 1,
- 0,
- '/bar/foo/asd/',
- 'test',
- 1,
- ''
- ),
- ]);
-
- $cache->method('get')
- ->with(1)
- ->willReturn($fileInfo);
-
- $manager->method('getMountFromMountInfo')
- ->willReturnCallback(function (ICachedMountInfo $mountInfo) use ($mount1, $mount2) {
- if ($mountInfo->getMountPoint() === $mount1->getMountPoint()) {
- return $mount1;
- }
- if ($mountInfo->getMountPoint() === $mount2->getMountPoint()) {
- return $mount2;
- }
- return null;
- });
- $manager->method('getMountsByMountProvider')
- ->willReturn([$mount1, $mount2]);
-
- $node = new Folder($root, $view, '/bar/foo');
- $result = $node->getById(1);
- $this->assertEquals(2, count($result));
- $this->assertEquals('/bar/foo/qwerty', $result[0]->getPath());
- $this->assertEquals('/bar/foo/asd/foo/qwerty', $result[1]->getPath());
- }
-
- public static function uniqueNameProvider(): array {
- return [
- // input, existing, expected
- ['foo', [], 'foo'],
- ['foo', ['foo'], 'foo (2)'],
- ['foo', ['foo', 'foo (2)'], 'foo (3)'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('uniqueNameProvider')]
- public function testGetUniqueName($name, $existingFiles, $expected): void {
- $manager = $this->createMock(Manager::class);
- $folderPath = '/bar/foo';
- $view = $this->getRootViewMock();
- $root = $this->getMockBuilder(Root::class)
- ->onlyMethods(['getUser', 'getMountsIn', 'getMount'])
- ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
-
- $view->expects($this->any())
- ->method('file_exists')
- ->willReturnCallback(function ($path) use ($existingFiles, $folderPath) {
- foreach ($existingFiles as $existing) {
- if ($folderPath . '/' . $existing === $path) {
- return true;
- }
- }
- return false;
- });
-
- $node = new Folder($root, $view, $folderPath);
- $this->assertEquals($expected, $node->getNonExistingName($name));
- }
-
- public function testRecent(): void {
- $manager = $this->createMock(Manager::class);
- $folderPath = '/bar/foo';
- $view = $this->getRootViewMock();
- /** @var \PHPUnit\Framework\MockObject\MockObject|Root $root */
- $root = $this->getMockBuilder(Root::class)
- ->onlyMethods(['getUser', 'getMountsIn', 'getMount'])
- ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
- /** @var \PHPUnit\Framework\MockObject\MockObject|FileInfo $folderInfo */
- $folderInfo = $this->getMockBuilder(FileInfo::class)
- ->disableOriginalConstructor()->getMock();
-
- $baseTime = time();
- $storage = new Temporary();
- $mount = new MountPoint($storage, '');
-
- $folderInfo->expects($this->any())
- ->method('getMountPoint')
- ->willReturn($mount);
- $root->method('getMount')
- ->willReturn($mount);
- $root->method('getMountsIn')
- ->willReturn([]);
-
- $cache = $storage->getCache();
-
- $cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $cache->insert('bar', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $cache->insert('bar/foo', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $cache->insert('bar/asd', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $id1 = $cache->put('bar/foo/inside.txt', [
- 'storage_mtime' => $baseTime,
- 'mtime' => $baseTime,
- 'mimetype' => 'text/plain',
- 'size' => 3,
- 'permissions' => Constants::PERMISSION_ALL,
- ]);
- $id2 = $cache->put('bar/foo/old.txt', [
- 'storage_mtime' => $baseTime - 100,
- 'mtime' => $baseTime - 100,
- 'mimetype' => 'text/plain',
- 'size' => 3,
- 'permissions' => Constants::PERMISSION_READ,
- ]);
- $cache->put('bar/asd/outside.txt', [
- 'storage_mtime' => $baseTime,
- 'mtime' => $baseTime,
- 'mimetype' => 'text/plain',
- 'size' => 3,
- ]);
- $id3 = $cache->put('bar/foo/older.txt', [
- 'storage_mtime' => $baseTime - 600,
- 'mtime' => $baseTime - 600,
- 'mimetype' => 'text/plain',
- 'size' => 3,
- 'permissions' => Constants::PERMISSION_ALL,
- ]);
-
- $node = new Folder($root, $view, $folderPath, $folderInfo);
-
- $nodes = $node->getRecent(5);
- $ids = array_map(function (Node $node) {
- return (int)$node->getId();
- }, $nodes);
- $this->assertEquals([$id1, $id2, $id3], $ids);
- }
-
- public function testRecentFolder(): void {
- $manager = $this->createMock(Manager::class);
- $folderPath = '/bar/foo';
- $view = $this->getRootViewMock();
- /** @var \PHPUnit\Framework\MockObject\MockObject|Root $root */
- $root = $this->getMockBuilder(Root::class)
- ->onlyMethods(['getUser', 'getMountsIn', 'getMount'])
- ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
- /** @var \PHPUnit\Framework\MockObject\MockObject|FileInfo $folderInfo */
- $folderInfo = $this->getMockBuilder(FileInfo::class)
- ->disableOriginalConstructor()->getMock();
-
- $baseTime = time();
- $storage = new Temporary();
- $mount = new MountPoint($storage, '');
-
- $folderInfo->expects($this->any())
- ->method('getMountPoint')
- ->willReturn($mount);
-
- $root->method('getMount')
- ->willReturn($mount);
- $root->method('getMountsIn')
- ->willReturn([]);
-
- $cache = $storage->getCache();
-
- $cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $cache->insert('bar', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $cache->insert('bar/foo', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $id1 = $cache->put('bar/foo/folder', [
- 'storage_mtime' => $baseTime,
- 'mtime' => $baseTime,
- 'mimetype' => \OCP\Files\FileInfo::MIMETYPE_FOLDER,
- 'size' => 3,
- 'permissions' => 0,
- ]);
- $id2 = $cache->put('bar/foo/folder/bar.txt', [
- 'storage_mtime' => $baseTime,
- 'mtime' => $baseTime,
- 'mimetype' => 'text/plain',
- 'size' => 3,
- 'parent' => $id1,
- 'permissions' => Constants::PERMISSION_ALL,
- ]);
- $id3 = $cache->put('bar/foo/folder/asd.txt', [
- 'storage_mtime' => $baseTime - 100,
- 'mtime' => $baseTime - 100,
- 'mimetype' => 'text/plain',
- 'size' => 3,
- 'parent' => $id1,
- 'permissions' => Constants::PERMISSION_ALL,
- ]);
-
- $node = new Folder($root, $view, $folderPath, $folderInfo);
-
- $nodes = $node->getRecent(5);
- $ids = array_map(function (Node $node) {
- return (int)$node->getId();
- }, $nodes);
- $this->assertEquals([$id2, $id3], $ids);
- $this->assertEquals($baseTime, $nodes[0]->getMTime());
- $this->assertEquals($baseTime - 100, $nodes[1]->getMTime());
- }
-
- public function testRecentJail(): void {
- $manager = $this->createMock(Manager::class);
- $folderPath = '/bar/foo';
- $view = $this->getRootViewMock();
- /** @var \PHPUnit\Framework\MockObject\MockObject|Root $root */
- $root = $this->getMockBuilder(Root::class)
- ->onlyMethods(['getUser', 'getMountsIn', 'getMount'])
- ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
- /** @var \PHPUnit\Framework\MockObject\MockObject|FileInfo $folderInfo */
- $folderInfo = $this->getMockBuilder(FileInfo::class)
- ->disableOriginalConstructor()->getMock();
-
- $baseTime = time();
- $storage = new Temporary();
- $jail = new Jail([
- 'storage' => $storage,
- 'root' => 'folder',
- ]);
- $mount = new MountPoint($jail, '/bar/foo');
-
- $folderInfo->expects($this->any())
- ->method('getMountPoint')
- ->willReturn($mount);
- $root->method('getMount')
- ->willReturn($mount);
- $root->method('getMountsIn')
- ->willReturn([]);
-
- $cache = $storage->getCache();
-
- $cache->insert('', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $cache->insert('folder', ['size' => 0, 'mtime' => 0, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $id1 = $cache->put('folder/inside.txt', [
- 'storage_mtime' => $baseTime,
- 'mtime' => $baseTime,
- 'mimetype' => 'text/plain',
- 'size' => 3,
- 'permissions' => Constants::PERMISSION_ALL,
- ]);
-
- $cache->put('outside.txt', [
- 'storage_mtime' => $baseTime - 100,
- 'mtime' => $baseTime - 100,
- 'mimetype' => 'text/plain',
- 'size' => 3,
- ]);
-
- $node = new Folder($root, $view, $folderPath, $folderInfo);
-
- $nodes = $node->getRecent(5);
- $ids = array_map(function (Node $node) {
- return (int)$node->getId();
- }, $nodes);
- $this->assertEquals([$id1], $ids);
- }
-
- public static function offsetLimitProvider(): array {
- return [
- [0, 10, ['/bar/foo/foo1', '/bar/foo/foo2', '/bar/foo/foo3', '/bar/foo/foo4', '/bar/foo/sub1/foo5', '/bar/foo/sub1/foo6', '/bar/foo/sub2/foo7', '/bar/foo/sub2/foo8'], []],
- [0, 5, ['/bar/foo/foo1', '/bar/foo/foo2', '/bar/foo/foo3', '/bar/foo/foo4', '/bar/foo/sub1/foo5'], []],
- [0, 2, ['/bar/foo/foo1', '/bar/foo/foo2'], []],
- [3, 2, ['/bar/foo/foo4', '/bar/foo/sub1/foo5'], []],
- [3, 5, ['/bar/foo/foo4', '/bar/foo/sub1/foo5', '/bar/foo/sub1/foo6', '/bar/foo/sub2/foo7', '/bar/foo/sub2/foo8'], []],
- [5, 2, ['/bar/foo/sub1/foo6', '/bar/foo/sub2/foo7'], []],
- [6, 2, ['/bar/foo/sub2/foo7', '/bar/foo/sub2/foo8'], []],
- [7, 2, ['/bar/foo/sub2/foo8'], []],
- [10, 2, [], []],
- [0, 5, ['/bar/foo/sub2/foo7', '/bar/foo/foo1', '/bar/foo/sub1/foo5', '/bar/foo/foo2', '/bar/foo/foo3'], [new SearchOrder(ISearchOrder::DIRECTION_ASCENDING, 'mtime')]],
- [3, 2, ['/bar/foo/foo2', '/bar/foo/foo3'], [new SearchOrder(ISearchOrder::DIRECTION_ASCENDING, 'mtime')]],
- [0, 5, ['/bar/foo/sub1/foo5', '/bar/foo/sub1/foo6', '/bar/foo/sub2/foo7', '/bar/foo/foo1', '/bar/foo/foo2'], [
- new SearchOrder(ISearchOrder::DIRECTION_DESCENDING, 'size'),
- new SearchOrder(ISearchOrder::DIRECTION_ASCENDING, 'mtime')
- ]],
- ];
- }
-
- /**
- * @param int $offset
- * @param int $limit
- * @param string[] $expectedPaths
- * @param ISearchOrder[] $ordering
- * @throws NotFoundException
- * @throws InvalidPathException
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('offsetLimitProvider')]
- public function testSearchSubStoragesLimitOffset(int $offset, int $limit, array $expectedPaths, array $ordering): void {
- if (!$ordering) {
- $ordering = [new SearchOrder(ISearchOrder::DIRECTION_ASCENDING, 'fileid')];
- }
-
- $manager = $this->createMock(Manager::class);
- $view = $this->getRootViewMock();
- $root = $this->getMockBuilder(Root::class)
- ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
- $root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
- $storage = $this->createMock(IStorage::class);
- $storage->method('getId')->willReturn('test::1');
- $cache = new Cache($storage);
- $subStorage1 = $this->createMock(IStorage::class);
- $subStorage1->method('getId')->willReturn('test::2');
- $subCache1 = new Cache($subStorage1);
- $subMount1 = $this->getMockBuilder(MountPoint::class)->setConstructorArgs([Temporary::class, ''])->getMock();
- $subStorage2 = $this->createMock(IStorage::class);
- $subStorage2->method('getId')->willReturn('test::3');
- $subCache2 = new Cache($subStorage2);
- $subMount2 = $this->getMockBuilder(MountPoint::class)->setConstructorArgs([Temporary::class, ''])->getMock();
-
- $mount = $this->createMock(IMountPoint::class);
- $mount->method('getStorage')
- ->willReturn($storage);
- $mount->method('getInternalPath')
- ->willReturn('foo');
-
- $subMount1->method('getStorage')
- ->willReturn($subStorage1);
-
- $subMount1->method('getMountPoint')
- ->willReturn('/bar/foo/sub1/');
-
- $storage->method('getCache')
- ->willReturn($cache);
- $storage->method('getOwner')
- ->willReturn('owner');
-
- $subStorage1->method('getCache')
- ->willReturn($subCache1);
- $subStorage1->method('getOwner')
- ->willReturn('owner');
-
- $subMount2->method('getStorage')
- ->willReturn($subStorage2);
-
- $subMount2->method('getMountPoint')
- ->willReturn('/bar/foo/sub2/');
-
- $subStorage2->method('getCache')
- ->willReturn($subCache2);
- $subStorage2->method('getOwner')
- ->willReturn('owner');
-
- $cache->insert('', ['size' => 0, 'mtime' => 10, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $cache->insert('foo', ['size' => 0, 'mtime' => 10, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $cache->insert('foo/foo1', ['size' => 200, 'mtime' => 10, 'mimetype' => 'text/plain']);
- $cache->insert('foo/foo2', ['size' => 200, 'mtime' => 20, 'mimetype' => 'text/plain']);
- $cache->insert('foo/foo3', ['size' => 200, 'mtime' => 30, 'mimetype' => 'text/plain']);
- $cache->insert('foo/foo4', ['size' => 200, 'mtime' => 40, 'mimetype' => 'text/plain']);
-
- $subCache1->insert('', ['size' => 0, 'mtime' => 10, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $subCache1->insert('foo5', ['size' => 300, 'mtime' => 15, 'mimetype' => 'text/plain']);
- $subCache1->insert('foo6', ['size' => 300, 'mtime' => 50, 'mimetype' => 'text/plain']);
-
- $subCache2->insert('', ['size' => 0, 'mtime' => 10, 'mimetype' => ICacheEntry::DIRECTORY_MIMETYPE]);
- $subCache2->insert('foo7', ['size' => 200, 'mtime' => 5, 'mimetype' => 'text/plain']);
- $subCache2->insert('foo8', ['size' => 200, 'mtime' => 60, 'mimetype' => 'text/plain']);
-
- $root->method('getMountsIn')
- ->with('/bar/foo')
- ->willReturn([$subMount1, $subMount2]);
-
- $root->method('getMount')
- ->with('/bar/foo')
- ->willReturn($mount);
-
- $node = new Folder($root, $view, '/bar/foo');
- $comparison = new SearchComparison(ISearchComparison::COMPARE_LIKE, 'name', '%foo%');
- $operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- $comparison,
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_NOT, [new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', ICacheEntry::DIRECTORY_MIMETYPE)]),
- ]);
- $query = new SearchQuery($operator, $limit, $offset, $ordering);
- $result = $node->search($query);
- $cache->clear();
- $subCache1->clear();
- $subCache2->clear();
- $ids = array_map(function (Node $info) {
- return $info->getPath();
- }, $result);
- $this->assertEquals($expectedPaths, $ids);
- }
-
- public static function dataGetOrCreateFolder(): \Generator {
- yield 'Create new folder' => [0];
- yield 'Get existing folder' => [1];
- yield 'Create new folder while a file with the same name already exists' => [2];
- }
-
- #[DataProvider('dataGetOrCreateFolder')]
- public function testGetOrCreateFolder(int $case): void {
- $folderName = 'asd';
-
- $view = $this->getRootViewMock();
- $manager = $this->createMock(Manager::class);
- $root = $this->getMockBuilder(Root::class)
- ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
- $root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
-
- $view->method('getFileInfo')
- ->willReturnCallback(function (string $path) use ($folderName) {
- if ($path === '/bar/foo' || $path === '/bar/foo/' . $folderName) {
- return $this->getFileInfo(['permissions' => Constants::PERMISSION_ALL]);
- }
- $this->fail('Trying to get ' . $path);
- });
-
- $view->method('mkdir')
- ->willReturn(true);
-
- $view->method('touch')
- ->with('/bar/foo/asd')
- ->willReturn(true);
-
- $node = new Folder($root, $view, '/bar/foo');
-
- switch ($case) {
- case 0:
- $child = new Folder($root, $view, '/bar/foo/' . $folderName, null, $node);
-
- $root->expects($this->any())
- ->method('get')
- ->willReturnCallback(function (string $path) use ($root, $view, $folderName) {
- if ($path === '/bar/foo/') {
- return new Folder($root, $view, '/bar/foo/');
- } elseif ($path === '/bar/foo/' . $folderName) {
- throw new NotFoundException();
- }
- $this->fail('Trying to get ' . $path);
- });
-
- break; // do nothing
- case 1:
- $child = new Folder($root, $view, '/bar/foo/' . $folderName, null, $node);
-
- $root->expects($this->any())
- ->method('get')
- ->with('/bar/foo/' . $folderName)
- ->willReturn($child);
- $node->newFolder($folderName);
- break;
- case 2:
- $child = new Folder($root, $view, '/bar/foo/' . $folderName . ' (1)', null, $node);
- $root->expects($this->any())
- ->method('get')
- ->willReturnCallback(function (string $path) use ($root, $view, $folderName) {
- if ($path === '/bar/foo/') {
- return new Folder($root, $view, '/bar/foo/');
- } elseif ($path === '/bar/foo/' . $folderName) {
- return new File($root, $view, '/bar/foo/asd');
- } elseif ($path === '/bar/foo/' . $folderName . ' (1)') {
- throw new NotFoundException();
- }
- $this->fail('Trying to get ' . $path);
- });
- $node->newFile($folderName);
- break;
- }
-
- $result = $node->getOrCreateFolder($folderName);
- $this->assertEquals($child, $result);
- }
-
- private function callBuildNotExistingFileNameForView(string $path, string $name, View&MockObject $view): string {
- $rootFolder = $this->createMock(IRootFolder::class);
- $folder = new Folder($rootFolder, $view, $path);
- return $path . (str_ends_with('/', $path) ? '' : '/') . $folder->getNonExistingName($name);
- }
-
- public function testBuildNotExistingFileNameForView(): void {
- $viewMock = $this->createMock(View::class);
- $this->assertEquals('/filename', $this->callBuildNotExistingFileNameForView('/', 'filename', $viewMock));
- $this->assertEquals('dir/filename.ext', $this->callBuildNotExistingFileNameForView('dir', 'filename.ext', $viewMock));
-
- $viewMock = $this->createMock(View::class);
- $viewMock->expects($this->exactly(2))
- ->method('file_exists')
- ->willReturnMap([
- // Conflict on filename.ext
- ['dir/filename.ext', true],
- ['dir/filename (2).ext', false],
- ]);
- $this->assertEquals('dir/filename (2).ext', $this->callBuildNotExistingFileNameForView('dir', 'filename.ext', $viewMock));
-
- $viewMock = $this->createMock(View::class);
- $viewMock->expects($this->exactly(3))
- ->method('file_exists')
- ->willReturnMap([
- // Conflict on filename.ext
- ['dir/filename.ext', true],
- ['dir/filename (2).ext', true],
- ['dir/filename (3).ext', false],
- ]);
- $this->assertEquals('dir/filename (3).ext', $this->callBuildNotExistingFileNameForView('dir', 'filename.ext', $viewMock));
-
- $viewMock = $this->createMock(View::class);
- $viewMock->expects($this->exactly(2))
- ->method('file_exists')
- ->willReturnMap([
- ['dir/filename (1).ext', true],
- ['dir/filename (2).ext', false],
- ]);
- $this->assertEquals('dir/filename (2).ext', $this->callBuildNotExistingFileNameForView('dir', 'filename (1).ext', $viewMock));
-
- $viewMock = $this->createMock(View::class);
- $viewMock->expects($this->exactly(2))
- ->method('file_exists')
- ->willReturnMap([
- ['dir/filename (2).ext', true],
- ['dir/filename (3).ext', false],
- ]);
- $this->assertEquals('dir/filename (3).ext', $this->callBuildNotExistingFileNameForView('dir', 'filename (2).ext', $viewMock));
-
- $viewMock = $this->createMock(View::class);
- $viewMock->expects($this->exactly(3))
- ->method('file_exists')
- ->willReturnMap([
- ['dir/filename (2).ext', true],
- ['dir/filename (3).ext', true],
- ['dir/filename (4).ext', false],
- ]);
- $this->assertEquals('dir/filename (4).ext', $this->callBuildNotExistingFileNameForView('dir', 'filename (2).ext', $viewMock));
-
- $viewMock = $this->createMock(View::class);
- $viewMock->expects($this->exactly(2))
- ->method('file_exists')
- ->willReturnMap([
- ['dir/filename(1).ext', true],
- ['dir/filename(2).ext', false],
- ]);
- $this->assertEquals('dir/filename(2).ext', $this->callBuildNotExistingFileNameForView('dir', 'filename(1).ext', $viewMock));
-
- $viewMock = $this->createMock(View::class);
- $viewMock->expects($this->exactly(2))
- ->method('file_exists')
- ->willReturnMap([
- ['dir/filename(1) (1).ext', true],
- ['dir/filename(1) (2).ext', false],
- ]);
- $this->assertEquals('dir/filename(1) (2).ext', $this->callBuildNotExistingFileNameForView('dir', 'filename(1) (1).ext', $viewMock));
-
- $viewMock = $this->createMock(View::class);
- $viewMock->expects($this->exactly(3))
- ->method('file_exists')
- ->willReturnMap([
- ['dir/filename(1) (1).ext', true],
- ['dir/filename(1) (2).ext', true],
- ['dir/filename(1) (3).ext', false],
- ]);
- $this->assertEquals('dir/filename(1) (3).ext', $this->callBuildNotExistingFileNameForView('dir', 'filename(1) (1).ext', $viewMock));
-
- $viewMock = $this->createMock(View::class);
- $viewMock->expects($this->exactly(2))
- ->method('file_exists')
- ->willReturnMap([
- ['dir/filename(1) (2) (3).ext', true],
- ['dir/filename(1) (2) (4).ext', false],
- ]);
- $this->assertEquals('dir/filename(1) (2) (4).ext', $this->callBuildNotExistingFileNameForView('dir', 'filename(1) (2) (3).ext', $viewMock));
- }
-}
--- a/tests/lib/Files/Node/HookConnectorTest.php
+++ /dev/null
@@ -1,331 +0,0 @@
-userId = $this->getUniqueID();
- $this->createUser($this->userId, 'pass');
- // this will setup the FS
- $this->loginAsUser($this->userId);
- $this->registerMount($this->userId, new Temporary(), '/' . $this->userId . '/files/');
- $cacheFactory = $this->createMock(ICacheFactory::class);
- $cacheFactory->method('createLocal')
- ->willReturnCallback(function () {
- return new ArrayCache();
- });
- $this->view = new View();
- $this->root = new Root(
- Filesystem::getMountManager(),
- $this->view,
- Server::get(IUserManager::class)->get($this->userId),
- Server::get(IUserMountCache::class),
- $this->createMock(LoggerInterface::class),
- $this->createMock(IUserManager::class),
- $this->createMock(IEventDispatcher::class),
- $cacheFactory,
- $this->createMock(IAppConfig::class),
- );
- $this->eventDispatcher = Server::get(IEventDispatcher::class);
- $this->logger = Server::get(LoggerInterface::class);
- }
-
- #[\Override]
- protected function tearDown(): void {
- parent::tearDown();
- \OC_Hook::clear('OC_Filesystem');
- \OC_Util::tearDownFS();
- }
-
- public static function viewToNodeProvider(): array {
- return [
- [function (): void {
- Filesystem::file_put_contents('test.txt', 'asd');
- }, 'preWrite', '\OCP\Files::preWrite', BeforeNodeWrittenEvent::class],
- [function (): void {
- Filesystem::file_put_contents('test.txt', 'asd');
- }, 'postWrite', '\OCP\Files::postWrite', NodeWrittenEvent::class],
- [function (): void {
- Filesystem::file_put_contents('test.txt', 'asd');
- }, 'preCreate', '\OCP\Files::preCreate', BeforeNodeCreatedEvent::class],
- [function (): void {
- Filesystem::file_put_contents('test.txt', 'asd');
- }, 'postCreate', '\OCP\Files::postCreate', NodeCreatedEvent::class],
- [function (): void {
- Filesystem::mkdir('test.txt');
- }, 'preCreate', '\OCP\Files::preCreate', BeforeNodeCreatedEvent::class],
- [function (): void {
- Filesystem::mkdir('test.txt');
- }, 'postCreate', '\OCP\Files::postCreate', NodeCreatedEvent::class],
- [function (): void {
- Filesystem::touch('test.txt');
- }, 'preTouch', '\OCP\Files::preTouch', BeforeNodeTouchedEvent::class],
- [function (): void {
- Filesystem::touch('test.txt');
- }, 'postTouch', '\OCP\Files::postTouch', NodeTouchedEvent::class],
- [function (): void {
- Filesystem::touch('test.txt');
- }, 'preCreate', '\OCP\Files::preCreate', BeforeNodeCreatedEvent::class],
- [function (): void {
- Filesystem::touch('test.txt');
- }, 'postCreate', '\OCP\Files::postCreate', NodeCreatedEvent::class],
- [function (): void {
- Filesystem::file_put_contents('test.txt', 'asd');
- Filesystem::unlink('test.txt');
- }, 'preDelete', '\OCP\Files::preDelete', BeforeNodeDeletedEvent::class],
- [function (): void {
- Filesystem::file_put_contents('test.txt', 'asd');
- Filesystem::unlink('test.txt');
- }, 'postDelete', '\OCP\Files::postDelete', NodeDeletedEvent::class],
- [function (): void {
- Filesystem::mkdir('test.txt');
- Filesystem::rmdir('test.txt');
- }, 'preDelete', '\OCP\Files::preDelete', BeforeNodeDeletedEvent::class],
- [function (): void {
- Filesystem::mkdir('test.txt');
- Filesystem::rmdir('test.txt');
- }, 'postDelete', '\OCP\Files::postDelete', NodeDeletedEvent::class],
- ];
- }
-
- /**
- * @param callable $operation
- * @param string $expectedHook
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('viewToNodeProvider')]
- public function testViewToNode(callable $operation, $expectedHook, $expectedLegacyEvent, $expectedEvent): void {
- $connector = new HookConnector($this->root, $this->view, $this->eventDispatcher, $this->logger);
- $connector->viewToNode();
- $hookCalled = false;
- /** @var Node $hookNode */
- $hookNode = null;
-
- $this->root->listen('\OC\Files', $expectedHook, function ($node) use (&$hookNode, &$hookCalled): void {
- $hookCalled = true;
- $hookNode = $node;
- });
-
- $dispatcherCalled = false;
- /** @var Node $dispatcherNode */
- $dispatcherNode = null;
- $this->eventDispatcher->addListener($expectedLegacyEvent, function ($event) use (&$dispatcherCalled, &$dispatcherNode): void {
- /** @var GenericEvent|APIGenericEvent $event */
- $dispatcherCalled = true;
- $dispatcherNode = $event->getSubject();
- });
-
- $newDispatcherCalled = false;
- $newDispatcherNode = null;
- $this->eventDispatcher->addListener($expectedEvent, function ($event) use ($expectedEvent, &$newDispatcherCalled, &$newDispatcherNode): void {
- if ($event instanceof $expectedEvent) {
- /** @var AbstractNodeEvent $event */
- $newDispatcherCalled = true;
- $newDispatcherNode = $event->getNode();
- }
- });
-
- $operation();
-
- $this->assertTrue($hookCalled);
- $this->assertEquals('/' . $this->userId . '/files/test.txt', $hookNode->getPath());
-
- $this->assertTrue($dispatcherCalled);
- $this->assertEquals('/' . $this->userId . '/files/test.txt', $dispatcherNode->getPath());
-
- $this->assertTrue($newDispatcherCalled);
- $this->assertEquals('/' . $this->userId . '/files/test.txt', $newDispatcherNode->getPath());
- }
-
- public static function viewToNodeProviderCopyRename(): array {
- return [
- [function (): void {
- Filesystem::file_put_contents('source', 'asd');
- Filesystem::rename('source', 'target');
- }, 'preRename', '\OCP\Files::preRename', BeforeNodeRenamedEvent::class],
- [function (): void {
- Filesystem::file_put_contents('source', 'asd');
- Filesystem::rename('source', 'target');
- }, 'postRename', '\OCP\Files::postRename', NodeRenamedEvent::class],
- [function (): void {
- Filesystem::file_put_contents('source', 'asd');
- Filesystem::copy('source', 'target');
- }, 'preCopy', '\OCP\Files::preCopy', BeforeNodeCopiedEvent::class],
- [function (): void {
- Filesystem::file_put_contents('source', 'asd');
- Filesystem::copy('source', 'target');
- }, 'postCopy', '\OCP\Files::postCopy', NodeCopiedEvent::class],
- ];
- }
-
- /**
- * @param callable $operation
- * @param string $expectedHook
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('viewToNodeProviderCopyRename')]
- public function testViewToNodeCopyRename(callable $operation, $expectedHook, $expectedLegacyEvent, $expectedEvent): void {
- $connector = new HookConnector($this->root, $this->view, $this->eventDispatcher, $this->logger);
- $connector->viewToNode();
- $hookCalled = false;
- /** @var Node $hookSourceNode */
- $hookSourceNode = null;
- /** @var Node $hookTargetNode */
- $hookTargetNode = null;
-
- $this->root->listen('\OC\Files', $expectedHook, function ($sourceNode, $targetNode) use (&$hookCalled, &$hookSourceNode, &$hookTargetNode): void {
- $hookCalled = true;
- $hookSourceNode = $sourceNode;
- $hookTargetNode = $targetNode;
- });
-
- $dispatcherCalled = false;
- /** @var Node $dispatcherSourceNode */
- $dispatcherSourceNode = null;
- /** @var Node $dispatcherTargetNode */
- $dispatcherTargetNode = null;
- $this->eventDispatcher->addListener($expectedLegacyEvent, function ($event) use (&$dispatcherSourceNode, &$dispatcherTargetNode, &$dispatcherCalled): void {
- /** @var GenericEvent|APIGenericEvent $event */
- $dispatcherCalled = true;
- [$dispatcherSourceNode, $dispatcherTargetNode] = $event->getSubject();
- });
-
- $newDispatcherCalled = false;
- /** @var Node $dispatcherSourceNode */
- $newDispatcherSourceNode = null;
- /** @var Node $dispatcherTargetNode */
- $newDispatcherTargetNode = null;
- $this->eventDispatcher->addListener($expectedEvent, function ($event) use ($expectedEvent, &$newDispatcherSourceNode, &$newDispatcherTargetNode, &$newDispatcherCalled): void {
- if ($event instanceof $expectedEvent) {
- /** @var AbstractNodesEvent$event */
- $newDispatcherCalled = true;
- $newDispatcherSourceNode = $event->getSource();
- $newDispatcherTargetNode = $event->getTarget();
- }
- });
-
- $operation();
-
- $this->assertTrue($hookCalled);
- $this->assertEquals('/' . $this->userId . '/files/source', $hookSourceNode->getPath());
- $this->assertEquals('/' . $this->userId . '/files/target', $hookTargetNode->getPath());
-
- $this->assertTrue($dispatcherCalled);
- $this->assertEquals('/' . $this->userId . '/files/source', $dispatcherSourceNode->getPath());
- $this->assertEquals('/' . $this->userId . '/files/target', $dispatcherTargetNode->getPath());
-
- $this->assertTrue($newDispatcherCalled);
- $this->assertEquals('/' . $this->userId . '/files/source', $newDispatcherSourceNode->getPath());
- $this->assertEquals('/' . $this->userId . '/files/target', $newDispatcherTargetNode->getPath());
- }
-
- public function testPostDeleteMeta(): void {
- $connector = new HookConnector($this->root, $this->view, $this->eventDispatcher, $this->logger);
- $connector->viewToNode();
- $hookCalled = false;
- /** @var Node $hookNode */
- $hookNode = null;
-
- $this->root->listen('\OC\Files', 'postDelete', function ($node) use (&$hookNode, &$hookCalled): void {
- $hookCalled = true;
- $hookNode = $node;
- });
-
- $dispatcherCalled = false;
- /** @var Node $dispatcherNode */
- $dispatcherNode = null;
- $this->eventDispatcher->addListener('\OCP\Files::postDelete', function ($event) use (&$dispatcherCalled, &$dispatcherNode): void {
- /** @var GenericEvent|APIGenericEvent $event */
- $dispatcherCalled = true;
- $dispatcherNode = $event->getSubject();
- });
-
- $newDispatcherCalled = false;
- /** @var Node $dispatcherNode */
- $newDispatcherNode = null;
- $this->eventDispatcher->addListener(NodeDeletedEvent::class, function ($event) use (&$newDispatcherCalled, &$newDispatcherNode): void {
- if ($event instanceof NodeDeletedEvent) {
- /** @var AbstractNodeEvent $event */
- $newDispatcherCalled = true;
- $newDispatcherNode = $event->getNode();
- }
- });
-
- Filesystem::file_put_contents('test.txt', 'asd');
- $info = Filesystem::getFileInfo('test.txt');
- Filesystem::unlink('test.txt');
-
- $this->assertTrue($hookCalled);
- $this->assertEquals($hookNode->getId(), $info->getId());
-
- $this->assertTrue($dispatcherCalled);
- $this->assertEquals($dispatcherNode->getId(), $info->getId());
-
- $this->assertTrue($newDispatcherCalled);
- $this->assertEquals($newDispatcherNode->getId(), $info->getId());
- }
-}
--- a/tests/lib/Files/Node/IntegrationTest.php
+++ /dev/null
@@ -1,155 +0,0 @@
-createUser($this->getUniqueID('user'), '');
- $this->loginAsUser($user->getUID());
- $cacheFactory = $this->createMock(ICacheFactory::class);
- $cacheFactory->method('createLocal')
- ->willReturnCallback(function () {
- return new ArrayCache();
- });
-
- $this->view = new View();
- $this->root = new Root(
- $manager,
- $this->view,
- $user,
- Server::get(IUserMountCache::class),
- $this->createMock(LoggerInterface::class),
- $this->createMock(IUserManager::class),
- $this->createMock(IEventDispatcher::class),
- $cacheFactory,
- $this->createMock(IAppConfig::class),
- );
- $storage = new Temporary([]);
- $subStorage = new Temporary([]);
- $this->storages[] = $storage;
- $this->storages[] = $subStorage;
- $this->root->mount($storage, '/');
- $this->root->mount($subStorage, '/substorage/');
- $manager->removeMount('/' . $user->getUID());
- }
-
- #[\Override]
- protected function tearDown(): void {
- foreach ($this->storages as $storage) {
- $storage->getCache()->clear();
- }
-
- $this->logout();
- parent::tearDown();
- }
-
- public function testBasicFile(): void {
- $file = $this->root->newFile('/foo.txt');
- $this->assertCount(2, $this->root->getDirectoryListing());
- $this->assertTrue($this->root->nodeExists('/foo.txt'));
- $id = $file->getId();
- $this->assertInstanceOf('\OC\Files\Node\File', $file);
- $file->putContent('qwerty');
- $this->assertEquals('text/plain', $file->getMimeType());
- $this->assertEquals('qwerty', $file->getContent());
- $this->assertFalse($this->root->nodeExists('/bar.txt'));
- $target = $file->move('/bar.txt');
- $this->assertEquals($id, $target->getId());
- $this->assertEquals($id, $file->getId());
- $this->assertFalse($this->root->nodeExists('/foo.txt'));
- $this->assertTrue($this->root->nodeExists('/bar.txt'));
- $this->assertEquals('bar.txt', $file->getName());
- $this->assertEquals('bar.txt', $file->getInternalPath());
-
- $file->move('/substorage/bar.txt');
- $this->assertEquals($id, $file->getId());
- $this->assertEquals('qwerty', $file->getContent());
- }
-
- public function testBasicFolder(): void {
- $folder = $this->root->newFolder('/foo');
- $this->assertTrue($this->root->nodeExists('/foo'));
- $file = $folder->newFile('/bar');
- $this->assertTrue($this->root->nodeExists('/foo/bar'));
- $file->putContent('qwerty');
-
- $listing = $folder->getDirectoryListing();
- $this->assertEquals(1, count($listing));
- $this->assertEquals($file->getId(), $listing[0]->getId());
- $this->assertEquals($file->getStorage(), $listing[0]->getStorage());
-
- $rootListing = $this->root->getDirectoryListing();
- $this->assertEquals(2, count($rootListing));
-
- $folder->move('/asd');
- /**
- * @var File $file
- */
- $file = $folder->get('/bar');
- $this->assertInstanceOf('\OC\Files\Node\File', $file);
- $this->assertFalse($this->root->nodeExists('/foo/bar'));
- $this->assertTrue($this->root->nodeExists('/asd/bar'));
- $this->assertEquals('qwerty', $file->getContent());
- $folder->move('/substorage/foo');
- /**
- * @var File $file
- */
- $file = $folder->get('/bar');
- $this->assertInstanceOf('\OC\Files\Node\File', $file);
- $this->assertTrue($this->root->nodeExists('/substorage/foo/bar'));
- $this->assertEquals('qwerty', $file->getContent());
- }
-}
--- a/tests/lib/Files/Node/NodeTestCase.php
+++ /dev/null
@@ -1,771 +0,0 @@
-user = $this->createMock(IUser::class);
- $this->manager = $this->getMockBuilder(Manager::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->view = $this->getMockBuilder(View::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->view->expects($this->any())
- ->method('getRoot')
- ->willReturn('');
- $this->userMountCache = $this->getMockBuilder('\OCP\Files\Config\IUserMountCache')
- ->disableOriginalConstructor()
- ->getMock();
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->userManager = $this->createMock(IUserManager::class);
- $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
- $this->cacheFactory = $this->createMock(ICacheFactory::class);
- $this->cacheFactory->method('createLocal')
- ->willReturnCallback(function () {
- return new ArrayCache();
- });
- $this->appConfig = $this->createMock(IAppConfig::class);
-
- $this->root = $this->getMockBuilder(Root::class)
- ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->getMock();
- }
-
- /**
- * @return View|\PHPUnit\Framework\MockObject\MockObject $view
- */
- protected function getRootViewMock() {
- $view = $this->createMock(View::class);
- $view->expects($this->any())
- ->method('getRoot')
- ->willReturn('');
- return $view;
- }
-
- abstract protected function createTestNode(IRootFolder $root, View&MockObject $view, string $path, array $data = [], string $internalPath = '', ?IStorage $storage = null): Node;
-
- /**
- * @return class-string
- */
- abstract protected function getNodeClass(): string;
-
- /**
- * @return class-string
- */
- abstract protected function getNonExistingNodeClass(): string;
-
- /**
- * @return string
- */
- abstract protected function getViewDeleteMethod(): string;
-
- protected function getMockStorage(): IStorage&MockObject {
- $storage = $this->getMockBuilder(IStorage::class)
- ->disableOriginalConstructor()
- ->getMock();
- $storage->expects($this->any())
- ->method('getId')
- ->willReturn('home::someuser');
- return $storage;
- }
-
- protected function getFileInfo($data, $internalPath = '', ?IStorage $storage = null) {
- $mount = $this->createMock(IMountPoint::class);
- $mount->method('getStorage')
- ->willReturn($storage);
- return new FileInfo('', $this->getMockStorage(), $internalPath, $data, $mount);
- }
-
- public function testDelete(): void {
- $this->root->expects($this->exactly(2))
- ->method('emit')
- ->willReturn(true);
- $this->root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
-
- $this->view->expects($this->once())
- ->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL]));
-
- $this->view->expects($this->once())
- ->method($this->getViewDeleteMethod())
- ->with('/bar/foo')
- ->willReturn(true);
-
- $node = $this->createTestNode($this->root, $this->view, '/bar/foo');
- $node->delete();
- }
-
- public function testDeleteHooks(): void {
- $test = $this;
- $hooksRun = 0;
- /**
- * @param File $node
- */
- $preListener = function ($node) use (&$test, &$hooksRun): void {
- $test->assertInstanceOf($this->getNodeClass(), $node);
- $test->assertEquals('foo', $node->getInternalPath());
- $test->assertEquals('/bar/foo', $node->getPath());
- $test->assertEquals(1, $node->getId());
- $hooksRun++;
- };
-
- /**
- * @param File $node
- */
- $postListener = function ($node) use (&$test, &$hooksRun): void {
- $test->assertInstanceOf($this->getNonExistingNodeClass(), $node);
- $test->assertEquals('foo', $node->getInternalPath());
- $test->assertEquals('/bar/foo', $node->getPath());
- $test->assertEquals(1, $node->getId());
- $test->assertEquals('text/plain', $node->getMimeType());
- $hooksRun++;
- };
-
- $root = new Root(
- $this->manager,
- $this->view,
- $this->user,
- $this->userMountCache,
- $this->logger,
- $this->userManager,
- $this->eventDispatcher,
- $this->cacheFactory,
- $this->createMock(IAppConfig::class),
- );
-
- $root->listen('\OC\Files', 'preDelete', $preListener);
- $root->listen('\OC\Files', 'postDelete', $postListener);
-
- $this->view->expects($this->any())
- ->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL, 'fileid' => 1, 'mimetype' => 'text/plain'], 'foo'));
-
- $this->view->expects($this->once())
- ->method($this->getViewDeleteMethod())
- ->with('/bar/foo')
- ->willReturn(true);
-
- $node = $this->createTestNode($root, $this->view, '/bar/foo');
- $node->delete();
- $this->assertEquals(2, $hooksRun);
- }
-
- public function testDeleteNotPermitted(): void {
- $this->expectException(NotPermittedException::class);
-
- $this->root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
-
- $this->view->expects($this->once())
- ->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ]));
-
- $node = $this->createTestNode($this->root, $this->view, '/bar/foo');
- $node->delete();
- }
-
- public function testStat(): void {
- $this->root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
-
- $stat = [
- 'fileid' => 1,
- 'size' => 100,
- 'etag' => 'qwerty',
- 'mtime' => 50,
- 'permissions' => 0
- ];
-
- $this->view->expects($this->once())
- ->method('stat')
- ->with('/bar/foo')
- ->willReturn($stat);
-
- $node = $this->createTestNode($this->root, $this->view, '/bar/foo');
- $this->assertEquals($stat, $node->stat());
- }
-
- public function testGetId(): void {
- $this->root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
-
- $stat = $this->getFileInfo([
- 'fileid' => 1,
- 'size' => 100,
- 'etag' => 'qwerty',
- 'mtime' => 50
- ]);
-
- $this->view->expects($this->once())
- ->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($stat);
-
- $node = $this->createTestNode($this->root, $this->view, '/bar/foo');
- $this->assertEquals(1, $node->getId());
- }
-
- public function testGetSize(): void {
- $this->root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
-
- $stat = $this->getFileInfo([
- 'fileid' => 1,
- 'size' => 100,
- 'etag' => 'qwerty',
- 'mtime' => 50
- ]);
-
- $this->view->expects($this->once())
- ->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($stat);
-
- $node = $this->createTestNode($this->root, $this->view, '/bar/foo');
- $this->assertEquals(100, $node->getSize());
- }
-
- public function testGetEtag(): void {
- $this->root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
-
- $stat = $this->getFileInfo([
- 'fileid' => 1,
- 'size' => 100,
- 'etag' => 'qwerty',
- 'mtime' => 50
- ]);
-
- $this->view->expects($this->once())
- ->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($stat);
-
- $node = $this->createTestNode($this->root, $this->view, '/bar/foo');
- $this->assertEquals('qwerty', $node->getEtag());
- }
-
- public function testGetMTime(): void {
- $this->root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
-
- $stat = $this->getFileInfo([
- 'fileid' => 1,
- 'size' => 100,
- 'etag' => 'qwerty',
- 'mtime' => 50
- ]);
-
- $this->view->expects($this->once())
- ->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($stat);
-
- $node = $this->createTestNode($this->root, $this->view, '/bar/foo');
- $this->assertEquals(50, $node->getMTime());
- }
-
- public function testGetStorage(): void {
- $this->root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
- /**
- * @var Storage|\PHPUnit\Framework\MockObject\MockObject $storage
- */
- $storage = $this->getMockBuilder('\OC\Files\Storage\Storage')
- ->disableOriginalConstructor()
- ->getMock();
-
- $node = $this->createTestNode($this->root, $this->view, '/bar/foo', [], 'foo', $storage);
- $this->assertEquals($storage, $node->getStorage());
- }
-
- public function testGetPath(): void {
- $this->root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
-
- $node = $this->createTestNode($this->root, $this->view, '/bar/foo');
- $this->assertEquals('/bar/foo', $node->getPath());
- }
-
- public function testGetInternalPath(): void {
- $this->root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
- /**
- * @var Storage|\PHPUnit\Framework\MockObject\MockObject $storage
- */
- $storage = $this->getMockBuilder('\OC\Files\Storage\Storage')
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->view->expects($this->once())
- ->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($this->getFileInfo([], 'foo'));
-
- $node = $this->createTestNode($this->root, $this->view, '/bar/foo');
- $this->assertEquals('foo', $node->getInternalPath());
- }
-
- public function testGetName(): void {
- $this->root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
-
- $node = $this->createTestNode($this->root, $this->view, '/bar/foo');
- $this->assertEquals('foo', $node->getName());
- }
-
- public function testTouchSetMTime(): void {
- $this->root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
-
- $this->view->expects($this->once())
- ->method('touch')
- ->with('/bar/foo', 100)
- ->willReturn(true);
-
- $this->view->expects($this->once())
- ->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL]));
-
- $node = $this->createTestNode($this->root, $this->view, '/bar/foo');
- $node->touch(100);
- $this->assertEquals(100, $node->getMTime());
- }
-
- public function testTouchHooks(): void {
- $test = $this;
- $hooksRun = 0;
- /**
- * @param File $node
- */
- $preListener = function ($node) use (&$test, &$hooksRun): void {
- $test->assertEquals('foo', $node->getInternalPath());
- $test->assertEquals('/bar/foo', $node->getPath());
- $hooksRun++;
- };
-
- /**
- * @param File $node
- */
- $postListener = function ($node) use (&$test, &$hooksRun): void {
- $test->assertEquals('foo', $node->getInternalPath());
- $test->assertEquals('/bar/foo', $node->getPath());
- $hooksRun++;
- };
-
- $root = new Root(
- $this->manager,
- $this->view,
- $this->user,
- $this->userMountCache,
- $this->logger,
- $this->userManager,
- $this->eventDispatcher,
- $this->cacheFactory,
- $this->createMock(IAppConfig::class),
- );
- $root->listen('\OC\Files', 'preTouch', $preListener);
- $root->listen('\OC\Files', 'postTouch', $postListener);
-
- $this->view->expects($this->once())
- ->method('touch')
- ->with('/bar/foo', 100)
- ->willReturn(true);
-
- $this->view->expects($this->any())
- ->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL], 'foo'));
-
- $node = $this->createTestNode($root, $this->view, '/bar/foo');
- $node->touch(100);
- $this->assertEquals(2, $hooksRun);
- }
-
- public function testTouchNotPermitted(): void {
- $this->expectException(NotPermittedException::class);
-
- $this->root->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
-
- $this->view->expects($this->any())
- ->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ]));
-
- $node = $this->createTestNode($this->root, $this->view, '/bar/foo');
- $node->touch(100);
- }
-
- public function testInvalidPath(): void {
- $this->expectException(InvalidPathException::class);
-
- $node = $this->createTestNode($this->root, $this->view, '/../foo');
- $node->getFileInfo();
- }
-
- public function testCopySameStorage(): void {
- $this->view->expects($this->any())
- ->method('copy')
- ->with('/bar/foo', '/bar/asd')
- ->willReturn(true);
-
- $this->view->expects($this->any())
- ->method('getFileInfo')
- ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL, 'fileid' => 3]));
-
- $node = $this->createTestNode($this->root, $this->view, '/bar/foo');
- $parentNode = new Folder($this->root, $this->view, '/bar');
- $newNode = $this->createTestNode($this->root, $this->view, '/bar/asd');
-
- $this->root->method('get')
- ->willReturnMap([
- ['/bar/asd', $newNode],
- ['/bar', $parentNode]
- ]);
-
- $target = $node->copy('/bar/asd');
- $this->assertInstanceOf($this->getNodeClass(), $target);
- $this->assertEquals(3, $target->getId());
- }
-
- public function testCopyNotPermitted(): void {
- $this->expectException(NotPermittedException::class);
-
- /**
- * @var Storage|\PHPUnit\Framework\MockObject\MockObject $storage
- */
- $storage = $this->createMock('\OC\Files\Storage\Storage');
-
- $this->root->expects($this->never())
- ->method('getMount');
-
- $storage->expects($this->never())
- ->method('copy');
-
- $this->view->expects($this->any())
- ->method('getFileInfo')
- ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ, 'fileid' => 3]));
-
- $node = $this->createTestNode($this->root, $this->view, '/bar/foo');
- $parentNode = new Folder($this->root, $this->view, '/bar');
-
- $this->root->expects($this->once())
- ->method('get')
- ->willReturnMap([
- ['/bar', $parentNode]
- ]);
-
- $node->copy('/bar/asd');
- }
-
- public function testCopyNoParent(): void {
- $this->expectException(NotFoundException::class);
-
- $this->view->expects($this->never())
- ->method('copy');
-
- $node = $this->createTestNode($this->root, $this->view, '/bar/foo');
-
- $this->root->expects($this->once())
- ->method('get')
- ->with('/bar/asd')
- ->willThrowException(new NotFoundException());
-
- $node->copy('/bar/asd/foo');
- }
-
- public function testCopyParentIsFile(): void {
- $this->expectException(NotPermittedException::class);
-
- $this->view->expects($this->never())
- ->method('copy');
-
- $node = $this->createTestNode($this->root, $this->view, '/bar/foo');
- $parentNode = new File($this->root, $this->view, '/bar');
-
- $this->root->expects($this->once())
- ->method('get')
- ->willReturnMap([
- ['/bar', $parentNode]
- ]);
-
- $node->copy('/bar/asd');
- }
-
- public function testMoveSameStorage(): void {
- $this->view->expects($this->any())
- ->method('rename')
- ->with('/bar/foo', '/bar/asd')
- ->willReturn(true);
-
- $this->view->expects($this->any())
- ->method('getFileInfo')
- ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL, 'fileid' => 1]));
-
- $node = $this->createTestNode($this->root, $this->view, '/bar/foo');
- $parentNode = new Folder($this->root, $this->view, '/bar');
-
- $this->root->expects($this->any())
- ->method('get')
- ->willReturnMap([['/bar', $parentNode], ['/bar/asd', $node]]);
-
- $target = $node->move('/bar/asd');
- $this->assertInstanceOf($this->getNodeClass(), $target);
- $this->assertEquals(1, $target->getId());
- $this->assertEquals('/bar/asd', $node->getPath());
- }
-
- public static function moveOrCopyProvider(): array {
- return [
- ['move', 'rename', 'preRename', 'postRename'],
- ['copy', 'copy', 'preCopy', 'postCopy'],
- ];
- }
-
- /**
- * @param string $operationMethod
- * @param string $viewMethod
- * @param string $preHookName
- * @param string $postHookName
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('moveOrCopyProvider')]
- public function testMoveCopyHooks($operationMethod, $viewMethod, $preHookName, $postHookName): void {
- /** @var IRootFolder|\PHPUnit\Framework\MockObject\MockObject $root */
- $root = $this->getMockBuilder(Root::class)
- ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig])
- ->onlyMethods(['get'])
- ->getMock();
-
- $this->view->expects($this->any())
- ->method($viewMethod)
- ->with('/bar/foo', '/bar/asd')
- ->willReturn(true);
-
- $this->view->expects($this->any())
- ->method('getFileInfo')
- ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL, 'fileid' => 1]));
-
- /**
- * @var File|\PHPUnit\Framework\MockObject\MockObject $node
- */
- $node = $this->createTestNode($root, $this->view, '/bar/foo');
- $parentNode = new Folder($root, $this->view, '/bar');
- $targetTestNode = $this->createTestNode($root, $this->view, '/bar/asd');
-
- $root->expects($this->any())
- ->method('get')
- ->willReturnMap([['/bar', $parentNode], ['/bar/asd', $targetTestNode]]);
-
- $hooksRun = 0;
-
- $preListener = function (Node $sourceNode, Node $targetNode) use (&$hooksRun, $node): void {
- $this->assertSame($node, $sourceNode);
- $this->assertInstanceOf($this->getNodeClass(), $sourceNode);
- $this->assertInstanceOf($this->getNonExistingNodeClass(), $targetNode);
- $this->assertEquals('/bar/asd', $targetNode->getPath());
- $hooksRun++;
- };
-
- $postListener = function (Node $sourceNode, Node $targetNode) use (&$hooksRun, $node, $targetTestNode): void {
- $this->assertSame($node, $sourceNode);
- $this->assertNotSame($node, $targetNode);
- $this->assertSame($targetTestNode, $targetNode);
- $this->assertInstanceOf($this->getNodeClass(), $sourceNode);
- $this->assertInstanceOf($this->getNodeClass(), $targetNode);
- $hooksRun++;
- };
-
- $preWriteListener = function (Node $targetNode) use (&$hooksRun): void {
- $this->assertInstanceOf($this->getNonExistingNodeClass(), $targetNode);
- $this->assertEquals('/bar/asd', $targetNode->getPath());
- $hooksRun++;
- };
-
- $postWriteListener = function (Node $targetNode) use (&$hooksRun, $targetTestNode): void {
- $this->assertSame($targetTestNode, $targetNode);
- $hooksRun++;
- };
-
- $root->listen('\OC\Files', $preHookName, $preListener);
- $root->listen('\OC\Files', 'preWrite', $preWriteListener);
- $root->listen('\OC\Files', $postHookName, $postListener);
- $root->listen('\OC\Files', 'postWrite', $postWriteListener);
-
- $node->$operationMethod('/bar/asd');
-
- $this->assertEquals(4, $hooksRun);
- }
-
- public function testMoveNotPermitted(): void {
- $this->expectException(NotPermittedException::class);
-
- $this->view->expects($this->any())
- ->method('getFileInfo')
- ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_READ]));
-
- $this->view->expects($this->never())
- ->method('rename');
-
- $node = $this->createTestNode($this->root, $this->view, '/bar/foo');
- $parentNode = new Folder($this->root, $this->view, '/bar');
-
- $this->root->expects($this->once())
- ->method('get')
- ->with('/bar')
- ->willReturn($parentNode);
-
- $node->move('/bar/asd');
- }
-
- public function testMoveNoParent(): void {
- $this->expectException(NotFoundException::class);
-
- /**
- * @var Storage|\PHPUnit\Framework\MockObject\MockObject $storage
- */
- $storage = $this->createMock('\OC\Files\Storage\Storage');
-
- $storage->expects($this->never())
- ->method('rename');
-
- $node = $this->createTestNode($this->root, $this->view, '/bar/foo');
-
- $this->root->expects($this->once())
- ->method('get')
- ->with('/bar')
- ->willThrowException(new NotFoundException());
-
- $node->move('/bar/asd');
- }
-
- public function testMoveParentIsFile(): void {
- $this->expectException(NotPermittedException::class);
-
- $this->view->expects($this->never())
- ->method('rename');
-
- $node = $this->createTestNode($this->root, $this->view, '/bar/foo');
- $parentNode = new File($this->root, $this->view, '/bar');
-
- $this->root->expects($this->once())
- ->method('get')
- ->with('/bar')
- ->willReturn($parentNode);
-
- $node->move('/bar/asd');
- }
-
- public function testMoveFailed(): void {
- $this->expectException(NotPermittedException::class);
-
- $this->view->expects($this->any())
- ->method('rename')
- ->with('/bar/foo', '/bar/asd')
- ->willReturn(false);
-
- $this->view->expects($this->any())
- ->method('getFileInfo')
- ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL, 'fileid' => 1]));
-
- $node = $this->createTestNode($this->root, $this->view, '/bar/foo');
- $parentNode = new Folder($this->root, $this->view, '/bar');
-
- $this->root->expects($this->any())
- ->method('get')
- ->willReturnMap([['/bar', $parentNode], ['/bar/asd', $node]]);
-
- $node->move('/bar/asd');
- }
-
- public function testCopyFailed(): void {
- $this->expectException(NotPermittedException::class);
-
- $this->view->expects($this->any())
- ->method('copy')
- ->with('/bar/foo', '/bar/asd')
- ->willReturn(false);
-
- $this->view->expects($this->any())
- ->method('getFileInfo')
- ->willReturn($this->getFileInfo(['permissions' => Constants::PERMISSION_ALL, 'fileid' => 1]));
-
- $node = $this->createTestNode($this->root, $this->view, '/bar/foo');
- $parentNode = new Folder($this->root, $this->view, '/bar');
-
- $this->root->expects($this->any())
- ->method('get')
- ->willReturnMap([['/bar', $parentNode], ['/bar/asd', $node]]);
-
- $node->copy('/bar/asd');
- }
-}
--- a/tests/lib/Files/Node/RootTest.php
+++ /dev/null
@@ -1,252 +0,0 @@
-user = $this->createMock(IUser::class);
- $this->manager = $this->getMockBuilder(Manager::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->userMountCache = $this->getMockBuilder('\OCP\Files\Config\IUserMountCache')
- ->disableOriginalConstructor()
- ->getMock();
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->userManager = $this->createMock(IUserManager::class);
- $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
- $this->cacheFactory = $this->createMock(ICacheFactory::class);
- $this->cacheFactory->method('createLocal')
- ->willReturnCallback(function () {
- return new ArrayCache();
- });
- $this->appConfig = $this->createMock(IAppConfig::class);
- }
-
- /**
- * @return View&MockObject $view
- */
- protected function getRootViewMock() {
- $view = $this->createMock(View::class);
- $view->expects($this->any())
- ->method('getRoot')
- ->willReturn('');
- return $view;
- }
-
- protected function getFileInfo($data) {
- return new FileInfo('', null, '', $data, null);
- }
-
- public function testGet(): void {
- /**
- * @var Storage $storage
- */
- $storage = $this->getMockBuilder('\OC\Files\Storage\Storage')
- ->disableOriginalConstructor()
- ->getMock();
- $view = $this->getRootViewMock();
- $root = new Root(
- $this->manager,
- $view,
- $this->user,
- $this->userMountCache,
- $this->logger,
- $this->userManager,
- $this->eventDispatcher,
- $this->cacheFactory,
- $this->appConfig,
- );
-
- $view->expects($this->once())
- ->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn($this->getFileInfo(['fileid' => 10, 'path' => 'bar/foo', 'name', 'mimetype' => 'text/plain']));
-
- $root->mount($storage, '');
- $node = $root->get('/bar/foo');
- $this->assertEquals(10, $node->getId());
- $this->assertInstanceOf('\OC\Files\Node\File', $node);
- }
-
- public function testGetNotFound(): void {
- $this->expectException(NotFoundException::class);
-
- /**
- * @var Storage $storage
- */
- $storage = $this->getMockBuilder('\OC\Files\Storage\Storage')
- ->disableOriginalConstructor()
- ->getMock();
- $view = $this->getRootViewMock();
- $root = new Root(
- $this->manager,
- $view,
- $this->user,
- $this->userMountCache,
- $this->logger,
- $this->userManager,
- $this->eventDispatcher,
- $this->cacheFactory,
- $this->appConfig,
- );
-
- $view->expects($this->once())
- ->method('getFileInfo')
- ->with('/bar/foo')
- ->willReturn(false);
-
- $root->mount($storage, '');
- $root->get('/bar/foo');
- }
-
- public function testGetInvalidPath(): void {
- $this->expectException(NotPermittedException::class);
-
- $view = $this->getRootViewMock();
- $root = new Root(
- $this->manager,
- $view,
- $this->user,
- $this->userMountCache,
- $this->logger,
- $this->userManager,
- $this->eventDispatcher,
- $this->cacheFactory,
- $this->appConfig,
- );
-
- $root->get('/../foo');
- }
-
- public function testGetNoStorages(): void {
- $this->expectException(NotFoundException::class);
-
- $view = $this->getRootViewMock();
- $root = new Root(
- $this->manager,
- $view,
- $this->user,
- $this->userMountCache,
- $this->logger,
- $this->userManager,
- $this->eventDispatcher,
- $this->cacheFactory,
- $this->appConfig,
- );
-
- $root->get('/bar/foo');
- }
-
- public function testGetUserFolder(): void {
- $root = new Root(
- $this->manager,
- $this->getRootViewMock(),
- $this->user,
- $this->userMountCache,
- $this->logger,
- $this->userManager,
- $this->eventDispatcher,
- $this->cacheFactory,
- $this->appConfig,
- );
- $user = $this->createMock(IUser::class);
- $user
- ->expects($this->once())
- ->method('getUID')
- ->willReturn('MyUserId');
- $this->userManager
- ->expects($this->once())
- ->method('get')
- ->with('MyUserId')
- ->willReturn($user);
- /** @var CappedMemoryCache&MockObject $cappedMemoryCache */
- $cappedMemoryCache = $this->createMock(CappedMemoryCache::class);
- $cappedMemoryCache
- ->expects($this->once())
- ->method('hasKey')
- ->willReturn(true);
- $folder = $this->createMock(Folder::class);
- $cappedMemoryCache
- ->expects($this->once())
- ->method('get')
- ->with('MyUserId')
- ->willReturn($folder);
-
- $this->invokePrivate($root, 'userFolderCache', [$cappedMemoryCache]);
- $this->assertEquals($folder, $root->getUserFolder('MyUserId'));
- }
-
- public function testGetUserFolderWithNoUserObj(): void {
- $this->expectException(UserNotFoundException::class);
- $this->expectExceptionMessage('Backends provided no user object');
-
- $root = new Root(
- $this->createMock(Manager::class),
- $this->getRootViewMock(),
- null,
- $this->userMountCache,
- $this->logger,
- $this->userManager,
- $this->eventDispatcher,
- $this->cacheFactory,
- $this->appConfig,
- );
- $this->userManager
- ->expects($this->once())
- ->method('get')
- ->with('NotExistingUser')
- ->willReturn(null);
- $this->logger
- ->expects($this->once())
- ->method('error')
- ->with(
- 'Backends provided no user object for NotExistingUser',
- $this->anything()
- );
-
- $root->getUserFolder('NotExistingUser');
- }
-}
--- a/tests/lib/Files/ObjectStore/AzureTest.php
+++ /dev/null
@@ -1,30 +0,0 @@
-getSystemValue('objectstore');
- if (!is_array($config) || $config['class'] !== 'OC\\Files\\ObjectStore\\Azure') {
- $this->markTestSkipped('objectstore not configured for azure');
- }
-
- return new Azure($config['arguments']);
- }
-
- #[\Override]
- public function testFseekSize(): void {
- $this->markTestSkipped('azure does not support seeking at the moment');
- }
-}
--- a/tests/lib/Files/ObjectStore/FailDeleteObjectStore.php
+++ /dev/null
@@ -1,53 +0,0 @@
-objectStore->getStorageId();
- }
-
- #[\Override]
- public function readObject($urn) {
- return $this->objectStore->readObject($urn);
- }
-
- #[\Override]
- public function writeObject($urn, $stream, ?string $mimetype = null) {
- return $this->objectStore->writeObject($urn, $stream, $mimetype);
- }
-
- #[\Override]
- public function deleteObject($urn) {
- throw new \Exception();
- }
-
- #[\Override]
- public function objectExists($urn) {
- return $this->objectStore->objectExists($urn);
- }
-
- #[\Override]
- public function copyObject($from, $to) {
- $this->objectStore->copyObject($from, $to);
- }
-
- #[\Override]
- public function preSignedUrl(string $urn, \DateTimeInterface $expiration): ?string {
- return null;
- }
-}
--- a/tests/lib/Files/ObjectStore/FailWriteObjectStore.php
+++ /dev/null
@@ -1,54 +0,0 @@
-objectStore->getStorageId();
- }
-
- #[\Override]
- public function readObject($urn) {
- return $this->objectStore->readObject($urn);
- }
-
- #[\Override]
- public function writeObject($urn, $stream, ?string $mimetype = null) {
- // emulate a failed write that didn't throw an error
- return true;
- }
-
- #[\Override]
- public function deleteObject($urn) {
- $this->objectStore->deleteObject($urn);
- }
-
- #[\Override]
- public function objectExists($urn) {
- return $this->objectStore->objectExists($urn);
- }
-
- #[\Override]
- public function copyObject($from, $to) {
- $this->objectStore->copyObject($from, $to);
- }
-
- #[\Override]
- public function preSignedUrl(string $urn, \DateTimeInterface $expiration): ?string {
- return null;
- }
-}
--- a/tests/lib/Files/ObjectStore/LocalTest.php
+++ /dev/null
@@ -1,25 +0,0 @@
-user = $this->createMock(IUser::class);
- }
-
- public static function dataGetBucket(): array {
- return [
- ['user', 64, 0, '17'],
- ['user', 64, 64, '64'],
- ['USER', 64, 0, '0'],
- ['bc0e8b52-a66c-1035-90c6-d9663bda9e3f', 64, 0, '56'],
- ['user', 8, 0, '1'],
- ['user', 2, 0, '1'],
- ['USER', 2, 0, '0'],
- ['user', 128, 64, '81'],
- ];
- }
-
- /**
- * @param string $username
- * @param int $numBuckets
- * @param string $expectedBucket
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetBucket')]
- public function testGetBucket($username, $numBuckets, $bucketShift, $expectedBucket): void {
- $mapper = new Mapper($this->user, ['arguments' => ['min_bucket' => $bucketShift]]);
- $this->user->method('getUID')
- ->willReturn($username);
-
- $result = $mapper->getBucket($numBuckets);
- $this->assertEquals($expectedBucket, $result);
- }
-}
--- a/tests/lib/Files/ObjectStore/ObjectStoreScannerTest.php
+++ /dev/null
@@ -1,78 +0,0 @@
-storage = new Temporary([]);
- $this->cache = $this->storage->getCache();
- $this->scanner = new ObjectStoreScanner($this->storage);
- $this->realScanner = new Scanner($this->storage);
- }
-
- public function testFile(): void {
- $data = "dummy file data\n";
- $this->storage->file_put_contents('foo.txt', $data);
-
- $this->assertNull($this->scanner->scanFile('foo.txt'),
- 'Asserting that no error occurred while scanFile()'
- );
- }
-
- private function fillTestFolders() {
- $textData = "dummy file data\n";
- $imgData = file_get_contents(\OC::$SERVERROOT . '/core/img/logo/logo.png');
- $this->storage->mkdir('folder');
- $this->storage->file_put_contents('foo.txt', $textData);
- $this->storage->file_put_contents('foo.png', $imgData);
- $this->storage->file_put_contents('folder/bar.txt', $textData);
- }
-
- public function testFolder(): void {
- $this->fillTestFolders();
-
- $this->assertNull(
- $this->scanner->scan(''),
- 'Asserting that no error occurred while scan()'
- );
- }
-
- public function testBackgroundScan(): void {
- $this->fillTestFolders();
- $this->storage->mkdir('folder2');
- $this->storage->file_put_contents('folder2/bar.txt', 'foobar');
-
- $this->realScanner->scan('');
-
- $this->assertEquals(6, $this->cache->get('folder2')->getSize());
-
- $this->cache->put('folder2', ['size' => -1]);
-
- $this->assertEquals(-1, $this->cache->get('folder2')->getSize());
-
- $this->scanner->backgroundScan();
-
- $this->assertEquals(6, $this->cache->get('folder2')->getSize());
- }
-}
--- a/tests/lib/Files/ObjectStore/ObjectStoreStorageOverwrite.php
+++ /dev/null
@@ -1,30 +0,0 @@
-objectStore = $objectStore;
- }
-
- #[\Override]
- public function getObjectStore(): IObjectStore {
- return $this->objectStore;
- }
-
- public function setValidateWrites(bool $validate): void {
- $this->validateWrites = $validate;
- }
-}
--- a/tests/lib/Files/ObjectStore/ObjectStoreStorageTest.php
+++ /dev/null
@@ -1,264 +0,0 @@
-objectStorage = new StorageObjectStore($baseStorage);
- $config['objectstore'] = $this->objectStorage;
- $this->instance = new ObjectStoreStorageOverwrite($config);
- }
-
- #[\Override]
- protected function tearDown(): void {
- if (is_null($this->instance)) {
- return;
- }
- $this->instance->getCache()->clear();
-
- parent::tearDown();
- }
-
- #[\Override]
- public function testStat(): void {
- $textFile = \OC::$SERVERROOT . '/tests/data/lorem.txt';
- $ctimeStart = time();
- $this->instance->file_put_contents('/lorem.txt', file_get_contents($textFile));
- $this->assertTrue($this->instance->isReadable('/lorem.txt'));
- $ctimeEnd = time();
- $mTime = $this->instance->filemtime('/lorem.txt');
-
- // check that ($ctimeStart - 5) <= $mTime <= ($ctimeEnd + 1)
- $this->assertGreaterThanOrEqual(($ctimeStart - 5), $mTime);
- $this->assertLessThanOrEqual(($ctimeEnd + 1), $mTime);
- $this->assertEquals(filesize($textFile), $this->instance->filesize('/lorem.txt'));
-
- $stat = $this->instance->stat('/lorem.txt');
- //only size and mtime are required in the result
- $this->assertEquals($stat['size'], $this->instance->filesize('/lorem.txt'));
- $this->assertEquals($stat['mtime'], $mTime);
-
- if ($this->instance->touch('/lorem.txt', 100) !== false) {
- $mTime = $this->instance->filemtime('/lorem.txt');
- $this->assertEquals($mTime, 100);
- }
- }
-
- #[\Override]
- public function testCheckUpdate(): void {
- $this->markTestSkipped('Detecting external changes is not supported on object storages');
- }
-
- #[\Override]
- #[\PHPUnit\Framework\Attributes\DataProvider('copyAndMoveProvider')]
- public function testMove($source, $target): void {
- $this->initSourceAndTarget($source);
- $sourceId = $this->instance->getCache()->getId(ltrim($source, '/'));
- $this->assertNotEquals(-1, $sourceId);
-
- $this->instance->rename($source, $target);
-
- $this->assertTrue($this->instance->file_exists($target), $target . ' was not created');
- $this->assertFalse($this->instance->file_exists($source), $source . ' still exists');
- $this->assertSameAsLorem($target);
-
- $targetId = $this->instance->getCache()->getId(ltrim($target, '/'));
- $this->assertSame($sourceId, $targetId, 'fileid must be stable on move or shares will break');
- }
-
- #[\Override]
- public function testRenameDirectory(): void {
- $this->instance->mkdir('source');
- $this->instance->file_put_contents('source/test1.txt', 'foo');
- $this->instance->file_put_contents('source/test2.txt', 'qwerty');
- $this->instance->mkdir('source/subfolder');
- $this->instance->file_put_contents('source/subfolder/test.txt', 'bar');
- $sourceId = $this->instance->getCache()->getId('source');
- $this->assertNotEquals(-1, $sourceId);
- $this->instance->rename('source', 'target');
-
- $this->assertFalse($this->instance->file_exists('source'));
- $this->assertFalse($this->instance->file_exists('source/test1.txt'));
- $this->assertFalse($this->instance->file_exists('source/test2.txt'));
- $this->assertFalse($this->instance->file_exists('source/subfolder'));
- $this->assertFalse($this->instance->file_exists('source/subfolder/test.txt'));
-
- $this->assertTrue($this->instance->file_exists('target'));
- $this->assertTrue($this->instance->file_exists('target/test1.txt'));
- $this->assertTrue($this->instance->file_exists('target/test2.txt'));
- $this->assertTrue($this->instance->file_exists('target/subfolder'));
- $this->assertTrue($this->instance->file_exists('target/subfolder/test.txt'));
-
- $this->assertEquals('foo', $this->instance->file_get_contents('target/test1.txt'));
- $this->assertEquals('qwerty', $this->instance->file_get_contents('target/test2.txt'));
- $this->assertEquals('bar', $this->instance->file_get_contents('target/subfolder/test.txt'));
- $targetId = $this->instance->getCache()->getId('target');
- $this->assertSame($sourceId, $targetId, 'fileid must be stable on move or shares will break');
- }
-
- #[\Override]
- public function testRenameOverWriteDirectory(): void {
- $this->instance->mkdir('source');
- $this->instance->file_put_contents('source/test1.txt', 'foo');
- $sourceId = $this->instance->getCache()->getId('source');
- $this->assertNotEquals(-1, $sourceId);
-
- $this->instance->mkdir('target');
- $this->instance->file_put_contents('target/test1.txt', 'bar');
- $this->instance->file_put_contents('target/test2.txt', 'bar');
-
- $this->instance->rename('source', 'target');
-
- $this->assertFalse($this->instance->file_exists('source'));
- $this->assertFalse($this->instance->file_exists('source/test1.txt'));
- $this->assertFalse($this->instance->file_exists('target/test2.txt'));
- $this->assertEquals('foo', $this->instance->file_get_contents('target/test1.txt'));
- $targetId = $this->instance->getCache()->getId('target');
- $this->assertSame($sourceId, $targetId, 'fileid must be stable on move or shares will break');
- }
-
- #[\Override]
- public function testRenameOverWriteDirectoryOverFile(): void {
- $this->instance->mkdir('source');
- $this->instance->file_put_contents('source/test1.txt', 'foo');
- $sourceId = $this->instance->getCache()->getId('source');
- $this->assertNotEquals(-1, $sourceId);
-
- $this->instance->file_put_contents('target', 'bar');
-
- $this->instance->rename('source', 'target');
-
- $this->assertFalse($this->instance->file_exists('source'));
- $this->assertFalse($this->instance->file_exists('source/test1.txt'));
- $this->assertEquals('foo', $this->instance->file_get_contents('target/test1.txt'));
- $targetId = $this->instance->getCache()->getId('target');
- $this->assertSame($sourceId, $targetId, 'fileid must be stable on move or shares will break');
- }
-
- public function testWriteObjectSilentFailure(): void {
- $objectStore = $this->instance->getObjectStore();
- $this->instance->setObjectStore(new FailWriteObjectStore($objectStore));
-
- try {
- $this->instance->file_put_contents('test.txt', 'foo');
- $this->fail('expected exception');
- } catch (\Exception $e) {
- $this->assertStringStartsWith('Object not found after writing', $e->getMessage());
- }
- $this->assertFalse($this->instance->file_exists('test.txt'));
- }
-
- public function testWriteObjectSilentFailureNoCheck(): void {
- $objectStore = $this->instance->getObjectStore();
- $this->instance->setObjectStore(new FailWriteObjectStore($objectStore));
- $this->instance->setValidateWrites(false);
-
- $this->instance->file_put_contents('test.txt', 'foo');
- $this->assertTrue($this->instance->file_exists('test.txt'));
- }
-
- public function testDeleteObjectFailureKeepCache(): void {
- $objectStore = $this->instance->getObjectStore();
- $this->instance->setObjectStore(new FailDeleteObjectStore($objectStore));
- $cache = $this->instance->getCache();
-
- $this->instance->file_put_contents('test.txt', 'foo');
-
- $this->assertTrue($cache->inCache('test.txt'));
-
- $this->assertFalse($this->instance->unlink('test.txt'));
-
- $this->assertTrue($cache->inCache('test.txt'));
-
- $this->instance->mkdir('foo');
- $this->instance->file_put_contents('foo/test.txt', 'foo');
-
- $this->assertTrue($cache->inCache('foo'));
- $this->assertTrue($cache->inCache('foo/test.txt'));
-
- $this->instance->rmdir('foo');
-
- $this->assertTrue($cache->inCache('foo'));
- $this->assertTrue($cache->inCache('foo/test.txt'));
- }
-
- public function testCopyBetweenJails(): void {
- $this->instance->mkdir('a');
- $this->instance->mkdir('b');
- $jailA = new Jail([
- 'storage' => $this->instance,
- 'root' => 'a'
- ]);
- $jailB = new Jail([
- 'storage' => $this->instance,
- 'root' => 'b'
- ]);
- $jailA->mkdir('sub');
- $jailA->file_put_contents('1.txt', '1');
- $jailA->file_put_contents('sub/2.txt', '2');
- $jailA->file_put_contents('sub/3.txt', '3');
-
- $jailB->copyFromStorage($jailA, '', 'target');
-
- $this->assertEquals('1', $this->instance->file_get_contents('b/target/1.txt'));
- $this->assertEquals('2', $this->instance->file_get_contents('b/target/sub/2.txt'));
- $this->assertEquals('3', $this->instance->file_get_contents('b/target/sub/3.txt'));
- }
-
- public function testCopyGrantsPermissions(): void {
- $config['objectstore'] = $this->objectStorage;
- $instance = new ObjectStoreStorageOverwrite($config);
-
- $cache = $instance->getCache();
-
- $instance->file_put_contents('test.txt', 'foo');
- $this->assertTrue($cache->inCache('test.txt'));
-
- $cache->update($cache->getId('test.txt'), ['permissions' => Constants::PERMISSION_READ]);
- $this->assertEquals(Constants::PERMISSION_READ, $instance->getPermissions('test.txt'));
-
- $this->assertTrue($instance->copy('test.txt', 'new.txt'));
-
- $this->assertTrue($cache->inCache('new.txt'));
- $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE, $instance->getPermissions('new.txt'));
- }
-
- public function testCopyFolderSize(): void {
- $cache = $this->instance->getCache();
-
- $this->instance->mkdir('source');
- $this->instance->file_put_contents('source/test.txt', 'foo');
- $this->instance->getUpdater()->update('source/test.txt');
- $this->assertEquals(3, $cache->get('source')->getSize());
-
- $this->assertTrue($this->instance->copy('source', 'target'));
-
- $this->assertEquals(3, $cache->get('target')->getSize());
- }
-}
--- a/tests/lib/Files/ObjectStore/ObjectStoreStoragesDifferentBucketTest.php
+++ /dev/null
@@ -1,44 +0,0 @@
-objectStore1 = new StorageObjectStore($baseStorage1);
- $config['objectstore'] = $this->objectStore1;
- $this->storage1 = new ObjectStoreStorageOverwrite($config);
-
- $baseStorage2 = new Temporary();
- $this->objectStore2 = new StorageObjectStore($baseStorage2);
- $config['objectstore'] = $this->objectStore2;
- $this->storage2 = new ObjectStoreStorageOverwrite($config);
- }
-}
--- a/tests/lib/Files/ObjectStore/ObjectStoreStoragesSameBucketTest.php
+++ /dev/null
@@ -1,36 +0,0 @@
-objectStore = new StorageObjectStore($baseStorage);
- $config['objectstore'] = $this->objectStore;
- // storage1 and storage2 share the same object store.
- $this->storage1 = new ObjectStoreStorageOverwrite($config);
- $this->storage2 = new ObjectStoreStorageOverwrite($config);
- }
-}
--- a/tests/lib/Files/ObjectStore/ObjectStoreTestCase.php
+++ /dev/null
@@ -1,186 +0,0 @@
-cleanup[] = $urn;
- }
-
- #[\Override]
- public function setUp(): void {
- parent::setUp();
-
- $this->instance = $this->getInstance();
- }
-
- #[\Override]
- public function tearDown(): void {
- if ($this->instance) {
- foreach ($this->cleanup as $urn) {
- $this->instance->deleteObject($urn);
- }
- }
-
- parent::tearDown();
- }
-
- protected function stringToStream($data) {
- $stream = fopen('php://temp', 'w+');
- fwrite($stream, $data);
- rewind($stream);
- return $stream;
- }
-
- public function testWriteRead(): void {
- $stream = $this->stringToStream('foobar');
-
- $instance = $this->getInstance();
-
- $instance->writeObject('1', $stream);
-
- $result = $instance->readObject('1');
- $instance->deleteObject('1');
-
- $this->assertEquals('foobar', stream_get_contents($result));
- }
-
- protected function assertOnlyExpectedWarnings(array $warnings): void {
- $onlyFopenWarnings = array_reduce(
- $warnings,
- fn (bool $ok, string $warning) => $ok && str_starts_with($warning, 'fopen('),
- true,
- );
- $this->assertTrue($onlyFopenWarnings);
- }
-
- public function testDelete(): void {
- $stream = $this->stringToStream('foobar');
-
- $instance = $this->getInstance();
-
- $instance->writeObject('2', $stream);
-
- $instance->deleteObject('2');
-
- $warnings = [];
- try {
- set_error_handler(
- function (int $errno, string $errstr) use (&$warnings): void {
- if ($errno === E_DEPRECATED || $errno === E_USER_DEPRECATED) {
- return;
- }
- $warnings[] = $errstr;
- },
- );
- // to to read to verify that the object no longer exists
- $instance->readObject('2');
- $this->fail();
- } catch (\Exception $e) {
- $this->assertOnlyExpectedWarnings($warnings);
- } finally {
- restore_error_handler();
- }
- }
-
- public function testReadNonExisting(): void {
- $instance = $this->getInstance();
-
- $warnings = [];
- try {
- set_error_handler(
- function (int $errno, string $errstr) use (&$warnings): void {
- if ($errno === E_DEPRECATED || $errno === E_USER_DEPRECATED) {
- return;
- }
- $warnings[] = $errstr;
- },
- );
- $instance->readObject('non-existing');
- $this->fail();
- } catch (\Exception $e) {
- $this->assertOnlyExpectedWarnings($warnings);
- } finally {
- restore_error_handler();
- }
- }
-
- public function testDeleteNonExisting(): void {
- $instance = $this->getInstance();
-
- try {
- $instance->deleteObject('non-existing');
- $this->fail();
- } catch (\Exception $e) {
- // dummy assert to keep phpunit happy
- $this->assertEquals(1, 1);
- }
- }
-
- public function testExists(): void {
- $stream = $this->stringToStream('foobar');
-
- $instance = $this->getInstance();
- $this->assertFalse($instance->objectExists('2'));
-
- $instance->writeObject('2', $stream);
-
- $this->assertTrue($instance->objectExists('2'));
-
- $instance->deleteObject('2');
-
- $this->assertFalse($instance->objectExists('2'));
- }
-
- public function testCopy(): void {
- $this->cleanupAfter('source');
- $this->cleanupAfter('target');
-
- $stream = $this->stringToStream('foobar');
-
- $instance = $this->getInstance();
-
- $instance->writeObject('source', $stream);
-
- $this->assertFalse($instance->objectExists('target'));
-
- $instance->copyObject('source', 'target');
-
- $this->assertTrue($instance->objectExists('target'));
-
- $this->assertEquals('foobar', stream_get_contents($instance->readObject('target')));
- }
-
- public function testFseekSize(): void {
- $instance = $this->getInstance();
-
- $textFile = \OC::$SERVERROOT . '/tests/data/lorem.txt';
- $size = filesize($textFile);
- $instance->writeObject('source', fopen($textFile, 'r'));
-
- $fh = $instance->readObject('source');
-
- fseek($fh, 0, SEEK_END);
- $pos = ftell($fh);
-
- $this->assertEquals($size, $pos);
- }
-}
--- a/tests/lib/Files/ObjectStore/PrimaryObjectStoreConfigTest.php
+++ /dev/null
@@ -1,296 +0,0 @@
-
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-namespace lib\Files\ObjectStore;
-
-use OC\Files\ObjectStore\PrimaryObjectStoreConfig;
-use OC\Files\ObjectStore\StorageObjectStore;
-use OCP\App\IAppManager;
-use OCP\IConfig;
-use OCP\IUser;
-use PHPUnit\Framework\MockObject\MockObject;
-use Test\TestCase;
-
-class PrimaryObjectStoreConfigTest extends TestCase {
- private array $systemConfig = [];
- private array $userConfig = [];
- private IConfig&MockObject $config;
- private IAppManager&MockObject $appManager;
- private PrimaryObjectStoreConfig $objectStoreConfig;
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- $this->systemConfig = [];
- $this->config = $this->createMock(IConfig::class);
- $this->appManager = $this->createMock(IAppManager::class);
- $this->config->method('getSystemValue')
- ->willReturnCallback(function ($key, $default = '') {
- if (isset($this->systemConfig[$key])) {
- return $this->systemConfig[$key];
- } else {
- return $default;
- }
- });
- $this->config->method('getUserValue')
- ->willReturnCallback(function ($userId, $appName, $key, $default = '') {
- if (isset($this->userConfig[$userId][$appName][$key])) {
- return $this->userConfig[$userId][$appName][$key];
- } else {
- return $default;
- }
- });
- $this->config->method('setUserValue')
- ->willReturnCallback(function ($userId, $appName, $key, $value): void {
- $this->userConfig[$userId][$appName][$key] = $value;
- });
-
- $this->objectStoreConfig = new PrimaryObjectStoreConfig($this->config, $this->appManager);
- }
-
- private function getUser(string $uid): IUser {
- $user = $this->createMock(IUser::class);
- $user->method('getUID')
- ->willReturn($uid);
- return $user;
- }
-
- private function setConfig(string $key, $value) {
- $this->systemConfig[$key] = $value;
- }
-
- public function testNewUserGetsDefault() {
- $this->setConfig('objectstore', [
- 'default' => 'server1',
- 'server1' => [
- 'class' => StorageObjectStore::class,
- 'arguments' => [
- 'host' => 'server1',
- ],
- ],
- ]);
-
- $result = $this->objectStoreConfig->getObjectStoreConfigForUser($this->getUser('test'));
- $this->assertEquals('server1', $result['arguments']['host']);
-
- $this->assertEquals('server1', $this->config->getUserValue('test', 'homeobjectstore', 'objectstore', null));
- }
-
- public function testExistingUserKeepsStorage() {
- // setup user with `server1` as storage
- $this->testNewUserGetsDefault();
-
- $this->setConfig('objectstore', [
- 'default' => 'server2',
- 'server1' => [
- 'class' => StorageObjectStore::class,
- 'arguments' => [
- 'host' => 'server1',
- 'bucket' => '1',
- ],
- ],
- 'server2' => [
- 'class' => StorageObjectStore::class,
- 'arguments' => [
- 'host' => 'server2',
- 'bucket' => '2',
- ],
- ],
- ]);
-
- $result = $this->objectStoreConfig->getObjectStoreConfigForUser($this->getUser('test'));
- $this->assertEquals('server1', $result['arguments']['host']);
-
- $this->assertEquals('server1', $this->config->getUserValue('test', 'homeobjectstore', 'objectstore', null));
-
- $result = $this->objectStoreConfig->getObjectStoreConfigForUser($this->getUser('other-user'));
- $this->assertEquals('server2', $result['arguments']['host']);
- }
-
- public function testNestedAliases() {
- $this->setConfig('objectstore', [
- 'default' => 'a1',
- 'a1' => 'a2',
- 'a2' => 'server1',
- 'server1' => [
- 'class' => StorageObjectStore::class,
- 'arguments' => [
- 'host' => 'server1',
- 'bucket' => '1',
- ],
- ],
- ]);
- $this->assertEquals('server1', $this->objectStoreConfig->resolveAlias('default'));
- }
-
- public function testMultibucketChangedConfig() {
- $this->setConfig('objectstore', [
- 'default' => 'server1',
- 'server1' => [
- 'class' => StorageObjectStore::class,
- 'arguments' => [
- 'host' => 'server1',
- 'multibucket' => true,
- 'num_buckets' => 8,
- 'bucket' => 'bucket1-'
- ],
- ],
- ]);
-
- $result = $this->objectStoreConfig->getObjectStoreConfigForUser($this->getUser('test'));
- $this->assertEquals('server1', $result['arguments']['host']);
- $this->assertEquals('bucket1-7', $result['arguments']['bucket']);
-
- $this->setConfig('objectstore', [
- 'default' => 'server1',
- 'server1' => [
- 'class' => StorageObjectStore::class,
- 'arguments' => [
- 'host' => 'server1',
- 'multibucket' => true,
- 'num_buckets' => 64,
- 'bucket' => 'bucket1-'
- ],
- ],
- ]);
-
- $result = $this->objectStoreConfig->getObjectStoreConfigForUser($this->getUser('test'));
- $this->assertEquals('server1', $result['arguments']['host']);
- $this->assertEquals('bucket1-7', $result['arguments']['bucket']);
-
- $result = $this->objectStoreConfig->getObjectStoreConfigForUser($this->getUser('test-foo'));
- $this->assertEquals('server1', $result['arguments']['host']);
- $this->assertEquals('bucket1-40', $result['arguments']['bucket']);
-
- $this->setConfig('objectstore', [
- 'default' => 'server2',
- 'server1' => [
- 'class' => StorageObjectStore::class,
- 'arguments' => [
- 'host' => 'server1',
- 'multibucket' => true,
- 'num_buckets' => 64,
- 'bucket' => 'bucket1-'
- ],
- ],
- 'server2' => [
- 'class' => StorageObjectStore::class,
- 'arguments' => [
- 'host' => 'server2',
- 'multibucket' => true,
- 'num_buckets' => 16,
- 'bucket' => 'bucket2-'
- ],
- ],
- ]);
-
- $result = $this->objectStoreConfig->getObjectStoreConfigForUser($this->getUser('test'));
- $this->assertEquals('server1', $result['arguments']['host']);
- $this->assertEquals('bucket1-7', $result['arguments']['bucket']);
-
- $result = $this->objectStoreConfig->getObjectStoreConfigForUser($this->getUser('test-bar'));
- $this->assertEquals('server2', $result['arguments']['host']);
- $this->assertEquals('bucket2-4', $result['arguments']['bucket']);
- }
-
- public function testMultibucketOldConfig() {
- $this->setConfig('objectstore_multibucket', [
- 'class' => StorageObjectStore::class,
- 'arguments' => [
- 'host' => 'server1',
- 'multibucket' => true,
- 'num_buckets' => 8,
- 'bucket' => 'bucket-'
- ],
- ]);
- $configs = $this->objectStoreConfig->getObjectStoreConfigs();
- $this->assertEquals([
- 'default' => 'server1',
- 'root' => 'server1',
- 'preview' => 'server1',
- 'server1' => [
- 'class' => StorageObjectStore::class,
- 'arguments' => [
- 'host' => 'server1',
- 'multibucket' => true,
- 'num_buckets' => 8,
- 'bucket' => 'bucket-'
- ],
- ],
- ], $configs);
- }
-
- public function testSingleObjectStore() {
- $this->setConfig('objectstore', [
- 'class' => StorageObjectStore::class,
- 'arguments' => [
- 'host' => 'server1',
- ],
- ]);
- $configs = $this->objectStoreConfig->getObjectStoreConfigs();
- $this->assertEquals([
- 'default' => 'server1',
- 'root' => 'server1',
- 'preview' => 'server1',
- 'server1' => [
- 'class' => StorageObjectStore::class,
- 'arguments' => [
- 'host' => 'server1',
- 'multibucket' => false,
- ],
- ],
- ], $configs);
- }
-
- public function testRoot() {
- $this->setConfig('objectstore', [
- 'default' => 'server1',
- 'server1' => [
- 'class' => StorageObjectStore::class,
- 'arguments' => [
- 'host' => 'server1',
- 'bucket' => '1',
- ],
- ],
- 'server2' => [
- 'class' => StorageObjectStore::class,
- 'arguments' => [
- 'host' => 'server2',
- 'bucket' => '2',
- ],
- ],
- ]);
-
- $result = $this->objectStoreConfig->getObjectStoreConfigForRoot();
- $this->assertEquals('server1', $result['arguments']['host']);
-
- $this->setConfig('objectstore', [
- 'default' => 'server1',
- 'root' => 'server2',
- 'preview' => 'server1',
- 'server1' => [
- 'class' => StorageObjectStore::class,
- 'arguments' => [
- 'host' => 'server1',
- 'bucket' => '1',
- ],
- ],
- 'server2' => [
- 'class' => StorageObjectStore::class,
- 'arguments' => [
- 'host' => 'server2',
- 'bucket' => '2',
- ],
- ],
- ]);
-
- $result = $this->objectStoreConfig->getObjectStoreConfigForRoot();
- $this->assertEquals('server2', $result['arguments']['host']);
- }
-}
--- a/tests/lib/Files/ObjectStore/S3ContentMd5MiddlewareTest.php
+++ /dev/null
@@ -1,175 +0,0 @@
-getUri()->getQuery() !== 'delete') {
- return $request;
- }
-
- if (!$request->hasHeader('Content-MD5')) {
- $body = $request->getBody();
- $contentMd5 = base64_encode(Utils::hash($body, 'md5', true));
- return $request->withHeader('Content-MD5', $contentMd5);
- }
-
- return $request;
- }
-
- /**
- * Test that Content-MD5 header is added to DeleteObjects requests
- */
- public function testContentMd5HeaderAddedToDeleteObjects(): void {
- $testBody = ' ';
- $request = new Request('POST', 'http://s3.example.com/bucket?delete', [], $testBody);
-
- // Calculate expected MD5
- $expectedMd5 = base64_encode(md5($testBody, true));
-
- // Apply middleware logic
- $resultRequest = $this->applyContentMd5Middleware($request);
-
- // Verify header was added
- $this->assertTrue($resultRequest->hasHeader('Content-MD5'));
- $this->assertEquals($expectedMd5, $resultRequest->getHeaderLine('Content-MD5'));
- }
-
- /**
- * Test that Content-MD5 header is NOT added to non-DeleteObjects requests
- */
- public function testContentMd5NotAddedToNonDeleteRequests(): void {
- $testCases = [
- 'GET request' => new Request('GET', 'http://s3.example.com/bucket/key'),
- 'PUT request' => new Request('PUT', 'http://s3.example.com/bucket/key'),
- 'HEAD request' => new Request('HEAD', 'http://s3.example.com/bucket/key'),
- 'POST with different query' => new Request('POST', 'http://s3.example.com/bucket?uploads'),
- ];
-
- foreach ($testCases as $label => $request) {
- $resultRequest = $this->applyContentMd5Middleware($request);
-
- // Verify header was NOT added for non-delete requests
- $this->assertFalse($resultRequest->hasHeader('Content-MD5'), "Content-MD5 should not be added for: $label");
- }
- }
-
- /**
- * Test that existing Content-MD5 header is preserved
- */
- public function testExistingContentMd5HeaderPreserved(): void {
- $testBody = 'test data';
- $existingMd5 = 'existing-md5-value';
- $request = new Request(
- 'POST',
- 'http://s3.example.com/bucket?delete',
- ['Content-MD5' => $existingMd5],
- $testBody
- );
-
- // Apply middleware logic
- $resultRequest = $this->applyContentMd5Middleware($request);
-
- // Verify existing header was preserved
- $this->assertTrue($resultRequest->hasHeader('Content-MD5'));
- $this->assertEquals($existingMd5, $resultRequest->getHeaderLine('Content-MD5'));
- }
-
- /**
- * Test MD5 calculation with various body sizes
- */
- public function testMd5CalculationWithVariousSizes(): void {
- $testBodies = [
- 'small' => 'x',
- 'medium' => str_repeat('y', 1000),
- 'large' => str_repeat('z', 10000),
- 'xml_payload' => ' ',
- ];
-
- foreach ($testBodies as $label => $body) {
- $request = new Request('POST', 'http://s3.example.com/bucket?delete', [], $body);
- $expectedMd5 = base64_encode(md5($body, true));
-
- $resultRequest = $this->applyContentMd5Middleware($request);
-
- $this->assertEquals(
- $expectedMd5,
- $resultRequest->getHeaderLine('Content-MD5'),
- "MD5 mismatch for $label body size"
- );
- }
- }
-
- /**
- * Test MD5 header format is base64-encoded
- */
- public function testMd5HeaderFormatIsBase64(): void {
- $testBody = 'test data for base64 validation';
- $request = new Request('POST', 'http://s3.example.com/bucket?delete', [], $testBody);
-
- $resultRequest = $this->applyContentMd5Middleware($request);
-
- $md5Header = $resultRequest->getHeaderLine('Content-MD5');
-
- // Verify it's a valid base64 string
- $this->assertNotEmpty($md5Header);
- $this->assertEquals($md5Header, base64_encode(base64_decode($md5Header, true)));
-
- // Verify MD5 is typically 24 chars when base64-encoded (16 bytes)
- $this->assertEquals(24, strlen($md5Header));
- }
-
- /**
- * Test edge case: Empty body in DeleteObjects request
- */
- public function testMd5CalculationWithEmptyBody(): void {
- $request = new Request('POST', 'http://s3.example.com/bucket?delete', [], '');
-
- $resultRequest = $this->applyContentMd5Middleware($request);
-
- // MD5 of empty string should still produce a valid header
- $this->assertTrue($resultRequest->hasHeader('Content-MD5'));
- $this->assertNotEmpty($resultRequest->getHeaderLine('Content-MD5'));
- }
-
- /**
- * Test that middleware is idempotent (doesn't double-hash)
- */
- public function testMiddlewareIsIdempotent(): void {
- $testBody = 'test data';
- $request = new Request('POST', 'http://s3.example.com/bucket?delete', [], $testBody);
-
- // Apply middleware twice
- $resultRequest1 = $this->applyContentMd5Middleware($request);
- $resultRequest2 = $this->applyContentMd5Middleware($resultRequest1);
-
- // Headers should be identical
- $this->assertEquals(
- $resultRequest1->getHeaderLine('Content-MD5'),
- $resultRequest2->getHeaderLine('Content-MD5'),
- 'Middleware should be idempotent'
- );
- }
-}
--- a/tests/lib/Files/ObjectStore/S3SSEKMSTest.php
+++ /dev/null
@@ -1,399 +0,0 @@
-getSystemValue('objectstore');
- if (!is_array($config) || $config['class'] !== S3::class) {
- self::markTestSkipped('S3 primary storage not configured');
- }
-
- $arguments = $config['arguments'] ?? [];
- if (empty($arguments['sse_kms_enabled'])) {
- self::markTestSkipped('SSE-KMS not enabled. Set sse_kms_enabled=true in objectstore config');
- }
- }
-
- #[\Override]
- protected function getInstance() {
- if (!isset($this->instance)) {
- $config = Server::get(IConfig::class)->getSystemValue('objectstore');
- $this->instance = new S3($config['arguments']);
- }
- return $this->instance;
- }
-
- /**
- * Test basic write and read with SSE-KMS
- */
- public function testWriteReadWithKMS(): void {
- $this->cleanupAfter('kms-test-write-read');
-
- $s3 = $this->getInstance();
- $data = 'Test data for SSE-KMS encryption';
- $stream = fopen('php://temp', 'r+');
- fwrite($stream, $data);
- rewind($stream);
-
- // Write with SSE-KMS
- $s3->writeObject('kms-test-write-read', $stream);
-
- // Read back
- $result = $s3->readObject('kms-test-write-read');
- $readData = stream_get_contents($result);
- fclose($result);
-
- $this->assertEquals($data, $readData, 'Data should be readable after SSE-KMS encryption');
- }
-
- /**
- * Test copy operation with SSE-KMS
- */
- public function testCopyWithKMS(): void {
- $this->cleanupAfter('kms-test-copy-source');
- $this->cleanupAfter('kms-test-copy-target');
-
- $s3 = $this->getInstance();
- $data = 'Test data for SSE-KMS copy operation';
- $stream = fopen('php://temp', 'r+');
- fwrite($stream, $data);
- rewind($stream);
-
- // Write source file
- $s3->writeObject('kms-test-copy-source', $stream);
-
- // Copy (should re-encrypt with same KMS key)
- $s3->copyObject('kms-test-copy-source', 'kms-test-copy-target');
-
- // Verify copy
- $this->assertTrue($s3->objectExists('kms-test-copy-target'), 'Copied object should exist');
-
- $result = $s3->readObject('kms-test-copy-target');
- $readData = stream_get_contents($result);
- fclose($result);
-
- $this->assertEquals($data, $readData, 'Copied data should match original');
- }
-
- /**
- * Test multipart upload with SSE-KMS
- */
- public function testMultipartUploadWithKMS(): void {
- $this->cleanupAfter('kms-test-multipart');
-
- $s3 = $this->getInstance();
-
- // Create 6MB data to trigger multipart
- $data = str_repeat('A', 6 * 1024 * 1024);
- $stream = fopen('php://temp', 'r+');
- fwrite($stream, $data);
- rewind($stream);
-
- // Write with multipart (forces multipart upload)
- $s3->writeObject('kms-test-multipart', $stream, 'application/octet-stream');
-
- // Verify
- $this->assertTrue($s3->objectExists('kms-test-multipart'), 'Multipart object should exist');
-
- // Read back first 1000 bytes to verify
- $result = $s3->readObject('kms-test-multipart');
- $readData = fread($result, 1000);
- fclose($result);
-
- $this->assertEquals(substr($data, 0, 1000), $readData, 'Multipart data should be readable');
- }
-
- /**
- * Data provider for various file sizes
- */
- public static function dataFileSizes(): array {
- return [
- '1KB' => [1024],
- '1MB' => [1024 * 1024],
- '10MB' => [10 * 1024 * 1024],
- ];
- }
-
- /**
- * Data provider for large file sizes to test multipart upload threshold behavior
- */
- public static function dataLargeFileSizes(): array {
- return [
- '50MB' => [50 * 1024 * 1024],
- '100MB' => [100 * 1024 * 1024],
- '150MB' => [150 * 1024 * 1024],
- ];
- }
-
- /**
- * Test various file sizes with SSE-KMS
- *
- * @dataProvider dataFileSizes
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataFileSizes')]
- public function testFileSizesWithKMS(int $size): void {
- $urn = 'kms-test-size-' . ($size / 1024) . 'kb';
- $this->cleanupAfter($urn);
-
- $s3 = $this->getInstance();
- $data = str_repeat('X', $size);
- $stream = fopen('php://temp', 'r+');
- fwrite($stream, $data);
- rewind($stream);
-
- // Write
- $s3->writeObject($urn, $stream);
-
- // Verify object exists
- $this->assertTrue($s3->objectExists($urn), "Object should exist for size $size");
-
- // Read back and verify
- $result = $s3->readObject($urn);
- $readData = stream_get_contents($result);
- fclose($result);
-
- $this->assertEquals($size, strlen($readData), "Size mismatch for $size byte file");
- $this->assertEquals($data, $readData, "Content mismatch for $size byte file");
- }
-
- /**
- * Test that SSE-KMS metadata is set on objects
- */
- public function testKMSMetadataPresent(): void {
- $this->cleanupAfter('kms-test-metadata');
-
- $s3 = $this->getInstance();
- $data = 'Test KMS metadata';
- $stream = fopen('php://temp', 'r+');
- fwrite($stream, $data);
- rewind($stream);
-
- // Write with SSE-KMS
- $s3->writeObject('kms-test-metadata', $stream);
-
- // Check metadata via headObject
- $result = $s3->getConnection()->headObject([
- 'Bucket' => $s3->getBucket(),
- 'Key' => 'kms-test-metadata',
- ]);
-
- // Verify SSE is KMS
- $this->assertEquals('aws:kms', $result->get('ServerSideEncryption'),
- 'Object should have SSE-KMS encryption');
-
- // If specific key configured, verify it's used
- $config = Server::get(IConfig::class)->getSystemValue('objectstore');
- if (!empty($config['arguments']['sse_kms_key_id'])) {
- $this->assertNotNull($result->get('SSEKMSKeyId'),
- 'KMS Key ID should be present when specific key configured');
- }
- }
-
- /**
- * Test zero-byte file with SSE-KMS
- *
- * Note: Zero-byte files are a known edge case with S3.
- * While they can be written, reading them back may fail due to
- * Range header issues with empty objects.
- */
- public function testZeroByteFileWithKMS(): void {
- $this->cleanupAfter('kms-test-zerobyte');
-
- $s3 = $this->getInstance();
- $stream = fopen('php://temp', 'r+');
- // Write nothing (zero bytes)
- rewind($stream);
-
- // Write zero-byte file
- $s3->writeObject('kms-test-zerobyte', $stream);
-
- // Verify exists
- $this->assertTrue($s3->objectExists('kms-test-zerobyte'), 'Zero-byte object should exist');
-
- // Verify via headObject instead of read (avoids Range header issue)
- $metadata = $s3->getConnection()->headObject([
- 'Bucket' => $s3->getBucket(),
- 'Key' => 'kms-test-zerobyte',
- ]);
-
- $this->assertEquals(0, $metadata->get('ContentLength'),
- 'Zero-byte file should have ContentLength of 0');
- $this->assertEquals('aws:kms', $metadata->get('ServerSideEncryption'),
- 'Zero-byte file should still have SSE-KMS encryption');
- }
-
- /**
- * Test large file sizes with SSE-KMS to verify multipart threshold behavior
- *
- * @dataProvider dataLargeFileSizes
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataLargeFileSizes')]
- #[\PHPUnit\Framework\Attributes\Group('SLOWDB')]
- public function testLargeFileSizesWithKMS(int $size): void {
- $urn = 'kms-test-large-size-' . ($size / 1024 / 1024) . 'mb';
- $this->cleanupAfter($urn);
-
- $s3 = $this->getInstance();
- $data = str_repeat('L', $size);
- $stream = fopen('php://temp', 'r+');
- fwrite($stream, $data);
- rewind($stream);
-
- // Write (should trigger multipart for files >= 100MB)
- $s3->writeObject($urn, $stream);
-
- // Verify object exists
- $this->assertTrue($s3->objectExists($urn), "Object should exist for size $size");
-
- // Verify metadata via headObject
- $metadata = $s3->getConnection()->headObject([
- 'Bucket' => $s3->getBucket(),
- 'Key' => $urn,
- ]);
-
- $this->assertEquals('aws:kms', $metadata->get('ServerSideEncryption'),
- "Object should have SSE-KMS encryption for size $size");
- $this->assertEquals($size, $metadata->get('ContentLength'),
- "Size should match for $size byte file");
- }
-
- /**
- * Test multipart copy operation with large files
- */
- #[\PHPUnit\Framework\Attributes\Group('SLOWDB')]
- public function testMultipartCopyWithKMS(): void {
- $this->cleanupAfter('kms-test-multipart-copy-source');
- $this->cleanupAfter('kms-test-multipart-copy-target');
-
- $s3 = $this->getInstance();
-
- // Create large file to trigger multipart copy (> copySizeLimit, default 5GB)
- // Use 10MB for test efficiency, but in production this would be > 5GB
- $size = 10 * 1024 * 1024;
- $data = str_repeat('C', $size);
- $stream = fopen('php://temp', 'r+');
- fwrite($stream, $data);
- rewind($stream);
-
- // Write source file
- $s3->writeObject('kms-test-multipart-copy-source', $stream);
-
- // Copy (should re-encrypt with same KMS key)
- $s3->copyObject('kms-test-multipart-copy-source', 'kms-test-multipart-copy-target');
-
- // Verify copy exists
- $this->assertTrue($s3->objectExists('kms-test-multipart-copy-target'), 'Copied object should exist');
-
- // Verify encryption on target
- $metadata = $s3->getConnection()->headObject([
- 'Bucket' => $s3->getBucket(),
- 'Key' => 'kms-test-multipart-copy-target',
- ]);
-
- $this->assertEquals('aws:kms', $metadata->get('ServerSideEncryption'),
- 'Copied object should have SSE-KMS encryption');
- }
-
- /**
- * Test delete operation with KMS-encrypted objects
- */
- public function testDeleteWithKMS(): void {
- $this->cleanupAfter('kms-test-delete');
-
- $s3 = $this->getInstance();
- $data = 'Test data for delete operation';
- $stream = fopen('php://temp', 'r+');
- fwrite($stream, $data);
- rewind($stream);
-
- // Write with SSE-KMS
- $s3->writeObject('kms-test-delete', $stream);
-
- // Verify exists
- $this->assertTrue($s3->objectExists('kms-test-delete'), 'Object should exist before delete');
-
- // Delete
- $s3->deleteObject('kms-test-delete');
-
- // Verify deleted
- $this->assertFalse($s3->objectExists('kms-test-delete'), 'Object should not exist after delete');
- }
-
- /**
- * Test overwriting existing KMS-encrypted objects
- */
- public function testOverwriteWithKMS(): void {
- $this->cleanupAfter('kms-test-overwrite');
-
- $s3 = $this->getInstance();
-
- // Write initial data
- $data1 = 'Initial data for overwrite test';
- $stream1 = fopen('php://temp', 'r+');
- fwrite($stream1, $data1);
- rewind($stream1);
- $s3->writeObject('kms-test-overwrite', $stream1);
-
- // Verify initial write
- $result1 = $s3->readObject('kms-test-overwrite');
- $readData1 = stream_get_contents($result1);
- fclose($result1);
- $this->assertEquals($data1, $readData1, 'Initial data should match');
-
- // Overwrite with new data
- $data2 = 'Overwritten data with different content';
- $stream2 = fopen('php://temp', 'r+');
- fwrite($stream2, $data2);
- rewind($stream2);
- $s3->writeObject('kms-test-overwrite', $stream2);
-
- // Verify overwrite
- $result2 = $s3->readObject('kms-test-overwrite');
- $readData2 = stream_get_contents($result2);
- fclose($result2);
- $this->assertEquals($data2, $readData2, 'Overwritten data should match');
-
- // Verify still encrypted with KMS
- $metadata = $s3->getConnection()->headObject([
- 'Bucket' => $s3->getBucket(),
- 'Key' => 'kms-test-overwrite',
- ]);
-
- $this->assertEquals('aws:kms', $metadata->get('ServerSideEncryption'),
- 'Overwritten object should still have SSE-KMS encryption');
- }
-}
--- a/tests/lib/Files/ObjectStore/S3Test.php
+++ /dev/null
@@ -1,193 +0,0 @@
-getConnection()->upload($this->bucket, $urn, $stream, 'private', [
- 'mup_threshold' => 1,
- ]);
- }
-}
-
-class NonSeekableStream extends Wrapper {
- public static function wrap($source) {
- $context = stream_context_create([
- 'nonseek' => [
- 'source' => $source,
- ],
- ]);
- return Wrapper::wrapSource($source, $context, 'nonseek', self::class);
- }
-
- #[\Override]
- public function dir_opendir($path, $options) {
- return false;
- }
-
- #[\Override]
- public function stream_open($path, $mode, $options, &$opened_path) {
- $this->loadContext('nonseek');
- return true;
- }
-
- #[\Override]
- public function stream_seek($offset, $whence = SEEK_SET) {
- return false;
- }
-}
-
-#[\PHPUnit\Framework\Attributes\Group('PRIMARY-s3')]
-class S3Test extends ObjectStoreTestCase {
- #[\Override]
- public function setUp(): void {
- parent::setUp();
- $s3 = $this->getInstance();
- $s3->deleteObject('multiparttest');
- }
-
- #[\Override]
- protected function getInstance() {
- $config = Server::get(IConfig::class)->getSystemValue('objectstore');
- if (!is_array($config) || $config['class'] !== S3::class) {
- $this->markTestSkipped('objectstore not configured for s3');
- }
-
- return new S3($config['arguments']);
- }
-
- public function testUploadNonSeekable(): void {
- $this->cleanupAfter('multiparttest');
-
- $s3 = $this->getInstance();
-
- $s3->writeObject('multiparttest', NonSeekableStream::wrap(fopen(__FILE__, 'r')));
-
- $result = $s3->readObject('multiparttest');
-
- $this->assertEquals(file_get_contents(__FILE__), stream_get_contents($result));
- }
-
- public function testSeek(): void {
- $this->cleanupAfter('seek');
-
- $data = file_get_contents(__FILE__);
-
- $instance = $this->getInstance();
- $instance->writeObject('seek', $this->stringToStream($data));
-
- $read = $instance->readObject('seek');
- $this->assertEquals(substr($data, 0, 100), fread($read, 100));
-
- fseek($read, 10);
- $this->assertEquals(substr($data, 10, 100), fread($read, 100));
-
- fseek($read, 100, SEEK_CUR);
- $this->assertEquals(substr($data, 210, 100), fread($read, 100));
- }
-
- public function assertNoUpload($objectUrn) {
- /** @var S3 */
- $s3 = $this->getInstance();
- $s3client = $s3->getConnection();
- $uploads = $s3client->listMultipartUploads([
- 'Bucket' => $s3->getBucket(),
- 'Prefix' => $objectUrn,
- ]);
- $this->assertArrayNotHasKey('Uploads', $uploads, 'Assert is not uploaded');
- }
-
- public function testEmptyUpload(): void {
- $s3 = $this->getInstance();
-
- $emptyStream = fopen('php://memory', 'r');
- fwrite($emptyStream, '');
-
- $warnings = [];
- set_error_handler(
- function (int $errno, string $errstr) use (&$warnings): void {
- if ($errno === E_DEPRECATED || $errno === E_USER_DEPRECATED) {
- return;
- }
- $warnings[] = $errstr;
- },
- );
-
- $s3->writeObject('emptystream', $emptyStream);
-
- $this->assertNoUpload('emptystream');
- $this->assertTrue($s3->objectExists('emptystream'), 'Object exists on S3');
-
- $thrown = false;
- try {
- self::assertFalse($s3->readObject('emptystream'), 'Reading empty stream object should return false');
- } catch (\Exception $e) {
- // An exception is expected here since 0 byte files are wrapped
- // to be read from an empty memory stream in the ObjectStoreStorage
- $thrown = true;
- }
- self::assertTrue($thrown, 'readObject with range requests are not expected to work on empty objects');
-
- $s3->deleteObject('emptystream');
- $this->assertOnlyExpectedWarnings($warnings);
- restore_error_handler();
- }
-
- /** File size to upload in bytes */
- public static function dataFileSizes(): array {
- return [
- [1000000], [2000000], [5242879], [5242880], [5242881], [10000000]
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataFileSizes')]
- public function testFileSizes($size): void {
- $this->cleanupAfter('testfilesizes');
- $s3 = $this->getInstance();
-
- $sourceStream = fopen('php://memory', 'wb+');
- $writeChunkSize = 1024;
- $chunk = str_repeat('A', $writeChunkSize);
- $remainingSize = $size;
-
- while ($remainingSize > 0) {
- $bytesToWrite = min($writeChunkSize, $remainingSize);
- fwrite($sourceStream, ($bytesToWrite === $writeChunkSize) ? $chunk : str_repeat('A', $bytesToWrite));
- $remainingSize -= $bytesToWrite;
- }
-
- rewind($sourceStream);
- $s3->writeObject('testfilesizes', $sourceStream);
-
- $this->assertNoUpload('testfilesizes');
- self::assertTrue($s3->objectExists('testfilesizes'), 'Object exists on S3');
-
- $result = $s3->readObject('testfilesizes');
-
- // compare first 100 bytes
- self::assertSame(str_repeat('A', 100), fread($result, 100), 'Compare first 100 bytes');
-
- // compare last 100 bytes
- self::assertSame(0, fseek($result, $size - 100), 'Seek to last 100 bytes succeeds');
- self::assertSame(str_repeat('A', 100), fread($result, 100), 'Compare last 100 bytes');
-
- // end of file reached
- self::assertSame(0, fseek($result, $size), 'Seek to EOF succeeds');
- self::assertSame($size, ftell($result), 'Pointer is at the end of file');
- self::assertSame('', fread($result, 1), 'Reading at end of file returns no bytes');
- self::assertTrue(feof($result), 'End of file reached after read attempt');
-
- $this->assertNoUpload('testfilesizes');
- }
-}
--- a/tests/lib/Files/ObjectStore/SwiftTest.php
+++ /dev/null
@@ -1,35 +0,0 @@
-getSystemValue('objectstore');
- if (!is_array($config) || $config['class'] !== 'OC\\Files\\ObjectStore\\Swift') {
- $this->markTestSkipped('objectstore not configured for swift');
- }
-
- return new Swift($config['arguments']);
- }
-
- #[\Override]
- public function testFseekSize(): void {
- $this->markTestSkipped('Swift does not support seeking at the moment');
- }
-}
--- a/tests/lib/Files/PathVerificationTest.php
+++ /dev/null
@@ -1,128 +0,0 @@
-view = new View();
- }
-
- public function testPathVerificationFileNameTooLong(): void {
- $this->expectException(InvalidPathException::class);
- $this->expectExceptionMessage('Filename is too long');
-
- $fileName = str_repeat('a', 500);
- $this->view->verifyPath('', $fileName);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providesEmptyFiles')]
- public function testPathVerificationEmptyFileName($fileName): void {
- $this->expectException(InvalidPathException::class);
- $this->expectExceptionMessage('Empty filename is not allowed');
-
- $this->view->verifyPath('', $fileName);
- }
-
- public static function providesEmptyFiles(): array {
- return [
- [''],
- [' '],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providesDotFiles')]
- public function testPathVerificationDotFiles($fileName): void {
- $this->expectException(InvalidPathException::class);
- $this->expectExceptionMessage('Filenames that begin with a dot are not allowed');
-
- $this->view->verifyPath('', $fileName);
- }
-
- public static function providesDotFiles(): array {
- return [
- ['.'],
- ['..'],
- [' .'],
- [' ..'],
- ['. '],
- ['.. '],
- [' . '],
- [' .. '],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providesAstralPlane')]
- public function testPathVerificationAstralPlane($fileName): void {
- $connection = Server::get(IDBConnection::class);
-
- if (!$connection->supports4ByteText()) {
- $this->expectException(InvalidPathException::class);
- $this->expectExceptionMessage('File name contains at least one invalid character');
- } else {
- $this->addToAssertionCount(1);
- }
-
- $this->view->verifyPath('', $fileName);
- }
-
- public static function providesAstralPlane(): array {
- return [
- // this is the monkey emoji - http://en.wikipedia.org/w/index.php?title=%F0%9F%90%B5&redirect=no
- ['🐵'],
- ['🐵.txt'],
- ['txt.💩'],
- ['💩🐵.txt'],
- ['💩🐵'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providesValidPosixPaths')]
- public function testPathVerificationValidPaths($fileName): void {
- $storage = new Local(['datadir' => '']);
-
- self::invokePrivate($storage, 'verifyPosixPath', [$fileName]);
- // nothing thrown
- $this->addToAssertionCount(1);
- }
-
- public static function providesValidPosixPaths(): array {
- return [
- ['simple'],
- ['simple.txt'],
- ['\''],
- ['`'],
- ['%'],
- ['()'],
- ['[]'],
- ['!'],
- ['$'],
- ['_'],
- ];
- }
-}
--- a/tests/lib/Files/Search/QueryOptimizer/CombinedTests.php
+++ /dev/null
@@ -1,232 +0,0 @@
-optimizer = new QueryOptimizer();
- }
-
- public function testBasicOrOfAnds(): void {
- $operator = new SearchBinaryOperator(
- ISearchBinaryOperator::OPERATOR_OR,
- [
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'asd'),
- ])
- ]
- );
- $this->assertEquals('((storage eq 1 and path eq "foo") or (storage eq 1 and path eq "bar") or (storage eq 1 and path eq "asd"))', $operator->__toString());
-
- $this->optimizer->processOperator($operator);
-
- $this->assertEquals('(storage eq 1 and path in ["foo","bar","asd"])', $operator->__toString());
- }
-
- public function testComplexSearchPattern1(): void {
- $operator = new SearchBinaryOperator(
- ISearchBinaryOperator::OPERATOR_OR,
- [
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 2),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', '201'),
- new SearchComparison(ISearchComparison::COMPARE_LIKE, 'path', '201/%'),
- ]),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 3),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', '301'),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 4),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', '401'),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 3),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', '302'),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 4),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', '402'),
- ]),
- ]
- );
- $this->assertEquals('((storage eq 1) or (storage eq 2 and (path eq "201" or path like "201\/%")) or (storage eq 3 and path eq "301") or (storage eq 4 and path eq "401") or (storage eq 3 and path eq "302") or (storage eq 4 and path eq "402"))', $operator->__toString());
-
- $this->optimizer->processOperator($operator);
-
- $this->assertEquals('(storage eq 1 or (storage eq 2 and (path eq "201" or path like "201\/%")) or (storage eq 3 and path in ["301","302"]) or (storage eq 4 and path in ["401","402"]))', $operator->__toString());
- }
-
- public function testComplexSearchPattern2(): void {
- $operator = new SearchBinaryOperator(
- ISearchBinaryOperator::OPERATOR_OR,
- [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 2),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', '201'),
- new SearchComparison(ISearchComparison::COMPARE_LIKE, 'path', '201/%'),
- ]),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 3),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', '301'),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 4),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', '401'),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 3),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', '302'),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 4),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', '402'),
- ]),
- ]
- );
- $this->assertEquals('(storage eq 1 or (storage eq 2 and (path eq "201" or path like "201\/%")) or (storage eq 3 and path eq "301") or (storage eq 4 and path eq "401") or (storage eq 3 and path eq "302") or (storage eq 4 and path eq "402"))', $operator->__toString());
-
- $this->optimizer->processOperator($operator);
-
- $this->assertEquals('(storage eq 1 or (storage eq 2 and (path eq "201" or path like "201\/%")) or (storage eq 3 and path in ["301","302"]) or (storage eq 4 and path in ["401","402"]))', $operator->__toString());
- }
-
- public function testApplySearchConstraints1(): void {
- $operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', 'image/png'),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', 'image/jpeg'),
- ]),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'files'),
- new SearchComparison(ISearchComparison::COMPARE_LIKE, 'path', 'files/%'),
- ]),
- ]),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 2),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 3),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'files/301'),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 3),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'files/302'),
- ]),
- ]),
- ]);
- $this->assertEquals('(((mimetype eq "image\/png" or mimetype eq "image\/jpeg")) and ((storage eq 1 and (path eq "files" or path like "files\/%")) or storage eq 2 or (storage eq 3 and path eq "files\/301") or (storage eq 3 and path eq "files\/302")))', $operator->__toString());
-
- $this->optimizer->processOperator($operator);
-
- $this->assertEquals('(mimetype in ["image\/png","image\/jpeg"] and ((storage eq 1 and (path eq "files" or path like "files\/%")) or storage eq 2 or (storage eq 3 and path in ["files\/301","files\/302"])))', $operator->__toString());
- }
-
- public function testApplySearchConstraints2(): void {
- $operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', 'image/png'),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', 'image/jpeg'),
- ]),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'files'),
- new SearchComparison(ISearchComparison::COMPARE_LIKE, 'path', 'files/%'),
- ]),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 2),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 3),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'files/301'),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 3),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'files/302'),
- ]),
- ]),
- ]);
- $this->assertEquals('(((mimetype eq "image\/png" or mimetype eq "image\/jpeg")) and ((storage eq 1 and (path eq "files" or path like "files\/%")) or (storage eq 2) or (storage eq 3 and path eq "files\/301") or (storage eq 3 and path eq "files\/302")))', $operator->__toString());
-
- $this->optimizer->processOperator($operator);
-
- $this->assertEquals('(mimetype in ["image\/png","image\/jpeg"] and ((storage eq 1 and (path eq "files" or path like "files\/%")) or storage eq 2 or (storage eq 3 and path in ["files\/301","files\/302"])))', $operator->__toString());
- }
-
- public function testNegatedGroupSurvivesTheFullPipeline(): void {
- $operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_NOT, [
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'),
- ]),
- ]);
-
- $this->optimizer->processOperator($operator);
-
- $this->assertEquals('((not storage eq 1) or (not path eq "foo"))', $operator->__toString());
- }
-
- public function testNegatedLargeInIsPushedDownAfterSplitting(): void {
- $ids = range(1, 1500);
- $operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_NOT, [
- new SearchComparison(ISearchComparison::COMPARE_IN, 'fileid', $ids),
- ]);
-
- $this->optimizer->processOperator($operator);
-
- // SplitLargeIn turns the single `in` into `or (in, in)`, which needs pushing back down
- // through the `not` a second time before it is something SearchBuilder can turn into SQL.
- $chunks = array_chunk($ids, IQueryBuilder::MAX_IN_PARAMETERS);
- $expected = '(' . implode(' and ', array_map(
- static fn (array $chunk): string => '(not fileid in ' . json_encode($chunk) . ')',
- $chunks,
- )) . ')';
- $this->assertEquals($expected, $operator->__toString());
- }
-}
--- a/tests/lib/Files/Search/QueryOptimizer/FlattenNestedBoolTest.php
+++ /dev/null
@@ -1,48 +0,0 @@
-optimizer = new FlattenNestedBool();
- $this->simplifier = new FlattenSingleArgumentBinaryOperation();
- }
-
- public function testOrs(): void {
- $operator = new SearchBinaryOperator(
- ISearchBinaryOperator::OPERATOR_OR,
- [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'asd'),
- ])
- ]
- );
- $this->assertEquals('(path eq "foo" or (path eq "bar" or path eq "asd"))', $operator->__toString());
-
- $this->optimizer->processOperator($operator);
- $this->simplifier->processOperator($operator);
-
- $this->assertEquals('(path eq "foo" or path eq "bar" or path eq "asd")', $operator->__toString());
- }
-}
--- a/tests/lib/Files/Search/QueryOptimizer/MergeDistributiveOperationsTest.php
+++ /dev/null
@@ -1,166 +0,0 @@
-optimizer = new MergeDistributiveOperations();
- $this->simplifier = new FlattenSingleArgumentBinaryOperation();
- }
-
- public function testBasicOrOfAnds(): void {
- $operator = new SearchBinaryOperator(
- ISearchBinaryOperator::OPERATOR_OR,
- [
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'asd'),
- ])
- ]
- );
- $this->assertEquals('((storage eq 1 and path eq "foo") or (storage eq 1 and path eq "bar") or (storage eq 1 and path eq "asd"))', $operator->__toString());
-
- $this->optimizer->processOperator($operator);
- $this->simplifier->processOperator($operator);
-
- $this->assertEquals('(storage eq 1 and (path eq "foo" or path eq "bar" or path eq "asd"))', $operator->__toString());
- }
-
- public function testDontTouchIfNotSame(): void {
- $operator = new SearchBinaryOperator(
- ISearchBinaryOperator::OPERATOR_OR,
- [
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 2),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 3),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'asd'),
- ])
- ]
- );
- $this->assertEquals('((storage eq 1 and path eq "foo") or (storage eq 2 and path eq "bar") or (storage eq 3 and path eq "asd"))', $operator->__toString());
-
- $this->optimizer->processOperator($operator);
- $this->simplifier->processOperator($operator);
-
- $this->assertEquals('((storage eq 1 and path eq "foo") or (storage eq 2 and path eq "bar") or (storage eq 3 and path eq "asd"))', $operator->__toString());
- }
-
- public function testMergePartial(): void {
- $operator = new SearchBinaryOperator(
- ISearchBinaryOperator::OPERATOR_OR,
- [
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 2),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'asd'),
- ])
- ]
- );
- $this->assertEquals('((storage eq 1 and path eq "foo") or (storage eq 1 and path eq "bar") or (storage eq 2 and path eq "asd"))', $operator->__toString());
-
- $this->optimizer->processOperator($operator);
- $this->simplifier->processOperator($operator);
-
- $this->assertEquals('((storage eq 1 and (path eq "foo" or path eq "bar")) or (storage eq 2 and path eq "asd"))', $operator->__toString());
- }
-
- public function testOptimizeInside(): void {
- $operator = new SearchBinaryOperator(
- ISearchBinaryOperator::OPERATOR_AND,
- [
- new SearchBinaryOperator(
- ISearchBinaryOperator::OPERATOR_OR,
- [
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'asd'),
- ])
- ]
- ),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', 'text')
- ]
- );
- $this->assertEquals('(((storage eq 1 and path eq "foo") or (storage eq 1 and path eq "bar") or (storage eq 1 and path eq "asd")) and mimetype eq "text")', $operator->__toString());
-
- $this->optimizer->processOperator($operator);
- $this->simplifier->processOperator($operator);
-
- $this->assertEquals('((storage eq 1 and (path eq "foo" or path eq "bar" or path eq "asd")) and mimetype eq "text")', $operator->__toString());
- }
-
- public function testMoveInnerOperations(): void {
- $operator = new SearchBinaryOperator(
- ISearchBinaryOperator::OPERATOR_OR,
- [
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'),
- ]),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'storage', 1),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'asd'),
- new SearchComparison(ISearchComparison::COMPARE_GREATER_THAN, 'size', '100'),
- ])
- ]
- );
- $this->assertEquals('((storage eq 1 and path eq "foo") or (storage eq 1 and path eq "bar") or (storage eq 1 and path eq "asd" and size gt "100"))', $operator->__toString());
-
- $this->optimizer->processOperator($operator);
- $this->simplifier->processOperator($operator);
-
- $this->assertEquals('(storage eq 1 and (path eq "foo" or path eq "bar" or (path eq "asd" and size gt "100")))', $operator->__toString());
- }
-}
--- a/tests/lib/Files/Search/QueryOptimizer/OrEqualsToInTest.php
+++ /dev/null
@@ -1,126 +0,0 @@
-optimizer = new OrEqualsToIn();
- $this->simplifier = new FlattenSingleArgumentBinaryOperation();
- }
-
- public function testOrs(): void {
- $operator = new SearchBinaryOperator(
- ISearchBinaryOperator::OPERATOR_OR,
- [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'asd'),
- ]
- );
- $this->assertEquals('(path eq "foo" or path eq "bar" or path eq "asd")', $operator->__toString());
-
- $this->optimizer->processOperator($operator);
- $this->simplifier->processOperator($operator);
-
- $this->assertEquals('path in ["foo","bar","asd"]', $operator->__toString());
- }
-
- public function testOrsMultipleFields(): void {
- $operator = new SearchBinaryOperator(
- ISearchBinaryOperator::OPERATOR_OR,
- [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'fileid', 1),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'fileid', 2),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', 'asd'),
- ]
- );
- $this->assertEquals('(path eq "foo" or path eq "bar" or fileid eq 1 or fileid eq 2 or mimetype eq "asd")', $operator->__toString());
-
- $this->optimizer->processOperator($operator);
- $this->simplifier->processOperator($operator);
-
- $this->assertEquals('(path in ["foo","bar"] or fileid in [1,2] or mimetype eq "asd")', $operator->__toString());
- }
-
- public function testPreserveHints(): void {
- $operator = new SearchBinaryOperator(
- ISearchBinaryOperator::OPERATOR_OR,
- [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'asd'),
- ]
- );
- foreach ($operator->getArguments() as $argument) {
- $argument->setQueryHint(ISearchComparison::HINT_PATH_EQ_HASH, false);
- }
- $this->assertEquals('(path eq "foo" or path eq "bar" or path eq "asd")', $operator->__toString());
-
- $this->optimizer->processOperator($operator);
- $this->simplifier->processOperator($operator);
-
- $this->assertEquals('path in ["foo","bar","asd"]', $operator->__toString());
- $this->assertEquals(false, $operator->getQueryHint(ISearchComparison::HINT_PATH_EQ_HASH, true));
- }
-
- public function testOrSomeEq(): void {
- $operator = new SearchBinaryOperator(
- ISearchBinaryOperator::OPERATOR_OR,
- [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'),
- new SearchComparison(ISearchComparison::COMPARE_LIKE, 'path', 'foo%'),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'),
- ]
- );
- $this->assertEquals('(path eq "foo" or path like "foo%" or path eq "bar")', $operator->__toString());
-
- $this->optimizer->processOperator($operator);
- $this->simplifier->processOperator($operator);
-
- $this->assertEquals('(path in ["foo","bar"] or path like "foo%")', $operator->__toString());
- }
-
- public function testOrsInside(): void {
- $operator = new SearchBinaryOperator(
- ISearchBinaryOperator::OPERATOR_AND,
- [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mimetype', 'text'),
- new SearchBinaryOperator(
- ISearchBinaryOperator::OPERATOR_OR,
- [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'asd'),
- ]
- )
- ]
- );
- $this->assertEquals('(mimetype eq "text" and (path eq "foo" or path eq "bar" or path eq "asd"))', $operator->__toString());
-
- $this->optimizer->processOperator($operator);
- $this->simplifier->processOperator($operator);
-
- $this->assertEquals('(mimetype eq "text" and path in ["foo","bar","asd"])', $operator->__toString());
- }
-}
--- a/tests/lib/Files/Search/QueryOptimizer/PushDownNegationTest.php
+++ /dev/null
@@ -1,143 +0,0 @@
-optimizer = new PushDownNegation();
- }
-
- public function testNotOfComparisonIsLeftAlone(): void {
- $operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_NOT, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'),
- ]);
-
- $this->optimizer->processOperator($operator);
-
- $this->assertEquals('(not path eq "foo")', $operator->__toString());
- }
-
- public function testNotOfAndBecomesOrOfNots(): void {
- $operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_NOT, [
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'),
- ]),
- ]);
-
- $this->optimizer->processOperator($operator);
-
- $this->assertEquals('((not path eq "foo") or (not path eq "bar"))', $operator->__toString());
- }
-
- public function testNotOfOrBecomesAndOfNots(): void {
- $operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_NOT, [
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'bar'),
- ]),
- ]);
-
- $this->optimizer->processOperator($operator);
-
- $this->assertEquals('((not path eq "foo") and (not path eq "bar"))', $operator->__toString());
- }
-
- public function testDoubleNegationCancelsOut(): void {
- $operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_NOT, [
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_NOT, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'),
- ]),
- ]);
-
- $this->optimizer->processOperator($operator);
-
- $this->assertEquals('path eq "foo"', $operator->__toString());
- }
-
- public function testTripleNegationLeavesASingleNot(): void {
- $operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_NOT, [
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_NOT, [
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_NOT, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'),
- ]),
- ]),
- ]);
-
- $this->optimizer->processOperator($operator);
-
- $this->assertEquals('(not path eq "foo")', $operator->__toString());
- }
-
- public function testNegatedNestedGroupIsFullyPushedDown(): void {
- // not (a and (b or c)) == (not a) or ((not b) and (not c))
- $operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_NOT, [
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'a'),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'b'),
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'c'),
- ]),
- ]),
- ]);
-
- $this->optimizer->processOperator($operator);
-
- $this->assertEquals(
- '((not path eq "a") or ((not path eq "b") and (not path eq "c")))',
- $operator->__toString(),
- );
- }
-
- public static function emptyGroupProvider(): array {
- return [
- 'and' => [ISearchBinaryOperator::OPERATOR_AND],
- 'or' => [ISearchBinaryOperator::OPERATOR_OR],
- 'not' => [ISearchBinaryOperator::OPERATOR_NOT],
- ];
- }
-
- #[DataProvider('emptyGroupProvider')]
- public function testNotOfEmptyGroupIsLeftAlone(string $type): void {
- $empty = new SearchBinaryOperator($type, []);
- $not = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_NOT, [$empty]);
- $operator = $not;
-
- $this->assertFalse($this->optimizer->processOperator($operator));
-
- $this->assertSame($not, $operator);
- $this->assertSame([$empty], $operator->getArguments());
- $this->assertSame([], $empty->getArguments());
- }
-
- public function testNestedNotOfEmptyGroupIsLeftAlone(): void {
- $operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [
- new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'path', 'foo'),
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_NOT, [
- new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, []),
- ]),
- ]);
-
- $this->optimizer->processOperator($operator);
-
- $this->assertEquals('(path eq "foo" and (not ()))', $operator->__toString());
- }
-}
--- a/tests/lib/Files/Search/SearchIntegrationTest.php
+++ /dev/null
@@ -1,93 +0,0 @@
-user = $this->createMock(IUser::class);
- $this->user->method('getUID')
- ->willReturn('user');
- $this->storage = new Temporary([]);
- $this->cache = $this->storage->getCache();
- $this->storage->getScanner()->scan('');
- $this->mountCache = Server::get(IUserMountCache::class);
- $this->mountPoint = '/user/files/search_test/';
- $this->mountCache->addMount($this->user, $this->mountPoint, $this->cache->get(''), 'dummy');
- }
-
- protected function tearDown(): void {
- $this->mountCache->removeMount($this->mountPoint);
-
- parent::tearDown();
- }
-
- public function testThousandAndOneFilters(): void {
- $id = $this->cache->put('file10', ['size' => 1, 'mtime' => 50, 'mimetype' => 'foo/folder']);
-
- $comparisons = [];
- for ($i = 1; $i <= 1001; $i++) {
- $comparisons[] = new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'name', "file$i");
- }
- $operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, $comparisons);
- $query = new SearchQuery($operator, 10, 0, []);
-
- $results = $this->cache->searchQuery($query);
-
- $this->assertCount(1, $results);
- $this->assertEquals($id, $results[0]->getId());
- }
-
- public static function searchMountNameProvider(): array {
- return [
- [new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mount_point_name', '%search%'), ''],
- [new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'mount_point_name', 'search_test'), ''],
- [new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mount_point_name', '%search_test%'), ''],
- [new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mount_point_name', '%files%'), null],
- ];
- }
-
- #[DataProvider('searchMountNameProvider')]
- public function testSearchMountName(ISearchOperator $operator, ?string $resultPath): void {
- $query = new SearchQuery($operator, 10, 0, [], $this->user);
-
- $results = $this->cache->searchQuery($query);
-
- if (is_null($resultPath)) {
- $this->assertCount(0, $results);
- } else {
- $this->assertCount(1, $results);
- $this->assertEquals($this->cache->getId($resultPath), $results[0]->getId());
- }
- }
-}
--- a/tests/lib/Files/SetupManagerTest.php
+++ /dev/null
@@ -1,672 +0,0 @@
-createMock(IEventLogger::class);
- $eventLogger->method('start');
- $eventLogger->method('end');
-
- $this->userManager = $this->createMock(IUserManager::class);
- $this->cache = $this->createMock(ICache::class);
-
- $this->userId = 'alice';
- $this->path = "/{$this->userId}/files/folder";
- $this->mountPoint = "{$this->path}/";
-
- $this->user = $this->createMock(IUser::class);
- $this->user->method('getUID')->willReturn($this->userId);
- $this->userManager->method('get')
- ->with($this->userId)
- ->willReturn($this->user);
-
- // avoid triggering full setup required check
- $this->cache->method('get')
- ->with($this->userId)
- ->willReturn(true);
-
- $this->mountProviderCollection = $this->createMock(MountProviderCollection::class);
- $this->mountManager = $this->createMock(IMountManager::class);
- $eventDispatcher = $this->createMock(IEventDispatcher::class);
- $eventDispatcher->method('addListener');
- $this->userMountCache = $this->createMock(IUserMountCache::class);
- $lockdownManager = $this->createMock(ILockdownManager::class);
- $userSession = $this->createMock(IUserSession::class);
- $cacheFactory = $this->createMock(ICacheFactory::class);
- $cacheFactory->expects($this->once())
- ->method('createDistributed')
- ->with('setupmanager::')
- ->willReturn($this->cache);
- $logger = $this->createMock(LoggerInterface::class);
- $config = $this->createMock(IConfig::class);
- $config->method('getSystemValueBool')->willReturn(false);
- $shareDisableChecker = $this->createMock(ShareDisableChecker::class);
- $appManager = $this->createMock(IAppManager::class);
- $this->fileAccess = $this->createMock(FileAccess::class);
-
- $lockdownManager->method('canAccessFilesystem')->willReturn(true);
-
- $this->setupManager = new SetupManager(
- $eventLogger,
- $this->mountProviderCollection,
- $this->mountManager,
- $this->userManager,
- $eventDispatcher,
- $this->userMountCache,
- $lockdownManager,
- $userSession,
- $cacheFactory,
- $logger,
- $config,
- $shareDisableChecker,
- $appManager,
- $this->fileAccess,
- $this->createMock(IAppConfig::class),
- $this->createMock(IStorageFactory::class),
- );
- }
-
- public function testTearDown(): void {
- $this->setupManager->tearDown();
- }
-
- /**
- * Tests that a path is not set up twice for providers implementing
- * IPartialMountProvider in setupForPath.
- */
- public function testSetupForPathWithPartialProviderSkipsAlreadySetupPath(): void {
- $cachedMount = $this->getCachedMountInfo($this->mountPoint, 42);
-
- $this->userMountCache->expects($this->exactly(2))
- ->method('getMountForPath')
- ->with($this->user, $this->path)
- ->willReturn($cachedMount);
- $this->userMountCache->expects($this->never())->method('registerMounts');
- $this->userMountCache->expects($this->never())->method('getMountsInPath');
-
- $this->fileAccess->expects($this->once())
- ->method('getByFileId')
- ->with(42)
- ->willReturn($this->createMock(CacheEntry::class));
-
- $partialMount = $this->createMock(IMountPoint::class);
-
- $this->mountProviderCollection->expects($this->once())
- ->method('getUserMountsFromProviderByPath')
- ->with(
- SetupManagerTestPartialMountProvider::class,
- $this->path,
- false,
- $this->callback(function (array $args) use ($cachedMount) {
- $this->assertCount(1, $args);
- $this->assertInstanceOf(MountProviderArgs::class, $args[0]);
- $this->assertSame($cachedMount, $args[0]->mountInfo);
- return true;
- })
- )
- ->willReturn([$partialMount]);
-
- $homeMount = $this->createMock(IMountPoint::class);
-
- $this->mountProviderCollection->expects($this->once())
- ->method('getHomeMountForUser')
- ->willReturn($homeMount);
-
- $this->mountProviderCollection->expects($this->never())
- ->method('getUserMountsForProviderClasses');
-
- $invokedCount = $this->exactly(2);
- $addMountExpectations = [
- 1 => $homeMount,
- 2 => $partialMount,
- ];
- $this->mountManager->expects($invokedCount)
- ->method('addMount')
- ->willReturnCallback($this->getAddMountCheckCallback($invokedCount, $addMountExpectations));
-
- // setup called twice, provider should only be called once
- $this->setupManager->setupForPath($this->path, false);
- $this->setupManager->setupForPath($this->path, false);
- }
-
- /**
- * Tests that providers that are not implementing IPartialMountProvider are
- * not set up more than once by setupForPath.
- */
- public function testSetupForPathWithNonPartialProviderSkipsAlreadySetupProvider(): void {
- $cachedMount = $this->getCachedMountInfo($this->mountPoint, 42,
- IMountProvider::class);
-
- $this->userMountCache->expects($this->exactly(2))
- ->method('getMountForPath')
- ->with($this->user, $this->path)
- ->willReturn($cachedMount);
-
- $this->userMountCache->expects($this->once())->method('registerMounts');
- $this->userMountCache->expects($this->never())->method('getMountsInPath');
-
- $providerMount = $this->createMock(IMountPoint::class);
-
- $this->mountProviderCollection->expects($this->once())
- ->method('getUserMountsForProviderClasses')
- ->with($this->user, [IMountProvider::class])
- ->willReturn([$providerMount]);
-
- $homeMount = $this->createMock(IMountPoint::class);
-
- $this->mountProviderCollection->expects($this->once())
- ->method('getHomeMountForUser')
- ->willReturn($homeMount);
-
- $invokedCount = $this->exactly(2);
- $addMountExpectations = [
- 1 => $homeMount,
- 2 => $providerMount,
- ];
- $this->mountManager->expects($invokedCount)
- ->method('addMount')
- ->willReturnCallback($this->getAddMountCheckCallback($invokedCount, $addMountExpectations));
-
- // setup called twice, provider should only be called once
- $this->setupManager->setupForPath($this->path, false);
- $this->setupManager->setupForPath($this->path, false);
- }
-
- /**
- * Tests that setupForPath does not instantiate already set up providers
- * when called for the same path first with $withChildren set to true
- * and then set to false.
- */
- public function testSetupForPathWithChildrenAndNonPartialProviderSkipsAlreadySetupProvider(): void {
- $cachedMount = $this->getCachedMountInfo($this->mountPoint, 42, IMountProvider::class);
- $additionalCachedMount = $this->getCachedMountInfo($this->mountPoint . 'additional/', 43, SetupManagerTestFullMountProvider::class);
-
- $this->userMountCache->expects($this->exactly(2))
- ->method('getMountForPath')
- ->with($this->user, $this->path)
- ->willReturn($cachedMount);
-
- $this->userMountCache->expects($this->once())->method('registerMounts');
- $this->userMountCache->expects($this->once())->method('getMountsInPath')
- ->willReturn([$additionalCachedMount]);
-
- $mount = $this->createMock(IMountPoint::class);
- $additionalMount = $this->createMock(IMountPoint::class);
-
- $invokedCount = $this->exactly(2);
- $this->mountProviderCollection->expects($invokedCount)
- ->method('getUserMountsForProviderClasses')
- ->willReturnCallback(function (IUser $userArg, array $providersArg) use (
- $additionalMount,
- $mount,
- $invokedCount) {
- if ($invokedCount->numberOfInvocations() === 1) {
- $providers = [IMountProvider::class];
- $returnMounts = [$mount];
- } else {
- $providers = [SetupManagerTestFullMountProvider::class];
- $returnMounts = [$additionalMount];
- }
-
- $this->assertSame($this->user, $userArg);
- $this->assertSame($providersArg, $providers);
-
- return $returnMounts;
- });
-
- $homeMount = $this->createMock(IMountPoint::class);
-
- $this->mountProviderCollection->expects($this->once())
- ->method('getHomeMountForUser')
- ->willReturn($homeMount);
-
- $invokedCount = $this->exactly(3);
- $addMountExpectations = [
- 1 => $homeMount,
- 2 => $mount,
- 3 => $additionalMount,
- ];
- $this->mountManager->expects($invokedCount)
- ->method('addMount')
- ->willReturnCallback($this->getAddMountCheckCallback($invokedCount, $addMountExpectations));
-
- // setup called twice, provider should only be called once
- $this->setupManager->setupForPath($this->path, true);
- $this->setupManager->setupForPath($this->path, false);
- }
-
- /**
- * Tests that setupForPath does not set up child mounts again if a parent
- * was set up with $withChildren set to true.
- */
- public function testSetupForPathWithChildrenAndPartialProviderSkipsIfParentAlreadySetup(): void {
- $childPath = "{$this->path}/child";
- $childMountPoint = "{$childPath}/";
-
- $cachedMount = $this->getCachedMountInfo($this->mountPoint, 42);
- $cachedChildMount = $this->getCachedMountInfo($childMountPoint, 43);
-
- $invokedCount = $this->exactly(3);
- $this->userMountCache->expects($invokedCount)
- ->method('getMountForPath')
- ->willReturnCallback(function (IUser $userArg, string $pathArg) use (
- $cachedChildMount,
- $cachedMount,
- $childPath,
- $invokedCount) {
- if ($invokedCount->numberOfInvocations() === 1) {
- $expectedPath = $this->path;
- $returnMount = $cachedMount;
- } else {
- $expectedPath = $childPath;
- $returnMount = $cachedChildMount;
- }
-
- $this->assertSame($this->user, $userArg);
- $this->assertSame($expectedPath, $pathArg);
-
- return $returnMount;
- });
-
- $this->userMountCache->expects($this->never())->method('registerMounts');
- $this->userMountCache->expects($this->exactly(2))
- ->method('getMountsInPath')
- ->willReturn([$cachedChildMount]);
-
- $this->fileAccess->expects($this->once())
- ->method('getByFileId')
- ->with(42)
- ->willReturn($this->createMock(CacheEntry::class));
-
- $this->fileAccess->expects($this->once())
- ->method('getByFileIds')
- ->with([43])
- ->willReturn([43 => $this->createMock(CacheEntry::class)]);
-
- $partialMount = $this->createMock(IMountPoint::class);
- $partialChildMount = $this->createMock(IMountPoint::class);
-
- $invokedCount = $this->exactly(2);
- $this->mountProviderCollection->expects($invokedCount)
- ->method('getUserMountsFromProviderByPath')
- ->willReturnCallback(function (
- string $providerClass,
- string $pathArg,
- bool $forChildren,
- array $mountProviderArgs,
- ) use (
- $cachedChildMount,
- $partialMount,
- $partialChildMount,
- $cachedMount,
- $invokedCount
- ) {
- $expectedPath = $this->path;
- if ($invokedCount->numberOfInvocations() === 1) {
- // call for the parent
- $expectedCachedMount = $cachedMount;
- $mountPoints = [$partialMount];
- $expectedForChildren = false;
- } else {
- // call for the children
- $expectedCachedMount = $cachedChildMount;
- $mountPoints = [$partialChildMount];
- $expectedForChildren = true;
- }
-
- $this->assertSame(SetupManagerTestPartialMountProvider::class, $providerClass);
- $this->assertSame($expectedPath, $pathArg);
- $this->assertSame($expectedForChildren, $forChildren);
- $this->assertCount(1, $mountProviderArgs);
- $this->assertInstanceOf(MountProviderArgs::class, $mountProviderArgs[0]);
- $this->assertSame($expectedCachedMount, $mountProviderArgs[0]->mountInfo);
-
- return $mountPoints;
- });
-
- $homeMount = $this->createMock(IMountPoint::class);
-
- $this->mountProviderCollection->expects($this->once())
- ->method('getHomeMountForUser')
- ->willReturn($homeMount);
-
- $this->mountProviderCollection->expects($this->never())
- ->method('getUserMountsForProviderClasses');
-
- $invokedCount = $this->exactly(3);
- $addMountExpectations = [
- 1 => $homeMount,
- 2 => $partialMount,
- 3 => $partialChildMount,
- ];
- $this->mountManager->expects($invokedCount)
- ->method('addMount')
- ->willReturnCallback($this->getAddMountCheckCallback($invokedCount, $addMountExpectations));
-
- // once the setup for a path has been done with children, setup for sub
- // paths should not create the same new mounts again
- $this->setupManager->setupForPath($this->path, true);
- $this->setupManager->setupForPath($childPath, false);
- $this->setupManager->setupForPath($childPath, true);
- }
-
- /**
- * Tests that when called twice setupForPath does not set up mounts from
- * providers implementing IPartialMountProviders or IMountProvider.
- */
- public function testSetupForPathHandlesPartialAndFullProvidersWithChildren(): void {
- $parentPartialCachedMount = $this->getCachedMountInfo($this->mountPoint, 42);
- $childCachedPartialMount = $this->getCachedMountInfo("{$this->mountPoint}partial/", 43);
- $childCachedFullMount = $this->getCachedMountInfo("{$this->mountPoint}full/", 44, SetupManagerTestFullMountProvider::class);
-
- $this->userMountCache->expects($this->exactly(2))
- ->method('getMountForPath')
- ->with($this->user, $this->path)
- ->willReturn($parentPartialCachedMount);
- $this->userMountCache->expects($this->exactly(2))
- ->method('getMountsInPath')
- ->with($this->user, $this->path)
- ->willReturn([$childCachedPartialMount, $childCachedFullMount]);
-
- $homeMount = $this->createMock(IMountPoint::class);
- $parentPartialMount = $this->createMock(IMountPoint::class);
- $childPartialMount = $this->createMock(IMountPoint::class);
- $childFullProviderMount = $this->createMock(IMountPoint::class);
-
- $this->mountProviderCollection->expects($this->once())
- ->method('getHomeMountForUser')
- ->willReturn($homeMount);
-
- $this->userMountCache->expects($this->once())
- ->method('registerMounts')
- ->with(
- $this->user, [$childFullProviderMount],
- [SetupManagerTestFullMountProvider::class],
- );
-
- $this->fileAccess->expects($this->once())
- ->method('getByFileId')
- ->with(42)
- ->willReturn($this->createMock(CacheEntry::class));
- $childMetadata = $this->createMock(CacheEntry::class);
- $this->fileAccess->expects($this->once())
- ->method('getByFileIds')
- ->with([43])
- ->willReturn([43 => $childMetadata]);
-
- $invokedCount = $this->exactly(2);
- $this->mountProviderCollection->expects($invokedCount)
- ->method('getUserMountsFromProviderByPath')
- ->willReturnCallback(function (string $providerClass, string $pathArg, bool $forChildren, array $mountProviderArgs) use (
- $childCachedPartialMount,
- $childPartialMount,
- $parentPartialMount,
- $parentPartialCachedMount,
- $invokedCount) {
- $expectedPath = $this->path;
- if ($invokedCount->numberOfInvocations() === 1) {
- // call for the parent
- $expectedCachedMount = $parentPartialCachedMount;
- $mountPoints = [$parentPartialMount];
- $expectedForChildren = false;
- } else {
- // call for the children
- $expectedCachedMount = $childCachedPartialMount;
- $mountPoints = [$childPartialMount];
- $expectedForChildren = true;
- }
-
- $this->assertSame(SetupManagerTestPartialMountProvider::class, $providerClass);
- $this->assertSame($expectedPath, $pathArg);
- $this->assertSame($expectedForChildren, $forChildren);
- $this->assertCount(1, $mountProviderArgs);
- $this->assertInstanceOf(MountProviderArgs::class, $mountProviderArgs[0]);
- $this->assertSame($expectedCachedMount, $mountProviderArgs[0]->mountInfo);
-
- return $mountPoints;
- });
-
- $this->mountProviderCollection->expects($this->once())
- ->method('getUserMountsForProviderClasses')
- ->with($this->user, [SetupManagerTestFullMountProvider::class])
- ->willReturn([$childFullProviderMount]);
-
- $invokedCount = $this->exactly(4);
- $addMountExpectations = [
- 1 => $homeMount,
- 2 => $childFullProviderMount,
- 3 => $parentPartialMount,
- 4 => $childPartialMount,
- ];
- $this->mountManager->expects($invokedCount)
- ->method('addMount')
- ->willReturnCallback($this->getAddMountCheckCallback($invokedCount, $addMountExpectations));
-
- // call twice to test that providers and mounts are only called once
- $this->setupManager->setupForPath($this->path, true);
- $this->setupManager->setupForPath($this->path, true);
- }
-
- public function testSetupForUserResetsUserPaths(): void {
- $cachedMount = $this->getCachedMountInfo($this->mountPoint, 42);
-
- $this->userMountCache->expects($this->once())
- ->method('getMountForPath')
- ->with($this->user, $this->path)
- ->willReturn($cachedMount);
- $this->userMountCache->expects($this->never())
- ->method('getMountsInPath');
-
- $this->fileAccess->expects($this->once())
- ->method('getByFileId')
- ->with(42)
- ->willReturn($this->createMock(CacheEntry::class));
-
- $partialMount = $this->createMock(IMountPoint::class);
-
- $this->mountProviderCollection->expects($this->once())
- ->method('getUserMountsFromProviderByPath')
- ->with(
- SetupManagerTestPartialMountProvider::class,
- $this->path,
- false,
- $this->callback(function (array $args) use ($cachedMount) {
- $this->assertCount(1, $args);
- $this->assertInstanceOf(MountProviderArgs::class,
- $args[0]);
- $this->assertSame($cachedMount, $args[0]->mountInfo);
- return true;
- })
- )
- ->willReturn([$partialMount]);
-
- $homeMount = $this->createMock(IMountPoint::class);
-
- $this->mountProviderCollection->expects($this->once())
- ->method('getHomeMountForUser')
- ->willReturn($homeMount);
- $this->mountProviderCollection->expects($this->never())
- ->method('getUserMountsForProviderClasses');
-
- $invokedCount = $this->exactly(2);
- $addMountExpectations = [
- 1 => $homeMount,
- 2 => $partialMount,
- ];
- $this->mountManager->expects($invokedCount)
- ->method('addMount')
- ->willReturnCallback($this->getAddMountCheckCallback($invokedCount,
- $addMountExpectations));
-
- // setting up for $path but then for user should remove the setup path
- $this->setupManager->setupForPath($this->path, false);
-
- // note that only the mount known by SetupManrger is removed not the
- // home mount, because MountManager is mocked
- $this->mountManager->expects($this->once())
- ->method('removeMount')
- ->with($this->mountPoint);
-
- $this->setupManager->setupForUser($this->user);
- }
-
- /**
- * Tests that after a path is setup by a
- */
- public function testSetupForProviderResetsUserProviderPaths(): void {
- $cachedMount = $this->getCachedMountInfo($this->mountPoint, 42);
-
- $this->userMountCache->expects($this->once())
- ->method('getMountForPath')
- ->with($this->user, $this->path)
- ->willReturn($cachedMount);
- $this->userMountCache->expects($this->never())
- ->method('getMountsInPath');
-
- $this->fileAccess->expects($this->once())
- ->method('getByFileId')
- ->with(42)
- ->willReturn($this->createMock(CacheEntry::class));
-
- $partialMount = $this->createMock(IMountPoint::class);
- $partialMount->expects($this->once())->method('getMountProvider')
- ->willReturn(SetupManagerTestFullMountProvider::class);
-
- $this->mountProviderCollection->expects($this->once())
- ->method('getUserMountsFromProviderByPath')
- ->with(
- SetupManagerTestPartialMountProvider::class,
- $this->path,
- false,
- $this->callback(function (array $args) use ($cachedMount) {
- $this->assertCount(1, $args);
- $this->assertInstanceOf(MountProviderArgs::class,
- $args[0]);
- $this->assertSame($cachedMount, $args[0]->mountInfo);
- return true;
- })
- )
- ->willReturn([$partialMount]);
-
- $homeMount = $this->createMock(IMountPoint::class);
-
- $this->mountProviderCollection->expects($this->once())
- ->method('getHomeMountForUser')
- ->willReturn($homeMount);
-
- $invokedCount = $this->exactly(2);
- $addMountExpectations = [
- 1 => $homeMount,
- 2 => $partialMount,
- ];
- $this->mountManager->expects($invokedCount)
- ->method('addMount')
- ->willReturnCallback($this->getAddMountCheckCallback($invokedCount,
- $addMountExpectations));
- $this->mountManager->method('getAll')
- ->willReturn([$this->mountPoint => $partialMount]);
-
- // setting up for $path but then for user should remove the setup path
- $this->setupManager->setupForPath($this->path, false);
-
- // note that only the mount known by SetupManrger is removed not the
- // home mount, because MountManager is mocked
- $this->mountManager->expects($this->once())
- ->method('removeMount')
- ->with($this->mountPoint);
-
- $this->mountProviderCollection->expects($this->once())
- ->method('getUserMountsForProviderClasses')
- ->with($this->user, [SetupManagerTestFullMountProvider::class]);
-
- $this->setupManager->setupForProvider($this->path,
- [SetupManagerTestFullMountProvider::class]);
- }
-
- private function getAddMountCheckCallback(InvokedCount $invokedCount, $expectations): \Closure {
- return function (IMountPoint $actualMount) use ($invokedCount, $expectations): void {
- $expectedMount = $expectations[$invokedCount->numberOfInvocations()] ?? null;
- $this->assertSame($expectedMount, $actualMount);
- };
- }
-
- public function getCachedMountInfo(string $mountPoint, int $rootId, string $providerClass = SetupManagerTestPartialMountProvider::class): ICachedMountInfo&MockObject {
- $cachedMount = $this->createMock(ICachedMountInfo::class);
- $cachedMount->method('getMountProvider')->willReturn($providerClass);
- $cachedMount->method('getMountPoint')->willReturn($mountPoint);
- $cachedMount->method('getRootId')->willReturn($rootId);
-
- return $cachedMount;
- }
-}
-
-class SetupManagerTestPartialMountProvider implements IPartialMountProvider {
- #[\Override]
- public function getMountsForUser(IUser $user, IStorageFactory $loader): array {
- return [];
- }
-
- #[\Override]
- public function getMountsForPath(string $path, bool $forChildren, array $mountProviderArgs, IStorageFactory $loader): array {
- return [];
- }
-}
-
-class SetupManagerTestFullMountProvider implements IMountProvider {
- #[\Override]
- public function getMountsForUser(IUser $user, IStorageFactory $loader): array {
- return [];
- }
-}
--- a/tests/lib/Files/SimpleFS/InMemoryFileTest.php
+++ /dev/null
@@ -1,120 +0,0 @@
-testPdf = new InMemoryFile('test.pdf', $fileContents);
- }
-
- /**
- * Asserts that putContent replaces the file contents.
- *
- * @return void
- */
- public function testPutContent(): void {
- $this->testPdf->putContent('test');
- self::assertEquals('test', $this->testPdf->getContent());
- }
-
- #[\PHPUnit\Framework\Attributes\DoesNotPerformAssertions]
- public function testDelete(): void {
- $this->testPdf->delete();
- }
-
- /**
- * Asserts that getName returns the name passed on file creation.
- *
- * @return void
- */
- public function testGetName(): void {
- self::assertEquals('test.pdf', $this->testPdf->getName());
- }
-
- /**
- * Asserts that the file size is the size of the test file.
- *
- * @return void
- */
- public function testGetSize(): void {
- self::assertEquals(7083, $this->testPdf->getSize());
- }
-
- /**
- * Asserts the file contents are the same than the original file contents.
- *
- * @return void
- */
- public function testGetContent(): void {
- self::assertEquals(
- file_get_contents(__DIR__ . '/../../../data/test.pdf'),
- $this->testPdf->getContent()
- );
- }
-
- /**
- * Asserts the test file modification time is an integer.
- *
- * @return void
- */
- public function testGetMTime(): void {
- self::assertTrue(is_int($this->testPdf->getMTime()));
- }
-
- /**
- * Asserts the test file mime type is "application/json".
- *
- * @return void
- */
- public function testGetMimeType(): void {
- self::assertEquals('application/pdf', $this->testPdf->getMimeType());
- }
-
- /**
- * Ensure that read() returns a stream with the same contents than the original file.
- */
- public function testRead(): void {
- self::assertEquals(
- file_get_contents(__DIR__ . '/../../../data/test.pdf'),
- stream_get_contents($this->testPdf->read()),
- );
- }
-
- /**
- * Asserts that write() raises an NotPermittedException.
- *
- * @return void
- */
- public function testWrite(): void {
- self::expectException(NotPermittedException::class);
- $this->testPdf->write();
- }
-}
--- a/tests/lib/Files/SimpleFS/SimpleFileTest.php
+++ /dev/null
@@ -1,127 +0,0 @@
-file = $this->createMock(File::class);
- $this->simpleFile = new SimpleFile($this->file);
- }
-
- public function testGetName(): void {
- $this->file->expects($this->once())
- ->method('getName')
- ->willReturn('myname');
-
- $this->assertEquals('myname', $this->simpleFile->getName());
- }
-
- public function testGetSize(): void {
- $this->file->expects($this->once())
- ->method('getSize')
- ->willReturn(42);
-
- $this->assertEquals(42, $this->simpleFile->getSize());
- }
-
- public function testGetETag(): void {
- $this->file->expects($this->once())
- ->method('getETag')
- ->willReturn('etag');
-
- $this->assertEquals('etag', $this->simpleFile->getETag());
- }
-
- public function testGetMTime(): void {
- $this->file->expects($this->once())
- ->method('getMTime')
- ->willReturn(101);
-
- $this->assertEquals(101, $this->simpleFile->getMTime());
- }
-
- public function testGetContent(): void {
- $this->file->expects($this->once())
- ->method('getContent')
- ->willReturn('foo');
-
- $this->assertEquals('foo', $this->simpleFile->getContent());
- }
-
- public function testPutContent(): void {
- $this->file->expects($this->once())
- ->method('putContent')
- ->with($this->equalTo('bar'));
-
- $this->simpleFile->putContent('bar');
- }
-
- public function testDelete(): void {
- $this->file->expects($this->once())
- ->method('delete');
-
- $this->simpleFile->delete();
- }
-
- public function testGetMimeType(): void {
- $this->file->expects($this->once())
- ->method('getMimeType')
- ->willReturn('app/awesome');
-
- $this->assertEquals('app/awesome', $this->simpleFile->getMimeType());
- }
-
- public function testGetContentInvalidAppData(): void {
- $this->file->method('getContent')
- ->willReturn(false);
- $this->file->method('stat')->willReturn(false);
-
- $parent = $this->createMock(Folder::class);
- $parent->method('stat')->willReturn(false);
-
- $root = $this->createMock(Folder::class);
- $root->method('stat')->willReturn([]);
-
- $this->file->method('getParent')->willReturn($parent);
- $parent->method('getParent')->willReturn($root);
-
- $this->expectException(NotFoundException::class);
-
- $this->simpleFile->getContent();
- }
-
- public function testRead(): void {
- $this->file->expects($this->once())
- ->method('fopen')
- ->with('r');
-
- $this->simpleFile->read();
- }
-
- public function testWrite(): void {
- $this->file->expects($this->once())
- ->method('fopen')
- ->with('w');
-
- $this->simpleFile->write();
- }
-}
--- a/tests/lib/Files/SimpleFS/SimpleFolderTest.php
+++ /dev/null
@@ -1,114 +0,0 @@
-storage = new Temporary([]);
- $this->createUser('simple', 'simple');
- $this->registerMount('simple', $this->storage, '/simple/files');
- $this->loginAsUser('simple');
-
- $this->parentFolder = \OC::$server->getUserFolder('simple');
-
- $this->folder = $this->parentFolder->newFolder('test');
- $this->simpleFolder = new SimpleFolder($this->folder);
- }
-
- public function testGetName(): void {
- $this->assertEquals('test', $this->simpleFolder->getName());
- }
-
- public function testDelete(): void {
- $this->assertTrue($this->parentFolder->nodeExists('test'));
- $this->simpleFolder->delete();
- $this->assertFalse($this->parentFolder->nodeExists('test'));
- }
-
- public function testFileExists(): void {
- $this->folder->newFile('exists');
-
- $this->assertFalse($this->simpleFolder->fileExists('not-exists'));
- $this->assertTrue($this->simpleFolder->fileExists('exists'));
- }
-
- public function testGetFile(): void {
- $this->folder->newFile('exists');
-
- $result = $this->simpleFolder->getFile('exists');
- $this->assertInstanceOf(ISimpleFile::class, $result);
-
- $this->expectException(NotFoundException::class);
- $this->simpleFolder->getFile('not-exists');
- }
-
- public function testNewFile(): void {
- $result = $this->simpleFolder->newFile('file');
- $this->assertInstanceOf(ISimpleFile::class, $result);
- $this->assertFalse($this->folder->nodeExists('file'));
- $result->putContent('bar');
-
- $this->assertTrue($this->folder->nodeExists('file'));
- $this->assertEquals('bar', $result->getContent());
- }
-
- public function testGetDirectoryListing(): void {
- $this->folder->newFile('file1');
- $this->folder->newFile('file2');
-
- $result = $this->simpleFolder->getDirectoryListing();
- $this->assertCount(2, $result);
- $this->assertInstanceOf(ISimpleFile::class, $result[0]);
- $this->assertInstanceOf(ISimpleFile::class, $result[1]);
- }
-
- public function testGetFolder(): void {
- $this->folder->newFolder('exists');
-
- $result = $this->simpleFolder->getFolder('exists');
- $this->assertInstanceOf(ISimpleFolder::class, $result);
-
- $this->expectException(NotFoundException::class);
- $this->simpleFolder->getFolder('not-exists');
- }
-
- public function testNewFolder(): void {
- $result = $this->simpleFolder->newFolder('folder');
- $this->assertInstanceOf(ISimpleFolder::class, $result);
- $result->newFile('file');
-
- $this->assertTrue($this->folder->nodeExists('folder'));
- }
-}
--- a/tests/lib/Files/Storage/BearerAuthAwareSabreClientTest.php
+++ /dev/null
@@ -1,74 +0,0 @@
-getProperty('curlSettings');
- $property->setAccessible(true);
- $settings = $property->getValue($client);
- return $settings[$key] ?? null;
- }
-
- public function testBearerTokenIsUsedForAuthentication(): void {
- $client = new BearerAuthAwareSabreClient([
- 'baseUri' => 'https://example.com/',
- // The user name holds the long-lived share secret, never the bearer.
- 'userName' => 'refresh-secret',
- 'password' => '',
- 'authType' => BearerAuthAwareSabreClient::AUTH_BEARER,
- 'bearerToken' => 'the-access-jwt',
- ]);
-
- $this->assertSame('the-access-jwt', $this->getCurlSetting($client, CURLOPT_XOAUTH2_BEARER));
- }
-
- public function testShareSecretIsNotUsedAsBearer(): void {
- $client = new BearerAuthAwareSabreClient([
- 'baseUri' => 'https://example.com/',
- 'userName' => 'refresh-secret',
- 'password' => '',
- 'authType' => BearerAuthAwareSabreClient::AUTH_BEARER,
- 'bearerToken' => 'the-access-jwt',
- ]);
-
- $this->assertNotSame('refresh-secret', $this->getCurlSetting($client, CURLOPT_XOAUTH2_BEARER));
- }
-
- public function testNoBearerAppliedWithoutBearerAuthType(): void {
- $client = new BearerAuthAwareSabreClient([
- 'baseUri' => 'https://example.com/',
- 'userName' => 'user',
- 'password' => 'pass',
- 'authType' => \Sabre\DAV\Client::AUTH_BASIC,
- 'bearerToken' => 'the-access-jwt',
- ]);
-
- $this->assertNull($this->getCurlSetting($client, CURLOPT_XOAUTH2_BEARER));
- }
-
- public function testNoBearerAppliedWithoutBearerToken(): void {
- $client = new BearerAuthAwareSabreClient([
- 'baseUri' => 'https://example.com/',
- 'userName' => 'refresh-secret',
- 'password' => '',
- 'authType' => BearerAuthAwareSabreClient::AUTH_BEARER,
- ]);
-
- $this->assertNull($this->getCurlSetting($client, CURLOPT_XOAUTH2_BEARER));
- }
-}
--- a/tests/lib/Files/Storage/CommonTest.php
+++ /dev/null
@@ -1,141 +0,0 @@
-filenameValidator = $this->createMock(IFilenameValidator::class);
- $this->overwriteService(IFilenameValidator::class, $this->filenameValidator);
- $this->tmpDir = Server::get(ITempManager::class)->getTemporaryFolder();
- $this->instance = new \OC\Files\Storage\CommonTest(['datadir' => $this->tmpDir]);
- }
-
- #[\Override]
- protected function tearDown(): void {
- Files::rmdirr($this->tmpDir);
- $this->restoreService(IFilenameValidator::class);
- parent::tearDown();
- }
-
- public function testVerifyPath(): void {
- $this->filenameValidator
- ->expects($this->once())
- ->method('validateFilename')
- ->with('invalid:char.txt')
- ->willThrowException(new InvalidCharacterInPathException());
- $this->expectException(InvalidPathException::class);
-
- $this->instance->verifyPath('/', 'invalid:char.txt');
- }
-
- public function testVerifyPathSucceed(): void {
- $this->filenameValidator
- ->expects($this->once())
- ->method('validateFilename')
- ->with('valid-char.txt');
-
- $this->instance->verifyPath('/', 'valid-char.txt');
- }
-
- public function testMoveFromStorageWrapped(): void {
- /** @var \OC\Files\Storage\CommonTest|MockObject $instance */
- $instance = $this->getMockBuilder(\OC\Files\Storage\CommonTest::class)
- ->onlyMethods(['copyFromStorage', 'rmdir', 'unlink'])
- ->setConstructorArgs([['datadir' => $this->tmpDir]])
- ->getMock();
- $instance->method('copyFromStorage')
- ->willThrowException(new \Exception('copy'));
-
- $source = new Wrapper([
- 'storage' => $instance,
- ]);
-
- $instance->file_put_contents('foo.txt', 'bar');
- $instance->moveFromStorage($source, 'foo.txt', 'bar.txt');
- $this->assertTrue($instance->file_exists('bar.txt'));
- }
-
- public function testMoveFromStorageJailed(): void {
- /** @var \OC\Files\Storage\CommonTest|MockObject $instance */
- $instance = $this->getMockBuilder(\OC\Files\Storage\CommonTest::class)
- ->onlyMethods(['copyFromStorage', 'rmdir', 'unlink'])
- ->setConstructorArgs([['datadir' => $this->tmpDir]])
- ->getMock();
- $instance->method('copyFromStorage')
- ->willThrowException(new \Exception('copy'));
-
- $source = new Jail([
- 'storage' => $instance,
- 'root' => 'foo'
- ]);
- $source = new Wrapper([
- 'storage' => $source
- ]);
-
- $instance->mkdir('foo');
- $instance->file_put_contents('foo/foo.txt', 'bar');
- $instance->moveFromStorage($source, 'foo.txt', 'bar.txt');
- $this->assertTrue($instance->file_exists('bar.txt'));
- }
-
- public function testMoveFromStorageNestedJail(): void {
- /** @var \OC\Files\Storage\CommonTest|MockObject $instance */
- $instance = $this->getMockBuilder(\OC\Files\Storage\CommonTest::class)
- ->onlyMethods(['copyFromStorage', 'rmdir', 'unlink'])
- ->setConstructorArgs([['datadir' => $this->tmpDir]])
- ->getMock();
- $instance->method('copyFromStorage')
- ->willThrowException(new \Exception('copy'));
-
- $source = new Jail([
- 'storage' => $instance,
- 'root' => 'foo'
- ]);
- $source = new Wrapper([
- 'storage' => $source
- ]);
- $source = new Jail([
- 'storage' => $source,
- 'root' => 'bar'
- ]);
- $source = new Wrapper([
- 'storage' => $source
- ]);
-
- $instance->mkdir('foo');
- $instance->mkdir('foo/bar');
- $instance->file_put_contents('foo/bar/foo.txt', 'bar');
- $instance->moveFromStorage($source, 'foo.txt', 'bar.txt');
- $this->assertTrue($instance->file_exists('bar.txt'));
- }
-}
--- a/tests/lib/Files/Storage/CopyDirectoryTest.php
+++ /dev/null
@@ -1,43 +0,0 @@
-is_dir($source)) {
- return false;
- }
- return copy($this->getSourcePath($source), $this->getSourcePath($target));
- }
-}
-
-class CopyDirectoryStorage extends StorageNoRecursiveCopy {
- use CopyDirectory;
-}
-
-/**
- * Class CopyDirectoryTest
- *
- *
- * @package Test\Files\Storage
- */
-#[\PHPUnit\Framework\Attributes\Group('DB')]
-class CopyDirectoryTest extends Storage {
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
- $this->instance = new CopyDirectoryStorage([]);
- }
-}
--- a/tests/lib/Files/Storage/HomeTest.php
+++ /dev/null
@@ -1,88 +0,0 @@
-home;
- }
-
- #[\Override]
- public function getUID(): string {
- return $this->uid;
- }
-}
-
-/**
- * Class Home
- *
- *
- * @package Test\Files\Storage
- */
-#[\PHPUnit\Framework\Attributes\Group('DB')]
-class HomeTest extends Storage {
- /**
- * @var string tmpDir
- */
- private $tmpDir;
-
- private $userId;
-
- /**
- * @var User $user
- */
- private $user;
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- $this->tmpDir = Server::get(ITempManager::class)->getTemporaryFolder();
- $this->userId = $this->getUniqueID('user_');
- $this->user = new DummyUser($this->userId, $this->tmpDir);
- $this->instance = new Home(['user' => $this->user]);
- }
-
- #[\Override]
- protected function tearDown(): void {
- Files::rmdirr($this->tmpDir);
- parent::tearDown();
- }
-
- /**
- * Tests that the home id is in the format home::user1
- */
- public function testId(): void {
- $this->assertEquals('home::' . $this->userId, $this->instance->getId());
- }
-
- /**
- * Tests that getCache() returns an instance of HomeCache
- */
- public function testGetCacheReturnsHomeCache(): void {
- $this->assertInstanceOf('\OC\Files\Cache\HomeCache', $this->instance->getCache());
- }
-
- public function testGetOwner(): void {
- $this->assertEquals($this->userId, $this->instance->getOwner(''));
- }
-}
--- a/tests/lib/Files/Storage/LocalTest.php
+++ /dev/null
@@ -1,283 +0,0 @@
-tmpDir = Server::get(ITempManager::class)->getTemporaryFolder();
- $this->instance = new Local(['datadir' => $this->tmpDir]);
- }
-
- #[\Override]
- protected function tearDown(): void {
- Files::rmdirr($this->tmpDir);
- parent::tearDown();
- }
-
- public function testStableEtag(): void {
- $this->instance->file_put_contents('test.txt', 'foobar');
- $etag1 = $this->instance->getETag('test.txt');
- $etag2 = $this->instance->getETag('test.txt');
- $this->assertEquals($etag1, $etag2);
- }
-
- public function testEtagChange(): void {
- $this->instance->file_put_contents('test.txt', 'foo');
- $this->instance->touch('test.txt', time() - 2);
- $etag1 = $this->instance->getETag('test.txt');
- $this->instance->file_put_contents('test.txt', 'bar');
- $etag2 = $this->instance->getETag('test.txt');
- $this->assertNotEquals($etag1, $etag2);
- }
-
- public function testInvalidArgumentsEmptyArray(): void {
- $this->expectException(\InvalidArgumentException::class);
-
- new Local([]);
- }
-
- public function testInvalidArgumentsNoArray(): void {
- $this->expectException(\InvalidArgumentException::class);
-
- new Local([]);
- }
-
- public function testDisallowSymlinksOutsideDatadir(): void {
- $this->expectException(ForbiddenException::class);
-
- $subDir1 = $this->tmpDir . 'sub1';
- $subDir2 = $this->tmpDir . 'sub2';
- $sym = $this->tmpDir . 'sub1/sym';
- mkdir($subDir1);
- mkdir($subDir2);
-
- symlink($subDir2, $sym);
-
- $storage = new Local(['datadir' => $subDir1]);
-
- $storage->file_put_contents('sym/foo', 'bar');
- }
-
- public function testDisallowSymlinksInsideDatadir(): void {
- $subDir1 = $this->tmpDir . 'sub1';
- $subDir2 = $this->tmpDir . 'sub1/sub2';
- $sym = $this->tmpDir . 'sub1/sym';
- mkdir($subDir1);
- mkdir($subDir2);
-
- symlink($subDir2, $sym);
-
- $storage = new Local(['datadir' => $subDir1]);
-
- $storage->file_put_contents('sym/foo', 'bar');
- $this->addToAssertionCount(1);
- }
-
- public function testWriteUmaskFilePutContents(): void {
- $oldMask = umask(0333);
- $this->instance->file_put_contents('test.txt', 'sad');
- umask($oldMask);
- $this->assertTrue($this->instance->isUpdatable('test.txt'));
- }
-
- public function testWriteUmaskMkdir(): void {
- $oldMask = umask(0333);
- $this->instance->mkdir('test.txt');
- umask($oldMask);
- $this->assertTrue($this->instance->isUpdatable('test.txt'));
- }
-
- public function testWriteUmaskFopen(): void {
- $oldMask = umask(0333);
- $handle = $this->instance->fopen('test.txt', 'w');
- fwrite($handle, 'foo');
- fclose($handle);
- umask($oldMask);
- $this->assertTrue($this->instance->isUpdatable('test.txt'));
- }
-
- public function testWriteUmaskCopy(): void {
- $this->instance->file_put_contents('source.txt', 'sad');
- $oldMask = umask(0333);
- $this->instance->copy('source.txt', 'test.txt');
- umask($oldMask);
- $this->assertTrue($this->instance->isUpdatable('test.txt'));
- }
-
- public function testUnavailableExternal(): void {
- $this->expectException(StorageNotAvailableException::class);
- $this->instance = new Local(['datadir' => $this->tmpDir . '/unexist', 'isExternal' => true]);
- }
-
- public function testUnavailableNonExternal(): void {
- $this->instance = new Local(['datadir' => $this->tmpDir . '/unexist']);
- // no exception thrown
- $this->assertNotNull($this->instance);
- }
-
- public function testMoveNestedJail(): void {
- $this->instance->mkdir('foo');
- $this->instance->mkdir('foo/bar');
- $this->instance->mkdir('target');
- $this->instance->file_put_contents('foo/bar/file.txt', 'foo');
- $jail1 = new Jail([
- 'storage' => $this->instance,
- 'root' => 'foo'
- ]);
- $jail2 = new Jail([
- 'storage' => $jail1,
- 'root' => 'bar'
- ]);
- $jail3 = new Jail([
- 'storage' => $this->instance,
- 'root' => 'target'
- ]);
- $jail3->moveFromStorage($jail2, 'file.txt', 'file.txt');
- $this->assertTrue($this->instance->file_exists('target/file.txt'));
- }
-
- public function testFopenRestoresUmaskWhenTheWritePathThrows(): void {
- $storage = new class(['datadir' => $this->tmpDir]) extends Local {
- #[\Override]
- public function __construct(array $parameters) {
- parent::__construct($parameters);
- $this->unlinkOnTruncate = true;
- }
-
- #[\Override]
- public function unlink(string $path): bool {
- throw new \RuntimeException('unlink failed');
- }
- };
-
- $ambient = umask(0077);
- $thrown = false;
- try {
- $storage->fopen('target.txt', 'w');
- } catch (\RuntimeException) {
- $thrown = true;
- }
- $leaked = umask($ambient);
-
- $this->assertTrue($thrown, 'the exception has to propagate');
- $this->assertSame(0077, $leaked, 'umask has to be restored when the write path throws');
- }
-
- public function testFopenRecoveryLeavesEntriesOutsideTheDataDirectoryAlone(): void {
- if (!function_exists('exec')) {
- $this->markTestSkipped('exec() is required to change the path type out of process');
- }
-
- $dataDir = rtrim($this->tmpDir, '/');
- $stalePath = $this->tmpDir . 'folder';
- $file = $stalePath . '/a.txt';
-
- // opened only to get the "not a directory" entry into the realpath cache
- $this->instance->file_put_contents('folder', 'its a file');
- $handle = $this->instance->fopen('folder', 'r');
- $this->assertIsResource($handle);
- fclose($handle);
-
- if ((realpath_cache_get()[$stalePath]['is_dir'] ?? null) !== false) {
- $this->markTestSkipped('the realpath cache of this setup does not hold the directory flag');
- }
-
- realpath(__DIR__);
- realpath($dataDir);
- $this->assertArrayHasKey(__DIR__, realpath_cache_get());
- $this->assertArrayHasKey($dataDir, realpath_cache_get());
-
- exec(
- 'rm -f ' . escapeshellarg($stalePath)
- . ' && mkdir -p ' . escapeshellarg($stalePath)
- . ' && printf abc > ' . escapeshellarg($file),
- $output,
- $status
- );
- $this->assertSame(0, $status, 'failed to replace the file with a directory');
-
- $handle = $this->instance->fopen('folder/a.txt', 'r');
- $this->assertIsResource($handle);
- fclose($handle);
-
- $cache = realpath_cache_get();
- $this->assertArrayHasKey(__DIR__, $cache, 'entries outside the data directory have to survive');
- $this->assertArrayHasKey($dataDir, $cache, 'the walk has to stop at the data directory');
- }
-
- public static function dataStaleRealpathCache(): array {
- return [
- // invalidating only the direct parent passes the first and fails the second
- 'stale direct parent' => ['staleName' => 'folder', 'filePath' => 'folder/a.txt'],
- 'stale grandparent' => ['staleName' => 'nickname', 'filePath' => 'nickname/folder/a.txt'],
- ];
- }
-
- /**
- * The type change has to happen out of process: PHP drops its own realpath cache
- * entry when it is the one calling unlink() and mkdir(), so doing it here would
- * leave nothing stale and the test would pass either way.
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataStaleRealpathCache')]
- public function testFopenRecoversFromStaleRealpathCache(string $staleName, string $filePath): void {
- if (!function_exists('exec')) {
- $this->markTestSkipped('exec() is required to change the path type out of process');
- }
-
- $stalePath = $this->tmpDir . $staleName;
- $file = $this->tmpDir . $filePath;
-
- // opened only to get the "not a directory" entry into the realpath cache
- $this->instance->file_put_contents($staleName, 'its a file');
- $handle = $this->instance->fopen($staleName, 'r');
- $this->assertIsResource($handle);
- fclose($handle);
-
- if ((realpath_cache_get()[$stalePath]['is_dir'] ?? null) !== false) {
- $this->markTestSkipped('the realpath cache of this setup does not hold the directory flag');
- }
-
- exec(
- 'rm -f ' . escapeshellarg($stalePath)
- . ' && mkdir -p ' . escapeshellarg(dirname($file))
- . ' && printf abc > ' . escapeshellarg($file),
- $output,
- $status
- );
- $this->assertSame(0, $status, 'failed to replace the file with a directory');
-
- $handle = $this->instance->fopen($filePath, 'r');
- $this->assertIsResource($handle, 'fopen() has to recover from the stale realpath cache entry');
- $this->assertSame('abc', stream_get_contents($handle));
- fclose($handle);
- }
-}
--- a/tests/lib/Files/Storage/Storage.php
+++ /dev/null
@@ -1,663 +0,0 @@
-waitDelay > 0) {
- sleep($this->waitDelay);
- }
- }
-
- /**
- * the root folder of the storage should always exist, be readable and be recognized as a directory
- */
- public function testRoot(): void {
- $this->assertTrue($this->instance->file_exists('/'), 'Root folder does not exist');
- $this->assertTrue($this->instance->isReadable('/'), 'Root folder is not readable');
- $this->assertTrue($this->instance->is_dir('/'), 'Root folder is not a directory');
- $this->assertFalse($this->instance->is_file('/'), 'Root folder is a file');
- $this->assertEquals('dir', $this->instance->filetype('/'));
-
- //without this, any further testing would be useless, not an actual requirement for filestorage though
- $this->assertTrue($this->instance->isUpdatable('/'), 'Root folder is not writable');
- }
-
- /**
- * Check that the test() function works
- */
- public function testTestFunction(): void {
- $this->assertTrue($this->instance->test());
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('directoryProvider')]
- public function testDirectories($directory): void {
- $this->assertFalse($this->instance->file_exists('/' . $directory));
-
- $this->assertTrue($this->instance->mkdir('/' . $directory));
-
- $this->assertTrue($this->instance->file_exists('/' . $directory));
- $this->assertTrue($this->instance->is_dir('/' . $directory));
- $this->assertFalse($this->instance->is_file('/' . $directory));
- $this->assertEquals('dir', $this->instance->filetype('/' . $directory));
- $this->assertEquals(0, $this->instance->filesize('/' . $directory));
- $this->assertTrue($this->instance->isReadable('/' . $directory));
- $this->assertTrue($this->instance->isUpdatable('/' . $directory));
-
- $dh = $this->instance->opendir('/');
- $content = [];
- while (($file = readdir($dh)) !== false) {
- if ($file !== '.' && $file !== '..') {
- $content[] = $file;
- }
- }
- $this->assertEquals([$directory], $content);
-
- $content = iterator_to_array($this->instance->getDirectoryContent('/'));
-
- $this->assertCount(1, $content);
- $dirEntry = $content[0];
- unset($dirEntry['scan_permissions']);
- unset($dirEntry['etag']);
- $this->assertLessThanOrEqual(1, abs($dirEntry['mtime'] - $this->instance->filemtime($directory)));
- unset($dirEntry['mtime']);
- unset($dirEntry['storage_mtime']);
- $this->assertEquals([
- 'name' => $directory,
- 'mimetype' => $this->instance->getMimeType($directory),
- 'size' => -1,
- 'permissions' => $this->instance->getPermissions($directory),
- ], $dirEntry);
-
- $this->assertFalse($this->instance->mkdir('/' . $directory)); //can't create existing folders
- $this->assertTrue($this->instance->rmdir('/' . $directory));
-
- $this->wait();
- $this->assertFalse($this->instance->file_exists('/' . $directory));
-
- $this->assertFalse($this->instance->rmdir('/' . $directory)); //can't remove non existing folders
-
- $dh = $this->instance->opendir('/');
- $content = [];
- while (($file = readdir($dh)) !== false) {
- if ($file !== '.' && $file !== '..') {
- $content[] = $file;
- }
- }
- $this->assertEquals([], $content);
- }
-
- public static function fileNameProvider(): array {
- return [
- ['file.txt'],
- [' file.txt'],
- ['folder .txt'],
- ['file with space.txt'],
- ['spéciäl fäile'],
- ['test single\'quote.txt'],
- /*['0'],*/ // disabled until upstream aws-sdk is patched
- ['#'],
- ['%'],
- ['%20'],
- ];
- }
-
- public static function directoryProvider(): array {
- return [
- ['folder'],
- [' folder'],
- ['folder '],
- ['folder with space'],
- ['spéciäl földer'],
- ['test single\'quote'],
- /*['0'],*/ // disabled until upstream aws-sdk is patched
- ['#'],
- ['%'],
- ['%20'],
- ];
- }
-
- public static function loremFileProvider(): array {
- $root = \OC::$SERVERROOT . '/tests/data/';
- return [
- // small file
- [$root . 'lorem.txt'],
- // bigger file (> 8 KB which is the standard PHP block size)
- [$root . 'lorem-big.txt']
- ];
- }
-
- /**
- * test the various uses of file_get_contents and file_put_contents
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('loremFileProvider')]
- public function testGetPutContents($sourceFile): void {
- $sourceText = file_get_contents($sourceFile);
-
- //fill a file with string data
- $this->instance->file_put_contents('/lorem.txt', $sourceText);
- $this->assertFalse($this->instance->is_dir('/lorem.txt'));
- $this->assertEquals($sourceText, $this->instance->file_get_contents('/lorem.txt'), 'data returned from file_get_contents is not equal to the source data');
-
- //empty the file
- $this->instance->file_put_contents('/lorem.txt', '');
- $this->assertEquals('', $this->instance->file_get_contents('/lorem.txt'), 'file not emptied');
- }
-
- /**
- * test various known mimetypes
- */
- public function testMimeType(): void {
- $this->assertEquals('httpd/unix-directory', $this->instance->getMimeType('/'));
- $this->assertEquals(false, $this->instance->getMimeType('/non/existing/file'));
-
- $textFile = \OC::$SERVERROOT . '/tests/data/lorem.txt';
- $this->instance->file_put_contents('/lorem.txt', file_get_contents($textFile, 'r'));
- $this->assertEquals('text/plain', $this->instance->getMimeType('/lorem.txt'));
-
- $pngFile = \OC::$SERVERROOT . '/tests/data/desktopapp.png';
- $this->instance->file_put_contents('/desktopapp.png', file_get_contents($pngFile, 'r'));
- $this->assertEquals('image/png', $this->instance->getMimeType('/desktopapp.png'));
-
- $svgFile = \OC::$SERVERROOT . '/tests/data/desktopapp.svg';
- $this->instance->file_put_contents('/desktopapp.svg', file_get_contents($svgFile, 'r'));
- $this->assertEquals('image/svg+xml', $this->instance->getMimeType('/desktopapp.svg'));
- }
-
- public static function copyAndMoveProvider(): array {
- return [
- ['/source.txt', '/target.txt'],
- ['/source.txt', '/target with space.txt'],
- ['/source with space.txt', '/target.txt'],
- ['/source with space.txt', '/target with space.txt'],
- ['/source.txt', '/tärgét.txt'],
- ['/sòurcē.txt', '/target.txt'],
- ['/sòurcē.txt', '/tärgét.txt'],
- ['/single \' quote.txt', '/tar\'get.txt'],
- ];
- }
-
- public function initSourceAndTarget($source, $target = null) {
- $textFile = \OC::$SERVERROOT . '/tests/data/lorem.txt';
- $this->instance->file_put_contents($source, file_get_contents($textFile));
- if ($target) {
- $testContents = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
- $this->instance->file_put_contents($target, $testContents);
- }
- }
-
- public function assertSameAsLorem($file) {
- $textFile = \OC::$SERVERROOT . '/tests/data/lorem.txt';
- $this->assertEquals(
- file_get_contents($textFile),
- $this->instance->file_get_contents($file),
- 'Expected ' . $file . ' to be a copy of ' . $textFile
- );
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('copyAndMoveProvider')]
- public function testCopy($source, $target): void {
- $this->initSourceAndTarget($source);
-
- $this->instance->copy($source, $target);
-
- $this->assertTrue($this->instance->file_exists($target), $target . ' was not created');
- $this->assertSameAsLorem($target);
- $this->assertTrue($this->instance->file_exists($source), $source . ' was deleted');
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('copyAndMoveProvider')]
- public function testMove($source, $target): void {
- $this->initSourceAndTarget($source);
-
- $this->instance->rename($source, $target);
-
- $this->wait();
- $this->assertTrue($this->instance->file_exists($target), $target . ' was not created');
- $this->assertFalse($this->instance->file_exists($source), $source . ' still exists');
- $this->assertSameAsLorem($target);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('copyAndMoveProvider')]
- public function testCopyOverwrite($source, $target): void {
- $this->initSourceAndTarget($source, $target);
-
- $this->instance->copy($source, $target);
-
- $this->assertTrue($this->instance->file_exists($target), $target . ' was not created');
- $this->assertTrue($this->instance->file_exists($source), $source . ' was deleted');
- $this->assertSameAsLorem($target);
- $this->assertSameAsLorem($source);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('copyAndMoveProvider')]
- public function testMoveOverwrite($source, $target): void {
- $this->initSourceAndTarget($source, $target);
-
- $this->instance->rename($source, $target);
-
- $this->assertTrue($this->instance->file_exists($target), $target . ' was not created');
- $this->assertFalse($this->instance->file_exists($source), $source . ' still exists');
- $this->assertSameAsLorem($target);
- }
-
- public function testLocal(): void {
- $textFile = \OC::$SERVERROOT . '/tests/data/lorem.txt';
- $this->instance->file_put_contents('/lorem.txt', file_get_contents($textFile));
- $localFile = $this->instance->getLocalFile('/lorem.txt');
- $this->assertTrue(file_exists($localFile));
- $this->assertEquals(file_get_contents($textFile), file_get_contents($localFile));
-
- $this->instance->mkdir('/folder');
- $this->instance->file_put_contents('/folder/lorem.txt', file_get_contents($textFile));
- $this->instance->file_put_contents('/folder/bar.txt', 'asd');
- $this->instance->mkdir('/folder/recursive');
- $this->instance->file_put_contents('/folder/recursive/file.txt', 'foo');
-
- // test below require to use instance->getLocalFile because the physical storage might be different
- $localFile = $this->instance->getLocalFile('/folder/lorem.txt');
- $this->assertTrue(file_exists($localFile));
- $this->assertEquals(file_get_contents($localFile), file_get_contents($textFile));
-
- $localFile = $this->instance->getLocalFile('/folder/bar.txt');
- $this->assertTrue(file_exists($localFile));
- $this->assertEquals(file_get_contents($localFile), 'asd');
-
- $localFile = $this->instance->getLocalFile('/folder/recursive/file.txt');
- $this->assertTrue(file_exists($localFile));
- $this->assertEquals(file_get_contents($localFile), 'foo');
- }
-
- public function testStat(): void {
- $textFile = \OC::$SERVERROOT . '/tests/data/lorem.txt';
- $ctimeStart = time();
- $this->instance->file_put_contents('/lorem.txt', file_get_contents($textFile));
- $this->assertTrue($this->instance->isReadable('/lorem.txt'));
- $ctimeEnd = time();
- $mTime = $this->instance->filemtime('/lorem.txt');
- $this->assertTrue($this->instance->hasUpdated('/lorem.txt', $ctimeStart - 5));
- $this->assertTrue($this->instance->hasUpdated('/', $ctimeStart - 5));
-
- // check that ($ctimeStart - 5) <= $mTime <= ($ctimeEnd + 1)
- $this->assertGreaterThanOrEqual(($ctimeStart - 5), $mTime);
- $this->assertLessThanOrEqual(($ctimeEnd + 1), $mTime);
- $this->assertEquals(filesize($textFile), $this->instance->filesize('/lorem.txt'));
-
- $stat = $this->instance->stat('/lorem.txt');
- //only size and mtime are required in the result
- $this->assertEquals($stat['size'], $this->instance->filesize('/lorem.txt'));
- $this->assertEquals($stat['mtime'], $mTime);
-
- if ($this->instance->touch('/lorem.txt', 100) !== false) {
- $mTime = $this->instance->filemtime('/lorem.txt');
- $this->assertEquals($mTime, 100);
- }
-
- $mtimeStart = time();
-
- $this->instance->unlink('/lorem.txt');
- $this->assertTrue($this->instance->hasUpdated('/', $mtimeStart - 5));
-
- $this->assertFalse($this->instance->filesize('/non-existing-file.txt'));
- }
-
- /**
- * Test whether checkUpdate properly returns false when there was
- * no change.
- */
- public function testCheckUpdate(): void {
- if ($this->instance instanceof Wrapper) {
- $this->markTestSkipped('Cannot test update check on wrappers');
- }
- $textFile = \OC::$SERVERROOT . '/tests/data/lorem.txt';
- $watcher = $this->instance->getWatcher();
- $watcher->setPolicy(Watcher::CHECK_ALWAYS);
- $this->instance->file_put_contents('/lorem.txt', file_get_contents($textFile));
- $this->assertTrue($watcher->checkUpdate('/lorem.txt'), 'Update detected');
- $this->assertFalse($watcher->checkUpdate('/lorem.txt'), 'No update');
- }
-
- public function testUnlink(): void {
- $textFile = \OC::$SERVERROOT . '/tests/data/lorem.txt';
- $this->instance->file_put_contents('/lorem.txt', file_get_contents($textFile));
-
- $this->assertTrue($this->instance->file_exists('/lorem.txt'));
-
- $this->assertTrue($this->instance->unlink('/lorem.txt'));
- $this->wait();
-
- $this->assertFalse($this->instance->file_exists('/lorem.txt'));
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('fileNameProvider')]
- public function testFOpen($fileName): void {
- $textFile = \OC::$SERVERROOT . '/tests/data/lorem.txt';
-
- $fh = @$this->instance->fopen($fileName, 'r');
- if ($fh) {
- fclose($fh);
- }
- $this->assertFalse($fh);
- $this->assertFalse($this->instance->file_exists($fileName));
-
- $fh = $this->instance->fopen($fileName, 'w');
- fwrite($fh, file_get_contents($textFile));
- fclose($fh);
- $this->assertTrue($this->instance->file_exists($fileName));
-
- $fh = $this->instance->fopen($fileName, 'r');
- $this->assertTrue(is_resource($fh));
- $content = stream_get_contents($fh);
- $this->assertEquals(file_get_contents($textFile), $content);
- }
-
- public function testTouchCreateFile(): void {
- $this->assertFalse($this->instance->file_exists('touch'));
- // returns true on success
- $this->assertTrue($this->instance->touch('touch'));
- $this->assertTrue($this->instance->file_exists('touch'));
- }
-
- public function testRecursiveRmdir(): void {
- $this->instance->mkdir('folder');
- $this->instance->mkdir('folder/bar');
- $this->wait();
- $this->instance->file_put_contents('folder/asd.txt', 'foobar');
- $this->instance->file_put_contents('folder/bar/foo.txt', 'asd');
- $this->assertTrue($this->instance->rmdir('folder'));
- $this->wait();
- $this->assertFalse($this->instance->file_exists('folder/asd.txt'));
- $this->assertFalse($this->instance->file_exists('folder/bar/foo.txt'));
- $this->assertFalse($this->instance->file_exists('folder/bar'));
- $this->assertFalse($this->instance->file_exists('folder'));
- }
-
- public function testRmdirEmptyFolder(): void {
- $this->assertTrue($this->instance->mkdir('empty'));
- $this->wait();
- $this->assertTrue($this->instance->rmdir('empty'));
- $this->assertFalse($this->instance->file_exists('empty'));
- }
-
- public function testRecursiveUnlink(): void {
- $this->instance->mkdir('folder');
- $this->instance->mkdir('folder/bar');
- $this->instance->file_put_contents('folder/asd.txt', 'foobar');
- $this->instance->file_put_contents('folder/bar/foo.txt', 'asd');
- $this->assertTrue($this->instance->unlink('folder'));
- $this->wait();
- $this->assertFalse($this->instance->file_exists('folder/asd.txt'));
- $this->assertFalse($this->instance->file_exists('folder/bar/foo.txt'));
- $this->assertFalse($this->instance->file_exists('folder/bar'));
- $this->assertFalse($this->instance->file_exists('folder'));
- }
-
- public static function hashProvider(): array {
- return [
- ['Foobar', 'md5'],
- ['Foobar', 'sha1'],
- ['Foobar', 'sha256'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('hashProvider')]
- public function testHash($data, $type): void {
- $this->instance->file_put_contents('hash.txt', $data);
- $this->assertEquals(hash($type, $data), $this->instance->hash($type, 'hash.txt'));
- $this->assertEquals(hash($type, $data, true), $this->instance->hash($type, 'hash.txt', true));
- }
-
- public function testHashInFileName(): void {
- $this->instance->file_put_contents('#test.txt', 'data');
- $this->assertEquals('data', $this->instance->file_get_contents('#test.txt'));
-
- $this->instance->mkdir('#foo');
- $this->instance->file_put_contents('#foo/test.txt', 'data');
- $this->assertEquals('data', $this->instance->file_get_contents('#foo/test.txt'));
-
- $dh = $this->instance->opendir('#foo');
- $content = [];
- while ($file = readdir($dh)) {
- if ($file !== '.' && $file !== '..') {
- $content[] = $file;
- }
- }
-
- $this->assertEquals(['test.txt'], $content);
- }
-
- public function testCopyOverWriteFile(): void {
- $this->instance->file_put_contents('target.txt', 'foo');
- $this->instance->file_put_contents('source.txt', 'bar');
- $this->instance->copy('source.txt', 'target.txt');
- $this->assertEquals('bar', $this->instance->file_get_contents('target.txt'));
- }
-
- public function testRenameOverWriteFile(): void {
- $this->instance->file_put_contents('target.txt', 'foo');
- $this->instance->file_put_contents('source.txt', 'bar');
- $this->instance->rename('source.txt', 'target.txt');
- $this->assertEquals('bar', $this->instance->file_get_contents('target.txt'));
- $this->assertFalse($this->instance->file_exists('source.txt'));
- }
-
- public function testRenameDirectory(): void {
- $this->instance->mkdir('source');
- $this->instance->file_put_contents('source/test1.txt', 'foo');
- $this->instance->file_put_contents('source/test2.txt', 'qwerty');
- $this->instance->mkdir('source/subfolder');
- $this->instance->file_put_contents('source/subfolder/test.txt', 'bar');
- $this->instance->rename('source', 'target');
-
- $this->assertFalse($this->instance->file_exists('source'));
- $this->assertFalse($this->instance->file_exists('source/test1.txt'));
- $this->assertFalse($this->instance->file_exists('source/test2.txt'));
- $this->assertFalse($this->instance->file_exists('source/subfolder'));
- $this->assertFalse($this->instance->file_exists('source/subfolder/test.txt'));
-
- $this->assertTrue($this->instance->file_exists('target'));
- $this->assertTrue($this->instance->file_exists('target/test1.txt'));
- $this->assertTrue($this->instance->file_exists('target/test2.txt'));
- $this->assertTrue($this->instance->file_exists('target/subfolder'));
- $this->assertTrue($this->instance->file_exists('target/subfolder/test.txt'));
-
- $contents = iterator_to_array($this->instance->getDirectoryContent(''));
- $this->assertCount(1, $contents);
-
- $this->assertEquals('foo', $this->instance->file_get_contents('target/test1.txt'));
- $this->assertEquals('qwerty', $this->instance->file_get_contents('target/test2.txt'));
- $this->assertEquals('bar', $this->instance->file_get_contents('target/subfolder/test.txt'));
- }
-
- public function testRenameOverWriteDirectory(): void {
- $this->instance->mkdir('source');
- $this->instance->file_put_contents('source/test1.txt', 'foo');
-
- $this->instance->mkdir('target');
- $this->instance->file_put_contents('target/test1.txt', 'bar');
- $this->instance->file_put_contents('target/test2.txt', 'bar');
-
- $this->assertTrue($this->instance->rename('source', 'target'), 'rename must return true on success');
-
- $this->assertFalse($this->instance->file_exists('source'), 'source has not been removed');
- $this->assertFalse($this->instance->file_exists('source/test1.txt'), 'source/test1.txt has not been removed');
- $this->assertFalse($this->instance->file_exists('target/test2.txt'), 'target/test2.txt has not been removed');
- $this->assertEquals('foo', $this->instance->file_get_contents('target/test1.txt'), 'target/test1.txt has not been overwritten');
- }
-
- public function testRenameOverWriteDirectoryOverFile(): void {
- $this->instance->mkdir('source');
- $this->instance->file_put_contents('source/test1.txt', 'foo');
-
- $this->instance->file_put_contents('target', 'bar');
-
- $this->assertTrue($this->instance->rename('source', 'target'), 'rename must return true on success');
-
- $this->assertFalse($this->instance->file_exists('source'));
- $this->assertFalse($this->instance->file_exists('source/test1.txt'));
- $this->assertEquals('foo', $this->instance->file_get_contents('target/test1.txt'));
- }
-
- public function testCopyDirectory(): void {
- $this->instance->mkdir('source');
- $this->instance->file_put_contents('source/test1.txt', 'foo');
- $this->instance->file_put_contents('source/test2.txt', 'qwerty');
- $this->instance->mkdir('source/subfolder');
- $this->instance->file_put_contents('source/subfolder/test.txt', 'bar');
- $this->instance->copy('source', 'target');
-
- $this->assertTrue($this->instance->file_exists('source'));
- $this->assertTrue($this->instance->file_exists('source/test1.txt'));
- $this->assertTrue($this->instance->file_exists('source/test2.txt'));
- $this->assertTrue($this->instance->file_exists('source/subfolder'));
- $this->assertTrue($this->instance->file_exists('source/subfolder/test.txt'));
-
- $this->assertTrue($this->instance->file_exists('target'));
- $this->assertTrue($this->instance->file_exists('target/test1.txt'));
- $this->assertTrue($this->instance->file_exists('target/test2.txt'));
- $this->assertTrue($this->instance->file_exists('target/subfolder'));
- $this->assertTrue($this->instance->file_exists('target/subfolder/test.txt'));
-
- $this->assertEquals('foo', $this->instance->file_get_contents('target/test1.txt'));
- $this->assertEquals('qwerty', $this->instance->file_get_contents('target/test2.txt'));
- $this->assertEquals('bar', $this->instance->file_get_contents('target/subfolder/test.txt'));
- }
-
- public function testCopyOverWriteDirectory(): void {
- $this->instance->mkdir('source');
- $this->instance->file_put_contents('source/test1.txt', 'foo');
-
- $this->instance->mkdir('target');
- $this->instance->file_put_contents('target/test1.txt', 'bar');
- $this->instance->file_put_contents('target/test2.txt', 'bar');
-
- $this->instance->copy('source', 'target');
-
- $this->assertFalse($this->instance->file_exists('target/test2.txt'), 'File target/test2.txt should no longer exist, but does');
- $this->assertEquals('foo', $this->instance->file_get_contents('target/test1.txt'));
- }
-
- public function testCopyOverWriteDirectoryOverFile(): void {
- $this->instance->mkdir('source');
- $this->instance->file_put_contents('source/test1.txt', 'foo');
-
- $this->instance->file_put_contents('target', 'bar');
-
- $this->instance->copy('source', 'target');
-
- $this->assertEquals('foo', $this->instance->file_get_contents('target/test1.txt'));
- }
-
- public function testInstanceOfStorage(): void {
- $this->assertTrue($this->instance->instanceOfStorage(IStorage::class));
- $this->assertTrue($this->instance->instanceOfStorage(get_class($this->instance)));
- $this->assertFalse($this->instance->instanceOfStorage('\OC'));
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('copyAndMoveProvider')]
- public function testCopyFromSameStorage($source, $target): void {
- $this->initSourceAndTarget($source);
-
- $this->instance->copyFromStorage($this->instance, $source, $target);
-
- $this->assertTrue($this->instance->file_exists($target), $target . ' was not created');
- $this->assertSameAsLorem($target);
- $this->assertTrue($this->instance->file_exists($source), $source . ' was deleted');
- }
-
- public function testIsCreatable(): void {
- $this->instance->mkdir('source');
- $this->assertTrue($this->instance->isCreatable('source'));
- }
-
- public function testIsReadable(): void {
- $this->instance->mkdir('source');
- $this->assertTrue($this->instance->isReadable('source'));
- }
-
- public function testIsUpdatable(): void {
- $this->instance->mkdir('source');
- $this->assertTrue($this->instance->isUpdatable('source'));
- }
-
- public function testIsDeletable(): void {
- $this->instance->mkdir('source');
- $this->assertTrue($this->instance->isDeletable('source'));
- }
-
- public function testIsShareable(): void {
- $this->instance->mkdir('source');
- $this->assertTrue($this->instance->isSharable('source'));
- }
-
- public function testStatAfterWrite(): void {
- $this->instance->file_put_contents('foo.txt', 'bar');
- $stat = $this->instance->stat('foo.txt');
- $this->assertEquals(3, $stat['size']);
-
- $fh = $this->instance->fopen('foo.txt', 'w');
- fwrite($fh, 'qwerty');
- fclose($fh);
-
- $stat = $this->instance->stat('foo.txt');
- $this->assertEquals(6, $stat['size']);
- }
-
- public function testPartFile(): void {
- $this->instance->file_put_contents('bar.txt.part', 'bar');
- $this->instance->rename('bar.txt.part', 'bar.txt');
- $this->assertEquals('bar', $this->instance->file_get_contents('bar.txt'));
- }
-
- public function testWriteStream(): void {
- $textFile = \OC::$SERVERROOT . '/tests/data/lorem.txt';
-
- if (!$this->instance->instanceOfStorage(IWriteStreamStorage::class)) {
- $this->markTestSkipped('Not a WriteSteamStorage');
- }
- /** @var IWriteStreamStorage $storage */
- $storage = $this->instance;
-
- $source = fopen($textFile, 'r');
-
- $storage->writeStream('test.txt', $source);
- $this->assertTrue($storage->file_exists('test.txt'));
- $this->assertStringEqualsFile($textFile, $storage->file_get_contents('test.txt'));
- $this->assertEquals('resource (closed)', gettype($source));
- }
-
- public function testFseekSize(): void {
- $textFile = \OC::$SERVERROOT . '/tests/data/lorem.txt';
- $this->instance->file_put_contents('bar.txt', file_get_contents($textFile));
-
- $size = $this->instance->filesize('bar.txt');
- $this->assertEquals(filesize($textFile), $size);
- $fh = $this->instance->fopen('bar.txt', 'r');
-
- fseek($fh, 0, SEEK_END);
- $pos = ftell($fh);
-
- $this->assertEquals($size, $pos);
- }
-}
--- a/tests/lib/Files/Storage/StorageFactoryTest.php
+++ /dev/null
@@ -1,73 +0,0 @@
-data = $parameters['data'];
- }
- }
-}
-
-class StorageFactoryTest extends TestCase {
- public function testSimpleWrapper(): void {
- $instance = new StorageFactory();
- $mount = new MountPoint('\OC\Files\Storage\Temporary', '/foo', [[]], $instance);
- $instance->addStorageWrapper('dummy', function ($mountPoint, IStorage $storage, IMountPoint $mount) {
- $this->assertInstanceOf('\OC\Files\Storage\Temporary', $storage);
- $this->assertEquals('/foo/', $mount->getMountPoint());
- $this->assertEquals('/foo/', $mountPoint);
- return new DummyWrapper(['storage' => $storage]);
- });
- $wrapped = $mount->getStorage();
- $this->assertInstanceOf('\Test\Files\Storage\DummyWrapper', $wrapped);
- }
-
- public function testRemoveWrapper(): void {
- $instance = new StorageFactory();
- $mount = new MountPoint('\OC\Files\Storage\Temporary', '/foo', [[]], $instance);
- $instance->addStorageWrapper('dummy', function ($mountPoint, IStorage $storage) {
- return new DummyWrapper(['storage' => $storage]);
- });
- $instance->removeStorageWrapper('dummy');
- $wrapped = $mount->getStorage();
- $this->assertInstanceOf('\OC\Files\Storage\Temporary', $wrapped);
- }
-
- public function testWrapperPriority(): void {
- $instance = new StorageFactory();
- $mount = new MountPoint('\OC\Files\Storage\Temporary', '/foo', [[]], $instance);
- $instance->addStorageWrapper('dummy1', function ($mountPoint, IStorage $storage) {
- return new DummyWrapper(['storage' => $storage, 'data' => 1]);
- }, 1);
- $instance->addStorageWrapper('dummy2', function ($mountPoint, IStorage $storage) {
- return new DummyWrapper(['storage' => $storage, 'data' => 100]);
- }, 100);
- $instance->addStorageWrapper('dummy3', function ($mountPoint, IStorage $storage) {
- return new DummyWrapper(['storage' => $storage, 'data' => 50]);
- }, 50);
- /** @var \Test\Files\Storage\DummyWrapper $wrapped */
- $wrapped = $mount->getStorage();
- $this->assertInstanceOf('\Test\Files\Storage\DummyWrapper', $wrapped);
- $this->assertEquals(1, $wrapped->data);// lowest priority is applied last, called first
- $this->assertEquals(50, $wrapped->getWrapperStorage()->data);
- $this->assertEquals(100, $wrapped->getWrapperStorage()->getWrapperStorage()->data);
- }
-}
--- a/tests/lib/Files/Storage/StoragesTestCase.php
+++ /dev/null
@@ -1,129 +0,0 @@
-storage1) && is_null($this->storage2)) {
- return;
- }
- $this->storage1->getCache()->clear();
- $this->storage2->getCache()->clear();
-
- parent::tearDown();
- }
-
- public function testMoveFileFromStorage() {
- $source = 'source.txt';
- $target = 'target.txt';
- $this->storage2->file_put_contents($source, 'foo');
-
- $this->storage1->moveFromStorage($this->storage2, $source, $target);
-
- $this->assertTrue($this->storage1->file_exists($target), $target . ' was not created');
- $this->assertFalse($this->storage2->file_exists($source), $source . ' still exists');
- $this->assertEquals('foo', $this->storage1->file_get_contents($target));
- }
-
- public function testMoveFileFromStorageWithExistingTarget() {
- $source = 'source.txt';
- $target = 'target.txt';
- $this->storage1->file_put_contents($target, 'bar');
- $this->storage2->file_put_contents($source, 'foo');
-
- $targetURN = $this->storage1->getURN($this->storage1->getCache()->get($target)->getID());
- $sourceURN = $this->storage2->getURN($this->storage2->getCache()->get($source)->getID());
-
- $this->storage1->moveFromStorage($this->storage2, $source, $target);
-
- $this->assertTrue($this->storage1->file_exists($target), $target . ' was not created in DB');
- $this->assertFalse($this->storage2->file_exists($source), $source . ' still exists in DB');
- $this->assertTrue($this->storage1->getObjectStore()->objectExists($sourceURN), $sourceURN . ' was not created in bucket');
- $this->assertFalse($this->storage1->getObjectStore()->objectExists($targetURN), $targetURN . ' still exists in bucket');
- $this->assertEquals('foo', $this->storage1->file_get_contents($target));
- }
-
- public function testMoveDirectoryFromStorage() {
- $this->storage2->mkdir('source');
- $this->storage2->file_put_contents('source/test1.txt', 'foo');
- $this->storage2->file_put_contents('source/test2.txt', 'qwerty');
- $this->storage2->mkdir('source/subfolder');
- $this->storage2->file_put_contents('source/subfolder/test.txt', 'bar');
-
- $this->storage1->moveFromStorage($this->storage2, 'source', 'target');
-
- $this->assertTrue($this->storage1->file_exists('target'));
- $this->assertTrue($this->storage1->file_exists('target/test1.txt'));
- $this->assertTrue($this->storage1->file_exists('target/test2.txt'));
- $this->assertTrue($this->storage1->file_exists('target/subfolder'));
- $this->assertTrue($this->storage1->file_exists('target/subfolder/test.txt'));
-
- $this->assertFalse($this->storage2->file_exists('source'));
- $this->assertFalse($this->storage2->file_exists('source/test1.txt'));
- $this->assertFalse($this->storage2->file_exists('source/test2.txt'));
- $this->assertFalse($this->storage2->file_exists('source/subfolder'));
- $this->assertFalse($this->storage2->file_exists('source/subfolder/test.txt'));
-
- $this->assertEquals('foo', $this->storage1->file_get_contents('target/test1.txt'));
- $this->assertEquals('qwerty', $this->storage1->file_get_contents('target/test2.txt'));
- $this->assertEquals('bar', $this->storage1->file_get_contents('target/subfolder/test.txt'));
- }
-
- public function testCopyFileFromStorage() {
- $source = 'source.txt';
- $target = 'target.txt';
- $this->storage2->file_put_contents($source, 'foo');
-
- $this->storage1->copyFromStorage($this->storage2, $source, $target);
-
- $this->assertTrue($this->storage1->file_exists($target), $target . ' was not created');
- $this->assertTrue($this->storage2->file_exists($source), $source . ' was deleted');
- $this->assertEquals('foo', $this->storage1->file_get_contents($target));
- }
-
- public function testCopyDirectoryFromStorage() {
- $this->storage2->mkdir('source');
- $this->storage2->file_put_contents('source/test1.txt', 'foo');
- $this->storage2->file_put_contents('source/test2.txt', 'qwerty');
- $this->storage2->mkdir('source/subfolder');
- $this->storage2->file_put_contents('source/subfolder/test.txt', 'bar');
-
- $this->storage1->copyFromStorage($this->storage2, 'source', 'target');
-
- $this->assertTrue($this->storage1->file_exists('target'));
- $this->assertTrue($this->storage1->file_exists('target/test1.txt'));
- $this->assertTrue($this->storage1->file_exists('target/test2.txt'));
- $this->assertTrue($this->storage1->file_exists('target/subfolder'));
- $this->assertTrue($this->storage1->file_exists('target/subfolder/test.txt'));
-
- $this->assertTrue($this->storage2->file_exists('source'));
- $this->assertTrue($this->storage2->file_exists('source/test1.txt'));
- $this->assertTrue($this->storage2->file_exists('source/test2.txt'));
- $this->assertTrue($this->storage2->file_exists('source/subfolder'));
- $this->assertTrue($this->storage2->file_exists('source/subfolder/test.txt'));
-
- $this->assertEquals('foo', $this->storage1->file_get_contents('target/test1.txt'));
- $this->assertEquals('qwerty', $this->storage1->file_get_contents('target/test2.txt'));
- $this->assertEquals('bar', $this->storage1->file_get_contents('target/subfolder/test.txt'));
- }
-}
--- a/tests/lib/Files/Storage/Wrapper/AvailabilityTest.php
+++ /dev/null
@@ -1,185 +0,0 @@
-storageCache = $this->createMock(StorageCache::class);
-
- $this->storage = $this->createMock(Temporary::class);
- $this->storage->expects($this->any())
- ->method('getStorageCache')
- ->willReturn($this->storageCache);
-
- $this->wrapper = new Availability(['storage' => $this->storage]);
- }
-
- /**
- * Storage is available
- */
- public function testAvailable(): void {
- $this->storage->expects($this->once())
- ->method('getAvailability')
- ->willReturn(['available' => true, 'last_checked' => 0]);
- $this->storage->expects($this->never())
- ->method('test');
- $this->storage->expects($this->once())
- ->method('mkdir');
-
- $this->wrapper->mkdir('foobar');
- }
-
- /**
- * Storage marked unavailable, TTL not expired
- *
- */
- public function testUnavailable(): void {
- $this->expectException(StorageNotAvailableException::class);
-
- $this->storage->expects($this->once())
- ->method('getAvailability')
- ->willReturn(['available' => false, 'last_checked' => time()]);
- $this->storage->expects($this->never())
- ->method('test');
- $this->storage->expects($this->never())
- ->method('mkdir');
-
- $this->wrapper->mkdir('foobar');
- }
-
- /**
- * Storage marked unavailable, TTL expired
- */
- public function testUnavailableRecheck(): void {
- $this->storage->expects($this->once())
- ->method('getAvailability')
- ->willReturn(['available' => false, 'last_checked' => 0]);
- $this->storage->expects($this->once())
- ->method('test')
- ->willReturn(true);
- $calls = [
- false, // prevents concurrent rechecks
- true, // sets correct availability
- ];
- $this->storage->expects($this->exactly(2))
- ->method('setAvailability')
- ->willReturnCallback(function ($value) use (&$calls): void {
- $expected = array_shift($calls);
- $this->assertEquals($expected, $value);
- });
- $this->storage->expects($this->once())
- ->method('mkdir');
-
- $this->wrapper->mkdir('foobar');
- }
-
- /**
- * Storage marked available, but throws StorageNotAvailableException
- *
- */
- public function testAvailableThrowStorageNotAvailable(): void {
- $this->expectException(StorageNotAvailableException::class);
-
- $this->storage->expects($this->once())
- ->method('getAvailability')
- ->willReturn(['available' => true, 'last_checked' => 0]);
- $this->storage->expects($this->never())
- ->method('test');
- $this->storage->expects($this->once())
- ->method('mkdir')
- ->willThrowException(new StorageNotAvailableException());
- $this->storageCache->expects($this->once())
- ->method('setAvailability')
- ->with($this->equalTo(false));
-
- $this->wrapper->mkdir('foobar');
- }
-
- /**
- * Storage available, but call fails
- * Method failure does not indicate storage unavailability
- */
- public function testAvailableFailure(): void {
- $this->storage->expects($this->once())
- ->method('getAvailability')
- ->willReturn(['available' => true, 'last_checked' => 0]);
- $this->storage->expects($this->never())
- ->method('test');
- $this->storage->expects($this->once())
- ->method('mkdir')
- ->willReturn(false);
- $this->storage->expects($this->never())
- ->method('setAvailability');
-
- $this->wrapper->mkdir('foobar');
- }
-
- /**
- * Storage available, but throws exception
- * Standard exception does not indicate storage unavailability
- *
- */
- public function testAvailableThrow(): void {
- $this->expectException(\Exception::class);
-
- $this->storage->expects($this->once())
- ->method('getAvailability')
- ->willReturn(['available' => true, 'last_checked' => 0]);
- $this->storage->expects($this->never())
- ->method('test');
- $this->storage->expects($this->once())
- ->method('mkdir')
- ->willThrowException(new \Exception());
- $this->storage->expects($this->never())
- ->method('setAvailability');
-
- $this->wrapper->mkdir('foobar');
- }
-
- public function testUnavailableMultiple(): void {
- $this->storage->expects($this->once())
- ->method('getAvailability')
- ->willReturn(['available' => true, 'last_checked' => 0]);
- $this->storage->expects($this->never())
- ->method('test');
- $this->storage
- ->expects($this->once()) // load-bearing `once`
- ->method('mkdir')
- ->willThrowException(new StorageNotAvailableException());
-
- try {
- $this->wrapper->mkdir('foobar');
- $this->fail();
- } catch (StorageNotAvailableException) {
- }
-
- $this->storage->expects($this->never())->method('file_exists');
-
- try {
- $this->wrapper->mkdir('foobar');
- $this->fail();
- } catch (StorageNotAvailableException) {
- }
- }
-}
--- a/tests/lib/Files/Storage/Wrapper/EncodingTest.php
+++ /dev/null
@@ -1,244 +0,0 @@
-sourceStorage = new Temporary([]);
- $this->instance = new Encoding([
- 'storage' => $this->sourceStorage
- ]);
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->sourceStorage->cleanUp();
- parent::tearDown();
- }
-
- #[\Override]
- public static function directoryProvider(): array {
- $a = parent::directoryProvider();
- $a[] = [self::NFC_NAME];
- return $a;
- }
-
- #[\Override]
- public static function fileNameProvider(): array {
- $a = parent::fileNameProvider();
- $a[] = [self::NFD_NAME . '.txt'];
- return $a;
- }
-
- #[\Override]
- public static function copyAndMoveProvider(): array {
- $a = parent::copyAndMoveProvider();
- $a[] = [self::NFD_NAME . '.txt', self::NFC_NAME . '-renamed.txt'];
- return $a;
- }
-
- public static function accessNameProvider(): array {
- return [
- [self::NFD_NAME],
- [self::NFC_NAME],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('accessNameProvider')]
- public function testFputEncoding($accessName): void {
- $this->sourceStorage->file_put_contents(self::NFD_NAME, 'bar');
- $this->assertEquals('bar', $this->instance->file_get_contents($accessName));
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('accessNameProvider')]
- public function testFopenReadEncoding($accessName): void {
- $this->sourceStorage->file_put_contents(self::NFD_NAME, 'bar');
- $fh = $this->instance->fopen($accessName, 'r');
- $data = fgets($fh);
- fclose($fh);
- $this->assertEquals('bar', $data);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('accessNameProvider')]
- public function testFopenOverwriteEncoding($accessName): void {
- $this->sourceStorage->file_put_contents(self::NFD_NAME, 'bar');
- $fh = $this->instance->fopen($accessName, 'w');
- $data = fputs($fh, 'test');
- fclose($fh);
- $data = $this->sourceStorage->file_get_contents(self::NFD_NAME);
- $this->assertEquals('test', $data);
- $this->assertFalse($this->sourceStorage->file_exists(self::NFC_NAME));
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('accessNameProvider')]
- public function testFileExistsEncoding($accessName): void {
- $this->sourceStorage->file_put_contents(self::NFD_NAME, 'bar');
- $this->assertTrue($this->instance->file_exists($accessName));
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('accessNameProvider')]
- public function testUnlinkEncoding($accessName): void {
- $this->sourceStorage->file_put_contents(self::NFD_NAME, 'bar');
- $this->assertTrue($this->instance->unlink($accessName));
- $this->assertFalse($this->sourceStorage->file_exists(self::NFC_NAME));
- $this->assertFalse($this->sourceStorage->file_exists(self::NFD_NAME));
- }
-
- public function testNfcHigherPriority(): void {
- $this->sourceStorage->file_put_contents(self::NFC_NAME, 'nfc');
- $this->sourceStorage->file_put_contents(self::NFD_NAME, 'nfd');
- $this->assertEquals('nfc', $this->instance->file_get_contents(self::NFC_NAME));
- }
-
- public static function encodedDirectoriesProvider(): array {
- return [
- [self::NFD_NAME, self::NFC_NAME],
- [self::NFD_NAME . '/' . self::NFD_NAME, self::NFC_NAME . '/' . self::NFC_NAME],
- [self::NFD_NAME . '/' . self::NFC_NAME . '/' . self::NFD_NAME, self::NFC_NAME . '/' . self::NFC_NAME . '/' . self::NFC_NAME],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('encodedDirectoriesProvider')]
- public function testOperationInsideDirectory($sourceDir, $accessDir): void {
- $this->sourceStorage->mkdir($sourceDir);
- $this->instance->file_put_contents($accessDir . '/test.txt', 'bar');
- $this->assertTrue($this->instance->file_exists($accessDir . '/test.txt'));
- $this->assertEquals('bar', $this->instance->file_get_contents($accessDir . '/test.txt'));
-
- $this->sourceStorage->file_put_contents($sourceDir . '/' . self::NFD_NAME, 'foo');
- $this->assertTrue($this->instance->file_exists($accessDir . '/' . self::NFC_NAME));
- $this->assertEquals('foo', $this->instance->file_get_contents($accessDir . '/' . self::NFC_NAME));
-
- // try again to make it use cached path
- $this->assertEquals('bar', $this->instance->file_get_contents($accessDir . '/test.txt'));
- $this->assertTrue($this->instance->file_exists($accessDir . '/test.txt'));
- $this->assertEquals('foo', $this->instance->file_get_contents($accessDir . '/' . self::NFC_NAME));
- $this->assertTrue($this->instance->file_exists($accessDir . '/' . self::NFC_NAME));
- }
-
- public function testCacheExtraSlash(): void {
- $this->sourceStorage->file_put_contents(self::NFD_NAME, 'foo');
- $this->assertEquals(3, $this->instance->file_put_contents(self::NFC_NAME, 'bar'));
- $this->assertEquals('bar', $this->instance->file_get_contents(self::NFC_NAME));
- clearstatcache();
- $this->assertEquals(5, $this->instance->file_put_contents('/' . self::NFC_NAME, 'baric'));
- $this->assertEquals('baric', $this->instance->file_get_contents(self::NFC_NAME));
- clearstatcache();
- $this->assertEquals(8, $this->instance->file_put_contents('/' . self::NFC_NAME, 'barbaric'));
- $this->assertEquals('barbaric', $this->instance->file_get_contents('//' . self::NFC_NAME));
- }
-
- public static function sourceAndTargetDirectoryProvider(): array {
- return [
- [self::NFC_NAME . '1', self::NFC_NAME . '2'],
- [self::NFD_NAME . '1', self::NFC_NAME . '2'],
- [self::NFC_NAME . '1', self::NFD_NAME . '2'],
- [self::NFD_NAME . '1', self::NFD_NAME . '2'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('sourceAndTargetDirectoryProvider')]
- public function testCopyAndMoveEncodedFolder($sourceDir, $targetDir): void {
- $this->sourceStorage->mkdir($sourceDir);
- $this->sourceStorage->mkdir($targetDir);
- $this->sourceStorage->file_put_contents($sourceDir . '/test.txt', 'bar');
- $this->assertTrue($this->instance->copy(self::NFC_NAME . '1/test.txt', self::NFC_NAME . '2/test.txt'));
-
- $this->assertTrue($this->instance->file_exists(self::NFC_NAME . '1/test.txt'));
- $this->assertTrue($this->instance->file_exists(self::NFC_NAME . '2/test.txt'));
- $this->assertEquals('bar', $this->instance->file_get_contents(self::NFC_NAME . '2/test.txt'));
-
- $this->assertTrue($this->instance->rename(self::NFC_NAME . '1/test.txt', self::NFC_NAME . '2/test2.txt'));
- $this->assertFalse($this->instance->file_exists(self::NFC_NAME . '1/test.txt'));
- $this->assertTrue($this->instance->file_exists(self::NFC_NAME . '2/test2.txt'));
-
- $this->assertEquals('bar', $this->instance->file_get_contents(self::NFC_NAME . '2/test2.txt'));
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('sourceAndTargetDirectoryProvider')]
- public function testCopyAndMoveFromStorageEncodedFolder($sourceDir, $targetDir): void {
- $this->sourceStorage->mkdir($sourceDir);
- $this->sourceStorage->mkdir($targetDir);
- $this->sourceStorage->file_put_contents($sourceDir . '/test.txt', 'bar');
- $this->assertTrue($this->instance->copyFromStorage($this->instance, self::NFC_NAME . '1/test.txt', self::NFC_NAME . '2/test.txt'));
-
- $this->assertTrue($this->instance->file_exists(self::NFC_NAME . '1/test.txt'));
- $this->assertTrue($this->instance->file_exists(self::NFC_NAME . '2/test.txt'));
- $this->assertEquals('bar', $this->instance->file_get_contents(self::NFC_NAME . '2/test.txt'));
-
- $this->assertTrue($this->instance->moveFromStorage($this->instance, self::NFC_NAME . '1/test.txt', self::NFC_NAME . '2/test2.txt'));
- $this->assertFalse($this->instance->file_exists(self::NFC_NAME . '1/test.txt'));
- $this->assertTrue($this->instance->file_exists(self::NFC_NAME . '2/test2.txt'));
-
- $this->assertEquals('bar', $this->instance->file_get_contents(self::NFC_NAME . '2/test2.txt'));
- }
-
- public function testNormalizedDirectoryEntriesOpenDir(): void {
- $this->sourceStorage->mkdir('/test');
- $this->sourceStorage->mkdir('/test/' . self::NFD_NAME);
-
- $this->assertTrue($this->instance->file_exists('/test/' . self::NFC_NAME));
- $this->assertTrue($this->instance->file_exists('/test/' . self::NFD_NAME));
-
- $dh = $this->instance->opendir('/test');
- $content = [];
- while (($file = readdir($dh)) !== false) {
- if ($file !== '.' && $file !== '..') {
- $content[] = $file;
- }
- }
-
- $this->assertCount(1, $content);
- $this->assertEquals(self::NFC_NAME, $content[0]);
- }
-
- public function testNormalizedDirectoryEntriesGetDirectoryContent(): void {
- $this->sourceStorage->mkdir('/test');
- $this->sourceStorage->mkdir('/test/' . self::NFD_NAME);
-
- $this->assertTrue($this->instance->file_exists('/test/' . self::NFC_NAME));
- $this->assertTrue($this->instance->file_exists('/test/' . self::NFD_NAME));
-
- $content = iterator_to_array($this->instance->getDirectoryContent('/test'));
- $this->assertCount(1, $content);
- $this->assertEquals(self::NFC_NAME, $content[0]['name']);
- }
-
- public function testNormalizedGetMetaData(): void {
- $this->sourceStorage->mkdir('/test');
- $this->sourceStorage->mkdir('/test/' . self::NFD_NAME);
-
- $entry = $this->instance->getMetaData('/test/' . self::NFC_NAME);
- $this->assertEquals(self::NFC_NAME, $entry['name']);
-
- $entry = $this->instance->getMetaData('/test/' . self::NFD_NAME);
- $this->assertEquals(self::NFC_NAME, $entry['name']);
- }
-
- /**
- * Regression test of https://github.com/nextcloud/server/issues/50431
- */
- public function testNoMetadata() {
- $this->assertNull($this->instance->getMetaData('/test/null'));
- }
-
-}
--- a/tests/lib/Files/Storage/Wrapper/EncryptionTest.php
+++ /dev/null
@@ -1,1099 +0,0 @@
-buildMockModule();
- $this->encryptionManager = $this->getMockBuilder(\OC\Encryption\Manager::class)
- ->disableOriginalConstructor()
- ->onlyMethods(['getEncryptionModule', 'isEnabled'])
- ->getMock();
- $this->encryptionManager->expects($this->any())
- ->method('getEncryptionModule')
- ->willReturn($mockModule);
-
- $this->arrayCache = $this->createMock(ArrayCache::class);
- $this->config = $this->getMockBuilder(IConfig::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->groupManager = $this->getMockBuilder('\OC\Group\Manager')
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->util = $this->getMockBuilder(Util::class)
- ->onlyMethods(['getUidAndFilename', 'isFile', 'isExcluded', 'stripPartialFileExtension'])
- ->setConstructorArgs([new View(), new Manager(
- $this->config,
- $this->createMock(ICacheFactory::class),
- $this->createMock(IEventDispatcher::class),
- $this->createMock(LoggerInterface::class),
- ), $this->groupManager, $this->config, $this->arrayCache])
- ->getMock();
- $this->util->expects($this->any())
- ->method('getUidAndFilename')
- ->willReturnCallback(function ($path) {
- return ['user1', $path];
- });
- $this->util->expects($this->any())
- ->method('stripPartialFileExtension')
- ->willReturnCallback(function ($path) {
- return $path;
- });
-
- $this->file = $this->getMockBuilder(File::class)
- ->disableOriginalConstructor()
- ->onlyMethods(['getAccessList'])
- ->getMock();
- $this->file->expects($this->any())->method('getAccessList')->willReturn([]);
-
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $this->sourceStorage = new Temporary([]);
-
- $this->keyStore = $this->createMock(\OC\Encryption\Keys\Storage::class);
-
- $this->mount = $this->getMockBuilder(MountPoint::class)
- ->disableOriginalConstructor()
- ->onlyMethods(['getOption'])
- ->getMock();
- $this->mount->expects($this->any())->method('getOption')->willReturnCallback(function ($option, $default) {
- if ($option === 'encrypt' && $default === true) {
- global $mockedMountPointEncryptionEnabled;
- if ($mockedMountPointEncryptionEnabled !== null) {
- return $mockedMountPointEncryptionEnabled;
- }
- }
- return true;
- });
-
- $this->cache = $this->getMockBuilder('\OC\Files\Cache\Cache')
- ->disableOriginalConstructor()->getMock();
- $this->cache->expects($this->any())
- ->method('get')
- ->willReturnCallback(function ($path) {
- return ['encrypted' => false, 'path' => $path];
- });
-
- $this->mountManager = $this->createMock(\OC\Files\Mount\Manager::class);
- $this->mountManager->method('findByStorageId')
- ->willReturn([]);
-
- $this->instance = $this->getMockBuilder(Encryption::class)
- ->setConstructorArgs(
- [
- [
- 'storage' => $this->sourceStorage,
- 'root' => 'foo',
- 'mountPoint' => '/',
- 'mount' => $this->mount
- ],
- $this->encryptionManager,
- $this->util,
- $this->logger,
- $this->file,
- null,
- $this->keyStore,
- $this->mountManager,
- $this->arrayCache
- ]
- )
- ->onlyMethods(['getMetaData', 'getCache', 'getEncryptionModule'])
- ->getMock();
-
- $this->instance->expects($this->any())
- ->method('getMetaData')
- ->willReturnCallback(function ($path) {
- return ['encrypted' => true, 'size' => $this->dummySize, 'path' => $path];
- });
-
- $this->instance->expects($this->any())
- ->method('getCache')
- ->willReturn($this->cache);
-
- $this->instance->expects($this->any())
- ->method('getEncryptionModule')
- ->willReturn($mockModule);
- }
-
- protected function buildMockModule(): IEncryptionModule&MockObject {
- $this->encryptionModule = $this->getMockBuilder('\OCP\Encryption\IEncryptionModule')
- ->disableOriginalConstructor()
- ->onlyMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable', 'encryptAll', 'prepareDecryptAll', 'isReadyForUser', 'needDetailedAccessList'])
- ->getMock();
-
- $this->encryptionModule->expects($this->any())->method('getId')->willReturn('UNIT_TEST_MODULE');
- $this->encryptionModule->expects($this->any())->method('getDisplayName')->willReturn('Unit test module');
- $this->encryptionModule->expects($this->any())->method('begin')->willReturn([]);
- $this->encryptionModule->expects($this->any())->method('end')->willReturn('');
- $this->encryptionModule->expects($this->any())->method('encrypt')->willReturnArgument(0);
- $this->encryptionModule->expects($this->any())->method('decrypt')->willReturnArgument(0);
- $this->encryptionModule->expects($this->any())->method('update')->willReturn(true);
- $this->encryptionModule->expects($this->any())->method('shouldEncrypt')->willReturn(true);
- $this->encryptionModule->expects($this->any())->method('getUnencryptedBlockSize')->willReturn(8192);
- $this->encryptionModule->expects($this->any())->method('isReadable')->willReturn(true);
- $this->encryptionModule->expects($this->any())->method('needDetailedAccessList')->willReturn(false);
- return $this->encryptionModule;
- }
-
- /**
- *
- * @param string $path
- * @param array $metaData
- * @param bool $encrypted
- * @param bool $unencryptedSizeSet
- * @param int $storedUnencryptedSize
- * @param array $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetMetaData')]
- public function testGetMetaData($path, $metaData, $encrypted, $unencryptedSizeSet, $storedUnencryptedSize, $expected): void {
- $sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage')
- ->disableOriginalConstructor()->getMock();
-
- $cache = $this->getMockBuilder('\OC\Files\Cache\Cache')
- ->disableOriginalConstructor()->getMock();
- $cache->expects($this->any())
- ->method('get')
- ->willReturnCallback(
- function ($path) use ($encrypted) {
- return new CacheEntry(['encrypted' => $encrypted, 'path' => $path, 'size' => 0, 'fileid' => 1]);
- }
- );
-
- $this->instance = $this->getMockBuilder(Encryption::class)
- ->setConstructorArgs(
- [
- [
- 'storage' => $sourceStorage,
- 'root' => 'foo',
- 'mountPoint' => '/',
- 'mount' => $this->mount
- ],
- $this->encryptionManager,
- $this->util,
- $this->logger,
- $this->file,
- null,
- $this->keyStore,
- $this->mountManager,
- $this->arrayCache,
- ]
- )
- ->onlyMethods(['getCache', 'verifyUnencryptedSize'])
- ->getMock();
-
- if ($unencryptedSizeSet) {
- $this->invokePrivate($this->instance, 'unencryptedSize', [[$path => $storedUnencryptedSize]]);
- }
-
- $fileEntry = $this->getMockBuilder('\OC\Files\Cache\Cache')
- ->disableOriginalConstructor()->getMock();
- $sourceStorage->expects($this->once())->method('getMetaData')->with($path)
- ->willReturn($metaData);
- $sourceStorage->expects($this->any())
- ->method('getCache')
- ->with($path)
- ->willReturn($fileEntry);
- if ($metaData !== null) {
- $fileEntry->expects($this->any())
- ->method('get')
- ->with($metaData['fileid']);
- }
-
- $this->instance->expects($this->any())->method('getCache')->willReturn($cache);
- if ($expected !== null) {
- $this->instance->expects($this->any())->method('verifyUnencryptedSize')
- ->with($path, 0)->willReturn($expected['size']);
- }
-
- $result = $this->instance->getMetaData($path);
- if (isset($expected['encrypted'])) {
- $this->assertSame($expected['encrypted'], (bool)$result['encrypted']);
-
- if (isset($expected['encryptedVersion'])) {
- $this->assertSame($expected['encryptedVersion'], $result['encryptedVersion']);
- }
- }
-
- if ($expected !== null) {
- $this->assertSame($expected['size'], $result['size']);
- } else {
- $this->assertSame(null, $result);
- }
- }
-
- public static function dataTestGetMetaData(): array {
- return [
- ['/test.txt', ['size' => 42, 'encrypted' => 2, 'encryptedVersion' => 2, 'fileid' => 1], true, true, 12, ['size' => 12, 'encrypted' => true, 'encryptedVersion' => 2]],
- ['/test.txt', null, true, true, 12, null],
- ['/test.txt', ['size' => 42, 'encrypted' => 0, 'fileid' => 1], false, false, 12, ['size' => 42, 'encrypted' => false]],
- ['/test.txt', ['size' => 42, 'encrypted' => false, 'fileid' => 1], true, false, 12, ['size' => 12, 'encrypted' => true]]
- ];
- }
-
- public function testFilesize(): void {
- $cache = $this->getMockBuilder('\OC\Files\Cache\Cache')
- ->disableOriginalConstructor()->getMock();
- $cache->expects($this->any())
- ->method('get')
- ->willReturn(new CacheEntry(['encrypted' => true, 'path' => '/test.txt', 'size' => 0, 'fileid' => 1]));
-
- $this->instance = $this->getMockBuilder(Encryption::class)
- ->setConstructorArgs(
- [
- [
- 'storage' => $this->sourceStorage,
- 'root' => 'foo',
- 'mountPoint' => '/',
- 'mount' => $this->mount
- ],
- $this->encryptionManager,
- $this->util,
- $this->logger,
- $this->file,
- null,
- $this->keyStore,
- $this->mountManager,
- $this->arrayCache,
- ]
- )
- ->onlyMethods(['getCache', 'verifyUnencryptedSize'])
- ->getMock();
-
- $this->instance->expects($this->any())->method('getCache')->willReturn($cache);
- $this->instance->expects($this->any())->method('verifyUnencryptedSize')
- ->willReturn(42);
-
- $this->assertSame(42,
- $this->instance->filesize('/test.txt')
- );
- }
-
- /**
- *
- * @param int $encryptedSize
- * @param int $unencryptedSize
- * @param bool $failure
- * @param int $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestVerifyUnencryptedSize')]
- public function testVerifyUnencryptedSize($encryptedSize, $unencryptedSize, $failure, $expected): void {
- $sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage')
- ->disableOriginalConstructor()->getMock();
-
- $this->instance = $this->getMockBuilder(Encryption::class)
- ->setConstructorArgs(
- [
- [
- 'storage' => $sourceStorage,
- 'root' => 'foo',
- 'mountPoint' => '/',
- 'mount' => $this->mount
- ],
- $this->encryptionManager,
- $this->util,
- $this->logger,
- $this->file,
- null,
- $this->keyStore,
- $this->mountManager,
- $this->arrayCache,
- ]
- )
- ->onlyMethods(['fixUnencryptedSize'])
- ->getMock();
-
- $sourceStorage->expects($this->once())->method('filesize')->willReturn($encryptedSize);
-
- $this->instance->expects($this->any())->method('fixUnencryptedSize')
- ->with('/test.txt', $encryptedSize, $unencryptedSize)
- ->willReturnCallback(
- function () use ($failure, $expected) {
- if ($failure) {
- throw new Exception();
- } else {
- return $expected;
- }
- }
- );
-
- $this->assertSame(
- $expected,
- $this->invokePrivate($this->instance, 'verifyUnencryptedSize', ['/test.txt', $unencryptedSize])
- );
- }
-
- public static function dataTestVerifyUnencryptedSize(): array {
- return [
- [120, 80, false, 80],
- [120, 120, false, 80],
- [120, -1, false, 80],
- [120, -1, true, -1],
- // Zero-byte encrypted file: on-disk size equals header only (8192) — should NOT recalculate
- [8192, 0, false, 0],
- // Encrypted file with content beyond header but unencrypted_size=0 — SHOULD recalculate
- [16384, 0, false, 80],
- ];
- }
-
- /**
- *
- * @param string $source
- * @param string $target
- * @param $encryptionEnabled
- * @param boolean $renameKeysReturn
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestCopyAndRename')]
- public function testRename($source,
- $target,
- $encryptionEnabled,
- $renameKeysReturn): void {
- if ($encryptionEnabled) {
- $this->keyStore
- ->expects($this->once())
- ->method('renameKeys')
- ->willReturn($renameKeysReturn);
- } else {
- $this->keyStore
- ->expects($this->never())->method('renameKeys');
- }
- $this->util->expects($this->any())
- ->method('isFile')->willReturn(true);
- $this->encryptionManager->expects($this->once())
- ->method('isEnabled')->willReturn($encryptionEnabled);
-
- $this->instance->mkdir($source);
- $this->instance->mkdir(dirname($target));
- $this->instance->rename($source, $target);
- }
-
- public function testCopyEncryption(): void {
- $this->instance->file_put_contents('source.txt', 'bar');
- $this->instance->copy('source.txt', 'target.txt');
- $this->assertSame('bar', $this->instance->file_get_contents('target.txt'));
- $targetMeta = $this->instance->getMetaData('target.txt');
- $sourceMeta = $this->instance->getMetaData('source.txt');
- $this->assertSame($sourceMeta['encrypted'], $targetMeta['encrypted']);
- $this->assertSame($sourceMeta['size'], $targetMeta['size']);
- }
-
- /**
- * data provider for testCopyTesting() and dataTestCopyAndRename()
- *
- * @return array
- */
- public static function dataTestCopyAndRename(): array {
- return [
- ['source', 'target', true, false, false],
- ['source', 'target', true, true, false],
- ['source', '/subFolder/target', true, false, false],
- ['source', '/subFolder/target', true, true, true],
- ['source', '/subFolder/target', false, true, false],
- ];
- }
-
- public function testIsLocal(): void {
- $this->encryptionManager->expects($this->once())
- ->method('isEnabled')->willReturn(true);
- $this->assertFalse($this->instance->isLocal());
- }
-
- /**
- *
- * @param string $path
- * @param boolean $rmdirResult
- * @param boolean $isExcluded
- * @param boolean $encryptionEnabled
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestRmdir')]
- public function testRmdir($path, $rmdirResult, $isExcluded, $encryptionEnabled): void {
- $sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage')
- ->disableOriginalConstructor()->getMock();
-
- $util = $this->getMockBuilder('\OC\Encryption\Util')->disableOriginalConstructor()->getMock();
-
- $sourceStorage->expects($this->once())->method('rmdir')->willReturn($rmdirResult);
- $util->expects($this->any())->method('isExcluded')->willReturn($isExcluded);
- $this->encryptionManager->expects($this->any())->method('isEnabled')->willReturn($encryptionEnabled);
-
- $encryptionStorage = new Encryption(
- [
- 'storage' => $sourceStorage,
- 'root' => 'foo',
- 'mountPoint' => '/mountPoint',
- 'mount' => $this->mount
- ],
- $this->encryptionManager,
- $util,
- $this->logger,
- $this->file,
- null,
- $this->keyStore,
- $this->mountManager,
- $this->arrayCache,
- );
-
- if ($rmdirResult === true && $isExcluded === false && $encryptionEnabled === true) {
- $this->keyStore->expects($this->once())->method('deleteAllFileKeys')->with('/mountPoint' . $path);
- } else {
- $this->keyStore->expects($this->never())->method('deleteAllFileKeys');
- }
-
- $encryptionStorage->rmdir($path);
- }
-
- public static function dataTestRmdir(): array {
- return [
- ['/file.txt', true, true, true],
- ['/file.txt', false, true, true],
- ['/file.txt', true, false, true],
- ['/file.txt', false, false, true],
- ['/file.txt', true, true, false],
- ['/file.txt', false, true, false],
- ['/file.txt', true, false, false],
- ['/file.txt', false, false, false],
- ];
- }
-
- /**
- *
- * @param boolean $excluded
- * @param boolean $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestCopyKeys')]
- public function testCopyKeys($excluded, $expected): void {
- $this->util->expects($this->once())
- ->method('isExcluded')
- ->willReturn($excluded);
-
- if ($excluded) {
- $this->keyStore->expects($this->never())->method('copyKeys');
- } else {
- $this->keyStore->expects($this->once())->method('copyKeys')->willReturn(true);
- }
-
- $this->assertSame($expected,
- self::invokePrivate($this->instance, 'copyKeys', ['/source', '/target'])
- );
- }
-
- public static function dataTestCopyKeys(): array {
- return [
- [true, false],
- [false, true],
- ];
- }
-
- /**
- *
- * @param string $path
- * @param bool $strippedPathExists
- * @param string $strippedPath
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetHeader')]
- public function testGetHeader($path, $strippedPathExists, $strippedPath): void {
- $sourceStorage = $this->getMockBuilder('\OC\Files\Storage\Storage')
- ->disableOriginalConstructor()->getMock();
-
- $util = $this->getMockBuilder('\OC\Encryption\Util')
- ->setConstructorArgs(
- [
- new View(),
- new Manager(
- $this->config,
- $this->createMock(ICacheFactory::class),
- $this->createMock(IEventDispatcher::class),
- $this->createMock(LoggerInterface::class),
- ),
- $this->groupManager,
- $this->config,
- $this->arrayCache
- ]
- )->getMock();
-
- $cache = $this->getMockBuilder('\OC\Files\Cache\Cache')
- ->disableOriginalConstructor()->getMock();
- $cache->expects($this->any())
- ->method('get')
- ->willReturnCallback(function ($path) {
- return ['encrypted' => true, 'path' => $path];
- });
-
- $instance = $this->getMockBuilder(Encryption::class)
- ->setConstructorArgs(
- [
- [
- 'storage' => $sourceStorage,
- 'root' => 'foo',
- 'mountPoint' => '/',
- 'mount' => $this->mount
- ],
- $this->encryptionManager,
- $util,
- $this->logger,
- $this->file,
- null,
- $this->keyStore,
- $this->mountManager,
- $this->arrayCache,
- ]
- )
- ->onlyMethods(['getCache', 'readFirstBlock'])
- ->getMock();
-
- $instance->method('getCache')->willReturn($cache);
-
- $util->method('parseRawHeader')
- ->willReturn([Util::HEADER_ENCRYPTION_MODULE_KEY => 'OC_DEFAULT_MODULE']);
-
- if ($strippedPathExists) {
- $instance->method('readFirstBlock')
- ->with($strippedPath)->willReturn('');
- } else {
- $instance->method('readFirstBlock')
- ->with($path)->willReturn('');
- }
-
- $util->expects($this->once())->method('stripPartialFileExtension')
- ->with($path)->willReturn($strippedPath);
- $sourceStorage->expects($this->once())
- ->method('is_file')
- ->with($strippedPath)
- ->willReturn($strippedPathExists);
-
- $this->invokePrivate($instance, 'getHeader', [$path]);
- }
-
- public static function dataTestGetHeader(): array {
- return [
- ['/foo/bar.txt', false, '/foo/bar.txt'],
- ['/foo/bar.txt.part', false, '/foo/bar.txt'],
- ['/foo/bar.txt.ocTransferId7437493.part', false, '/foo/bar.txt'],
- ['/foo/bar.txt.part', true, '/foo/bar.txt'],
- ['/foo/bar.txt.ocTransferId7437493.part', true, '/foo/bar.txt'],
- ];
- }
-
- /**
- * test if getHeader adds the default module correctly to the header for
- * legacy files
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetHeaderAddLegacyModule')]
- public function testGetHeaderAddLegacyModule($header, $isEncrypted, $strippedPathExists, $expected): void {
- $sourceStorage = $this->getMockBuilder(\OC\Files\Storage\Storage::class)
- ->disableOriginalConstructor()->getMock();
-
- $sourceStorage->expects($this->once())
- ->method('is_file')
- ->with('test.txt')
- ->willReturn($strippedPathExists);
-
- $util = $this->getMockBuilder(Util::class)
- ->onlyMethods(['stripPartialFileExtension', 'parseRawHeader'])
- ->setConstructorArgs([new View(), new Manager(
- $this->config,
- $this->createMock(ICacheFactory::class),
- $this->createMock(IEventDispatcher::class),
- $this->createMock(LoggerInterface::class),
- ), $this->groupManager, $this->config, $this->arrayCache])
- ->getMock();
- $util->expects($this->any())
- ->method('stripPartialFileExtension')
- ->willReturnCallback(function ($path) {
- return $path;
- });
-
- $cache = $this->createMock(Cache::class);
- $cache->expects($this->any())
- ->method('get')
- ->willReturnCallback(function ($path) use ($isEncrypted) {
- return ['encrypted' => $isEncrypted, 'path' => $path];
- });
-
- $instance = $this->getMockBuilder(Encryption::class)
- ->setConstructorArgs(
- [
- [
- 'storage' => $sourceStorage,
- 'root' => 'foo',
- 'mountPoint' => '/',
- 'mount' => $this->mount
- ],
- $this->encryptionManager,
- $util,
- $this->logger,
- $this->file,
- null,
- $this->keyStore,
- $this->mountManager,
- $this->arrayCache,
- ]
- )
- ->onlyMethods(['readFirstBlock', 'getCache'])
- ->getMock();
-
- $instance->method('readFirstBlock')->willReturn('');
-
- $util->method(('parseRawHeader'))->willReturn($header);
- $instance->method('getCache')->willReturn($cache);
-
- $result = $this->invokePrivate($instance, 'getHeader', ['test.txt']);
- $this->assertSameSize($expected, $result);
- foreach ($result as $key => $value) {
- $this->assertArrayHasKey($key, $expected);
- $this->assertSame($expected[$key], $value);
- }
- }
-
- public static function dataTestGetHeaderAddLegacyModule(): array {
- return [
- [['cipher' => 'AES-128'], true, true, ['cipher' => 'AES-128', Util::HEADER_ENCRYPTION_MODULE_KEY => 'OC_DEFAULT_MODULE']],
- [[], true, false, []],
- [[], true, true, [Util::HEADER_ENCRYPTION_MODULE_KEY => 'OC_DEFAULT_MODULE']],
- [[], false, true, []],
- ];
- }
-
- public static function dataCopyBetweenStorage(): array {
- return [
- [true, true, true],
- [true, false, false],
- [false, true, false],
- [false, false, false],
- ];
- }
-
- public static function dataUpdateEncryptedVersion(): array {
- return [
- // the target is written through the encryption stream, which signs its blocks
- // with the version that follows the version of the file they replace
- 'copy onto an existing file' => [['encryptedVersion' => 4], ['encryptedVersion' => 3], false, 3],
- 'copy onto a new file' => [['encryptedVersion' => 4], false, false, 1],
- 'copy onto a file that is not encrypted yet' => [['encryptedVersion' => 4], ['encryptedVersion' => 0], false, 1],
- // a 1:1 copy reuses the keys and the ciphertext of the source
- '1:1 copy' => [['encryptedVersion' => 5], false, true, 5],
- '1:1 copy of a file that is not encrypted yet' => [['encryptedVersion' => 0], false, true, 1],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataUpdateEncryptedVersion')]
- public function testUpdateEncryptedVersion(
- array|false $sourceCacheEntry,
- array|false $targetCacheEntry,
- bool $keepEncryptionVersion,
- int $expectedVersion,
- ): void {
- $sourceCache = $this->createMock(ICache::class);
- $sourceCache->method('get')
- ->with('source.txt')
- ->willReturn($sourceCacheEntry);
- $sourceStorage = $this->createMock(\OC\Files\Storage\Storage::class);
- $sourceStorage->method('getCache')
- ->willReturn($sourceCache);
-
- $targetCache = $this->createMock(ICache::class);
- $targetCache->method('get')
- ->with('target.txt')
- ->willReturn($targetCacheEntry);
- $targetCache->expects($this->once())
- ->method('put')
- ->with('target.txt', ['encrypted' => true, 'encryptedVersion' => $expectedVersion]);
-
- $instance = $this->getMockBuilder(Encryption::class)
- ->setConstructorArgs(
- [
- [
- 'storage' => $this->sourceStorage,
- 'root' => 'foo',
- 'mountPoint' => '/',
- 'mount' => $this->mount
- ],
- $this->encryptionManager,
- $this->util,
- $this->logger,
- $this->file,
- null,
- $this->keyStore,
- $this->mountManager,
- $this->arrayCache
- ]
- )
- ->onlyMethods(['getCache', 'getEncryptionModule'])
- ->getMock();
- $instance->method('getCache')->willReturn($targetCache);
- $instance->method('getEncryptionModule')->willReturn($this->encryptionModule);
-
- $this->encryptionManager->expects($this->any())
- ->method('isEnabled')
- ->willReturn(true);
- global $mockedMountPointEncryptionEnabled;
- $mockedMountPointEncryptionEnabled = true;
-
- $this->invokePrivate($instance, 'updateEncryptedVersion', [$sourceStorage, 'source.txt', 'target.txt', false, $keepEncryptionVersion]);
- }
-
- public function testCopyBetweenStorageMinimumEncryptedVersion(): void {
- $storage2 = $this->createMock(\OC\Files\Storage\Storage::class);
-
- $sourceInternalPath = $targetInternalPath = 'file.txt';
- $preserveMtime = $isRename = false;
-
- $storage2->expects($this->any())
- ->method('fopen')
- ->willReturnCallback(function ($path, $mode) {
- $temp = Server::get(ITempManager::class);
- return fopen($temp->getTemporaryFile(), $mode);
- });
- $storage2->method('getId')
- ->willReturn('stroage2');
- $cache = $this->createMock(ICache::class);
- $cache->expects($this->once())
- ->method('get')
- ->with($sourceInternalPath)
- ->willReturn(['encryptedVersion' => 0]);
- $storage2->expects($this->once())
- ->method('getCache')
- ->willReturn($cache);
- $this->encryptionManager->expects($this->any())
- ->method('isEnabled')
- ->willReturn(true);
- global $mockedMountPointEncryptionEnabled;
- $mockedMountPointEncryptionEnabled = true;
-
- $expectedCachePut = [
- 'encrypted' => true,
- ];
- $expectedCachePut['encryptedVersion'] = 1;
-
- $this->cache->expects($this->once())
- ->method('put')
- ->with($sourceInternalPath, $expectedCachePut);
-
- $this->invokePrivate($this->instance, 'copyBetweenStorage', [$storage2, $sourceInternalPath, $targetInternalPath, $preserveMtime, $isRename]);
-
- $this->assertFalse(false);
- }
-
- /**
- *
- * @param bool $encryptionEnabled
- * @param bool $mountPointEncryptionEnabled
- * @param bool $expectedEncrypted
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataCopyBetweenStorage')]
- public function testCopyBetweenStorage($encryptionEnabled, $mountPointEncryptionEnabled, $expectedEncrypted): void {
- $storage2 = $this->createMock(\OC\Files\Storage\Storage::class);
-
- $sourceInternalPath = $targetInternalPath = 'file.txt';
- $preserveMtime = $isRename = false;
-
- $storage2->expects($this->any())
- ->method('fopen')
- ->willReturnCallback(function ($path, $mode) {
- $temp = Server::get(ITempManager::class);
- return fopen($temp->getTemporaryFile(), $mode);
- });
- $storage2->method('getId')
- ->willReturn('stroage2');
- if ($expectedEncrypted) {
- $cache = $this->createMock(ICache::class);
- $cache->expects($this->once())
- ->method('get')
- ->with($sourceInternalPath)
- ->willReturn(['encryptedVersion' => 12345]);
- $storage2->expects($this->once())
- ->method('getCache')
- ->willReturn($cache);
- }
- $this->encryptionManager->expects($this->any())
- ->method('isEnabled')
- ->willReturn($encryptionEnabled);
- // FIXME can not overwrite the return after definition
- // $this->mount->expects($this->at(0))
- // ->method('getOption')
- // ->with('encrypt', true)
- // ->willReturn($mountPointEncryptionEnabled);
- global $mockedMountPointEncryptionEnabled;
- $mockedMountPointEncryptionEnabled = $mountPointEncryptionEnabled;
-
- $expectedCachePut = [
- 'encrypted' => $expectedEncrypted,
- ];
- if ($expectedEncrypted === true) {
- $expectedCachePut['encryptedVersion'] = 1;
- }
-
- $this->arrayCache->expects($this->never())->method('set');
-
- $this->cache->expects($this->once())
- ->method('put')
- ->with($sourceInternalPath, $expectedCachePut);
-
- $this->invokePrivate($this->instance, 'copyBetweenStorage', [$storage2, $sourceInternalPath, $targetInternalPath, $preserveMtime, $isRename]);
-
- $this->assertFalse(false);
- }
-
- /**
- *
- * @param string $sourceInternalPath
- * @param string $targetInternalPath
- * @param bool $copyResult
- * @param bool $encrypted
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestCopyBetweenStorageVersions')]
- public function testCopyBetweenStorageVersions($sourceInternalPath, $targetInternalPath, $copyResult, $encrypted): void {
- $sourceStorage = $this->createMock(\OC\Files\Storage\Storage::class);
-
- $targetStorage = $this->createMock(\OC\Files\Storage\Storage::class);
-
- $cache = $this->getMockBuilder('\OC\Files\Cache\Cache')
- ->disableOriginalConstructor()->getMock();
-
- $mountPoint = '/mountPoint';
-
- /** @var Encryption |MockObject $instance */
- $instance = $this->getMockBuilder(Encryption::class)
- ->setConstructorArgs(
- [
- [
- 'storage' => $targetStorage,
- 'root' => 'foo',
- 'mountPoint' => $mountPoint,
- 'mount' => $this->mount
- ],
- $this->encryptionManager,
- $this->util,
- $this->logger,
- $this->file,
- null,
- $this->keyStore,
- $this->mountManager,
- $this->arrayCache
- ]
- )
- ->onlyMethods(['updateUnencryptedSize', 'getCache'])
- ->getMock();
-
- $targetStorage->expects($this->once())->method('copyFromStorage')
- ->with($sourceStorage, $sourceInternalPath, $targetInternalPath)
- ->willReturn($copyResult);
-
- $instance->expects($this->any())->method('getCache')
- ->willReturn($cache);
-
- $this->arrayCache->expects($this->once())->method('set')
- ->with('encryption_copy_version_' . $sourceInternalPath, true);
-
- if ($copyResult) {
- $cache->expects($this->once())->method('get')
- ->with($sourceInternalPath)
- ->willReturn(new CacheEntry(['encrypted' => $encrypted, 'size' => 42]));
- if ($encrypted) {
- $instance->expects($this->once())->method('updateUnencryptedSize')
- ->with($mountPoint . $targetInternalPath, 42);
- } else {
- $instance->expects($this->never())->method('updateUnencryptedSize');
- }
- } else {
- $instance->expects($this->never())->method('updateUnencryptedSize');
- }
-
- $result = $this->invokePrivate(
- $instance,
- 'copyBetweenStorage',
- [
- $sourceStorage,
- $sourceInternalPath,
- $targetInternalPath,
- false,
- false
- ]
- );
-
- $this->assertSame($copyResult, $result);
- }
-
- public static function dataTestCopyBetweenStorageVersions(): array {
- return [
- ['/files/foo.txt', '/files_versions/foo.txt.768743', true, true],
- ['/files/foo.txt', '/files_versions/foo.txt.768743', true, false],
- ['/files/foo.txt', '/files_versions/foo.txt.768743', false, true],
- ['/files/foo.txt', '/files_versions/foo.txt.768743', false, false],
- ['/files_versions/foo.txt.6487634', '/files/foo.txt', true, true],
- ['/files_versions/foo.txt.6487634', '/files/foo.txt', true, false],
- ['/files_versions/foo.txt.6487634', '/files/foo.txt', false, true],
- ['/files_versions/foo.txt.6487634', '/files/foo.txt', false, false],
- ];
- }
-
- /**
- * @param string $path
- * @param bool $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestIsVersion')]
- public function testIsVersion($path, $expected): void {
- $this->assertSame($expected,
- $this->invokePrivate($this->instance, 'isVersion', [$path])
- );
- }
-
- public static function dataTestIsVersion(): array {
- return [
- ['files_versions/foo', true],
- ['/files_versions/foo', true],
- ['//files_versions/foo', true],
- ['files/versions/foo', false],
- ['files/files_versions/foo', false],
- ['files_versions_test/foo', false],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestShouldEncrypt')]
- public function testShouldEncrypt(
- bool $encryptionEnabled,
- bool|string $encryptMountPoint,
- ?bool $encryptionModule,
- bool $encryptionModuleShouldEncrypt,
- bool $expected,
- ): void {
- $encryptionManager = $this->createMock(\OC\Encryption\Manager::class);
- $util = $this->createMock(Util::class);
- $fileHelper = $this->createMock(IFile::class);
- $keyStorage = $this->createMock(IStorage::class);
- $mountManager = $this->createMock(\OC\Files\Mount\Manager::class);
- $mount = $this->createMock(IMountPoint::class);
- $arrayCache = $this->createMock(ArrayCache::class);
- $path = '/welcome.txt';
- $fullPath = 'admin/files/welcome.txt';
- $defaultEncryptionModule = $this->createMock(IEncryptionModule::class);
-
- $wrapper = $this->getMockBuilder(Encryption::class)
- ->setConstructorArgs(
- [
- ['mountPoint' => '', 'mount' => $mount, 'storage' => null],
- $encryptionManager,
- $util,
- $this->logger,
- $fileHelper,
- null,
- $keyStorage,
- $mountManager,
- $arrayCache
- ]
- )
- ->onlyMethods(['getFullPath', 'getEncryptionModule'])
- ->getMock();
-
- $encryptionManager->method('isEnabled')->willReturn($encryptionEnabled);
-
- if ($encryptionModule === true) {
- /** @var IEncryptionModule|MockObject $encryptionModule */
- $encryptionModule = $this->createMock(IEncryptionModule::class);
- }
-
- $wrapper->method('getFullPath')->with($path)->willReturn($fullPath);
- $wrapper->expects(($encryptionEnabled && $encryptMountPoint) ? $this->once() : $this->never())
- ->method('getEncryptionModule')
- ->with($fullPath)
- ->willReturnCallback(
- function () use ($encryptionModule) {
- if ($encryptionModule === false) {
- throw new ModuleDoesNotExistsException();
- }
- return $encryptionModule;
- }
- );
- $mount->expects($encryptionEnabled ? $this->once() : $this->never())
- ->method('getOption')->with('encrypt', true)
- ->willReturn($encryptMountPoint);
-
- if ($encryptionModule !== null && $encryptionModule !== false) {
- $encryptionModule
- ->method('shouldEncrypt')
- ->with($fullPath)
- ->willReturn($encryptionModuleShouldEncrypt);
- }
-
- if ($encryptionModule === null) {
- $encryptionManager->expects($this->once())
- ->method('getEncryptionModule')
- ->willReturn($defaultEncryptionModule);
- }
- $defaultEncryptionModule->method('shouldEncrypt')->willReturn(true);
-
- $result = $this->invokePrivate($wrapper, 'shouldEncrypt', [$path]);
-
- $this->assertSame($expected, $result);
- }
-
- public static function dataTestShouldEncrypt(): array {
- return [
- [true, false, false, false, false],
- [true, '', false, false, false],
- [true, true, false, false, false],
- [true, true, true, false, false],
- [true, true, true, true, true],
- [true, true, null, false, true],
- [false, true, true, true, false],
- ];
- }
-}
--- a/tests/lib/Files/Storage/Wrapper/JailTest.php
+++ /dev/null
@@ -1,56 +0,0 @@
-sourceStorage = new Temporary([]);
- $this->sourceStorage->mkdir('foo');
- $this->instance = new Jail([
- 'storage' => $this->sourceStorage,
- 'root' => 'foo'
- ]);
- }
-
- #[\Override]
- protected function tearDown(): void {
- // test that nothing outside our jail is touched
- $contents = [];
- $dh = $this->sourceStorage->opendir('');
- while (($file = readdir($dh)) !== false) {
- if (!Filesystem::isIgnoredDir($file)) {
- $contents[] = $file;
- }
- }
- $this->assertEquals(['foo'], $contents);
- $this->sourceStorage->cleanUp();
- parent::tearDown();
- }
-
- public function testMkDirRooted(): void {
- $this->instance->mkdir('bar');
- $this->assertTrue($this->sourceStorage->is_dir('foo/bar'));
- }
-
- public function testFilePutContentsRooted(): void {
- $this->instance->file_put_contents('bar', 'asd');
- $this->assertEquals('asd', $this->sourceStorage->file_get_contents('foo/bar'));
- }
-}
--- a/tests/lib/Files/Storage/Wrapper/KnownMtimeTest.php
+++ /dev/null
@@ -1,69 +0,0 @@
-fakeTime = 0;
- $this->sourceStorage = new Temporary([]);
- $this->clock = $this->createMock(ClockInterface::class);
- $this->clock->method('now')->willReturnCallback(function () {
- if ($this->fakeTime) {
- return new \DateTimeImmutable("@{$this->fakeTime}");
- } else {
- return new \DateTimeImmutable();
- }
- });
- $this->instance = $this->getWrappedStorage();
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->sourceStorage->cleanUp();
- parent::tearDown();
- }
-
- protected function getWrappedStorage() {
- return new KnownMtime([
- 'storage' => $this->sourceStorage,
- 'clock' => $this->clock,
- ]);
- }
-
- public function testNewerKnownMtime(): void {
- $future = time() + 1000;
- $this->fakeTime = $future;
-
- $this->instance->file_put_contents('foo.txt', 'bar');
-
- // fuzzy match since the clock might have ticked
- $this->assertLessThan(2, abs(time() - $this->sourceStorage->filemtime('foo.txt')));
- $this->assertEquals($this->sourceStorage->filemtime('foo.txt'), $this->sourceStorage->stat('foo.txt')['mtime']);
- $this->assertEquals($this->sourceStorage->filemtime('foo.txt'), $this->sourceStorage->getMetaData('foo.txt')['mtime']);
-
- $this->assertEquals($future, $this->instance->filemtime('foo.txt'));
- $this->assertEquals($future, $this->instance->stat('foo.txt')['mtime']);
- $this->assertEquals($future, $this->instance->getMetaData('foo.txt')['mtime']);
- }
-}
--- a/tests/lib/Files/Storage/Wrapper/PermissionsMaskTest.php
+++ /dev/null
@@ -1,180 +0,0 @@
-sourceStorage = new Temporary([]);
- $this->instance = $this->getMaskedStorage(Constants::PERMISSION_ALL);
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->sourceStorage->cleanUp();
- parent::tearDown();
- }
-
- protected function getMaskedStorage($mask) {
- return new PermissionsMask([
- 'storage' => $this->sourceStorage,
- 'mask' => $mask
- ]);
- }
-
- public function testMkdirNoCreate(): void {
- $storage = $this->getMaskedStorage(Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE);
- $this->assertFalse($storage->mkdir('foo'));
- $this->assertFalse($storage->file_exists('foo'));
- }
-
- public function testRmdirNoDelete(): void {
- $storage = $this->getMaskedStorage(Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE);
- $this->assertTrue($storage->mkdir('foo'));
- $this->assertTrue($storage->file_exists('foo'));
- $this->assertFalse($storage->rmdir('foo'));
- $this->assertTrue($storage->file_exists('foo'));
- }
-
- public function testTouchNewFileNoCreate(): void {
- $storage = $this->getMaskedStorage(Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE);
- $this->assertFalse($storage->touch('foo'));
- $this->assertFalse($storage->file_exists('foo'));
- }
-
- public function testTouchNewFileNoUpdate(): void {
- $storage = $this->getMaskedStorage(Constants::PERMISSION_ALL - Constants::PERMISSION_UPDATE);
- $this->assertTrue($storage->touch('foo'));
- $this->assertTrue($storage->file_exists('foo'));
- }
-
- public function testTouchExistingFileNoUpdate(): void {
- $this->sourceStorage->touch('foo');
- $storage = $this->getMaskedStorage(Constants::PERMISSION_ALL - Constants::PERMISSION_UPDATE);
- $this->assertFalse($storage->touch('foo'));
- }
-
- public function testUnlinkNoDelete(): void {
- $storage = $this->getMaskedStorage(Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE);
- $this->assertTrue($storage->touch('foo'));
- $this->assertTrue($storage->file_exists('foo'));
- $this->assertFalse($storage->unlink('foo'));
- $this->assertTrue($storage->file_exists('foo'));
- }
-
- public function testPutContentsNewFileNoUpdate(): void {
- $storage = $this->getMaskedStorage(Constants::PERMISSION_ALL - Constants::PERMISSION_UPDATE);
- $this->assertEquals(3, $storage->file_put_contents('foo', 'bar'));
- $this->assertEquals('bar', $storage->file_get_contents('foo'));
- }
-
- public function testPutContentsNewFileNoCreate(): void {
- $storage = $this->getMaskedStorage(Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE);
- $this->assertFalse($storage->file_put_contents('foo', 'bar'));
- }
-
- public function testPutContentsExistingFileNoUpdate(): void {
- $this->sourceStorage->touch('foo');
- $storage = $this->getMaskedStorage(Constants::PERMISSION_ALL - Constants::PERMISSION_UPDATE);
- $this->assertFalse($storage->file_put_contents('foo', 'bar'));
- }
-
- public function testFopenExistingFileNoUpdate(): void {
- $this->sourceStorage->touch('foo');
- $storage = $this->getMaskedStorage(Constants::PERMISSION_ALL - Constants::PERMISSION_UPDATE);
- $this->assertFalse($storage->fopen('foo', 'w'));
- }
-
- public function testFopenNewFileNoCreate(): void {
- $storage = $this->getMaskedStorage(Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE);
- $this->assertFalse($storage->fopen('foo', 'w'));
- }
-
- public function testScanNewFiles(): void {
- $storage = $this->getMaskedStorage(Constants::PERMISSION_READ + Constants::PERMISSION_CREATE);
- $storage->file_put_contents('foo', 'bar');
- $storage->getScanner()->scan('');
-
- $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE, $this->sourceStorage->getCache()->get('foo')->getPermissions());
- $this->assertEquals(Constants::PERMISSION_READ, $storage->getCache()->get('foo')->getPermissions());
- }
-
- public function testScanNewWrappedFiles(): void {
- $storage = $this->getMaskedStorage(Constants::PERMISSION_READ + Constants::PERMISSION_CREATE);
- $wrappedStorage = new Wrapper(['storage' => $storage]);
- $wrappedStorage->file_put_contents('foo', 'bar');
- $wrappedStorage->getScanner()->scan('');
-
- $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE, $this->sourceStorage->getCache()->get('foo')->getPermissions());
- $this->assertEquals(Constants::PERMISSION_READ, $storage->getCache()->get('foo')->getPermissions());
- }
-
- public function testScanNewFilesNested(): void {
- $storage = $this->getMaskedStorage(Constants::PERMISSION_READ + Constants::PERMISSION_CREATE + Constants::PERMISSION_UPDATE);
- $nestedStorage = new PermissionsMask([
- 'storage' => $storage,
- 'mask' => Constants::PERMISSION_READ + Constants::PERMISSION_CREATE
- ]);
- $wrappedStorage = new Wrapper(['storage' => $nestedStorage]);
- $wrappedStorage->file_put_contents('foo', 'bar');
- $wrappedStorage->getScanner()->scan('');
-
- $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE, $this->sourceStorage->getCache()->get('foo')->getPermissions());
- $this->assertEquals(Constants::PERMISSION_READ + Constants::PERMISSION_UPDATE, $storage->getCache()->get('foo')->getPermissions());
- $this->assertEquals(Constants::PERMISSION_READ, $wrappedStorage->getCache()->get('foo')->getPermissions());
- }
-
- public function testScanUnchanged(): void {
- $this->sourceStorage->mkdir('foo');
- $this->sourceStorage->file_put_contents('foo/bar.txt', 'bar');
-
- $this->sourceStorage->getScanner()->scan('foo');
-
- $storage = $this->getMaskedStorage(Constants::PERMISSION_READ);
- $scanner = $storage->getScanner();
- $called = false;
- $scanner->listen('\OC\Files\Cache\Scanner', 'addToCache', function () use (&$called): void {
- $called = true;
- });
- $scanner->scan('foo', IScanner::SCAN_RECURSIVE, IScanner::REUSE_ETAG | IScanner::REUSE_SIZE);
-
- $this->assertFalse($called);
- }
-
- public function testScanUnchangedWrapped(): void {
- $this->sourceStorage->mkdir('foo');
- $this->sourceStorage->file_put_contents('foo/bar.txt', 'bar');
-
- $this->sourceStorage->getScanner()->scan('foo');
-
- $storage = $this->getMaskedStorage(Constants::PERMISSION_READ);
- $wrappedStorage = new Wrapper(['storage' => $storage]);
- $scanner = $wrappedStorage->getScanner();
- $called = false;
- $scanner->listen('\OC\Files\Cache\Scanner', 'addToCache', function () use (&$called): void {
- $called = true;
- });
- $scanner->scan('foo', IScanner::SCAN_RECURSIVE, IScanner::REUSE_ETAG | IScanner::REUSE_SIZE);
-
- $this->assertFalse($called);
- }
-}
--- a/tests/lib/Files/Storage/Wrapper/QuotaTest.php
+++ /dev/null
@@ -1,280 +0,0 @@
-tmpDir = Server::get(ITempManager::class)->getTemporaryFolder();
- $storage = new Local(['datadir' => $this->tmpDir]);
- $this->instance = new Quota(['storage' => $storage, 'quota' => 10000000]);
- }
-
- #[\Override]
- protected function tearDown(): void {
- Files::rmdirr($this->tmpDir);
- parent::tearDown();
- }
-
- /**
- * @param integer $limit
- */
- protected function getLimitedStorage($limit) {
- $storage = new Local(['datadir' => $this->tmpDir]);
- $storage->mkdir('files');
- $storage->getScanner()->scan('');
- return new Quota(['storage' => $storage, 'quota' => $limit]);
- }
-
- public function testFilePutContentsNotEnoughSpace(): void {
- $instance = $this->getLimitedStorage(3);
- $this->assertFalse($instance->file_put_contents('files/foo', 'foobar'));
- }
-
- public function testCopyNotEnoughSpace(): void {
- $instance = $this->getLimitedStorage(9);
- $this->assertEquals(6, $instance->file_put_contents('files/foo', 'foobar'));
- $instance->getScanner()->scan('');
- $this->assertFalse($instance->copy('files/foo', 'files/bar'));
- }
-
- public function testFreeSpace(): void {
- $instance = $this->getLimitedStorage(9);
- $this->assertEquals(9, $instance->free_space(''));
- }
-
- public function testFreeSpaceWithUsedSpace(): void {
- $instance = $this->getLimitedStorage(9);
- $instance->getCache()->put(
- '', ['size' => 3]
- );
- $this->assertEquals(6, $instance->free_space(''));
- }
-
- public function testFreeSpaceWithUnknownDiskSpace(): void {
- $storage = $this->getMockBuilder(Local::class)
- ->onlyMethods(['free_space'])
- ->setConstructorArgs([['datadir' => $this->tmpDir]])
- ->getMock();
- $storage->expects($this->any())
- ->method('free_space')
- ->willReturn(-2);
- $storage->getScanner()->scan('');
-
- $instance = new Quota(['storage' => $storage, 'quota' => 9]);
- $instance->getCache()->put(
- '', ['size' => 3]
- );
- $this->assertEquals(6, $instance->free_space(''));
- }
-
- public function testFreeSpaceWithUsedSpaceAndEncryption(): void {
- $instance = $this->getLimitedStorage(9);
- $instance->getCache()->put(
- '', ['size' => 7]
- );
- $this->assertEquals(2, $instance->free_space(''));
- }
-
- public function testFWriteNotEnoughSpace(): void {
- $instance = $this->getLimitedStorage(9);
- $stream = $instance->fopen('files/foo', 'w+');
- $this->assertEquals(6, fwrite($stream, 'foobar'));
- $this->assertEquals(3, fwrite($stream, 'qwerty'));
- fclose($stream);
- $this->assertEquals('foobarqwe', $instance->file_get_contents('files/foo'));
- }
-
- public function testStreamCopyWithEnoughSpace(): void {
- $instance = $this->getLimitedStorage(16);
- $inputStream = fopen('data://text/plain,foobarqwerty', 'r');
- $outputStream = $instance->fopen('files/foo', 'w+');
- $count = stream_copy_to_stream($inputStream, $outputStream);
- $this->assertEquals(12, $count);
- fclose($inputStream);
- fclose($outputStream);
- }
-
- public function testStreamCopyNotEnoughSpace(): void {
- $instance = $this->getLimitedStorage(9);
- $inputStream = fopen('data://text/plain,foobarqwerty', 'r');
- $outputStream = $instance->fopen('files/foo', 'w+');
- $count = stream_copy_to_stream($inputStream, $outputStream);
- $this->assertFalse($count);
- fclose($inputStream);
- fclose($outputStream);
- }
-
- public function testReturnFalseWhenFopenFailed(): void {
- $failStorage = $this->getMockBuilder(Local::class)
- ->onlyMethods(['fopen'])
- ->setConstructorArgs([['datadir' => $this->tmpDir]])
- ->getMock();
- $failStorage->expects($this->any())
- ->method('fopen')
- ->willReturn(false);
-
- $instance = new Quota(['storage' => $failStorage, 'quota' => 1000]);
-
- $this->assertFalse($instance->fopen('failedfopen', 'r'));
- }
-
- public function testReturnRegularStreamOnRead(): void {
- $instance = $this->getLimitedStorage(9);
-
- // create test file first
- $stream = $instance->fopen('files/foo', 'w+');
- fwrite($stream, 'blablacontent');
- fclose($stream);
-
- $stream = $instance->fopen('files/foo', 'r');
- $meta = stream_get_meta_data($stream);
- $this->assertEquals('plainfile', $meta['wrapper_type']);
- fclose($stream);
-
- $stream = $instance->fopen('files/foo', 'rb');
- $meta = stream_get_meta_data($stream);
- $this->assertEquals('plainfile', $meta['wrapper_type']);
- fclose($stream);
- }
-
- public function testReturnRegularStreamWhenOutsideFiles(): void {
- $instance = $this->getLimitedStorage(9);
- $instance->mkdir('files_other');
-
- // create test file first
- $stream = $instance->fopen('files_other/foo', 'w+');
- $meta = stream_get_meta_data($stream);
- $this->assertEquals('plainfile', $meta['wrapper_type']);
- fclose($stream);
- }
-
- public function testReturnQuotaStreamOnWrite(): void {
- $instance = $this->getLimitedStorage(9);
- $stream = $instance->fopen('files/foo', 'w+');
- $meta = stream_get_meta_data($stream);
- $expected_type = 'user-space';
- $this->assertEquals($expected_type, $meta['wrapper_type']);
- fclose($stream);
- }
-
- public function testSpaceRoot(): void {
- $storage = $this->getMockBuilder(Local::class)->disableOriginalConstructor()->getMock();
- $cache = $this->getMockBuilder('\OC\Files\Cache\Cache')->disableOriginalConstructor()->getMock();
- $storage->expects($this->once())
- ->method('getCache')
- ->willReturn($cache);
- $storage->expects($this->once())
- ->method('free_space')
- ->willReturn(2048);
- $cache->expects($this->once())
- ->method('get')
- ->with('files')
- ->willReturn(new CacheEntry(['size' => 50]));
-
- $instance = new Quota(['storage' => $storage, 'quota' => 1024, 'root' => 'files']);
-
- $this->assertEquals(1024 - 50, $instance->free_space(''));
- }
-
- public function testInstanceOfStorageWrapper(): void {
- $this->assertTrue($this->instance->instanceOfStorage('\OC\Files\Storage\Local'));
- $this->assertTrue($this->instance->instanceOfStorage('\OC\Files\Storage\Wrapper\Wrapper'));
- $this->assertTrue($this->instance->instanceOfStorage('\OC\Files\Storage\Wrapper\Quota'));
- }
-
- public function testNoMkdirQuotaZero(): void {
- $instance = $this->getLimitedStorage(0.0);
- $this->assertFalse($instance->mkdir('files'));
- $this->assertFalse($instance->mkdir('files/foobar'));
- }
-
- public function testMkdirQuotaZeroTrashbin(): void {
- $instance = $this->getLimitedStorage(0.0);
- $this->assertTrue($instance->mkdir('files_trashbin'));
- $this->assertTrue($instance->mkdir('files_trashbin/files'));
- $this->assertTrue($instance->mkdir('files_versions'));
- $this->assertTrue($instance->mkdir('cache'));
- }
-
- public function testNoTouchQuotaZero(): void {
- $instance = $this->getLimitedStorage(0.0);
- $this->assertFalse($instance->touch('foobar'));
- }
-
- public function testNoFopenQuotaZero(): void {
- $instance = $this->getLimitedStorage(0.0);
- $fh = $instance->fopen('files/test.txt', 'w');
- $this->assertFalse($fh);
- }
-
- public function testNoWriteStreamQuota(): void {
- $instance = $this->getLimitedStorage(5.0);
- $stream = fopen('php://temp', 'w+');
- fwrite($stream, 'foo');
- rewind($stream);
- $instance->writeStream('files/test.txt', $stream);
-
- $stream = fopen('php://temp', 'w+');
- fwrite($stream, 'foobar');
- rewind($stream);
- $this->expectException(NotEnoughSpaceException::class);
- $instance->writeStream('files/test.txt', $stream);
- }
-
- public function testWriteStreamAllowsUploadPathWithUnlimitedFreeSpace(): void {
- $storage = $this->getMockBuilder(Local::class)
- ->onlyMethods(['free_space'])
- ->setConstructorArgs([['datadir' => $this->tmpDir]])
- ->getMock();
- $storage->expects($this->any())
- ->method('free_space')
- ->willReturn(Files\FileInfo::SPACE_UNLIMITED);
- $storage->mkdir('uploads');
-
- $instance = new Quota(['storage' => $storage, 'quota' => 5.0]);
-
- $stream = fopen('php://temp', 'w+');
- fwrite($stream, 'foobar');
- rewind($stream);
-
- $this->assertEquals(6, $instance->writeStream('uploads/chunk', $stream, 6));
- $this->assertEquals('foobar', $instance->file_get_contents('uploads/chunk'));
- }
-
- public function testNoWriteStreamQuotaZero(): void {
- $instance = $this->getLimitedStorage(0.0);
- $stream = fopen('php://temp', 'w+');
- $this->expectException(NotEnoughSpaceException::class);
- $instance->writeStream('files/test.txt', $stream);
- }
-}
--- a/tests/lib/Files/Storage/Wrapper/WrapperTest.php
+++ /dev/null
@@ -1,42 +0,0 @@
-tmpDir = Server::get(ITempManager::class)->getTemporaryFolder();
- $storage = new Local(['datadir' => $this->tmpDir]);
- $this->instance = new Wrapper(['storage' => $storage]);
- }
-
- #[\Override]
- protected function tearDown(): void {
- Files::rmdirr($this->tmpDir);
- parent::tearDown();
- }
-
- public function testInstanceOfStorageWrapper(): void {
- $this->assertTrue($this->instance->instanceOfStorage('\OC\Files\Storage\Local'));
- $this->assertTrue($this->instance->instanceOfStorage('\OC\Files\Storage\Wrapper\Wrapper'));
- }
-}
--- a/tests/lib/Files/Stream/DummyEncryptionWrapper.php
+++ /dev/null
@@ -1,26 +0,0 @@
- $wrapper
- * @return resource
- */
- protected function getStream(string $fileName, string $mode, int $unencryptedSize, string $wrapper = self::DEFAULT_WRAPPER, int $unencryptedSizeOnClose = 0) {
- clearstatcache();
- $size = filesize($fileName);
- $source = fopen($fileName, $mode);
- $internalPath = $fileName;
- $fullPath = $fileName;
- $header = [];
- $uid = '';
- $this->encryptionModule = $this->buildMockModule();
- $cache = $this->createMock(ICache::class);
- $storage = $this->getMockBuilder('\OC\Files\Storage\Storage')
- ->disableOriginalConstructor()->getMock();
- $encStorage = $this->getMockBuilder('\OC\Files\Storage\Wrapper\Encryption')
- ->disableOriginalConstructor()->getMock();
- $config = $this->getMockBuilder(IConfig::class)
- ->disableOriginalConstructor()
- ->getMock();
- $arrayCache = $this->createMock(ArrayCache::class);
- $groupManager = $this->getMockBuilder('\OC\Group\Manager')
- ->disableOriginalConstructor()
- ->getMock();
- $file = $this->getMockBuilder('\OC\Encryption\File')
- ->disableOriginalConstructor()
- ->onlyMethods(['getAccessList'])
- ->getMock();
- $file->expects($this->any())->method('getAccessList')->willReturn([]);
- $util = $this->getMockBuilder('\OC\Encryption\Util')
- ->onlyMethods(['getUidAndFilename'])
- ->setConstructorArgs([new View(), new \OC\User\Manager(
- $config,
- $this->createMock(ICacheFactory::class),
- $this->createMock(IEventDispatcher::class),
- $this->createMock(LoggerInterface::class),
- ), $groupManager, $config, $arrayCache])
- ->getMock();
- $util->expects($this->any())
- ->method('getUidAndFilename')
- ->willReturn(['user1', $internalPath]);
- $storage->expects($this->any())->method('getCache')->willReturn($cache);
- $entry = new CacheEntry([
- 'fileid' => 5,
- 'encryptedVersion' => 2,
- 'unencrypted_size' => $unencryptedSizeOnClose,
- ]);
- $cache->expects($this->any())->method('get')->willReturn($entry);
- $cache->expects($this->any())->method('update')->with(5, ['encrypted' => 3, 'encryptedVersion' => 3, 'unencrypted_size' => $unencryptedSizeOnClose]);
-
- return $wrapper::wrap(
- $source,
- $internalPath,
- $fullPath,
- $header,
- $uid,
- $this->encryptionModule,
- $storage,
- $encStorage,
- $util,
- $file,
- $mode,
- $size,
- $unencryptedSize,
- 8192,
- true,
- $wrapper,
- );
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataProviderStreamOpen')]
- public function testStreamOpen(
- $isMasterKeyUsed,
- $mode,
- $fullPath,
- $fileExists,
- $expectedSharePath,
- $expectedSize,
- $expectedUnencryptedSize,
- $expectedReadOnly,
- ): void {
- // build mocks
- $encryptionModuleMock = $this->createMock(IEncryptionModule::class);
- $encryptionModuleMock->expects($this->any())->method('needDetailedAccessList')->willReturn(!$isMasterKeyUsed);
- $encryptionModuleMock->expects($this->once())
- ->method('getUnencryptedBlockSize')->willReturn(99);
- $encryptionModuleMock->expects($this->once())
- ->method('begin')->willReturn([]);
-
- $storageMock = $this->createMock(Storage::class);
- $storageMock->expects($this->once())->method('file_exists')->willReturn($fileExists);
-
- $fileMock = $this->createMock(File::class);
- if ($isMasterKeyUsed) {
- $fileMock->expects($this->never())->method('getAccessList');
- } else {
- $fileMock->expects($this->once())->method('getAccessList')
- ->willReturnCallback(function ($sharePath) use ($expectedSharePath) {
- $this->assertSame($expectedSharePath, $sharePath);
- return [];
- });
- }
- $utilMock = $this->getMockBuilder(Util::class)
- ->disableOriginalConstructor()->getMock();
- $utilMock->expects($this->any())
- ->method('getHeaderSize')
- ->willReturn(8192);
-
- // get a instance of the stream wrapper
- $streamWrapper = $this->getMockBuilder(Encryption::class)
- ->onlyMethods(['loadContext', 'writeHeader', 'skipHeader'])
- ->disableOriginalConstructor()
- ->getMock();
-
- // set internal properties of the stream wrapper
- $stream = new \ReflectionClass(Encryption::class);
- $encryptionModule = $stream->getProperty('encryptionModule');
- $encryptionModule->setValue($streamWrapper, $encryptionModuleMock);
- $storage = $stream->getProperty('storage');
- $storage->setValue($streamWrapper, $storageMock);
- $file = $stream->getProperty('file');
- $file->setValue($streamWrapper, $fileMock);
- $util = $stream->getProperty('util');
- $util->setValue($streamWrapper, $utilMock);
- $fullPathP = $stream->getProperty('fullPath');
- $fullPathP->setValue($streamWrapper, $fullPath);
- $header = $stream->getProperty('header');
- $header->setValue($streamWrapper, []);
- $this->invokePrivate($streamWrapper, 'signed', [true]);
- $this->invokePrivate($streamWrapper, 'internalPath', [$fullPath]);
- $this->invokePrivate($streamWrapper, 'uid', ['test']);
-
- // call stream_open, that's the method we want to test
- $dummyVar = 'foo';
- $streamWrapper->stream_open('', $mode, '', $dummyVar);
-
- // check internal properties
- $size = $stream->getProperty('size');
- $this->assertSame($expectedSize, $size->getValue($streamWrapper));
-
- $unencryptedSize = $stream->getProperty('unencryptedSize');
- $this->assertSame($expectedUnencryptedSize, $unencryptedSize->getValue($streamWrapper));
-
- $readOnly = $stream->getProperty('readOnly');
- $this->assertSame($expectedReadOnly, $readOnly->getValue($streamWrapper));
- }
-
- public static function dataProviderStreamOpen(): array {
- return [
- [false, 'r', '/foo/bar/test.txt', true, '/foo/bar/test.txt', null, null, true],
- [false, 'r', '/foo/bar/test.txt', false, '/foo/bar', null, null, true],
- [false, 'w', '/foo/bar/test.txt', true, '/foo/bar/test.txt', 8192, 0, false],
- [true, 'r', '/foo/bar/test.txt', true, '/foo/bar/test.txt', null, null, true],
- [true, 'r', '/foo/bar/test.txt', false, '/foo/bar', null, null, true],
- [true, 'w', '/foo/bar/test.txt', true, '/foo/bar/test.txt', 8192, 0, false],
- ];
- }
-
- public function testWriteRead(): void {
- $fileName = tempnam('/tmp', 'FOO');
- $stream = $this->getStream($fileName, 'w+', 0, self::DEFAULT_WRAPPER, 6);
- $this->assertEquals(6, fwrite($stream, 'foobar'));
- fclose($stream);
-
- $stream = $this->getStream($fileName, 'r', 6);
- $this->assertEquals('foobar', fread($stream, 100));
- fclose($stream);
-
- $stream = $this->getStream($fileName, 'r+', 6, self::DEFAULT_WRAPPER, 6);
- $this->assertEquals(3, fwrite($stream, 'bar'));
- fclose($stream);
-
- $stream = $this->getStream($fileName, 'r', 6);
- $this->assertEquals('barbar', fread($stream, 100));
- fclose($stream);
-
- unlink($fileName);
- }
-
- public function testRewind(): void {
- $fileName = tempnam('/tmp', 'FOO');
- $stream = $this->getStream($fileName, 'w+', 0, self::DEFAULT_WRAPPER, 6);
- $this->assertEquals(6, fwrite($stream, 'foobar'));
- $this->assertEquals(true, rewind($stream));
- $this->assertEquals('foobar', fread($stream, 100));
- $this->assertEquals(true, rewind($stream));
- $this->assertEquals(3, fwrite($stream, 'bar'));
- fclose($stream);
-
- $stream = $this->getStream($fileName, 'r', 6);
- $this->assertEquals('barbar', fread($stream, 100));
- fclose($stream);
-
- unlink($fileName);
- }
-
- public function testSeek(): void {
- $fileName = tempnam('/tmp', 'FOO');
-
- $stream = $this->getStream($fileName, 'w+', 0, self::DEFAULT_WRAPPER, 9);
- $this->assertEquals(6, fwrite($stream, 'foobar'));
- $this->assertEquals(0, fseek($stream, 3));
- $this->assertEquals(6, fwrite($stream, 'foobar'));
- fclose($stream);
-
- $stream = $this->getStream($fileName, 'r', 9);
- $this->assertEquals('foofoobar', fread($stream, 100));
- $this->assertEquals(-1, fseek($stream, 10));
- $this->assertEquals(0, fseek($stream, 9));
- $this->assertEquals(-1, fseek($stream, -10, SEEK_CUR));
- $this->assertEquals(0, fseek($stream, -9, SEEK_CUR));
- $this->assertEquals(-1, fseek($stream, -10, SEEK_END));
- $this->assertEquals(0, fseek($stream, -9, SEEK_END));
- fclose($stream);
-
- unlink($fileName);
- }
-
- public static function dataFilesProvider(): array {
- return [
- ['lorem-big.txt'],
- ['block-aligned.txt'],
- ['block-aligned-plus-one.txt'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataFilesProvider')]
- public function testWriteReadBigFile($testFile): void {
- $expectedData = file_get_contents(\OC::$SERVERROOT . '/tests/data/' . $testFile);
- // write it
- $fileName = tempnam('/tmp', 'FOO');
- $stream = $this->getStream($fileName, 'w+', 0, self::DEFAULT_WRAPPER, strlen($expectedData));
- // while writing the file from the beginning to the end we should never try
- // to read parts of the file. This should only happen for write operations
- // in the middle of a file
- $this->encryptionModule->expects($this->never())->method('decrypt');
- fwrite($stream, $expectedData);
- fclose($stream);
-
- // read it all
- $stream = $this->getStream($fileName, 'r', strlen($expectedData));
- $data = stream_get_contents($stream);
- fclose($stream);
-
- $this->assertEquals($expectedData, $data);
-
- // another read test with a loop like we do in several places:
- $stream = $this->getStream($fileName, 'r', strlen($expectedData));
- $data = '';
- while (!feof($stream)) {
- $data .= fread($stream, 8192);
- }
- fclose($stream);
-
- $this->assertEquals($expectedData, $data);
-
- unlink($fileName);
- }
-
- /**
- * simulate a non-seekable storage
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataFilesProvider')]
- public function testWriteToNonSeekableStorage($testFile): void {
- $wrapper = $this->getMockBuilder(Encryption::class)
- ->onlyMethods(['parentStreamSeek'])
- ->getMock();
- $wrapper->expects($this->any())
- ->method('parentStreamSeek')
- ->willReturn(false);
-
- $expectedData = file_get_contents(\OC::$SERVERROOT . '/tests/data/' . $testFile);
- // write it
- $fileName = tempnam('/tmp', 'FOO');
- $stream = $this->getStream($fileName, 'w+', 0, '\Test\Files\Stream\DummyEncryptionWrapper', strlen($expectedData));
- // while writing the file from the beginning to the end we should never try
- // to read parts of the file. This should only happen for write operations
- // in the middle of a file
- $this->encryptionModule->expects($this->never())->method('decrypt');
- fwrite($stream, $expectedData);
- fclose($stream);
-
- // read it all
- $stream = $this->getStream($fileName, 'r', strlen($expectedData), '\Test\Files\Stream\DummyEncryptionWrapper', strlen($expectedData));
- $data = stream_get_contents($stream);
- fclose($stream);
-
- $this->assertEquals($expectedData, $data);
-
- // another read test with a loop like we do in several places:
- $stream = $this->getStream($fileName, 'r', strlen($expectedData));
- $data = '';
- while (!feof($stream)) {
- $data .= fread($stream, 8192);
- }
- fclose($stream);
-
- $this->assertEquals($expectedData, $data);
-
- unlink($fileName);
- }
-
- protected function buildMockModule(): IEncryptionModule&MockObject {
- $encryptionModule = $this->getMockBuilder(IEncryptionModule::class)
- ->disableOriginalConstructor()
- ->onlyMethods(['getId', 'getDisplayName', 'begin', 'end', 'encrypt', 'decrypt', 'update', 'shouldEncrypt', 'getUnencryptedBlockSize', 'isReadable', 'encryptAll', 'prepareDecryptAll', 'isReadyForUser', 'needDetailedAccessList'])
- ->getMock();
-
- $encryptionModule->expects($this->any())->method('getId')->willReturn('UNIT_TEST_MODULE');
- $encryptionModule->expects($this->any())->method('getDisplayName')->willReturn('Unit test module');
- $encryptionModule->expects($this->any())->method('begin')->willReturn([]);
- $encryptionModule->expects($this->any())->method('end')->willReturn('');
- $encryptionModule->expects($this->any())->method('isReadable')->willReturn(true);
- $encryptionModule->expects($this->any())->method('needDetailedAccessList')->willReturn(false);
- $encryptionModule->expects($this->any())->method('encrypt')->willReturnCallback(function ($data) {
- // simulate different block size by adding some padding to the data
- if (isset($data[6125])) {
- return str_pad($data, 8192, 'X');
- }
- // last block
- return $data;
- });
- $encryptionModule->expects($this->any())->method('decrypt')->willReturnCallback(function ($data) {
- if (isset($data[8191])) {
- return substr($data, 0, 6126);
- }
- // last block
- return $data;
- });
- $encryptionModule->expects($this->any())->method('update')->willReturn(true);
- $encryptionModule->expects($this->any())->method('shouldEncrypt')->willReturn(true);
- $encryptionModule->expects($this->any())->method('getUnencryptedBlockSize')->willReturn(6126);
- return $encryptionModule;
- }
-}
--- a/tests/lib/Files/Stream/HashWrapperTest.php
+++ /dev/null
@@ -1,40 +0,0 @@
-assertEquals($hash, $result);
- });
- stream_get_contents($wrapper);
- }
-
- public static function hashProvider(): array {
- return [
- ['foo', 'md5', 'acbd18db4cc2f85cedef654fccc4a4d8'],
- ['foo', 'sha1', '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33'],
- ['foo', 'sha256', '2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae'],
- [str_repeat('foo', 8192), 'md5', '96684d2b796a2c54a026b5d60f9de819'],
- ];
- }
-}
--- a/tests/lib/Files/Stream/QuotaTest.php
+++ /dev/null
@@ -1,153 +0,0 @@
-getStream('w+', 100);
- $this->assertEquals(6, fwrite($stream, 'foobar'));
- rewind($stream);
- $this->assertEquals('foobar', fread($stream, 100));
- }
-
- public function testWriteNotEnoughSpace(): void {
- $stream = $this->getStream('w+', 3);
- $this->assertEquals(3, fwrite($stream, 'foobar'));
- rewind($stream);
- $this->assertEquals('foo', fread($stream, 100));
- }
-
- public function testWriteNotEnoughSpaceSecondTime(): void {
- $stream = $this->getStream('w+', 9);
- $this->assertEquals(6, fwrite($stream, 'foobar'));
- $this->assertEquals(3, fwrite($stream, 'qwerty'));
- rewind($stream);
- $this->assertEquals('foobarqwe', fread($stream, 100));
- }
-
- public function testWriteEnoughSpaceRewind(): void {
- $stream = $this->getStream('w+', 6);
- $this->assertEquals(6, fwrite($stream, 'foobar'));
- rewind($stream);
- $this->assertEquals(3, fwrite($stream, 'qwe'));
- rewind($stream);
- $this->assertEquals('qwebar', fread($stream, 100));
- }
-
- public function testWriteNotEnoughSpaceRead(): void {
- $stream = $this->getStream('w+', 6);
- $this->assertEquals(6, fwrite($stream, 'foobar'));
- rewind($stream);
- $this->assertEquals('foobar', fread($stream, 6));
- $this->assertEquals(0, fwrite($stream, 'qwe'));
- }
-
- public function testWriteNotEnoughSpaceExistingStream(): void {
- $source = fopen('php://temp', 'w+');
- fwrite($source, 'foobar');
- $stream = Quota::wrap($source, 3);
- $this->assertEquals(3, fwrite($stream, 'foobar'));
- rewind($stream);
- $this->assertEquals('foobarfoo', fread($stream, 100));
- }
-
- public function testWriteNotEnoughSpaceExistingStreamRewind(): void {
- $source = fopen('php://temp', 'w+');
- fwrite($source, 'foobar');
- $stream = Quota::wrap($source, 3);
- rewind($stream);
- $this->assertEquals(6, fwrite($stream, 'qwerty'));
- rewind($stream);
- $this->assertEquals('qwerty', fread($stream, 100));
- }
-
- public function testFseekReturnsSuccess(): void {
- $stream = $this->getStream('w+', 100);
- fwrite($stream, '0123456789');
- $this->assertEquals(0, fseek($stream, 3, SEEK_SET));
- $this->assertEquals(0, fseek($stream, -1, SEEK_CUR));
- $this->assertEquals(0, fseek($stream, -4, SEEK_END));
- }
-
- public function testWriteAfterSeekEndWithEnoughSpace(): void {
- $stream = $this->getStream('w+', 100);
- fwrite($stream, '0123456789');
- fseek($stream, -3, SEEK_END);
- $this->assertEquals(11, fwrite($stream, 'abcdefghijk'));
- rewind($stream);
- $this->assertEquals('0123456abcdefghijk', fread($stream, 100));
- }
-
- public function testWriteAfterSeekEndWithNotEnoughSpace(): void {
- $stream = $this->getStream('w+', 13);
- fwrite($stream, '0123456789');
- // seek forward first to potentially week out
- // potential limit calculation errors
- fseek($stream, 4, SEEK_SET);
- // seek to the end
- fseek($stream, -3, SEEK_END);
- $this->assertEquals(6, fwrite($stream, 'abcdefghijk'));
- rewind($stream);
- $this->assertEquals('0123456abcdef', fread($stream, 100));
- }
-
- public function testWriteAfterSeekSetWithEnoughSpace(): void {
- $stream = $this->getStream('w+', 100);
- fwrite($stream, '0123456789');
- fseek($stream, 7, SEEK_SET);
- $this->assertEquals(11, fwrite($stream, 'abcdefghijk'));
- rewind($stream);
- $this->assertEquals('0123456abcdefghijk', fread($stream, 100));
- }
-
- public function testWriteAfterSeekSetWithNotEnoughSpace(): void {
- $stream = $this->getStream('w+', 13);
- fwrite($stream, '0123456789');
- fseek($stream, 7, SEEK_SET);
- $this->assertEquals(6, fwrite($stream, 'abcdefghijk'));
- rewind($stream);
- $this->assertEquals('0123456abcdef', fread($stream, 100));
- }
-
- public function testWriteAfterSeekCurWithEnoughSpace(): void {
- $stream = $this->getStream('w+', 100);
- fwrite($stream, '0123456789');
- rewind($stream);
- fseek($stream, 3, SEEK_CUR);
- fseek($stream, 5, SEEK_CUR);
- fseek($stream, -1, SEEK_CUR);
- $this->assertEquals(11, fwrite($stream, 'abcdefghijk'));
- rewind($stream);
- $this->assertEquals('0123456abcdefghijk', fread($stream, 100));
- }
-
- public function testWriteAfterSeekCurWithNotEnoughSpace(): void {
- $stream = $this->getStream('w+', 13);
- fwrite($stream, '0123456789');
- rewind($stream);
- fseek($stream, 3, SEEK_CUR);
- fseek($stream, 5, SEEK_CUR);
- fseek($stream, -1, SEEK_CUR);
- $this->assertEquals(6, fwrite($stream, 'abcdefghijk'));
- rewind($stream);
- $this->assertEquals('0123456abcdef', fread($stream, 100));
- }
-}
--- a/tests/lib/Files/Template/TemplateManagerTest.php
+++ /dev/null
@@ -1,114 +0,0 @@
-createMock(IL10N::class);
- $l10n->method('t')
- ->willReturnCallback(fn ($string, $params) => sprintf($string, ...$params));
- $l10nFactory = $this->createMock(IFactory::class);
- $l10nFactory->method('get')
- ->willReturn($l10n);
- $database = $this->createMock(IDBConnection::class);
- $database->method('supports4ByteText')->willReturn(true);
- $config = $this->createMock(IConfig::class);
- $logger = new NullLogger();
-
- $filenameValidator = new FilenameValidator(
- $l10nFactory,
- $database,
- $config,
- $logger,
- );
-
- $serverContainer = $this->createMock(ContainerInterface::class);
- $eventDispatcher = $this->createMock(IEventDispatcher::class);
- $this->bootstrapCoordinator = $this->createMock(Coordinator::class);
- $this->bootstrapCoordinator->method('getRegistrationContext')
- ->willReturn(new RegistrationContext($logger));
- $this->rootFolder = $this->createMock(IRootFolder::class);
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('user1');
- $userSession = $this->createMock(IUserSession::class);
- $userSession->method('getUser')
- ->willReturn($user);
- $userManager = $this->createMock(IUserManager::class);
- $previewManager = $this->createMock(IPreview::class);
-
- $this->templateManager = new TemplateManager(
- $serverContainer,
- $eventDispatcher,
- $this->bootstrapCoordinator,
- $this->rootFolder,
- $userSession,
- $userManager,
- $previewManager,
- $config,
- $l10nFactory,
- $logger,
- $filenameValidator,
- \OC::$SERVERROOT,
- );
- }
-
- public function testCreateFromTemplateShoudValidateFilename(): void {
- $this->expectException(GenericFileException::class);
-
- $fileDirectory = '/';
- $filePath = $fileDirectory . str_repeat('a', 251);
-
- $userFolder = $this->createMock(Folder::class);
- $userFolder->method('get')
- ->willReturnCallback(function ($path) use ($filePath, $fileDirectory) {
- if ($path === $filePath) {
- throw new NotFoundException();
- }
- return $this->createMock(Folder::class);
- });
- $userFolder->method('nodeExists')
- ->willReturnCallback(function ($path) use ($filePath, $fileDirectory): bool {
- return $path === $fileDirectory;
- });
- $this->rootFolder->method('getUserFolder')
- ->willReturn($userFolder);
-
- $this->templateManager->createFromTemplate($filePath);
- }
-}
--- a/tests/lib/Files/Type/DetectionTest.php
+++ /dev/null
@@ -1,400 +0,0 @@
-detection = new Detection(
- Server::get(IURLGenerator::class),
- Server::get(LoggerInterface::class),
- \OC::$SERVERROOT . '/config/',
- \OC::$SERVERROOT . '/resources/config/'
- );
- }
-
- public static function dataDetectPath(): array {
- return [
- ['foo.txt', 'text/plain'],
- ['foo.png', 'image/png'],
- ['foo.bar.png', 'image/png'],
- ['.hidden.png', 'image/png'],
- ['.hidden.foo.png', 'image/png'],
- ['.hidden/foo.png', 'image/png'],
- ['.hidden/.hidden.png', 'image/png'],
- ['test.jpg/foo.png', 'image/png'],
- ['.png', 'application/octet-stream'],
- ['..hidden', 'application/octet-stream'],
- ['foo', 'application/octet-stream'],
- ['', 'application/octet-stream'],
- ['foo.png.ocTransferId123456789.part', 'image/png'],
- ['foo.png.v1234567890', 'image/png'],
- ];
- }
-
- /**
- *
- * @param string $path
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataDetectPath')]
- public function testDetectPath(string $path, string $expected): void {
- $this->assertEquals($expected, $this->detection->detectPath($path));
- }
-
- public static function dataDetectContent(): array {
- return [
- ['/', 'httpd/unix-directory'],
- ['/data.tar.gz', 'application/gzip'],
- ['/data.zip', 'application/zip'],
- ['/testimage.mp3', 'audio/mpeg'],
- ['/testimage.png', 'image/png'],
- ];
- }
-
- /**
- *
- * @param string $path
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataDetectContent')]
- public function testDetectContent(string $path, string $expected): void {
- $this->assertEquals($expected, $this->detection->detectContent(\OC::$SERVERROOT . '/tests/data' . $path));
- }
-
- public static function dataDetect(): array {
- return [
- ['/', 'httpd/unix-directory'],
- ['/data.tar.gz', 'application/gzip'],
- ['/data.zip', 'application/zip'],
- ['/testimagelarge.svg', 'image/svg+xml'],
- ['/testimage.png', 'image/png'],
- ];
- }
-
- /**
- *
- * @param string $path
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataDetect')]
- public function testDetect(string $path, string $expected): void {
- $this->assertEquals($expected, $this->detection->detect(\OC::$SERVERROOT . '/tests/data' . $path));
- }
-
- public function testDetectString(): void {
- $result = $this->detection->detectString('/data/data.tar.gz');
- $expected = 'text/plain';
- $this->assertEquals($expected, $result);
- }
-
- public static function dataMimeTypeCustom(): array {
- return [
- ['123', 'foobar/123'],
- ['a123', 'foobar/123'],
- ['bar', 'foobar/bar'],
- ];
- }
-
- /**
- *
- * @param string $ext
- * @param string $mime
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataMimeTypeCustom')]
- public function testDetectMimeTypeCustom(string $ext, string $mime): void {
- $tmpDir = sys_get_temp_dir() . '/nc-detect-' . uniqid('', true);
- mkdir($tmpDir, 0700);
-
- try {
- // Create a default / shipped mapping file (dist)
- file_put_contents($tmpDir . '/mimetypemapping.dist.json', json_encode([]));
-
- // Create new custom mapping file that should be picked up by Detection
- file_put_contents($tmpDir . '/mimetypemapping.json', json_encode([$ext => [$mime]]));
-
- /** @var IURLGenerator $urlGenerator */
- $urlGenerator = $this->getMockBuilder(IURLGenerator::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- /** @var LoggerInterface $logger */
- $logger = $this->createMock(LoggerInterface::class);
-
- $detection = new Detection($urlGenerator, $logger, $tmpDir, $tmpDir);
- $mappings = $detection->getAllMappings();
-
- $this->assertArrayHasKey($ext, $mappings);
- $this->assertEquals($mime, $detection->detectPath('foo.' . $ext));
- } finally {
- // cleanup
- @unlink($tmpDir . '/mimetypemapping.json');
- @unlink($tmpDir . '/mimetypemapping.dist.json');
- @rmdir($tmpDir);
- }
- }
-
- public function testDetectMimeTypePreservesLeadingZeroKeys(): void {
- $tmpDir = sys_get_temp_dir() . '/nc-detect-' . uniqid();
- mkdir($tmpDir, 0700);
- try {
- // Create a default / shipped mapping file (dist)
- file_put_contents($tmpDir . '/mimetypemapping.dist.json', json_encode([]));
-
- // Create new custom mapping file with potentially problematic keys
- $mappings = [
- '001' => ['application/x-zeroone', null],
- '1' => ['application/x-one', null],
- ];
- file_put_contents($tmpDir . '/mimetypemapping.json', json_encode($mappings));
-
- /** @var IURLGenerator $urlGenerator */
- $urlGenerator = $this->getMockBuilder(IURLGenerator::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- /** @var LoggerInterface $logger */
- $logger = $this->createMock(LoggerInterface::class);
-
- $detection = new Detection($urlGenerator, $logger, $tmpDir, $tmpDir);
- $mappings = $detection->getAllMappings();
-
- $this->assertArrayHasKey('001', $mappings, 'Expected mapping to contain key "001"');
- $this->assertArrayHasKey('1', $mappings, 'Expected mapping to contain key "1"');
-
- $this->assertEquals('application/x-zeroone', $detection->detectPath('foo.001'));
- $this->assertEquals('application/x-one', $detection->detectPath('foo.1'));
- } finally {
- @unlink($tmpDir . '/mimetypemapping.json');
- @unlink($tmpDir . '/mimetypemapping.dist.json');
- @rmdir($tmpDir);
- }
- }
-
- public static function dataGetSecureMimeType(): array {
- return [
- ['image/svg+xml', 'text/plain'],
- ['image/png', 'image/png'],
- ];
- }
-
- /**
- *
- * @param string $mimeType
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetSecureMimeType')]
- public function testGetSecureMimeType(string $mimeType, string $expected): void {
- $this->assertEquals($expected, $this->detection->getSecureMimeType($mimeType));
- }
-
- public function testMimeTypeIcon(): void {
- if (!class_exists('org\\bovigo\\vfs\\vfsStream')) {
- $this->markTestSkipped('Package vfsStream not installed');
- }
- $confDir = \org\bovigo\vfs\vfsStream::setup();
- $mimetypealiases_dist = \org\bovigo\vfs\vfsStream::newFile('mimetypealiases.dist.json')->at($confDir);
-
- //Empty alias file
- $mimetypealiases_dist->setContent(json_encode([], JSON_FORCE_OBJECT));
-
- /*
- * Test dir mimetype
- */
-
- //Mock UrlGenerator
- $urlGenerator = $this->getMockBuilder(IURLGenerator::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- /** @var LoggerInterface $logger */
- $logger = $this->createMock(LoggerInterface::class);
-
- //Only call the url generator once
- $urlGenerator->expects($this->once())
- ->method('imagePath')
- ->with($this->equalTo('core'), $this->equalTo('filetypes/folder.png'))
- ->willReturn('folder.svg');
-
- $detection = new Detection($urlGenerator, $logger, $confDir->url(), $confDir->url());
- $mimeType = $detection->mimeTypeIcon('dir');
- $this->assertEquals('folder.svg', $mimeType);
-
- /*
- * Test dir-shareed mimetype
- */
- //Mock UrlGenerator
- $urlGenerator = $this->getMockBuilder(IURLGenerator::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- //Only call the url generator once
- $urlGenerator->expects($this->once())
- ->method('imagePath')
- ->with($this->equalTo('core'), $this->equalTo('filetypes/folder-shared.png'))
- ->willReturn('folder-shared.svg');
-
- $detection = new Detection($urlGenerator, $logger, $confDir->url(), $confDir->url());
- $mimeType = $detection->mimeTypeIcon('dir-shared');
- $this->assertEquals('folder-shared.svg', $mimeType);
-
- /*
- * Test dir external
- */
-
- //Mock UrlGenerator
- $urlGenerator = $this->getMockBuilder(IURLGenerator::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- //Only call the url generator once
- $urlGenerator->expects($this->once())
- ->method('imagePath')
- ->with($this->equalTo('core'), $this->equalTo('filetypes/folder-external.png'))
- ->willReturn('folder-external.svg');
-
- $detection = new Detection($urlGenerator, $logger, $confDir->url(), $confDir->url());
- $mimeType = $detection->mimeTypeIcon('dir-external');
- $this->assertEquals('folder-external.svg', $mimeType);
-
- /*
- * Test complete mimetype
- */
-
- //Mock UrlGenerator
- $urlGenerator = $this->getMockBuilder(IURLGenerator::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- //Only call the url generator once
- $urlGenerator->expects($this->once())
- ->method('imagePath')
- ->with($this->equalTo('core'), $this->equalTo('filetypes/my-type.png'))
- ->willReturn('my-type.svg');
-
- $detection = new Detection($urlGenerator, $logger, $confDir->url(), $confDir->url());
- $mimeType = $detection->mimeTypeIcon('my-type');
- $this->assertEquals('my-type.svg', $mimeType);
-
- /*
- * Test subtype
- */
-
- //Mock UrlGenerator
- $urlGenerator = $this->getMockBuilder(IURLGenerator::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- //Only call the url generator once
- $calls = [
- ['core', 'filetypes/my-type.png'],
- ['core', 'filetypes/my.png'],
- ];
- $urlGenerator->expects($this->exactly(2))
- ->method('imagePath')
- ->willReturnCallback(
- function ($appName, $file) use (&$calls) {
- $expected = array_shift($calls);
- $this->assertEquals($expected, [$appName, $file]);
- if ($file === 'filetypes/my.png') {
- return 'my.svg';
- }
- throw new \RuntimeException();
- }
- );
-
- $detection = new Detection($urlGenerator, $logger, $confDir->url(), $confDir->url());
- $mimeType = $detection->mimeTypeIcon('my-type');
- $this->assertEquals('my.svg', $mimeType);
-
- /*
- * Test default mimetype
- */
-
- //Mock UrlGenerator
- $urlGenerator = $this->getMockBuilder(IURLGenerator::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- //Only call the url generator once
- $calls = [
- ['core', 'filetypes/foo-bar.png'],
- ['core', 'filetypes/foo.png'],
- ['core', 'filetypes/file.png'],
- ];
- $urlGenerator->expects($this->exactly(3))
- ->method('imagePath')
- ->willReturnCallback(
- function ($appName, $file) use (&$calls) {
- $expected = array_shift($calls);
- $this->assertEquals($expected, [$appName, $file]);
- if ($file === 'filetypes/file.png') {
- return 'file.svg';
- }
- throw new \RuntimeException();
- }
- );
-
- $detection = new Detection($urlGenerator, $logger, $confDir->url(), $confDir->url());
- $mimeType = $detection->mimeTypeIcon('foo-bar');
- $this->assertEquals('file.svg', $mimeType);
-
- /*
- * Test chaching
- */
-
- //Mock UrlGenerator
- $urlGenerator = $this->getMockBuilder(IURLGenerator::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- //Only call the url generator once
- $urlGenerator->expects($this->once())
- ->method('imagePath')
- ->with($this->equalTo('core'), $this->equalTo('filetypes/foo-bar.png'))
- ->willReturn('foo-bar.svg');
-
- $detection = new Detection($urlGenerator, $logger, $confDir->url(), $confDir->url());
- $mimeType = $detection->mimeTypeIcon('foo-bar');
- $this->assertEquals('foo-bar.svg', $mimeType);
- $mimeType = $detection->mimeTypeIcon('foo-bar');
- $this->assertEquals('foo-bar.svg', $mimeType);
-
- /*
- * Test aliases
- */
-
- //Put alias
- $mimetypealiases_dist->setContent(json_encode(['foo' => 'foobar/baz'], JSON_FORCE_OBJECT));
-
- //Mock UrlGenerator
- $urlGenerator = $this->getMockBuilder(IURLGenerator::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- //Only call the url generator once
- $urlGenerator->expects($this->once())
- ->method('imagePath')
- ->with($this->equalTo('core'), $this->equalTo('filetypes/foobar-baz.png'))
- ->willReturn('foobar-baz.svg');
-
- $detection = new Detection($urlGenerator, $logger, $confDir->url(), $confDir->url());
- $mimeType = $detection->mimeTypeIcon('foo');
- $this->assertEquals('foobar-baz.svg', $mimeType);
- }
-}
--- a/tests/lib/Files/Type/LoaderTest.php
+++ /dev/null
@@ -1,87 +0,0 @@
-db = Server::get(IDBConnection::class);
- $this->loader = new Loader($this->db);
- }
-
- #[\Override]
- protected function tearDown(): void {
- $deleteMimetypes = $this->db->getQueryBuilder();
- $deleteMimetypes->delete('mimetypes')
- ->where($deleteMimetypes->expr()->like(
- 'mimetype', $deleteMimetypes->createPositionalParameter('testing/%')
- ));
- $deleteMimetypes->executeStatement();
- parent::tearDown();
- }
-
- public function testGetMimetype(): void {
- $qb = $this->db->getQueryBuilder();
- $qb->insert('mimetypes')
- ->values([
- 'mimetype' => $qb->createPositionalParameter('testing/mymimetype')
- ]);
- $qb->executeStatement();
-
- $this->assertTrue($this->loader->exists('testing/mymimetype'));
- $mimetypeId = $this->loader->getId('testing/mymimetype');
- $this->assertNotNull($mimetypeId);
-
- $mimetype = $this->loader->getMimetypeById($mimetypeId);
- $this->assertEquals('testing/mymimetype', $mimetype);
- }
-
- public function testGetNonexistentMimetype(): void {
- $this->assertFalse($this->loader->exists('testing/nonexistent'));
- // hopefully this ID doesn't exist
- $this->assertNull($this->loader->getMimetypeById(12345));
- }
-
- public function testStore(): void {
- $this->assertFalse($this->loader->exists('testing/mymimetype'));
- $mimetypeId = $this->loader->getId('testing/mymimetype');
-
- $qb = $this->db->getQueryBuilder();
- $qb->select('mimetype')
- ->from('mimetypes')
- ->where($qb->expr()->eq('id', $qb->createPositionalParameter($mimetypeId)));
-
- $result = $qb->executeQuery();
- $mimetype = $result->fetchAssociative();
- $result->closeCursor();
- $this->assertEquals('testing/mymimetype', $mimetype['mimetype']);
-
- $this->assertEquals('testing/mymimetype', $this->loader->getMimetypeById($mimetypeId));
- $this->assertEquals($mimetypeId, $this->loader->getId('testing/mymimetype'));
- }
-
- public function testStoreExists(): void {
- $mimetypeId = $this->loader->getId('testing/mymimetype');
- $mimetypeId2 = $this->loader->getId('testing/mymimetype');
-
- $this->assertEquals($mimetypeId, $mimetypeId2);
- }
-}
--- a/tests/lib/Files/Utils/ScannerTest.php
+++ /dev/null
@@ -1,247 +0,0 @@
- $mounts */
- private array $mounts = [];
-
- public function addMount(MountPoint $mount): void {
- $this->mounts[$mount->getMountPoint()] = $mount;
- }
-
- #[\Override]
- protected function getMounts(string $dir): array {
- return $this->mounts;
- }
-}
-
-#[Group('DB')]
-class ScannerTest extends TestCase {
- private Dummy $userBackend;
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- $this->userBackend = new Dummy();
- Server::get(IUserManager::class)->registerBackend($this->userBackend);
- $this->loginAsUser();
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->logout();
- Server::get(IUserManager::class)->removeBackend($this->userBackend);
- parent::tearDown();
- }
-
- public function testReuseExistingRoot(): void {
- $storage = new Temporary([]);
- $mount = new MountPoint($storage, '');
- Filesystem::getMountManager()->addMount($mount);
- $cache = $storage->getCache();
-
- $storage->mkdir('folder');
- $storage->file_put_contents('foo.txt', 'qwerty');
- $storage->file_put_contents('folder/bar.txt', 'qwerty');
-
- $scanner = new TestScanner(
- Server::get(IUserManager::class)->get(''),
- Server::get(IDBConnection::class),
- $this->createMock(IEventDispatcher::class),
- Server::get(LoggerInterface::class),
- Server::get(SetupManager::class),
- );
- $scanner->addMount($mount);
-
- $scanner->scan('');
- $this->assertTrue($cache->inCache('folder/bar.txt'));
- $oldRoot = $cache->get('');
-
- $scanner->scan('');
- $newRoot = $cache->get('');
- $this->assertEquals($oldRoot, $newRoot);
- }
-
- public function testReuseExistingFile(): void {
- $storage = new Temporary([]);
- $mount = new MountPoint($storage, '');
- Filesystem::getMountManager()->addMount($mount);
- $cache = $storage->getCache();
-
- $storage->mkdir('folder');
- $storage->file_put_contents('foo.txt', 'qwerty');
- $storage->file_put_contents('folder/bar.txt', 'qwerty');
-
- $scanner = new TestScanner(
- Server::get(IUserManager::class)->get(''),
- Server::get(IDBConnection::class),
- $this->createMock(IEventDispatcher::class),
- Server::get(LoggerInterface::class),
- Server::get(SetupManager::class),
- );
- $scanner->addMount($mount);
-
- $scanner->scan('');
- $this->assertTrue($cache->inCache('folder/bar.txt'));
- $old = $cache->get('folder/bar.txt');
-
- $scanner->scan('');
- $new = $cache->get('folder/bar.txt');
- $this->assertEquals($old, $new);
- }
-
- public function testScanSubMount(): void {
- $uid = $this->getUniqueID();
- $this->userBackend->createUser($uid, 'test');
-
- $mountProvider = $this->createMock(IMountProvider::class);
-
- $storage = new Temporary([]);
- $mount = new MountPoint($storage, '/' . $uid . '/files/foo');
-
- $mountProvider->expects($this->any())
- ->method('getMountsForUser')
- ->willReturnCallback(function (IUser $user, IStorageFactory $storageFactory) use ($mount, $uid) {
- if ($user->getUID() === $uid) {
- return [$mount];
- } else {
- return [];
- }
- });
-
- Server::get(IMountProviderCollection::class)->registerProvider($mountProvider);
- $cache = $storage->getCache();
-
- $storage->mkdir('folder');
- $storage->file_put_contents('foo.txt', 'qwerty');
- $storage->file_put_contents('folder/bar.txt', 'qwerty');
-
- $scanner = new Scanner(
- Server::get(IUserManager::class)->get($uid),
- Server::get(IDBConnection::class),
- Server::get(IEventDispatcher::class),
- Server::get(LoggerInterface::class),
- Server::get(SetupManager::class),
- );
-
- $this->assertFalse($cache->inCache('folder/bar.txt'));
- $scanner->scan('/' . $uid . '/files/foo');
- $this->assertTrue($cache->inCache('folder/bar.txt'));
- }
-
- public static function invalidPathProvider(): \Generator {
- yield [ '../' ];
- yield [ '..\\' ];
- yield [ '../..\\../' ];
- }
-
- #[DataProvider(methodName: 'invalidPathProvider')]
- public function testInvalidPathScanning(string $invalidPath): void {
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Invalid path to scan');
-
- $scanner = new TestScanner(
- Server::get(IUserManager::class)->get(''),
- Server::get(IDBConnection::class),
- $this->createMock(IEventDispatcher::class),
- Server::get(LoggerInterface::class),
- Server::get(SetupManager::class),
- );
- $scanner->scan($invalidPath);
- }
-
- public function testPropagateEtag(): void {
- $storage = new Temporary([]);
- $mount = new MountPoint($storage, '');
- Filesystem::getMountManager()->addMount($mount);
- $cache = $storage->getCache();
-
- $storage->mkdir('folder');
- $storage->file_put_contents('folder/bar.txt', 'qwerty');
- $storage->touch('folder/bar.txt', time() - 200);
-
- $scanner = new TestScanner(
- Server::get(IUserManager::class)->get(''),
- Server::get(IDBConnection::class),
- $this->createMock(IEventDispatcher::class),
- Server::get(LoggerInterface::class),
- Server::get(SetupManager::class),
- );
- $scanner->addMount($mount);
-
- $scanner->scan('');
- $this->assertTrue($cache->inCache('folder/bar.txt'));
- $oldRoot = $cache->get('');
-
- $storage->file_put_contents('folder/bar.txt', 'qwerty');
- $scanner->scan('');
- $newRoot = $cache->get('');
-
- $this->assertNotEquals($oldRoot->getEtag(), $newRoot->getEtag());
- }
-
- public function testShallow(): void {
- $storage = new Temporary([]);
- $mount = new MountPoint($storage, '');
- Filesystem::getMountManager()->addMount($mount);
- $cache = $storage->getCache();
-
- $storage->mkdir('folder');
- $storage->mkdir('folder/subfolder');
- $storage->file_put_contents('foo.txt', 'qwerty');
- $storage->file_put_contents('folder/bar.txt', 'qwerty');
- $storage->file_put_contents('folder/subfolder/foobar.txt', 'qwerty');
-
- $scanner = new TestScanner(
- Server::get(IUserManager::class)->get(''),
- Server::get(IDBConnection::class),
- $this->createMock(IEventDispatcher::class),
- Server::get(LoggerInterface::class),
- Server::get(SetupManager::class),
- );
- $scanner->addMount($mount);
-
- $scanner->scan('', false);
- $this->assertTrue($cache->inCache('folder'));
- $this->assertFalse($cache->inCache('folder/subfolder'));
- $this->assertTrue($cache->inCache('foo.txt'));
- $this->assertFalse($cache->inCache('folder/bar.txt'));
- $this->assertFalse($cache->inCache('folder/subfolder/foobar.txt'));
-
- $scanner->scan('folder', false);
- $this->assertTrue($cache->inCache('folder'));
- $this->assertTrue($cache->inCache('folder/subfolder'));
- $this->assertTrue($cache->inCache('foo.txt'));
- $this->assertTrue($cache->inCache('folder/bar.txt'));
- $this->assertFalse($cache->inCache('folder/subfolder/foobar.txt'));
- }
-}
--- a/tests/lib/Files/ViewTest.php
+++ /dev/null
@@ -1,2939 +0,0 @@
-clearBackends();
- Server::get(IUserManager::class)->registerBackend(new \Test\Util\User\Dummy());
-
- $userManager = Server::get(IUserManager::class);
- $groupManager = Server::get(IGroupManager::class);
-
- self::$userObject = $userManager->createUser(self::$user, self::$user);
- self::$groupObject = $groupManager->createGroup('group1');
- self::$groupObject->addUser(self::$userObject);
- }
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
- \OC_Hook::clear();
-
- self::loginAsUser(self::$user);
-
- $manager = Server::get(IMountManager::class);
- $manager->removeMount('/test');
-
- $this->tempStorage = null;
- }
-
- #[\Override]
- protected function tearDown(): void {
- \OC_User::setUserId(self::$user);
- foreach ($this->storages as $storage) {
- $cache = $storage->getCache();
- $ids = $cache->getAll();
- $cache->clear();
- }
-
- if ($this->tempStorage) {
- system('rm -rf ' . escapeshellarg($this->tempStorage->getDataDir()));
- }
-
- self::logout();
-
- /** @var SetupManager $setupManager */
- $setupManager = Server::get(SetupManager::class);
- $setupManager->setupRoot();
-
- $mountProviderCollection = Server::get(IMountProviderCollection::class);
- self::invokePrivate($mountProviderCollection, 'providers', [[]]);
-
- parent::tearDown();
- }
-
- #[Override]
- public static function tearDownAfterClass(): void {
- self::$userObject->delete();
- self::$groupObject->delete();
- }
-
- public function testCacheAPI(): void {
- $storage1 = $this->getTestStorage();
- $storage2 = $this->getTestStorage();
- $storage3 = $this->getTestStorage();
- $root = self::getUniqueID('/');
- Filesystem::mount($storage1, [], $root . '/');
- Filesystem::mount($storage2, [], $root . '/substorage');
- Filesystem::mount($storage3, [], $root . '/folder/anotherstorage');
- $textSize = strlen("dummy file data\n");
- $imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo/logo.png');
- $storageSize = $textSize * 2 + $imageSize;
-
- $storageInfo = $storage3->getCache()->get('');
- $this->assertEquals($storageSize, $storageInfo['size']);
-
- $rootView = new View($root);
-
- $cachedData = $rootView->getFileInfo('/foo.txt');
- $this->assertEquals($textSize, $cachedData['size']);
- $this->assertEquals('text/plain', $cachedData['mimetype']);
- $this->assertNotEquals(-1, $cachedData['permissions']);
-
- $cachedData = $rootView->getFileInfo('/');
- $this->assertEquals($storageSize * 3, $cachedData['size']);
- $this->assertEquals('httpd/unix-directory', $cachedData['mimetype']);
-
- // get cached data excluding mount points
- $cachedData = $rootView->getFileInfo('/', false);
- $this->assertEquals($storageSize, $cachedData['size']);
- $this->assertEquals('httpd/unix-directory', $cachedData['mimetype']);
-
- $cachedData = $rootView->getFileInfo('/folder');
- $this->assertEquals($storageSize + $textSize, $cachedData['size']);
- $this->assertEquals('httpd/unix-directory', $cachedData['mimetype']);
-
- $folderData = $rootView->getDirectoryContent('/');
- usort($folderData, fn (FileInfo $a, FileInfo $b) => $a->getName() <=> $b->getName());
- /**
- * expected entries:
- * folder
- * foo.png
- * foo.txt
- * substorage
- */
- $this->assertCount(4, $folderData);
- $this->assertEquals('folder', $folderData[0]['name']);
- $this->assertEquals('foo.png', $folderData[1]['name']);
- $this->assertEquals('foo.txt', $folderData[2]['name']);
- $this->assertEquals('substorage', $folderData[3]['name']);
-
- $this->assertEquals($storageSize + $textSize, $folderData[0]['size']);
- $this->assertEquals($imageSize, $folderData[1]['size']);
- $this->assertEquals($textSize, $folderData[2]['size']);
- $this->assertEquals($storageSize, $folderData[3]['size']);
-
- $folderData = $rootView->getDirectoryContent('/substorage');
- usort($folderData, fn (FileInfo $a, FileInfo $b) => $a->getName() <=> $b->getName());
- /**
- * expected entries:
- * folder
- * foo.png
- * foo.txt
- */
- $this->assertCount(3, $folderData);
- $this->assertEquals('folder', $folderData[0]['name']);
- $this->assertEquals('foo.png', $folderData[1]['name']);
- $this->assertEquals('foo.txt', $folderData[2]['name']);
-
- $folderView = new View($root . '/folder');
- $this->assertEquals($rootView->getFileInfo('/folder'), $folderView->getFileInfo('/'));
-
- $cachedData = $rootView->getFileInfo('/foo.txt');
- $this->assertFalse($cachedData['encrypted']);
- $id = $rootView->putFileInfo('/foo.txt', ['encrypted' => true]);
- $cachedData = $rootView->getFileInfo('/foo.txt');
- $this->assertTrue($cachedData['encrypted']);
- $this->assertEquals($cachedData['fileid'], $id);
-
- $this->assertFalse($rootView->getFileInfo('/non/existing'));
- $this->assertEquals([], $rootView->getDirectoryContent('/non/existing'));
- }
-
- public function testGetPath(): void {
- $user = $this->createMock(IUser::class);
- $user->method('getUID')
- ->willReturn('test');
- $storage1 = $this->getTestStorage();
- $storage2 = $this->getTestStorage();
- $storage3 = $this->getTestStorage();
-
- Filesystem::mount($storage1, [], '/test/files');
- Filesystem::mount($storage2, [], '/test/files/substorage');
- Filesystem::mount($storage3, [], '/test/files/folder/anotherstorage');
-
- $userMountCache = Server::get(IUserMountCache::class);
- $userMountCache->registerMounts($user, [
- new MountPoint($storage1, '/test/files'),
- new MountPoint($storage2, '/test/files/substorage'),
- new MountPoint($storage3, '/test/files/folder/anotherstorage'),
- ]);
-
- $rootView = new View('/test/files');
-
- $cachedData = $rootView->getFileInfo('/foo.txt');
- $id1 = $cachedData->getId();
- $this->assertEquals('/foo.txt', $rootView->getPath($id1));
-
- $cachedData = $rootView->getFileInfo('/substorage/foo.txt');
- $id2 = $cachedData->getId();
- $this->assertEquals('/substorage/foo.txt', $rootView->getPath($id2));
-
- $folderView = new View('/test/files/substorage');
- $this->assertEquals('/foo.txt', $folderView->getPath($id2));
- }
-
- public function testGetPathNotExisting(): void {
- $this->expectException(NotFoundException::class);
-
- $storage1 = $this->getTestStorage();
- Filesystem::mount($storage1, [], '/');
-
- $rootView = new View('');
- $cachedData = $rootView->getFileInfo('/foo.txt');
- /** @var int $id1 */
- $id1 = $cachedData['fileid'];
- $folderView = new View('/substorage');
- $this->assertNull($folderView->getPath($id1));
- }
-
- public function testMountPointOverwrite(): void {
- $storage1 = $this->getTestStorage(false);
- $storage2 = $this->getTestStorage();
- $storage1->mkdir('substorage');
- Filesystem::mount($storage1, [], '/');
- Filesystem::mount($storage2, [], '/substorage');
-
- $rootView = new View('');
- $folderContent = $rootView->getDirectoryContent('/');
- $this->assertCount(4, $folderContent);
- }
-
- public static function sharingDisabledPermissionProvider(): array {
- return [
- ['no', '', true],
- ['yes', 'group1', false],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('sharingDisabledPermissionProvider')]
- public function testRemoveSharePermissionWhenSharingDisabledForUser($excludeGroups, $excludeGroupsList, $expectedShareable): void {
- // Reset sharing disabled for users cache
- self::invokePrivate(Server::get(ShareDisableChecker::class), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]);
-
- $config = Server::get(IConfig::class);
- $oldExcludeGroupsFlag = $config->getAppValue('core', 'shareapi_exclude_groups', 'no');
- $oldExcludeGroupsList = $config->getAppValue('core', 'shareapi_exclude_groups_list', '');
- $config->setAppValue('core', 'shareapi_exclude_groups', $excludeGroups);
- $config->setAppValue('core', 'shareapi_exclude_groups_list', $excludeGroupsList);
-
- $storage1 = $this->getTestStorage();
- $storage2 = $this->getTestStorage();
- Filesystem::mount($storage1, [], '/');
- Filesystem::mount($storage2, [], '/mount');
-
- $view = new View('/');
-
- $folderContent = $view->getDirectoryContent('');
- $this->assertEquals($expectedShareable, $folderContent[0]->isShareable());
-
- $folderContent = $view->getDirectoryContent('mount');
- $this->assertEquals($expectedShareable, $folderContent[0]->isShareable());
-
- $config->setAppValue('core', 'shareapi_exclude_groups', $oldExcludeGroupsFlag);
- $config->setAppValue('core', 'shareapi_exclude_groups_list', $oldExcludeGroupsList);
-
- // Reset sharing disabled for users cache
- self::invokePrivate(Server::get(ShareDisableChecker::class), 'sharingDisabledForUsersCache', [new CappedMemoryCache()]);
- }
-
- public function testCacheIncompleteFolder(): void {
- $storage1 = $this->getTestStorage(false);
- Filesystem::mount($storage1, [], '/incomplete');
- $rootView = new View('/incomplete');
-
- $entries = $rootView->getDirectoryContent('/');
- $this->assertCount(3, $entries);
-
- // /folder will already be in the cache but not scanned
- $entries = $rootView->getDirectoryContent('/folder');
- $this->assertCount(1, $entries);
- }
-
- public function testAutoScan(): void {
- $storage1 = $this->getTestStorage(false);
- $storage2 = $this->getTestStorage(false);
- Filesystem::mount($storage1, [], '/');
- Filesystem::mount($storage2, [], '/substorage');
- $textSize = strlen("dummy file data\n");
-
- $rootView = new View('');
-
- $cachedData = $rootView->getFileInfo('/');
- $this->assertEquals('httpd/unix-directory', $cachedData['mimetype']);
- $this->assertEquals(-1, $cachedData['size']);
-
- $folderData = $rootView->getDirectoryContent('/substorage/folder');
- $this->assertEquals('text/plain', $folderData[0]['mimetype']);
- $this->assertEquals($textSize, $folderData[0]['size']);
- }
-
- public function testSearch(): void {
- $storage1 = $this->getTestStorage();
- $storage2 = $this->getTestStorage();
- $storage3 = $this->getTestStorage();
- Filesystem::mount($storage1, [], '/');
- Filesystem::mount($storage2, [], '/substorage');
- Filesystem::mount($storage3, [], '/folder/anotherstorage');
-
- $rootView = new View('');
-
- $results = $rootView->search('foo');
- $this->assertCount(6, $results);
- $paths = [];
- foreach ($results as $result) {
- $this->assertEquals($result['path'], Filesystem::normalizePath($result['path']));
- $paths[] = $result['path'];
- }
- $this->assertContains('/foo.txt', $paths);
- $this->assertContains('/foo.png', $paths);
- $this->assertContains('/substorage/foo.txt', $paths);
- $this->assertContains('/substorage/foo.png', $paths);
- $this->assertContains('/folder/anotherstorage/foo.txt', $paths);
- $this->assertContains('/folder/anotherstorage/foo.png', $paths);
-
- $folderView = new View('/folder');
- $results = $folderView->search('bar');
- $this->assertCount(2, $results);
- $paths = [];
- foreach ($results as $result) {
- $paths[] = $result['path'];
- }
- $this->assertContains('/folder/anotherstorage/folder/bar.txt', $paths);
- $this->assertContains('/folder/bar.txt', $paths);
-
- $results = $folderView->search('foo');
- $this->assertCount(2, $results);
- $paths = [];
- foreach ($results as $result) {
- $paths[] = $result['path'];
- }
- $this->assertContains('/folder/anotherstorage/foo.txt', $paths);
- $this->assertContains('/folder/anotherstorage/foo.png', $paths);
-
- $this->assertCount(6, $rootView->searchByMime('text'));
- $this->assertCount(3, $folderView->searchByMime('text'));
- }
-
- public function testWatcher(): void {
- $storage1 = $this->getTestStorage();
- Filesystem::mount($storage1, [], '/');
- $storage1->getWatcher()->setPolicy(Watcher::CHECK_ALWAYS);
-
- $rootView = new View('');
-
- $cachedData = $rootView->getFileInfo('foo.txt');
- $this->assertEquals(16, $cachedData['size']);
-
- $rootView->putFileInfo('foo.txt', ['storage_mtime' => 10]);
- $storage1->file_put_contents('foo.txt', 'foo');
- clearstatcache();
-
- $cachedData = $rootView->getFileInfo('foo.txt');
- $this->assertEquals(3, $cachedData['size']);
- }
-
- /**
- * Regression test for View::getCacheEntry unconditionally calling propagateChange
- * when the watcher reports needsUpdate = true, regardless of whether the scanner
- * found any actual storage change.
- *
- * Backends like Amazon S3 always return hasUpdated() = true for directory paths
- * because S3 has no real directory objects. Before the fix, every getFileInfo()
- * call on a folder fired propagateChange(path, time()), stamping all ancestor
- * folders in the filecache with the current request timestamp.
- *
- * After the fix, propagateChange is only called when watcher->update() actually
- * changes the cached metadata for the path.
- */
- public function testWatcherDoesNotPropagateWhenStorageMtimeUnchanged(): void {
- $storage = $this->getTestStorage(true, TemporaryAlwaysUpdated::class);
- Filesystem::mount($storage, [], '/');
- $storage->getWatcher()->setPolicy(Watcher::CHECK_ALWAYS);
-
- $rootView = new View('');
-
- // Use an mtime that cannot accidentally equal the current timestamp. This makes
- // the test fail reliably if propagateChange() is called during this request.
- $rootEntry = $storage->getCache()->get('');
- $storage->getCache()->update($rootEntry->getId(), [
- 'mtime' => 1,
- 'etag' => 'unchanged-root-etag',
- ]);
-
- // Access a subfolder. The watcher will fire (hasUpdated always returns true),
- // but the scanner leaves the cached metadata for 'folder' unchanged.
- // getCacheEntry must therefore NOT call propagateChange('folder', time()),
- // which would update the root entry's mtime and etag.
- $rootView->getFileInfo('folder');
-
- // Read the root entry directly from the cache to avoid triggering another watcher cycle.
- $rootEntryAfter = $storage->getCache()->get('');
-
- $this->assertSame(
- 1,
- $rootEntryAfter->getMTime(),
- 'Root folder mtime must not change when the watcher finds no metadata change',
- );
- $this->assertSame(
- 'unchanged-root-etag',
- $rootEntryAfter->getEtag(),
- 'Root folder etag must not change when the watcher finds no metadata change',
- );
- }
-
- public function testCopyBetweenStorageNoCross(): void {
- $storage1 = $this->getTestStorage(true, TemporaryNoCross::class);
- $storage2 = $this->getTestStorage(true, TemporaryNoCross::class);
- $this->copyBetweenStorages($storage1, $storage2);
- }
-
- public function testCopyBetweenStorageCross(): void {
- $storage1 = $this->getTestStorage();
- $storage2 = $this->getTestStorage();
- $this->copyBetweenStorages($storage1, $storage2);
- }
-
- public function testCopyBetweenStorageCrossNonLocal(): void {
- $storage1 = $this->getTestStorage(true, TemporaryNoLocal::class);
- $storage2 = $this->getTestStorage(true, TemporaryNoLocal::class);
- $this->copyBetweenStorages($storage1, $storage2);
- }
-
- public function copyBetweenStorages($storage1, $storage2) {
- Filesystem::mount($storage1, [], '/');
- Filesystem::mount($storage2, [], '/substorage');
-
- $rootView = new View('');
- $rootView->mkdir('substorage/emptyfolder');
- $rootView->copy('substorage', 'anotherfolder');
- $this->assertTrue($rootView->is_dir('/anotherfolder'));
- $this->assertTrue($rootView->is_dir('/substorage'));
- $this->assertTrue($rootView->is_dir('/anotherfolder/emptyfolder'));
- $this->assertTrue($rootView->is_dir('/substorage/emptyfolder'));
- $this->assertTrue($rootView->file_exists('/anotherfolder/foo.txt'));
- $this->assertTrue($rootView->file_exists('/anotherfolder/foo.png'));
- $this->assertTrue($rootView->file_exists('/anotherfolder/folder/bar.txt'));
- $this->assertTrue($rootView->file_exists('/substorage/foo.txt'));
- $this->assertTrue($rootView->file_exists('/substorage/foo.png'));
- $this->assertTrue($rootView->file_exists('/substorage/folder/bar.txt'));
- }
-
- public function testMoveBetweenStorageNoCross(): void {
- $storage1 = $this->getTestStorage(true, TemporaryNoCross::class);
- $storage2 = $this->getTestStorage(true, TemporaryNoCross::class);
- $this->moveBetweenStorages($storage1, $storage2);
- }
-
- public function testMoveBetweenStorageCross(): void {
- $storage1 = $this->getTestStorage();
- $storage2 = $this->getTestStorage();
- $this->moveBetweenStorages($storage1, $storage2);
- }
-
- public function testMoveBetweenStorageCrossNonLocal(): void {
- $storage1 = $this->getTestStorage(true, TemporaryNoLocal::class);
- $storage2 = $this->getTestStorage(true, TemporaryNoLocal::class);
- $this->moveBetweenStorages($storage1, $storage2);
- }
-
- public function moveBetweenStorages($storage1, $storage2) {
- Filesystem::mount($storage1, [], '/' . self::$user . '/');
- Filesystem::mount($storage2, [], '/' . self::$user . '/substorage');
-
- $rootView = new View('/' . self::$user);
- $rootView->rename('foo.txt', 'substorage/folder/foo.txt');
- $this->assertFalse($rootView->file_exists('foo.txt'));
- $this->assertTrue($rootView->file_exists('substorage/folder/foo.txt'));
- $rootView->rename('substorage/folder', 'anotherfolder');
- $this->assertFalse($rootView->is_dir('substorage/folder'));
- $this->assertTrue($rootView->file_exists('anotherfolder/foo.txt'));
- $this->assertTrue($rootView->file_exists('anotherfolder/bar.txt'));
- }
-
- public function testUnlink(): void {
- $storage1 = $this->getTestStorage();
- $storage2 = $this->getTestStorage();
- Filesystem::mount($storage1, [], '/');
- Filesystem::mount($storage2, [], '/substorage');
-
- $rootView = new View('');
- $rootView->file_put_contents('/foo.txt', 'asd');
- $rootView->file_put_contents('/substorage/bar.txt', 'asd');
-
- $this->assertTrue($rootView->file_exists('foo.txt'));
- $this->assertTrue($rootView->file_exists('substorage/bar.txt'));
-
- $this->assertTrue($rootView->unlink('foo.txt'));
- $this->assertTrue($rootView->unlink('substorage/bar.txt'));
-
- $this->assertFalse($rootView->file_exists('foo.txt'));
- $this->assertFalse($rootView->file_exists('substorage/bar.txt'));
- }
-
- public static function rmdirOrUnlinkDataProvider(): array {
- return [['rmdir'], ['unlink']];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('rmdirOrUnlinkDataProvider')]
- public function testRmdir($method): void {
- $storage1 = $this->getTestStorage();
- Filesystem::mount($storage1, [], '/');
-
- $rootView = new View('');
- $rootView->mkdir('sub');
- $rootView->mkdir('sub/deep');
- $rootView->file_put_contents('/sub/deep/foo.txt', 'asd');
-
- $this->assertTrue($rootView->file_exists('sub/deep/foo.txt'));
-
- $this->assertTrue($rootView->$method('sub'));
-
- $this->assertFalse($rootView->file_exists('sub'));
- }
-
- public function testUnlinkRootMustFail(): void {
- $storage1 = $this->getTestStorage();
- $storage2 = $this->getTestStorage();
- Filesystem::mount($storage1, [], '/');
- Filesystem::mount($storage2, [], '/substorage');
-
- $rootView = new View('');
- $rootView->file_put_contents('/foo.txt', 'asd');
- $rootView->file_put_contents('/substorage/bar.txt', 'asd');
-
- $this->assertFalse($rootView->unlink(''));
- $this->assertFalse($rootView->unlink('/'));
- $this->assertFalse($rootView->unlink('substorage'));
- $this->assertFalse($rootView->unlink('/substorage'));
- }
-
- public function testTouch(): void {
- $storage = $this->getTestStorage(true, TemporaryNoTouch::class);
-
- Filesystem::mount($storage, [], '/');
-
- $rootView = new View('');
- $oldCachedData = $rootView->getFileInfo('foo.txt');
-
- $rootView->touch('foo.txt', 500);
-
- $cachedData = $rootView->getFileInfo('foo.txt');
- $this->assertEquals(500, $cachedData['mtime']);
- $this->assertEquals($oldCachedData['storage_mtime'], $cachedData['storage_mtime']);
-
- $rootView->putFileInfo('foo.txt', ['storage_mtime' => 1000]); //make sure the watcher detects the change
- $rootView->file_put_contents('foo.txt', 'asd');
- $cachedData = $rootView->getFileInfo('foo.txt');
- $this->assertGreaterThanOrEqual($oldCachedData['mtime'], $cachedData['mtime']);
- $this->assertEquals($cachedData['storage_mtime'], $cachedData['mtime']);
- }
-
- public function testViewHooks(): void {
- $storage1 = $this->getTestStorage();
- $storage2 = $this->getTestStorage();
- $defaultRoot = Filesystem::getRoot();
- Filesystem::mount($storage1, [], '/');
- Filesystem::mount($storage2, [], $defaultRoot . '/substorage');
- \OC_Hook::connect('OC_Filesystem', 'post_write', $this, 'dummyHook');
-
- $rootView = new View('');
- $subView = new View($defaultRoot . '/substorage');
- $this->hookPath = null;
-
- $rootView->file_put_contents('/foo.txt', 'asd');
- $this->assertNull($this->hookPath);
-
- $subView->file_put_contents('/foo.txt', 'asd');
- $this->assertEquals('/substorage/foo.txt', $this->hookPath);
- }
-
- private $hookPath;
-
- public function dummyHook($params) {
- $this->hookPath = $params['path'];
- }
-
- public function testSearchNotOutsideView(): void {
- $storage1 = $this->getTestStorage();
- Filesystem::mount($storage1, [], '/');
- $storage1->rename('folder', 'foo');
- $scanner = $storage1->getScanner();
- $scanner->scan('');
-
- $view = new View('/foo');
-
- $result = $view->search('.txt');
- $this->assertCount(1, $result);
- }
-
- /**
- * @param bool $scan
- * @param string $class
- * @return Storage
- */
- private function getTestStorage($scan = true, $class = Temporary::class) {
- /**
- * @var Storage $storage
- */
- $storage = new $class([]);
- $textData = "dummy file data\n";
- $imgData = file_get_contents(\OC::$SERVERROOT . '/core/img/logo/logo.png');
- $storage->mkdir('folder');
- $storage->file_put_contents('foo.txt', $textData);
- $storage->file_put_contents('foo.png', $imgData);
- $storage->file_put_contents('folder/bar.txt', $textData);
-
- if ($scan) {
- $scanner = $storage->getScanner();
- $scanner->scan('');
- }
- $this->storages[] = $storage;
- return $storage;
- }
-
- public function testViewHooksIfRootStartsTheSame(): void {
- $storage1 = $this->getTestStorage();
- $storage2 = $this->getTestStorage();
- $defaultRoot = Filesystem::getRoot();
- Filesystem::mount($storage1, [], '/');
- Filesystem::mount($storage2, [], $defaultRoot . '_substorage');
- \OC_Hook::connect('OC_Filesystem', 'post_write', $this, 'dummyHook');
-
- $subView = new View($defaultRoot . '_substorage');
- $this->hookPath = null;
-
- $subView->file_put_contents('/foo.txt', 'asd');
- $this->assertNull($this->hookPath);
- }
-
- private $hookWritePath;
- private $hookCreatePath;
- private $hookUpdatePath;
-
- public function dummyHookWrite($params) {
- $this->hookWritePath = $params['path'];
- }
-
- public function dummyHookUpdate($params) {
- $this->hookUpdatePath = $params['path'];
- }
-
- public function dummyHookCreate($params) {
- $this->hookCreatePath = $params['path'];
- }
-
- public function testEditNoCreateHook(): void {
- $storage1 = $this->getTestStorage();
- $storage2 = $this->getTestStorage();
- $defaultRoot = Filesystem::getRoot();
- Filesystem::mount($storage1, [], '/');
- Filesystem::mount($storage2, [], $defaultRoot);
- \OC_Hook::connect('OC_Filesystem', 'post_create', $this, 'dummyHookCreate');
- \OC_Hook::connect('OC_Filesystem', 'post_update', $this, 'dummyHookUpdate');
- \OC_Hook::connect('OC_Filesystem', 'post_write', $this, 'dummyHookWrite');
-
- $view = new View($defaultRoot);
- $this->hookWritePath = $this->hookUpdatePath = $this->hookCreatePath = null;
-
- $view->file_put_contents('/asd.txt', 'foo');
- $this->assertEquals('/asd.txt', $this->hookCreatePath);
- $this->assertNull($this->hookUpdatePath);
- $this->assertEquals('/asd.txt', $this->hookWritePath);
-
- $this->hookWritePath = $this->hookUpdatePath = $this->hookCreatePath = null;
-
- $view->file_put_contents('/asd.txt', 'foo');
- $this->assertNull($this->hookCreatePath);
- $this->assertEquals('/asd.txt', $this->hookUpdatePath);
- $this->assertEquals('/asd.txt', $this->hookWritePath);
-
- \OC_Hook::clear('OC_Filesystem', 'post_create');
- \OC_Hook::clear('OC_Filesystem', 'post_update');
- \OC_Hook::clear('OC_Filesystem', 'post_write');
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('resolvePathTestProvider')]
- public function testResolvePath($expected, $pathToTest): void {
- $storage1 = $this->getTestStorage();
- Filesystem::mount($storage1, [], '/');
-
- $view = new View('');
-
- $result = $view->resolvePath($pathToTest);
- $this->assertEquals($expected, $result[1]);
-
- $exists = $view->file_exists($pathToTest);
- $this->assertTrue($exists);
-
- $exists = $view->file_exists($result[1]);
- $this->assertTrue($exists);
- }
-
- public static function resolvePathTestProvider(): array {
- return [
- ['foo.txt', 'foo.txt'],
- ['foo.txt', '/foo.txt'],
- ['folder', 'folder'],
- ['folder', '/folder'],
- ['folder', 'folder/'],
- ['folder', '/folder/'],
- ['folder/bar.txt', 'folder/bar.txt'],
- ['folder/bar.txt', '/folder/bar.txt'],
- ['', ''],
- ['', '/'],
- ];
- }
-
- public function testUTF8Names(): void {
- $names = ['虚', '和知しゃ和で', 'regular ascii', 'sɨˈrɪlɪk', 'ѨѬ', 'أنا أحب القراءة كثيرا'];
-
- $storage = new Temporary([]);
- Filesystem::mount($storage, [], '/');
-
- $rootView = new View('');
- foreach ($names as $name) {
- $rootView->file_put_contents('/' . $name, 'dummy content');
- }
-
- $list = $rootView->getDirectoryContent('/');
-
- $this->assertCount(count($names), $list);
- foreach ($list as $item) {
- $this->assertContains($item['name'], $names);
- }
-
- $cache = $storage->getCache();
- $scanner = $storage->getScanner();
- $scanner->scan('');
-
- $list = $cache->getFolderContents('');
-
- $this->assertCount(count($names), $list);
- foreach ($list as $item) {
- $this->assertContains($item['name'], $names);
- }
- }
-
- public function xtestLongPath() {
- $storage = new Temporary([]);
- Filesystem::mount($storage, [], '/');
-
- $rootView = new View('');
-
- $longPath = '';
- $ds = DIRECTORY_SEPARATOR;
- /*
- * 4096 is the maximum path length in file_cache.path in *nix
- */
- $folderName = 'abcdefghijklmnopqrstuvwxyz012345678901234567890123456789';
- $tmpdirLength = strlen(Server::get(ITempManager::class)->getTemporaryFolder());
- $depth = ((4000 - $tmpdirLength) / 57);
-
- foreach (range(0, $depth - 1) as $i) {
- $longPath .= $ds . $folderName;
- $result = $rootView->mkdir($longPath);
- $this->assertTrue($result, "mkdir failed on $i - path length: " . strlen($longPath));
-
- $result = $rootView->file_put_contents($longPath . "{$ds}test.txt", 'lorem');
- $this->assertEquals(5, $result, "file_put_contents failed on $i");
-
- $this->assertTrue($rootView->file_exists($longPath));
- $this->assertTrue($rootView->file_exists($longPath . "{$ds}test.txt"));
- }
-
- $cache = $storage->getCache();
- $scanner = $storage->getScanner();
- $scanner->scan('');
-
- $longPath = $folderName;
- foreach (range(0, $depth - 1) as $i) {
- $cachedFolder = $cache->get($longPath);
- $this->assertTrue(is_array($cachedFolder), "No cache entry for folder at $i");
- $this->assertEquals($folderName, $cachedFolder['name'], "Wrong cache entry for folder at $i");
-
- $cachedFile = $cache->get($longPath . '/test.txt');
- $this->assertTrue(is_array($cachedFile), "No cache entry for file at $i");
- $this->assertEquals('test.txt', $cachedFile['name'], "Wrong cache entry for file at $i");
-
- $longPath .= $ds . $folderName;
- }
- }
-
- public function testTouchNotSupported(): void {
- $storage = new TemporaryNoTouch([]);
- $scanner = $storage->getScanner();
- Filesystem::mount($storage, [], '/test/');
- $past = time() - 100;
- $storage->file_put_contents('test', 'foobar');
- $scanner->scan('');
- $view = new View('');
- $info = $view->getFileInfo('/test/test');
-
- $view->touch('/test/test', $past);
- $scanner->scanFile('test', Scanner::REUSE_ETAG);
-
- $info2 = $view->getFileInfo('/test/test');
- $this->assertSame($info['etag'], $info2['etag']);
- }
-
- public function testWatcherEtagCrossStorage(): void {
- $storage1 = new Temporary([]);
- $storage2 = new Temporary([]);
- $scanner1 = $storage1->getScanner();
- $scanner2 = $storage2->getScanner();
- $storage1->mkdir('sub');
- Filesystem::mount($storage1, [], '/test/');
- Filesystem::mount($storage2, [], '/test/sub/storage');
-
- $past = time() - 100;
- $storage2->file_put_contents('test.txt', 'foobar');
- $scanner1->scan('');
- $scanner2->scan('');
- $view = new View('');
-
- $storage2->getWatcher('')->setPolicy(Watcher::CHECK_ALWAYS);
-
- $oldFileInfo = $view->getFileInfo('/test/sub/storage/test.txt');
- $oldFolderInfo = $view->getFileInfo('/test');
-
- $storage2->getCache()->update($oldFileInfo->getId(), [
- 'storage_mtime' => $past,
- ]);
-
- $oldEtag = $oldFolderInfo->getEtag();
-
- $view->getFileInfo('/test/sub/storage/test.txt');
- $newFolderInfo = $view->getFileInfo('/test');
-
- $this->assertNotEquals($newFolderInfo->getEtag(), $oldEtag);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('absolutePathProvider')]
- public function testGetAbsolutePath($expectedPath, $relativePath): void {
- $view = new View('/files');
- $this->assertEquals($expectedPath, $view->getAbsolutePath($relativePath));
- }
-
- public function testPartFileInfo(): void {
- $storage = new Temporary([]);
- $scanner = $storage->getScanner();
- Filesystem::mount($storage, [], '/test/');
- $sizeWritten = $storage->file_put_contents('test.part', 'foobar');
- $scanner->scan('');
- $view = new View('/test');
- $info = $view->getFileInfo('test.part');
-
- $this->assertInstanceOf('\OCP\Files\FileInfo', $info);
- $this->assertNull($info->getId());
- $this->assertEquals(6, $sizeWritten);
- $this->assertEquals(6, $info->getSize());
- $this->assertEquals('foobar', $view->file_get_contents('test.part'));
- }
-
- public static function absolutePathProvider(): array {
- return [
- ['/files', ''],
- ['/files/0', '0'],
- ['/files/false', 'false'],
- ['/files/true', 'true'],
- ['/files', '/'],
- ['/files/test', 'test'],
- ['/files/test', '/test'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('chrootRelativePathProvider')]
- public function testChrootGetRelativePath($root, $absolutePath, $expectedPath): void {
- $view = new View('/files');
- $view->chroot($root);
- $this->assertEquals($expectedPath, $view->getRelativePath($absolutePath));
- }
-
- public static function chrootRelativePathProvider(): array {
- return self::relativePathProvider('/');
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('initRelativePathProvider')]
- public function testInitGetRelativePath($root, $absolutePath, $expectedPath): void {
- $view = new View($root);
- $this->assertEquals($expectedPath, $view->getRelativePath($absolutePath));
- }
-
- public static function initRelativePathProvider(): array {
- return self::relativePathProvider(null);
- }
-
- public static function relativePathProvider($missingRootExpectedPath): array {
- return [
- // No root - returns the path
- ['', '/files', '/files'],
- ['', '/files/', '/files/'],
-
- // Root equals path - /
- ['/files/', '/files/', '/'],
- ['/files/', '/files', '/'],
- ['/files', '/files/', '/'],
- ['/files', '/files', '/'],
-
- // False negatives: chroot fixes those by adding the leading slash.
- // But setting them up with this root (instead of chroot($root))
- // will fail them, although they should be the same.
- // TODO init should be fixed, so it also adds the leading slash
- ['files/', '/files/', $missingRootExpectedPath],
- ['files', '/files/', $missingRootExpectedPath],
- ['files/', '/files', $missingRootExpectedPath],
- ['files', '/files', $missingRootExpectedPath],
-
- // False negatives: Paths provided to the method should have a leading slash
- // TODO input should be checked to have a leading slash
- ['/files/', 'files/', null],
- ['/files', 'files/', null],
- ['/files/', 'files', null],
- ['/files', 'files', null],
-
- // with trailing slashes
- ['/files/', '/files/0', '0'],
- ['/files/', '/files/false', 'false'],
- ['/files/', '/files/true', 'true'],
- ['/files/', '/files/test', 'test'],
- ['/files/', '/files/test/foo', 'test/foo'],
-
- // without trailing slashes
- // TODO false expectation: Should match "with trailing slashes"
- ['/files', '/files/0', '/0'],
- ['/files', '/files/false', '/false'],
- ['/files', '/files/true', '/true'],
- ['/files', '/files/test', '/test'],
- ['/files', '/files/test/foo', '/test/foo'],
-
- // leading slashes
- ['/files/', '/files_trashbin/', null],
- ['/files', '/files_trashbin/', null],
- ['/files/', '/files_trashbin', null],
- ['/files', '/files_trashbin', null],
-
- // no leading slashes
- ['files/', 'files_trashbin/', null],
- ['files', 'files_trashbin/', null],
- ['files/', 'files_trashbin', null],
- ['files', 'files_trashbin', null],
-
- // mixed leading slashes
- ['files/', '/files_trashbin/', null],
- ['/files/', 'files_trashbin/', null],
- ['files', '/files_trashbin/', null],
- ['/files', 'files_trashbin/', null],
- ['files/', '/files_trashbin', null],
- ['/files/', 'files_trashbin', null],
- ['files', '/files_trashbin', null],
- ['/files', 'files_trashbin', null],
-
- ['files', 'files_trashbin/test', null],
- ['/files', '/files_trashbin/test', null],
- ['/files', 'files_trashbin/test', null],
- ];
- }
-
- public function testFileView(): void {
- $storage = new Temporary([]);
- $scanner = $storage->getScanner();
- $storage->file_put_contents('foo.txt', 'bar');
- Filesystem::mount($storage, [], '/test/');
- $scanner->scan('');
- $view = new View('/test/foo.txt');
-
- $this->assertEquals('bar', $view->file_get_contents(''));
- $fh = tmpfile();
- fwrite($fh, 'foo');
- rewind($fh);
- $view->file_put_contents('', $fh);
- $this->assertEquals('foo', $view->file_get_contents(''));
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('tooLongPathDataProvider')]
- public function testTooLongPath($operation, $param0 = null): void {
- $this->expectException(InvalidPathException::class);
-
- $longPath = '';
- // 4000 is the maximum path length in file_cache.path
- $folderName = 'abcdefghijklmnopqrstuvwxyz012345678901234567890123456789';
- $depth = (4000 / 57);
- foreach (range(0, $depth + 1) as $i) {
- $longPath .= '/' . $folderName;
- }
-
- $storage = new Temporary([]);
- $this->tempStorage = $storage; // for later hard cleanup
- Filesystem::mount($storage, [], '/');
-
- $rootView = new View('');
-
- if ($param0 === '@0') {
- $param0 = $longPath;
- }
-
- if ($operation === 'hash') {
- $param0 = $longPath;
- $longPath = 'md5';
- }
-
- call_user_func([$rootView, $operation], $longPath, $param0);
- }
-
- public static function tooLongPathDataProvider(): array {
- return [
- ['getAbsolutePath'],
- ['getRelativePath'],
- ['getMountPoint'],
- ['resolvePath'],
- ['getLocalFile'],
- ['mkdir'],
- ['rmdir'],
- ['opendir'],
- ['is_dir'],
- ['is_file'],
- ['stat'],
- ['filetype'],
- ['filesize'],
- ['readfile'],
- ['isCreatable'],
- ['isReadable'],
- ['isUpdatable'],
- ['isDeletable'],
- ['isSharable'],
- ['file_exists'],
- ['filemtime'],
- ['touch'],
- ['file_get_contents'],
- ['unlink'],
- ['deleteAll'],
- ['toTmpFile'],
- ['getMimeType'],
- ['free_space'],
- ['getFileInfo'],
- ['getDirectoryContent'],
- ['getOwner'],
- ['getETag'],
- ['file_put_contents', 'ipsum'],
- ['rename', '@0'],
- ['copy', '@0'],
- ['fopen', 'r'],
- ['fromTmpFile', '@0'],
- ['hash'],
- ['hasUpdated', 0],
- ['putFileInfo', []],
- ];
- }
-
- public function testRenameCrossStoragePreserveMtime(): void {
- $storage1 = new Temporary([]);
- $storage2 = new Temporary([]);
- $storage1->mkdir('sub');
- $storage1->mkdir('foo');
- $storage1->file_put_contents('foo.txt', 'asd');
- $storage1->file_put_contents('foo/bar.txt', 'asd');
- Filesystem::mount($storage1, [], '/test/');
- Filesystem::mount($storage2, [], '/test/sub/storage');
-
- $view = new View('');
- $time = time() - 200;
- $view->touch('/test/foo.txt', $time);
- $view->touch('/test/foo', $time);
- $view->touch('/test/foo/bar.txt', $time);
-
- $view->rename('/test/foo.txt', '/test/sub/storage/foo.txt');
-
- $this->assertEquals($time, $view->filemtime('/test/sub/storage/foo.txt'));
-
- $view->rename('/test/foo', '/test/sub/storage/foo');
-
- $this->assertEquals($time, $view->filemtime('/test/sub/storage/foo/bar.txt'));
- }
-
- public function testRenameFailDeleteTargetKeepSource(): void {
- $this->doTestCopyRenameFail('rename');
- }
-
- public function testCopyFailDeleteTargetKeepSource(): void {
- $this->doTestCopyRenameFail('copy');
- }
-
- private function doTestCopyRenameFail($operation) {
- $storage1 = new Temporary([]);
- /** @var \PHPUnit\Framework\MockObject\MockObject|Temporary $storage2 */
- $storage2 = $this->getMockBuilder(TemporaryNoCross::class)
- ->setConstructorArgs([[]])
- ->onlyMethods(['fopen', 'writeStream'])
- ->getMock();
-
- $storage2->method('writeStream')
- ->willThrowException(new GenericFileException('Failed to copy stream'));
-
- $storage2->method('fopen')
- ->willReturn(false);
-
- $storage1->mkdir('sub');
- $storage1->file_put_contents('foo.txt', '0123456789ABCDEFGH');
- $storage1->mkdir('dirtomove');
- $storage1->file_put_contents('dirtomove/indir1.txt', '0123456'); // fits
- $storage1->file_put_contents('dirtomove/indir2.txt', '0123456789ABCDEFGH'); // doesn't fit
- $storage2->file_put_contents('existing.txt', '0123');
- $storage1->getScanner()->scan('');
- $storage2->getScanner()->scan('');
- Filesystem::mount($storage1, [], '/test/');
- Filesystem::mount($storage2, [], '/test/sub/storage');
-
- // move file
- $view = new View('');
- $this->assertTrue($storage1->file_exists('foo.txt'));
- $this->assertFalse($storage2->file_exists('foo.txt'));
- $this->assertFalse($view->$operation('/test/foo.txt', '/test/sub/storage/foo.txt'));
- $this->assertFalse($storage2->file_exists('foo.txt'));
- $this->assertFalse($storage2->getCache()->get('foo.txt'));
- $this->assertTrue($storage1->file_exists('foo.txt'));
-
- // if target exists, it will be deleted too
- $this->assertFalse($view->$operation('/test/foo.txt', '/test/sub/storage/existing.txt'));
- $this->assertFalse($storage2->file_exists('existing.txt'));
- $this->assertFalse($storage2->getCache()->get('existing.txt'));
- $this->assertTrue($storage1->file_exists('foo.txt'));
-
- // move folder
- $this->assertFalse($view->$operation('/test/dirtomove/', '/test/sub/storage/dirtomove/'));
- // since the move failed, the full source tree is kept
- $this->assertTrue($storage1->file_exists('dirtomove/indir1.txt'));
- $this->assertTrue($storage1->file_exists('dirtomove/indir2.txt'));
- // second file not moved/copied
- $this->assertFalse($storage2->file_exists('dirtomove/indir2.txt'));
- $this->assertFalse($storage2->getCache()->get('dirtomove/indir2.txt'));
- }
-
- public function testDeleteFailKeepCache(): void {
- /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
- $storage = $this->getMockBuilder(Temporary::class)
- ->setConstructorArgs([[]])
- ->onlyMethods(['unlink'])
- ->getMock();
- $storage->expects($this->once())
- ->method('unlink')
- ->willReturn(false);
- $scanner = $storage->getScanner();
- $cache = $storage->getCache();
- $storage->file_put_contents('foo.txt', 'asd');
- $scanner->scan('');
- Filesystem::mount($storage, [], '/test/');
-
- $view = new View('/test');
-
- $this->assertFalse($view->unlink('foo.txt'));
- $this->assertTrue($cache->inCache('foo.txt'));
- }
-
- public static function directoryTraversalProvider(): array {
- return [
- ['../test/'],
- ['..\\test\\my/../folder'],
- ['/test/my/../foo\\'],
- ];
- }
-
- /**
- * @param string $root
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('directoryTraversalProvider')]
- public function testConstructDirectoryTraversalException($root): void {
- $this->expectException(\Exception::class);
-
- new View($root);
- }
-
- public function testRenameOverWrite(): void {
- $storage = new Temporary([]);
- $scanner = $storage->getScanner();
- $storage->mkdir('sub');
- $storage->mkdir('foo');
- $storage->file_put_contents('foo.txt', 'asd');
- $storage->file_put_contents('foo/bar.txt', 'asd');
- $scanner->scan('');
- Filesystem::mount($storage, [], '/test/');
- $view = new View('');
- $this->assertTrue($view->rename('/test/foo.txt', '/test/foo/bar.txt'));
- }
-
- public function testSetMountOptionsInStorage(): void {
- $mount = new MountPoint(Temporary::class, '/asd/', [[]], Filesystem::getLoader(), ['foo' => 'bar']);
- Filesystem::getMountManager()->addMount($mount);
- /** @var Common $storage */
- $storage = $mount->getStorage();
- $this->assertEquals($storage->getMountOption('foo'), 'bar');
- }
-
- public function testSetMountOptionsWatcherPolicy(): void {
- $mount = new MountPoint(Temporary::class, '/asd/', [[]], Filesystem::getLoader(), ['filesystem_check_changes' => Watcher::CHECK_NEVER]);
- Filesystem::getMountManager()->addMount($mount);
- /** @var Common $storage */
- $storage = $mount->getStorage();
- $watcher = $storage->getWatcher();
- $this->assertEquals(Watcher::CHECK_NEVER, $watcher->getPolicy());
- }
-
- public function testGetAbsolutePathOnNull(): void {
- $view = new View();
- $this->assertNull($view->getAbsolutePath(null));
- }
-
- public function testGetRelativePathOnNull(): void {
- $view = new View();
- $this->assertNull($view->getRelativePath(null));
- }
-
- public function testNullAsRoot(): void {
- $this->expectException(\TypeError::class);
-
- new View(null);
- }
-
- /**
- * e.g. reading from a folder that's being renamed
- *
- *
- *
- * @param string $rootPath
- * @param string $pathPrefix
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataLockPaths')]
- public function testReadFromWriteLockedPath($rootPath, $pathPrefix): void {
- $this->expectException(LockedException::class);
-
- $rootPath = str_replace('{folder}', 'files', $rootPath);
- $pathPrefix = str_replace('{folder}', 'files', $pathPrefix);
-
- $view = new View($rootPath);
- $storage = new Temporary([]);
- Filesystem::mount($storage, [], '/');
- $this->assertTrue($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_EXCLUSIVE));
- $view->lockFile($pathPrefix . '/foo/bar/asd', ILockingProvider::LOCK_SHARED);
- }
-
- /**
- * Reading from a files_encryption folder that's being renamed
- *
- *
- * @param string $rootPath
- * @param string $pathPrefix
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataLockPaths')]
- public function testReadFromWriteUnlockablePath($rootPath, $pathPrefix): void {
- $rootPath = str_replace('{folder}', 'files_encryption', $rootPath);
- $pathPrefix = str_replace('{folder}', 'files_encryption', $pathPrefix);
-
- $view = new View($rootPath);
- $storage = new Temporary([]);
- Filesystem::mount($storage, [], '/');
- $this->assertFalse($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_EXCLUSIVE));
- $this->assertFalse($view->lockFile($pathPrefix . '/foo/bar/asd', ILockingProvider::LOCK_SHARED));
- }
-
- /**
- * e.g. writing a file that's being downloaded
- *
- *
- *
- * @param string $rootPath
- * @param string $pathPrefix
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataLockPaths')]
- public function testWriteToReadLockedFile($rootPath, $pathPrefix): void {
- $this->expectException(LockedException::class);
-
- $rootPath = str_replace('{folder}', 'files', $rootPath);
- $pathPrefix = str_replace('{folder}', 'files', $pathPrefix);
-
- $view = new View($rootPath);
- $storage = new Temporary([]);
- Filesystem::mount($storage, [], '/');
- $this->assertTrue($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_SHARED));
- $view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_EXCLUSIVE);
- }
-
- /**
- * Writing a file that's being downloaded
- *
- *
- * @param string $rootPath
- * @param string $pathPrefix
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataLockPaths')]
- public function testWriteToReadUnlockableFile($rootPath, $pathPrefix): void {
- $rootPath = str_replace('{folder}', 'files_encryption', $rootPath);
- $pathPrefix = str_replace('{folder}', 'files_encryption', $pathPrefix);
-
- $view = new View($rootPath);
- $storage = new Temporary([]);
- Filesystem::mount($storage, [], '/');
- $this->assertFalse($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_SHARED));
- $this->assertFalse($view->lockFile($pathPrefix . '/foo/bar', ILockingProvider::LOCK_EXCLUSIVE));
- }
-
- /**
- * Test that locks are on mount point paths instead of mount root
- */
- public function testLockLocalMountPointPathInsteadOfStorageRoot(): void {
- $lockingProvider = Server::get(ILockingProvider::class);
- $view = new View('/testuser/files/');
- $storage = new Temporary([]);
- Filesystem::mount($storage, [], '/');
- $mountedStorage = new Temporary([]);
- Filesystem::mount($mountedStorage, [], '/testuser/files/mountpoint');
-
- $this->assertTrue(
- $view->lockFile('/mountpoint', ILockingProvider::LOCK_EXCLUSIVE, true),
- 'Can lock mount point'
- );
-
- // no exception here because storage root was not locked
- $mountedStorage->acquireLock('', ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
-
- $thrown = false;
- try {
- $storage->acquireLock('/testuser/files/mountpoint', ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
- } catch (LockedException $e) {
- $thrown = true;
- }
- $this->assertTrue($thrown, 'Mount point path was locked on root storage');
-
- $lockingProvider->releaseAll();
- }
-
- /**
- * Test that locks are on mount point paths and also mount root when requested
- */
- public function testLockStorageRootButNotLocalMountPoint(): void {
- $lockingProvider = Server::get(ILockingProvider::class);
- $view = new View('/testuser/files/');
- $storage = new Temporary([]);
- Filesystem::mount($storage, [], '/');
- $mountedStorage = new Temporary([]);
- Filesystem::mount($mountedStorage, [], '/testuser/files/mountpoint');
-
- $this->assertTrue(
- $view->lockFile('/mountpoint', ILockingProvider::LOCK_EXCLUSIVE, false),
- 'Can lock mount point'
- );
-
- $thrown = false;
- try {
- $mountedStorage->acquireLock('', ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
- } catch (LockedException $e) {
- $thrown = true;
- }
- $this->assertTrue($thrown, 'Mount point storage root was locked on original storage');
-
- // local mount point was not locked
- $storage->acquireLock('/testuser/files/mountpoint', ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
-
- $lockingProvider->releaseAll();
- }
-
- /**
- * Test that locks are on mount point paths and also mount root when requested
- */
- public function testLockMountPointPathFailReleasesBoth(): void {
- $lockingProvider = Server::get(ILockingProvider::class);
- $view = new View('/testuser/files/');
- $storage = new Temporary([]);
- Filesystem::mount($storage, [], '/');
- $mountedStorage = new Temporary([]);
- Filesystem::mount($mountedStorage, [], '/testuser/files/mountpoint.txt');
-
- // this would happen if someone is writing on the mount point
- $mountedStorage->acquireLock('', ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
-
- $thrown = false;
- try {
- // this actually acquires two locks, one on the mount point and one on the storage root,
- // but the one on the storage root will fail
- $view->lockFile('/mountpoint.txt', ILockingProvider::LOCK_SHARED);
- } catch (LockedException $e) {
- $thrown = true;
- }
- $this->assertTrue($thrown, 'Cannot acquire shared lock because storage root is already locked');
-
- // from here we expect that the lock on the local mount point was released properly
- // so acquiring an exclusive lock will succeed
- $storage->acquireLock('/testuser/files/mountpoint.txt', ILockingProvider::LOCK_EXCLUSIVE, $lockingProvider);
-
- $lockingProvider->releaseAll();
- }
-
- public static function dataLockPaths(): array {
- return [
- ['/testuser/{folder}', ''],
- ['/testuser', '/{folder}'],
- ['', '/testuser/{folder}'],
- ];
- }
-
- public static function pathRelativeToFilesProvider(): array {
- return [
- ['admin/files', ''],
- ['admin/files/x', 'x'],
- ['/admin/files', ''],
- ['/admin/files/sub', 'sub'],
- ['/admin/files/sub/', 'sub'],
- ['/admin/files/sub/sub2', 'sub/sub2'],
- ['//admin//files/sub//sub2', 'sub/sub2'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('pathRelativeToFilesProvider')]
- public function testGetPathRelativeToFiles($path, $expectedPath): void {
- $view = new View();
- $this->assertEquals($expectedPath, $view->getPathRelativeToFiles($path));
- }
-
- public static function pathRelativeToFilesProviderExceptionCases(): array {
- return [
- [''],
- ['x'],
- ['files'],
- ['/files'],
- ['/admin/files_versions/abc'],
- ];
- }
-
- /**
- * @param string $path
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('pathRelativeToFilesProviderExceptionCases')]
- public function testGetPathRelativeToFilesWithInvalidArgument($path): void {
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('$absolutePath must be relative to "files"');
-
- $view = new View();
- $view->getPathRelativeToFiles($path);
- }
-
- public function testChangeLock(): void {
- $view = new View('/testuser/files/');
- $storage = new Temporary([]);
- Filesystem::mount($storage, [], '/');
-
- $view->lockFile('/test/sub', ILockingProvider::LOCK_SHARED);
- $this->assertTrue($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_SHARED));
- $this->assertFalse($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_EXCLUSIVE));
-
- $view->changeLock('//test/sub', ILockingProvider::LOCK_EXCLUSIVE);
- $this->assertTrue($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_EXCLUSIVE));
-
- $view->changeLock('test/sub', ILockingProvider::LOCK_SHARED);
- $this->assertTrue($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_SHARED));
-
- $view->unlockFile('/test/sub/', ILockingProvider::LOCK_SHARED);
-
- $this->assertFalse($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_SHARED));
- $this->assertFalse($this->isFileLocked($view, '/test//sub', ILockingProvider::LOCK_EXCLUSIVE));
- }
-
- public static function hookPathProvider(): array {
- return [
- ['/foo/files', '/foo', true],
- ['/foo/files/bar', '/foo', true],
- ['/foo', '/foo', false],
- ['/foo', '/files/foo', true],
- ['/foo', 'filesfoo', false],
- ['', '/foo/files', true],
- ['', '/foo/files/bar.txt', true],
- ];
- }
-
- /**
- * @param $root
- * @param $path
- * @param $shouldEmit
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('hookPathProvider')]
- public function testHookPaths($root, $path, $shouldEmit): void {
- $filesystemReflection = new \ReflectionClass(Filesystem::class);
- $defaultRootValue = $filesystemReflection->getProperty('defaultInstance');
- $oldRoot = $defaultRootValue->getValue();
- $defaultView = new View('/foo/files');
- $defaultRootValue->setValue(null, $defaultView);
- $view = new View($root);
- $result = self::invokePrivate($view, 'shouldEmitHooks', [$path]);
- $defaultRootValue->setValue(null, $oldRoot);
- $this->assertEquals($shouldEmit, $result);
- }
-
- /**
- * Create test movable mount points
- *
- * @param array $mountPoints array of mount point locations
- * @return array array of MountPoint objects
- */
- private function createTestMovableMountPoints($mountPoints) {
- $mounts = [];
- foreach ($mountPoints as $mountPoint) {
- $storage = $this->getMockBuilder(Storage::class)
- ->onlyMethods([])
- ->getMock();
- $storage->method('getId')->willReturn('non-null-id');
- $storage->method('getStorageCache')->willReturnCallback(function () use ($storage) {
- return new \OC\Files\Cache\Storage($storage, true, Server::get(IDBConnection::class));
- });
-
- $mount = $this->getMockBuilder(TestMoveableMountPoint::class)
- ->onlyMethods(['moveMount', 'getNumericStorageId'])
- ->setConstructorArgs([$storage, $mountPoint])
- ->getMock();
-
- $mount->method('getNumericStorageId')
- ->willReturn(1);
-
- $mounts[] = $mount;
- }
-
- /** @var IMountProvider|\PHPUnit\Framework\MockObject\MockObject $mountProvider */
- $mountProvider = $this->createMock(IMountProvider::class);
- $mountProvider->expects($this->any())
- ->method('getMountsForUser')
- ->willReturn($mounts);
-
- $mountProviderCollection = Server::get(IMountProviderCollection::class);
- $mountProviderCollection->registerProvider($mountProvider);
-
- return $mounts;
- }
-
- /**
- * Test mount point move
- */
- public function testMountPointMove(): void {
- self::loginAsUser(self::$user);
-
- [$mount1, $mount2] = $this->createTestMovableMountPoints([
- self::$user . '/files/mount1',
- self::$user . '/files/mount2',
- ]);
- $mount1->expects($this->once())
- ->method('moveMount')
- ->willReturn(true);
-
- $mount2->expects($this->once())
- ->method('moveMount')
- ->willReturn(true);
-
- $view = new View('/' . self::$user . '/files/');
- $view->mkdir('sub');
-
- $this->assertTrue($view->rename('mount1', 'renamed_mount'), 'Can rename mount point');
- $this->assertTrue($view->rename('mount2', 'sub/moved_mount'), 'Can move a mount point into a subdirectory');
- }
-
- public function testMoveMountPointOverwrite(): void {
- self::loginAsUser(self::$user);
-
- [$mount1, $mount2] = $this->createTestMovableMountPoints([
- self::$user . '/files/mount1',
- self::$user . '/files/mount2',
- ]);
-
- $mount1->expects($this->never())
- ->method('moveMount');
-
- $mount2->expects($this->never())
- ->method('moveMount');
-
- $view = new View('/' . self::$user . '/files/');
-
- $this->expectException(ForbiddenException::class);
- $view->rename('mount1', 'mount2');
- }
-
- public function testMoveMountPointIntoMount(): void {
- self::loginAsUser(self::$user);
-
- [$mount1, $mount2] = $this->createTestMovableMountPoints([
- self::$user . '/files/mount1',
- self::$user . '/files/mount2',
- ]);
-
- $mount1->expects($this->never())
- ->method('moveMount');
-
- $mount2->expects($this->never())
- ->method('moveMount');
-
- $view = new View('/' . self::$user . '/files/');
-
- $this->expectException(ForbiddenException::class);
- $view->rename('mount1', 'mount2/sub');
- }
-
- /**
- * Test that moving a mount point into a shared folder is forbidden
- */
- public function testMoveMountPointIntoSharedFolder(): void {
- self::loginAsUser(self::$user);
-
- [$mount1, $mount2] = $this->createTestMovableMountPoints([
- self::$user . '/files/mount1',
- self::$user . '/files/mount2',
- ]);
-
- $mount1->expects($this->never())
- ->method('moveMount');
-
- $mount2->expects($this->once())
- ->method('moveMount')
- ->willReturn(true);
-
- $view = new View('/' . self::$user . '/files/');
- $view->mkdir('shareddir');
- $view->mkdir('shareddir/sub');
- $view->mkdir('shareddir/sub2');
- // Create a similar named but non-shared folder
- $view->mkdir('shareddir notshared');
-
- $fileId = $view->getFileInfo('shareddir')->getId();
- $userObject = Server::get(IUserManager::class)->createUser('test2', 'IHateNonMockableStaticClasses');
-
- $userFolder = \OC::$server->getUserFolder(self::$user);
- $shareDir = $userFolder->get('shareddir');
- $shareManager = Server::get(IShareManager::class);
- $share = $shareManager->newShare();
- $share->setSharedWith('test2')
- ->setSharedBy(self::$user)
- ->setShareType(IShare::TYPE_USER)
- ->setPermissions(Constants::PERMISSION_READ)
- ->setNode($shareDir);
- $shareManager->createShare($share);
-
- try {
- $view->rename('mount1', 'shareddir');
- $this->fail('Cannot overwrite shared folder');
- } catch (ForbiddenException $e) {
-
- }
- try {
- $view->rename('mount1', 'shareddir/sub');
- $this->fail('Cannot move mount point into shared folder');
- } catch (ForbiddenException $e) {
-
- }
- try {
- $view->rename('mount1', 'shareddir/sub/sub2');
- $this->fail('Cannot move mount point into shared subfolder');
- } catch (ForbiddenException $e) {
-
- }
- $this->assertTrue($view->rename('mount2', 'shareddir notshared/sub'), 'Can move mount point into a similarly named but non-shared folder');
-
- $shareManager->deleteShare($share);
- $userObject->delete();
- }
-
- public static function basicOperationProviderForLocks(): array {
- return [
- // --- write hook ----
- [
- 'touch',
- ['touch-create.txt'],
- 'touch-create.txt',
- 'create',
- ILockingProvider::LOCK_SHARED,
- ILockingProvider::LOCK_EXCLUSIVE,
- ILockingProvider::LOCK_SHARED,
- ],
- [
- 'fopen',
- ['test-write.txt', 'w'],
- 'test-write.txt',
- 'write',
- ILockingProvider::LOCK_SHARED,
- ILockingProvider::LOCK_EXCLUSIVE,
- null,
- // exclusive lock stays until fclose
- ILockingProvider::LOCK_EXCLUSIVE,
- ],
- [
- 'mkdir',
- ['newdir'],
- 'newdir',
- 'write',
- ILockingProvider::LOCK_SHARED,
- ILockingProvider::LOCK_EXCLUSIVE,
- ILockingProvider::LOCK_SHARED,
- ],
- [
- 'file_put_contents',
- ['file_put_contents.txt', 'blah'],
- 'file_put_contents.txt',
- 'write',
- ILockingProvider::LOCK_SHARED,
- ILockingProvider::LOCK_EXCLUSIVE,
- ILockingProvider::LOCK_SHARED,
- null,
- 0,
- ],
-
- // ---- delete hook ----
- [
- 'rmdir',
- ['dir'],
- 'dir',
- 'delete',
- ILockingProvider::LOCK_SHARED,
- ILockingProvider::LOCK_EXCLUSIVE,
- ILockingProvider::LOCK_SHARED,
- ],
- [
- 'unlink',
- ['test.txt'],
- 'test.txt',
- 'delete',
- ILockingProvider::LOCK_SHARED,
- ILockingProvider::LOCK_EXCLUSIVE,
- ILockingProvider::LOCK_SHARED,
- ],
-
- // ---- read hook (no post hooks) ----
- [
- 'file_get_contents',
- ['test.txt'],
- 'test.txt',
- 'read',
- ILockingProvider::LOCK_SHARED,
- ILockingProvider::LOCK_SHARED,
- null,
- null,
- false,
- ],
- [
- 'fopen',
- ['test.txt', 'r'],
- 'test.txt',
- 'read',
- ILockingProvider::LOCK_SHARED,
- ILockingProvider::LOCK_SHARED,
- null,
- ],
- [
- 'opendir',
- ['dir'],
- 'dir',
- 'read',
- ILockingProvider::LOCK_SHARED,
- ILockingProvider::LOCK_SHARED,
- null,
- ],
-
- // ---- no lock, touch hook ---
- ['touch', ['test.txt'], 'test.txt', 'touch', null, null, null],
-
- // ---- no hooks, no locks ---
- ['is_dir', ['dir'], 'dir', ''],
- ['is_file', ['dir'], 'dir', ''],
- [
- 'stat',
- ['dir'],
- 'dir',
- '',
- ILockingProvider::LOCK_SHARED,
- ILockingProvider::LOCK_SHARED,
- ILockingProvider::LOCK_SHARED,
- null,
- false,
- ],
- [
- 'filetype',
- ['dir'],
- 'dir',
- '',
- ILockingProvider::LOCK_SHARED,
- ILockingProvider::LOCK_SHARED,
- ILockingProvider::LOCK_SHARED,
- null,
- false,
- ],
- [
- 'filesize',
- ['dir'],
- 'dir',
- '',
- ILockingProvider::LOCK_SHARED,
- ILockingProvider::LOCK_SHARED,
- ILockingProvider::LOCK_SHARED,
- null,
- /* Return an int */
- 100
- ],
- ['isCreatable', ['dir'], 'dir', ''],
- ['isReadable', ['dir'], 'dir', ''],
- ['isUpdatable', ['dir'], 'dir', ''],
- ['isDeletable', ['dir'], 'dir', ''],
- ['isSharable', ['dir'], 'dir', ''],
- ['file_exists', ['dir'], 'dir', ''],
- [
- 'filemtime',
- ['dir'],
- 'dir',
- '',
- ILockingProvider::LOCK_SHARED,
- ILockingProvider::LOCK_SHARED,
- ILockingProvider::LOCK_SHARED,
- null,
- false,
- ],
- ];
- }
-
- /**
- * Test whether locks are set before and after the operation
- *
- *
- * @param string $operation operation name on the view
- * @param array $operationArgs arguments for the operation
- * @param string $lockedPath path of the locked item to check
- * @param string $hookType hook type
- * @param ?int $expectedLockBefore expected lock during pre hooks
- * @param ?int $expectedLockDuring expected lock during operation
- * @param ?int $expectedLockAfter expected lock during post hooks
- * @param ?int $expectedStrayLock expected lock after returning, should
- * be null (unlock) for most operations
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('basicOperationProviderForLocks')]
- public function testLockBasicOperation(
- string $operation,
- array $operationArgs,
- string $lockedPath,
- string $hookType,
- ?int $expectedLockBefore = ILockingProvider::LOCK_SHARED,
- ?int $expectedLockDuring = ILockingProvider::LOCK_SHARED,
- ?int $expectedLockAfter = ILockingProvider::LOCK_SHARED,
- ?int $expectedStrayLock = null,
- mixed $returnValue = true,
- ): void {
- $view = new View('/' . self::$user . '/files/');
-
- /** @var Temporary&MockObject $storage */
- $storage = $this->getMockBuilder(Temporary::class)
- ->onlyMethods([$operation])
- ->getMock();
-
- /* Pause trash to avoid the trashbin intercepting rmdir and unlink calls */
- Server::get(ITrashManager::class)->pauseTrash();
- /* Same thing with encryption wrapper */
- Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption');
-
- Filesystem::mount($storage, [], self::$user . '/');
-
- // work directly on disk because mkdir might be mocked
- $realPath = $storage->getSourcePath('');
- mkdir($realPath . '/files');
- mkdir($realPath . '/files/dir');
- file_put_contents($realPath . '/files/test.txt', 'blah');
- $storage->getScanner()->scan('files');
-
- $storage->expects($this->once())
- ->method($operation)
- ->willReturnCallback(
- function () use ($view, $lockedPath, &$lockTypeDuring, $returnValue) {
- $lockTypeDuring = $this->getFileLockType($view, $lockedPath);
-
- return $returnValue;
- }
- );
-
- $this->assertNull($this->getFileLockType($view, $lockedPath), 'File not locked before operation');
-
- $this->connectMockHooks($hookType, $view, $lockedPath, $lockTypePre, $lockTypePost);
-
- // do operation
- call_user_func_array([$view, $operation], $operationArgs);
-
- if ($hookType !== '') {
- $this->assertEquals($expectedLockBefore, $lockTypePre, 'File locked properly during pre-hook');
- $this->assertEquals($expectedLockAfter, $lockTypePost, 'File locked properly during post-hook');
- $this->assertEquals($expectedLockDuring, $lockTypeDuring, 'File locked properly during operation');
- } else {
- $this->assertNull($lockTypeDuring, 'File not locked during operation');
- }
-
- $this->assertEquals($expectedStrayLock, $this->getFileLockType($view, $lockedPath));
-
- /* Resume trash to avoid side effects */
- Server::get(ITrashManager::class)->resumeTrash();
- }
-
- /**
- * Test locks for file_put_content with stream.
- * This code path uses $storage->fopen instead
- */
- public function testLockFilePutContentWithStream(): void {
- $view = new View('/' . self::$user . '/files/');
-
- $path = 'test_file_put_contents.txt';
- /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
- $storage = $this->getMockBuilder(Temporary::class)
- ->onlyMethods(['fopen'])
- ->getMock();
-
- Filesystem::mount($storage, [], self::$user . '/');
- $storage->mkdir('files');
-
- $storage->expects($this->once())
- ->method('fopen')
- ->willReturnCallback(
- function () use ($view, $path, &$lockTypeDuring) {
- $lockTypeDuring = $this->getFileLockType($view, $path);
-
- return fopen('php://temp', 'r+');
- }
- );
-
- $this->connectMockHooks('write', $view, $path, $lockTypePre, $lockTypePost);
-
- $this->assertNull($this->getFileLockType($view, $path), 'File not locked before operation');
-
- // do operation
- $view->file_put_contents($path, fopen('php://temp', 'r+'));
-
- $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypePre, 'File locked properly during pre-hook');
- $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypePost, 'File locked properly during post-hook');
- $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeDuring, 'File locked properly during operation');
-
- $this->assertNull($this->getFileLockType($view, $path));
- }
-
- /**
- * Test locks for fopen with fclose at the end
- */
- public function testLockFopen(): void {
- $view = new View('/' . self::$user . '/files/');
-
- $path = 'test_file_put_contents.txt';
- /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
- $storage = $this->getMockBuilder(Temporary::class)
- ->onlyMethods(['fopen'])
- ->getMock();
-
- Filesystem::mount($storage, [], self::$user . '/');
- $storage->mkdir('files');
-
- $storage->expects($this->once())
- ->method('fopen')
- ->willReturnCallback(
- function () use ($view, $path, &$lockTypeDuring) {
- $lockTypeDuring = $this->getFileLockType($view, $path);
-
- return fopen('php://temp', 'r+');
- }
- );
-
- $this->connectMockHooks('write', $view, $path, $lockTypePre, $lockTypePost);
-
- $this->assertNull($this->getFileLockType($view, $path), 'File not locked before operation');
-
- // do operation
- $res = $view->fopen($path, 'w');
-
- $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypePre, 'File locked properly during pre-hook');
- $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeDuring, 'File locked properly during operation');
- $this->assertNull($lockTypePost, 'No post hook, no lock check possible');
-
- $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeDuring, 'File still locked after fopen');
-
- fclose($res);
-
- $this->assertNull($this->getFileLockType($view, $path), 'File unlocked after fclose');
- }
-
- /**
- * Test locks for fopen with fclose at the end
- *
- *
- * @param string $operation operation name on the view
- * @param array $operationArgs arguments for the operation
- * @param string $path path of the locked item to check
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('basicOperationProviderForLocks')]
- public function testLockBasicOperationUnlocksAfterException(
- $operation,
- $operationArgs,
- $path,
- ): void {
- if ($operation === 'touch') {
- $this->markTestSkipped('touch handles storage exceptions internally');
- }
- $view = new View('/' . self::$user . '/files/');
-
- /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
- $storage = $this->getMockBuilder(Temporary::class)
- ->onlyMethods([$operation])
- ->getMock();
-
- /* Pause trash to avoid the trashbin intercepting rmdir and unlink calls */
- Server::get(ITrashManager::class)->pauseTrash();
- /* Same thing with encryption wrapper */
- Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption');
-
- Filesystem::mount($storage, [], self::$user . '/');
-
- // work directly on disk because mkdir might be mocked
- $realPath = $storage->getSourcePath('');
- mkdir($realPath . '/files');
- mkdir($realPath . '/files/dir');
- file_put_contents($realPath . '/files/test.txt', 'blah');
- $storage->getScanner()->scan('files');
-
- $storage->expects($this->once())
- ->method($operation)
- ->willReturnCallback(
- function (): void {
- throw new \Exception('Simulated exception');
- }
- );
-
- $thrown = false;
- try {
- call_user_func_array([$view, $operation], $operationArgs);
- } catch (\Exception $e) {
- $thrown = true;
- $this->assertEquals('Simulated exception', $e->getMessage());
- }
- $this->assertTrue($thrown, 'Exception was rethrown');
- $this->assertNull($this->getFileLockType($view, $path), 'File got unlocked after exception');
-
- /* Resume trash to avoid side effects */
- Server::get(ITrashManager::class)->resumeTrash();
- }
-
- public function testLockBasicOperationUnlocksAfterLockException(): void {
- $view = new View('/' . self::$user . '/files/');
-
- $storage = new Temporary([]);
-
- Filesystem::mount($storage, [], self::$user . '/');
-
- $storage->mkdir('files');
- $storage->mkdir('files/dir');
- $storage->file_put_contents('files/test.txt', 'blah');
- $storage->getScanner()->scan('files');
-
- // get a shared lock
- $handle = $view->fopen('test.txt', 'r');
-
- $thrown = false;
- try {
- // try (and fail) to get a write lock
- $view->unlink('test.txt');
- } catch (\Exception $e) {
- $thrown = true;
- $this->assertInstanceOf(LockedException::class, $e);
- }
- $this->assertTrue($thrown, 'Exception was rethrown');
-
- // clean shared lock
- fclose($handle);
-
- $this->assertNull($this->getFileLockType($view, 'test.txt'), 'File got unlocked');
- }
-
- /**
- * Test locks for fopen with fclose at the end
- *
- *
- * @param string $operation operation name on the view
- * @param array $operationArgs arguments for the operation
- * @param string $path path of the locked item to check
- * @param string $hookType hook type
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('basicOperationProviderForLocks')]
- public function testLockBasicOperationUnlocksAfterCancelledHook(
- $operation,
- $operationArgs,
- $path,
- $hookType,
- ): void {
- $view = new View('/' . self::$user . '/files/');
-
- /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
- $storage = $this->getMockBuilder(Temporary::class)
- ->onlyMethods([$operation])
- ->getMock();
-
- Filesystem::mount($storage, [], self::$user . '/');
- $storage->mkdir('files');
-
- Util::connectHook(
- Filesystem::CLASSNAME,
- $hookType,
- HookHelper::class,
- 'cancellingCallback'
- );
-
- call_user_func_array([$view, $operation], $operationArgs);
-
- $this->assertNull($this->getFileLockType($view, $path), 'File got unlocked after exception');
- }
-
- public static function lockFileRenameOrCopyDataProvider(): array {
- return [
- ['rename', ILockingProvider::LOCK_EXCLUSIVE],
- ['copy', ILockingProvider::LOCK_SHARED],
- ];
- }
-
- /**
- * Test locks for rename or copy operation
- *
- *
- * @param string $operation operation to be done on the view
- * @param int $expectedLockTypeSourceDuring expected lock type on source file during
- * the operation
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('lockFileRenameOrCopyDataProvider')]
- public function testLockFileRename($operation, $expectedLockTypeSourceDuring): void {
- $view = new View('/' . self::$user . '/files/');
-
- /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
- $storage = $this->getMockBuilder(Temporary::class)
- ->onlyMethods([$operation, 'getMetaData', 'filemtime'])
- ->getMock();
-
- $storage->expects($this->any())
- ->method('getMetaData')
- ->willReturn([
- 'mtime' => 1885434487,
- 'etag' => '',
- 'mimetype' => 'text/plain',
- 'permissions' => Constants::PERMISSION_ALL,
- 'size' => 3
- ]);
- $storage->expects($this->any())
- ->method('filemtime')
- ->willReturn(123456789);
-
- $sourcePath = 'original.txt';
- $targetPath = 'target.txt';
-
- /* Disable encryption wrapper to avoid it intercepting mocked call */
- Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption');
-
- Filesystem::mount($storage, [], self::$user . '/');
- $storage->mkdir('files');
- $view->file_put_contents($sourcePath, 'meh');
-
- $storage->expects($this->once())
- ->method($operation)
- ->willReturnCallback(
- function () use ($view, $sourcePath, $targetPath, &$lockTypeSourceDuring, &$lockTypeTargetDuring) {
- $lockTypeSourceDuring = $this->getFileLockType($view, $sourcePath);
- $lockTypeTargetDuring = $this->getFileLockType($view, $targetPath);
-
- return true;
- }
- );
-
- $this->connectMockHooks($operation, $view, $sourcePath, $lockTypeSourcePre, $lockTypeSourcePost);
- $this->connectMockHooks($operation, $view, $targetPath, $lockTypeTargetPre, $lockTypeTargetPost);
-
- $this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked before operation');
- $this->assertNull($this->getFileLockType($view, $targetPath), 'Target file not locked before operation');
-
- $view->$operation($sourcePath, $targetPath);
-
- $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeSourcePre, 'Source file locked properly during pre-hook');
- $this->assertEquals($expectedLockTypeSourceDuring, $lockTypeSourceDuring, 'Source file locked properly during operation');
- $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeSourcePost, 'Source file locked properly during post-hook');
-
- $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeTargetPre, 'Target file locked properly during pre-hook');
- $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeTargetDuring, 'Target file locked properly during operation');
- $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeTargetPost, 'Target file locked properly during post-hook');
-
- $this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked after operation');
- $this->assertNull($this->getFileLockType($view, $targetPath), 'Target file not locked after operation');
- }
-
- /**
- * simulate a failed copy operation.
- * We expect that we catch the exception, free the lock and re-throw it.
- *
- */
- public function testLockFileCopyException(): void {
- $this->expectException(\Exception::class);
-
- $view = new View('/' . self::$user . '/files/');
-
- /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
- $storage = $this->getMockBuilder(Temporary::class)
- ->onlyMethods(['copy'])
- ->getMock();
-
- $sourcePath = 'original.txt';
- $targetPath = 'target.txt';
-
- /* Disable encryption wrapper to avoid it intercepting mocked call */
- Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption');
-
- Filesystem::mount($storage, [], self::$user . '/');
- $storage->mkdir('files');
- $view->file_put_contents($sourcePath, 'meh');
-
- $storage->expects($this->once())
- ->method('copy')
- ->willReturnCallback(
- function (): void {
- throw new \Exception();
- }
- );
-
- $this->connectMockHooks('copy', $view, $sourcePath, $lockTypeSourcePre, $lockTypeSourcePost);
- $this->connectMockHooks('copy', $view, $targetPath, $lockTypeTargetPre, $lockTypeTargetPost);
-
- $this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked before operation');
- $this->assertNull($this->getFileLockType($view, $targetPath), 'Target file not locked before operation');
-
- try {
- $view->copy($sourcePath, $targetPath);
- } catch (\Exception $e) {
- $this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked after operation');
- $this->assertNull($this->getFileLockType($view, $targetPath), 'Target file not locked after operation');
- throw $e;
- }
- }
-
- /**
- * Test rename operation: unlock first path when second path was locked
- */
- public function testLockFileRenameUnlockOnException(): void {
- self::loginAsUser('test');
-
- $view = new View('/' . self::$user . '/files/');
-
- $sourcePath = 'original.txt';
- $targetPath = 'target.txt';
- $view->file_put_contents($sourcePath, 'meh');
-
- // simulate that the target path is already locked
- $view->lockFile($targetPath, ILockingProvider::LOCK_EXCLUSIVE);
-
- $this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked before operation');
- $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $this->getFileLockType($view, $targetPath), 'Target file is locked before operation');
-
- $thrown = false;
- try {
- $view->rename($sourcePath, $targetPath);
- } catch (LockedException $e) {
- $thrown = true;
- }
-
- $this->assertTrue($thrown, 'LockedException thrown');
-
- $this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked after operation');
- $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $this->getFileLockType($view, $targetPath), 'Target file still locked after operation');
-
- $view->unlockFile($targetPath, ILockingProvider::LOCK_EXCLUSIVE);
- }
-
- /**
- * Test rename operation: unlock first path when second path was locked
- */
- public function testGetOwner(): void {
- self::loginAsUser('test');
-
- $view = new View('/test/files/');
-
- $path = 'foo.txt';
- $view->file_put_contents($path, 'meh');
-
- $this->assertEquals('test', $view->getFileInfo($path)->getOwner()->getUID());
-
- $folderInfo = $view->getDirectoryContent('');
- $folderInfo = array_values(array_filter($folderInfo, function (FileInfo $info) {
- return $info->getName() === 'foo.txt';
- }));
-
- $this->assertEquals('test', $folderInfo[0]->getOwner()->getUID());
-
- $subStorage = new Temporary();
- Filesystem::mount($subStorage, [], '/test/files/asd');
-
- $folderInfo = $view->getDirectoryContent('');
- $folderInfo = array_values(array_filter($folderInfo, function (FileInfo $info) {
- return $info->getName() === 'asd';
- }));
-
- $this->assertEquals('test', $folderInfo[0]->getOwner()->getUID());
- }
-
- public static function lockFileRenameOrCopyCrossStorageDataProvider(): array {
- return [
- ['rename', 'moveFromStorage', ILockingProvider::LOCK_EXCLUSIVE],
- ['copy', 'copyFromStorage', ILockingProvider::LOCK_SHARED],
- ];
- }
-
- /**
- * Test locks for rename or copy operation cross-storage
- *
- *
- * @param string $viewOperation operation to be done on the view
- * @param string $storageOperation operation to be mocked on the storage
- * @param int $expectedLockTypeSourceDuring expected lock type on source file during
- * the operation
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('lockFileRenameOrCopyCrossStorageDataProvider')]
- public function testLockFileRenameCrossStorage($viewOperation, $storageOperation, $expectedLockTypeSourceDuring): void {
- $view = new View('/' . self::$user . '/files/');
-
- /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage */
- $storage = $this->getMockBuilder(Temporary::class)
- ->onlyMethods([$storageOperation])
- ->getMock();
- /** @var Temporary|\PHPUnit\Framework\MockObject\MockObject $storage2 */
- $storage2 = $this->getMockBuilder(Temporary::class)
- ->onlyMethods([$storageOperation, 'getMetaData', 'filemtime'])
- ->getMock();
-
- $storage2->expects($this->any())
- ->method('getMetaData')
- ->willReturn([
- 'mtime' => 1885434487,
- 'etag' => '',
- 'mimetype' => 'text/plain',
- 'permissions' => Constants::PERMISSION_ALL,
- 'size' => 3
- ]);
- $storage2->expects($this->any())
- ->method('filemtime')
- ->willReturn(123456789);
-
- $sourcePath = 'original.txt';
- $targetPath = 'substorage/target.txt';
-
- /* Disable encryption wrapper to avoid it intercepting mocked call */
- Server::get(IStorageFactory::class)->removeStorageWrapper('oc_encryption');
-
- Filesystem::mount($storage, [], self::$user . '/');
- Filesystem::mount($storage2, [], self::$user . '/files/substorage');
- $storage->mkdir('files');
- $view->file_put_contents($sourcePath, 'meh');
- $storage2->getUpdater()->update('');
-
- $storage->expects($this->never())
- ->method($storageOperation);
- $storage2->expects($this->once())
- ->method($storageOperation)
- ->willReturnCallback(
- function () use ($view, $sourcePath, $targetPath, &$lockTypeSourceDuring, &$lockTypeTargetDuring) {
- $lockTypeSourceDuring = $this->getFileLockType($view, $sourcePath);
- $lockTypeTargetDuring = $this->getFileLockType($view, $targetPath);
-
- return true;
- }
- );
-
- $this->connectMockHooks($viewOperation, $view, $sourcePath, $lockTypeSourcePre, $lockTypeSourcePost);
- $this->connectMockHooks($viewOperation, $view, $targetPath, $lockTypeTargetPre, $lockTypeTargetPost);
-
- $this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked before operation');
- $this->assertNull($this->getFileLockType($view, $targetPath), 'Target file not locked before operation');
-
- $view->$viewOperation($sourcePath, $targetPath);
-
- $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeSourcePre, 'Source file locked properly during pre-hook');
- $this->assertEquals($expectedLockTypeSourceDuring, $lockTypeSourceDuring, 'Source file locked properly during operation');
- $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeSourcePost, 'Source file locked properly during post-hook');
-
- $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeTargetPre, 'Target file locked properly during pre-hook');
- $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeTargetDuring, 'Target file locked properly during operation');
- $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeTargetPost, 'Target file locked properly during post-hook');
-
- $this->assertNull($this->getFileLockType($view, $sourcePath), 'Source file not locked after operation');
- $this->assertNull($this->getFileLockType($view, $targetPath), 'Target file not locked after operation');
- }
-
- /**
- * Test locks when moving a mount point
- */
- public function testLockMoveMountPoint(): void {
- self::loginAsUser('test');
-
- [$mount] = $this->createTestMovableMountPoints([
- self::$user . '/files/substorage',
- ]);
-
- $view = new View('/' . self::$user . '/files/');
- $view->mkdir('subdir');
-
- $sourcePath = 'substorage';
- $targetPath = 'subdir/substorage_moved';
-
- $mount->expects($this->once())
- ->method('moveMount')
- ->willReturnCallback(
- function ($target) use ($mount, $view, $sourcePath, $targetPath, &$lockTypeSourceDuring, &$lockTypeTargetDuring, &$lockTypeSharedRootDuring) {
- $lockTypeSourceDuring = $this->getFileLockType($view, $sourcePath, true);
- $lockTypeTargetDuring = $this->getFileLockType($view, $targetPath, true);
-
- $lockTypeSharedRootDuring = $this->getFileLockType($view, $sourcePath, false);
-
- $mount->setMountPoint($target);
-
- return true;
- }
- );
-
- $this->connectMockHooks('rename', $view, $sourcePath, $lockTypeSourcePre, $lockTypeSourcePost, true);
- $this->connectMockHooks('rename', $view, $targetPath, $lockTypeTargetPre, $lockTypeTargetPost, true);
- // in pre-hook, mount point is still on $sourcePath
- $this->connectMockHooks('rename', $view, $sourcePath, $lockTypeSharedRootPre, $dummy, false);
- // in post-hook, mount point is now on $targetPath
- $this->connectMockHooks('rename', $view, $targetPath, $dummy, $lockTypeSharedRootPost, false);
-
- $this->assertNull($this->getFileLockType($view, $sourcePath, false), 'Shared storage root not locked before operation');
- $this->assertNull($this->getFileLockType($view, $sourcePath, true), 'Source path not locked before operation');
- $this->assertNull($this->getFileLockType($view, $targetPath, true), 'Target path not locked before operation');
-
- $view->rename($sourcePath, $targetPath);
-
- $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeSourcePre, 'Source path locked properly during pre-hook');
- $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeSourceDuring, 'Source path locked properly during operation');
- $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeSourcePost, 'Source path locked properly during post-hook');
-
- $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeTargetPre, 'Target path locked properly during pre-hook');
- $this->assertEquals(ILockingProvider::LOCK_EXCLUSIVE, $lockTypeTargetDuring, 'Target path locked properly during operation');
- $this->assertEquals(ILockingProvider::LOCK_SHARED, $lockTypeTargetPost, 'Target path locked properly during post-hook');
-
- $this->assertNull($lockTypeSharedRootPre, 'Shared storage root not locked during pre-hook');
- $this->assertNull($lockTypeSharedRootDuring, 'Shared storage root not locked during move');
- $this->assertNull($lockTypeSharedRootPost, 'Shared storage root not locked during post-hook');
-
- $this->assertNull($this->getFileLockType($view, $sourcePath, false), 'Shared storage root not locked after operation');
- $this->assertNull($this->getFileLockType($view, $sourcePath, true), 'Source path not locked after operation');
- $this->assertNull($this->getFileLockType($view, $targetPath, true), 'Target path not locked after operation');
- }
-
- /**
- * Connect hook callbacks for hook type
- *
- * @param string $hookType hook type or null for none
- * @param View $view view to check the lock on
- * @param string $path path for which to check the lock
- * @param int $lockTypePre variable to receive lock type that was active in the pre-hook
- * @param int $lockTypePost variable to receive lock type that was active in the post-hook
- * @param bool $onMountPoint true to check the mount point instead of the
- * mounted storage
- */
- private function connectMockHooks($hookType, $view, $path, &$lockTypePre, &$lockTypePost, $onMountPoint = false) {
- if ($hookType === null) {
- return;
- }
-
- $eventHandler = $this->getMockBuilder(TestEventHandler::class)
- ->onlyMethods(['preCallback', 'postCallback'])
- ->getMock();
-
- $eventHandler->expects($this->any())
- ->method('preCallback')
- ->willReturnCallback(
- function () use ($view, $path, $onMountPoint, &$lockTypePre): void {
- $lockTypePre = $this->getFileLockType($view, $path, $onMountPoint);
- }
- );
- $eventHandler->expects($this->any())
- ->method('postCallback')
- ->willReturnCallback(
- function () use ($view, $path, $onMountPoint, &$lockTypePost): void {
- $lockTypePost = $this->getFileLockType($view, $path, $onMountPoint);
- }
- );
-
- if ($hookType !== '') {
- Util::connectHook(
- Filesystem::CLASSNAME,
- $hookType,
- $eventHandler,
- 'preCallback'
- );
- Util::connectHook(
- Filesystem::CLASSNAME,
- 'post_' . $hookType,
- $eventHandler,
- 'postCallback'
- );
- }
- }
-
- /**
- * Returns the file lock type
- *
- * @param View $view view
- * @param string $path path
- * @param bool $onMountPoint true to check the mount point instead of the
- * mounted storage
- *
- * @return int lock type or null if file was not locked
- */
- private function getFileLockType(View $view, $path, $onMountPoint = false) {
- if ($this->isFileLocked($view, $path, ILockingProvider::LOCK_EXCLUSIVE, $onMountPoint)) {
- return ILockingProvider::LOCK_EXCLUSIVE;
- } elseif ($this->isFileLocked($view, $path, ILockingProvider::LOCK_SHARED, $onMountPoint)) {
- return ILockingProvider::LOCK_SHARED;
- }
- return null;
- }
-
- public function testRemoveMoveableMountPoint(): void {
- $mountPoint = '/' . self::$user . '/files/mount/';
-
- // Mock the mount point
- /** @var TestMoveableMountPoint|\PHPUnit\Framework\MockObject\MockObject $mount */
- $mount = $this->createMock(TestMoveableMountPoint::class);
- $mount->expects($this->once())
- ->method('getMountPoint')
- ->willReturn($mountPoint);
- $mount->expects($this->once())
- ->method('removeMount')
- ->willReturn(true);
- $mount->expects($this->any())
- ->method('getInternalPath')
- ->willReturn('');
-
- // Register mount
- Filesystem::getMountManager()->addMount($mount);
-
- // Listen for events
- $eventHandler = $this->getMockBuilder(TestEventHandler::class)
- ->onlyMethods(['umount', 'post_umount'])
- ->getMock();
- $eventHandler->expects($this->once())
- ->method('umount')
- ->with([Filesystem::signal_param_path => '/mount']);
- $eventHandler->expects($this->once())
- ->method('post_umount')
- ->with([Filesystem::signal_param_path => '/mount']);
- Util::connectHook(
- Filesystem::CLASSNAME,
- 'umount',
- $eventHandler,
- 'umount'
- );
- Util::connectHook(
- Filesystem::CLASSNAME,
- 'post_umount',
- $eventHandler,
- 'post_umount'
- );
-
- //Delete the mountpoint
- $view = new View('/' . self::$user . '/files');
- $this->assertEquals('foo', $view->rmdir('mount'));
- }
-
- public static function mimeFilterProvider(): array {
- return [
- [null, ['test1.txt', 'test2.txt', 'test3.md', 'test4.png']],
- ['text/plain', ['test1.txt', 'test2.txt']],
- ['text/markdown', ['test3.md']],
- ['text', ['test1.txt', 'test2.txt', 'test3.md']],
- ];
- }
-
- /**
- * @param string $filter
- * @param string[] $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('mimeFilterProvider')]
- public function testGetDirectoryContentMimeFilter($filter, $expected): void {
- $storage1 = new Temporary();
- $root = self::getUniqueID('/');
- Filesystem::mount($storage1, [], $root . '/');
- $view = new View($root);
-
- $view->file_put_contents('test1.txt', 'asd');
- $view->file_put_contents('test2.txt', 'asd');
- $view->file_put_contents('test3.md', 'asd');
- $view->file_put_contents('test4.png', '');
-
- $content = $view->getDirectoryContent('', $filter);
-
- $files = array_map(function (FileInfo $info) {
- return $info->getName();
- }, $content);
- sort($files);
-
- $this->assertEquals($expected, $files);
- }
-
- public function testFilePutContentsClearsChecksum(): void {
- $storage = new Temporary([]);
- $scanner = $storage->getScanner();
- $storage->file_put_contents('foo.txt', 'bar');
- Filesystem::mount($storage, [], '/test/');
- $scanner->scan('');
-
- $view = new View('/test/foo.txt');
- $view->putFileInfo('.', ['checksum' => '42']);
-
- $this->assertEquals('bar', $view->file_get_contents(''));
- $fh = tmpfile();
- fwrite($fh, 'fooo');
- rewind($fh);
- clearstatcache();
- $view->file_put_contents('', $fh);
- $this->assertEquals('fooo', $view->file_get_contents(''));
- $data = $view->getFileInfo('.');
- $this->assertEquals('', $data->getChecksum());
- }
-
- public function testDeleteGhostFile(): void {
- $storage = new Temporary([]);
- $scanner = $storage->getScanner();
- $cache = $storage->getCache();
- $storage->file_put_contents('foo.txt', 'bar');
- Filesystem::mount($storage, [], '/test/');
- $scanner->scan('');
-
- $storage->unlink('foo.txt');
-
- $this->assertTrue($cache->inCache('foo.txt'));
-
- $view = new View('/test');
- $rootInfo = $view->getFileInfo('');
- $this->assertEquals(3, $rootInfo->getSize());
- $view->unlink('foo.txt');
- $newInfo = $view->getFileInfo('');
-
- $this->assertFalse($cache->inCache('foo.txt'));
- $this->assertNotEquals($rootInfo->getEtag(), $newInfo->getEtag());
- $this->assertEquals(0, $newInfo->getSize());
- }
-
- public function testDeleteGhostFolder(): void {
- $storage = new Temporary([]);
- $scanner = $storage->getScanner();
- $cache = $storage->getCache();
- $storage->mkdir('foo');
- $storage->file_put_contents('foo/foo.txt', 'bar');
- Filesystem::mount($storage, [], '/test/');
- $scanner->scan('');
-
- $storage->rmdir('foo');
-
- $this->assertTrue($cache->inCache('foo'));
- $this->assertTrue($cache->inCache('foo/foo.txt'));
-
- $view = new View('/test');
- $rootInfo = $view->getFileInfo('');
- $this->assertEquals(3, $rootInfo->getSize());
- $view->rmdir('foo');
- $newInfo = $view->getFileInfo('');
-
- $this->assertFalse($cache->inCache('foo'));
- $this->assertFalse($cache->inCache('foo/foo.txt'));
- $this->assertNotEquals($rootInfo->getEtag(), $newInfo->getEtag());
- $this->assertEquals(0, $newInfo->getSize());
- }
-
- public function testCreateParentDirectories(): void {
- $view = $this->getMockBuilder(View::class)
- ->disableOriginalConstructor()
- ->onlyMethods([
- 'is_file',
- 'file_exists',
- 'mkdir',
- ])
- ->getMock();
-
- $view->expects($this->exactly(3))
- ->method('is_file')
- ->willReturnMap([
- ['/new', false],
- ['/new/folder', false],
- ['/new/folder/structure', false],
- ]);
- $view->expects($this->exactly(3))
- ->method('file_exists')
- ->willReturnMap([
- ['/new', true],
- ['/new/folder', false],
- ['/new/folder/structure', false],
- ]);
-
- $calls = ['/new/folder', '/new/folder/structure'];
- $view->expects($this->exactly(2))
- ->method('mkdir')
- ->willReturnCallback(function ($dir) use (&$calls): void {
- $expected = array_shift($calls);
- $this->assertEquals($expected, $dir);
- });
-
- $this->assertTrue(self::invokePrivate($view, 'createParentDirectories', ['/new/folder/structure']));
- }
-
- public function testCreateParentDirectoriesWithExistingFile(): void {
- $view = $this->getMockBuilder(View::class)
- ->disableOriginalConstructor()
- ->onlyMethods([
- 'is_file',
- 'file_exists',
- 'mkdir',
- ])
- ->getMock();
-
- $view
- ->expects($this->once())
- ->method('is_file')
- ->with('/file.txt')
- ->willReturn(true);
- $this->assertFalse(self::invokePrivate($view, 'createParentDirectories', ['/file.txt/folder/structure']));
- }
-
- public function testCacheExtension(): void {
- $storage = new Temporary([]);
- $scanner = $storage->getScanner();
- $storage->file_put_contents('foo.txt', 'bar');
- $scanner->scan('');
-
- Filesystem::mount($storage, [], '/test/');
- $view = new View('/test');
-
- $info = $view->getFileInfo('/foo.txt');
- $this->assertEquals(0, $info->getUploadTime());
- $this->assertEquals(0, $info->getCreationTime());
-
- $view->putFileInfo('/foo.txt', ['upload_time' => 25]);
-
- $info = $view->getFileInfo('/foo.txt');
- $this->assertEquals(25, $info->getUploadTime());
- $this->assertEquals(0, $info->getCreationTime());
- }
-
- public function testFopenGone(): void {
- $storage = new Temporary([]);
- $scanner = $storage->getScanner();
- $storage->file_put_contents('foo.txt', 'bar');
- $scanner->scan('');
- $cache = $storage->getCache();
-
- Filesystem::mount($storage, [], '/test/');
- $view = new View('/test');
-
- $storage->unlink('foo.txt');
-
- $this->assertTrue($cache->inCache('foo.txt'));
-
- $this->assertFalse($view->fopen('foo.txt', 'r'));
-
- $this->assertFalse($cache->inCache('foo.txt'));
- }
-
- /**
- * Regression test: checks that when fopen fails, the view checks the correct path and removes the cache entry.
- */
- public function testFopenGoneViewBelowMountRoot(): void {
- $storage = new Temporary([]);
- $storage->mkdir('sub');
- $storage->file_put_contents('foo.txt', 'bar');
- $storage->file_put_contents('sub/foo.txt', 'bar');
- $storage->getScanner()->scan('');
- $cache = $storage->getCache();
-
- Filesystem::mount($storage, [], '/test/');
- $view = new View('/test/sub');
-
- $storage->unlink('sub/foo.txt');
-
- $this->assertTrue($cache->inCache('sub/foo.txt'));
- $this->assertFalse($view->fopen('foo.txt', 'r'));
- $this->assertFalse($cache->inCache('sub/foo.txt'));
- }
-
- public function testFopenFailureOnExistingFileDoesNotPropagate(): void {
- /** @var Temporary&MockObject $storage */
- $storage = $this->getMockBuilder(Temporary::class)
- ->setConstructorArgs([[]])
- ->onlyMethods(['fopen'])
- ->getMock();
- $storage->method('fopen')
- ->willReturn(false);
- $storage->mkdir('sub');
- $storage->file_put_contents('sub/foo.txt', 'bar');
- $storage->getScanner()->scan('');
- $cache = $storage->getCache();
- $cache->update($cache->getId('sub'), ['mtime' => 100]);
-
- Filesystem::mount($storage, [], '/test/');
- $view = new View('/test/sub');
-
- $this->assertFalse($view->fopen('foo.txt', 'r'));
- $this->assertTrue($cache->inCache('sub/foo.txt'));
- $this->assertEquals(100, $cache->get('sub')->getMTime());
- }
-
- public function testMountpointParentsCreated(): void {
- $storage1 = $this->getTestStorage();
- Filesystem::mount($storage1, [], '/');
-
- $storage2 = $this->getTestStorage();
- Filesystem::mount($storage2, [], '/A/B/C');
-
- $rootView = new View('');
-
- $folderData = $rootView->getDirectoryContent('/');
- usort($folderData, fn (FileInfo $a, FileInfo $b) => $a->getName() <=> $b->getName());
- $this->assertCount(4, $folderData);
-
- $this->assertEquals('A', $folderData[0]['name']);
- $this->assertEquals('folder', $folderData[1]['name']);
- $this->assertEquals('foo.png', $folderData[2]['name']);
- $this->assertEquals('foo.txt', $folderData[3]['name']);
-
- $folderData = $rootView->getDirectoryContent('/A');
- $this->assertCount(1, $folderData);
- $this->assertEquals('B', $folderData[0]['name']);
-
- $folderData = $rootView->getDirectoryContent('/A/B');
- $this->assertCount(1, $folderData);
- $this->assertEquals('C', $folderData[0]['name']);
-
- $folderData = $rootView->getDirectoryContent('/A/B/C');
- usort($folderData, fn (FileInfo $a, FileInfo $b) => $a->getName() <=> $b->getName());
- $this->assertCount(3, $folderData);
- $this->assertEquals('folder', $folderData[0]['name']);
- $this->assertEquals('foo.png', $folderData[1]['name']);
- $this->assertEquals('foo.txt', $folderData[2]['name']);
- }
-
- public function testCopyPreservesContent() {
- $viewUser1 = new View('/' . 'userId' . '/files');
- $viewUser1->mkdir('');
- $viewUser1->file_put_contents('foo.txt', 'foo');
- $viewUser1->copy('foo.txt', 'bar.txt');
- $this->assertEquals('foo', $viewUser1->file_get_contents('bar.txt'));
- }
-}
--- a/tests/lib/FilesMetadata/FilesMetadataManagerTest.php
+++ /dev/null
@@ -1,132 +0,0 @@
-
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-namespace Test\FilesMetadata;
-
-use OC\BackgroundJob\JobList;
-use OC\Files\Storage\Temporary;
-use OC\FilesMetadata\FilesMetadataManager;
-use OC\FilesMetadata\Service\IndexRequestService;
-use OC\FilesMetadata\Service\MetadataRequestService;
-use OCP\DB\QueryBuilder\IExpressionBuilder;
-use OCP\DB\QueryBuilder\IQueryBuilder;
-use OCP\EventDispatcher\Event;
-use OCP\EventDispatcher\IEventDispatcher;
-use OCP\Files\Folder;
-use OCP\Files\IRootFolder;
-use OCP\FilesMetadata\AMetadataEvent;
-use OCP\IAppConfig;
-use OCP\IDBConnection;
-use OCP\Server;
-use Psr\Log\LoggerInterface;
-use Test\TestCase;
-use Test\Traits\MountProviderTrait;
-use Test\Traits\UserTrait;
-
-#[\PHPUnit\Framework\Attributes\Group('DB')]
-class FilesMetadataManagerTest extends TestCase {
- use UserTrait;
- use MountProviderTrait;
-
- private IEventDispatcher $eventDispatcher;
- private JobList $jobList;
- private IAppConfig $appConfig;
- private LoggerInterface $logger;
- private MetadataRequestService $metadataRequestService;
- private IndexRequestService $indexRequestService;
- private FilesMetadataManager $manager;
- private IDBConnection $connection;
- private Folder $userFolder;
- private array $metadata = [];
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- $this->jobList = $this->createMock(JobList::class);
- $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
- $this->eventDispatcher->method('dispatchTyped')->willReturnCallback(function (Event $event): void {
- if ($event instanceof AMetadataEvent) {
- $name = $event->getNode()->getName();
- if (isset($this->metadata[$name])) {
- $meta = $event->getMetadata();
- foreach ($this->metadata[$name] as $key => $value) {
- $meta->setString($key, $value);
- }
- }
- }
- });
- $this->appConfig = $this->createMock(IAppConfig::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $this->connection = Server::get(IDBConnection::class);
- $this->metadataRequestService = new MetadataRequestService($this->connection, $this->logger);
- $this->indexRequestService = new IndexRequestService($this->connection, $this->logger);
- $this->manager = new FilesMetadataManager(
- $this->eventDispatcher,
- $this->jobList,
- $this->appConfig,
- $this->logger,
- $this->metadataRequestService,
- $this->indexRequestService,
- );
-
- $this->createUser('metatest', '');
- $this->registerMount('metatest', new Temporary([]), '/metatest');
-
- $rootFolder = Server::get(IRootFolder::class);
- $this->userFolder = $rootFolder->getUserFolder('metatest');
- }
-
- public function testRefreshMetadata(): void {
- $this->metadata['test.txt'] = [
- 'istest' => 'yes'
- ];
- $file = $this->userFolder->newFile('test.txt', 'test');
- $stored = $this->manager->refreshMetadata($file);
- $this->assertEquals($file->getId(), $stored->getFileId());
- $this->assertEquals('yes', $stored->getString('istest'));
-
- $retrieved = $this->manager->getMetadata($file->getId());
- $this->assertEquals($file->getId(), $retrieved->getFileId());
- $this->assertEquals('yes', $retrieved->getString('istest'));
- }
-
- public function testDropMetadataForFilesChunking(): void {
- $connection = $this->createMock(IDBConnection::class);
- $qb = $this->createMock(IQueryBuilder::class);
- $expr = $this->createMock(IExpressionBuilder::class);
-
- $connection->method('getQueryBuilder')->willReturn($qb);
- $qb->method('expr')->willReturn($expr);
- $qb->method('delete')->willReturnSelf();
- $qb->method('where')->willReturnSelf();
- $qb->method('hintShardKey')->willReturnSelf();
-
- $fileIds = range(1, IQueryBuilder::MAX_IN_PARAMETERS * 2 + 1);
- $expectedChunks = array_chunk($fileIds, IQueryBuilder::MAX_IN_PARAMETERS);
- $boundChunks = [];
-
- $qb->expects($this->exactly(count($expectedChunks)))
- ->method('createNamedParameter')
- ->willReturnCallback(function (array $chunk, $type) use (&$boundChunks): string {
- $this->assertSame(IQueryBuilder::PARAM_INT_ARRAY, $type);
- $boundChunks[] = $chunk;
- return ':param';
- });
-
- $qb->expects($this->exactly(count($expectedChunks)))
- ->method('executeStatement')
- ->willReturn(1);
-
- $service = new MetadataRequestService($connection, $this->logger);
- $service->dropMetadataForFiles(123, $fileIds);
-
- $this->assertSame($expectedChunks, $boundChunks);
- }
-}
--- a/tests/lib/FilesTest.php
+++ /dev/null
@@ -1,42 +0,0 @@
-getTemporaryFolder() . '/';
- mkdir($baseDir . 'a/b/c/d/e', 0777, true);
- mkdir($baseDir . 'a/b/c1/d/e', 0777, true);
- mkdir($baseDir . 'a/b/c2/d/e', 0777, true);
- mkdir($baseDir . 'a/b1/c1/d/e', 0777, true);
- mkdir($baseDir . 'a/b2/c1/d/e', 0777, true);
- mkdir($baseDir . 'a/b3/c1/d/e', 0777, true);
- mkdir($baseDir . 'a1/b', 0777, true);
- mkdir($baseDir . 'a1/c', 0777, true);
- file_put_contents($baseDir . 'a/test.txt', 'Hello file!');
- file_put_contents($baseDir . 'a/b1/c1/test one.txt', 'Hello file one!');
- file_put_contents($baseDir . 'a1/b/test two.txt', 'Hello file two!');
- Files::rmdirr($baseDir . 'a');
-
- $this->assertFalse(file_exists($baseDir . 'a'));
- $this->assertTrue(file_exists($baseDir . 'a1'));
-
- Files::rmdirr($baseDir);
- $this->assertFalse(file_exists($baseDir));
- }
-}
--- a/tests/lib/GlobalScale/ConfigTest.php
+++ /dev/null
@@ -1,76 +0,0 @@
-config = $this->createMock(IConfig::class);
- }
-
- /**
- * @param array $mockMethods
- * @return Config|\PHPUnit\Framework\MockObject\MockObject
- */
- public function getInstance($mockMethods = []) {
- if (!empty($mockMethods)) {
- return $this->getMockBuilder(Config::class)
- ->setConstructorArgs([$this->config])
- ->onlyMethods($mockMethods)
- ->getMock();
- }
-
- return new Config($this->config);
- }
-
- public function testIsGlobalScaleEnabled(): void {
- $gsConfig = $this->getInstance();
- $this->config->expects($this->once())->method('getSystemValueBool')
- ->with('gs.enabled', false)->willReturn(true);
-
- $result = $gsConfig->isGlobalScaleEnabled();
-
- $this->assertTrue($result);
- }
-
- /**
- *
- * @param bool $gsEnabled
- * @param string $gsFederation
- * @param bool $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestOnlyInternalFederation')]
- public function testOnlyInternalFederation($gsEnabled, $gsFederation, $expected): void {
- $gsConfig = $this->getInstance(['isGlobalScaleEnabled']);
-
- $gsConfig->expects($this->any())->method('isGlobalScaleEnabled')->willReturn($gsEnabled);
-
- $this->config->expects($this->any())->method('getSystemValueString')
- ->with('gs.federation', 'internal')->willReturn($gsFederation);
-
- $this->assertSame($expected, $gsConfig->onlyInternalFederation());
- }
-
- public static function dataTestOnlyInternalFederation(): array {
- return [
- [true, 'global', false],
- [true, 'internal', true],
- [false, 'global', false],
- [false, 'internal', false]
- ];
- }
-}
--- a/tests/lib/Group/Backend.php
+++ /dev/null
@@ -1,144 +0,0 @@
-getUniqueID('test_');
- } else {
- return $name;
- }
- }
-
- /**
- * get a new unique user name
- * test cases can override this in order to clean up created user
- *
- * @return string
- */
- public function getUserName() {
- return $this->getUniqueID('test_');
- }
-
- public function testAddRemove(): void {
- //get the number of groups we start with, in case there are exising groups
- $startCount = count($this->backend->getGroups());
-
- $name1 = $this->getGroupName();
- $name2 = $this->getGroupName();
- $this->backend->createGroup($name1);
- $count = count($this->backend->getGroups()) - $startCount;
- $this->assertEquals(1, $count);
- $this->assertTrue((array_search($name1, $this->backend->getGroups()) !== false));
- $this->assertFalse((array_search($name2, $this->backend->getGroups()) !== false));
- $this->backend->createGroup($name2);
- $count = count($this->backend->getGroups()) - $startCount;
- $this->assertEquals(2, $count);
- $this->assertTrue((array_search($name1, $this->backend->getGroups()) !== false));
- $this->assertTrue((array_search($name2, $this->backend->getGroups()) !== false));
-
- $this->backend->deleteGroup($name2);
- $count = count($this->backend->getGroups()) - $startCount;
- $this->assertEquals(1, $count);
- $this->assertTrue((array_search($name1, $this->backend->getGroups()) !== false));
- $this->assertFalse((array_search($name2, $this->backend->getGroups()) !== false));
- }
-
- public function testUser(): void {
- $group1 = $this->getGroupName();
- $group2 = $this->getGroupName();
- $this->backend->createGroup($group1);
- $this->backend->createGroup($group2);
-
- $user1 = $this->getUserName();
- $user2 = $this->getUserName();
-
- $this->assertFalse($this->backend->inGroup($user1, $group1));
- $this->assertFalse($this->backend->inGroup($user2, $group1));
- $this->assertFalse($this->backend->inGroup($user1, $group2));
- $this->assertFalse($this->backend->inGroup($user2, $group2));
-
- $this->assertTrue($this->backend->addToGroup($user1, $group1));
-
- $this->assertTrue($this->backend->inGroup($user1, $group1));
- $this->assertFalse($this->backend->inGroup($user2, $group1));
- $this->assertFalse($this->backend->inGroup($user1, $group2));
- $this->assertFalse($this->backend->inGroup($user2, $group2));
-
- $this->assertFalse($this->backend->addToGroup($user1, $group1));
-
- $this->assertEquals([$user1], $this->backend->usersInGroup($group1));
- $this->assertEquals([], $this->backend->usersInGroup($group2));
-
- $this->assertEquals([$group1], $this->backend->getUserGroups($user1));
- $this->assertEquals([], $this->backend->getUserGroups($user2));
-
- $this->backend->deleteGroup($group1);
- $this->assertEquals([], $this->backend->getUserGroups($user1));
- $this->assertEquals([], $this->backend->usersInGroup($group1));
- $this->assertFalse($this->backend->inGroup($user1, $group1));
- }
-
- public function testSearchGroups(): void {
- $name1 = $this->getGroupName('foobarbaz');
- $name2 = $this->getGroupName('bazfoobarfoo');
- $name3 = $this->getGroupName('notme');
-
- $this->backend->createGroup($name1);
- $this->backend->createGroup($name2);
- $this->backend->createGroup($name3);
-
- $result = $this->backend->getGroups('foobar');
- $this->assertSame(2, count($result));
- }
-
- public function testSearchUsers(): void {
- $group = $this->getGroupName();
- $this->backend->createGroup($group);
-
- $name1 = 'foobarbaz';
- $name2 = 'bazbarfoo';
- $name3 = 'notme';
-
- $this->backend->addToGroup($name1, $group);
- $this->backend->addToGroup($name2, $group);
- $this->backend->addToGroup($name3, $group);
-
- $result = $this->backend->usersInGroup($group, 'bar');
- $this->assertSame(2, count($result));
-
- $result = $this->backend->countUsersInGroup($group, 'bar');
- $this->assertSame(2, $result);
- }
-
- public function testAddDouble(): void {
- $group = $this->getGroupName();
-
- $this->backend->createGroup($group);
- $this->backend->createGroup($group);
-
- $this->addToAssertionCount(1);
- }
-}
--- a/tests/lib/Group/DatabaseTest.php
+++ /dev/null
@@ -1,63 +0,0 @@
-groups[] = $name;
- return $name;
- }
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
- $this->backend = new Database();
- }
-
- #[\Override]
- protected function tearDown(): void {
- foreach ($this->groups as $group) {
- $this->backend->deleteGroup($group);
- }
- parent::tearDown();
- }
-
- public function testAddDoubleNoCache(): void {
- $group = $this->getGroupName();
-
- $this->backend->createGroup($group);
-
- $backend = new Database();
- $this->assertNull($backend->createGroup($group));
- }
-
- public function testAddLongGroupName(): void {
- $groupName = $this->getUniqueID('test_', 100);
-
- $gidCreated = $this->backend->createGroup($groupName);
- $this->assertEquals(64, strlen($gidCreated));
-
- $group = $this->backend->getGroupDetails($gidCreated);
- $this->assertEquals(['displayName' => $groupName], $group);
- }
-}
--- a/tests/lib/Group/Dummy.php
+++ /dev/null
@@ -1,24 +0,0 @@
-backend = new \Test\Util\Group\Dummy();
- }
-}
--- a/tests/lib/Group/GroupTest.php
+++ /dev/null
@@ -1,514 +0,0 @@
-dispatcher = $this->createMock(IEventDispatcher::class);
- }
-
- /**
- * @param string $uid
- * @param \OC\User\Backend $backend
- * @return User
- */
- private function newUser($uid, \OC\User\Backend $backend) {
- $user = $this->createMock(IUser::class);
- $user->method('getUID')
- ->willReturn($uid);
- $user->method('getBackend')
- ->willReturn($backend);
-
- return $user;
- }
-
- /**
- * @return \OC\User\Manager
- */
- protected function getUserManager() {
- $userManager = $this->getMockBuilder('\OC\User\Manager')
- ->disableOriginalConstructor()
- ->getMock();
- $backend = $this->getMockBuilder('\OC\User\Backend')
- ->disableOriginalConstructor()
- ->getMock();
- $user1 = $this->newUser('user1', $backend);
- $user2 = $this->newUser('user2', $backend);
- $user3 = $this->newUser('user3', $backend);
- $userManager->expects($this->any())
- ->method('get')
- ->willReturnMap([
- ['user1', $user1],
- ['user2', $user2],
- ['user3', $user3]
- ]);
- return $userManager;
- }
-
- public function testGetUsersSingleBackend(): void {
- $backend = $this->getMockBuilder('OC\Group\Database')
- ->disableOriginalConstructor()
- ->getMock();
- $userManager = $this->getUserManager();
- $group = new Group('group1', [$backend], $this->dispatcher, $userManager);
-
- $backend->expects($this->once())
- ->method('usersInGroup')
- ->with('group1')
- ->willReturn(['user1', 'user2']);
-
- $users = $group->getUsers();
-
- $this->assertEquals(2, count($users));
- $user1 = $users['user1'];
- $user2 = $users['user2'];
- $this->assertEquals('user1', $user1->getUID());
- $this->assertEquals('user2', $user2->getUID());
- }
-
- public function testGetUsersMultipleBackends(): void {
- $backend1 = $this->getMockBuilder('OC\Group\Database')
- ->disableOriginalConstructor()
- ->getMock();
- $backend2 = $this->getMockBuilder('OC\Group\Database')
- ->disableOriginalConstructor()
- ->getMock();
- $userManager = $this->getUserManager();
- $group = new Group('group1', [$backend1, $backend2], $this->dispatcher, $userManager);
-
- $backend1->expects($this->once())
- ->method('usersInGroup')
- ->with('group1')
- ->willReturn(['user1', 'user2']);
-
- $backend2->expects($this->once())
- ->method('usersInGroup')
- ->with('group1')
- ->willReturn(['user2', 'user3']);
-
- $users = $group->getUsers();
-
- $this->assertEquals(3, count($users));
- $user1 = $users['user1'];
- $user2 = $users['user2'];
- $user3 = $users['user3'];
- $this->assertEquals('user1', $user1->getUID());
- $this->assertEquals('user2', $user2->getUID());
- $this->assertEquals('user3', $user3->getUID());
- }
-
- public function testInGroupSingleBackend(): void {
- $backend = $this->getMockBuilder('OC\Group\Database')
- ->disableOriginalConstructor()
- ->getMock();
- $userManager = $this->getUserManager();
- $userBackend = $this->getMockBuilder('\OC\User\Backend')
- ->disableOriginalConstructor()
- ->getMock();
- $group = new Group('group1', [$backend], $this->dispatcher, $userManager);
-
- $backend->expects($this->once())
- ->method('inGroup')
- ->with('user1', 'group1')
- ->willReturn(true);
-
- $this->assertTrue($group->inGroup($this->newUser('user1', $userBackend)));
- }
-
- public function testInGroupMultipleBackends(): void {
- $backend1 = $this->getMockBuilder('OC\Group\Database')
- ->disableOriginalConstructor()
- ->getMock();
- $backend2 = $this->getMockBuilder('OC\Group\Database')
- ->disableOriginalConstructor()
- ->getMock();
- $userManager = $this->getUserManager();
- $userBackend = $this->getMockBuilder(\OC\User\Backend::class)
- ->disableOriginalConstructor()
- ->getMock();
- $group = new Group('group1', [$backend1, $backend2], $this->dispatcher, $userManager);
-
- $backend1->expects($this->once())
- ->method('inGroup')
- ->with('user1', 'group1')
- ->willReturn(false);
-
- $backend2->expects($this->once())
- ->method('inGroup')
- ->with('user1', 'group1')
- ->willReturn(true);
-
- $this->assertTrue($group->inGroup($this->newUser('user1', $userBackend)));
- }
-
- public function testAddUser(): void {
- $backend = $this->getMockBuilder('OC\Group\Database')
- ->disableOriginalConstructor()
- ->getMock();
- $userManager = $this->getUserManager();
- $userBackend = $this->getMockBuilder('\OC\User\Backend')
- ->disableOriginalConstructor()
- ->getMock();
- $group = new Group('group1', [$backend], $this->dispatcher, $userManager);
-
- $backend->expects($this->once())
- ->method('inGroup')
- ->with('user1', 'group1')
- ->willReturn(false);
- $backend->expects($this->any())
- ->method('implementsActions')
- ->willReturn(true);
-
- $backend->expects($this->once())
- ->method('addToGroup')
- ->with('user1', 'group1');
-
- $group->addUser($this->newUser('user1', $userBackend));
- }
-
- public function testAddUserAlreadyInGroup(): void {
- $backend = $this->getMockBuilder('OC\Group\Database')
- ->disableOriginalConstructor()
- ->getMock();
- $userManager = $this->getUserManager();
- $userBackend = $this->getMockBuilder('\OC\User\Backend')
- ->disableOriginalConstructor()
- ->getMock();
- $group = new Group('group1', [$backend], $this->dispatcher, $userManager);
-
- $backend->expects($this->once())
- ->method('inGroup')
- ->with('user1', 'group1')
- ->willReturn(true);
- $backend->expects($this->any())
- ->method('implementsActions')
- ->willReturn(true);
-
- $backend->expects($this->never())
- ->method('addToGroup');
-
- $group->addUser($this->newUser('user1', $userBackend));
- }
-
- public function testRemoveUser(): void {
- $backend = $this->getMockBuilder('OC\Group\Database')
- ->disableOriginalConstructor()
- ->getMock();
- $userManager = $this->getUserManager();
- $userBackend = $this->getMockBuilder('\OC\User\Backend')
- ->disableOriginalConstructor()
- ->getMock();
- $group = new Group('group1', [$backend], $this->dispatcher, $userManager);
-
- $backend->expects($this->once())
- ->method('inGroup')
- ->with('user1', 'group1')
- ->willReturn(true);
- $backend->expects($this->any())
- ->method('implementsActions')
- ->willReturn(true);
-
- $backend->expects($this->once())
- ->method('removeFromGroup')
- ->with('user1', 'group1');
-
- $group->removeUser($this->newUser('user1', $userBackend));
- }
-
- public function testRemoveUserNotInGroup(): void {
- $backend = $this->getMockBuilder('OC\Group\Database')
- ->disableOriginalConstructor()
- ->getMock();
- $userManager = $this->getUserManager();
- $userBackend = $this->getMockBuilder(\OC\User\Backend::class)
- ->disableOriginalConstructor()
- ->getMock();
- $group = new Group('group1', [$backend], $this->dispatcher, $userManager);
-
- $backend->expects($this->once())
- ->method('inGroup')
- ->with('user1', 'group1')
- ->willReturn(false);
- $backend->expects($this->any())
- ->method('implementsActions')
- ->willReturn(true);
-
- $backend->expects($this->never())
- ->method('removeFromGroup');
-
- $group->removeUser($this->newUser('user1', $userBackend));
- }
-
- public function testRemoveUserMultipleBackends(): void {
- $backend1 = $this->getMockBuilder('OC\Group\Database')
- ->disableOriginalConstructor()
- ->getMock();
- $backend2 = $this->getMockBuilder('OC\Group\Database')
- ->disableOriginalConstructor()
- ->getMock();
- $userManager = $this->getUserManager();
- $userBackend = $this->getMockBuilder('\OC\User\Backend')
- ->disableOriginalConstructor()
- ->getMock();
- $group = new Group('group1', [$backend1, $backend2], $this->dispatcher, $userManager);
-
- $backend1->expects($this->once())
- ->method('inGroup')
- ->with('user1', 'group1')
- ->willReturn(true);
- $backend1->expects($this->any())
- ->method('implementsActions')
- ->willReturn(true);
-
- $backend1->expects($this->once())
- ->method('removeFromGroup')
- ->with('user1', 'group1');
-
- $backend2->expects($this->once())
- ->method('inGroup')
- ->with('user1', 'group1')
- ->willReturn(true);
- $backend2->expects($this->any())
- ->method('implementsActions')
- ->willReturn(true);
-
- $backend2->expects($this->once())
- ->method('removeFromGroup')
- ->with('user1', 'group1');
-
- $group->removeUser($this->newUser('user1', $userBackend));
- }
-
- public function testSearchUsers(): void {
- $backend = $this->getMockBuilder('OC\Group\Database')
- ->disableOriginalConstructor()
- ->getMock();
- $userManager = $this->getUserManager();
- $group = new Group('group1', [$backend], $this->dispatcher, $userManager);
-
- $backend->expects($this->once())
- ->method('searchInGroup')
- ->with('group1', '2')
- ->willReturn(['user2' => new User('user2', null, $this->dispatcher)]);
-
- $users = $group->searchUsers('2');
-
- $this->assertEquals(1, count($users));
- $user2 = reset($users);
- $this->assertEquals('user2', $user2->getUID());
- }
-
- public function testSearchUsersMultipleBackends(): void {
- $backend1 = $this->getMockBuilder('OC\Group\Database')
- ->disableOriginalConstructor()
- ->getMock();
- $backend2 = $this->getMockBuilder('OC\Group\Database')
- ->disableOriginalConstructor()
- ->getMock();
- $userManager = $this->getUserManager();
- $group = new Group('group1', [$backend1, $backend2], $this->dispatcher, $userManager);
-
- $backend1->expects($this->once())
- ->method('searchInGroup')
- ->with('group1', '2')
- ->willReturn(['user2' => new User('user2', null, $this->dispatcher)]);
- $backend2->expects($this->once())
- ->method('searchInGroup')
- ->with('group1', '2')
- ->willReturn(['user2' => new User('user2', null, $this->dispatcher)]);
-
- $users = $group->searchUsers('2');
-
- $this->assertEquals(1, count($users));
- $user2 = reset($users);
- $this->assertEquals('user2', $user2->getUID());
- }
-
- public function testSearchUsersLimitAndOffset(): void {
- $backend = $this->getMockBuilder('OC\Group\Database')
- ->disableOriginalConstructor()
- ->getMock();
- $userManager = $this->getUserManager();
- $group = new Group('group1', [$backend], $this->dispatcher, $userManager);
-
- $backend->expects($this->once())
- ->method('searchInGroup')
- ->with('group1', 'user', 1, 1)
- ->willReturn(['user2' => new User('user2', null, $this->dispatcher)]);
-
- $users = $group->searchUsers('user', 1, 1);
-
- $this->assertEquals(1, count($users));
- $user2 = reset($users);
- $this->assertEquals('user2', $user2->getUID());
- }
-
- public function testSearchUsersMultipleBackendsLimitAndOffset(): void {
- $backend1 = $this->getMockBuilder('OC\Group\Database')
- ->disableOriginalConstructor()
- ->getMock();
- $backend2 = $this->getMockBuilder('OC\Group\Database')
- ->disableOriginalConstructor()
- ->getMock();
- $userManager = $this->getUserManager();
- $group = new Group('group1', [$backend1, $backend2], $this->dispatcher, $userManager);
-
- $backend1->expects($this->once())
- ->method('searchInGroup')
- ->with('group1', 'user', 2, 1)
- ->willReturn(['user2' => new User('user2', null, $this->dispatcher)]);
- $backend2->expects($this->once())
- ->method('searchInGroup')
- ->with('group1', 'user', 2, 1)
- ->willReturn(['user1' => new User('user1', null, $this->dispatcher)]);
-
- $users = $group->searchUsers('user', 2, 1);
-
- $this->assertEquals(2, count($users));
- $user2 = reset($users);
- $user1 = next($users);
- $this->assertEquals('user2', $user2->getUID());
- $this->assertEquals('user1', $user1->getUID());
- }
-
- public function testCountUsers(): void {
- $backend1 = $this->getMockBuilder('OC\Group\Database')
- ->disableOriginalConstructor()
- ->getMock();
- $userManager = $this->getUserManager();
- $group = new Group('group1', [$backend1], $this->dispatcher, $userManager);
-
- $backend1->expects($this->once())
- ->method('countUsersInGroup')
- ->with('group1', '2')
- ->willReturn(3);
-
- $backend1->expects($this->any())
- ->method('implementsActions')
- ->willReturn(true);
-
- $users = $group->count('2');
-
- $this->assertSame(3, $users);
- }
-
- public function testCountUsersMultipleBackends(): void {
- $backend1 = $this->getMockBuilder('OC\Group\Database')
- ->disableOriginalConstructor()
- ->getMock();
- $backend2 = $this->getMockBuilder('OC\Group\Database')
- ->disableOriginalConstructor()
- ->getMock();
- $userManager = $this->getUserManager();
- $group = new Group('group1', [$backend1, $backend2], $this->dispatcher, $userManager);
-
- $backend1->expects($this->once())
- ->method('countUsersInGroup')
- ->with('group1', '2')
- ->willReturn(3);
- $backend1->expects($this->any())
- ->method('implementsActions')
- ->willReturn(true);
-
- $backend2->expects($this->once())
- ->method('countUsersInGroup')
- ->with('group1', '2')
- ->willReturn(4);
- $backend2->expects($this->any())
- ->method('implementsActions')
- ->willReturn(true);
-
- $users = $group->count('2');
-
- $this->assertSame(7, $users);
- }
-
- public function testCountUsersNoMethod(): void {
- $backend1 = $this->getMockBuilder('OC\Group\Database')
- ->disableOriginalConstructor()
- ->getMock();
- $userManager = $this->getUserManager();
- $group = new Group('group1', [$backend1], $this->dispatcher, $userManager);
-
- $backend1->expects($this->never())
- ->method('countUsersInGroup');
- $backend1->expects($this->any())
- ->method('implementsActions')
- ->willReturn(false);
-
- $users = $group->count('2');
-
- $this->assertSame(false, $users);
- }
-
- public function testSetDisplayNameDispatchesOldValue(): void {
- $backend = $this->getMockBuilder('OC\Group\Database')
- ->disableOriginalConstructor()
- ->getMock();
- $userManager = $this->getUserManager();
-
- $dispatcher = $this->createMock(IEventDispatcher::class);
- $invocation = 0;
- $dispatcher->expects($this->exactly(2))
- ->method('dispatchTyped')
- ->willReturnCallback(function ($event) use (&$invocation): void {
- $invocation++;
- if ($invocation === 1) {
- $this->assertInstanceOf(BeforeGroupChangedEvent::class, $event);
- $this->assertSame('displayName', $event->getFeature());
- $this->assertSame('New Name', $event->getValue());
- $this->assertSame('Old Name', $event->getOldValue());
- return;
- }
-
- $this->assertInstanceOf(GroupChangedEvent::class, $event);
- $this->assertSame('displayName', $event->getFeature());
- $this->assertSame('New Name', $event->getValue());
- $this->assertSame('Old Name', $event->getOldValue());
- });
-
- $backend->expects($this->once())
- ->method('setDisplayName')
- ->with('group1', 'New Name')
- ->willReturn(true);
-
- $group = new Group('group1', [$backend], $dispatcher, $userManager, null, 'Old Name');
- $this->assertTrue($group->setDisplayName('New Name'));
- }
-
- public function testDelete(): void {
- $backend = $this->getMockBuilder('OC\Group\Database')
- ->disableOriginalConstructor()
- ->getMock();
- $userManager = $this->getUserManager();
- $group = new Group('group1', [$backend], $this->dispatcher, $userManager);
-
- $backend->expects($this->once())
- ->method('deleteGroup')
- ->with('group1');
- $backend->expects($this->any())
- ->method('implementsActions')
- ->willReturn(true);
-
- $group->delete();
- }
-}
--- a/tests/lib/Group/HideFromCollaborationTest.php
+++ /dev/null
@@ -1,53 +0,0 @@
-userManager = $this->createMock(IUserManager::class);
- $this->dispatcher = $this->createMock(IEventDispatcher::class);
- }
-
- public function testHideFromCollaboration(): void {
- // Arrange
- $backend1 = $this->createMock(HideFromCollaborationBackendTest::class);
- $backend1->method('hideGroup')
- ->willReturn(false);
- $backend2 = $this->createMock(HideFromCollaborationBackendTest::class);
- $backend2->method('hideGroup')
- ->willReturn(true);
- $group = new Group('group1', [$backend1, $backend2], $this->dispatcher, $this->userManager);
-
- // Act
- $result = $group->hideFromCollaboration();
-
- // Assert
- $this->assertTrue($result);
- }
-}
--- a/tests/lib/Group/ManagerTest.php
+++ /dev/null
@@ -1,995 +0,0 @@
-userManager = $this->createMock(Manager::class);
- $this->dispatcher = $this->createMock(IEventDispatcher::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->cache = $this->createMock(ICacheFactory::class);
- $this->userSession = $this->createMock(IUserSession::class);
-
- $this->remoteIpAddress = $this->createMock(IRemoteAddress::class);
- $this->remoteIpAddress->method('allowsAdminActions')->willReturn(true);
- }
-
- private function getTestUser($userId) {
- $mockUser = $this->createMock(IUser::class);
- $mockUser->expects($this->any())
- ->method('getUID')
- ->willReturn($userId);
- $mockUser->expects($this->any())
- ->method('getDisplayName')
- ->willReturn($userId);
- return $mockUser;
- }
-
- /**
- * @param null|int $implementedActions
- * @return \PHPUnit\Framework\MockObject\MockObject
- */
- private function getTestBackend($implementedActions = null) {
- if ($implementedActions === null) {
- $implementedActions
- = GroupInterface::ADD_TO_GROUP
- | GroupInterface::REMOVE_FROM_GOUP
- | GroupInterface::COUNT_USERS
- | GroupInterface::CREATE_GROUP
- | GroupInterface::DELETE_GROUP;
- }
- // need to declare it this way due to optional methods
- // thanks to the implementsActions logic
- $backend = $this->getMockBuilder(TestBackend::class)
- ->disableOriginalConstructor()
- ->onlyMethods([
- 'getGroupDetails',
- 'implementsActions',
- 'getUserGroups',
- 'inGroup',
- 'getGroups',
- 'groupExists',
- 'groupsExists',
- 'usersInGroup',
- 'createGroup',
- 'addToGroup',
- 'removeFromGroup',
- 'searchInGroup',
- ])
- ->getMock();
- $backend->expects($this->any())
- ->method('implementsActions')
- ->willReturnCallback(function ($actions) use ($implementedActions) {
- return (bool)($actions & $implementedActions);
- });
- return $backend;
- }
-
- public function testGet(): void {
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend
- */
- $backend = $this->getTestBackend();
- $backend->expects($this->any())
- ->method('groupExists')
- ->with('group1')
- ->willReturn(true);
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend);
-
- $group = $manager->get('group1');
- $this->assertNotNull($group);
- $this->assertEquals('group1', $group->getGID());
- }
-
- public function testGetNoBackend(): void {
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
-
- $this->assertNull($manager->get('group1'));
- }
-
- public function testGetNotExists(): void {
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend
- */
- $backend = $this->getTestBackend();
- $backend->expects($this->once())
- ->method('groupExists')
- ->with('group1')
- ->willReturn(false);
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend);
-
- $this->assertNull($manager->get('group1'));
- }
-
- public function testGetDeleted(): void {
- $backend = new \Test\Util\Group\Dummy();
- $backend->createGroup('group1');
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend);
-
- $group = $manager->get('group1');
- $group->delete();
- $this->assertNull($manager->get('group1'));
- }
-
- public function testGetMultipleBackends(): void {
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend1
- */
- $backend1 = $this->getTestBackend();
- $backend1->expects($this->any())
- ->method('groupExists')
- ->with('group1')
- ->willReturn(false);
-
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend2
- */
- $backend2 = $this->getTestBackend();
- $backend2->expects($this->any())
- ->method('groupExists')
- ->with('group1')
- ->willReturn(true);
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend1);
- $manager->addBackend($backend2);
-
- $group = $manager->get('group1');
- $this->assertNotNull($group);
- $this->assertEquals('group1', $group->getGID());
- }
-
- public function testCreate(): void {
- /** @var \PHPUnit\Framework\MockObject\MockObject|\OC\Group\Backend $backend */
- $backendGroupCreated = false;
- $backend = $this->getTestBackend();
- $backend->expects($this->any())
- ->method('groupExists')
- ->with('group1')
- ->willReturnCallback(function () use (&$backendGroupCreated) {
- return $backendGroupCreated;
- });
- $backend->expects($this->once())
- ->method('createGroup')
- ->willReturnCallback(function () use (&$backendGroupCreated) {
- $backendGroupCreated = true;
- return true;
- });
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend);
-
- $group = $manager->createGroup('group1');
- $this->assertEquals('group1', $group->getGID());
- }
-
- public function testCreateFailure(): void {
- /** @var \PHPUnit\Framework\MockObject\MockObject|\OC\Group\Backend $backend */
- $backendGroupCreated = false;
- $backend = $this->getTestBackend(
- GroupInterface::ADD_TO_GROUP
- | GroupInterface::REMOVE_FROM_GOUP
- | GroupInterface::COUNT_USERS
- | GroupInterface::CREATE_GROUP
- | GroupInterface::DELETE_GROUP
- | GroupInterface::GROUP_DETAILS
- );
- $backend->expects($this->any())
- ->method('groupExists')
- ->with('group1')
- ->willReturn(false);
- $backend->expects($this->once())
- ->method('createGroup')
- ->willReturn(false);
- $backend->expects($this->once())
- ->method('getGroupDetails')
- ->willReturn([]);
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend);
-
- $group = $manager->createGroup('group1');
- $this->assertEquals(null, $group);
- }
-
- public function testCreateTooLong(): void {
- /** @var \PHPUnit\Framework\MockObject\MockObject|\OC\Group\Backend $backend */
- $backendGroupCreated = false;
- $backend = $this->getTestBackend(
- GroupInterface::ADD_TO_GROUP
- | GroupInterface::REMOVE_FROM_GOUP
- | GroupInterface::COUNT_USERS
- | GroupInterface::CREATE_GROUP
- | GroupInterface::DELETE_GROUP
- | GroupInterface::GROUP_DETAILS
- );
- $groupName = str_repeat('x', 256);
- $backend->expects($this->any())
- ->method('groupExists')
- ->with($groupName)
- ->willReturn(false);
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend);
-
- $this->expectException(\Exception::class);
- $group = $manager->createGroup($groupName);
- }
-
- public function testCreateExists(): void {
- /** @var \PHPUnit\Framework\MockObject\MockObject|\OC\Group\Backend $backend */
- $backend = $this->getTestBackend();
- $backend->expects($this->any())
- ->method('groupExists')
- ->with('group1')
- ->willReturn(true);
- $backend->expects($this->never())
- ->method('createGroup');
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend);
-
- $group = $manager->createGroup('group1');
- $this->assertEquals('group1', $group->getGID());
- }
-
- public function testSearch(): void {
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend
- */
- $backend = $this->getTestBackend();
- $backend->expects($this->once())
- ->method('getGroups')
- ->with('1')
- ->willReturn(['group1']);
- $backend->expects($this->once())
- ->method('getGroupDetails')
- ->willReturnMap([
- ['group1', ['displayName' => 'group1']],
- ]);
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend);
-
- $groups = $manager->search('1');
- $this->assertCount(1, $groups);
- $group1 = reset($groups);
- $this->assertEquals('group1', $group1->getGID());
- }
-
- public function testSearchMultipleBackends(): void {
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend1
- */
- $backend1 = $this->getTestBackend();
- $backend1->expects($this->once())
- ->method('getGroups')
- ->with('1')
- ->willReturn(['group1']);
- $backend1->expects($this->any())
- ->method('getGroupDetails')
- ->willReturnMap([
- ['group1', ['displayName' => 'group1']],
- ['group12', []],
- ]);
-
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend2
- */
- $backend2 = $this->getTestBackend();
- $backend2->expects($this->once())
- ->method('getGroups')
- ->with('1')
- ->willReturn(['group12', 'group1']);
- $backend2->expects($this->any())
- ->method('getGroupDetails')
- ->willReturnMap([
- ['group12', ['displayName' => 'group12']],
- ['group1', ['displayName' => 'group1']],
- ]);
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend1);
- $manager->addBackend($backend2);
-
- $groups = $manager->search('1');
- $this->assertCount(2, $groups);
- $group1 = reset($groups);
- $group12 = next($groups);
- $this->assertEquals('group1', $group1->getGID());
- $this->assertEquals('group12', $group12->getGID());
- }
-
- public function testSearchMultipleBackendsLimitAndOffset(): void {
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject|\OC\Group\Backend $backend1
- */
- $backend1 = $this->getTestBackend();
- $backend1->expects($this->once())
- ->method('getGroups')
- ->with('1', 2, 1)
- ->willReturn(['group1']);
- $backend1->expects($this->any())
- ->method('getGroupDetails')
- ->willReturnMap([
- [1, []],
- [2, []],
- ['group1', ['displayName' => 'group1']],
- ['group12', []],
- ]);
-
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject|\OC\Group\Backend $backend2
- */
- $backend2 = $this->getTestBackend();
- $backend2->expects($this->once())
- ->method('getGroups')
- ->with('1', 2, 1)
- ->willReturn(['group12']);
- $backend2->expects($this->any())
- ->method('getGroupDetails')
- ->willReturnMap([
- [1, []],
- [2, []],
- ['group1', []],
- ['group12', ['displayName' => 'group12']],
- ]);
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend1);
- $manager->addBackend($backend2);
-
- $groups = $manager->search('1', 2, 1);
- $this->assertCount(2, $groups);
- $group1 = reset($groups);
- $group12 = next($groups);
- $this->assertEquals('group1', $group1->getGID());
- $this->assertEquals('group12', $group12->getGID());
- }
-
- public function testSearchResultExistsButGroupDoesNot(): void {
- /** @var \PHPUnit\Framework\MockObject\MockObject|\OC\Group\Backend $backend */
- $backend = $this->createMock(Database::class);
- $backend->expects($this->once())
- ->method('getGroups')
- ->with('1')
- ->willReturn(['group1']);
- $backend->expects($this->never())
- ->method('groupExists');
- $backend->expects($this->once())
- ->method('getGroupsDetails')
- ->with(['group1'])
- ->willReturn([]);
-
- /** @var \OC\User\Manager $userManager */
- $userManager = $this->createMock(Manager::class);
-
- $manager = new \OC\Group\Manager($userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend);
-
- $groups = $manager->search('1');
- $this->assertEmpty($groups);
- }
-
- public function testGetUserGroups(): void {
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend
- */
- $backend = $this->getTestBackend();
- $backend->expects($this->once())
- ->method('getUserGroups')
- ->with('user1')
- ->willReturn(['group1']);
- $backend->expects($this->any())
- ->method('getGroupDetails')
- ->with('group1')
- ->willReturn(['displayName' => 'group1']);
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend);
-
- $groups = $manager->getUserGroups($this->getTestUser('user1'));
- $this->assertCount(1, $groups);
- $group1 = reset($groups);
- $this->assertEquals('group1', $group1->getGID());
- }
-
- public function testGetUserGroupIds(): void {
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend
- */
- $backend = $this->getTestBackend();
- $backend->method('getUserGroups')
- ->with('myUID')
- ->willReturn(['123', 'abc']);
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend);
-
- /** @var User|\PHPUnit\Framework\MockObject\MockObject $user */
- $user = $this->createMock(IUser::class);
- $user->method('getUID')
- ->willReturn('myUID');
-
- $groups = $manager->getUserGroupIds($user);
- $this->assertCount(2, $groups);
-
- foreach ($groups as $group) {
- $this->assertIsString($group);
- }
- }
-
- public function testGetUserGroupsWithDeletedGroup(): void {
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend
- */
- $backend = $this->createMock(Database::class);
- $backend->expects($this->once())
- ->method('getUserGroups')
- ->with('user1')
- ->willReturn(['group1']);
- $backend->expects($this->any())
- ->method('getGroupsDetails')
- ->with(['group1'])
- ->willReturn(['group1' => []]);
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend);
-
- /** @var User|\PHPUnit\Framework\MockObject\MockObject $user */
- $user = $this->createMock(IUser::class);
- $user->expects($this->atLeastOnce())
- ->method('getUID')
- ->willReturn('user1');
-
- $groups = $manager->getUserGroups($user);
- $this->assertEmpty($groups);
- }
-
- public function testInGroup(): void {
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend
- */
- $backend = $this->getTestBackend();
- $backend->expects($this->once())
- ->method('getUserGroups')
- ->with('user1')
- ->willReturn(['group1', 'admin', 'group2']);
- $backend->expects($this->any())
- ->method('groupExists')
- ->willReturn(true);
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend);
-
- $this->assertTrue($manager->isInGroup('user1', 'group1'));
- }
-
- public function testIsAdmin(): void {
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend
- */
- $backend = $this->getTestBackend();
- $backend->expects($this->once())
- ->method('getUserGroups')
- ->with('user1')
- ->willReturn(['group1', 'admin', 'group2']);
- $backend->expects($this->any())
- ->method('groupExists')
- ->willReturn(true);
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend);
-
- $this->assertTrue($manager->isAdmin('user1'));
- }
-
- public function testNotAdmin(): void {
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend
- */
- $backend = $this->getTestBackend();
- $backend->expects($this->once())
- ->method('getUserGroups')
- ->with('user1')
- ->willReturn(['group1', 'group2']);
- $backend->expects($this->any())
- ->method('groupExists')
- ->willReturn(true);
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend);
-
- $this->assertFalse($manager->isAdmin('user1'));
- }
-
- public function testGetUserGroupsMultipleBackends(): void {
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend1
- */
- $backend1 = $this->getTestBackend();
- $backend1->expects($this->once())
- ->method('getUserGroups')
- ->with('user1')
- ->willReturn(['group1']);
- $backend1->expects($this->any())
- ->method('getGroupDetails')
- ->willReturnCallback(fn ($gid) => $gid === 'group1' ? ['displayName' => 'group1'] : []);
-
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend2
- */
- $backend2 = $this->getTestBackend();
- $backend2->expects($this->once())
- ->method('getUserGroups')
- ->with('user1')
- ->willReturn(['group1', 'group2']);
- $backend2->expects($this->any())
- ->method('getGroupDetails')
- ->willReturnCallback(fn ($gid) => ['displayName' => $gid]);
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend1);
- $manager->addBackend($backend2);
-
- $groups = $manager->getUserGroups($this->getTestUser('user1'));
- $this->assertCount(2, $groups);
- $group1 = reset($groups);
- $group2 = next($groups);
- $this->assertEquals('group1', $group1->getGID());
- $this->assertEquals('group2', $group2->getGID());
- }
-
- public function testDisplayNamesInGroupWithOneUserBackend(): void {
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend
- */
- $backend = $this->getTestBackend();
- $backend->expects($this->exactly(1))
- ->method('groupExists')
- ->with('testgroup')
- ->willReturn(true);
-
- $backend->expects($this->any())
- ->method('inGroup')
- ->willReturnCallback(function ($uid, $gid) {
- switch ($uid) {
- case 'user1': return false;
- case 'user2': return true;
- case 'user3': return false;
- case 'user33': return true;
- default:
- return null;
- }
- });
-
- $this->userManager->expects($this->any())
- ->method('searchDisplayName')
- ->with('user3')
- ->willReturnCallback(function ($search, $limit, $offset) {
- switch ($offset) {
- case 0: return ['user3' => $this->getTestUser('user3'),
- 'user33' => $this->getTestUser('user33')];
- case 2: return [];
- }
- return null;
- });
- $this->userManager->expects($this->any())
- ->method('get')
- ->willReturnCallback(function ($uid) {
- switch ($uid) {
- case 'user1': return $this->getTestUser('user1');
- case 'user2': return $this->getTestUser('user2');
- case 'user3': return $this->getTestUser('user3');
- case 'user33': return $this->getTestUser('user33');
- default:
- return null;
- }
- });
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend);
-
- $users = $manager->displayNamesInGroup('testgroup', 'user3');
- $this->assertCount(1, $users);
- $this->assertFalse(isset($users['user1']));
- $this->assertFalse(isset($users['user2']));
- $this->assertFalse(isset($users['user3']));
- $this->assertTrue(isset($users['user33']));
- }
-
- public function testDisplayNamesInGroupWithOneUserBackendWithLimitSpecified(): void {
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend
- */
- $backend = $this->getTestBackend();
- $backend->expects($this->exactly(1))
- ->method('groupExists')
- ->with('testgroup')
- ->willReturn(true);
-
- $backend->expects($this->any())
- ->method('inGroup')
- ->willReturnCallback(function ($uid, $gid) {
- switch ($uid) {
- case 'user1': return false;
- case 'user2': return true;
- case 'user3': return false;
- case 'user33': return true;
- case 'user333': return true;
- default:
- return null;
- }
- });
-
- $this->userManager->expects($this->any())
- ->method('searchDisplayName')
- ->with('user3')
- ->willReturnCallback(function ($search, $limit, $offset) {
- switch ($offset) {
- case 0: return ['user3' => $this->getTestUser('user3'),
- 'user33' => $this->getTestUser('user33')];
- case 2: return ['user333' => $this->getTestUser('user333')];
- }
- return null;
- });
- $this->userManager->expects($this->any())
- ->method('get')
- ->willReturnCallback(function ($uid) {
- switch ($uid) {
- case 'user1': return $this->getTestUser('user1');
- case 'user2': return $this->getTestUser('user2');
- case 'user3': return $this->getTestUser('user3');
- case 'user33': return $this->getTestUser('user33');
- case 'user333': return $this->getTestUser('user333');
- default:
- return null;
- }
- });
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend);
-
- $users = $manager->displayNamesInGroup('testgroup', 'user3', 1);
- $this->assertCount(1, $users);
- $this->assertFalse(isset($users['user1']));
- $this->assertFalse(isset($users['user2']));
- $this->assertFalse(isset($users['user3']));
- $this->assertTrue(isset($users['user33']));
- $this->assertFalse(isset($users['user333']));
- }
-
- public function testDisplayNamesInGroupWithOneUserBackendWithLimitAndOffsetSpecified(): void {
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend
- */
- $backend = $this->getTestBackend();
- $backend->expects($this->exactly(1))
- ->method('groupExists')
- ->with('testgroup')
- ->willReturn(true);
-
- $backend->expects($this->any())
- ->method('inGroup')
- ->willReturnCallback(function ($uid) {
- switch ($uid) {
- case 'user1': return false;
- case 'user2': return true;
- case 'user3': return false;
- case 'user33': return true;
- case 'user333': return true;
- default:
- return null;
- }
- });
-
- $this->userManager->expects($this->any())
- ->method('searchDisplayName')
- ->with('user3')
- ->willReturnCallback(function ($search, $limit, $offset) {
- switch ($offset) {
- case 0:
- return [
- 'user3' => $this->getTestUser('user3'),
- 'user33' => $this->getTestUser('user33'),
- 'user333' => $this->getTestUser('user333')
- ];
- }
- return null;
- });
- $this->userManager->expects($this->any())
- ->method('get')
- ->willReturnCallback(function ($uid) {
- switch ($uid) {
- case 'user1': return $this->getTestUser('user1');
- case 'user2': return $this->getTestUser('user2');
- case 'user3': return $this->getTestUser('user3');
- case 'user33': return $this->getTestUser('user33');
- case 'user333': return $this->getTestUser('user333');
- default:
- return null;
- }
- });
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend);
-
- $users = $manager->displayNamesInGroup('testgroup', 'user3', 1, 1);
- $this->assertCount(1, $users);
- $this->assertFalse(isset($users['user1']));
- $this->assertFalse(isset($users['user2']));
- $this->assertFalse(isset($users['user3']));
- $this->assertFalse(isset($users['user33']));
- $this->assertTrue(isset($users['user333']));
- }
-
- public function testDisplayNamesInGroupWithOneUserBackendAndSearchEmpty(): void {
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject|\OC\Group\Backend $backend
- */
- $backend = $this->getTestBackend();
- $backend->expects($this->exactly(1))
- ->method('groupExists')
- ->with('testgroup')
- ->willReturn(true);
-
- $backend->expects($this->once())
- ->method('searchInGroup')
- ->with('testgroup', '', -1, 0)
- ->willReturn(['user2' => $this->getTestUser('user2'), 'user33' => $this->getTestUser('user33')]);
-
- $this->userManager->expects($this->never())->method('get');
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend);
-
- $users = $manager->displayNamesInGroup('testgroup', '');
- $this->assertCount(2, $users);
- $this->assertFalse(isset($users['user1']));
- $this->assertTrue(isset($users['user2']));
- $this->assertFalse(isset($users['user3']));
- $this->assertTrue(isset($users['user33']));
- }
-
- public function testDisplayNamesInGroupWithOneUserBackendAndSearchEmptyAndLimitSpecified(): void {
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend
- */
- $backend = $this->getTestBackend();
- $backend->expects($this->exactly(1))
- ->method('groupExists')
- ->with('testgroup')
- ->willReturn(true);
-
- $backend->expects($this->once())
- ->method('searchInGroup')
- ->with('testgroup', '', 1, 0)
- ->willReturn([new User('user2', null, $this->dispatcher)]);
-
- $this->userManager->expects($this->never())->method('get');
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend);
-
- $users = $manager->displayNamesInGroup('testgroup', '', 1);
- $this->assertCount(1, $users);
- $this->assertFalse(isset($users['user1']));
- $this->assertTrue(isset($users['user2']));
- $this->assertFalse(isset($users['user3']));
- $this->assertFalse(isset($users['user33']));
- }
-
- public function testDisplayNamesInGroupWithOneUserBackendAndSearchEmptyAndLimitAndOffsetSpecified(): void {
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend
- */
- $backend = $this->getTestBackend();
- $backend->expects($this->exactly(1))
- ->method('groupExists')
- ->with('testgroup')
- ->willReturn(true);
-
- $backend->expects($this->once())
- ->method('searchInGroup')
- ->with('testgroup', '', 1, 1)
- ->willReturn(['user33' => $this->getTestUser('user33')]);
-
- $this->userManager->expects($this->never())->method('get');
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend);
-
- $users = $manager->displayNamesInGroup('testgroup', '', 1, 1);
- $this->assertCount(1, $users);
- $this->assertFalse(isset($users['user1']));
- $this->assertFalse(isset($users['user2']));
- $this->assertFalse(isset($users['user3']));
- $this->assertTrue(isset($users['user33']));
- }
-
- public function testGetUserGroupsWithAddUser(): void {
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend
- */
- $backend = $this->getTestBackend();
- $expectedGroups = [];
- $backend->expects($this->any())
- ->method('getUserGroups')
- ->with('user1')
- ->willReturnCallback(function () use (&$expectedGroups) {
- return $expectedGroups;
- });
- $backend->expects($this->any())
- ->method('groupExists')
- ->with('group1')
- ->willReturn(true);
- $backend->expects($this->any())
- ->method('getGroupDetails')
- ->with('group1')
- ->willReturn(['displayName' => 'group1']);
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend);
-
- // prime cache
- $user1 = $this->getTestUser('user1');
- $groups = $manager->getUserGroups($user1);
- $this->assertEquals([], $groups);
-
- // add user
- $group = $manager->get('group1');
- $group->addUser($user1);
- $expectedGroups[] = 'group1';
-
- // check result
- $groups = $manager->getUserGroups($user1);
- $this->assertCount(1, $groups);
- $group1 = reset($groups);
- $this->assertEquals('group1', $group1->getGID());
- }
-
- public function testGetUserGroupsWithRemoveUser(): void {
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend
- */
- $backend = $this->getTestBackend();
- $expectedGroups = ['group1'];
- $backend->expects($this->any())
- ->method('getUserGroups')
- ->with('user1')
- ->willReturnCallback(function () use (&$expectedGroups) {
- return $expectedGroups;
- });
- $backend->expects($this->any())
- ->method('getGroupDetails')
- ->with('group1')
- ->willReturn(['displayName' => 'group1']);
- $backend->expects($this->once())
- ->method('inGroup')
- ->willReturn(true);
- $backend->expects($this->once())
- ->method('removeFromGroup')
- ->willReturn(true);
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend);
-
- // prime cache
- $user1 = $this->getTestUser('user1');
- $groups = $manager->getUserGroups($user1);
- $this->assertCount(1, $groups);
- $group1 = reset($groups);
- $this->assertEquals('group1', $group1->getGID());
-
- // remove user
- $group = $manager->get('group1');
- $group->removeUser($user1);
- $expectedGroups = [];
-
- // check result
- $groups = $manager->getUserGroups($user1);
- $this->assertEquals($expectedGroups, $groups);
- }
-
- public function testGetUserIdGroups(): void {
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend
- */
- $backend = $this->getTestBackend();
- $backend->expects($this->any())
- ->method('getUserGroups')
- ->with('user1')
- ->willReturn(null);
-
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend);
-
- $groups = $manager->getUserIdGroups('user1');
- $this->assertEquals([], $groups);
- }
-
- public function testGroupDisplayName(): void {
- /**
- * @var \PHPUnit\Framework\MockObject\MockObject | \OC\Group\Backend $backend
- */
- $backend = $this->getTestBackend(
- GroupInterface::ADD_TO_GROUP
- | GroupInterface::REMOVE_FROM_GOUP
- | GroupInterface::COUNT_USERS
- | GroupInterface::CREATE_GROUP
- | GroupInterface::DELETE_GROUP
- | GroupInterface::GROUP_DETAILS
- );
- $backend->expects($this->any())
- ->method('getGroupDetails')
- ->willReturnMap([
- ['group1', ['gid' => 'group1', 'displayName' => 'Group One']],
- ['group2', ['gid' => 'group2']],
- ]);
- $manager = new \OC\Group\Manager($this->userManager, $this->dispatcher, $this->logger, $this->cache, $this->remoteIpAddress, $this->userSession);
- $manager->addBackend($backend);
-
- // group with display name
- $group = $manager->get('group1');
- $this->assertNotNull($group);
- $this->assertEquals('group1', $group->getGID());
- $this->assertEquals('Group One', $group->getDisplayName());
-
- // group without display name
- $group = $manager->get('group2');
- $this->assertNotNull($group);
- $this->assertEquals('group2', $group->getGID());
- $this->assertEquals('group2', $group->getDisplayName());
- }
-}
--- a/tests/lib/Group/MetaDataTest.php
+++ /dev/null
@@ -1,121 +0,0 @@
-groupManager = $this->getMockBuilder('\OC\Group\Manager')
- ->disableOriginalConstructor()
- ->getMock();
- $this->userSession = $this->createMock(IUserSession::class);
- $this->groupMetadata = new MetaData(
- 'foo',
- $this->isAdmin,
- $this->isDelegatedAdmin,
- $this->groupManager,
- $this->userSession
- );
- }
-
- private function getGroupMock($countCallCount = 0) {
- $group = $this->getMockBuilder('\OC\Group\Group')
- ->disableOriginalConstructor()
- ->getMock();
-
- $group->expects($this->exactly(6))
- ->method('getGID')->willReturnOnConsecutiveCalls('admin', 'admin', 'g2', 'g2', 'g3', 'g3');
-
- $group->expects($this->exactly(3))
- ->method('getDisplayName')->willReturnOnConsecutiveCalls('admin', 'g2', 'g3');
-
- $group->expects($this->exactly($countCallCount))
- ->method('count')
- ->with('')->willReturnOnConsecutiveCalls(2, 3, 5);
-
- return $group;
- }
-
- public function testGet(): void {
- $group = $this->getGroupMock();
- $groups = array_fill(0, 3, $group);
-
- $this->groupManager->expects($this->once())
- ->method('search')
- ->with('')
- ->willReturn($groups);
-
- [$adminGroups, $ordinaryGroups] = $this->groupMetadata->get();
-
- $this->assertSame(1, count($adminGroups));
- $this->assertSame(2, count($ordinaryGroups));
-
- $this->assertSame('g2', $ordinaryGroups[0]['name']);
- // user count is not loaded
- $this->assertSame(0, $ordinaryGroups[0]['usercount']);
- }
-
- public function testGetWithSorting(): void {
- $this->groupMetadata->setSorting(1);
- $group = $this->getGroupMock(3);
- $groups = array_fill(0, 3, $group);
-
- $this->groupManager->expects($this->once())
- ->method('search')
- ->with('')
- ->willReturn($groups);
-
- [$adminGroups, $ordinaryGroups] = $this->groupMetadata->get();
-
- $this->assertSame(1, count($adminGroups));
- $this->assertSame(2, count($ordinaryGroups));
-
- $this->assertSame('g3', $ordinaryGroups[0]['name']);
- $this->assertSame(5, $ordinaryGroups[0]['usercount']);
- }
-
- public function testGetWithCache(): void {
- $group = $this->getGroupMock();
- $groups = array_fill(0, 3, $group);
-
- $this->groupManager->expects($this->once())
- ->method('search')
- ->with('')
- ->willReturn($groups);
-
- //two calls, if caching fails call counts for group and groupmanager
- //are exceeded
- $this->groupMetadata->get();
- $this->groupMetadata->get();
- }
-
- //get() does not need to be tested with search parameters, because they are
- //solely and only passed to GroupManager and Group.
-
- public function testGetGroupsAsAdmin(): void {
- $this->groupManager
- ->expects($this->once())
- ->method('search')
- ->with('Foo')
- ->willReturn(['DummyValue']);
-
- $expected = ['DummyValue'];
- $this->assertSame($expected, $this->invokePrivate($this->groupMetadata, 'getGroups', ['Foo']));
- }
-}
--- a/tests/lib/HelperStorageTest.php
+++ /dev/null
@@ -1,252 +0,0 @@
-user = $this->getUniqueID('user_');
- $this->createUser($this->user, $this->user);
- $this->savedQuotaIncludeExternalStorage = $this->getIncludeExternalStorage();
-
- Filesystem::tearDown();
- \OC_User::setUserId($this->user);
- Filesystem::init($this->user, '/' . $this->user . '/files');
-
- /** @var IMountManager $manager */
- $manager = Server::get(IMountManager::class);
- $manager->removeMount('/' . $this->user);
-
- $this->storageMock = null;
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->setIncludeExternalStorage($this->savedQuotaIncludeExternalStorage);
- $this->user = null;
-
- if ($this->storageMock) {
- $this->storageMock->getCache()->clear();
- $this->storageMock = null;
- }
- Filesystem::tearDown();
-
- \OC_User::setUserId('');
- Server::get(IConfig::class)->deleteAllUserValues($this->user);
-
- parent::tearDown();
- }
-
- /**
- * Returns a storage mock that returns the given value as
- * free space
- *
- * @param int $freeSpace free space value
- * @return Storage
- */
- private function getStorageMock($freeSpace = 12) {
- $this->storageMock = $this->getMockBuilder(Temporary::class)
- ->onlyMethods(['free_space'])
- ->setConstructorArgs([[]])
- ->getMock();
-
- $this->storageMock->expects($this->once())
- ->method('free_space')
- ->willReturn($freeSpace);
- return $this->storageMock;
- }
-
- /**
- * Test getting the storage info
- */
- public function testGetStorageInfo(): void {
- $homeStorage = $this->getStorageMock(12);
- Filesystem::mount($homeStorage, [], '/' . $this->user . '/files');
- $homeStorage->file_put_contents('test.txt', '01234');
-
- $storageInfo = \OC_Helper::getStorageInfo('');
- $this->assertEquals(12, $storageInfo['free']);
- $this->assertEquals(5, $storageInfo['used']);
- $this->assertEquals(17, $storageInfo['total']);
- }
-
- private function getIncludeExternalStorage(): bool {
- $class = new \ReflectionClass(\OC_Helper::class);
- $prop = $class->getProperty('quotaIncludeExternalStorage');
- return $prop->getValue(null) ?? false;
- }
-
- private function setIncludeExternalStorage(bool $include) {
- $class = new \ReflectionClass(\OC_Helper::class);
- $prop = $class->getProperty('quotaIncludeExternalStorage');
- $prop->setValue(null, $include);
- }
-
- /**
- * Test getting the storage info, ignoring extra mount points
- */
- public function testGetStorageInfoExcludingExtStorage(): void {
- $homeStorage = $this->getStorageMock(12);
- Filesystem::mount($homeStorage, [], '/' . $this->user . '/files');
- $homeStorage->file_put_contents('test.txt', '01234');
-
- $extStorage = new Temporary([]);
- $extStorage->file_put_contents('extfile.txt', 'abcdefghijklmnopq');
- $extStorage->getScanner()->scan(''); // update root size
-
- $this->setIncludeExternalStorage(false);
-
- Filesystem::mount($extStorage, [], '/' . $this->user . '/files/ext');
-
- $storageInfo = \OC_Helper::getStorageInfo('');
- $this->assertEquals(12, $storageInfo['free']);
- $this->assertEquals(5, $storageInfo['used']);
- $this->assertEquals(17, $storageInfo['total']);
- }
-
- /**
- * Test getting the storage info, including extra mount points
- */
- public function testGetStorageInfoIncludingExtStorage(): void {
- $homeStorage = new Temporary([]);
- Filesystem::mount($homeStorage, [], '/' . $this->user . '/files');
- $homeStorage->file_put_contents('test.txt', '01234');
-
- $extStorage = new Temporary([]);
- $extStorage->file_put_contents('extfile.txt', 'abcdefghijklmnopq');
- $extStorage->getScanner()->scan(''); // update root size
-
- Filesystem::mount($extStorage, [], '/' . $this->user . '/files/ext');
-
- $this->setIncludeExternalStorage(true);
-
- $config = Server::get(IConfig::class);
- $config->setUserValue($this->user, 'files', 'quota', '25');
-
- $storageInfo = \OC_Helper::getStorageInfo('');
- $this->assertEquals(3, $storageInfo['free']);
- $this->assertEquals(22, $storageInfo['used']);
- $this->assertEquals(25, $storageInfo['total']);
-
- $config->setUserValue($this->user, 'files', 'quota', 'default');
- }
-
- /**
- * Test getting the storage info excluding extra mount points
- * when user has no quota set, even when quota ext storage option
- * was set
- */
- public function testGetStorageInfoIncludingExtStorageWithNoUserQuota(): void {
- $homeStorage = $this->getStorageMock(12);
- Filesystem::mount($homeStorage, [], '/' . $this->user . '/files');
- $homeStorage->file_put_contents('test.txt', '01234');
-
- $extStorage = new Temporary([]);
- $extStorage->file_put_contents('extfile.txt', 'abcdefghijklmnopq');
- $extStorage->getScanner()->scan(''); // update root size
-
- Filesystem::mount($extStorage, [], '/' . $this->user . '/files/ext');
-
- $config = Server::get(IConfig::class);
- $this->setIncludeExternalStorage(true);
-
- $storageInfo = \OC_Helper::getStorageInfo('');
- $this->assertEquals(12, $storageInfo['free'], '12 bytes free in home storage');
- $this->assertEquals(22, $storageInfo['used'], '5 bytes of home storage and 17 bytes of the temporary storage are used');
- $this->assertEquals(34, $storageInfo['total'], '5 bytes used and 12 bytes free in home storage as well as 17 bytes used in temporary storage');
- }
-
- /**
- * Test getting the storage info with quota enabled
- */
- public function testGetStorageInfoWithQuota(): void {
- $homeStorage = $this->getStorageMock(12);
- $homeStorage->file_put_contents('test.txt', '01234');
- $homeStorage = new Quota(
- [
- 'storage' => $homeStorage,
- 'quota' => 7
- ]
- );
- Filesystem::mount($homeStorage, [], '/' . $this->user . '/files');
-
- $storageInfo = \OC_Helper::getStorageInfo('');
- $this->assertEquals(2, $storageInfo['free']);
- $this->assertEquals(5, $storageInfo['used']);
- $this->assertEquals(7, $storageInfo['total']);
- }
-
- /**
- * Test getting the storage info when data exceeds quota
- */
- public function testGetStorageInfoWhenSizeExceedsQuota(): void {
- $homeStorage = $this->getStorageMock(12);
- $homeStorage->file_put_contents('test.txt', '0123456789');
- $homeStorage = new Quota(
- [
- 'storage' => $homeStorage,
- 'quota' => 7
- ]
- );
- Filesystem::mount($homeStorage, [], '/' . $this->user . '/files');
-
- $storageInfo = \OC_Helper::getStorageInfo('');
- $this->assertEquals(0, $storageInfo['free']);
- $this->assertEquals(10, $storageInfo['used']);
- // total = quota
- $this->assertEquals(7, $storageInfo['total']);
- }
-
- /**
- * Test getting the storage info when the remaining
- * free storage space is less than the quota
- */
- public function testGetStorageInfoWhenFreeSpaceLessThanQuota(): void {
- $homeStorage = $this->getStorageMock(12);
- $homeStorage->file_put_contents('test.txt', '01234');
- $homeStorage = new Quota(
- [
- 'storage' => $homeStorage,
- 'quota' => 18
- ]
- );
- Filesystem::mount($homeStorage, [], '/' . $this->user . '/files');
-
- $storageInfo = \OC_Helper::getStorageInfo('');
- $this->assertEquals(12, $storageInfo['free']);
- $this->assertEquals(5, $storageInfo['used']);
- // total = free + used (because quota > total)
- $this->assertEquals(17, $storageInfo['total']);
- }
-}
--- a/tests/lib/HookHelper.php
+++ /dev/null
@@ -1,115 +0,0 @@
- Filesystem::signal_create,
- 'params' => $params
- ];
- }
-
- public static function updateCallback($params) {
- self::$hookCalls[] = [
- 'signal' => Filesystem::signal_update,
- 'params' => $params
- ];
- }
-
- public static function writeCallback($params) {
- self::$hookCalls[] = [
- 'signal' => Filesystem::signal_write,
- 'params' => $params
- ];
- }
-
- public static function postCreateCallback($params) {
- self::$hookCalls[] = [
- 'signal' => Filesystem::signal_post_create,
- 'params' => $params
- ];
- }
-
- public static function postUpdateCallback($params) {
- self::$hookCalls[] = [
- 'signal' => Filesystem::signal_post_update,
- 'params' => $params
- ];
- }
-
- public static function postWriteCallback($params) {
- self::$hookCalls[] = [
- 'signal' => Filesystem::signal_post_write,
- 'params' => $params
- ];
- }
-
- /**
- * Callback that sets the run parameter to false
- */
- public static function cancellingCallback($params) {
- $params[Filesystem::signal_param_run] = false;
- }
-}
--- a/tests/lib/Hooks/BasicEmitterTest.php
+++ /dev/null
@@ -1,275 +0,0 @@
-emit($scope, $method, $arguments);
- }
-}
-
-/**
- * Class EmittedException
- *
- * a dummy exception so we can check if an event is emitted
- *
- * @package Test\Hooks
- */
-class EmittedException extends \Exception {
-}
-
-class BasicEmitterTest extends \Test\TestCase {
- /**
- * @var Emitter $emitter
- */
- protected $emitter;
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
- $this->emitter = new DummyEmitter();
- }
-
- public function nonStaticCallBack() {
- throw new EmittedException;
- }
-
- public static function staticCallBack() {
- throw new EmittedException;
- }
-
- public function testAnonymousFunction(): void {
- $this->expectException(\Test\Hooks\EmittedException::class);
-
- $this->emitter->listen('Test', 'test', function (): void {
- throw new EmittedException;
- });
- $this->emitter->emitEvent('Test', 'test');
- }
-
- public function testStaticCallback(): void {
- $this->expectException(\Test\Hooks\EmittedException::class);
-
- $this->emitter->listen('Test', 'test', ['\Test\Hooks\BasicEmitterTest', 'staticCallBack']);
- $this->emitter->emitEvent('Test', 'test');
- }
-
- public function testNonStaticCallback(): void {
- $this->expectException(\Test\Hooks\EmittedException::class);
-
- $this->emitter->listen('Test', 'test', [$this, 'nonStaticCallBack']);
- $this->emitter->emitEvent('Test', 'test');
- }
-
- public function testOnlyCallOnce(): void {
- $count = 0;
- $listener = function () use (&$count): void {
- $count++;
- };
- $this->emitter->listen('Test', 'test', $listener);
- $this->emitter->listen('Test', 'test', $listener);
- $this->emitter->emitEvent('Test', 'test');
- $this->assertEquals(1, $count, 'Listener called an invalid number of times (' . $count . ') expected 1');
- }
-
- public function testDifferentMethods(): void {
- $count = 0;
- $listener = function () use (&$count): void {
- $count++;
- };
- $this->emitter->listen('Test', 'test', $listener);
- $this->emitter->listen('Test', 'foo', $listener);
- $this->emitter->emitEvent('Test', 'test');
- $this->emitter->emitEvent('Test', 'foo');
- $this->assertEquals(2, $count, 'Listener called an invalid number of times (' . $count . ') expected 2');
- }
-
- public function testDifferentScopes(): void {
- $count = 0;
- $listener = function () use (&$count): void {
- $count++;
- };
- $this->emitter->listen('Test', 'test', $listener);
- $this->emitter->listen('Bar', 'test', $listener);
- $this->emitter->emitEvent('Test', 'test');
- $this->emitter->emitEvent('Bar', 'test');
- $this->assertEquals(2, $count, 'Listener called an invalid number of times (' . $count . ') expected 2');
- }
-
- public function testDifferentCallbacks(): void {
- $count = 0;
- $listener1 = function () use (&$count): void {
- $count++;
- };
- $listener2 = function () use (&$count): void {
- $count++;
- };
- $this->emitter->listen('Test', 'test', $listener1);
- $this->emitter->listen('Test', 'test', $listener2);
- $this->emitter->emitEvent('Test', 'test');
- $this->assertEquals(2, $count, 'Listener called an invalid number of times (' . $count . ') expected 2');
- }
-
- public function testArguments(): void {
- $this->expectException(\Test\Hooks\EmittedException::class);
-
- $this->emitter->listen('Test', 'test', function ($foo, $bar): void {
- if ($foo === 'foo' && $bar === 'bar') {
- throw new EmittedException;
- }
- });
- $this->emitter->emitEvent('Test', 'test', ['foo', 'bar']);
- }
-
- public function testNamedArguments(): void {
- $this->expectException(\Test\Hooks\EmittedException::class);
-
- $this->emitter->listen('Test', 'test', function ($foo, $bar): void {
- if ($foo === 'foo' && $bar === 'bar') {
- throw new EmittedException;
- }
- });
- $this->emitter->emitEvent('Test', 'test', ['foo' => 'foo', 'bar' => 'bar']);
- }
-
- public function testRemoveAllSpecified(): void {
- $listener = function (): void {
- throw new EmittedException;
- };
- $this->emitter->listen('Test', 'test', $listener);
- $this->emitter->removeListener('Test', 'test', $listener);
- $this->emitter->emitEvent('Test', 'test');
-
- $this->addToAssertionCount(1);
- }
-
- public function testRemoveWildcardListener(): void {
- $listener1 = function (): void {
- throw new EmittedException;
- };
- $listener2 = function (): void {
- throw new EmittedException;
- };
- $this->emitter->listen('Test', 'test', $listener1);
- $this->emitter->listen('Test', 'test', $listener2);
- $this->emitter->removeListener('Test', 'test');
- $this->emitter->emitEvent('Test', 'test');
-
- $this->addToAssertionCount(1);
- }
-
- public function testRemoveWildcardMethod(): void {
- $listener = function (): void {
- throw new EmittedException;
- };
- $this->emitter->listen('Test', 'test', $listener);
- $this->emitter->listen('Test', 'foo', $listener);
- $this->emitter->removeListener('Test', null, $listener);
- $this->emitter->emitEvent('Test', 'test');
- $this->emitter->emitEvent('Test', 'foo');
-
- $this->addToAssertionCount(1);
- }
-
- public function testRemoveWildcardScope(): void {
- $listener = function (): void {
- throw new EmittedException;
- };
- $this->emitter->listen('Test', 'test', $listener);
- $this->emitter->listen('Bar', 'test', $listener);
- $this->emitter->removeListener(null, 'test', $listener);
- $this->emitter->emitEvent('Test', 'test');
- $this->emitter->emitEvent('Bar', 'test');
-
- $this->addToAssertionCount(1);
- }
-
- public function testRemoveWildcardScopeAndMethod(): void {
- $listener = function (): void {
- throw new EmittedException;
- };
- $this->emitter->listen('Test', 'test', $listener);
- $this->emitter->listen('Test', 'foo', $listener);
- $this->emitter->listen('Bar', 'foo', $listener);
- $this->emitter->removeListener(null, null, $listener);
- $this->emitter->emitEvent('Test', 'test');
- $this->emitter->emitEvent('Test', 'foo');
- $this->emitter->emitEvent('Bar', 'foo');
-
- $this->addToAssertionCount(1);
- }
-
- public function testRemoveKeepOtherCallback(): void {
- $this->expectException(\Test\Hooks\EmittedException::class);
-
- $listener1 = function (): void {
- throw new EmittedException;
- };
- $listener2 = function (): void {
- throw new EmittedException;
- };
- $this->emitter->listen('Test', 'test', $listener1);
- $this->emitter->listen('Test', 'test', $listener2);
- $this->emitter->removeListener('Test', 'test', $listener1);
- $this->emitter->emitEvent('Test', 'test');
-
- $this->addToAssertionCount(1);
- }
-
- public function testRemoveKeepOtherMethod(): void {
- $this->expectException(\Test\Hooks\EmittedException::class);
-
- $listener = function (): void {
- throw new EmittedException;
- };
- $this->emitter->listen('Test', 'test', $listener);
- $this->emitter->listen('Test', 'foo', $listener);
- $this->emitter->removeListener('Test', 'foo', $listener);
- $this->emitter->emitEvent('Test', 'test');
-
- $this->addToAssertionCount(1);
- }
-
- public function testRemoveKeepOtherScope(): void {
- $this->expectException(\Test\Hooks\EmittedException::class);
-
- $listener = function (): void {
- throw new EmittedException;
- };
- $this->emitter->listen('Test', 'test', $listener);
- $this->emitter->listen('Bar', 'test', $listener);
- $this->emitter->removeListener('Bar', 'test', $listener);
- $this->emitter->emitEvent('Test', 'test');
-
- $this->addToAssertionCount(1);
- }
-
- public function testRemoveNonExistingName(): void {
- $this->expectException(\Test\Hooks\EmittedException::class);
-
- $listener = function (): void {
- throw new EmittedException;
- };
- $this->emitter->listen('Test', 'test', $listener);
- $this->emitter->removeListener('Bar', 'test', $listener);
- $this->emitter->emitEvent('Test', 'test');
-
- $this->addToAssertionCount(1);
- }
-}
--- a/tests/lib/Http/Client/ClientServiceTest.php
+++ /dev/null
@@ -1,134 +0,0 @@
-createMock(IConfig::class);
- $config->method('getSystemValueBool')
- ->with('dns_pinning', true)
- ->willReturn(true);
- /** @var ICertificateManager $certificateManager */
- $certificateManager = $this->createMock(ICertificateManager::class);
- $dnsPinMiddleware = $this->createMock(DnsPinMiddleware::class);
- $dnsPinMiddleware
- ->expects($this->atLeastOnce())
- ->method('addDnsPinning')
- ->willReturn(function (): void {
- });
- $remoteHostValidator = $this->createMock(IRemoteHostValidator::class);
- $eventLogger = $this->createMock(IEventLogger::class);
- $logger = $this->createMock(LoggerInterface::class);
- $serverVersion = $this->createMock(ServerVersion::class);
-
- $clientService = new ClientService(
- $config,
- $certificateManager,
- $dnsPinMiddleware,
- $remoteHostValidator,
- $eventLogger,
- $logger,
- $serverVersion,
- );
-
- $handler = new CurlHandler();
- $stack = HandlerStack::create($handler);
- $stack->push($dnsPinMiddleware->addDnsPinning());
- $stack->push(Middleware::tap(function (RequestInterface $request) use ($eventLogger): void {
- $eventLogger->start('http:request', $request->getMethod() . ' request to ' . $request->getRequestTarget());
- }, function () use ($eventLogger): void {
- $eventLogger->end('http:request');
- }), 'event logger');
- $guzzleClient = new GuzzleClient(['handler' => $stack]);
-
- $this->assertEquals(
- new Client(
- $config,
- $certificateManager,
- $guzzleClient,
- $remoteHostValidator,
- $logger,
- $serverVersion,
- ),
- $clientService->newClient()
- );
- }
-
- public function testDisableDnsPinning(): void {
- /** @var IConfig $config */
- $config = $this->createMock(IConfig::class);
- $config->method('getSystemValueBool')
- ->with('dns_pinning', true)
- ->willReturn(false);
- /** @var ICertificateManager $certificateManager */
- $certificateManager = $this->createMock(ICertificateManager::class);
- $dnsPinMiddleware = $this->createMock(DnsPinMiddleware::class);
- $dnsPinMiddleware
- ->expects($this->never())
- ->method('addDnsPinning')
- ->willReturn(function (): void {
- });
- $remoteHostValidator = $this->createMock(IRemoteHostValidator::class);
- $eventLogger = $this->createMock(IEventLogger::class);
- $logger = $this->createMock(LoggerInterface::class);
- $serverVersion = $this->createMock(ServerVersion::class);
-
- $clientService = new ClientService(
- $config,
- $certificateManager,
- $dnsPinMiddleware,
- $remoteHostValidator,
- $eventLogger,
- $logger,
- $serverVersion,
- );
-
- $handler = new CurlHandler();
- $stack = HandlerStack::create($handler);
- $stack->push(Middleware::tap(function (RequestInterface $request) use ($eventLogger): void {
- $eventLogger->start('http:request', $request->getMethod() . ' request to ' . $request->getRequestTarget());
- }, function () use ($eventLogger): void {
- $eventLogger->end('http:request');
- }), 'event logger');
- $guzzleClient = new GuzzleClient(['handler' => $stack]);
-
- $this->assertEquals(
- new Client(
- $config,
- $certificateManager,
- $guzzleClient,
- $remoteHostValidator,
- $logger,
- $serverVersion,
- ),
- $clientService->newClient()
- );
- }
-}
--- a/tests/lib/Http/Client/ClientTest.php
+++ /dev/null
@@ -1,706 +0,0 @@
-config = $this->createMock(IConfig::class);
- $this->guzzleClient = $this->createMock(\GuzzleHttp\Client::class);
- $this->certificateManager = $this->createMock(ICertificateManager::class);
- $this->remoteHostValidator = $this->createMock(IRemoteHostValidator::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->serverVersion = $this->createMock(ServerVersion::class);
-
- $this->client = new Client(
- $this->config,
- $this->certificateManager,
- $this->guzzleClient,
- $this->remoteHostValidator,
- $this->logger,
- $this->serverVersion,
- );
- }
-
- public function testGetProxyUri(): void {
- $this->config
- ->method('getSystemValueString')
- ->with('proxy', '')
- ->willReturn('');
- $this->assertNull(self::invokePrivate($this->client, 'getProxyUri'));
- }
-
- public function testGetProxyUriProxyHostEmptyPassword(): void {
- $this->config
- ->method('getSystemValue')
- ->willReturnMap([
- ['proxyexclude', [], []],
- ]);
-
- $this->config
- ->method('getSystemValueString')
- ->willReturnMap([
- ['proxy', '', 'foo'],
- ['proxyuserpwd', '', ''],
- ]);
-
- $this->assertEquals([
- 'http' => 'foo',
- 'https' => 'foo'
- ], self::invokePrivate($this->client, 'getProxyUri'));
- }
-
- public function testGetProxyUriProxyHostWithPassword(): void {
- $this->config
- ->expects($this->once())
- ->method('getSystemValue')
- ->with('proxyexclude', [])
- ->willReturn([]);
- $this->config
- ->expects($this->exactly(2))
- ->method('getSystemValueString')
- ->willReturnMap([
- ['proxy', '', 'foo'],
- ['proxyuserpwd', '', 'username:password'],
- ]);
- $this->assertEquals([
- 'http' => 'username:password@foo',
- 'https' => 'username:password@foo'
- ], self::invokePrivate($this->client, 'getProxyUri'));
- }
-
- public function testGetProxyUriProxyHostWithPasswordAndExclude(): void {
- $this->config
- ->expects($this->once())
- ->method('getSystemValue')
- ->with('proxyexclude', [])
- ->willReturn(['bar']);
- $this->config
- ->expects($this->exactly(2))
- ->method('getSystemValueString')
- ->willReturnMap([
- ['proxy', '', 'foo'],
- ['proxyuserpwd', '', 'username:password'],
- ]);
- $this->assertEquals([
- 'http' => 'username:password@foo',
- 'https' => 'username:password@foo',
- 'no' => ['bar']
- ], self::invokePrivate($this->client, 'getProxyUri'));
- }
-
- public function testPreventLocalAddressThrowOnInvalidUri(): void {
- $this->expectException(LocalServerException::class);
- $this->expectExceptionMessage('Could not detect any host');
-
- self::invokePrivate($this->client, 'preventLocalAddress', ['!@#$', []]);
- }
-
- public static function dataPreventLocalAddress(): array {
- return [
- ['https://localhost/foo.bar'],
- ['https://localHost/foo.bar'],
- ['https://random-host/foo.bar'],
- ['https://[::1]/bla.blub'],
- ['https://[::]/bla.blub'],
- ['https://192.168.0.1'],
- ['https://172.16.42.1'],
- ['https://[fdf8:f53b:82e4::53]/secret.ics'],
- ['https://[fe80::200:5aee:feaa:20a2]/secret.ics'],
- ['https://[0:0:0:0:0:0:10.0.0.1]/secret.ics'],
- ['https://[0:0:0:0:0:ffff:127.0.0.0]/secret.ics'],
- ['https://10.0.0.1'],
- ['https://another-host.local'],
- ['https://service.localhost'],
- ['https://normal.host.com'],
- ['https://com.one-.nextcloud-one.com'],
- ];
- }
-
- /**
- * @param string $uri
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataPreventLocalAddress')]
- public function testPreventLocalAddressDisabledByGlobalConfig(string $uri): void {
- $this->config->expects($this->once())
- ->method('getSystemValueBool')
- ->with('allow_local_remote_servers', false)
- ->willReturn(true);
-
- self::invokePrivate($this->client, 'preventLocalAddress', [$uri, []]);
- }
-
- /**
- * @param string $uri
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataPreventLocalAddress')]
- public function testPreventLocalAddressDisabledByOption(string $uri): void {
- $this->config->expects($this->never())
- ->method('getSystemValueBool');
-
- self::invokePrivate($this->client, 'preventLocalAddress', [$uri, [
- 'nextcloud' => ['allow_local_address' => true],
- ]]);
- }
-
- /**
- * @param string $uri
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataPreventLocalAddress')]
- public function testPreventLocalAddressOnGet(string $uri): void {
- $host = parse_url($uri, PHP_URL_HOST);
- $this->expectException(LocalServerException::class);
- $this->remoteHostValidator
- ->method('isValid')
- ->with($host)
- ->willReturn(false);
-
- $this->client->get($uri);
- }
-
- /**
- * @param string $uri
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataPreventLocalAddress')]
- public function testPreventLocalAddressOnHead(string $uri): void {
- $host = parse_url($uri, PHP_URL_HOST);
- $this->expectException(LocalServerException::class);
- $this->remoteHostValidator
- ->method('isValid')
- ->with($host)
- ->willReturn(false);
-
- $this->client->head($uri);
- }
-
- /**
- * @param string $uri
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataPreventLocalAddress')]
- public function testPreventLocalAddressOnPost(string $uri): void {
- $host = parse_url($uri, PHP_URL_HOST);
- $this->expectException(LocalServerException::class);
- $this->remoteHostValidator
- ->method('isValid')
- ->with($host)
- ->willReturn(false);
-
- $this->client->post($uri);
- }
-
- /**
- * @param string $uri
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataPreventLocalAddress')]
- public function testPreventLocalAddressOnPut(string $uri): void {
- $host = parse_url($uri, PHP_URL_HOST);
- $this->expectException(LocalServerException::class);
- $this->remoteHostValidator
- ->method('isValid')
- ->with($host)
- ->willReturn(false);
-
- $this->client->put($uri);
- }
-
- /**
- * @param string $uri
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataPreventLocalAddress')]
- public function testPreventLocalAddressOnDelete(string $uri): void {
- $host = parse_url($uri, PHP_URL_HOST);
- $this->expectException(LocalServerException::class);
- $this->remoteHostValidator
- ->method('isValid')
- ->with($host)
- ->willReturn(false);
-
- $this->client->delete($uri);
- }
-
- private function setUpDefaultRequestOptions(): void {
- $this->config
- ->method('getSystemValue')
- ->willReturnMap([
- ['proxyexclude', [], []],
- ]);
- $this->config
- ->method('getSystemValueString')
- ->willReturnMap([
- ['proxy', '', 'foo'],
- ['proxyuserpwd', '', ''],
- ['overwrite.cli.url', '', '']
- ]);
- $this->config
- ->method('getSystemValueBool')
- ->willReturnMap([
- ['installed', false, true],
- ['allow_local_remote_servers', false, true],
- ['http_client_add_user_agent_url', false, false]
- ]);
-
- $this->certificateManager
- ->expects($this->once())
- ->method('getAbsoluteBundlePath')
- ->with()
- ->willReturn('/my/path.crt');
-
- $this->serverVersion->method('getVersionString')
- ->willReturn('123.45.6');
-
- $acceptEnc = (((curl_version()['features'] ?? 0) & CURL_VERSION_BROTLI) === CURL_VERSION_BROTLI) ? 'br, gzip' : 'gzip';
- $this->defaultRequestOptions = [
- 'verify' => '/my/path.crt',
- 'proxy' => [
- 'http' => 'foo',
- 'https' => 'foo'
- ],
- 'headers' => [
- 'User-Agent' => 'Nextcloud-Server-Crawler/123.45.6',
- 'Accept-Encoding' => $acceptEnc,
- ],
- 'timeout' => 30,
- 'nextcloud' => [
- 'allow_local_address' => true,
- ],
- 'version' => '2.0',
- 'curl' => [
- \CURLOPT_HTTP_VERSION => \CURL_HTTP_VERSION_2TLS,
- ],
- ];
- }
-
- public function testGet(): void {
- $this->setUpDefaultRequestOptions();
-
- $this->guzzleClient->method('request')
- ->with('get', 'http://localhost/', $this->defaultRequestOptions)
- ->willReturn(new Response(418));
- $this->assertEquals(418, $this->client->get('http://localhost/', [])->getStatusCode());
- }
-
- public function testGetWithOptions(): void {
- $this->setUpDefaultRequestOptions();
-
- $options = array_merge($this->defaultRequestOptions, [
- 'verify' => false,
- 'proxy' => [
- 'http' => 'bar',
- 'https' => 'bar'
- ],
- ]);
-
- $this->guzzleClient->method('request')
- ->with('get', 'http://localhost/', $options)
- ->willReturn(new Response(418));
- $this->assertEquals(418, $this->client->get('http://localhost/', $options)->getStatusCode());
- }
-
- public function testPost(): void {
- $this->setUpDefaultRequestOptions();
-
- $this->guzzleClient->method('request')
- ->with('post', 'http://localhost/', $this->defaultRequestOptions)
- ->willReturn(new Response(418));
- $this->assertEquals(418, $this->client->post('http://localhost/', [])->getStatusCode());
- }
-
- public function testPostWithOptions(): void {
- $this->setUpDefaultRequestOptions();
-
- $options = array_merge($this->defaultRequestOptions, [
- 'verify' => false,
- 'proxy' => [
- 'http' => 'bar',
- 'https' => 'bar'
- ],
- ]);
-
- $this->guzzleClient->method('request')
- ->with('post', 'http://localhost/', $options)
- ->willReturn(new Response(418));
- $this->assertEquals(418, $this->client->post('http://localhost/', $options)->getStatusCode());
- }
-
- public function testPut(): void {
- $this->setUpDefaultRequestOptions();
-
- $this->guzzleClient->method('request')
- ->with('put', 'http://localhost/', $this->defaultRequestOptions)
- ->willReturn(new Response(418));
- $this->assertEquals(418, $this->client->put('http://localhost/', [])->getStatusCode());
- }
-
- public function testPutWithOptions(): void {
- $this->setUpDefaultRequestOptions();
-
- $options = array_merge($this->defaultRequestOptions, [
- 'verify' => false,
- 'proxy' => [
- 'http' => 'bar',
- 'https' => 'bar'
- ],
- ]);
-
- $this->guzzleClient->method('request')
- ->with('put', 'http://localhost/', $options)
- ->willReturn(new Response(418));
- $this->assertEquals(418, $this->client->put('http://localhost/', $options)->getStatusCode());
- }
-
- public function testDelete(): void {
- $this->setUpDefaultRequestOptions();
-
- $this->guzzleClient->method('request')
- ->with('delete', 'http://localhost/', $this->defaultRequestOptions)
- ->willReturn(new Response(418));
- $this->assertEquals(418, $this->client->delete('http://localhost/', [])->getStatusCode());
- }
-
- public function testDeleteWithOptions(): void {
- $this->setUpDefaultRequestOptions();
-
- $options = array_merge($this->defaultRequestOptions, [
- 'verify' => false,
- 'proxy' => [
- 'http' => 'bar',
- 'https' => 'bar'
- ],
- ]);
-
- $this->guzzleClient->method('request')
- ->with('delete', 'http://localhost/', $options)
- ->willReturn(new Response(418));
- $this->assertEquals(418, $this->client->delete('http://localhost/', $options)->getStatusCode());
- }
-
- public function testOptions(): void {
- $this->setUpDefaultRequestOptions();
-
- $this->guzzleClient->method('request')
- ->with('options', 'http://localhost/', $this->defaultRequestOptions)
- ->willReturn(new Response(418));
- $this->assertEquals(418, $this->client->options('http://localhost/', [])->getStatusCode());
- }
-
- public function testOptionsWithOptions(): void {
- $this->setUpDefaultRequestOptions();
-
- $options = array_merge($this->defaultRequestOptions, [
- 'verify' => false,
- 'proxy' => [
- 'http' => 'bar',
- 'https' => 'bar'
- ],
- ]);
-
- $this->guzzleClient->method('request')
- ->with('options', 'http://localhost/', $options)
- ->willReturn(new Response(418));
- $this->assertEquals(418, $this->client->options('http://localhost/', $options)->getStatusCode());
- }
-
- public function testHead(): void {
- $this->setUpDefaultRequestOptions();
-
- $this->guzzleClient->method('request')
- ->with('head', 'http://localhost/', $this->defaultRequestOptions)
- ->willReturn(new Response(418));
- $this->assertEquals(418, $this->client->head('http://localhost/', [])->getStatusCode());
- }
-
- public function testHeadWithOptions(): void {
- $this->setUpDefaultRequestOptions();
-
- $options = array_merge($this->defaultRequestOptions, [
- 'verify' => false,
- 'proxy' => [
- 'http' => 'bar',
- 'https' => 'bar'
- ],
- ]);
-
- $this->guzzleClient->method('request')
- ->with('head', 'http://localhost/', $options)
- ->willReturn(new Response(418));
- $this->assertEquals(418, $this->client->head('http://localhost/', $options)->getStatusCode());
- }
-
- public function testSetDefaultOptionsWithNotInstalled(): void {
- $this->config
- ->expects($this->exactly(3))
- ->method('getSystemValueBool')
- ->willReturnMap([
- ['installed', false, false],
- ['allow_local_remote_servers', false, false],
- ['http_client_add_user_agent_url', false, false],
- ]);
- $this->config
- ->expects($this->exactly(2))
- ->method('getSystemValueString')
- ->willReturnMap([
- ['proxy', '', ''],
- ['overwrite.cli.url', '', ''],
- ]);
- $this->certificateManager
- ->expects($this->never())
- ->method('listCertificates');
- $this->certificateManager
- ->expects($this->once())
- ->method('getDefaultCertificatesBundlePath')
- ->willReturn(\OC::$SERVERROOT . '/resources/config/ca-bundle.crt');
-
- $this->serverVersion->method('getVersionString')
- ->willReturn('123.45.6');
-
- $acceptEnc = (((curl_version()['features'] ?? 0) & CURL_VERSION_BROTLI) === CURL_VERSION_BROTLI) ? 'br, gzip' : 'gzip';
-
- $this->assertEquals([
- 'verify' => \OC::$SERVERROOT . '/resources/config/ca-bundle.crt',
- 'headers' => [
- 'User-Agent' => 'Nextcloud-Server-Crawler/123.45.6',
- 'Accept-Encoding' => $acceptEnc,
- ],
- 'timeout' => 30,
- 'nextcloud' => [
- 'allow_local_address' => false,
- ],
- 'allow_redirects' => [
- 'on_redirect' => function (
- \Psr\Http\Message\RequestInterface $request,
- \Psr\Http\Message\ResponseInterface $response,
- \Psr\Http\Message\UriInterface $uri,
- ): void {
- },
- ],
- 'version' => '2.0',
- 'curl' => [
- \CURLOPT_HTTP_VERSION => \CURL_HTTP_VERSION_2TLS,
- ],
- ], self::invokePrivate($this->client, 'buildRequestOptions', [[]]));
- }
-
- public function testSetDefaultOptionsWithProxy(): void {
- $this->config
- ->expects($this->exactly(3))
- ->method('getSystemValueBool')
- ->willReturnMap([
- ['installed', false, true],
- ['allow_local_remote_servers', false, false],
- ['http_client_add_user_agent_url', false, false],
- ]);
- $this->config
- ->expects($this->once())
- ->method('getSystemValue')
- ->with('proxyexclude', [])
- ->willReturn([]);
- $this->config
- ->expects($this->exactly(3))
- ->method('getSystemValueString')
- ->willReturnMap([
- ['proxy', '', 'foo'],
- ['proxyuserpwd', '', ''],
- ['overwrite.cli.url', '', ''],
- ]);
- $this->certificateManager
- ->expects($this->once())
- ->method('getAbsoluteBundlePath')
- ->with()
- ->willReturn('/my/path.crt');
-
- $this->serverVersion->method('getVersionString')
- ->willReturn('123.45.6');
-
- $acceptEnc = (((curl_version()['features'] ?? 0) & CURL_VERSION_BROTLI) === CURL_VERSION_BROTLI) ? 'br, gzip' : 'gzip';
-
- $this->assertEquals([
- 'verify' => '/my/path.crt',
- 'proxy' => [
- 'http' => 'foo',
- 'https' => 'foo'
- ],
- 'headers' => [
- 'User-Agent' => 'Nextcloud-Server-Crawler/123.45.6',
- 'Accept-Encoding' => $acceptEnc,
- ],
- 'timeout' => 30,
- 'nextcloud' => [
- 'allow_local_address' => false,
- ],
- 'allow_redirects' => [
- 'on_redirect' => function (
- \Psr\Http\Message\RequestInterface $request,
- \Psr\Http\Message\ResponseInterface $response,
- \Psr\Http\Message\UriInterface $uri,
- ): void {
- },
- ],
- 'version' => '2.0',
- 'curl' => [
- \CURLOPT_HTTP_VERSION => \CURL_HTTP_VERSION_2TLS,
- ],
- ], self::invokePrivate($this->client, 'buildRequestOptions', [[]]));
- }
-
- public function testSetDefaultOptionsWithProxyAndExclude(): void {
- $this->config
- ->expects($this->exactly(3))
- ->method('getSystemValueBool')
- ->willReturnMap([
- ['installed', false, true],
- ['allow_local_remote_servers', false, false],
- ['http_client_add_user_agent_url', false, false],
- ]);
- $this->config
- ->expects($this->once())
- ->method('getSystemValue')
- ->with('proxyexclude', [])
- ->willReturn(['bar']);
- $this->config
- ->expects($this->exactly(3))
- ->method('getSystemValueString')
- ->willReturnMap([
- ['proxy', '', 'foo'],
- ['proxyuserpwd', '', ''],
- ['overwrite.cli.url', '', ''],
- ]);
- $this->certificateManager
- ->expects($this->once())
- ->method('getAbsoluteBundlePath')
- ->with()
- ->willReturn('/my/path.crt');
-
- $this->serverVersion->method('getVersionString')
- ->willReturn('123.45.6');
-
- $acceptEnc = (((curl_version()['features'] ?? 0) & CURL_VERSION_BROTLI) === CURL_VERSION_BROTLI) ? 'br, gzip' : 'gzip';
-
- $this->assertEquals([
- 'verify' => '/my/path.crt',
- 'proxy' => [
- 'http' => 'foo',
- 'https' => 'foo',
- 'no' => ['bar']
- ],
- 'headers' => [
- 'User-Agent' => 'Nextcloud-Server-Crawler/123.45.6',
- 'Accept-Encoding' => $acceptEnc,
- ],
- 'timeout' => 30,
- 'nextcloud' => [
- 'allow_local_address' => false,
- ],
- 'allow_redirects' => [
- 'on_redirect' => function (
- \Psr\Http\Message\RequestInterface $request,
- \Psr\Http\Message\ResponseInterface $response,
- \Psr\Http\Message\UriInterface $uri,
- ): void {
- },
- ],
- 'version' => '2.0',
- 'curl' => [
- \CURLOPT_HTTP_VERSION => \CURL_HTTP_VERSION_2TLS,
- ],
- ], self::invokePrivate($this->client, 'buildRequestOptions', [[]]));
- }
-
- public static function dataForTestSetServerUrlInUserAgent(): array {
- return [
- ['https://example.com/', 'Nextcloud-Server-Crawler/123.45.6; +https://example.com'],
- ['', 'Nextcloud-Server-Crawler/123.45.6'],
- ];
- }
-
- #[DataProvider('dataForTestSetServerUrlInUserAgent')]
- public function testSetServerUrlInUserAgent(string $url, string $userAgent): void {
- $this->config
- ->expects($this->exactly(3))
- ->method('getSystemValueBool')
- ->willReturnMap([
- ['installed', false, true],
- ['allow_local_remote_servers', false, false],
- ['http_client_add_user_agent_url', false, true],
- ]);
- $this->config
- ->expects($this->exactly(2))
- ->method('getSystemValueString')
- ->willReturnMap([
- ['proxy', '', ''],
- ['overwrite.cli.url', '', $url],
- ]);
- $this->certificateManager
- ->expects($this->once())
- ->method('getAbsoluteBundlePath')
- ->with()
- ->willReturn('/my/path.crt');
-
- $this->serverVersion->method('getVersionString')
- ->willReturn('123.45.6');
-
- $acceptEnc = (((curl_version()['features'] ?? 0) & CURL_VERSION_BROTLI) === CURL_VERSION_BROTLI) ? 'br, gzip' : 'gzip';
- $this->assertEquals([
- 'verify' => '/my/path.crt',
- 'headers' => [
- 'User-Agent' => $userAgent,
- 'Accept-Encoding' => $acceptEnc,
- ],
- 'timeout' => 30,
- 'nextcloud' => [
- 'allow_local_address' => false,
- ],
- 'allow_redirects' => [
- 'on_redirect' => function (
- \Psr\Http\Message\RequestInterface $request,
- \Psr\Http\Message\ResponseInterface $response,
- \Psr\Http\Message\UriInterface $uri,
- ): void {
- },
- ],
- 'version' => '2.0',
- 'curl' => [
- \CURLOPT_HTTP_VERSION => \CURL_HTTP_VERSION_2TLS,
- ],
- ], self::invokePrivate($this->client, 'buildRequestOptions', [[]]));
- }
-}
--- a/tests/lib/Http/Client/DnsPinMiddlewareTest.php
+++ /dev/null
@@ -1,550 +0,0 @@
-createMock(ICacheFactory::class);
- $cacheFactory
- ->method('createLocal')
- ->willReturn(new NullCache());
-
- $ipAddressClassifier = new IpAddressClassifier();
- $negativeDnsCache = new NegativeDnsCache($cacheFactory);
- $logger = new NullLogger();
-
- $this->dnsPinMiddleware = $this->getMockBuilder(DnsPinMiddleware::class)
- ->setConstructorArgs([$negativeDnsCache, $ipAddressClassifier, $logger])
- ->onlyMethods(['dnsGetRecord'])
- ->getMock();
- }
-
- public function testPopulateDnsCacheIPv4(): void {
- $mockHandler = new MockHandler([
- static function (RequestInterface $request, array $options) {
- self::arrayHasKey('curl', $options);
- self::arrayHasKey(CURLOPT_RESOLVE, $options['curl']);
- self::assertEquals([
- 'www.example.com:80:1.1.1.1',
- 'www.example.com:443:1.1.1.1'
- ], $options['curl'][CURLOPT_RESOLVE]);
- return new Response(200);
- },
- ]);
-
- $this->dnsPinMiddleware
- ->method('dnsGetRecord')
- ->willReturnCallback(function (string $hostname, int $type) {
- // example.com SOA
- if ($hostname === 'example.com.') {
- return match ($type) {
- DNS_SOA => [
- [
- 'host' => 'example.com',
- 'class' => 'IN',
- 'ttl' => 7079,
- 'type' => 'SOA',
- 'minimum-ttl' => 3600,
- ]
- ],
- };
- }
-
- // example.com A, AAAA, CNAME
- if ($hostname === 'www.example.com.') {
- return match ($type) {
- DNS_A => [],
- DNS_AAAA => [],
- DNS_CNAME => [
- [
- 'host' => 'www.example.com',
- 'class' => 'IN',
- 'ttl' => 1800,
- 'type' => 'A',
- 'target' => 'www.example.net'
- ]
- ],
- };
- }
-
- // example.net SOA
- if ($hostname === 'example.net.') {
- return match ($type) {
- DNS_SOA => [
- [
- 'host' => 'example.net',
- 'class' => 'IN',
- 'ttl' => 7079,
- 'type' => 'SOA',
- 'minimum-ttl' => 3600,
- ]
- ],
- };
- }
-
- // example.net A, AAAA, CNAME
- if ($hostname === 'www.example.net.') {
- return match ($type) {
- DNS_A => [
- [
- 'host' => 'www.example.net',
- 'class' => 'IN',
- 'ttl' => 1800,
- 'type' => 'A',
- 'ip' => '1.1.1.1'
- ]
- ],
- DNS_AAAA => [],
- DNS_CNAME => [],
- };
- }
-
- return false;
- });
-
- $stack = new HandlerStack($mockHandler);
- $stack->push($this->dnsPinMiddleware->addDnsPinning());
- $handler = $stack->resolve();
-
- $handler(
- new Request('GET', 'https://www.example.com'),
- ['nextcloud' => ['allow_local_address' => false]]
- );
- }
-
- public function testPopulateDnsCacheIPv6(): void {
- $mockHandler = new MockHandler([
- static function (RequestInterface $request, array $options) {
- self::arrayHasKey('curl', $options);
- self::arrayHasKey(CURLOPT_RESOLVE, $options['curl']);
- self::assertEquals([
- 'www.example.com:80:1.1.1.1,1.0.0.1,2606:4700:4700::1111,2606:4700:4700::1001',
- 'www.example.com:443:1.1.1.1,1.0.0.1,2606:4700:4700::1111,2606:4700:4700::1001'
- ], $options['curl'][CURLOPT_RESOLVE]);
- return new Response(200);
- },
- ]);
-
- $this->dnsPinMiddleware
- ->method('dnsGetRecord')
- ->willReturnCallback(function (string $hostname, int $type) {
- // example.com SOA
- if ($hostname === 'example.com.') {
- return match ($type) {
- DNS_SOA => [
- [
- 'host' => 'example.com',
- 'class' => 'IN',
- 'ttl' => 7079,
- 'type' => 'SOA',
- 'minimum-ttl' => 3600,
- ]
- ],
- };
- }
-
- // example.com A, AAAA, CNAME
- if ($hostname === 'www.example.com.') {
- return match ($type) {
- DNS_A => [],
- DNS_AAAA => [],
- DNS_CNAME => [
- [
- 'host' => 'www.example.com',
- 'class' => 'IN',
- 'ttl' => 1800,
- 'type' => 'A',
- 'target' => 'www.example.net'
- ]
- ],
- };
- }
-
- // example.net SOA
- if ($hostname === 'example.net.') {
- return match ($type) {
- DNS_SOA => [
- [
- 'host' => 'example.net',
- 'class' => 'IN',
- 'ttl' => 7079,
- 'type' => 'SOA',
- 'minimum-ttl' => 3600,
- ]
- ],
- };
- }
-
- // example.net A, AAAA, CNAME
- if ($hostname === 'www.example.net.') {
- return match ($type) {
- DNS_A => [
- [
- 'host' => 'www.example.net',
- 'class' => 'IN',
- 'ttl' => 1800,
- 'type' => 'A',
- 'ip' => '1.1.1.1'
- ],
- [
- 'host' => 'www.example.net',
- 'class' => 'IN',
- 'ttl' => 1800,
- 'type' => 'A',
- 'ip' => '1.0.0.1'
- ],
- ],
- DNS_AAAA => [
- [
- 'host' => 'www.example.net',
- 'class' => 'IN',
- 'ttl' => 1800,
- 'type' => 'AAAA',
- 'ip' => '2606:4700:4700::1111'
- ],
- [
- 'host' => 'www.example.net',
- 'class' => 'IN',
- 'ttl' => 1800,
- 'type' => 'AAAA',
- 'ip' => '2606:4700:4700::1001'
- ],
- ],
- DNS_CNAME => [],
- };
- }
-
- return false;
- });
-
- $stack = new HandlerStack($mockHandler);
- $stack->push($this->dnsPinMiddleware->addDnsPinning());
- $handler = $stack->resolve();
-
- $handler(
- new Request('GET', 'https://www.example.com'),
- ['nextcloud' => ['allow_local_address' => false]]
- );
- }
-
- public function testAllowLocalAddress(): void {
- $mockHandler = new MockHandler([
- static function (RequestInterface $request, array $options) {
- self::assertArrayNotHasKey('curl', $options);
- return new Response(200);
- },
- ]);
-
- $stack = new HandlerStack($mockHandler);
- $stack->push($this->dnsPinMiddleware->addDnsPinning());
- $handler = $stack->resolve();
-
- $handler(
- new Request('GET', 'https://www.example.com'),
- ['nextcloud' => ['allow_local_address' => true]]
- );
- }
-
- public function testRejectIPv4(): void {
- $this->expectException(LocalServerException::class);
- $this->expectExceptionMessage('violates local access rules');
-
- $mockHandler = new MockHandler([
- static function (RequestInterface $request, array $options): void {
- // The handler should not be called
- },
- ]);
-
- $this->dnsPinMiddleware
- ->method('dnsGetRecord')
- ->willReturnCallback(function (string $hostname, int $type) {
- return match ($type) {
- DNS_SOA => [
- [
- 'host' => 'example.com',
- 'class' => 'IN',
- 'ttl' => 7079,
- 'type' => 'SOA',
- 'minimum-ttl' => 3600,
- ]
- ],
- DNS_A => [
- [
- 'host' => 'example.com',
- 'class' => 'IN',
- 'ttl' => 1800,
- 'type' => 'A',
- 'ip' => '192.168.0.1'
- ]
- ],
- DNS_AAAA => [],
- DNS_CNAME => [],
- };
- });
-
- $stack = new HandlerStack($mockHandler);
- $stack->push($this->dnsPinMiddleware->addDnsPinning());
- $handler = $stack->resolve();
-
- $handler(
- new Request('GET', 'https://www.example.com'),
- ['nextcloud' => ['allow_local_address' => false]]
- );
- }
-
- public function testRejectIPv6(): void {
- $this->expectException(LocalServerException::class);
- $this->expectExceptionMessage('violates local access rules');
-
- $mockHandler = new MockHandler([
- static function (RequestInterface $request, array $options): void {
- // The handler should not be called
- },
- ]);
-
- $this->dnsPinMiddleware
- ->method('dnsGetRecord')
- ->willReturnCallback(function (string $hostname, int $type) {
- return match ($type) {
- DNS_SOA => [
- [
- 'host' => 'example.com',
- 'class' => 'IN',
- 'ttl' => 7079,
- 'type' => 'SOA',
- 'minimum-ttl' => 3600,
- ]
- ],
- DNS_A => [],
- DNS_AAAA => [
- [
- 'host' => 'ipv6.example.com',
- 'class' => 'IN',
- 'ttl' => 1800,
- 'type' => 'AAAA',
- 'ipv6' => 'fd12:3456:789a:1::1'
- ]
- ],
- DNS_CNAME => [],
- };
- });
-
- $stack = new HandlerStack($mockHandler);
- $stack->push($this->dnsPinMiddleware->addDnsPinning());
- $handler = $stack->resolve();
-
- $handler(
- new Request('GET', 'https://ipv6.example.com'),
- ['nextcloud' => ['allow_local_address' => false]]
- );
- }
-
- public function testRejectCanonicalName(): void {
- $this->expectException(LocalServerException::class);
- $this->expectExceptionMessage('violates local access rules');
-
- $mockHandler = new MockHandler([
- static function (RequestInterface $request, array $options): void {
- // The handler should not be called
- },
- ]);
-
- $this->dnsPinMiddleware
- ->method('dnsGetRecord')
- ->willReturnCallback(function (string $hostname, int $type) {
- // example.com SOA
- if ($hostname === 'example.com.') {
- return match ($type) {
- DNS_SOA => [
- [
- 'host' => 'example.com',
- 'class' => 'IN',
- 'ttl' => 7079,
- 'type' => 'SOA',
- 'minimum-ttl' => 3600,
- ]
- ],
- };
- }
-
- // example.com A, AAAA, CNAME
- if ($hostname === 'www.example.com.') {
- return match ($type) {
- DNS_A => [],
- DNS_AAAA => [],
- DNS_CNAME => [
- [
- 'host' => 'www.example.com',
- 'class' => 'IN',
- 'ttl' => 1800,
- 'type' => 'A',
- 'target' => 'www.example.net'
- ]
- ],
- };
- }
-
- // example.net SOA
- if ($hostname === 'example.net.') {
- return match ($type) {
- DNS_SOA => [
- [
- 'host' => 'example.net',
- 'class' => 'IN',
- 'ttl' => 7079,
- 'type' => 'SOA',
- 'minimum-ttl' => 3600,
- ]
- ],
- };
- }
-
- // example.net A, AAAA, CNAME
- if ($hostname === 'www.example.net.') {
- return match ($type) {
- DNS_A => [
- [
- 'host' => 'www.example.net',
- 'class' => 'IN',
- 'ttl' => 1800,
- 'type' => 'A',
- 'ip' => '192.168.0.2'
- ]
- ],
- DNS_AAAA => [],
- DNS_CNAME => [],
- };
- }
-
- return false;
- });
-
- $stack = new HandlerStack($mockHandler);
- $stack->push($this->dnsPinMiddleware->addDnsPinning());
- $handler = $stack->resolve();
-
- $handler(
- new Request('GET', 'https://www.example.com'),
- ['nextcloud' => ['allow_local_address' => false]]
- );
- }
-
- public function testRejectFaultyResponse(): void {
- $this->expectException(LocalServerException::class);
- $this->expectExceptionMessage('No DNS record found for www.example.com');
-
- $mockHandler = new MockHandler([
- static function (RequestInterface $request, array $options): void {
- // The handler should not be called
- },
- ]);
-
- $this->dnsPinMiddleware
- ->method('dnsGetRecord')
- ->willReturnCallback(function (string $hostname, int $type) {
- return false;
- });
-
- $stack = new HandlerStack($mockHandler);
- $stack->push($this->dnsPinMiddleware->addDnsPinning());
- $handler = $stack->resolve();
-
- $handler(
- new Request('GET', 'https://www.example.com'),
- ['nextcloud' => ['allow_local_address' => false]]
- );
- }
-
- public function testIgnoreSubdomainForSoaQuery(): void {
- $mockHandler = new MockHandler([
- static function (RequestInterface $request, array $options): void {
- // The handler should not be called
- },
- ]);
-
- $dnsQueries = [];
-
- $this->dnsPinMiddleware
- ->method('dnsGetRecord')
- ->willReturnCallback(function (string $hostname, int $type) use (&$dnsQueries) {
- // log query
- $dnsQueries[] = $hostname . $type;
-
- // example.com SOA
- if ($hostname === 'example.com.') {
- return match ($type) {
- DNS_SOA => [
- [
- 'host' => 'example.com',
- 'class' => 'IN',
- 'ttl' => 7079,
- 'type' => 'SOA',
- 'minimum-ttl' => 3600,
- ]
- ],
- };
- }
-
- // example.net A, AAAA, CNAME
- if ($hostname === 'subsubdomain.subdomain.example.com.') {
- return match ($type) {
- DNS_A => [
- [
- 'host' => 'subsubdomain.subdomain.example.com',
- 'class' => 'IN',
- 'ttl' => 1800,
- 'type' => 'A',
- 'ip' => '1.1.1.1'
- ]
- ],
- DNS_AAAA => [],
- DNS_CNAME => [],
- };
- }
-
- return false;
- });
-
- $stack = new HandlerStack($mockHandler);
- $stack->push($this->dnsPinMiddleware->addDnsPinning());
- $handler = $stack->resolve();
-
- $handler(
- new Request('GET', 'https://subsubdomain.subdomain.example.com'),
- ['nextcloud' => ['allow_local_address' => false]]
- );
-
- $this->assertCount(3, $dnsQueries);
- $this->assertContains('example.com.' . DNS_SOA, $dnsQueries);
- $this->assertContains('subsubdomain.subdomain.example.com.' . DNS_A, $dnsQueries);
- $this->assertContains('subsubdomain.subdomain.example.com.' . DNS_AAAA, $dnsQueries);
- // CNAME should not be queried if A or AAAA succeeded already
- $this->assertNotContains('subsubdomain.subdomain.example.com.' . DNS_CNAME, $dnsQueries);
- }
-}
--- a/tests/lib/Http/Client/NegativeDnsCacheTest.php
+++ /dev/null
@@ -1,56 +0,0 @@
-cache = $this->createMock(ICache::class);
- $this->cacheFactory = $this->createMock(ICacheFactory::class);
- $this->cacheFactory
- ->method('createLocal')
- ->with('NegativeDnsCache')
- ->willReturn($this->cache);
-
- $this->negativeDnsCache = new NegativeDnsCache($this->cacheFactory);
- }
-
- public function testSetNegativeCacheForDnsType() : void {
- $this->cache
- ->expects($this->once())
- ->method('set')
- ->with('www.example.com-1', 'true', 3600);
-
- $this->negativeDnsCache->setNegativeCacheForDnsType('www.example.com', DNS_A, 3600);
- }
-
- public function testIsNegativeCached(): void {
- $this->cache
- ->expects($this->once())
- ->method('hasKey')
- ->with('www.example.com-1')
- ->willReturn(true);
-
- $this->assertTrue($this->negativeDnsCache->isNegativeCached('www.example.com', DNS_A));
- }
-}
--- a/tests/lib/Http/Client/ResponseTest.php
+++ /dev/null
@@ -1,60 +0,0 @@
-guzzleResponse = new GuzzleResponse(418);
- }
-
- public function testGetBody(): void {
- $response = new Response($this->guzzleResponse->withBody(Utils::streamFor('MyResponse')));
- $this->assertSame('MyResponse', $response->getBody());
- }
-
- public function testGetStatusCode(): void {
- $response = new Response($this->guzzleResponse);
- $this->assertSame(418, $response->getStatusCode());
- }
-
- public function testGetHeader(): void {
- $response = new Response($this->guzzleResponse->withHeader('bar', 'foo'));
- $this->assertSame('foo', $response->getHeader('bar'));
- }
-
- public function testGetHeaders(): void {
- $response = new Response($this->guzzleResponse
- ->withHeader('bar', 'foo')
- ->withHeader('x-awesome', 'yes')
- );
-
- $expected = [
- 'bar' => [
- 0 => 'foo',
- ],
- 'x-awesome' => [
- 0 => 'yes',
- ],
- ];
- $this->assertSame($expected, $response->getHeaders());
- $this->assertSame('yes', $response->getHeader('x-awesome'));
- }
-}
--- a/tests/lib/Http/WellKnown/GenericResponseTest.php
+++ /dev/null
@@ -1,24 +0,0 @@
-createMock(JSONResponse::class);
-
- $response = new GenericResponse($httpResponse);
-
- self::assertSame($httpResponse, $response->toHttpResponse());
- }
-}
--- a/tests/lib/Http/WellKnown/JrdResponseTest.php
+++ /dev/null
@@ -1,92 +0,0 @@
-toHttpResponse();
-
- self::assertTrue($response->isEmpty());
- self::assertInstanceOf(JSONResponse::class, $httpResponse);
- /** @var JSONResponse $httpResponse */
- self::assertEquals(
- [
- 'subject' => 'subject',
- ],
- $httpResponse->getData()
- );
- }
-
- public function testComplexToHttpResponse(): void {
- $response = new JrdResponse('subject');
- $response->addAlias('alias');
- $response->addAlias('blias');
- $response->addProperty('propa', 'a');
- $response->addProperty('propb', null);
- $response->setExpires('tomorrow');
- $response->addLink('rel', null, null);
- $response->addLink('rel', 'type', null);
- $response->addLink('rel', 'type', 'href', ['title' => 'titlevalue']);
- $response->addLink('rel', 'type', 'href', ['title' => 'titlevalue'], ['propx' => 'valx']);
- $httpResponse = $response->toHttpResponse();
-
- self::assertFalse($response->isEmpty());
- self::assertInstanceOf(JSONResponse::class, $httpResponse);
- /** @var JSONResponse $httpResponse */
- self::assertEquals(
- [
- 'subject' => 'subject',
- 'aliases' => [
- 'alias',
- 'blias',
- ],
- 'properties' => [
- 'propa' => 'a',
- 'propb' => null,
- ],
- 'expires' => 'tomorrow',
- 'links' => [
- [
- 'rel' => 'rel',
- ],
- [
- 'rel' => 'rel',
- 'type' => 'type',
- ],
- [
- 'rel' => 'rel',
- 'type' => 'type',
- 'href' => 'href',
- 'titles' => [
- 'title' => 'titlevalue',
- ],
- ],
- [
- 'rel' => 'rel',
- 'type' => 'type',
- 'href' => 'href',
- 'titles' => [
- 'title' => 'titlevalue',
- ],
- 'properties' => [
- 'propx' => 'valx',
- ],
- ],
- ]
- ],
- $httpResponse->getData()
- );
- }
-}
--- a/tests/lib/Http/WellKnown/RequestManagerTest.php
+++ /dev/null
@@ -1,149 +0,0 @@
-coordinator = $this->createMock(Coordinator::class);
- $this->container = $this->createMock(ContainerInterface::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $this->manager = new RequestManager(
- $this->coordinator,
- $this->container,
- $this->logger,
- );
- }
-
- public function testProcessAppsNotRegistered(): void {
- $request = $this->createMock(IRequest::class);
- $this->expectException(RuntimeException::class);
-
- $this->manager->process('webfinger', $request);
- }
-
- public function testProcessNoHandlersRegistered(): void {
- $request = $this->createMock(IRequest::class);
- $registrationContext = $this->createMock(RegistrationContext::class);
- $this->coordinator->expects(self::once())
- ->method('getRegistrationContext')
- ->willReturn($registrationContext);
- $registrationContext->expects(self::once())
- ->method('getWellKnownHandlers')
- ->willReturn([]);
-
- $response = $this->manager->process('webfinger', $request);
-
- self::assertNull($response);
- }
-
- public function testProcessHandlerNotLoadable(): void {
- $request = $this->createMock(IRequest::class);
- $registrationContext = $this->createMock(RegistrationContext::class);
- $this->coordinator->expects(self::once())
- ->method('getRegistrationContext')
- ->willReturn($registrationContext);
- $handler = new class {
- };
- $registrationContext->expects(self::once())
- ->method('getWellKnownHandlers')
- ->willReturn([
- new ServiceRegistration('test', get_class($handler)),
- ]);
- $this->container->expects(self::once())
- ->method('get')
- ->with(get_class($handler))
- ->willThrowException(new QueryException(''));
- $this->logger->expects(self::once())
- ->method('error');
-
- $response = $this->manager->process('webfinger', $request);
-
- self::assertNull($response);
- }
-
- public function testProcessHandlerOfWrongType(): void {
- $request = $this->createMock(IRequest::class);
- $registrationContext = $this->createMock(RegistrationContext::class);
- $this->coordinator->expects(self::once())
- ->method('getRegistrationContext')
- ->willReturn($registrationContext);
- $handler = new class {
- };
- $registrationContext->expects(self::once())
- ->method('getWellKnownHandlers')
- ->willReturn([
- new ServiceRegistration('test', get_class($handler)),
- ]);
- $this->container->expects(self::once())
- ->method('get')
- ->with(get_class($handler))
- ->willReturn($handler);
- $this->logger->expects(self::once())
- ->method('error');
-
- $response = $this->manager->process('webfinger', $request);
-
- self::assertNull($response);
- }
-
- public function testProcess(): void {
- $request = $this->createMock(IRequest::class);
- $registrationContext = $this->createMock(RegistrationContext::class);
- $this->coordinator->expects(self::once())
- ->method('getRegistrationContext')
- ->willReturn($registrationContext);
- $handler = new class implements IHandler {
- #[\Override]
- public function handle(string $service, IRequestContext $context, ?IResponse $previousResponse): ?IResponse {
- return (new JrdResponse($service))->addAlias('alias');
- }
- };
- $registrationContext->expects(self::once())
- ->method('getWellKnownHandlers')
- ->willReturn([
- new ServiceRegistration('test', get_class($handler)),
- ]);
- $this->container->expects(self::once())
- ->method('get')
- ->with(get_class($handler))
- ->willReturn($handler);
-
- $response = $this->manager->process('webfinger', $request);
-
- self::assertNotNull($response);
- self::assertInstanceOf(JrdResponse::class, $response);
- }
-}
--- a/tests/lib/HttpRequestLogger.php
+++ /dev/null
@@ -1,106 +0,0 @@
-registerService(IClientService::class, static fn (): IClientService
- => new self(Server::get(ClientService::class), $logFile));
-
- // A test running in a separate process re-runs the bootstrap, so only the
- // process that owns the run may truncate. Children inherit the marker.
- if (getenv(self::RUN_MARKER) === false) {
- putenv(self::RUN_MARKER . '=' . getmypid());
- file_put_contents($logFile, '');
- }
- }
-
- #[\Override]
- public function newClient(?callable $handler = null): IClient {
- $next = $handler ?? new CurlHandler();
-
- return $this->inner->newClient(
- function (RequestInterface $request, array $options) use ($next): PromiseInterface {
- $start = microtime(true);
-
- return $next($request, $options)->then(
- function (ResponseInterface $response) use ($request, $start): ResponseInterface {
- $this->record($request, $start, (string)$response->getStatusCode());
- return $response;
- },
- function (mixed $reason) use ($request, $start): PromiseInterface {
- $this->record($request, $start, 'error');
- return Create::rejectionFor($reason);
- },
- );
- },
- );
- }
-
- private function record(RequestInterface $request, float $start, string $outcome): void {
- $line = json_encode([
- 'test' => self::currentTest(),
- 'method' => $request->getMethod(),
- 'uri' => (string)$request->getUri(),
- 'outcome' => $outcome,
- 'duration' => round(microtime(true) - $start, 6),
- ], JSON_INVALID_UTF8_SUBSTITUTE | JSON_UNESCAPED_SLASHES);
-
- if ($line !== false) {
- file_put_contents($this->logFile, $line . "\n", FILE_APPEND);
- }
- }
-
- /** PHPUnit exposes no global for the running test, so walk the stack for it. */
- private static function currentTest(): string {
- $test = '(unknown)';
-
- foreach (debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS) as $frame) {
- $class = $frame['class'] ?? '';
- $function = $frame['function'] ?? '';
-
- if ($class !== ''
- && str_starts_with($function, 'test')
- && is_subclass_of($class, \PHPUnit\Framework\TestCase::class)
- ) {
- $test = $class . '::' . $function;
- }
- }
-
- return $test;
- }
-}
--- a/tests/lib/ImageTest.php
+++ /dev/null
@@ -1,384 +0,0 @@
-loadFromFile(OC::$SERVERROOT . '/tests/data/testimage.png');
- $this->assertInstanceOf('\OC\Image', $img);
- $this->assertInstanceOf('\OCP\IImage', $img);
- unset($img);
-
- $imgcreate = imagecreatefromjpeg(OC::$SERVERROOT . '/tests/data/testimage.jpg');
- $img = new Image();
- $img->setResource($imgcreate);
- $this->assertInstanceOf('\OC\Image', $img);
- $this->assertInstanceOf('\OCP\IImage', $img);
- unset($img);
-
- $base64 = base64_encode(file_get_contents(OC::$SERVERROOT . '/tests/data/testimage.gif'));
- $img = new Image();
- $img->loadFromBase64($base64);
- $this->assertInstanceOf('\OC\Image', $img);
- $this->assertInstanceOf('\OCP\IImage', $img);
- unset($img);
-
- $img = new Image();
- $this->assertInstanceOf('\OC\Image', $img);
- $this->assertInstanceOf('\OCP\IImage', $img);
- unset($img);
- }
-
- public function testValid(): void {
- $img = new Image();
- $img->loadFromFile(OC::$SERVERROOT . '/tests/data/testimage.png');
- $this->assertTrue($img->valid());
-
- $text = base64_encode('Lorem ipsum dolor sir amet …');
- $img = new Image();
- $img->loadFromBase64($text);
- $this->assertFalse($img->valid());
-
- $img = new Image();
- $this->assertFalse($img->valid());
- }
-
- public function testMimeType(): void {
- $img = new Image();
- $img->loadFromFile(OC::$SERVERROOT . '/tests/data/testimage.png');
- $this->assertEquals('image/png', $img->mimeType());
-
- $img = new Image();
- $this->assertEquals('', $img->mimeType());
-
- $img = new Image();
- $img->loadFromData(file_get_contents(OC::$SERVERROOT . '/tests/data/testimage.jpg'));
- $this->assertEquals('image/jpeg', $img->mimeType());
-
- $img = new Image();
- $img->loadFromBase64(base64_encode(file_get_contents(OC::$SERVERROOT . '/tests/data/testimage.gif')));
- $this->assertEquals('image/gif', $img->mimeType());
- }
-
- public function testWidth(): void {
- $img = new Image();
- $img->loadFromFile(OC::$SERVERROOT . '/tests/data/testimage.png');
- $this->assertEquals(128, $img->width());
-
- $img = new Image();
- $img->loadFromData(file_get_contents(OC::$SERVERROOT . '/tests/data/testimage.jpg'));
- $this->assertEquals(1680, $img->width());
-
- $img = new Image();
- $img->loadFromBase64(base64_encode(file_get_contents(OC::$SERVERROOT . '/tests/data/testimage.gif')));
- $this->assertEquals(64, $img->width());
-
- $img = new Image();
- $this->assertEquals(-1, $img->width());
- }
-
- public function testHeight(): void {
- $img = new Image();
- $img->loadFromFile(OC::$SERVERROOT . '/tests/data/testimage.png');
- $this->assertEquals(128, $img->height());
-
- $img = new Image();
- $img->loadFromData(file_get_contents(OC::$SERVERROOT . '/tests/data/testimage.jpg'));
- $this->assertEquals(1050, $img->height());
-
- $img = new Image();
- $img->loadFromBase64(base64_encode(file_get_contents(OC::$SERVERROOT . '/tests/data/testimage.gif')));
- $this->assertEquals(64, $img->height());
-
- $img = new Image();
- $this->assertEquals(-1, $img->height());
- }
-
- public function testSave(): void {
- $img = new Image();
- $img->loadFromFile(OC::$SERVERROOT . '/tests/data/testimage.png');
- $img->resize(16);
- $img->save(OC::$SERVERROOT . '/tests/data/testimage2.png');
- $this->assertEquals(file_get_contents(OC::$SERVERROOT . '/tests/data/testimage2.png'), $img->data());
-
- $img = new Image();
- $img->loadFromFile(OC::$SERVERROOT . '/tests/data/testimage.jpg');
- $img->resize(128);
- $img->save(OC::$SERVERROOT . '/tests/data/testimage2.jpg');
- $this->assertEquals(file_get_contents(OC::$SERVERROOT . '/tests/data/testimage2.jpg'), $img->data());
- }
-
- public function testData(): void {
- $img = new Image();
- $img->loadFromFile(OC::$SERVERROOT . '/tests/data/testimage.png');
- $raw = imagecreatefromstring(file_get_contents(OC::$SERVERROOT . '/tests/data/testimage.png'));
- // Preserve transparency
- imagealphablending($raw, true);
- imagesavealpha($raw, true);
- ob_start();
- imagepng($raw);
- $expected = ob_get_clean();
- $this->assertEquals($expected, $img->data());
-
- $appConfig = $this->createMock(IAppConfig::class);
- $appConfig->expects($this->once())
- ->method('getValueInt')
- ->with('preview', 'jpeg_quality', 80)
- ->willReturn(80);
- $config = $this->createMock(IConfig::class);
- $config->expects($this->once())
- ->method('getSystemValueInt')
- ->with('preview_max_memory', 256)
- ->willReturn(256);
- $img = new Image(null, $appConfig, $config);
- $img->loadFromFile(OC::$SERVERROOT . '/tests/data/testimage.jpg');
- $raw = imagecreatefromstring(file_get_contents(OC::$SERVERROOT . '/tests/data/testimage.jpg'));
- imageinterlace($raw, true);
- ob_start();
- imagejpeg($raw, null, 80);
- $expected = ob_get_clean();
- $this->assertEquals($expected, $img->data());
-
- $img = new Image();
- $img->loadFromFile(OC::$SERVERROOT . '/tests/data/testimage.gif');
- $raw = imagecreatefromstring(file_get_contents(OC::$SERVERROOT . '/tests/data/testimage.gif'));
- ob_start();
- imagegif($raw);
- $expected = ob_get_clean();
- $this->assertEquals($expected, $img->data());
- }
-
- public function testDataNoResource(): void {
- $img = new Image();
- $this->assertNull($img->data());
- }
-
- #[\PHPUnit\Framework\Attributes\Depends('testData')]
- public function testToString(): void {
- $img = new Image();
- $img->loadFromFile(OC::$SERVERROOT . '/tests/data/testimage.png');
- $expected = base64_encode($img->data());
- $this->assertEquals($expected, (string)$img);
-
- $img = new Image();
- $img->loadFromData(file_get_contents(OC::$SERVERROOT . '/tests/data/testimage.jpg'));
- $expected = base64_encode($img->data());
- $this->assertEquals($expected, (string)$img);
-
- $img = new Image();
- $img->loadFromFile(OC::$SERVERROOT . '/tests/data/testimage.gif');
- $expected = base64_encode($img->data());
- $this->assertEquals($expected, (string)$img);
- }
-
- public function testResize(): void {
- $img = new Image();
- $img->loadFromFile(OC::$SERVERROOT . '/tests/data/testimage.png');
- $this->assertTrue($img->resize(32));
- $this->assertEquals(32, $img->width());
- $this->assertEquals(32, $img->height());
-
- $img = new Image();
- $img->loadFromData(file_get_contents(OC::$SERVERROOT . '/tests/data/testimage.jpg'));
- $this->assertTrue($img->resize(840));
- $this->assertEquals(840, $img->width());
- $this->assertEquals(525, $img->height());
-
- $img = new Image();
- $img->loadFromBase64(base64_encode(file_get_contents(OC::$SERVERROOT . '/tests/data/testimage.gif')));
- $this->assertTrue($img->resize(100));
- $this->assertEquals(100, $img->width());
- $this->assertEquals(100, $img->height());
- }
-
- public function testPreciseResize(): void {
- $img = new Image();
- $img->loadFromFile(OC::$SERVERROOT . '/tests/data/testimage.png');
- $this->assertTrue($img->preciseResize(128, 512));
- $this->assertEquals(128, $img->width());
- $this->assertEquals(512, $img->height());
-
- $img = new Image();
- $img->loadFromData(file_get_contents(OC::$SERVERROOT . '/tests/data/testimage.jpg'));
- $this->assertTrue($img->preciseResize(64, 840));
- $this->assertEquals(64, $img->width());
- $this->assertEquals(840, $img->height());
-
- $img = new Image();
- $img->loadFromBase64(base64_encode(file_get_contents(OC::$SERVERROOT . '/tests/data/testimage.gif')));
- $this->assertTrue($img->preciseResize(1000, 1337));
- $this->assertEquals(1000, $img->width());
- $this->assertEquals(1337, $img->height());
- }
-
- public function testCenterCrop(): void {
- $img = new Image();
- $img->loadFromFile(OC::$SERVERROOT . '/tests/data/testimage.png');
- $img->centerCrop();
- $this->assertEquals(128, $img->width());
- $this->assertEquals(128, $img->height());
-
- $img = new Image();
- $img->loadFromData(file_get_contents(OC::$SERVERROOT . '/tests/data/testimage.jpg'));
- $img->centerCrop();
- $this->assertEquals(1050, $img->width());
- $this->assertEquals(1050, $img->height());
-
- $img = new Image();
- $img->loadFromBase64(base64_encode(file_get_contents(OC::$SERVERROOT . '/tests/data/testimage.gif')));
- $img->centerCrop(512);
- $this->assertEquals(512, $img->width());
- $this->assertEquals(512, $img->height());
- }
-
- public function testCrop(): void {
- $img = new Image();
- $img->loadFromFile(OC::$SERVERROOT . '/tests/data/testimage.png');
- $this->assertTrue($img->crop(0, 0, 50, 20));
- $this->assertEquals(50, $img->width());
- $this->assertEquals(20, $img->height());
-
- $img = new Image();
- $img->loadFromData(file_get_contents(OC::$SERVERROOT . '/tests/data/testimage.jpg'));
- $this->assertTrue($img->crop(500, 700, 550, 300));
- $this->assertEquals(550, $img->width());
- $this->assertEquals(300, $img->height());
-
- $img = new Image();
- $img->loadFromBase64(base64_encode(file_get_contents(OC::$SERVERROOT . '/tests/data/testimage.gif')));
- $this->assertTrue($img->crop(10, 10, 15, 15));
- $this->assertEquals(15, $img->width());
- $this->assertEquals(15, $img->height());
- }
-
- public static function sampleProvider(): array {
- return [
- ['testimage.png', [200, 100], [100, 100]],
- ['testimage.jpg', [840, 840], [840, 525]],
- ['testimage.gif', [200, 250], [200, 200]]
- ];
- }
-
- /**
- *
- * @param string $filename
- * @param int[] $asked
- * @param int[] $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('sampleProvider')]
- public function testFitIn($filename, $asked, $expected): void {
- $img = new Image();
- $img->loadFromFile(OC::$SERVERROOT . '/tests/data/' . $filename);
- $this->assertTrue($img->fitIn($asked[0], $asked[1]));
- $this->assertEquals($expected[0], $img->width());
- $this->assertEquals($expected[1], $img->height());
- }
-
- public static function sampleFilenamesProvider(): array {
- return [
- ['testimage.png'],
- ['testimage.jpg'],
- ['testimage.gif']
- ];
- }
-
- /**
- * Image should not be resized if it's already smaller than what is required
- *
- *
- * @param string $filename
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('sampleFilenamesProvider')]
- public function testScaleDownToFitWhenSmallerAlready($filename): void {
- $img = new Image();
- $img->loadFromFile(OC::$SERVERROOT . '/tests/data/' . $filename);
- $currentWidth = $img->width();
- $currentHeight = $img->height();
- // We pick something larger than the image we want to scale down
- $this->assertFalse($img->scaleDownToFit(4000, 4000));
- // The dimensions of the image should not have changed since it's smaller already
- $resizedWidth = $img->width();
- $resizedHeight = $img->height();
- $this->assertEquals(
- $currentWidth, $img->width(), "currentWidth $currentWidth resizedWidth $resizedWidth \n"
- );
- $this->assertEquals(
- $currentHeight, $img->height(),
- "currentHeight $currentHeight resizedHeight $resizedHeight \n"
- );
- }
-
- public static function largeSampleProvider(): array {
- return [
- ['testimage.png', [200, 100], [100, 100]],
- ['testimage.jpg', [840, 840], [840, 525]],
- ];
- }
-
- /**
- *
- * @param string $filename
- * @param int[] $asked
- * @param int[] $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('largeSampleProvider')]
- public function testScaleDownWhenBigger($filename, $asked, $expected): void {
- $img = new Image();
- $img->loadFromFile(OC::$SERVERROOT . '/tests/data/' . $filename);
- //$this->assertTrue($img->scaleDownToFit($asked[0], $asked[1]));
- $img->scaleDownToFit($asked[0], $asked[1]);
- $this->assertEquals($expected[0], $img->width());
- $this->assertEquals($expected[1], $img->height());
- }
-
- public static function convertDataProvider(): array {
- return [
- [ 'image/gif'],
- [ 'image/jpeg'],
- [ 'image/png'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('convertDataProvider')]
- public function testConvert($mimeType): void {
- $img = new Image();
- $img->loadFromFile(OC::$SERVERROOT . '/tests/data/testimage.png');
- $tempFile = tempnam(sys_get_temp_dir(), 'img-test');
-
- $img->save($tempFile, $mimeType);
- $this->assertEquals($mimeType, image_type_to_mime_type(exif_imagetype($tempFile)));
- }
-
- public function testMemoryLimitFromFile(): void {
- $img = new Image();
- $img->loadFromFile(OC::$SERVERROOT . '/tests/data/testimage-badheader.jpg');
- $this->assertFalse($img->valid());
- }
-
- public function testMemoryLimitFromData(): void {
- $data = file_get_contents(OC::$SERVERROOT . '/tests/data/testimage-badheader.jpg');
- $img = new Image();
- $img->loadFromData($data);
- $this->assertFalse($img->valid());
- }
-}
--- a/tests/lib/InfoXmlTest.php
+++ /dev/null
@@ -1,145 +0,0 @@
-appManager = Server::get(AppManager::class);
- }
-
- public static function dataApps(): array {
- return [
- ['admin_audit'],
- ['comments'],
- ['dav'],
- ['encryption'],
- ['federatedfilesharing'],
- ['federation'],
- ['files'],
- ['files_external'],
- ['files_sharing'],
- ['files_trashbin'],
- ['files_versions'],
- ['provisioning_api'],
- ['systemtags'],
- ['theming'],
- ['settings'],
- ['twofactor_backupcodes'],
- ['updatenotification'],
- ['user_ldap'],
- ['workflowengine'],
- ];
- }
-
- /**
- * @param string $app
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataApps')]
- public function testClasses($app): void {
- $appInfo = $this->appManager->getAppInfo($app);
- $appPath = $this->appManager->getAppPath($app);
- $this->appManager->registerAutoloading($app, $appPath);
-
- //Add the appcontainer
- $applicationClassName = $this->appManager->getAppNamespace($app) . '\\AppInfo\\Application';
- if (class_exists($applicationClassName)) {
- $application = new $applicationClassName();
- $this->addToAssertionCount(1);
- } else {
- $application = new App($app);
- $this->addToAssertionCount(1);
- }
-
- if (isset($appInfo['background-jobs'])) {
- foreach ($appInfo['background-jobs'] as $job) {
- $this->assertTrue(class_exists($job), 'Asserting background job "' . $job . '" exists');
- $this->assertInstanceOf($job, Server::get($job));
- }
- }
-
- if (isset($appInfo['two-factor-providers'])) {
- foreach ($appInfo['two-factor-providers'] as $provider) {
- $this->assertTrue(class_exists($provider), 'Asserting two-factor providers "' . $provider . '" exists');
- $this->assertInstanceOf($provider, Server::get($provider));
- }
- }
-
- if (isset($appInfo['commands'])) {
- foreach ($appInfo['commands'] as $command) {
- $this->assertTrue(class_exists($command), 'Asserting command "' . $command . '" exists');
- $this->assertInstanceOf($command, Server::get($command));
- }
- }
-
- if (isset($appInfo['repair-steps']['pre-migration'])) {
- foreach ($appInfo['repair-steps']['pre-migration'] as $migration) {
- $this->assertTrue(class_exists($migration), 'Asserting pre-migration "' . $migration . '" exists');
- $this->assertInstanceOf($migration, Server::get($migration));
- }
- }
-
- if (isset($appInfo['repair-steps']['post-migration'])) {
- foreach ($appInfo['repair-steps']['post-migration'] as $migration) {
- $this->assertTrue(class_exists($migration), 'Asserting post-migration "' . $migration . '" exists');
- $this->assertInstanceOf($migration, Server::get($migration));
- }
- }
-
- if (isset($appInfo['repair-steps']['live-migration'])) {
- foreach ($appInfo['repair-steps']['live-migration'] as $migration) {
- $this->assertTrue(class_exists($migration), 'Asserting live-migration "' . $migration . '" exists');
- $this->assertInstanceOf($migration, Server::get($migration));
- }
- }
-
- if (isset($appInfo['repair-steps']['install'])) {
- foreach ($appInfo['repair-steps']['install'] as $migration) {
- $this->assertTrue(class_exists($migration), 'Asserting install-migration "' . $migration . '" exists');
- $this->assertInstanceOf($migration, Server::get($migration));
- }
- }
-
- if (isset($appInfo['repair-steps']['uninstall'])) {
- foreach ($appInfo['repair-steps']['uninstall'] as $migration) {
- $this->assertTrue(class_exists($migration), 'Asserting uninstall-migration "' . $migration . '" exists');
- $this->assertInstanceOf($migration, Server::get($migration));
- }
- }
-
- if (isset($appInfo['commands'])) {
- foreach ($appInfo['commands'] as $command) {
- $this->assertTrue(class_exists($command), 'Asserting command "' . $command . '"exists');
- $this->assertInstanceOf($command, Server::get($command));
- }
- }
-
- if (isset($appInfo['openmetrics']['exporter'])) {
- foreach ($appInfo['openmetrics']['exporter'] as $class) {
- $this->assertTrue(class_exists($class), 'Asserting exporter "' . $class . '"exists');
- $exporter = Server::get($class);
- $this->assertInstanceOf($class, $exporter);
- $this->assertInstanceOf(IMetricFamily::class, $exporter);
- }
- }
- }
-}
--- a/tests/lib/InitialStateServiceTest.php
+++ /dev/null
@@ -1,101 +0,0 @@
-logger = $this->createMock(LoggerInterface::class);
-
- $this->service = new InitialStateService(
- $this->logger,
- $this->createMock(Coordinator::class),
- $this->createMock(ContainerInterface::class)
- );
- }
-
- public static function staticData(): array {
- return [
- ['string'],
- [23],
- [2.3],
- [new class implements JsonSerializable {
- #[\Override]
- public function jsonSerialize(): int {
- return 3;
- }
- }],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('staticData')]
- public function testStaticData(mixed $value): void {
- $this->service->provideInitialState('test', 'key', $value);
- $data = $this->service->getInitialStates();
-
- $this->assertEquals(
- ['test-key' => json_encode($value)],
- $data
- );
- }
-
- public function testValidDataButFailsToJSONEncode(): void {
- $this->logger->expects($this->once())
- ->method('error');
-
- $this->service->provideInitialState('test', 'key', ['upload' => INF]);
- $data = $this->service->getInitialStates();
-
- $this->assertEquals(
- [],
- $data
- );
- }
-
- public function testStaticButInvalidData(): void {
- $this->logger->expects($this->once())
- ->method('warning');
-
- $this->service->provideInitialState('test', 'key', new stdClass());
- $data = $this->service->getInitialStates();
-
- $this->assertEquals(
- [],
- $data
- );
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('staticData')]
- public function testLazyData(mixed $value): void {
- $this->service->provideLazyInitialState('test', 'key', function () use ($value) {
- return $value;
- });
- $data = $this->service->getInitialStates();
-
- $this->assertEquals(
- ['test-key' => json_encode($value)],
- $data
- );
- }
-}
--- a/tests/lib/Install/Events/InstallationCompletedEventTest.php
+++ /dev/null
@@ -1,89 +0,0 @@
-assertEquals($dataDir, $event->getDataDirectory());
- $this->assertEquals($adminUsername, $event->getAdminUsername());
- $this->assertEquals($adminEmail, $event->getAdminEmail());
- $this->assertTrue($event->hasAdminUser());
- }
-
- public function testConstructorWithMinimalParameters(): void {
- $dataDir = '/path/to/data';
-
- $event = new InstallationCompletedEvent($dataDir);
-
- $this->assertEquals($dataDir, $event->getDataDirectory());
- $this->assertNull($event->getAdminUsername());
- $this->assertNull($event->getAdminEmail());
- $this->assertFalse($event->hasAdminUser());
- }
-
- public function testConstructorWithUsernameOnly(): void {
- $dataDir = '/path/to/data';
- $adminUsername = 'admin';
-
- $event = new InstallationCompletedEvent($dataDir, $adminUsername);
-
- $this->assertEquals($dataDir, $event->getDataDirectory());
- $this->assertEquals($adminUsername, $event->getAdminUsername());
- $this->assertNull($event->getAdminEmail());
- $this->assertTrue($event->hasAdminUser());
- }
-
- public function testConstructorWithUsernameAndEmail(): void {
- $dataDir = '/path/to/data';
- $adminUsername = 'admin';
- $adminEmail = 'admin@example.com';
-
- $event = new InstallationCompletedEvent($dataDir, $adminUsername, $adminEmail);
-
- $this->assertEquals($dataDir, $event->getDataDirectory());
- $this->assertEquals($adminUsername, $event->getAdminUsername());
- $this->assertEquals($adminEmail, $event->getAdminEmail());
- $this->assertTrue($event->hasAdminUser());
- }
-
- public function testHasAdminUserReturnsFalseWhenUsernameIsNull(): void {
- $event = new InstallationCompletedEvent('/path/to/data', null, 'admin@example.com');
-
- $this->assertFalse($event->hasAdminUser());
- $this->assertNull($event->getAdminUsername());
- $this->assertEquals('admin@example.com', $event->getAdminEmail());
- }
-
- public function testDataDirectoryCanBeAnyString(): void {
- $customPath = '/custom/data/directory';
- $event = new InstallationCompletedEvent($customPath);
-
- $this->assertEquals($customPath, $event->getDataDirectory());
- }
-
- public function testEmailCanBeSetWithoutUsername(): void {
- $dataDir = '/path/to/data';
- $email = 'admin@example.com';
-
- $event = new InstallationCompletedEvent($dataDir, null, $email);
-
- $this->assertNull($event->getAdminUsername());
- $this->assertEquals($email, $event->getAdminEmail());
- $this->assertFalse($event->hasAdminUser());
- }
-}
--- a/tests/lib/InstallerTest.php
+++ /dev/null
@@ -1,689 +0,0 @@
-appFetcher = $this->createMock(AppFetcher::class);
- $this->clientService = $this->createMock(IClientService::class);
- $this->tempManager = $this->createMock(ITempManager::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->config = $this->createMock(IConfig::class);
- $this->appManager = $this->createMock(AppManager::class);
- $this->l10nFactory = $this->createMock(IFactory::class);
-
- $installer = Server::get(Installer::class);
- $installer->removeApp(self::$appid);
- }
-
- protected function getInstaller() {
- return new Installer(
- $this->appFetcher,
- $this->clientService,
- $this->tempManager,
- $this->logger,
- $this->config,
- $this->appManager,
- $this->l10nFactory,
- false
- );
- }
-
- #[\Override]
- protected function tearDown(): void {
- $installer = Server::get(Installer::class);
- $installer->removeApp(self::$appid);
-
- parent::tearDown();
- }
-
- public function testInstallApp(): void {
- // Read the current version of the app to check for bug #2572
- Server::get(IAppManager::class)->getAppVersion('testapp', true);
-
- // Build installer
- $installer = Server::get(Installer::class);
-
- // Extract app
- $pathOfTestApp = __DIR__ . '/../data/testapp.zip';
- $tar = new ZIP($pathOfTestApp);
- $tar->extract($installer->getInstallPath());
-
- // Install app
- $this->assertNull(Server::get(IConfig::class)->getAppValue('testapp', 'enabled', null), 'Check that the app is not listed before installation');
- $this->assertSame('testapp', $installer->installApp(self::$appid));
- $this->assertSame('no', Server::get(IConfig::class)->getAppValue('testapp', 'enabled', null), 'Check that the app is listed after installation');
- $this->assertSame('0.9', Server::get(IConfig::class)->getAppValue('testapp', 'installed_version'));
- $installer->removeApp(self::$appid);
- }
-
- public static function updateArrayProvider(): array {
- return [
- // Update available
- [
- [
- [
- 'id' => 'files',
- 'releases' => [
- [
- 'version' => '1111.0'
- ],
- ],
- ],
- ],
- '1111.0',
- ],
- // No update available
- [
- [
- [
- 'id' => 'files',
- 'releases' => [
- [
- 'version' => '1.0'
- ],
- ],
- ],
- ],
- false,
- ],
- ];
- }
-
- /**
- * @param array $appArray
- * @param string|bool $updateAvailable
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('updateArrayProvider')]
- public function testIsUpdateAvailable(array $appArray, $updateAvailable): void {
- $this->appFetcher
- ->expects($this->once())
- ->method('get')
- ->willReturn($appArray);
- $this->appManager
- ->expects($this->exactly(2))
- ->method('getAppVersion')
- ->willReturn('1.0');
-
- $installer = $this->getInstaller();
- $this->assertSame($updateAvailable, $installer->isUpdateAvailable('files'));
- $this->assertSame($updateAvailable, $installer->isUpdateAvailable('files'), 'Cached result should be returned and fetcher should be only called once');
- }
-
- public function testDownloadAppWithRevokedCertificate(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Certificate "4112" has been revoked');
-
- $appArray = [
- [
- 'id' => 'news',
- 'certificate' => '-----BEGIN CERTIFICATE-----
-MIIEAjCCAuoCAhAQMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
-VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
-MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
-dXRob3JpdHkwHhcNMTYxMDAzMTMyNDM3WhcNMjcwMTA5MTMyNDM3WjASMRAwDgYD
-VQQDDAdwYXNzbWFuMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEApEt+
-KZGs+WqdZkHZflzqk+ophYWB8qB47XCzy+xdTGPFM84/9wXltRPbcQQWJJl5aOx0
-FPbsyTGhIt/IYZ2Vl0XrDRJjsaxzPcrofrwpJ2tqforXjGohl6mZUBA0ESzFiPzT
-SAZe8E14+Jk8rbF/ecrkqcWf2cTMV3Qfu9YvJo8WVs4lHc95r1F+Nalh/OLkHkzb
-fYPno2Z5cco6U7BXunFQG2gqy3wWQwmlhDxh5fwrCoFzPWm7WhwSyK+eMoSDz+Vp
-3kmtyijsqnda0zA9bfNzgW26czbJaObbnkdtDC2nfoAWXndlS/5YRI8yHd9miB5C
-u1OC8LUWToDGNa9+FOxBSj7Nk6iyjbVfRXcTqThdkVZdOOPaBRMsL9R4UYywCbhA
-yGNiQ0ahfXD8MZSb08rlQg8tAtcUZW1sYQcbtMGnu8OyC5J7N1efzv5mys4+9hBS
-5ECeyCuQTuOkF4H/XS2BMSFZWF2xh7wzhMLca+5yauDW4i8baFEv74QTeY1DADgI
-Lz29NJ6z9xYzEnPesjNrwIcJwIjV52EkdLTi+EIf83UjXLQdwDbLxu76qxqP7K0I
-oMmwbl7UNA0wzq7nmgRhvqhow5RoCaSJjTz0EYQVSa1xelwiKeJiSKj2G9Mgt5Ms
-Miuy3C3VAGvQJ2ocILPGOt54oVeNRFLpnCo1e3sCAwEAATANBgkqhkiG9w0BAQsF
-AAOCAQEAkGYtg21rGpUVT/AokGUfI0PeyYAkcXKy2yuBAzfRk+uIXnRR0vK+OMpx
-shBoYGR3JEGUHZcMTRh8wjAZ0wuyYlQONtJbFFF3bCfODXxCsw0Vm8/Ms+KCmE4Z
-SyQafWEQf1sdqNw4VS4DYS2mlpDgAl+U9UY6HQKuT3+GFIxCsQSdS0GTaiYVKPVE
-p/eKou739h+5dM4FEhIYZX+7PWlHmX6wPCFAjgNu3kiRGmF6LKmCNNXTySATEP86
-tczQMzLtVdTg5z8XMi//6TkAPxRPjYi8Vef/s2mLo7KystTmofxI/HZePSieJ9tj
-gLgK8d8sKL60JMmKHN3boHrsThKBVA==
------END CERTIFICATE-----',
- ],
- ];
- $this->appFetcher
- ->expects($this->once())
- ->method('get')
- ->willReturn($appArray);
-
- $installer = $this->getInstaller();
- $installer->downloadApp('news');
- }
-
- public function testDownloadAppWithNotNextcloudCertificate(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('App with id news has a certificate not issued by a trusted Code Signing Authority');
-
- $appArray = [
- [
- 'id' => 'news',
- 'certificate' => '-----BEGIN CERTIFICATE-----
-MIID8TCCAdkCAhAAMA0GCSqGSIb3DQEBCwUAMG0xCzAJBgNVBAYTAlVTMQ8wDQYD
-VQQIDAZCb3N0b24xFjAUBgNVBAoMDW93bkNsb3VkIEluYy4xNTAzBgNVBAMMLG93
-bkNsb3VkIENvZGUgU2lnbmluZyBJbnRlcm1lZGlhdGUgQXV0aG9yaXR5MB4XDTE2
-MDIwMzE3NTE0OVoXDTI2MDEzMTE3NTE0OVowDzENMAsGA1UEAwwEY29yZTCCASIw
-DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPHdSljnHI+ueQd27UyWPO9n4Lqt
-bK0kdekiC3si7Mee7uXXJaGuqXJozHEZYB1LIFLdCU/itCxEk9hyLcyNzeT+nRT/
-zDuOYdbLgCj7/A5bX+u3jc29UlCYybSFchfMdvn7a0njCna4dE+73b4yEj16tS2h
-S1EUygSzgicWlJqMD3Z9Qc+zLEpdhq9oDdDB8HURi2NW4KzIraVncSH+zF1QduOh
-nERDnF8x48D3FLdTxGA0W/Kg4gYsq4NRvU6g3DJNdp4YfqRSFMmLFDCgzDuhan7D
-wgRlI9NAeHbnyoUPtrDBUceI7shIbC/i87xk9ptqV0AyFonkJtK6lWwZjNkCAwEA
-ATANBgkqhkiG9w0BAQsFAAOCAgEAAMgymqZE1YaHYlRGwvTE7gGDY3gmFOMaxQL4
-E5m0CnkBz4BdIPRsQFFdOv3l/MIWkw5ED3vUB925VpQZYFSiEuv5NbnlPaHZlIMI
-n8AV/sTP5jue3LhtAN4EM63xNBhudAT6wVsvGwOuQOx9Xv+ptO8Po7sTuNYP0CMH
-EOQN+/q8tYlSm2VW+dAlaJ+zVZwZldhVjL+lSH4E9ktWn3PmgNQeKfcnJISUbus6
-ZtsYDF/X96/Z2ZQvMXOKksgvU6XlvIxllcyebC9Bxe/h0D63GCO2tqN5CWQzIIqn
-apUynPX8BlLaaExqYGERwlUi/yOGaUVPUjEPVehviOQYgAqxlrkJk1dWeCrwUori
-CXpi+IUYkidfgiJ9F88M3ElpwqIaXp7G3/4oHBuE2u6M+L+1/vqPJeTCAWUxxpJE
-yYmM+db6D4TySFpQPENNzPS8bpR6T8w2hRumkldC42HrnyJJbpjOieTXhXzjdPvZ
-IEP9JGtkhB2du6nBF2MNAq2TqRXpcfQrQEbnQ13aV9bl+roTwwO+SOWK/wgvdOMI
-STQ0Xk0sTGlmQjPYPkibVceaWMR3sX4cNt5c33YhJys5jxHoAh42km4nN9tfykR5
-crl5lBlKjXh2GP0+omSO3x1jX4+iQPCW2TWoyKkUdLu/hGHG2w8RrTeme+kATECH
-YSu356M=
------END CERTIFICATE-----',
- ],
- ];
- $this->appFetcher
- ->expects($this->once())
- ->method('get')
- ->willReturn($appArray);
-
- $installer = $this->getInstaller();
- $installer->downloadApp('news');
- }
-
- public function testDownloadAppWithDifferentCN(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('App with id news has a cert issued to passman');
-
- $appArray = [
- [
- 'id' => 'news',
- 'certificate' => '-----BEGIN CERTIFICATE-----
-MIIEAjCCAuoCAhDUMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
-VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
-MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
-dXRob3JpdHkwHhcNMTkwMTMwMTMwMjA5WhcNMjkwNTA3MTMwMjA5WjASMRAwDgYD
-VQQDDAdwYXNzbWFuMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwT1/
-hO/RSUkj0T9a879LPjy4c5V7FBDffEh6H/n1aiOEzofr8vqP3wJ4ZLwIvpZIZNFC
-CY4HjBTIgk+QOlAv2oV2w/8XxkSQ708H3m99GFNRQg9EztjiIeKn7y1HhFOeiVaF
-Eq6R1Tnre8cjzv6/yf1f1EFpPY3ptCefUjdLfpU/YrPhFxGLS+n5hyr8b6EszqKm
-6NhGI09sd1Wb1y8o+dtQIQr24gWeo3l3QGLxjcJQqHCxE38rGdTNd04qDEm69BMD
-Kjk4/JmUBBOn0svg9IAks+4nDnpr3IABfcnKYlmAucVEMNMYfA6kXXWEALsE2yo9
-8Y7GeV8En5Ztn4w3Pt2HMNpJV2m7MWWocSbF+ocp8oJ0cIEcthBubiE2kJhdPi5a
-Yo5Bwh54hx53an+XfiDn+bfidvNz5TsJtmZykB84gLcdBQyMHrZcDcD6g74KdW3X
-du/AnNUlJujhIU0fsw3UUPB845Q8XjbsIK5WhgaQeXJum8HXnXWkEfh6mE4j9l2Z
-6FJVe8fQlF5lspn6z3qYsWlYRalD3N9Qxy3vpRgCAYTPc3D+fbVP9KJw1cWux1+O
-0X/hNWMDOTenhgyQS+mqhRvdWq71mFjP/RXjOSfED3ipFamPofCz9QC7EERtvZPE
-Pr8tBnbxnyTbAOQrN9N2mA9UJbOhk2l5kCSkDpMCAwEAATANBgkqhkiG9w0BAQsF
-AAOCAQEAdVaizjIBQLGFo3uzOVAjh5IoFt/DBiSjVYOOt4HacWDDkRKOmQp3AJaA
-Zecbd+tF7OsIqrBzbbfK7wp1+y1pLrPNFQghhk7gVxBP9LscLQ1JmDK2vHv9rLAg
-d/gv90gf3HVeRQWntAhGTqtiSZkafcXJIRCM4GygoPy2zxeWeCTt85yEbQnuGZ4Z
-qtWS/yyJR5ZQEwYG7jFWRxaZicuUdWBhlUMafRUxzjxBhsQplGKSI66eFQ5VtB7L
-u/spPSSVhaun5BA1FlphB2TkgnzlCmxJa63nFY045e/Jq+IKMcqqZl/092gbI2EQ
-5EpZaQ1l6H5DBXwrz58a8WTPC2Mu8g==
------END CERTIFICATE-----',
- ],
- ];
- $this->appFetcher
- ->expects($this->once())
- ->method('get')
- ->willReturn($appArray);
-
- $installer = $this->getInstaller();
- $installer->downloadApp('news');
- }
-
- public function testDownloadAppWithInvalidSignature(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('App with id passman has invalid signature');
-
- $appArray = [
- [
- 'id' => 'passman',
- 'certificate' => '-----BEGIN CERTIFICATE-----
-MIIEAjCCAuoCAhDUMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
-VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
-MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
-dXRob3JpdHkwHhcNMTkwMTMwMTMwMjA5WhcNMjkwNTA3MTMwMjA5WjASMRAwDgYD
-VQQDDAdwYXNzbWFuMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwT1/
-hO/RSUkj0T9a879LPjy4c5V7FBDffEh6H/n1aiOEzofr8vqP3wJ4ZLwIvpZIZNFC
-CY4HjBTIgk+QOlAv2oV2w/8XxkSQ708H3m99GFNRQg9EztjiIeKn7y1HhFOeiVaF
-Eq6R1Tnre8cjzv6/yf1f1EFpPY3ptCefUjdLfpU/YrPhFxGLS+n5hyr8b6EszqKm
-6NhGI09sd1Wb1y8o+dtQIQr24gWeo3l3QGLxjcJQqHCxE38rGdTNd04qDEm69BMD
-Kjk4/JmUBBOn0svg9IAks+4nDnpr3IABfcnKYlmAucVEMNMYfA6kXXWEALsE2yo9
-8Y7GeV8En5Ztn4w3Pt2HMNpJV2m7MWWocSbF+ocp8oJ0cIEcthBubiE2kJhdPi5a
-Yo5Bwh54hx53an+XfiDn+bfidvNz5TsJtmZykB84gLcdBQyMHrZcDcD6g74KdW3X
-du/AnNUlJujhIU0fsw3UUPB845Q8XjbsIK5WhgaQeXJum8HXnXWkEfh6mE4j9l2Z
-6FJVe8fQlF5lspn6z3qYsWlYRalD3N9Qxy3vpRgCAYTPc3D+fbVP9KJw1cWux1+O
-0X/hNWMDOTenhgyQS+mqhRvdWq71mFjP/RXjOSfED3ipFamPofCz9QC7EERtvZPE
-Pr8tBnbxnyTbAOQrN9N2mA9UJbOhk2l5kCSkDpMCAwEAATANBgkqhkiG9w0BAQsF
-AAOCAQEAdVaizjIBQLGFo3uzOVAjh5IoFt/DBiSjVYOOt4HacWDDkRKOmQp3AJaA
-Zecbd+tF7OsIqrBzbbfK7wp1+y1pLrPNFQghhk7gVxBP9LscLQ1JmDK2vHv9rLAg
-d/gv90gf3HVeRQWntAhGTqtiSZkafcXJIRCM4GygoPy2zxeWeCTt85yEbQnuGZ4Z
-qtWS/yyJR5ZQEwYG7jFWRxaZicuUdWBhlUMafRUxzjxBhsQplGKSI66eFQ5VtB7L
-u/spPSSVhaun5BA1FlphB2TkgnzlCmxJa63nFY045e/Jq+IKMcqqZl/092gbI2EQ
-5EpZaQ1l6H5DBXwrz58a8WTPC2Mu8g==
------END CERTIFICATE-----',
- 'releases' => [
- [
- 'download' => 'https://example.com',
- 'signature' => 'MySignature',
- ],
- [
- 'download' => 'https://nextcloud.com',
- ],
- ],
- ],
- ];
- $this->appFetcher
- ->expects($this->once())
- ->method('get')
- ->willReturn($appArray);
- $realTmpFile = Server::get(ITempManager::class)->getTemporaryFile('.tar.gz');
- copy(__DIR__ . '/../data/testapp.tar.gz', $realTmpFile);
- $this->tempManager
- ->expects($this->once())
- ->method('getTemporaryFile')
- ->with('.tar.gz')
- ->willReturn($realTmpFile);
- $client = $this->createMock(IClient::class);
- $client
- ->expects($this->once())
- ->method('get')
- ->with('https://example.com', ['sink' => $realTmpFile, 'timeout' => 120]);
- $this->clientService
- ->expects($this->once())
- ->method('newClient')
- ->willReturn($client);
-
- $installer = $this->getInstaller();
- $installer->downloadApp('passman');
- }
-
- public function testDownloadAppWithMoreThanOneFolderDownloaded(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Extracted app testapp has more than 1 folder');
-
- $appArray = [
- [
- 'id' => 'testapp',
- 'certificate' => '-----BEGIN CERTIFICATE-----
-MIIEAjCCAuoCAhAbMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
-VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
-MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
-dXRob3JpdHkwHhcNMTYxMDMxMTgxNTI2WhcNMjcwMjA2MTgxNTI2WjASMRAwDgYD
-VQQDEwd0ZXN0YXBwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAqa0x
-FcVa0YcO/ABqSNdbf7Bzp2PBBJzVM9gI4/HzzBKU/NY9/RibBBpNjAIWEFAbTI4j
-ilFSoxHDQ8HrboFOeKCrOIdp9ATQ8SnYVNIQ12Ym3LA/XxcG0gG0H7DeS9C0uACe
-svN8fwD1wnKnLLU9GBzO77jwYkneed85wwKG4waHd3965gxQWq0N5gnYS0TTn7Yr
-l1veRiw+ryefXvfWI0cN1WBZJ/4XAkwVlpG1HP60AunIpcwn9bfG4XCka+7x26E4
-6Hw0Ot7D7j0yzVzimJDPB2h2buEtPVd6m+oNPueVvKGta+p6cEEaHlFVh2Pa9DI+
-me3nb6aXE2kABWXav3BmK18A5Rg4ZY4VFYvmHmxkOhT/ulGZRqy6TccL/optqs52
-KQ6P0e5dfmhLeoCvJObD+ZYKv+kJCRFtX1Hve/R4IHG6XSFKUfrRjyor9b6TX2L/
-l2vV0mFjmy4g3l05vWHg1Edtq7M29S/xNA3/hF29NjBq6NoMbLGcBtFced1iK07Z
-yHLjXRZRfURP671Svqqg8pjxuDqkJ2vIj/Vpod4kF2jeiZYXcfmNKhEhxpkccSe0
-dI6p76Ne7XSUpf8yCPiSnWZLadqKZdEulcB4SlrZO2+/pycgqrqihofDrvDeWeeg
-gQyvbZZKl4ylRNj6IRKnosKLVXNqMHQxLmxLHeUCAwEAATANBgkqhkiG9w0BAQsF
-AAOCAQEALkKQwa40HfuP4Q6ShwBFJbXLyodIAXCT014kBVjReDKNl5oHtMXRjPxj
-nj9doKu+3bLNuLCv9uU3H5+t/GFogReV3Av3z/fCqJ6wHv/KX+lacj31dWXZGD8G
-z+RYibrxKkPN0V6q1mSvkg3hJOOE+/4FPIdc8PNlgratv3WS4dT8QwGSUavHW2Kx
-89nIdnwtLEFpgML/bTG0dm8BH57xER8LCYixW1VmpV6A4IsoKVsnB7KUCRTK3iUJ
-Zh8Xg8UMNrOtXc1Wx1Wmjaa4ZE9dY6/KkU2ny2UWyDHKU/9VE8QQ4HN93gxU4+H7
-cUg0V1uAxqUvKytKkMfcyPWsz/AINA==
------END CERTIFICATE-----',
- 'releases' => [
- [
- 'download' => 'https://example.com',
- 'signature' => 'h8H3tUy2dDlwrV/hY/ZxqYqe8Vue+IINluLtAt1HxX2cjz3vdoVHJRINRkMYYcdz
-VlndvHyKdqJHDAACphR8tVV6EFrPermn7gEgWk7a51LbUM7sAN7RV7ijEooUo+TQ
-jNW9Ch48Wg3jvebMwWNr5t5U4MEXTP5f0YX/kxvkJoUrG3a3spt7ziEuHaq8IPvt
-Jj/JSDFhvRNpom7yNNcI1Ijoq8yC11sg7RJBNfrHdGPHPZVz2SyBiY9OcvgGSpUU
-bfvzhIZDCl/RRi5fs39jLLupAP69Ez6+jylNXEMsNwM0YL5+egSXFtkCvgOw8UBg
-ZqNZZojcS22acuvHRnoa6PDDhwHdCH+zpifXSOhSQvue5n6q+FVX6aeD1LnCQkYB
-D2wvNyZWwdADJtvDj03DKhm21g+TPy63XC94q4IqvjQ94pV8U+qrBBfkQ62NGjaC
-oOU6y5sEmQeAdVRpWVo0Hewmjp4Adoj5JRwuqCVEynTC6DXHs3HvHxYlmib1F05a
-GqEhdDmOHsxNaeJ08Hlptq5yLv3+0wEdtriVjgAZNVduHG1F1FkhPIrDHaB6pd67
-0AFvO/pZgMSHDRHD+safBgaLb5dBZ895Qvudbq3RQevVnO+YZQYZkpmjoF/+TQ7/
-YwDVP+QmNRzx72jtqAN/Kc3CvQ9nkgYhU65B95aX0xA=',
- ],
- [
- 'download' => 'https://nextcloud.com',
- ],
- ],
- ],
- ];
- $this->appFetcher
- ->expects($this->once())
- ->method('get')
- ->willReturn($appArray);
- $realTmpFile = Server::get(ITempManager::class)->getTemporaryFile('.tar.gz');
- copy(__DIR__ . '/../data/testapp1.tar.gz', $realTmpFile);
- $this->tempManager
- ->expects($this->once())
- ->method('getTemporaryFile')
- ->with('.tar.gz')
- ->willReturn($realTmpFile);
- $realTmpFolder = Server::get(ITempManager::class)->getTemporaryFolder();
- mkdir($realTmpFolder . '/testfolder');
- $this->tempManager
- ->expects($this->once())
- ->method('getTemporaryFolder')
- ->willReturn($realTmpFolder);
- $client = $this->createMock(IClient::class);
- $client
- ->expects($this->once())
- ->method('get')
- ->with('https://example.com', ['sink' => $realTmpFile, 'timeout' => 120]);
- $this->clientService
- ->expects($this->once())
- ->method('newClient')
- ->willReturn($client);
-
- $installer = $this->getInstaller();
- $installer->downloadApp('testapp');
- }
-
- public function testDownloadAppWithMismatchingIdentifier(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('App for id testapp has a wrong app ID in info.xml: testapp1');
-
- $appArray = [
- [
- 'id' => 'testapp',
- 'certificate' => '-----BEGIN CERTIFICATE-----
-MIIEAjCCAuoCAhAbMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
-VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
-MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
-dXRob3JpdHkwHhcNMTYxMDMxMTgxNTI2WhcNMjcwMjA2MTgxNTI2WjASMRAwDgYD
-VQQDEwd0ZXN0YXBwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAqa0x
-FcVa0YcO/ABqSNdbf7Bzp2PBBJzVM9gI4/HzzBKU/NY9/RibBBpNjAIWEFAbTI4j
-ilFSoxHDQ8HrboFOeKCrOIdp9ATQ8SnYVNIQ12Ym3LA/XxcG0gG0H7DeS9C0uACe
-svN8fwD1wnKnLLU9GBzO77jwYkneed85wwKG4waHd3965gxQWq0N5gnYS0TTn7Yr
-l1veRiw+ryefXvfWI0cN1WBZJ/4XAkwVlpG1HP60AunIpcwn9bfG4XCka+7x26E4
-6Hw0Ot7D7j0yzVzimJDPB2h2buEtPVd6m+oNPueVvKGta+p6cEEaHlFVh2Pa9DI+
-me3nb6aXE2kABWXav3BmK18A5Rg4ZY4VFYvmHmxkOhT/ulGZRqy6TccL/optqs52
-KQ6P0e5dfmhLeoCvJObD+ZYKv+kJCRFtX1Hve/R4IHG6XSFKUfrRjyor9b6TX2L/
-l2vV0mFjmy4g3l05vWHg1Edtq7M29S/xNA3/hF29NjBq6NoMbLGcBtFced1iK07Z
-yHLjXRZRfURP671Svqqg8pjxuDqkJ2vIj/Vpod4kF2jeiZYXcfmNKhEhxpkccSe0
-dI6p76Ne7XSUpf8yCPiSnWZLadqKZdEulcB4SlrZO2+/pycgqrqihofDrvDeWeeg
-gQyvbZZKl4ylRNj6IRKnosKLVXNqMHQxLmxLHeUCAwEAATANBgkqhkiG9w0BAQsF
-AAOCAQEALkKQwa40HfuP4Q6ShwBFJbXLyodIAXCT014kBVjReDKNl5oHtMXRjPxj
-nj9doKu+3bLNuLCv9uU3H5+t/GFogReV3Av3z/fCqJ6wHv/KX+lacj31dWXZGD8G
-z+RYibrxKkPN0V6q1mSvkg3hJOOE+/4FPIdc8PNlgratv3WS4dT8QwGSUavHW2Kx
-89nIdnwtLEFpgML/bTG0dm8BH57xER8LCYixW1VmpV6A4IsoKVsnB7KUCRTK3iUJ
-Zh8Xg8UMNrOtXc1Wx1Wmjaa4ZE9dY6/KkU2ny2UWyDHKU/9VE8QQ4HN93gxU4+H7
-cUg0V1uAxqUvKytKkMfcyPWsz/AINA==
------END CERTIFICATE-----',
- 'releases' => [
- [
- 'download' => 'https://example.com',
- 'signature' => 'h8H3tUy2dDlwrV/hY/ZxqYqe8Vue+IINluLtAt1HxX2cjz3vdoVHJRINRkMYYcdz
-VlndvHyKdqJHDAACphR8tVV6EFrPermn7gEgWk7a51LbUM7sAN7RV7ijEooUo+TQ
-jNW9Ch48Wg3jvebMwWNr5t5U4MEXTP5f0YX/kxvkJoUrG3a3spt7ziEuHaq8IPvt
-Jj/JSDFhvRNpom7yNNcI1Ijoq8yC11sg7RJBNfrHdGPHPZVz2SyBiY9OcvgGSpUU
-bfvzhIZDCl/RRi5fs39jLLupAP69Ez6+jylNXEMsNwM0YL5+egSXFtkCvgOw8UBg
-ZqNZZojcS22acuvHRnoa6PDDhwHdCH+zpifXSOhSQvue5n6q+FVX6aeD1LnCQkYB
-D2wvNyZWwdADJtvDj03DKhm21g+TPy63XC94q4IqvjQ94pV8U+qrBBfkQ62NGjaC
-oOU6y5sEmQeAdVRpWVo0Hewmjp4Adoj5JRwuqCVEynTC6DXHs3HvHxYlmib1F05a
-GqEhdDmOHsxNaeJ08Hlptq5yLv3+0wEdtriVjgAZNVduHG1F1FkhPIrDHaB6pd67
-0AFvO/pZgMSHDRHD+safBgaLb5dBZ895Qvudbq3RQevVnO+YZQYZkpmjoF/+TQ7/
-YwDVP+QmNRzx72jtqAN/Kc3CvQ9nkgYhU65B95aX0xA=',
- ],
- [
- 'download' => 'https://nextcloud.com',
- ],
- ],
- ],
- ];
- $this->appFetcher
- ->expects($this->once())
- ->method('get')
- ->willReturn($appArray);
- $realTmpFile = Server::get(ITempManager::class)->getTemporaryFile('.tar.gz');
- copy(__DIR__ . '/../data/testapp1.tar.gz', $realTmpFile);
- $this->tempManager
- ->expects($this->once())
- ->method('getTemporaryFile')
- ->with('.tar.gz')
- ->willReturn($realTmpFile);
- $realTmpFolder = Server::get(ITempManager::class)->getTemporaryFolder();
- $this->tempManager
- ->expects($this->once())
- ->method('getTemporaryFolder')
- ->willReturn($realTmpFolder);
- $client = $this->createMock(IClient::class);
- $client
- ->expects($this->once())
- ->method('get')
- ->with('https://example.com', ['sink' => $realTmpFile, 'timeout' => 120]);
- $this->clientService
- ->expects($this->once())
- ->method('newClient')
- ->willReturn($client);
-
- $installer = $this->getInstaller();
- $installer->downloadApp('testapp');
- }
-
- public function testDownloadAppSuccessful(): void {
- $appArray = [
- [
- 'id' => 'testapp',
- 'certificate' => '-----BEGIN CERTIFICATE-----
-MIIEAjCCAuoCAhAbMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
-VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
-MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
-dXRob3JpdHkwHhcNMTYxMDMxMTgxNTI2WhcNMjcwMjA2MTgxNTI2WjASMRAwDgYD
-VQQDEwd0ZXN0YXBwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAqa0x
-FcVa0YcO/ABqSNdbf7Bzp2PBBJzVM9gI4/HzzBKU/NY9/RibBBpNjAIWEFAbTI4j
-ilFSoxHDQ8HrboFOeKCrOIdp9ATQ8SnYVNIQ12Ym3LA/XxcG0gG0H7DeS9C0uACe
-svN8fwD1wnKnLLU9GBzO77jwYkneed85wwKG4waHd3965gxQWq0N5gnYS0TTn7Yr
-l1veRiw+ryefXvfWI0cN1WBZJ/4XAkwVlpG1HP60AunIpcwn9bfG4XCka+7x26E4
-6Hw0Ot7D7j0yzVzimJDPB2h2buEtPVd6m+oNPueVvKGta+p6cEEaHlFVh2Pa9DI+
-me3nb6aXE2kABWXav3BmK18A5Rg4ZY4VFYvmHmxkOhT/ulGZRqy6TccL/optqs52
-KQ6P0e5dfmhLeoCvJObD+ZYKv+kJCRFtX1Hve/R4IHG6XSFKUfrRjyor9b6TX2L/
-l2vV0mFjmy4g3l05vWHg1Edtq7M29S/xNA3/hF29NjBq6NoMbLGcBtFced1iK07Z
-yHLjXRZRfURP671Svqqg8pjxuDqkJ2vIj/Vpod4kF2jeiZYXcfmNKhEhxpkccSe0
-dI6p76Ne7XSUpf8yCPiSnWZLadqKZdEulcB4SlrZO2+/pycgqrqihofDrvDeWeeg
-gQyvbZZKl4ylRNj6IRKnosKLVXNqMHQxLmxLHeUCAwEAATANBgkqhkiG9w0BAQsF
-AAOCAQEALkKQwa40HfuP4Q6ShwBFJbXLyodIAXCT014kBVjReDKNl5oHtMXRjPxj
-nj9doKu+3bLNuLCv9uU3H5+t/GFogReV3Av3z/fCqJ6wHv/KX+lacj31dWXZGD8G
-z+RYibrxKkPN0V6q1mSvkg3hJOOE+/4FPIdc8PNlgratv3WS4dT8QwGSUavHW2Kx
-89nIdnwtLEFpgML/bTG0dm8BH57xER8LCYixW1VmpV6A4IsoKVsnB7KUCRTK3iUJ
-Zh8Xg8UMNrOtXc1Wx1Wmjaa4ZE9dY6/KkU2ny2UWyDHKU/9VE8QQ4HN93gxU4+H7
-cUg0V1uAxqUvKytKkMfcyPWsz/AINA==
------END CERTIFICATE-----',
- 'releases' => [
- [
- 'download' => 'https://example.com',
- 'signature' => 'O5UWFRnSx4mSdEX83Uh9u7KW+Gl1OWU4uaFg6aYY19zc+lWP4rKCbAUH7Jo1Bohf
-qxQbhXs4cMqGmoL8dW4zeFUqSJCRk52LA+ciLezjPFv275q+BxEgyWOylLnbhBaz
-+v6lXLaeG0J/ry8wEdg+rwP8FCYPsvKlXSVbFjgubvCR/owKJJf5iL0B93noBwBN
-jfbcxi7Kh16HAKy6f/gVZ6hf/4Uo7iEFMCPEHjidope+ejUpqbd8XhQg5/yh7TQ7
-VKR7pkdDG2eFr5c3CpaECdNg5ZIGRbQNJHBXHT/wliorWpYJtwtNAQJ4xC635gLP
-4klkKN4XtSj8bJUaJC6aaksLFgRSeKXaYAHai/XP6BkeyNzlSbsmyZk8cZbySx8F
-gVOzPok1c94UGT57FjeW5eqRjtmzbYivQdP89Ouz6et7PY69yOCqiRFQanrqzwoX
-MPLX6f5V9tCJtlH6ztmEcDROfvuVc0U3rEhqx2hphoyo+MZrPFpdcJL8KkIdMKbY
-7yQWrsV7QvAzygAOFsC0TlSNJbmMCljouUk9di4CUZ+xsQ6n6TZtE7gsdljlKjPS
-3Ys+e3V1HUaVzv8SaSmKwjRoQxQxHWLtXpJS2Yq+i+gq7LuC+aStzxAzV/h2plDW
-358picx/PobNDi71Q97+/CAOq+4wDOwhKwls7lwudIs=',
- ],
- [
- 'download' => 'https://nextcloud.com',
- ],
- ],
- ],
- ];
- $this->appFetcher
- ->expects($this->once())
- ->method('get')
- ->willReturn($appArray);
- $realTmpFile = Server::get(ITempManager::class)->getTemporaryFile('.tar.gz');
- copy(__DIR__ . '/../data/testapp.tar.gz', $realTmpFile);
- $this->tempManager
- ->expects($this->once())
- ->method('getTemporaryFile')
- ->with('.tar.gz')
- ->willReturn($realTmpFile);
- $realTmpFolder = Server::get(ITempManager::class)->getTemporaryFolder();
- $this->tempManager
- ->expects($this->once())
- ->method('getTemporaryFolder')
- ->willReturn($realTmpFolder);
- $client = $this->createMock(IClient::class);
- $client
- ->expects($this->once())
- ->method('get')
- ->with('https://example.com', ['sink' => $realTmpFile, 'timeout' => 120]);
- $this->clientService
- ->expects($this->once())
- ->method('newClient')
- ->willReturn($client);
-
- $installer = $this->getInstaller();
- $installer->downloadApp('testapp');
-
- $this->assertTrue(file_exists(__DIR__ . '/../../apps/testapp/appinfo/info.xml'));
- $this->assertEquals('0.9', \OC_App::getAppVersionByPath(__DIR__ . '/../../apps/testapp/'));
- }
-
- public function testDownloadAppWithDowngrade(): void {
- // Use previous test to download the application in version 0.9
- $this->testDownloadAppSuccessful();
-
- // Reset mocks
- $this->appFetcher = $this->createMock(AppFetcher::class);
- $this->clientService = $this->createMock(IClientService::class);
- $this->tempManager = $this->createMock(ITempManager::class);
-
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('App for id testapp has version 0.9 and tried to update to lower version 0.8');
-
- $appArray = [
- [
- 'id' => 'testapp',
- 'certificate' => '-----BEGIN CERTIFICATE-----
-MIIEAjCCAuoCAhAbMA0GCSqGSIb3DQEBCwUAMHsxCzAJBgNVBAYTAkRFMRswGQYD
-VQQIDBJCYWRlbi1XdWVydHRlbWJlcmcxFzAVBgNVBAoMDk5leHRjbG91ZCBHbWJI
-MTYwNAYDVQQDDC1OZXh0Y2xvdWQgQ29kZSBTaWduaW5nIEludGVybWVkaWF0ZSBB
-dXRob3JpdHkwHhcNMTYxMDMxMTgxNTI2WhcNMjcwMjA2MTgxNTI2WjASMRAwDgYD
-VQQDEwd0ZXN0YXBwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAqa0x
-FcVa0YcO/ABqSNdbf7Bzp2PBBJzVM9gI4/HzzBKU/NY9/RibBBpNjAIWEFAbTI4j
-ilFSoxHDQ8HrboFOeKCrOIdp9ATQ8SnYVNIQ12Ym3LA/XxcG0gG0H7DeS9C0uACe
-svN8fwD1wnKnLLU9GBzO77jwYkneed85wwKG4waHd3965gxQWq0N5gnYS0TTn7Yr
-l1veRiw+ryefXvfWI0cN1WBZJ/4XAkwVlpG1HP60AunIpcwn9bfG4XCka+7x26E4
-6Hw0Ot7D7j0yzVzimJDPB2h2buEtPVd6m+oNPueVvKGta+p6cEEaHlFVh2Pa9DI+
-me3nb6aXE2kABWXav3BmK18A5Rg4ZY4VFYvmHmxkOhT/ulGZRqy6TccL/optqs52
-KQ6P0e5dfmhLeoCvJObD+ZYKv+kJCRFtX1Hve/R4IHG6XSFKUfrRjyor9b6TX2L/
-l2vV0mFjmy4g3l05vWHg1Edtq7M29S/xNA3/hF29NjBq6NoMbLGcBtFced1iK07Z
-yHLjXRZRfURP671Svqqg8pjxuDqkJ2vIj/Vpod4kF2jeiZYXcfmNKhEhxpkccSe0
-dI6p76Ne7XSUpf8yCPiSnWZLadqKZdEulcB4SlrZO2+/pycgqrqihofDrvDeWeeg
-gQyvbZZKl4ylRNj6IRKnosKLVXNqMHQxLmxLHeUCAwEAATANBgkqhkiG9w0BAQsF
-AAOCAQEALkKQwa40HfuP4Q6ShwBFJbXLyodIAXCT014kBVjReDKNl5oHtMXRjPxj
-nj9doKu+3bLNuLCv9uU3H5+t/GFogReV3Av3z/fCqJ6wHv/KX+lacj31dWXZGD8G
-z+RYibrxKkPN0V6q1mSvkg3hJOOE+/4FPIdc8PNlgratv3WS4dT8QwGSUavHW2Kx
-89nIdnwtLEFpgML/bTG0dm8BH57xER8LCYixW1VmpV6A4IsoKVsnB7KUCRTK3iUJ
-Zh8Xg8UMNrOtXc1Wx1Wmjaa4ZE9dY6/KkU2ny2UWyDHKU/9VE8QQ4HN93gxU4+H7
-cUg0V1uAxqUvKytKkMfcyPWsz/AINA==
------END CERTIFICATE-----',
- 'releases' => [
- [
- 'download' => 'https://example.com',
- 'signature' => 'KMSao4cKdMIYxeT8Bm4lrmSeIQnk7YzJZh+Vz+4LVSBwF+OMmcujryQuWLXmbPfg
-4hGI9zS025469VNjUoCprn01H8NBq3O1cXz+ewG1oxYWMMQFZDkOtUQ+XZ27b91t
-y0l45H6C8j0sTeSrUb/LCjrdm+buUygkhC2RZxCI6tLi4rYWj0MiqDz98XkbB3te
-pW3ZND6mG6Jxn1fnd35paqZ/+URMftoLQ4K+6vJoBVGnug9nk1RpGLouICI0zCrz
-YPTsBHo0s2mPvQQ/ASacWYmSe5R6r5JCzNeGMpViGCqCYPbwuebgqK079s2zvSF9
-mSLAm2Tk6gCM29N8Vdfr6ppCvIbuNzlLU/dGdYHAILgxEsm/odZjt1Fhs4lOo3A5
-9ToaNl5+qOEkggwfE/QqceHAY2soW9V5d9izhTCDgXmxpPpPXkwPPTz04ZUpi1Yc
-OdZZOswbEcc2jUC5T7a7Tnp0uBOkdqat6jB4oMGwU1ldYLCGRyy546cPPTXJw5kH
-9WfeKJ/mavrSLVa7QqZ4RCcMigmijT1kdqbaEh05IZNrzs6VDcS2EIrbDX8SGXUk
-uDDkPXZEXqNDEjyONfDXVRLiqDa52Gg+I4vW/l/4ZOFgAWdZkqPPuZFaqzZpsJXm
-JXhrdaWDZ8fzpUjugrtC3qslsqL0dzgU37anS3HwrT8=',
- ],
- [
- 'download' => 'https://nextcloud.com',
- ],
- ],
- ],
- ];
- $this->appFetcher
- ->expects($this->once())
- ->method('get')
- ->willReturn($appArray);
- $realTmpFile = Server::get(ITempManager::class)->getTemporaryFile('.tar.gz');
- copy(__DIR__ . '/../data/testapp.0.8.tar.gz', $realTmpFile);
- $this->tempManager
- ->expects($this->once())
- ->method('getTemporaryFile')
- ->with('.tar.gz')
- ->willReturn($realTmpFile);
- $realTmpFolder = Server::get(ITempManager::class)->getTemporaryFolder();
- $this->tempManager
- ->expects($this->once())
- ->method('getTemporaryFolder')
- ->willReturn($realTmpFolder);
- $client = $this->createMock(IClient::class);
- $client
- ->expects($this->once())
- ->method('get')
- ->with('https://example.com', ['sink' => $realTmpFile, 'timeout' => 120]);
- $this->clientService
- ->expects($this->once())
- ->method('newClient')
- ->willReturn($client);
- $this->assertTrue(file_exists(__DIR__ . '/../../apps/testapp/appinfo/info.xml'));
- $this->assertEquals('0.9', \OC_App::getAppVersionByPath(__DIR__ . '/../../apps/testapp/'));
-
- $this->appManager
- ->expects($this->once())
- ->method('getAppVersion')
- ->willReturn('0.9');
-
- $installer = $this->getInstaller();
- $installer->downloadApp('testapp');
- $this->assertTrue(file_exists(__DIR__ . '/../../apps/testapp/appinfo/info.xml'));
- $this->assertEquals('0.8', \OC_App::getAppVersionByPath(__DIR__ . '/../../apps/testapp/'));
- }
-}
--- a/tests/lib/IntegrityCheck/CheckerTest.php
+++ /dev/null
@@ -1,1158 +0,0 @@
-serverVersion = $this->createMock(ServerVersion::class);
- $this->environmentHelper = $this->createMock(EnvironmentHelper::class);
- $this->fileAccessHelper = $this->createMock(FileAccessHelper::class);
- $this->config = $this->createMock(IConfig::class);
- $this->appConfig = $this->createMock(IAppConfig::class);
- $this->cacheFactory = $this->createMock(ICacheFactory::class);
- $this->appManager = $this->createMock(IAppManager::class);
- $this->mimeTypeDetector = $this->createMock(Detection::class);
-
- $this->config->method('getAppValue')
- ->willReturnArgument(2);
-
- $this->cacheFactory
- ->expects($this->any())
- ->method('createDistributed')
- ->with('oc.integritycheck.checker')
- ->willReturn(new NullCache());
-
- $this->checker = new Checker(
- $this->serverVersion,
- $this->environmentHelper,
- $this->fileAccessHelper,
- $this->config,
- $this->appConfig,
- $this->cacheFactory,
- $this->appManager,
- $this->mimeTypeDetector
- );
- }
-
- public function testWriteAppSignatureOfNotExistingApp(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Exception message');
-
- $this->fileAccessHelper
- ->expects($this->once())
- ->method('assertDirectoryExists')
- ->with('NotExistingApp/appinfo')
- ->willThrowException(new \Exception('Exception message'));
- $this->fileAccessHelper
- ->expects($this->once())
- ->method('is_writable')
- ->with('NotExistingApp/appinfo')
- ->willReturn(true);
-
- $keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.crt');
- $rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.key');
- $rsa = RSA::loadPrivateKey($rsaPrivateKey);
- // After loading the key, always set the PSS padding and options:
- $rsa = $rsa
- ->withPadding(RSA::SIGNATURE_PSS)
- ->withMGFHash('sha512')
- ->withSaltLength(0);
- $x509 = new X509();
- $x509->loadX509($keyBundle);
- $this->checker->writeAppSignature('NotExistingApp', $x509, $rsa);
- }
-
- public function testWriteAppSignatureWrongPermissions(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessageMatches('/[a-zA-Z\\/_-]+ is not writable/');
-
- $this->fileAccessHelper
- ->expects($this->once())
- ->method('file_put_contents')
- ->willThrowException(new \Exception('Exception message'));
-
- $keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.crt');
- $rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.key');
- $rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.key');
- $rsa = RSA::loadPrivateKey($rsaPrivateKey);
- // After loading the key, always set the PSS padding and options:
- $rsa = $rsa
- ->withPadding(RSA::SIGNATURE_PSS)
- ->withMGFHash('sha512')
- ->withSaltLength(0);
- $x509 = new X509();
- $x509->loadX509($keyBundle);
- $this->checker->writeAppSignature(\OC::$SERVERROOT . '/tests/data/integritycheck/app/', $x509, $rsa);
- }
-
- public function testWriteAppSignature(): void {
- $expectedSignatureFileData = '{
- "hashes": {
- "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
- "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
- },
- "signature": "Y5yvXvcGHVPuRRatKVDUONWq1FpLXugZd6Km\/+aEHsQj7coVl9FeMj9OsWamBf7yRIw3dtNLguTLlAA9QAv\/b0uHN3JnbNZN+dwFOve4NMtqXfSDlWftqKN00VS+RJXpG1S2IIx9Poyp2NoghL\/5AuTv4GHiNb7zU\/DT\/kt71pUGPgPR6IIFaE+zHOD96vjYkrH+GfWZzKR0FCdLib9yyNvk+EGrcjKM6qjs2GKfS\/XFjj\/\/neDnh\/0kcPuKE3ZbofnI4TIDTv0CGqvOp7PtqVNc3Vy\/UKa7uF1PT0MAUKMww6EiMUSFZdUVP4WWF0Y72W53Qdtf1hrAZa2kfKyoK5kd7sQmCSKUPSU8978AUVZlBtTRlyT803IKwMV0iHMkw+xYB1sN2FlHup\/DESADqxhdgYuK35bCPvgkb4SBe4B8Voz\/izTvcP7VT5UvkYdAO+05\/jzdaHEmzmsD92CFfvX0q8O\/Y\/29ubftUJsqcHeMDKgcR4eZOE8+\/QVc\/89QO6WnKNuNuV+5bybO6g6PAdC9ZPsCvnihS61O2mwRXHLR3jv2UleFWm+lZEquPKtkhi6SLtDiijA4GV6dmS+dzujSLb7hGeD5o1plZcZ94uhWljl+QIp82+zU\/lYB1Zfr4Mb4e+V7r2gv7Fbv7y6YtjE2GIQwRhC5jq56bD0ZB+I=",
- "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEwTCCAqmgAwIBAgIUWv0iujufs5lUr0svCf\/qTQvoyKAwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIyNDk1M1oXDTE2MTEwMzIyNDk1M1owEjEQMA4GA1UEAwwHU29tZUFwcDCCAiIw\r\nDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK8q0x62agGSRBqeWsaeEwFfepMk\r\nF8cAobMMi50qHCv9IrOn\/ZH9l52xBrbIkErVmRjmly0d4JhD8Ymhidsh9ONKYl\/j\r\n+ishsZDM8eNNdp3Ew+fEYVvY1W7mR1qU24NWj0bzVsClI7hvPVIuw7AjfBDq1C5+\r\nA+ZSLSXYvOK2cEWjdxQfuNZwEZSjmA63DUllBIrm35IaTvfuyhU6BW9yHZxmb8+M\r\nw0xDv30D5UkE\/2N7Pa\/HQJLxCR+3zKibRK3nUyRDLSXxMkU9PnFNaPNX59VPgyj4\r\nGB1CFSToldJVPF4pzh7p36uGXZVxs8m3LFD4Ol8mhi7jkxDZjqFN46gzR0r23Py6\r\ndol9vfawGIoUwp9LvL0S7MvdRY0oazLXwClLP4OQ17zpSMAiCj7fgNT661JamPGj\r\nt5O7Zn2wA7I4ddDS\/HDTWCu98Zwc9fHIpsJPgCZ9awoqxi4Mnf7Pk9g5nnXhszGC\r\ncxxIASQKM+GhdzoRxKknax2RzUCwCzcPRtCj8AQT\/x\/mqN3PfRmlnFBNACUw9bpZ\r\nSOoNq2pCF9igftDWpSIXQ38pVpKLWowjjg3DVRmVKBgivHnUnVLyzYBahHPj0vaz\r\ntFtUFRaqXDnt+4qyUGyrT5h5pjZaTcHIcSB4PiarYwdVvgslgwnQzOUcGAzRWBD4\r\n6jV2brP5vFY3g6iPAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBACTY3CCHC+Z28gCf\r\nFWGKQ3wAKs+k4+0yoti0qm2EKX7rSGQ0PHSas6uW79WstC4Rj+DYkDtIhGMSg8FS\r\nHVGZHGBCc0HwdX+BOAt3zi4p7Sf3oQef70\/4imPoKxbAVCpd\/cveVcFyDC19j1yB\r\nBapwu87oh+muoeaZxOlqQI4UxjBlR\/uRSMhOn2UGauIr3dWJgAF4pGt7TtIzt+1v\r\n0uA6FtN1Y4R5O8AaJPh1bIG0CVvFBE58esGzjEYLhOydgKFnEP94kVPgJD5ds9C3\r\npPhEpo1dRpiXaF7WGIV1X6DI\/ipWvfrF7CEy6I\/kP1InY\/vMDjQjeDnJ\/VrXIWXO\r\nyZvHXVaN\/m+1RlETsH7YO\/QmxRue9ZHN3gvvWtmpCeA95sfpepOk7UcHxHZYyQbF\r\n49\/au8j+5tsr4A83xzsT1JbcKRxkAaQ7WDJpOnE5O1+H0fB+BaLakTg6XX9d4Fo7\r\n7Gin7hVWX7pL+JIyxMzME3LhfI61+CRcqZQIrpyaafUziPQbWIPfEs7h8tCOWyvW\r\nUO8ZLervYCB3j44ivkrxPlcBklDCqqKKBzDP9dYOtS\/P4RB1NkHA9+NTvmBpTonS\r\nSFXdg9fFMD7VfjDE3Vnk+8DWkVH5wBYowTAD7w9Wuzr7DumiAULexnP\/Y7xwxLv7\r\n4B+pXTAcRK0zECDEaX3npS8xWzrB\r\n-----END CERTIFICATE-----"
-}';
- $this->fileAccessHelper
- ->expects(self::any())
- ->method('is_writable')
- ->with(
- $this->equalTo(\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo'),
- )
- ->willReturn(true);
-
- $this->fileAccessHelper
- ->expects($this->once())
- ->method('file_put_contents')
- ->with(
- $this->equalTo(\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json'),
- $this->callback(function ($signature) use ($expectedSignatureFileData) {
- $expectedArray = json_decode($expectedSignatureFileData, true);
- $actualArray = json_decode($signature, true);
- $this->assertEquals($expectedArray, $actualArray);
- return true;
- })
- );
-
- $keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.crt');
- $rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.key');
- $rsa = RSA::loadPrivateKey($rsaPrivateKey);
- // After loading the key, always set the PSS padding and options:
- $rsa = $rsa
- ->withPadding(RSA::SIGNATURE_PSS)
- ->withMGFHash('sha512')
- ->withSaltLength(0);
- $x509 = new X509();
- $x509->loadX509($keyBundle);
- $this->checker->writeAppSignature(\OC::$SERVERROOT . '/tests/data/integritycheck/app/', $x509, $rsa);
- }
-
- public function testVerifyAppSignatureWithoutSignatureData(): void {
- $this->serverVersion
- ->expects($this->once())
- ->method('getChannel')
- ->willReturn('stable');
- $this->config
- ->expects($this->any())
- ->method('getSystemValueBool')
- ->with('integrity.check.disabled', false)
- ->willReturn(false);
-
- $expected = [
- 'EXCEPTION' => [
- 'class' => 'OC\IntegrityCheck\Exceptions\InvalidSignatureException',
- 'message' => 'Signature data not found.',
- ],
- ];
- $this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp'));
- }
-
- public function testVerifyAppSignatureWithValidSignatureData(): void {
- $this->serverVersion
- ->expects($this->once())
- ->method('getChannel')
- ->willReturn('stable');
- $this->config
- ->expects($this->any())
- ->method('getSystemValueBool')
- ->with('integrity.check.disabled', false)
- ->willReturn(false);
-
- $this->appManager
- ->expects($this->once())
- ->method('getAppPath')
- ->with('SomeApp')
- ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
- $signatureDataFile = '{
- "hashes": {
- "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
- "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
- },
- "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
- "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
-}';
- $this->fileAccessHelper
- ->expects($this->exactly(2))
- ->method('file_get_contents')
- ->willReturnMap([
- [\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json', $signatureDataFile],
- ['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
- ]);
-
- $this->assertSame([], $this->checker->verifyAppSignature('SomeApp'));
- }
-
- public function testVerifyAppSignatureWithTamperedSignatureData(): void {
- $this->serverVersion
- ->expects($this->once())
- ->method('getChannel')
- ->willReturn('stable');
- $this->config
- ->expects($this->any())
- ->method('getSystemValueBool')
- ->with('integrity.check.disabled', false)
- ->willReturn(false);
-
- $this->appManager
- ->expects($this->once())
- ->method('getAppPath')
- ->with('SomeApp')
- ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
- $signatureDataFile = '{
- "hashes": {
- "AnotherFile.txt": "tampered",
- "subfolder\/file.txt": "tampered"
- },
- "signature": "EL49UaSeyMAqyMtqId+tgOhhwgOevPZsRLX4j2blnybAB6fN07z0936JqZV7+eMPiE30Idx+UCY6rCFN531Kqe9vAOCdgtHUSOjjKyKc+lvULESlMb6YQcrZrvDlEMMjzjH49ewG7Ai8sNN6HrRUd9U8ws+ewSkW2DOOBItj\/21RBnkrSt+2AtGXGigEvuTm57HrCYDj8\/lSkumC2GVkjLUHeLOKYo4PRNOr6yP5mED5v7zo66AWvXl2fKv54InZcdxsAk35lyK9DGZbk\/027ZRd0AOHT3LImRLvQ+8EAg3XLlRUy0hOFGgPC+jYonMzgYvsAXAXi2j8LnLJlsLwpFwu1k1B+kZVPMumKZvP9OvJb70EirecXmz62V+Jiyuaq7ne4y7Kp5gKZT\/T8SeZ0lFtCmPfYyzBB0y8s5ldmTTmdVYHs54t\/OCCW82HzQZxnFNPzDTRa8HglsaMKrqPtW59+R4UvRKSWhB8M\/Ah57qgzycvPV4KMz\/FbD4l\/\/9chRKSlCfc2k3b8ZSHNmi+EzCKgJjWIoKdgN1yax94puU8jfn8UW+G7H9Y1Jsf\/jox6QLyYEgtV1vOHY2xLT7fVs2vhyvkN2MNjJnmQ70gFG5Qz2lBz5wi6ZpB+tOfCcpbLxWAkoWoIrmC\/Ilqh7mfmRZ43g5upjkepHNd93ONuY8=",
- "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEwTCCAqmgAwIBAgIUWv0iujufs5lUr0svCf\/qTQvoyKAwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIyNDk1M1oXDTE2MTEwMzIyNDk1M1owEjEQMA4GA1UEAwwHU29tZUFwcDCCAiIw\r\nDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK8q0x62agGSRBqeWsaeEwFfepMk\r\nF8cAobMMi50qHCv9IrOn\/ZH9l52xBrbIkErVmRjmly0d4JhD8Ymhidsh9ONKYl\/j\r\n+ishsZDM8eNNdp3Ew+fEYVvY1W7mR1qU24NWj0bzVsClI7hvPVIuw7AjfBDq1C5+\r\nA+ZSLSXYvOK2cEWjdxQfuNZwEZSjmA63DUllBIrm35IaTvfuyhU6BW9yHZxmb8+M\r\nw0xDv30D5UkE\/2N7Pa\/HQJLxCR+3zKibRK3nUyRDLSXxMkU9PnFNaPNX59VPgyj4\r\nGB1CFSToldJVPF4pzh7p36uGXZVxs8m3LFD4Ol8mhi7jkxDZjqFN46gzR0r23Py6\r\ndol9vfawGIoUwp9LvL0S7MvdRY0oazLXwClLP4OQ17zpSMAiCj7fgNT661JamPGj\r\nt5O7Zn2wA7I4ddDS\/HDTWCu98Zwc9fHIpsJPgCZ9awoqxi4Mnf7Pk9g5nnXhszGC\r\ncxxIASQKM+GhdzoRxKknax2RzUCwCzcPRtCj8AQT\/x\/mqN3PfRmlnFBNACUw9bpZ\r\nSOoNq2pCF9igftDWpSIXQ38pVpKLWowjjg3DVRmVKBgivHnUnVLyzYBahHPj0vaz\r\ntFtUFRaqXDnt+4qyUGyrT5h5pjZaTcHIcSB4PiarYwdVvgslgwnQzOUcGAzRWBD4\r\n6jV2brP5vFY3g6iPAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBACTY3CCHC+Z28gCf\r\nFWGKQ3wAKs+k4+0yoti0qm2EKX7rSGQ0PHSas6uW79WstC4Rj+DYkDtIhGMSg8FS\r\nHVGZHGBCc0HwdX+BOAt3zi4p7Sf3oQef70\/4imPoKxbAVCpd\/cveVcFyDC19j1yB\r\nBapwu87oh+muoeaZxOlqQI4UxjBlR\/uRSMhOn2UGauIr3dWJgAF4pGt7TtIzt+1v\r\n0uA6FtN1Y4R5O8AaJPh1bIG0CVvFBE58esGzjEYLhOydgKFnEP94kVPgJD5ds9C3\r\npPhEpo1dRpiXaF7WGIV1X6DI\/ipWvfrF7CEy6I\/kP1InY\/vMDjQjeDnJ\/VrXIWXO\r\nyZvHXVaN\/m+1RlETsH7YO\/QmxRue9ZHN3gvvWtmpCeA95sfpepOk7UcHxHZYyQbF\r\n49\/au8j+5tsr4A83xzsT1JbcKRxkAaQ7WDJpOnE5O1+H0fB+BaLakTg6XX9d4Fo7\r\n7Gin7hVWX7pL+JIyxMzME3LhfI61+CRcqZQIrpyaafUziPQbWIPfEs7h8tCOWyvW\r\nUO8ZLervYCB3j44ivkrxPlcBklDCqqKKBzDP9dYOtS\/P4RB1NkHA9+NTvmBpTonS\r\nSFXdg9fFMD7VfjDE3Vnk+8DWkVH5wBYowTAD7w9Wuzr7DumiAULexnP\/Y7xwxLv7\r\n4B+pXTAcRK0zECDEaX3npS8xWzrB\r\n-----END CERTIFICATE-----"
-}';
- $this->fileAccessHelper
- ->expects($this->exactly(2))
- ->method('file_get_contents')
- ->willReturnMap([
- [\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json', $signatureDataFile],
- ['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
- ]);
-
- $expected = [
- 'EXCEPTION' => [
- 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
- 'message' => 'Signature could not get verified.',
- ],
- ];
- $this->assertEquals($expected, $this->checker->verifyAppSignature('SomeApp'));
- }
-
- public function testVerifyAppSignatureWithTamperedFiles(): void {
- $this->serverVersion
- ->expects($this->once())
- ->method('getChannel')
- ->willReturn('stable');
- $this->config
- ->expects($this->any())
- ->method('getSystemValueBool')
- ->with('integrity.check.disabled', false)
- ->willReturn(false);
-
- $this->appManager
- ->expects($this->once())
- ->method('getAppPath')
- ->with('SomeApp')
- ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/');
- $signatureDataFile = '{
- "hashes": {
- "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
- "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
- },
- "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
- "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
-}';
- $this->fileAccessHelper
- ->expects($this->exactly(2))
- ->method('file_get_contents')
- ->willReturnMap([
- [\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//appinfo/signature.json', $signatureDataFile],
- ['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
- ]);
-
- $expected = [
- 'INVALID_HASH' => [
- 'AnotherFile.txt' => [
- 'expected' => '1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112',
- 'current' => '7322348ba269c6d5522efe02f424fa3a0da319a7cd9c33142a5afe32a2d9af2da3a411f086fcfc96ff4301ea566f481dba0960c2abeef3594c4d930462f6584c',
- ],
- ],
- 'FILE_MISSING' => [
- 'subfolder/file.txt' => [
- 'expected' => '410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b',
- 'current' => '',
- ],
- ],
- 'EXTRA_FILE' => [
- 'UnecessaryFile' => [
- 'expected' => '',
- 'current' => 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e',
- ],
- ],
- ];
- $this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp'));
- }
-
- public function testVerifyAppSignatureWithTamperedFilesAndAlternatePath(): void {
- $this->serverVersion
- ->expects($this->once())
- ->method('getChannel')
- ->willReturn('stable');
- $this->config
- ->expects($this->any())
- ->method('getSystemValueBool')
- ->with('integrity.check.disabled', false)
- ->willReturn(false);
-
- $this->appManager
- ->expects($this->never())
- ->method('getAppPath')
- ->with('SomeApp');
- $signatureDataFile = '{
- "hashes": {
- "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
- "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
- },
- "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
- "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
-}';
- $this->fileAccessHelper
- ->expects($this->exactly(2))
- ->method('file_get_contents')
- ->willReturnMap([
- [\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//appinfo/signature.json', $signatureDataFile],
- ['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
- ]);
-
- $expected = [
- 'INVALID_HASH' => [
- 'AnotherFile.txt' => [
- 'expected' => '1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112',
- 'current' => '7322348ba269c6d5522efe02f424fa3a0da319a7cd9c33142a5afe32a2d9af2da3a411f086fcfc96ff4301ea566f481dba0960c2abeef3594c4d930462f6584c',
- ],
- ],
- 'FILE_MISSING' => [
- 'subfolder/file.txt' => [
- 'expected' => '410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b',
- 'current' => '',
- ],
- ],
- 'EXTRA_FILE' => [
- 'UnecessaryFile' => [
- 'expected' => '',
- 'current' => 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e',
- ],
- ],
- ];
- $this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp', \OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/'));
- }
-
- public function testVerifyAppWithDifferentScope(): void {
- $this->serverVersion
- ->expects($this->once())
- ->method('getChannel')
- ->willReturn('stable');
- $this->config
- ->expects($this->any())
- ->method('getSystemValueBool')
- ->with('integrity.check.disabled', false)
- ->willReturn(false);
-
- $this->appManager
- ->expects($this->once())
- ->method('getAppPath')
- ->with('SomeApp')
- ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/');
- $signatureDataFile = '{
- "hashes": {
- "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
- "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
- },
- "signature": "eXesvDm3pkek12xSwMG10y9suRES79Nye3jYNe5KYq1tTUPqRRNgxmMGAfcUro0zpLeAr2YgHeSMWtglblGOW7pmwGVPZ0O1Y4r1fE6jnep0kW+35PLIaqCorIOnCAtSzDNKBhwd1ow3zW2wC0DFouuEkIO8u5Fw28g8E8dp8zEk1xMblNPy+xtWkmYHrVJ\/dQgun1bYOF2ZFtAzatwndTI\/bGsy1i3Wsl+x6HyWKQdq8y8VObtOqKDH7uERBEpB9DHVyKflj1v1gQuEH6BhaRdATc7ee0MiQdGblraIySwYRdfo2d8i82OVKrenMB3SLwyCvDPyQ9iKpTOnSF52ZBqaqSXKM2N\/RAkweeBFQQCwcHhqxvB0cfbyHcbkOLeCZe\/tsh68IxwTiYgzvLfl7sOZ5arnZbzrPpZmB+hfV2omkoJ1tDwOWz9hEmLLNtfo2OxyUH1m0+XFaC+Gbn4WkVDgf7YZkwUcG+Qoa3oKDNMss8MEyZxewl2iDGZcf402dlidHRprlfmXbAYuVQ08\/a0HxIKYPGh\/nsMGmwnO15CWtFpAbhUA\/D5oRjsIxnvXaMDg0iAFpdu\/5Ffsj7g3EPdBkiQHNYK7YU1RRx609eH0bZyiIYHdUPw7ikLupvrebZmELqi3mqDFO99u4eISlxFJlUbUND3L4BtmWTWrKwI=",
- "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIExjCCAq6gAwIBAgIUHSJjhJqMwr+3TkoiQFg4SVVYQ1gwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIzMjc1NVoXDTE2MTEwMzIzMjc1NVowFzEVMBMGA1UEAwwMQW5vdGhlclNjb3Bl\r\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA33npb5RmUkXrDT+TbwMf\r\n0zQ33SlzsjoGxCrbSwJOn6leGGInJ6ZrdzLL0WTi\/dTpg+Y\/JS+72XWm5NSjaTxo\r\n7OHc3cQBwXQj4tN6j\/y5qqY0GDLYufEkx2rpazqt9lBSJ72u1bGl2yoOXzYCz5i0\r\n60KsJXC9K44LKzGsarzbwAgskSVNkjAsPgjnCWZmcl6icpLi5Fz9rs2UMOWbdvdI\r\nAROsn0eC9E\/akmXTy5YMu6bAIGpvjZFHzyA83FQRbvv5o1V5Gsye\/VQLEgh7rqfz\r\nT\/jgWifP+JgoeB6otzuRZ3fFsmbBiyCIRtIOzQQflozhUlWtmiEGwg4GySuMUjEH\r\nA1LF86LO+ZzDQgd2oYNKmrQ8O+EcLqx9BpV4AFhEvqdk7uycJYPHs6yl+yfbzTeJ\r\n2Xd0yVAfd9r\/iDr36clLj2bzEObdl9xzKjcCIXE4Q0G4Pur41\/BJUDK9PI390ccQ\r\nnFjjVYBMsC859OwW64tMP0zkM9Vv72LCaEzaR8jqH0j11catqxunr+StfMcmxLTN\r\nbqBJbSEq4ER3mJxCTI2UrIVmdQ7+wRxgv3QTDNOZyqrz2L8A1Rpb3h0APxtQv+oA\r\n8KIZYID5\/qsS2V2jITkMQ8Nd1W3b0cZhZ600z+znh3jLJ0TYLvwN6\/qBQTUDaM2o\r\ng1+icMqXIXIeKuoPCVVsG7cCAwEAATANBgkqhkiG9w0BAQUFAAOCAgEAHc4F\/kOV\r\nHc8In5MmGg2YtjwZzjdeoC5TIPZczRqz0B+wRbJzN6aYryKZKLmP+wKpgRnJWDzp\r\nrgKGyyEQIAfK63DEv4B9p4N1B+B3aeMKsSpVcw7wbFTD57V5A7pURGoo31d0mw5L\r\nUIXZ2u+TUfGbzucMxLdFhTwjGpz9M6Kkm\/POxmV0tvLija5LdbdKnYR9BFmyu4IX\r\nqyoIAtComATNLl+3URu3SZxhE3NxhzMz+eAeNfh1KuIf2gWIIeDCXalVSJLym+OQ\r\nHFDpqRhJqfTMprrRlmmU7Zntgbj8\/RRZuXnBvH9cQ2KykLOb4UoCPlGUqOqKyP9m\r\nDJSFRiMJfpgMQUaJk1TLhKF+IR6FnmwURLEtkONJumtDQju9KaWPlhueONdyGi0p\r\nqxLVUo1Vb52XnPhk2GEEduxpDc9V5ePJ+pdcEdMifY\/uPNBRuBj2c87yq1DLH+U4\r\n3XzP1MlwjnBWZYuoFo0j6Jq0r\/MG6HjGdmkGIsRoheRi8Z8Scz5AW5QRkNz8pKop\r\nTELFqQy9g6TyQzzC8t6HZcpNe842ZUk4raEAbCZe\/XqxWMw5svPgNceBqM3fh7sZ\r\nBSykOHLaL8kiRO\/IS3y1yZEAuiWBvtxcTNLzBb+hdRpm2y8\/qH\/pKo+CMj1VzjNT\r\nD8YRQg0cjmDytJzHDrtV\/aTc9W1aPHun0vw=\r\n-----END CERTIFICATE-----"
-}';
- $this->fileAccessHelper
- ->expects($this->exactly(2))
- ->method('file_get_contents')
- ->willReturnMap([
- [\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//appinfo/signature.json', $signatureDataFile],
- ['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
- ]);
-
- $expected = [
- 'EXCEPTION' => [
- 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
- 'message' => 'Certificate is not valid for required scope. (Requested: SomeApp, current: CN=AnotherScope)',
- ],
- ];
- $this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp'));
- }
-
- public function testVerifyAppWithDifferentScopeAndAlwaysTrustedCore(): void {
- $this->serverVersion
- ->expects($this->once())
- ->method('getChannel')
- ->willReturn('stable');
- $this->config
- ->expects($this->any())
- ->method('getSystemValueBool')
- ->with('integrity.check.disabled', false)
- ->willReturn(false);
-
- $this->appManager
- ->expects($this->once())
- ->method('getAppPath')
- ->with('SomeApp')
- ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
- $signatureDataFile = '{
- "hashes": {
- "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
- "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
- },
- "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
- "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
-}';
- $this->fileAccessHelper
- ->expects($this->exactly(2))
- ->method('file_get_contents')
- ->willReturnMap([
- [\OC::$SERVERROOT . '/tests/data/integritycheck/app//appinfo/signature.json', $signatureDataFile],
- ['/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
- ]);
-
- $this->assertSame([], $this->checker->verifyAppSignature('SomeApp'));
- }
-
- public function testWriteCoreSignatureWithException(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Exception message');
-
- $this->fileAccessHelper
- ->expects($this->once())
- ->method('assertDirectoryExists')
- ->willThrowException(new \Exception('Exception message'));
- $this->fileAccessHelper
- ->expects($this->once())
- ->method('is_writable')
- ->with(__DIR__ . '/core')
- ->willReturn(true);
-
- $keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.crt');
- $rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.key');
- $rsa = RSA::loadPrivateKey($rsaPrivateKey);
- // After loading the key, always set the PSS padding and options:
- $rsa = $rsa
- ->withPadding(RSA::SIGNATURE_PSS)
- ->withMGFHash('sha512')
- ->withSaltLength(0);
- $x509 = new X509();
- $x509->loadX509($keyBundle);
- $this->checker->writeCoreSignature($x509, $rsa, __DIR__);
- }
-
- public function testWriteCoreSignatureWrongPermissions(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessageMatches('/[a-zA-Z\\/_-]+ is not writable/');
-
- $this->fileAccessHelper
- ->expects($this->once())
- ->method('assertDirectoryExists')
- ->willThrowException(new \Exception('Exception message'));
- $this->fileAccessHelper
- ->expects($this->once())
- ->method('is_writable')
- ->with(__DIR__ . '/core')
- ->willReturn(false);
-
- $keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.crt');
- $rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/SomeApp.key');
- $rsa = RSA::loadPrivateKey($rsaPrivateKey);
- // After loading the key, always set the PSS padding and options:
- $rsa = $rsa
- ->withPadding(RSA::SIGNATURE_PSS)
- ->withMGFHash('sha512')
- ->withSaltLength(0);
- $x509 = new X509();
- $x509->loadX509($keyBundle);
- $this->checker->writeCoreSignature($x509, $rsa, __DIR__);
- }
-
- public function testWriteCoreSignature(): void {
- $expectedSignatureFileData = '{
- "hashes": {
- "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
- "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
- },
- "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
- "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
-}';
- $this->environmentHelper
- ->expects($this->any())
- ->method('getServerRoot')
- ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
- $this->fileAccessHelper
- ->expects($this->once())
- ->method('file_put_contents')
- ->with(
- \OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json',
- $this->callback(function ($signature) use ($expectedSignatureFileData) {
- $expectedArray = json_decode($expectedSignatureFileData, true);
- $actualArray = json_decode($signature, true);
- $this->assertEquals($expectedArray, $actualArray);
- return true;
- })
- );
-
- $keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/core.crt');
- $rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/core.key');
- $rsa = RSA::loadPrivateKey($rsaPrivateKey);
- // After loading the key, always set the PSS padding and options:
- $rsa = $rsa
- ->withPadding(RSA::SIGNATURE_PSS)
- ->withMGFHash('sha512')
- ->withSaltLength(0);
- $x509 = new X509();
- $x509->loadX509($keyBundle);
- $this->checker->writeCoreSignature($x509, $rsa, \OC::$SERVERROOT . '/tests/data/integritycheck/app/');
- }
-
- public function testWriteCoreSignatureWithUnmodifiedHtaccess(): void {
- $expectedSignatureFileData = '{
- "hashes": {
- ".htaccess": "dc479770a6232061e04a768ee1f9133fdb3aea7b3a99f7105b0e0b6197474733e8d14b5b2bbad054e6b62a410fe5d0b3d790242dee1e0f11274af2100f5289e2",
- "subfolder\/.htaccess": "2c57b1e25050e11dc3ae975832f378c452159f7b69f818e47eeeafadd6ba568517461dcb4d843b90b906cd7c89d161bc1b89dff8e3ae0eb6f5088508c47befd1"
- },
- "signature": "nRtR377DB\/I\/4hmh9q3elMQYfSHnQFlNtjchNgrdfmUQqVmgkU\/4qgGyxDqYkV8mSMbH2gYysfP42nx\/3zSo7n0dBYDfU87Q6f96Cv597vEV27do8CaBkEk8Xjn2SxhHw8hVxracvE2OBAPxk0H3sRp\/cQBgjoXpju4kQin0N5E+DEJMh7Sp+u8aKoFpb+2FaAZJFn\/hnqxLTlVi2nyDxGL3U0eobWY+jWH9XPt52v3Hyh8TDhcAnQ1cN30B8Jn2+jkrm8ib+buchaCXHk0cPX72xuPECdwOEKLCBNrJa3FGSvO1zWiecnCgxCXgt+R8hUgsVPTsbrdFY2YRJGIhHndYZL98XzgG7cw85SnnMMe2SulzeL7xANGF8qiEVyiC7x83bbj5xOkeM\/CUTajrLBO3vyZ23KKOxvskjgI0t+Zw1zFsl+sYW0\/O\/V5WzPOwMwV8+iApQ8k9gEMiYQg98QLEMYnSohncmp0Z9qx2qFcQuHLcKJVa1J6wGtE\/EHR\/4d0aYPd6IRjg+qshCJmdzud\/12xjpGTl+BT0Hi0VsU5o7ZMi7WhmukZmmv8u0uZsvKREQNATm4cO4WCkYySt5O9gZEJOF+jjgeynDoAh09lyrNXIgMpM9ufm\/XEG\/I\/f2zIwbAUc6J6qks5OuYlJzW5vscTiOKhwcGZU9WBLgh0=",
- "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
-}';
- $this->environmentHelper
- ->expects($this->any())
- ->method('getServerRoot')
- ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessUnmodified/');
- $this->fileAccessHelper
- ->expects($this->once())
- ->method('file_put_contents')
- ->with(
- \OC::$SERVERROOT . '/tests/data/integritycheck/htaccessUnmodified//core/signature.json',
- $this->callback(function ($signature) use ($expectedSignatureFileData) {
- $expectedArray = json_decode($expectedSignatureFileData, true);
- $actualArray = json_decode($signature, true);
- $this->assertEquals($expectedArray, $actualArray);
- return true;
- })
- );
-
- $keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/core.crt');
- $rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/core.key');
- $rsa = RSA::loadPrivateKey($rsaPrivateKey);
- // After loading the key, always set the PSS padding and options:
- $rsa = $rsa
- ->withPadding(RSA::SIGNATURE_PSS)
- ->withMGFHash('sha512')
- ->withSaltLength(0);
- $x509 = new X509();
- $x509->loadX509($keyBundle);
- $this->checker->writeCoreSignature($x509, $rsa, \OC::$SERVERROOT . '/tests/data/integritycheck/htaccessUnmodified/');
- }
-
- public function testWriteCoreSignatureWithInvalidModifiedHtaccess(): void {
- $expectedSignatureFileData = '{
- "hashes": {
- ".htaccess": "4a54273dc8d697b2ca615acf2ae2c1ee3c1c643492cb04f42b10984fa9aacff1420dc829fd82f93ad3476fbd0cdab0251142c887dc8f872d03e39a3a3eb6d381"
- },
- "signature": "qpDddYGgAKNR3TszOgjPXRphUl2P9Ym5OQaetltocgZASGDkOun5D64+1D0QJRKb4SG2+48muxGOHyL2Ngos4NUrrSR+SIkywZacay82YQBCEdr7\/4MjW1WHRPjvboLwEJwViw0EdAjsWRpD68aPnzUGrGsy2BsCo06P5iwjk9cXcHxdjC9R39npvoC3QNvQ2jmNIbh1Lc4U97dbb+CsXEQCLU1OSa9p3q6cEFV98Easwt7uF\/DzHK+CbeZlxVZ0DwLh2\/ylT1PyGou8QC1b3vKAnPjLWMO+UsCPpCKhk3C5pV+5etQ8puGd+0x2t5tEU+qXxLzek91zWNC+rqgC\/WlqLKbwPb\/BCHs4zLGV55Q2fEQmT21x0KCUELdPs4dBnYP4Ox5tEDugtJujWFzOHzoY6gGa\/BY\/78pSZXmq9o8dWkBEtioWWvaNZ1rM0ddE83GBlBTgjigi9Ay1D++bUW\/FCBB7CMk6qyNlV81H+cBuIEODw2aymmkM9LLDD2Qbmvo8gHEPRjiQxPC5OpDlcdSNiL+zcxVxeuX4FpT+9xzz\/\/DRONhufxRpsbuCOMxd96RW7y9U2N2Uxb3Bzn\/BIqEayUUsdgZjfaGcXXYKR+chu\/LOwNYN6RlnLsgqL\/dhGKwlRVKXw1RA2\/af\/CpqyR7uVP6al1YJo\/YJ+5XJ6zE=",
- "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
-}';
- $this->fileAccessHelper
- ->expects($this->once())
- ->method('file_put_contents')
- ->with(
- \OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithInvalidModifiedContent//core/signature.json',
- $this->callback(function ($signature) use ($expectedSignatureFileData) {
- $expectedArray = json_decode($expectedSignatureFileData, true);
- $actualArray = json_decode($signature, true);
- $this->assertEquals($expectedArray, $actualArray);
- return true;
- })
- );
-
- $keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/core.crt');
- $rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/core.key');
- $rsa = RSA::loadPrivateKey($rsaPrivateKey);
- // After loading the key, always set the PSS padding and options:
- $rsa = $rsa
- ->withPadding(RSA::SIGNATURE_PSS)
- ->withMGFHash('sha512')
- ->withSaltLength(0);
- $x509 = new X509();
- $x509->loadX509($keyBundle);
- $this->checker->writeCoreSignature($x509, $rsa, \OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithInvalidModifiedContent/');
- }
-
- public function testWriteCoreSignatureWithValidModifiedHtaccess(): void {
- $expectedSignatureFileData = '{
- "hashes": {
- ".htaccess": "ee900c70c32f5475f301648f790f0836f330fe77af94154dfec7d290327c14b505aaea27945eb6862eaa104bbf8346c011752767af455b77b6676e382bcd5344",
- "subfolder\/.htaccess": "2c57b1e25050e11dc3ae975832f378c452159f7b69f818e47eeeafadd6ba568517461dcb4d843b90b906cd7c89d161bc1b89dff8e3ae0eb6f5088508c47befd1"
- },
- "signature": "LhHHUjQlsNacPWGO7dmnDYNU4pvuVuaXAG01w41A6ijwTB1ii3khtaAJcsT4HYoDWLZ9KnmAYPwmYWjEh9xvzvC7arOFsZFixSjaEjNQADqwUwacyNCyJ4an3JRw/nZPJqAgCDOtr3pJWixf2v7qOWbJrlu7yB/SF3wiSXRfE1s6Y8jAMERZJ5bfMjiLC1yXJ4VpIzn5ed3RnWfl0MWtPr/XEtOLkh+eoSExhmDw91w/gyJo4/+iCnnLjKDsIEPRcoe2/t4azaxdvCIwlStLsQXygXNUySeO2m2HnjZOCHy2E4MfTbrV8XuT3wPEYHWQTc3QAMehbjEsctTaI4RAoKbNtnjWrBqP+Z10cpIfiIENFMvMYu/mSzhZH84rjoywWaZ+MyQ+LxeMiugVZKLEEAWQ7rCKjBEILnJ1ssrTOkBf7tmom52FvCSTNkieEBNzupR5GjePRXfzrSXw56Yg8veq9ALkQH7yXviEiHlRDLa0F1MfOjp4QgJzH0yMVDZAnYjqWnHq05/VNxUJsMAtXHym9REekueo0WMTgqIAbV4ODSE5MBDGVSFGf+GkcpYaW6kl+lAF7UgoLTKCibNZNeeZzSy4h3aD3XGlQD3YGgN3smQRRNrfPjdgyy69x2wRarXZCsigvCDIlW/90Ubbaa9tbqPpODEkGR8M2Wbybzs=",
- "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
-}';
- $expectedArray = json_decode($expectedSignatureFileData, true);
- $actualArray = '';
-
- $this->environmentHelper
- ->expects($this->any())
- ->method('getServerRoot')
- ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent');
- $this->fileAccessHelper
- ->expects($this->once())
- ->method('file_put_contents')
- ->with(
- \OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent/core/signature.json',
- $this->callback(function ($signature) use (&$actualArray) {
- $actualArray = json_decode($signature, true);
- return true;
- })
- );
-
- $keyBundle = file_get_contents(__DIR__ . '/../../data/integritycheck/core.crt');
- $rsaPrivateKey = file_get_contents(__DIR__ . '/../../data/integritycheck/core.key');
- $rsa = RSA::loadPrivateKey($rsaPrivateKey);
- // After loading the key, always set the PSS padding and options:
- $rsa = $rsa
- ->withPadding(RSA::SIGNATURE_PSS)
- ->withMGFHash('sha512')
- ->withSaltLength(0);
- $x509 = new X509();
- $x509->loadX509($keyBundle);
- $this->checker->writeCoreSignature($x509, $rsa, \OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent');
-
- // now check that the actual signature file content matches the expected content
- // we cannot do in the callback because throwing an assertion error there would not be properly reported by PHPUnit
- $this->assertEquals($expectedArray, $actualArray);
- }
-
- public function testVerifyCoreSignatureWithoutSignatureData(): void {
- $this->serverVersion
- ->expects($this->once())
- ->method('getChannel')
- ->willReturn('stable');
- $this->config
- ->expects($this->any())
- ->method('getSystemValueBool')
- ->with('integrity.check.disabled', false)
- ->willReturn(false);
-
- $expected = [
- 'EXCEPTION' => [
- 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
- 'message' => 'Signature data not found.',
- ],
- ];
- $this->assertSame($expected, $this->checker->verifyCoreSignature());
- }
-
- public function testVerifyCoreSignatureWithValidSignatureData(): void {
- $this->serverVersion
- ->expects($this->once())
- ->method('getChannel')
- ->willReturn('stable');
- $this->config
- ->expects($this->any())
- ->method('getSystemValueBool')
- ->with('integrity.check.disabled', false)
- ->willReturn(false);
-
- $this->environmentHelper
- ->expects($this->any())
- ->method('getServerRoot')
- ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
- $signatureDataFile = '{
- "hashes": {
- "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
- "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
- },
- "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
- "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
-}';
- $this->fileAccessHelper
- ->expects($this->exactly(2))
- ->method('file_get_contents')
- ->willReturnMap([
- [\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json', $signatureDataFile],
- [\OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
- ]);
-
- $this->assertSame([], $this->checker->verifyCoreSignature());
- }
-
- /**
- * When updated use this to generate new signature data:
- * `occ integrity:sign-core --privateKey=./tests/data/integritycheck/core.key --certificate=./tests/data/integritycheck/core.crt --path=tests/data/integritycheck/htaccessWithValidModifiedContent`
- */
- public function testVerifyCoreSignatureWithValidModifiedHtaccessSignatureData(): void {
- $this->serverVersion
- ->expects($this->once())
- ->method('getChannel')
- ->willReturn('stable');
- $this->config
- ->expects($this->any())
- ->method('getSystemValueBool')
- ->with('integrity.check.disabled', false)
- ->willReturn(false);
-
- $this->environmentHelper
- ->expects($this->any())
- ->method('getServerRoot')
- ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent');
- $signatureDataFile = '{
- "hashes": {
- ".htaccess": "ee900c70c32f5475f301648f790f0836f330fe77af94154dfec7d290327c14b505aaea27945eb6862eaa104bbf8346c011752767af455b77b6676e382bcd5344",
- "subfolder\/.htaccess": "2c57b1e25050e11dc3ae975832f378c452159f7b69f818e47eeeafadd6ba568517461dcb4d843b90b906cd7c89d161bc1b89dff8e3ae0eb6f5088508c47befd1"
- },
- "signature": "LhHHUjQlsNacPWGO7dmnDYNU4pvuVuaXAG01w41A6ijwTB1ii3khtaAJcsT4HYoDWLZ9KnmAYPwmYWjEh9xvzvC7arOFsZFixSjaEjNQADqwUwacyNCyJ4an3JRw\/nZPJqAgCDOtr3pJWixf2v7qOWbJrlu7yB\/SF3wiSXRfE1s6Y8jAMERZJ5bfMjiLC1yXJ4VpIzn5ed3RnWfl0MWtPr\/XEtOLkh+eoSExhmDw91w\/gyJo4\/+iCnnLjKDsIEPRcoe2\/t4azaxdvCIwlStLsQXygXNUySeO2m2HnjZOCHy2E4MfTbrV8XuT3wPEYHWQTc3QAMehbjEsctTaI4RAoKbNtnjWrBqP+Z10cpIfiIENFMvMYu\/mSzhZH84rjoywWaZ+MyQ+LxeMiugVZKLEEAWQ7rCKjBEILnJ1ssrTOkBf7tmom52FvCSTNkieEBNzupR5GjePRXfzrSXw56Yg8veq9ALkQH7yXviEiHlRDLa0F1MfOjp4QgJzH0yMVDZAnYjqWnHq05\/VNxUJsMAtXHym9REekueo0WMTgqIAbV4ODSE5MBDGVSFGf+GkcpYaW6kl+lAF7UgoLTKCibNZNeeZzSy4h3aD3XGlQD3YGgN3smQRRNrfPjdgyy69x2wRarXZCsigvCDIlW\/90Ubbaa9tbqPpODEkGR8M2Wbybzs=",
- "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
-}';
- $this->fileAccessHelper
- ->expects($this->exactly(2))
- ->method('file_get_contents')
- ->willReturnMap([
- [\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent/core/signature.json', $signatureDataFile],
- [\OC::$SERVERROOT . '/tests/data/integritycheck/htaccessWithValidModifiedContent/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
- ]);
-
- $this->assertSame([], $this->checker->verifyCoreSignature());
- }
-
- /**
- * See inline instruction on how to update the test assets when changing mimetypealiases.dist.json
- */
- public function testVerifyCoreSignatureWithModifiedMimetypelistSignatureData(): void {
- $shippedMimetypeAliases = (array)json_decode(file_get_contents(\OC::$SERVERROOT . '/resources/config/mimetypealiases.dist.json'));
- $shippedMimetypeNames = (array)json_decode(file_get_contents(\OC::$SERVERROOT . '/resources/config/mimetypenames.dist.json'));
- $allAliases = array_merge($shippedMimetypeAliases, ['my-custom/mimetype' => 'custom']);
- $allMimetypeNames = array_merge($shippedMimetypeNames, ['my-custom/mimetype' => 'Custom Document']);
-
- $this->mimeTypeDetector
- ->method('getOnlyDefaultAliases')
- ->willReturn($shippedMimetypeAliases);
-
- $this->mimeTypeDetector
- ->method('getAllAliases')
- ->willReturn($allAliases);
-
- $this->mimeTypeDetector
- ->method('getAllNamings')
- ->willReturn($allMimetypeNames);
-
- $oldMimetypeList = new GenerateMimetypeFileBuilder();
- $all = $this->mimeTypeDetector->getAllAliases();
- $namings = $this->mimeTypeDetector->getAllNamings();
- $newFile = $oldMimetypeList->generateFile($all, $namings);
-
- // When updating the mimetype list the test assets need to be updated as well
- // 1. Update core/js/mimetypelist.js with the new generated js by running the test with the next line uncommented:
- // file_put_contents(\OC::$SERVERROOT . '/tests/data/integritycheck/mimetypeListModified/core/js/mimetypelist.js', $newFile);
- // 2. Update signature.json using the following occ command:
- // occ integrity:sign-core --privateKey=./tests/data/integritycheck/core.key --certificate=./tests/data/integritycheck/core.crt --path=./tests/data/integritycheck/mimetypeListModified
- self::assertEquals($newFile, file_get_contents(\OC::$SERVERROOT . '/tests/data/integritycheck/mimetypeListModified/core/js/mimetypelist.js'));
-
- $this->serverVersion
- ->expects($this->once())
- ->method('getChannel')
- ->willReturn('stable');
- $this->config
- ->expects($this->any())
- ->method('getSystemValueBool')
- ->with('integrity.check.disabled', false)
- ->willReturn(false);
-
- $this->environmentHelper
- ->expects($this->any())
- ->method('getServerRoot')
- ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/mimetypeListModified');
-
- $signatureDataFile = file_get_contents(__DIR__ . '/../../data/integritycheck/mimetypeListModified/core/signature.json');
- $this->fileAccessHelper
- ->method('file_get_contents')
- ->willReturnMap([
- [\OC::$SERVERROOT . '/tests/data/integritycheck/mimetypeListModified/core/signature.json', $signatureDataFile],
- [\OC::$SERVERROOT . '/tests/data/integritycheck/mimetypeListModified/resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
- ]);
-
- $this->assertSame([], $this->checker->verifyCoreSignature());
- }
-
- public function testVerifyCoreSignatureWithValidSignatureDataAndNotAlphabeticOrder(): void {
- $this->serverVersion
- ->expects($this->once())
- ->method('getChannel')
- ->willReturn('stable');
- $this->config
- ->expects($this->any())
- ->method('getSystemValueBool')
- ->with('integrity.check.disabled', false)
- ->willReturn(false);
-
- $this->environmentHelper
- ->expects($this->any())
- ->method('getServerRoot')
- ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
- $signatureDataFile = '{
- "hashes": {
- "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
- "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
- },
- "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
- "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
-}';
- $this->fileAccessHelper
- ->expects($this->exactly(2))
- ->method('file_get_contents')
- ->willReturnMap([
- [\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json', $signatureDataFile],
- [\OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
- ]);
-
- $this->assertSame([], $this->checker->verifyCoreSignature());
- }
-
- public function testVerifyCoreSignatureWithTamperedSignatureData(): void {
- $this->serverVersion
- ->expects($this->once())
- ->method('getChannel')
- ->willReturn('stable');
- $this->config
- ->expects($this->any())
- ->method('getSystemValueBool')
- ->with('integrity.check.disabled', false)
- ->willReturn(false);
-
- $this->environmentHelper
- ->expects($this->any())
- ->method('getServerRoot')
- ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/');
- $signatureDataFile = '{
- "hashes": {
- "AnotherFile.txt": "tampered",
- "subfolder\/file.txt": "tampered"
- },
- "signature": "eXesvDm3pkek12xSwMG10y9suRES79Nye3jYNe5KYq1tTUPqRRNgxmMGAfcUro0zpLeAr2YgHeSMWtglblGOW7pmwGVPZ0O1Y4r1fE6jnep0kW+35PLIaqCorIOnCAtSzDNKBhwd1ow3zW2wC0DFouuEkIO8u5Fw28g8E8dp8zEk1xMblNPy+xtWkmYHrVJ\/dQgun1bYOF2ZFtAzatwndTI\/bGsy1i3Wsl+x6HyWKQdq8y8VObtOqKDH7uERBEpB9DHVyKflj1v1gQuEH6BhaRdATc7ee0MiQdGblraIySwYRdfo2d8i82OVKrenMB3SLwyCvDPyQ9iKpTOnSF52ZBqaqSXKM2N\/RAkweeBFQQCwcHhqxvB0cfbyHcbkOLeCZe\/tsh68IxwTiYgzvLfl7sOZ5arnZbzrPpZmB+hfV2omkoJ1tDwOWz9hEmLLNtfo2OxyUH1m0+XFaC+Gbn4WkVDgf7YZkwUcG+Qoa3oKDNMss8MEyZxewl2iDGZcf402dlidHRprlfmXbAYuVQ08\/a0HxIKYPGh\/nsMGmwnO15CWtFpAbhUA\/D5oRjsIxnvXaMDg0iAFpdu\/5Ffsj7g3EPdBkiQHNYK7YU1RRx609eH0bZyiIYHdUPw7ikLupvrebZmELqi3mqDFO99u4eISlxFJlUbUND3L4BtmWTWrKwI=",
- "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
-}';
- $this->fileAccessHelper
- ->expects($this->exactly(2))
- ->method('file_get_contents')
- ->willReturnMap([
- [\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//core/signature.json', $signatureDataFile],
- [\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
- ]);
-
- $expected = [
- 'EXCEPTION' => [
- 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
- 'message' => 'Signature could not get verified.',
- ]
- ];
- $this->assertSame($expected, $this->checker->verifyCoreSignature());
- }
-
- public function testVerifyCoreSignatureWithTamperedFiles(): void {
- $this->serverVersion
- ->expects($this->once())
- ->method('getChannel')
- ->willReturn('stable');
- $this->config
- ->expects($this->any())
- ->method('getSystemValueBool')
- ->with('integrity.check.disabled', false)
- ->willReturn(false);
-
- $this->environmentHelper
- ->expects($this->any())
- ->method('getServerRoot')
- ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData/');
- $signatureDataFile = '{
- "hashes": {
- "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
- "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
- },
- "signature": "dYoohBaWIFR\/To1FXEbMQB5apUhVYlEauBGSPo12nq84wxWkBx2EM3KDRgkB5Sub2tr0CgmAc2EVjPhKIEzAam26cyUb48bJziz1V6wvW7z4GZAfaJpzLkyHdSfV5117VSf5w1rDcAeZDXfGUaaNEJPWytaF4ZIxVge7f3NGshHy4odFVPADy\/u6c43BWvaOtJ4m3aJQbP6sxCO9dxwcm5yJJJR3n36jfh229sdWBxyl8BhwhH1e1DEv78\/aiL6ckKFPVNzx01R6yDFt3TgEMR97YZ\/R6lWiXG+dsJ305jNFlusLu518zBUvl7g5yjzGN778H29b2C8VLZKmi\/h1CH9jGdD72fCqCYdenD2uZKzb6dsUtXtvBmVcVT6BUGz41W1pkkEEB+YJpMrHILIxAiHRGv1+aZa9\/Oz8LWFd+BEUQjC2LJgojPnpzaG\/msw1nBkX16NNVDWWtJ25Bc\/r\/mG46rwjWB\/cmV6Lwt6KODiqlxgrC4lm9ALOCEWw+23OcYhLwNfQTYevXqHqsFfXOkhUnM8z5vDUb\/HBraB1DjFXN8iLK+1YewD4P495e+SRzrR79Oi3F8SEqRIzRLfN2rnW1BTms\/wYsz0p67cup1Slk1XlNmHwbWX25NVd2PPlLOvZRGoqcKFpIjC5few8THiZfyjiNFwt3RM0AFdZcXY=",
- "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUc\/0FxYrsgSs9rDxp03EJmbjN0NwwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIxMDMzM1oXDTE2MTEwMzIxMDMzM1owDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBALb6EgHpkAqZbO5vRO8XSh7G7XGWHw5s\r\niOf4RwPXR6SE9bWZEm\/b72SfWk\/\/J6AbrD8WiOzBuT\/ODy6k5T1arEdHO+Pux0W1\r\nMxYJJI4kH74KKgMpC0SB0Rt+8WrMqV1r3hhJ46df6Xr\/xolP3oD+eLbShPcblhdS\r\nVtkZEkoev8Sh6L2wDCeHDyPxzvj1w2dTdGVO9Kztn0xIlyfEBakqvBWtcxyi3Ln0\r\nklnxlMx3tPDUE4kqvpia9qNiB1AN2PV93eNr5\/2riAzIssMFSCarWCx0AKYb54+d\r\nxLpcYFyqPJ0ydBCkF78DD45RCZet6PNYkdzgbqlUWEGGomkuDoJbBg4wzgzO0D77\r\nH87KFhYW8tKFFvF1V3AHl\/sFQ9tDHaxM9Y0pZ2jPp\/ccdiqnmdkBxBDqsiRvHvVB\r\nCn6qpb4vWGFC7vHOBfYspmEL1zLlKXZv3ezMZEZw7O9ZvUP3VO\/wAtd2vUW8UFiq\r\ns2v1QnNLN6jNh51obcwmrBvWhJy9vQIdtIjQbDxqWTHh1zUSrw9wrlklCBZ\/zrM0\r\ni8nfCFwTxWRxp3H9KoECzO\/zS5R5KIS7s3\/wq\/w9T2Ie4rcecgXwDizwnn0C\/aKc\r\nbDIjujpL1s9HO05pcD\/V3wKcPZ1izymBkmMyIbL52iRVN5FTVHeZdXPpFuq+CTQJ\r\nQ238lC+A\/KOVAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAGoKTnh8RfJV4sQItVC2\r\nAvfJagkrIqZ3iiQTUBQGTKBsTnAqE1H7QgUSV9vSd+8rgvHkyZsRjmtyR1e3A6Ji\r\noNCXUbExC\/0iCPUqdHZIVb+Lc\/vWuv4ByFMybGPydgtLoEUX2ZrKFWmcgZFDUSRd\r\n9Uj26vtUhCC4bU4jgu6hIrR9IuxOBLQUxGTRZyAcXvj7obqRAEZwFAKQgFpfpqTb\r\nH+kjcbZSaAlLVSF7vBc1syyI8RGYbqpwvtREqJtl5IEIwe6huEqJ3zPnlP2th\/55\r\ncf3Fovj6JJgbb9XFxrdnsOsDOu\/tpnaRWlvv5ib4+SzG5wWFT5UUEo4Wg2STQiiX\r\nuVSRQxK1LE1yg84bs3NZk9FSQh4B8vZVuRr5FaJsZZkwlFlhRO\/\/+TJtXRbyNgsf\r\noMRZGi8DLGU2SGEAHcRH\/QZHq\/XDUWVzdxrSBYcy7GSpT7UDVzGv1rEJUrn5veP1\r\n0KmauAqtiIaYRm4f6YBsn0INcZxzIPZ0p8qFtVZBPeHhvQtvOt0iXI\/XUxEWOa2F\r\nK2EqhErgMK\/N07U1JJJay5tYZRtvkGq46oP\/5kQG8hYST0MDK6VihJoPpvCmAm4E\r\npEYKQ96x6A4EH9Y9mZlYozH\/eqmxPbTK8n89\/p7Ydun4rI+B2iiLnY8REWWy6+UQ\r\nV204fGUkJqW5CrKy3P3XvY9X\r\n-----END CERTIFICATE-----"
-}';
- $this->fileAccessHelper
- ->expects($this->exactly(2))
- ->method('file_get_contents')
- ->willReturnMap([
- [\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//core/signature.json', $signatureDataFile],
- [\OC::$SERVERROOT . '/tests/data/integritycheck/appWithInvalidData//resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
- ]);
-
- $expected = [
- 'INVALID_HASH' => [
- 'AnotherFile.txt' => [
- 'expected' => '1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112',
- 'current' => '7322348ba269c6d5522efe02f424fa3a0da319a7cd9c33142a5afe32a2d9af2da3a411f086fcfc96ff4301ea566f481dba0960c2abeef3594c4d930462f6584c',
- ],
- ],
- 'FILE_MISSING' => [
- 'subfolder/file.txt' => [
- 'expected' => '410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b',
- 'current' => '',
- ],
- ],
- 'EXTRA_FILE' => [
- 'UnecessaryFile' => [
- 'expected' => '',
- 'current' => 'cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e',
- ],
- ],
- ];
- $this->assertSame($expected, $this->checker->verifyCoreSignature());
- }
-
- public function testVerifyCoreWithInvalidCertificate(): void {
- $this->serverVersion
- ->expects($this->once())
- ->method('getChannel')
- ->willReturn('stable');
- $this->config
- ->expects($this->any())
- ->method('getSystemValueBool')
- ->with('integrity.check.disabled', false)
- ->willReturn(false);
-
- $this->environmentHelper
- ->expects($this->any())
- ->method('getServerRoot')
- ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
- $signatureDataFile = '{
- "hashes": {
- "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
- "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
- },
- "signature": "eXesvDm3pkek12xSwMG10y9suRES79Nye3jYNe5KYq1tTUPqRRNgxmMGAfcUro0zpLeAr2YgHeSMWtglblGOW7pmwGVPZ0O1Y4r1fE6jnep0kW+35PLIaqCorIOnCAtSzDNKBhwd1ow3zW2wC0DFouuEkIO8u5Fw28g8E8dp8zEk1xMblNPy+xtWkmYHrVJ\/dQgun1bYOF2ZFtAzatwndTI\/bGsy1i3Wsl+x6HyWKQdq8y8VObtOqKDH7uERBEpB9DHVyKflj1v1gQuEH6BhaRdATc7ee0MiQdGblraIySwYRdfo2d8i82OVKrenMB3SLwyCvDPyQ9iKpTOnSF52ZBqaqSXKM2N\/RAkweeBFQQCwcHhqxvB0cfbyHcbkOLeCZe\/tsh68IxwTiYgzvLfl7sOZ5arnZbzrPpZmB+hfV2omkoJ1tDwOWz9hEmLLNtfo2OxyUH1m0+XFaC+Gbn4WkVDgf7YZkwUcG+Qoa3oKDNMss8MEyZxewl2iDGZcf402dlidHRprlfmXbAYuVQ08\/a0HxIKYPGh\/nsMGmwnO15CWtFpAbhUA\/D5oRjsIxnvXaMDg0iAFpdu\/5Ffsj7g3EPdBkiQHNYK7YU1RRx609eH0bZyiIYHdUPw7ikLupvrebZmELqi3mqDFO99u4eISlxFJlUbUND3L4BtmWTWrKwI=",
- "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEvjCCAqagAwIBAgIUPYoweUxCPqbDW4ntuh7QvgyqSrgwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIyNDIwNloXDTE2MTEwMzIyNDIwNlowDzENMAsGA1UEAwwEY29yZTCCAiIwDQYJ\r\nKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJui3nDbjOIjxNnthdBZplphujsN6u8K\r\nQ\/62zAuSwzXVp0+3IMgM\/2sepklVE8YfCyVJ5+SUJqnqHoUWVRVfs8jL0wW6nrHM\r\n\/lsscAguWCee4iAdNOqI9kq4+DUau8J45e62XA9mrAo\/8\/NKzFE2y2WduDoQZcm+\r\n8+dwcUUHXw2jl8dfrmvEMYSqTNDdb4rGmQpeV+dr9BLqr+x03U1Q08qCG9j7mSOz\r\ncvJENjOvC5uzAh5LCuCgxqG4o+mPzB0FtNnwoRRu6IsF3Y3KacRqPc30fB\/iXDn5\r\nBPr14uNxTTYWoZJ1F0tZrLzRbXdjJJOC+dnQurTtXWZ8WjPB1BWQYK7fW6t82mkN\r\n2Qe2xen99gs9nX5yY\/sHM3TKSJdM7AVCEv\/emW3gNjkvWTtRlN\/Nc7X2ckNwXcvo\r\n0yi3fSPjzXpDgLbhp1FzrMlHDn1VzmRT3r8wLByWa\/hsxrJDsBzwunMJYhXhmeKb\r\n3wX0tN\/EUJTWBntpwVOIGnRPD51oBoQUOMaEAq\/kz8PgN181bWZkJbRuf+FWkijQ\r\no+HR2lVF1jWXXst5Uc+s9HN81Uly7X4O9MMg0QxT4+wymtGDs6AOkwMi9rgBTrRB\r\n3tLU3XL2UIwRXgmd8cPtTu\/I6Bm7LdyaYtZ3yJTxRewq3nZdWypqBhD8uhpIYVkf\r\no4bxmGkVAQVTAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBAKKAX5EHgU1grODnJ0of\r\nspFpgB1K67YvclNUyuU6NQ6zBJx1\/w1RnM7uxLcxiiWj1BbUhwZQ0ojmEHeUyi6O\r\nGrDVajwhTccDMmja3u5adhEncx65\/H+lD85IPRRkS2qBDssMDdJHhZ0uI+40nI7M\r\nMq1kFjl+6wiuqZXqps66DuLbk45g\/ZlrFIrIo3Ix5vj0OVqwT+gO4LYirJK6KgVS\r\nUttbcEsc\/yKU9ThnM8\/n4m2jstZXfzKPgOsJrQcZrFOtpj+CWmBzVElBSPlDT3Nh\r\nHSgOeTFJ8bQBxj2iG5dLA+JZJQKxyJ1gy2ZtxIJ2GyvLtSe8NUSqvfPWOaAKEUV2\r\ngniytnEFLr+PcD+9EGux6jZNuj6HmtWVThTfD5VGFmtlVU2z71ZRYY0kn6J3mmFc\r\nS2ecEcCUwqG5YNLncEUCyZhC2klWql2SHyGctCEyWWY7ikIDjVzYt2EbcFvLNBnP\r\ntybN1TYHRRZxlug00CCoOE9EZfk46FkZpDvU6KmqJRofkNZ5sj+SffyGcwYwNrDH\r\nKqe8m+9lHf3CRTIDeMu8r2xl1I6M6ZZfjabbmVP9Jd6WN4s6f1FlXDWzhlT1N0Qw\r\nGzJj6xB+SPtS3UV05tBlvbfA4e06D5G9uD7Q8ONcINtMS0xsSJ2oo82AqlpvlF\/q\r\noj7YKHsaTVGA+FxBktZHfoxD\r\n-----END CERTIFICATE-----"
-}';
- $this->fileAccessHelper
- ->expects($this->exactly(2))
- ->method('file_get_contents')
- ->willReturnMap([
- [\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json', $signatureDataFile],
- [\OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
- ]);
-
- $expected = [
- 'EXCEPTION' => [
- 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
- 'message' => 'Certificate is not valid.',
- ]
- ];
- $this->assertSame($expected, $this->checker->verifyCoreSignature());
- }
-
- public function testVerifyCoreWithDifferentScope(): void {
- $this->serverVersion
- ->expects($this->once())
- ->method('getChannel')
- ->willReturn('stable');
- $this->config
- ->expects($this->any())
- ->method('getSystemValueBool')
- ->with('integrity.check.disabled', false)
- ->willReturn(false);
-
- $this->environmentHelper
- ->expects($this->any())
- ->method('getServerRoot')
- ->willReturn(\OC::$SERVERROOT . '/tests/data/integritycheck/app/');
- $signatureDataFile = '{
- "hashes": {
- "AnotherFile.txt": "1570ca9420e37629de4328f48c51da29840ddeaa03ae733da4bf1d854b8364f594aac560601270f9e1797ed4cd57c1aea87bf44cf4245295c94f2e935a2f0112",
- "subfolder\/file.txt": "410738545fb623c0a5c8a71f561e48ea69e3ada0981a455e920a5ae9bf17c6831ae654df324f9328ff8453de179276ae51931cca0fa71fe8ccde6c083ca0574b"
- },
- "signature": "EL49UaSeyMAqyMtqId+tgOhhwgOevPZsRLX4j2blnybAB6fN07z0936JqZV7+eMPiE30Idx+UCY6rCFN531Kqe9vAOCdgtHUSOjjKyKc+lvULESlMb6YQcrZrvDlEMMjzjH49ewG7Ai8sNN6HrRUd9U8ws+ewSkW2DOOBItj\/21RBnkrSt+2AtGXGigEvuTm57HrCYDj8\/lSkumC2GVkjLUHeLOKYo4PRNOr6yP5mED5v7zo66AWvXl2fKv54InZcdxsAk35lyK9DGZbk\/027ZRd0AOHT3LImRLvQ+8EAg3XLlRUy0hOFGgPC+jYonMzgYvsAXAXi2j8LnLJlsLwpFwu1k1B+kZVPMumKZvP9OvJb70EirecXmz62V+Jiyuaq7ne4y7Kp5gKZT\/T8SeZ0lFtCmPfYyzBB0y8s5ldmTTmdVYHs54t\/OCCW82HzQZxnFNPzDTRa8HglsaMKrqPtW59+R4UvRKSWhB8M\/Ah57qgzycvPV4KMz\/FbD4l\/\/9chRKSlCfc2k3b8ZSHNmi+EzCKgJjWIoKdgN1yax94puU8jfn8UW+G7H9Y1Jsf\/jox6QLyYEgtV1vOHY2xLT7fVs2vhyvkN2MNjJnmQ70gFG5Qz2lBz5wi6ZpB+tOfCcpbLxWAkoWoIrmC\/Ilqh7mfmRZ43g5upjkepHNd93ONuY8=",
- "certificate": "-----BEGIN CERTIFICATE-----\r\nMIIEwTCCAqmgAwIBAgIUWv0iujufs5lUr0svCf\/qTQvoyKAwDQYJKoZIhvcNAQEF\r\nBQAwIzEhMB8GA1UECgwYb3duQ2xvdWQgQ29kZSBTaWduaW5nIENBMB4XDTE1MTEw\r\nMzIyNDk1M1oXDTE2MTEwMzIyNDk1M1owEjEQMA4GA1UEAwwHU29tZUFwcDCCAiIw\r\nDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK8q0x62agGSRBqeWsaeEwFfepMk\r\nF8cAobMMi50qHCv9IrOn\/ZH9l52xBrbIkErVmRjmly0d4JhD8Ymhidsh9ONKYl\/j\r\n+ishsZDM8eNNdp3Ew+fEYVvY1W7mR1qU24NWj0bzVsClI7hvPVIuw7AjfBDq1C5+\r\nA+ZSLSXYvOK2cEWjdxQfuNZwEZSjmA63DUllBIrm35IaTvfuyhU6BW9yHZxmb8+M\r\nw0xDv30D5UkE\/2N7Pa\/HQJLxCR+3zKibRK3nUyRDLSXxMkU9PnFNaPNX59VPgyj4\r\nGB1CFSToldJVPF4pzh7p36uGXZVxs8m3LFD4Ol8mhi7jkxDZjqFN46gzR0r23Py6\r\ndol9vfawGIoUwp9LvL0S7MvdRY0oazLXwClLP4OQ17zpSMAiCj7fgNT661JamPGj\r\nt5O7Zn2wA7I4ddDS\/HDTWCu98Zwc9fHIpsJPgCZ9awoqxi4Mnf7Pk9g5nnXhszGC\r\ncxxIASQKM+GhdzoRxKknax2RzUCwCzcPRtCj8AQT\/x\/mqN3PfRmlnFBNACUw9bpZ\r\nSOoNq2pCF9igftDWpSIXQ38pVpKLWowjjg3DVRmVKBgivHnUnVLyzYBahHPj0vaz\r\ntFtUFRaqXDnt+4qyUGyrT5h5pjZaTcHIcSB4PiarYwdVvgslgwnQzOUcGAzRWBD4\r\n6jV2brP5vFY3g6iPAgMBAAEwDQYJKoZIhvcNAQEFBQADggIBACTY3CCHC+Z28gCf\r\nFWGKQ3wAKs+k4+0yoti0qm2EKX7rSGQ0PHSas6uW79WstC4Rj+DYkDtIhGMSg8FS\r\nHVGZHGBCc0HwdX+BOAt3zi4p7Sf3oQef70\/4imPoKxbAVCpd\/cveVcFyDC19j1yB\r\nBapwu87oh+muoeaZxOlqQI4UxjBlR\/uRSMhOn2UGauIr3dWJgAF4pGt7TtIzt+1v\r\n0uA6FtN1Y4R5O8AaJPh1bIG0CVvFBE58esGzjEYLhOydgKFnEP94kVPgJD5ds9C3\r\npPhEpo1dRpiXaF7WGIV1X6DI\/ipWvfrF7CEy6I\/kP1InY\/vMDjQjeDnJ\/VrXIWXO\r\nyZvHXVaN\/m+1RlETsH7YO\/QmxRue9ZHN3gvvWtmpCeA95sfpepOk7UcHxHZYyQbF\r\n49\/au8j+5tsr4A83xzsT1JbcKRxkAaQ7WDJpOnE5O1+H0fB+BaLakTg6XX9d4Fo7\r\n7Gin7hVWX7pL+JIyxMzME3LhfI61+CRcqZQIrpyaafUziPQbWIPfEs7h8tCOWyvW\r\nUO8ZLervYCB3j44ivkrxPlcBklDCqqKKBzDP9dYOtS\/P4RB1NkHA9+NTvmBpTonS\r\nSFXdg9fFMD7VfjDE3Vnk+8DWkVH5wBYowTAD7w9Wuzr7DumiAULexnP\/Y7xwxLv7\r\n4B+pXTAcRK0zECDEaX3npS8xWzrB\r\n-----END CERTIFICATE-----"
-}';
- $this->fileAccessHelper
- ->expects($this->exactly(2))
- ->method('file_get_contents')
- ->willReturnMap([
- [\OC::$SERVERROOT . '/tests/data/integritycheck/app//core/signature.json', $signatureDataFile],
- [\OC::$SERVERROOT . '/tests/data/integritycheck/app//resources/codesigning/root.crt', file_get_contents(__DIR__ . '/../../data/integritycheck/root.crt')],
- ]);
-
- $expected = [
- 'EXCEPTION' => [
- 'class' => 'OC\\IntegrityCheck\\Exceptions\\InvalidSignatureException',
- 'message' => 'Certificate is not valid for required scope. (Requested: core, current: CN=SomeApp)',
- ]
- ];
- $this->assertSame($expected, $this->checker->verifyCoreSignature());
- }
-
- public function testRunInstanceVerification(): void {
- /** @var Checker&MockObject */
- $checker = $this->getMockBuilder('\OC\IntegrityCheck\Checker')
- ->setConstructorArgs([
- $this->serverVersion,
- $this->environmentHelper,
- $this->fileAccessHelper,
- $this->config,
- $this->appConfig,
- $this->cacheFactory,
- $this->appManager,
- $this->mimeTypeDetector,
- ])
- ->onlyMethods([
- 'verifyCoreSignature',
- 'verifyAppSignature',
- ])
- ->getMock();
-
- $checker
- ->expects($this->once())
- ->method('verifyCoreSignature');
- $this->appManager
- ->expects($this->once())
- ->method('getAllAppsInAppsFolders')
- ->willReturn([
- 'files',
- 'calendar',
- 'contacts',
- 'dav',
- ]);
- $this->appManager
- ->expects($this->exactly(4))
- ->method('isShipped')
- ->willReturnMap([
- ['files', true],
- ['calendar', false],
- ['contacts', false],
- ['dav', true],
- ]);
-
- $calls = [
- 'files',
- 'calendar',
- 'dav',
- ];
- $checker
- ->expects($this->exactly(3))
- ->method('verifyAppSignature')
- ->willReturnCallback(function ($app) use (&$calls) {
- $expected = array_shift($calls);
- $this->assertSame($expected, $app);
- return [];
- });
- $this->appManager
- ->expects($this->exactly(2))
- ->method('getAppPath')
- ->willReturnMap([
- ['calendar', '/apps/calendar'],
- ['contacts', '/apps/contacts'],
- ]);
- $this->fileAccessHelper
- ->expects($this->exactly(2))
- ->method('file_exists')
- ->willReturnMap([
- ['/apps/calendar/appinfo/signature.json', true],
- ['/apps/contacts/appinfo/signature.json', false],
- ]);
- $this->appConfig
- ->expects($this->once())
- ->method('deleteKey')
- ->with('core', 'oc.integritycheck.checker');
-
- $checker->runInstanceVerification();
- }
-
- public function testVerifyAppSignatureWithoutSignatureDataAndCodeCheckerDisabled(): void {
- $this->serverVersion
- ->expects($this->once())
- ->method('getChannel')
- ->willReturn('stable');
- $this->config
- ->expects($this->any())
- ->method('getSystemValueBool')
- ->with('integrity.check.disabled', false)
- ->willReturn(true);
-
- $expected = [];
- $this->assertSame($expected, $this->checker->verifyAppSignature('SomeApp'));
- }
-
- public static function channelDataProvider(): array {
- return [
- ['stable', true],
- ['git', false],
- ];
- }
-
- /**
- * @param string $channel
- * @param bool $isCodeSigningEnforced
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('channelDataProvider')]
- public function testIsCodeCheckEnforced($channel, $isCodeSigningEnforced): void {
- $this->serverVersion
- ->expects($this->once())
- ->method('getChannel')
- ->willReturn($channel);
- $this->config
- ->expects($this->any())
- ->method('getSystemValueBool')
- ->with('integrity.check.disabled', false)
- ->willReturn(false);
-
- $this->assertSame($isCodeSigningEnforced, $this->checker->isCodeCheckEnforced());
- }
-
- /**
- * @param string $channel
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('channelDataProvider')]
- public function testIsCodeCheckEnforcedWithDisabledConfigSwitch($channel): void {
- $this->serverVersion
- ->expects($this->once())
- ->method('getChannel')
- ->willReturn($channel);
- $this->config
- ->expects($this->any())
- ->method('getSystemValueBool')
- ->with('integrity.check.disabled', false)
- ->willReturn(true);
-
- $this->assertFalse(self::invokePrivate($this->checker, 'isCodeCheckEnforced'));
- }
-}
--- a/tests/lib/IntegrityCheck/Helpers/EnvironmentHelperTest.php
+++ /dev/null
@@ -1,27 +0,0 @@
-environmentHelper = new EnvironmentHelper();
- parent::setUp();
- }
-
- public function testGetServerRoot(): void {
- $this->assertSame(\OC::$SERVERROOT, $this->environmentHelper->getServerRoot());
- }
-}
--- a/tests/lib/IntegrityCheck/Helpers/FileAccessHelperTest.php
+++ /dev/null
@@ -1,58 +0,0 @@
-fileAccessHelper = new FileAccessHelper();
- }
-
- public function testReadAndWrite(): void {
- $tempManager = Server::get(ITempManager::class);
- $filePath = $tempManager->getTemporaryFile();
- $data = 'SomeDataGeneratedByIntegrityCheck';
-
- $this->fileAccessHelper->file_put_contents($filePath, $data);
- $this->assertSame($data, $this->fileAccessHelper->file_get_contents($filePath));
- }
-
- public function testFile_put_contentsWithException(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Failed to write into /anabsolutelynotexistingfolder/on/the/system.txt');
-
- $this->fileAccessHelper->file_put_contents('/anabsolutelynotexistingfolder/on/the/system.txt', 'MyFiles');
- }
-
- public function testIs_writable(): void {
- $this->assertFalse($this->fileAccessHelper->is_writable('/anabsolutelynotexistingfolder/on/the/system.txt'));
- $this->assertTrue($this->fileAccessHelper->is_writable(Server::get(ITempManager::class)->getTemporaryFile('MyFile')));
- }
-
- public function testAssertDirectoryExistsWithException(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Directory /anabsolutelynotexistingfolder/on/the/system does not exist.');
-
- $this->fileAccessHelper->assertDirectoryExists('/anabsolutelynotexistingfolder/on/the/system');
- }
-
- public function testAssertDirectoryExists(): void {
- $this->fileAccessHelper->assertDirectoryExists(Server::get(ITempManager::class)->getTemporaryFolder('/testfolder/'));
- $this->addToAssertionCount(1);
- }
-}
--- a/tests/lib/IntegrityCheck/Iterator/ExcludeFileByNameFilterIteratorTest.php
+++ /dev/null
@@ -1,83 +0,0 @@
-filter = $this->getMockBuilder(ExcludeFileByNameFilterIterator::class)
- ->disableOriginalConstructor()
- ->onlyMethods(['current'])
- ->getMock();
- }
-
- public static function fileNameProvider(): array {
- return [
- ['a file', true],
- ['Thumbs.db', false],
- ['another file', true],
- ['.directory', false],
- ['.webapp-nextcloud-15.0.2', false],
- ['.webapp-nextcloud-14.0.5-r3', false],
- ['wx.webapp-nextcloud-obee', true],
- ['.rnd', false],
- ];
- }
-
- /**
- * @param string $fileName
- * @param bool $expectedResult
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('fileNameProvider')]
- public function testAcceptForFiles($fileName, $expectedResult): void {
- $iteratorMock = $this->getMockBuilder(\RecursiveDirectoryIterator::class)
- ->disableOriginalConstructor()
- ->onlyMethods(['getFilename', 'isDir'])
- ->getMock();
-
- $iteratorMock->method('getFilename')
- ->willReturn($fileName);
- $iteratorMock->method('isDir')
- ->willReturn(false);
- $this->filter->method('current')
- ->willReturn($iteratorMock);
-
- $actualResult = $this->filter->accept();
- $this->assertEquals($expectedResult, $actualResult);
- }
-
- /**
- * @param string $fileName
- * @param bool $expectedResult
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('fileNameProvider')]
- public function testAcceptForDirs($fileName, $expectedResult): void {
- $iteratorMock = $this->getMockBuilder(\RecursiveDirectoryIterator::class)
- ->disableOriginalConstructor()
- ->onlyMethods(['getFilename', 'isDir'])
- ->getMock();
-
- $iteratorMock->method('getFilename')
- ->willReturn($fileName);
- $iteratorMock->method('isDir')
- ->willReturn(true);
- $this->filter->method('current')
- ->willReturn($iteratorMock);
-
- $actualResult = $this->filter->accept();
- $this->assertTrue($actualResult);
- }
-}
--- a/tests/lib/Interaction/RestrictInteractionEventTest.php
+++ /dev/null
@@ -1,111 +0,0 @@
-
- */
- public static function dataIsInteractionRestricted(): array {
- return [
- [true],
- [false],
- ];
- }
-
- #[DataProvider('dataIsInteractionRestricted')]
- public function testIsInteractionRestricted(bool $isRestricted): void {
- $eventDispatcher = Server::get(IEventDispatcher::class);
-
- $auditEvents = [];
- $auditEventListener = function (CriticalActionPerformedEvent $event) use (&$auditEvents): void {
- $auditEvents[] = $event;
- };
- $eventDispatcher->addListener(CriticalActionPerformedEvent::class, $auditEventListener);
-
- /** @psalm-suppress UnusedClosureParam */
- $restrictInteractionEventListener = function (RestrictInteractionEvent $event) use ($isRestricted): void {
- if ($isRestricted) {
- throw new InteractionRestrictedException('my restriction message', 'my restriction hint');
- }
- };
- $eventDispatcher->addListener(RestrictInteractionEvent::class, $restrictInteractionEventListener);
-
- $user = $this->createMock(IUser::class);
- $user
- ->method('getUID')
- ->willReturn('my-uid');
-
- $resource1 = $this->createMock(InteractionResource::class);
- $resource1
- ->method('getID')
- ->willReturn('my-resource1');
-
- $resource2 = $this->createMock(InteractionResource::class);
- $resource2
- ->method('getID')
- ->willReturn('my-resource2');
-
- $action = $this->createStub(InteractionAction::class);
-
- $receiver1 = $this->createMock(InteractionReceiver::class);
- $receiver1
- ->method('getID')
- ->willReturn('my-receiver1');
-
- $receiver2 = $this->createMock(InteractionReceiver::class);
- $receiver2
- ->method('getID')
- ->willReturn('my-receiver2');
-
- $event = new RestrictInteractionEvent(
- $user->getUID(),
- $user,
- [$resource1, $resource2],
- $action,
- [$receiver1, $receiver2],
- );
-
- if ($isRestricted) {
- $this->assertEquals('my restriction hint', $event->isInteractionRestricted());
- } else {
- $this->assertFalse($event->isInteractionRestricted());
- }
-
- $this->assertEquals([
- new CriticalActionPerformedEvent(
- $isRestricted
- ? 'Interaction %s from user %s on %s to %s is restricted: my restriction message'
- : 'Interaction %s from user %s on %s to %s is allowed.',
- [
- $action::class,
- 'my-uid',
- 'my-resource1, my-resource2',
- 'my-receiver1, my-receiver2',
- ],
- ),
- ], $auditEvents);
-
- $eventDispatcher->removeListener(CriticalActionPerformedEvent::class, $auditEventListener);
- $eventDispatcher->removeListener(RestrictInteractionEvent::class, $restrictInteractionEventListener);
- }
-}
--- a/tests/lib/L10N/FactoryTest.php
+++ /dev/null
@@ -1,783 +0,0 @@
-config = $this->createMock(IConfig::class);
- $this->request = $this->createMock(IRequest::class);
- $this->userSession = $this->createMock(IUserSession::class);
- $this->cacheFactory = $this->createMock(ICacheFactory::class);
- $this->appManager = $this->createMock(IAppManager::class);
-
- $this->serverRoot = \OC::$SERVERROOT;
-
- $this->config
- ->method('getSystemValueBool')
- ->willReturnMap([
- ['installed', false, true],
- ]);
- }
-
- /**
- * @param string[] $methods
- * @param bool $mockRequestGetHeaderMethod
- *
- * @return Factory|MockObject
- */
- protected function getFactory(array $methods = [], $mockRequestGetHeaderMethod = false) {
- if ($mockRequestGetHeaderMethod) {
- $this->request->expects(self::any())
- ->method('getHeader')
- ->willReturn('');
- }
-
- if (!empty($methods)) {
- return $this->getMockBuilder(Factory::class)
- ->setConstructorArgs([
- $this->config,
- $this->request,
- $this->userSession,
- $this->cacheFactory,
- $this->serverRoot,
- $this->appManager,
- ])
- ->onlyMethods($methods)
- ->getMock();
- }
-
- return new Factory($this->config, $this->request, $this->userSession, $this->cacheFactory, $this->serverRoot, $this->appManager);
- }
-
- public static function dataCleanLanguage(): array {
- return [
- 'null shortcut' => [null, null],
- 'default language' => ['de', 'de'],
- 'regional language' => ['de_DE', 'de_DE'],
- 'malicious language' => ['de/../fr', 'defr'],
- 'request language' => ['kab;q=0.8,ka;q=0.7,de;q=0.6', 'kab;q=0.8,ka;q=0.7,de;q=0.6'],
- ];
- }
-
- #[DataProvider('dataCleanLanguage')]
- public function testCleanLanguage(?string $lang, ?string $expected): void {
- $factory = $this->getFactory();
- $this->assertSame($expected, self::invokePrivate($factory, 'cleanLanguage', [$lang]));
- }
-
- public static function dataFindAvailableLanguages(): array {
- return [
- [null],
- ['files'],
- ];
- }
-
- public function testFindLanguageWithExistingRequestLanguageAndNoApp(): void {
- $factory = $this->getFactory(['languageExists']);
- $this->invokePrivate($factory, 'requestLanguage', ['de']);
- $factory->expects(self::once())
- ->method('languageExists')
- ->with(null, 'de')
- ->willReturn(true);
-
- self::assertSame('de', $factory->findLanguage());
- }
-
- public function testFindLanguageWithExistingRequestLanguageAndApp(): void {
- $factory = $this->getFactory(['languageExists']);
- $this->invokePrivate($factory, 'requestLanguage', ['de']);
- $factory->expects(self::once())
- ->method('languageExists')
- ->with('MyApp', 'de')
- ->willReturn(true);
-
- self::assertSame('de', $factory->findLanguage('MyApp'));
- }
-
- public function testFindLanguageWithNotExistingRequestLanguageAndExistingStoredUserLanguage(): void {
- $factory = $this->getFactory(['languageExists']);
- $this->invokePrivate($factory, 'requestLanguage', ['de']);
- $factory->expects($this->exactly(2))
- ->method('languageExists')
- ->willReturnMap([
- ['MyApp', 'de', false],
- ['MyApp', 'jp', true],
- ]);
- $this->config
- ->expects($this->exactly(1))
- ->method('getSystemValue')
- ->willReturnMap([
- ['force_language', false, false],
- ]);
- $user = $this->createMock(IUser::class);
- $user->expects(self::once())
- ->method('getUID')
- ->willReturn('MyUserUid');
- $this->userSession
- ->expects(self::exactly(2))
- ->method('getUser')
- ->willReturn($user);
- $this->config
- ->expects(self::once())
- ->method('getUserValue')
- ->with('MyUserUid', 'core', 'lang', null)
- ->willReturn('jp');
-
- self::assertSame('jp', $factory->findLanguage('MyApp'));
- }
-
- public function testFindLanguageWithNotExistingRequestLanguageAndNotExistingStoredUserLanguage(): void {
- $factory = $this->getFactory(['languageExists'], true);
- $this->invokePrivate($factory, 'requestLanguage', ['de']);
- $factory->expects($this->exactly(3))
- ->method('languageExists')
- ->willReturnMap([
- ['MyApp', 'de', false],
- ['MyApp', 'jp', false],
- ['MyApp', 'es', true],
- ]);
- $this->config
- ->expects($this->exactly(2))
- ->method('getSystemValue')
- ->willReturnMap([
- ['force_language', false, false],
- ['default_language', false, 'es']
- ]);
- $user = $this->createMock(IUser::class);
- $user->expects(self::once())
- ->method('getUID')
- ->willReturn('MyUserUid');
- $this->userSession
- ->expects(self::exactly(2))
- ->method('getUser')
- ->willReturn($user);
- $this->config
- ->expects(self::once())
- ->method('getUserValue')
- ->with('MyUserUid', 'core', 'lang', null)
- ->willReturn('jp');
-
- self::assertSame('es', $factory->findLanguage('MyApp'));
- }
-
- public function testFindLanguageWithNotExistingRequestLanguageAndNotExistingStoredUserLanguageAndNotExistingDefault(): void {
- $factory = $this->getFactory(['languageExists'], true);
- $this->invokePrivate($factory, 'requestLanguage', ['de']);
- $factory->expects($this->exactly(3))
- ->method('languageExists')
- ->willReturnMap([
- ['MyApp', 'de', false],
- ['MyApp', 'jp', false],
- ['MyApp', 'es', false],
- ]);
- $this->config
- ->expects($this->exactly(2))
- ->method('getSystemValue')
- ->willReturnMap([
- ['force_language', false, false],
- ['default_language', false, 'es']
- ]);
- $user = $this->createMock(IUser::class);
- $user->expects(self::once())
- ->method('getUID')
- ->willReturn('MyUserUid');
- $this->userSession
- ->expects(self::exactly(2))
- ->method('getUser')
- ->willReturn($user);
- $this->config
- ->expects(self::once())
- ->method('getUserValue')
- ->with('MyUserUid', 'core', 'lang', null)
- ->willReturn('jp');
- $this->config
- ->expects(self::never())
- ->method('setUserValue');
-
- self::assertSame('en', $factory->findLanguage('MyApp'));
- }
-
- public function testFindLanguageWithNotExistingRequestLanguageAndNotExistingStoredUserLanguageAndNotExistingDefaultAndNoAppInScope(): void {
- $factory = $this->getFactory(['languageExists'], true);
- $this->invokePrivate($factory, 'requestLanguage', ['de']);
- $factory->expects($this->exactly(3))
- ->method('languageExists')
- ->willReturnMap([
- ['MyApp', 'de', false],
- ['MyApp', 'jp', false],
- ['MyApp', 'es', false],
- ]);
- $this->config
- ->expects($this->exactly(2))
- ->method('getSystemValue')
- ->willReturnMap([
- ['force_language', false, false],
- ['default_language', false, 'es']
- ]);
- $user = $this->createMock(IUser::class);
- $user->expects(self::once())
- ->method('getUID')
- ->willReturn('MyUserUid');
- $this->userSession
- ->expects(self::exactly(2))
- ->method('getUser')
- ->willReturn($user);
- $this->config
- ->expects(self::once())
- ->method('getUserValue')
- ->with('MyUserUid', 'core', 'lang', null)
- ->willReturn('jp');
- $this->config
- ->expects(self::never())
- ->method('setUserValue')
- ->with('MyUserUid', 'core', 'lang', 'en');
-
- self::assertSame('en', $factory->findLanguage('MyApp'));
- }
-
- public function testFindLanguageWithForcedLanguage(): void {
- $factory = $this->getFactory(['languageExists']);
- $this->config
- ->expects($this->once())
- ->method('getSystemValue')
- ->with('force_language', false)
- ->willReturn('de');
-
- $factory->expects($this->once())
- ->method('languageExists')
- ->with('MyApp', 'de')
- ->willReturn(true);
-
- self::assertSame('de', $factory->findLanguage('MyApp'));
- }
-
- /**
- * @param string|null $app
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataFindAvailableLanguages')]
- public function testFindAvailableLanguages($app): void {
- $factory = $this->getFactory(['findL10nDir']);
- $factory->expects(self::once())
- ->method('findL10nDir')
- ->with($app)
- ->willReturn(\OC::$SERVERROOT . '/tests/data/l10n/');
-
- self::assertEqualsCanonicalizing(['cs', 'de', 'en', 'ru'], $factory->findAvailableLanguages($app));
- }
-
- public static function dataLanguageExists(): array {
- return [
- [null, 'en', [], true],
- [null, 'de', [], false],
- [null, 'de', ['ru'], false],
- [null, 'de', ['ru', 'de'], true],
- ['files', 'en', [], true],
- ['files', 'de', [], false],
- ['files', 'de', ['ru'], false],
- ['files', 'de', ['de', 'ru'], true],
- ];
- }
-
- public function testFindAvailableLanguagesWithThemes(): void {
- $this->serverRoot .= '/tests/data';
- $app = 'files';
-
- $factory = $this->getFactory(['findL10nDir']);
- $factory->expects(self::once())
- ->method('findL10nDir')
- ->with($app)
- ->willReturn($this->serverRoot . '/apps/files/l10n/');
- $this->config
- ->expects(self::once())
- ->method('getSystemValueString')
- ->with('theme')
- ->willReturn('abc');
-
- self::assertEqualsCanonicalizing(['en', 'zz'], $factory->findAvailableLanguages($app));
- }
-
- /**
- *
- * @param string|null $app
- * @param string $lang
- * @param string[] $availableLanguages
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataLanguageExists')]
- public function testLanguageExists($app, $lang, array $availableLanguages, $expected): void {
- $factory = $this->getFactory(['findAvailableLanguages']);
- $factory->expects(($lang === 'en') ? self::never() : self::once())
- ->method('findAvailableLanguages')
- ->with($app)
- ->willReturn($availableLanguages);
-
- self::assertSame($expected, $factory->languageExists($app, $lang));
- }
-
- public static function dataSetLanguageFromRequest(): array {
- return [
- // Language is available
- [null, 'de', ['de'], 'de'],
- [null, 'de,en', ['de'], 'de'],
- [null, 'de-DE,en-US;q=0.8,en;q=0.6', ['de'], 'de'],
- // Language is not available
- [null, 'de', ['ru'], new LanguageNotFoundException()],
- [null, 'de,en', ['ru', 'en'], 'en'],
- [null, 'de-DE,en-US;q=0.8,en;q=0.6', ['ru', 'en'], 'en'],
-
- // Don't fall back from kab (Kabyle) to ka (Georgian) - Unless specifically requested
- [null, 'kab;q=0.8,en;q=0.6', ['ka', 'en'], 'en'],
- [null, 'kab;q=0.8,de;q=0.6', ['ka', 'en', 'de'], 'de'],
- [null, 'kab;q=0.8,de;q=0.7,ka;q=0.6', ['ka', 'en', 'de'], 'de'],
- [null, 'kab;q=0.8,ka;q=0.7,de;q=0.6', ['ka', 'en', 'de'], 'ka'],
-
- // Language for app
- ['files_pdfviewer', 'de', ['de'], 'de'],
- ['files_pdfviewer', 'de,en', ['de'], 'de'],
- ['files_pdfviewer', 'de-DE,en-US;q=0.8,en;q=0.6', ['de'], 'de'],
- // Language for app is not available
- ['files_pdfviewer', 'de', ['ru'], new LanguageNotFoundException()],
- ['files_pdfviewer', 'de,en', ['ru', 'en'], 'en'],
- ['files_pdfviewer', 'de-DE,en-US;q=0.8,en;q=0.6', ['ru', 'en'], 'en'],
- ];
- }
-
- /**
- *
- * @param string|null $app
- * @param string $header
- * @param string[] $availableLanguages
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetLanguageFromRequest')]
- public function testGetLanguageFromRequest($app, $header, array $availableLanguages, $expected): void {
- $factory = $this->getFactory(['findAvailableLanguages', 'respectDefaultLanguage']);
- $factory->expects(self::once())
- ->method('findAvailableLanguages')
- ->with($app)
- ->willReturn($availableLanguages);
-
- $factory->expects(self::any())
- ->method('respectDefaultLanguage')->willReturnCallback(function ($app, $lang) {
- return $lang;
- });
-
- $this->request->expects(self::once())
- ->method('getHeader')
- ->with('ACCEPT_LANGUAGE')
- ->willReturn($header);
-
- if ($expected instanceof LanguageNotFoundException) {
- $this->expectException(LanguageNotFoundException::class);
- self::invokePrivate($factory, 'getLanguageFromRequest', [$app]);
- } else {
- self::assertSame($expected, self::invokePrivate($factory, 'getLanguageFromRequest', [$app]), 'Asserting returned language');
- }
- }
-
- public static function dataGetL10nFilesForApp(): array {
- return [
- ['', 'de', [\OC::$SERVERROOT . '/core/l10n/de.json']],
- ['core', 'ru', [\OC::$SERVERROOT . '/core/l10n/ru.json']],
- ['lib', 'ru', [\OC::$SERVERROOT . '/lib/l10n/ru.json']],
- ['settings', 'de', [\OC::$SERVERROOT . '/apps/settings/l10n/de.json']],
- ['files', 'de', [\OC::$SERVERROOT . '/apps/files/l10n/de.json']],
- ['files', '_lang_never_exists_', []],
- ['_app_never_exists_', 'de', [\OC::$SERVERROOT . '/core/l10n/de.json']],
- ];
- }
-
- /**
- *
- * @param string $app
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetL10nFilesForApp')]
- public function testGetL10nFilesForApp($app, $lang, $expected): void {
- $factory = $this->getFactory();
- if (in_array($app, ['settings','files'])) {
- $this->appManager
- ->method('getAppPath')
- ->with($app)
- ->willReturn(\OC::$SERVERROOT . '/apps/' . $app);
- } else {
- $this->appManager
- ->method('getAppPath')
- ->with($app)
- ->willThrowException(new AppPathNotFoundException());
- }
- self::assertSame($expected, $this->invokePrivate($factory, 'getL10nFilesForApp', [$app, $lang]));
- }
-
- public static function dataFindL10NDir(): array {
- return [
- ['', \OC::$SERVERROOT . '/core/l10n/'],
- ['core', \OC::$SERVERROOT . '/core/l10n/'],
- ['lib', \OC::$SERVERROOT . '/lib/l10n/'],
- ['settings', \OC::$SERVERROOT . '/apps/settings/l10n/'],
- ['files', \OC::$SERVERROOT . '/apps/files/l10n/'],
- ['_app_never_exists_', \OC::$SERVERROOT . '/core/l10n/'],
- ];
- }
-
- /**
- *
- * @param string $app
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataFindL10NDir')]
- public function testFindL10NDir($app, $expected): void {
- $factory = $this->getFactory();
- if (in_array($app, ['settings','files'])) {
- $this->appManager
- ->method('getAppPath')
- ->with($app)
- ->willReturn(\OC::$SERVERROOT . '/apps/' . $app);
- } else {
- $this->appManager
- ->method('getAppPath')
- ->with($app)
- ->willThrowException(new AppPathNotFoundException());
- }
- self::assertSame($expected, $this->invokePrivate($factory, 'findL10nDir', [$app]));
- }
-
- public static function dataFindLanguage(): array {
- return [
- // Not logged in
- [false, [], 'en'],
- [false, ['fr'], 'fr'],
- [false, ['de', 'fr'], 'de'],
- [false, ['nl', 'de', 'fr'], 'de'],
-
- [true, [], 'en'],
- [true, ['fr'], 'fr'],
- [true, ['de', 'fr'], 'de'],
- [true, ['nl', 'de', 'fr'], 'nl'],
- ];
- }
-
- /**
- *
- * @param bool $loggedIn
- * @param array $availableLang
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataFindLanguage')]
- public function testFindLanguage($loggedIn, $availableLang, $expected): void {
- $userLang = 'nl';
- $browserLang = 'de';
- $defaultLang = 'fr';
-
- $this->config->expects(self::any())
- ->method('getSystemValue')
- ->willReturnCallback(function ($var, $default) use ($defaultLang) {
- if ($var === 'default_language') {
- return $defaultLang;
- } else {
- return $default;
- }
- });
-
- if ($loggedIn) {
- $user = $this->createMock(IUser::class);
- $user->expects(self::any())
- ->method('getUID')
- ->willReturn('MyUserUid');
- $this->userSession
- ->expects(self::any())
- ->method('getUser')
- ->willReturn($user);
- $this->config->expects(self::any())
- ->method('getUserValue')
- ->with('MyUserUid', 'core', 'lang', null)
- ->willReturn($userLang);
- } else {
- $this->userSession
- ->expects(self::any())
- ->method('getUser')
- ->willReturn(null);
- }
-
- $this->request->expects(self::any())
- ->method('getHeader')
- ->with($this->equalTo('ACCEPT_LANGUAGE'))
- ->willReturn($browserLang);
-
- $factory = $this->getFactory(['languageExists', 'findAvailableLanguages', 'respectDefaultLanguage']);
- $factory->expects(self::any())
- ->method('languageExists')
- ->willReturnCallback(function ($app, $lang) use ($availableLang) {
- return in_array($lang, $availableLang);
- });
- $factory->expects(self::any())
- ->method('findAvailableLanguages')
- ->willReturnCallback(function ($app) use ($availableLang) {
- return $availableLang;
- });
- $factory->expects(self::any())
- ->method('respectDefaultLanguage')->willReturnCallback(function ($app, $lang) {
- return $lang;
- });
-
- $lang = $factory->findLanguage();
-
- self::assertSame($expected, $lang);
- }
-
- public function testFindGenericLanguageByRequestParam(): void {
- $factory = $this->getFactory();
- $this->request->expects(self::once())
- ->method('getParam')
- ->with('forceLanguage')
- ->willReturn('cz');
-
- $lang = $factory->findGenericLanguage();
-
- self::assertSame('cz', $lang);
- }
-
- public function testFindGenericLanguageByEnforcedLanguage(): void {
- $factory = $this->getFactory();
- $this->request->expects(self::once())
- ->method('getParam')
- ->with('forceLanguage')
- ->willReturn(null);
- $this->config->expects(self::once())
- ->method('getSystemValue')
- ->with('force_language', false)
- ->willReturn('cz');
-
- $lang = $factory->findGenericLanguage();
-
- self::assertSame('cz', $lang);
- }
-
- public function testFindGenericLanguageByDefaultLanguage(): void {
- $factory = $this->getFactory(['languageExists']);
- $this->request->expects(self::once())
- ->method('getParam')
- ->with('forceLanguage')
- ->willReturn(null);
- $this->config->expects(self::exactly(2))
- ->method('getSystemValue')
- ->willReturnMap([
- ['force_language', false, false,],
- ['default_language', false, 'cz',],
- ]);
- $factory->expects(self::once())
- ->method('languageExists')
- ->with(null, 'cz')
- ->willReturn(true);
-
- $lang = $factory->findGenericLanguage();
-
- self::assertSame('cz', $lang);
- }
-
- public function testFindGenericLanguageByDefaultLanguageNotExists(): void {
- $factory = $this->getFactory(['languageExists']);
- $this->request->expects(self::once())
- ->method('getParam')
- ->with('forceLanguage')
- ->willReturn(null);
- $this->config->expects(self::exactly(2))
- ->method('getSystemValue')
- ->willReturnMap([
- ['force_language', false, false,],
- ['default_language', false, 'cz',],
- ]);
- $factory->expects(self::once())
- ->method('languageExists')
- ->with(null, 'cz')
- ->willReturn(false);
-
- $lang = $factory->findGenericLanguage();
-
- self::assertSame('en', $lang);
- }
-
- public function testFindGenericLanguageFallback(): void {
- $factory = $this->getFactory();
- $this->request->expects(self::once())
- ->method('getParam')
- ->with('forceLanguage')
- ->willReturn(null);
- $this->config->expects(self::exactly(2))
- ->method('getSystemValue')
- ->willReturnMap([
- ['force_language', false, false,],
- ['default_language', false, false,],
- ]);
-
- $lang = $factory->findGenericLanguage();
-
- self::assertSame('en', $lang);
- }
-
- public static function dataTestRespectDefaultLanguage(): array {
- return [
- ['de', 'de_DE', true, 'de_DE'],
- ['de', 'de', true, 'de'],
- ['de', false, true, 'de'],
- ['fr', 'de_DE', true, 'fr'],
- ];
- }
-
- /**
- * test if we respect default language if possible
- *
- *
- * @param string $lang
- * @param string $defaultLanguage
- * @param bool $langExists
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestRespectDefaultLanguage')]
- public function testRespectDefaultLanguage($lang, $defaultLanguage, $langExists, $expected): void {
- $factory = $this->getFactory(['languageExists']);
- $factory->expects(self::any())
- ->method('languageExists')->willReturn($langExists);
- $this->config->expects(self::any())
- ->method('getSystemValue')->with('default_language', false)->willReturn($defaultLanguage);
-
- $result = $this->invokePrivate($factory, 'respectDefaultLanguage', ['app', $lang]);
- self::assertSame($expected, $result);
- }
-
- public static function dataTestReduceToLanguages(): array {
- return [
- ['en', ['en', 'de', 'fr', 'it', 'es'], ['en', 'fr', 'de'], ['en', 'fr', 'de']],
- ['en', ['en', 'de', 'fr', 'it', 'es'], ['en', 'de'], ['en', 'de']],
- ['en', ['en', 'de', 'fr', 'it', 'es'], [], ['de', 'en', 'es', 'fr', 'it']],
- ];
- }
-
- /**
- * test
- * - if available languages set can be reduced by configuration
- * - if available languages set is not reduced to an empty set if
- * the reduce config is an empty set
- *
- *
- * @param string $lang
- * @param array $availableLanguages
- * @param array $reducedLanguageSet
- * @param array $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestReduceToLanguages')]
- public function testReduceLanguagesByConfiguration(string $lang, array $availableLanguages, array $reducedLanguageSet, array $expected): void {
- $factory = $this->getFactory(['findAvailableLanguages', 'languageExists']);
- $factory->expects(self::any())
- ->method('languageExists')->willReturn(true);
- $factory->expects(self::any())
- ->method('findAvailableLanguages')
- ->willReturnCallback(function ($app) use ($availableLanguages) {
- return $availableLanguages;
- });
-
- $this->config
- ->method('getSystemValue')
- ->willReturnMap([
- ['force_language', false, false],
- ['default_language', false, $lang],
- ['reduce_to_languages', [], $reducedLanguageSet]
- ]);
-
- $result = $this->invokePrivate($factory, 'getLanguages');
- $commonLanguagesCodes = array_map(function ($lang) {
- return $lang['code'];
- }, $result['commonLanguages']);
-
- self::assertEqualsCanonicalizing($expected, $commonLanguagesCodes);
- }
-
- public static function languageIteratorRequestProvider(): array {
- return [
- [ true, true],
- [ false, true],
- [ false, false],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('languageIteratorRequestProvider')]
- public function testGetLanguageIterator(bool $hasSession, bool $mockUser): void {
- $factory = $this->getFactory();
- $user = null;
-
- if (!$mockUser) {
- $matcher = $this->userSession->expects(self::once())
- ->method('getUser');
-
- if ($hasSession) {
- $matcher->willReturn($this->createMock(IUser::class));
- } else {
- $this->expectException(\RuntimeException::class);
- }
- } else {
- $user = $this->createMock(IUser::class);
- }
-
- $iterator = $factory->getLanguageIterator($user);
- self::assertInstanceOf(ILanguageIterator::class, $iterator);
- }
-
- public static function dataGetLanguageDirection(): array {
- return [
- ['en', 'ltr'],
- ['de', 'ltr'],
- ['fa', 'rtl'],
- ['ar', 'rtl']
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetLanguageDirection')]
- public function testGetLanguageDirection(string $language, string $expectedDirection) {
- $factory = $this->getFactory();
-
- self::assertEquals($expectedDirection, $factory->getLanguageDirection($language));
- }
-}
--- a/tests/lib/L10N/L10nTest.php
+++ /dev/null
@@ -1,248 +0,0 @@
-createMock(IConfig::class);
- /** @var IRequest $request */
- $request = $this->createMock(IRequest::class);
- /** @var IUserSession $userSession */
- $userSession = $this->createMock(IUserSession::class);
- $cacheFactory = $this->createMock(ICacheFactory::class);
- $appManager = $this->createMock(IAppManager::class);
- return new Factory($config, $request, $userSession, $cacheFactory, \OC::$SERVERROOT, $appManager);
- }
-
- public function testSimpleTranslationWithTrailingColon(): void {
- $transFile = \OC::$SERVERROOT . '/tests/data/l10n/de.json';
- $l = new L10N($this->getFactory(), 'test', 'de', 'de_AT', [$transFile]);
-
- $this->assertEquals('Files:', $l->t('Files:'));
- }
-
- public function testGermanPluralTranslations(): void {
- $transFile = \OC::$SERVERROOT . '/tests/data/l10n/de.json';
- $l = new L10N($this->getFactory(), 'test', 'de', 'de_AT', [$transFile]);
-
- $this->assertEquals('1 Datei', (string)$l->n('%n file', '%n files', 1));
- $this->assertEquals('2 Dateien', (string)$l->n('%n file', '%n files', 2));
- }
-
- public function testRussianPluralTranslations(): void {
- $transFile = \OC::$SERVERROOT . '/tests/data/l10n/ru.json';
- $l = new L10N($this->getFactory(), 'test', 'ru', 'ru_UA', [$transFile]);
-
- $this->assertEquals('1 файл', (string)$l->n('%n file', '%n files', 1));
- $this->assertEquals('2 файла', (string)$l->n('%n file', '%n files', 2));
- $this->assertEquals('6 файлов', (string)$l->n('%n file', '%n files', 6));
- $this->assertEquals('21 файл', (string)$l->n('%n file', '%n files', 21));
- $this->assertEquals('22 файла', (string)$l->n('%n file', '%n files', 22));
- $this->assertEquals('26 файлов', (string)$l->n('%n file', '%n files', 26));
-
- /*
- 1 file 1 файл 1 папка
- 2-4 files 2-4 файла 2-4 папки
- 5-20 files 5-20 файлов 5-20 папок
- 21 files 21 файл 21 папка
- 22-24 files 22-24 файла 22-24 папки
- 25-30 files 25-30 файлов 25-30 папок
- etc
- 100 files 100 файлов, 100 папок
- 1000 files 1000 файлов 1000 папок
- */
- }
-
- public function testCzechPluralTranslations(): void {
- $transFile = \OC::$SERVERROOT . '/tests/data/l10n/cs.json';
- $l = new L10N($this->getFactory(), 'test', 'cs', 'cs_CZ', [$transFile]);
-
- $this->assertEquals('1 okno', (string)$l->n('%n window', '%n windows', 1));
- $this->assertEquals('2 okna', (string)$l->n('%n window', '%n windows', 2));
- $this->assertEquals('5 oken', (string)$l->n('%n window', '%n windows', 5));
- }
-
- public function testGermanPluralWithCzechLocaleTranslations(): void {
- $transFile = \OC::$SERVERROOT . '/tests/data/l10n/de.json';
- $l = new L10N($this->getFactory(), 'test', 'de', 'cs_CZ', [$transFile]);
-
- $this->assertEquals('1 Datei', (string)$l->n('%n file', '%n files', 1));
- $this->assertEquals('2 Dateien', (string)$l->n('%n file', '%n files', 2));
- $this->assertEquals('5 Dateien', (string)$l->n('%n file', '%n files', 5));
- }
-
- public static function dataPlaceholders(): array {
- return [
- ['Ordered placeholders one %s two %s', 'Placeholder one 1 two 2'],
- ['Reordered placeholders one %s two %s', 'Placeholder two 2 one 1'],
- ['Reordered placeholders one %1$s two %2$s', 'Placeholder two 2 one 1'],
- ];
- }
-
- /**
- *
- * @param $string
- * @param $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataPlaceholders')]
- public function testPlaceholders($string, $expected): void {
- $transFile = \OC::$SERVERROOT . '/tests/data/l10n/de.json';
- $l = new L10N($this->getFactory(), 'test', 'de', 'de_AT', [$transFile]);
-
- $this->assertEquals($expected, $l->t($string, ['1', '2']));
- }
-
- public static function localizationData(): array {
- return [
- // timestamp as string
- ["February 13, 2009, 11:31:30\xE2\x80\xAFPM UTC", 'en', 'en_US', 'datetime', '1234567890'],
- ['13. Februar 2009, 23:31:30 UTC', 'de', 'de_DE', 'datetime', '1234567890'],
- ['February 13, 2009', 'en', 'en_US', 'date', '1234567890'],
- ['13. Februar 2009', 'de', 'de_DE', 'date', '1234567890'],
- ["11:31:30\xE2\x80\xAFPM UTC", 'en', 'en_US', 'time', '1234567890'],
- ['23:31:30 UTC', 'de', 'de_DE', 'time', '1234567890'],
-
- // timestamp as int
- ["February 13, 2009, 11:31:30\xE2\x80\xAFPM UTC", 'en', 'en_US', 'datetime', 1234567890],
- ['13. Februar 2009, 23:31:30 UTC', 'de', 'de_DE', 'datetime', 1234567890],
- ['February 13, 2009', 'en', 'en_US', 'date', 1234567890],
- ['13. Februar 2009', 'de', 'de_DE', 'date', 1234567890],
- ["11:31:30\xE2\x80\xAFPM UTC", 'en', 'en_US', 'time', 1234567890],
- ['23:31:30 UTC', 'de', 'de_DE', 'time', 1234567890],
-
- // DateTime object
- ["February 13, 2009, 11:31:30\xE2\x80\xAFPM GMT+0", 'en', 'en_US', 'datetime', new DateTime('@1234567890')],
- ['13. Februar 2009, 23:31:30 GMT+0', 'de', 'de_DE', 'datetime', new DateTime('@1234567890')],
- ['February 13, 2009', 'en', 'en_US', 'date', new DateTime('@1234567890')],
- ['13. Februar 2009', 'de', 'de_DE', 'date', new DateTime('@1234567890')],
- ["11:31:30\xE2\x80\xAFPM GMT+0", 'en', 'en_US', 'time', new DateTime('@1234567890')],
- ['23:31:30 GMT+0', 'de', 'de_DE', 'time', new DateTime('@1234567890')],
-
- // DateTimeImmutable object
- ["February 13, 2009, 11:31:30\xE2\x80\xAFPM GMT+0", 'en', 'en_US', 'datetime', new DateTimeImmutable('@1234567890')],
- ['13. Februar 2009, 23:31:30 GMT+0', 'de', 'de_DE', 'datetime', new DateTimeImmutable('@1234567890')],
- ['February 13, 2009', 'en', 'en_US', 'date', new DateTimeImmutable('@1234567890')],
- ['13. Februar 2009', 'de', 'de_DE', 'date', new DateTimeImmutable('@1234567890')],
- ["11:31:30\xE2\x80\xAFPM GMT+0", 'en', 'en_US', 'time', new DateTimeImmutable('@1234567890')],
- ['23:31:30 GMT+0', 'de', 'de_DE', 'time', new DateTimeImmutable('@1234567890')],
-
- // en_GB
- ['13 February 2009, 23:31:30 GMT+0', 'en_GB', 'en_GB', 'datetime', new DateTime('@1234567890')],
- ['13 February 2009', 'en_GB', 'en_GB', 'date', new DateTime('@1234567890')],
- ['23:31:30 GMT+0', 'en_GB', 'en_GB', 'time', new DateTime('@1234567890')],
- ['13 February 2009, 23:31:30 GMT+0', 'en-GB', 'en_GB', 'datetime', new DateTime('@1234567890')],
- ['13 February 2009', 'en-GB', 'en_GB', 'date', new DateTime('@1234567890')],
- ['23:31:30 GMT+0', 'en-GB', 'en_GB', 'time', new DateTime('@1234567890')],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('localizationData')]
- public function testNumericStringLocalization($expectedDate, $lang, $locale, $type, $value): void {
- $l = new L10N($this->getFactory(), 'test', $lang, $locale, []);
- $this->assertSame($expectedDate, $l->l($type, $value));
- }
-
- public static function firstDayData(): array {
- return [
- [1, 'de', 'de_DE'],
- [0, 'en', 'en_US'],
- ];
- }
-
- /**
- * @param $expected
- * @param $lang
- * @param $locale
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('firstDayData')]
- public function testFirstWeekDay($expected, $lang, $locale): void {
- $l = new L10N($this->getFactory(), 'test', $lang, $locale, []);
- $this->assertSame($expected, $l->l('firstday', 'firstday'));
- }
-
- public static function jsDateData(): array {
- return [
- ['dd.MM.yy', 'de', 'de_DE'],
- ['M/d/yy', 'en', 'en_US'],
- ];
- }
-
- /**
- * @param $expected
- * @param $lang
- * @param $locale
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('jsDateData')]
- public function testJSDate($expected, $lang, $locale): void {
- $l = new L10N($this->getFactory(), 'test', $lang, $locale, []);
- $this->assertSame($expected, $l->l('jsdate', 'jsdate'));
- }
-
- public function testFactoryGetLanguageCode(): void {
- $l = $this->getFactory()->get('lib', 'de');
- $this->assertEquals('de', $l->getLanguageCode());
- }
-
- public function testServiceGetLanguageCode(): void {
- $l = Util::getL10N('lib', 'de');
- $this->assertEquals('de', $l->getLanguageCode());
- }
-
- public function testWeekdayName(): void {
- $l = Util::getL10N('lib', 'de');
- $this->assertEquals('Mo.', $l->l('weekdayName', new \DateTime('2017-11-6'), ['width' => 'abbreviated']));
- }
-
- /**
- * @param $locale
- * @param $language
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('findLanguageFromLocaleData')]
- public function testFindLanguageFromLocale($locale, $language): void {
- $this->assertEquals(
- $language,
- Server::get(IFactory::class)->findLanguageFromLocale('lib', $locale)
- );
- }
-
- public static function findLanguageFromLocaleData(): array {
- return [
- 'en_US' => ['en_US', 'en'],
- 'en_UK' => ['en_UK', 'en'],
- 'de_DE' => ['de_DE', 'de_DE'],
- 'de_AT' => ['de_AT', 'de'],
- 'es_EC' => ['es_EC', 'es_EC'],
- 'fi_FI' => ['fi_FI', 'fi'],
- 'zh_CN' => ['zh_CN', 'zh_CN'],
- ];
- }
-}
--- a/tests/lib/L10N/LanguageIteratorTest.php
+++ /dev/null
@@ -1,84 +0,0 @@
-user = $this->createMock(IUser::class);
- $this->config = $this->createMock(IConfig::class);
-
- $this->iterator = new LanguageIterator($this->user, $this->config);
- }
-
- public static function languageSettingsProvider(): array {
- return [
- // all language settings set
- [ 'de_DE', 'es_CU', 'zh_TW', ['de_DE', 'de', 'es_CU', 'es', 'zh_TW', 'zh', 'en']],
- [ 'de', 'es', 'zh', ['de', 'es', 'zh', 'en']],
- [ 'en', 'en', 'en', ['en', 'en', 'en', 'en']],
- // one possible setting is missing each
- [ false, 'es_CU', 'zh_TW', ['es_CU', 'es', 'zh_TW', 'zh', 'en']],
- [ false, 'es', 'zh_TW', ['es', 'zh_TW', 'zh', 'en']],
- [ false, 'es_CU', 'zh', ['es_CU', 'es', 'zh', 'en']],
- [ 'de_DE', null, 'zh_TW', ['de_DE', 'de', 'zh_TW', 'zh', 'en']],
- [ 'de_DE', null, 'zh', ['de_DE', 'de', 'zh', 'en']],
- [ 'de', null, 'zh_TW', ['de', 'zh_TW', 'zh', 'en']],
- [ 'de_DE', 'es_CU', 'en', ['de_DE', 'de', 'es_CU', 'es', 'en', 'en']],
- [ 'de', 'es_CU', 'en', ['de', 'es_CU', 'es', 'en', 'en']],
- [ 'de_DE', 'es', 'en', ['de_DE', 'de', 'es', 'en', 'en']],
- // two possible settings are missing each
- [ false, null, 'zh_TW', ['zh_TW', 'zh', 'en']],
- [ false, null, 'zh', ['zh', 'en']],
- [ false, 'es_CU', 'en', ['es_CU', 'es', 'en', 'en']],
- [ false, 'es', 'en', ['es', 'en', 'en']],
- [ 'de_DE', null, 'en', ['de_DE', 'de', 'en', 'en']],
- [ 'de', null, 'en', ['de', 'en', 'en']],
- // nothing is set
- [ false, null, 'en', ['en', 'en']],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('languageSettingsProvider')]
- public function testIterator($forcedLang, $userLang, $sysLang, $expectedValues): void {
- $this->config->expects($this->any())
- ->method('getSystemValue')
- ->willReturnMap([
- ['force_language', false, $forcedLang],
- ]);
- $this->config->expects($this->any())
- ->method('getSystemValueString')
- ->willReturnMap([
- ['default_language', 'en', $sysLang],
- ]);
- $this->config->expects($this->any())
- ->method('getUserValue')
- ->willReturn($userLang);
-
- foreach ($expectedValues as $expected) {
- $this->assertTrue($this->iterator->valid());
- $this->assertSame($expected, $this->iterator->current());
- $this->iterator->next();
- }
- $this->assertFalse($this->iterator->valid());
- }
-}
--- a/tests/lib/LargeFileHelperGetFileSizeTest.php
+++ /dev/null
@@ -1,84 +0,0 @@
-helper = new LargeFileHelper();
- }
-
- public static function dataFileNameProvider(): array {
- $path = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR;
-
- return [
- [ $path . 'lorem.txt', 446 ],
- [ $path . 'strängé filename (duplicate #2).txt', 446 ],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataFileNameProvider')]
- public function XtestGetFileSizeViaCurl($filename, $fileSize) {
- if (!extension_loaded('curl')) {
- $this->markTestSkipped(
- 'The PHP curl extension is required for this test.'
- );
- }
- if (Server::get(IniGetWrapper::class)->getString('open_basedir') !== '') {
- $this->markTestSkipped(
- 'The PHP curl extension does not work with the file:// protocol when open_basedir is enabled.'
- );
- }
- $this->assertSame(
- $fileSize,
- $this->helper->getFileSizeViaCurl($filename)
- );
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataFileNameProvider')]
- public function testGetFileSizeViaExec($filename, $fileSize): void {
- if (escapeshellarg('strängé') !== '\'strängé\'') {
- $this->markTestSkipped('Your escapeshell args removes accents');
- }
- if (!Util::isFunctionEnabled('exec')) {
- $this->markTestSkipped(
- 'The exec() function needs to be enabled for this test.'
- );
- }
- $this->assertSame(
- $fileSize,
- $this->helper->getFileSizeViaExec($filename)
- );
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataFileNameProvider')]
- public function testGetFileSizeNative($filename, $fileSize): void {
- $this->assertSame(
- $fileSize,
- $this->helper->getFileSizeNative($filename)
- );
- }
-}
--- a/tests/lib/LargeFileHelperTest.php
+++ /dev/null
@@ -1,48 +0,0 @@
-helper = new LargeFileHelper;
- }
-
- public function testFormatUnsignedIntegerFloat(): void {
- $this->assertSame(
- '9007199254740992',
- $this->helper->formatUnsignedInteger((float)9007199254740992)
- );
- }
-
- public function testFormatUnsignedIntegerInt(): void {
- $this->assertSame(
- PHP_INT_SIZE === 4 ? '4294967295' : '18446744073709551615',
- $this->helper->formatUnsignedInteger(-1)
- );
- }
-
- public function testFormatUnsignedIntegerString(): void {
- $this->assertSame(
- '9007199254740993',
- $this->helper->formatUnsignedInteger('9007199254740993')
- );
- }
-
- public function testFormatUnsignedIntegerStringException(): void {
- $this->expectException(\UnexpectedValueException::class);
-
- $this->helper->formatUnsignedInteger('900ABCD254740993');
- }
-}
--- a/tests/lib/LegacyHookTest.php
+++ /dev/null
@@ -1,52 +0,0 @@
-assertTrue(\OC_Hook::emit('LegacyHookTest', 'error'));
-
- $this->assertCount(1, \OC_Hook::$thrownExceptions);
- $this->assertInstanceOf(\TypeError::class, \OC_Hook::$thrownExceptions[0]);
- }
-
- public function testEmitRethrowsHintException(): void {
- \OC_Hook::connect('LegacyHookTest', 'hint', self::class, 'throwHintException');
-
- $this->expectException(HintException::class);
- \OC_Hook::emit('LegacyHookTest', 'hint');
- }
-}
--- a/tests/lib/Lock/DBLockingProviderTest.php
+++ /dev/null
@@ -1,127 +0,0 @@
-currentTime = time();
- $this->timeFactory = $this->createMock(ITimeFactory::class);
- $this->timeFactory->expects($this->any())
- ->method('getTime')
- ->willReturnCallback(function () {
- return $this->currentTime;
- });
- parent::setUp();
- }
-
- /**
- * @return ILockingProvider
- */
- #[\Override]
- protected function getInstance() {
- $this->connection = Server::get(IDBConnection::class);
- return new DBLockingProvider($this->connection, $this->timeFactory, 3600);
- }
-
- #[\Override]
- protected function tearDown(): void {
- $qb = $this->connection->getQueryBuilder();
- $qb->delete('file_locks')->executeStatement();
- parent::tearDown();
- }
-
- public function testCleanEmptyLocks(): void {
- $this->currentTime = 100;
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
- $this->instance->acquireLock('asd', ILockingProvider::LOCK_EXCLUSIVE);
-
- $this->currentTime = 200;
- $this->instance->acquireLock('bar', ILockingProvider::LOCK_EXCLUSIVE);
- $this->instance->changeLock('asd', ILockingProvider::LOCK_SHARED);
-
- $this->currentTime = 150 + 3600;
-
- $this->assertEquals(3, $this->getLockEntryCount());
-
- $this->instance->cleanExpiredLocks();
-
- $this->assertEquals(2, $this->getLockEntryCount());
- }
-
- private function getLockEntryCount(): int {
- $qb = $this->connection->getQueryBuilder();
- $result = $qb->select($qb->func()->count('*'))
- ->from('file_locks')
- ->executeQuery();
- return (int)$result->fetchOne();
- }
-
- protected function getLockValue($key) {
- $query = $this->connection->getQueryBuilder();
- $query->select('lock')
- ->from('file_locks')
- ->where($query->expr()->eq('key', $query->createNamedParameter($key)));
-
- $result = $query->executeQuery();
- $rows = $result->fetchOne();
- $result->closeCursor();
-
- return $rows;
- }
-
- public function testDoubleShared(): void {
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
-
- $this->assertEquals(1, $this->getLockValue('foo'));
-
- $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED);
-
- $this->assertEquals(1, $this->getLockValue('foo'));
-
- $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED);
-
- $this->assertEquals(1, $this->getLockValue('foo'));
-
- $this->instance->releaseAll();
-
- $this->assertEquals(0, $this->getLockValue('foo'));
- }
-}
--- a/tests/lib/Lock/LockingProvider.php
+++ /dev/null
@@ -1,243 +0,0 @@
-instance = $this->getInstance();
- }
-
- public function testExclusiveLock(): void {
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
- $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
- $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
- }
-
- public function testSharedLock(): void {
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
- $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
- $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
- }
-
- public function testDoubleSharedLock(): void {
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
- $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
- $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
- $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
- }
-
- public function testReleaseSharedLock(): void {
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
- $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
- $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
- $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
- $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED);
- $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
- $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED);
- $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
- }
-
- public function testDoubleExclusiveLock(): void {
- $this->expectException(LockedException::class);
-
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
- $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
- }
-
- public function testReleaseExclusiveLock(): void {
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
- $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
- $this->instance->releaseLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
- $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
- }
-
- public function testExclusiveLockAfterShared(): void {
- $this->expectException(LockedException::class);
-
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
- $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
- }
-
- public function testExclusiveLockAfterSharedReleased(): void {
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
- $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
- $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED);
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
- $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
- }
-
- public function testReleaseAll(): void {
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
- $this->instance->acquireLock('bar', ILockingProvider::LOCK_SHARED);
- $this->instance->acquireLock('asd', ILockingProvider::LOCK_EXCLUSIVE);
- $this->instance->acquireLock('fizz#A=23', ILockingProvider::LOCK_EXCLUSIVE);
-
- $this->instance->releaseAll();
-
- $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
- $this->assertFalse($this->instance->isLocked('bar', ILockingProvider::LOCK_SHARED));
- $this->assertFalse($this->instance->isLocked('asd', ILockingProvider::LOCK_EXCLUSIVE));
- $this->assertFalse($this->instance->isLocked('fizz#A=23', ILockingProvider::LOCK_EXCLUSIVE));
- }
-
- public function testReleaseAllAfterChange(): void {
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
- $this->instance->acquireLock('bar', ILockingProvider::LOCK_SHARED);
- $this->instance->acquireLock('asd', ILockingProvider::LOCK_EXCLUSIVE);
-
- $this->instance->changeLock('bar', ILockingProvider::LOCK_EXCLUSIVE);
-
- $this->instance->releaseAll();
-
- $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
- $this->assertFalse($this->instance->isLocked('bar', ILockingProvider::LOCK_SHARED));
- $this->assertFalse($this->instance->isLocked('bar', ILockingProvider::LOCK_EXCLUSIVE));
- $this->assertFalse($this->instance->isLocked('asd', ILockingProvider::LOCK_EXCLUSIVE));
- }
-
- public function testReleaseAllAfterUnlock(): void {
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
- $this->instance->acquireLock('bar', ILockingProvider::LOCK_SHARED);
- $this->instance->acquireLock('asd', ILockingProvider::LOCK_EXCLUSIVE);
-
- $this->instance->releaseLock('bar', ILockingProvider::LOCK_SHARED);
-
- $this->instance->releaseAll();
-
- $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
- $this->assertFalse($this->instance->isLocked('asd', ILockingProvider::LOCK_EXCLUSIVE));
- }
-
- public function testReleaseAfterReleaseAll(): void {
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
-
- $this->instance->releaseAll();
-
- $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
-
- $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED);
- }
-
- public function testSharedLockAfterExclusive(): void {
- $this->expectException(LockedException::class);
-
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
- $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
- }
-
- public function testLockedExceptionHasPathForShared(): void {
- try {
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
- $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
-
- $this->fail('Expected locked exception');
- } catch (LockedException $e) {
- $this->assertEquals('foo', $e->getPath());
- }
- }
-
- public function testLockedExceptionHasPathForExclusive(): void {
- try {
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
- $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
-
- $this->fail('Expected locked exception');
- } catch (LockedException $e) {
- $this->assertEquals('foo', $e->getPath());
- }
- }
-
- public function testChangeLockToExclusive(): void {
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
- $this->instance->changeLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
- $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
- $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
- }
-
- public function testChangeLockToShared(): void {
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
- $this->instance->changeLock('foo', ILockingProvider::LOCK_SHARED);
- $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
- $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
- }
-
- public function testChangeLockToExclusiveDoubleShared(): void {
- $this->expectException(LockedException::class);
-
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
- $this->instance->changeLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
- }
-
- public function testChangeLockToExclusiveNoShared(): void {
- $this->expectException(LockedException::class);
-
- $this->instance->changeLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
- }
-
- public function testChangeLockToExclusiveFromExclusive(): void {
- $this->expectException(LockedException::class);
-
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
- $this->instance->changeLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
- }
-
- public function testChangeLockToSharedNoExclusive(): void {
- $this->expectException(LockedException::class);
-
- $this->instance->changeLock('foo', ILockingProvider::LOCK_SHARED);
- }
-
- public function testChangeLockToSharedFromShared(): void {
- $this->expectException(LockedException::class);
-
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
- $this->instance->changeLock('foo', ILockingProvider::LOCK_SHARED);
- }
-
- public function testReleaseNonExistingShared(): void {
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
- $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED);
-
- // releasing a lock once to many should not result in a locked state
- $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED);
-
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
- $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
- $this->instance->releaseLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
- }
-}
--- a/tests/lib/Lock/MemcacheLockingProviderTest.php
+++ /dev/null
@@ -1,39 +0,0 @@
-memcache = new ArrayCache();
- $timeProvider = Server::get(ITimeFactory::class);
- return new MemcacheLockingProvider($this->memcache, $timeProvider);
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->memcache->clear();
- parent::tearDown();
- }
-}
--- a/tests/lib/Lock/NonCachingDBLockingProviderTest.php
+++ /dev/null
@@ -1,44 +0,0 @@
-connection = Server::get(IDBConnection::class);
- return new DBLockingProvider($this->connection, $this->timeFactory, 3600, false);
- }
-
- #[\Override]
- public function testDoubleShared(): void {
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
- $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
-
- $this->assertEquals(2, $this->getLockValue('foo'));
-
- $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED);
-
- $this->assertEquals(1, $this->getLockValue('foo'));
-
- $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED);
-
- $this->assertEquals(0, $this->getLockValue('foo'));
- }
-}
--- a/tests/lib/Lockdown/Filesystem/NoFSTest.php
+++ /dev/null
@@ -1,50 +0,0 @@
-setScope([
- IToken::SCOPE_FILESYSTEM => true
- ]);
- Server::get(ILockdownManager::class)->setToken($token);
- parent::tearDown();
- }
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
- $token = new PublicKeyToken();
- $token->setScope([
- IToken::SCOPE_FILESYSTEM => false
- ]);
-
- Server::get(ILockdownManager::class)->setToken($token);
- $this->createUser('foo', 'var');
- }
-
- public function testSetupFS(): void {
- \OC_Util::tearDownFS();
- \OC_Util::setupFS('foo');
-
- $this->assertInstanceOf(NullStorage::class, Filesystem::getStorage('/foo/files'));
- }
-}
--- a/tests/lib/Lockdown/Filesystem/NullCacheTest.php
+++ /dev/null
@@ -1,137 +0,0 @@
-cache = new NullCache();
- }
-
- public function testGetNumericStorageId(): void {
- $this->assertSame(-1, $this->cache->getNumericStorageId());
- }
-
- public function testGetEmpty(): void {
- $this->assertFalse($this->cache->get('foo'));
- }
-
- public function testGet(): void {
- $data = $this->cache->get('');
-
- $this->assertEquals(-1, $data['fileid']);
- $this->assertEquals(-1, $data['parent']);
- $this->assertEquals('', $data['name']);
- $this->assertEquals('', $data['path']);
- $this->assertEquals('0', $data['size']);
- $this->assertEquals('', $data['etag']);
- $this->assertEquals(FileInfo::MIMETYPE_FOLDER, $data['mimetype']);
- $this->assertEquals('httpd', $data['mimepart']);
- $this->assertEquals(Constants::PERMISSION_READ, $data['permissions']);
- }
-
- public function testGetFolderContents(): void {
- $this->assertSame([], $this->cache->getFolderContents('foo'));
- }
-
- public function testGetFolderContentsById(): void {
- $this->assertSame([], $this->cache->getFolderContentsById(42));
- }
-
- public function testPut(): void {
- $this->expectException(ForbiddenException::class);
- $this->expectExceptionMessage('This request is not allowed to access the filesystem');
-
- $this->cache->put('foo', ['size' => 100]);
- }
-
- public function testInsert(): void {
- $this->expectException(ForbiddenException::class);
- $this->expectExceptionMessage('This request is not allowed to access the filesystem');
-
- $this->cache->insert('foo', ['size' => 100]);
- }
-
- public function testUpdate(): void {
- $this->expectException(ForbiddenException::class);
- $this->expectExceptionMessage('This request is not allowed to access the filesystem');
-
- $this->cache->update('foo', ['size' => 100]);
- }
-
- public function testGetId(): void {
- $this->assertSame(-1, $this->cache->getId('foo'));
- }
-
- public function testGetParentId(): void {
- $this->assertSame(-1, $this->cache->getParentId('foo'));
- }
-
- public function testInCache(): void {
- $this->assertTrue($this->cache->inCache(''));
- $this->assertFalse($this->cache->inCache('foo'));
- }
-
- public function testRemove(): void {
- $this->expectException(ForbiddenException::class);
- $this->expectExceptionMessage('This request is not allowed to access the filesystem');
-
- $this->cache->remove('foo');
- }
-
- public function testMove(): void {
- $this->expectException(ForbiddenException::class);
- $this->expectExceptionMessage('This request is not allowed to access the filesystem');
-
- $this->cache->move('foo', 'bar');
- }
-
- public function testMoveFromCache(): void {
- $sourceCache = $this->createMock(ICache::class);
-
- $this->expectException(ForbiddenException::class);
- $this->expectExceptionMessage('This request is not allowed to access the filesystem');
-
- $this->cache->moveFromCache($sourceCache, 'foo', 'bar');
- }
-
- public function testGetStatus(): void {
- $this->assertSame(ICache::COMPLETE, $this->cache->getStatus('foo'));
- }
-
- public function testSearch(): void {
- $this->assertSame([], $this->cache->search('foo'));
- }
-
- public function testSearchByMime(): void {
- $this->assertSame([], $this->cache->searchByMime('foo'));
- }
-
- public function testGetIncomplete(): void {
- $this->assertSame([], $this->cache->getIncomplete());
- }
-
- public function testGetPathById(): void {
- $this->assertSame('', $this->cache->getPathById(42));
- }
-
- public function testNormalize(): void {
- $this->assertSame('foo/ bar /', $this->cache->normalize('foo/ bar /'));
- }
-}
--- a/tests/lib/Lockdown/Filesystem/NullStorageTest.php
+++ /dev/null
@@ -1,230 +0,0 @@
-storage = new NullStorage([]);
- }
-
- public function testGetId(): void {
- $this->assertSame('null', $this->storage->getId());
- }
-
- public function testMkdir(): void {
- $this->expectException(ForbiddenException::class);
- $this->expectExceptionMessage('This request is not allowed to access the filesystem');
-
- $this->storage->mkdir('foo');
- }
-
- public function testRmdir(): void {
- $this->expectException(ForbiddenException::class);
- $this->expectExceptionMessage('This request is not allowed to access the filesystem');
-
- $this->storage->rmdir('foo');
- }
-
- public function testOpendir(): void {
- $this->assertInstanceOf(IteratorDirectory::class, $this->storage->opendir('foo'));
- }
-
- public function testIs_dir(): void {
- $this->assertTrue($this->storage->is_dir(''));
- $this->assertFalse($this->storage->is_dir('foo'));
- }
-
- public function testIs_file(): void {
- $this->assertFalse($this->storage->is_file('foo'));
- }
-
- public function testStat(): void {
- $this->expectException(ForbiddenException::class);
- $this->expectExceptionMessage('This request is not allowed to access the filesystem');
-
- $this->storage->stat('foo');
- }
-
- public function testFiletype(): void {
- $this->assertSame('dir', $this->storage->filetype(''));
- $this->assertFalse($this->storage->filetype('foo'));
- }
-
- public function testFilesize(): void {
- $this->expectException(ForbiddenException::class);
- $this->expectExceptionMessage('This request is not allowed to access the filesystem');
-
- $this->storage->filesize('foo');
- }
-
- public function testIsCreatable(): void {
- $this->assertFalse($this->storage->isCreatable('foo'));
- }
-
- public function testIsReadable(): void {
- $this->assertTrue($this->storage->isReadable(''));
- $this->assertFalse($this->storage->isReadable('foo'));
- }
-
- public function testIsUpdatable(): void {
- $this->assertFalse($this->storage->isUpdatable('foo'));
- }
-
- public function testIsDeletable(): void {
- $this->assertFalse($this->storage->isDeletable('foo'));
- }
-
- public function testIsSharable(): void {
- $this->assertFalse($this->storage->isSharable('foo'));
- }
-
- public function testGetPermissions(): void {
- $this->assertEquals(0, $this->storage->getPermissions('foo'));
- }
-
- public function testFile_exists(): void {
- $this->assertTrue($this->storage->file_exists(''));
- $this->assertFalse($this->storage->file_exists('foo'));
- }
-
- public function testFilemtime(): void {
- $this->assertFalse($this->storage->filemtime('foo'));
- }
-
- public function testFile_get_contents(): void {
- $this->expectException(ForbiddenException::class);
- $this->expectExceptionMessage('This request is not allowed to access the filesystem');
-
- $this->storage->file_get_contents('foo');
- }
-
- public function testFile_put_contents(): void {
- $this->expectException(ForbiddenException::class);
- $this->expectExceptionMessage('This request is not allowed to access the filesystem');
-
- $this->storage->file_put_contents('foo', 'bar');
- }
-
- public function testUnlink(): void {
- $this->expectException(ForbiddenException::class);
- $this->expectExceptionMessage('This request is not allowed to access the filesystem');
-
- $this->storage->unlink('foo');
- }
-
- public function testRename(): void {
- $this->expectException(ForbiddenException::class);
- $this->expectExceptionMessage('This request is not allowed to access the filesystem');
-
- $this->storage->rename('foo', 'bar');
- }
-
- public function testCopy(): void {
- $this->expectException(ForbiddenException::class);
- $this->expectExceptionMessage('This request is not allowed to access the filesystem');
-
- $this->storage->copy('foo', 'bar');
- }
-
- public function testFopen(): void {
- $this->expectException(ForbiddenException::class);
- $this->expectExceptionMessage('This request is not allowed to access the filesystem');
-
- $this->storage->fopen('foo', 'R');
- }
-
- public function testGetMimeType(): void {
- $this->expectException(ForbiddenException::class);
- $this->expectExceptionMessage('This request is not allowed to access the filesystem');
-
- $this->storage->getMimeType('foo');
- }
-
- public function testHash(): void {
- $this->expectException(ForbiddenException::class);
- $this->expectExceptionMessage('This request is not allowed to access the filesystem');
-
- $this->storage->hash('md5', 'foo', true);
- }
-
- public function testFree_space(): void {
- $this->assertSame(FileInfo::SPACE_UNKNOWN, $this->storage->free_space('foo'));
- }
-
- public function testTouch(): void {
- $this->expectException(ForbiddenException::class);
- $this->expectExceptionMessage('This request is not allowed to access the filesystem');
-
- $this->storage->touch('foo');
- }
-
- public function testGetLocalFile(): void {
- $this->assertFalse($this->storage->getLocalFile('foo'));
- }
-
- public function testHasUpdated(): void {
- $this->assertFalse($this->storage->hasUpdated('foo', 42));
- }
-
- public function testGetETag(): void {
- $this->assertSame('', $this->storage->getETag('foo'));
- }
-
- public function testIsLocal(): void {
- $this->assertFalse($this->storage->isLocal());
- }
-
- public function testGetDirectDownload(): void {
- $this->assertFalse($this->storage->getDirectDownload('foo'));
- }
-
- public function testCopyFromStorage(): void {
- $sourceStorage = $this->createMock(IStorage::class);
-
- $this->expectException(ForbiddenException::class);
- $this->expectExceptionMessage('This request is not allowed to access the filesystem');
-
- $this->storage->copyFromStorage($sourceStorage, 'foo', 'bar');
- }
-
- public function testMoveFromStorage(): void {
- $sourceStorage = $this->createMock(IStorage::class);
-
- $this->expectException(ForbiddenException::class);
- $this->expectExceptionMessage('This request is not allowed to access the filesystem');
-
- $this->storage->moveFromStorage($sourceStorage, 'foo', 'bar');
- }
-
- public function testTest() {
- $this->assertTrue($this->storage->test());
- return true;
- }
-
- public function testGetOwner(): void {
- $this->assertFalse($this->storage->getOwner('foo'));
- }
-
- public function testGetCache(): void {
- $this->assertInstanceOf(NullCache::class, $this->storage->getCache('foo'));
- }
-}
--- a/tests/lib/Lockdown/LockdownManagerTest.php
+++ /dev/null
@@ -1,48 +0,0 @@
-sessionCallback = function () {
- return $this->createMock(ISession::class);
- };
- }
-
- public function testCanAccessFilesystemDisabled(): void {
- $manager = new LockdownManager($this->sessionCallback);
- $this->assertTrue($manager->canAccessFilesystem());
- }
-
- public function testCanAccessFilesystemAllowed(): void {
- $token = new PublicKeyToken();
- $token->setScope([IToken::SCOPE_FILESYSTEM => true]);
- $manager = new LockdownManager($this->sessionCallback);
- $manager->setToken($token);
- $this->assertTrue($manager->canAccessFilesystem());
- }
-
- public function testCanAccessFilesystemNotAllowed(): void {
- $token = new PublicKeyToken();
- $token->setScope([IToken::SCOPE_FILESYSTEM => false]);
- $manager = new LockdownManager($this->sessionCallback);
- $manager->setToken($token);
- $this->assertFalse($manager->canAccessFilesystem());
- }
-}
--- a/tests/lib/Log/ExceptionSerializerTest.php
+++ /dev/null
@@ -1,68 +0,0 @@
-createMock(SystemConfig::class);
- $this->serializer = new ExceptionSerializer($config);
- }
-
- private function emit($arguments) {
- \call_user_func_array([$this, 'bind'], $arguments);
- }
-
- private function bind(array &$myValues): void {
- throw new \Exception('my exception');
- }
-
- private function customMagicAuthThing(string $login, string $parole): void {
- throw new \Exception('expected custom auth exception');
- }
-
- /**
- * this test ensures that the serializer does not overwrite referenced
- * variables. It is crafted after a scenario we experienced: the DAV server
- * emitting the "validateTokens" event, of which later on a handled
- * exception was passed to the logger. The token was replaced, the original
- * variable overwritten.
- */
- public function testSerializer(): void {
- try {
- $secret = ['Secret'];
- $this->emit([&$secret]);
- } catch (\Exception $e) {
- $serializedData = $this->serializer->serializeException($e);
- $this->assertSame(['Secret'], $secret);
- $this->assertSame(ExceptionSerializer::SENSITIVE_VALUE_PLACEHOLDER, $serializedData['Trace'][0]['args'][0]);
- }
- }
-
- public function testSerializerWithRegisteredMethods(): void {
- $this->serializer->enlistSensitiveMethods(self::class, ['customMagicAuthThing']);
- try {
- $this->customMagicAuthThing('u57474', 'Secret');
- } catch (\Exception $e) {
- $serializedData = $this->serializer->serializeException($e);
- $this->assertSame('customMagicAuthThing', $serializedData['Trace'][0]['function']);
- $this->assertSame(ExceptionSerializer::SENSITIVE_VALUE_PLACEHOLDER, $serializedData['Trace'][0]['args'][0]);
- $this->assertFalse(isset($serializedData['Trace'][0]['args'][1]));
- }
- }
-}
--- a/tests/lib/Log/FileTest.php
+++ /dev/null
@@ -1,95 +0,0 @@
-restore_logfile = $config->getValue('logfile');
- $this->restore_logdateformat = $config->getValue('logdateformat');
-
- $config->setValue('logfile', $config->getValue('datadirectory') . '/logtest.log');
- $this->logFile = new File($config->getValue('datadirectory') . '/logtest.log', '', $config);
- }
- #[\Override]
- protected function tearDown(): void {
- $config = Server::get(SystemConfig::class);
- if (isset($this->restore_logfile)) {
- $config->getValue('logfile', $this->restore_logfile);
- } else {
- $config->deleteValue('logfile');
- }
- if (isset($this->restore_logdateformat)) {
- $config->getValue('logdateformat', $this->restore_logdateformat);
- } else {
- $config->deleteValue('logdateformat');
- }
- $this->logFile = new File($this->restore_logfile, '', $config);
- parent::tearDown();
- }
-
- public function testLogging(): void {
- $config = Server::get(IConfig::class);
- # delete old logfile
- unlink($config->getSystemValue('logfile'));
-
- # set format & write log line
- $config->setSystemValue('logdateformat', 'u');
- $this->logFile->write('code', ['something' => 'extra', 'message' => 'Testing logging'], ILogger::ERROR);
-
- # read log line
- $handle = @fopen($config->getSystemValue('logfile'), 'r');
- $line = fread($handle, 1000);
- fclose($handle);
-
- # check log has data content
- $values = (array)json_decode($line, true);
- $this->assertArrayNotHasKey('message', $values['data']);
- $this->assertEquals('extra', $values['data']['something']);
- $this->assertEquals('Testing logging', $values['message']);
- }
-
- public function testMicrosecondsLogTimestamp(): void {
- $config = Server::get(IConfig::class);
- # delete old logfile
- unlink($config->getSystemValue('logfile'));
-
- # set format & write log line
- $config->setSystemValue('logdateformat', 'u');
- $this->logFile->write('test', 'message', ILogger::ERROR);
-
- # read log line
- $handle = @fopen($config->getSystemValue('logfile'), 'r');
- $line = fread($handle, 1000);
- fclose($handle);
-
- # check timestamp has microseconds part
- $values = (array)json_decode($line);
- $microseconds = $values['time'];
- $this->assertNotEquals(0, $microseconds);
- }
-}
--- a/tests/lib/Log/LogFactoryTest.php
+++ /dev/null
@@ -1,132 +0,0 @@
-c = $this->createMock(ContainerInterface::class);
- $this->systemConfig = $this->createMock(SystemConfig::class);
-
- $this->factory = new LogFactory($this->c, $this->systemConfig, \OC::$SERVERROOT);
- }
-
- public static function fileTypeProvider(): array {
- return [
- [
- 'file'
- ],
- [
- 'nextcloud'
- ],
- [
- 'owncloud'
- ],
- [
- 'krzxkyr_default'
- ]
- ];
- }
-
- /**
- * @param string $type
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('fileTypeProvider')]
- public function testFile(string $type): void {
- $datadir = \OC::$SERVERROOT . '/data';
- $defaultLog = $datadir . '/nextcloud.log';
-
- $this->systemConfig->expects($this->exactly(3))
- ->method('getValue')
- ->willReturnMap([
- ['datadirectory', $datadir, $datadir],
- ['logfile', $defaultLog, $defaultLog],
- ['logfilemode', 0640, 0640],
- ]);
-
- $log = $this->factory->get($type);
- $this->assertInstanceOf(File::class, $log);
- }
-
- public static function logFilePathProvider():array {
- return [
- [
- '/dev/null',
- '/dev/null'
- ],
- [
- '/xdev/youshallfallback',
- \OC::$SERVERROOT . '/data/nextcloud.log'
- ]
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('logFilePathProvider')]
- public function testFileCustomPath($path, $expected): void {
- $datadir = \OC::$SERVERROOT . '/data';
- $defaultLog = $datadir . '/nextcloud.log';
-
- $this->systemConfig->expects($this->exactly(3))
- ->method('getValue')
- ->willReturnMap([
- ['datadirectory', $datadir, $datadir],
- ['logfile', $defaultLog, $path],
- ['logfilemode', 0640, 0640],
- ]);
-
- $log = $this->factory->get('file');
- $this->assertInstanceOf(File::class, $log);
- $this->assertSame($expected, $log->getLogFilePath());
- }
-
- public function testErrorLog(): void {
- $log = $this->factory->get('errorlog');
- $this->assertInstanceOf(Errorlog::class, $log);
- }
-
- public function testSystemLog(): void {
- $this->c->expects($this->once())
- ->method('get')
- ->with(Syslog::class)
- ->willReturn($this->createMock(Syslog::class));
-
- $log = $this->factory->get('syslog');
- $this->assertInstanceOf(Syslog::class, $log);
- }
-
- public function testSystemdLog(): void {
- $this->c->expects($this->once())
- ->method('get')
- ->with(Systemdlog::class)
- ->willReturn($this->createMock(Systemdlog::class));
-
- $log = $this->factory->get('systemd');
- $this->assertInstanceOf(Systemdlog::class, $log);
- }
-}
--- a/tests/lib/Log/PsrLoggerAdapterTest.php
+++ /dev/null
@@ -1,83 +0,0 @@
-logger = $this->createMock(Log::class);
- $this->loggerAdapter = new PsrLoggerAdapter($this->logger);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataPsrLoggingLevels')]
- public function testLoggingWithPsrLogLevels(string $level, int $expectedLevel): void {
- $this->logger->expects(self::once())
- ->method('log')
- ->with($expectedLevel, 'test message', ['app' => 'test']);
- $this->loggerAdapter->log($level, 'test message', ['app' => 'test']);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataPsrLoggingLevels')]
- public function testLogLevelToInt(string $level, int $expectedLevel): void {
- $this->assertEquals($expectedLevel, PsrLoggerAdapter::logLevelToInt($level));
- }
-
- public static function dataPsrLoggingLevels(): array {
- return [
- [LogLevel::ALERT, ILogger::ERROR],
- [LogLevel::CRITICAL, ILogger::ERROR],
- [LogLevel::DEBUG, ILogger::DEBUG],
- [LogLevel::EMERGENCY, ILogger::FATAL],
- [LogLevel::ERROR, ILogger::ERROR],
- [LogLevel::INFO, ILogger::INFO],
- [LogLevel::NOTICE, ILogger::INFO],
- [LogLevel::WARNING, ILogger::WARN],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataInvalidLoggingLevel')]
- public function testInvalidLoggingLevel($level): void {
- $this->logger->expects(self::never())
- ->method('log');
- $this->expectException(InvalidArgumentException::class);
-
- $this->loggerAdapter->log($level, 'valid message');
- }
-
- public static function dataInvalidLoggingLevel(): array {
- return [
- // invalid string
- ['this is not a level'],
- // int out of range
- [ILogger::DEBUG - 1],
- [ILogger::FATAL + 1],
- // float is not allowed
- [1.2345],
- // boolean is not a level
- [true],
- [false],
- //
- [null],
- ];
- }
-}
--- a/tests/lib/LoggerTest.php
+++ /dev/null
@@ -1,339 +0,0 @@
-logs = [];
- $this->config = $this->createMock(SystemConfig::class);
- $this->registry = $this->createMock(IRegistry::class);
- $this->logger = new Log($this, $this->config, crashReporters: $this->registry);
- }
-
- private function mockDefaultLogLevel(): void {
- $this->config->expects($this->any())
- ->method('getValue')
- ->willReturnMap([
- ['loglevel', ILogger::WARN, ILogger::WARN],
- ]);
- }
-
- public function testInterpolation(): void {
- $this->mockDefaultLogLevel();
- $logger = $this->logger;
- $logger->warning('{Message {nothing} {user} {foo.bar} a}', ['user' => 'Bob', 'foo.bar' => 'Bar']);
-
- $expected = ['2 {Message {nothing} Bob Bar a}'];
- $this->assertEquals($expected, $this->getLogs());
- }
-
- public function testAppCondition(): void {
- $this->config->expects($this->any())
- ->method('getValue')
- ->willReturnMap([
- ['loglevel', ILogger::WARN, ILogger::WARN],
- ['log.condition', [], ['apps' => ['files']]]
- ]);
- $logger = $this->logger;
-
- $logger->info('Don\'t display info messages');
- $logger->info('Show info messages of files app', ['app' => 'files']);
- $logger->warning('Show warning messages of other apps');
-
- $expected = [
- '1 Show info messages of files app',
- '2 Show warning messages of other apps',
- ];
- $this->assertEquals($expected, $this->getLogs());
- }
-
- public static function dataMatchesCondition(): array {
- return [
- [
- 'user0',
- [
- 'apps' => ['app2'],
- ],
- [
- '1 Info of app2',
- ],
- ],
- [
- 'user2',
- [
- 'users' => ['user1', 'user2'],
- 'apps' => ['app1'],
- ],
- [
- '1 Info of app1',
- ],
- ],
- [
- 'user3',
- [
- 'users' => ['user3'],
- ],
- [
- '1 Info without app',
- '1 Info of app1',
- '1 Info of app2',
- '0 Debug of app3',
- ],
- ],
- [
- 'user4',
- [
- 'users' => ['user4'],
- 'apps' => ['app3'],
- 'loglevel' => 0,
- ],
- [
- '0 Debug of app3',
- ],
- ],
- [
- 'user4',
- [
- 'message' => ' of ',
- ],
- [
- '1 Info of app1',
- '1 Info of app2',
- '0 Debug of app3',
- ],
- ],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataMatchesCondition')]
- public function testMatchesCondition(string $userId, array $conditions, array $expectedLogs): void {
- $this->config->expects($this->any())
- ->method('getValue')
- ->willReturnMap([
- ['loglevel', ILogger::WARN, ILogger::WARN],
- ['log.condition', [], ['matches' => [
- $conditions,
- ]]],
- ]);
- $logger = $this->logger;
-
- $user = $this->createMock(IUser::class);
- $user->method('getUID')
- ->willReturn($userId);
- $userSession = $this->createMock(IUserSession::class);
- $userSession->method('getUser')
- ->willReturn($user);
- $this->overwriteService(IUserSession::class, $userSession);
-
- $logger->info('Info without app');
- $logger->info('Info of app1', ['app' => 'app1']);
- $logger->info('Info of app2', ['app' => 'app2']);
- $logger->debug('Debug of app3', ['app' => 'app3']);
-
- $this->assertEquals($expectedLogs, $this->getLogs());
- }
-
- public function testMatchesConditionIncreaseLoglevel(): void {
- $this->config->expects($this->any())
- ->method('getValue')
- ->willReturnMap([
- ['loglevel', ILogger::WARN, ILogger::INFO],
- ['log.condition', [], ['matches' => [
- [
- 'message' => 'catched',
- 'loglevel' => 3,
- ]
- ]]],
- ]);
- $logger = $this->logger;
-
- $user = $this->createMock(IUser::class);
- $user->method('getUID')
- ->willReturn('test-userid');
- $userSession = $this->createMock(IUserSession::class);
- $userSession->method('getUser')
- ->willReturn($user);
- $this->overwriteService(IUserSession::class, $userSession);
-
- $logger->info('catched message');
- $logger->info('info level message');
-
- $this->assertEquals(['1 info level message'], $this->getLogs());
- }
-
- public function testLoggingWithDataArray(): void {
- $this->mockDefaultLogLevel();
- /** @var IWriter&MockObject */
- $writerMock = $this->createMock(IWriter::class);
- $logFile = new Log($writerMock, $this->config);
- $writerMock->expects($this->once())->method('write')->with('no app in context', ['something' => 'extra', 'message' => 'Testing logging with john']);
- $logFile->error('Testing logging with {user}', ['something' => 'extra', 'user' => 'john']);
- }
-
- private function getLogs(): array {
- return $this->logs;
- }
-
- #[\Override]
- public function write(string $app, $message, int $level) {
- $textMessage = $message;
- if (is_array($message)) {
- $textMessage = $message['message'];
- }
- $this->logs[] = $level . ' ' . $textMessage;
- }
-
- public static function userAndPasswordData(): array {
- return [
- ['mySpecialUsername', 'MySuperSecretPassword'],
- ['my-user', '324324()#ä234'],
- ['my-user', ')qwer'],
- ['my-user', 'qwer)asdf'],
- ['my-user', 'qwer)'],
- ['my-user', '(qwer'],
- ['my-user', 'qwer(asdf'],
- ['my-user', 'qwer('],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('userAndPasswordData')]
- public function testDetectlogin(string $user, string $password): void {
- $this->mockDefaultLogLevel();
- $e = new \Exception('test');
- $this->registry->expects($this->once())
- ->method('delegateReport')
- ->with($e, ['level' => 3]);
-
- $this->logger->logException($e);
-
- $logLines = $this->getLogs();
- foreach ($logLines as $logLine) {
- if (is_array($logLine)) {
- $logLine = json_encode($logLine);
- }
- $this->assertStringNotContainsString($user, $logLine);
- $this->assertStringNotContainsString($password, $logLine);
- $this->assertStringContainsString('*** sensitive parameters replaced ***', $logLine);
- }
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('userAndPasswordData')]
- public function testDetectcheckPassword(string $user, string $password): void {
- $this->mockDefaultLogLevel();
- $e = new \Exception('test');
- $this->registry->expects($this->once())
- ->method('delegateReport')
- ->with($e, ['level' => 3]);
-
- $this->logger->logException($e);
-
- $logLines = $this->getLogs();
- foreach ($logLines as $logLine) {
- if (is_array($logLine)) {
- $logLine = json_encode($logLine);
- }
- $this->assertStringNotContainsString($user, $logLine);
- $this->assertStringNotContainsString($password, $logLine);
- $this->assertStringContainsString('*** sensitive parameters replaced ***', $logLine);
- }
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('userAndPasswordData')]
- public function testDetectvalidateUserPass(string $user, string $password): void {
- $this->mockDefaultLogLevel();
- $e = new \Exception('test');
- $this->registry->expects($this->once())
- ->method('delegateReport')
- ->with($e, ['level' => 3]);
-
- $this->logger->logException($e);
-
- $logLines = $this->getLogs();
- foreach ($logLines as $logLine) {
- if (is_array($logLine)) {
- $logLine = json_encode($logLine);
- }
- $this->assertStringNotContainsString($user, $logLine);
- $this->assertStringNotContainsString($password, $logLine);
- $this->assertStringContainsString('*** sensitive parameters replaced ***', $logLine);
- }
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('userAndPasswordData')]
- public function testDetecttryLogin(string $user, string $password): void {
- $this->mockDefaultLogLevel();
- $e = new \Exception('test');
- $this->registry->expects($this->once())
- ->method('delegateReport')
- ->with($e, ['level' => 3]);
-
- $this->logger->logException($e);
-
- $logLines = $this->getLogs();
- foreach ($logLines as $logLine) {
- if (is_array($logLine)) {
- $logLine = json_encode($logLine);
- }
- $this->assertStringNotContainsString($user, $logLine);
- $this->assertStringNotContainsString($password, $logLine);
- $this->assertStringContainsString('*** sensitive parameters replaced ***', $logLine);
- }
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('userAndPasswordData')]
- public function testDetectclosure(string $user, string $password): void {
- $this->mockDefaultLogLevel();
- $a = function ($user, $password): void {
- throw new \Exception('test');
- };
- $this->registry->expects($this->once())
- ->method('delegateReport');
-
- try {
- $a($user, $password);
- } catch (\Exception $e) {
- $this->logger->logException($e);
- }
-
- $logLines = $this->getLogs();
- foreach ($logLines as $logLine) {
- if (is_array($logLine)) {
- $logLine = json_encode($logLine);
- }
- $log = explode('\n', $logLine);
- unset($log[1]); // Remove `testDetectclosure(` because we are not testing this here, but the closure on stack trace 0
- $logLine = implode('\n', $log);
-
- $this->assertStringNotContainsString($user, $logLine);
- $this->assertStringNotContainsString($password, $logLine);
- $this->assertStringContainsString('*** sensitive parameters replaced ***', $logLine);
- }
- }
-}
--- a/tests/lib/Mail/EMailTemplateTest.php
+++ /dev/null
@@ -1,236 +0,0 @@
-defaults = $this->createMock(Defaults::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->l10n = $this->createMock(IFactory::class);
-
- $this->l10n->method('get')
- ->with('lib', '')
- ->willReturn($this->createMock(IL10N::class));
-
- $this->emailTemplate = new EMailTemplate(
- $this->defaults,
- $this->urlGenerator,
- $this->l10n,
- 252,
- 120,
- 'test.TestTemplate',
- []
- );
- }
-
- public function testEMailTemplateCustomFooter(): void {
- $this->defaults
- ->expects($this->atLeastOnce())
- ->method('getDefaultColorPrimary')
- ->willReturn('#0082c9');
- $this->defaults
- ->expects($this->any())
- ->method('getLogo')
- ->willReturn('/img/logo-mail-header.png');
- $this->defaults
- ->expects($this->any())
- ->method('getName')
- ->willReturn('TestCloud');
- $this->defaults
- ->expects($this->atLeastOnce())
- ->method('getDefaultTextColorPrimary')
- ->willReturn('#ffffff');
- $this->urlGenerator
- ->expects($this->once())
- ->method('getAbsoluteURL')
- ->with('/img/logo-mail-header.png')
- ->willReturn('https://example.org/img/logo-mail-header.png');
-
- $this->emailTemplate->addHeader();
- $this->emailTemplate->addHeading('Welcome aboard');
- $this->emailTemplate->addBodyText('Welcome to your Nextcloud account, you can add, protect, and share your data.');
- $this->emailTemplate->addBodyText('Your username is: abc');
- $this->emailTemplate->addBodyButtonGroup(
- 'Set your password', 'https://example.org/resetPassword/123',
- 'Install Client', 'https://nextcloud.com/install/#install-clients'
- );
- $this->emailTemplate->addFooter(
- 'TestCloud - A safe home for your data
This is an automatically sent email, please do not reply.'
- );
-
- $expectedHTML = file_get_contents(\OC::$SERVERROOT . '/tests/data/emails/new-account-email.html');
- $this->assertSame($expectedHTML, $this->emailTemplate->renderHtml());
- $expectedTXT = file_get_contents(\OC::$SERVERROOT . '/tests/data/emails/new-account-email.txt');
- $this->assertSame($expectedTXT, $this->emailTemplate->renderText());
- }
-
- public function testEMailTemplateDefaultFooter(): void {
- $this->defaults
- ->expects($this->atLeastOnce())
- ->method('getDefaultColorPrimary')
- ->willReturn('#0082c9');
- $this->defaults
- ->expects($this->any())
- ->method('getName')
- ->willReturn('TestCloud');
- $this->defaults
- ->expects($this->any())
- ->method('getSlogan')
- ->willReturn('A safe home for your data');
- $this->defaults
- ->expects($this->any())
- ->method('getLogo')
- ->willReturn('/img/logo-mail-header.png');
- $this->defaults
- ->expects($this->atLeastOnce())
- ->method('getDefaultTextColorPrimary')
- ->willReturn('#ffffff');
- $this->urlGenerator
- ->expects($this->once())
- ->method('getAbsoluteURL')
- ->with('/img/logo-mail-header.png')
- ->willReturn('https://example.org/img/logo-mail-header.png');
-
- $this->emailTemplate->addHeader();
- $this->emailTemplate->addHeading('Welcome aboard');
- $this->emailTemplate->addBodyText('Welcome to your Nextcloud account, you can add, protect, and share your data.');
- $this->emailTemplate->addBodyText('Your username is: abc');
- $this->emailTemplate->addBodyButtonGroup(
- 'Set your password', 'https://example.org/resetPassword/123',
- 'Install Client', 'https://nextcloud.com/install/#install-clients'
- );
- $this->emailTemplate->addFooter();
-
- $expectedHTML = file_get_contents(\OC::$SERVERROOT . '/tests/data/emails/new-account-email-custom.html');
- $this->assertSame($expectedHTML, $this->emailTemplate->renderHtml());
- $expectedTXT = file_get_contents(\OC::$SERVERROOT . '/tests/data/emails/new-account-email-custom.txt');
- $this->assertSame($expectedTXT, $this->emailTemplate->renderText());
- }
-
- public function testEMailTemplateSingleButton(): void {
- $this->defaults
- ->expects($this->atLeastOnce())
- ->method('getDefaultColorPrimary')
- ->willReturn('#0082c9');
- $this->defaults
- ->expects($this->any())
- ->method('getName')
- ->willReturn('TestCloud');
- $this->defaults
- ->expects($this->any())
- ->method('getSlogan')
- ->willReturn('A safe home for your data');
- $this->defaults
- ->expects($this->any())
- ->method('getLogo')
- ->willReturn('/img/logo-mail-header.png');
- $this->defaults
- ->expects($this->atLeastOnce())
- ->method('getDefaultTextColorPrimary')
- ->willReturn('#ffffff');
- $this->urlGenerator
- ->expects($this->once())
- ->method('getAbsoluteURL')
- ->with('/img/logo-mail-header.png')
- ->willReturn('https://example.org/img/logo-mail-header.png');
-
- $this->emailTemplate->addHeader();
- $this->emailTemplate->addHeading('Welcome aboard');
- $this->emailTemplate->addBodyText('Welcome to your Nextcloud account, you can add, protect, and share your data.');
- $this->emailTemplate->addBodyText('Your username is: abc');
- $this->emailTemplate->addBodyButton(
- 'Set your password', 'https://example.org/resetPassword/123',
- false
- );
- $this->emailTemplate->addFooter();
-
- $expectedHTML = file_get_contents(\OC::$SERVERROOT . '/tests/data/emails/new-account-email-single-button.html');
- $this->assertSame($expectedHTML, $this->emailTemplate->renderHtml());
- $expectedTXT = file_get_contents(\OC::$SERVERROOT . '/tests/data/emails/new-account-email-single-button.txt');
- $this->assertSame($expectedTXT, $this->emailTemplate->renderText());
- }
-
- public function testEMailTemplateAlternativePlainTexts(): void {
- $this->defaults
- ->expects($this->atLeastOnce())
- ->method('getDefaultColorPrimary')
- ->willReturn('#0082c9');
- $this->defaults
- ->expects($this->any())
- ->method('getName')
- ->willReturn('TestCloud');
- $this->defaults
- ->expects($this->any())
- ->method('getSlogan')
- ->willReturn('A safe home for your data');
- $this->defaults
- ->expects($this->any())
- ->method('getLogo')
- ->willReturn('/img/logo-mail-header.png');
- $this->defaults
- ->expects($this->atLeastOnce())
- ->method('getDefaultTextColorPrimary')
- ->willReturn('#ffffff');
- $this->urlGenerator
- ->expects($this->once())
- ->method('getAbsoluteURL')
- ->with('/img/logo-mail-header.png')
- ->willReturn('https://example.org/img/logo-mail-header.png');
-
- $this->emailTemplate->addHeader();
- $this->emailTemplate->addHeading('Welcome aboard', 'Welcome aboard - text');
- $this->emailTemplate->addBodyText('Welcome to your Nextcloud account, you can add, protect, and share your data.', 'Welcome to your Nextcloud account, you can add, protect, and share your data. - text');
- $this->emailTemplate->addBodyText('Your username is: abc');
- $this->emailTemplate->addBodyButtonGroup(
- 'Set your password', 'https://example.org/resetPassword/123',
- 'Install Client', 'https://nextcloud.com/install/#install-clients',
- 'Set your password - text', 'Install Client - text'
- );
- $this->emailTemplate->addFooter();
-
- $expectedHTML = file_get_contents(\OC::$SERVERROOT . '/tests/data/emails/new-account-email-custom.html');
- $this->assertSame($expectedHTML, $this->emailTemplate->renderHtml());
- $expectedTXT = file_get_contents(\OC::$SERVERROOT . '/tests/data/emails/new-account-email-custom-text-alternative.txt');
- $this->assertSame($expectedTXT, $this->emailTemplate->renderText());
- }
-
- public function testEMailTemplateEmbedsLogo(): void {
- $this->defaults->method('getDefaultColorPrimary')->willReturn('#0082c9');
- $this->defaults->method('getName')->willReturn('TestCloud');
- $this->defaults->method('getLogoImage')
- ->willReturn(['content' => 'PNGDATA', 'mimeType' => 'image/png']);
- $this->urlGenerator->expects($this->never())->method('getAbsoluteURL');
-
- $this->emailTemplate->addHeader();
-
- $this->assertStringContainsString('src="cid:logo"', $this->emailTemplate->renderHtml());
- $this->assertSame(
- [['name' => 'logo', 'content' => 'PNGDATA', 'mimeType' => 'image/png']],
- $this->emailTemplate->getInlineImages()
- );
- }
-}
--- a/tests/lib/Mail/EmailValidatorTest.php
+++ /dev/null
@@ -1,53 +0,0 @@
-appConfig = $this->createMock(IAppConfig::class);
- $this->emailValidator = new EmailValidator($this->appConfig);
- }
-
- public static function mailAddressProvider(): array {
- return [
- ['lukas@nextcloud.com', true, false],
- ['lukas@localhost', true, false],
- ['lukas@192.168.1.1', true, false],
- ['lukas@éxämplè.com', true, false],
- ['asdf', false, false],
- ['', false, false],
- ['lukas@nextcloud.org@nextcloud.com', false, false],
- ['test@localhost', true, false],
- ['test@localhost', false, true],
- ];
- }
-
- #[DataProvider('mailAddressProvider')]
- public function testIsValid($email, $expected, $strict): void {
- $this->appConfig
- ->expects($this->atMost(1))
- ->method('getValueString')
- ->with('core', 'enforce_strict_email_check', 'yes')
- ->willReturn($strict ? 'yes' : 'no');
- $this->assertSame($expected, $this->emailValidator->isValid($email));
- }
-}
--- a/tests/lib/Mail/MailerTest.php
+++ /dev/null
@@ -1,341 +0,0 @@
-config = $this->createMock(IConfig::class);
- $this->defaults = $this->createMock(Defaults::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->l10n = $this->createMock(IL10N::class);
- $this->dispatcher = $this->createMock(IEventDispatcher::class);
- $this->mailer = new Mailer(
- $this->config,
- $this->logger,
- $this->defaults,
- $this->urlGenerator,
- $this->l10n,
- $this->dispatcher,
- $this->createMock(IFactory::class),
- $this->createMock(IEmailValidator::class),
- );
- }
-
- /**
- * @return array
- */
- public static function sendmailModeProvider(): array {
- return [
- 'smtp' => ['smtp', ' -bs'],
- 'pipe' => ['pipe', ' -t -i'],
- ];
- }
-
- /**
- * @param $sendmailMode
- * @param $binaryParam
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('sendmailModeProvider')]
- public function testGetSendmailInstanceSendMail($sendmailMode, $binaryParam): void {
- $this->config
- ->expects($this->exactly(2))
- ->method('getSystemValueString')
- ->willReturnMap([
- ['mail_smtpmode', 'smtp', 'sendmail'],
- ['mail_sendmailmode', 'smtp', $sendmailMode],
- ]);
-
- $path = Server::get(IBinaryFinder::class)->findBinaryPath('sendmail');
- if ($path === false) {
- $path = '/usr/sbin/sendmail';
- }
-
- $expected = new SendmailTransport($path . $binaryParam, null, $this->logger);
- $this->assertEquals($expected, self::invokePrivate($this->mailer, 'getSendMailInstance'));
- }
-
- /**
- * @param $sendmailMode
- * @param $binaryParam
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('sendmailModeProvider')]
- public function testGetSendmailInstanceSendMailQmail($sendmailMode, $binaryParam): void {
- $this->config
- ->expects($this->exactly(2))
- ->method('getSystemValueString')
- ->willReturnMap([
- ['mail_smtpmode', 'smtp', 'qmail'],
- ['mail_sendmailmode', 'smtp', $sendmailMode],
- ]);
-
- $sendmail = new SendmailTransport('/var/qmail/bin/sendmail' . $binaryParam, null, $this->logger);
- $this->assertEquals($sendmail, self::invokePrivate($this->mailer, 'getSendMailInstance'));
- }
-
- public function testEventForNullTransport(): void {
- $this->config
- ->expects($this->exactly(1))
- ->method('getSystemValueString')
- ->with('mail_smtpmode', 'smtp')
- ->willReturn('null');
-
- $message = $this->createMock(Message::class);
- $message->expects($this->once())
- ->method('getSymfonyEmail')
- ->willReturn((new Email())->to('foo@bar.com')->from('bar@foo.com')->text(''));
-
- $event = new BeforeMessageSent($message);
- $this->dispatcher->expects($this->once())
- ->method('dispatchTyped')
- ->with($this->equalTo($event));
-
- $this->mailer->send($message);
- }
-
- public function testGetInstanceDefault(): void {
- $this->config
- ->method('getSystemValue')
- ->willReturnMap([
- ['mail_smtphost', '127.0.0.1', '127.0.0.1'],
- ['mail_smtpport', 25, 25],
- ['mail_smtptimeout', 10, 10],
- ]);
- $mailer = self::invokePrivate($this->mailer, 'getInstance');
- $this->assertInstanceOf(SymfonyMailer::class, $mailer);
- $transport = self::invokePrivate($mailer, 'transport');
- $this->assertInstanceOf(EsmtpTransport::class, $transport);
- }
-
- public function testGetInstanceSendmail(): void {
- $this->config
- ->method('getSystemValueString')
- ->willReturnMap([
- ['mail_smtpmode', 'smtp', 'sendmail'],
- ['mail_sendmailmode', 'smtp', 'smtp'],
- ]);
-
- $mailer = self::invokePrivate($this->mailer, 'getInstance');
- $this->assertInstanceOf(SymfonyMailer::class, $mailer);
- $transport = self::invokePrivate($mailer, 'transport');
- $this->assertInstanceOf(SendmailTransport::class, $transport);
- }
-
- public function testEvents(): void {
- $this->config
- ->method('getSystemValue')
- ->willReturnMap([
- ['mail_smtphost', '127.0.0.1', '127.0.0.1'],
- ['mail_smtpport', 25, 25],
- ]);
- $this->mailer = $this->getMockBuilder(Mailer::class)
- ->onlyMethods(['getInstance'])
- ->setConstructorArgs(
- [
- $this->config,
- $this->logger,
- $this->defaults,
- $this->urlGenerator,
- $this->l10n,
- $this->dispatcher,
- $this->createMock(IFactory::class),
- $this->createMock(IEmailValidator::class),
- ]
- )
- ->getMock();
-
- $message = $this->createMock(Message::class);
-
- $event = new BeforeMessageSent($message);
- $this->dispatcher->expects($this->once())
- ->method('dispatchTyped')
- ->with($this->equalTo($event));
-
- $this->mailer->send($message);
- }
-
- public function testCreateMessage(): void {
- $this->config
- ->expects($this->any())
- ->method('getSystemValueBool')
- ->with('mail_send_plaintext_only', false)
- ->willReturn(false);
- $this->assertInstanceOf('\OC\Mail\Message', $this->mailer->createMessage());
- }
-
- public function testSendInvalidMailException(): void {
- $this->config
- ->method('getSystemValue')
- ->willReturnMap([
- ['mail_smtphost', '127.0.0.1', '127.0.0.1'],
- ['mail_smtpport', 25, 25],
- ['mail_smtptimeout', 10, 10],
- ]);
- $this->expectException(\Exception::class);
-
- /** @var Message&MockObject */
- $message = $this->getMockBuilder('\OC\Mail\Message')
- ->disableOriginalConstructor()->getMock();
- $message->expects($this->once())
- ->method('getSymfonyEmail')
- ->willReturn(new Email());
-
- $this->mailer->send($message);
- }
-
- public function testCreateEMailTemplate(): void {
- $this->config->method('getSystemValueString')
- ->with('mail_template_class', '')
- ->willReturnArgument(1);
- $this->config->method('getAppValue')
- ->with('theming', 'logoDimensions', Mailer::DEFAULT_DIMENSIONS)
- ->willReturn(Mailer::DEFAULT_DIMENSIONS);
-
- $this->assertSame(EMailTemplate::class, get_class($this->mailer->createEMailTemplate('tests.MailerTest')));
- }
-
- public function testStreamingOptions(): void {
- $this->config->method('getSystemValue')
- ->willReturnMap([
- ['mail_smtpstreamoptions', [], ['foo' => 1]],
- ]);
- $this->config->method('getSystemValueString')
- ->willReturnMap([
- ['mail_smtpmode', 'smtp', 'smtp'],
- ['overwrite.cli.url', '', ''],
- ['mail_smtphost', '127.0.0.1', '127.0.0.1'],
- ]);
- $this->config->method('getSystemValueInt')
- ->willReturnMap([
- ['mail_smtpport', 25, 25],
- ['mail_smtptimeout', 10, 10],
- ]);
- $mailer = self::invokePrivate($this->mailer, 'getInstance');
- /** @var EsmtpTransport $transport */
- $transport = self::invokePrivate($mailer, 'transport');
- $this->assertInstanceOf(EsmtpTransport::class, $transport);
- $this->assertEquals(1, count($transport->getStream()->getStreamOptions()));
- $this->assertTrue(isset($transport->getStream()->getStreamOptions()['foo']));
- }
-
- public function testStreamingOptionsWrongType(): void {
- $this->config->method('getSystemValue')
- ->willReturnMap([
- ['mail_smtpstreamoptions', [], 'bar'],
- ]);
- $this->config->method('getSystemValueString')
- ->willReturnMap([
- ['mail_smtpmode', 'smtp', 'smtp'],
- ['overwrite.cli.url', '', ''],
- ['mail_smtphost', '127.0.0.1', '127.0.0.1'],
- ]);
- $this->config->method('getSystemValueInt')
- ->willReturnMap([
- ['mail_smtpport', 25, 25],
- ['mail_smtptimeout', 10, 10],
- ]);
-
- $mailer = self::invokePrivate($this->mailer, 'getInstance');
- /** @var EsmtpTransport $transport */
- $transport = self::invokePrivate($mailer, 'transport');
- $this->assertInstanceOf(EsmtpTransport::class, $transport);
- $this->assertEquals(0, count($transport->getStream()->getStreamOptions()));
- }
-
- public function testLocalDomain(): void {
- $this->config->method('getSystemValueString')
- ->willReturnMap([
- ['mail_smtpmode', 'smtp', 'smtp'],
- ['overwrite.cli.url', '', 'https://some.valid.url.com:8080'],
- ['mail_smtphost', '127.0.0.1', '127.0.0.1'],
- ]);
- $this->config->method('getSystemValueInt')
- ->willReturnMap([
- ['mail_smtpport', 25, 25],
- ['mail_smtptimeout', 10, 10],
- ]);
-
- /** @var SymfonyMailer $mailer */
- $mailer = self::invokePrivate($this->mailer, 'getInstance');
- self::assertInstanceOf(SymfonyMailer::class, $mailer);
-
- /** @var EsmtpTransport $transport */
- $transport = self::invokePrivate($mailer, 'transport');
- self::assertInstanceOf(EsmtpTransport::class, $transport);
- self::assertEquals('some.valid.url.com', $transport->getLocalDomain());
- }
-
- public function testLocalDomainInvalidUrl(): void {
- $this->config->method('getSystemValueString')
- ->willReturnMap([
- ['mail_smtpmode', 'smtp', 'smtp'],
- ['overwrite.cli.url', '', 'https:only.slash.does.not.work:8080'],
- ['mail_smtphost', '127.0.0.1', '127.0.0.1'],
- ]);
- $this->config->method('getSystemValueInt')
- ->willReturnMap([
- ['mail_smtpport', 25, 25],
- ['mail_smtptimeout', 10, 10],
- ]);
-
- /** @var SymfonyMailer $mailer */
- $mailer = self::invokePrivate($this->mailer, 'getInstance');
- self::assertInstanceOf(SymfonyMailer::class, $mailer);
-
- /** @var EsmtpTransport $transport */
- $transport = self::invokePrivate($mailer, 'transport');
- self::assertInstanceOf(EsmtpTransport::class, $transport);
- self::assertEquals('[127.0.0.1]', $transport->getLocalDomain());
- }
-
- public function testCaching(): void {
- $symfonyMailer1 = self::invokePrivate($this->mailer, 'getInstance');
- $symfonyMailer2 = self::invokePrivate($this->mailer, 'getInstance');
- self::assertSame($symfonyMailer1, $symfonyMailer2);
- }
-}
--- a/tests/lib/Mail/MessageTest.php
+++ /dev/null
@@ -1,295 +0,0 @@
- 'Lukas Reschke'],
- [new Address('lukas@owncloud.com', 'Lukas Reschke')]
- ],
- [
- [
- 'lukas@owncloud.com' => 'Lukas Reschke',
- 'lukas@öwnclöüd.com',
- 'lukäs@owncloud.örg' => 'Lükäs Réschke'
- ],
- [
- new Address('lukas@owncloud.com', 'Lukas Reschke'),
- new Address('lukas@öwnclöüd.com'),
- new Address('lukäs@owncloud.örg', 'Lükäs Réschke')
- ]
- ],
- [
- ['lukas@öwnclöüd.com'],
- [new Address('lukas@öwnclöüd.com')]
- ],
- ];
- }
-
- /**
- * @return array
- */
- public function getMailAddressProvider() {
- return [
- [[], []],
- [['lukas@owncloud.com' => 'Lukas Reschke'], ['lukas@owncloud.com' => 'Lukas Reschke']],
- ];
- }
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- $this->symfonyEmail = $this->createMock(Email::class);
-
- $this->message = new Message($this->symfonyEmail, false);
- }
-
- /**
- *
- * @param string $unconverted
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('mailAddressProvider')]
- public function testConvertAddresses($unconverted, $expected): void {
- $this->assertEquals($expected, self::invokePrivate($this->message, 'convertAddresses', [$unconverted]));
- }
-
- public function testSetRecipients(): void {
- $this->message = $this->message->setFrom(['pierres-general-store@stardewvalley.com' => 'Pierres General Store']);
- $this->message = $this->message->setTo(['lewis-tent@stardewvalley.com' => "Lewis' Tent Life"]);
- $this->message = $this->message->setReplyTo(['penny@stardewvalley-library.co.edu' => 'Penny']);
- $this->message = $this->message->setCc(['gunther@stardewvalley-library.co.edu' => 'Gunther']);
- $this->message = $this->message->setBcc(['pam@stardewvalley-bus.com' => 'Pam']);
-
- $this->symfonyEmail
- ->expects($this->once())
- ->method('from')
- ->with(new Address('pierres-general-store@stardewvalley.com', 'Pierres General Store'));
- $this->symfonyEmail
- ->expects($this->once())
- ->method('to')
- ->with(new Address('lewis-tent@stardewvalley.com', "Lewis' Tent Life"));
- $this->symfonyEmail
- ->expects($this->once())
- ->method('replyTo')
- ->with(new Address('penny@stardewvalley-library.co.edu', 'Penny'));
- $this->symfonyEmail
- ->expects($this->once())
- ->method('cc')
- ->with(new Address('gunther@stardewvalley-library.co.edu', 'Gunther'));
- $this->symfonyEmail
- ->expects($this->once())
- ->method('bcc')
- ->with(new Address('pam@stardewvalley-bus.com', 'Pam'));
-
- $this->message->setRecipients();
- }
-
- public function testSetTo(): void {
- $expected = ['pierres-general-store@stardewvalley.com' => 'Pierres General Store'];
-
- $message = $this->message->setTo(['pierres-general-store@stardewvalley.com' => 'Pierres General Store']);
-
- $this->assertEquals($expected, $message->getTo());
- }
- public function testSetRecipientsException(): void {
- $message = $this->message->setTo(['lewis-tent@~~~~.com' => "Lewis' Tent Life"]);
-
- $this->symfonyEmail
- ->expects($this->once())
- ->method('to')
- ->willThrowException(new RfcComplianceException());
-
- $this->expectException(RfcComplianceException::class);
- $message->setRecipients();
- }
-
- public function testSetRecipientsEmptyValues(): void {
- $message = $this->message->setTo([]);
-
- $this->symfonyEmail
- ->expects($this->once())
- ->method('to');
-
- $message->setRecipients();
- }
-
- public function testSetGetFrom(): void {
- $expected = ['pierres-general-store@stardewvalley.com' => 'Pierres General Store'];
-
- $message = $this->message->setFrom(['pierres-general-store@stardewvalley.com' => 'Pierres General Store']);
-
- $this->assertEquals($expected, $message->getFrom());
- }
-
- public function testSetGetTo(): void {
- $expected = ['lewis-tent@stardewvalley.com' => "Lewis' Tent Life"];
-
- $message = $this->message->setTo(['lewis-tent@stardewvalley.com' => "Lewis' Tent Life"]);
-
- $this->assertEquals($expected, $message->getTo());
- }
-
- public function testSetGetReplyTo(): void {
- $expected = ['penny@stardewvalley-library.co.edu' => 'Penny'];
-
- $message = $this->message->setReplyTo(['penny@stardewvalley-library.co.edu' => 'Penny']);
-
- $this->assertEquals($expected, $message->getReplyTo());
- }
-
- public function testSetGetCC(): void {
- $expected = ['gunther@stardewvalley-library.co.edu' => 'Gunther'];
-
- $message = $this->message->setCc(['gunther@stardewvalley-library.co.edu' => 'Gunther']);
-
- $this->assertEquals($expected, $message->getCc());
- }
-
- public function testSetGetBCC(): void {
- $expected = ['pam@stardewvalley-bus.com' => 'Pam'];
-
- $message = $this->message->setBcc(['pam@stardewvalley-bus.com' => 'Pam']);
-
- $this->assertEquals($expected, $message->getBcc());
- }
-
- public function testSetPlainBody(): void {
- $this->symfonyEmail
- ->expects($this->once())
- ->method('text')
- ->with('Fancy Body');
-
- $this->message->setPlainBody('Fancy Body');
- }
-
- public function testGetPlainBody(): void {
- $this->symfonyEmail
- ->expects($this->once())
- ->method('getTextBody')
- ->willReturn('Fancy Body');
-
- $this->assertSame('Fancy Body', $this->message->getPlainBody());
- }
-
- public function testSetHtmlBody(): void {
- $this->symfonyEmail
- ->expects($this->once())
- ->method('html')
- ->with('', 'utf-8');
-
- $this->message->setHtmlBody('');
- }
-
- public function testPlainTextRenderOption(): void {
- /** @var MockObject|Email $symfonyEmail */
- $symfonyEmail = $this->getMockBuilder(Email::class)
- ->disableOriginalConstructor()->getMock();
- /** @var MockObject|IEMailTemplate $template */
- $template = $this->getMockBuilder(IEMailTemplate::class)
- ->disableOriginalConstructor()->getMock();
-
- $message = new Message($symfonyEmail, true);
-
- $template
- ->expects($this->never())
- ->method('renderHTML');
- $template
- ->expects($this->once())
- ->method('renderText');
- $template
- ->expects($this->once())
- ->method('renderSubject');
-
- $message->useTemplate($template);
- }
-
- public function testBothRenderingOptions(): void {
- /** @var MockObject|Email $symfonyEmail */
- $symfonyEmail = $this->getMockBuilder(Email::class)
- ->disableOriginalConstructor()->getMock();
- /** @var MockObject|IEMailTemplate $template */
- $template = $this->getMockBuilder(IEMailTemplate::class)
- ->disableOriginalConstructor()->getMock();
-
- $message = new Message($symfonyEmail, false);
-
- $template
- ->expects($this->once())
- ->method('renderHTML');
- $template
- ->expects($this->once())
- ->method('renderText');
- $template
- ->expects($this->once())
- ->method('renderSubject');
-
- $message->useTemplate($template);
- }
-
- public function testSetAutoSubmitted1(): void {
- $headers = new Headers($this->createMock(HeaderInterface::class));
- $headers->addTextHeader(AutoSubmitted::HEADER, 'yes');
- $symfonyEmail = $this->createMock(Email::class);
-
- $symfonyEmail->method('getHeaders')
- ->willReturn($headers);
-
- $message = new Message($symfonyEmail, false);
- $message->setAutoSubmitted(AutoSubmitted::VALUE_AUTO_GENERATED);
- $this->assertNotSame('no', $message->getAutoSubmitted());
- }
-
- public function testSetAutoSubmitted2(): void {
- $headers = new Headers($this->createMock(HeaderInterface::class));
- $headers->addTextHeader(AutoSubmitted::HEADER, 'no');
- $symfonyEmail = $this->createMock(Email::class);
-
- $symfonyEmail->method('getHeaders')
- ->willReturn($headers);
-
- $message = new Message($symfonyEmail, false);
- $message->setAutoSubmitted(AutoSubmitted::VALUE_AUTO_GENERATED);
- $this->assertSame('auto-generated', $message->getAutoSubmitted());
- }
-
- public function testGetAutoSubmitted(): void {
- $headers = new Headers($this->createMock(HeaderInterface::class));
- $headers->addTextHeader(AutoSubmitted::HEADER, 'no');
- $symfonyEmail = $this->createMock(Email::class);
-
- $symfonyEmail->method('getHeaders')
- ->willReturn($headers);
-
- $message = new Message($symfonyEmail, false);
- $this->assertSame('no', $message->getAutoSubmitted());
- }
-}
--- a/tests/lib/Mail/Provider/AddressTest.php
+++ /dev/null
@@ -1,48 +0,0 @@
-address = new Address('user1@testing.com', 'User One');
-
- }
-
- public function testAddress(): void {
-
- // test set by constructor
- $this->assertEquals('user1@testing.com', $this->address->getAddress());
- // test set by setter
- $this->address->setAddress('user2@testing.com');
- $this->assertEquals('user2@testing.com', $this->address->getAddress());
-
- }
-
- public function testLabel(): void {
-
- // test set by constructor
- $this->assertEquals('User One', $this->address->getLabel());
- // test set by setter
- $this->address->setLabel('User Two');
- $this->assertEquals('User Two', $this->address->getLabel());
-
- }
-
-}
--- a/tests/lib/Mail/Provider/AttachmentTest.php
+++ /dev/null
@@ -1,73 +0,0 @@
-attachment = new Attachment(
- 'This is the contents of a file',
- 'example1.txt',
- 'text/plain',
- false
- );
-
- }
-
- public function testName(): void {
-
- // test set by constructor
- $this->assertEquals('example1.txt', $this->attachment->getName());
- // test set by setter
- $this->attachment->setName('example2.txt');
- $this->assertEquals('example2.txt', $this->attachment->getName());
-
- }
-
- public function testType(): void {
-
- // test set by constructor
- $this->assertEquals('text/plain', $this->attachment->getType());
- // test set by setter
- $this->attachment->setType('text/html');
- $this->assertEquals('text/html', $this->attachment->getType());
-
- }
-
- public function testContents(): void {
-
- // test set by constructor
- $this->assertEquals('This is the contents of a file', $this->attachment->getContents());
- // test set by setter
- $this->attachment->setContents('This is the modified contents of a file');
- $this->assertEquals('This is the modified contents of a file', $this->attachment->getContents());
-
- }
-
- public function testEmbedded(): void {
-
- // test set by constructor
- $this->assertEquals(false, $this->attachment->getEmbedded());
- // test set by setter
- $this->attachment->setEmbedded(true);
- $this->assertEquals(true, $this->attachment->getEmbedded());
-
- }
-
-}
--- a/tests/lib/Mail/Provider/ManagerTest.php
+++ /dev/null
@@ -1,191 +0,0 @@
-logger = $this->createMock(LoggerInterface::class);
-
- // construct service registration
- $registration = $this->createMock(ServiceRegistration::class);
- $registration
- ->method('getService')
- ->willReturn('Mock\Provider\MailProvider');
- // construct registration context
- $context = $this->createMock(RegistrationContext::class);
- $context
- ->method('getMailProviders')
- ->willReturn([$registration]);
- // construct coordinator
- $this->coordinator = $this->createMock(Coordinator::class);
- $this->coordinator
- ->method('getRegistrationContext')
- ->willReturn($context);
-
- // construct mail service
- $this->service = $this->createMock(IService::class);
- $this->service
- ->method('id')
- ->willReturn('100');
- $this->service
- ->method('getLabel')
- ->willReturn('Mock Mail Service');
- $this->service
- ->method('getPrimaryAddress')
- ->willReturn((new Address('user1@testing.com', 'User One')));
- // construct mail provider
- $this->provider = $this->createMock(IProvider::class);
- $this->provider
- ->method('id')
- ->willReturn('mock-provider');
- $this->provider
- ->method('label')
- ->willReturn('Mock Provider');
- $this->provider
- ->method('listServices')
- ->willReturnMap([
- ['user0', []],
- ['user1', [$this->service->id() => $this->service]]
- ]);
- $this->provider
- ->method('findServiceById')
- ->willReturnMap([
- ['user0', '100', null],
- ['user1', '100', $this->service]
- ]);
- $this->provider
- ->method('findServiceByAddress')
- ->willReturnMap([
- ['user0', 'user0@testing.com', null],
- ['user1', 'user1@testing.com', $this->service]
- ]);
- // construct container interface
- $this->container = $this->createMock(ContainerInterface::class);
- $this->container
- ->method('get')
- ->willReturnMap([
- ['Mock\Provider\MailProvider', $this->provider]
- ]);
-
- }
-
- public function testHas(): void {
-
- // construct mail manager
- $manager = new Manager($this->coordinator, $this->container, $this->logger);
- // test result with providers found
- $this->assertTrue($manager->has());
-
- }
-
- public function testCount(): void {
-
- // construct mail manager
- $manager = new Manager($this->coordinator, $this->container, $this->logger);
- // test result with providers found
- $this->assertGreaterThan(0, $manager->count());
-
- }
-
- public function testTypes(): void {
-
- // construct mail manager
- $manager = new Manager($this->coordinator, $this->container, $this->logger);
- // test result with providers found
- $this->assertEquals(['mock-provider' => 'Mock Provider'], $manager->types());
-
- }
-
- public function testProviders(): void {
-
- // construct mail manager
- $manager = new Manager($this->coordinator, $this->container, $this->logger);
- // test result with providers found
- $this->assertEquals([$this->provider->id() => $this->provider], $manager->providers());
-
- }
-
- public function testFindProviderById(): void {
-
- // construct mail manager
- $manager = new Manager($this->coordinator, $this->container, $this->logger);
- // test result with providers found
- $this->assertEquals($this->provider, $manager->findProviderById($this->provider->id()));
-
- }
-
- public function testServices(): void {
-
- // construct mail manager
- $manager = new Manager($this->coordinator, $this->container, $this->logger);
- // test result with no services found
- $this->assertEquals([], $manager->services('user0'));
- // test result with services found
- $this->assertEquals([$this->provider->id() => [$this->service->id() => $this->service]], $manager->services('user1'));
-
- }
-
- public function testFindServiceById(): void {
-
- // construct mail manager
- $manager = new Manager($this->coordinator, $this->container, $this->logger);
- // test result with no services found and not provider specified
- $this->assertEquals(null, $manager->findServiceById('user0', '100'));
- // test result with no services found and provider specified
- $this->assertEquals(null, $manager->findServiceById('user0', '100', $this->provider->id()));
- // test result with services found and not provider specified
- $this->assertEquals($this->service, $manager->findServiceById('user1', '100'));
- // test result with services found and provider specified
- $this->assertEquals($this->service, $manager->findServiceById('user1', '100', $this->provider->id()));
-
- }
-
- public function testFindServiceByAddress(): void {
-
- // construct mail manager
- $manager = new Manager($this->coordinator, $this->container, $this->logger);
- // test result with no services found and not provider specified
- $this->assertEquals(null, $manager->findServiceByAddress('user0', 'user0@testing.com'));
- // test result with no services found and provider specified
- $this->assertEquals(null, $manager->findServiceByAddress('user0', 'user0@testing.com', $this->provider->id()));
- // test result with services found and not provider specified
- $this->assertEquals($this->service, $manager->findServiceByAddress('user1', 'user1@testing.com'));
- // test result with services found and provider specified
- $this->assertEquals($this->service, $manager->findServiceByAddress('user1', 'user1@testing.com', $this->provider->id()));
-
- }
-
-}
--- a/tests/lib/Mail/Provider/MessageTest.php
+++ /dev/null
@@ -1,165 +0,0 @@
-message = new Message(
- ['id' => 'cd02ea42-feac-4863-b9d8-484d16a587ea']
- );
- $this->address1 = new Address(
- 'user1@testing.com',
- 'User One'
- );
- $this->address2 = new Address(
- 'user2@testing.com',
- 'User Two'
- );
- $this->attachment1 = new Attachment(
- 'This is the contents of the first attachment',
- 'example1.txt',
- 'text/plain',
- false
- );
- $this->attachment2 = new Attachment(
- 'This is the contents of the second attachment',
- 'example1.txt',
- 'text/plain',
- false
- );
-
- }
-
- public function testId(): void {
-
- // test set by constructor
- $this->assertEquals('cd02ea42-feac-4863-b9d8-484d16a587ea', $this->message->id());
-
- }
-
- public function testFrom(): void {
-
- // test not set
- $this->assertNull($this->message->getFrom());
- // test set by setter
- $this->message->setFrom($this->address1);
- $this->assertEquals($this->address1, $this->message->getFrom());
-
- }
-
- public function testReplyTo(): void {
-
- // test not set
- $this->assertNull($this->message->getReplyTo());
- // test set by setter
- $this->message->setReplyTo($this->address1);
- $this->assertEquals($this->address1, $this->message->getReplyTo());
-
- }
-
- public function testTo(): void {
-
- // test not set
- $this->assertEquals([], $this->message->getTo());
- // test set by setter single
- $this->message->setTo($this->address1);
- $this->assertEquals([$this->address1], $this->message->getTo());
- // test set by setter multiple
- $this->message->setTo($this->address1, $this->address2);
- $this->assertEquals([$this->address1, $this->address2], $this->message->getTo());
-
- }
-
- public function testCc(): void {
-
- // test not set
- $this->assertEquals([], $this->message->getCc());
- // test set by setter single
- $this->message->setCc($this->address1);
- $this->assertEquals([$this->address1], $this->message->getCc());
- // test set by setter multiple
- $this->message->setCc($this->address1, $this->address2);
- $this->assertEquals([$this->address1, $this->address2], $this->message->getCc());
-
- }
-
- public function testBcc(): void {
-
- // test not set
- $this->assertEquals([], $this->message->getBcc());
- // test set by setter single
- $this->message->setBcc($this->address1);
- $this->assertEquals([$this->address1], $this->message->getBcc());
- // test set by setter multiple
- $this->message->setBcc($this->address1, $this->address2);
- $this->assertEquals([$this->address1, $this->address2], $this->message->getBcc());
-
- }
-
- public function testSubject(): void {
-
- // test not set
- $this->assertNull($this->message->getSubject());
- // test set by setter
- $this->message->setSubject('Testing Mail Subject');
- $this->assertEquals('Testing Mail Subject', $this->message->getSubject());
-
- }
-
- public function testBody(): void {
-
- // test not set
- $this->assertNull($this->message->getBody());
- // test set by setter - text body
- $this->message->setBody('Testing Text Body', false);
- $this->assertEquals('Testing Text Body', $this->message->getBody());
- $this->message->setBodyPlain('Testing Text Body Again', false);
- $this->assertEquals('Testing Text Body Again', $this->message->getBodyPlain());
- // test set by setter - html body
- $this->message->setBody('Testing HTML Body', true);
- $this->assertEquals('Testing HTML Body', $this->message->getBody());
- $this->message->setBodyHtml('Testing HTML Body Again', false);
- $this->assertEquals('Testing HTML Body Again', $this->message->getBodyHtml());
-
- }
-
- public function testAttachments(): void {
-
- // test not set
- $this->assertEquals([], $this->message->getAttachments());
- // test set by setter single
- $this->message->setAttachments($this->attachment1);
- $this->assertEquals([$this->attachment1], $this->message->getAttachments());
- // test set by setter multiple
- $this->message->setAttachments($this->attachment1, $this->attachment2);
- $this->assertEquals([$this->attachment1, $this->attachment2], $this->message->getAttachments());
-
- }
-}
--- a/tests/lib/Memcache/APCuTest.php
+++ /dev/null
@@ -1,38 +0,0 @@
-markTestSkipped('The APCu extension is not available.');
- return;
- }
- $this->instance = new APCu($this->getUniqueID());
- }
-
- public function testCasIntChanged(): void {
- $this->instance->set('foo', 1);
- $this->assertTrue($this->instance->cas('foo', 1, 2));
- $this->assertEquals(2, $this->instance->get('foo'));
- }
-
- public function testCasIntNotChanged(): void {
- $this->instance->set('foo', 1);
- $this->assertFalse($this->instance->cas('foo', 2, 3));
- $this->assertEquals(1, $this->instance->get('foo'));
- }
-}
--- a/tests/lib/Memcache/ArrayCacheTest.php
+++ /dev/null
@@ -1,22 +0,0 @@
-instance = new ArrayCache('');
- }
-}
--- a/tests/lib/Memcache/Cache.php
+++ /dev/null
@@ -1,158 +0,0 @@
-assertFalse($this->instance->hasKey('foo'));
- $this->instance->set('foo', 'bar');
- $this->assertTrue($this->instance->hasKey('foo'));
- }
-
- public function testGetAfterSet(): void {
- $this->assertNull($this->instance->get('foo'));
- $this->instance->set('foo', 'bar');
- $this->assertEquals('bar', $this->instance->get('foo'));
- }
-
- public function testGetArrayAfterSet(): void {
- $this->assertNull($this->instance->get('foo'));
- $this->instance->set('foo', ['bar']);
- $this->assertEquals(['bar'], $this->instance->get('foo'));
- }
-
- public function testDoesNotExistAfterRemove(): void {
- $this->instance->set('foo', 'bar');
- $this->instance->remove('foo');
- $this->assertFalse($this->instance->hasKey('foo'));
- }
-
- public function testRemoveNonExisting(): void {
- $this->instance->remove('foo');
- $this->assertFalse($this->instance->hasKey('foo'));
- }
-
- public function testArrayAccessSet(): void {
- $this->instance['foo'] = 'bar';
- $this->assertEquals('bar', $this->instance->get('foo'));
- }
-
- public function testArrayAccessGet(): void {
- $this->instance->set('foo', 'bar');
- $this->assertEquals('bar', $this->instance['foo']);
- }
-
- public function testArrayAccessExists(): void {
- $this->assertFalse(isset($this->instance['foo']));
- $this->instance->set('foo', 'bar');
- $this->assertTrue(isset($this->instance['foo']));
- }
-
- public function testArrayAccessUnset(): void {
- $this->instance->set('foo', 'bar');
- unset($this->instance['foo']);
- $this->assertFalse($this->instance->hasKey('foo'));
- }
-
- public function testAdd(): void {
- $this->assertTrue($this->instance->add('foo', 'bar'));
- $this->assertEquals('bar', $this->instance->get('foo'));
- $this->assertFalse($this->instance->add('foo', 'asd'));
- $this->assertEquals('bar', $this->instance->get('foo'));
- }
-
- public function testInc(): void {
- $this->assertEquals(1, $this->instance->inc('foo'));
- $this->assertEquals(1, $this->instance->get('foo'));
- $this->assertEquals(2, $this->instance->inc('foo'));
- $this->assertEquals(2, $this->instance->get('foo'));
- $this->assertEquals(12, $this->instance->inc('foo', 10));
- $this->assertEquals(12, $this->instance->get('foo'));
-
- $this->instance->set('foo', 'bar');
- $this->assertFalse($this->instance->inc('foo'));
- $this->assertEquals('bar', $this->instance->get('foo'));
- }
-
- public function testDec(): void {
- $this->assertFalse($this->instance->dec('foo'));
- $this->instance->set('foo', 20);
- $this->assertEquals(19, $this->instance->dec('foo'));
- $this->assertEquals(19, $this->instance->get('foo'));
- $this->assertEquals(9, $this->instance->dec('foo', 10));
-
- $this->instance->set('foo', 'bar');
- $this->assertFalse($this->instance->dec('foo'));
- $this->assertEquals('bar', $this->instance->get('foo'));
- }
-
- public function testCasNotChanged(): void {
- $this->instance->set('foo', 'bar');
- $this->assertTrue($this->instance->cas('foo', 'bar', 'asd'));
- $this->assertEquals('asd', $this->instance->get('foo'));
- }
-
- public function testCasChanged(): void {
- $this->instance->set('foo', 'bar1');
- $this->assertFalse($this->instance->cas('foo', 'bar', 'asd'));
- $this->assertEquals('bar1', $this->instance->get('foo'));
- }
-
- public function testCasNotSet(): void {
- $this->assertFalse($this->instance->cas('foo', 'bar', 'asd'));
- }
-
- public function testCadNotChanged(): void {
- $this->instance->set('foo', 'bar');
- $this->assertTrue($this->instance->cad('foo', 'bar'));
- $this->assertFalse($this->instance->hasKey('foo'));
- }
-
- public function testCadChanged(): void {
- $this->instance->set('foo', 'bar1');
- $this->assertFalse($this->instance->cad('foo', 'bar'));
- $this->assertTrue($this->instance->hasKey('foo'));
- }
-
- public function testCadNotSet(): void {
- $this->assertFalse($this->instance->cad('foo', 'bar'));
- }
-
- public function testNcadNotChanged(): void {
- $this->instance->set('foo', 'bar');
- $this->assertFalse($this->instance->ncad('foo', 'bar'));
- $this->assertTrue($this->instance->hasKey('foo'));
- }
-
- public function testNcadChanged(): void {
- $this->instance->set('foo', 'bar1');
- $this->assertTrue($this->instance->ncad('foo', 'bar'));
- $this->assertFalse($this->instance->hasKey('foo'));
- }
-
- public function testNcadNotSet(): void {
- $this->assertFalse($this->instance->ncad('foo', 'bar'));
- }
-
- #[\Override]
- protected function tearDown(): void {
- if ($this->instance) {
- $this->instance->clear();
- }
-
- parent::tearDown();
- }
-}
--- a/tests/lib/Memcache/CasTraitTest.php
+++ /dev/null
@@ -1,68 +0,0 @@
-getMockBuilder(CasTraitTestClass::class)->onlyMethods([
- 'set',
- 'get',
- 'add',
- 'remove',
- ])->getMock();
-
- $mock->expects($this->any())
- ->method('set')
- ->willReturnCallback(function ($key, $value, $ttl) use ($sourceCache) {
- return $sourceCache->set($key, $value, $ttl);
- });
-
- $mock->expects($this->any())
- ->method('get')
- ->willReturnCallback(function ($key) use ($sourceCache) {
- return $sourceCache->get($key);
- });
-
- $mock->expects($this->any())
- ->method('add')
- ->willReturnCallback(function ($key, $value, $ttl) use ($sourceCache) {
- return $sourceCache->add($key, $value, $ttl);
- });
-
- $mock->expects($this->any())
- ->method('remove')
- ->willReturnCallback(function ($key) use ($sourceCache) {
- return $sourceCache->remove($key);
- });
- return $mock;
- }
-
- public function testCasNotChanged(): void {
- $cache = $this->getCache();
- $cache->set('foo', 'bar');
- $this->assertTrue($cache->cas('foo', 'bar', 'asd'));
- $this->assertEquals('asd', $cache->get('foo'));
- }
-
- public function testCasChanged(): void {
- $cache = $this->getCache();
- $cache->set('foo', 'bar1');
- $this->assertFalse($cache->cas('foo', 'bar', 'asd'));
- $this->assertEquals('bar1', $cache->get('foo'));
- }
-}
--- a/tests/lib/Memcache/CasTraitTestClass.php
+++ /dev/null
@@ -1,30 +0,0 @@
-getMockBuilder(LoggerInterface::class)->getMock();
- $profiler = $this->getMockBuilder(IProfiler::class)->getMock();
- $serverVersion = $this->createMock(ServerVersion::class);
- $factory = new Factory($logger, $profiler, $serverVersion, $localCache, $distributedCache, $lockingCache);
- $this->assertTrue(is_a($factory->createLocal(), $expectedLocalCache));
- $this->assertTrue(is_a($factory->createDistributed(), $expectedDistributedCache));
- $this->assertTrue(is_a($factory->createLocking(), $expectedLockingCache));
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('cacheUnavailableProvider')]
- public function testCacheNotAvailableException($localCache, $distributedCache): void {
- $this->expectException(HintException::class);
-
- $logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
- $profiler = $this->getMockBuilder(IProfiler::class)->getMock();
- $serverVersion = $this->createMock(ServerVersion::class);
- new Factory($logger, $profiler, $serverVersion, $localCache, $distributedCache);
- }
-
- public function testCreateInMemory(): void {
- $logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
- $profiler = $this->getMockBuilder(IProfiler::class)->getMock();
- $serverVersion = $this->createMock(ServerVersion::class);
- $factory = new Factory($logger, $profiler, $serverVersion, null, null, null);
-
- $cache = $factory->createInMemory();
- $cache->set('test', 48);
-
- self::assertSame(48, $cache->get('test'));
- }
-}
--- a/tests/lib/Memcache/KeyValueCacheFactoryTest.php
+++ /dev/null
@@ -1,275 +0,0 @@
-config = $this->createMock(SystemConfig::class);
- $this->eventLogger = $this->createMock(IEventLogger::class);
- $this->factory = new KeyValueCacheFactory($this->config, $this->eventLogger);
- }
-
- public function testSingleServerTcp(): void {
- [$parameters, $options] = $this->factory->buildConnectionConfig([
- 'server' => ['host' => 'cache.example', 'port' => 6380],
- ]);
-
- $this->assertSame([
- 'scheme' => 'tcp',
- 'host' => 'cache.example',
- 'port' => 6380,
- ], $parameters);
- $this->assertSame([], $options);
- }
-
- public function testSingleServerDefaultPort(): void {
- [$parameters] = $this->factory->buildConnectionConfig([
- 'server' => ['host' => 'cache.example'],
- ]);
-
- $this->assertSame(6379, $parameters['port']);
- }
-
- public function testSingleServerUnixSocketFromProtocol(): void {
- [$parameters] = $this->factory->buildConnectionConfig([
- 'server' => ['host' => '/tmp/cache.sock', 'protocol' => 'unix'],
- ]);
-
- $this->assertSame('unix', $parameters['scheme']);
- $this->assertSame('/tmp/cache.sock', $parameters['path']);
- $this->assertArrayNotHasKey('port', $parameters);
- $this->assertArrayNotHasKey('host', $parameters);
- }
-
- public function testSingleServerUnixSocketInferredFromHost(): void {
- [$parameters] = $this->factory->buildConnectionConfig([
- 'server' => ['host' => '/tmp/cache.sock'],
- ]);
-
- $this->assertSame('unix', $parameters['scheme']);
- $this->assertSame('/tmp/cache.sock', $parameters['path']);
- }
-
- public function testSingleServerTls(): void {
- $sslContext = ['cafile' => '/certs/ca.crt'];
- [$parameters] = $this->factory->buildConnectionConfig([
- 'server' => ['host' => 'cache.example', 'protocol' => 'tls'],
- 'ssl_context' => $sslContext,
- ]);
-
- $this->assertSame('tls', $parameters['scheme']);
- $this->assertSame($sslContext, $parameters['ssl']);
- }
-
- public function testSslContextIgnoredWithoutTls(): void {
- [$parameters] = $this->factory->buildConnectionConfig([
- 'server' => ['host' => 'cache.example'],
- 'ssl_context' => ['cafile' => '/certs/ca.crt'],
- ]);
-
- $this->assertArrayNotHasKey('ssl', $parameters);
- }
-
- public function testSingleServerWithDbIndex(): void {
- [$parameters] = $this->factory->buildConnectionConfig([
- 'server' => ['host' => 'cache.example'],
- 'dbindex' => 5,
- ]);
-
- $this->assertSame(5, $parameters['database']);
- }
-
- public function testAuthWithUserAndPassword(): void {
- [$parameters] = $this->factory->buildConnectionConfig([
- 'server' => ['host' => 'cache.example'],
- 'user' => 'nextcloud',
- 'password' => 's3cret',
- ]);
-
- $this->assertSame('nextcloud', $parameters['username']);
- $this->assertSame('s3cret', $parameters['password']);
- }
-
- public function testAuthWithPasswordOnly(): void {
- [$parameters] = $this->factory->buildConnectionConfig([
- 'server' => ['host' => 'cache.example'],
- 'password' => 's3cret',
- ]);
-
- $this->assertSame('s3cret', $parameters['password']);
- $this->assertArrayNotHasKey('username', $parameters);
- }
-
- public function testEmptyPasswordIsIgnored(): void {
- [$parameters] = $this->factory->buildConnectionConfig([
- 'server' => ['host' => 'cache.example'],
- 'user' => 'nextcloud',
- 'password' => '',
- ]);
-
- $this->assertArrayNotHasKey('password', $parameters);
- $this->assertArrayNotHasKey('username', $parameters);
- }
-
- public function testTimeoutAndPersistence(): void {
- [$parameters] = $this->factory->buildConnectionConfig([
- 'server' => ['host' => 'cache.example'],
- 'timeout' => 1.5,
- 'read_timeout' => 2.5,
- 'persistent' => true,
- ]);
-
- $this->assertSame(1.5, $parameters['timeout']);
- $this->assertSame(2.5, $parameters['read_write_timeout']);
- $this->assertTrue($parameters['persistent']);
- }
-
- public function testMissingServerThrows(): void {
- $this->expectException(\RuntimeException::class);
- $this->factory->buildConnectionConfig([]);
- }
-
- public function testCluster(): void {
- [$parameters, $options] = $this->factory->buildConnectionConfig([
- 'seeds' => [
- ['host' => 'node1', 'port' => 7000],
- ['host' => 'node2', 'port' => 7001],
- ],
- 'password' => 's3cret',
- ]);
-
- $this->assertCount(2, $parameters);
- $this->assertSame('node1', $parameters[0]['host']);
- $this->assertSame(7000, $parameters[0]['port']);
- $this->assertSame('node2', $parameters[1]['host']);
- $this->assertSame('redis', $options['cluster']);
- // Authentication is propagated to the nodes discovered in the cluster
- $this->assertSame('s3cret', $options['parameters']['password']);
- }
-
- public function testClusterEmptySeedsThrows(): void {
- $this->expectException(\RuntimeException::class);
- $this->factory->buildConnectionConfig(['seeds' => []]);
- }
-
- public function testSentinel(): void {
- [$parameters, $options] = $this->factory->buildConnectionConfig([
- 'sentinel' => [
- 'service' => 'mymaster',
- 'seeds' => [
- ['host' => 'sentinel1', 'port' => 26379],
- ['host' => 'sentinel2', 'port' => 26380],
- ],
- ],
- 'password' => 's3cret',
- 'dbindex' => 3,
- ]);
-
- $this->assertCount(2, $parameters);
- $this->assertSame('sentinel1', $parameters[0]['host']);
- $this->assertSame(26379, $parameters[0]['port']);
- $this->assertSame('sentinel', $options['replication']);
- $this->assertSame('mymaster', $options['service']);
- // Auth and database are applied to the resolved primary / replica
- $this->assertSame('s3cret', $options['parameters']['password']);
- $this->assertSame(3, $options['parameters']['database']);
- }
-
- public function testSentinelMissingServiceThrows(): void {
- $this->expectException(\RuntimeException::class);
- $this->factory->buildConnectionConfig([
- 'sentinel' => ['seeds' => [['host' => 'sentinel1', 'port' => 26379]]],
- ]);
- }
-
- public function testSentinelMissingSeedsThrows(): void {
- $this->expectException(\RuntimeException::class);
- $this->factory->buildConnectionConfig([
- 'sentinel' => ['service' => 'mymaster'],
- ]);
- }
-
- public function testIsAvailableWithoutConfig(): void {
- $this->config->method('getValue')->with('memcache.kvstore', [])->willReturn([]);
- $this->assertFalse($this->factory->isAvailable());
- }
-
- public function testIsAvailableWithConfig(): void {
- $this->config->method('getValue')->with('memcache.kvstore', [])
- ->willReturn(['server' => ['host' => 'localhost']]);
- $this->assertTrue($this->factory->isAvailable());
- }
-
- public function testGetInstanceThrowsWhenUnavailable(): void {
- $this->config->method('getValue')->with('memcache.kvstore', [])->willReturn([]);
- $this->expectException(\RuntimeException::class);
- $this->factory->getInstance();
- }
-
- public function testGetInstanceSingleServer(): void {
- $this->config->method('getValue')->with('memcache.kvstore', [])
- ->willReturn(['server' => ['host' => 'localhost', 'port' => 6379]]);
-
- $client = $this->factory->getInstance();
- $this->assertInstanceOf(Client::class, $client);
- $this->assertInstanceOf(NodeConnectionInterface::class, $client->getConnection());
- }
-
- public function testGetInstanceCluster(): void {
- $this->config->method('getValue')->with('memcache.kvstore', [])
- ->willReturn(['seeds' => [['host' => 'localhost', 'port' => 7000]]]);
-
- $client = $this->factory->getInstance();
- $this->assertInstanceOf(ClusterInterface::class, $client->getConnection());
- }
-
- public function testGetInstanceSentinel(): void {
- $this->config->method('getValue')->with('memcache.kvstore', [])
- ->willReturn([
- 'sentinel' => [
- 'service' => 'mymaster',
- 'seeds' => [['host' => 'localhost', 'port' => 26379]],
- ],
- ]);
-
- $client = $this->factory->getInstance();
- $this->assertInstanceOf(SentinelReplication::class, $client->getConnection());
- }
-
- public function testGetInstanceIsMemoized(): void {
- $this->config->method('getValue')->with('memcache.kvstore', [])
- ->willReturn(['server' => ['host' => 'localhost']]);
-
- $this->assertSame($this->factory->getInstance(), $this->factory->getInstance());
- }
-}
--- a/tests/lib/Memcache/KeyValueCacheTest.php
+++ /dev/null
@@ -1,108 +0,0 @@
-getSystemValue('memcache.kvstore', []) === []) {
- self::markTestSkipped('Key-value store not configured in config.php');
- }
-
- if (!KeyValueCache::isAvailable()) {
- self::markTestSkipped('The predis library is not available.');
- }
-
- try {
- $instance = new KeyValueCache(self::getUniqueID());
- if ($instance->set(self::getUniqueID(), self::getUniqueID()) === false) {
- self::markTestSkipped('Key-value store server seems to be down.');
- }
- } catch (CommunicationException $e) {
- self::markTestSkipped('Key-value store server is not reachable: ' . $e->getMessage());
- }
- }
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
- $this->instance = new KeyValueCache($this->getUniqueID());
- }
-
- /**
- * @return array
- */
- public static function roundtripValuesProvider(): array {
- return [
- 'string' => ['some string value'],
- 'empty string' => [''],
- 'numeric string' => ['0123'],
- 'integer' => [1234],
- 'zero' => [0],
- 'negative integer' => [-42],
- 'boolean true' => [true],
- 'boolean false' => [false],
- 'null' => [null],
- 'list' => [['a', 'b', 'c']],
- 'associative array' => [['foo' => 'bar', 'baz' => 42]],
- 'nested array' => [['a' => ['b' => ['c' => 'd']]]],
- ];
- }
-
- /**
- * A value that is written to the cache must be readable again unchanged.
- *
- * Runs against every configured topology (single server, Sentinel and
- * cluster) through the CI matrix, see .github/workflows/phpunit-kvstore.yml.
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('roundtripValuesProvider')]
- public function testStoreAndReadBack(mixed $value): void {
- $this->assertNull($this->instance->get('roundtrip'), 'key should be empty before storing');
-
- $this->assertNotFalse($this->instance->set('roundtrip', $value), 'value should be stored');
- $this->assertTrue($this->instance->hasKey('roundtrip'), 'stored key should exist');
- $this->assertEquals($value, $this->instance->get('roundtrip'), 'stored value should be read back unchanged');
- }
-
- public function testScriptHashes(): void {
- $scripts = self::invokePrivate($this->instance, 'LUA_SCRIPTS');
- foreach ($scripts as $script) {
- $this->assertEquals(sha1($script[0]), $script[1]);
- }
- }
-
- public function testCasTtlNotChanged(): void {
- $this->instance->set('foo', 'bar', 50);
- $this->assertTrue($this->instance->compareSetTTL('foo', 'bar', 100));
- // allow for 1s of inaccuracy due to time moving forward
- $this->assertLessThan(1, 100 - $this->instance->getTTL('foo'));
- }
-
- public function testCasTtlChanged(): void {
- $this->instance->set('foo', 'bar1', 50);
- $this->assertFalse($this->instance->compareSetTTL('foo', 'bar', 100));
- // allow for 1s of inaccuracy due to time moving forward
- $this->assertLessThan(1, 50 - $this->instance->getTTL('foo'));
- }
-}
--- a/tests/lib/Memcache/MemcachedTest.php
+++ /dev/null
@@ -1,58 +0,0 @@
-set(self::getUniqueID(), self::getUniqueID()) === false) {
- self::markTestSkipped('memcached server seems to be down.');
- }
- }
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
- $this->instance = new Memcached($this->getUniqueID());
- }
-
- #[\Override]
- public function testClear(): void {
- // Memcached is sometimes broken with clear(), so we don't test it thoroughly
- $value = 'ipsum lorum';
- $this->instance->set('1_value1', $value);
- $this->instance->set('1_value2', $value);
- $this->instance->set('2_value1', $value);
- $this->instance->set('3_value1', $value);
-
- $this->assertTrue($this->instance->clear('1_'));
-
- $this->assertFalse($this->instance->hasKey('1_value1'));
- $this->assertFalse($this->instance->hasKey('1_value2'));
- //$this->assertTrue($this->instance->hasKey('2_value1'));
- //$this->assertTrue($this->instance->hasKey('3_value1'));
-
- $this->assertTrue($this->instance->clear());
-
- $this->assertFalse($this->instance->hasKey('1_value1'));
- $this->assertFalse($this->instance->hasKey('1_value2'));
- $this->assertFalse($this->instance->hasKey('2_value1'));
- $this->assertFalse($this->instance->hasKey('3_value1'));
- }
-}
--- a/tests/lib/Memcache/RedisTest.php
+++ /dev/null
@@ -1,120 +0,0 @@
-getSystemValue('redis', []) === []) {
- self::markTestSkipped('Redis not configured in config.php');
- }
-
- $errorOccurred = false;
- set_error_handler(
- function ($errno, $errstr): void {
- throw new \RuntimeException($errstr, 123456789);
- },
- E_WARNING
- );
- $instance = null;
- try {
- $instance = new Redis(self::getUniqueID());
- } catch (\RuntimeException $e) {
- $errorOccurred = $e->getCode() === 123456789 ? $e->getMessage() : false;
- }
- restore_error_handler();
- if ($errorOccurred !== false) {
- self::markTestSkipped($errorOccurred);
- }
-
- if ($instance === null) {
- throw new \Exception('redis server is not reachable');
- }
-
- if ($instance->set(self::getUniqueID(), self::getUniqueID()) === false) {
- self::markTestSkipped('redis server seems to be down.');
- }
- }
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
- $this->instance = new Redis($this->getUniqueID());
- }
-
- public function testScriptHashes(): void {
- foreach (Redis::LUA_SCRIPTS as $script) {
- $this->assertEquals(sha1($script[0]), $script[1]);
- }
- }
-
- public function testCasTtlNotChanged(): void {
- $this->instance->set('foo', 'bar', 50);
- $this->assertTrue($this->instance->compareSetTTL('foo', 'bar', 100));
- // allow for 1s of inaccuracy due to time moving forward
- $this->assertLessThan(1, 100 - $this->instance->getTTL('foo'));
- }
-
- public function testCasTtlChanged(): void {
- $this->instance->set('foo', 'bar1', 50);
- $this->assertFalse($this->instance->compareSetTTL('foo', 'bar', 100));
- // allow for 1s of inaccuracy due to time moving forward
- $this->assertLessThan(1, 50 - $this->instance->getTTL('foo'));
- }
-
- public function testClearWithPrefixOnlyRemovesMatchingKeys(): void {
- $this->instance->set('foo1', 'a');
- $this->instance->set('foo2', 'b');
- $this->instance->set('bar1', 'c');
-
- $this->assertTrue($this->instance->clear('foo'));
-
- $this->assertFalse($this->instance->hasKey('foo1'));
- $this->assertFalse($this->instance->hasKey('foo2'));
- $this->assertTrue($this->instance->hasKey('bar1'));
- }
-
- public function testClearWithoutMatchesReturnsTrue(): void {
- // Nothing is stored under this prefix; clearing must not error out
- // (regression guard for calling UNLINK/DEL with an empty key list).
- $this->assertTrue($this->instance->clear('no-such-prefix'));
- }
-
- public function testClearRemovesEntriesAcrossMultipleScanBatches(): void {
- // More keys than a single SCAN batch (self::SCAN_COUNT) to exercise the
- // cursor loop and make sure nothing is left behind.
- $count = 1500;
- for ($i = 0; $i < $count; $i++) {
- $this->instance->set('bulk-' . $i, $i);
- }
-
- $this->assertTrue($this->instance->clear('bulk-'));
-
- $this->assertFalse($this->instance->hasKey('bulk-0'));
- $this->assertFalse($this->instance->hasKey('bulk-' . ($count - 1)));
- $this->assertFalse($this->instance->hasKey('bulk-' . intdiv($count, 2)));
- }
-}
--- a/tests/lib/MemoryInfoTest.php
+++ /dev/null
@@ -1,85 +0,0 @@
-iniSettingBeforeTest = ini_get('memory_limit');
- }
-
- #[\PHPUnit\Framework\Attributes\AfterClass()]
- public function restoreMemoryInfoIniSetting() {
- ini_set('memory_limit', $this->iniSettingBeforeTest);
- }
-
- public static function getMemoryLimitTestData(): array {
- return [
- 'unlimited' => ['-1', -1,],
- '524288000 bytes' => ['524288000', 524288000,],
- '500M' => ['500M', 524288000,],
- '512000K' => ['512000K', 524288000,],
- '2G' => ['2G', 2147483648,],
- ];
- }
-
- /**
- * Tests that getMemoryLimit works as expected.
- *
- * @param string $iniValue The "memory_limit" ini data.
- * @param int|float $expected The expected detected memory limit.
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('getMemoryLimitTestData')]
- public function testMemoryLimit(string $iniValue, int|float $expected): void {
- ini_set('memory_limit', $iniValue);
- $memoryInfo = new MemoryInfo();
- self::assertEquals($expected, $memoryInfo->getMemoryLimit());
- }
-
- public static function getSufficientMemoryTestData(): array {
- return [
- 'unlimited' => [-1, true,],
- '512M' => [512 * 1024 * 1024, true,],
- '1G' => [1024 * 1024 * 1024, true,],
- '256M' => [256 * 1024 * 1024, false,],
- ];
- }
-
- /**
- * Tests that isMemoryLimitSufficient returns the correct values.
- *
- * @param int $memoryLimit The memory limit
- * @param bool $expected If the memory limit is sufficient.
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('getSufficientMemoryTestData')]
- public function testIsMemoryLimitSufficient(int $memoryLimit, bool $expected): void {
- /* @var MemoryInfo|MockObject $memoryInfo */
- $memoryInfo = $this->getMockBuilder(MemoryInfo::class)
- ->onlyMethods(['getMemoryLimit',])
- ->getMock();
-
- $memoryInfo
- ->method('getMemoryLimit')
- ->willReturn($memoryLimit);
-
- $isMemoryLimitSufficient = $memoryInfo->isMemoryLimitSufficient();
- self::assertEquals($expected, $isMemoryLimitSufficient);
- }
-}
--- a/tests/lib/Migration/BackgroundRepairTest.php
+++ /dev/null
@@ -1,105 +0,0 @@
-jobList = $this->createMock(JobList::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->dispatcher = $this->createMock(IEventDispatcher::class);
- $this->time = $this->createMock(ITimeFactory::class);
- $this->time->method('getTime')
- ->willReturn(999999);
- $this->appManager = $this->createMock(IAppManager::class);
- $this->repair = new Repair($this->dispatcher, $this->logger);
- $this->job = new BackgroundRepair($this->repair, $this->time, $this->logger, $this->jobList, $this->appManager);
- }
-
- public function testNoArguments(): void {
- $this->jobList->expects($this->once())->method('remove');
- $this->job->start($this->jobList);
- }
-
- public function testUnknownStep(): void {
- $this->dispatcher->expects($this->never())->method('dispatchTyped');
-
- $this->jobList->expects($this->once())->method('remove');
- $this->logger->expects($this->once())->method('error');
-
- $this->job->setArgument([
- 'app' => 'test',
- 'step' => 'j'
- ]);
- $this->job->start($this->jobList);
- }
-
- public function testWorkingStep(): void {
- $this->dispatcher->expects($this->once())->method('dispatchTyped')
- ->with($this->equalTo(new RepairStepEvent('A test repair step')));
-
- $this->jobList->expects($this->once())->method('remove');
- $this->appManager->expects(self::once())
- ->method('loadApp')
- ->with('test');
-
- $this->job->setArgument([
- 'app' => 'test',
- 'step' => '\Test\Migration\TestRepairStep'
- ]);
- $this->job->start($this->jobList);
- }
-}
--- a/tests/lib/Migration/MetadataManagerTest.php
+++ /dev/null
@@ -1,197 +0,0 @@
-appManager = Server::get(IAppManager::class);
- }
-
- public function testExtractMigrationAttributes(): void {
- $metadataManager = Server::get(MetadataManager::class);
- $this->appManager->loadApp('testing');
-
- $this->assertEquals(
- self::getMigrationMetadata(),
- json_decode(json_encode($metadataManager->extractMigrationAttributes('testing')), true),
- );
-
- $this->appManager->disableApp('testing');
- }
-
- public function testDeserializeMigrationMetadata(): void {
- $metadataManager = Server::get(MetadataManager::class);
- $this->assertEquals(
- [
- 'core' => [],
- 'apps' => [
- 'testing' => [
- '30000Date20240102030405' => [
- new DropTable('old_table'),
- new CreateTable('new_table',
- description: 'Table is used to store things, but also to get more things',
- notes: ['this is a notice', 'and another one, if really needed']
- ),
- new AddColumn('my_table'),
- new AddColumn('my_table', 'another_field'),
- new AddColumn('other_table', 'last_one', ColumnType::DATE),
- new AddIndex('my_table'),
- new AddIndex('my_table', IndexType::PRIMARY),
- new DropColumn('other_table'),
- new DropColumn('other_table', 'old_column',
- description: 'field is not used anymore and replaced by \'last_one\''
- ),
- new DropIndex('other_table'),
- new ModifyColumn('other_table'),
- new ModifyColumn('other_table', 'this_field'),
- new ModifyColumn('other_table', 'this_field', ColumnType::BIGINT)
- ]
- ]
- ]
- ],
- $metadataManager->getMigrationsAttributesFromReleaseMetadata(
- [
- 'core' => [],
- 'apps' => ['testing' => self::getMigrationMetadata()]
- ]
- )
- );
- }
-
- private static function getMigrationMetadata(): array {
- return [
- '30000Date20240102030405' => [
- [
- 'class' => 'OCP\\Migration\\Attributes\\DropTable',
- 'table' => 'old_table',
- 'description' => '',
- 'notes' => [],
- 'columns' => []
- ],
- [
- 'class' => 'OCP\\Migration\\Attributes\\CreateTable',
- 'table' => 'new_table',
- 'description' => 'Table is used to store things, but also to get more things',
- 'notes'
- => [
- 'this is a notice',
- 'and another one, if really needed'
- ],
- 'columns' => []
- ],
- [
- 'class' => 'OCP\\Migration\\Attributes\\AddColumn',
- 'table' => 'my_table',
- 'description' => '',
- 'notes' => [],
- 'name' => '',
- 'type' => ''
- ],
- [
- 'class' => 'OCP\\Migration\\Attributes\\AddColumn',
- 'table' => 'my_table',
- 'description' => '',
- 'notes' => [],
- 'name' => 'another_field',
- 'type' => ''
- ],
- [
- 'class' => 'OCP\\Migration\\Attributes\\AddColumn',
- 'table' => 'other_table',
- 'description' => '',
- 'notes' => [],
- 'name' => 'last_one',
- 'type' => 'date'
- ],
- [
- 'class' => 'OCP\\Migration\\Attributes\\AddIndex',
- 'table' => 'my_table',
- 'description' => '',
- 'notes' => [],
- 'type' => ''
- ],
- [
- 'class' => 'OCP\\Migration\\Attributes\\AddIndex',
- 'table' => 'my_table',
- 'description' => '',
- 'notes' => [],
- 'type' => 'primary'
- ],
- [
- 'class' => 'OCP\\Migration\\Attributes\\DropColumn',
- 'table' => 'other_table',
- 'description' => '',
- 'notes' => [],
- 'name' => '',
- 'type' => ''
- ],
- [
- 'class' => 'OCP\\Migration\\Attributes\\DropColumn',
- 'table' => 'other_table',
- 'description' => 'field is not used anymore and replaced by \'last_one\'',
- 'notes' => [],
- 'name' => 'old_column',
- 'type' => ''
- ],
- [
- 'class' => 'OCP\\Migration\\Attributes\\DropIndex',
- 'table' => 'other_table',
- 'description' => '',
- 'notes' => [],
- 'type' => ''
- ],
- [
- 'class' => 'OCP\\Migration\\Attributes\\ModifyColumn',
- 'table' => 'other_table',
- 'description' => '',
- 'notes' => [],
- 'name' => '',
- 'type' => ''
- ],
- [
- 'class' => 'OCP\\Migration\\Attributes\\ModifyColumn',
- 'table' => 'other_table',
- 'description' => '',
- 'notes' => [],
- 'name' => 'this_field',
- 'type' => ''
- ],
- [
- 'class' => 'OCP\\Migration\\Attributes\\ModifyColumn',
- 'table' => 'other_table',
- 'description' => '',
- 'notes' => [],
- 'name' => 'this_field',
- 'type' => 'bigint'
- ],
- ]
- ];
- }
-}
--- a/tests/lib/Mock/Config/MockAppConfig.php
+++ /dev/null
@@ -1,200 +0,0 @@
-config[$app][$key]);
- }
-
- #[\Override]
- public function getValues($app, $key): array {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function getFilteredValues($app): array {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function getApps(): array {
- return array_keys($this->config);
- }
-
- #[\Override]
- public function getKeys(string $app): array {
- return array_keys($this->config[$app] ?? []);
- }
-
- #[\Override]
- public function isSensitive(string $app, string $key, ?bool $lazy = false): bool {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function isLazy(string $app, string $key): bool {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function getAllValues(string $app, string $prefix = '', bool $filtered = false): array {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function searchValues(string $key, bool $lazy = false, ?int $typedAs = null): array {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function getValueString(string $app, string $key, string $default = '', bool $lazy = false): string {
- return (string)(($this->config[$app] ?? [])[$key] ?? $default);
- }
-
- #[\Override]
- public function getValueInt(string $app, string $key, int $default = 0, bool $lazy = false): int {
- return (int)(($this->config[$app] ?? [])[$key] ?? $default);
- }
-
- #[\Override]
- public function getValueFloat(string $app, string $key, float $default = 0, bool $lazy = false): float {
- return (float)(($this->config[$app] ?? [])[$key] ?? $default);
- }
-
- #[\Override]
- public function getValueBool(string $app, string $key, bool $default = false, bool $lazy = false): bool {
- return (bool)(($this->config[$app] ?? [])[$key] ?? $default);
- }
-
- #[\Override]
- public function getValueArray(string $app, string $key, array $default = [], bool $lazy = false): array {
- return ($this->config[$app] ?? [])[$key] ?? $default;
- }
-
- #[\Override]
- public function getValueType(string $app, string $key, ?bool $lazy = null): int {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function setValueString(string $app, string $key, string $value, bool $lazy = false, bool $sensitive = false): bool {
- $this->config[$app][$key] = $value;
- return true;
- }
-
- #[\Override]
- public function setValueInt(string $app, string $key, int $value, bool $lazy = false, bool $sensitive = false): bool {
- $this->config[$app][$key] = $value;
- return true;
- }
-
- #[\Override]
- public function setValueFloat(string $app, string $key, float $value, bool $lazy = false, bool $sensitive = false): bool {
- $this->config[$app][$key] = $value;
- return true;
- }
-
- #[\Override]
- public function setValueBool(string $app, string $key, bool $value, bool $lazy = false): bool {
- $this->config[$app][$key] = $value;
- return true;
- }
-
- #[\Override]
- public function setValueArray(string $app, string $key, array $value, bool $lazy = false, bool $sensitive = false): bool {
- $this->config[$app][$key] = $value;
- return true;
- }
-
- #[\Override]
- public function updateSensitive(string $app, string $key, bool $sensitive): bool {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function updateLazy(string $app, string $key, bool $lazy): bool {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function getDetails(string $app, string $key): array {
- throw new \Exception('not implemented');
- }
-
- #[\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 {
- if ($this->hasKey($app, $key)) {
- unset($this->config[$app][$key]);
- }
- }
-
- #[\Override]
- public function deleteApp(string $app): void {
- if (isset($this->config[$app])) {
- unset($this->config[$app]);
- }
- }
-
- #[\Override]
- public function clearCache(bool $reload = false): void {
- }
-
- #[\Override]
- public function searchKeys(string $app, string $prefix = '', bool $lazy = false): array {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function getKeyDetails(string $app, string $key): array {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function getAppInstalledVersions(bool $onlyEnabled = false): array {
- throw new \Exception('not implemented');
- }
-}
--- a/tests/lib/Mock/Config/MockUserConfig.php
+++ /dev/null
@@ -1,249 +0,0 @@
-config);
- }
-
- #[\Override]
- public function getApps(string $userId): array {
- return array_keys($this->config[$userId] ?? []);
- }
-
- #[\Override]
- public function getKeys(string $userId, string $app): array {
- if (isset($this->config[$userId][$app])) {
- return array_keys($this->config[$userId][$app]);
- } else {
- return [];
- }
- }
-
- #[\Override]
- public function hasKey(string $userId, string $app, string $key, ?bool $lazy = false): bool {
- return isset($this->config[$userId][$app][$key]);
- }
-
- #[\Override]
- public function isSensitive(string $userId, string $app, string $key, ?bool $lazy = false): bool {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function isIndexed(string $userId, string $app, string $key, ?bool $lazy = false): bool {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function isLazy(string $userId, string $app, string $key): bool {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function getValues(string $userId, string $app, string $prefix = '', bool $filtered = false): array {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function getAllValues(string $userId, bool $filtered = false): array {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function getValuesByApps(string $userId, string $key, bool $lazy = false, ?ValueType $typedAs = null): array {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function getValuesByUsers(string $app, string $key, ?ValueType $typedAs = null, ?array $userIds = null): array {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function searchUsersByValueString(string $app, string $key, string $value, bool $caseInsensitive = false): Generator {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function searchUsersByValueInt(string $app, string $key, int $value): Generator {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function searchUsersByValues(string $app, string $key, array $values): Generator {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function searchUsersByValueBool(string $app, string $key, bool $value): Generator {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function getValueString(string $userId, string $app, string $key, string $default = '', bool $lazy = false): string {
- if (isset($this->config[$userId][$app])) {
- return (string)$this->config[$userId][$app][$key];
- } else {
- return $default;
- }
- }
-
- #[\Override]
- public function getValueInt(string $userId, string $app, string $key, int $default = 0, bool $lazy = false): int {
- if (isset($this->config[$userId][$app])) {
- return (int)$this->config[$userId][$app][$key];
- } else {
- return $default;
- }
- }
-
- #[\Override]
- public function getValueFloat(string $userId, string $app, string $key, float $default = 0, bool $lazy = false): float {
- if (isset($this->config[$userId][$app])) {
- return (float)$this->config[$userId][$app][$key];
- } else {
- return $default;
- }
- }
-
- #[\Override]
- public function getValueBool(string $userId, string $app, string $key, bool $default = false, bool $lazy = false): bool {
- if (isset($this->config[$userId][$app])) {
- return (bool)$this->config[$userId][$app][$key];
- } else {
- return $default;
- }
- }
-
- #[\Override]
- public function getValueArray(string $userId, string $app, string $key, array $default = [], bool $lazy = false): array {
- if (isset($this->config[$userId][$app])) {
- return $this->config[$userId][$app][$key];
- } else {
- return $default;
- }
- }
-
- #[\Override]
- public function getValueType(string $userId, string $app, string $key, ?bool $lazy = null): ValueType {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function getValueFlags(string $userId, string $app, string $key, bool $lazy = false): int {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function setValueString(string $userId, string $app, string $key, string $value, bool $lazy = false, int $flags = 0): bool {
- $this->config[$userId][$app][$key] = $value;
- return true;
- }
-
- #[\Override]
- public function setValueInt(string $userId, string $app, string $key, int $value, bool $lazy = false, int $flags = 0): bool {
- $this->config[$userId][$app][$key] = $value;
- return true;
- }
-
- #[\Override]
- public function setValueFloat(string $userId, string $app, string $key, float $value, bool $lazy = false, int $flags = 0): bool {
- $this->config[$userId][$app][$key] = $value;
- return true;
- }
-
- #[\Override]
- public function setValueBool(string $userId, string $app, string $key, bool $value, bool $lazy = false): bool {
- $this->config[$userId][$app][$key] = $value;
- return true;
- }
-
- #[\Override]
- public function setValueArray(string $userId, string $app, string $key, array $value, bool $lazy = false, int $flags = 0): bool {
- $this->config[$userId][$app][$key] = $value;
- return true;
- }
-
- #[\Override]
- public function updateSensitive(string $userId, string $app, string $key, bool $sensitive): bool {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function updateGlobalSensitive(string $app, string $key, bool $sensitive): void {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function updateIndexed(string $userId, string $app, string $key, bool $indexed): bool {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function updateGlobalIndexed(string $app, string $key, bool $indexed): void {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function updateLazy(string $userId, string $app, string $key, bool $lazy): bool {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function updateGlobalLazy(string $app, string $key, bool $lazy): void {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function getDetails(string $userId, string $app, string $key): array {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function deleteUserConfig(string $userId, string $app, string $key): void {
- unset($this->config[$userId][$app][$key]);
- }
-
- #[\Override]
- public function deleteKey(string $app, string $key): void {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function deleteApp(string $app): void {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function deleteAllUserConfig(string $userId): void {
- unset($this->config[$userId]);
- }
-
- #[\Override]
- public function clearCache(string $userId, bool $reload = false): void {
- throw new \Exception('not implemented');
- }
-
- #[\Override]
- public function clearCacheAll(): void {
- throw new \Exception('not implemented');
- }
-}
--- a/tests/lib/NaturalSortTest.php
+++ /dev/null
@@ -1,266 +0,0 @@
-markTestSkipped('The intl module is not available, natural sorting might not work as expected.');
- return;
- }
- $comparator = NaturalSort::getInstance();
- usort($array, [$comparator, 'compare']);
- $this->assertEquals($sorted, $array);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('defaultCollatorDataProvider')]
- public function testDefaultCollatorCompare($array, $sorted): void {
- $comparator = new NaturalSort(new NaturalSort_DefaultCollator());
- usort($array, [$comparator, 'compare']);
- $this->assertEquals($sorted, $array);
- }
-
- /**
- * Data provider for natural sorting with php5-intl's Collator.
- * Must provide the same result as in core/js/tests/specs/coreSpec.js
- * @return array test cases
- */
- public static function naturalSortDataProvider(): array {
- return [
- // different casing
- [
- // unsorted
- [
- 'aaa',
- 'bbb',
- 'BBB',
- 'AAA'
- ],
- // sorted
- [
- 'aaa',
- 'AAA',
- 'bbb',
- 'BBB'
- ]
- ],
- // numbers
- [
- // unsorted
- [
- '124.txt',
- 'abc1',
- '123.txt',
- 'abc',
- 'abc2',
- 'def (2).txt',
- 'ghi 10.txt',
- 'abc12',
- 'def.txt',
- 'def (1).txt',
- 'ghi 2.txt',
- 'def (10).txt',
- 'abc10',
- 'def (12).txt',
- 'z',
- 'ghi.txt',
- 'za',
- 'ghi 1.txt',
- 'ghi 12.txt',
- 'zz',
- '15.txt',
- '15b.txt',
- ],
- // sorted
- [
- '15.txt',
- '15b.txt',
- '123.txt',
- '124.txt',
- 'abc',
- 'abc1',
- 'abc2',
- 'abc10',
- 'abc12',
- 'def.txt',
- 'def (1).txt',
- 'def (2).txt',
- 'def (10).txt',
- 'def (12).txt',
- 'ghi.txt',
- 'ghi 1.txt',
- 'ghi 2.txt',
- 'ghi 10.txt',
- 'ghi 12.txt',
- 'z',
- 'za',
- 'zz',
- ]
- ],
- // chinese characters
- [
- // unsorted
- [
- '十.txt',
- '一.txt',
- '二.txt',
- '十 2.txt',
- '三.txt',
- '四.txt',
- 'abc.txt',
- '五.txt',
- '七.txt',
- '八.txt',
- '九.txt',
- '六.txt',
- '十一.txt',
- '波.txt',
- '破.txt',
- '莫.txt',
- '啊.txt',
- '123.txt',
- ],
- // sorted
- [
- '123.txt',
- 'abc.txt',
- '一.txt',
- '七.txt',
- '三.txt',
- '九.txt',
- '二.txt',
- '五.txt',
- '八.txt',
- '六.txt',
- '十.txt',
- '十 2.txt',
- '十一.txt',
- '啊.txt',
- '四.txt',
- '波.txt',
- '破.txt',
- '莫.txt',
- ]
- ],
- // with umlauts
- [
- // unsorted
- [
- 'öh.txt',
- 'Äh.txt',
- 'oh.txt',
- 'Üh 2.txt',
- 'Üh.txt',
- 'ah.txt',
- 'Öh.txt',
- 'uh.txt',
- 'üh.txt',
- 'äh.txt',
- ],
- // sorted
- [
- 'ah.txt',
- 'äh.txt',
- 'Äh.txt',
- 'oh.txt',
- 'öh.txt',
- 'Öh.txt',
- 'uh.txt',
- 'üh.txt',
- 'Üh.txt',
- 'Üh 2.txt',
- ]
- ],
- ];
- }
-
- /**
- * Data provider for natural sorting with \OC\NaturalSort_DefaultCollator.
- * Must provide the same result as in core/js/tests/specs/coreSpec.js
- * @return array test cases
- */
- public static function defaultCollatorDataProvider(): array {
- return [
- // different casing
- [
- // unsorted
- [
- 'aaa',
- 'bbb',
- 'BBB',
- 'AAA'
- ],
- // sorted
- [
- 'aaa',
- 'AAA',
- 'bbb',
- 'BBB'
- ]
- ],
- // numbers
- [
- // unsorted
- [
- '124.txt',
- 'abc1',
- '123.txt',
- 'abc',
- 'abc2',
- 'def (2).txt',
- 'ghi 10.txt',
- 'abc12',
- 'def.txt',
- 'def (1).txt',
- 'ghi 2.txt',
- 'def (10).txt',
- 'abc10',
- 'def (12).txt',
- 'z',
- 'ghi.txt',
- 'za',
- 'ghi 1.txt',
- 'ghi 12.txt',
- 'zz',
- '15.txt',
- '15b.txt',
- ],
- // sorted
- [
- '15.txt',
- '15b.txt',
- '123.txt',
- '124.txt',
- 'abc',
- 'abc1',
- 'abc2',
- 'abc10',
- 'abc12',
- 'def.txt',
- 'def (1).txt',
- 'def (2).txt',
- 'def (10).txt',
- 'def (12).txt',
- 'ghi.txt',
- 'ghi 1.txt',
- 'ghi 2.txt',
- 'ghi 10.txt',
- 'ghi 12.txt',
- 'z',
- 'za',
- 'zz',
- ]
- ],
- ];
- }
-}
--- a/tests/lib/NavigationManagerTest.php
+++ /dev/null
@@ -1,963 +0,0 @@
-appManager = $this->createMock(AppManager::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->l10nFac = $this->createMock(IFactory::class);
- $this->userSession = $this->createMock(IUserSession::class);
- $this->groupManager = $this->createMock(Manager::class);
- $this->config = $this->createMock(IConfig::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->dispatcher = $this->createMock(IEventDispatcher::class);
- $this->navigationManager = new NavigationManager(
- $this->appManager,
- $this->urlGenerator,
- $this->l10nFac,
- $this->userSession,
- $this->groupManager,
- $this->config,
- $this->logger,
- $this->dispatcher,
- );
-
- $this->navigationManager->clear(false);
- }
-
- public static function addArrayData(): array {
- return [
- [
- 'entry' => [
- 'id' => 'entry id',
- 'name' => 'link text',
- 'order' => 1,
- 'icon' => 'optional',
- 'href' => 'url',
- 'type' => 'settings',
- 'classes' => '',
- 'unread' => 0
- ],
- 'expectedEntry' => [
- 'id' => 'entry id',
- 'name' => 'link text',
- 'order' => 1,
- 'icon' => 'optional',
- 'href' => 'url',
- 'active' => false,
- 'type' => 'settings',
- 'classes' => '',
- 'unread' => 0,
- ]
- ],
- [
- 'entry' => [
- 'id' => 'entry id',
- 'name' => 'link text',
- 'order' => 1,
- //'icon' => 'optional',
- 'href' => 'url',
- 'active' => true,
- 'unread' => 0,
- ],
- 'expectedEntry' => [
- 'id' => 'entry id',
- 'name' => 'link text',
- 'order' => 1,
- 'icon' => '',
- 'href' => 'url',
- 'active' => false,
- 'type' => 'link',
- 'classes' => '',
- 'unread' => 0,
- 'default' => true,
- ]
- ]
- ];
- }
-
- /**
- *
- * @param array $entry
- * @param array $expectedEntry
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('addArrayData')]
- public function testAddArray(array $entry, array $expectedEntry): void {
- $this->assertEmpty($this->navigationManager->getAll('all'), 'Expected no navigation entry exists');
- $this->navigationManager->add($entry);
-
- $navigationEntries = $this->navigationManager->getAll('all');
- $this->assertCount(1, $navigationEntries, 'Expected that 1 navigation entry exists');
- $this->assertEquals($expectedEntry, $navigationEntries['entry id']);
-
- $this->navigationManager->clear(false);
- $this->assertEmpty($this->navigationManager->getAll('all'), 'Expected no navigation entry exists after clear()');
- }
-
- /**
- *
- * @param array $entry
- * @param array $expectedEntry
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('addArrayData')]
- public function testAddClosure(array $entry, array $expectedEntry): void {
- global $testAddClosureNumberOfCalls;
- $testAddClosureNumberOfCalls = 0;
-
- $this->navigationManager->add(function () use ($entry) {
- global $testAddClosureNumberOfCalls;
- $testAddClosureNumberOfCalls++;
-
- return $entry;
- });
-
- $this->assertEquals(0, $testAddClosureNumberOfCalls, 'Expected that the closure is not called by add()');
-
- $this->navigationManager->setup();
- $navigationEntries = $this->navigationManager->getAll('all');
- $this->assertEquals(1, $testAddClosureNumberOfCalls, 'Expected that the closure is called by getAll()');
- $this->assertCount(1, $navigationEntries, 'Expected that 1 navigation entry exists');
- $this->assertEquals($expectedEntry, $navigationEntries['entry id']);
-
- $navigationEntries = $this->navigationManager->getAll('all');
- $this->assertEquals(1, $testAddClosureNumberOfCalls, 'Expected that the closure is only called once for getAll()');
- $this->assertCount(1, $navigationEntries, 'Expected that 1 navigation entry exists');
- $this->assertEquals($expectedEntry, $navigationEntries['entry id']);
-
- $this->navigationManager->clear(false);
- $this->assertEmpty($this->navigationManager->getAll('all'), 'Expected no navigation entry exists after clear()');
- }
-
- /**
- * Entry points that never call setup() (e.g. the OCS dispatch in ocs/v1.php)
- * must not silently lose closure-registered entries such as an app's nav
- * link: getAll() should only resolve what it can, not resolve nothing.
- */
- public function testGetAllDoesNotResolveClosureBeforeSetup(): void {
- $numberOfCalls = 0;
- $this->navigationManager->add(function () use (&$numberOfCalls) {
- $numberOfCalls++;
-
- return [
- 'id' => 'entry id',
- 'name' => 'link text',
- 'order' => 1,
- 'href' => 'url',
- ];
- });
-
- $navigationEntries = $this->navigationManager->getAll('all');
-
- $this->assertEquals(0, $numberOfCalls, 'Expected that the closure is not called by getAll() before setup()');
- $this->assertEmpty($navigationEntries, 'Expected no navigation entry exists before setup()');
-
- $this->navigationManager->setup();
- $navigationEntries = $this->navigationManager->getAll('all');
-
- $this->assertEquals(1, $numberOfCalls, 'Expected that the closure is called by getAll() once setup() has run');
- $this->assertArrayHasKey('entry id', $navigationEntries);
- }
-
- public function testAddClosureAfterSetup(): void {
- $this->navigationManager->setup();
- $this->assertEmpty($this->navigationManager->getAll('all'), 'Expected no navigation entry exists');
-
- $numberOfCalls = 0;
- $this->navigationManager->add(function () use (&$numberOfCalls) {
- $numberOfCalls++;
-
- return [
- 'id' => 'late entry',
- 'name' => 'link text',
- 'order' => 1,
- 'href' => 'url',
- ];
- });
-
- $this->assertEquals(0, $numberOfCalls, 'Expected that the closure is not called by add()');
-
- $navigationEntries = $this->navigationManager->getAll('all');
- $this->assertEquals(1, $numberOfCalls, 'Expected that the closure added after setup() is called by getAll()');
- $this->assertCount(1, $navigationEntries, 'Expected that 1 navigation entry exists');
- $this->assertArrayHasKey('late entry', $navigationEntries);
-
- $navigationEntries = $this->navigationManager->getAll('all');
- $this->assertEquals(1, $numberOfCalls, 'Expected that the closure is only called once');
- $this->assertCount(1, $navigationEntries, 'Expected that 1 navigation entry exists');
- $this->assertArrayHasKey('late entry', $navigationEntries);
- }
-
- public function testGetAllFiltersActions(): void {
- $this->navigationManager->add([
- 'id' => 'files',
- 'name' => 'Files',
- 'order' => 1,
- 'href' => 'url',
- ]);
- $this->navigationManager->add([
- 'id' => 'logout',
- 'name' => 'Log out',
- 'order' => 2,
- 'href' => 'url',
- 'type' => INavigationManager::TYPE_ACTION,
- ]);
-
- $this->assertEquals(['logout'], array_keys($this->navigationManager->getAll(INavigationManager::TYPE_ACTION)));
- $this->assertEquals(['files'], array_keys($this->navigationManager->getAll(INavigationManager::TYPE_APPS)));
- $this->assertEquals(['files', 'logout'], array_keys($this->navigationManager->getAll(INavigationManager::TYPE_ALL)));
- }
-
- public function testAddArrayClearGetAll(): void {
- $entry = [
- 'id' => 'entry id',
- 'name' => 'link text',
- 'order' => 1,
- 'icon' => 'optional',
- 'href' => 'url'
- ];
-
- $this->assertEmpty($this->navigationManager->getAll(), 'Expected no navigation entry exists');
- $this->navigationManager->add($entry);
- $this->navigationManager->clear(false);
- $this->assertEmpty($this->navigationManager->getAll(), 'Expected no navigation entry exists after clear()');
- }
-
- public function testAddClosureClearGetAll(): void {
- $this->assertEmpty($this->navigationManager->getAll(), 'Expected no navigation entry exists');
-
- $entry = [
- 'id' => 'entry id',
- 'name' => 'link text',
- 'order' => 1,
- 'icon' => 'optional',
- 'href' => 'url'
- ];
-
- global $testAddClosureNumberOfCalls;
- $testAddClosureNumberOfCalls = 0;
-
- $this->navigationManager->add(function () use ($entry) {
- global $testAddClosureNumberOfCalls;
- $testAddClosureNumberOfCalls++;
-
- return $entry;
- });
-
- $this->assertEquals(0, $testAddClosureNumberOfCalls, 'Expected that the closure is not called by add()');
- $this->navigationManager->clear(false);
- $this->assertEquals(0, $testAddClosureNumberOfCalls, 'Expected that the closure is not called by clear()');
- $this->assertEmpty($this->navigationManager->getAll(), 'Expected no navigation entry exists after clear()');
- $this->assertEquals(0, $testAddClosureNumberOfCalls, 'Expected that the closure is not called by getAll()');
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providesNavigationConfig')]
- public function testWithAppManager($expected, $navigation, $isAdmin = false): void {
- $l = $this->createMock(IL10N::class);
- $l->expects($this->any())->method('t')->willReturnCallback(function ($text, $parameters = []) {
- return vsprintf($text, $parameters);
- });
-
- /* Return default value */
- $this->config->method('getUserValue')
- ->willReturnArgument(3);
-
- $this->appManager->expects($this->any())
- ->method('isEnabledForUser')
- ->with('theming')
- ->willReturn(true);
- $this->appManager->expects($this->once())
- ->method('getAppInfo')
- ->with('test')
- ->willReturn($navigation);
- $this->appManager->expects($this->any())
- ->method('isAppLoaded')
- ->willReturnMap([
- ['test', true],
- ['files', true],
- ]);
- $this->urlGenerator->expects($this->any())
- ->method('imagePath')
- ->willReturnCallback(function ($appName, $file) {
- return "/apps/$appName/img/$file";
- });
- $this->appManager->expects($this->any())
- ->method('getAppIcon')
- ->willReturnCallback(fn (string $appName) => "/apps/$appName/img/app.svg");
- $this->l10nFac->expects($this->any())->method('get')->willReturn($l);
- $this->urlGenerator->expects($this->any())->method('linkToRoute')->willReturnCallback(function ($route) {
- if ($route === 'core.login.logout') {
- return 'https://example.com/logout';
- }
- return '/apps/test/';
- });
- $user = $this->createMock(IUser::class);
- $user->expects($this->any())->method('getUID')->willReturn('user001');
- $this->userSession->expects($this->any())->method('getUser')->willReturn($user);
- $this->userSession->expects($this->any())->method('isLoggedIn')->willReturn(true);
- $this->appManager->expects($this->any())
- ->method('getEnabledAppsForUser')
- ->with($user)
- ->willReturn(['test']);
- $this->groupManager->expects($this->any())->method('isAdmin')->willReturn($isAdmin);
-
- $this->navigationManager->clear();
- $this->dispatcher->expects($this->atLeastOnce())
- ->method('dispatchTyped')
- ->willReturnCallback(function ($event): void {
- $this->assertInstanceOf(LoadAdditionalEntriesEvent::class, $event);
- });
- $this->navigationManager->setup();
- $entries = $this->navigationManager->getAll('all');
- $this->assertEquals($expected, $entries);
- }
-
- public static function providesNavigationConfig(): array {
- return [
- 'minimalistic' => [
- ['test' => [
- 'id' => 'test',
- 'order' => 100,
- 'href' => '/apps/test/',
- 'icon' => '/apps/test/img/app.svg',
- 'name' => 'Test',
- 'active' => false,
- 'type' => 'link',
- 'classes' => '',
- 'unread' => 0,
- 'default' => true,
- 'app' => 'test',
- ]],
- ['navigations' => [
- 'navigation' => [
- ['route' => 'test.page.index', 'name' => 'Test']
- ]
- ]]
- ],
- 'minimalistic-settings' => [
- ['test' => [
- 'id' => 'test',
- 'order' => 100,
- 'href' => '/apps/test/',
- 'icon' => '/apps/test/img/app.svg',
- 'name' => 'Test',
- 'active' => false,
- 'type' => 'settings',
- 'classes' => '',
- 'unread' => 0,
- ]],
- ['navigations' => [
- 'navigation' => [
- ['route' => 'test.page.index', 'name' => 'Test', 'type' => 'settings']
- ],
- ]]
- ],
- 'with-multiple' => [
- ['test' => [
- 'id' => 'test',
- 'order' => 100,
- 'href' => '/apps/test/',
- 'icon' => '/apps/test/img/app.svg',
- 'name' => 'Test',
- 'active' => false,
- 'type' => 'link',
- 'classes' => '',
- 'unread' => 0,
- 'default' => false,
- 'app' => 'test',
- ],
- 'test1' => [
- 'id' => 'test1',
- 'order' => 50,
- 'href' => '/apps/test/',
- 'icon' => '/apps/test/img/app.svg',
- 'name' => 'Other test',
- 'active' => false,
- 'type' => 'link',
- 'classes' => '',
- 'unread' => 0,
- 'default' => true, // because of order
- 'app' => 'test',
- ]],
- ['navigations' => [
- 'navigation' => [
- ['route' => 'test.page.index', 'name' => 'Test'],
- ['route' => 'test.page.index', 'name' => 'Other test', 'order' => 50],
- ]
- ]]
- ],
- 'admin' => [
- ['test' => [
- 'id' => 'test',
- 'order' => 100,
- 'href' => '/apps/test/',
- 'icon' => '/apps/test/img/app.svg',
- 'name' => 'Test',
- 'active' => false,
- 'type' => 'link',
- 'classes' => '',
- 'unread' => 0,
- 'default' => true,
- 'app' => 'test',
- ]],
- ['navigations' => [
- 'navigation' => [
- ['@attributes' => ['role' => 'admin'], 'route' => 'test.page.index', 'name' => 'Test']
- ],
- ]],
- true
- ],
- 'no name' => [
- [], // nothing because the entry is not added because it has no name
- ['navigations' => [
- 'navigation' => [
- ['@attributes' => ['role' => 'admin'], 'route' => 'test.page.index']
- ],
- ]],
- true
- ],
- 'no admin' => [
- [], // nothing because user is not an admin
- ['navigations' => [
- 'navigation' => [
- ['@attributes' => ['role' => 'admin'], 'route' => 'test.page.index', 'name' => 'Test']
- ],
- ]],
- false,
- ]
- ];
- }
-
- public function testWithAppManagerAndApporder(): void {
- $l = $this->createMock(IL10N::class);
- $l->expects($this->any())->method('t')->willReturnCallback(function ($text, $parameters = []) {
- return vsprintf($text, $parameters);
- });
-
- $testOrder = 12;
- $expected = [
- 'test' => [
- 'type' => 'link',
- 'id' => 'test',
- 'order' => $testOrder,
- 'href' => '/apps/test/',
- 'name' => 'Test',
- 'icon' => '/apps/test/img/app.svg',
- 'active' => false,
- 'classes' => '',
- 'unread' => 0,
- 'default' => true,
- 'app' => 'test',
- ],
- ];
- $navigation = ['navigations' => [
- 'navigation' => [
- ['route' => 'test.page.index', 'name' => 'Test']
- ],
- ]];
-
- $this->config->method('getUserValue')
- ->willReturnCallback(
- function (string $userId, string $appName, string $key, mixed $default = '') use ($testOrder) {
- $this->assertEquals('user001', $userId);
- if ($key === 'apporder') {
- return json_encode(['test' => ['app' => 'test', 'order' => $testOrder]]);
- }
- return $default;
- }
- );
-
- $this->appManager->expects($this->any())
- ->method('isEnabledForUser')
- ->with('theming')
- ->willReturn(true);
- $this->appManager->expects($this->once())
- ->method('getAppIcon')
- ->with('test')
- ->willReturn('/apps/test/img/app.svg');
- $this->appManager->expects($this->once())
- ->method('getAppInfo')
- ->with('test')
- ->willReturn($navigation);
- $this->appManager->expects($this->atLeastOnce())
- ->method('isAppLoaded')
- ->willReturnMap([
- ['test', true],
- ['files', true],
- ]);
- $this->l10nFac->expects($this->any())->method('get')->willReturn($l);
- $this->urlGenerator->expects($this->any())->method('imagePath')->willReturnCallback(function ($appName, $file) {
- return "/apps/$appName/img/$file";
- });
- $this->urlGenerator->expects($this->any())->method('linkToRoute')->willReturnCallback(function ($route) {
- if ($route === 'core.login.logout') {
- return 'https://example.com/logout';
- }
- return '/apps/test/';
- });
- $user = $this->createMock(IUser::class);
- $user->expects($this->any())->method('getUID')->willReturn('user001');
- $this->userSession->expects($this->any())->method('getUser')->willReturn($user);
- $this->userSession->expects($this->any())->method('isLoggedIn')->willReturn(true);
- $this->appManager->expects($this->any())
- ->method('getEnabledAppsForUser')
- ->with($user)
- ->willReturn(['test']);
- $this->groupManager->expects($this->any())->method('isAdmin')->willReturn(false);
-
- $this->navigationManager->clear();
- $this->dispatcher->expects($this->once())
- ->method('dispatchTyped')
- ->willReturnCallback(function ($event): void {
- $this->assertInstanceOf(LoadAdditionalEntriesEvent::class, $event);
- });
- $this->navigationManager->setup();
- $entries = $this->navigationManager->getAll();
- $this->assertEquals($expected, $entries);
- }
-
- /**
- * Known apps get a default order, all other apps keep the order from their info.xml.
- */
- public function testDefaultAppOrder(): void {
- $this->userSession->method('isLoggedIn')->willReturn(false);
- $this->appManager->method('getEnabledApps')->willReturn([]);
- $this->appManager->method('isEnabledForUser')->willReturn(true);
-
- // order as shipped by the apps themselves
- $apps = ['circles' => 80, 'activity' => 1, 'other' => 2, 'spreed' => -5, 'files' => 0, 'dashboard' => -10];
- foreach ($apps as $id => $order) {
- $this->navigationManager->add(['id' => $id, 'name' => $id, 'href' => '/', 'order' => $order]);
- }
-
- $this->assertSame(
- ['dashboard', 'files', 'spreed', 'circles', 'activity', 'other'],
- array_keys($this->navigationManager->getAll()),
- );
- }
-
- /**
- * Users that sorted the apps themselves keep their order, also for apps they never sorted.
- */
- public function testDefaultAppOrderIsSkippedForCustomOrder(): void {
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('user001');
- $this->userSession->method('getUser')->willReturn($user);
- $this->userSession->method('isLoggedIn')->willReturn(true);
- $this->appManager->method('getEnabledAppsForUser')->willReturn([]);
- $this->appManager->method('isEnabledForUser')->willReturn(true);
- $this->groupManager->expects($this->any())->method('isAdmin')->willReturn(false);
- $this->config->method('getUserValue')
- ->willReturnCallback(static function (string $userId, string $appName, string $key, mixed $default = '') {
- return $key === 'apporder' ? json_encode(['other' => ['app' => 'other', 'order' => 0]]) : $default;
- });
-
- // `circles` is not part of the user order, so it keeps the order from its info.xml
- // instead of moving to the front of the user order
- foreach (['other' => 2, 'circles' => 80] as $id => $order) {
- $this->navigationManager->add(['id' => $id, 'name' => $id, 'href' => '/', 'order' => $order]);
- }
-
- $this->assertSame(
- ['other', 'circles'],
- array_keys($this->navigationManager->getAll()),
- );
- }
-
- /**
- * Navigation entries of enabled apps that are not booted yet must not be resolved.
- */
- public function testResolveOnlyLoadedApps(): void {
- /* Return default value */
- $this->config->method('getUserValue')->willReturnArgument(3);
-
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('user001');
- $this->userSession->method('getUser')->willReturn($user);
- $this->userSession->method('isLoggedIn')->willReturn(true);
- $this->appManager->method('getEnabledAppsForUser')->with($user)->willReturn(['test']);
- $this->groupManager->expects($this->any())->method('isAdmin')->willReturn(false);
-
- // The app is enabled but not booted yet ...
- $this->appManager->expects($this->atLeastOnce())
- ->method('isAppLoaded')
- ->with('test')
- ->willReturn(false);
- // ... so its info.xml navigation entries must never be read
- $this->appManager->expects($this->never())->method('getAppInfo');
-
- $this->navigationManager->clear();
- $this->assertEquals([], $this->navigationManager->getAll('all'));
- }
-
- /**
- * The LoadAdditionalEntriesEvent is only dispatched by setup(), not by getAll().
- */
- public function testGetAllDoesNotDispatchAdditionalEntries(): void {
- $this->userSession->method('isLoggedIn')->willReturn(false);
- $this->appManager->method('getEnabledApps')->willReturn([]);
-
- $this->dispatcher->expects($this->never())->method('dispatchTyped');
-
- $this->navigationManager->clear();
- $this->assertEquals([], $this->navigationManager->getAll('all'));
- }
-
- /**
- * An app's info.xml must only be resolved once, even across multiple getAll() calls
- * and even when the app does not provide any navigation entries.
- */
- public function testAppInfoResolvedOnlyOnce(): void {
- $this->config->method('getUserValue')->willReturnArgument(3);
-
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('user001');
- $this->userSession->method('getUser')->willReturn($user);
- $this->userSession->method('isLoggedIn')->willReturn(true);
- $this->appManager->method('getEnabledAppsForUser')->with($user)->willReturn(['test']);
- $this->appManager->method('isAppLoaded')->with('test')->willReturn(true);
- $this->groupManager->expects($this->any())->method('isAdmin')->willReturn(false);
-
- // App has no navigation entries; info.xml must only be read once
- $this->appManager->expects($this->once())
- ->method('getAppInfo')
- ->with('test')
- ->willReturn(['navigations' => []]);
-
- $this->navigationManager->clear();
- $this->assertEquals([], $this->navigationManager->getAll('all'));
- $this->assertEquals([], $this->navigationManager->getAll('all'));
- }
-
- /**
- * clear(false) keeps the resolved state, so already loaded apps are not resolved again;
- * clear(true) resets it, forcing a fresh resolve.
- */
- public function testClearResetsResolvedStateOnlyWhenRequested(): void {
- $this->config->method('getUserValue')->willReturnArgument(3);
-
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('user001');
- $this->userSession->method('getUser')->willReturn($user);
- $this->userSession->method('isLoggedIn')->willReturn(true);
- $this->appManager->method('getEnabledAppsForUser')->with($user)->willReturn(['test']);
- $this->appManager->method('isAppLoaded')->with('test')->willReturn(true);
- $this->groupManager->expects($this->any())->method('isAdmin')->willReturn(false);
-
- // Resolved once for the initial getAll(), then again after clear(true) resets the state
- $this->appManager->expects($this->exactly(2))
- ->method('getAppInfo')
- ->with('test')
- ->willReturn(['navigations' => []]);
-
- $this->navigationManager->clear();
- $this->assertEquals([], $this->navigationManager->getAll('all'));
-
- // Soft clear keeps the resolved state, so getAppInfo is not called again
- $this->navigationManager->clear(false);
- $this->assertEquals([], $this->navigationManager->getAll('all'));
-
- // Full clear resets the resolved state, so the app is resolved again
- $this->navigationManager->clear(true);
- $this->assertEquals([], $this->navigationManager->getAll('all'));
- }
-
- public static function provideDefaultEntries(): array {
- return [
- // none specified, default to files
- [
- '',
- '',
- '{}',
- true,
- 'files',
- ],
- // none specified, without fallback
- [
- '',
- '',
- '{}',
- false,
- '',
- ],
- // unexisting or inaccessible app specified, default to files
- [
- 'unexist',
- '',
- '{}',
- true,
- 'files',
- ],
- // unexisting or inaccessible app specified, without fallbacks
- [
- 'unexist',
- '',
- '{}',
- false,
- '',
- ],
- // non-standard app
- [
- 'settings',
- '',
- '{}',
- true,
- 'settings',
- ],
- // non-standard app, without fallback
- [
- 'settings',
- '',
- '{}',
- false,
- 'settings',
- ],
- // non-standard app with fallback
- [
- 'unexist,settings',
- '',
- '{}',
- true,
- 'settings',
- ],
- // system default app and user apporder
- [
- // system default is settings
- 'unexist,settings',
- '',
- // apporder says default app is files (order is lower)
- '{"files_id":{"app":"files","order":1},"settings_id":{"app":"settings","order":2}}',
- true,
- // system default should override apporder
- 'settings'
- ],
- // user-customized defaultapp
- [
- '',
- 'files',
- '',
- true,
- 'files',
- ],
- // user-customized defaultapp with systemwide
- [
- 'unexist,settings',
- 'files',
- '',
- true,
- 'files',
- ],
- // user-customized defaultapp with system wide and apporder
- [
- 'unexist,settings',
- 'files',
- '{"settings_id":{"app":"settings","order":1},"files_id":{"app":"files","order":2}}',
- true,
- 'files',
- ],
- // user-customized apporder fallback
- [
- '',
- '',
- '{"settings_id":{"app":"settings","order":1},"files":{"app":"files","order":2}}',
- true,
- 'settings',
- ],
- // user-customized apporder fallback with missing app key (entries added by closures does not always have an app key set (Nextcloud 27 spreed app for example))
- [
- '',
- '',
- '{"spreed":{"order":1},"files":{"app":"files","order":2}}',
- true,
- 'files',
- ],
- // user-customized apporder, but called without fallback
- [
- '',
- '',
- '{"settings":{"app":"settings","order":1},"files":{"app":"files","order":2}}',
- false,
- '',
- ],
- // user-customized apporder with an app that has multiple routes
- [
- '',
- '',
- '{"settings_id":{"app":"settings","order":1},"settings_id_2":{"app":"settings","order":3},"id_files":{"app":"files","order":2}}',
- true,
- 'settings',
- ],
- // closure navigation entries are also resolved
- [
- 'closure2',
- '',
- '',
- true,
- 'closure2',
- ],
- [
- '',
- 'closure2',
- '',
- true,
- 'closure2',
- ],
- [
- '',
- '',
- '{"closure2":{"order":1,"app":"closure2","href":"/closure2"}}',
- true,
- 'closure2',
- ],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('provideDefaultEntries')]
- public function testGetDefaultEntryIdForUser(string $defaultApps, string $userDefaultApps, string $userApporder, bool $withFallbacks, string $expectedApp): void {
- $this->navigationManager->add([
- 'id' => 'files',
- ]);
- $this->navigationManager->add([
- 'id' => 'settings',
- ]);
- $this->navigationManager->add(static function (): array {
- return [
- 'id' => 'closure1',
- 'href' => '/closure1',
- ];
- });
- $this->navigationManager->add(static function (): array {
- return [
- 'id' => 'closure2',
- 'href' => '/closure2',
- ];
- });
-
- $this->appManager->method('getEnabledApps')->willReturn(['files']);
- $this->appManager->method('getEnabledAppsForUser')->willReturn(['files']);
- $this->appManager->expects($this->atLeastOnce())
- ->method('isAppLoaded')
- ->willReturnMap([
- ['test', true],
- ['files', true],
- ]);
-
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('user1');
-
- $this->userSession->expects($this->atLeastOnce())
- ->method('getUser')
- ->willReturn($user);
-
- $this->config->expects($this->atLeastOnce())
- ->method('getSystemValueString')
- ->with('defaultapp', $this->anything())
- ->willReturn($defaultApps);
-
- $this->config->expects($this->atLeastOnce())
- ->method('getUserValue')
- ->willReturnMap([
- ['user1', 'core', 'defaultapp', '', $userDefaultApps],
- ['user1', 'core', 'apporder', '[]', $userApporder],
- ]);
- $this->groupManager->expects($this->any())->method('isAdmin')->willReturn(false);
-
- $this->navigationManager->setup();
- $this->assertEquals($expectedApp, $this->navigationManager->getDefaultEntryIdForUser(null, $withFallbacks));
- }
-
- public function testDefaultEntryUpdated(): void {
- $this->appManager->method('getEnabledApps')->willReturn([]);
- $this->appManager->method('getEnabledAppsForUser')->willReturn([]);
- $this->groupManager->expects($this->any())->method('isAdmin')->willReturn(false);
-
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('user1');
-
- $this->userSession
- ->method('getUser')
- ->willReturn($user);
-
- $this->config
- ->method('getSystemValueString')
- ->with('defaultapp', $this->anything())
- ->willReturn('app4,app3,app2,app1');
-
- $this->config
- ->method('getUserValue')
- ->willReturnMap([
- ['user1', 'core', 'defaultapp', '', ''],
- ['user1', 'core', 'apporder', '[]', ''],
- ]);
-
- $this->navigationManager->add([
- 'id' => 'app1',
- ]);
-
- $this->assertEquals('app1', $this->navigationManager->getDefaultEntryIdForUser(null, false));
- $this->assertEquals(true, $this->navigationManager->get('app1')['default']);
-
- $this->navigationManager->add([
- 'id' => 'app3',
- ]);
-
- $this->assertEquals('app3', $this->navigationManager->getDefaultEntryIdForUser(null, false));
- $this->assertEquals(false, $this->navigationManager->get('app1')['default']);
- $this->assertEquals(true, $this->navigationManager->get('app3')['default']);
-
- $this->navigationManager->add([
- 'id' => 'app2',
- ]);
-
- $this->assertEquals('app3', $this->navigationManager->getDefaultEntryIdForUser(null, false));
- $this->assertEquals(false, $this->navigationManager->get('app1')['default']);
- $this->assertEquals(false, $this->navigationManager->get('app2')['default']);
- $this->assertEquals(true, $this->navigationManager->get('app3')['default']);
-
- $this->navigationManager->add([
- 'id' => 'app4',
- ]);
-
- $this->assertEquals('app4', $this->navigationManager->getDefaultEntryIdForUser(null, false));
- $this->assertEquals(false, $this->navigationManager->get('app1')['default']);
- $this->assertEquals(false, $this->navigationManager->get('app2')['default']);
- $this->assertEquals(false, $this->navigationManager->get('app3')['default']);
- $this->assertEquals(true, $this->navigationManager->get('app4')['default']);
- }
-}
--- a/tests/lib/Net/HostnameClassifierTest.php
+++ /dev/null
@@ -1,61 +0,0 @@
-classifier = new HostnameClassifier();
- }
-
- public static function localHostnamesData(): array {
- return [
- ['localhost'],
- ['localHost'],
- ['random-host'],
- ['another-host.local'],
- ['service.localhost'],
- ['randomdomain.internal'],
- ['another-host.local.'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('localHostnamesData')]
- public function testLocalHostname(string $host): void {
- $isLocal = $this->classifier->isLocalHostname($host);
-
- self::assertTrue($isLocal);
- }
-
- public static function publicHostnamesData(): array {
- return [
- ['example.com'],
- ['example.net'],
- ['example.org'],
- ['host.domain'],
- ['cloud.domain.tld'],
- ['cloud.domain.tld.'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('publicHostnamesData')]
- public function testPublicHostname(string $host): void {
- $isLocal = $this->classifier->isLocalHostname($host);
-
- self::assertFalse($isLocal);
- }
-}
--- a/tests/lib/Net/IpAddressClassifierTest.php
+++ /dev/null
@@ -1,94 +0,0 @@
-classifier = new IpAddressClassifier();
- }
-
- public static function publicIpAddressData(): array {
- return [
- ['8.8.8.8'],
- ['8.8.4.4'],
- ['2001:4860:4860::8888'],
- ['2001:4860:4860::8844'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('publicIpAddressData')]
- public function testPublicAddress(string $ip): void {
- $isLocal = $this->classifier->isLocalAddress($ip);
-
- self::assertFalse($isLocal);
- }
-
- public static function localIpAddressData(): array {
- return [
- ['127.0.0.1'],
- ['127.0.0.13'], // all 127.0.0.0/8 network is loopback address
- ['192.168.0.1'],
- ['fe80::200:5aee:feaa:20a2'],
- ['fe80::1fc4:15d8:78db:2319%enp4s0'], // v6 zone ID
- ['0:0:0:0:0:ffff:10.0.0.1'],
- ['0:0:0:0:0:ffff:127.0.0.0'],
- ['10.0.0.1'],
- ['::'],
- ['::1'],
- ['100.100.100.200'],
- ['192.0.0.1'],
- ['64:ff9b::a9fe:a9fe'], // NAT64 of 169.254.169.254
- ['64:ff9b:1::a9fe:a9fe'], // rfc8215
- ['::ffff:127.0.0.1'],
- ['2130706433'],
- ['0177.0.0.1'],
- ['169.254.169.254'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('localIpAddressData')]
- public function testLocalAddress(string $ip): void {
- $isLocal = $this->classifier->isLocalAddress($ip);
-
- self::assertTrue($isLocal);
- }
-
- public static function mappedAddresses(): array {
- return [
- ['64:ff9b::a9fe:a9fe', '169.254.169.254'],
- ['::ffff:7f00:1', '127.0.0.1'],
- ['::127.0.0.1', '127.0.0.1'],
- ['::7f00:1', '127.0.0.1'],
- ['2001:0000:4136:e378:8000:63bf:3fff:fdd2', '192.0.2.45'],
- ['2001:4860:4860::8888', null],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('mappedAddresses')]
- public function testMappedAddresses(string $ipv6, ?string $ipv4): void {
- $mapped = $this->classifier->getMappedIpv4(IPv6::parseString($ipv6));
-
- if ($ipv4 === null) {
- self::assertEquals(null, $mapped);
- } else {
- self::assertEquals(IPv4::parseString($ipv4), $mapped);
- }
- }
-}
--- a/tests/lib/Notification/ActionTest.php
+++ /dev/null
@@ -1,196 +0,0 @@
-action = new Action();
- }
-
- public static function dataSetLabel(): array {
- return [
- ['test1'],
- [str_repeat('a', 1)],
- [str_repeat('a', 32)],
- ];
- }
-
- /**
- * @param string $label
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetLabel')]
- public function testSetLabel($label): void {
- $this->assertSame('', $this->action->getLabel());
- $this->assertSame($this->action, $this->action->setLabel($label));
- $this->assertSame($label, $this->action->getLabel());
- }
-
- public static function dataSetLabelInvalid(): array {
- return [
- [''],
- [str_repeat('a', 33)],
- ];
- }
-
- /**
- * @param mixed $label
- *
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetLabelInvalid')]
- public function testSetLabelInvalid($label): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $this->action->setLabel($label);
- }
-
- public static function dataSetParsedLabel(): array {
- return [
- ['test1'],
- [str_repeat('a', 1)],
- [str_repeat('a', 32)],
- ];
- }
-
- /**
- * @param string $label
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetParsedLabel')]
- public function testSetParsedLabel($label): void {
- $this->assertSame('', $this->action->getParsedLabel());
- $this->assertSame($this->action, $this->action->setParsedLabel($label));
- $this->assertSame($label, $this->action->getParsedLabel());
- }
-
- public static function dataSetParsedLabelInvalid(): array {
- return [
- [''],
- ];
- }
-
- /**
- * @param mixed $label
- *
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetParsedLabelInvalid')]
- public function testSetParsedLabelInvalid($label): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $this->action->setParsedLabel($label);
- }
-
- public static function dataSetLink(): array {
- return [
- ['http://example.tld/', 'GET'],
- ['https://example.tld/api/v1/resource', 'POST'],
- ['https://example.tld/?q=1&r=2', 'PUT'],
- ['https://example.tld/path#frag', 'DELETE'],
- ['https://example.tld/web', 'WEB'],
- // Maximum length (256 chars total, including the scheme)
- ['https://' . str_repeat('a', 256 - 8), 'GET'],
- ];
- }
-
- /**
- * @param string $link
- * @param string $type
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetLink')]
- public function testSetLink($link, $type): void {
- $this->assertSame('', $this->action->getLink());
- $this->assertSame($this->action, $this->action->setLink($link, $type));
- $this->assertSame($link, $this->action->getLink());
- $this->assertSame($type, $this->action->getRequestType());
- }
-
- public static function dataSetLinkInvalid(): array {
- return [
- // Invalid link — empty / too long
- ['', 'GET'],
- ['https://' . str_repeat('a', 257 - 8), 'GET'],
-
- // Disallowed schemes
- ['javascript:alert(1)', 'WEB'],
- ['JavaScript:alert(1)', 'WEB'],
- ['javascript:alert(1)', 'GET'],
- ['data:text/html,', 'WEB'],
- ['vbscript:msgbox("xss")', 'WEB'],
- ['file:///etc/passwd', 'GET'],
- ['mailto:test@example.tld', 'WEB'],
- ['ftp://example.tld/', 'GET'],
-
- // Relative urls
- ['/relative/path', 'WEB'],
- ['//protocol-relative.tld/', 'GET'],
- ['url', 'GET'],
-
- // Invalid type (with a valid http(s) link, so the type check is what trips)
- ['https://example.tld/', 'notGET'],
- ];
- }
-
- /**
- * @param mixed $link
- * @param mixed $type
- *
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetLinkInvalid')]
- public function testSetLinkInvalid($link, $type): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $this->action->setLink($link, $type);
- }
-
- public static function dataSetPrimary(): array {
- return [
- [true],
- [false],
- ];
- }
-
- /**
- * @param bool $primary
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetPrimary')]
- public function testSetPrimary($primary): void {
- $this->assertSame(false, $this->action->isPrimary());
- $this->assertSame($this->action, $this->action->setPrimary($primary));
- $this->assertSame($primary, $this->action->isPrimary());
- }
-
- public function testIsValid(): void {
- $this->assertFalse($this->action->isValid());
- $this->assertFalse($this->action->isValidParsed());
- $this->action->setLabel('label');
- $this->assertFalse($this->action->isValid());
- $this->assertFalse($this->action->isValidParsed());
- $this->action->setLink('https://example.tld/', 'GET');
- $this->assertTrue($this->action->isValid());
- $this->assertFalse($this->action->isValidParsed());
- }
-
- public function testIsValidParsed(): void {
- $this->assertFalse($this->action->isValid());
- $this->assertFalse($this->action->isValidParsed());
- $this->action->setParsedLabel('label');
- $this->assertFalse($this->action->isValid());
- $this->assertFalse($this->action->isValidParsed());
- $this->action->setLink('https://example.tld/', 'GET');
- $this->assertFalse($this->action->isValid());
- $this->assertTrue($this->action->isValidParsed());
- }
-}
--- a/tests/lib/Notification/DummyApp.php
+++ /dev/null
@@ -1,43 +0,0 @@
-validator = $this->createMock(IValidator::class);
- $this->richTextFormatter = $this->createMock(IRichTextFormatter::class);
- $this->userManager = $this->createMock(IUserManager::class);
- $this->cache = $this->createMock(ICache::class);
- $this->subscriptionRegistry = $this->createMock(IRegistry::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $this->cacheFactory = $this->createMock(ICacheFactory::class);
- $this->cacheFactory->method('createDistributed')
- ->with('notifications')
- ->willReturn($this->cache);
-
- $this->registrationContext = $this->createMock(RegistrationContext::class);
- $this->coordinator = $this->createMock(Coordinator::class);
- $this->coordinator->method('getRegistrationContext')
- ->willReturn($this->registrationContext);
-
- $this->manager = new Manager(
- $this->validator,
- $this->userManager,
- $this->cacheFactory,
- $this->subscriptionRegistry,
- $this->logger,
- $this->coordinator,
- $this->richTextFormatter,
- );
- }
-
- public function testRegisterApp(): void {
- $this->assertEquals([], self::invokePrivate($this->manager, 'getApps'));
-
- $this->manager->registerApp(DummyApp::class);
-
- $this->assertCount(1, self::invokePrivate($this->manager, 'getApps'));
- $this->assertCount(1, self::invokePrivate($this->manager, 'getApps'));
-
- $this->manager->registerApp(DummyApp::class);
-
- $this->assertCount(2, self::invokePrivate($this->manager, 'getApps'));
- }
-
- public function testRegisterAppInvalid(): void {
- $this->manager->registerApp(DummyNotifier::class);
-
- $this->logger->expects($this->once())
- ->method('error');
- self::invokePrivate($this->manager, 'getApps');
- }
-
- public function testRegisterNotifier(): void {
- $this->assertEquals([], self::invokePrivate($this->manager, 'getNotifiers'));
-
- $this->manager->registerNotifierService(DummyNotifier::class);
-
- $this->assertCount(1, self::invokePrivate($this->manager, 'getNotifiers'));
- $this->assertCount(1, self::invokePrivate($this->manager, 'getNotifiers'));
-
- $this->manager->registerNotifierService(DummyNotifier::class);
-
- $this->assertCount(2, self::invokePrivate($this->manager, 'getNotifiers'));
- }
-
- public function testRegisterNotifierBootstrap(): void {
- $this->registrationContext->method('getNotifierServices')
- ->willReturn([
- new ServiceRegistration('app', DummyNotifier::class),
- ]);
-
- $this->assertCount(1, self::invokePrivate($this->manager, 'getNotifiers'));
- $this->assertCount(1, self::invokePrivate($this->manager, 'getNotifiers'));
- }
-
- public function testRegisterNotifierInvalid(): void {
- $this->manager->registerNotifierService(DummyApp::class);
-
- $this->logger->expects($this->once())
- ->method('error');
- self::invokePrivate($this->manager, 'getNotifiers');
- }
-
- public function testCreateNotification(): void {
- $action = $this->manager->createNotification();
- $this->assertInstanceOf(INotification::class, $action);
- }
-
- public function testNotify(): void {
- /** @var INotification|MockObject $notification */
- $notification = $this->getMockBuilder(INotification::class)
- ->disableOriginalConstructor()
- ->getMock();
- $notification->expects($this->once())
- ->method('isValid')
- ->willReturn(true);
-
- $manager = $this->getMockBuilder(Manager::class)
- ->setConstructorArgs([
- $this->validator,
- $this->userManager,
- $this->cacheFactory,
- $this->subscriptionRegistry,
- $this->logger,
- $this->coordinator,
- $this->richTextFormatter,
- ])
- ->onlyMethods(['getApps'])
- ->getMock();
-
- $manager->expects($this->once())
- ->method('getApps')
- ->willReturn([]);
-
- $manager->notify($notification);
- }
-
- public function testNotifyInvalid(): void {
- $this->expectException(\InvalidArgumentException::class);
-
- /** @var INotification|MockObject $notification */
- $notification = $this->getMockBuilder(INotification::class)
- ->disableOriginalConstructor()
- ->getMock();
- $notification->expects($this->once())
- ->method('isValid')
- ->willReturn(false);
-
- $manager = $this->getMockBuilder(Manager::class)
- ->setConstructorArgs([
- $this->validator,
- $this->userManager,
- $this->cacheFactory,
- $this->subscriptionRegistry,
- $this->logger,
- $this->coordinator,
- $this->richTextFormatter,
- ])
- ->onlyMethods(['getApps'])
- ->getMock();
-
- $manager->expects($this->never())
- ->method('getApps');
-
- $manager->notify($notification);
- }
-
- public function testMarkProcessed(): void {
- /** @var INotification|MockObject $notification */
- $notification = $this->getMockBuilder(INotification::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $manager = $this->getMockBuilder(Manager::class)
- ->setConstructorArgs([
- $this->validator,
- $this->userManager,
- $this->cacheFactory,
- $this->subscriptionRegistry,
- $this->logger,
- $this->coordinator,
- $this->richTextFormatter,
- ])
- ->onlyMethods(['getApps'])
- ->getMock();
-
- $manager->expects($this->once())
- ->method('getApps')
- ->willReturn([]);
-
- $manager->markProcessed($notification);
- }
-
- public function testGetCount(): void {
- /** @var INotification|MockObject $notification */
- $notification = $this->getMockBuilder(INotification::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $manager = $this->getMockBuilder(Manager::class)
- ->setConstructorArgs([
- $this->validator,
- $this->userManager,
- $this->cacheFactory,
- $this->subscriptionRegistry,
- $this->logger,
- $this->coordinator,
- $this->richTextFormatter,
- ])
- ->onlyMethods(['getApps'])
- ->getMock();
-
- $manager->expects($this->once())
- ->method('getApps')
- ->willReturn([]);
-
- $manager->getCount($notification);
- }
-
- public static function dataIsFairUseOfFreePushService(): array {
- return [
- [true, 999, true],
- [true, 1000, true],
- [false, 999, true],
- [false, 1000, false],
- ];
- }
-
- /**
- * @param bool $hasValidSubscription
- * @param int $userCount
- * @param bool $isFair
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataIsFairUseOfFreePushService')]
- public function testIsFairUseOfFreePushService(bool $hasValidSubscription, int $userCount, bool $isFair): void {
- $this->subscriptionRegistry->method('delegateHasValidSubscription')
- ->willReturn($hasValidSubscription);
-
- $this->userManager->method('countSeenUsers')
- ->willReturn($userCount);
-
- $this->assertSame($isFair, $this->manager->isFairUseOfFreePushService());
- }
-}
--- a/tests/lib/Notification/NotificationTest.php
+++ /dev/null
@@ -1,652 +0,0 @@
-validator = $this->createMock(IValidator::class);
- $this->richTextFormatter = $this->createMock(IRichTextFormatter::class);
- $this->notification = new Notification($this->validator, $this->richTextFormatter);
- }
-
- protected static function dataValidString($maxLength): array {
- $dataSets = [
- ['test1'],
- ['1564'],
- [str_repeat('a', 1)],
- ];
- if ($maxLength !== false) {
- $dataSets[] = [str_repeat('a', $maxLength)];
- }
- return $dataSets;
- }
-
- protected static function dataInvalidString($maxLength): array {
- $dataSets = [
- ['']
- ];
- if ($maxLength !== false) {
- $dataSets[] = [str_repeat('a', $maxLength + 1)];
- }
- return $dataSets;
- }
-
- public static function dataSetApp(): array {
- return self::dataValidString(32);
- }
-
- /**
- * @param string $app
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetApp')]
- public function testSetApp($app): void {
- $this->assertSame('', $this->notification->getApp());
- $this->assertSame($this->notification, $this->notification->setApp($app));
- $this->assertSame($app, $this->notification->getApp());
- }
-
- public static function dataSetAppInvalid(): array {
- return self::dataInvalidString(32);
- }
-
- /**
- * @param mixed $app
- *
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetAppInvalid')]
- public function testSetAppInvalid($app): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $this->notification->setApp($app);
- }
-
- public static function dataSetUser(): array {
- return self::dataValidString(64);
- }
-
- /**
- * @param string $user
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetUser')]
- public function testSetUser($user): void {
- $this->assertSame('', $this->notification->getUser());
- $this->assertSame($this->notification, $this->notification->setUser($user));
- $this->assertSame($user, $this->notification->getUser());
- }
-
- public static function dataSetUserInvalid(): array {
- return self::dataInvalidString(64);
- }
-
- /**
- * @param mixed $user
- *
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetUserInvalid')]
- public function testSetUserInvalid($user): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $this->notification->setUser($user);
- }
-
- public static function dataSetDateTime(): array {
- $past = new \DateTime();
- $past->sub(new \DateInterval('P1Y'));
- $current = new \DateTime();
- $future = new \DateTime();
- $future->add(new \DateInterval('P1Y'));
-
- return [
- [$past],
- [$current],
- [$future],
- ];
- }
-
- /**
- * @param \DateTime $dateTime
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetDateTime')]
- public function testSetDateTime(\DateTime $dateTime): void {
- $this->assertSame(0, $this->notification->getDateTime()->getTimestamp());
- $this->assertSame($this->notification, $this->notification->setDateTime($dateTime));
- $this->assertSame($dateTime, $this->notification->getDateTime());
- }
-
- public static function dataSetDateTimeZero(): array {
- $nineTeenSeventy = new \DateTime();
- $nineTeenSeventy->setTimestamp(0);
- return [
- [$nineTeenSeventy],
- ];
- }
-
- /**
- * @param \DateTime $dateTime
- * @expectedMessage 'The given date time is invalid'
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetDateTimeZero')]
- public function testSetDateTimeZero($dateTime): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $this->notification->setDateTime($dateTime);
- }
-
- public static function dataSetObject(): array {
- return [
- ['a', '21'],
- [str_repeat('a', 64), '42'],
- ];
- }
-
- /**
- * @param string $type
- * @param string $id
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetObject')]
- public function testSetObject($type, $id): void {
- $this->assertSame('', $this->notification->getObjectType());
- $this->assertSame('', $this->notification->getObjectId());
- $this->assertSame($this->notification, $this->notification->setObject($type, $id));
- $this->assertSame($type, $this->notification->getObjectType());
- $this->assertSame($id, $this->notification->getObjectId());
- }
-
- public static function dataSetObjectTypeInvalid(): array {
- return self::dataInvalidString(64);
- }
-
- public static function dataSetObjectIdInvalid(): array {
- return [
- [''],
- [str_repeat('a', 64 + 1)],
- ];
- }
-
- /**
- * @param mixed $id
- * @expectedMessage 'The given object id is invalid'
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetObjectIdInvalid')]
- public function testSetObjectIdInvalid($id): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $this->notification->setObject('object', $id);
- }
-
- public static function dataSetSubject(): array {
- return [
- ['a', []],
- [str_repeat('a', 64), [str_repeat('a', 160)]],
- [str_repeat('a', 64), array_fill(0, 160, 'a')],
- ];
- }
-
- /**
- * @param string $subject
- * @param array $parameters
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetSubject')]
- public function testSetSubject($subject, $parameters): void {
- $this->assertSame('', $this->notification->getSubject());
- $this->assertSame([], $this->notification->getSubjectParameters());
- $this->assertSame($this->notification, $this->notification->setSubject($subject, $parameters));
- $this->assertSame($subject, $this->notification->getSubject());
- $this->assertSame($parameters, $this->notification->getSubjectParameters());
- }
-
- public static function dataSetSubjectInvalidSubject(): array {
- return self::dataInvalidString(64);
- }
-
- /**
- * @param mixed $subject
- *
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetSubjectInvalidSubject')]
- public function testSetSubjectInvalidSubject($subject): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $this->notification->setSubject($subject, []);
- }
-
- public static function dataSetParsedSubject(): array {
- return self::dataValidString(false);
- }
-
- /**
- * @param string $subject
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetParsedSubject')]
- public function testSetParsedSubject($subject): void {
- $this->assertSame('', $this->notification->getParsedSubject());
- $this->assertSame($this->notification, $this->notification->setParsedSubject($subject));
- $this->assertSame($subject, $this->notification->getParsedSubject());
- }
-
- public static function dataSetParsedSubjectInvalid(): array {
- return self::dataInvalidString(false);
- }
-
- /**
- * @param mixed $subject
- *
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetParsedSubjectInvalid')]
- public function testSetParsedSubjectInvalid($subject): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $this->notification->setParsedSubject($subject);
- }
-
- public static function dataSetMessage(): array {
- return [
- ['a', []],
- [str_repeat('a', 64), [str_repeat('a', 160)]],
- [str_repeat('a', 64), array_fill(0, 160, 'a')],
- ];
- }
-
- /**
- * @param string $message
- * @param array $parameters
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetMessage')]
- public function testSetMessage($message, $parameters): void {
- $this->assertSame('', $this->notification->getMessage());
- $this->assertSame([], $this->notification->getMessageParameters());
- $this->assertSame($this->notification, $this->notification->setMessage($message, $parameters));
- $this->assertSame($message, $this->notification->getMessage());
- $this->assertSame($parameters, $this->notification->getMessageParameters());
- }
-
- public static function dataSetMessageInvalidMessage(): array {
- return self::dataInvalidString(64);
- }
-
- /**
- * @param mixed $message
- *
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetMessageInvalidMessage')]
- public function testSetMessageInvalidMessage($message): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $this->notification->setMessage($message, []);
- }
-
- public static function dataSetParsedMessage(): array {
- return self::dataValidString(false);
- }
-
- /**
- * @param string $message
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetParsedMessage')]
- public function testSetParsedMessage($message): void {
- $this->assertSame('', $this->notification->getParsedMessage());
- $this->assertSame($this->notification, $this->notification->setParsedMessage($message));
- $this->assertSame($message, $this->notification->getParsedMessage());
- }
-
- public static function dataSetParsedMessageInvalid(): array {
- return self::dataInvalidString(false);
- }
-
- /**
- * @param mixed $message
- *
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetParsedMessageInvalid')]
- public function testSetParsedMessageInvalid($message): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $this->notification->setParsedMessage($message);
- }
-
- public static function dataSetLink(): array {
- return [
- ['http://example.tld/'],
- ['https://example.tld/'],
- ['https://example.tld/path/to/resource?query=1&other=2#fragment'],
- // Maximum length (4000 chars total, including the scheme)
- ['https://' . str_repeat('a', 4000 - 8)],
- ];
- }
-
- /**
- * @param string $link
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetLink')]
- public function testSetLink($link): void {
- $this->assertSame('', $this->notification->getLink());
- $this->assertSame($this->notification, $this->notification->setLink($link));
- $this->assertSame($link, $this->notification->getLink());
- }
-
- public static function dataSetLinkInvalid(): array {
- return [
- // Empty / too long
- [''],
- ['https://' . str_repeat('a', 4001 - 8)],
-
- // Disallowed schemes
- ['javascript:alert(1)'],
- ['JavaScript:alert(1)'],
- ['data:text/html,'],
- ['vbscript:msgbox("xss")'],
- ['file:///etc/passwd'],
- ['mailto:test@example.tld'],
- ['ftp://example.tld/'],
- ['ws://example.tld/'],
-
- // Relative urls
- ['/relative/path'],
- ['//protocol-relative.tld/'],
- ['example.tld/path'],
- ['test1'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetLinkInvalid')]
- public function testSetLinkInvalid(string $link): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $this->notification->setLink($link);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetLink')]
- public function testSetIcon(string $icon): void {
- $this->assertSame('', $this->notification->getIcon());
- $this->assertSame($this->notification, $this->notification->setIcon($icon));
- $this->assertSame($icon, $this->notification->getIcon());
- }
-
- public static function dataSetIconInvalid(): array {
- return self::dataInvalidString(4000);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataSetLinkInvalid')]
- public function testSetIconInvalid(string $icon): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $this->notification->setIcon($icon);
- }
-
- public function testCreateAction(): void {
- $action = $this->notification->createAction();
- $this->assertInstanceOf(IAction::class, $action);
- }
-
- public function testAddAction(): void {
- /** @var IAction|\PHPUnit\Framework\MockObject\MockObject $action */
- $action = $this->createMock(IAction::class);
- $action->expects($this->once())
- ->method('isValid')
- ->willReturn(true);
- $action->expects($this->never())
- ->method('isValidParsed');
-
- $this->assertSame($this->notification, $this->notification->addAction($action));
-
- $this->assertEquals([$action], $this->notification->getActions());
- $this->assertEquals([], $this->notification->getParsedActions());
- }
-
- public function testAddActionInvalid(): void {
- $this->expectException(\InvalidArgumentException::class);
-
- /** @var IAction|\PHPUnit\Framework\MockObject\MockObject $action */
- $action = $this->createMock(IAction::class);
- $action->expects($this->once())
- ->method('isValid')
- ->willReturn(false);
- $action->expects($this->never())
- ->method('isValidParsed');
-
- $this->notification->addAction($action);
- }
-
- public function testAddActionSecondPrimary(): void {
- /** @var IAction|\PHPUnit\Framework\MockObject\MockObject $action */
- $action = $this->createMock(IAction::class);
- $action->expects($this->exactly(2))
- ->method('isValid')
- ->willReturn(true);
- $action->expects($this->exactly(2))
- ->method('isPrimary')
- ->willReturn(true);
-
- $this->assertSame($this->notification, $this->notification->addAction($action));
-
- $this->expectException(\InvalidArgumentException::class);
- $this->notification->addAction($action);
- }
-
- public function testAddParsedAction(): void {
- /** @var IAction|\PHPUnit\Framework\MockObject\MockObject $action */
- $action = $this->createMock(IAction::class);
- $action->expects($this->once())
- ->method('isValidParsed')
- ->willReturn(true);
- $action->expects($this->never())
- ->method('isValid');
-
- $this->assertSame($this->notification, $this->notification->addParsedAction($action));
-
- $this->assertEquals([$action], $this->notification->getParsedActions());
- $this->assertEquals([], $this->notification->getActions());
- }
-
- public function testAddParsedActionInvalid(): void {
- $this->expectException(\InvalidArgumentException::class);
-
- /** @var IAction|\PHPUnit\Framework\MockObject\MockObject $action */
- $action = $this->createMock(IAction::class);
- $action->expects($this->once())
- ->method('isValidParsed')
- ->willReturn(false);
- $action->expects($this->never())
- ->method('isValid');
-
- $this->notification->addParsedAction($action);
- }
-
- public function testAddActionSecondParsedPrimary(): void {
- /** @var IAction|\PHPUnit\Framework\MockObject\MockObject $action */
- $action = $this->createMock(IAction::class);
- $action->expects($this->exactly(2))
- ->method('isValidParsed')
- ->willReturn(true);
- $action->expects($this->exactly(2))
- ->method('isPrimary')
- ->willReturn(true);
-
- $this->assertSame($this->notification, $this->notification->addParsedAction($action));
-
- $this->expectException(\InvalidArgumentException::class);
- $this->notification->addParsedAction($action);
- }
-
- public function testAddActionParsedPrimaryEnd(): void {
- /** @var IAction|\PHPUnit\Framework\MockObject\MockObject $action */
- $action1 = $this->createMock(IAction::class);
- $action1->expects($this->exactly(2))
- ->method('isValidParsed')
- ->willReturn(true);
- $action1->expects($this->exactly(2))
- ->method('isPrimary')
- ->willReturn(false);
- /** @var IAction|\PHPUnit\Framework\MockObject\MockObject $action */
- $action2 = $this->createMock(IAction::class);
- $action2->expects($this->once())
- ->method('isValidParsed')
- ->willReturn(true);
- $action2->expects($this->once())
- ->method('isPrimary')
- ->willReturn(true);
-
- $this->assertSame($this->notification, $this->notification->addParsedAction($action1));
- $this->assertSame($this->notification, $this->notification->addParsedAction($action2));
- $this->assertSame($this->notification, $this->notification->addParsedAction($action1));
-
- $this->assertEquals([$action2, $action1, $action1], $this->notification->getParsedActions());
- }
-
- public static function dataIsValid(): array {
- return [
- [false, '', false],
- [true, '', false],
- [false, 'a', false],
- [true, 'a', true],
- ];
- }
-
- /**
- *
- * @param bool $isValidCommon
- * @param string $subject
- * @param bool $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataIsValid')]
- public function testIsValid($isValidCommon, $subject, $expected): void {
- /** @var INotification|\PHPUnit\Framework\MockObject\MockObject $notification */
- $notification = $this->getMockBuilder(Notification::class)
- ->onlyMethods([
- 'isValidCommon',
- 'getSubject',
- 'getParsedSubject',
- ])
- ->setConstructorArgs([$this->validator, $this->richTextFormatter])
- ->getMock();
-
- $notification->expects($this->once())
- ->method('isValidCommon')
- ->willReturn($isValidCommon);
-
- $notification->expects(!$isValidCommon ? $this->never() : $this->once())
- ->method('getSubject')
- ->willReturn($subject);
-
- $notification->expects($this->never())
- ->method('getParsedSubject')
- ->willReturn($subject);
-
- $this->assertEquals($expected, $notification->isValid());
- }
-
- /**
- *
- * @param bool $isValidCommon
- * @param string $subject
- * @param bool $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataIsValid')]
- public function testIsParsedValid($isValidCommon, $subject, $expected): void {
- /** @var INotification|\PHPUnit\Framework\MockObject\MockObject $notification */
- $notification = $this->getMockBuilder(Notification::class)
- ->onlyMethods([
- 'isValidCommon',
- 'getParsedSubject',
- 'getSubject',
- ])
- ->setConstructorArgs([$this->validator, $this->richTextFormatter])
- ->getMock();
-
- $notification->expects($this->once())
- ->method('isValidCommon')
- ->willReturn($isValidCommon);
-
- $notification->expects(!$isValidCommon ? $this->never() : $this->once())
- ->method('getParsedSubject')
- ->willReturn($subject);
-
- $notification->expects($this->never())
- ->method('getSubject')
- ->willReturn($subject);
-
- $this->assertEquals($expected, $notification->isValidParsed());
- }
-
- public static function dataIsValidCommon(): array {
- return [
- ['', '', 0, '', '', false],
- ['app', '', 0, '', '', false],
- ['app', 'user', 0, '', '', false],
- ['app', 'user', time(), '', '', false],
- ['app', 'user', time(), 'type', '', false],
- ['app', 'user', time(), 'type', '42', true],
- ];
- }
-
- /**
- *
- * @param string $app
- * @param string $user
- * @param int $timestamp
- * @param string $objectType
- * @param string $objectId
- * @param bool $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataIsValidCommon')]
- public function testIsValidCommon($app, $user, $timestamp, $objectType, $objectId, $expected): void {
- /** @var INotification|\PHPUnit\Framework\MockObject\MockObject $notification */
- $notification = $this->getMockBuilder(Notification::class)
- ->onlyMethods([
- 'getApp',
- 'getUser',
- 'getDateTime',
- 'getObjectType',
- 'getObjectId',
- ])
- ->setConstructorArgs([$this->validator, $this->richTextFormatter])
- ->getMock();
-
- $notification->expects($this->any())
- ->method('getApp')
- ->willReturn($app);
-
- $notification->expects($this->any())
- ->method('getUser')
- ->willReturn($user);
-
- $dateTime = new \DateTime();
- $dateTime->setTimestamp($timestamp);
-
- $notification->expects($this->any())
- ->method('getDateTime')
- ->willReturn($dateTime);
-
- $notification->expects($this->any())
- ->method('getObjectType')
- ->willReturn($objectType);
-
- $notification->expects($this->any())
- ->method('getObjectId')
- ->willReturn($objectId);
-
- $this->assertEquals($expected, $this->invokePrivate($notification, 'isValidCommon'));
- }
-}
--- a/tests/lib/OCM/DiscoveryServiceTest.php
+++ /dev/null
@@ -1,157 +0,0 @@
-logger = $this->createMock(LoggerInterface::class);
- $this->context = Server::get(RegistrationContext::class);
- $this->dispatcher = Server::get(IEventDispatcher::class);
- $this->discoveryService = Server::get(OCMDiscoveryService::class);
- $this->config = Server::get(IConfig::class);
-
- // reset $localProvider value between tests
- $reflection = new ReflectionClass($this->discoveryService);
- $localProvider = $reflection->getProperty('localProvider');
- $localProvider->setValue($this->discoveryService, null);
-
- $this->requestController = Server::get(OCMRequestController::class);
- }
-
- public static function dataTestOCMRequest(): array {
- return [
- ['/inexistant-path/', 404, null],
- ['/ocm-capability-test/', 404, null],
- ['/ocm-capability-test/get', 200,
- [
- 'capability' => 'ocm-capability-test',
- 'path' => '/get',
- 'args' => ['get'],
- 'totalArgs' => 1,
- 'typedArgs' => ['get'],
- ]
- ],
- ['/ocm-capability-test/get/10/', 200,
- [
- 'capability' => 'ocm-capability-test',
- 'path' => '/get/10',
- 'args' => ['get', '10'],
- 'totalArgs' => 2,
- 'typedArgs' => ['get', '10'],
- ]
- ],
- ['/ocm-capability-test/get/random/10/', 200,
- [
- 'capability' => 'ocm-capability-test',
- 'path' => '/get/random/10',
- 'args' => ['get', 'random', '10'],
- 'totalArgs' => 3,
- 'typedArgs' => ['get', 'random', 10],
- ]
- ],
- ['/ocm-capability-test/get/random/10/1', 200,
- [
- 'capability' => 'ocm-capability-test',
- 'path' => '/get/random/10/1',
- 'args' => ['get', 'random', '10', '1'],
- 'totalArgs' => 4,
- 'typedArgs' => ['get', 'random', 10, true],
- ]
- ],
- ['/ocm-capability-test/get/random/10/true', 200,
- [
- 'capability' => 'ocm-capability-test',
- 'path' => '/get/random/10/true',
- 'args' => ['get', 'random', '10', 'true'],
- 'totalArgs' => 4,
- 'typedArgs' => ['get', 'random', 10, true],
- ]
- ],
- ['/ocm-capability-test/get/random/10/true/42', 200,
- [
- 'capability' => 'ocm-capability-test',
- 'path' => '/get/random/10/true/42',
- 'args' => ['get', 'random', '10', 'true', '42'],
- 'totalArgs' => 5,
- 'typedArgs' => ['get', 'random', 10, true, 42],
- ]
- ],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestOCMRequest')]
- public function testOCMRequest(string $path, int $expectedStatus, ?array $expectedResult): void {
- $this->context->for('ocm-request-app')->registerEventListener(OCMEndpointRequestEvent::class, OCMEndpointRequestTestEvent::class);
- $this->context->delegateEventListenerRegistrations($this->dispatcher);
-
- $response = $this->requestController->manageOCMRequests($path);
- $this->assertSame($expectedStatus, $response->getStatus());
- if ($expectedResult !== null) {
- $this->assertSame($expectedResult, $response->getData());
- }
- }
-
- public function testLocalBaseCapability(): void {
- $local = $this->discoveryService->getLocalOCMProvider();
- $this->assertEmpty(array_diff(['notifications', 'shares'], $local->getCapabilities()->toArray()));
- }
-
- public function testLocalCapabilitiesAdvertiseHttpSigByDefault(): void {
- // `http-sig` is the OCM-spec flag signalling RFC 9421 support backed
- // by the JWK Set published at the URL in `jwksUri`. Advertised
- // whenever signing is not disabled outright.
- $local = $this->discoveryService->getLocalOCMProvider();
- $this->assertTrue($local->hasCapability('http-sig'));
- }
-
- public function testLocalDiscoveryAdvertisesJwksUri(): void {
- // scheme follows the instance base URL
- $local = $this->discoveryService->getLocalOCMProvider();
- $jwksUri = $local->getJwksUri();
- $baseUrl = Server::get(IURLGenerator::class)->getBaseUrl();
- $expectedScheme = str_starts_with($baseUrl, 'http://') ? 'http://' : 'https://';
- $this->assertStringStartsWith($expectedScheme, $jwksUri);
- $this->assertStringContainsString('cloud_federation_api/api/v1/jwks', $jwksUri);
- }
-
- public function testLocalAddedCapability(): void {
- $this->context->for('ocm-capability-app')->registerEventListener(LocalOCMDiscoveryEvent::class, LocalOCMDiscoveryTestEvent::class);
- $this->context->delegateEventListenerRegistrations($this->dispatcher);
- $local = $this->discoveryService->getLocalOCMProvider();
- $this->assertEmpty(array_diff(['notifications', 'shares', 'ocm-capability-test'], $local->getCapabilities()->toArray()));
- }
-
-}
--- a/tests/lib/OCM/Listeners/LocalOCMDiscoveryTestEvent.php
+++ /dev/null
@@ -1,30 +0,0 @@
- */
-class LocalOCMDiscoveryTestEvent implements IEventListener {
- public function __construct(
- ) {
- }
-
- #[\Override]
- public function handle(Event $event): void {
- if (!($event instanceof LocalOCMDiscoveryEvent)) {
- return;
- }
-
- $event->addCapability('ocm-capability-test');
- }
-}
--- a/tests/lib/OCM/Listeners/OCMEndpointRequestTestEvent.php
+++ /dev/null
@@ -1,52 +0,0 @@
- */
-class OCMEndpointRequestTestEvent implements IEventListener {
- public function __construct(
- ) {
- }
-
- #[\Override]
- public function handle(Event $event): void {
- if (!($event instanceof OCMEndpointRequestEvent)) {
- return;
- }
-
- if ($event->getPath() === '/') {
- $event->setResponse(new Response(404));
- return;
- }
-
- $event->setResponse(new DataResponse(
- [
- 'capability' => $event->getRequestedCapability(),
- 'path' => $event->getPath(),
- 'args' => $event->getArgs(),
- 'totalArgs' => $event->getArgsCount(),
- 'typedArgs' => $event->getArgs(
- ParamType::STRING,
- ParamType::STRING,
- ParamType::INT,
- ParamType::BOOL,
- ParamType::INT
- )
- ]
- ));
- }
-}
--- a/tests/lib/OCM/OCMProviderTest.php
+++ /dev/null
@@ -1,98 +0,0 @@
-provider = new OCMProvider();
- }
-
- private function resource(string $name, array $shareTypes, array $protocols): IOCMResource {
- $resource = $this->provider->createNewResourceType();
- $resource->setName($name)
- ->setShareTypes($shareTypes)
- ->setProtocols($protocols);
- return $resource;
- }
-
- public function testAddResourceTypeKeepsDistinctNames(): void {
- $this->provider->addResourceType($this->resource('file', ['user'], ['webdav' => '/dav/']));
- $this->provider->addResourceType($this->resource('folder', ['user'], ['webapp' => []]));
-
- $this->assertCount(2, $this->provider->getResourceTypes());
- }
-
- public function testAddResourceTypeMergesSameName(): void {
- $this->provider->addResourceType($this->resource('folder', ['user'], ['webapp' => []]));
- $this->provider->addResourceType($this->resource('folder', ['user'], ['webapp-receive' => ['targets' => ['blank', 'iframe']]]));
-
- $resourceTypes = $this->provider->getResourceTypes();
- $this->assertCount(1, $resourceTypes);
- $this->assertSame(
- ['webapp' => [], 'webapp-receive' => ['targets' => ['blank', 'iframe']]],
- $resourceTypes[0]->getProtocols(),
- );
- }
-
- public function testAddResourceTypeDedupesShareTypes(): void {
- $this->provider->addResourceType($this->resource('folder', ['user'], ['webapp' => []]));
- $this->provider->addResourceType($this->resource('folder', ['user', 'group'], ['webapp-receive' => []]));
-
- $shareTypes = $this->provider->getResourceTypes()[0]->getShareTypes();
- $this->assertSame(['user', 'group'], $shareTypes);
- // Deduplication must not leave key gaps, or shareTypes would
- // serialize as a JSON object instead of an array.
- $this->assertSame('["user","group"]', json_encode($shareTypes));
- }
-
- public function testAddResourceTypeMergeOverwritesSameProtocol(): void {
- $this->provider->addResourceType($this->resource('folder', ['user'], ['webapp' => ['a' => 1]]));
- $this->provider->addResourceType($this->resource('folder', ['user'], ['webapp' => ['b' => 2]]));
-
- $this->assertSame(
- ['webapp' => ['b' => 2]],
- $this->provider->getResourceTypes()[0]->getProtocols(),
- );
- }
-
- public function testJwksUriImportedFromDiscoveryData(): void {
- $this->provider->import([
- 'enabled' => true,
- 'apiVersion' => '1.1.0',
- 'endPoint' => 'https://cloud.example.org/ocm',
- 'capabilities' => ['http-sig'],
- 'jwksUri' => 'https://cloud.example.org/ocm/jwks',
- ]);
-
- $this->assertSame('https://cloud.example.org/ocm/jwks', $this->provider->getJwksUri());
- }
-
- public function testJwksUriSerializedOnlyWhenSet(): void {
- $this->provider->setEnabled(true)
- ->setApiVersion('1.1.0')
- ->setEndPoint('https://cloud.example.org/ocm');
-
- $this->assertArrayNotHasKey('jwksUri', $this->provider->jsonSerialize());
-
- $this->provider->setJwksUri('https://cloud.example.org/ocm/jwks');
- $this->assertSame(
- 'https://cloud.example.org/ocm/jwks',
- $this->provider->jsonSerialize()['jwksUri'],
- );
- }
-}
--- a/tests/lib/OCM/OCMSignatoryManagerJwksTest.php
+++ /dev/null
@@ -1,368 +0,0 @@
-appConfig = $this->createMock(IAppConfig::class);
- $this->signatureManager = $this->createMock(ISignatureManager::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->identityProofManager = $this->createMock(IdentityProofManager::class);
- $this->clientService = $this->createMock(IClientService::class);
- $this->config = $this->createMock(IConfig::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->client = $this->createMock(IClient::class);
- $this->discoveryService = $this->createMock(IOCMDiscoveryService::class);
-
- $this->clientService->method('newClient')->willReturn($this->client);
- $this->overwriteService(IOCMDiscoveryService::class, $this->discoveryService);
-
- $cacheFactory = $this->createMock(ICacheFactory::class);
- $cacheFactory->method('createDistributed')->willReturn(new ArrayCache(''));
-
- $this->signatoryManager = new OCMSignatoryManager(
- $this->appConfig,
- $this->signatureManager,
- $this->urlGenerator,
- $this->identityProofManager,
- $this->clientService,
- $this->config,
- $cacheFactory,
- $this->logger,
- );
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->restoreService(IOCMDiscoveryService::class);
- parent::tearDown();
- }
-
- /** Remote discovery response advertising http-sig and $jwksUri. */
- private function primeDiscovery(
- string $jwksUri = self::JWKS_URI,
- array $capabilities = ['http-sig'],
- bool $httpOnly = false,
- ): void {
- $provider = new OCMProvider();
- $provider->setCapabilities($capabilities);
- $provider->setJwksUri($jwksUri);
- $this->discoveryService->method('discover')
- ->willReturnCallback(static function (string $remote) use ($httpOnly, $provider): OCMProvider {
- if ($httpOnly && str_starts_with($remote, 'https://')) {
- throw new OCMProviderException('HTTPS unavailable');
- }
- return $provider;
- });
- }
-
- public function testGetRemoteKeyFetchesAndMatchesByKid(): void {
- $this->primeDiscovery();
- $kid = 'sender.example.org#key1';
- $jwks = [
- 'keys' => [
- $this->ecJwk('other'),
- $this->ecJwk($kid),
- ],
- ];
- $this->respondWith($jwks);
-
- $key = $this->signatoryManager->getRemoteKey('sender.example.org', $kid);
- $this->assertNotNull($key);
- $this->assertSame('ES256', $key->getAlgorithm());
- }
-
- public function testGetRemoteKeyReturnsNullWhenKidMissing(): void {
- $this->primeDiscovery();
- $this->respondWith(['keys' => [$this->ecJwk('unrelated')]]);
- $this->assertNull($this->signatoryManager->getRemoteKey('sender.example.org', 'other-kid'));
- }
-
- public function testGetRemoteKeyReturnsNullOnHttpError(): void {
- $this->primeDiscovery();
- $this->client->method('get')->willThrowException(new \RuntimeException('boom'));
- $this->logger->expects($this->once())->method('warning');
- $this->assertNull($this->signatoryManager->getRemoteKey('sender.example.org', 'kid'));
- }
-
- public function testGetRemoteKeyReturnsNullOnInvalidJson(): void {
- $this->primeDiscovery();
- $response = $this->createMock(IResponse::class);
- $response->method('getBody')->willReturn('not json');
- $this->client->method('get')->willReturn($response);
- $this->logger->expects($this->once())->method('warning');
- $this->assertNull($this->signatoryManager->getRemoteKey('sender.example.org', 'kid'));
- }
-
- public function testGetRemoteKeyReturnsNullWhenKeysMissing(): void {
- $this->primeDiscovery();
- $this->respondWith(['no-keys-here' => []]);
- $this->assertNull($this->signatoryManager->getRemoteKey('sender.example.org', 'kid'));
- }
-
- public function testGetRemoteKeyReturnsNullOnUnparseableJwk(): void {
- $this->primeDiscovery();
- // JWK with kty=EC but no crv: parseKey rejects.
- $this->respondWith(['keys' => [['kty' => 'EC', 'kid' => 'kid', 'alg' => 'ES256', 'x' => self::TEST_X, 'y' => self::TEST_Y]]]);
- $this->logger->expects($this->once())->method('warning');
- $this->assertNull($this->signatoryManager->getRemoteKey('sender.example.org', 'kid'));
- }
-
- public function testGetRemoteKeyFetchesFromAdvertisedJwksUri(): void {
- $this->primeDiscovery();
- $this->client->expects($this->once())
- ->method('get')
- ->with(
- $this->equalTo(self::JWKS_URI),
- $this->isType('array'),
- )
- ->willReturn($this->jsonResponse(['keys' => []]));
-
- $this->signatoryManager->getRemoteKey('sender.example.org', 'kid');
- }
-
- public function testGetRemoteKeyRejectsMissingJwksUriWhenHttpSigAdvertised(): void {
- // a peer advertising http-sig without a jwksUri is non-conformant
- $this->primeDiscovery(jwksUri: '');
- $this->client->expects($this->never())->method('get');
- $this->logger->expects($this->once())->method('warning');
- $this->assertNull($this->signatoryManager->getRemoteKey('sender.example.org', 'kid'));
- }
-
- public function testGetRemoteKeyRejectsHttpJwksUriFromHttpsPeer(): void {
- // downgrade guard: http jwksUri from an https peer
- $this->primeDiscovery(jwksUri: 'http://sender.example.org/ocm/jwks');
- $this->client->expects($this->never())->method('get');
- $this->logger->expects($this->once())->method('warning');
- $this->assertNull($this->signatoryManager->getRemoteKey('sender.example.org', 'kid'));
- }
-
- public function testGetRemoteKeyAcceptsHttpJwksUriFromHttpPeer(): void {
- // the spec's http fallback for testing setups
- $this->primeDiscovery(
- jwksUri: 'http://sender.example.org/ocm/jwks',
- httpOnly: true,
- );
- $kid = 'sender.example.org#key1';
- $this->client->expects($this->once())
- ->method('get')
- ->with(
- $this->equalTo('http://sender.example.org/ocm/jwks'),
- $this->isType('array'),
- )
- ->willReturn($this->jsonResponse(['keys' => [$this->ecJwk($kid)]]));
-
- $this->assertNotNull($this->signatoryManager->getRemoteKey('sender.example.org', $kid));
- }
-
- public function testGetRemoteKeyReturnsNullWhenDiscoveryFails(): void {
- $this->discoveryService->method('discover')
- ->willThrowException(new OCMProviderException('no discovery'));
- $this->client->expects($this->never())->method('get');
- $this->logger->expects($this->once())->method('warning');
- $this->assertNull($this->signatoryManager->getRemoteKey('sender.example.org', 'kid'));
- }
-
- public function testGetRemoteKeyRejectsJwkWithoutAlg(): void {
- $this->primeDiscovery();
- $jwk = $this->ecJwk('kid');
- unset($jwk['alg']);
- $this->respondWith(['keys' => [$jwk]]);
- $this->logger->expects($this->once())->method('warning');
- $this->assertNull($this->signatoryManager->getRemoteKey('sender.example.org', 'kid'));
- }
-
- public function testGetRemoteKeyRejectsJwkWithSymmetricAlg(): void {
- $this->primeDiscovery();
- $jwk = $this->ecJwk('kid');
- $jwk['alg'] = 'HS256';
- $this->respondWith(['keys' => [$jwk]]);
- $this->logger->expects($this->once())->method('warning');
- $this->assertNull($this->signatoryManager->getRemoteKey('sender.example.org', 'kid'));
- }
-
- public function testGetRemoteKeyRejectsJwkAlgMismatchingKeyType(): void {
- $this->primeDiscovery();
- // EC P-256 key claiming an Ed25519 algorithm
- $jwk = $this->ecJwk('kid');
- $jwk['alg'] = 'Ed25519';
- $this->respondWith(['keys' => [$jwk]]);
- $this->logger->expects($this->once())->method('warning');
- $this->assertNull($this->signatoryManager->getRemoteKey('sender.example.org', 'kid'));
- }
-
- public function testGetRemoteKeyAcceptsFullySpecifiedEd25519Alg(): void {
- $this->primeDiscovery();
- $this->respondWith(['keys' => [[
- 'kty' => 'OKP',
- 'crv' => 'Ed25519',
- 'kid' => 'kid',
- 'alg' => 'Ed25519',
- 'use' => 'sig',
- 'x' => self::TEST_OKP_X,
- ]]]);
-
- $key = $this->signatoryManager->getRemoteKey('sender.example.org', 'kid');
- $this->assertNotNull($key);
- $this->assertSame('Ed25519', $key->getAlgorithm());
- }
-
- public function testGetRemoteKeyPassesSelfSignedFlagThrough(): void {
- $this->primeDiscovery();
- $this->config->method('getSystemValueBool')
- ->with('sharing.federation.allowSelfSignedCertificates')
- ->willReturn(true);
-
- $this->client->expects($this->once())
- ->method('get')
- ->with(
- $this->anything(),
- $this->callback(static fn (array $opts): bool => ($opts['verify'] ?? null) === false),
- )
- ->willReturn($this->jsonResponse(['keys' => []]));
-
- $this->signatoryManager->getRemoteKey('sender.example.org', 'kid');
- }
-
- public function testJwksCachedAcrossCallsToTheSameOrigin(): void {
- $this->primeDiscovery();
- $kid = 'sender.example.org#key1';
- $jwks = ['keys' => [$this->ecJwk($kid)]];
- $this->client->expects($this->once())
- ->method('get')
- ->willReturn($this->jsonResponse($jwks));
-
- $this->assertNotNull($this->signatoryManager->getRemoteKey('sender.example.org', $kid));
- $this->assertNotNull($this->signatoryManager->getRemoteKey('sender.example.org', $kid));
- }
-
- public function testCacheMissOnNewKidTriggersRefetchOnce(): void {
- $this->primeDiscovery();
- $first = ['keys' => [$this->ecJwk('old')]];
- $second = ['keys' => [$this->ecJwk('new')]];
- $this->client->expects($this->exactly(2))
- ->method('get')
- ->willReturnOnConsecutiveCalls(
- $this->jsonResponse($first),
- $this->jsonResponse($second),
- );
-
- $this->assertNotNull($this->signatoryManager->getRemoteKey('sender.example.org', 'old'));
- $this->assertNotNull($this->signatoryManager->getRemoteKey('sender.example.org', 'new'));
- }
-
- public function testGetRemoteKeyAcceptsHttpsJwksUriFromHttpPeer(): void {
- // upgrade from an http-only peer is fine
- $this->primeDiscovery(
- httpOnly: true,
- );
- $kid = 'sender.example.org#key1';
- $this->client->expects($this->once())
- ->method('get')
- ->with(
- $this->equalTo(self::JWKS_URI),
- $this->isType('array'),
- )
- ->willReturn($this->jsonResponse(['keys' => [$this->ecJwk($kid)]]));
-
- $this->assertNotNull($this->signatoryManager->getRemoteKey('sender.example.org', $kid));
- }
-
- public function testGetRemoteKeyAcceptsJwksUriOnDifferentHost(): void {
- // the JWK Set may live on a different host than the peer
- $this->primeDiscovery(
- jwksUri: 'https://keys.example.net/ocm/jwks',
- httpOnly: true,
- );
- $kid = 'sender.example.org#key1';
- $this->client->expects($this->once())
- ->method('get')
- ->with(
- $this->equalTo('https://keys.example.net/ocm/jwks'),
- $this->isType('array'),
- )
- ->willReturn($this->jsonResponse(['keys' => [$this->ecJwk($kid)]]));
-
- $this->assertNotNull($this->signatoryManager->getRemoteKey('sender.example.org', $kid));
- }
-
- public function testGetLocalJwksUriPointsAtAppRoute(): void {
- $this->urlGenerator->method('linkToRouteAbsolute')
- ->willReturn('https://sender.example.org/index.php/apps/cloud_federation_api/api/v1/jwks');
-
- $this->assertSame(
- 'https://sender.example.org/index.php/apps/cloud_federation_api/api/v1/jwks',
- $this->signatoryManager->getLocalJwksUri(),
- );
- }
-
- private function respondWith(array $body): void {
- $this->client->method('get')->willReturn($this->jsonResponse($body));
- }
-
- private function jsonResponse(array $body): IResponse {
- $response = $this->createMock(IResponse::class);
- $response->method('getBody')->willReturn(json_encode($body, JSON_THROW_ON_ERROR));
- return $response;
- }
-
- /** @return array */
- private function ecJwk(string $kid): array {
- return [
- 'kty' => 'EC',
- 'crv' => 'P-256',
- 'kid' => $kid,
- 'alg' => 'ES256',
- 'use' => 'sig',
- 'x' => self::TEST_X,
- 'y' => self::TEST_Y,
- ];
- }
-}
--- a/tests/lib/OCM/OCMSignatoryManagerRotationTest.php
+++ /dev/null
@@ -1,283 +0,0 @@
- in-memory backing store for IAppConfig core/* */
- private array $appConfigStore = [];
- /** @var array in-memory backing store for IdentityProofManager appkeys */
- private array $appKeyStore = [];
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- $this->appConfig = $this->createMock(IAppConfig::class);
- $this->identityProofManager = $this->createMock(IdentityProofManager::class);
-
- $this->wireAppConfig();
- $this->wireIdentityProofManager();
-
- $signatureManager = $this->createMock(ISignatureManager::class);
- $signatureManager->method('generateKeyIdFromConfig')
- ->willThrowException(new IdentityNotFoundException('no configured identity'));
- $urlGenerator = $this->createMock(IURLGenerator::class);
- $urlGenerator->method('getAbsoluteURL')
- ->willReturnCallback(static fn (string $path): string => 'https://alice.example' . $path);
-
- $cacheFactory = $this->createMock(ICacheFactory::class);
- $cacheFactory->method('createDistributed')->willReturn(new ArrayCache(''));
-
- $this->signatoryManager = new OCMSignatoryManager(
- $this->appConfig,
- $signatureManager,
- $urlGenerator,
- $this->identityProofManager,
- $this->stubClientService(),
- $this->createMock(IConfig::class),
- $cacheFactory,
- $this->createMock(LoggerInterface::class),
- );
- }
-
- public function testJwksBootstrapsActiveKeyOnFirstFetch(): void {
- // Fresh instance: first JWKS hit must provision the active key.
- $jwks = $this->signatoryManager->getLocalJwks();
- $this->assertCount(1, $jwks);
- $this->assertSame('https://alice.example/ocm#ecdsa-p256-sha256-1', $jwks[0]['kid']);
-
- // And the bootstrapped key is the active one for outbound signing.
- $signatory = $this->signatoryManager->getLocalJwksSignatory();
- $this->assertSame($jwks[0]['kid'], $signatory->getKeyId());
- }
-
- public function testFirstCallProvisionsActiveKey(): void {
- $signatory = $this->signatoryManager->getLocalJwksSignatory();
- $this->assertNotNull($signatory);
- $this->assertSame('https://alice.example/ocm#ecdsa-p256-sha256-1', $signatory->getKeyId());
-
- $jwks = $this->signatoryManager->getLocalJwks();
- $this->assertCount(1, $jwks);
- $this->assertSame($signatory->getKeyId(), $jwks[0]['kid']);
-
- $listed = $this->signatoryManager->listJwksKeys();
- $this->assertSame([['poolId' => 1, 'kid' => $signatory->getKeyId(), 'slot' => 'active']], $listed);
- }
-
- public function testStageDoesNotChangeActiveSignerButPublishesNewJwk(): void {
- $initial = $this->signatoryManager->getLocalJwksSignatory();
- $staged = $this->signatoryManager->stageJwksKey();
- $this->assertNotSame($initial->getKeyId(), $staged->getKeyId());
-
- // Active signer is unchanged.
- $this->assertSame($initial->getKeyId(), $this->signatoryManager->getLocalJwksSignatory()->getKeyId());
-
- // JWKS now advertises both kids, active first then pending.
- $jwks = $this->signatoryManager->getLocalJwks();
- $this->assertSame([$initial->getKeyId(), $staged->getKeyId()], array_column($jwks, 'kid'));
- }
-
- public function testStageRefusesIfPendingAlreadyExists(): void {
- $this->signatoryManager->stageJwksKey();
- $this->expectException(\RuntimeException::class);
- $this->expectExceptionMessageMatches('/pending JWKS key already exists/');
- $this->signatoryManager->stageJwksKey();
- }
-
- public function testActivatePromotesPendingAndDemotesActive(): void {
- $first = $this->signatoryManager->getLocalJwksSignatory();
- $staged = $this->signatoryManager->stageJwksKey();
- $this->signatoryManager->activateStagedJwksKey();
-
- // New signer is the formerly-staged key.
- $this->assertSame($staged->getKeyId(), $this->signatoryManager->getLocalJwksSignatory()->getKeyId());
-
- // JWKS still advertises the former active key as retiring so peers
- // verifying in-flight signatures with its kid don't fail.
- $kids = array_column($this->signatoryManager->getLocalJwks(), 'kid');
- $this->assertContains($first->getKeyId(), $kids);
- $this->assertContains($staged->getKeyId(), $kids);
- }
-
- public function testActivateRefusesIfRetiringStillPopulated(): void {
- $this->signatoryManager->getLocalJwksSignatory();
- $this->signatoryManager->stageJwksKey();
- $this->signatoryManager->activateStagedJwksKey();
- // Retiring slot is now populated; staging again is allowed but
- // activating must refuse until the admin explicitly retires the old
- // key.
- $this->signatoryManager->stageJwksKey();
- $this->expectException(\RuntimeException::class);
- $this->expectExceptionMessageMatches('/retiring JWKS key still exists/');
- $this->signatoryManager->activateStagedJwksKey();
- }
-
- public function testActivateRefusesWithoutPendingKey(): void {
- $this->signatoryManager->getLocalJwksSignatory();
- $this->expectException(\RuntimeException::class);
- $this->expectExceptionMessageMatches('/no pending JWKS key/');
- $this->signatoryManager->activateStagedJwksKey();
- }
-
- public function testRetireRemovesRetiringKeyFromJwks(): void {
- $first = $this->signatoryManager->getLocalJwksSignatory();
- $staged = $this->signatoryManager->stageJwksKey();
- $this->signatoryManager->activateStagedJwksKey();
- $this->signatoryManager->retireJwksKey();
-
- $kids = array_column($this->signatoryManager->getLocalJwks(), 'kid');
- $this->assertSame([$staged->getKeyId()], $kids);
- // listJwksKeys also drops the retired pool.
- $listed = $this->signatoryManager->listJwksKeys();
- $this->assertCount(1, $listed);
- $this->assertSame($staged->getKeyId(), $listed[0]['kid']);
- $this->assertNotContains($first->getKeyId(), array_column($listed, 'kid'));
- }
-
- public function testRetireRefusesWhenNothingToRetire(): void {
- $this->signatoryManager->getLocalJwksSignatory();
- $this->expectException(\RuntimeException::class);
- $this->expectExceptionMessageMatches('/no retiring JWKS key/');
- $this->signatoryManager->retireJwksKey();
- }
-
- public function testKidStaysStableThroughLifecycle(): void {
- $first = $this->signatoryManager->getLocalJwksSignatory();
- $staged = $this->signatoryManager->stageJwksKey();
- // kid for the staged key must stay the same once it is activated;
- // peers that cached it during the stage window must still resolve it.
- $this->signatoryManager->activateStagedJwksKey();
- $this->assertSame($staged->getKeyId(), $this->signatoryManager->getLocalJwksSignatory()->getKeyId());
-
- $this->signatoryManager->retireJwksKey();
- $this->signatoryManager->stageJwksKey();
- // And every newly minted kid must differ from prior ones, no pool
- // counter rewinding.
- $kids = array_column($this->signatoryManager->listJwksKeys(), 'kid');
- $this->assertNotContains($first->getKeyId(), $kids);
- $this->assertSame($kids, array_unique($kids));
- }
-
- public function testSignerReturnsNullWhenIdentityCannotBeDerived(): void {
- // Replace the signature manager with one that cannot derive an
- // identity at all; provisioning the first key should fail loudly so
- // the admin gets a clear message instead of a corrupt half-state.
- $signatureManager = $this->createMock(ISignatureManager::class);
- $signatureManager->method('generateKeyIdFromConfig')
- ->willThrowException(new IdentityNotFoundException('no configured identity'));
- $urlGenerator = $this->createMock(IURLGenerator::class);
- // getAbsoluteURL() yields no host, so the kid's identity cannot be
- // resolved; provisioning must fail loudly rather than corrupt state.
- $urlGenerator->method('getAbsoluteURL')->willReturn('');
-
- $cacheFactory = $this->createMock(ICacheFactory::class);
- $cacheFactory->method('createDistributed')->willReturn(new ArrayCache(''));
-
- $manager = new OCMSignatoryManager(
- $this->appConfig,
- $signatureManager,
- $urlGenerator,
- $this->identityProofManager,
- $this->stubClientService(),
- $this->createMock(IConfig::class),
- $cacheFactory,
- $this->createMock(LoggerInterface::class),
- );
-
- $this->expectException(\RuntimeException::class);
- $manager->getLocalJwksSignatory();
- }
-
- private function wireAppConfig(): void {
- $this->appConfig->method('hasKey')->willReturnCallback(
- fn (string $app, string $key): bool => $app === 'core' && array_key_exists($key, $this->appConfigStore)
- );
- $this->appConfig->method('getValueInt')->willReturnCallback(
- fn (string $app, string $key, int $default = 0): int => (int)($this->appConfigStore[$key] ?? $default)
- );
- $this->appConfig->method('setValueInt')->willReturnCallback(
- function (string $app, string $key, int $value): bool {
- $this->appConfigStore[$key] = (string)$value;
- return true;
- }
- );
- $this->appConfig->method('getValueString')->willReturnCallback(
- fn (string $app, string $key, string $default = '') => $this->appConfigStore[$key] ?? $default
- );
- $this->appConfig->method('setValueString')->willReturnCallback(
- function (string $app, string $key, string $value): bool {
- $this->appConfigStore[$key] = $value;
- return true;
- }
- );
- $this->appConfig->method('getValueBool')->willReturn(false);
- $this->appConfig->method('deleteKey')->willReturnCallback(
- function (string $app, string $key): void {
- unset($this->appConfigStore[$key]);
- }
- );
- }
-
- private function wireIdentityProofManager(): void {
- $this->identityProofManager->method('hasAppKey')->willReturnCallback(
- fn (string $app, string $name): bool => isset($this->appKeyStore[$app . '/' . $name])
- );
- $this->identityProofManager->method('generateEcdsaP256AppKey')->willReturnCallback(
- function (string $app, string $name): Key {
- $res = openssl_pkey_new([
- 'private_key_type' => OPENSSL_KEYTYPE_EC,
- 'curve_name' => 'prime256v1',
- ]);
- openssl_pkey_export($res, $privatePem);
- $publicPem = openssl_pkey_get_details($res)['key'];
- $key = new Key($publicPem, $privatePem);
- $this->appKeyStore[$app . '/' . $name] = $key;
- return $key;
- }
- );
- $this->identityProofManager->method('getAppKey')->willReturnCallback(
- fn (string $app, string $name): Key => $this->appKeyStore[$app . '/' . $name]
- );
- $this->identityProofManager->method('deleteAppKey')->willReturnCallback(
- function (string $app, string $name): bool {
- $existed = isset($this->appKeyStore[$app . '/' . $name]);
- unset($this->appKeyStore[$app . '/' . $name]);
- return $existed;
- }
- );
- }
-
- private function stubClientService(): IClientService&MockObject {
- $service = $this->createMock(IClientService::class);
- $service->method('newClient')->willReturn($this->createMock(IClient::class));
- return $service;
- }
-}
--- a/tests/lib/OCM/Rfc9421SignatoryManagerTest.php
+++ /dev/null
@@ -1,78 +0,0 @@
-delegate = $this->createMock(OCMSignatoryManager::class);
- $this->wrapper = new Rfc9421SignatoryManager($this->delegate);
- }
-
- public function testGetOptionsForcesRfc9421Format(): void {
- $this->delegate->method('getOptions')->willReturn([
- 'algorithm' => 'rsa-sha512',
- 'rfc9421.format' => false,
- ]);
-
- $options = $this->wrapper->getOptions();
- $this->assertTrue($options['rfc9421.format']);
- $this->assertSame('rsa-sha512', $options['algorithm']);
- }
-
- public function testGetLocalSignatoryReturnsJwksKey(): void {
- $signatory = $this->createMock(Signatory::class);
- $this->delegate->method('getLocalJwksSignatory')->willReturn($signatory);
-
- $this->assertSame($signatory, $this->wrapper->getLocalSignatory());
- }
-
- public function testGetLocalSignatoryThrowsWhenJwksKeyUnavailable(): void {
- $this->delegate->method('getLocalJwksSignatory')->willReturn(null);
-
- $this->expectException(IdentityNotFoundException::class);
- $this->wrapper->getLocalSignatory();
- }
-
- public function testProviderIdDelegated(): void {
- $this->delegate->method('getProviderId')->willReturn('ocm');
- $this->assertSame('ocm', $this->wrapper->getProviderId());
- }
-
- public function testRemoteSignatoryDelegated(): void {
- $signatory = $this->createMock(Signatory::class);
- $this->delegate->expects($this->once())
- ->method('getRemoteSignatory')
- ->with('sender.example.org')
- ->willReturn($signatory);
- $this->assertSame($signatory, $this->wrapper->getRemoteSignatory('sender.example.org'));
- }
-
- public function testRemoteKeyDelegated(): void {
- $key = $this->createMock(Key::class);
- $this->delegate->expects($this->once())
- ->method('getRemoteKey')
- ->with('sender.example.org', 'kid-1')
- ->willReturn($key);
- $this->assertSame($key, $this->wrapper->getRemoteKey('sender.example.org', 'kid-1'));
- }
-}
--- a/tests/lib/OCS/ApiHelperTest.php
+++ /dev/null
@@ -1,49 +0,0 @@
-getMockBuilder(IRequest::class)
- ->disableOriginalConstructor()
- ->getMock();
- $request
- ->expects($this->once())
- ->method('getScriptName')
- ->willReturn($scriptName);
-
- $this->assertEquals($expected, $this->invokePrivate(new ApiHelper, 'isV2', [$request]));
- }
-}
--- a/tests/lib/OCS/DiscoveryServiceTest.php
+++ /dev/null
@@ -1,82 +0,0 @@
-cacheFactory = $this->getMockBuilder(ICacheFactory::class)->getMock();
- $this->clientService = $this->getMockBuilder(IClientService::class)->getMock();
-
- $this->discoveryService = new DiscoveryService(
- $this->cacheFactory,
- $this->clientService
- );
- }
-
- /**
- *
- * @param string $url
- * @param bool $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestIsSafeUrl')]
- public function testIsSafeUrl($url, $expected): void {
- $result = $this->invokePrivate($this->discoveryService, 'isSafeUrl', [$url]);
- $this->assertSame($expected, $result);
- }
-
- public static function dataTestIsSafeUrl(): array {
- return [
- ['api/ocs/v1.php/foo', true],
- ['/api/ocs/v1.php/foo', true],
- ['api/ocs/v1.php/foo/', true],
- ['api/ocs/v1.php/foo-bar/', true],
- ['api/ocs/v1:php/foo', false],
- ['api/ocs//foo', false],
- ];
- }
-
- /**
- *
- * @param array $decodedServices
- * @param string $service
- * @param array $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestGetEndpoints')]
- public function testGetEndpoints($decodedServices, $service, $expected): void {
- $result = $this->invokePrivate($this->discoveryService, 'getEndpoints', [$decodedServices, $service]);
- $this->assertSame($expected, $result);
- }
-
- public static function dataTestGetEndpoints(): array {
- return [
- [['services' => ['myService' => ['endpoints' => []]]], 'myService', []],
- [['services' => ['myService' => ['endpoints' => ['foo' => '/bar']]]], 'myService', ['foo' => '/bar']],
- [['services' => ['myService' => ['endpoints' => ['foo' => '/bar']]]], 'anotherService', []],
- [['services' => ['myService' => ['endpoints' => ['foo' => '/barrequest = $this->getMockBuilder('\\OCP\\IRequest')->getMock();
- $this->appManager = $this->getMockBuilder('\\OCP\\App\\IAppManager')->getMock();
- $this->ocsProvider = new Provider('ocs_provider', $this->request, $this->appManager);
- }
-
- public function testBuildProviderListWithoutAnythingEnabled(): void {
- $this->appManager
- ->expects($this->exactly(4))
- ->method('isEnabledForUser')
- ->willReturnMap([
- ['files_sharing', null, false],
- ['federation', null, false],
- ['activity', null, false],
- ['provisioning_api', null, false],
- ]);
-
- $expected = new JSONResponse(
- [
- 'version' => 2,
- 'services' => [
- 'PRIVATE_DATA' => [
- 'version' => 1,
- 'endpoints' => [
- 'store' => '/ocs/v2.php/privatedata/setattribute',
- 'read' => '/ocs/v2.php/privatedata/getattribute',
- 'delete' => '/ocs/v2.php/privatedata/deleteattribute',
- ],
- ],
- ],
- ]
- );
-
- $this->assertEquals($expected, $this->ocsProvider->buildProviderList());
- }
-
- public function testBuildProviderListWithSharingEnabled(): void {
- $this->appManager
- ->expects($this->exactly(4))
- ->method('isEnabledForUser')
- ->willReturnMap([
- ['files_sharing', null, true],
- ['federation', null, false],
- ['activity', null, false],
- ['provisioning_api', null, false],
- ]);
-
- $expected = new JSONResponse(
- [
- 'version' => 2,
- 'services' => [
- 'PRIVATE_DATA' => [
- 'version' => 1,
- 'endpoints' => [
- 'store' => '/ocs/v2.php/privatedata/setattribute',
- 'read' => '/ocs/v2.php/privatedata/getattribute',
- 'delete' => '/ocs/v2.php/privatedata/deleteattribute',
- ],
- ],
- 'FEDERATED_SHARING' => [
- 'version' => 1,
- 'endpoints' => [
- 'share' => '/ocs/v2.php/cloud/shares',
- 'webdav' => '/public.php/webdav/',
- ],
- ],
- 'SHARING' => [
- 'version' => 1,
- 'endpoints' => [
- 'share' => '/ocs/v2.php/apps/files_sharing/api/v1/shares',
- ],
- ],
- ],
- ]
- );
-
- $this->assertEquals($expected, $this->ocsProvider->buildProviderList());
- }
-
- public function testBuildProviderListWithFederationEnabled(): void {
- $this->appManager
- ->expects($this->exactly(4))
- ->method('isEnabledForUser')
- ->willReturnMap([
- ['files_sharing', null, false],
- ['federation', null, true],
- ['activity', null, false],
- ['provisioning_api', null, false],
- ]);
-
- $expected = new JSONResponse(
- [
- 'version' => 2,
- 'services' => [
- 'PRIVATE_DATA' => [
- 'version' => 1,
- 'endpoints' => [
- 'store' => '/ocs/v2.php/privatedata/setattribute',
- 'read' => '/ocs/v2.php/privatedata/getattribute',
- 'delete' => '/ocs/v2.php/privatedata/deleteattribute',
- ],
- ],
- 'FEDERATED_SHARING' => [
- 'version' => 1,
- 'endpoints' => [
- 'shared-secret' => '/ocs/v2.php/cloud/shared-secret',
- 'system-address-book' => '/remote.php/dav/addressbooks/system/system/system',
- 'carddav-user' => 'system'
- ],
- ],
- ],
- ]
- );
-
- $this->assertEquals($expected, $this->ocsProvider->buildProviderList());
- }
-
- public function testBuildProviderListWithEverythingEnabled(): void {
- $this->appManager
- ->expects($this->any())
- ->method('isEnabledForUser')
- ->willReturn(true);
-
- $expected = new JSONResponse(
- [
- 'version' => 2,
- 'services' => [
- 'PRIVATE_DATA' => [
- 'version' => 1,
- 'endpoints' => [
- 'store' => '/ocs/v2.php/privatedata/setattribute',
- 'read' => '/ocs/v2.php/privatedata/getattribute',
- 'delete' => '/ocs/v2.php/privatedata/deleteattribute',
- ],
- ],
- 'FEDERATED_SHARING' => [
- 'version' => 1,
- 'endpoints' => [
- 'share' => '/ocs/v2.php/cloud/shares',
- 'webdav' => '/public.php/webdav/',
- 'shared-secret' => '/ocs/v2.php/cloud/shared-secret',
- 'system-address-book' => '/remote.php/dav/addressbooks/system/system/system',
- 'carddav-user' => 'system'
- ],
- ],
- 'SHARING' => [
- 'version' => 1,
- 'endpoints' => [
- 'share' => '/ocs/v2.php/apps/files_sharing/api/v1/shares',
- ],
- ],
- 'ACTIVITY' => [
- 'version' => 1,
- 'endpoints' => [
- 'list' => '/ocs/v2.php/cloud/activity',
- ],
- ],
- 'PROVISIONING' => [
- 'version' => 1,
- 'endpoints' => [
- 'user' => '/ocs/v2.php/cloud/users',
- 'groups' => '/ocs/v2.php/cloud/groups',
- 'apps' => '/ocs/v2.php/cloud/apps',
- ],
- ],
- ],
- ]
- );
-
- $this->assertEquals($expected, $this->ocsProvider->buildProviderList());
- }
-}
--- a/tests/lib/OpenMetrics/ExporterManagerTest.php
+++ /dev/null
@@ -1,23 +0,0 @@
-assertInstanceOf(ExporterManager::class, $exporter);
- foreach ($exporter->export() as $metric) {
- $this->assertInstanceOf(IMetricFamily::class, $metric);
- };
- }
-}
--- a/tests/lib/OpenMetrics/Exporters/ActiveSessionsTest.php
+++ /dev/null
@@ -1,33 +0,0 @@
-assertLabelsAre([
- ['time' => 'Last 5 minutes'],
- ['time' => 'Last 15 minutes'],
- ['time' => 'Last hour'],
- ['time' => 'Last day'],
- ]);
- }
-}
--- a/tests/lib/OpenMetrics/Exporters/ActiveUsersTest.php
+++ /dev/null
@@ -1,33 +0,0 @@
-assertLabelsAre([
- ['time' => 'Last 5 minutes'],
- ['time' => 'Last 15 minutes'],
- ['time' => 'Last hour'],
- ['time' => 'Last day'],
- ]);
- }
-}
--- a/tests/lib/OpenMetrics/Exporters/AppEnabledTest.php
+++ /dev/null
@@ -1,53 +0,0 @@
- '0.1.2',
- 'appB' => '1.2.3 beta 4',
- 'appC' => '6.2.1',
- 'appD' => '3.141.5',
- ];
- private array $installedAppsList = [
- 'appA', 'appB', 'appD'
- ];
-
- #[\Override]
- protected function getExporter():IMetricFamily {
- $this->appManager = $this->createMock(IAppManager::class);
- $this->appManager->method('getAppInstalledVersions')
- ->with(false)
- ->willReturn($this->appList);
- $this->appManager->method('getEnabledApps')
- ->willReturn($this->installedAppsList);
-
- return new AppEnabled($this->appManager);
- }
-
- public function testMetrics(): void {
- $this->assertCount(4, $this->metrics);
- foreach ($this->appList as $appId => $appVersion) {
- $metricForApp = array_find($this->metrics, function (Metric $metric) use ($appId) {
- return $metric->label('app_id') === $appId;
- });
-
- $expectedMetricValue = in_array($appId, $this->installedAppsList) ? 1 : 0;
- $this->assertEquals($expectedMetricValue, $metricForApp->value);
- $this->assertSame(['app_id' => $appId, 'version' => $appVersion], $metricForApp->labels);
- }
- }
-}
--- a/tests/lib/OpenMetrics/Exporters/ExporterTestCase.php
+++ /dev/null
@@ -1,77 +0,0 @@
-exporter = $this->getExporter();
- $this->metrics = iterator_to_array($this->exporter->metrics());
- }
-
- public function testNotEmptyData(): void {
- $this->assertNotEmpty($this->metrics);
- }
-
- public function testValidExporterName(): void {
- $exporterName = $this->exporter->name();
- $this->assertMatchesRegularExpression('/^[a-z_:][a-z0-9_:]*$/i', $exporterName, );
-
- $unit = $this->exporter->unit();
- if ($unit === '') {
- return;
- }
- // Unit name must follow metric name format
- $this->assertMatchesRegularExpression('/^[a-z_:][a-z0-9_:]*$/i', $unit);
- // Unit name must be a suffix in exporter name
- $this->assertMatchesRegularExpression(
- '/(^|_)' . $unit . '$/',
- $exporterName,
- 'Metric name "' . $exporterName . '" must contains unit "' . $unit . '" as a suffix',
- );
- }
-
- public function testValidLabelKey(): void {
- $labelNames = [];
- foreach ($this->metrics as $metric) {
- foreach ($metric->labels as $label => $value) {
- $labelNames[$label] = $label;
- }
- }
-
- if (empty($labelNames)) {
- $this->expectNotToPerformAssertions();
- return;
- }
- foreach ($labelNames as $label) {
- $this->assertMatchesRegularExpression('/^[a-z_][a-z0-9_]*$/i', $label);
- }
-
- }
-
- protected function assertLabelsAre(array $expectedLabels): void {
- $foundLabels = [];
- foreach ($this->metrics as $metric) {
- $foundLabels[] = $metric->labels;
- }
-
- $this->assertSame($foundLabels, $expectedLabels);
- }
-}
--- a/tests/lib/OpenMetrics/Exporters/FilesByTypeTest.php
+++ /dev/null
@@ -1,30 +0,0 @@
-systemConfig = $this->createMock(SystemConfig::class);
- $this->serverVersion = $this->createMock(ServerVersion::class);
- $this->serverVersion->method('getHumanVersion')->willReturn('33.13.17 Gold');
- $this->serverVersion->method('getVersion')->willReturn([33, 13, 17]);
- $this->serverVersion->method('getBuild')->willReturn('dev');
-
- return new InstanceInfo($this->systemConfig, $this->serverVersion);
- }
-
- public function testMetrics(): void {
- $this->assertCount(1, $this->metrics);
- $metric = array_pop($this->metrics);
- $this->assertSame([
- 'full_version' => '33.13.17 Gold',
- 'major_version' => '33',
- 'build' => 'dev',
- 'installed' => '0',
- ], $metric->labels);
- }
-}
--- a/tests/lib/OpenMetrics/Exporters/LogLevelTest.php
+++ /dev/null
@@ -1,22 +0,0 @@
- 42,
- 'backend B' => 51,
- 'backend C' => 0,
- ];
-
- #[\Override]
- protected function getExporter():IMetricFamily {
- $this->userManager = $this->createMock(IUserManager::class);
- $this->userManager->method('countUsers')
- ->with(true)
- ->willReturn($this->backendList);
- return new UsersByBackend($this->userManager);
- }
-
- public function testMetrics(): void {
- foreach ($this->metrics as $metric) {
- $this->assertEquals($this->backendList[$metric->label('backend')], $metric->value);
- }
- }
-}
--- a/tests/lib/Preview/BackgroundCleanupJobTest.php
+++ /dev/null
@@ -1,146 +0,0 @@
-userId = $this->getUniqueID();
- $user = $this->createUser($this->userId, $this->userId);
-
- $storage = new Temporary([]);
- $this->registerMount($this->userId, $storage, '');
-
- $this->loginAsUser($this->userId);
- $this->logout();
- $this->loginAsUser($this->userId);
-
- $appManager = Server::get(IAppManager::class);
- $this->trashEnabled = $appManager->isEnabledForUser('files_trashbin', $user);
- $appManager->disableApp('files_trashbin');
-
- $this->connection = Server::get(IDBConnection::class);
- $this->previewManager = Server::get(IPreview::class);
- $this->rootFolder = Server::get(IRootFolder::class);
- $this->mimeTypeLoader = Server::get(IMimeTypeLoader::class);
- $this->timeFactory = Server::get(ITimeFactory::class);
- $this->previewService = Server::get(PreviewService::class);
- }
-
- #[\Override]
- protected function tearDown(): void {
- if ($this->trashEnabled) {
- $appManager = Server::get(IAppManager::class);
- $appManager->enableApp('files_trashbin');
- }
-
- $this->logout();
-
- foreach ($this->previewService->getAvailablePreviewsForFile(5) as $preview) {
- $this->previewService->deletePreview($preview);
- }
-
- parent::tearDown();
- }
-
- private function setup11Previews(): array {
- $userFolder = $this->rootFolder->getUserFolder($this->userId);
-
- $files = [];
- foreach (range(0, 10) as $i) {
- $file = $userFolder->newFile($i . '.txt');
- $file->putContent('hello world!');
- $this->previewManager->getPreview($file);
- $files[] = $file;
- }
-
- return $files;
- }
-
- private function countPreviews(PreviewService $previewService, array $fileIds): int {
- $previews = $previewService->getAvailablePreviews($fileIds);
- return array_reduce($previews, fn (int $result, array $previews) => $result + count($previews), 0);
- }
-
- public function testCleanupSystemCron(): void {
- $files = $this->setup11Previews();
- $fileIds = array_map(fn (File $f): int => $f->getId(), $files);
-
- $this->assertSame(11, $this->countPreviews($this->previewService, $fileIds));
- $job = new BackgroundCleanupJob($this->timeFactory, $this->connection, $this->previewService, true);
- $job->run([]);
-
- foreach ($files as $file) {
- $file->delete();
- }
-
- $this->assertSame(11, $this->countPreviews($this->previewService, $fileIds));
- $job->run([]);
-
- $this->assertSame(0, $this->countPreviews($this->previewService, $fileIds));
- }
-
- public function testCleanupAjax(): void {
- if ($this->connection->getShardDefinition('filecache')) {
- $this->markTestSkipped('ajax cron is not supported for sharded setups');
- }
- $files = $this->setup11Previews();
- $fileIds = array_map(fn (File $f): int => $f->getId(), $files);
-
- $this->assertSame(11, $this->countPreviews($this->previewService, $fileIds));
- $job = new BackgroundCleanupJob($this->timeFactory, $this->connection, $this->previewService, false);
- $job->run([]);
-
- foreach ($files as $file) {
- $file->delete();
- }
-
- $this->assertSame(11, $this->countPreviews($this->previewService, $fileIds));
- $job->run([]);
-
- $this->assertSame(1, $this->countPreviews($this->previewService, $fileIds));
- $job->run([]);
-
- $this->assertSame(0, $this->countPreviews($this->previewService, $fileIds));
- }
-}
--- a/tests/lib/Preview/GeneratorTest.php
+++ /dev/null
@@ -1,526 +0,0 @@
-config = $this->createMock(IConfig::class);
- $this->appConfig = $this->createMock(IAppConfig::class);
- $this->previewManager = $this->createMock(IPreview::class);
- $this->helper = $this->createMock(GeneratorHelper::class);
- $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->previewMapper = $this->createMock(PreviewMapper::class);
- $this->storageFactory = $this->createMock(StorageFactory::class);
- $this->migrationService = $this->createMock(PreviewMigrationService::class);
-
- $this->generator = new Generator(
- $this->config,
- $this->appConfig,
- $this->previewManager,
- $this->helper,
- $this->eventDispatcher,
- $this->logger,
- $this->previewMapper,
- $this->storageFactory,
- $this->migrationService,
- );
- }
-
- private function getFile(int $fileId, string $mimeType, bool $hasVersion = false): File {
- $mountPoint = $this->createMock(IMountPoint::class);
- $mountPoint->method('getNumericStorageId')->willReturn(42);
- if ($hasVersion) {
- $file = $this->createMock(VersionedPreviewFile::class);
- $file->method('getPreviewVersion')->willReturn('abc');
- } else {
- $file = $this->createMock(File::class);
- }
- $file->method('isReadable')
- ->willReturn(true);
- $file->method('getMimeType')
- ->willReturn($mimeType);
- $file->method('getId')
- ->willReturn($fileId);
- $file->method('getMountPoint')
- ->willReturn($mountPoint);
- return $file;
- }
-
- #[TestWith([true])]
- #[TestWith([false])]
- public function testGetCachedPreview(bool $hasPreview): void {
- $file = $this->getFile(42, 'myMimeType', $hasPreview);
-
- $this->previewManager->method('isMimeSupported')
- ->with($this->equalTo('myMimeType'))
- ->willReturn(true);
-
- $maxPreview = new Preview();
- $maxPreview->setWidth(1000);
- $maxPreview->setHeight(1000);
- $maxPreview->setMax(true);
- $maxPreview->setSize(1000);
- $maxPreview->setCropped(false);
- $maxPreview->setStorageId(1);
- $maxPreview->setVersion($hasPreview ? 'abc' : null);
- $maxPreview->setMimeType('image/png');
-
- $previewFile = new Preview();
- $previewFile->setWidth(256);
- $previewFile->setHeight(256);
- $previewFile->setMax(false);
- $previewFile->setSize(1000);
- $previewFile->setVersion($hasPreview ? 'abc' : null);
- $previewFile->setCropped(false);
- $previewFile->setStorageId(1);
- $previewFile->setMimeType('image/png');
-
- $this->previewMapper->method('getAvailablePreviews')
- ->with($this->equalTo([42]))
- ->willReturn([42 => [
- $maxPreview,
- $previewFile,
- ]]);
-
- $this->eventDispatcher->expects($this->once())
- ->method('dispatchTyped')
- ->with(new BeforePreviewFetchedEvent($file, 100, 100, false, IPreview::MODE_FILL, null));
-
- $result = $this->generator->getPreview($file, 100, 100);
- $this->assertSame($hasPreview ? 'abc-256-256.png' : '256-256.png', $result->getName());
- $this->assertSame(1000, $result->getSize());
- }
-
- #[TestWith([true])]
- #[TestWith([false])]
- public function testGetNewPreview(bool $hasVersion): void {
- $file = $this->getFile(42, 'myMimeType', $hasVersion);
-
- $this->previewManager->method('isMimeSupported')
- ->with($this->equalTo('myMimeType'))
- ->willReturn(true);
-
- $this->previewMapper->method('getAvailablePreviews')
- ->with($this->equalTo([42]))
- ->willReturn([42 => []]);
-
- $this->config->method('getSystemValueString')
- ->willReturnCallback(function ($key, $default) {
- return $default;
- });
-
- $this->config->method('getSystemValueInt')
- ->willReturnCallback(function ($key, $default) {
- return $default;
- });
-
- $invalidProvider = $this->createMock(IProviderV2::class);
- $invalidProvider->method('isAvailable')
- ->willReturn(true);
- $unavailableProvider = $this->createMock(IProviderV2::class);
- $unavailableProvider->method('isAvailable')
- ->willReturn(false);
- $validProvider = $this->createMock(IProviderV2::class);
- $validProvider->method('isAvailable')
- ->with($file)
- ->willReturn(true);
-
- $this->previewManager->method('getProviders')
- ->willReturn([
- '/image\/png/' => ['wrongProvider'],
- '/myMimeType/' => ['brokenProvider', 'invalidProvider', 'unavailableProvider', 'validProvider'],
- ]);
-
- $this->helper->method('getProvider')
- ->willReturnCallback(function ($provider) use ($invalidProvider, $validProvider, $unavailableProvider) {
- if ($provider === 'wrongProvider') {
- $this->fail('Wrongprovider should not be constructed!');
- } elseif ($provider === 'brokenProvider') {
- return false;
- } elseif ($provider === 'invalidProvider') {
- return $invalidProvider;
- } elseif ($provider === 'validProvider') {
- return $validProvider;
- } elseif ($provider === 'unavailableProvider') {
- return $unavailableProvider;
- }
- $this->fail('Unexpected provider requested');
- });
-
- $image = $this->createMock(IImage::class);
- $image->method('width')->willReturn(2048);
- $image->method('height')->willReturn(2048);
- $image->method('valid')->willReturn(true);
- $image->method('dataMimeType')->willReturn('image/png');
-
- $this->helper->method('getThumbnail')
- ->willReturnCallback(function ($provider, $file, $x, $y) use ($invalidProvider, $validProvider, $image): false|IImage {
- if ($provider === $validProvider) {
- return $image;
- } else {
- return false;
- }
- });
-
- $image->method('data')
- ->willReturn('my data');
-
- $this->previewMapper->method('insert')
- ->willReturnCallback(fn (Preview $preview): Preview => $preview);
-
- $this->previewMapper->method('update')
- ->willReturnCallback(fn (Preview $preview): Preview => $preview);
-
- $this->storageFactory->method('writePreview')
- ->willReturnCallback(function (Preview $preview, mixed $data) use ($hasVersion): int {
- $data = stream_get_contents($data);
- if ($hasVersion) {
- switch ($preview->getName()) {
- case 'abc-2048-2048-max.png':
- $this->assertSame('my data', $data);
- return 1000;
- case 'abc-256-256.png':
- $this->assertSame('my resized data', $data);
- return 1000;
- }
- } else {
- switch ($preview->getName()) {
- case '2048-2048-max.png':
- $this->assertSame('my data', $data);
- return 1000;
- case '256-256.png':
- $this->assertSame('my resized data', $data);
- return 1000;
- }
- }
- $this->fail('file name is wrong:' . $preview->getName());
- });
-
- $image = $this->getMockImage(2048, 2048, 'my resized data');
- $this->helper->method('getImage')
- ->willReturn($image);
-
- $this->eventDispatcher->expects($this->once())
- ->method('dispatchTyped')
- ->with(new BeforePreviewFetchedEvent($file, 100, 100, false, IPreview::MODE_FILL, null));
-
- $result = $this->generator->getPreview($file, 100, 100);
- $this->assertSame($hasVersion ? 'abc-256-256.png' : '256-256.png', $result->getName());
- $this->assertSame(1000, $result->getSize());
- }
-
- public function testMigrateOldPreview(): void {
- $file = $this->getFile(42, 'myMimeType', false);
-
- $maxPreview = new Preview();
- $maxPreview->setWidth(1000);
- $maxPreview->setHeight(1000);
- $maxPreview->setMax(true);
- $maxPreview->setSize(1000);
- $maxPreview->setCropped(false);
- $maxPreview->setStorageId(1);
- $maxPreview->setVersion(null);
- $maxPreview->setMimeType('image/png');
-
- $previewFile = new Preview();
- $previewFile->setWidth(256);
- $previewFile->setHeight(256);
- $previewFile->setMax(false);
- $previewFile->setSize(1000);
- $previewFile->setVersion(null);
- $previewFile->setCropped(false);
- $previewFile->setStorageId(1);
- $previewFile->setMimeType('image/png');
-
- $this->previewManager->method('isMimeSupported')
- ->with($this->equalTo('myMimeType'))
- ->willReturn(true);
-
- $this->previewMapper->method('getAvailablePreviews')
- ->with($this->equalTo([42]))
- ->willReturn([42 => []]);
-
- $this->config->method('getSystemValueString')
- ->willReturnCallback(function ($key, $default) {
- return $default;
- });
-
- $this->config->method('getSystemValueInt')
- ->willReturnCallback(function ($key, $default) {
- return $default;
- });
-
- $this->appConfig->method('getValueBool')
- ->willReturnCallback(fn ($app, $key, $default) => match ($key) {
- ConfigLexicon::ON_DEMAND_PREVIEW_MIGRATION => true,
- 'previewMovedDone' => false,
- });
-
- $this->migrationService->expects($this->exactly(1))
- ->method('migrateFileId')
- ->willReturn([$maxPreview, $previewFile]);
-
- $result = $this->generator->getPreview($file, 100, 100);
- $this->assertSame('256-256.png', $result->getName());
- $this->assertSame(1000, $result->getSize());
- }
-
- public function testInvalidMimeType(): void {
- $this->expectException(NotFoundException::class);
-
- $file = $this->getFile(42, 'invalidType');
-
- $this->previewManager->method('isMimeSupported')
- ->with('invalidType')
- ->willReturn(false);
-
- $maxPreview = new Preview();
- $maxPreview->setWidth(2048);
- $maxPreview->setHeight(2048);
- $maxPreview->setMax(true);
- $maxPreview->setSize(1000);
- $maxPreview->setVersion(null);
- $maxPreview->setMimetype('image/png');
-
- $this->previewMapper->method('getAvailablePreviews')
- ->with($this->equalTo([42]))
- ->willReturn([42 => [
- $maxPreview,
- ]]);
-
- $this->eventDispatcher->expects($this->once())
- ->method('dispatchTyped')
- ->with(new BeforePreviewFetchedEvent($file, 1024, 512, true, IPreview::MODE_COVER, 'invalidType'));
-
- $this->generator->getPreview($file, 1024, 512, true, IPreview::MODE_COVER, 'invalidType');
- }
-
- public function testReturnCachedPreviewsWithoutCheckingSupportedMimetype(): void {
- $file = $this->getFile(42, 'myMimeType');
-
- $maxPreview = new Preview();
- $maxPreview->setWidth(2048);
- $maxPreview->setHeight(2048);
- $maxPreview->setMax(true);
- $maxPreview->setSize(1000);
- $maxPreview->setVersion(null);
- $maxPreview->setMimeType('image/png');
-
- $previewFile = new Preview();
- $previewFile->setWidth(1024);
- $previewFile->setHeight(512);
- $previewFile->setMax(false);
- $previewFile->setSize(1000);
- $previewFile->setCropped(true);
- $previewFile->setVersion(null);
- $previewFile->setMimeType('image/png');
-
- $this->previewMapper->method('getAvailablePreviews')
- ->with($this->equalTo([42]))
- ->willReturn([42 => [
- $maxPreview,
- $previewFile,
- ]]);
-
- $this->previewManager->expects($this->never())
- ->method('isMimeSupported');
-
- $this->eventDispatcher->expects($this->once())
- ->method('dispatchTyped')
- ->with(new BeforePreviewFetchedEvent($file, 1024, 512, true, IPreview::MODE_COVER, 'invalidType'));
-
- $result = $this->generator->getPreview($file, 1024, 512, true, IPreview::MODE_COVER, 'invalidType');
- $this->assertSame('1024-512-crop.png', $result->getName());
- }
-
- public function testNoProvider(): void {
- $file = $this->getFile(42, 'myMimeType');
-
- $this->previewMapper->method('getAvailablePreviews')
- ->with($this->equalTo([42]))
- ->willReturn([42 => []]);
-
- $this->previewManager->method('getProviders')
- ->willReturn([]);
-
- $this->eventDispatcher->expects($this->once())
- ->method('dispatchTyped')
- ->with(new BeforePreviewFetchedEvent($file, 100, 100, false, IPreview::MODE_FILL, null));
-
- $this->expectException(NotFoundException::class);
- $this->generator->getPreview($file, 100, 100);
- }
-
- private function getMockImage(int $width, int $height, string $data = '') {
- $image = $this->createMock(IImage::class);
- $image->method('height')->willReturn($width);
- $image->method('width')->willReturn($height);
- $image->method('valid')->willReturn(true);
- $image->method('dataMimeType')->willReturn('image/png');
- $image->method('data')->willReturn($data);
-
- $image->method('resizeCopy')->willReturnCallback(function ($size) use ($data) {
- return $this->getMockImage($size, $size, $data);
- });
- $image->method('preciseResizeCopy')->willReturnCallback(function ($width, $height) use ($data) {
- return $this->getMockImage($width, $height, $data);
- });
- $image->method('cropCopy')->willReturnCallback(function ($x, $y, $width, $height) use ($data) {
- return $this->getMockImage($width, $height, $data);
- });
-
- return $image;
- }
-
- public static function dataSize(): array {
- return [
- [1024, 2048, 512, 512, false, IPreview::MODE_FILL, 256, 512],
- [1024, 2048, 512, 512, false, IPreview::MODE_COVER, 512, 1024],
- [1024, 2048, 512, 512, true, IPreview::MODE_FILL, 1024, 1024],
- [1024, 2048, 512, 512, true, IPreview::MODE_COVER, 1024, 1024],
-
- [1024, 2048, -1, 512, false, IPreview::MODE_COVER, 256, 512],
- [1024, 2048, 512, -1, false, IPreview::MODE_FILL, 512, 1024],
-
- [1024, 2048, 250, 1100, true, IPreview::MODE_COVER, 256, 1126],
- [1024, 1100, 250, 1100, true, IPreview::MODE_COVER, 250, 1100],
-
- [1024, 2048, 4096, 2048, false, IPreview::MODE_FILL, 1024, 2048],
- [1024, 2048, 4096, 2048, false, IPreview::MODE_COVER, 1024, 2048],
-
- [2048, 1024, 512, 512, false, IPreview::MODE_FILL, 512, 256],
- [2048, 1024, 512, 512, false, IPreview::MODE_COVER, 1024, 512],
- [2048, 1024, 512, 512, true, IPreview::MODE_FILL, 1024, 1024],
- [2048, 1024, 512, 512, true, IPreview::MODE_COVER, 1024, 1024],
-
- [2048, 1024, -1, 512, false, IPreview::MODE_FILL, 1024, 512],
- [2048, 1024, 512, -1, false, IPreview::MODE_COVER, 512, 256],
-
- [2048, 1024, 4096, 1024, true, IPreview::MODE_FILL, 2048, 512],
- [2048, 1024, 4096, 1024, true, IPreview::MODE_COVER, 2048, 512],
-
- //Test minimum size
- [2048, 1024, 32, 32, false, IPreview::MODE_FILL, 64, 32],
- [2048, 1024, 32, 32, false, IPreview::MODE_COVER, 64, 32],
- [2048, 1024, 32, 32, true, IPreview::MODE_FILL, 64, 64],
- [2048, 1024, 32, 32, true, IPreview::MODE_COVER, 64, 64],
- ];
- }
-
- #[DataProvider('dataSize')]
- public function testCorrectSize(int $maxX, int $maxY, int $reqX, int $reqY, bool $crop, string $mode, int $expectedX, int $expectedY): void {
- $file = $this->getFile(42, 'myMimeType');
-
- $this->previewManager->method('isMimeSupported')
- ->with($this->equalTo('myMimeType'))
- ->willReturn(true);
-
- $maxPreview = new Preview();
- $maxPreview->setWidth($maxX);
- $maxPreview->setHeight($maxY);
- $maxPreview->setMax(true);
- $maxPreview->setSize(1000);
- $maxPreview->setVersion(null);
- $maxPreview->setMimeType('image/png');
-
- $this->assertSame($maxPreview->getName(), $maxX . '-' . $maxY . '-max.png');
- $this->assertSame($maxPreview->getMimeType(), 'image/png');
-
- $this->previewMapper->method('getAvailablePreviews')
- ->with($this->equalTo([42]))
- ->willReturn([42 => [
- $maxPreview,
- ]]);
-
- $filename = $expectedX . '-' . $expectedY;
- if ($crop) {
- $filename .= '-crop';
- }
- $filename .= '.png';
-
- $image = $this->getMockImage($maxX, $maxY);
- $this->helper->method('getImage')
- ->willReturn($image);
-
- $this->previewMapper->method('insert')
- ->willReturnCallback(function (Preview $preview) use ($filename): Preview {
- $this->assertSame($preview->getName(), $filename);
- return $preview;
- });
-
- $this->previewMapper->method('update')
- ->willReturnCallback(fn (Preview $preview): Preview => $preview);
-
- $this->storageFactory->method('writePreview')
- ->willReturn(1000);
-
- $this->eventDispatcher->expects($this->once())
- ->method('dispatchTyped')
- ->with(new BeforePreviewFetchedEvent($file, $reqX, $reqY, $crop, $mode, null));
-
- $result = $this->generator->getPreview($file, $reqX, $reqY, $crop, $mode);
- if ($expectedX === $maxX && $expectedY === $maxY) {
- $this->assertSame($maxPreview->getName(), $result->getName());
- } else {
- $this->assertSame($filename, $result->getName());
- }
- }
-
- public function testUnreadbleFile(): void {
- $file = $this->createMock(File::class);
- $file->method('isReadable')
- ->willReturn(false);
-
- $this->expectException(NotFoundException::class);
-
- $this->generator->getPreview($file, 100, 100, false);
- }
-}
--- a/tests/lib/Preview/HEICTest.php
+++ /dev/null
@@ -1,47 +0,0 @@
-markTestSkipped('ImageMagick is not HEIC aware. Skipping tests');
- }
-
- $fileName = 'testimage.heic';
- $sourcePath = \OC::$SERVERROOT . '/tests/data/' . $fileName;
-
- // queryFormats() only reports that the HEIC coder is registered, not that
- // ImageMagick can actually decode a HEIC file: the libheif delegate may be
- // missing or the coder may be disabled by ImageMagick's policy.xml. In that
- // case decoding throws, the provider returns null and the tests fail instead
- // of being skipped. Verify a real decode before running the tests.
- try {
- (new \Imagick())->readImage($sourcePath . '[0]');
- } catch (\ImagickException $e) {
- $this->markTestSkipped('ImageMagick cannot decode HEIC in this environment: ' . $e->getMessage() . '. Skipping tests');
- }
-
- parent::setUp();
-
- $this->imgPath = $this->prepareTestFile($fileName, $sourcePath);
- $this->width = 1680;
- $this->height = 1050;
- $this->provider = new HEIC;
- }
-}
--- a/tests/lib/Preview/ImageTest.php
+++ /dev/null
@@ -1,31 +0,0 @@
-imgPath = $this->prepareTestFile($fileName, \OC::$SERVERROOT . '/tests/data/' . $fileName);
- $this->width = 1680;
- $this->height = 1050;
- $this->provider = new JPEG();
- }
-}
--- a/tests/lib/Preview/MP3Test.php
+++ /dev/null
@@ -1,31 +0,0 @@
-imgPath = $this->prepareTestFile($fileName, \OC::$SERVERROOT . '/tests/data/' . $fileName);
- $this->width = 200;
- $this->height = 200;
- $this->provider = new MP3;
- }
-}
--- a/tests/lib/Preview/MovieBrokenStuckFfmpegTest.php
+++ /dev/null
@@ -1,20 +0,0 @@
-findBinaryPath('ffmpeg');
-
- if (is_string($movieBinary)) {
- parent::setUp();
-
- $this->imgPath = $this->prepareTestFile($this->fileName, \OC::$SERVERROOT . '/tests/data/' . $this->fileName);
- $this->provider = new Movie(['movieBinary' => $movieBinary]);
- } else {
- $this->markTestSkipped('No Movie provider present');
- }
- }
-}
--- a/tests/lib/Preview/MovieTestRemoteFile.php
+++ /dev/null
@@ -1,72 +0,0 @@
-findBinaryPath('ffmpeg');
- if (is_string($movieBinary)) {
- parent::setUp();
- $this->imgPath = $this->prepareTestFile($this->fileName, \OC::$SERVERROOT . '/tests/data/' . $this->fileName);
- $this->provider = new Movie(['movieBinary' => $movieBinary]);
- } else {
- $this->markTestSkipped('No Movie provider present');
- }
- }
-
- #[\Override]
- #[\PHPUnit\Framework\Attributes\DataProvider('dimensionsDataProvider')]
- public function testGetThumbnail($widthAdjustment, $heightAdjustment): void {
- $ratio = round($this->width / $this->height, 2);
- $this->maxWidth = $this->width - $widthAdjustment;
- $this->maxHeight = $this->height - $heightAdjustment;
- $file = new File(Server::get(IRootFolder::class), $this->rootView, $this->imgPath);
- // Create mock remote file to be passed
- $remoteStorage = $this->createMock(Storage::class);
- $remoteStorage->method('isLocal')
- ->willReturn(false);
- $mockRemoteVideo = $this->createMock(File::class);
- $mockRemoteVideo->method('getStorage')
- ->willReturn($remoteStorage);
- $mockRemoteVideo->method('getSize')
- ->willReturn($file->getSize());
- $mockRemoteVideo->method('fopen')
- ->with('r')
- ->willreturn($file->fopen('r'));
- $remotePreview = $this->provider->getThumbnail($mockRemoteVideo, $this->maxWidth, $this->maxHeight, $this->scalingUp);
- $localPreview = $this->provider->getThumbnail($file, $this->maxWidth, $this->maxHeight, $this->scalingUp);
- $this->assertNotFalse($remotePreview);
- $this->assertTrue($remotePreview->valid());
- $this->assertEquals($remotePreview->data(), $localPreview->data());
- }
-}
--- a/tests/lib/Preview/OfficeTest.php
+++ /dev/null
@@ -1,41 +0,0 @@
-findBinaryPath('libreoffice');
- $openofficeBinary = $libreofficeBinary === false ? $binaryFinder->findBinaryPath('openoffice') : false;
-
- if ($libreofficeBinary !== false || $openofficeBinary !== false) {
- parent::setUp();
-
- $fileName = 'testimage.odt';
- $this->imgPath = $this->prepareTestFile($fileName, \OC::$SERVERROOT . '/tests/data/' . $fileName);
- $this->width = 595;
- $this->height = 842;
- $this->provider = new OpenDocument;
- } else {
- $this->markTestSkipped('No Office provider present');
- }
- }
-}
--- a/tests/lib/Preview/PostscriptTest.php
+++ /dev/null
@@ -1,39 +0,0 @@
-markTestSkipped('Imagick does not support postscript.');
- }
- if (Server::get(BinaryFinder::class)->findBinaryPath('gs') === false) {
- // Imagick forwards postscript rendering to Ghostscript but does not report this in queryFormats
- $this->markTestSkipped('Ghostscript is not installed.');
- }
-
- parent::setUp();
-
- $fileName = 'testimage.eps';
- $this->imgPath = $this->prepareTestFile($fileName, \OC::$SERVERROOT . '/tests/data/' . $fileName);
- $this->width = 2400;
- $this->height = 1707;
- $this->provider = new Postscript;
- }
-}
--- a/tests/lib/Preview/PreviewMapperTest.php
+++ /dev/null
@@ -1,176 +0,0 @@
-previewMapper = Server::get(PreviewMapper::class);
- $this->connection = Server::get(IDBConnection::class);
- $this->snowflake = Server::get(ISnowflakeGenerator::class);
- $this->mimeTypeLoader = Server::get(IMimeTypeLoader::class);
-
- $qb = $this->connection->getQueryBuilder();
- $qb->delete('preview_locations')->executeStatement();
-
- $qb = $this->connection->getQueryBuilder();
- $qb->delete('preview_versions')->executeStatement();
-
- $qb = $this->connection->getQueryBuilder();
- $qb->delete('previews')->executeStatement();
- }
-
- #[\Override]
- public function tearDown(): void {
- $this->previewMapper->deleteAll();
- parent::tearDown();
- }
-
- public function testGetAvailablePreviews(): void {
- // Empty
- $this->assertEquals([], $this->previewMapper->getAvailablePreviews([]));
-
- // No preview available
- $this->assertEquals([42 => []], $this->previewMapper->getAvailablePreviews([42]));
-
- $this->createPreviewForFileId(42);
- $previews = $this->previewMapper->getAvailablePreviews([42]);
- $this->assertNotEmpty($previews[42]);
- $this->assertNull($previews[42][0]->getLocationId());
- $this->assertNull($previews[42][0]->getBucketName());
- $this->assertNull($previews[42][0]->getObjectStoreName());
-
- $this->createPreviewForFileId(43, 2);
- $previews = $this->previewMapper->getAvailablePreviews([43]);
- $this->assertNotEmpty($previews[43]);
- $this->assertEquals('preview-2', $previews[43][0]->getBucketName());
- $this->assertEquals('default', $previews[43][0]->getObjectStoreName());
- }
-
- private function createPreviewForFileId(int $fileId, ?int $bucket = null, int $size = 100, ?string $version = null, bool $cropped = true): string {
- $locationId = null;
- if ($bucket) {
- $qb = $this->connection->getQueryBuilder();
- $locationId = $this->snowflake->nextId();
- $qb->insert('preview_locations')
- ->values([
- 'id' => $locationId,
- 'bucket_name' => $qb->createNamedParameter('preview-' . $bucket),
- 'object_store_name' => $qb->createNamedParameter('default'),
- ]);
- $qb->executeStatement();
- }
- $preview = new Preview();
- $preview->generateId();
- $preview->setFileId($fileId);
- $preview->setStorageId(1);
- $preview->setCropped($cropped);
- $preview->setMax(true);
- $preview->setWidth($size);
- $preview->setSourceMimeType('image/jpeg');
- $preview->setHeight($size);
- $preview->setSize(100);
- $preview->setMtime(time());
- $preview->setMimetype('image/jpeg');
- $preview->setEtag('abcdefg');
- $preview->setVersion($version);
-
- if ($locationId !== null) {
- $preview->setLocationId($locationId);
- }
- $this->previewMapper->insert($preview);
-
- return $preview->id;
- }
-
- /**
- * The previews table is joined with preview_versions, which also has a
- * file_id column, so the condition has to be qualified with the alias.
- */
- public function testGetByFileId(): void {
- $fileId = 4242;
- $this->createPreviewForFileId($fileId);
- $this->createPreviewForFileId($fileId, size: 256);
- $this->createPreviewForFileId(4243);
-
- $previews = iterator_to_array($this->previewMapper->getByFileId($fileId));
-
- $this->assertCount(2, $previews);
- foreach ($previews as $preview) {
- $this->assertSame($fileId, $preview->getFileId());
- }
- }
-
- /**
- * Same ambiguity, reached through the specification lookup that
- * Generator::savePreview() uses to recover from a unique constraint
- * violation. It passes the cropped flag as a PHP bool, and false is the
- * common case, so both values have to be covered.
- */
- #[\PHPUnit\Framework\Attributes\TestWith([false])]
- #[\PHPUnit\Framework\Attributes\TestWith([true])]
- public function testGetPreviewForSpecification(bool $cropped): void {
- $fileId = 4244;
- $previewId = $this->createPreviewForFileId($fileId, cropped: $cropped);
-
- $preview = $this->previewMapper->getPreviewForSpecification([
- 'file_id' => $fileId,
- 'width' => 100,
- 'height' => 100,
- 'mimetype_id' => $this->mimeTypeLoader->getId('image/jpeg'),
- 'cropped' => $cropped,
- 'version_id' => '-1',
- ]);
-
- $this->assertNotNull($preview);
- $this->assertEquals($previewId, $preview->getId());
- }
-
- /**
- * version lives in the joined preview_versions table, so it has to keep
- * resolving to that alias rather than to the previews table.
- */
- public function testGetPreviewForSpecificationOnJoinedColumn(): void {
- $fileId = 4245;
- $previewId = $this->createPreviewForFileId($fileId, version: '1000');
-
- $preview = $this->previewMapper->getPreviewForSpecification([
- 'file_id' => $fileId,
- 'version' => '1000',
- ]);
-
- $this->assertNotNull($preview);
- $this->assertEquals($previewId, $preview->getId());
- }
-
- public function testLargeIdInsertRetrieve(): void {
- $fileId = PHP_INT_MAX;
- $originalPreviewId = $this->createPreviewForFileId($fileId);
-
- $dbPreview = $this->previewMapper->getAvailablePreviews([$fileId])[$fileId][0];
- $this->assertEquals($originalPreviewId, $dbPreview->id);
- $this->assertEquals($fileId, $dbPreview->getFileId());
- }
-}
--- a/tests/lib/Preview/PreviewMigrationJobTest.php
+++ /dev/null
@@ -1,346 +0,0 @@
-previewAppData = Server::get(IAppDataFactory::class)->get('preview');
- $this->previewMapper = Server::get(PreviewMapper::class);
- $this->config = Server::get(IConfig::class);
- $this->appConfig = $this->createMock(IAppConfig::class);
- $this->appConfig->expects($this->any())
- ->method('getValueBool')
- ->willReturn(false);
- $this->appConfig->expects($this->any())
- ->method('setValueBool')
- ->willReturn(true);
- $this->storageFactory = Server::get(StorageFactory::class);
- $this->previewService = Server::get(PreviewService::class);
- $this->db = Server::get(IDBConnection::class);
-
- $qb = $this->db->getQueryBuilder();
- $qb->delete('filecache')
- ->where($qb->expr()->eq('fileid', $qb->createNamedParameter(5)))
- ->executeStatement();
-
- $qb = $this->db->getQueryBuilder();
- $qb->insert('filecache')
- ->values([
- 'fileid' => $qb->createNamedParameter(5),
- 'storage' => $qb->createNamedParameter(1),
- 'path' => $qb->createNamedParameter('test/abc'),
- 'path_hash' => $qb->createNamedParameter(md5('test')),
- 'parent' => $qb->createNamedParameter(0),
- 'name' => $qb->createNamedParameter('abc'),
- 'mimetype' => $qb->createNamedParameter(42),
- 'size' => $qb->createNamedParameter(1000),
- 'mtime' => $qb->createNamedParameter(1000),
- 'storage_mtime' => $qb->createNamedParameter(1000),
- 'encrypted' => $qb->createNamedParameter(0),
- 'unencrypted_size' => $qb->createNamedParameter(0),
- 'etag' => $qb->createNamedParameter('abcdefg'),
- 'permissions' => $qb->createNamedParameter(0),
- 'checksum' => $qb->createNamedParameter('abcdefg'),
- ])->executeStatement();
-
- $this->mimeTypeDetector = $this->createMock(IMimeTypeDetector::class);
- $this->mimeTypeDetector->method('detectPath')->willReturn('image/png');
- $this->mimeTypeLoader = $this->createMock(IMimeTypeLoader::class);
- $this->mimeTypeLoader->method('getId')->with('image/png')->willReturn(42);
- $this->mimeTypeLoader->method('getMimetypeById')->with(42)->willReturn('image/png');
- $this->logger = $this->createMock(LoggerInterface::class);
- }
-
- #[\Override]
- public function tearDown(): void {
- foreach ($this->previewAppData->getDirectoryListing() as $folder) {
- $folder->delete();
- }
- $this->previewService->deleteAll();
-
- $qb = $this->db->getQueryBuilder();
- $qb->delete('filecache')
- ->where($qb->expr()->eq('fileid', $qb->createNamedParameter(5)))
- ->executeStatement();
- parent::tearDown();
- }
-
- #[TestDox('Test the migration from the legacy flat hierarchy to the new database format')]
- public function testMigrationLegacyPath(): void {
- $folder = $this->previewAppData->newFolder('5');
- $folder->newFile('64-64-crop.jpg', 'abcdefg');
- $folder->newFile('128-128-crop.png', 'abcdefg');
- $this->assertEquals(1, count($this->previewAppData->getDirectoryListing()));
- $this->assertEquals(2, count($folder->getDirectoryListing()));
- $this->assertEquals(0, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
-
- $job = $this->createJob();
- $this->invokePrivate($job, 'run', [[]]);
- $this->assertEquals(0, count($this->previewAppData->getDirectoryListing()));
- $this->assertEquals(2, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
- }
-
- private static function getInternalFolder(string $name): string {
- return implode('/', str_split(substr(md5($name), 0, 7))) . '/' . $name;
- }
-
- private function createJob(): PreviewMigrationJob {
- return new PreviewMigrationJob(
- Server::get(ITimeFactory::class),
- $this->appConfig,
- $this->config,
- Server::get(IRootFolder::class),
- new PreviewMigrationService(
- $this->config,
- Server::get(IRootFolder::class),
- $this->logger,
- $this->mimeTypeDetector,
- $this->mimeTypeLoader,
- Server::get(IDBConnection::class),
- $this->previewMapper,
- $this->storageFactory,
- Server::get(IAppDataFactory::class),
- ),
- $this->logger,
- );
- }
-
- private function insertFilecacheRow(string $path, string $etag): int {
- $qb = $this->db->getQueryBuilder();
- $qb->insert('filecache')
- ->values([
- 'storage' => $qb->createNamedParameter(1),
- 'path' => $qb->createNamedParameter($path),
- 'path_hash' => $qb->createNamedParameter(md5($path)),
- 'parent' => $qb->createNamedParameter(0),
- 'name' => $qb->createNamedParameter(basename($path)),
- 'mimetype' => $qb->createNamedParameter(42),
- 'size' => $qb->createNamedParameter(1000),
- 'mtime' => $qb->createNamedParameter(1000),
- 'storage_mtime' => $qb->createNamedParameter(1000),
- 'encrypted' => $qb->createNamedParameter(0),
- 'unencrypted_size' => $qb->createNamedParameter(0),
- 'etag' => $qb->createNamedParameter($etag),
- 'permissions' => $qb->createNamedParameter(0),
- 'checksum' => $qb->createNamedParameter($etag),
- ])->executeStatement();
- return $qb->getLastInsertId();
- }
-
- private function deleteFilecacheRow(int $fileId): void {
- $qb = $this->db->getQueryBuilder();
- $qb->delete('filecache')
- ->where($qb->expr()->eq('fileid', $qb->createNamedParameter($fileId)))
- ->executeStatement();
- }
-
- #[TestDox('A single run must migrate multiple different fileids in one pass, mixing both folder structures')]
- public function testMigrationMultipleFileIds(): void {
- $otherFileId = $this->insertFilecacheRow('test/def', 'xyz123');
-
- try {
- $flatFolder = $this->previewAppData->newFolder('5');
- $flatFolder->newFile('64-64-crop.jpg', 'abcdefg');
-
- $hierFolder = $this->previewAppData->newFolder(self::getInternalFolder((string)$otherFileId));
- $hierFolder->newFile('128-128.png', 'abcdefg');
-
- $this->invokePrivate($this->createJob(), 'run', [[]]);
-
- $this->assertEquals(0, count($this->previewAppData->getDirectoryListing()));
- $this->assertEquals(1, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
- $this->assertEquals(1, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile($otherFileId))));
- } finally {
- $this->deleteFilecacheRow($otherFileId);
- }
- }
-
- #[TestDox('Re-migrating a preview that already exists must skip the insert and still clean up the stale filecache row')]
- public function testMigrationSkipsDuplicatePreview(): void {
- $folder = $this->previewAppData->newFolder('5');
- $folder->newFile('64-64-crop.jpg', 'abcdefg');
-
- // Simulate a preview that was already migrated by an earlier, interrupted run:
- // same fileid/width/height/mimetype/cropped/version as what this migration
- // would produce for the file created above, so the insert below hits the
- // `previews_file_uniq_idx` unique constraint.
- $existing = Preview::fromPath('5/64-64-crop.jpg', $this->mimeTypeDetector);
- $this->assertNotFalse($existing);
- $existing->setFileId(5);
- $existing->setStorageId(1);
- $existing->setSourceMimeType('image/png');
- $existing->setEtag('abcdefg');
- $existing->setSize(7);
- $existing->setMtime(1000);
- $existing->setEncrypted(false);
- $existing->generateId();
- $this->previewMapper->insert($existing);
-
- $this->invokePrivate($this->createJob(), 'run', [[]]);
-
- // No duplicate preview row was inserted, but the legacy folder and its stale
- // filecache row were still cleaned up.
- $this->assertEquals(0, count($this->previewAppData->getDirectoryListing()));
- $this->assertEquals(1, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
- }
-
- #[TestDox('An orphaned preview folder whose source file no longer exists must be deleted, not migrated')]
- public function testMigrationDeletesOrphanedPreview(): void {
- $orphanFileId = 9999998;
- $folder = $this->previewAppData->newFolder((string)$orphanFileId);
- $folder->newFile('64-64-crop.jpg', 'abcdefg');
-
- $this->invokePrivate($this->createJob(), 'run', [[]]);
-
- $this->assertEquals(0, count($this->previewAppData->getDirectoryListing()));
- $this->assertEquals(0, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile($orphanFileId))));
- }
-
- #[TestDox('run() must complete without error when there is nothing to migrate')]
- public function testMigrationWithoutAnyPreviews(): void {
- $this->assertEquals(0, count($this->previewAppData->getDirectoryListing()));
-
- $this->invokePrivate($this->createJob(), 'run', [[]]);
-
- $this->assertEquals(0, count($this->previewAppData->getDirectoryListing()));
- $this->assertEquals(0, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
- }
-
- #[TestDox("Test the migration from the 'new' nested hierarchy to the database format")]
- public function testMigrationPath(): void {
- $folder = $this->previewAppData->newFolder(self::getInternalFolder((string)5));
- $folder->newFile('64-64-crop.jpg', 'abcdefg');
- $folder->newFile('128-128-crop.png', 'abcdefg');
-
- $folder = $this->previewAppData->getFolder(self::getInternalFolder((string)5));
- $this->assertEquals(2, count($folder->getDirectoryListing()));
- $this->assertEquals(0, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
-
- $job = new PreviewMigrationJob(
- Server::get(ITimeFactory::class),
- $this->appConfig,
- $this->config,
- Server::get(IRootFolder::class),
- new PreviewMigrationService(
- $this->config,
- Server::get(IRootFolder::class),
- $this->logger,
- $this->mimeTypeDetector,
- $this->mimeTypeLoader,
- Server::get(IDBConnection::class),
- $this->previewMapper,
- $this->storageFactory,
- Server::get(IAppDataFactory::class),
- ),
- $this->logger,
- );
- $this->invokePrivate($job, 'run', [[]]);
- $this->assertEquals(0, count($this->previewAppData->getDirectoryListing()));
- $this->assertEquals(2, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
- }
-
- #[TestDox("Test the migration from the 'new' nested hierarchy to the database format")]
- public function testMigrationPathWithVersion(): void {
- $folder = $this->previewAppData->newFolder(self::getInternalFolder((string)5));
- // No version
- $folder->newFile('128-128-crop.png', 'abcdefg');
- $folder->newFile('256-256-max.png', 'abcdefg');
- $folder->newFile('128-128.png', 'abcdefg');
-
- // Version 1000
- $folder->newFile('1000-128-128-crop.png', 'abcdefg');
- $folder->newFile('1000-256-256-max.png', 'abcdefg');
- $folder->newFile('1000-128-128.png', 'abcdefg');
-
- // Version 1001
- $folder->newFile('1001-128-128-crop.png', 'abcdefg');
- $folder->newFile('1001-256-256-max.png', 'abcdefg');
- $folder->newFile('1001-128-128.png', 'abcdefg');
-
- $folder = $this->previewAppData->getFolder(self::getInternalFolder((string)5));
- $this->assertEquals(9, count($folder->getDirectoryListing()));
- $this->assertEquals(0, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
-
- $job = new PreviewMigrationJob(
- Server::get(ITimeFactory::class),
- $this->appConfig,
- $this->config,
- Server::get(IRootFolder::class),
- new PreviewMigrationService(
- $this->config,
- Server::get(IRootFolder::class),
- $this->logger,
- $this->mimeTypeDetector,
- $this->mimeTypeLoader,
- Server::get(IDBConnection::class),
- $this->previewMapper,
- $this->storageFactory,
- Server::get(IAppDataFactory::class),
- ),
- $this->logger,
- );
- $this->invokePrivate($job, 'run', [[]]);
- $previews = iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5));
- $this->assertEquals(9, count($previews));
- $this->assertEquals(0, count($this->previewAppData->getDirectoryListing()));
-
- $nameVersionMapping = [];
- foreach ($previews as $preview) {
- $nameVersionMapping[$preview->getName($this->mimeTypeLoader)] = $preview->getVersion();
- }
-
- $this->assertEquals([
- '1000-128-128-crop.png' => 1000,
- '1000-128-128.png' => 1000,
- '1000-256-256-max.png' => 1000,
- '1001-128-128-crop.png' => 1001,
- '1001-128-128.png' => 1001,
- '1001-256-256-max.png' => 1001,
- '128-128-crop.png' => null,
- '128-128.png' => null,
- '256-256-max.png' => null,
- ], $nameVersionMapping);
- }
-}
--- a/tests/lib/Preview/PreviewServiceTest.php
+++ /dev/null
@@ -1,62 +0,0 @@
-previewService = Server::get(PreviewService::class);
- $this->previewMapper = Server::get(PreviewMapper::class);
- $this->previewService->deleteAll();
- }
-
- #[\Override]
- public function tearDown(): void {
- $this->previewService->deleteAll();
- parent::tearDown();
- }
-
- public function testGetAvailableFileIds(): void {
- foreach (range(1, 20) as $i) {
- $preview = new Preview();
- $preview->generateId();
- $preview->setFileId($i % 10);
- $preview->setStorageId(1);
- $preview->setWidth($i);
- $preview->setHeight($i);
- $preview->setMax(true);
- $preview->setSourceMimeType('image/jpeg');
- $preview->setCropped(true);
- $preview->setEncrypted(false);
- $preview->setMimetype('image/jpeg');
- $preview->setEtag('abc');
- $preview->setMtime((new \DateTime())->getTimestamp());
- $preview->setSize(0);
- $this->previewMapper->insert($preview);
- }
-
- $files = iterator_to_array($this->previewService->getAvailableFileIds());
- $this->assertCount(1, $files);
- $this->assertCount(10, $files[0]['fileIds']);
- }
-}
--- a/tests/lib/Preview/Provider.php
+++ /dev/null
@@ -1,178 +0,0 @@
-clearBackends();
- $backend = new \Test\Util\User\Dummy();
- $userManager->registerBackend($backend);
-
- $userId = $this->getUniqueID();
- $backend->createUser($userId, $userId);
- $this->loginAsUser($userId);
-
- $this->storage = new Temporary([]);
- Filesystem::mount($this->storage, [], '/' . $userId . '/');
-
- $this->rootView = new View('');
- $this->rootView->mkdir('/' . $userId);
- $this->rootView->mkdir('/' . $userId . '/files');
-
- $this->userId = $userId;
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->logout();
-
- parent::tearDown();
- }
-
- public static function dimensionsDataProvider() {
- return [
- [-rand(5, 100), -rand(5, 100)],
- [rand(5, 100), rand(5, 100)],
- [-rand(5, 100), rand(5, 100)],
- [rand(5, 100), -rand(5, 100)],
- ];
- }
-
- /**
- * Launches all the tests we have
- *
- *
- * @param int $widthAdjustment
- * @param int $heightAdjustment
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dimensionsDataProvider')]
- #[\PHPUnit\Framework\Attributes\RequiresPhpExtension('imagick')]
- public function testGetThumbnail($widthAdjustment, $heightAdjustment): void {
- $ratio = round($this->width / $this->height, 2);
- $this->maxWidth = $this->width - $widthAdjustment;
- $this->maxHeight = $this->height - $heightAdjustment;
-
- // Testing code
- /*print_r("w $this->width ");
- print_r("h $this->height ");
- print_r("r $ratio ");*/
-
- $preview = $this->getPreview($this->provider);
- // The TXT provider uses the max dimensions to create its canvas,
- // so the ratio will always be the one of the max dimension canvas
- if (!$this->provider instanceof TXT) {
- $this->doesRatioMatch($preview, $ratio);
- }
- $this->doesPreviewFit($preview);
- }
-
- /**
- * Adds the test file to the filesystem
- *
- * @param string $fileName name of the file to create
- * @param string $fileContent path to file to use for test
- *
- * @return string
- */
- protected function prepareTestFile($fileName, $fileContent) {
- $imgData = file_get_contents($fileContent);
- $imgPath = '/' . $this->userId . '/files/' . $fileName;
- $this->rootView->file_put_contents($imgPath, $imgData);
-
- $scanner = $this->storage->getScanner();
- $scanner->scan('');
-
- return $imgPath;
- }
-
- /**
- * Retrieves a max size thumbnail can be created
- *
- * @return bool|IImage
- */
- private function getPreview(ProviderV2 $provider) {
- $file = new File(Server::get(IRootFolder::class), $this->rootView, $this->imgPath);
- $preview = $provider->getThumbnail($file, $this->maxWidth, $this->maxHeight, $this->scalingUp);
-
- $this->assertNotEquals(false, $preview);
- $this->assertEquals(true, $preview->valid());
-
- return $preview;
- }
-
- /**
- * Checks if the preview ratio matches the original ratio
- *
- * @param IImage $preview
- * @param int $ratio
- */
- private function doesRatioMatch($preview, $ratio) {
- $previewRatio = round($preview->width() / $preview->height(), 2);
- $this->assertEquals($ratio, $previewRatio);
- }
-
- /**
- * Tests if a max size preview of smaller dimensions can be created
- *
- * @param IImage $preview
- */
- private function doesPreviewFit($preview) {
- $maxDimRatio = round($this->maxWidth / $this->maxHeight, 2);
- $previewRatio = round($preview->width() / $preview->height(), 2);
-
- // Testing code
- /*print_r("mw $this->maxWidth ");
- print_r("mh $this->maxHeight ");
- print_r("mr $maxDimRatio ");
- $pw = $preview->width();
- $ph = $preview->height();
- print_r("pw $pw ");
- print_r("ph $ph ");
- print_r("pr $previewRatio ");*/
-
- if ($maxDimRatio < $previewRatio) {
- $this->assertLessThanOrEqual($this->maxWidth, $preview->width());
- $this->assertLessThan($this->maxHeight, $preview->height());
- } elseif ($maxDimRatio > $previewRatio) {
- $this->assertLessThan($this->maxWidth, $preview->width());
- $this->assertLessThanOrEqual($this->maxHeight, $preview->height());
- } else { // Original had to be resized
- $this->assertLessThanOrEqual($this->maxWidth, $preview->width());
- $this->assertLessThanOrEqual($this->maxHeight, $preview->height());
- }
- }
-}
--- a/tests/lib/Preview/SVGTest.php
+++ /dev/null
@@ -1,110 +0,0 @@
-queryFormats('SVG')) === 1) {
- parent::setUp();
-
- $fileName = 'testimagelarge.svg';
- $this->imgPath = $this->prepareTestFile($fileName, \OC::$SERVERROOT . '/tests/data/' . $fileName);
- $this->width = 3000;
- $this->height = 2000;
- $this->provider = new SVG;
- } else {
- $this->markTestSkipped('No SVG provider present');
- }
- }
-
- public static function dataGetThumbnailSVGHref(): array {
- return [
- ['href'],
- [' href'],
- ["\nhref"],
- ['xlink:href'],
- [' xlink:href'],
- ["\nxlink:href"],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetThumbnailSVGHref')]
- #[\PHPUnit\Framework\Attributes\RequiresPhpExtension('imagick')]
- public function testGetThumbnailSVGHref(string $content): void {
- $handle = fopen('php://temp', 'w+');
- fwrite($handle, '');
- rewind($handle);
-
- $file = $this->createMock(File::class);
- $file->method('fopen')
- ->willReturn($handle);
-
- self::assertNull($this->provider->getThumbnail($file, 512, 512));
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetThumbnailSVGHrefNamespace')]
- #[\PHPUnit\Framework\Attributes\RequiresPhpExtension('imagick')]
- public function testGetThumbnailSvgHrefNamespace(string $namespace): void {
- $handle = fopen('php://temp', 'w+');
- fwrite($handle, '');
- rewind($handle);
-
- $file = $this->createMock(File::class);
- $file->method('fopen')
- ->willReturn($handle);
-
- self::assertNull($this->provider->getThumbnail($file, 512, 512));
- }
-
- public static function dataGetThumbnailSVGHrefNamespace(): array {
- return [
- ['xlink'],
- ['foo'],
- ['_foo'],
- ['fo_12'],
- ['foo-bar'],
- ['Fo_B1-ar'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetThumbnailSvgEncoded')]
- #[\PHPUnit\Framework\Attributes\RequiresPhpExtension('imagick')]
- public function testGetThumbnailSvgEncoded(string $content): void {
- $handle = fopen('php://temp', 'w+');
- fwrite($handle, $content);
- rewind($handle);
-
- $file = $this->createMock(File::class);
- $file->method('fopen')
- ->willReturn($handle);
- self::assertNull($this->provider->getThumbnail($file, 512, 512));
- }
-
- public static function dataGetThumbnailSvgEncoded(): array {
- return [
- 'iso-2022-jp' => ["\n"],
- ];
- }
-}
--- a/tests/lib/Preview/Storage/LocalPreviewStorageTest.php
+++ /dev/null
@@ -1,331 +0,0 @@
-tmpDir = sys_get_temp_dir() . '/nc_preview_test_' . uniqid();
- mkdir($this->tmpDir, 0777, true);
-
- $this->config = $this->createMock(IConfig::class);
- $this->config->method('getSystemValueString')
- ->with('datadirectory', $this->anything())
- ->willReturn($this->tmpDir);
-
- $this->rootFolder = $this->createMock(IRootFolder::class);
- $this->rootFolder->method('getAppDataDirectoryName')->willReturn('appdata_test');
-
- $this->previewMapper = $this->createMock(PreviewMapper::class);
- $this->appConfig = $this->createMock(IAppConfig::class);
- $this->connection = $this->createMock(IDBConnection::class);
- $this->mimeTypeDetector = $this->createMock(IMimeTypeDetector::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->mimeTypeLoader = $this->createMock(IMimeTypeLoader::class);
-
- $this->mimeTypeDetector->method('detectPath')->willReturn('image/jpeg');
- $this->mimeTypeLoader->method('getMimetypeById')->willReturn('image/jpeg');
-
- $this->storage = new LocalPreviewStorage(
- $this->config,
- $this->previewMapper,
- $this->appConfig,
- $this->connection,
- $this->mimeTypeDetector,
- $this->logger,
- $this->mimeTypeLoader,
- $this->rootFolder,
- );
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->removeDir($this->tmpDir);
- parent::tearDown();
- }
-
- private function removeDir(string $path): void {
- if (!is_dir($path)) {
- return;
- }
- foreach (scandir($path) as $entry) {
- if ($entry === '.' || $entry === '..') {
- continue;
- }
- $full = $path . '/' . $entry;
- is_dir($full) ? $this->removeDir($full) : unlink($full);
- }
- rmdir($path);
- }
-
- /**
- * Create a preview file in the legacy flat directory format so the scan
- * code will attempt to move it to the new nested path.
- * Returns the absolute path to the created file.
- */
- private function createFlatPreviewFile(int $fileId, string $previewName): string {
- $dir = $this->tmpDir . '/appdata_test/preview/' . $fileId;
- mkdir($dir, 0777, true);
- $path = $dir . '/' . $previewName;
- file_put_contents($path, 'fake preview data');
- return $path;
- }
-
- /**
- * Build a mock IQueryBuilder chain and configure it to return the given
- * rows from executeQuery()->fetchAssociative().
- */
- private function buildQueryBuilderMock(array $rows): IQueryBuilder&MockObject {
- $exprMock = $this->createMock(IExpressionBuilder::class);
- $exprMock->method('in')->willReturn('1=1');
-
- $callIndex = 0;
- $resultMock = $this->createMock(IResult::class);
- $resultMock->method('fetchAssociative')
- ->willReturnCallback(static function () use ($rows, &$callIndex) {
- return $rows[$callIndex++] ?? false;
- });
-
- $qbMock = $this->createMock(ITypedQueryBuilder::class);
- $qbMock->method('selectColumns')->willReturnSelf();
- $qbMock->method('from')->willReturnSelf();
- $qbMock->method('where')->willReturnSelf();
- $qbMock->method('andWhere')->willReturnSelf();
- $qbMock->method('runAcrossAllShards')->willReturnSelf();
- $qbMock->method('executeQuery')->willReturn($resultMock);
- $qbMock->method('expr')->willReturn($exprMock);
- $qbMock->method('createNamedParameter')->willReturn(':param');
-
- return $qbMock;
- }
-
- /**
- * Configure appConfig so migration is considered done, meaning
- * checkForFileCache = false (no legacy path-hash queries).
- */
- private function setMigrationDone(): void {
- $this->appConfig->method('getValueBool')
- ->with('core', 'previewMovedDone')
- ->willReturn(true);
- }
-
- /**
- * When fewer previews than SCAN_BATCH_SIZE exist, scan() must still open
- * and commit a transaction for the tail batch.
- *
- * Before the fix: commit() was never called for the tail batch, leaving the
- * transaction open.
- */
- public function testScanCommitsFinalBatch(): void {
- $this->setMigrationDone();
- $this->createFlatPreviewFile(self::FILE_ID, '1024-1024.jpg');
-
- $filecacheRow = [
- 'fileid' => (string)self::FILE_ID,
- 'storage' => '42',
- 'etag' => 'abc',
- 'mimetype' => '6',
- ];
- $this->connection->method('getTypedQueryBuilder')
- ->willReturn($this->buildQueryBuilderMock([$filecacheRow]));
-
- // Outer batch transaction + one inner savepoint for the insert.
- $this->connection->expects($this->exactly(2))->method('beginTransaction');
- $this->connection->expects($this->exactly(2))->method('commit');
- $this->connection->expects($this->never())->method('rollBack');
-
- $count = $this->storage->scan();
-
- $this->assertSame(1, $count);
- }
-
- /**
- * When previewMapper->insert() throws a unique-constraint violation, scan()
- * must roll back only the inner savepoint and continue, leaving the outer
- * transaction intact so its final commit() succeeds.
- *
- * Before the fix: the plain catch swallowed the PHP exception but left the
- * PostgreSQL transaction in an aborted state, so all subsequent queries
- * (including commit()) failed with "current transaction is aborted".
- */
- public function testScanHandlesUniqueConstraintViolation(): void {
- $this->setMigrationDone();
- $this->createFlatPreviewFile(self::FILE_ID, '1024-1024.jpg');
-
- $filecacheRow = [
- 'fileid' => (string)self::FILE_ID,
- 'storage' => '42',
- 'etag' => 'abc',
- 'mimetype' => '6',
- ];
- $this->connection->method('getTypedQueryBuilder')
- ->willReturn($this->buildQueryBuilderMock([$filecacheRow]));
-
- $ucvException = new class('duplicate key') extends DBException {
- #[\Override]
- public function getReason(): int {
- return self::REASON_UNIQUE_CONSTRAINT_VIOLATION;
- }
- };
- $this->previewMapper->method('insert')->willThrowException($ucvException);
-
- // Inner savepoint is rolled back; outer batch transaction is committed.
- $this->connection->expects($this->exactly(2))->method('beginTransaction');
- $this->connection->expects($this->once())->method('commit');
- $this->connection->expects($this->exactly(1))->method('rollBack');
-
- $count = $this->storage->scan();
-
- // Even when the DB row already exists the preview file still counts.
- $this->assertSame(1, $count);
- }
-
- /**
- * A non-UCE exception from previewMapper->insert() must be re-thrown after
- * rolling back both the inner savepoint and the outer batch transaction.
- */
- public function testScanRethrowsUnexpectedInsertException(): void {
- $this->setMigrationDone();
- $this->createFlatPreviewFile(self::FILE_ID, '1024-1024.jpg');
-
- $filecacheRow = [
- 'fileid' => (string)self::FILE_ID,
- 'storage' => '42',
- 'etag' => 'abc',
- 'mimetype' => '6',
- ];
- $this->connection->method('getTypedQueryBuilder')
- ->willReturn($this->buildQueryBuilderMock([$filecacheRow]));
-
- $driverException = new class('some driver error') extends DBException {
- #[\Override]
- public function getReason(): int {
- return self::REASON_DRIVER;
- }
- };
- $this->previewMapper->method('insert')->willThrowException($driverException);
-
- // Inner savepoint rolled back; outer batch also rolled back via rethrow.
- $this->connection->expects($this->exactly(2))->method('beginTransaction');
- $this->connection->expects($this->never())->method('commit');
- $this->connection->expects($this->exactly(2))->method('rollBack');
-
- $this->expectException(DBException::class);
- $this->storage->scan();
- }
-
- /**
- * fetchFilecacheByFileIds() must return a row for every file ID returned by
- * the query, not just one. Before the fix, the foreach loop iterated over
- * the key-value pairs of the first row, so previews for all but the first
- * file ID were silently deleted (filecache row not found → unlink).
- */
- public function testScanFetchesAllFilecacheRows(): void {
- $this->setMigrationDone();
-
- $fileIds = [1, 2, 3];
- foreach ($fileIds as $id) {
- $this->createFlatPreviewFile($id, '1024-1024.jpg');
- }
-
- $filecacheRows = array_map(static fn (int $id) => [
- 'fileid' => (string)$id,
- 'storage' => '42',
- 'etag' => 'abc',
- 'mimetype' => '6',
- ], $fileIds);
-
- $this->connection->method('getTypedQueryBuilder')
- ->willReturn($this->buildQueryBuilderMock($filecacheRows));
-
- // 1 outer batch transaction + 3 inner savepoints (one per preview insert).
- $this->connection->expects($this->exactly(4))->method('beginTransaction');
- $this->connection->expects($this->exactly(4))->method('commit');
- $this->connection->expects($this->never())->method('rollBack');
-
- $count = $this->storage->scan();
-
- $this->assertSame(3, $count);
- }
-
- private function makePreview(int $fileId = self::FILE_ID): Preview {
- $preview = new Preview();
- $preview->setFileId($fileId);
- $preview->setWidth(1024);
- $preview->setHeight(768);
- $preview->setCropped(false);
- $preview->setMax(true);
- $preview->setMimetype('image/jpeg');
-
- return $preview;
- }
-
- /**
- * Losing the insert race must not remove the local file: the path is derived
- * from the preview specification, so it is the same file the preview that
- * won the race now points at.
- */
- public function testDeleteUnreferencedPreviewKeepsTheSharedFile(): void {
- $preview = $this->makePreview();
- $this->storage->writePreview($preview, 'preview data');
-
- $this->storage->deleteUnreferencedPreview($preview);
-
- $this->assertSame('preview data', stream_get_contents($this->storage->readPreview($preview)));
- }
-
- /**
- * Deleting a preview for real still has to remove the file.
- */
- public function testDeletePreviewRemovesTheFile(): void {
- $preview = $this->makePreview();
- $this->storage->writePreview($preview, 'preview data');
-
- $this->storage->deletePreview($preview);
-
- $this->expectException(NotFoundException::class);
- $this->storage->readPreview($preview);
- }
-}
--- a/tests/lib/Preview/TXTTest.php
+++ /dev/null
@@ -1,32 +0,0 @@
-imgPath = $this->prepareTestFile($fileName, \OC::$SERVERROOT . '/tests/data/' . $fileName);
- // Arbitrary width and length which won't be used to calculate the ratio
- $this->width = 500;
- $this->height = 200;
- $this->provider = new TXT;
- }
-}
--- a/tests/lib/Profile/Actions/FediverseActionTest.php
+++ /dev/null
@@ -1,187 +0,0 @@
-accountManager = $this->createMock(IAccountManager::class);
- $this->l10n = $this->createMock(IL10N::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
-
- $factory = $this->createMock(IFactory::class);
- $factory->method('get')
- ->with('lib')
- ->willReturn($this->l10n);
-
- $this->action = new FediverseAction(
- $this->accountManager,
- $factory,
- $this->urlGenerator,
- );
- }
-
- public function testGetActionId(): void {
- self::assertEquals(
- IAccountManager::PROPERTY_FEDIVERSE,
- $this->action->getId(),
- );
- }
-
- public function testGetAppId(): void {
- self::assertEquals(
- 'core',
- $this->action->getAppId(),
- );
- }
-
- public function testGetDisplayId(): void {
- $this->l10n->expects(self::once())
- ->method('t')
- ->with('Fediverse')
- ->willReturn('Translated fediverse');
-
- self::assertEquals(
- 'Translated fediverse',
- $this->action->getDisplayId(),
- );
- }
-
- public function testGetPriority(): void {
- self::assertEquals(
- 50,
- $this->action->getPriority(),
- );
- }
-
- public function testGetIcon(): void {
- $this->urlGenerator->expects(self::once())
- ->method('getAbsoluteURL')
- ->with('the-image-path')
- ->willReturn('resolved-image-path');
-
- $this->urlGenerator->expects(self::once())
- ->method('imagePath')
- ->with('core', 'actions/mastodon.svg')
- ->willReturn('the-image-path');
-
- self::assertEquals(
- 'resolved-image-path',
- $this->action->getIcon(),
- );
- }
-
- public static function dataGetTitle(): array {
- return [
- ['the-user@example.com'],
- ['@the-user@example.com'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetTitle')]
- public function testGetTitle(string $value): void {
- $property = $this->createMock(IAccountProperty::class);
- $property->method('getValue')
- ->willReturn($value);
-
- $user = $this->createMock(IUser::class);
-
- $account = $this->createMock(IAccount::class);
- $account->method('getProperty')
- ->with(IAccountManager::PROPERTY_FEDIVERSE)
- ->willReturn($property);
-
- $this->accountManager->method('getAccount')
- ->with($user)
- ->willReturn($account);
-
- $this->l10n->expects(self::once())
- ->method('t')
- ->with(self::anything(), ['@the-user@example.com'])
- ->willReturn('Translated title');
-
- // Preload and test
- $this->action->preload($user);
- self::assertEquals(
- 'Translated title',
- $this->action->getTitle(),
- );
- }
-
- public static function dataGetTarget(): array {
- return [
- ['', null],
- [null, null],
- ['user@example.com', 'https://example.com/@user'],
- ['@user@example.com', 'https://example.com/@user'],
- ['@user@social.example.com', 'https://social.example.com/@user'],
- // invalid stuff
- ['@user', null],
- ['@example.com', null],
- ['@@example.com', null],
- // evil stuff
- ['user@example.com/evil.exe', null],
- ['@user@example.com/evil.exe', null],
- ['user/evil.exe@example.com', null],
- ['@user/evil.exe@example.com', null],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetTarget')]
- public function testGetTarget(?string $value, ?string $expected): void {
- $user = $this->createMock(IUser::class);
-
- $account = $this->createMock(IAccount::class);
- $this->accountManager->method('getAccount')
- ->with($user)
- ->willReturn($account);
-
- if ($value === null) {
- // Property does not exist so throw
- $account->method('getProperty')
- ->with(IAccountManager::PROPERTY_FEDIVERSE)
- ->willThrowException(new PropertyDoesNotExistException(IAccountManager::PROPERTY_FEDIVERSE));
- } else {
- $property = $this->createMock(IAccountProperty::class);
- $property->method('getValue')
- ->willReturn($value);
- $account->method('getProperty')
- ->willReturn($property);
- }
-
- // Preload and test
- $this->action->preload($user);
- self::assertEquals(
- $expected,
- $this->action->getTarget(),
- );
- }
-}
--- a/tests/lib/Repair/CleanTagsTest.php
+++ /dev/null
@@ -1,196 +0,0 @@
-outputMock = $this->getMockBuilder(IOutput::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->userManager = $this->getMockBuilder(IUserManager::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->connection = Server::get(IDBConnection::class);
- $this->repair = new CleanTags($this->connection, $this->userManager);
- $this->cleanUpTables();
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->cleanUpTables();
-
- parent::tearDown();
- }
-
- protected function cleanUpTables() {
- $qb = $this->connection->getQueryBuilder();
- $qb->delete('vcategory')
- ->executeStatement();
-
- $qb->delete('vcategory_to_object')
- ->executeStatement();
-
- $qb->delete('filecache')
- ->runAcrossAllShards()
- ->executeStatement();
- }
-
- public function testRun(): void {
- $cat1 = $this->addTagCategory('TestRepairCleanTags', 'files'); // Retained
- $cat2 = $this->addTagCategory('TestRepairCleanTags2', 'files'); // Deleted: Category will be empty
- $this->addTagCategory('TestRepairCleanTags3', 'files'); // Deleted: Category is empty
- $cat3 = $this->addTagCategory('TestRepairCleanTags', 'contacts'); // Retained
-
- $this->addTagEntry($this->getFileID(), $cat2, 'files'); // Retained
- $this->addTagEntry($this->getFileID() + 1, $cat1, 'files'); // Deleted: File is NULL
- $this->addTagEntry(9999999, $cat3, 'contacts'); // Retained
- $this->addTagEntry($this->getFileID(), $cat3 + 1, 'files'); // Deleted: Category is NULL
-
- $this->assertEntryCount('vcategory_to_object', 4, 'Assert tag entries count before repair step');
- $this->assertEntryCount('vcategory', 4, 'Assert tag categories count before repair step');
-
- self::invokePrivate($this->repair, 'deleteOrphanFileEntries', [$this->outputMock]);
- $this->assertEntryCount('vcategory_to_object', 3, 'Assert tag entries count after cleaning file entries');
- $this->assertEntryCount('vcategory', 4, 'Assert tag categories count after cleaning file entries');
-
- self::invokePrivate($this->repair, 'deleteOrphanTagEntries', [$this->outputMock]);
- $this->assertEntryCount('vcategory_to_object', 2, 'Assert tag entries count after cleaning tag entries');
- $this->assertEntryCount('vcategory', 4, 'Assert tag categories count after cleaning tag entries');
-
- self::invokePrivate($this->repair, 'deleteOrphanCategoryEntries', [$this->outputMock]);
- $this->assertEntryCount('vcategory_to_object', 2, 'Assert tag entries count after cleaning category entries');
- $this->assertEntryCount('vcategory', 2, 'Assert tag categories count after cleaning category entries');
-
- $this->addTagCategory('TestRepairCleanTags', 'contacts', 'userExists'); // Retained
- $this->assertEntryCount('vcategory', 3, 'Assert tag categories count before cleaning categories by users');
-
- $this->userManager->expects($this->exactly(2))
- ->method('userExists')
- ->willReturnMap([
- ['userExists', true],
- ['TestRepairCleanTags', false],
- ]);
-
- self::invokePrivate($this->repair, 'deleteOrphanTags', [$this->outputMock]);
- $this->assertEntryCount('vcategory', 1, 'Assert tag categories count after cleaning categories by users');
- }
-
- /**
- * @param string $tableName
- * @param int $expected
- * @param string $message
- */
- protected function assertEntryCount($tableName, $expected, $message = '') {
- $qb = $this->connection->getQueryBuilder();
- $result = $qb->select($qb->func()->count('*'))
- ->from($tableName)
- ->executeQuery();
-
- $this->assertEquals($expected, $result->fetchOne(), $message);
- }
-
- /**
- * Adds a new tag category to the database
- *
- * @param string $category
- * @param string $type
- * @param string $user
- * @return int
- */
- protected function addTagCategory($category, $type, $user = 'TestRepairCleanTags') {
- $qb = $this->connection->getQueryBuilder();
- $qb->insert('vcategory')
- ->values([
- 'uid' => $qb->createNamedParameter($user),
- 'category' => $qb->createNamedParameter($category),
- 'type' => $qb->createNamedParameter($type),
- ])
- ->executeStatement();
-
- return $qb->getLastInsertId();
- }
-
- /**
- * Adds a new tag entry to the database
- * @param int $objectId
- * @param int $category
- * @param string $type
- */
- protected function addTagEntry($objectId, $category, $type) {
- $qb = $this->connection->getQueryBuilder();
- $qb->insert('vcategory_to_object')
- ->values([
- 'objid' => $qb->createNamedParameter($objectId, IQueryBuilder::PARAM_INT),
- 'categoryid' => $qb->createNamedParameter($category, IQueryBuilder::PARAM_INT),
- 'type' => $qb->createNamedParameter($type),
- ])
- ->executeStatement();
- }
-
- /**
- * Gets the last fileid from the file cache
- * @return int
- */
- protected function getFileID() {
- if ($this->createdFile !== null) {
- return $this->createdFile;
- }
-
- $qb = $this->connection->getQueryBuilder();
-
- // We create a new file entry and delete it after the test again
- $fileName = $this->getUniqueID('TestRepairCleanTags', 12);
- $qb->insert('filecache')
- ->values([
- 'storage' => $qb->createNamedParameter(1, IQueryBuilder::PARAM_INT),
- 'path' => $qb->createNamedParameter($fileName),
- 'path_hash' => $qb->createNamedParameter(md5($fileName)),
- ])
- ->executeStatement();
- $fileName = $this->getUniqueID('TestRepairCleanTags', 12);
- $qb->insert('filecache')
- ->values([
- 'storage' => $qb->createNamedParameter(1, IQueryBuilder::PARAM_INT),
- 'path' => $qb->createNamedParameter($fileName),
- 'path_hash' => $qb->createNamedParameter(md5($fileName)),
- ])
- ->executeStatement();
-
- $this->createdFile = $qb->getLastInsertId();
- return $this->createdFile;
- }
-}
--- a/tests/lib/Repair/ClearFrontendCachesTest.php
+++ /dev/null
@@ -1,51 +0,0 @@
-outputMock = $this->createMock(IOutput::class);
-
- $this->cacheFactory = $this->createMock(ICacheFactory::class);
- $this->jsCombiner = $this->createMock(JSCombiner::class);
-
- $this->repair = new ClearFrontendCaches($this->cacheFactory, $this->jsCombiner);
- }
-
- public function testRun(): void {
- $imagePathCache = $this->createMock(ICache::class);
- $imagePathCache->expects($this->once())
- ->method('clear')
- ->with('');
- $this->jsCombiner->expects($this->once())
- ->method('resetCache');
- $this->cacheFactory->expects($this->once())
- ->method('createDistributed')
- ->with('imagePath')
- ->willReturn($imagePathCache);
-
- $this->repair->run($this->outputMock);
- }
-}
--- a/tests/lib/Repair/ClearGeneratedAvatarCacheTest.php
+++ /dev/null
@@ -1,62 +0,0 @@
-avatarManager = $this->createMock(AvatarManager::class);
- $this->config = $this->createMock(IConfig::class);
- $this->jobList = $this->createMock(IJobList::class);
-
- $this->repair = new ClearGeneratedAvatarCache($this->config, $this->avatarManager, $this->jobList);
- }
-
- public static function shouldRunDataProvider(): array {
- return [
- ['11.0.0.0', true],
- ['15.0.0.3', true],
- ['13.0.5.2', true],
- ['12.0.0.0', true],
- ['26.0.0.1', true],
- ['15.0.0.2', true],
- ['13.0.0.0', true],
- ['27.0.0.5', false]
- ];
- }
-
- /**
- *
- * @param string $from
- * @param boolean $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('shouldRunDataProvider')]
- public function testShouldRun($from, $expected): void {
- $this->config->expects($this->any())
- ->method('getSystemValueString')
- ->with('version', '0.0.0.0')
- ->willReturn($from);
-
- $this->assertEquals($expected, $this->invokePrivate($this->repair, 'shouldRun'));
- }
-}
--- a/tests/lib/Repair/NC29/SanitizeAccountPropertiesJobTest.php
+++ /dev/null
@@ -1,120 +0,0 @@
-userManager = $this->createMock(IUserManager::class);
- $this->accountManager = $this->createMock(IAccountManager::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $this->job = new SanitizeAccountPropertiesJob(
- $this->createMock(ITimeFactory::class),
- $this->userManager,
- $this->accountManager,
- $this->logger,
- );
- }
-
- public function testParallel() {
- self::assertFalse($this->job->getAllowParallelRuns());
- }
-
- public function testRun(): void {
- $users = [
- $this->createMock(IUser::class),
- $this->createMock(IUser::class),
- $this->createMock(IUser::class),
- ];
- $this->userManager
- ->expects(self::once())
- ->method('callForSeenUsers')
- ->willReturnCallback(fn ($fn) => array_map($fn, $users));
-
- $property = $this->createMock(IAccountProperty::class);
- $property->expects(self::once())->method('getName')->willReturn(IAccountManager::PROPERTY_PHONE);
- $property->expects(self::once())->method('getScope')->willReturn(IAccountManager::SCOPE_LOCAL);
-
- $account1 = $this->createMock(IAccount::class);
- $account1->expects(self::once())
- ->method('getProperty')
- ->with(IAccountManager::PROPERTY_PHONE)
- ->willReturn($property);
- $account1->expects(self::once())
- ->method('setProperty')
- ->with(IAccountManager::PROPERTY_PHONE, '', IAccountManager::SCOPE_LOCAL, IAccountManager::NOT_VERIFIED);
- $account1->expects(self::once())
- ->method('jsonSerialize')
- ->willReturn([
- IAccountManager::PROPERTY_DISPLAYNAME => [],
- IAccountManager::PROPERTY_PHONE => [],
- ]);
-
- $account2 = $this->createMock(IAccount::class);
- $account2->expects(self::never())
- ->method('getProperty');
- $account2->expects(self::once())
- ->method('jsonSerialize')
- ->willReturn([
- IAccountManager::PROPERTY_DISPLAYNAME => [],
- IAccountManager::PROPERTY_PHONE => [],
- ]);
-
- $account3 = $this->createMock(IAccount::class);
- $account3->expects(self::never())
- ->method('getProperty');
- $account3->expects(self::once())
- ->method('jsonSerialize')
- ->willReturn([
- IAccountManager::PROPERTY_DISPLAYNAME => [],
- ]);
-
- $this->accountManager
- ->expects(self::exactly(3))
- ->method('getAccount')
- ->willReturnMap([
- [$users[0], $account1],
- [$users[1], $account2],
- [$users[2], $account3],
- ]);
- $valid = false;
- $this->accountManager->expects(self::exactly(3))
- ->method('updateAccount')
- ->willReturnCallback(function (IAccount $account) use (&$account1, &$valid): void {
- if (!$valid && $account === $account1) {
- $valid = true;
- throw new InvalidArgumentException(IAccountManager::PROPERTY_PHONE);
- }
- });
-
- self::invokePrivate($this->job, 'run', [null]);
- }
-}
--- a/tests/lib/Repair/NC29/SanitizeAccountPropertiesTest.php
+++ /dev/null
@@ -1,47 +0,0 @@
-jobList = $this->createMock(IJobList::class);
-
- $this->repairStep = new SanitizeAccountProperties($this->jobList);
- }
-
- public function testGetName(): void {
- self::assertStringContainsString('Validate account properties', $this->repairStep->getName());
- }
-
- public function testRun(): void {
- $this->jobList->expects(self::once())
- ->method('add')
- ->with(SanitizeAccountPropertiesJob::class, null);
-
- $output = $this->createMock(IOutput::class);
- $output->expects(self::once())
- ->method('info')
- ->with(self::matchesRegularExpression('/queued background/i'));
-
- $this->repairStep->run($output);
- }
-}
--- a/tests/lib/Repair/OldGroupMembershipSharesTest.php
+++ /dev/null
@@ -1,135 +0,0 @@
-groupManager = $this->getMockBuilder(IGroupManager::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->connection = Server::get(IDBConnection::class);
-
- $this->deleteAllShares();
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->deleteAllShares();
-
- parent::tearDown();
- }
-
- protected function deleteAllShares() {
- $qb = $this->connection->getQueryBuilder();
- $qb->delete('share')->executeStatement();
- }
-
- public function testRun(): void {
- $repair = new OldGroupMembershipShares(
- $this->connection,
- $this->groupManager
- );
-
- $this->groupManager->expects($this->exactly(2))
- ->method('isInGroup')
- ->willReturnMap([
- ['member', 'group', true],
- ['not-a-member', 'group', false],
- ]);
-
- $parent = $this->createShare(IShare::TYPE_GROUP, 'group', null);
- $group2 = $this->createShare(IShare::TYPE_GROUP, 'group2', $parent);
- $user1 = $this->createShare(IShare::TYPE_USER, 'user1', $parent);
-
- // \OC\Share\Constant::$shareTypeGroupUserUnique === 2
- $member = $this->createShare(2, 'member', $parent);
- $notAMember = $this->createShare(2, 'not-a-member', $parent);
-
- $query = $this->connection->getQueryBuilder();
- $result = $query->select('id')
- ->from('share')
- ->orderBy('id', 'ASC')
- ->executeQuery();
- $rows = $result->fetchAllAssociative();
- $this->assertEquals([['id' => $parent], ['id' => $group2], ['id' => $user1], ['id' => $member], ['id' => $notAMember]], $rows);
- $result->closeCursor();
-
- /** @var IOutput | \PHPUnit\Framework\MockObject\MockObject $outputMock */
- $outputMock = $this->getMockBuilder('\OCP\Migration\IOutput')
- ->disableOriginalConstructor()
- ->getMock();
-
- $repair->run($outputMock);
-
- $query = $this->connection->getQueryBuilder();
- $result = $query->select('id')
- ->from('share')
- ->orderBy('id', 'ASC')
- ->executeQuery();
- $rows = $result->fetchAllAssociative();
- $this->assertEquals([['id' => $parent], ['id' => $group2], ['id' => $user1], ['id' => $member]], $rows);
- $result->closeCursor();
- }
-
- /**
- * @param string $shareType
- * @param string $shareWith
- * @param null|int $parent
- * @return int
- */
- protected function createShare($shareType, $shareWith, $parent) {
- $qb = $this->connection->getQueryBuilder();
- $shareValues = [
- 'share_type' => $qb->expr()->literal($shareType),
- 'share_with' => $qb->expr()->literal($shareWith),
- 'uid_owner' => $qb->expr()->literal('user1'),
- 'item_type' => $qb->expr()->literal('folder'),
- 'item_source' => $qb->expr()->literal(123),
- 'item_target' => $qb->expr()->literal('/123'),
- 'file_source' => $qb->expr()->literal(123),
- 'file_target' => $qb->expr()->literal('/test'),
- 'permissions' => $qb->expr()->literal(1),
- 'stime' => $qb->expr()->literal(time()),
- 'expiration' => $qb->expr()->literal('2015-09-25 00:00:00'),
- ];
-
- if ($parent) {
- $shareValues['parent'] = $qb->expr()->literal($parent);
- }
-
- $qb = $this->connection->getQueryBuilder();
- $qb->insert('share')
- ->values($shareValues)
- ->executeStatement();
-
- return $qb->getLastInsertId();
- }
-}
--- a/tests/lib/Repair/Owncloud/CleanPreviewsBackgroundJobTest.php
+++ /dev/null
@@ -1,238 +0,0 @@
-rootFolder = $this->createMock(IRootFolder::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->jobList = $this->createMock(IJobList::class);
- $this->timeFactory = $this->createMock(ITimeFactory::class);
- $this->userManager = $this->createMock(IUserManager::class);
-
- $this->userManager->expects($this->any())->method('userExists')->willReturn(true);
-
- $this->job = new CleanPreviewsBackgroundJob(
- $this->rootFolder,
- $this->logger,
- $this->jobList,
- $this->timeFactory,
- $this->userManager
- );
- }
-
- public function testCleanupPreviewsUnfinished(): void {
- $userFolder = $this->createMock(Folder::class);
- $userRoot = $this->createMock(Folder::class);
- $thumbnailFolder = $this->createMock(Folder::class);
-
- $this->rootFolder->method('getUserFolder')
- ->with($this->equalTo('myuid'))
- ->willReturn($userFolder);
-
- $userFolder->method('getParent')->willReturn($userRoot);
-
- $userRoot->method('get')
- ->with($this->equalTo('thumbnails'))
- ->willReturn($thumbnailFolder);
-
- $previewFolder1 = $this->createMock(Folder::class);
-
- $previewFolder1->expects($this->once())
- ->method('delete');
-
- $thumbnailFolder->method('getDirectoryListing')
- ->willReturn([$previewFolder1]);
- $thumbnailFolder->expects($this->never())
- ->method('delete');
-
- $this->timeFactory->method('getTime')->willReturnOnConsecutiveCalls(100, 200);
-
- $this->jobList->expects($this->once())
- ->method('add')
- ->with(
- $this->equalTo(CleanPreviewsBackgroundJob::class),
- $this->equalTo(['uid' => 'myuid'])
- );
-
- $loggerCalls = [];
- $this->logger->expects($this->exactly(2))
- ->method('info')
- ->willReturnCallback(function () use (&$loggerCalls): void {
- $loggerCalls[] = func_get_args();
- });
-
- $this->job->run(['uid' => 'myuid']);
- self::assertEquals([
- ['Started preview cleanup for myuid', []],
- ['New preview cleanup scheduled for myuid', []],
- ], $loggerCalls);
- }
-
- public function testCleanupPreviewsFinished(): void {
- $userFolder = $this->createMock(Folder::class);
- $userRoot = $this->createMock(Folder::class);
- $thumbnailFolder = $this->createMock(Folder::class);
-
- $this->rootFolder->method('getUserFolder')
- ->with($this->equalTo('myuid'))
- ->willReturn($userFolder);
-
- $userFolder->method('getParent')->willReturn($userRoot);
-
- $userRoot->method('get')
- ->with($this->equalTo('thumbnails'))
- ->willReturn($thumbnailFolder);
-
- $previewFolder1 = $this->createMock(Folder::class);
-
- $previewFolder1->expects($this->once())
- ->method('delete');
-
- $thumbnailFolder->method('getDirectoryListing')
- ->willReturn([$previewFolder1]);
-
- $this->timeFactory->method('getTime')->willReturnOnConsecutiveCalls(100, 101);
-
- $this->jobList->expects($this->never())
- ->method('add');
-
- $loggerCalls = [];
- $this->logger->expects($this->exactly(2))
- ->method('info')
- ->willReturnCallback(function () use (&$loggerCalls): void {
- $loggerCalls[] = func_get_args();
- });
-
- $thumbnailFolder->expects($this->once())
- ->method('delete');
-
- $this->job->run(['uid' => 'myuid']);
- self::assertEquals([
- ['Started preview cleanup for myuid', []],
- ['Preview cleanup done for myuid', []],
- ], $loggerCalls);
- }
-
- public function testNoUserFolder(): void {
- $this->rootFolder->method('getUserFolder')
- ->with($this->equalTo('myuid'))
- ->willThrowException(new NotFoundException());
-
- $loggerCalls = [];
- $this->logger->expects($this->exactly(2))
- ->method('info')
- ->willReturnCallback(function () use (&$loggerCalls): void {
- $loggerCalls[] = func_get_args();
- });
-
- $this->job->run(['uid' => 'myuid']);
- self::assertEquals([
- ['Started preview cleanup for myuid', []],
- ['Preview cleanup done for myuid', []],
- ], $loggerCalls);
- }
-
- public function testNoThumbnailFolder(): void {
- $userFolder = $this->createMock(Folder::class);
- $userRoot = $this->createMock(Folder::class);
-
- $this->rootFolder->method('getUserFolder')
- ->with($this->equalTo('myuid'))
- ->willReturn($userFolder);
-
- $userFolder->method('getParent')->willReturn($userRoot);
-
- $userRoot->method('get')
- ->with($this->equalTo('thumbnails'))
- ->willThrowException(new NotFoundException());
-
- $loggerCalls = [];
- $this->logger->expects($this->exactly(2))
- ->method('info')
- ->willReturnCallback(function () use (&$loggerCalls): void {
- $loggerCalls[] = func_get_args();
- });
-
- $this->job->run(['uid' => 'myuid']);
- self::assertEquals([
- ['Started preview cleanup for myuid', []],
- ['Preview cleanup done for myuid', []],
- ], $loggerCalls);
- }
-
- public function testNotPermittedToDelete(): void {
- $userFolder = $this->createMock(Folder::class);
- $userRoot = $this->createMock(Folder::class);
- $thumbnailFolder = $this->createMock(Folder::class);
-
- $this->rootFolder->method('getUserFolder')
- ->with($this->equalTo('myuid'))
- ->willReturn($userFolder);
-
- $userFolder->method('getParent')->willReturn($userRoot);
-
- $userRoot->method('get')
- ->with($this->equalTo('thumbnails'))
- ->willReturn($thumbnailFolder);
-
- $previewFolder1 = $this->createMock(Folder::class);
-
- $previewFolder1->expects($this->once())
- ->method('delete')
- ->willThrowException(new NotPermittedException());
-
- $thumbnailFolder->method('getDirectoryListing')
- ->willReturn([$previewFolder1]);
-
- $this->timeFactory->method('getTime')->willReturnOnConsecutiveCalls(100, 101);
-
- $this->jobList->expects($this->never())
- ->method('add');
-
- $thumbnailFolder->expects($this->once())
- ->method('delete')
- ->willThrowException(new NotPermittedException());
-
- $loggerCalls = [];
- $this->logger->expects($this->exactly(2))
- ->method('info')
- ->willReturnCallback(function () use (&$loggerCalls): void {
- $loggerCalls[] = func_get_args();
- });
-
- $this->job->run(['uid' => 'myuid']);
- self::assertEquals([
- ['Started preview cleanup for myuid', []],
- ['Preview cleanup done for myuid', []],
- ], $loggerCalls);
- }
-}
--- a/tests/lib/Repair/Owncloud/CleanPreviewsTest.php
+++ /dev/null
@@ -1,106 +0,0 @@
-jobList = $this->createMock(IJobList::class);
- $this->userManager = $this->createMock(IUserManager::class);
- $this->appConfig = $this->createMock(IAppConfig::class);
-
- $this->repair = new CleanPreviews(
- $this->jobList,
- $this->userManager,
- $this->appConfig
- );
- }
-
- public function testGetName(): void {
- $this->assertSame('Add preview cleanup background jobs', $this->repair->getName());
- }
-
- public function testRun(): void {
- $user1 = $this->createMock(IUser::class);
- $user1->method('getUID')
- ->willReturn('user1');
- $user2 = $this->createMock(IUser::class);
- $user2->method('getUID')
- ->willReturn('user2');
-
- $this->userManager->expects($this->once())
- ->method('callForSeenUsers')
- ->willReturnCallback(function (\Closure $function) use (&$user1, $user2): void {
- $function($user1);
- $function($user2);
- });
-
- $jobListCalls = [];
- $this->jobList->expects($this->exactly(2))
- ->method('add')
- ->willReturnCallback(function () use (&$jobListCalls): void {
- $jobListCalls[] = func_get_args();
- });
-
- $this->appConfig->expects($this->once())
- ->method('getValueBool')
- ->with(
- $this->equalTo('core'),
- $this->equalTo('previewsCleanedUp'),
- )->willReturn(false);
- $this->appConfig->expects($this->once())
- ->method('setValueBool')
- ->with(
- $this->equalTo('core'),
- $this->equalTo('previewsCleanedUp'),
- $this->equalTo(true)
- );
-
- $this->repair->run($this->createMock(IOutput::class));
- $this->assertEqualsCanonicalizing([
- [CleanPreviewsBackgroundJob::class, ['uid' => 'user1']],
- [CleanPreviewsBackgroundJob::class, ['uid' => 'user2']],
- ], $jobListCalls);
- }
-
- public function testRunAlreadyDone(): void {
- $this->userManager->expects($this->never())
- ->method($this->anything());
-
- $this->jobList->expects($this->never())
- ->method($this->anything());
-
- $this->appConfig->expects($this->once())
- ->method('getValueBool')
- ->with(
- $this->equalTo('core'),
- $this->equalTo('previewsCleanedUp'),
- )->willReturn(true);
- $this->appConfig->expects($this->never())
- ->method('setValueBool');
-
- $this->repair->run($this->createMock(IOutput::class));
- }
-}
--- a/tests/lib/Repair/Owncloud/UpdateLanguageCodesTest.php
+++ /dev/null
@@ -1,157 +0,0 @@
-connection = Server::get(IDBConnection::class);
- $this->config = $this->createMock(IConfig::class);
- }
-
- public function testRun(): void {
- $users = [
- ['userid' => 'user1', 'configvalue' => 'fi_FI'],
- ['userid' => 'user2', 'configvalue' => 'de'],
- ['userid' => 'user3', 'configvalue' => 'fi'],
- ['userid' => 'user4', 'configvalue' => 'ja'],
- ['userid' => 'user5', 'configvalue' => 'bg_BG'],
- ['userid' => 'user6', 'configvalue' => 'ja'],
- ['userid' => 'user7', 'configvalue' => 'th_TH'],
- ['userid' => 'user8', 'configvalue' => 'th_TH'],
- ];
-
- // insert test data
- $qb = $this->connection->getQueryBuilder();
- $qb->insert('preferences')
- ->values([
- 'userid' => $qb->createParameter('userid'),
- 'appid' => $qb->createParameter('appid'),
- 'configkey' => $qb->createParameter('configkey'),
- 'configvalue' => $qb->createParameter('configvalue'),
- ]);
- foreach ($users as $user) {
- $qb->setParameters([
- 'userid' => $user['userid'],
- 'appid' => 'core',
- 'configkey' => 'lang',
- 'configvalue' => $user['configvalue'],
- ])->executeStatement();
- }
-
- // check if test data is written to DB
- $qb = $this->connection->getQueryBuilder();
- $result = $qb->select(['userid', 'configvalue'])
- ->from('preferences')
- ->where($qb->expr()->eq('appid', $qb->createNamedParameter('core')))
- ->andWhere($qb->expr()->eq('configkey', $qb->createNamedParameter('lang')))
- ->orderBy('userid')
- ->executeQuery();
-
- $rows = $result->fetchAllAssociative();
- $result->closeCursor();
-
- $this->assertSame($users, $rows, 'Asserts that the entries are the ones from the test data set');
-
- $expectedOutput = [
- ['Changed 1 setting(s) from "bg_BG" to "bg" in preferences table.'],
- ['Changed 0 setting(s) from "cs_CZ" to "cs" in preferences table.'],
- ['Changed 1 setting(s) from "fi_FI" to "fi" in preferences table.'],
- ['Changed 0 setting(s) from "hu_HU" to "hu" in preferences table.'],
- ['Changed 0 setting(s) from "nb_NO" to "nb" in preferences table.'],
- ['Changed 0 setting(s) from "sk_SK" to "sk" in preferences table.'],
- ['Changed 2 setting(s) from "th_TH" to "th" in preferences table.'],
- ];
- $outputMessages = [];
- /** @var IOutput&MockObject $outputMock */
- $outputMock = $this->createMock(IOutput::class);
- $outputMock->expects($this->exactly(7))
- ->method('info')
- ->willReturnCallback(function () use (&$outputMessages): void {
- $outputMessages[] = func_get_args();
- });
-
- $this->config->expects($this->once())
- ->method('getSystemValueString')
- ->with('version', '0.0.0')
- ->willReturn('12.0.0.13');
-
- // run repair step
- $repair = new UpdateLanguageCodes($this->connection, $this->config);
- $repair->run($outputMock);
-
- // check if test data is correctly modified in DB
- $qb = $this->connection->getQueryBuilder();
- $result = $qb->select(['userid', 'configvalue'])
- ->from('preferences')
- ->where($qb->expr()->eq('appid', $qb->createNamedParameter('core')))
- ->andWhere($qb->expr()->eq('configkey', $qb->createNamedParameter('lang')))
- ->orderBy('userid')
- ->executeQuery();
-
- $rows = $result->fetchAllAssociative();
- $result->closeCursor();
-
- // value has changed for one user
- $users[0]['configvalue'] = 'fi';
- $users[4]['configvalue'] = 'bg';
- $users[6]['configvalue'] = 'th';
- $users[7]['configvalue'] = 'th';
- $this->assertSame($users, $rows, 'Asserts that the entries are updated correctly.');
-
- // remove test data
- foreach ($users as $user) {
- $qb = $this->connection->getQueryBuilder();
- $qb->delete('preferences')
- ->where($qb->expr()->eq('userid', $qb->createNamedParameter($user['userid'])))
- ->andWhere($qb->expr()->eq('appid', $qb->createNamedParameter('core')))
- ->andWhere($qb->expr()->eq('configkey', $qb->createNamedParameter('lang')))
- ->andWhere($qb->expr()->eq('configvalue', $qb->createNamedParameter($user['configvalue']), IQueryBuilder::PARAM_STR))
- ->executeStatement();
- }
- self::assertEquals($expectedOutput, $outputMessages);
- }
-
- public function testSecondRun(): void {
- /** @var IOutput&MockObject $outputMock */
- $outputMock = $this->createMock(IOutput::class);
- $outputMock->expects($this->never())
- ->method('info');
-
- $this->config->expects($this->once())
- ->method('getSystemValueString')
- ->with('version', '0.0.0')
- ->willReturn('12.0.0.14');
-
- // run repair step
- $repair = new UpdateLanguageCodes($this->connection, $this->config);
- $repair->run($outputMock);
- }
-}
--- a/tests/lib/Repair/RepairCollationTest.php
+++ /dev/null
@@ -1,87 +0,0 @@
-connection = Server::get(ConnectionAdapter::class);
- if ($this->connection->getDatabaseProvider() !== IDBConnection::PLATFORM_MYSQL) {
- $this->markTestSkipped('Test only relevant on MySql');
- }
-
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->config = Server::get(IConfig::class);
- $dbPrefix = $this->config->getSystemValueString('dbtableprefix');
- $this->tableName = $this->getUniqueID($dbPrefix . '_collation_test');
- $this->connection->prepare("CREATE TABLE $this->tableName(text VARCHAR(16)) COLLATE utf8_unicode_ci")->execute();
-
- $this->repair = new TestCollationRepair($this->config, $this->logger, $this->connection, false);
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->connection = Server::get(ConnectionAdapter::class);
- if ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_MYSQL) {
- // tear down only needed on MySQL
- $this->connection->getInner()->createSchemaManager()->dropTable($this->tableName);
- }
- parent::tearDown();
- }
-
- public function testCollationConvert(): void {
- $tables = $this->repair->getAllNonUTF8BinTables($this->connection);
- $this->assertGreaterThanOrEqual(1, count($tables));
-
- $outputMock = $this->getMockBuilder(IOutput::class)
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->repair->run($outputMock);
-
- $tables = $this->repair->getAllNonUTF8BinTables($this->connection);
- $this->assertCount(0, $tables);
- }
-}
--- a/tests/lib/Repair/RepairDavSharesTest.php
+++ /dev/null
@@ -1,179 +0,0 @@
-output = $this->createMock(IOutput::class);
-
- $this->config = $this->createMock(IConfig::class);
- $this->dbc = $this->createMock(IDBConnection::class);
- $this->groupManager = $this->createMock(IGroupManager::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $this->repair = new RepairDavShares(
- $this->config,
- $this->dbc,
- $this->groupManager,
- $this->logger
- );
- }
-
- public function testRun(): void {
- $this->config->expects($this->any())
- ->method('getSystemValueString')
- ->with('version', '0.0.0')
- ->willReturn('20.0.2');
-
- $this->output->expects($this->atLeastOnce())
- ->method('info');
-
- $existingGroups = [
- 'Innocent',
- 'Wants Repair',
- 'Well förmed',
- 'family+friends',
- 'family friends',
- ];
-
- $shareResultData = [
- [
- // No update, nothing to escape
- 'id' => 0,
- 'principaluri' => 'principals/groups/Innocent',
- ],
- [
- // Update
- 'id' => 1,
- 'principaluri' => 'principals/groups/Wants Repair',
- ],
- [
- // No update, already proper
- 'id' => 2,
- 'principaluri' => 'principals/groups/Well+f%C3%B6rmed',
- ],
- [
- // No update, unknown group
- 'id' => 3,
- 'principaluri' => 'principals/groups/Not known',
- ],
- [
- // No update, unknown group
- 'id' => 4,
- 'principaluri' => 'principals/groups/Also%2F%2FNot%23Known',
- ],
- [
- // No update, group exists in both forms
- 'id' => 5,
- 'principaluri' => 'principals/groups/family+friends',
- ],
- [
- // No update, already proper
- 'id' => 6,
- 'principaluri' => 'principals/groups/family%2Bfriends',
- ],
- [
- // Update
- 'id' => 7,
- 'principaluri' => 'principals/groups/family friends',
- ],
- ];
-
- $shareResults = $this->createMock(IResult::class);
- $shareResults->expects($this->any())
- ->method('fetchAssociative')
- ->willReturnCallback(function () use (&$shareResultData) {
- return array_pop($shareResultData) ?? false;
- });
-
- $expressionBuilder = $this->createMock(IExpressionBuilder::class);
-
- $selectMock = $this->createMock(IQueryBuilder::class);
- $selectMock->expects($this->any())
- ->method('expr')
- ->willReturn($expressionBuilder);
- $selectMock->expects($this->once())
- ->method('select')
- ->willReturnSelf();
- $selectMock->expects($this->once())
- ->method('from')
- ->willReturnSelf();
- $selectMock->expects($this->once())
- ->method('where')
- ->willReturnSelf();
- $selectMock->expects($this->once())
- ->method('executeQuery')
- ->willReturn($shareResults);
-
- $updateCalls = [];
- $updateMock = $this->createMock(IQueryBuilder::class);
- $updateMock->expects($this->any())
- ->method('expr')
- ->willReturn($expressionBuilder);
- $updateMock->expects($this->once())
- ->method('update')
- ->willReturnSelf();
- $updateMock->expects($this->any())
- ->method('set')
- ->willReturnSelf();
- $updateMock->expects($this->once())
- ->method('where')
- ->willReturnSelf();
- $updateMock->expects($this->exactly(4))
- ->method('setParameter')
- ->willReturnCallback(function () use (&$updateCalls, &$updateMock) {
- $updateCalls[] = func_get_args();
- return $updateMock;
- });
- $updateMock->expects($this->exactly(2))
- ->method('executeStatement');
-
- $this->dbc->expects($this->atLeast(2))
- ->method('getQueryBuilder')
- ->willReturnOnConsecutiveCalls($selectMock, $updateMock);
-
- $this->groupManager->expects($this->any())
- ->method('groupExists')
- ->willReturnCallback(function (string $gid) use ($existingGroups) {
- return in_array($gid, $existingGroups, true);
- });
-
- $this->repair->run($this->output);
- self::assertEquals([
- ['updatedPrincipalUri', 'principals/groups/' . urlencode('family friends'), null],
- ['shareId', 7, null],
- ['updatedPrincipalUri', 'principals/groups/' . urlencode('Wants Repair'), null],
- ['shareId', 1, null]
- ], $updateCalls);
- }
-}
--- a/tests/lib/Repair/RepairInvalidSharesTest.php
+++ /dev/null
@@ -1,272 +0,0 @@
-getMockBuilder(IConfig::class)
- ->disableOriginalConstructor()
- ->getMock();
- $config->expects($this->any())
- ->method('getSystemValueString')
- ->with('version')
- ->willReturn('12.0.0.0');
-
- $this->connection = Server::get(IDBConnection::class);
- $this->deleteAllShares();
-
- $this->appConfig = $this->createMock(IAppConfig::class);
-
- $this->repair = new RepairInvalidShares($config, $this->connection, $this->appConfig);
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->deleteAllShares();
-
- parent::tearDown();
- }
-
- protected function deleteAllShares() {
- $qb = $this->connection->getQueryBuilder();
- $qb->delete('share')->executeStatement();
- }
-
- /**
- * Test remove shares where the parent share does not exist anymore
- */
- public function testSharesNonExistingParent(): void {
- $qb = $this->connection->getQueryBuilder();
- $shareValues = [
- 'share_type' => $qb->expr()->literal(IShare::TYPE_USER),
- 'share_with' => $qb->expr()->literal('recipientuser1'),
- 'uid_owner' => $qb->expr()->literal('user1'),
- 'item_type' => $qb->expr()->literal('folder'),
- 'item_source' => $qb->expr()->literal(123),
- 'item_target' => $qb->expr()->literal('/123'),
- 'file_source' => $qb->expr()->literal(123),
- 'file_target' => $qb->expr()->literal('/test'),
- 'permissions' => $qb->expr()->literal(1),
- 'stime' => $qb->expr()->literal(time()),
- 'expiration' => $qb->expr()->literal('2015-09-25 00:00:00')
- ];
-
- // valid share
- $qb = $this->connection->getQueryBuilder();
- $qb->insert('share')
- ->values($shareValues)
- ->executeStatement();
- $parent = $qb->getLastInsertId();
-
- // share with existing parent
- $qb = $this->connection->getQueryBuilder();
- $qb->insert('share')
- ->values(array_merge($shareValues, [
- 'parent' => $qb->expr()->literal($parent),
- ]))->executeStatement();
- $validChild = $qb->getLastInsertId();
-
- // share with non-existing parent
- $qb = $this->connection->getQueryBuilder();
- $qb->insert('share')
- ->values(array_merge($shareValues, [
- 'parent' => $qb->expr()->literal($parent + 100),
- ]))->executeStatement();
- $invalidChild = $qb->getLastInsertId();
-
- $query = $this->connection->getQueryBuilder();
- $result = $query->select('id')
- ->from('share')
- ->orderBy('id', 'ASC')
- ->executeQuery();
- $rows = $result->fetchAllAssociative();
- $this->assertEquals([['id' => $parent], ['id' => $validChild], ['id' => $invalidChild]], $rows);
- $result->closeCursor();
-
- /** @var IOutput | \PHPUnit\Framework\MockObject\MockObject $outputMock */
- $outputMock = $this->getMockBuilder('\OCP\Migration\IOutput')
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->repair->run($outputMock);
-
- $query = $this->connection->getQueryBuilder();
- $result = $query->select('id')
- ->from('share')
- ->orderBy('id', 'ASC')
- ->executeQuery();
- $rows = $result->fetchAllAssociative();
- $this->assertEquals([['id' => $parent], ['id' => $validChild]], $rows);
- $result->closeCursor();
- }
-
- public static function trailingSlashProvider(): array {
- return [
- // trailing slash left behind by renaming a parent folder of a moved share
- ['/rename_folder/First_share_the_file.odt/', '/rename_folder/First_share_the_file.odt'],
- ['/shared_folder/', '/shared_folder'],
- // unchanged
- ['/rename_folder/First_share_the_file.odt', '/rename_folder/First_share_the_file.odt'],
- ['/', '/'],
- ];
- }
-
- /**
- * Test stripping trailing slashes from the share target
- */
- private function addShareWithTarget(string $fileTarget): void {
- $qb = $this->connection->getQueryBuilder();
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_USER),
- 'share_with' => $qb->expr()->literal('recipientuser1'),
- 'uid_owner' => $qb->expr()->literal('user1'),
- 'item_type' => $qb->expr()->literal('folder'),
- 'item_source' => $qb->expr()->literal(123),
- 'item_target' => $qb->expr()->literal('/123'),
- 'file_source' => $qb->expr()->literal(123),
- 'file_target' => $qb->expr()->literal($fileTarget),
- 'permissions' => $qb->expr()->literal(31),
- 'stime' => $qb->expr()->literal(time()),
- ])
- ->executeStatement();
- }
-
- private function getSingleFileTarget(): string {
- $results = $this->connection->getQueryBuilder()
- ->select('file_target')
- ->from('share')
- ->executeQuery()
- ->fetchAllAssociative();
-
- $this->assertCount(1, $results);
-
- return $results[0]['file_target'];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('trailingSlashProvider')]
- public function testRemoveTrailingSlashFromFileTarget(string $fileTarget, string $expectedFileTarget): void {
- $this->addShareWithTarget($fileTarget);
-
- $this->appConfig->method('getValueBool')
- ->with('core', ConfigLexicon::SHARE_REPAIR_REMOVED_TRAILING_SLASHES, false, true)
- ->willReturn(false);
- $this->appConfig->expects($this->once())
- ->method('setValueBool')
- ->with('core', ConfigLexicon::SHARE_REPAIR_REMOVED_TRAILING_SLASHES, true, true);
-
- $this->repair->run($this->createMock(IOutput::class));
-
- $this->assertSame($expectedFileTarget, $this->getSingleFileTarget());
- }
-
- public function testRemoveTrailingSlashFromFileTargetSkippedWhenAlreadyRun(): void {
- $this->addShareWithTarget('/rename_folder/First_share_the_file.odt/');
-
- $this->appConfig->method('getValueBool')
- ->with('core', ConfigLexicon::SHARE_REPAIR_REMOVED_TRAILING_SLASHES, false, true)
- ->willReturn(true);
- $this->appConfig->expects($this->never())
- ->method('setValueBool');
-
- $this->repair->run($this->createMock(IOutput::class));
-
- $this->assertSame('/rename_folder/First_share_the_file.odt/', $this->getSingleFileTarget());
- }
-
- public static function fileSharePermissionsProvider(): array {
- return [
- // unchanged for folder
- [
- 'folder',
- 31,
- 31,
- ],
- // unchanged for read-write + share
- [
- 'file',
- Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE | Constants::PERMISSION_SHARE,
- Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE | Constants::PERMISSION_SHARE,
- ],
- // fixed for all perms
- [
- 'file',
- Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE | Constants::PERMISSION_DELETE | Constants::PERMISSION_SHARE,
- Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE | Constants::PERMISSION_SHARE,
- ],
- ];
- }
-
- /**
- * Test adjusting file share permissions
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('fileSharePermissionsProvider')]
- public function testFileSharePermissions($itemType, $testPerms, $expectedPerms): void {
- $qb = $this->connection->getQueryBuilder();
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_LINK),
- 'uid_owner' => $qb->expr()->literal('user1'),
- 'item_type' => $qb->expr()->literal($itemType),
- 'item_source' => $qb->expr()->literal(123),
- 'item_target' => $qb->expr()->literal('/123'),
- 'file_source' => $qb->expr()->literal(123),
- 'file_target' => $qb->expr()->literal('/test'),
- 'permissions' => $qb->expr()->literal($testPerms),
- 'stime' => $qb->expr()->literal(time()),
- ])
- ->executeStatement();
-
- /** @var IOutput | \PHPUnit\Framework\MockObject\MockObject $outputMock */
- $outputMock = $this->getMockBuilder('\OCP\Migration\IOutput')
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->repair->run($outputMock);
-
- $results = $this->connection->getQueryBuilder()
- ->select('*')
- ->from('share')
- ->orderBy('permissions', 'ASC')
- ->executeQuery()
- ->fetchAllAssociative();
-
- $this->assertCount(1, $results);
-
- $updatedShare = $results[0];
-
- $this->assertEquals($expectedPerms, $updatedShare['permissions']);
- }
-}
--- a/tests/lib/Repair/RepairMimeTypesTest.php
+++ /dev/null
@@ -1,295 +0,0 @@
-mimetypeLoader = Server::get(IMimeTypeLoader::class);
- $this->db = Server::get(IDBConnection::class);
-
- $config = $this->getMockBuilder(IConfig::class)
- ->disableOriginalConstructor()
- ->getMock();
- $config->method('getSystemValueString')
- ->with('version')
- ->willReturn('11.0.0.0');
-
- $appConfig = $this->getMockBuilder(IAppConfig::class)
- ->disableOriginalConstructor()
- ->getMock();
- $appConfig->method('getValueString')
- ->with('files', 'mimetype_version')
- ->willReturn('11.0.0.0');
-
- $this->storage = new Temporary([]);
- $this->storage->getScanner()->scan('');
-
- $this->repair = new RepairMimeTypes(
- $config,
- $appConfig,
- Server::get(IDBConnection::class),
- );
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->storage->getCache()->clear();
-
- $qb = $this->db->getQueryBuilder();
- $qb->delete('storages')
- ->where($qb->expr()->eq('id', $qb->createNamedParameter($this->storage->getId())));
- $qb->executeStatement();
-
- $this->clearMimeTypes();
-
- parent::tearDown();
- }
-
- private function clearMimeTypes() {
- $qb = $this->db->getQueryBuilder();
- $qb->delete('mimetypes');
- $qb->executeStatement();
-
- $this->mimetypeLoader->reset();
- }
-
- private function addEntries($entries) {
- // create files for the different extensions, this
- // will also automatically create the corresponding mime types
- foreach ($entries as $entry) {
- $this->storage->getCache()->put(
- $entry[0],
- [
- 'size' => 0,
- 'mtime' => 0,
- 'mimetype' => $entry[1]
- ]
- );
- }
- }
-
- private function checkEntries($entries) {
- foreach ($entries as $entry) {
- $data = $this->storage->getCache()->get($entry[0]);
- $this->assertEquals($entry[1], $data['mimetype']);
- }
- }
-
- private function renameMimeTypes($currentMimeTypes, $fixedMimeTypes) {
- $this->addEntries($currentMimeTypes);
-
- /** @var IOutput | \PHPUnit\Framework\MockObject\MockObject $outputMock */
- $outputMock = $this->getMockBuilder('\OCP\Migration\IOutput')
- ->disableOriginalConstructor()
- ->getMock();
-
- $this->repair->run($outputMock);
-
- // force mimetype reload
- $this->mimetypeLoader->reset();
-
- $this->checkEntries($fixedMimeTypes);
- }
-
- /**
- * Test renaming the additional image mime types
- */
- public function testRenameImageTypes(): void {
- $currentMimeTypes = [
- ['test.jp2', 'application/octet-stream'],
- ['test.webp', 'application/octet-stream'],
- ];
-
- $fixedMimeTypes = [
- ['test.jp2', 'image/jp2'],
- ['test.webp', 'image/webp'],
- ];
-
- $this->renameMimeTypes($currentMimeTypes, $fixedMimeTypes);
- }
-
- /**
- * Test renaming the richdocuments additional office mime types
- */
- public function testRenameWindowsProgramTypes(): void {
- $currentMimeTypes = [
- ['test.htaccess', 'application/octet-stream'],
- ['.htaccess', 'application/octet-stream'],
- ['test.bat', 'application/octet-stream'],
- ['test.cmd', 'application/octet-stream'],
- ];
-
- $fixedMimeTypes = [
- ['test.htaccess', 'text/plain'],
- ['.htaccess', 'text/plain'],
- ['test.bat', 'application/x-msdos-program'],
- ['test.cmd', 'application/cmd'],
- ];
-
- $this->renameMimeTypes($currentMimeTypes, $fixedMimeTypes);
- }
-
- /**
- * Test that nothing happens and no error happens when all mimetypes are
- * already correct and no old ones exist..
- */
- public function testDoNothingWhenOnlyNewFiles(): void {
- $currentMimeTypes = [
- ['test.doc', 'application/msword'],
- ['test.docx', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'],
- ['test.xls', 'application/vnd.ms-excel'],
- ['test.xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],
- ['test.ppt', 'application/vnd.ms-powerpoint'],
- ['test.pptx', 'application/vnd.openxmlformats-officedocument.presentationml.presentation'],
- ['test.apk', 'application/vnd.android.package-archive'],
- ['test.ttf', 'application/font-sfnt'],
- ['test.otf', 'application/font-sfnt'],
- ['test.pfb', 'application/x-font'],
- ['test.eps', 'application/postscript'],
- ['test.ps', 'application/postscript'],
- ['test.arw', 'image/x-dcraw'],
- ['test.cr2', 'image/x-dcraw'],
- ['test.dcr', 'image/x-dcraw'],
- ['test.dng', 'image/x-dcraw'],
- ['test.erf', 'image/x-dcraw'],
- ['test.iiq', 'image/x-dcraw'],
- ['test.k25', 'image/x-dcraw'],
- ['test.kdc', 'image/x-dcraw'],
- ['test.mef', 'image/x-dcraw'],
- ['test.nef', 'image/x-dcraw'],
- ['test.orf', 'image/x-dcraw'],
- ['test.pef', 'image/x-dcraw'],
- ['test.raf', 'image/x-dcraw'],
- ['test.rw2', 'image/x-dcraw'],
- ['test.srf', 'image/x-dcraw'],
- ['test.sr2', 'image/x-dcraw'],
- ['test.xrf', 'image/x-dcraw'],
- ['test.DNG', 'image/x-dcraw'],
- ['test.jp2', 'image/jp2'],
- ['test.jps', 'image/jpeg'],
- ['test.MPO', 'image/jpeg'],
- ['test.webp', 'image/webp'],
- ['test.conf', 'text/plain'],
- ['test.cnf', 'text/plain'],
- ['test.yaml', 'application/yaml'],
- ['test.yml', 'application/yaml'],
- ['test.java', 'text/x-java-source'],
- ['test.class', 'application/java'],
- ['test.hpp', 'text/x-h'],
- ['test.rss', 'application/rss+xml'],
- ['test.rtf', 'text/rtf'],
- ['test.lwp', 'application/vnd.lotus-wordpro'],
- ['test.one', 'application/msonenote'],
- ['test.vsd', 'application/vnd.visio'],
- ['test.wpd', 'application/vnd.wordperfect'],
- ['test.htaccess', 'text/plain'],
- ['.htaccess', 'text/plain'],
- ['test.bat', 'application/x-msdos-program'],
- ['test.cmd', 'application/cmd'],
- ];
-
- $fixedMimeTypes = [
- ['test.doc', 'application/msword'],
- ['test.docx', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'],
- ['test.xls', 'application/vnd.ms-excel'],
- ['test.xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],
- ['test.ppt', 'application/vnd.ms-powerpoint'],
- ['test.pptx', 'application/vnd.openxmlformats-officedocument.presentationml.presentation'],
- ['test.apk', 'application/vnd.android.package-archive'],
- ['test.ttf', 'application/font-sfnt'],
- ['test.otf', 'application/font-sfnt'],
- ['test.pfb', 'application/x-font'],
- ['test.eps', 'application/postscript'],
- ['test.ps', 'application/postscript'],
- ['test.arw', 'image/x-dcraw'],
- ['test.cr2', 'image/x-dcraw'],
- ['test.dcr', 'image/x-dcraw'],
- ['test.dng', 'image/x-dcraw'],
- ['test.erf', 'image/x-dcraw'],
- ['test.iiq', 'image/x-dcraw'],
- ['test.k25', 'image/x-dcraw'],
- ['test.kdc', 'image/x-dcraw'],
- ['test.mef', 'image/x-dcraw'],
- ['test.nef', 'image/x-dcraw'],
- ['test.orf', 'image/x-dcraw'],
- ['test.pef', 'image/x-dcraw'],
- ['test.raf', 'image/x-dcraw'],
- ['test.rw2', 'image/x-dcraw'],
- ['test.srf', 'image/x-dcraw'],
- ['test.sr2', 'image/x-dcraw'],
- ['test.xrf', 'image/x-dcraw'],
- ['test.DNG', 'image/x-dcraw'],
- ['test.jp2', 'image/jp2'],
- ['test.jps', 'image/jpeg'],
- ['test.MPO', 'image/jpeg'],
- ['test.webp', 'image/webp'],
- ['test.conf', 'text/plain'],
- ['test.cnf', 'text/plain'],
- ['test.yaml', 'application/yaml'],
- ['test.yml', 'application/yaml'],
- ['test.java', 'text/x-java-source'],
- ['test.class', 'application/java'],
- ['test.hpp', 'text/x-h'],
- ['test.rss', 'application/rss+xml'],
- ['test.rtf', 'text/rtf'],
- ['test.lwp', 'application/vnd.lotus-wordpro'],
- ['test.one', 'application/msonenote'],
- ['test.vsd', 'application/vnd.visio'],
- ['test.wpd', 'application/vnd.wordperfect'],
- ['test.htaccess', 'text/plain'],
- ['.htaccess', 'text/plain'],
- ['test.bat', 'application/x-msdos-program'],
- ['test.cmd', 'application/cmd'],
- ];
-
- $this->renameMimeTypes($currentMimeTypes, $fixedMimeTypes);
- }
-
- /**
- * Test that mime type renaming does not affect folders
- */
- public function testDoNotChangeFolderMimeType(): void {
- $currentMimeTypes = [
- ['test.conf', 'httpd/unix-directory'],
- ['test.cnf', 'httpd/unix-directory'],
- ];
-
- $fixedMimeTypes = [
- ['test.conf', 'httpd/unix-directory'],
- ['test.cnf', 'httpd/unix-directory'],
- ];
-
- $this->renameMimeTypes($currentMimeTypes, $fixedMimeTypes);
- }
-}
--- a/tests/lib/Repair/RepairSanitizeSystemTagsTest.php
+++ /dev/null
@@ -1,211 +0,0 @@
-connection = Server::get(IDBConnection::class);
- $this->output = $this->createMock(IOutput::class);
- $this->repair = new RepairSanitizeSystemTags($this->connection);
-
- $this->cleanUpTables();
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->cleanUpTables();
- parent::tearDown();
- }
-
- private function cleanUpTables(): void {
- $this->connection->getQueryBuilder()->delete('systemtag_object_mapping')->executeStatement();
- $this->connection->getQueryBuilder()->delete('systemtag')->executeStatement();
- }
-
- public function testMigrationsAvailableFalseWhenAllClean(): void {
- $this->addTag('Clean');
- $this->addTag('Also clean');
-
- $this->assertFalse($this->repair->migrationsAvailable());
- }
-
- public function testMigrationsAvailableTrueWhenNameNeedsSanitizing(): void {
- $this->addTag('Clean');
- $this->addTag(' Needs trimming ');
-
- $this->assertTrue($this->repair->migrationsAvailable());
- }
-
- public function testMigrationsAvailableIgnoresFalsyName(): void {
- // A PHP-falsy name ('0') must not stop the scan before later dirty tags are
- // seen. We avoid '' here because Oracle stores it as NULL, which the notnull
- // name column rejects.
- $this->addTag('0');
- $this->addTag('Needs trimming');
-
- $this->assertTrue($this->repair->migrationsAvailable());
- }
-
- public function testRunSanitizesSingleTag(): void {
- // Leading/trailing whitespace is trimmed and inner runs collapse to a single space
- $id = $this->addTag(' My Tag ');
-
- $this->repair->run($this->output);
-
- $this->assertSame('My Tag', $this->getTagName($id));
- $this->assertFalse($this->repair->migrationsAvailable());
- }
-
- public function testRunMergesDuplicatesAndReassignsMappings(): void {
- // Both sanitize to 'Report card', same visibility/editable, so they must merge.
- // The variants differ by an inner double space, which is significant in the DB
- // unique index (unlike trailing spaces, which MySQL/Oracle ignore).
- $keep = $this->addTag('Report card');
- $dup = $this->addTag('Report card');
-
- // The kept tag is the one with the most object mappings
- $this->addMapping($keep, '1');
- $this->addMapping($keep, '2');
- $this->addMapping($dup, '3');
-
- $this->repair->run($this->output);
-
- // Only one tag remains, named 'Report card'
- $this->assertSame(1, $this->countTags());
- $this->assertSame('Report card', $this->getTagName($keep));
- $this->assertNull($this->getTagName($dup));
-
- // The duplicate's mapping was reassigned to the kept tag
- $this->assertSame(3, $this->countMappings($keep));
- }
-
- public function testRunKeepsTagWithMostMappings(): void {
- $few = $this->addTag('Photo album');
- $many = $this->addTag('Photo album');
-
- $this->addMapping($few, '1');
- $this->addMapping($many, '2');
- $this->addMapping($many, '3');
-
- $this->repair->run($this->output);
-
- $this->assertNull($this->getTagName($few));
- $this->assertSame('Photo album', $this->getTagName($many));
- $this->assertSame(3, $this->countMappings($many));
- }
-
- public function testRunDeletesConflictingMappings(): void {
- $a = $this->addTag('Draft doc');
- $b = $this->addTag('Draft doc');
-
- // Object '1' is mapped to both tags. After the merge that conflict must be
- // deduplicated: the surviving tag ends up with objects '1' and '2' only once each.
- $this->addMapping($a, '1');
- $this->addMapping($b, '1');
- $this->addMapping($b, '2');
-
- $this->repair->run($this->output);
-
- // A single tag remains and holds exactly two mappings (no duplicated object '1')
- $this->assertSame(1, $this->countTags());
- $this->assertSame(2, $this->countAllMappings());
- }
-
- public function testRunSkipsMergeWhenVisibilityDiffers(): void {
- // Same sanitized name but different visibility → must not merge, only warn
- $a = $this->addTag('Shared note', visibility: 1);
- $b = $this->addTag('Shared note', visibility: 0);
-
- $this->output->expects($this->atLeastOnce())->method('warning');
-
- $this->repair->run($this->output);
-
- $this->assertSame(2, $this->countTags());
- $this->assertNotNull($this->getTagName($a));
- $this->assertNotNull($this->getTagName($b));
- }
-
- private function addTag(string $name, int $visibility = 1, int $editable = 1): int {
- $qb = $this->connection->getQueryBuilder();
- $qb->insert('systemtag')
- ->values([
- 'name' => $qb->createNamedParameter($name),
- 'visibility' => $qb->createNamedParameter($visibility, IQueryBuilder::PARAM_INT),
- 'editable' => $qb->createNamedParameter($editable, IQueryBuilder::PARAM_INT),
- ])
- ->executeStatement();
-
- return $qb->getLastInsertId();
- }
-
- private function addMapping(int $tagId, string $objectId, string $objectType = 'files'): void {
- $qb = $this->connection->getQueryBuilder();
- $qb->insert('systemtag_object_mapping')
- ->values([
- 'objectid' => $qb->createNamedParameter($objectId),
- 'objecttype' => $qb->createNamedParameter($objectType),
- 'systemtagid' => $qb->createNamedParameter($tagId, IQueryBuilder::PARAM_INT),
- ])
- ->executeStatement();
- }
-
- private function getTagName(int $id): ?string {
- $qb = $this->connection->getQueryBuilder();
- $name = $qb->select('name')
- ->from('systemtag')
- ->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)))
- ->executeQuery()
- ->fetchOne();
-
- return $name === false ? null : $name;
- }
-
- private function countTags(): int {
- $qb = $this->connection->getQueryBuilder();
- return (int)$qb->select($qb->func()->count('*'))
- ->from('systemtag')
- ->executeQuery()
- ->fetchOne();
- }
-
- private function countMappings(int $tagId): int {
- $qb = $this->connection->getQueryBuilder();
- return (int)$qb->select($qb->func()->count('*'))
- ->from('systemtag_object_mapping')
- ->where($qb->expr()->eq('systemtagid', $qb->createNamedParameter($tagId, IQueryBuilder::PARAM_INT)))
- ->executeQuery()
- ->fetchOne();
- }
-
- private function countAllMappings(): int {
- $qb = $this->connection->getQueryBuilder();
- return (int)$qb->select($qb->func()->count('*'))
- ->from('systemtag_object_mapping')
- ->executeQuery()
- ->fetchOne();
- }
-}
--- a/tests/lib/RepairTest.php
+++ /dev/null
@@ -1,142 +0,0 @@
-warning) {
- $out->warning('Simulated warning');
- } else {
- $out->info('Simulated info');
- }
- }
-}
-
-class RepairTest extends TestCase {
- private Repair $repair;
-
- /** @var string[] */
- private array $outputArray = [];
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
- $dispatcher = Server::get(IEventDispatcher::class);
- $this->repair = new Repair($dispatcher, $this->createMock(LoggerInterface::class));
-
- $dispatcher->addListener(RepairWarningEvent::class, function (RepairWarningEvent $event): void {
- $this->outputArray[] = 'warning: ' . $event->getMessage();
- });
- $dispatcher->addListener(RepairInfoEvent::class, function (RepairInfoEvent $event): void {
- $this->outputArray[] = 'info: ' . $event->getMessage();
- });
- $dispatcher->addListener(RepairStepEvent::class, function (RepairStepEvent $event): void {
- $this->outputArray[] = 'step: ' . $event->getStepName();
- });
- $dispatcher->addListener(RepairErrorEvent::class, function (RepairErrorEvent $event): void {
- $this->outputArray[] = 'error: ' . $event->getMessage();
- });
- }
-
- public function testRunRepairStep(): void {
- $this->repair->addStep(new TestRepairStep(false));
- $this->repair->run();
-
- $this->assertEquals(
- [
- 'step: Test Name',
- 'info: Simulated info',
- ],
- $this->outputArray
- );
- }
-
- public function testRunRepairStepThatFail(): void {
- $this->repair->addStep(new TestRepairStep(true));
- $this->repair->run();
-
- $this->assertEquals(
- [
- 'step: Test Name',
- 'warning: Simulated warning',
- ],
- $this->outputArray
- );
- }
-
- public function testRunRepairStepsWithException(): void {
- $mock = $this->createMock(TestRepairStep::class);
- $mock->expects($this->any())
- ->method('run')
- ->willThrowException(new \Exception('Exception text'));
- $mock->expects($this->any())
- ->method('getName')
- ->willReturn('Exception Test');
-
- $this->repair->addStep($mock);
- $this->repair->addStep(new TestRepairStep(false));
-
- $thrown = false;
- try {
- $this->repair->run();
- } catch (\Exception $e) {
- $thrown = true;
- }
-
- $this->assertFalse($thrown);
- // jump out after exception
- $this->assertEquals(
- [
- 'step: Exception Test',
- 'error: Exception text',
- 'step: Test Name',
- 'info: Simulated info',
- ],
- $this->outputArray
- );
- }
-
- public function testRunRepairStepsContinueAfterWarning(): void {
- $this->repair->addStep(new TestRepairStep(true));
- $this->repair->addStep(new TestRepairStep(false));
- $this->repair->run();
-
- $this->assertEquals(
- [
- 'step: Test Name',
- 'warning: Simulated warning',
- 'step: Test Name',
- 'info: Simulated info',
- ],
- $this->outputArray
- );
- }
-}
--- a/tests/lib/RichObjectStrings/DefinitionsTest.php
+++ /dev/null
@@ -1,75 +0,0 @@
-definitions as $type => $definition) {
- $testsuite[] = [$type, $definition];
- }
- return $testsuite;
- }
-
- public function testGetDefinitionNotExisting(): void {
- $this->expectException(InvalidObjectExeption::class);
- $this->expectExceptionMessage('Object type is undefined');
-
- $definitions = new Definitions();
- $definitions->getDefinition('NotExistingType');
- }
-
- /**
- * @param string $type
- * @param array $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetDefinition')]
- public function testGetDefinition($type, array $expected): void {
- $definitions = new Definitions();
- $definition = $definitions->getDefinition($type);
-
- $this->assertEquals($expected, $definition);
- $this->assertArrayHasKey('author', $definition);
- $this->assertNotEquals('', $definition['author'], 'Author of definition must not be empty');
- $this->assertArrayHasKey('app', $definition);
- $this->assertNotEquals('', $definition['app'], 'App of definition must not be empty');
- $this->assertArrayHasKey('since', $definition);
- $this->assertNotEmpty($definition['since'], 'Since of definition must not be empty');
- $this->assertArrayHasKey('parameters', $definition);
- $this->assertTrue(is_array($definition['parameters']), 'Parameters of definition must be of type array');
- $this->assertNotEmpty($definition['parameters'], 'Parameters of definition must not be empty');
-
- $this->assertArrayHasKey('id', $definition['parameters'], 'Parameter ID must be defined');
- $this->assertArrayHasKey('name', $definition['parameters'], 'Parameter name must be defined');
-
- foreach ($definition['parameters'] as $parameter => $data) {
- $this->validateParameter($parameter, $data);
- }
- }
-
- public function validateParameter($parameter, $data) {
- $this->assertTrue(is_array($data), 'Parameter ' . $parameter . ' is invalid');
- $this->assertArrayHasKey('since', $data);
- $this->assertNotEmpty($data['since'], 'Since of parameter ' . $parameter . ' must not be empty');
- $this->assertArrayHasKey('required', $data);
- $this->assertTrue(is_bool($data['required']), 'Required of parameter ' . $parameter . ' must be a boolean');
- if ($parameter === 'id' || $parameter === 'name') {
- $this->assertTrue($data['required'], 'Parameter ' . $parameter . ' must be required');
- }
-
- $this->assertArrayHasKey('description', $data);
- $this->assertNotEquals('', $data['description'], 'Description of parameter ' . $parameter . ' must not be empty');
- $this->assertArrayHasKey('example', $data);
- $this->assertNotEquals('', $data['example'], 'Example of parameter ' . $parameter . ' must not be empty');
- }
-}
--- a/tests/lib/RichObjectStrings/ValidatorTest.php
+++ /dev/null
@@ -1,103 +0,0 @@
-validate('test', []);
- $v->validate('test {string1} test {foo} test {bar}.', [
- 'string1' => [
- 'type' => 'user',
- 'id' => 'johndoe',
- 'name' => 'John Doe',
- ],
- 'foo' => [
- 'type' => 'user-group',
- 'id' => 'sample',
- 'name' => 'Sample Group',
- ],
- 'bar' => [
- 'type' => 'file',
- 'id' => '42',
- 'name' => 'test.txt',
- 'path' => 'path/to/test.txt',
- ],
- ]);
- $this->addToAssertionCount(2);
-
- $this->expectException(InvalidObjectExeption::class);
-
- $this->expectExceptionMessage('Object for placeholder string1 is invalid, value 123 for key key is not a string');
- $v->validate('test {string1} test.', [
- 'string1' => [
- 'type' => 'user',
- 'id' => 'johndoe',
- 'name' => 'John Doe',
- 'key' => 123,
- ],
- ]);
-
- $this->expectExceptionMessage('Object for placeholder string1 is invalid, key 456 is not a string');
- $v->validate('test {string1} test.', [
- 'string1' => [
- 'type' => 'user',
- 'id' => 'johndoe',
- 'name' => 'John Doe',
- 456 => 'value',
- ],
- ]);
- }
-
- public static function dataValidateParameterKeys(): array {
- return [
- 'not a string' => ['key' => 0, 'throws' => 'Parameter key is invalid'],
- '@ is not allowed' => ['key' => 'user@0', 'throws' => 'Parameter key is invalid'],
- '? is not allowed' => ['key' => 'user?0', 'throws' => 'Parameter key is invalid'],
- 'slash is not allowed' => ['key' => 'user/0', 'throws' => 'Parameter key is invalid'],
- 'backslash is not allowed' => ['key' => 'user\\0', 'throws' => 'Parameter key is invalid'],
- 'hash is not allowed' => ['key' => 'user#0', 'throws' => 'Parameter key is invalid'],
- 'space is not allowed' => ['key' => 'user 0', 'throws' => 'Parameter key is invalid'],
- 'has to start with letter, but is number' => ['key' => '0abc', 'throws' => 'Parameter key is invalid'],
- 'has to start with letter, but is dot' => ['key' => '.abc', 'throws' => 'Parameter key is invalid'],
- 'has to start with letter, but is slash' => ['key' => '-abc', 'throws' => 'Parameter key is invalid'],
- 'has to start with letter, but is underscore' => ['key' => '_abc', 'throws' => 'Parameter key is invalid'],
- ['key' => 'user-0', 'throws' => null],
- ['key' => 'user_0', 'throws' => null],
- ['key' => 'user.0', 'throws' => null],
- ['key' => 'a._-0', 'throws' => null],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataValidateParameterKeys')]
- public function testValidateParameterKeys(mixed $key, ?string $throws): void {
-
- if ($throws !== null) {
- $this->expectExceptionMessage($throws);
- }
-
- $v = new Validator(new Definitions());
- $v->validate('{' . $key . '}', [
- $key => [
- 'type' => 'highlight',
- 'id' => 'identifier',
- 'name' => 'Display name',
- ],
- ]);
-
- if ($throws === null) {
- $this->addToAssertionCount(1);
- }
- }
-}
--- a/tests/lib/Route/RouterTest.php
+++ /dev/null
@@ -1,79 +0,0 @@
-createMock(LoggerInterface::class);
- $logger->method('info')
- ->willReturnCallback(
- function (string $message, array $data): void {
- $this->fail('Unexpected info log: ' . (string)($data['exception'] ?? $message));
- }
- );
-
- $this->appManager = $this->createMock(IAppManager::class);
-
- $this->router = new Router(
- $logger,
- $this->createMock(IRequest::class),
- $this->createMock(IConfig::class),
- $this->createMock(IEventLogger::class),
- $this->createMock(ContainerInterface::class),
- $this->appManager,
- );
- }
-
- public function testHeartbeat(): void {
- $this->assertEquals('/index.php/heartbeat', $this->router->generate('heartbeat'));
- }
-
- public function testGenerateConsecutively(): void {
- $this->appManager->expects(self::atLeastOnce())
- ->method('cleanAppId')
- ->willReturnArgument(0);
- $this->appManager->expects(self::atLeastOnce())
- ->method('getAppPath')
- ->willReturnCallback(fn (string $appid): string => \OC::$SERVERROOT . '/apps/' . $appid);
- $this->appManager->expects(self::atLeastOnce())
- ->method('isAppLoaded')
- ->willReturn(true);
-
- $this->assertEquals('/index.php/apps/files/', $this->router->generate('files.view.index'));
-
- // the OCS route is the prefixed one for the AppFramework - see /ocs/v1.php for routing details
- $this->assertEquals('/index.php/ocsapp/apps/dav/api/v1/direct', $this->router->generate('ocs.dav.direct.getUrl'));
-
- // test caching
- $this->assertEquals('/index.php/apps/files/', $this->router->generate('files.view.index'));
- }
-}
--- a/tests/lib/Search/SearchComposerTest.php
+++ /dev/null
@@ -1,291 +0,0 @@
-bootstrapCoordinator = $this->createMock(Coordinator::class);
- $this->container = $this->createMock(ContainerInterface::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->appConfig = $this->createMock(IAppConfig::class);
-
- $this->searchComposer = new SearchComposer(
- $this->bootstrapCoordinator,
- $this->container,
- $this->urlGenerator,
- $this->logger,
- $this->appConfig
- );
-
- $this->setupUrlGenerator();
- }
-
- private function setupUrlGenerator(): void {
- $this->urlGenerator->method('imagePath')
- ->willReturnCallback(function ($appId, $imageName) {
- return "/apps/$appId/img/$imageName";
- });
- }
-
- private function setupEmptyRegistrationContext(): void {
- $this->bootstrapCoordinator->expects($this->once())
- ->method('getRegistrationContext')
- ->willReturn(null);
- }
-
- private function setupAppConfigForAllowedProviders(array $allowedProviders = []): void {
- $this->appConfig->method('getValueArray')
- ->with('core', 'unified_search.providers_allowed')
- ->willReturn($allowedProviders);
- }
-
- /**
- * @param array $providerConfigs
- * @return array{registrations: ServiceRegistration[], providers: IProvider[]}
- */
- private function createMockProvidersAndRegistrations(array $providerConfigs): array {
- $registrations = [];
- $providers = [];
- $containerMap = [];
-
- foreach ($providerConfigs as $providerId => $config) {
- // Create registration mock
- $registration = $this->createMock(ServiceRegistration::class);
- $registration->method('getService')->willReturn($config['service']);
- $registration->method('getAppId')->willReturn($config['appId']);
- $registrations[] = $registration;
-
- // Create provider mock
- $providerClass = $config['isInApp'] ?? false ? IInAppSearch::class : IProvider::class;
- $provider = $this->createMock($providerClass);
- $provider->method('getId')->willReturn($providerId);
- $provider->method('getName')->willReturn("Provider $providerId");
- $provider->method('getOrder')->willReturn($config['order']);
-
- $providers[$providerId] = $provider;
- $containerMap[] = [$config['service'], $provider];
- }
-
- $this->container->expects($this->exactly(count($providerConfigs)))
- ->method('get')
- ->willReturnMap($containerMap);
-
- return ['registrations' => $registrations, 'providers' => $providers];
- }
-
- private function setupRegistrationContextWithProviders(array $registrations): void {
- $registrationContext = $this->createMock(RegistrationContext::class);
- $registrationContext->method('getSearchProviders')->willReturn($registrations);
-
- $this->bootstrapCoordinator->expects($this->once())
- ->method('getRegistrationContext')
- ->willReturn($registrationContext);
- }
-
- public function testGetProvidersWithNoRegisteredProviders(): void {
- $this->setupEmptyRegistrationContext();
-
- $providers = $this->searchComposer->getProviders('/test/route', []);
-
- $this->assertIsArray($providers);
- $this->assertEmpty($providers);
- }
-
- public function testSearchWithUnknownProvider(): void {
- $this->setupEmptyRegistrationContext();
-
- $user = $this->createMock(IUser::class);
- $query = $this->createMock(ISearchQuery::class);
-
- $this->expectException(InvalidArgumentException::class);
- $this->expectExceptionMessage('Provider unknown_provider is unknown');
-
- $this->searchComposer->search($user, 'unknown_provider', $query);
- }
-
- public function testGetProvidersWithMultipleProviders(): void {
- $providerConfigs = [
- 'provider1' => ['service' => 'provider1_service', 'appId' => 'app1', 'order' => 10],
- 'provider2' => ['service' => 'provider2_service', 'appId' => 'app2', 'order' => 5],
- 'provider3' => ['service' => 'provider3_service', 'appId' => 'app3', 'order' => 15, 'isInApp' => true],
- ];
-
- $mockData = $this->createMockProvidersAndRegistrations($providerConfigs);
- $this->setupRegistrationContextWithProviders($mockData['registrations']);
- $this->setupAppConfigForAllowedProviders();
-
- $providers = $this->searchComposer->getProviders('/test/route', []);
-
- $this->assertProvidersStructureAndSorting($providers, [
- ['id' => 'provider2', 'name' => 'Provider provider2', 'appId' => 'app2', 'order' => 5, 'inAppSearch' => false],
- ['id' => 'provider1', 'name' => 'Provider provider1', 'appId' => 'app1', 'order' => 10, 'inAppSearch' => false],
- ['id' => 'provider3', 'name' => 'Provider provider3', 'appId' => 'app3', 'order' => 15, 'inAppSearch' => true],
- ]);
- }
-
- public function testGetProvidersWithEmptyAllowedProvidersConfiguration(): void {
- $providerConfigs = [
- 'provider1' => ['service' => 'provider1_service', 'appId' => 'app1', 'order' => 10],
- 'provider2' => ['service' => 'provider2_service', 'appId' => 'app2', 'order' => 5],
- ];
-
- $mockData = $this->createMockProvidersAndRegistrations($providerConfigs);
- $this->setupRegistrationContextWithProviders($mockData['registrations']);
- $this->setupAppConfigForAllowedProviders();
-
- $providers = $this->searchComposer->getProviders('/test/route', []);
-
- $this->assertCount(2, $providers);
- $this->assertProvidersAreSortedByOrder($providers);
- $this->assertEquals('provider2', $providers[0]['id']);
- $this->assertEquals('provider1', $providers[1]['id']);
- }
-
- public function testGetProvidersWithAllowedProvidersRestriction(): void {
- $providerConfigs = [
- 'provider1' => ['service' => 'provider1_service', 'appId' => 'app1', 'order' => 10],
- 'provider2' => ['service' => 'provider2_service', 'appId' => 'app2', 'order' => 5],
- 'provider3' => ['service' => 'provider3_service', 'appId' => 'app3', 'order' => 15],
- 'provider4' => ['service' => 'provider4_service', 'appId' => 'app4', 'order' => 8],
- ];
-
- $mockData = $this->createMockProvidersAndRegistrations($providerConfigs);
- $this->setupRegistrationContextWithProviders($mockData['registrations']);
- $this->setupAppConfigForAllowedProviders(['provider1', 'provider3']);
-
- $providers = $this->searchComposer->getProviders('/test/route', []);
-
- $this->assertProvidersStructureAndSorting($providers, [
- ['id' => 'provider1', 'name' => 'Provider provider1', 'appId' => 'app1', 'order' => 10, 'inAppSearch' => false],
- ['id' => 'provider3', 'name' => 'Provider provider3', 'appId' => 'app3', 'order' => 15, 'inAppSearch' => false],
- ]);
-
- // Verify excluded providers are not present
- $providerIds = array_column($providers, 'id');
- $this->assertNotContains('provider2', $providerIds);
- $this->assertNotContains('provider4', $providerIds);
- }
-
- public function testGetProvidersFiltersByAllowedProvidersCompletely(): void {
- $providerConfigs = [
- 'provider1' => ['service' => 'provider1_service', 'appId' => 'app1', 'order' => 10],
- 'provider2' => ['service' => 'provider2_service', 'appId' => 'app2', 'order' => 5],
- ];
-
- $mockData = $this->createMockProvidersAndRegistrations($providerConfigs);
- $this->setupRegistrationContextWithProviders($mockData['registrations']);
- $this->setupAppConfigForAllowedProviders(['provider_not_exists']);
-
- $providers = $this->searchComposer->getProviders('/test/route', []);
-
- $this->assertIsArray($providers);
- $this->assertEmpty($providers);
- }
-
- public function testGetProvidersWithMixedOrderValues(): void {
- $providerConfigs = [
- 'provider1' => ['service' => 'provider1_service', 'appId' => 'app1', 'order' => 100],
- 'provider2' => ['service' => 'provider2_service', 'appId' => 'app2', 'order' => 1],
- 'provider3' => ['service' => 'provider3_service', 'appId' => 'app3', 'order' => 50],
- ];
-
- $mockData = $this->createMockProvidersAndRegistrations($providerConfigs);
- $this->setupRegistrationContextWithProviders($mockData['registrations']);
- $this->setupAppConfigForAllowedProviders();
-
- $providers = $this->searchComposer->getProviders('/test/route', []);
-
- $this->assertCount(3, $providers);
- $this->assertProvidersAreSortedByOrder($providers);
-
- // Verify correct ordering: provider2 (1), provider3 (50), provider1 (100)
- $this->assertEquals('provider2', $providers[0]['id']);
- $this->assertEquals('provider3', $providers[1]['id']);
- $this->assertEquals('provider1', $providers[2]['id']);
- }
-
- public function testProviderIconGeneration(): void {
- $providerConfigs = [
- 'provider1' => ['service' => 'provider1_service', 'appId' => 'app1', 'order' => 10],
- ];
-
- $mockData = $this->createMockProvidersAndRegistrations($providerConfigs);
- $this->setupRegistrationContextWithProviders($mockData['registrations']);
- $this->setupAppConfigForAllowedProviders();
-
- $providers = $this->searchComposer->getProviders('/test/route', []);
-
- $this->assertCount(1, $providers);
- $this->assertArrayHasKey('icon', $providers[0]);
- $this->assertStringContainsString('/apps/provider1/img/provider1.svg', $providers[0]['icon']);
- }
-
- /**
- * Assert providers array structure and expected sorting
- */
- private function assertProvidersStructureAndSorting(array $actualProviders, array $expectedProviders): void {
- $this->assertIsArray($actualProviders);
- $this->assertCount(count($expectedProviders), $actualProviders);
-
- foreach ($actualProviders as $index => $provider) {
- $this->assertProviderHasRequiredFields($provider);
-
- $expected = $expectedProviders[$index];
- $this->assertEquals($expected['id'], $provider['id']);
- $this->assertEquals($expected['name'], $provider['name']);
- $this->assertEquals($expected['appId'], $provider['appId']);
- $this->assertEquals($expected['order'], $provider['order']);
- $this->assertEquals($expected['inAppSearch'], $provider['inAppSearch']);
- }
-
- $this->assertProvidersAreSortedByOrder($actualProviders);
- }
-
- private function assertProviderHasRequiredFields(array $provider): void {
- $requiredFields = ['id', 'appId', 'name', 'icon', 'order', 'triggers', 'filters', 'inAppSearch'];
- foreach ($requiredFields as $field) {
- $this->assertArrayHasKey($field, $provider, "Provider must have '$field' field");
- }
- }
-
- private function assertProvidersAreSortedByOrder(array $providers): void {
- $orders = array_column($providers, 'order');
- $sortedOrders = $orders;
- sort($sortedOrders);
- $this->assertEquals($sortedOrders, $orders, 'Providers should be sorted by order');
- }
-}
--- a/tests/lib/Security/Bruteforce/Backend/MemoryCacheBackendTest.php
+++ /dev/null
@@ -1,138 +0,0 @@
-cacheFactory = $this->createMock(ICacheFactory::class);
- $this->timeFactory = $this->createMock(ITimeFactory::class);
- $this->cache = $this->createMock(ICache::class);
-
- $this->cacheFactory
- ->expects($this->once())
- ->method('createDistributed')
- ->with(MemoryCacheBackend::class)
- ->willReturn($this->cache);
-
- $this->backend = new MemoryCacheBackend(
- $this->cacheFactory,
- $this->timeFactory
- );
- }
-
- public function testGetAttemptsWithNoAttemptsBefore(): void {
- $this->cache
- ->expects($this->once())
- ->method('get')
- ->with('8b9da631d1f7b022bb2c3c489e16092f82b42fd4')
- ->willReturn(null);
-
- $this->assertSame(0, $this->backend->getAttempts('10.10.10.10/32', 0));
- }
-
- public static function dataGetAttempts(): array {
- return [
- [0, null, null, 4],
- [100, null, null, 2],
- [0, 'action1', null, 2],
- [100, 'action1', null, 1],
- [0, 'action1', ['metadata2'], 1],
- [100, 'action1', ['metadata2'], 1],
- [100, 'action1', ['metadata1'], 0],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetAttempts')]
- public function testGetAttempts(int $maxAge, ?string $action, ?array $metadata, int $expected): void {
- $this->cache
- ->expects($this->once())
- ->method('get')
- ->with('8b9da631d1f7b022bb2c3c489e16092f82b42fd4')
- ->willReturn(json_encode([
- '1' . '#' . hash('sha1', 'action1') . '#' . hash('sha1', json_encode(['metadata1'])),
- '300' . '#' . hash('sha1', 'action1') . '#' . hash('sha1', json_encode(['metadata2'])),
- '1' . '#' . hash('sha1', 'action2') . '#' . hash('sha1', json_encode(['metadata1'])),
- '300' . '#' . hash('sha1', 'action2') . '#' . hash('sha1', json_encode(['metadata2'])),
- ]));
-
- $this->assertSame($expected, $this->backend->getAttempts('10.10.10.10/32', $maxAge, $action, $metadata));
- }
-
- public function testRegisterAttemptWithNoAttemptsBefore(): void {
- $this->cache
- ->expects($this->once())
- ->method('get')
- ->with('8b9da631d1f7b022bb2c3c489e16092f82b42fd4')
- ->willReturn(null);
- $this->cache
- ->expects($this->once())
- ->method('set')
- ->with(
- '8b9da631d1f7b022bb2c3c489e16092f82b42fd4',
- json_encode(['223#' . hash('sha1', 'action1') . '#' . hash('sha1', json_encode(['metadata1']))])
- );
-
- $this->backend->registerAttempt('10.10.10.10', '10.10.10.10/32', 223, 'action1', ['metadata1']);
- }
-
- public function testRegisterAttempt(): void {
- $this->timeFactory
- ->expects($this->once())
- ->method('getTime')
- ->willReturn(12 * 3600 + 86);
-
- $this->cache
- ->expects($this->once())
- ->method('get')
- ->with('8b9da631d1f7b022bb2c3c489e16092f82b42fd4')
- ->willReturn(json_encode([
- '1#' . hash('sha1', 'action1') . '#' . hash('sha1', json_encode(['metadata1'])),
- '2#' . hash('sha1', 'action2') . '#' . hash('sha1', json_encode(['metadata1'])),
- '87#' . hash('sha1', 'action3') . '#' . hash('sha1', json_encode(['metadata1'])),
- '123#' . hash('sha1', 'action4') . '#' . hash('sha1', json_encode(['metadata1'])),
- '123#' . hash('sha1', 'action5') . '#' . hash('sha1', json_encode(['metadata1'])),
- '124#' . hash('sha1', 'action6') . '#' . hash('sha1', json_encode(['metadata1'])),
- ]));
- $this->cache
- ->expects($this->once())
- ->method('set')
- ->with(
- '8b9da631d1f7b022bb2c3c489e16092f82b42fd4',
- json_encode([
- '87#' . hash('sha1', 'action3') . '#' . hash('sha1', json_encode(['metadata1'])),
- '123#' . hash('sha1', 'action4') . '#' . hash('sha1', json_encode(['metadata1'])),
- '123#' . hash('sha1', 'action5') . '#' . hash('sha1', json_encode(['metadata1'])),
- '124#' . hash('sha1', 'action6') . '#' . hash('sha1', json_encode(['metadata1'])),
- '186#' . hash('sha1', 'action7') . '#' . hash('sha1', json_encode(['metadata2'])),
- ])
- );
-
- $this->backend->registerAttempt('10.10.10.10', '10.10.10.10/32', 186, 'action7', ['metadata2']);
- }
-}
--- a/tests/lib/Security/Bruteforce/CapabilitiesTest.php
+++ /dev/null
@@ -1,85 +0,0 @@
-request = $this->createMock(IRequest::class);
-
- $this->throttler = $this->createMock(IThrottler::class);
-
- $this->capabilities = new Capabilities(
- $this->request,
- $this->throttler
- );
- }
-
- public function testGetCapabilities(): void {
- $this->throttler->expects($this->atLeastOnce())
- ->method('getDelay')
- ->with('10.10.10.10')
- ->willReturn(42);
-
- $this->throttler->expects($this->atLeastOnce())
- ->method('isBypassListed')
- ->with('10.10.10.10')
- ->willReturn(true);
-
- $this->request->method('getRemoteAddress')
- ->willReturn('10.10.10.10');
-
- $expected = [
- 'bruteforce' => [
- 'delay' => 42,
- 'allow-listed' => true,
- ]
- ];
- $result = $this->capabilities->getCapabilities();
-
- $this->assertEquals($expected, $result);
- }
-
- public function testGetCapabilitiesOnCli(): void {
- $this->throttler->expects($this->atLeastOnce())
- ->method('getDelay')
- ->with('')
- ->willReturn(0);
-
- $this->request->method('getRemoteAddress')
- ->willReturn('');
-
- $expected = [
- 'bruteforce' => [
- 'delay' => 0,
- 'allow-listed' => false,
- ]
- ];
- $result = $this->capabilities->getCapabilities();
-
- $this->assertEquals($expected, $result);
- }
-}
--- a/tests/lib/Security/CSP/AddContentSecurityPolicyEventTest.php
+++ /dev/null
@@ -1,29 +0,0 @@
-createMock(ContentSecurityPolicyManager::class);
- $policy = $this->createMock(ContentSecurityPolicy::class);
- $event = new AddContentSecurityPolicyEvent($cspManager);
-
- $cspManager->expects($this->once())
- ->method('addDefaultPolicy')
- ->with($policy);
-
- $event->addPolicy($policy);
- }
-}
--- a/tests/lib/Security/CSP/ContentSecurityPolicyManagerTest.php
+++ /dev/null
@@ -1,104 +0,0 @@
-dispatcher = Server::get(IEventDispatcher::class);
- $this->contentSecurityPolicyManager = new ContentSecurityPolicyManager($this->dispatcher);
- }
-
- public function testAddDefaultPolicy(): void {
- $this->contentSecurityPolicyManager->addDefaultPolicy(new ContentSecurityPolicy());
- $this->addToAssertionCount(1);
- }
-
- public function testGetDefaultPolicyWithPolicies(): void {
- $policy = new ContentSecurityPolicy();
- $policy->addAllowedFontDomain('mydomain.com');
- $policy->addAllowedImageDomain('anotherdomain.de');
- $this->contentSecurityPolicyManager->addDefaultPolicy($policy);
- $policy = new ContentSecurityPolicy();
- $policy->addAllowedFontDomain('example.com');
- $policy->addAllowedImageDomain('example.org');
- $this->contentSecurityPolicyManager->addDefaultPolicy($policy);
- $policy = new EmptyContentSecurityPolicy();
- $policy->addAllowedFontDomain('anotherFontDomain');
- $policy->addAllowedFormActionDomain('thirdDomain');
- $this->contentSecurityPolicyManager->addDefaultPolicy($policy);
-
- $expected = new \OC\Security\CSP\ContentSecurityPolicy();
- $expected->addAllowedFontDomain('mydomain.com');
- $expected->addAllowedFontDomain('example.com');
- $expected->addAllowedFontDomain('anotherFontDomain');
- $expected->addAllowedFormActionDomain('thirdDomain');
- $expected->addAllowedImageDomain('anotherdomain.de');
- $expected->addAllowedImageDomain('example.org');
- $expectedStringPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob: anotherdomain.de example.org;font-src 'self' data: mydomain.com example.com anotherFontDomain;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self' thirdDomain";
-
- $this->assertEquals($expected, $this->contentSecurityPolicyManager->getDefaultPolicy());
- $this->assertSame($expectedStringPolicy, $this->contentSecurityPolicyManager->getDefaultPolicy()->buildPolicy());
- }
-
- public function testGetDefaultPolicyWithPoliciesViaEvent(): void {
- $this->dispatcher->addListener(AddContentSecurityPolicyEvent::class, function (AddContentSecurityPolicyEvent $e): void {
- $policy = new ContentSecurityPolicy();
- $policy->addAllowedFontDomain('mydomain.com');
- $policy->addAllowedImageDomain('anotherdomain.de');
- $policy->useStrictDynamic(true);
-
- $e->addPolicy($policy);
- });
-
- $this->dispatcher->addListener(AddContentSecurityPolicyEvent::class, function (AddContentSecurityPolicyEvent $e): void {
- $policy = new ContentSecurityPolicy();
- $policy->addAllowedFontDomain('example.com');
- $policy->addAllowedImageDomain('example.org');
- $e->addPolicy($policy);
- });
-
- $this->dispatcher->addListener(AddContentSecurityPolicyEvent::class, function (AddContentSecurityPolicyEvent $e): void {
- $policy = new EmptyContentSecurityPolicy();
- $policy->addAllowedFontDomain('anotherFontDomain');
- $policy->addAllowedFormActionDomain('thirdDomain');
- $e->addPolicy($policy);
- });
-
- $expected = new \OC\Security\CSP\ContentSecurityPolicy();
- $expected->addAllowedFontDomain('mydomain.com');
- $expected->addAllowedFontDomain('example.com');
- $expected->addAllowedFontDomain('anotherFontDomain');
- $expected->addAllowedImageDomain('anotherdomain.de');
- $expected->addAllowedImageDomain('example.org');
- $expected->addAllowedFormActionDomain('thirdDomain');
- $expected->useStrictDynamic(true);
- $expectedStringPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob: anotherdomain.de example.org;font-src 'self' data: mydomain.com example.com anotherFontDomain;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self' thirdDomain";
-
- $this->assertEquals($expected, $this->contentSecurityPolicyManager->getDefaultPolicy());
- $this->assertSame($expectedStringPolicy, $this->contentSecurityPolicyManager->getDefaultPolicy()->buildPolicy());
- }
-}
--- a/tests/lib/Security/CSP/ContentSecurityPolicyNonceManagerTest.php
+++ /dev/null
@@ -1,74 +0,0 @@
-CSRFTokenManager = $this->createMock(CsrfTokenManager::class);
- $this->request = $this->createMock(Request::class);
- $this->nonceManager = new ContentSecurityPolicyNonceManager(
- $this->CSRFTokenManager,
- $this->request
- );
- }
-
- public function testGetNonce(): void {
- $secret = base64_encode('secret');
- $tokenValue = base64_encode('secret' ^ 'value_') . ':' . $secret;
- $token = $this->createMock(CsrfToken::class);
- $token
- ->expects($this->once())
- ->method('getEncryptedValue')
- ->willReturn($tokenValue);
-
- $this->CSRFTokenManager
- ->expects($this->once())
- ->method('getToken')
- ->willReturn($token);
-
- $this->assertSame($secret, $this->nonceManager->getNonce());
- // call it twice but `getEncryptedValue` is expected to be called only once
- $this->assertSame($secret, $this->nonceManager->getNonce());
- }
-
- public function testGetNonceServerVar(): void {
- $token = 'SERVERNONCE';
- $this->request
- ->method('__isset')
- ->with('server')
- ->willReturn(true);
-
- $this->request
- ->method('__get')
- ->with('server')
- ->willReturn(['CSP_NONCE' => $token]);
-
- $this->assertSame($token, $this->nonceManager->getNonce());
- // call it twice but `CSP_NONCE` variable is expected to be loaded only once
- $this->assertSame($token, $this->nonceManager->getNonce());
- }
-}
--- a/tests/lib/Security/CSRF/CsrfTokenGeneratorTest.php
+++ /dev/null
@@ -1,47 +0,0 @@
-random = $this->getMockBuilder('\OCP\Security\ISecureRandom')
- ->disableOriginalConstructor()->getMock();
- $this->csrfTokenGenerator = new CsrfTokenGenerator($this->random);
- }
-
- public function testGenerateTokenWithCustomNumber(): void {
- $this->random
- ->expects($this->once())
- ->method('generate')
- ->with(3)
- ->willReturn('abc');
- $this->assertSame('abc', $this->csrfTokenGenerator->generateToken(3));
- }
-
- public function testGenerateTokenWithDefault(): void {
- $this->random
- ->expects($this->once())
- ->method('generate')
- ->with(32)
- ->willReturn('12345678901234567890123456789012');
- $this->assertSame('12345678901234567890123456789012', $this->csrfTokenGenerator->generateToken(32));
- }
-}
--- a/tests/lib/Security/CSRF/CsrfTokenManagerTest.php
+++ /dev/null
@@ -1,151 +0,0 @@
-tokenGenerator = $this->getMockBuilder('\OC\Security\CSRF\CsrfTokenGenerator')
- ->disableOriginalConstructor()->getMock();
- $this->storageInterface = $this->getMockBuilder('\OC\Security\CSRF\TokenStorage\SessionStorage')
- ->disableOriginalConstructor()->getMock();
-
- $this->csrfTokenManager = new CsrfTokenManager(
- $this->tokenGenerator,
- $this->storageInterface
- );
- }
-
- public function testGetTokenWithExistingToken(): void {
- $this->storageInterface
- ->expects($this->once())
- ->method('hasToken')
- ->willReturn(true);
- $this->storageInterface
- ->expects($this->once())
- ->method('getToken')
- ->willReturn('MyExistingToken');
-
- $expected = new CsrfToken('MyExistingToken');
- $this->assertEquals($expected, $this->csrfTokenManager->getToken());
- }
-
- public function testGetTokenWithExistingTokenKeepsOnSecondRequest(): void {
- $this->storageInterface
- ->expects($this->once())
- ->method('hasToken')
- ->willReturn(true);
- $this->storageInterface
- ->expects($this->once())
- ->method('getToken')
- ->willReturn('MyExistingToken');
-
- $expected = new CsrfToken('MyExistingToken');
- $token = $this->csrfTokenManager->getToken();
- $this->assertSame($token, $this->csrfTokenManager->getToken());
- $this->assertSame($token, $this->csrfTokenManager->getToken());
- }
-
- public function testGetTokenWithoutExistingToken(): void {
- $this->storageInterface
- ->expects($this->once())
- ->method('hasToken')
- ->willReturn(false);
- $this->tokenGenerator
- ->expects($this->once())
- ->method('generateToken')
- ->willReturn('MyNewToken');
- $this->storageInterface
- ->expects($this->once())
- ->method('setToken')
- ->with('MyNewToken');
-
- $expected = new CsrfToken('MyNewToken');
- $this->assertEquals($expected, $this->csrfTokenManager->getToken());
- }
-
- public function testRefreshToken(): void {
- $this->tokenGenerator
- ->expects($this->once())
- ->method('generateToken')
- ->willReturn('MyNewToken');
- $this->storageInterface
- ->expects($this->once())
- ->method('setToken')
- ->with('MyNewToken');
-
- $expected = new CsrfToken('MyNewToken');
- $this->assertEquals($expected, $this->csrfTokenManager->refreshToken());
- }
-
- public function testRemoveToken(): void {
- $this->storageInterface
- ->expects($this->once())
- ->method('removeToken');
-
- $this->csrfTokenManager->removeToken();
- }
-
- public function testIsTokenValidWithoutToken(): void {
- $this->storageInterface
- ->expects($this->once())
- ->method('hasToken')
- ->willReturn(false);
- $token = new CsrfToken('Token');
-
- $this->assertSame(false, $this->csrfTokenManager->isTokenValid($token));
- }
-
- public function testIsTokenValidWithWrongToken(): void {
- $this->storageInterface
- ->expects($this->once())
- ->method('hasToken')
- ->willReturn(true);
- $token = new CsrfToken('Token');
- $this->storageInterface
- ->expects($this->once())
- ->method('getToken')
- ->willReturn('MyToken');
-
- $this->assertSame(false, $this->csrfTokenManager->isTokenValid($token));
- }
-
- public function testIsTokenValidWithValidToken(): void {
- $a = 'abc';
- $b = 'def';
- $xorB64 = 'BQcF';
- $tokenVal = sprintf('%s:%s', $xorB64, base64_encode($a));
- $this->storageInterface
- ->expects($this->once())
- ->method('hasToken')
- ->willReturn(true);
- $token = new CsrfToken($tokenVal);
- $this->storageInterface
- ->expects($this->once())
- ->method('getToken')
- ->willReturn($b);
-
- $this->assertSame(true, $this->csrfTokenManager->isTokenValid($token));
- }
-}
--- a/tests/lib/Security/CSRF/CsrfTokenTest.php
+++ /dev/null
@@ -1,37 +0,0 @@
-assertSame(33, strlen($csrfToken->getEncryptedValue()));
- $this->assertSame(':', $csrfToken->getEncryptedValue()[16]);
- }
-
- public function testGetEncryptedValueStaysSameOnSecondRequest(): void {
- $csrfToken = new CsrfToken('MyCsrfToken');
- $tokenValue = $csrfToken->getEncryptedValue();
- $this->assertSame($tokenValue, $csrfToken->getEncryptedValue());
- $this->assertSame($tokenValue, $csrfToken->getEncryptedValue());
- }
-
- public function testGetDecryptedValue(): void {
- $a = 'abc';
- $b = 'def';
- $xorB64 = 'BQcF';
- $tokenVal = sprintf('%s:%s', $xorB64, base64_encode($a));
- $csrfToken = new CsrfToken($tokenVal);
- $this->assertSame($b, $csrfToken->getDecryptedValue());
- }
-}
--- a/tests/lib/Security/CSRF/TokenStorage/SessionStorageTest.php
+++ /dev/null
@@ -1,114 +0,0 @@
-session = $this->getMockBuilder(ISession::class)
- ->disableOriginalConstructor()->getMock();
- $this->sessionStorage = new SessionStorage($this->session);
- }
-
- /**
- * @return array
- */
- public static function getTokenDataProvider(): array {
- return [
- [
- '',
- ],
- [
- null,
- ],
- ];
- }
-
- /**
- * @param string $token
- *
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('getTokenDataProvider')]
- public function testGetTokenWithEmptyToken($token): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Session does not contain a requesttoken');
-
- $this->session
- ->expects($this->once())
- ->method('get')
- ->with('requesttoken')
- ->willReturn($token);
- $this->sessionStorage->getToken();
- }
-
- public function testGetTokenWithValidToken(): void {
- $this->session
- ->expects($this->once())
- ->method('get')
- ->with('requesttoken')
- ->willReturn('MyFancyCsrfToken');
- $this->assertSame('MyFancyCsrfToken', $this->sessionStorage->getToken());
- }
-
- public function testSetToken(): void {
- $this->session
- ->expects($this->once())
- ->method('set')
- ->with('requesttoken', 'TokenToSet');
- $this->sessionStorage->setToken('TokenToSet');
- }
-
- public function testRemoveToken(): void {
- $this->session
- ->expects($this->once())
- ->method('remove')
- ->with('requesttoken');
- $this->sessionStorage->removeToken();
- }
-
- public function testHasTokenWithExistingToken(): void {
- $this->session
- ->expects($this->once())
- ->method('exists')
- ->with('requesttoken')
- ->willReturn(true);
- $this->assertSame(true, $this->sessionStorage->hasToken());
- }
-
- public function testHasTokenWithoutExistingToken(): void {
- $this->session
- ->expects($this->once())
- ->method('exists')
- ->with('requesttoken')
- ->willReturn(false);
- $this->assertSame(false, $this->sessionStorage->hasToken());
- }
-
- public function testSetSession(): void {
- $session = $this->createMock(ISession::class);
- $session
- ->expects($this->once())
- ->method('get')
- ->with('requesttoken')
- ->willReturn('MyToken');
- $this->sessionStorage->setSession($session);
- $this->assertSame('MyToken', $this->sessionStorage->getToken());
- }
-}
--- a/tests/lib/Security/CertificateManagerTest.php
+++ /dev/null
@@ -1,207 +0,0 @@
-username = $this->getUniqueID('', 20);
- $this->createUser($this->username, '');
-
- $storage = new Temporary();
- $this->registerMount($this->username, $storage, '/' . $this->username . '/');
-
- \OC_Util::tearDownFS();
- \OC_User::setUserId($this->username);
- Filesystem::tearDown();
- \OC_Util::setupFS($this->username);
-
- $config = $this->createMock(IConfig::class);
- $config->expects($this->any())->method('getSystemValueBool')
- ->with('installed', false)->willReturn(true);
- $config
- ->expects($this->any())
- ->method('getSystemValueString')
- ->with('default_certificates_bundle_path', \OC::$SERVERROOT . '/resources/config/ca-bundle.crt')
- ->willReturn(\OC::$SERVERROOT . '/resources/config/ca-bundle.crt');
-
- $this->random = $this->createMock(ISecureRandom::class);
- $this->random->method('generate')
- ->willReturn('random');
-
- $this->certificateManager = new CertificateManager(
- new View(),
- $config,
- $this->createMock(LoggerInterface::class),
- $this->random
- );
- }
-
- #[\Override]
- protected function tearDown(): void {
- $user = Server::get(IUserManager::class)->get($this->username);
- if ($user !== null) {
- $user->delete();
- }
- parent::tearDown();
- }
-
- protected function assertEqualsArrays($expected, $actual) {
- sort($expected);
- sort($actual);
-
- $this->assertEquals($expected, $actual);
- }
-
- public function testListCertificates(): void {
- // Test empty certificate bundle
- $this->assertSame([], $this->certificateManager->listCertificates());
-
- // Add some certificates
- $this->certificateManager->addCertificate(file_get_contents(__DIR__ . '/../../data/certificates/goodCertificate.crt'), 'GoodCertificate');
- $certificateStore = [];
- $certificateStore[] = new Certificate(file_get_contents(__DIR__ . '/../../data/certificates/goodCertificate.crt'), 'GoodCertificate');
- $this->assertEqualsArrays($certificateStore, $this->certificateManager->listCertificates());
-
- // Add another certificates
- $this->certificateManager->addCertificate(file_get_contents(__DIR__ . '/../../data/certificates/expiredCertificate.crt'), 'ExpiredCertificate');
- $certificateStore[] = new Certificate(file_get_contents(__DIR__ . '/../../data/certificates/expiredCertificate.crt'), 'ExpiredCertificate');
- $this->assertEqualsArrays($certificateStore, $this->certificateManager->listCertificates());
- }
-
- public function testAddInvalidCertificate(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Certificate could not get parsed.');
-
- $this->certificateManager->addCertificate('InvalidCertificate', 'invalidCertificate');
- }
-
- public static function dangerousFileProvider(): array {
- return [
- ['.htaccess'],
- ['../../foo.txt'],
- ['..\..\foo.txt'],
- ];
- }
-
- /**
- * @param string $filename
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dangerousFileProvider')]
- public function testAddDangerousFile($filename): void {
- $this->expectException(InvalidPathException::class);
- $this->certificateManager->addCertificate(file_get_contents(__DIR__ . '/../../data/certificates/expiredCertificate.crt'), $filename);
- }
-
- public function testRemoveDangerousFile(): void {
- $this->assertFalse($this->certificateManager->removeCertificate('../../foo.txt'));
- }
-
- public function testRemoveExistingFile(): void {
- $this->certificateManager->addCertificate(file_get_contents(__DIR__ . '/../../data/certificates/goodCertificate.crt'), 'GoodCertificate');
- $this->assertTrue($this->certificateManager->removeCertificate('GoodCertificate'));
- }
-
- public function testGetCertificateBundle(): void {
- $this->assertSame('/files_external/rootcerts.crt', $this->certificateManager->getCertificateBundle());
- }
-
- /**
- *
- * @param int $CaBundleMtime
- * @param int $targetBundleMtime
- * @param int $targetBundleExists
- * @param bool $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestNeedRebundling')]
- public function testNeedRebundling($CaBundleMtime,
- $targetBundleMtime,
- $targetBundleExists,
- $expected,
- ): void {
- $view = $this->getMockBuilder(View::class)
- ->disableOriginalConstructor()->getMock();
- $config = $this->createMock(IConfig::class);
-
- /** @var CertificateManager | \PHPUnit\Framework\MockObject\MockObject $certificateManager */
- $certificateManager = $this->getMockBuilder('OC\Security\CertificateManager')
- ->setConstructorArgs([$view, $config, $this->createMock(LoggerInterface::class), $this->random])
- ->onlyMethods(['getFilemtimeOfCaBundle', 'getCertificateBundle'])
- ->getMock();
-
- $certificateManager->expects($this->any())->method('getFilemtimeOfCaBundle')
- ->willReturn($CaBundleMtime);
-
- $certificateManager->expects($this->once())->method('getCertificateBundle')
- ->willReturn('targetBundlePath');
-
- $view->expects($this->any())->method('file_exists')
- ->with('targetBundlePath')
- ->willReturn($targetBundleExists);
-
- $view->expects($this->any())->method('filemtime')
- ->willReturnCallback(function ($path) use ($targetBundleMtime) {
- if ($path === 'targetBundlePath') {
- return $targetBundleMtime;
- }
- throw new \Exception('unexpected path');
- });
-
- $this->assertSame($expected,
- $this->invokePrivate($certificateManager, 'needsRebundling')
- );
- }
-
- public static function dataTestNeedRebundling(): array {
- return [
- //values: CaBundleMtime, targetBundleMtime, targetBundleExists, expected
-
- [10, 30, true, false],
- [10, 15, true, false],
- [10, 8, true, true],
- [10, 30, true, false],
- [10, 8, true, true],
-
- // if no target bundle exists we always build a new one
- [10, 30, false, true],
- [10, 15, false, true],
- [10, 8, false, true],
- [10, 30, false, true],
- [10, 8, false, true],
- ];
- }
-}
--- a/tests/lib/Security/CertificateTest.php
+++ /dev/null
@@ -1,104 +0,0 @@
-goodCertificate = new Certificate($goodCertificate, 'GoodCertificate');
- $badCertificate = file_get_contents(__DIR__ . '/../../data/certificates/badCertificate.crt');
- $this->invalidCertificate = new Certificate($badCertificate, 'BadCertificate');
- $expiredCertificate = file_get_contents(__DIR__ . '/../../data/certificates/expiredCertificate.crt');
- $this->expiredCertificate = new Certificate($expiredCertificate, 'ExpiredCertificate');
- }
-
- public function testBogusData(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Certificate could not get parsed.');
-
- $certificate = new Certificate('foo', 'bar');
- $certificate->getIssueDate();
- }
-
- public function testOpenSslTrustedCertificateFormat(): void {
- $trustedCertificate = file_get_contents(__DIR__ . '/../../data/certificates/openSslTrustedCertificate.crt');
- $certificate = new Certificate($trustedCertificate, 'TrustedCertificate');
- $this->assertSame('thawte, Inc.', $certificate->getOrganization());
- }
-
- public function testCertificateStartingWithFileReference(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Certificate could not get parsed.');
-
- new Certificate('file://' . __DIR__ . '/../../data/certificates/goodCertificate.crt', 'bar');
- }
-
- public function testGetName(): void {
- $this->assertSame('GoodCertificate', $this->goodCertificate->getName());
- $this->assertSame('BadCertificate', $this->invalidCertificate->getName());
- }
-
- public function testGetCommonName(): void {
- $this->assertSame('security.nextcloud.com', $this->goodCertificate->getCommonName());
- $this->assertSame(null, $this->invalidCertificate->getCommonName());
- }
-
- public function testGetOrganization(): void {
- $this->assertSame('Nextcloud Security', $this->goodCertificate->getOrganization());
- $this->assertSame('Internet Widgits Pty Ltd', $this->invalidCertificate->getOrganization());
- }
-
- public function testGetIssueDate(): void {
- $expected = new \DateTime('2025-08-25 09:53:14 GMT');
- $this->assertEquals($expected->getTimestamp(), $this->goodCertificate->getIssueDate()->getTimestamp());
- $expected = new \DateTime('2025-08-25 10:00:15 GMT');
- $this->assertEquals($expected->getTimestamp(), $this->invalidCertificate->getIssueDate()->getTimestamp());
- }
-
- public function testGetExpireDate(): void {
- $expected = new \DateTime('2035-08-23 09:53:14 GMT');
- $this->assertEquals($expected->getTimestamp(), $this->goodCertificate->getExpireDate()->getTimestamp());
- $expected = new \DateTime('2035-08-23 10:00:15 GMT');
- $this->assertEquals($expected->getTimestamp(), $this->invalidCertificate->getExpireDate()->getTimestamp());
- $expected = new \DateTime('2014-08-28 09:12:43 GMT');
- $this->assertEquals($expected->getTimestamp(), $this->expiredCertificate->getExpireDate()->getTimestamp());
- }
-
- public function testIsExpired(): void {
- $this->assertSame(false, $this->goodCertificate->isExpired());
- $this->assertSame(false, $this->invalidCertificate->isExpired());
- $this->assertSame(true, $this->expiredCertificate->isExpired());
- }
-
- public function testGetIssuerName(): void {
- $this->assertSame('security.nextcloud.com', $this->goodCertificate->getIssuerName());
- $this->assertSame(null, $this->invalidCertificate->getIssuerName());
- $this->assertSame(null, $this->expiredCertificate->getIssuerName());
- }
-
- public function testGetIssuerOrganization(): void {
- $this->assertSame('Nextcloud Security', $this->goodCertificate->getIssuerOrganization());
- $this->assertSame('Internet Widgits Pty Ltd', $this->invalidCertificate->getIssuerOrganization());
- $this->assertSame('Internet Widgits Pty Ltd', $this->expiredCertificate->getIssuerOrganization());
- }
-}
--- a/tests/lib/Security/CredentialsManagerTest.php
+++ /dev/null
@@ -1,59 +0,0 @@
-store($userId, $identifier, $secrets);
- $received = $credentialsManager->retrieve($userId, $identifier);
-
- $this->assertSame($secrets, $received);
-
- $removedRows = $credentialsManager->delete($userId, $identifier);
- $this->assertSame(1, $removedRows);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('credentialsProvider')]
- public function testUpdate($userId, $identifier): void {
- $credentialsManager = Server::get(ICredentialsManager::class);
-
- $secrets = 'Open Sesame';
- $secretsRev = strrev($secrets);
-
- $credentialsManager->store($userId, $identifier, $secrets);
- $credentialsManager->store($userId, $identifier, $secretsRev);
- $received = $credentialsManager->retrieve($userId, $identifier);
-
- $this->assertSame($secretsRev, $received);
- }
-
- public static function credentialsProvider(): array {
- return [
- [
- 'alice',
- 'privateCredentials'
- ],
- [
- '',
- 'systemCredentials',
- ],
- ];
- }
-}
--- a/tests/lib/Security/CryptoTest.php
+++ /dev/null
@@ -1,132 +0,0 @@
-crypto = new Crypto(Server::get(IConfig::class));
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('defaultEncryptionProvider')]
- public function testDefaultEncrypt($stringToEncrypt): void {
- $ciphertext = $this->crypto->encrypt($stringToEncrypt);
- $this->assertEquals($stringToEncrypt, $this->crypto->decrypt($ciphertext));
- }
-
- public function testWrongPassword(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('HMAC does not match.');
-
- $stringToEncrypt = 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.';
- $ciphertext = $this->crypto->encrypt($stringToEncrypt);
- $this->crypto->decrypt($ciphertext, 'A wrong password!');
- }
-
- public function testLaterDecryption(): void {
- $stringToEncrypt = 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.';
- $encryptedString = '44a35023cca2e7a6125e06c29fc4b2ad9d8a33d0873a8b45b0de4ef9284f260c6c46bf25dc62120644c59b8bafe4281ddc47a70c35ae6c29ef7a63d79eefacc297e60b13042ac582733598d0a6b4de37311556bb5c480fd2633de4e6ebafa868c2d1e2d80a5d24f9660360dba4d6e0c8|lhrFgK0zd9U160Wo|a75e57ab701f9124e1113543fd1dc596f21e20d456a0d1e813d5a8aaec9adcb11213788e96598b67fe9486a9f0b99642c18296d0175db44b1ae426e4e91080ee';
- $this->assertEquals($stringToEncrypt, $this->crypto->decrypt($encryptedString, 'ThisIsAVeryS3cur3P4ssw0rd'));
- }
-
- public function testWrongIV(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('HMAC does not match.');
-
- $encryptedString = '560f5436ba864b9f12f7f7ca6d41c327554a6f2c0a160a03316b202af07c65163274993f3a46e7547c07ba89304f00594a2f3bd99f83859097c58049c39d0d4ade10e0de914ff0604961e7c849d0271ed6c0b23f984ba16e7d033e3305fb0910e7b6a2a65c988d17dbee71d8f953684d|d2kdFUspVjC0o0sr|1a5feacf87eaa6869a6abdfba9a296e7bbad45b6ad89f7dce67cdc98e2da5dc4379cc672cc655e52bbf19599bf59482fbea13a73937697fa656bf10f3fc4f1aa';
- $this->crypto->decrypt($encryptedString, 'ThisIsAVeryS3cur3P4ssw0rd');
- }
-
- public function testWrongParameters(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Authenticated ciphertext could not be decoded.');
-
- $encryptedString = '1|2';
- $this->crypto->decrypt($encryptedString, 'ThisIsAVeryS3cur3P4ssw0rd');
- }
-
- public function testLegacy(): void {
- $cipherText = 'e16599188e3d212f5c7f17fdc2abca46|M1WfLAxbcAmITeD6|509457885d6ca5e6c3bfd3741852687a7f2bffce197f8d5ae97b65818b15a1b7f616b68326ff312371540f4ca8ac55f8e2de4aa13aab3474bd3431e51214e3ee';
- $password = 'mypass';
-
- $this->assertSame('legacy test', $this->crypto->decrypt($cipherText, $password));
- }
-
- public function testVersion2CiphertextDecryptsToCorrectPlaintext(): void {
- $this->assertSame(
- 'This is a plaintext value that will be encrypted with version 2. Which addresses the reduced permutations on the IV.',
- $this->crypto->decrypt(
- 'be006387f753e8728717e43cfc5526c37adf7b2c9b4a113ceec03b7b0bccfebee74e0acfa0015c5712b4376dacbd7bce26a8fbca916fdccee46203d8289f6b2e4c19318044d375edfc67c72e6c3ae329d4c276b8d866ac1b281844e81f7681fe83d90bc4b6fffa4f3cbc157d64257a493b67fd2af3c8976cb76df520f5739305|02e78ea7c73a32f3b407c54227a9d2ce|3e7a09628f818b7b1cd7724467f5b1b33135de6d2ec62d8c0361be4f2c5203385f10babdcae017d7b30abe5be2117803e3195fb6d9ef20949fe35dad5e9241ea|2',
- 'insecure-static-password'
- )
- );
- }
-
- /**
- * Test data taken from https://github.com/owncloud/core/blob/9deb8196b20354c8de0cd720ad4d18d52ccc96d8/tests/lib/Security/CryptoTest.php#L56-L60
- */
- public function testOcVersion2CiphertextDecryptsToCorrectPlaintext(): void {
- $this->assertSame(
- 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt.',
- $this->crypto->decrypt(
- 'v2|d57dbe4d1317cdf19d4ddc2df807f6b5d63ab1e119c46590ce54bae56a9cd3969168c4ec1600ac9758dd7e7afb9c4c962dd23072c1463add1d9c77c467723b37bb768ef00e3c50898e59247cbb59ce56b74ce5990648ffe9e40d0e95076c27a785bdcf32c219ea4ad5c316b1f12f48c1|6bd21db258a5e406a2c288a444de195f|a19111a4cf1a11ee95fc1734699c20964eaa05bb007e1cecc4cc6872f827a4b7deedc977c13b138d728d68116aa3d82f9673e20c7e447a9788aa3be994b67cd6',
- 'ThisIsAVeryS3cur3P4ssw0rd'
- )
- );
- }
-
- public function testDecryptWithWrongSecretThrowsHmacExceptionNotValueError(): void {
- // Encrypt with 'secret-A'
- $configA = $this->createMock(IConfig::class);
- $configA->method('getSystemValue')->with('secret')->willReturn('secret-A');
- $configA->method('getSystemValueString')->with('secret')->willReturn('secret-A');
- $cryptoA = new Crypto($configA);
- $ciphertext = $cryptoA->encrypt('plaintext');
-
- // Decrypt with 'secret-B': first attempt fails (HMAC mismatch), fallback to empty
- // string must not propagate a ValueError for v3 ciphertexts — it must rethrow the
- // original HMAC exception instead.
- $configB = $this->createMock(IConfig::class);
- $configB->method('getSystemValue')->with('secret')->willReturn('secret-B');
- $configB->method('getSystemValueString')->with('secret')->willReturn('secret-B');
- $cryptoB = new Crypto($configB);
-
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('HMAC does not match.');
- $cryptoB->decrypt($ciphertext);
- }
-
- public function testVersion3CiphertextDecryptsToCorrectPlaintext(): void {
- $this->assertSame(
- 'Another plaintext value that will be encrypted with version 3. It addresses the related key issue. Old ciphertexts should be decrypted properly, but only use the better version for encryption.',
- $this->crypto->decrypt(
- 'c99823461db746aa74f819c8640e9e3c367fa3bb9c21dff905b5dd14072c1d1b0da8b7e6b7307bf1561b6ba7aaa932a16c23b1fd5217dc019d55233ef0813c65fccaeabd6ea3a971ce1bbbdfda790ae00fb4442693cbb50072e02875b9f50591df74d00e96fd5b9bd13cb02a5f57b062ec98a4c64fc518ed325d097454883adbfc1687c2af995a392407c5e040a54afee4b2997ab158fe48ef67ccf721a6a7031fcb44d51170892ce7971021a7f3a00d19002eb9b007efe7aecf397ec0dc22064fb5d4a15ad83949f0804feca3c69cdd|8476f53c8d49a7e119798a70086d8911|ae3f7e23d469fbc791714ceb07d854624b1bbd39ac6a4edc05d552e10659adfdcada3a059fae737ffd7d842dd3fcc84bcc364cd298e814dd4967de4ad4a658eb|3',
- 'insecure-static-password'
- )
- );
- }
-}
--- a/tests/lib/Security/Events/GenerateSecurePasswordEventTest.php
+++ /dev/null
@@ -1,33 +0,0 @@
-assertNull($event->getPassword());
- $this->assertEquals(PasswordContext::ACCOUNT, $event->getContext());
- }
-
- public function testSettingPassword(): void {
- $event = new GenerateSecurePasswordEvent();
- $event->setPassword('example');
- $this->assertEquals('example', $event->getPassword());
- }
-
- public function testSettingContext(): void {
- $event = new GenerateSecurePasswordEvent(PasswordContext::SHARING);
- $this->assertEquals(PasswordContext::SHARING, $event->getContext());
- }
-}
--- a/tests/lib/Security/Events/ValidatePasswordPolicyEventTest.php
+++ /dev/null
@@ -1,28 +0,0 @@
-assertEquals($password, $event->getPassword());
- $this->assertEquals(PasswordContext::ACCOUNT, $event->getContext());
- }
-
- public function testSettingContext(): void {
- $event = new ValidatePasswordPolicyEvent('example', PasswordContext::SHARING);
- $this->assertEquals(PasswordContext::SHARING, $event->getContext());
- }
-}
--- a/tests/lib/Security/FeaturePolicy/AddFeaturePolicyEventTest.php
+++ /dev/null
@@ -1,28 +0,0 @@
-createMock(FeaturePolicyManager::class);
- $policy = $this->createMock(FeaturePolicy::class);
- $event = new AddFeaturePolicyEvent($manager);
-
- $manager->expects($this->once())
- ->method('addDefaultPolicy')
- ->with($policy);
-
- $event->addPolicy($policy);
- }
-}
--- a/tests/lib/Security/FeaturePolicy/FeaturePolicyManagerTest.php
+++ /dev/null
@@ -1,73 +0,0 @@
-dispatcher = Server::get(IEventDispatcher::class);
- $this->manager = new FeaturePolicyManager($this->dispatcher);
- }
-
- public function testAddDefaultPolicy(): void {
- $this->manager->addDefaultPolicy(new FeaturePolicy());
- $this->addToAssertionCount(1);
- }
-
- public function testGetDefaultPolicyWithPoliciesViaEvent(): void {
- $this->dispatcher->addListener(AddFeaturePolicyEvent::class, function (AddFeaturePolicyEvent $e): void {
- $policy = new FeaturePolicy();
- $policy->addAllowedMicrophoneDomain('mydomain.com');
- $policy->addAllowedPaymentDomain('mypaymentdomain.com');
-
- $e->addPolicy($policy);
- });
-
- $this->dispatcher->addListener(AddFeaturePolicyEvent::class, function (AddFeaturePolicyEvent $e): void {
- $policy = new FeaturePolicy();
- $policy->addAllowedPaymentDomain('mydomainother.com');
- $policy->addAllowedGeoLocationDomain('mylocation.here');
-
- $e->addPolicy($policy);
- });
-
- $this->dispatcher->addListener(AddFeaturePolicyEvent::class, function (AddFeaturePolicyEvent $e): void {
- $policy = new FeaturePolicy();
- $policy->addAllowedAutoplayDomain('youtube.com');
-
- $e->addPolicy($policy);
- });
-
- $expected = new \OC\Security\FeaturePolicy\FeaturePolicy();
- $expected->addAllowedMicrophoneDomain('mydomain.com');
- $expected->addAllowedPaymentDomain('mypaymentdomain.com');
- $expected->addAllowedPaymentDomain('mydomainother.com');
- $expected->addAllowedGeoLocationDomain('mylocation.here');
- $expected->addAllowedAutoplayDomain('youtube.com');
-
- $expectedStringPolicy = "autoplay 'self' youtube.com;camera 'none';fullscreen 'self';geolocation mylocation.here;microphone mydomain.com;payment mypaymentdomain.com mydomainother.com";
-
- $this->assertEquals($expected, $this->manager->getDefaultPolicy());
- $this->assertSame($expectedStringPolicy, $this->manager->getDefaultPolicy()->buildPolicy());
- }
-}
--- a/tests/lib/Security/HasherTest.php
+++ /dev/null
@@ -1,277 +0,0 @@
- 1, 'hash' => '2|3|4']],
- ['1|我看|这本书。 我看這本書', ['version' => 1, 'hash' => '我看|这本书。 我看這本書']],
- ['2|newhash', ['version' => 2, 'hash' => 'newhash']],
- ];
- }
-
- public static function hashProviders70_71(): array {
- return [
- // Valid SHA1 strings
- ['password', '5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8', true],
- ['owncloud.com', '27a4643e43046c3569e33b68c1a4b15d31306d29', true],
-
- // Invalid SHA1 strings
- ['InvalidString', '5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8', false],
- ['AnotherInvalidOne', '27a4643e43046c3569e33b68c1a4b15d31306d29', false],
-
- // Valid legacy password string with password salt "6Wow67q1wZQZpUUeI6G2LsWUu4XKx"
- ['password', '$2a$08$emCpDEl.V.QwPWt5gPrqrOhdpH6ailBmkj2Hd2vD5U8qIy20HBe7.', true],
- ['password', '$2a$08$yjaLO4ev70SaOsWZ9gRS3eRSEpHVsmSWTdTms1949mylxJ279hzo2', true],
- ['password', '$2a$08$.jNRG/oB4r7gHJhAyb.mDupNUAqTnBIW/tWBqFobaYflKXiFeG0A6', true],
- ['owncloud.com', '$2a$08$YbEsyASX/hXVNMv8hXQo7ezreN17T8Jl6PjecGZvpX.Ayz2aUyaZ2', true],
- ['owncloud.com', '$2a$11$cHdDA2IkUP28oNGBwlL7jO/U3dpr8/0LIjTZmE8dMPA7OCUQsSTqS', true],
- ['owncloud.com', '$2a$08$GH.UoIfJ1e.qeZ85KPqzQe6NR8XWRgJXWIUeE1o/j1xndvyTA1x96', true],
-
- // Invalid legacy passwords
- ['password', '$2a$08$oKAQY5IhnZocP.61MwP7xu7TNeOb7Ostvk3j6UpacvaNMs.xRj7O2', false],
-
- // Valid passwords "6Wow67q1wZQZpUUeI6G2LsWUu4XKx"
- ['password', '1|$2a$05$ezAE0dkwk57jlfo6z5Pql.gcIK3ReXT15W7ITNxVS0ksfhO/4E4Kq', true],
- ['password', '1|$2a$05$4OQmloFW4yTVez2MEWGIleDO9Z5G9tWBXxn1vddogmKBQq/Mq93pe', true],
- ['password', '1|$2a$11$yj0hlp6qR32G9exGEXktB.yW2rgt2maRBbPgi3EyxcDwKrD14x/WO', true],
- ['owncloud.com', '1|$2a$10$Yiss2WVOqGakxuuqySv5UeOKpF8d8KmNjuAPcBMiRJGizJXjA2bKm', true],
- ['owncloud.com', '1|$2a$10$v9mh8/.mF/Ut9jZ7pRnpkuac3bdFCnc4W/gSumheQUi02Sr.xMjPi', true],
- ['owncloud.com', '1|$2a$05$ST5E.rplNRfDCzRpzq69leRzsTGtY7k88h9Vy2eWj0Ug/iA9w5kGK', true],
-
- // Invalid passwords
- ['password', '0|$2a$08$oKAQY5IhnZocP.61MwP7xu7TNeOb7Ostvk3j6UpacvaNMs.xRj7O2', false],
- ['password', '1|$2a$08$oKAQY5IhnZocP.61MwP7xu7TNeOb7Ostvk3j6UpacvaNMs.xRj7O2', false],
- ['password', '2|$2a$08$oKAQY5IhnZocP.61MwP7xu7TNeOb7Ostvk3j6UpacvaNMs.xRj7O2', false],
- ];
- }
-
- public static function hashProviders72(): array {
- return [
- // Valid ARGON2 hashes
- ['password', '2|$argon2i$v=19$m=1024,t=2,p=2$T3JGcEkxVFNOVktNSjZUcg$4/hyLtSejxNgAuzSFFV/HLM3qRQKBwEtKw61qPN4zWA', true],
- ['password', '2|$argon2i$v=19$m=1024,t=2,p=2$Zk52V24yNjMzTkhyYjJKOQ$vmqHkCaOD6SiiiFKD1GeKLg/D1ynWpyZbx4XA2yed34', true],
- ['password', '2|$argon2i$v=19$m=1024,t=2,p=2$R1pRcUZKamVlNndBc3l5ag$ToRhR8SiZc7fGMpOYfSc5haS5t9+Y00rljPJV7+qLkM', true],
- ['nextcloud.com', '2|$argon2i$v=19$m=1024,t=2,p=2$NC9xM0FFaDlzM01QM3kudg$fSfndwtO2mKMZlKdsT8XAtPY51cSS6pLSGS3xMqeJhg', true],
- ['nextcloud.com', '2|$argon2i$v=19$m=1024,t=2,p=2$UjkvUjEuL042WWl1cmdHOA$FZivLkBdZnloQsW6qq/jqWK95JSYUHW9rwQC4Ff9GN0', true],
- ['nextcloud.com', '2|$argon2i$v=19$m=1024,t=2,p=2$ZnpNdUlzMEpUTW40OVpiMQ$c+yHT9dtSYsjtVGsa7UKOsxxgQAMiUc781d9WsFACqs', true],
-
- //Invalid ARGON2 hashes
- ['password', '2|$argon2i$v=19$m=1024,t=2,p=2$UjFDUDg3cjBvM3FkbXVOWQ$7Y5xqFxSERnYn+2+7WChUpWZWMa5BEIhSHWnDgJ71Jk', false],
- ['password', '2|$argon2i$v=19$m=1024,t=2,p=2$ZUxSUi5aQklXdkcyMG1uVA$sYjoSvXg/CS/aS6Xnas/o9a/OPVcGKldzzmuiCD1Fxo', false],
- ['password', '2|$argon2i$v=19$m=1024,t=2,p=2$ZHQ5V0xMOFNmUC52by44Sg$DzQFk3bJTX0J4PVGwW6rMvtnBJRalBkbtpDIXR+d4A0', false],
- ];
- }
-
- public static function hashProviders73(): array {
- return [
- // Valid ARGON2ID hashes
- ['password', '2|$argon2id$v=19$m=65536,t=4,p=1$TEtIMnhUczliQzI0Y01WeA$BpMUDrApy25iagIogUAnlc0rNTPJmGs8lOEeVHujJ9Q', true],
- ['password', '2|$argon2id$v=19$m=65536,t=4,p=1$RzdUdDNvbHhZalVQa2VIcQ$Wo8CGasVCBcSe69ldPdoVKTWEDQkET2cgQJSUiKcIzs', true],
- ['password', '2|$argon2id$v=19$m=65536,t=4,p=1$djlDMTVkL3VnMlNZNWZPeg$PCMpdAjB+OtwGpM75IGWmYHh1h2I7l5P8YabYtKubWg', true],
- ['nextcloud.com', '2|$argon2id$v=19$m=65536,t=4,p=1$VGhGL05rcUI3d3k3WVhibQ$CSy0ShUnamZQhu8oeZfUTTd/S3z966zuQ/uz1Y80Rss', true],
- ['nextcloud.com', '2|$argon2id$v=19$m=65536,t=4,p=1$ZVlZTVlCaTZhRlZHOGFpYQ$xd1TtMz1Mi0SuZrP+VWB3v/hwoC7HfSVsUYmzOo2DUU', true],
- ['nextcloud.com', '2|$argon2id$v=19$m=65536,t=4,p=1$OG1wZUtzZ0tnLjF2MUZVMA$CBluq8W8ISmZ9QumeWsVhaVREP0Zcq8rwk2NrA9d4YE', true],
-
- //Invalid ARGON2ID hashes
- ['password', '2|$argon2id$v=19$m=65536,t=4,p=1$V3ovTHlvc0Eyb24xenVRNQ$iY/A0Yf24c2DToedj2rj9+KeoJBGsJYQOlJMoa0SFXk', false],
- ['password', '2|$argon2id$v=19$m=65536,t=4,p=1$NlYuMlQ0ODIudTRkZDhYUw$/Z71ckOIuydujedUGK73iXC9vbLzlH/iXkG9+gGgn+c', false],
- ['password', '2|$argon2id$v=19$m=65536,t=4,p=1$b09kNFZTZWFjS05aTkl6ZA$llE4TnIYYrC0H7wkTL1JsIwAAgoMJERlqtFcHHQcXTs', false],
- ];
- }
-
- /** @var Hasher */
- protected $hasher;
-
- /** @var IConfig */
- protected $config;
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- $this->config = $this->createMock(IConfig::class);
-
- $this->config->method('getSystemValueInt')
- ->willReturnCallback(function ($name, $default) {
- return $default;
- });
-
- $this->hasher = new Hasher($this->config);
- }
-
- public function testHash(): void {
- $hash = $this->hasher->hash('String To Hash');
- $this->assertNotNull($hash);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('versionHashProvider')]
- public function testSplitHash($hash, $expected): void {
- $relativePath = self::invokePrivate($this->hasher, 'splitHash', [$hash]);
- $this->assertSame($expected, $relativePath);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('hashProviders70_71')]
- public function testVerify($password, $hash, $expected): void {
- $this->config
- ->expects($this->any())
- ->method('getSystemValue')
- ->willReturnCallback(function ($key, $default) {
- if ($key === 'passwordsalt') {
- return '6Wow67q1wZQZpUUeI6G2LsWUu4XKx';
- }
- return $default;
- });
-
- $result = $this->hasher->verify($password, $hash);
- $this->assertSame($expected, $result);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('hashProviders72')]
- public function testVerifyArgon2i($password, $hash, $expected): void {
- if (!\defined('PASSWORD_ARGON2I')) {
- $this->markTestSkipped('Need ARGON2 support to test ARGON2 hashes');
- }
-
- $result = $this->hasher->verify($password, $hash);
- $this->assertSame($expected, $result);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('hashProviders73')]
- public function testVerifyArgon2id(string $password, string $hash, bool $expected): void {
- if (!\defined('PASSWORD_ARGON2ID')) {
- $this->markTestSkipped('Need ARGON2ID support to test ARGON2ID hashes');
- }
-
- $result = $this->hasher->verify($password, $hash);
- $this->assertSame($expected, $result);
- }
-
- public function testUpgradeHashBlowFishToArgon2(): void {
- if (!\defined('PASSWORD_ARGON2I')) {
- $this->markTestSkipped('Need ARGON2 support to test ARGON2 hashes');
- }
-
- $message = 'mysecret';
-
- $blowfish = 1 . '|' . password_hash($message, PASSWORD_BCRYPT, []);
- $argon2 = 2 . '|' . password_hash($message, PASSWORD_ARGON2I, []);
-
- $newAlg = PASSWORD_ARGON2I;
- if (\defined('PASSWORD_ARGON2ID')) {
- $newAlg = PASSWORD_ARGON2ID;
- $argon2 = 2 . '|' . password_hash($message, PASSWORD_ARGON2ID, []);
- }
-
- $this->assertTrue($this->hasher->verify($message, $blowfish, $newHash));
- $this->assertTrue($this->hasher->verify($message, $argon2));
-
- $relativePath = self::invokePrivate($this->hasher, 'splitHash', [$newHash]);
-
- $this->assertFalse(password_needs_rehash($relativePath['hash'], $newAlg, []));
- }
-
- public function testUsePasswordDefaultArgon2iVerify(): void {
- if (!\defined('PASSWORD_ARGON2I')) {
- $this->markTestSkipped('Need ARGON2 support to test ARGON2 hashes');
- }
-
- $this->config->method('getSystemValueBool')
- ->with('hashing_default_password')
- ->willReturn(true);
-
- $message = 'mysecret';
-
- $argon2i = 2 . '|' . password_hash($message, PASSWORD_ARGON2I, []);
-
- $newHash = null;
- $this->assertTrue($this->hasher->verify($message, $argon2i, $newHash));
- $this->assertNotNull($newHash);
-
- $relativePath = self::invokePrivate($this->hasher, 'splitHash', [$newHash]);
- $this->assertEquals(1, $relativePath['version']);
- $this->assertEquals(PASSWORD_BCRYPT, password_get_info($relativePath['hash'])['algo']);
- $this->assertFalse(password_needs_rehash($relativePath['hash'], PASSWORD_BCRYPT));
- $this->assertTrue(password_verify($message, $relativePath['hash']));
- }
-
- public function testDoNotUsePasswordDefaultArgon2idVerify(): void {
- if (!\defined('PASSWORD_ARGON2ID')) {
- $this->markTestSkipped('Need ARGON2ID support to test ARGON2ID hashes');
- }
-
- $this->config->method('getSystemValueBool')
- ->with('hashing_default_password')
- ->willReturn(false);
-
- $message = 'mysecret';
-
- $argon2id = 3 . '|' . password_hash($message, PASSWORD_ARGON2ID, []);
-
- $newHash = null;
- $this->assertTrue($this->hasher->verify($message, $argon2id, $newHash));
- $this->assertNull($newHash);
- }
-
- public function testHashUsePasswordDefault(): void {
- if (!\defined('PASSWORD_ARGON2I')) {
- $this->markTestSkipped('Need ARGON2 support to test ARGON2 hashes');
- }
-
- $this->config->method('getSystemValueBool')
- ->with('hashing_default_password')
- ->willReturn(true);
-
- $message = 'mysecret';
-
- $hash = $this->hasher->hash($message);
- $relativePath = self::invokePrivate($this->hasher, 'splitHash', [$hash]);
-
- $this->assertSame(1, $relativePath['version']);
-
- $info = password_get_info($relativePath['hash']);
- $this->assertEquals(PASSWORD_BCRYPT, $info['algo']);
- }
-
- public function testValidHash(): void {
- $hash = '3|$argon2id$v=19$m=65536,t=4,p=1$czFCSjk3LklVdXppZ2VCWA$li0NgdXe2/jwSRxgteGQPWlzJU0E0xdtfHbCbrpych0';
-
- $isValid = $this->hasher->validate($hash);
-
- $this->assertTrue($isValid);
- }
-
- public function testValidGeneratedHash(): void {
- $message = 'secret';
- $hash = $this->hasher->hash($message);
-
- $isValid = $this->hasher->validate($hash);
-
- $this->assertTrue($isValid);
- }
-
- public function testInvalidHash(): void {
- $invalidHash = 'someInvalidHash';
-
- $isValid = $this->hasher->validate($invalidHash);
-
- $this->assertFalse($isValid);
- }
-}
--- a/tests/lib/Security/IdentityProof/KeyTest.php
+++ /dev/null
@@ -1,33 +0,0 @@
-key = new Key('public', 'private');
- }
-
- public function testGetPrivate(): void {
- $this->assertSame('private', $this->key->getPrivate());
- }
-
- public function testGetPublic(): void {
- $this->assertSame('public', $this->key->getPublic());
- }
-}
--- a/tests/lib/Security/IdentityProof/ManagerTest.php
+++ /dev/null
@@ -1,274 +0,0 @@
-factory = $this->createMock(Factory::class);
- $this->appData = $this->createMock(AppData::class);
- $this->config = $this->createMock(IConfig::class);
- $this->factory->expects($this->any())
- ->method('get')
- ->with('identityproof')
- ->willReturn($this->appData);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->cacheFactory = $this->createMock(ICacheFactory::class);
- $this->cache = $this->createMock(ICache::class);
-
- $this->cacheFactory->expects($this->any())
- ->method('createDistributed')
- ->willReturn($this->cache);
-
- $this->crypto = $this->createMock(ICrypto::class);
- $this->manager = $this->getManager(['generateKeyPair']);
- }
-
- /**
- * create manager object
- *
- * @param array $setMethods
- * @return Manager|\PHPUnit\Framework\MockObject\MockObject
- */
- protected function getManager($setMethods = []) {
- if (empty($setMethods)) {
- return new Manager(
- $this->factory,
- $this->crypto,
- $this->config,
- $this->logger,
- $this->cacheFactory,
- );
- } else {
- return $this->getMockBuilder(Manager::class)
- ->setConstructorArgs([
- $this->factory,
- $this->crypto,
- $this->config,
- $this->logger,
- $this->cacheFactory,
- ])
- ->onlyMethods($setMethods)
- ->getMock();
- }
- }
-
- public function testGetKeyWithExistingKey(): void {
- $user = $this->createMock(IUser::class);
- $user
- ->expects($this->once())
- ->method('getUID')
- ->willReturn('MyUid');
- $folder = $this->createMock(ISimpleFolder::class);
- $privateFile = $this->createMock(ISimpleFile::class);
- $privateFile
- ->expects($this->once())
- ->method('getContent')
- ->willReturn('EncryptedPrivateKey');
- $publicFile = $this->createMock(ISimpleFile::class);
- $publicFile
- ->expects($this->once())
- ->method('getContent')
- ->willReturn('MyPublicKey');
- $this->crypto
- ->expects($this->once())
- ->method('decrypt')
- ->with('EncryptedPrivateKey')
- ->willReturn('MyPrivateKey');
- $folder
- ->expects($this->exactly(2))
- ->method('getFile')
- ->willReturnMap([
- ['private', $privateFile],
- ['public', $publicFile],
- ]);
- $this->appData
- ->expects($this->once())
- ->method('getFolder')
- ->with('user-MyUid')
- ->willReturn($folder);
- $this->cache
- ->expects($this->exactly(2))
- ->method('get')
- ->willReturn(null);
-
- $expected = new Key('MyPublicKey', 'MyPrivateKey');
- $this->assertEquals($expected, $this->manager->getKey($user));
- }
-
- public function testGetKeyWithExistingKeyCached(): void {
- $user = $this->createMock(IUser::class);
- $user
- ->expects($this->once())
- ->method('getUID')
- ->willReturn('MyUid');
- $this->crypto
- ->expects($this->once())
- ->method('decrypt')
- ->with('EncryptedPrivateKey')
- ->willReturn('MyPrivateKey');
- $this->cache
- ->expects($this->exactly(2))
- ->method('get')
- ->willReturnMap([
- ['user-MyUid-public', 'MyPublicKey'],
- ['user-MyUid-private', 'EncryptedPrivateKey'],
- ]);
-
- $expected = new Key('MyPublicKey', 'MyPrivateKey');
- $this->assertEquals($expected, $this->manager->getKey($user));
- }
-
- public function testSetPublicKey(): void {
- $user = $this->createMock(IUser::class);
- $user
- ->expects($this->exactly(1))
- ->method('getUID')
- ->willReturn('MyUid');
- $publicFile = $this->createMock(ISimpleFile::class);
- $folder = $this->createMock(ISimpleFolder::class);
- $folder
- ->expects($this->once())
- ->method('newFile')
- ->willReturnMap([
- ['public', 'MyNewPublicKey', $publicFile],
- ]);
- $this->appData
- ->expects($this->once())
- ->method('getFolder')
- ->with('user-MyUid')
- ->willReturn($folder);
- $this->cache
- ->expects($this->once())
- ->method('set')
- ->with('user-MyUid-public', 'MyNewPublicKey');
-
- $this->manager->setPublicKey($user, 'MyNewPublicKey');
- }
-
- public function testGetKeyWithNotExistingKey(): void {
- $user = $this->createMock(IUser::class);
- $user
- ->expects($this->once())
- ->method('getUID')
- ->willReturn('MyUid');
- $this->manager
- ->expects($this->once())
- ->method('generateKeyPair')
- ->willReturn(['MyNewPublicKey', 'MyNewPrivateKey']);
- $this->appData
- ->expects($this->once())
- ->method('newFolder')
- ->with('user-MyUid');
- $folder = $this->createMock(ISimpleFolder::class);
- $this->crypto
- ->expects($this->once())
- ->method('encrypt')
- ->with('MyNewPrivateKey')
- ->willReturn('MyNewEncryptedPrivateKey');
- $privateFile = $this->createMock(ISimpleFile::class);
- $privateFile
- ->expects($this->once())
- ->method('putContent')
- ->with('MyNewEncryptedPrivateKey');
- $publicFile = $this->createMock(ISimpleFile::class);
- $publicFile
- ->expects($this->once())
- ->method('putContent')
- ->with('MyNewPublicKey');
- $folder
- ->expects($this->exactly(2))
- ->method('newFile')
- ->willReturnMap([
- ['private', null, $privateFile],
- ['public', null, $publicFile],
- ]);
- $this->appData
- ->expects($this->exactly(2))
- ->method('getFolder')
- ->with('user-MyUid')
- ->willReturnOnConsecutiveCalls(
- $this->throwException(new \Exception()),
- $folder
- );
-
- $expected = new Key('MyNewPublicKey', 'MyNewPrivateKey');
- $this->assertEquals($expected, $this->manager->getKey($user));
- }
-
- public function testGenerateKeyPair(): void {
- $manager = $this->getManager();
- $data = 'MyTestData';
-
- [$resultPublicKey, $resultPrivateKey] = self::invokePrivate($manager, 'generateKeyPair');
- openssl_sign($data, $signature, $resultPrivateKey);
- $details = openssl_pkey_get_details(openssl_pkey_get_public($resultPublicKey));
-
- $this->assertSame(1, openssl_verify($data, $signature, $resultPublicKey));
- $this->assertSame(2048, $details['bits']);
- }
-
- public function testGetSystemKey(): void {
- $manager = $this->getManager(['retrieveKey']);
-
- /** @var Key|\PHPUnit\Framework\MockObject\MockObject $key */
- $key = $this->createMock(Key::class);
-
- $this->config->expects($this->once())->method('getSystemValue')
- ->with('instanceid', null)->willReturn('instanceId');
-
- $manager->expects($this->once())->method('retrieveKey')->with('system-instanceId')
- ->willReturn($key);
-
- $this->assertSame($key, $manager->getSystemKey());
- }
-
- public function testGetSystemKeyFailure(): void {
- $this->expectException(\RuntimeException::class);
-
- $manager = $this->getManager(['retrieveKey']);
-
- /** @var Key|\PHPUnit\Framework\MockObject\MockObject $key */
- $key = $this->createMock(Key::class);
-
- $this->config->expects($this->once())->method('getSystemValue')
- ->with('instanceid', null)->willReturn(null);
-
- $manager->getSystemKey();
- }
-}
--- a/tests/lib/Security/IdentityProof/SignerTest.php
+++ /dev/null
@@ -1,189 +0,0 @@
-key = new Key($this->public, $this->private);
-
- $this->keyManager = $this->createMock(Manager::class);
- $this->timeFactory = $this->createMock(ITimeFactory::class);
- $this->userManager = $this->createMock(IUserManager::class);
-
- $this->signer = new Signer(
- $this->keyManager,
- $this->timeFactory,
- $this->userManager
- );
- }
-
- public function testSign(): void {
- $user = $this->createMock(IUser::class);
- $user->method('getCloudId')
- ->willReturn('foo@example.com');
-
- $this->timeFactory->method('getTime')
- ->willReturn(42);
-
- $this->keyManager->method('getKey')
- ->with($this->equalTo($user))
- ->willReturn($this->key);
-
- $data = [
- 'foo' => 'bar',
- 'abc' => 'def',
- 'xyz' => 123,
- ];
-
- $expects = [
- 'message' => [
- 'data' => $data,
- 'type' => 'myType',
- 'signer' => 'foo@example.com',
- 'timestamp' => 42,
- ],
- 'signature' => 'E1fNdoWMX1QmSyKv+S3FtOgLe9niYGQFWOKGaMLxc2h7s3V++EIqJvw/NCLBfrUowzWkTzhkjfbHaf88Hz34WAn4sAwXYAO8cnboQs6SClKRzQ/nvbtLgd2wm9RQ8UTOM7wR6C7HpIn4qqJ4BTQ1bAwYAiJ2GoK+W8wC0o0Gpub2906j3JJ4cbc9lufd5ohWKCev8Ubem/EEKaRIZA7qHh+Q1MKXTaJQJlCjTMe5PyGy0fsmtVxsPls3/Fkd9sVeHEHSYHzOiF6ttlxWou4TdRbq3WSEVpt1DOOvkKI9w2+zBJ7IPH8CnVpXcdIzWDctUygZKzNMUQnweDOOziEdUw=='
- ];
-
- $result = $this->signer->sign('myType', $data, $user);
-
- $this->assertEquals($expects, $result);
- }
-
- public function testVerifyValid(): void {
- $data = [
- 'message' => [
- 'data' => [
- 'foo' => 'bar',
- 'abc' => 'def',
- 'xyz' => 123,
- ],
- 'type' => 'myType',
- 'signer' => 'foo@example.com',
- 'timestamp' => 42,
- ],
- 'signature' => 'E1fNdoWMX1QmSyKv+S3FtOgLe9niYGQFWOKGaMLxc2h7s3V++EIqJvw/NCLBfrUowzWkTzhkjfbHaf88Hz34WAn4sAwXYAO8cnboQs6SClKRzQ/nvbtLgd2wm9RQ8UTOM7wR6C7HpIn4qqJ4BTQ1bAwYAiJ2GoK+W8wC0o0Gpub2906j3JJ4cbc9lufd5ohWKCev8Ubem/EEKaRIZA7qHh+Q1MKXTaJQJlCjTMe5PyGy0fsmtVxsPls3/Fkd9sVeHEHSYHzOiF6ttlxWou4TdRbq3WSEVpt1DOOvkKI9w2+zBJ7IPH8CnVpXcdIzWDctUygZKzNMUQnweDOOziEdUw=='
- ];
-
- $user = $this->createMock(IUser::class);
-
- $this->keyManager->method('getKey')
- ->with($this->equalTo($user))
- ->willReturn($this->key);
-
- $this->userManager->method('get')
- ->with('foo')
- ->willReturn($user);
-
- $this->assertTrue($this->signer->verify($data));
- }
-
- public function testVerifyInvalid(): void {
- $data = [
- 'message' => [
- 'data' => [
- 'foo' => 'bar',
- 'abc' => 'def',
- 'xyz' => 123,
- ],
- 'type' => 'myType',
- 'signer' => 'foo@example.com',
- 'timestamp' => 42,
- ],
- 'signature' => 'Invalid sig'
- ];
-
- $user = $this->createMock(IUser::class);
-
- $this->keyManager->method('getKey')
- ->with($this->equalTo($user))
- ->willReturn($this->key);
-
- $this->userManager->method('get')
- ->with('foo')
- ->willReturn($user);
-
- $this->assertFalse($this->signer->verify($data));
- }
-
- public function testVerifyInvalidData(): void {
- $data = [
- ];
-
- $this->assertFalse($this->signer->verify($data));
- }
-}
--- a/tests/lib/Security/Ip/BruteforceAllowListTest.php
+++ /dev/null
@@ -1,162 +0,0 @@
-appConfig = $this->createMock(IAppConfig::class);
- $this->factory = new Factory();
-
- $this->allowList = new BruteforceAllowList(
- $this->appConfig,
- $this->factory,
- );
- }
-
- public static function dataIsBypassListed(): array {
- return [
- [
- '10.10.10.10',
- [
- 'whitelist_0' => '10.10.10.0/24',
- ],
- true,
- ],
- [
- '10.10.10.10',
- [
- 'whitelist_0' => '192.168.0.0/16',
- ],
- false,
- ],
- [
- '10.10.10.10',
- [
- 'whitelist_0' => '192.168.0.0/16',
- 'whitelist_1' => '10.10.10.0/24',
- ],
- true,
- ],
- [
- '10.10.10.10',
- [
- 'whitelist_0' => '10.10.10.11/31',
- ],
- true,
- ],
- [
- '10.10.10.10',
- [
- 'whitelist_0' => '10.10.10.9/31',
- ],
- false,
- ],
- [
- '10.10.10.10',
- [
- 'whitelist_0' => '10.10.10.15/29',
- ],
- true,
- ],
- [
- 'dead:beef:cafe::1',
- [
- 'whitelist_0' => '192.168.0.0/16',
- 'whitelist_1' => '10.10.10.0/24',
- 'whitelist_2' => 'deaf:beef:cafe:1234::/64'
- ],
- false,
- ],
- [
- 'dead:beef:cafe::1',
- [
- 'whitelist_0' => '192.168.0.0/16',
- 'whitelist_1' => '10.10.10.0/24',
- 'whitelist_2' => 'deaf:beef::/64'
- ],
- false,
- ],
- [
- 'dead:beef:cafe::1',
- [
- 'whitelist_0' => '192.168.0.0/16',
- 'whitelist_1' => '10.10.10.0/24',
- 'whitelist_2' => 'deaf:cafe::/8'
- ],
- true,
- ],
- [
- 'dead:beef:cafe::1111',
- [
- 'whitelist_0' => 'dead:beef:cafe::1100/123',
- ],
- true,
- ],
- [
- 'invalid',
- [],
- false,
- ],
- ];
- }
-
- /**
- * @param string[] $allowList
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataIsBypassListed')]
- public function testIsBypassListed(
- string $ip,
- array $allowList,
- bool $isAllowListed,
- ): void {
- $this->appConfig->method('searchKeys')
- ->with($this->equalTo('bruteForce'), $this->equalTo('whitelist_'))
- ->willReturn(array_keys($allowList));
-
- $this->appConfig->method('getValueString')
- ->willReturnCallback(function ($app, $key, $default) use ($allowList) {
- if ($app !== 'bruteForce') {
- return $default;
- }
- if (isset($allowList[$key])) {
- return $allowList[$key];
- }
- return $default;
- });
-
- $this->assertSame(
- $isAllowListed,
- $this->allowList->isBypassListed($ip)
- );
- }
-}
--- a/tests/lib/Security/Ip/RemoteAddressTest.php
+++ /dev/null
@@ -1,80 +0,0 @@
-config = $this->createMock(IConfig::class);
- $this->request = $this->createMock(IRequest::class);
- }
-
- /**
- * @param mixed $allowedRanges
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataProvider')]
- public function testAllowedIps(string $remoteIp, $allowedRanges, bool $expected): void {
- $this->request
- ->method('getRemoteAddress')
- ->willReturn($remoteIp);
- $this->config
- ->method('getSystemValue')
- ->with('allowed_admin_ranges', false)
- ->willReturn($allowedRanges);
-
- $remoteAddress = new RemoteAddress($this->config, $this->request);
-
- $this->assertEquals($expected, $remoteAddress->allowsAdminActions());
- }
-
- /**
- * @return array
- */
- public static function dataProvider(): array {
- return [
- // No IP (ie. CLI)
- ['', ['192.168.1.2/24'], true],
- ['', ['fe80/8'], true],
- // No configuration
- ['1.2.3.4', false, true],
- ['1234:4567:8910::', false, true],
- // v6 Zone ID
- ['fe80::1fc4:15d8:78db:2319%enp4s0', false, true],
- // Empty configuration
- ['1.2.3.4', [], true],
- ['1234:4567:8910::', [], true],
- // Invalid configuration
- ['1.2.3.4', 'hello', true],
- ['1234:4567:8910::', 'world', true],
- // Mixed configuration
- ['192.168.1.5', ['1.2.3.*', '1234::/8'], false],
- ['::1', ['127.0.0.1', '1234::/8'], false],
- ['192.168.1.5', ['192.168.1.0/24', '1234::/8'], true],
- // Allowed IP
- ['1.2.3.4', ['1.2.3.*'], true],
- ['fc00:1:2:3::1', ['fc00::/7'], true],
- ['1.2.3.4', ['192.168.1.2/24', '1.2.3.0/24'], true],
- ['1234:4567:8910::1', ['fe80::/8','1234:4567::/16'], true],
- // Blocked IP
- ['192.168.1.5', ['1.2.3.*'], false],
- ['9234:4567:8910::', ['1234:4567::1'], false],
- ['192.168.2.1', ['192.168.1.2/24', '1.2.3.0/24'], false],
- ['9234:4567:8910::', ['fe80::/8','1234:4567::/16'], false],
- ];
- }
-}
--- a/tests/lib/Security/Normalizer/IpAddressTest.php
+++ /dev/null
@@ -1,70 +0,0 @@
-assertSame($expected, (new IpAddress($input))->getSubnet());
- }
-
- public function testToString(): void {
- $this->assertSame('127.0.0.1', (string)(new IpAddress('127.0.0.1')));
- }
-}
--- a/tests/lib/Security/RateLimiting/Backend/MemoryCacheBackendTest.php
+++ /dev/null
@@ -1,144 +0,0 @@
-config = $this->createMock(IConfig::class);
- $this->cacheFactory = $this->createMock(ICacheFactory::class);
- $this->timeFactory = $this->createMock(ITimeFactory::class);
- $this->cache = $this->createMock(ICache::class);
-
- $this->cacheFactory
- ->expects($this->once())
- ->method('createDistributed')
- ->with('OC\Security\RateLimiting\Backend\MemoryCacheBackend')
- ->willReturn($this->cache);
-
- $this->config->method('getSystemValueBool')
- ->with('ratelimit.protection.enabled')
- ->willReturn(true);
-
- $this->memoryCache = new MemoryCacheBackend(
- $this->config,
- $this->cacheFactory,
- $this->timeFactory
- );
- }
-
- public function testGetAttemptsWithNoAttemptsBefore(): void {
- $this->cache
- ->expects($this->once())
- ->method('get')
- ->with('eea460b8d756885099c7f0a4c083bf6a745069ee4a301984e726df58fd4510bffa2dac4b7fd5d835726a6753ffa8343ba31c7e902bbef78fc68c2e743667cb4b')
- ->willReturn(null);
-
- $this->assertSame(0, $this->memoryCache->getAttempts('Method', 'User'));
- }
-
- public function testGetAttempts(): void {
- $this->timeFactory
- ->expects($this->once())
- ->method('getTime')
- ->willReturn(210);
- $this->cache
- ->expects($this->once())
- ->method('get')
- ->with('eea460b8d756885099c7f0a4c083bf6a745069ee4a301984e726df58fd4510bffa2dac4b7fd5d835726a6753ffa8343ba31c7e902bbef78fc68c2e743667cb4b')
- ->willReturn(json_encode([
- '1',
- '2',
- '87',
- '223',
- '223',
- '224',
- ]));
-
- $this->assertSame(3, $this->memoryCache->getAttempts('Method', 'User'));
- }
-
- public function testRegisterAttemptWithNoAttemptsBefore(): void {
- $this->timeFactory
- ->expects($this->once())
- ->method('getTime')
- ->willReturn(123);
-
- $this->cache
- ->expects($this->once())
- ->method('get')
- ->with('eea460b8d756885099c7f0a4c083bf6a745069ee4a301984e726df58fd4510bffa2dac4b7fd5d835726a6753ffa8343ba31c7e902bbef78fc68c2e743667cb4b')
- ->willReturn(null);
- $this->cache
- ->expects($this->once())
- ->method('set')
- ->with(
- 'eea460b8d756885099c7f0a4c083bf6a745069ee4a301984e726df58fd4510bffa2dac4b7fd5d835726a6753ffa8343ba31c7e902bbef78fc68c2e743667cb4b',
- json_encode(['223'])
- );
-
- $this->memoryCache->registerAttempt('Method', 'User', 100);
- }
-
- public function testRegisterAttempt(): void {
- $this->timeFactory
- ->expects($this->once())
- ->method('getTime')
- ->willReturn(86);
-
- $this->cache
- ->expects($this->once())
- ->method('get')
- ->with('eea460b8d756885099c7f0a4c083bf6a745069ee4a301984e726df58fd4510bffa2dac4b7fd5d835726a6753ffa8343ba31c7e902bbef78fc68c2e743667cb4b')
- ->willReturn(json_encode([
- '1',
- '2',
- '87',
- '123',
- '123',
- '124',
- ]));
- $this->cache
- ->expects($this->once())
- ->method('set')
- ->with(
- 'eea460b8d756885099c7f0a4c083bf6a745069ee4a301984e726df58fd4510bffa2dac4b7fd5d835726a6753ffa8343ba31c7e902bbef78fc68c2e743667cb4b',
- json_encode([
- '87',
- '123',
- '123',
- '124',
- '186',
- ])
- );
-
- $this->memoryCache->registerAttempt('Method', 'User', 100);
- }
-}
--- a/tests/lib/Security/RateLimiting/LimiterTest.php
+++ /dev/null
@@ -1,134 +0,0 @@
-backend = $this->createMock(IBackend::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $this->limiter = new Limiter(
- $this->backend,
- $this->logger,
- );
- }
-
- public function testRegisterAnonRequestExceeded(): void {
- $this->expectException(RateLimitExceededException::class);
- $this->expectExceptionMessage('Rate limit exceeded');
-
- $this->backend
- ->expects($this->once())
- ->method('getAttempts')
- ->with(
- 'MyIdentifier',
- '4664f0d9c88dcb7552be47b37bb52ce35977b2e60e1ac13757cf625f31f87050a41f3da064887fa87d49fd042e4c8eb20de8f10464877d3959677ab011b73a47'
- )
- ->willReturn(101);
- $this->logger->expects($this->once())
- ->method('info');
-
- $this->limiter->registerAnonRequest('MyIdentifier', 100, 100, '127.0.0.1');
- }
-
- public function testRegisterAnonRequestSuccess(): void {
- $this->backend
- ->expects($this->once())
- ->method('getAttempts')
- ->with(
- 'MyIdentifier',
- '4664f0d9c88dcb7552be47b37bb52ce35977b2e60e1ac13757cf625f31f87050a41f3da064887fa87d49fd042e4c8eb20de8f10464877d3959677ab011b73a47'
- )
- ->willReturn(99);
- $this->backend
- ->expects($this->once())
- ->method('registerAttempt')
- ->with(
- 'MyIdentifier',
- '4664f0d9c88dcb7552be47b37bb52ce35977b2e60e1ac13757cf625f31f87050a41f3da064887fa87d49fd042e4c8eb20de8f10464877d3959677ab011b73a47',
- 100
- );
- $this->logger->expects($this->never())
- ->method('info');
-
- $this->limiter->registerAnonRequest('MyIdentifier', 100, 100, '127.0.0.1');
- }
-
- public function testRegisterUserRequestExceeded(): void {
- $this->expectException(RateLimitExceededException::class);
- $this->expectExceptionMessage('Rate limit exceeded');
-
- /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
- $user = $this->createMock(IUser::class);
- $user
- ->expects($this->once())
- ->method('getUID')
- ->willReturn('MyUid');
- $this->backend
- ->expects($this->once())
- ->method('getAttempts')
- ->with(
- 'MyIdentifier',
- 'ddb2ec50fa973fd49ecf3d816f677c8095143e944ad10485f30fb3dac85c13a346dace4dae2d0a15af91867320957bfd38a43d9eefbb74fe6919e15119b6d805'
- )
- ->willReturn(101);
- $this->logger->expects($this->once())
- ->method('info');
-
- $this->limiter->registerUserRequest('MyIdentifier', 100, 100, $user);
- }
-
- public function testRegisterUserRequestSuccess(): void {
- /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */
- $user = $this->createMock(IUser::class);
- $user
- ->expects($this->once())
- ->method('getUID')
- ->willReturn('MyUid');
-
- $this->backend
- ->expects($this->once())
- ->method('getAttempts')
- ->with(
- 'MyIdentifier',
- 'ddb2ec50fa973fd49ecf3d816f677c8095143e944ad10485f30fb3dac85c13a346dace4dae2d0a15af91867320957bfd38a43d9eefbb74fe6919e15119b6d805'
- )
- ->willReturn(99);
- $this->backend
- ->expects($this->once())
- ->method('registerAttempt')
- ->with(
- 'MyIdentifier',
- 'ddb2ec50fa973fd49ecf3d816f677c8095143e944ad10485f30fb3dac85c13a346dace4dae2d0a15af91867320957bfd38a43d9eefbb74fe6919e15119b6d805',
- 100
- );
- $this->logger->expects($this->never())
- ->method('info');
-
- $this->limiter->registerUserRequest('MyIdentifier', 100, 100, $user);
- }
-}
--- a/tests/lib/Security/RemoteHostValidatorIntegrationTest.php
+++ /dev/null
@@ -1,123 +0,0 @@
-config = $this->createMock(IConfig::class);
-
- $this->validator = new RemoteHostValidator(
- $this->config,
- Server::get(HostnameClassifier::class),
- Server::get(IpAddressClassifier::class),
- new NullLogger(),
- );
- }
-
- public static function localHostsData(): array {
- return [
- ['[::1]'],
- ['[::]'],
- ['192.168.0.1'],
- ['127.0.0.1'],
- ['127.0.0.13'], // all 127.0.0.0/8 network is loopback address
- ['172.16.42.1'],
- ['[fdf8:f53b:82e4::53]'],
- ['[fe80::200:5aee:feaa:20a2]'],
- ['[0:0:0:0:0:ffff:10.0.0.1]'],
- ['[0:0:0:0:0:ffff:127.0.0.0]'],
- ['10.0.0.1'],
- ['!@#$'], // test invalid url
- ['100.100.100.200'],
- ['192.0.0.1'],
- ['0177.0.0.9'],
- ['⑯⑨。②⑤④。⑯⑨。②⑤④'],
- ['127。②⑤④。⑯⑨.②⑤④'],
- ['127.0.00000000000000000000000000000000001'],
- ['127.1'],
- ['127.000.001'],
- ['0177.0.0.01'],
- ['0x7f.0x0.0x0.0x1'],
- ['0x7f000001'],
- ['2130706433'],
- ['00000000000000000000000000000000000000000000000000177.1'],
- ['0x7f.1'],
- ['127.0x1'],
- ['[0000:0000:0000:0000:0000:0000:0000:0001]'],
- ['[0:0:0:0:0:0:0:1]'],
- ['[0:0:0:0::0:0:1]'],
- ['%31%32%37%2E%30%2E%30%2E%31'],
- ['%31%32%37%2E%30%2E%30.%31'],
- ['[%3A%3A%31]'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('localHostsData')]
- public function testLocalHostsWhenNotAllowed(string $host): void {
- $this->config
- ->method('getSystemValueBool')
- ->with('allow_local_remote_servers', false)
- ->willReturn(false);
-
- $isValid = $this->validator->isValid($host);
-
- self::assertFalse($isValid);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('localHostsData')]
- public function testLocalHostsWhenAllowed(string $host): void {
- $this->config
- ->method('getSystemValueBool')
- ->with('allow_local_remote_servers', false)
- ->willReturn(true);
-
- $isValid = $this->validator->isValid($host);
-
- self::assertTrue($isValid);
- }
-
- public static function externalAddressesData():array {
- return [
- ['8.8.8.8'],
- ['8.8.4.4'],
- ['8.8.8.8'],
- ['8.8.4.4'],
- ['[2001:4860:4860::8888]'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('externalAddressesData')]
- public function testExternalHost(string $host): void {
- $this->config
- ->method('getSystemValueBool')
- ->with('allow_local_remote_servers', false)
- ->willReturn(false);
-
- $isValid = $this->validator->isValid($host);
-
- self::assertTrue($isValid);
- }
-}
--- a/tests/lib/Security/RemoteHostValidatorTest.php
+++ /dev/null
@@ -1,101 +0,0 @@
-config = $this->createMock(IConfig::class);
- $this->hostnameClassifier = $this->createMock(HostnameClassifier::class);
- $this->ipAddressClassifier = $this->createMock(IpAddressClassifier::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $this->validator = new RemoteHostValidator(
- $this->config,
- $this->hostnameClassifier,
- $this->ipAddressClassifier,
- $this->logger,
- );
- }
-
- public static function dataValid(): array {
- return [
- ['nextcloud.com', true],
- ['com.one-.nextcloud-one.com', false],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataValid')]
- public function testValid(string $host, bool $expected): void {
- $this->hostnameClassifier
- ->method('isLocalHostname')
- ->with($host)
- ->willReturn(false);
- $this->ipAddressClassifier
- ->method('isLocalAddress')
- ->with($host)
- ->willReturn(false);
-
- $valid = $this->validator->isValid($host);
-
- self::assertSame($expected, $valid);
- }
-
- public function testLocalHostname(): void {
- $host = 'localhost';
- $this->hostnameClassifier
- ->method('isLocalHostname')
- ->with($host)
- ->willReturn(true);
- $this->ipAddressClassifier
- ->method('isLocalAddress')
- ->with($host)
- ->willReturn(false);
-
- $valid = $this->validator->isValid($host);
-
- self::assertFalse($valid);
- }
-
- public function testLocalAddress(): void {
- $host = '10.0.0.10';
- $this->hostnameClassifier
- ->method('isLocalHostname')
- ->with($host)
- ->willReturn(false);
- $this->ipAddressClassifier
- ->method('isLocalAddress')
- ->with($host)
- ->willReturn(true);
-
- $valid = $this->validator->isValid($host);
-
- self::assertFalse($valid);
- }
-}
--- a/tests/lib/Security/SecureRandomTest.php
+++ /dev/null
@@ -1,85 +0,0 @@
-rng = new SecureRandom();
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('stringGenerationProvider')]
- public function testGetLowStrengthGeneratorLength($length, $expectedLength): void {
- $generator = $this->rng;
-
- $this->assertEquals($expectedLength, strlen($generator->generate($length)));
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('stringGenerationProvider')]
- public function testMediumLowStrengthGeneratorLength($length, $expectedLength): void {
- $generator = $this->rng;
-
- $this->assertEquals($expectedLength, strlen($generator->generate($length)));
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('stringGenerationProvider')]
- public function testUninitializedGenerate($length, $expectedLength): void {
- $this->assertEquals($expectedLength, strlen($this->rng->generate($length)));
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('charCombinations')]
- public function testScheme($charName, $chars): void {
- $generator = $this->rng;
- $scheme = constant('OCP\Security\ISecureRandom::' . $charName);
- $randomString = $generator->generate(100, $scheme);
- $matchesRegex = preg_match('/^' . $chars . '+$/', $randomString);
- $this->assertSame(1, $matchesRegex);
- }
-
- public static function invalidLengths(): array {
- return [
- [0],
- [-1],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('invalidLengths')]
- public function testInvalidLengths($length): void {
- $this->expectException(\LengthException::class);
- $generator = $this->rng;
- $generator->generate($length);
- }
-}
--- a/tests/lib/Security/Signature/Model/Rfc9421RoundTripTest.php
+++ /dev/null
@@ -1,554 +0,0 @@
-ecdsaP256Material('https://sender.example.org/ocm#ecdsa-p256-sha256');
- $signatoryManager = $this->makeSignatoryManager($signatory);
-
- $body = '{"hello":"world"}';
- $method = 'POST';
- $uri = 'https://receiver.example.org/ocm/shares';
-
- $out = new Rfc9421OutgoingSignedRequest($body, $signatoryManager, 'receiver.example.org', $method, $uri);
- $out->sign();
-
- $req = $this->mockRequestFromOutgoing($out, $method, '/ocm/shares', 'receiver.example.org');
- $in = new Rfc9421IncomingSignedRequest($body, $req);
- $in->setKey($jwk);
-
- $this->assertSame($out->getSignatureBaseString(), $in->getSignatureBaseString());
- // the Date header is deliberately not covered by the signature
- $this->assertSame(
- ['@method', '@target-uri', 'content-digest', 'content-length'],
- $in->getCoveredComponents(),
- );
- $in->verify(); // throws on failure
- $this->addToAssertionCount(1);
- }
-
- public function testEd25519VerifyAcceptedWhenSodiumLoaded(): void {
- $this->skipUnlessSodium();
- [$signatory, $jwk] = $this->ed25519Material('https://sender.example.org/ocm#ecdsa-p256-sha256');
- $signatoryManager = $this->makeSignatoryManagerWithSigningAlgorithm($signatory, 'ed25519');
-
- $body = '{"hello":"world"}';
- $out = new Rfc9421OutgoingSignedRequest($body, $signatoryManager, 'receiver.example.org', 'POST', 'https://receiver.example.org/ocm/shares');
- // Ed25519 sign() throws via Algorithm::sign; produce the signature directly
- // over a manually reconstructed signature base.
- $headers = $out->getHeaders();
- $paramsLine = '("@method" "@target-uri" "content-digest" "content-length");created=' . time() . ';keyid="' . $signatory->getKeyId() . '";tag="ocm"';
- $base = implode("\n", [
- '"@method": POST',
- '"@target-uri": https://receiver.example.org/ocm/shares',
- '"content-digest": ' . $headers['Content-Digest'],
- '"content-length": ' . $headers['Content-Length'],
- '"@signature-params": ' . $paramsLine,
- ]);
- $rawSig = sodium_crypto_sign_detached($base, $signatory->getPrivateKey());
- $headers['Signature-Input'] = 'ocm=' . $paramsLine;
- $headers['Signature'] = 'ocm=:' . base64_encode($rawSig) . ':';
-
- $req = $this->mockRequest($headers, 'POST', '/ocm/shares', 'receiver.example.org');
- $in = new Rfc9421IncomingSignedRequest($body, $req);
- $in->setKey($jwk);
- $in->verify();
- $this->addToAssertionCount(1);
- }
-
- public function testTamperedBodyRejected(): void {
- [$signatory] = $this->ecdsaP256Material('https://sender.example.org/ocm#ecdsa-p256-sha256');
- $signatoryManager = $this->makeSignatoryManager($signatory);
-
- $body = 'original';
- $out = new Rfc9421OutgoingSignedRequest($body, $signatoryManager, 'receiver.example.org', 'POST', 'https://receiver.example.org/ocm/shares');
- $out->sign();
-
- $req = $this->mockRequestFromOutgoing($out, 'POST', '/ocm/shares', 'receiver.example.org');
- $this->expectException(IncomingRequestException::class);
- new Rfc9421IncomingSignedRequest('tampered', $req);
- }
-
- public function testTamperedSignatureRejected(): void {
- [$signatory, $jwk] = $this->ecdsaP256Material('https://sender.example.org/ocm#ecdsa-p256-sha256');
- $signatoryManager = $this->makeSignatoryManager($signatory);
-
- $body = 'msg';
- $out = new Rfc9421OutgoingSignedRequest($body, $signatoryManager, 'receiver.example.org', 'POST', 'https://receiver.example.org/ocm/shares');
- $out->sign();
-
- $headers = $out->getHeaders();
- // Replace the inner base64 of the signature with a different valid base64.
- $headers['Signature'] = preg_replace('/=:[^:]+:/', '=:' . base64_encode(random_bytes(64)) . ':', (string)$headers['Signature']);
-
- $req = $this->mockRequest($headers, 'POST', '/ocm/shares', 'receiver.example.org');
- $in = new Rfc9421IncomingSignedRequest($body, $req);
- $in->setKey($jwk);
-
- $this->expectException(InvalidSignatureException::class);
- $in->verify();
- }
-
- public function testOutgoingCarriesOcmTag(): void {
- [$signatory] = $this->ecdsaP256Material('https://sender.example.org/ocm#ecdsa-p256-sha256');
- $signatoryManager = $this->makeSignatoryManager($signatory);
-
- $out = new Rfc9421OutgoingSignedRequest('msg', $signatoryManager, 'receiver.example.org', 'POST', 'https://receiver.example.org/ocm/shares');
- $out->sign();
-
- $headers = $out->getHeaders();
- // the label is cosmetic; the integrity-protected tag parameter is
- // what marks the signature as the OCM one
- $this->assertStringStartsWith('ocm=(', (string)$headers['Signature-Input']);
- $this->assertStringContainsString(';tag="ocm"', (string)$headers['Signature-Input']);
- $this->assertStringStartsWith('ocm=:', (string)$headers['Signature']);
- }
-
- public function testArbitraryLabelWithOcmTagVerifies(): void {
- [$signatory, $jwk] = $this->ecdsaP256Material('https://sender.example.org/ocm#ecdsa-p256-sha256');
- $signatoryManager = $this->makeSignatoryManager($signatory);
-
- $out = new Rfc9421OutgoingSignedRequest('msg', $signatoryManager, 'receiver.example.org', 'POST', 'https://receiver.example.org/ocm/shares');
- $out->sign();
-
- // Rename the dictionary label; the verifier MUST select by the
- // tag="ocm" parameter and disregard labels.
- $headers = $out->getHeaders();
- $headers['Signature-Input'] = preg_replace('/^ocm=/', 'sig1=', (string)$headers['Signature-Input']);
- $headers['Signature'] = preg_replace('/^ocm=/', 'sig1=', (string)$headers['Signature']);
-
- $req = $this->mockRequest($headers, 'POST', '/ocm/shares', 'receiver.example.org');
- $in = new Rfc9421IncomingSignedRequest('msg', $req);
- $in->setKey($jwk);
- $in->verify();
- $this->addToAssertionCount(1);
- }
-
- public function testRequestWithoutOcmTagTreatedAsUnsigned(): void {
- [$signatory] = $this->ecdsaP256Material('https://sender.example.org/ocm#ecdsa-p256-sha256');
- $signatoryManager = $this->makeSignatoryManager($signatory);
-
- $out = new Rfc9421OutgoingSignedRequest('msg', $signatoryManager, 'receiver.example.org', 'POST', 'https://receiver.example.org/ocm/shares');
- $out->sign();
-
- // Strip the tag parameter; without tag="ocm" the request carries no
- // OCM signature and is handled as unsigned.
- $headers = $out->getHeaders();
- $headers['Signature-Input'] = str_replace(';tag="ocm"', '', (string)$headers['Signature-Input']);
-
- $req = $this->mockRequest($headers, 'POST', '/ocm/shares', 'receiver.example.org');
- $this->expectException(SignatureNotFoundException::class);
- new Rfc9421IncomingSignedRequest('msg', $req);
- }
-
- public function testTwoSignaturesCarryingOcmTagRejected(): void {
- [$signatory] = $this->ecdsaP256Material('https://sender.example.org/ocm#ecdsa-p256-sha256');
- $signatoryManager = $this->makeSignatoryManager($signatory);
-
- $out = new Rfc9421OutgoingSignedRequest('msg', $signatoryManager, 'receiver.example.org', 'POST', 'https://receiver.example.org/ocm/shares');
- $out->sign();
-
- // A second, differently-labeled signature also carrying tag="ocm":
- // the entire message MUST be rejected.
- $headers = $out->getHeaders();
- $headers['Signature-Input'] = (string)$headers['Signature-Input'] . ', ' . preg_replace('/^ocm=/', 'sig2=', (string)$headers['Signature-Input']);
- $headers['Signature'] = (string)$headers['Signature'] . ', ' . preg_replace('/^ocm=/', 'sig2=', (string)$headers['Signature']);
-
- $req = $this->mockRequest($headers, 'POST', '/ocm/shares', 'receiver.example.org');
- $this->expectException(IncomingRequestException::class);
- new Rfc9421IncomingSignedRequest('msg', $req);
- }
-
- public function testDuplicateOcmLabelRejected(): void {
- // RFC 8941 §4.2 last-wins on duplicate dictionary keys, which would
- // silently hide one of two identically-labeled OCM signatures; that
- // ambiguity on the selected entry causes outright rejection.
- [$signatory] = $this->ecdsaP256Material('https://sender.example.org/ocm#ecdsa-p256-sha256');
- $signatoryManager = $this->makeSignatoryManager($signatory);
-
- $out = new Rfc9421OutgoingSignedRequest('msg', $signatoryManager, 'receiver.example.org', 'POST', 'https://receiver.example.org/ocm/shares');
- $out->sign();
-
- $headers = $out->getHeaders();
- $headers['Signature-Input'] = (string)$headers['Signature-Input'] . ', ' . (string)$headers['Signature-Input'];
- $headers['Signature'] = (string)$headers['Signature'] . ', ' . (string)$headers['Signature'];
-
- $req = $this->mockRequest($headers, 'POST', '/ocm/shares', 'receiver.example.org');
- $this->expectException(IncomingRequestException::class);
- new Rfc9421IncomingSignedRequest('msg', $req);
- }
-
- public function testForeignSiblingLabelIgnored(): void {
- [$signatory, $jwk] = $this->ecdsaP256Material('https://sender.example.org/ocm#ecdsa-p256-sha256');
- $signatoryManager = $this->makeSignatoryManager($signatory);
-
- $out = new Rfc9421OutgoingSignedRequest('msg', $signatoryManager, 'receiver.example.org', 'POST', 'https://receiver.example.org/ocm/shares');
- $out->sign();
-
- // Splice in a sibling proxy_sig1 entry; the verifier must ignore it
- // and still verify the ocm-labeled signature successfully.
- $headers = $out->getHeaders();
- $proxyParams = '("@method");created=1;keyid="proxy"';
- $proxySig = base64_encode(random_bytes(64));
- $headers['Signature-Input'] = (string)$headers['Signature-Input'] . ', proxy_sig1=' . $proxyParams;
- $headers['Signature'] = (string)$headers['Signature'] . ', proxy_sig1=:' . $proxySig . ':';
-
- $req = $this->mockRequest($headers, 'POST', '/ocm/shares', 'receiver.example.org');
- $in = new Rfc9421IncomingSignedRequest('msg', $req);
- $in->setKey($jwk);
- $in->verify();
- $this->addToAssertionCount(1);
- }
-
- public function testTooOldSignatureRejected(): void {
- [$signatory] = $this->ecdsaP256Material('https://sender.example.org/ocm#ecdsa-p256-sha256');
- $signatoryManager = $this->makeSignatoryManager($signatory);
-
- $body = 'msg';
- $out = new Rfc9421OutgoingSignedRequest($body, $signatoryManager, 'receiver.example.org', 'POST', 'https://receiver.example.org/ocm/shares');
- $out->sign();
-
- // Backdate `created` in Signature-Input by 10 minutes.
- $headers = $out->getHeaders();
- $pastCreated = time() - 600;
- $headers['Signature-Input'] = preg_replace('/created=\d+/', 'created=' . $pastCreated, (string)$headers['Signature-Input']);
-
- $req = $this->mockRequest($headers, 'POST', '/ocm/shares', 'receiver.example.org');
- $this->expectException(IncomingRequestException::class);
- new Rfc9421IncomingSignedRequest($body, $req, ['ttl' => 300]);
- }
-
- public function testFutureCreatedRejected(): void {
- [$signatory] = $this->ecdsaP256Material('https://sender.example.org/ocm#ecdsa-p256-sha256');
- $signatoryManager = $this->makeSignatoryManager($signatory);
-
- $body = 'msg';
- $out = new Rfc9421OutgoingSignedRequest($body, $signatoryManager, 'receiver.example.org', 'POST', 'https://receiver.example.org/ocm/shares');
- $out->sign();
-
- // Push `created` 10 minutes into the future, well past the
- // 60-second skew tolerance.
- $headers = $out->getHeaders();
- $futureCreated = time() + 600;
- $headers['Signature-Input'] = preg_replace('/created=\d+/', 'created=' . $futureCreated, (string)$headers['Signature-Input']);
-
- $req = $this->mockRequest($headers, 'POST', '/ocm/shares', 'receiver.example.org');
- $this->expectException(IncomingRequestException::class);
- new Rfc9421IncomingSignedRequest($body, $req);
- }
-
- public function testMissingCreatedRejected(): void {
- [$signatory] = $this->ecdsaP256Material('https://sender.example.org/ocm#ecdsa-p256-sha256');
- $signatoryManager = $this->makeSignatoryManager($signatory);
-
- $body = 'msg';
- $out = new Rfc9421OutgoingSignedRequest($body, $signatoryManager, 'receiver.example.org', 'POST', 'https://receiver.example.org/ocm/shares');
- $out->sign();
-
- // Strip the `;created=...` parameter so the signature loses its
- // freshness anchor.
- $headers = $out->getHeaders();
- $headers['Signature-Input'] = preg_replace('/;created=\d+/', '', (string)$headers['Signature-Input']);
-
- $req = $this->mockRequest($headers, 'POST', '/ocm/shares', 'receiver.example.org');
- $this->expectException(IncomingRequestException::class);
- new Rfc9421IncomingSignedRequest($body, $req);
- }
-
- public function testMissingKeyidRejected(): void {
- [$signatory] = $this->ecdsaP256Material('https://sender.example.org/ocm#ecdsa-p256-sha256');
- $signatoryManager = $this->makeSignatoryManager($signatory);
-
- $body = 'msg';
- $out = new Rfc9421OutgoingSignedRequest($body, $signatoryManager, 'receiver.example.org', 'POST', 'https://receiver.example.org/ocm/shares');
- $out->sign();
-
- // Strip the `;keyid="..."` parameter; verifiers MUST reject
- // signatures without it.
- $headers = $out->getHeaders();
- $headers['Signature-Input'] = preg_replace('/;keyid="[^"]*"/', '', (string)$headers['Signature-Input']);
-
- $req = $this->mockRequest($headers, 'POST', '/ocm/shares', 'receiver.example.org');
- $this->expectException(IncomingRequestException::class);
- new Rfc9421IncomingSignedRequest($body, $req);
- }
-
- public function testExtraCoveredDateStillVerifies(): void {
- // covering more than the mandatory components (here: `date`) is
- // allowed; only the four baseline components are required
- [$signatory, $jwk] = $this->ecdsaP256Material('https://sender.example.org/ocm#ecdsa-p256-sha256');
- $signatoryManager = $this->makeSignatoryManagerWithComponents(
- $signatory,
- ['@method', '@target-uri', 'content-digest', 'content-length', 'date'],
- );
-
- $body = 'msg';
- $out = new Rfc9421OutgoingSignedRequest($body, $signatoryManager, 'receiver.example.org', 'POST', 'https://receiver.example.org/ocm/shares');
- $out->sign();
-
- $req = $this->mockRequestFromOutgoing($out, 'POST', '/ocm/shares', 'receiver.example.org');
- $in = new Rfc9421IncomingSignedRequest($body, $req);
- $in->setKey($jwk);
- $in->verify();
- $this->addToAssertionCount(1);
- }
-
- public function testSignatureNotCoveringRequiredComponentsRejected(): void {
- // A peer that signs only `@method` and `@target-uri`: the body and
- // freshness window aren't bound. Even with a valid signature we
- // must refuse it.
- [$signatory] = $this->ecdsaP256Material('https://sender.example.org/ocm#ecdsa-p256-sha256');
- $signatoryManager = $this->makeSignatoryManagerWithComponents(
- $signatory,
- ['@method', '@target-uri'],
- );
-
- $body = 'msg';
- $out = new Rfc9421OutgoingSignedRequest($body, $signatoryManager, 'receiver.example.org', 'POST', 'https://receiver.example.org/ocm/shares');
- $out->sign();
- $req = $this->mockRequest($out->getHeaders(), 'POST', '/ocm/shares', 'receiver.example.org');
-
- $this->expectException(IncomingRequestException::class);
- new Rfc9421IncomingSignedRequest($body, $req);
- }
-
- public function testKeyIdIsOpaqueAndOriginIsExternal(): void {
- // keyid is opaque; the origin is supplied by the caller via setOrigin().
- $kid = 'sender.example.org#key1';
- [$privatePem, $jwk] = $this->ecdsaP256Jwk($kid);
-
- $body = 'msg';
- $digest = ContentDigest::compute($body, ContentDigest::ALGO_SHA256);
- $paramsLine = '("@method" "@target-uri" "content-digest" "content-length");created=' . time() . ';keyid="' . $kid . '";tag="ocm"';
- $base = implode("\n", [
- '"@method": POST',
- '"@target-uri": https://receiver.example.org/ocm/shares',
- '"content-digest": ' . $digest,
- '"content-length": ' . strlen($body),
- '"@signature-params": ' . $paramsLine,
- ]);
- $rawSig = Algorithm::sign($base, $privatePem, 'ecdsa-p256-sha256');
- $headers = [
- 'Content-Digest' => $digest,
- 'Content-Length' => (string)strlen($body),
- 'Signature-Input' => 'sig1=' . $paramsLine,
- 'Signature' => 'sig1=:' . base64_encode($rawSig) . ':',
- ];
-
- $req = $this->mockRequest($headers, 'POST', '/ocm/shares', 'receiver.example.org');
- $in = new Rfc9421IncomingSignedRequest($body, $req);
- // The keyid is not parsed; the origin comes from the caller.
- $in->setOrigin('sender.example.org');
- $this->assertSame('sender.example.org', $in->getOrigin());
- $in->setKey($jwk);
- $in->verify();
- $this->addToAssertionCount(1);
- }
-
- private function skipUnlessSodium(): void {
- if (!extension_loaded('sodium')) {
- $this->markTestSkipped('ext-sodium is not loaded');
- }
- }
-
- private function makeSignatoryManagerWithComponents(Signatory $signatory, array $components): ISignatoryManager {
- return new class($signatory, $components) implements ISignatoryManager {
- public function __construct(
- private Signatory $sig,
- private array $components,
- ) {
- }
-
- public function getProviderId(): string {
- return 'test';
- }
-
- public function getOptions(): array {
- return [
- 'algorithm' => SignatureAlgorithm::RSA_SHA256,
- 'digestAlgorithm' => DigestAlgorithm::SHA256,
- 'rfc9421.coveredComponents' => $this->components,
- ];
- }
-
- public function getLocalSignatory(): Signatory {
- return $this->sig;
- }
-
- public function getRemoteSignatory(string $remote): ?Signatory {
- return null;
- }
- };
- }
-
- private function makeSignatoryManagerWithSigningAlgorithm(Signatory $signatory, string $signingAlgorithm): ISignatoryManager {
- return new class($signatory, $signingAlgorithm) implements ISignatoryManager {
- public function __construct(
- private Signatory $sig,
- private string $signingAlgorithm,
- ) {
- }
-
- public function getProviderId(): string {
- return 'test';
- }
-
- public function getOptions(): array {
- return [
- 'algorithm' => SignatureAlgorithm::RSA_SHA256,
- 'digestAlgorithm' => DigestAlgorithm::SHA256,
- 'rfc9421.signingAlgorithm' => $this->signingAlgorithm,
- ];
- }
-
- public function getLocalSignatory(): Signatory {
- return $this->sig;
- }
-
- public function getRemoteSignatory(string $remote): ?Signatory {
- return null;
- }
- };
- }
-
- /**
- * @return array{0: Signatory, 1: \Firebase\JWT\Key}
- */
- private function ecdsaP256Material(string $kid): array {
- $pkey = openssl_pkey_new(['private_key_type' => OPENSSL_KEYTYPE_EC, 'curve_name' => 'prime256v1']);
- $privatePem = '';
- openssl_pkey_export($pkey, $privatePem);
- $details = openssl_pkey_get_details($pkey);
- $publicPem = $details['key'];
-
- $signatory = new Signatory(true);
- $signatory->setKeyId($kid);
- $signatory->setPublicKey($publicPem);
- $signatory->setPrivateKey($privatePem);
-
- $key = self::jwkFromEcDetails($details, $kid);
- return [$signatory, $key];
- }
-
- /**
- * Key material for a peer whose kid is not a URL; Nextcloud's Signatory
- * model cannot represent those.
- *
- * @return array{0: string, 1: \Firebase\JWT\Key} [private key PEM, verification key]
- */
- private function ecdsaP256Jwk(string $kid): array {
- $pkey = openssl_pkey_new(['private_key_type' => OPENSSL_KEYTYPE_EC, 'curve_name' => 'prime256v1']);
- $privatePem = '';
- openssl_pkey_export($pkey, $privatePem);
- $details = openssl_pkey_get_details($pkey);
-
- return [$privatePem, self::jwkFromEcDetails($details, $kid)];
- }
-
- private static function jwkFromEcDetails(array $details, string $kid): \Firebase\JWT\Key {
- $x = str_pad($details['ec']['x'], 32, "\x00", STR_PAD_LEFT);
- $y = str_pad($details['ec']['y'], 32, "\x00", STR_PAD_LEFT);
- return JWK::parseKey([
- 'kty' => 'EC',
- 'crv' => 'P-256',
- 'kid' => $kid,
- 'alg' => 'ES256',
- 'x' => self::b64url($x),
- 'y' => self::b64url($y),
- ], 'ES256');
- }
-
- /**
- * @return array{0: Signatory, 1: \Firebase\JWT\Key}
- */
- private function ed25519Material(string $kid): array {
- $keypair = sodium_crypto_sign_keypair();
- $publicKey = sodium_crypto_sign_publickey($keypair);
- $secretKey = sodium_crypto_sign_secretkey($keypair);
- $signatory = new Signatory(true);
- $signatory->setKeyId($kid);
- $signatory->setPublicKey($publicKey);
- $signatory->setPrivateKey($secretKey);
- $key = JWK::parseKey([
- 'kty' => 'OKP',
- 'crv' => 'Ed25519',
- 'kid' => $kid,
- 'alg' => 'EdDSA',
- 'x' => self::b64url($publicKey),
- ], 'EdDSA');
- return [$signatory, $key];
- }
-
- private static function b64url(string $bin): string {
- return rtrim(strtr(base64_encode($bin), '+/', '-_'), '=');
- }
-
- private function makeSignatoryManager(Signatory $signatory): ISignatoryManager {
- return new class($signatory) implements ISignatoryManager {
- public function __construct(
- private Signatory $sig,
- ) {
- }
-
- public function getProviderId(): string {
- return 'test';
- }
-
- public function getOptions(): array {
- return [
- 'algorithm' => SignatureAlgorithm::RSA_SHA256,
- 'digestAlgorithm' => DigestAlgorithm::SHA256,
- ];
- }
-
- public function getLocalSignatory(): Signatory {
- return $this->sig;
- }
-
- public function getRemoteSignatory(string $remote): ?Signatory {
- return null;
- }
- };
- }
-
- private function mockRequestFromOutgoing(Rfc9421OutgoingSignedRequest $out, string $method, string $path, string $host): IRequest {
- return $this->mockRequest($out->getHeaders(), $method, $path, $host);
- }
-
- private function mockRequest(array $headers, string $method, string $path, string $host): IRequest {
- $lowered = [];
- foreach ($headers as $name => $value) {
- $lowered[strtolower($name)] = (string)$value;
- }
- $mock = $this->createMock(IRequest::class);
- $mock->method('getHeader')->willReturnCallback(static fn (string $h) => $lowered[strtolower($h)] ?? '');
- $mock->method('getMethod')->willReturn($method);
- $mock->method('getRequestUri')->willReturn($path);
- $mock->method('getServerProtocol')->willReturn('https');
- $mock->method('getServerHost')->willReturn($host);
- return $mock;
- }
-}
--- a/tests/lib/Security/Signature/Rfc9421/AlgorithmTest.php
+++ /dev/null
@@ -1,218 +0,0 @@
-assertSame('ed25519', Algorithm::normalize('ed25519'));
- $this->assertSame('rsa-v1_5-sha256', Algorithm::normalize('rsa-v1_5-sha256'));
- $this->assertSame('rsa-v1_5-sha384', Algorithm::normalize('rsa-v1_5-sha384'));
- $this->assertSame('rsa-v1_5-sha512', Algorithm::normalize('rsa-v1_5-sha512'));
- $this->assertSame('ecdsa-p256-sha256', Algorithm::normalize('ecdsa-p256-sha256'));
- $this->assertSame('ecdsa-p384-sha384', Algorithm::normalize('ecdsa-p384-sha384'));
- }
-
- public function testNormalizeJoseAliases(): void {
- $this->assertSame('ed25519', Algorithm::normalize('EdDSA'));
- // fully-specified RFC 9864 name, recommended by the OCM spec
- $this->assertSame('ed25519', Algorithm::normalize('Ed25519'));
- $this->assertSame('ecdsa-p256-sha256', Algorithm::normalize('ES256'));
- $this->assertSame('ecdsa-p384-sha384', Algorithm::normalize('ES384'));
- $this->assertSame('rsa-v1_5-sha256', Algorithm::normalize('RS256'));
- }
-
- public function testNormalizeRejectsUnknown(): void {
- $this->expectException(SignatureException::class);
- Algorithm::normalize('totally-not-real');
- }
-
- public function testNormalizeRejectsRsaPss(): void {
- $this->expectException(SignatureException::class);
- Algorithm::normalize('rsa-pss-sha512');
- }
-
- public function testNormalizeRejectsJosePsAlias(): void {
- $this->expectException(SignatureException::class);
- Algorithm::normalize('PS512');
- }
-
- public function testDeriveJoseAlgFromJwk(): void {
- $this->assertSame('EdDSA', Algorithm::deriveJoseAlgFromJwk(['kty' => 'OKP', 'crv' => 'Ed25519']));
- $this->assertSame('ES256', Algorithm::deriveJoseAlgFromJwk(['kty' => 'EC', 'crv' => 'P-256']));
- $this->assertSame('ES384', Algorithm::deriveJoseAlgFromJwk(['kty' => 'EC', 'crv' => 'P-384']));
- // RSA: hash function isn't determined by key shape.
- $this->assertNull(Algorithm::deriveJoseAlgFromJwk(['kty' => 'RSA']));
- $this->assertNull(Algorithm::deriveJoseAlgFromJwk([]));
- }
-
- public function testEd25519SigningIsRejected(): void {
- $this->expectException(SignatureException::class);
- $this->expectExceptionMessageMatches('/Ed25519 signing is not supported/');
- Algorithm::sign('payload', str_repeat("\x00", 64), 'ed25519');
- }
-
- public function testEd25519VerifyRoundTripWithSodium(): void {
- $this->skipUnlessSodium();
- [$secret, $key] = $this->ed25519KeyPair();
- $base = 'arbitrary signature base';
- $sig = sodium_crypto_sign_detached($base, $secret);
- $this->assertSame(64, strlen($sig));
- $this->assertTrue(Algorithm::verify($base, $sig, $key, 'ed25519'));
- // JOSE alias accepted.
- $this->assertTrue(Algorithm::verify($base, $sig, $key, 'EdDSA'));
- // alg-omitted path resolves through Key alg.
- $this->assertTrue(Algorithm::verify($base, $sig, $key, null));
- // tamper detection
- $this->assertFalse(Algorithm::verify($base . 'x', $sig, $key, 'ed25519'));
- }
-
- public function testRsaPkcs1RoundTrip(): void {
- [$priv, $key] = $this->rsaKeyPair();
- $sig = Algorithm::sign('payload', $priv, 'rsa-v1_5-sha256');
- $this->assertSame(256, strlen($sig));
- $this->assertTrue(Algorithm::verify('payload', $sig, $key, 'rsa-v1_5-sha256'));
- $this->assertTrue(Algorithm::verify('payload', $sig, $key, 'RS256'));
- }
-
- public function testEcdsaP256RoundTrip(): void {
- [$priv, $key] = $this->ecKeyPair('prime256v1', 'P-256', 'ES256');
- $sig = Algorithm::sign('payload', $priv, 'ecdsa-p256-sha256');
- $this->assertSame(64, strlen($sig));
- $this->assertTrue(Algorithm::verify('payload', $sig, $key, 'ecdsa-p256-sha256'));
- $this->assertTrue(Algorithm::verify('payload', $sig, $key, 'ES256'));
- }
-
- public function testEcdsaP384RoundTrip(): void {
- [$priv, $key] = $this->ecKeyPair('secp384r1', 'P-384', 'ES384');
- $sig = Algorithm::sign('payload', $priv, 'ecdsa-p384-sha384');
- $this->assertSame(96, strlen($sig));
- $this->assertTrue(Algorithm::verify('payload', $sig, $key, 'ecdsa-p384-sha384'));
- }
-
- public function testKeyTypeMismatchFailsClosed(): void {
- [, $rsaKey] = $this->rsaKeyPair();
- $this->expectException(SignatureException::class);
- Algorithm::verify('payload', random_bytes(64), $rsaKey, 'ed25519');
- }
-
- public function testAlgHintConflictsWithJwkAlgRejected(): void {
- $this->skipUnlessSodium();
- // Ed25519 JWK, request claims ES256: RFC 9421 §3.2 step 6 disagreement.
- [, $key] = $this->ed25519KeyPair();
- $this->expectException(SignatureException::class);
- Algorithm::verify('payload', random_bytes(64), $key, 'ES256');
- }
-
- public function testParseKeyRejectsContradictoryAlg(): void {
- $this->markTestSkipped(
- 'firebase/php-jwt JWK::parseKey does not validate kty/crv/alg coherence; '
- . 'OCMSignatoryManager::findKid() rejects such keys before parsing '
- . '(see OCMSignatoryManagerJwksTest::testGetRemoteKeyRejectsJwkAlgMismatchingKeyType) '
- . 'and a remaining mismatch is caught at verify() time.'
- );
- }
-
- public function testEcdsaRawToDerProducesValidSignature(): void {
- [$priv, $key] = $this->ecKeyPair('prime256v1', 'P-256', 'ES256');
- $rawSig = Algorithm::sign('msg', $priv, 'ecdsa-p256-sha256');
- $der = Algorithm::ecdsaRawToDer($rawSig, 32);
- $this->assertNotNull($der);
- $this->assertTrue(Algorithm::verify('msg', $rawSig, $key, 'ecdsa-p256-sha256'));
- }
-
- public function testEcdsaRawToDerWrongLength(): void {
- $this->assertNull(Algorithm::ecdsaRawToDer('short', 32));
- }
-
- private function skipUnlessSodium(): void {
- if (!extension_loaded('sodium')) {
- $this->markTestSkipped('ext-sodium is not loaded');
- }
- }
-
- /**
- * @return array{0: string, 1: Key}
- */
- private function ed25519KeyPair(): array {
- $keypair = sodium_crypto_sign_keypair();
- $publicKey = sodium_crypto_sign_publickey($keypair);
- $secretKey = sodium_crypto_sign_secretkey($keypair);
- $key = JWK::parseKey([
- 'kty' => 'OKP',
- 'crv' => 'Ed25519',
- 'kid' => 'k',
- 'alg' => 'EdDSA',
- 'x' => self::b64url($publicKey),
- ], 'EdDSA');
- return [$secretKey, $key];
- }
-
- /**
- * @return array{0: string, 1: Key}
- */
- private function rsaKeyPair(): array {
- $pkey = openssl_pkey_new(['private_key_type' => OPENSSL_KEYTYPE_RSA, 'private_key_bits' => 2048]);
- $priv = '';
- openssl_pkey_export($pkey, $priv);
- $details = openssl_pkey_get_details($pkey);
- $key = JWK::parseKey([
- 'kty' => 'RSA',
- 'kid' => 'k',
- 'alg' => 'RS256',
- 'n' => self::b64url($details['rsa']['n']),
- 'e' => self::b64url($details['rsa']['e']),
- ], 'RS256');
- return [$priv, $key];
- }
-
- /**
- * @return array{0: string, 1: Key}
- */
- private function ecKeyPair(string $opensslCurve, string $jwkCurve, string $joseAlg): array {
- $pkey = openssl_pkey_new(['private_key_type' => OPENSSL_KEYTYPE_EC, 'curve_name' => $opensslCurve]);
- $priv = '';
- openssl_pkey_export($pkey, $priv);
- $details = openssl_pkey_get_details($pkey);
- $coordinateSize = $opensslCurve === 'prime256v1' ? 32 : 48;
- [$x, $y] = self::zeroPadCoordinates($details['ec']['x'], $details['ec']['y'], $coordinateSize);
- $key = JWK::parseKey([
- 'kty' => 'EC',
- 'crv' => $jwkCurve,
- 'kid' => 'k',
- 'alg' => $joseAlg,
- 'x' => self::b64url($x),
- 'y' => self::b64url($y),
- ], $joseAlg);
- return [$priv, $key];
- }
-
- /**
- * openssl strips leading zero bytes from EC coordinates; JWK requires them
- * zero-padded to the full field size (RFC 7518 §6.2.1.2).
- *
- * @return array{0: string, 1: string}
- */
- private static function zeroPadCoordinates(string $x, string $y, int $coordinateSize): array {
- return [
- str_pad($x, $coordinateSize, "\x00", STR_PAD_LEFT),
- str_pad($y, $coordinateSize, "\x00", STR_PAD_LEFT),
- ];
- }
-
- private static function b64url(string $bin): string {
- return rtrim(strtr(base64_encode($bin), '+/', '-_'), '=');
- }
-}
--- a/tests/lib/Security/Signature/Rfc9421/ContentDigestTest.php
+++ /dev/null
@@ -1,76 +0,0 @@
-assertStringStartsWith('sha-256=:', $header);
- $this->assertStringEndsWith(':', $header);
- $this->assertTrue(ContentDigest::verify($header, $body));
- }
-
- public function testDifferentBodyFails(): void {
- $header = ContentDigest::compute('hello', ContentDigest::ALGO_SHA256);
- $this->assertFalse(ContentDigest::verify($header, 'goodbye'));
- }
-
- public function testSha512(): void {
- $header = ContentDigest::compute('payload', ContentDigest::ALGO_SHA512);
- $this->assertStringStartsWith('sha-512=:', $header);
- $this->assertTrue(ContentDigest::verify($header, 'payload'));
- }
-
- public function testParseMultipleAlgorithmsAcceptsAnyMatch(): void {
- $body = 'data';
- $sha256 = ContentDigest::compute($body, ContentDigest::ALGO_SHA256);
- $sha512 = ContentDigest::compute($body, ContentDigest::ALGO_SHA512);
- $header = $sha256 . ', ' . $sha512;
- $this->assertTrue(ContentDigest::verify($header, $body));
- }
-
- public function testFailsIfAnyRecognisedAlgorithmMismatches(): void {
- // All recognised digests must agree. A correct sha-256 alongside a
- // wrong sha-512 is treated as an attack on the weaker algorithm,
- // not as a successful match on the stronger one.
- $body = 'data';
- $sha256 = ContentDigest::compute($body, ContentDigest::ALGO_SHA256);
- $wrongSha512 = 'sha-512=:' . base64_encode(hash('sha512', 'tampered', true)) . ':';
- $this->assertFalse(ContentDigest::verify($sha256 . ', ' . $wrongSha512, $body));
- // And the inverse ordering.
- $this->assertFalse(ContentDigest::verify($wrongSha512 . ', ' . $sha256, $body));
- }
-
- public function testUnknownAlgorithmIsIgnored(): void {
- $body = 'data';
- $sha256 = ContentDigest::compute($body, ContentDigest::ALGO_SHA256);
- $header = 'md5=:abcd:, ' . $sha256;
- $this->assertTrue(ContentDigest::verify($header, $body));
- }
-
- public function testEmptyHeaderFails(): void {
- $this->assertFalse(ContentDigest::verify('', 'body'));
- }
-
- public function testGarbageHeaderFails(): void {
- $this->assertFalse(ContentDigest::verify('not a digest', 'body'));
- }
-
- public function testParseExtractsRawBytes(): void {
- $header = ContentDigest::compute('abc', ContentDigest::ALGO_SHA256);
- $parsed = ContentDigest::parse($header);
- $this->assertArrayHasKey('sha-256', $parsed);
- $this->assertSame(hash('sha256', 'abc', true), $parsed['sha-256']);
- }
-}
--- a/tests/lib/Security/Signature/Rfc9421/SignatureBaseTest.php
+++ /dev/null
@@ -1,85 +0,0 @@
- 'sha-256=:abcd:',
- 'date' => 'Mon, 04 May 2026 12:00:00 GMT',
- ],
- components: ['@method', '@target-uri', 'content-digest', 'date'],
- signatureParamsLine: '("@method" "@target-uri" "content-digest" "date");created=1;keyid="k"',
- );
-
- $expected = '"@method": POST' . "\n"
- . '"@target-uri": https://example.org/foo?bar=baz' . "\n"
- . '"content-digest": sha-256=:abcd:' . "\n"
- . '"date": Mon, 04 May 2026 12:00:00 GMT' . "\n"
- . '"@signature-params": ("@method" "@target-uri" "content-digest" "date");created=1;keyid="k"';
- $this->assertSame($expected, $base);
- }
-
- public function testAuthorityStripsDefaultPort(): void {
- $base = SignatureBase::build('GET', 'https://EXAMPLE.org:443/x', [], ['@authority'], '()');
- $this->assertStringContainsString('"@authority": example.org' . "\n", $base);
- }
-
- public function testAuthorityKeepsCustomPort(): void {
- $base = SignatureBase::build('GET', 'https://example.org:8443/x', [], ['@authority'], '()');
- $this->assertStringContainsString('"@authority": example.org:8443' . "\n", $base);
- }
-
- public function testQueryComponent(): void {
- $base = SignatureBase::build('GET', 'https://example.org/x?a=1', [], ['@query'], '()');
- $this->assertStringContainsString('"@query": ?a=1' . "\n", $base);
- }
-
- public function testMissingFieldThrows(): void {
- $this->expectException(SignatureException::class);
- SignatureBase::build('GET', 'https://example.org/', [], ['x-missing'], '()');
- }
-
- public function testFieldValueIsTrimmed(): void {
- $base = SignatureBase::build(
- 'GET',
- 'https://example.org/',
- ['date' => ' Mon, 04 May 2026 12:00:00 GMT '],
- ['date'],
- '()'
- );
- $this->assertStringContainsString('"date": Mon, 04 May 2026 12:00:00 GMT' . "\n", $base);
- }
-
- public function testSerializeSignatureParams(): void {
- $line = SignatureBase::serializeSignatureParams(
- ['@method', '@target-uri'],
- ['created' => 100, 'keyid' => 'kid', 'expires' => 200],
- );
- $this->assertSame('("@method" "@target-uri");created=100;keyid="kid";expires=200', $line);
- }
-
- public function testSerializeBareItemEscapesQuotes(): void {
- $this->assertSame('"\\"hi\\""', SignatureBase::serializeBareItem('"hi"'));
- $this->assertSame('"\\\\"', SignatureBase::serializeBareItem('\\'));
- }
-
- public function testSerializeBareItemBoolean(): void {
- $this->assertSame('?1', SignatureBase::serializeBareItem(true));
- $this->assertSame('?0', SignatureBase::serializeBareItem(false));
- }
-}
--- a/tests/lib/Security/Signature/SignatureManagerDispatchTest.php
+++ /dev/null
@@ -1,288 +0,0 @@
-request = $this->createMock(IRequest::class);
- $this->mapper = $this->createMock(SignatoryMapper::class);
- $this->appConfig = $this->createMock(IAppConfig::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $this->signatureManager = new SignatureManager(
- $this->request,
- $this->mapper,
- $this->appConfig,
- $this->logger,
- );
- }
-
- public function testOutgoingDispatchesToCavageByDefault(): void {
- $signatoryManager = $this->rsaSignatoryManager();
-
- $signed = $this->signatureManager->getOutgoingSignedRequest(
- $signatoryManager,
- '{}',
- 'POST',
- 'https://receiver.example.org/ocm/shares',
- );
-
- $this->assertNotInstanceOf(Rfc9421OutgoingSignedRequest::class, $signed);
- }
-
- public function testOutgoingDispatchesToRfc9421WhenOptionSet(): void {
- [$signatoryManager,] = $this->ecdsaP256SignatoryManager(rfc9421Format: true);
-
- $signed = $this->signatureManager->getOutgoingSignedRequest(
- $signatoryManager,
- '{}',
- 'POST',
- 'https://receiver.example.org/ocm/shares',
- );
-
- $this->assertInstanceOf(Rfc9421OutgoingSignedRequest::class, $signed);
- $headers = $signed->getHeaders();
- $this->assertArrayHasKey('Signature-Input', $headers);
- $this->assertStringStartsWith('ocm=(', (string)$headers['Signature-Input']);
- }
-
- public function testInboundDispatchesToRfc9421WhenSignatureInputPresent(): void {
- [$signatoryManager, $jwk] = $this->ecdsaP256SignatoryManager(rfc9421Format: true);
-
- // Build a real signed request and replay its headers as the inbound
- // request to exercise the full inbound path including verification.
- $body = '{"hello":"world"}';
- $out = new Rfc9421OutgoingSignedRequest(
- $body,
- $signatoryManager,
- 'receiver.example.org',
- 'POST',
- 'https://receiver.example.org/ocm/shares',
- );
- $out->sign();
- $headers = $out->getHeaders();
-
- $this->primeRequest($headers, 'POST', '/ocm/shares', 'receiver.example.org');
-
- $resolver = $this->makeKeyResolver($signatoryManager, $jwk, 'https://sender.example.org/ocm#ecdsa-p256-sha256');
-
- // RFC 9421 verification needs the sender origin from the caller.
- $signed = $this->signatureManager->getIncomingSignedRequest($resolver, $body, 'sender.example.org');
- $this->assertInstanceOf(Rfc9421IncomingSignedRequest::class, $signed);
- }
-
- public function testInboundRejectsRfc9421WhenSignatoryManagerCannotResolve(): void {
- [$signatoryManager,] = $this->ecdsaP256SignatoryManager(rfc9421Format: true);
-
- $body = '{"hello":"world"}';
- $out = new Rfc9421OutgoingSignedRequest(
- $body,
- $signatoryManager,
- 'receiver.example.org',
- 'POST',
- 'https://receiver.example.org/ocm/shares',
- );
- $out->sign();
- $this->primeRequest($out->getHeaders(), 'POST', '/ocm/shares', 'receiver.example.org');
-
- // $signatoryManager does NOT implement IJwkResolvingSignatoryManager.
- $this->expectException(IncomingRequestException::class);
- $this->signatureManager->getIncomingSignedRequest($signatoryManager, $body);
- }
-
- public function testInboundRejectsRfc9421WhenNoKeyResolvedForKeyid(): void {
- [$signatoryManager, $jwk] = $this->ecdsaP256SignatoryManager(rfc9421Format: true);
-
- $body = '{"hello":"world"}';
- $out = new Rfc9421OutgoingSignedRequest(
- $body,
- $signatoryManager,
- 'receiver.example.org',
- 'POST',
- 'https://receiver.example.org/ocm/shares',
- );
- $out->sign();
- $this->primeRequest($out->getHeaders(), 'POST', '/ocm/shares', 'receiver.example.org');
-
- // resolver knows a different kid only: a present signature whose key
- // cannot be resolved is a verification failure, not an unsigned
- // request
- $resolver = $this->makeKeyResolver($signatoryManager, $jwk, 'https://other.example.org/ocm#nomatch');
-
- $this->expectException(IncomingRequestException::class);
- $this->signatureManager->getIncomingSignedRequest($resolver, $body, 'sender.example.org');
- }
-
- private function rsaSignatoryManager(): ISignatoryManager {
- $key = openssl_pkey_new(['private_key_type' => OPENSSL_KEYTYPE_RSA, 'private_key_bits' => 2048]);
- $priv = '';
- openssl_pkey_export($key, $priv);
- $pub = openssl_pkey_get_details($key)['key'];
-
- $signatory = new Signatory(true);
- $signatory->setKeyId('https://sender.example.org/ocm#signature');
- $signatory->setPublicKey($pub);
- $signatory->setPrivateKey($priv);
-
- return new class($signatory) implements ISignatoryManager {
- public function __construct(
- private Signatory $signatory,
- ) {
- }
-
- public function getProviderId(): string {
- return 'test';
- }
-
- public function getOptions(): array {
- return [
- 'algorithm' => SignatureAlgorithm::RSA_SHA256,
- 'digestAlgorithm' => DigestAlgorithm::SHA256,
- ];
- }
-
- public function getLocalSignatory(): Signatory {
- return $this->signatory;
- }
-
- public function getRemoteSignatory(string $remote): ?Signatory {
- return null;
- }
- };
- }
-
- /**
- * @return array{ISignatoryManager, Key} [manager, parsed verification key]
- */
- private function ecdsaP256SignatoryManager(bool $rfc9421Format): array {
- $pkey = openssl_pkey_new(['private_key_type' => OPENSSL_KEYTYPE_EC, 'curve_name' => 'prime256v1']);
- $privatePem = '';
- openssl_pkey_export($pkey, $privatePem);
- $details = openssl_pkey_get_details($pkey);
- $publicPem = $details['key'];
- $kid = 'https://sender.example.org/ocm#ecdsa-p256-sha256';
-
- $signatory = new Signatory(true);
- $signatory->setKeyId($kid);
- $signatory->setPublicKey($publicPem);
- $signatory->setPrivateKey($privatePem);
-
- $x = str_pad($details['ec']['x'], 32, "\x00", STR_PAD_LEFT);
- $y = str_pad($details['ec']['y'], 32, "\x00", STR_PAD_LEFT);
- $key = JWK::parseKey([
- 'kty' => 'EC',
- 'crv' => 'P-256',
- 'kid' => $kid,
- 'alg' => 'ES256',
- 'x' => rtrim(strtr(base64_encode($x), '+/', '-_'), '='),
- 'y' => rtrim(strtr(base64_encode($y), '+/', '-_'), '='),
- ], 'ES256');
-
- $manager = new class($signatory, $rfc9421Format) implements ISignatoryManager {
- public function __construct(
- private Signatory $signatory,
- private bool $rfc9421,
- ) {
- }
-
- public function getProviderId(): string {
- return 'test';
- }
-
- public function getOptions(): array {
- return [
- 'algorithm' => SignatureAlgorithm::RSA_SHA256,
- 'digestAlgorithm' => DigestAlgorithm::SHA256,
- 'rfc9421.format' => $this->rfc9421,
- ];
- }
-
- public function getLocalSignatory(): Signatory {
- return $this->signatory;
- }
-
- public function getRemoteSignatory(string $remote): ?Signatory {
- return null;
- }
- };
- return [$manager, $key];
- }
-
- private function makeKeyResolver(ISignatoryManager $delegate, Key $key, string $kid): IJwkResolvingSignatoryManager {
- return new class($delegate, $key, $kid) implements IJwkResolvingSignatoryManager {
- public function __construct(
- private ISignatoryManager $delegate,
- private Key $key,
- private string $kid,
- ) {
- }
-
- public function getProviderId(): string {
- return $this->delegate->getProviderId();
- }
-
- public function getOptions(): array {
- return $this->delegate->getOptions();
- }
-
- public function getLocalSignatory(): Signatory {
- return $this->delegate->getLocalSignatory();
- }
-
- public function getRemoteSignatory(string $remote): ?Signatory {
- return $this->delegate->getRemoteSignatory($remote);
- }
-
- public function getRemoteKey(string $origin, string $keyId): ?Key {
- return $keyId === $this->kid ? $this->key : null;
- }
- };
- }
-
- private function primeRequest(array $headers, string $method, string $path, string $host): void {
- $lowered = [];
- foreach ($headers as $name => $value) {
- $lowered[strtolower($name)] = (string)$value;
- }
- $this->request->method('getHeader')
- ->willReturnCallback(static fn (string $name) => $lowered[strtolower($name)] ?? '');
- $this->request->method('getMethod')->willReturn($method);
- $this->request->method('getRequestUri')->willReturn($path);
- $this->request->method('getServerProtocol')->willReturn('https');
- $this->request->method('getServerHost')->willReturn($host);
- }
-}
--- a/tests/lib/Security/TrustedDomainHelperTest.php
+++ /dev/null
@@ -1,147 +0,0 @@
-config = $this->getMockBuilder(IConfig::class)->getMock();
- }
-
- /**
- * @param string $trustedDomains
- * @param string $testDomain
- * @param bool $result
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('trustedDomainDataProvider')]
- public function testIsTrustedUrl($trustedDomains, $testDomain, $result): void {
- $this->config->method('getSystemValue')
- ->willReturnMap([
- ['overwritehost', '', ''],
- ['trusted_domains', [], $trustedDomains],
- ]);
-
- $trustedDomainHelper = new TrustedDomainHelper($this->config);
- $this->assertEquals($result, $trustedDomainHelper->isTrustedUrl('https://' . $testDomain . '/index.php/something'));
- }
-
- /**
- * @param string $trustedDomains
- * @param string $testDomain
- * @param bool $result
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('trustedDomainDataProvider')]
- public function testIsTrustedDomain($trustedDomains, $testDomain, $result): void {
- $this->config->method('getSystemValue')
- ->willReturnMap([
- ['overwritehost', '', ''],
- ['trusted_domains', [], $trustedDomains],
- ]);
-
- $trustedDomainHelper = new TrustedDomainHelper($this->config);
- $this->assertEquals($result, $trustedDomainHelper->isTrustedDomain($testDomain));
- }
-
- /**
- * @return array
- */
- public static function trustedDomainDataProvider(): array {
- $trustedHostTestList = [
- 'host.one.test',
- 'host.two.test',
- '[1fff:0:a88:85a3::ac1f]',
- 'host.three.test:443',
- '*.leading.host',
- 'trailing.host*',
- 'cen*ter',
- '*.leadingwith.port:123',
- 'trailingwith.port*:456',
- 'UPPERCASE.DOMAIN',
- 'lowercase.domain',
- ];
- return [
- // empty defaults to false with 8.1
- [null, 'host.one.test:8080', false],
- ['', 'host.one.test:8080', false],
- [[], 'host.one.test:8080', false],
- // trust list when defined
- [$trustedHostTestList, 'host.two.test:8080', true],
- [$trustedHostTestList, 'host.two.test:9999', true],
- [$trustedHostTestList, 'host.three.test:8080', false],
- [$trustedHostTestList, 'host.two.test:8080:aa:222', false],
- [$trustedHostTestList, '[1fff:0:a88:85a3::ac1f]', true],
- [$trustedHostTestList, '[1fff:0:a88:85a3::ac1f]:801', true],
- [$trustedHostTestList, '[1fff:0:a88:85a3::ac1f]:801:34', false],
- [$trustedHostTestList, 'host.three.test:443', true],
- [$trustedHostTestList, 'host.three.test:80', false],
- [$trustedHostTestList, 'host.three.test', false],
- // trust localhost regardless of trust list
- [$trustedHostTestList, 'localhost', true],
- [$trustedHostTestList, 'localhost:8080', true],
- [$trustedHostTestList, '127.0.0.1', true],
- [$trustedHostTestList, '127.0.0.1:8080', true],
- // do not trust invalid localhosts
- [$trustedHostTestList, 'localhost:1:2', false],
- [$trustedHostTestList, 'localhost: evil.host', false],
- // do not trust casting
- [[1], '1', false],
- // leading *
- [$trustedHostTestList, 'abc.leading.host', true],
- [$trustedHostTestList, 'abc.def.leading.host', true],
- [$trustedHostTestList, 'abc.def.leading.host.another', false],
- [$trustedHostTestList, 'abc.def.leading.host:123', true],
- [$trustedHostTestList, 'leading.host', false],
- // trailing *
- [$trustedHostTestList, 'trailing.host', true],
- [$trustedHostTestList, 'trailing.host.abc', true],
- [$trustedHostTestList, 'trailing.host.abc.def', true],
- [$trustedHostTestList, 'trailing.host.abc:123', true],
- [$trustedHostTestList, 'another.trailing.host', false],
- // center *
- [$trustedHostTestList, 'center', true],
- [$trustedHostTestList, 'cenxxxter', true],
- [$trustedHostTestList, 'cen.x.y.ter', true],
- // with port
- [$trustedHostTestList, 'abc.leadingwith.port:123', true],
- [$trustedHostTestList, 'abc.leadingwith.port:1234', false],
- [$trustedHostTestList, 'trailingwith.port.abc:456', true],
- [$trustedHostTestList, 'trailingwith.port.abc:123', false],
- // bad hostname
- [$trustedHostTestList, '-bad', false],
- [$trustedHostTestList, '-bad.leading.host', false],
- [$trustedHostTestList, 'bad..der.leading.host', false],
- // case sensitivity
- [$trustedHostTestList, 'uppercase.domain', true],
- [$trustedHostTestList, 'LOWERCASE.DOMAIN', true],
- ];
- }
-
- public function testIsTrustedDomainOverwriteHost(): void {
- $this->config->method('getSystemValue')
- ->with('overwritehost')
- ->willReturn('myproxyhost');
-
- $trustedDomainHelper = new TrustedDomainHelper($this->config);
- $this->assertTrue($trustedDomainHelper->isTrustedDomain('myproxyhost'));
- $this->assertTrue($trustedDomainHelper->isTrustedDomain('myotherhost'));
- }
-}
--- a/tests/lib/Security/VerificationToken/VerificationTokenTest.php
+++ /dev/null
@@ -1,329 +0,0 @@
-config = $this->createMock(IConfig::class);
- $this->crypto = $this->createMock(ICrypto::class);
- $this->timeFactory = $this->createMock(ITimeFactory::class);
- $this->secureRandom = $this->createMock(ISecureRandom::class);
- $this->jobList = $this->createMock(IJobList::class);
- $this->lastInteractiveLogin = $this->createMock(LastInteractiveLogin::class);
-
- $this->token = new VerificationToken(
- $this->config,
- $this->crypto,
- $this->timeFactory,
- $this->secureRandom,
- $this->jobList,
- $this->lastInteractiveLogin,
- );
- }
-
- protected function mockLastInteractiveLogin(int $timestamp): void {
- $this->lastInteractiveLogin->expects($this->atLeastOnce())
- ->method('get')
- ->willReturn($timestamp);
- }
-
- public function testTokenUserUnknown(): void {
- $this->expectException(InvalidTokenException::class);
- $this->expectExceptionCode(InvalidTokenException::USER_UNKNOWN);
- $this->token->check('encryptedToken', null, 'fingerprintToken', 'foobar');
- }
-
- public function testTokenUserUnknown2(): void {
- $user = $this->createMock(IUser::class);
- $user->expects($this->atLeastOnce())
- ->method('isEnabled')
- ->willReturn(false);
-
- $this->expectException(InvalidTokenException::class);
- $this->expectExceptionCode(InvalidTokenException::USER_UNKNOWN);
- $this->token->check('encryptedToken', $user, 'fingerprintToken', 'foobar');
- }
-
- public function testTokenNotFound(): void {
- $user = $this->createMock(IUser::class);
- $user->expects($this->atLeastOnce())
- ->method('isEnabled')
- ->willReturn(true);
- $user->expects($this->atLeastOnce())
- ->method('getUID')
- ->willReturn('alice');
-
- // implicit: IConfig::getUserValue returns null by default
-
- $this->expectException(InvalidTokenException::class);
- $this->expectExceptionCode(InvalidTokenException::TOKEN_NOT_FOUND);
- $this->token->check('encryptedToken', $user, 'fingerprintToken', 'foobar');
- }
-
- public function testTokenDecryptionError(): void {
- $user = $this->createMock(IUser::class);
- $user->expects($this->atLeastOnce())
- ->method('isEnabled')
- ->willReturn(true);
- $user->expects($this->atLeastOnce())
- ->method('getUID')
- ->willReturn('alice');
-
- $this->config->expects($this->atLeastOnce())
- ->method('getUserValue')
- ->with('alice', 'core', 'fingerprintToken', null)
- ->willReturn('encryptedToken');
- $this->config->expects($this->any())
- ->method('getSystemValueString')
- ->with('secret')
- ->willReturn('357111317');
-
- $this->crypto->method('decrypt')
- ->with('encryptedToken', 'foobar' . '357111317')
- ->willThrowException(new \Exception('decryption failed'));
-
- $this->expectException(InvalidTokenException::class);
- $this->expectExceptionCode(InvalidTokenException::TOKEN_DECRYPTION_ERROR);
- $this->token->check('encryptedToken', $user, 'fingerprintToken', 'foobar');
- }
-
- public function testTokenInvalidFormat(): void {
- $user = $this->createMock(IUser::class);
- $user->expects($this->atLeastOnce())
- ->method('isEnabled')
- ->willReturn(true);
- $user->expects($this->atLeastOnce())
- ->method('getUID')
- ->willReturn('alice');
-
- $this->config->expects($this->atLeastOnce())
- ->method('getUserValue')
- ->with('alice', 'core', 'fingerprintToken', null)
- ->willReturn('encryptedToken');
- $this->config->expects($this->any())
- ->method('getSystemValueString')
- ->with('secret')
- ->willReturn('357111317');
-
- $this->crypto->method('decrypt')
- ->with('encryptedToken', 'foobar' . '357111317')
- ->willReturn('decrypted^nonsense');
-
- $this->expectException(InvalidTokenException::class);
- $this->expectExceptionCode(InvalidTokenException::TOKEN_INVALID_FORMAT);
- $this->token->check('encryptedToken', $user, 'fingerprintToken', 'foobar');
- }
-
- public function testTokenExpired(): void {
- $user = $this->createMock(IUser::class);
- $user->expects($this->atLeastOnce())
- ->method('isEnabled')
- ->willReturn(true);
- $user->expects($this->atLeastOnce())
- ->method('getUID')
- ->willReturn('alice');
-
- $this->config->expects($this->atLeastOnce())
- ->method('getUserValue')
- ->with('alice', 'core', 'fingerprintToken', null)
- ->willReturn('encryptedToken');
- $this->config->expects($this->any())
- ->method('getSystemValueString')
- ->with('secret')
- ->willReturn('357111317');
-
- $this->crypto->method('decrypt')
- ->with('encryptedToken', 'foobar' . '357111317')
- ->willReturn('604800:mY70K3n');
-
- $this->timeFactory->expects($this->any())
- ->method('getTime')
- ->willReturn(604800 * 3);
-
- $this->expectException(InvalidTokenException::class);
- $this->expectExceptionCode(InvalidTokenException::TOKEN_EXPIRED);
- $this->token->check('encryptedToken', $user, 'fingerprintToken', 'foobar');
- }
-
- public function testTokenExpiredByLogin(): void {
- $user = $this->createMock(IUser::class);
- $user->expects($this->atLeastOnce())
- ->method('isEnabled')
- ->willReturn(true);
- $user->expects($this->atLeastOnce())
- ->method('getUID')
- ->willReturn('alice');
- $this->mockLastInteractiveLogin(604803);
-
- $this->config->expects($this->atLeastOnce())
- ->method('getUserValue')
- ->with('alice', 'core', 'fingerprintToken', null)
- ->willReturn('encryptedToken');
- $this->config->expects($this->any())
- ->method('getSystemValueString')
- ->with('secret')
- ->willReturn('357111317');
-
- $this->crypto->method('decrypt')
- ->with('encryptedToken', 'foobar' . '357111317')
- ->willReturn('604800:mY70K3n');
-
- $this->timeFactory->expects($this->any())
- ->method('getTime')
- ->willReturn(604801);
-
- $this->expectException(InvalidTokenException::class);
- $this->expectExceptionCode(InvalidTokenException::TOKEN_EXPIRED);
- $this->token->check('encryptedToken', $user, 'fingerprintToken', 'foobar', true);
- }
-
- public function testTokenNotExpiredBySessionRevalidation(): void {
- $user = $this->createMock(IUser::class);
- $user->expects($this->atLeastOnce())
- ->method('isEnabled')
- ->willReturn(true);
- $user->expects($this->atLeastOnce())
- ->method('getUID')
- ->willReturn('alice');
- $user->expects($this->never())
- ->method('getLastLogin');
- // last actual authentication predates the token
- $this->mockLastInteractiveLogin(604700);
-
- $this->config->expects($this->atLeastOnce())
- ->method('getUserValue')
- ->with('alice', 'core', 'fingerprintToken', null)
- ->willReturn('encryptedToken');
- $this->config->expects($this->any())
- ->method('getSystemValueString')
- ->with('secret')
- ->willReturn('357111317');
-
- $this->crypto->method('decrypt')
- ->with('encryptedToken', 'foobar' . '357111317')
- ->willReturn('604800:barfoo');
-
- $this->timeFactory->expects($this->any())
- ->method('getTime')
- ->willReturn(604801);
-
- $this->token->check('barfoo', $user, 'fingerprintToken', 'foobar', true);
- }
-
- public function testTokenMismatch(): void {
- $user = $this->createMock(IUser::class);
- $user->expects($this->atLeastOnce())
- ->method('isEnabled')
- ->willReturn(true);
- $user->expects($this->atLeastOnce())
- ->method('getUID')
- ->willReturn('alice');
-
- $this->config->expects($this->atLeastOnce())
- ->method('getUserValue')
- ->with('alice', 'core', 'fingerprintToken', null)
- ->willReturn('encryptedToken');
- $this->config->expects($this->any())
- ->method('getSystemValueString')
- ->with('secret')
- ->willReturn('357111317');
-
- $this->crypto->method('decrypt')
- ->with('encryptedToken', 'foobar' . '357111317')
- ->willReturn('604802:mY70K3n');
-
- $this->timeFactory->expects($this->any())
- ->method('getTime')
- ->willReturn(604801);
-
- $this->expectException(InvalidTokenException::class);
- $this->expectExceptionCode(InvalidTokenException::TOKEN_MISMATCH);
- $this->token->check('encryptedToken', $user, 'fingerprintToken', 'foobar');
- }
-
- public function testTokenSuccess(): void {
- $user = $this->createMock(IUser::class);
- $user->expects($this->atLeastOnce())
- ->method('isEnabled')
- ->willReturn(true);
- $user->expects($this->atLeastOnce())
- ->method('getUID')
- ->willReturn('alice');
-
- $this->config->expects($this->atLeastOnce())
- ->method('getUserValue')
- ->with('alice', 'core', 'fingerprintToken', null)
- ->willReturn('encryptedToken');
- $this->config->expects($this->any())
- ->method('getSystemValueString')
- ->with('secret')
- ->willReturn('357111317');
-
- $this->crypto->method('decrypt')
- ->with('encryptedToken', 'foobar' . '357111317')
- ->willReturn('604802:barfoo');
-
- $this->timeFactory->expects($this->any())
- ->method('getTime')
- ->willReturn(604801);
-
- $this->token->check('barfoo', $user, 'fingerprintToken', 'foobar');
- }
-
- public function testCreate(): void {
- $user = $this->createMock(IUser::class);
- $user->expects($this->any())
- ->method('getUID')
- ->willReturn('alice');
-
- $this->secureRandom->expects($this->atLeastOnce())
- ->method('generate')
- ->willReturn('barfoo');
- $this->crypto->expects($this->atLeastOnce())
- ->method('encrypt')
- ->willReturn('encryptedToken');
- $this->config->expects($this->atLeastOnce())
- ->method('setUserValue')
- ->with('alice', 'core', 'fingerprintToken', 'encryptedToken');
-
- $vToken = $this->token->create($user, 'fingerprintToken', 'foobar');
- $this->assertSame('barfoo', $vToken);
- }
-}
--- a/tests/lib/ServerTest.php
+++ /dev/null
@@ -1,68 +0,0 @@
-server = new Server('', $config);
- }
-
- public static function dataTestQuery(): array {
- return [
- ['\OCP\Activity\IManager', '\OC\Activity\Manager'],
- ['\OCP\IConfig', '\OC\AllConfig'],
- ['\OCP\IAppConfig', '\OC\AppConfig'],
- [AppFetcher::class, AppFetcher::class],
- ['\OCP\App\IAppManager', '\OC\App\AppManager'],
- ['\OCP\Command\IBus', '\OC\Command\AsyncBus'],
- ['\OCP\IAvatarManager', '\OC\Avatar\AvatarManager'],
- ];
- }
-
- /**
- *
- * @param string $serviceName
- * @param string $instanceOf
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataTestQuery')]
- public function testQuery(string $serviceName, string $instanceOf): void {
- $this->assertInstanceOf($instanceOf, $this->server->query($serviceName), 'Service "' . $serviceName . '"" did not return the right class');
- }
-
- public function testOverwriteDefaultCommentsManager(): void {
- $config = $this->server->get(IConfig::class);
- $defaultManagerFactory = $config->getSystemValue('comments.managerFactory', '\OC\Comments\ManagerFactory');
-
- $config->setSystemValue('comments.managerFactory', '\Test\Comments\FakeFactory');
-
- $manager = $this->server->get(ICommentsManager::class);
- $this->assertInstanceOf('\OCP\Comments\ICommentsManager', $manager);
-
- $config->setSystemValue('comments.managerFactory', $defaultManagerFactory);
- }
-}
--- a/tests/lib/Session/CryptoSessionDataTest.php
+++ /dev/null
@@ -1,45 +0,0 @@
-wrappedSession = new Memory();
- $this->crypto = $this->createMock(ICrypto::class);
- $this->crypto->expects($this->any())
- ->method('encrypt')
- ->willReturnCallback(function ($input) {
- return '#' . $input . '#';
- });
- $this->crypto->expects($this->any())
- ->method('decrypt')
- ->willReturnCallback(function ($input) {
- if ($input === '') {
- return '';
- }
- return substr($input, 1, -1);
- });
-
- $this->instance = new CryptoSessionData($this->wrappedSession, $this->crypto, 'PASS');
- }
-}
--- a/tests/lib/Session/CryptoWrappingTest.php
+++ /dev/null
@@ -1,66 +0,0 @@
-wrappedSession = $this->getMockBuilder(ISession::class)
- ->disableOriginalConstructor()
- ->getMock();
- $this->crypto = $this->getMockBuilder('OCP\Security\ICrypto')
- ->disableOriginalConstructor()
- ->getMock();
- $this->crypto->expects($this->any())
- ->method('encrypt')
- ->willReturnCallback(function ($input) {
- return $input;
- });
- $this->crypto->expects($this->any())
- ->method('decrypt')
- ->willReturnCallback(function ($input) {
- if ($input === '') {
- return '';
- }
- return substr($input, 1, -1);
- });
-
- $this->instance = new CryptoSessionData($this->wrappedSession, $this->crypto, 'PASS');
- }
-
- public function testUnwrappingGet(): void {
- $unencryptedValue = 'foobar';
- $encryptedValue = $this->crypto->encrypt($unencryptedValue);
-
- $this->wrappedSession->expects($this->once())
- ->method('get')
- ->with('encrypted_session_data')
- ->willReturnCallback(function () use ($encryptedValue) {
- return $encryptedValue;
- });
-
- $this->assertSame($unencryptedValue, $this->wrappedSession->get('encrypted_session_data'));
- }
-}
--- a/tests/lib/Session/MemoryTest.php
+++ /dev/null
@@ -1,26 +0,0 @@
-instance = new Memory();
- }
-
- public function testThrowsExceptionOnGetId(): void {
- $this->expectException(SessionNotAvailableException::class);
-
- $this->instance->getId();
- }
-}
--- a/tests/lib/Session/Session.php
+++ /dev/null
@@ -1,67 +0,0 @@
-instance->clear();
- parent::tearDown();
- }
-
- public function testNotExistsEmpty(): void {
- $this->assertFalse($this->instance->exists('foo'));
- }
-
- public function testExistsAfterSet(): void {
- $this->instance->set('foo', 1);
- $this->assertTrue($this->instance->exists('foo'));
- }
-
- public function testNotExistsAfterRemove(): void {
- $this->instance->set('foo', 1);
- $this->instance->remove('foo');
- $this->assertFalse($this->instance->exists('foo'));
- }
-
- public function testGetNonExisting(): void {
- $this->assertNull($this->instance->get('foo'));
- }
-
- public function testGetAfterSet(): void {
- $this->instance->set('foo', 'bar');
- $this->assertEquals('bar', $this->instance->get(('foo')));
- }
-
- public function testRemoveNonExisting(): void {
- $this->assertFalse($this->instance->exists('foo'));
- $this->instance->remove('foo');
- $this->assertFalse($this->instance->exists('foo'));
- }
-
- public function testNotExistsAfterClear(): void {
- $this->instance->set('foo', 1);
- $this->instance->clear();
- $this->assertFalse($this->instance->exists('foo'));
- }
-
- public function testArrayInterface(): void {
- $this->assertFalse(isset($this->instance['foo']));
- $this->instance['foo'] = 'bar';
- $this->assertTrue(isset($this->instance['foo']));
- $this->assertEquals('bar', $this->instance['foo']);
- unset($this->instance['foo']);
- $this->assertFalse(isset($this->instance['foo']));
- }
-}
--- a/tests/lib/Settings/DeclarativeManagerTest.php
+++ /dev/null
@@ -1,639 +0,0 @@
- 'test_form_1',
- 'priority' => 10,
- 'section_type' => DeclarativeSettingsTypes::SECTION_TYPE_ADMIN, // admin, personal
- 'section_id' => 'additional',
- 'storage_type' => DeclarativeSettingsTypes::STORAGE_TYPE_INTERNAL, // external, internal (handled by core to store in appconfig and preferences)
- 'title' => 'Test declarative settings', // NcSettingsSection name
- 'description' => 'These fields are rendered dynamically from declarative schema', // NcSettingsSection description
- 'doc_url' => '', // NcSettingsSection doc_url for documentation or help page, empty string if not needed
- 'fields' => [
- [
- 'id' => 'test_field_7', // configkey
- 'title' => 'Multi-selection', // name or label
- 'description' => 'Select some option setting', // hint
- 'type' => DeclarativeSettingsTypes::MULTI_SELECT,
- 'options' => ['foo', 'bar', 'baz'], // simple options for select, radio, multi-select
- 'placeholder' => 'Select some multiple options', // input placeholder
- 'default' => ['foo', 'bar'],
- ],
- [
- 'id' => 'some_real_setting',
- 'title' => 'Select single option',
- 'description' => 'Single option radio buttons',
- 'type' => DeclarativeSettingsTypes::RADIO, // radio (NcCheckboxRadioSwitch type radio)
- 'placeholder' => 'Select single option, test interval',
- 'default' => '40m',
- 'options' => [
- [
- 'name' => 'Each 40 minutes', // NcCheckboxRadioSwitch display name
- 'value' => '40m' // NcCheckboxRadioSwitch value
- ],
- [
- 'name' => 'Each 60 minutes',
- 'value' => '60m'
- ],
- [
- 'name' => 'Each 120 minutes',
- 'value' => '120m'
- ],
- [
- 'name' => 'Each day',
- 'value' => 60 * 24 . 'm'
- ],
- ],
- ],
- [
- 'id' => 'test_field_1', // configkey
- 'title' => 'Default text field', // label
- 'description' => 'Set some simple text setting', // hint
- 'type' => DeclarativeSettingsTypes::TEXT,
- 'placeholder' => 'Enter text setting', // placeholder
- 'default' => 'foo',
- ],
- [
- 'id' => 'test_field_1_1',
- 'title' => 'Email field',
- 'description' => 'Set email config',
- 'type' => DeclarativeSettingsTypes::EMAIL,
- 'placeholder' => 'Enter email',
- 'default' => '',
- ],
- [
- 'id' => 'test_field_1_2',
- 'title' => 'Tel field',
- 'description' => 'Set tel config',
- 'type' => DeclarativeSettingsTypes::TEL,
- 'placeholder' => 'Enter your tel',
- 'default' => '',
- ],
- [
- 'id' => 'test_field_1_3',
- 'title' => 'Url (website) field',
- 'description' => 'Set url config',
- 'type' => 'url',
- 'placeholder' => 'Enter url',
- 'default' => '',
- ],
- [
- 'id' => 'test_field_1_4',
- 'title' => 'Number field',
- 'description' => 'Set number config',
- 'type' => DeclarativeSettingsTypes::NUMBER,
- 'placeholder' => 'Enter number value',
- 'default' => 0,
- ],
- [
- 'id' => 'test_field_2',
- 'title' => 'Password',
- 'description' => 'Set some secure value setting',
- 'type' => 'password',
- 'placeholder' => 'Set secure value',
- 'default' => '',
- ],
- [
- 'id' => 'test_field_3',
- 'title' => 'Selection',
- 'description' => 'Select some option setting',
- 'type' => DeclarativeSettingsTypes::SELECT,
- 'options' => ['foo', 'bar', 'baz'],
- 'placeholder' => 'Select some option setting',
- 'default' => 'foo',
- ],
- [
- 'id' => 'test_field_4',
- 'title' => 'Toggle something',
- 'description' => 'Select checkbox option setting',
- 'type' => DeclarativeSettingsTypes::CHECKBOX,
- 'label' => 'Verify something if enabled',
- 'default' => false,
- ],
- [
- 'id' => 'test_field_5',
- 'title' => 'Multiple checkbox toggles, describing one setting, checked options are saved as an JSON object {foo: true, bar: false}',
- 'description' => 'Select checkbox option setting',
- 'type' => DeclarativeSettingsTypes::MULTI_CHECKBOX,
- 'default' => ['foo' => true, 'bar' => true],
- 'options' => [
- [
- 'name' => 'Foo',
- 'value' => 'foo', // multiple-checkbox configkey
- ],
- [
- 'name' => 'Bar',
- 'value' => 'bar',
- ],
- [
- 'name' => 'Baz',
- 'value' => 'baz',
- ],
- [
- 'name' => 'Qux',
- 'value' => 'qux',
- ],
- ],
- ],
- [
- 'id' => 'test_field_6',
- 'title' => 'Radio toggles, describing one setting like single select',
- 'description' => 'Select radio option setting',
- 'type' => DeclarativeSettingsTypes::RADIO, // radio (NcCheckboxRadioSwitch type radio)
- 'label' => 'Select single toggle',
- 'default' => 'foo',
- 'options' => [
- [
- 'name' => 'First radio', // NcCheckboxRadioSwitch display name
- 'value' => 'foo' // NcCheckboxRadioSwitch value
- ],
- [
- 'name' => 'Second radio',
- 'value' => 'bar'
- ],
- [
- 'name' => 'Second radio',
- 'value' => 'baz'
- ],
- ],
- ],
- [
- 'id' => 'test_sensitive_field',
- 'title' => 'Sensitive text field',
- 'description' => 'Set some secure value setting that is stored encrypted',
- 'type' => DeclarativeSettingsTypes::TEXT,
- 'label' => 'Sensitive field',
- 'placeholder' => 'Set secure value',
- 'default' => '',
- 'sensitive' => true, // only for TEXT, PASSWORD types
- ],
- [
- 'id' => 'test_sensitive_field_2',
- 'title' => 'Sensitive password field',
- 'description' => 'Set some password setting that is stored encrypted',
- 'type' => DeclarativeSettingsTypes::PASSWORD,
- 'label' => 'Sensitive field',
- 'placeholder' => 'Set secure value',
- 'default' => '',
- 'sensitive' => true, // only for TEXT, PASSWORD types
- ],
- [
- 'id' => 'test_non_sensitive_field',
- 'title' => 'Password field',
- 'description' => 'Set some password setting',
- 'type' => DeclarativeSettingsTypes::PASSWORD,
- 'label' => 'Password field',
- 'placeholder' => 'Set secure value',
- 'default' => '',
- 'sensitive' => false,
- ],
- ],
- ];
-
- public static bool $testSetInternalValueAfterChange = false;
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
- $this->groupManager = $this->createMock(IGroupManager::class);
- $this->coordinator = $this->createMock(Coordinator::class);
- $this->config = $this->createMock(IConfig::class);
- $this->appConfig = $this->createMock(IAppConfig::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->crypto = $this->createMock(ICrypto::class);
-
- $this->declarativeManager = new DeclarativeManager(
- $this->eventDispatcher,
- $this->groupManager,
- $this->coordinator,
- $this->config,
- $this->appConfig,
- $this->logger,
- $this->crypto,
- );
-
- $this->user = $this->createMock(IUser::class);
- $this->user->expects($this->any())
- ->method('getUID')
- ->willReturn('test_user');
-
- $this->adminUser = $this->createMock(IUser::class);
- $this->adminUser->expects($this->any())
- ->method('getUID')
- ->willReturn('admin_test_user');
-
- $this->groupManager->expects($this->any())
- ->method('isAdmin')
- ->willReturnCallback(function ($userId) {
- return $userId === 'admin_test_user';
- });
- }
-
- public function testRegisterSchema(): void {
- $app = 'testing';
- $schema = self::validSchemaAllFields;
- $this->declarativeManager->registerSchema($app, $schema);
- $formIds = $this->declarativeManager->getFormIDs($this->adminUser, $schema['section_type'], $schema['section_id']);
- $this->assertTrue(isset($formIds[$app]) && in_array($schema['id'], $formIds[$app]));
- }
-
- /**
- * Simple test to verify that exception is thrown when trying to register schema with duplicate id
- */
- public function testRegisterDuplicateSchema(): void {
- $this->declarativeManager->registerSchema('testing', self::validSchemaAllFields);
- $this->expectException(\Exception::class);
- $this->declarativeManager->registerSchema('testing', self::validSchemaAllFields);
- }
-
- /**
- * It's not allowed to register schema with duplicate fields ids for the same app
- */
- public function testRegisterSchemaWithDuplicateFields(): void {
- // Register first valid schema
- $this->declarativeManager->registerSchema('testing', self::validSchemaAllFields);
- // Register second schema with duplicate fields, but different schema id
- $this->expectException(\Exception::class);
- $schema = self::validSchemaAllFields;
- $schema['id'] = 'test_form_2';
- $this->declarativeManager->registerSchema('testing', $schema);
- }
-
- public function testRegisterMultipleSchemasAndDuplicate(): void {
- $app = 'testing';
- $schema = self::validSchemaAllFields;
- $this->declarativeManager->registerSchema($app, $schema);
- $formIds = $this->declarativeManager->getFormIDs($this->adminUser, $schema['section_type'], $schema['section_id']);
- // 1. Check that form is registered for the app
- $this->assertTrue(isset($formIds[$app]) && in_array($schema['id'], $formIds[$app]));
- $app = 'testing2';
- $this->declarativeManager->registerSchema($app, $schema);
- $formIds = $this->declarativeManager->getFormIDs($this->adminUser, $schema['section_type'], $schema['section_id']);
- // 2. Check that form is registered for the second app
- $this->assertTrue(isset($formIds[$app]) && in_array($schema['id'], $formIds[$app]));
- $app = 'testing';
- $this->expectException(\Exception::class); // expecting duplicate form id and duplicate fields ids exception
- $this->declarativeManager->registerSchema($app, $schema);
- $schemaDuplicateFields = self::validSchemaAllFields;
- $schemaDuplicateFields['id'] = 'test_form_2'; // change form id to test duplicate fields
- $this->declarativeManager->registerSchema($app, $schemaDuplicateFields);
- // 3. Check that not valid form with duplicate fields is not registered
- $formIds = $this->declarativeManager->getFormIDs($this->adminUser, $schemaDuplicateFields['section_type'], $schemaDuplicateFields['section_id']);
- $this->assertFalse(isset($formIds[$app]) && in_array($schemaDuplicateFields['id'], $formIds[$app]));
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataValidateSchema')]
- public function testValidateSchema(bool $expected, bool $expectException, string $app, array $schema): void {
- if ($expectException) {
- $this->expectException(\Exception::class);
- }
- $this->declarativeManager->registerSchema($app, $schema);
- $formIds = $this->declarativeManager->getFormIDs($this->adminUser, $schema['section_type'], $schema['section_id']);
- $this->assertEquals($expected, isset($formIds[$app]) && in_array($schema['id'], $formIds[$app]));
- }
-
- public static function dataValidateSchema(): array {
- return [
- 'valid schema with all supported fields' => [
- true,
- false,
- 'testing',
- self::validSchemaAllFields,
- ],
- 'invalid schema with missing id' => [
- false,
- true,
- 'testing',
- [
- 'priority' => 10,
- 'section_type' => DeclarativeSettingsTypes::SECTION_TYPE_ADMIN,
- 'section_id' => 'additional',
- 'storage_type' => DeclarativeSettingsTypes::STORAGE_TYPE_INTERNAL,
- 'title' => 'Test declarative settings',
- 'description' => 'These fields are rendered dynamically from declarative schema',
- 'doc_url' => '',
- 'fields' => [
- [
- 'id' => 'test_field_7',
- 'title' => 'Multi-selection',
- 'description' => 'Select some option setting',
- 'type' => DeclarativeSettingsTypes::MULTI_SELECT,
- 'options' => ['foo', 'bar', 'baz'],
- 'placeholder' => 'Select some multiple options',
- 'default' => ['foo', 'bar'],
- ],
- ],
- ],
- ],
- 'invalid schema with invalid field' => [
- false,
- true,
- 'testing',
- [
- 'id' => 'test_form_1',
- 'priority' => 10,
- 'section_type' => DeclarativeSettingsTypes::SECTION_TYPE_ADMIN,
- 'section_id' => 'additional',
- 'storage_type' => DeclarativeSettingsTypes::STORAGE_TYPE_INTERNAL,
- 'title' => 'Test declarative settings',
- 'description' => 'These fields are rendered dynamically from declarative schema',
- 'doc_url' => '',
- 'fields' => [
- [
- 'id' => 'test_invalid_field',
- 'title' => 'Invalid field',
- 'description' => 'Some invalid setting description',
- 'type' => 'some_invalid_type',
- 'placeholder' => 'Some invalid field placeholder',
- 'default' => null,
- ],
- ],
- ],
- ],
- ];
- }
-
- public function testGetFormIDs(): void {
- $app = 'testing';
- $schema = self::validSchemaAllFields;
- $this->declarativeManager->registerSchema($app, $schema);
- $formIds = $this->declarativeManager->getFormIDs($this->adminUser, $schema['section_type'], $schema['section_id']);
- $this->assertTrue(isset($formIds[$app]) && in_array($schema['id'], $formIds[$app]));
- $app = 'testing2';
- $this->declarativeManager->registerSchema($app, $schema);
- $formIds = $this->declarativeManager->getFormIDs($this->adminUser, $schema['section_type'], $schema['section_id']);
- $this->assertTrue(isset($formIds[$app]) && in_array($schema['id'], $formIds[$app]));
- }
-
- /**
- * Check that form with default values is returned with internal storage_type
- */
- public function testGetFormsWithDefaultValues(): void {
- $app = 'testing';
- $schema = self::validSchemaAllFields;
- $this->declarativeManager->registerSchema($app, $schema);
-
- $this->config->expects($this->any())
- ->method('getAppValue')
- ->willReturnCallback(fn ($app, $configkey, $default) => $default);
-
- $forms = $this->declarativeManager->getFormsWithValues($this->adminUser, $schema['section_type'], $schema['section_id']);
- $this->assertNotEmpty($forms);
- $this->assertTrue(array_search($schema['id'], array_column($forms, 'id')) !== false);
- // Check some_real_setting field default value
- $someRealSettingField = array_values(array_filter(array_filter($forms, fn ($form) => $form['id'] === $schema['id'])[0]['fields'], fn ($field) => $field['id'] === 'some_real_setting'))[0];
- $schemaSomeRealSettingField = array_values(array_filter($schema['fields'], fn ($field) => $field['id'] === 'some_real_setting'))[0];
- $this->assertEquals($schemaSomeRealSettingField['default'], $someRealSettingField['default']);
- }
-
- /**
- * Check values in json format to ensure that they are properly encoded
- */
- public function testGetFormsWithDefaultValuesJson(): void {
- $app = 'testing';
- $schema = [
- 'id' => 'test_form_1',
- 'priority' => 10,
- 'section_type' => DeclarativeSettingsTypes::SECTION_TYPE_PERSONAL,
- 'section_id' => 'additional',
- 'storage_type' => DeclarativeSettingsTypes::STORAGE_TYPE_INTERNAL,
- 'title' => 'Test declarative settings',
- 'description' => 'These fields are rendered dynamically from declarative schema',
- 'doc_url' => '',
- 'fields' => [
- [
- 'id' => 'test_field_json',
- 'title' => 'Multi-selection',
- 'description' => 'Select some option setting',
- 'type' => DeclarativeSettingsTypes::MULTI_SELECT,
- 'options' => ['foo', 'bar', 'baz'],
- 'placeholder' => 'Select some multiple options',
- 'default' => ['foo', 'bar'],
- ],
- ],
- ];
- $this->declarativeManager->registerSchema($app, $schema);
-
- // config->getUserValue() should be called with json encoded default value
- $this->config->expects($this->once())
- ->method('getUserValue')
- ->with($this->adminUser->getUID(), $app, 'test_field_json', json_encode($schema['fields'][0]['default']))
- ->willReturn(json_encode($schema['fields'][0]['default']));
-
- $forms = $this->declarativeManager->getFormsWithValues($this->adminUser, $schema['section_type'], $schema['section_id']);
- $this->assertNotEmpty($forms);
- $this->assertTrue(array_search($schema['id'], array_column($forms, 'id')) !== false);
- $testFieldJson = array_values(array_filter(array_filter($forms, fn ($form) => $form['id'] === $schema['id'])[0]['fields'], fn ($field) => $field['id'] === 'test_field_json'))[0];
- $this->assertEquals(json_encode($schema['fields'][0]['default']), $testFieldJson['value']);
- }
-
- /**
- * Check that saving value for field with internal storage_type is handled by core
- */
- public function testSetInternalValue(): void {
- $app = 'testing';
- $schema = self::validSchemaAllFields;
- $this->declarativeManager->registerSchema($app, $schema);
- self::$testSetInternalValueAfterChange = false;
-
- $this->config->expects($this->any())
- ->method('getAppValue')
- ->willReturnCallback(function ($app, $configkey, $default) {
- if ($configkey === 'some_real_setting' && self::$testSetInternalValueAfterChange) {
- return '120m';
- }
- return $default;
- });
-
- $this->appConfig->expects($this->once())
- ->method('setValueString')
- ->with($app, 'some_real_setting', '120m');
-
- $forms = $this->declarativeManager->getFormsWithValues($this->adminUser, $schema['section_type'], $schema['section_id']);
- $someRealSettingField = array_values(array_filter(array_filter($forms, fn ($form) => $form['id'] === $schema['id'])[0]['fields'], fn ($field) => $field['id'] === 'some_real_setting'))[0];
- $this->assertEquals('40m', $someRealSettingField['value']); // first check that default value (40m) is returned
-
- // Set new value for some_real_setting field
- $this->declarativeManager->setValue($this->adminUser, $app, $schema['id'], 'some_real_setting', '120m');
- self::$testSetInternalValueAfterChange = true;
-
- $forms = $this->declarativeManager->getFormsWithValues($this->adminUser, $schema['section_type'], $schema['section_id']);
- $this->assertNotEmpty($forms);
- $this->assertTrue(array_search($schema['id'], array_column($forms, 'id')) !== false);
- // Check some_real_setting field default value
- $someRealSettingField = array_values(array_filter(array_filter($forms, fn ($form) => $form['id'] === $schema['id'])[0]['fields'], fn ($field) => $field['id'] === 'some_real_setting'))[0];
- $this->assertEquals('120m', $someRealSettingField['value']);
- }
-
- public function testSetExternalValue(): void {
- $app = 'testing';
- $schema = self::validSchemaAllFields;
- // Change storage_type to external and section_type to personal
- $schema['storage_type'] = DeclarativeSettingsTypes::STORAGE_TYPE_EXTERNAL;
- $schema['section_type'] = DeclarativeSettingsTypes::SECTION_TYPE_PERSONAL;
- $this->declarativeManager->registerSchema($app, $schema);
-
- $setDeclarativeSettingsValueEvent = new DeclarativeSettingsSetValueEvent(
- $this->adminUser,
- $app,
- $schema['id'],
- 'some_real_setting',
- '120m'
- );
-
- $this->eventDispatcher->expects($this->once())
- ->method('dispatchTyped')
- ->with($setDeclarativeSettingsValueEvent);
- $this->declarativeManager->setValue($this->adminUser, $app, $schema['id'], 'some_real_setting', '120m');
- }
-
- public function testAdminFormUserUnauthorized(): void {
- $app = 'testing';
- $schema = self::validSchemaAllFields;
- $this->declarativeManager->registerSchema($app, $schema);
-
- // A non-admin user must not receive admin declarative forms, but this must not
- // throw: it would abort rendering of a section a user can legitimately access
- // through admin delegation (which only covers non-declarative settings).
- $forms = $this->declarativeManager->getFormsWithValues($this->user, $schema['section_type'], $schema['section_id']);
- $this->assertEmpty($forms);
-
- // Writing to an admin declarative form is still forbidden for a non-admin user.
- $this->expectException(\Exception::class);
- $this->declarativeManager->setValue($this->user, $app, $schema['id'], 'some_real_setting', '120m');
- }
-
- /**
- * Ensure that the `setValue` method is called if the form implements the handler interface.
- */
- public function testSetValueWithHandler(): void {
- $schema = self::validSchemaAllFields;
- $schema['storage_type'] = DeclarativeSettingsTypes::STORAGE_TYPE_EXTERNAL;
-
- $form = $this->createMock(IDeclarativeSettingsFormWithHandlers::class);
- $form->expects(self::atLeastOnce())
- ->method('getSchema')
- ->willReturn($schema);
- // The setter should be called once!
- $form->expects(self::once())
- ->method('setValue')
- ->with('test_field_2', 'some password', $this->adminUser);
-
- \OC::$server->registerService('OCA\\Testing\\Settings\\DeclarativeForm', fn () => $form, false);
-
- $context = $this->createMock(RegistrationContext::class);
- $context->expects(self::atLeastOnce())
- ->method('getDeclarativeSettings')
- ->willReturn([new ServiceRegistration('testing', 'OCA\\Testing\\Settings\\DeclarativeForm')]);
-
- $this->coordinator->expects(self::atLeastOnce())
- ->method('getRegistrationContext')
- ->willReturn($context);
-
- $this->declarativeManager->loadSchemas();
-
- $this->eventDispatcher->expects(self::never())
- ->method('dispatchTyped');
-
- $this->declarativeManager->setValue($this->adminUser, 'testing', 'test_form_1', 'test_field_2', 'some password');
- }
-
- public function testGetValueWithHandler(): void {
- $schema = self::validSchemaAllFields;
- $schema['storage_type'] = DeclarativeSettingsTypes::STORAGE_TYPE_EXTERNAL;
-
- $form = $this->createMock(IDeclarativeSettingsFormWithHandlers::class);
- $form->expects(self::atLeastOnce())
- ->method('getSchema')
- ->willReturn($schema);
- // The setter should be called once!
- $form->expects(self::once())
- ->method('getValue')
- ->with('test_field_2', $this->adminUser)
- ->willReturn('very secret password');
-
- \OC::$server->registerService('OCA\\Testing\\Settings\\DeclarativeForm', fn () => $form, false);
-
- $context = $this->createMock(RegistrationContext::class);
- $context->expects(self::atLeastOnce())
- ->method('getDeclarativeSettings')
- ->willReturn([new ServiceRegistration('testing', 'OCA\\Testing\\Settings\\DeclarativeForm')]);
-
- $this->coordinator->expects(self::atLeastOnce())
- ->method('getRegistrationContext')
- ->willReturn($context);
-
- $this->declarativeManager->loadSchemas();
-
- $this->eventDispatcher->expects(self::never())
- ->method('dispatchTyped');
-
- $password = $this->invokePrivate($this->declarativeManager, 'getValue', [$this->adminUser, 'testing', 'test_form_1', 'test_field_2']);
- self::assertEquals('very secret password', $password);
- }
-
-}
--- a/tests/lib/Settings/ManagerTest.php
+++ /dev/null
@@ -1,284 +0,0 @@
-logger = $this->createMock(LoggerInterface::class);
- $this->l10n = $this->createMock(IL10N::class);
- $this->l10nFactory = $this->createMock(IFactory::class);
- $this->url = $this->createMock(IURLGenerator::class);
- $this->container = $this->createMock(ContainerInterface::class);
- $this->mapper = $this->createMock(AuthorizedGroupMapper::class);
- $this->groupManager = $this->createMock(IGroupManager::class);
- $this->subAdmin = $this->createMock(ISubAdmin::class);
- $this->appManager = $this->createMock(IAppManager::class);
-
- $this->manager = new Manager(
- $this->logger,
- $this->l10nFactory,
- $this->url,
- $this->container,
- $this->mapper,
- $this->groupManager,
- $this->subAdmin,
- $this->appManager,
- );
- }
-
- public function testGetAdminSections(): void {
- $this->manager->registerSection('admin', Section::class);
-
- $section = Server::get(Section::class);
- $this->container->method('get')
- ->with(Section::class)
- ->willReturn($section);
-
- $this->assertEquals([
- 55 => [$section],
- ], $this->manager->getAdminSections());
- }
-
- public function testGetPersonalSections(): void {
- $this->manager->registerSection('personal', Section::class);
-
- $section = Server::get(Section::class);
- $this->container->method('get')
- ->with(Section::class)
- ->willReturn($section);
-
- $this->assertEquals([
- 55 => [$section],
- ], $this->manager->getPersonalSections());
- }
-
- public function testGetAdminSectionsEmptySection(): void {
- $this->assertEquals([], $this->manager->getAdminSections());
- }
-
- public function testGetPersonalSectionsEmptySection(): void {
- $this->l10nFactory
- ->expects($this->once())
- ->method('get')
- ->with('lib')
- ->willReturn($this->l10n);
- $this->l10n
- ->expects($this->any())
- ->method('t')
- ->willReturnArgument(0);
-
- $this->assertEquals([], $this->manager->getPersonalSections());
- }
-
- public function testGetAdminSettings(): void {
- $section = $this->createMock(ISettings::class);
- $section->method('getPriority')
- ->willReturn(13);
- $section->method('getSection')
- ->willReturn('sharing');
- $this->container->method('get')
- ->with('myAdminClass')
- ->willReturn($section);
-
- $this->manager->registerSetting('admin', 'myAdminClass');
- $settings = $this->manager->getAdminSettings('sharing');
-
- $this->assertEquals([
- 13 => [$section]
- ], $settings);
- }
-
- public function testGetAdminSettingsAsSubAdmin(): void {
- $section = $this->createMock(ISettings::class);
- $section->method('getPriority')
- ->willReturn(13);
- $section->method('getSection')
- ->willReturn('sharing');
- $this->container->method('get')
- ->with('myAdminClass')
- ->willReturn($section);
-
- $this->manager->registerSetting('admin', 'myAdminClass');
- $settings = $this->manager->getAdminSettings('sharing', true);
-
- $this->assertEquals([], $settings);
- }
-
- public function testGetSubAdminSettingsAsSubAdmin(): void {
- $section = $this->createMock(ISubAdminSettings::class);
- $section->method('getPriority')
- ->willReturn(13);
- $section->method('getSection')
- ->willReturn('sharing');
- $this->container->expects($this->once())
- ->method('get')
- ->with('mySubAdminClass')
- ->willReturn($section);
-
- $this->manager->registerSetting('admin', 'mySubAdminClass');
- $settings = $this->manager->getAdminSettings('sharing', true);
-
- $this->assertEquals([
- 13 => [$section]
- ], $settings);
- }
-
- public function testGetPersonalSettings(): void {
- $section = $this->createMock(ISettings::class);
- $section->method('getPriority')
- ->willReturn(16);
- $section->method('getSection')
- ->willReturn('security');
- $section2 = $this->createMock(ISettings::class);
- $section2->method('getPriority')
- ->willReturn(100);
- $section2->method('getSection')
- ->willReturn('security');
-
- $this->manager->registerSetting('personal', 'section1');
- $this->manager->registerSetting('personal', 'section2');
-
- $this->container->expects($this->exactly(2))
- ->method('get')
- ->willReturnMap([
- ['section1', $section],
- ['section2', $section2],
- ]);
-
- $settings = $this->manager->getPersonalSettings('security');
-
- $this->assertEquals([
- 16 => [$section],
- 100 => [$section2],
- ], $settings);
- }
-
- public function testGetPersonalSettingsHidesSettingsOfAppsNotEnabledForUser(): void {
- $visible = $this->createMock(ISettings::class);
- $visible->method('getPriority')
- ->willReturn(16);
- $visible->method('getSection')
- ->willReturn('security');
-
- $this->manager->registerSetting('personal', 'visibleClass', 'enabled_app');
- $this->manager->registerSetting('personal', 'hiddenClass', 'restricted_app');
-
- $this->appManager->method('isEnabledForUser')
- ->willReturnCallback(static fn (string $appId): bool => $appId === 'enabled_app');
-
- // The settings of the app the user has no access to are never instantiated.
- $this->container->expects($this->once())
- ->method('get')
- ->with('visibleClass')
- ->willReturn($visible);
-
- $this->assertEquals([
- 16 => [$visible],
- ], $this->manager->getPersonalSettings('security'));
- }
-
- public function testGetPersonalSectionsHidesSectionsOfAppsNotEnabledForUser(): void {
- $this->l10nFactory->method('get')
- ->with('lib')
- ->willReturn($this->l10n);
- $this->l10n->method('t')
- ->willReturnArgument(0);
-
- $this->manager->registerSection('personal', Section::class, 'restricted_app');
-
- $this->appManager->method('isEnabledForUser')
- ->with('restricted_app')
- ->willReturn(false);
-
- $this->container->expects($this->never())
- ->method('get');
-
- $this->assertEquals([], $this->manager->getPersonalSections());
- }
-
- public function testGetAdminSettingsAreNotHiddenForAppsNotEnabledForUser(): void {
- // Admins configure apps they are not a member of themselves.
- $setting = $this->createMock(ISettings::class);
- $setting->method('getPriority')
- ->willReturn(13);
- $setting->method('getSection')
- ->willReturn('sharing');
-
- $this->manager->registerSetting('admin', 'myAdminClass', 'restricted_app');
-
- $this->appManager->expects($this->never())
- ->method('isEnabledForUser');
- $this->container->method('get')
- ->with('myAdminClass')
- ->willReturn($setting);
-
- $this->assertEquals([
- 13 => [$setting],
- ], $this->manager->getAdminSettings('sharing'));
- }
-
- public function testSameSectionAsPersonalAndAdmin(): void {
- $this->l10nFactory
- ->expects($this->once())
- ->method('get')
- ->with('lib')
- ->willReturn($this->l10n);
- $this->l10n
- ->expects($this->any())
- ->method('t')
- ->willReturnArgument(0);
-
- $this->manager->registerSection('personal', Section::class);
- $this->manager->registerSection('admin', Section::class);
-
- $section = Server::get(Section::class);
- $this->container->method('get')
- ->with(Section::class)
- ->willReturn($section);
-
- $this->assertEquals([
- 55 => [$section],
- ], $this->manager->getPersonalSections());
-
- $this->assertEquals([
- 55 => [$section],
- ], $this->manager->getAdminSections());
- }
-}
--- a/tests/lib/Settings/SectionTest.php
+++ /dev/null
@@ -1,23 +0,0 @@
-assertSame('ldap', (new Section('ldap', 'name', 1))->getID());
- }
- public function testGetName(): void {
- $this->assertSame('name', (new Section('ldap', 'name', 1))->getName());
- }
- public function testGetPriority(): void {
- $this->assertSame(1, (new Section('ldap', 'name', 1))->getPriority());
- }
-}
--- a/tests/lib/Setup/AbstractDatabaseTest.php
+++ /dev/null
@@ -1,289 +0,0 @@
-config = $this->createMock(SystemConfig::class);
- $this->connectionFactory = $this->createMock(ConnectionFactory::class);
- $this->connection = $this->createMock(Connection::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $l10n = $this->createMock(IL10N::class);
- $l10n->method('t')
- ->willReturnCallback(fn (string $text, array $parameters = []) => vsprintf($text, $parameters));
-
- $this->database = new TestDatabase(
- $l10n,
- $this->config,
- $this->logger,
- $this->createMock(ISecureRandom::class),
- );
- $this->database->connectionFactory = $this->connectionFactory;
- }
-
- public function testInitializeWritesConnectionConfig(): void {
- $this->config->expects($this->once())
- ->method('setValues')
- ->with([
- 'dbname' => 'nextcloud',
- 'dbhost' => 'db.example.org:5432',
- 'dbtableprefix' => 'nc_',
- ]);
-
- $this->database->initialize([
- 'dbuser' => 'admin',
- 'dbpass' => 'admin-password',
- 'dbname' => 'nextcloud',
- 'dbhost' => 'db.example.org:5432',
- 'dbtableprefix' => 'nc_',
- ]);
- }
-
- public function testInitializeFallsBackToLocalhost(): void {
- $this->config->expects($this->once())
- ->method('setValues')
- ->with([
- 'dbname' => 'nextcloud',
- 'dbhost' => 'localhost',
- 'dbtableprefix' => 'oc_',
- ]);
-
- $this->database->initialize([
- 'dbuser' => 'admin',
- 'dbpass' => 'admin-password',
- 'dbname' => 'nextcloud',
- 'dbhost' => '',
- ]);
- }
-
- /**
- * The connection encryption options are only read from the system config, so they have
- * to be persisted by initialize() - before any connection is opened by setupDatabase().
- */
- public function testInitializePersistsDriverOptions(): void {
- $this->config->expects($this->once())
- ->method('setValues')
- ->with([
- 'dbname' => 'nextcloud',
- 'dbhost' => 'db.example.org',
- 'dbtableprefix' => 'oc_',
- 'dbdriveroptions' => [self::MYSQL_ATTR_SSL_CA => '/ca.pem'],
- ]);
-
- $this->database->initialize($this->options([
- 'dbdriveroptions' => [self::MYSQL_ATTR_SSL_CA => '/ca.pem'],
- ]));
- }
-
- /**
- * Only the options of the database being set up may be persisted, every database
- * configures an encrypted connection differently.
- */
- public function testInitializeSkipsOptionsOfOtherDatabases(): void {
- $this->config->expects($this->once())
- ->method('setValues')
- ->with([
- 'dbname' => 'nextcloud',
- 'dbhost' => 'db.example.org',
- 'dbtableprefix' => 'oc_',
- ]);
-
- $this->database->initialize($this->options([
- 'pgsql_ssl' => ['mode' => 'verify-full'],
- ]));
- }
-
- public static function emptyEncryptionOptions(): array {
- return [
- 'not provided' => [[]],
- 'empty array' => [['dbdriveroptions' => []]],
- 'null' => [['dbdriveroptions' => null]],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('emptyEncryptionOptions')]
- public function testInitializeSkipsEmptyEncryptionOptions(array $additional): void {
- $this->config->expects($this->once())
- ->method('setValues')
- ->with([
- 'dbname' => 'nextcloud',
- 'dbhost' => 'db.example.org',
- 'dbtableprefix' => 'oc_',
- ]);
-
- $this->database->initialize($this->options($additional));
- }
-
- /**
- * A malformed option must never be persisted, as that would end up configuring an
- * unencrypted connection while the admin expects an encrypted one.
- */
- public function testInitializeRejectsMalformedEncryptionOptions(): void {
- $this->config->expects($this->once())
- ->method('setValues')
- ->with([
- 'dbname' => 'nextcloud',
- 'dbhost' => 'db.example.org',
- 'dbtableprefix' => 'oc_',
- ]);
- $this->logger->expects($this->once())
- ->method('error');
-
- $this->database->initialize($this->options(['dbdriveroptions' => '/ca.pem']));
- }
-
- public function testValidateRejectsMalformedEncryptionOptions(): void {
- $errors = $this->database->validate($this->options(['dbdriveroptions' => '/ca.pem']));
-
- $this->assertEquals([
- 'The database option "dbdriveroptions" for Test has to be a list of values',
- ], $errors);
- }
-
- public static function encryptionOptions(): array {
- return [
- 'dbsslmode' => ['dbsslmode', 'verify-full'],
- 'dbsslca' => ['dbsslca', '/ca.pem'],
- 'dbsslcert' => ['dbsslcert', '/client.crt'],
- 'dbsslkey' => ['dbsslkey', '/client.key'],
- 'dbsslcrl' => ['dbsslcrl', '/crl.pem'],
- 'dbsslnoverify' => ['dbsslnoverify', true],
- ];
- }
-
- /**
- * A database that cannot be configured to use an encrypted connection has to reject
- * every such option instead of installing an unencrypted instance silently.
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('encryptionOptions')]
- public function testValidateRejectsUnsupportedEncryptionOptions(string $option, string|bool $value): void {
- $errors = $this->database->validate($this->options([$option => $value]));
-
- $this->assertContains("The database option \"$option\" is not supported by Test", $errors);
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('encryptionOptions')]
- public function testInitializeSkipsUnsupportedEncryptionOptions(string $option, string|bool $value): void {
- $this->config->expects($this->once())
- ->method('setValues')
- ->with([
- 'dbname' => 'nextcloud',
- 'dbhost' => 'db.example.org',
- 'dbtableprefix' => 'oc_',
- ]);
-
- $this->database->initialize($this->options([$option => $value]));
- }
-
- public function testValidateAcceptsEncryptionOptions(): void {
- $errors = $this->database->validate($this->options([
- 'dbdriveroptions' => [self::MYSQL_ATTR_SSL_CA => '/ca.pem'],
- // not an option of this database, so it is not validated either
- 'pgsql_ssl' => 'verify-full',
- ]));
-
- $this->assertEquals([], $errors);
- }
-
- /**
- * Host, database name and table prefix must not be passed as additional parameters:
- * they are resolved from the system config by the connection factory, so that setup
- * connects exactly like the installed instance will.
- */
- public function testConnectOnlyPassesCredentials(): void {
- $this->expectConnection('mysql', [
- 'user' => 'admin',
- 'password' => 'admin-password',
- ]);
-
- $this->database->initialize($this->options());
-
- $this->assertSame($this->connection, $this->database->connectForTest());
- }
-
- public function testConnectPassesPort(): void {
- $this->expectConnection('pgsql', [
- 'user' => 'admin',
- 'password' => 'admin-password',
- 'port' => 5432,
- ]);
-
- $this->database->initialize($this->options(['dbport' => '5432']));
- $this->database->connectForTest();
- }
-
- public function testConnectPassesSocket(): void {
- $this->expectConnection('mysql', [
- 'user' => 'admin',
- 'password' => 'admin-password',
- 'unix_socket' => '/var/run/mysqld/mysqld.sock',
- ]);
-
- $this->database->initialize($this->options(['dbport' => '/var/run/mysqld/mysqld.sock']));
- $this->database->connectForTest();
- }
-
- public function testConnectAppliesConfigOverwrite(): void {
- $this->expectConnection('pgsql', [
- 'user' => 'admin',
- 'password' => 'admin-password',
- 'dbname' => 'postgres',
- ]);
-
- $this->database->initialize($this->options());
- $this->database->connectForTest(['dbname' => 'postgres']);
- }
-
- private function options(array $additional = []): array {
- return array_merge([
- 'dbuser' => 'admin',
- 'dbpass' => 'admin-password',
- 'dbname' => 'nextcloud',
- 'dbhost' => 'db.example.org',
- ], $additional);
- }
-
- private function expectConnection(string $dbType, array $expectedParams): void {
- $this->config->method('getValue')
- ->willReturnCallback(fn ($key, $default = '') => $key === 'dbtype' ? $dbType : $default);
-
- $this->connectionFactory->expects($this->once())
- ->method('getConnection')
- ->with($dbType, $expectedParams)
- ->willReturn($this->connection);
-
- $this->connection->expects($this->once())
- ->method('ensureConnectedToPrimary');
- }
-}
--- a/tests/lib/Setup/MySQLTest.php
+++ /dev/null
@@ -1,170 +0,0 @@
-config = $this->createMock(SystemConfig::class);
-
- $l10n = $this->createMock(IL10N::class);
- $l10n->method('t')
- ->willReturnCallback(fn (string $text, array $parameters = []) => vsprintf($text, $parameters));
-
- $this->database = new MySQL(
- $l10n,
- $this->config,
- $this->createMock(LoggerInterface::class),
- $this->createMock(ISecureRandom::class),
- );
- }
-
- /**
- * The numeric value of a PDO MySQL attribute, e.g. `SSL_CA`.
- *
- * The values are not stable across PHP versions, so they must never be hardcoded.
- * Since PHP 8.5 the `PDO::MYSQL_ATTR_*` constants are deprecated in favor of
- * `Pdo\Mysql::ATTR_*`, and either only exists with the MySQL driver installed.
- */
- private function attribute(string $name): int {
- if (!extension_loaded('pdo_mysql')) {
- $this->markTestSkipped('The pdo_mysql extension is required to resolve the PDO attribute values');
- }
- if (PHP_VERSION_ID >= 80500 && class_exists(\Pdo\Mysql::class)) {
- return (int)constant('Pdo\Mysql::ATTR_' . $name);
- }
- return (int)constant('PDO::MYSQL_ATTR_' . $name);
- }
-
- /**
- * MySQL/MariaDB is configured through PDO driver options, keyed by the numeric PDO
- * attributes - which is why the web installer and CLI cannot pass them directly.
- */
- public function testInitializeMapsEncryptionOptions(): void {
- $this->config->expects($this->once())
- ->method('setValues')
- ->with([
- 'dbname' => 'nextcloud',
- 'dbhost' => 'db.example.org',
- 'dbtableprefix' => 'oc_',
- 'dbdriveroptions' => [
- $this->attribute('SSL_CA') => '/ca.pem',
- $this->attribute('SSL_CERT') => '/client.crt',
- $this->attribute('SSL_KEY') => '/client.key',
- $this->attribute('SSL_VERIFY_SERVER_CERT') => false,
- ],
- ]);
-
- $this->database->initialize($this->options([
- 'dbsslca' => '/ca.pem',
- 'dbsslcert' => '/client.crt',
- 'dbsslkey' => '/client.key',
- 'dbsslnoverify' => true,
- ]));
- }
-
- /**
- * Driver options provided as raw config value, e.g. through an autoconfig file, must
- * survive - and their numeric keys must not be renumbered.
- */
- public function testInitializeMergesWithRawDriverOptions(): void {
- $this->config->expects($this->once())
- ->method('setValues')
- ->with([
- 'dbname' => 'nextcloud',
- 'dbhost' => 'db.example.org',
- 'dbtableprefix' => 'oc_',
- 'dbdriveroptions' => [
- $this->attribute('INIT_COMMAND') => 'SET wait_timeout = 28800',
- $this->attribute('SSL_CA') => '/ca.pem',
- ],
- ]);
-
- $this->database->initialize($this->options([
- 'dbdriveroptions' => [$this->attribute('INIT_COMMAND') => 'SET wait_timeout = 28800'],
- 'dbsslca' => '/ca.pem',
- ]));
- }
-
- public function testInitializeSkipsEmptyEncryptionOptions(): void {
- $this->config->expects($this->once())
- ->method('setValues')
- ->with([
- 'dbname' => 'nextcloud',
- 'dbhost' => 'db.example.org',
- 'dbtableprefix' => 'oc_',
- ]);
-
- $this->database->initialize($this->options([
- 'dbsslca' => '',
- 'dbsslcert' => '',
- 'dbsslkey' => '',
- 'dbsslnoverify' => false,
- ]));
- }
-
- public static function unsupportedEncryptionOptions(): array {
- return [
- // There is no PDO equivalent of the PostgreSQL sslmode
- 'dbsslmode' => ['dbsslmode', 'require'],
- // A revocation list cannot be passed through PDO
- 'dbsslcrl' => ['dbsslcrl', '/crl.pem'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('unsupportedEncryptionOptions')]
- public function testValidateRejectsUnsupportedEncryptionOptions(string $option, string $value): void {
- $errors = $this->database->validate($this->options([$option => $value]));
-
- $this->assertEquals([
- "The database option \"$option\" is not supported by MySQL/MariaDB",
- ], $errors);
- }
-
- public function testValidateRejectsIncompleteClientCertificate(): void {
- $errors = $this->database->validate($this->options(['dbsslcert' => '/client.crt']));
-
- $this->assertEquals([
- 'The database options "dbsslcert" and "dbsslkey" have to be provided together',
- ], $errors);
- }
-
- public function testValidateAcceptsEncryptionOptions(): void {
- $errors = $this->database->validate($this->options([
- 'dbsslca' => '/ca.pem',
- 'dbsslcert' => '/client.crt',
- 'dbsslkey' => '/client.key',
- 'dbsslnoverify' => true,
- ]));
-
- $this->assertEquals([], $errors);
- }
-
- private function options(array $additional = []): array {
- return array_merge([
- 'dbuser' => 'admin',
- 'dbpass' => 'admin-password',
- 'dbname' => 'nextcloud',
- 'dbhost' => 'db.example.org',
- ], $additional);
- }
-}
--- a/tests/lib/Setup/PostgreSQLTest.php
+++ /dev/null
@@ -1,202 +0,0 @@
- 'verify-full',
- 'rootcert' => '/rootCA.crt',
- 'cert' => '/client.crt',
- 'key' => '/client.key',
- ];
-
- private SystemConfig&MockObject $config;
- private LoggerInterface&MockObject $logger;
- private PostgreSQL $database;
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- $this->config = $this->createMock(SystemConfig::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $l10n = $this->createMock(IL10N::class);
- $l10n->method('t')
- ->willReturnCallback(fn (string $text, array $parameters = []) => vsprintf($text, $parameters));
-
- $this->database = new PostgreSQL(
- $l10n,
- $this->config,
- $this->logger,
- $this->createMock(ISecureRandom::class),
- );
- }
-
- /**
- * PostgreSQL is configured through its own set of connection parameters instead of PDO
- * driver options. They are only read from the system config, so they have to be
- * persisted by initialize() - before any connection is opened by setupDatabase().
- */
- public function testInitializePersistsPgsqlSsl(): void {
- $this->config->expects($this->once())
- ->method('setValues')
- ->with([
- 'dbname' => 'nextcloud',
- 'dbhost' => 'db.example.org',
- 'dbtableprefix' => 'oc_',
- 'pgsql_ssl' => self::PGSQL_SSL,
- ]);
-
- $this->database->initialize($this->options(['pgsql_ssl' => self::PGSQL_SSL]));
- }
-
- /**
- * The database independent options provided by the web installer and the CLI have to
- * end up in the `pgsql_ssl` connection parameters.
- */
- public function testInitializeMapsEncryptionOptions(): void {
- $this->config->expects($this->once())
- ->method('setValues')
- ->with([
- 'dbname' => 'nextcloud',
- 'dbhost' => 'db.example.org',
- 'dbtableprefix' => 'oc_',
- 'pgsql_ssl' => self::PGSQL_SSL + ['crl' => '/client.crl'],
- ]);
-
- $this->database->initialize($this->options([
- 'dbsslmode' => 'verify-full',
- 'dbsslca' => '/rootCA.crt',
- 'dbsslcert' => '/client.crt',
- 'dbsslkey' => '/client.key',
- 'dbsslcrl' => '/client.crl',
- ]));
- }
-
- /**
- * A `pgsql_ssl` value provided as raw config value, e.g. through an autoconfig file,
- * must survive the mapped options.
- */
- public function testInitializeMergesWithRawPgsqlSsl(): void {
- $this->config->expects($this->once())
- ->method('setValues')
- ->with([
- 'dbname' => 'nextcloud',
- 'dbhost' => 'db.example.org',
- 'dbtableprefix' => 'oc_',
- 'pgsql_ssl' => ['rootcert' => '/rootCA.crt', 'mode' => 'verify-full'],
- ]);
-
- $this->database->initialize($this->options([
- 'pgsql_ssl' => ['rootcert' => '/rootCA.crt'],
- 'dbsslmode' => 'verify-full',
- ]));
- }
-
- public function testValidateRejectsUnsupportedEncryptionOptions(): void {
- // There is no PDO attribute to skip the host verification for PostgreSQL,
- // the sslmode covers it
- $errors = $this->database->validate($this->options(['dbsslnoverify' => true]));
-
- $this->assertEquals([
- 'The database option "dbsslnoverify" is not supported by PostgreSQL',
- ], $errors);
- }
-
- public function testValidateRejectsIncompleteClientCertificate(): void {
- $errors = $this->database->validate($this->options(['dbsslkey' => '/client.key']));
-
- $this->assertEquals([
- 'The database options "dbsslcert" and "dbsslkey" have to be provided together',
- ], $errors);
- }
-
- public function testValidateAcceptsEncryptionOptions(): void {
- $errors = $this->database->validate($this->options([
- 'dbsslmode' => 'verify-full',
- 'dbsslca' => '/rootCA.crt',
- 'dbsslcert' => '/client.crt',
- 'dbsslkey' => '/client.key',
- 'dbsslcrl' => '/client.crl',
- ]));
-
- $this->assertEquals([], $errors);
- }
-
- public static function emptyPgsqlSsl(): array {
- return [
- 'not provided' => [[]],
- 'empty array' => [['pgsql_ssl' => []]],
- 'null' => [['pgsql_ssl' => null]],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('emptyPgsqlSsl')]
- public function testInitializeSkipsEmptyPgsqlSsl(array $additional): void {
- $this->config->expects($this->once())
- ->method('setValues')
- ->with([
- 'dbname' => 'nextcloud',
- 'dbhost' => 'db.example.org',
- 'dbtableprefix' => 'oc_',
- ]);
-
- $this->database->initialize($this->options($additional));
- }
-
- /**
- * A malformed option must never be persisted, as that would end up configuring an
- * unencrypted connection while the admin expects an encrypted one.
- */
- public function testInitializeRejectsMalformedPgsqlSsl(): void {
- $this->config->expects($this->once())
- ->method('setValues')
- ->with([
- 'dbname' => 'nextcloud',
- 'dbhost' => 'db.example.org',
- 'dbtableprefix' => 'oc_',
- ]);
- $this->logger->expects($this->once())
- ->method('error');
-
- $this->database->initialize($this->options(['pgsql_ssl' => 'verify-full']));
- }
-
- public function testValidateRejectsMalformedPgsqlSsl(): void {
- $errors = $this->database->validate($this->options(['pgsql_ssl' => 'verify-full']));
-
- $this->assertEquals([
- 'The database option "pgsql_ssl" for PostgreSQL has to be a list of values',
- ], $errors);
- }
-
- public function testValidateAcceptsPgsqlSsl(): void {
- $errors = $this->database->validate($this->options(['pgsql_ssl' => self::PGSQL_SSL]));
-
- $this->assertEquals([], $errors);
- }
-
- private function options(array $additional = []): array {
- return array_merge([
- 'dbuser' => 'admin',
- 'dbpass' => 'admin-password',
- 'dbname' => 'nextcloud',
- 'dbhost' => 'db.example.org',
- ], $additional);
- }
-}
--- a/tests/lib/Setup/TestDatabase.php
+++ /dev/null
@@ -1,36 +0,0 @@
-connectionFactory;
- }
-
- #[\Override]
- public function setupDatabase(): void {
- }
-
- public function connectForTest(array $configOverwrite = []): Connection {
- return $this->connect($configOverwrite);
- }
-}
--- a/tests/lib/SetupCheck/CheckServerResponseTraitImplementation.php
+++ /dev/null
@@ -1,40 +0,0 @@
-l10n = $this->createMock(IL10N::class);
- $this->l10n->method('t')
- ->willReturnArgument(0);
- $this->config = $this->createMock(IConfig::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->clientService = $this->createMock(IClientService::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $this->trait = new CheckServerResponseTraitImplementation(
- $this->l10n,
- $this->config,
- $this->urlGenerator,
- $this->clientService,
- $this->logger,
- );
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataNormalizeUrl')]
- public function testNormalizeUrl(string $url, bool $isRootRequest, string $expected): void {
- $this->assertEquals($expected, $this->trait->normalizeUrl($url, $isRootRequest));
- }
-
- public static function dataNormalizeUrl(): array {
- return [
- // untouched web-root
- 'valid and nothing to change' => ['http://example.com/root', false, 'http://example.com/root'],
- 'valid with port and nothing to change' => ['http://example.com:8081/root', false, 'http://example.com:8081/root'],
- 'trailing slash' => ['http://example.com/root/', false, 'http://example.com/root'],
- 'deep web root' => ['http://example.com/deep/webroot', false, 'http://example.com/deep/webroot'],
- // removal of the web-root
- 'remove web root' => ['http://example.com/root/', true, 'http://example.com'],
- 'remove web root but empty' => ['http://example.com', true, 'http://example.com'],
- 'remove deep web root' => ['http://example.com/deep/webroot', true, 'http://example.com'],
- 'remove web root with port' => ['http://example.com:8081/root', true, 'http://example.com:8081'],
- 'remove web root with port but empty' => ['http://example.com:8081', true, 'http://example.com:8081'],
- 'remove web root from IP' => ['https://127.0.0.1/root', true, 'https://127.0.0.1'],
- 'remove web root from IP with port' => ['https://127.0.0.1:8080/root', true, 'https://127.0.0.1:8080'],
- 'remove web root from IPv6' => ['https://[ff02::1]/root', true, 'https://[ff02::1]'],
- 'remove web root from IPv6 with port' => ['https://[ff02::1]:8080/root', true, 'https://[ff02::1]:8080'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetTestUrls')]
- public function testGetTestUrls(
- string $url,
- bool $isRootRequest,
- string $cliUrl,
- string $webRoot,
- array $trustedDomains,
- array $expected,
- ): void {
- $this->config->expects(self::atLeastOnce())
- ->method('getSystemValueString')
- ->with('overwrite.cli.url', '')
- ->willReturn($cliUrl);
-
- $this->config->expects(self::atLeastOnce())
- ->method('getSystemValue')
- ->with('trusted_domains', [])
- ->willReturn($trustedDomains);
-
- $this->urlGenerator->expects(self::atLeastOnce())
- ->method('getWebroot')
- ->willReturn($webRoot);
-
- $this->urlGenerator->expects(self::atLeastOnce())
- ->method('getBaseUrl')
- ->willReturn(self::BASE_URL . $webRoot);
-
- $result = $this->trait->getTestUrls($url, $isRootRequest);
- $this->assertEquals($expected, $result);
- }
-
- /**
- * @return array, list}>
- */
- public static function dataGetTestUrls(): array {
- return [
- 'same cli and base URL' => [
- '/apps/files/js/example.js', false, 'https://nextcloud.local', '', ['nextcloud.local'], [
- // from cli url
- 'https://nextcloud.local/apps/files/js/example.js',
- // http variant from trusted domains
- 'http://nextcloud.local/apps/files/js/example.js',
- ]
- ],
- 'different cli and base URL' => [
- '/apps/files/js/example.js', false, 'https://example.com', '', ['nextcloud.local'], [
- // from cli url
- 'https://example.com/apps/files/js/example.js',
- // from base url
- 'https://nextcloud.local/apps/files/js/example.js',
- // http variant from trusted domains
- 'http://nextcloud.local/apps/files/js/example.js',
- ]
- ],
- 'different cli and base URL and trusted domains' => [
- '/apps/files/js/example.js', false, 'https://example.com', '', ['nextcloud.local', 'example.com', '127.0.0.1'], [
- // from cli url
- 'https://example.com/apps/files/js/example.js',
- // from base url
- 'https://nextcloud.local/apps/files/js/example.js',
- // http variant from trusted domains
- 'http://nextcloud.local/apps/files/js/example.js',
- 'http://example.com/apps/files/js/example.js',
- // trusted domains
- 'https://127.0.0.1/apps/files/js/example.js',
- 'http://127.0.0.1/apps/files/js/example.js',
- ]
- ],
- 'wildcard trusted domains' => [
- '/apps/files/js/example.js', false, '', '', ['nextcloud.local', '*.example.com'], [
- // from base url
- 'https://nextcloud.local/apps/files/js/example.js',
- // http variant from trusted domains
- 'http://nextcloud.local/apps/files/js/example.js',
- // trusted domains with wild card are skipped
- ]
- ],
- 'missing leading slash' => [
- 'apps/files/js/example.js', false, 'https://nextcloud.local', '', ['nextcloud.local'], [
- // from cli url
- 'https://nextcloud.local/apps/files/js/example.js',
- // http variant from trusted domains
- 'http://nextcloud.local/apps/files/js/example.js',
- ]
- ],
- 'keep web-root' => [
- '/apps/files/js/example.js', false, 'https://example.com', '/nextcloud', ['nextcloud.local', 'example.com', '192.168.100.1'], [
- // from cli url (note that the CLI url has NO web root)
- 'https://example.com/apps/files/js/example.js',
- // from base url
- 'https://nextcloud.local/nextcloud/apps/files/js/example.js',
- // http variant from trusted domains
- 'http://nextcloud.local/nextcloud/apps/files/js/example.js',
- // trusted domains with web-root
- 'https://example.com/nextcloud/apps/files/js/example.js',
- 'http://example.com/nextcloud/apps/files/js/example.js',
- 'https://192.168.100.1/nextcloud/apps/files/js/example.js',
- 'http://192.168.100.1/nextcloud/apps/files/js/example.js',
- ]
- ],
- // example if the URL is generated by the URL generator
- 'keep web-root and web root in url' => [
- '/nextcloud/apps/files/js/example.js', false, 'https://example.com', '/nextcloud', ['nextcloud.local', 'example.com', '192.168.100.1'], [
- // from cli url (note that the CLI url has NO web root)
- 'https://example.com/apps/files/js/example.js',
- // from base url
- 'https://nextcloud.local/nextcloud/apps/files/js/example.js',
- // http variant from trusted domains
- 'http://nextcloud.local/nextcloud/apps/files/js/example.js',
- // trusted domains with web-root
- 'https://example.com/nextcloud/apps/files/js/example.js',
- 'http://example.com/nextcloud/apps/files/js/example.js',
- 'https://192.168.100.1/nextcloud/apps/files/js/example.js',
- 'http://192.168.100.1/nextcloud/apps/files/js/example.js',
- ]
- ],
- 'remove web-root' => [
- '/.well-known/caldav', true, 'https://example.com', '/nextcloud', ['nextcloud.local', 'example.com', '192.168.100.1'], [
- // from cli url (note that the CLI url has NO web root)
- 'https://example.com/.well-known/caldav',
- // from base url
- 'https://nextcloud.local/.well-known/caldav',
- // http variant from trusted domains
- 'http://nextcloud.local/.well-known/caldav',
- 'http://example.com/.well-known/caldav',
- // trusted domains with web-root
- 'https://192.168.100.1/.well-known/caldav',
- 'http://192.168.100.1/.well-known/caldav',
- ]
- ],
- ];
- }
-
-}
--- a/tests/lib/SetupCheck/SetupCheckManagerTest.php
+++ /dev/null
@@ -1,158 +0,0 @@
- */
- public static array $ran = [];
-
- #[\Override]
- public function getCategory(): string {
- return 'security';
- }
-
- #[\Override]
- public function getName(): string {
- return 'Security test check';
- }
-
- #[\Override]
- public function run(): SetupResult {
- self::$ran[] = static::class;
- return SetupResult::success();
- }
-}
-
-class SetupCheckManagerTestSystemCheck extends SetupCheckManagerTestSecurityCheck {
- #[\Override]
- public function getCategory(): string {
- return 'system';
- }
-
- #[\Override]
- public function getName(): string {
- return 'System test check';
- }
-}
-
-class SetupCheckManagerTest extends TestCase {
- private Coordinator&MockObject $coordinator;
- private LoggerInterface&MockObject $logger;
- private SetupCheckManager $manager;
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- SetupCheckManagerTestSecurityCheck::$ran = [];
-
- $registrationContext = $this->createMock(RegistrationContext::class);
- $registrationContext->method('getSetupChecks')
- ->willReturn([
- new ServiceRegistration('test', SetupCheckManagerTestSecurityCheck::class),
- new ServiceRegistration('test', SetupCheckManagerTestSystemCheck::class),
- ]);
-
- $this->coordinator = $this->createMock(Coordinator::class);
- $this->coordinator->method('getRegistrationContext')
- ->willReturn($registrationContext);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $this->manager = new SetupCheckManager($this->coordinator, $this->logger);
- }
-
- /**
- * Record the reported progress, together with the checks that already ran when it was reported.
- *
- * @param list}> $reported
- */
- private function createOutput(array &$reported): IOutput&MockObject {
- $output = $this->createMock(IOutput::class);
- $output->method('debug')
- ->willReturnCallback(function (string $message) use (&$reported): void {
- $reported[] = [$message, SetupCheckManagerTestSecurityCheck::$ran];
- });
- return $output;
- }
-
- public function testRunAllReportsEveryCheckBeforeAndAfterItIsRun(): void {
- $reported = [];
-
- $results = $this->manager->runAll($this->createOutput($reported));
-
- $security = preg_quote(SetupCheckManagerTestSecurityCheck::class, '/');
- $system = preg_quote(SetupCheckManagerTestSystemCheck::class, '/');
-
- $this->assertCount(4, $reported);
- // The check is reported before it gets a chance to run
- $this->assertSame('Starting check Security test check (' . SetupCheckManagerTestSecurityCheck::class . ')', $reported[0][0]);
- $this->assertSame([], $reported[0][1]);
- $this->assertMatchesRegularExpression(
- '/^Check Security test check \(' . $security . '\) done in \d+\.\d\d seconds, peak memory usage: /',
- $reported[1][0]
- );
- $this->assertSame([SetupCheckManagerTestSecurityCheck::class], $reported[1][1]);
- $this->assertSame('Starting check System test check (' . SetupCheckManagerTestSystemCheck::class . ')', $reported[2][0]);
- $this->assertSame([SetupCheckManagerTestSecurityCheck::class], $reported[2][1]);
- $this->assertMatchesRegularExpression(
- '/^Check System test check \(' . $system . '\) done in \d+\.\d\d seconds, peak memory usage: /',
- $reported[3][0]
- );
-
- $this->assertEquals(
- ['security' => [SetupCheckManagerTestSecurityCheck::class], 'system' => [SetupCheckManagerTestSystemCheck::class]],
- array_map(array_keys(...), $results)
- );
- }
-
- public function testRunByCategoryOnlyReportsMatchingChecks(): void {
- $reported = [];
-
- $this->manager->runByCategory('system', $this->createOutput($reported));
-
- $this->assertCount(2, $reported);
- $this->assertSame('Starting check System test check (' . SetupCheckManagerTestSystemCheck::class . ')', $reported[0][0]);
- $this->assertStringStartsWith('Check System test check (' . SetupCheckManagerTestSystemCheck::class . ') done in ', $reported[1][0]);
- $this->assertEquals([SetupCheckManagerTestSystemCheck::class], SetupCheckManagerTestSecurityCheck::$ran);
- }
-
- public function testRunByClassOnlyReportsMatchingChecks(): void {
- $reported = [];
-
- $this->manager->runByClass(SetupCheckManagerTestSecurityCheck::class, $this->createOutput($reported));
-
- $this->assertCount(2, $reported);
- $this->assertStringContainsString(SetupCheckManagerTestSecurityCheck::class, $reported[0][0]);
- $this->assertEquals([SetupCheckManagerTestSecurityCheck::class], SetupCheckManagerTestSecurityCheck::$ran);
- }
-
- public function testRunByClassAcceptsALeadingBackslash(): void {
- $results = $this->manager->runByClass('\\' . SetupCheckManagerTestSystemCheck::class);
-
- $this->assertEquals(['system' => [SetupCheckManagerTestSystemCheck::class]], array_map(array_keys(...), $results));
- }
-
- public function testRunWithoutOutput(): void {
- $results = $this->manager->runAll();
-
- $this->assertCount(2, $results);
- }
-}
--- a/tests/lib/SetupTest.php
+++ /dev/null
@@ -1,176 +0,0 @@
-config = $this->createMock(SystemConfig::class);
- $this->iniWrapper = $this->createMock(IniGetWrapper::class);
- $this->l10n = $this->createMock(IL10N::class);
- $this->l10nFactory = $this->createMock(IL10NFactory::class);
- $this->l10nFactory->method('get')
- ->willReturn($this->l10n);
- $this->defaults = $this->createMock(Defaults::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->random = $this->createMock(ISecureRandom::class);
- $this->installer = $this->createMock(Installer::class);
- $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
- $this->setupClass = $this->getMockBuilder(Setup::class)
- ->onlyMethods(['class_exists', 'is_callable', 'getAvailableDbDriversForPdo'])
- ->setConstructorArgs([$this->config, $this->iniWrapper, $this->l10nFactory, $this->defaults, $this->logger, $this->random, $this->installer, $this->eventDispatcher])
- ->getMock();
- }
-
- public function testGetSupportedDatabasesWithOneWorking(): void {
- $this->config
- ->expects($this->once())
- ->method('getValue')
- ->willReturn(
- ['sqlite', 'mysql', 'oci']
- );
- $this->setupClass
- ->expects($this->once())
- ->method('is_callable')
- ->willReturn(false);
- $this->setupClass
- ->expects($this->any())
- ->method('getAvailableDbDriversForPdo')
- ->willReturn(['sqlite']);
- $result = $this->setupClass->getSupportedDatabases();
- $expectedResult = [
- 'sqlite' => 'SQLite'
- ];
-
- $this->assertSame($expectedResult, $result);
- }
-
- public function testGetSupportedDatabasesWithNoWorking(): void {
- $this->config
- ->expects($this->once())
- ->method('getValue')
- ->willReturn(
- ['sqlite', 'mysql', 'oci', 'pgsql']
- );
- $this->setupClass
- ->expects($this->any())
- ->method('is_callable')
- ->willReturn(false);
- $this->setupClass
- ->expects($this->any())
- ->method('getAvailableDbDriversForPdo')
- ->willReturn([]);
- $result = $this->setupClass->getSupportedDatabases();
-
- $this->assertSame([], $result);
- }
-
- public function testGetSupportedDatabasesWithAllWorking(): void {
- $this->config
- ->expects($this->once())
- ->method('getValue')
- ->willReturn(
- ['sqlite', 'mysql', 'pgsql', 'oci']
- );
- $this->setupClass
- ->expects($this->any())
- ->method('is_callable')
- ->willReturn(true);
- $this->setupClass
- ->expects($this->any())
- ->method('getAvailableDbDriversForPdo')
- ->willReturn(['sqlite', 'mysql', 'pgsql']);
- $result = $this->setupClass->getSupportedDatabases();
- $expectedResult = [
- 'sqlite' => 'SQLite',
- 'mysql' => 'MySQL/MariaDB',
- 'pgsql' => 'PostgreSQL',
- 'oci' => 'Oracle'
- ];
- $this->assertSame($expectedResult, $result);
- }
-
- public function testGetSupportedDatabaseException(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Supported databases are not properly configured.');
-
- $this->config
- ->expects($this->once())
- ->method('getValue')
- ->willReturn('NotAnArray');
- $this->setupClass->getSupportedDatabases();
- }
-
- /**
- * @param $url
- * @param $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('findWebRootProvider')]
- public function testFindWebRootCli($url, $expected): void {
- $cliState = \OC::$CLI;
-
- $this->config
- ->expects($this->once())
- ->method('getValue')
- ->willReturn($url);
- \OC::$CLI = true;
-
- try {
- $webRoot = self::invokePrivate($this->setupClass, 'findWebRoot', [$this->config]);
- } catch (\InvalidArgumentException $e) {
- $webRoot = false;
- }
-
- \OC::$CLI = $cliState;
- $this->assertSame($webRoot, $expected);
- }
-
- public static function findWebRootProvider(): array {
- return [
- 'https://www.example.com/nextcloud/' => ['https://www.example.com/nextcloud/', '/nextcloud'],
- 'https://www.example.com/nextcloud' => ['https://www.example.com/nextcloud', '/nextcloud'],
- 'https://www.example.com/' => ['https://www.example.com/', ''],
- 'https://www.example.com' => ['https://www.example.com', ''],
- 'https://nctest13pgsql.lan/test123/' => ['https://nctest13pgsql.lan/test123/', '/test123'],
- 'https://nctest13pgsql.lan/test123' => ['https://nctest13pgsql.lan/test123', '/test123'],
- 'https://nctest13pgsql.lan/' => ['https://nctest13pgsql.lan/', ''],
- 'https://nctest13pgsql.lan' => ['https://nctest13pgsql.lan', ''],
- 'https://192.168.10.10/nc/' => ['https://192.168.10.10/nc/', '/nc'],
- 'https://192.168.10.10/nc' => ['https://192.168.10.10/nc', '/nc'],
- 'https://192.168.10.10/' => ['https://192.168.10.10/', ''],
- 'https://192.168.10.10' => ['https://192.168.10.10', ''],
- 'invalid' => ['invalid', false],
- 'empty' => ['', false],
- ];
- }
-}
--- a/tests/lib/Share20/DefaultShareProviderTest.php
+++ /dev/null
@@ -1,3139 +0,0 @@
-dbConn = Server::get(IDBConnection::class);
- $this->userManager = $this->createMock(IUserManager::class);
- $this->groupManager = $this->createMock(IGroupManager::class);
- $this->rootFolder = $this->createMock(IRootFolder::class);
- $this->mailer = $this->createMock(IMailer::class);
- $this->l10nFactory = $this->createMock(IFactory::class);
- $this->l10n = $this->createMock(IL10N::class);
- $this->defaults = $this->getMockBuilder(Defaults::class)->disableOriginalConstructor()->getMock();
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->timeFactory = $this->createMock(ITimeFactory::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->shareManager = $this->createMock(IShareManager::class);
- $this->config = $this->createMock(IConfig::class);
-
- $this->userManager->expects($this->any())->method('userExists')->willReturn(true);
- $this->timeFactory->expects($this->any())->method('now')->willReturn(new \DateTimeImmutable('2023-05-04 00:00 Europe/Berlin'));
-
- //Empty share table
- $this->dbConn->getQueryBuilder()->delete('share')->executeStatement();
-
- $this->provider = new DefaultShareProvider(
- $this->dbConn,
- $this->userManager,
- $this->groupManager,
- $this->rootFolder,
- $this->mailer,
- $this->defaults,
- $this->l10nFactory,
- $this->urlGenerator,
- $this->timeFactory,
- $this->logger,
- $this->shareManager,
- $this->config,
- );
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->dbConn->getQueryBuilder()->delete('share')->executeStatement();
- $this->dbConn->getQueryBuilder()->delete('filecache')->runAcrossAllShards()->executeStatement();
- $this->dbConn->getQueryBuilder()->delete('storages')->executeStatement();
- parent::tearDown();
- }
-
- /**
- * @param int $shareType
- * @param string $sharedWith
- * @param string $sharedBy
- * @param string $shareOwner
- * @param string $itemType
- * @param int $fileSource
- * @param string $fileTarget
- * @param int $permissions
- * @param $token
- * @param $expiration
- * @return int
- */
- private function addShareToDB($shareType, $sharedWith, $sharedBy, $shareOwner,
- $itemType, $fileSource, $fileTarget, $permissions, $token, $expiration,
- $parent = null) {
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share');
-
- if ($shareType) {
- $qb->setValue('share_type', $qb->expr()->literal($shareType));
- }
- if ($sharedWith) {
- $qb->setValue('share_with', $qb->expr()->literal($sharedWith));
- }
- if ($sharedBy) {
- $qb->setValue('uid_initiator', $qb->expr()->literal($sharedBy));
- }
- if ($shareOwner) {
- $qb->setValue('uid_owner', $qb->expr()->literal($shareOwner));
- }
- if ($itemType) {
- $qb->setValue('item_type', $qb->expr()->literal($itemType));
- }
- if ($fileSource) {
- $qb->setValue('file_source', $qb->expr()->literal($fileSource));
- }
- if ($fileTarget) {
- $qb->setValue('file_target', $qb->expr()->literal($fileTarget));
- }
- if ($permissions) {
- $qb->setValue('permissions', $qb->expr()->literal($permissions));
- }
- if ($token) {
- $qb->setValue('token', $qb->expr()->literal($token));
- }
- if ($expiration) {
- $qb->setValue('expiration', $qb->createNamedParameter($expiration, IQueryBuilder::PARAM_DATETIME_MUTABLE));
- }
- if ($parent) {
- $qb->setValue('parent', $qb->expr()->literal($parent));
- }
-
- $this->assertEquals(1, $qb->executeStatement());
- return $qb->getLastInsertId();
- }
-
- public function testGetShareByIdNotExist(): void {
- $this->expectException(ShareNotFound::class);
-
- $this->provider->getShareById(1);
- }
-
- public function testGetShareByIdUserShare(): void {
- $qb = $this->dbConn->getQueryBuilder();
-
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_USER),
- 'share_with' => $qb->expr()->literal('sharedWith'),
- 'uid_owner' => $qb->expr()->literal('shareOwner'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(42),
- 'file_target' => $qb->expr()->literal('myTarget'),
- 'permissions' => $qb->expr()->literal(13),
- ]);
- $qb->executeStatement();
-
- $id = $qb->getLastInsertId();
-
- $sharedBy = $this->createMock(IUser::class);
- $sharedBy->method('getUID')->willReturn('sharedBy');
- $shareOwner = $this->createMock(IUser::class);
- $shareOwner->method('getUID')->willReturn('shareOwner');
-
- $ownerPath = $this->createMock(File::class);
- $shareOwnerFolder = $this->createMock(Folder::class);
- $shareOwnerFolder->method('getFirstNodeById')->with(42)->willReturn($ownerPath);
-
- $this->rootFolder
- ->method('getUserFolder')
- ->willReturnMap([
- ['shareOwner', $shareOwnerFolder],
- ]);
-
- $share = $this->provider->getShareById($id);
-
- $this->assertEquals($id, $share->getId());
- $this->assertEquals(IShare::TYPE_USER, $share->getShareType());
- $this->assertEquals('sharedWith', $share->getSharedWith());
- $this->assertEquals('sharedBy', $share->getSharedBy());
- $this->assertEquals('shareOwner', $share->getShareOwner());
- $this->assertEquals($ownerPath, $share->getNode());
- $this->assertEquals(13, $share->getPermissions());
- $this->assertEquals(null, $share->getToken());
- $this->assertEquals(null, $share->getExpirationDate());
- $this->assertEquals('myTarget', $share->getTarget());
- }
-
- public function testGetShareByIdLazy(): void {
- $qb = $this->dbConn->getQueryBuilder();
-
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_USER),
- 'share_with' => $qb->expr()->literal('sharedWith'),
- 'uid_owner' => $qb->expr()->literal('shareOwner'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(42),
- 'file_target' => $qb->expr()->literal('myTarget'),
- 'permissions' => $qb->expr()->literal(13),
- ]);
- $qb->executeStatement();
-
- $id = $qb->getLastInsertId();
-
- $this->rootFolder->expects($this->never())->method('getUserFolder');
-
- $share = $this->provider->getShareById($id);
-
- // We do not fetch the node so the rootfolder is never called.
-
- $this->assertEquals($id, $share->getId());
- $this->assertEquals(IShare::TYPE_USER, $share->getShareType());
- $this->assertEquals('sharedWith', $share->getSharedWith());
- $this->assertEquals('sharedBy', $share->getSharedBy());
- $this->assertEquals('shareOwner', $share->getShareOwner());
- $this->assertEquals(13, $share->getPermissions());
- $this->assertEquals(null, $share->getToken());
- $this->assertEquals(null, $share->getExpirationDate());
- $this->assertEquals('myTarget', $share->getTarget());
- }
-
- public function testGetShareByIdLazy2(): void {
- $qb = $this->dbConn->getQueryBuilder();
-
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_USER),
- 'share_with' => $qb->expr()->literal('sharedWith'),
- 'uid_owner' => $qb->expr()->literal('shareOwner'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(42),
- 'file_target' => $qb->expr()->literal('myTarget'),
- 'permissions' => $qb->expr()->literal(13),
- ]);
- $qb->executeStatement();
-
- $id = $qb->getLastInsertId();
-
- $ownerPath = $this->createMock(File::class);
-
- $shareOwnerFolder = $this->createMock(Folder::class);
- $shareOwnerFolder->method('getFirstNodeById')->with(42)->willReturn($ownerPath);
-
- $this->rootFolder
- ->method('getUserFolder')
- ->with('shareOwner')
- ->willReturn($shareOwnerFolder);
-
- $share = $this->provider->getShareById($id);
-
- // We fetch the node so the root folder is eventually called
-
- $this->assertEquals($id, $share->getId());
- $this->assertEquals(IShare::TYPE_USER, $share->getShareType());
- $this->assertEquals('sharedWith', $share->getSharedWith());
- $this->assertEquals('sharedBy', $share->getSharedBy());
- $this->assertEquals('shareOwner', $share->getShareOwner());
- $this->assertEquals($ownerPath, $share->getNode());
- $this->assertEquals(13, $share->getPermissions());
- $this->assertEquals(null, $share->getToken());
- $this->assertEquals(null, $share->getExpirationDate());
- $this->assertEquals('myTarget', $share->getTarget());
- }
-
- public function testGetShareByIdGroupShare(): void {
- $qb = $this->dbConn->getQueryBuilder();
-
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_GROUP),
- 'share_with' => $qb->expr()->literal('sharedWith'),
- 'uid_owner' => $qb->expr()->literal('shareOwner'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(42),
- 'file_target' => $qb->expr()->literal('myTarget'),
- 'permissions' => $qb->expr()->literal(13),
- ]);
- $this->assertEquals(1, $qb->executeStatement());
-
- // Get the id
- $id = $qb->getLastInsertId();
-
- $ownerPath = $this->createMock(Folder::class);
- $shareOwnerFolder = $this->createMock(Folder::class);
- $shareOwnerFolder->method('getFirstNodeById')->with(42)->willReturn($ownerPath);
-
- $this->rootFolder
- ->method('getUserFolder')
- ->willReturnMap([
- ['shareOwner', $shareOwnerFolder],
- ]);
-
- $share = $this->provider->getShareById($id);
-
- $this->assertEquals($id, $share->getId());
- $this->assertEquals(IShare::TYPE_GROUP, $share->getShareType());
- $this->assertEquals('sharedWith', $share->getSharedWith());
- $this->assertEquals('sharedBy', $share->getSharedBy());
- $this->assertEquals('shareOwner', $share->getShareOwner());
- $this->assertEquals($ownerPath, $share->getNode());
- $this->assertEquals(13, $share->getPermissions());
- $this->assertEquals(null, $share->getToken());
- $this->assertEquals(null, $share->getExpirationDate());
- $this->assertEquals('myTarget', $share->getTarget());
- }
-
- public function testGetShareByIdUserGroupShare(): void {
- $id = $this->addShareToDB(IShare::TYPE_GROUP, 'group0', 'user0', 'user0', 'file', 42, 'myTarget', 31, null, null);
- $this->addShareToDB(2, 'user1', 'user0', 'user0', 'file', 42, 'userTarget', 0, null, null, $id);
-
- $user0 = $this->createMock(IUser::class);
- $user0->method('getUID')->willReturn('user0');
- $user1 = $this->createMock(IUser::class);
- $user1->method('getUID')->willReturn('user1');
-
- $group0 = $this->createMock(IGroup::class);
- $group0->method('inGroup')->with($user1)->willReturn(true);
- $group0->method('getDisplayName')->willReturn('g0-displayname');
-
- $node = $this->createMock(Folder::class);
- $node->method('getId')->willReturn(42);
- $node->method('getName')->willReturn('myTarget');
-
- $this->rootFolder->method('getUserFolder')->with('user0')->willReturnSelf();
- $this->rootFolder->method('getFirstNodeById')->willReturn($node);
-
- $this->userManager->method('get')->willReturnMap([
- ['user0', $user0],
- ['user1', $user1],
- ]);
- $this->groupManager->method('get')->with('group0')->willReturn($group0);
-
- $share = $this->provider->getShareById($id, 'user1');
-
- $this->assertEquals($id, $share->getId());
- $this->assertEquals(IShare::TYPE_GROUP, $share->getShareType());
- $this->assertSame('group0', $share->getSharedWith());
- $this->assertSame('user0', $share->getSharedBy());
- $this->assertSame('user0', $share->getShareOwner());
- $this->assertSame($node, $share->getNode());
- $this->assertEquals(0, $share->getPermissions());
- $this->assertEquals(null, $share->getToken());
- $this->assertEquals(null, $share->getExpirationDate());
- $this->assertEquals('userTarget', $share->getTarget());
- }
-
- public function testGetShareByIdLinkShare(): void {
- $passwordHash = Server::get(IHasher::class)->hash('password');
-
- $qb = $this->dbConn->getQueryBuilder();
-
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_LINK),
- 'password' => $qb->expr()->literal($passwordHash),
- 'password_by_talk' => $qb->expr()->literal(true),
- 'uid_owner' => $qb->expr()->literal('shareOwner'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(42),
- 'file_target' => $qb->expr()->literal('myTarget'),
- 'permissions' => $qb->expr()->literal(13),
- 'token' => $qb->expr()->literal('token'),
- 'expiration' => $qb->expr()->literal('2000-01-02 00:00:00'),
- ]);
- $this->assertEquals(1, $qb->executeStatement());
-
- $id = $qb->getLastInsertId();
-
- $ownerPath = $this->createMock(Folder::class);
- $shareOwnerFolder = $this->createMock(Folder::class);
- $shareOwnerFolder->method('getFirstNodeById')->with(42)->willReturn($ownerPath);
-
- $this->rootFolder
- ->method('getUserFolder')
- ->willReturnMap([
- ['shareOwner', $shareOwnerFolder],
- ]);
-
- $share = $this->provider->getShareById($id);
-
- $this->assertEquals($id, $share->getId());
- $this->assertEquals(IShare::TYPE_LINK, $share->getShareType());
- $this->assertNull($share->getSharedWith());
- $this->assertTrue($share->isPasswordHashed());
- $this->assertEquals($passwordHash, $share->getPassword());
- $this->assertEquals(true, $share->getSendPasswordByTalk());
- $this->assertEquals('sharedBy', $share->getSharedBy());
- $this->assertEquals('shareOwner', $share->getShareOwner());
- $this->assertEquals($ownerPath, $share->getNode());
- $this->assertEquals(13, $share->getPermissions());
- $this->assertEquals('token', $share->getToken());
- $this->assertEquals(\DateTime::createFromFormat('Y-m-d H:i:s', '2000-01-02 00:00:00'), $share->getExpirationDate());
- $this->assertEquals('myTarget', $share->getTarget());
- }
-
- public function testDeleteSingleShare(): void {
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_USER),
- 'share_with' => $qb->expr()->literal('sharedWith'),
- 'uid_owner' => $qb->expr()->literal('sharedBy'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(42),
- 'file_target' => $qb->expr()->literal('myTarget'),
- 'permissions' => $qb->expr()->literal(13),
- ]);
- $this->assertEquals(1, $qb->executeStatement());
-
- $id = (string)$qb->getLastInsertId();
-
- $share = $this->createMock(IShare::class);
- $share->method('getId')->willReturn($id);
-
- /** @var DefaultShareProvider $provider */
- $provider = $this->getMockBuilder(DefaultShareProvider::class)
- ->setConstructorArgs([
- $this->dbConn,
- $this->userManager,
- $this->groupManager,
- $this->rootFolder,
- $this->mailer,
- $this->defaults,
- $this->l10nFactory,
- $this->urlGenerator,
- $this->timeFactory,
- $this->logger,
- $this->shareManager,
- $this->config,
- ])
- ->onlyMethods(['getShareById'])
- ->getMock();
-
- $provider->delete($share);
-
- $qb = $this->dbConn->getQueryBuilder();
- $qb->select('*')
- ->from('share');
-
- $cursor = $qb->executeQuery();
- $result = $cursor->fetchAllAssociative();
- $cursor->closeCursor();
-
- $this->assertEmpty($result);
- }
-
- public function testDeleteSingleShareLazy(): void {
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_USER),
- 'share_with' => $qb->expr()->literal('sharedWith'),
- 'uid_owner' => $qb->expr()->literal('shareOwner'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(42),
- 'file_target' => $qb->expr()->literal('myTarget'),
- 'permissions' => $qb->expr()->literal(13),
- ]);
- $this->assertEquals(1, $qb->executeStatement());
-
- $id = $qb->getLastInsertId();
-
- $this->rootFolder->expects($this->never())->method($this->anything());
-
- $share = $this->provider->getShareById($id);
- $this->provider->delete($share);
-
- $qb = $this->dbConn->getQueryBuilder();
- $qb->select('*')
- ->from('share');
-
- $cursor = $qb->executeQuery();
- $result = $cursor->fetchAllAssociative();
- $cursor->closeCursor();
-
- $this->assertEmpty($result);
- }
-
- public function testDeleteGroupShareWithUserGroupShares(): void {
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_GROUP),
- 'share_with' => $qb->expr()->literal('sharedWith'),
- 'uid_owner' => $qb->expr()->literal('sharedBy'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(42),
- 'file_target' => $qb->expr()->literal('myTarget'),
- 'permissions' => $qb->expr()->literal(13),
- ]);
- $this->assertEquals(1, $qb->executeStatement());
- $id = (string)$qb->getLastInsertId();
-
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(2),
- 'share_with' => $qb->expr()->literal('sharedWithUser'),
- 'uid_owner' => $qb->expr()->literal('sharedBy'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(42),
- 'file_target' => $qb->expr()->literal('myTarget'),
- 'permissions' => $qb->expr()->literal(13),
- 'parent' => $qb->expr()->literal($id),
- ]);
- $this->assertEquals(1, $qb->executeStatement());
-
- $share = $this->createMock(IShare::class);
- $share->method('getId')->willReturn($id);
- $share->method('getShareType')->willReturn(IShare::TYPE_GROUP);
-
- /** @var DefaultShareProvider $provider */
- $provider = $this->getMockBuilder(DefaultShareProvider::class)
- ->setConstructorArgs([
- $this->dbConn,
- $this->userManager,
- $this->groupManager,
- $this->rootFolder,
- $this->mailer,
- $this->defaults,
- $this->l10nFactory,
- $this->urlGenerator,
- $this->timeFactory,
- $this->logger,
- $this->shareManager,
- $this->config,
- ])
- ->onlyMethods(['getShareById'])
- ->getMock();
-
- $provider->delete($share);
-
- $qb = $this->dbConn->getQueryBuilder();
- $qb->select('*')
- ->from('share');
-
- $cursor = $qb->executeQuery();
- $result = $cursor->fetchAllAssociative();
- $cursor->closeCursor();
-
- $this->assertEmpty($result);
- }
-
- public function testGetChildren(): void {
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_USER),
- 'share_with' => $qb->expr()->literal('sharedWith'),
- 'uid_owner' => $qb->expr()->literal('shareOwner'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(42),
- 'file_target' => $qb->expr()->literal('myTarget'),
- 'permissions' => $qb->expr()->literal(13),
- ]);
- $qb->executeStatement();
-
- // Get the id
- $id = (string)$qb->getLastInsertId();
-
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_USER),
- 'share_with' => $qb->expr()->literal('user1'),
- 'uid_owner' => $qb->expr()->literal('shareOwner'),
- 'uid_initiator' => $qb->expr()->literal('user2'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(1),
- 'file_target' => $qb->expr()->literal('myTarget1'),
- 'permissions' => $qb->expr()->literal(2),
- 'parent' => $qb->expr()->literal($id),
- ]);
- $qb->executeStatement();
-
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_GROUP),
- 'share_with' => $qb->expr()->literal('group1'),
- 'uid_owner' => $qb->expr()->literal('shareOwner'),
- 'uid_initiator' => $qb->expr()->literal('user3'),
- 'item_type' => $qb->expr()->literal('folder'),
- 'file_source' => $qb->expr()->literal(3),
- 'file_target' => $qb->expr()->literal('myTarget2'),
- 'permissions' => $qb->expr()->literal(4),
- 'parent' => $qb->expr()->literal($id),
- ]);
- $qb->executeStatement();
-
- $ownerPath = $this->createMock(Folder::class);
- $ownerFolder = $this->createMock(Folder::class);
- $ownerFolder->method('getFirstNodeById')->willReturn($ownerPath);
-
- $this->rootFolder
- ->method('getUserFolder')
- ->willReturnMap([
- ['shareOwner', $ownerFolder],
- ]);
-
- $share = $this->createMock(IShare::class);
- $share->method('getId')->willReturn($id);
-
- $children = $this->provider->getChildren($share);
- usort($children, fn (IShare $a, IShare $b) => $a->getId() <=> $b->getId());
-
- $this->assertCount(2, $children);
-
- //Child1
- $this->assertEquals(IShare::TYPE_USER, $children[0]->getShareType());
- $this->assertEquals('user1', $children[0]->getSharedWith());
- $this->assertEquals('user2', $children[0]->getSharedBy());
- $this->assertEquals('shareOwner', $children[0]->getShareOwner());
- $this->assertEquals($ownerPath, $children[0]->getNode());
- $this->assertEquals(2, $children[0]->getPermissions());
- $this->assertEquals(null, $children[0]->getToken());
- $this->assertEquals(null, $children[0]->getExpirationDate());
- $this->assertEquals('myTarget1', $children[0]->getTarget());
-
- //Child2
- $this->assertEquals(IShare::TYPE_GROUP, $children[1]->getShareType());
- $this->assertEquals('group1', $children[1]->getSharedWith());
- $this->assertEquals('user3', $children[1]->getSharedBy());
- $this->assertEquals('shareOwner', $children[1]->getShareOwner());
- $this->assertEquals($ownerPath, $children[1]->getNode());
- $this->assertEquals(4, $children[1]->getPermissions());
- $this->assertEquals(null, $children[1]->getToken());
- $this->assertEquals(null, $children[1]->getExpirationDate());
- $this->assertEquals('myTarget2', $children[1]->getTarget());
- }
-
- public function testCreateUserShare(): void {
- $share = new Share($this->rootFolder, $this->userManager);
-
- $shareOwner = $this->createMock(IUser::class);
- $shareOwner->method('getUID')->willReturn('shareOwner');
-
- $path = $this->createMock(File::class);
- $path->method('getId')->willReturn(100);
- $path->method('getOwner')->willReturn($shareOwner);
-
- $ownerFolder = $this->createMock(Folder::class);
- $userFolder = $this->createMock(Folder::class);
- $this->rootFolder
- ->method('getUserFolder')
- ->willReturnMap([
- ['sharedBy', $userFolder],
- ['shareOwner', $ownerFolder],
- ]);
-
- $userFolder->method('getFirstNodeById')
- ->with(100)
- ->willReturn($path);
- $ownerFolder->method('getFirstNodeById')
- ->with(100)
- ->willReturn($path);
-
- $share->setShareType(IShare::TYPE_USER);
- $share->setSharedWith('sharedWith');
- $share->setSharedBy('sharedBy');
- $share->setShareOwner('shareOwner');
- $share->setNode($path);
- $share->setSharedWithDisplayName('Displayed Name');
- $share->setSharedWithAvatar('/path/to/image.svg');
- $share->setPermissions(1);
-
- $attrs = new ShareAttributes();
- $attrs->setAttribute('permissions', 'download', true);
- $share->setAttributes($attrs);
-
- $share->setTarget('/target');
-
- $share2 = $this->provider->create($share);
-
- $this->assertNotNull($share2->getId());
- $this->assertSame('ocinternal:' . $share2->getId(), $share2->getFullId());
- $this->assertSame(IShare::TYPE_USER, $share2->getShareType());
- $this->assertSame('sharedWith', $share2->getSharedWith());
- $this->assertSame('sharedBy', $share2->getSharedBy());
- $this->assertSame('shareOwner', $share2->getShareOwner());
- $this->assertSame(1, $share2->getPermissions());
- $this->assertSame('/target', $share2->getTarget());
- $this->assertLessThanOrEqual(new \DateTime(), $share2->getShareTime());
- $this->assertSame($path, $share2->getNode());
-
- // Data is kept after creation
- $this->assertSame('Displayed Name', $share->getSharedWithDisplayName());
- $this->assertSame('/path/to/image.svg', $share->getSharedWithAvatar());
- $this->assertSame('Displayed Name', $share2->getSharedWithDisplayName());
- $this->assertSame('/path/to/image.svg', $share2->getSharedWithAvatar());
-
- $this->assertSame(
- [
- [
- 'scope' => 'permissions',
- 'key' => 'download',
- 'value' => true
- ]
- ],
- $share->getAttributes()->toArray()
- );
- }
-
- public function testCreateGroupShare(): void {
- $share = new Share($this->rootFolder, $this->userManager);
-
- $shareOwner = $this->createMock(IUser::class);
- $shareOwner->method('getUID')->willReturn('shareOwner');
-
- $path = $this->createMock(Folder::class);
- $path->method('getId')->willReturn(100);
- $path->method('getOwner')->willReturn($shareOwner);
-
- $ownerFolder = $this->createMock(Folder::class);
- $userFolder = $this->createMock(Folder::class);
- $this->rootFolder
- ->method('getUserFolder')
- ->willReturnMap([
- ['sharedBy', $userFolder],
- ['shareOwner', $ownerFolder],
- ]);
-
- $userFolder->method('getFirstNodeById')
- ->with(100)
- ->willReturn($path);
- $ownerFolder->method('getFirstNodeById')
- ->with(100)
- ->willReturn($path);
-
- $share->setShareType(IShare::TYPE_GROUP);
- $share->setSharedWith('sharedWith');
- $share->setSharedBy('sharedBy');
- $share->setShareOwner('shareOwner');
- $share->setNode($path);
- $share->setPermissions(1);
- $share->setSharedWithDisplayName('Displayed Name');
- $share->setSharedWithAvatar('/path/to/image.svg');
- $share->setTarget('/target');
- $attrs = new ShareAttributes();
- $attrs->setAttribute('permissions', 'download', true);
- $share->setAttributes($attrs);
-
- $share2 = $this->provider->create($share);
-
- $this->assertNotNull($share2->getId());
- $this->assertSame('ocinternal:' . $share2->getId(), $share2->getFullId());
- $this->assertSame(IShare::TYPE_GROUP, $share2->getShareType());
- $this->assertSame('sharedWith', $share2->getSharedWith());
- $this->assertSame('sharedBy', $share2->getSharedBy());
- $this->assertSame('shareOwner', $share2->getShareOwner());
- $this->assertSame(1, $share2->getPermissions());
- $this->assertSame('/target', $share2->getTarget());
- $this->assertLessThanOrEqual(new \DateTime(), $share2->getShareTime());
- $this->assertSame($path, $share2->getNode());
-
- // Data is kept after creation
- $this->assertSame('Displayed Name', $share->getSharedWithDisplayName());
- $this->assertSame('/path/to/image.svg', $share->getSharedWithAvatar());
- $this->assertSame('Displayed Name', $share2->getSharedWithDisplayName());
- $this->assertSame('/path/to/image.svg', $share2->getSharedWithAvatar());
-
- $this->assertSame(
- [
- [
- 'scope' => 'permissions',
- 'key' => 'download',
- 'value' => true
- ]
- ],
- $share->getAttributes()->toArray()
- );
- }
-
- public function testCreateLinkShare(): void {
- $passwordHash = Server::get(IHasher::class)->hash('password');
-
- $share = new Share($this->rootFolder, $this->userManager);
-
- $shareOwner = $this->createMock(IUser::class);
- $shareOwner->method('getUID')->willReturn('shareOwner');
-
- $path = $this->createMock(Folder::class);
- $path->method('getId')->willReturn(100);
- $path->method('getOwner')->willReturn($shareOwner);
-
- $ownerFolder = $this->createMock(Folder::class);
- $userFolder = $this->createMock(Folder::class);
- $this->rootFolder
- ->method('getUserFolder')
- ->willReturnMap([
- ['sharedBy', $userFolder],
- ['shareOwner', $ownerFolder],
- ]);
-
- $userFolder->method('getFirstNodeById')
- ->with(100)
- ->willReturn($path);
- $ownerFolder->method('getFirstNodeById')
- ->with(100)
- ->willReturn($path);
-
- $share->setShareType(IShare::TYPE_LINK);
- $share->setSharedBy('sharedBy');
- $share->setShareOwner('shareOwner');
- $share->setNode($path);
- $share->setPermissions(1);
- $share->setPasswordHash($passwordHash);
- $share->setSendPasswordByTalk(true);
- $share->setToken('token');
- $expireDate = new \DateTime();
- $share->setExpirationDate($expireDate);
- $share->setTarget('/target');
-
- $share2 = $this->provider->create($share);
-
- $this->assertNotNull($share2->getId());
- $this->assertSame('ocinternal:' . $share2->getId(), $share2->getFullId());
- $this->assertSame(IShare::TYPE_LINK, $share2->getShareType());
- $this->assertSame('sharedBy', $share2->getSharedBy());
- $this->assertSame('shareOwner', $share2->getShareOwner());
- $this->assertSame(1, $share2->getPermissions());
- $this->assertSame('/target', $share2->getTarget());
- $this->assertLessThanOrEqual(new \DateTime(), $share2->getShareTime());
- $this->assertSame($path, $share2->getNode());
- $this->assertTrue($share2->isPasswordHashed());
- $this->assertSame($passwordHash, $share2->getPassword());
- $this->assertSame(true, $share2->getSendPasswordByTalk());
- $this->assertSame('token', $share2->getToken());
- $this->assertEquals($expireDate->getTimestamp(), $share2->getExpirationDate()->getTimestamp());
- }
-
- public function testGetShareByToken(): void {
- $passwordHash = Server::get(IHasher::class)->hash('password');
-
- $qb = $this->dbConn->getQueryBuilder();
-
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_LINK),
- 'password' => $qb->expr()->literal($passwordHash),
- 'password_by_talk' => $qb->expr()->literal(true),
- 'uid_owner' => $qb->expr()->literal('shareOwner'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(42),
- 'file_target' => $qb->expr()->literal('myTarget'),
- 'permissions' => $qb->expr()->literal(13),
- 'token' => $qb->expr()->literal('secrettoken'),
- 'label' => $qb->expr()->literal('the label'),
- ]);
- $qb->executeStatement();
- $id = $qb->getLastInsertId();
-
- $file = $this->createMock(File::class);
-
- $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturnSelf();
- $this->rootFolder->method('getFirstNodeById')->with(42)->willReturn($file);
-
- $share = $this->provider->getShareByToken('secrettoken');
- $this->assertEquals($id, $share->getId());
- $this->assertSame('shareOwner', $share->getShareOwner());
- $this->assertSame('sharedBy', $share->getSharedBy());
- $this->assertSame('secrettoken', $share->getToken());
- $this->assertTrue($share->isPasswordHashed());
- $this->assertSame($passwordHash, $share->getPassword());
- $this->assertSame('the label', $share->getLabel());
- $this->assertSame(true, $share->getSendPasswordByTalk());
- $this->assertSame(null, $share->getSharedWith());
- }
-
- /**
- * Assert that if no label is provided the label is correctly,
- * as types on IShare, a string and not null
- */
- public function testGetShareByTokenNullLabel(): void {
- $passwordHash = Server::get(IHasher::class)->hash('password');
-
- $qb = $this->dbConn->getQueryBuilder();
-
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_LINK),
- 'password' => $qb->expr()->literal($passwordHash),
- 'password_by_talk' => $qb->expr()->literal(true),
- 'uid_owner' => $qb->expr()->literal('shareOwner'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(42),
- 'file_target' => $qb->expr()->literal('myTarget'),
- 'permissions' => $qb->expr()->literal(13),
- 'token' => $qb->expr()->literal('secrettoken'),
- ]);
- $qb->executeStatement();
- $id = $qb->getLastInsertId();
-
- $file = $this->createMock(File::class);
-
- $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturnSelf();
- $this->rootFolder->method('getFirstNodeById')->with(42)->willReturn($file);
-
- $share = $this->provider->getShareByToken('secrettoken');
- $this->assertEquals($id, $share->getId());
- $this->assertSame('', $share->getLabel());
- }
-
- public function testGetShareByTokenNotFound(): void {
- $this->expectException(ShareNotFound::class);
-
- $this->provider->getShareByToken('invalidtoken');
- }
-
- private function createTestStorageEntry($storageStringId) {
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('storages')
- ->values([
- 'id' => $qb->expr()->literal($storageStringId),
- ]);
- $this->assertEquals(1, $qb->executeStatement());
- return $qb->getLastInsertId();
- }
-
- private function createTestFileEntry($path, $storage = 1) {
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('filecache')
- ->values([
- 'storage' => $qb->createNamedParameter($storage, IQueryBuilder::PARAM_INT),
- 'path' => $qb->createNamedParameter($path),
- 'path_hash' => $qb->createNamedParameter(md5($path)),
- 'name' => $qb->createNamedParameter(basename($path)),
- ]);
- $this->assertEquals(1, $qb->executeStatement());
- return $qb->getLastInsertId();
- }
-
- public static function storageAndFileNameProvider(): array {
- return [
- // regular file on regular storage
- ['home::shareOwner', 'files/test.txt', 'files/test2.txt'],
- // regular file on external storage
- ['smb::whatever', 'files/test.txt', 'files/test2.txt'],
- // regular file on external storage in trashbin-like folder,
- ['smb::whatever', 'files_trashbin/files/test.txt', 'files_trashbin/files/test2.txt'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('storageAndFileNameProvider')]
- public function testGetSharedWithUser($storageStringId, $fileName1, $fileName2): void {
- $storageId = $this->createTestStorageEntry($storageStringId);
- $fileId = $this->createTestFileEntry($fileName1, $storageId);
- $fileId2 = $this->createTestFileEntry($fileName2, $storageId);
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_USER),
- 'share_with' => $qb->expr()->literal('sharedWith'),
- 'uid_owner' => $qb->expr()->literal('shareOwner'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal($fileId),
- 'file_target' => $qb->expr()->literal('myTarget'),
- 'permissions' => $qb->expr()->literal(13),
- ]);
- $this->assertEquals(1, $qb->executeStatement());
- $id = $qb->getLastInsertId();
-
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_USER),
- 'share_with' => $qb->expr()->literal('sharedWith2'),
- 'uid_owner' => $qb->expr()->literal('shareOwner2'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy2'),
- 'item_type' => $qb->expr()->literal('file2'),
- 'file_source' => $qb->expr()->literal($fileId2),
- 'file_target' => $qb->expr()->literal('myTarget2'),
- 'permissions' => $qb->expr()->literal(14),
- ]);
- $this->assertEquals(1, $qb->executeStatement());
-
- $file = $this->createMock(File::class);
- $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturnSelf();
- $this->rootFolder->method('getFirstNodeById')->with($fileId)->willReturn($file);
-
- $share = $this->provider->getSharedWith('sharedWith', IShare::TYPE_USER, null, 1, 0);
- $this->assertCount(1, $share);
-
- $share = $share[0];
- $this->assertEquals($id, $share->getId());
- $this->assertEquals('sharedWith', $share->getSharedWith());
- $this->assertEquals('shareOwner', $share->getShareOwner());
- $this->assertEquals('sharedBy', $share->getSharedBy());
- $this->assertEquals(IShare::TYPE_USER, $share->getShareType());
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('storageAndFileNameProvider')]
- public function testGetSharedWithGroup($storageStringId, $fileName1, $fileName2): void {
- $storageId = $this->createTestStorageEntry($storageStringId);
- $fileId = $this->createTestFileEntry($fileName1, $storageId);
- $fileId2 = $this->createTestFileEntry($fileName2, $storageId);
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_GROUP),
- 'share_with' => $qb->expr()->literal('sharedWith'),
- 'uid_owner' => $qb->expr()->literal('shareOwner2'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy2'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal($fileId2),
- 'file_target' => $qb->expr()->literal('myTarget2'),
- 'permissions' => $qb->expr()->literal(14),
- ]);
- $this->assertEquals(1, $qb->executeStatement());
-
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_GROUP),
- 'share_with' => $qb->expr()->literal('sharedWith'),
- 'uid_owner' => $qb->expr()->literal('shareOwner'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal($fileId),
- 'file_target' => $qb->expr()->literal('myTarget'),
- 'permissions' => $qb->expr()->literal(13),
- ]);
- $this->assertEquals(1, $qb->executeStatement());
- $id = $qb->getLastInsertId();
-
- $groups = [];
- foreach (range(0, 100) as $i) {
- $groups[] = 'group' . $i;
- }
-
- $groups[] = 'sharedWith';
-
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('sharedWith');
- $owner = $this->createMock(IUser::class);
- $owner->method('getUID')->willReturn('shareOwner');
- $initiator = $this->createMock(IUser::class);
- $initiator->method('getUID')->willReturn('sharedBy');
-
- $this->userManager->method('get')->willReturnMap([
- ['sharedWith', $user],
- ['shareOwner', $owner],
- ['sharedBy', $initiator],
- ]);
- $this->groupManager
- ->method('getUserGroupIds')
- ->willReturnCallback(fn (IUser $user) => ($user->getUID() === 'sharedWith' ? $groups : []));
-
- $file = $this->createMock(File::class);
- $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturnSelf();
- $this->rootFolder->method('getFirstNodeById')->with($fileId)->willReturn($file);
-
- $share = $this->provider->getSharedWith('sharedWith', IShare::TYPE_GROUP, null, 20, 1);
- $this->assertCount(1, $share);
-
- $share = $share[0];
- $this->assertEquals($id, $share->getId());
- $this->assertEquals('sharedWith', $share->getSharedWith());
- $this->assertEquals('shareOwner', $share->getShareOwner());
- $this->assertEquals('sharedBy', $share->getSharedBy());
- $this->assertEquals(IShare::TYPE_GROUP, $share->getShareType());
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('storageAndFileNameProvider')]
- public function testGetSharedWithGroupUserModified($storageStringId, $fileName1, $fileName2): void {
- $storageId = $this->createTestStorageEntry($storageStringId);
- $fileId = $this->createTestFileEntry($fileName1, $storageId);
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_GROUP),
- 'share_with' => $qb->expr()->literal('sharedWith'),
- 'uid_owner' => $qb->expr()->literal('shareOwner'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal($fileId),
- 'file_target' => $qb->expr()->literal('myTarget'),
- 'permissions' => $qb->expr()->literal(13),
- ]);
- $this->assertEquals(1, $qb->executeStatement());
- $id = $qb->getLastInsertId();
-
- /*
- * Wrong share. Should not be taken by code.
- */
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(2),
- 'share_with' => $qb->expr()->literal('user2'),
- 'uid_owner' => $qb->expr()->literal('shareOwner'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal($fileId),
- 'file_target' => $qb->expr()->literal('wrongTarget'),
- 'permissions' => $qb->expr()->literal(31),
- 'parent' => $qb->expr()->literal($id),
- ]);
- $this->assertEquals(1, $qb->executeStatement());
-
- /*
- * Correct share. should be taken by code path.
- */
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(2),
- 'share_with' => $qb->expr()->literal('user'),
- 'uid_owner' => $qb->expr()->literal('shareOwner'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal($fileId),
- 'file_target' => $qb->expr()->literal('userTarget'),
- 'permissions' => $qb->expr()->literal(0),
- 'parent' => $qb->expr()->literal($id),
- ]);
- $this->assertEquals(1, $qb->executeStatement());
-
- $groups = ['sharedWith'];
-
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('user');
- $owner = $this->createMock(IUser::class);
- $owner->method('getUID')->willReturn('shareOwner');
- $initiator = $this->createMock(IUser::class);
- $initiator->method('getUID')->willReturn('sharedBy');
-
- $this->userManager->method('get')->willReturnMap([
- ['user', $user],
- ['shareOwner', $owner],
- ['sharedBy', $initiator],
- ]);
- $this->groupManager
- ->method('getUserGroupIds')
- ->willReturnCallback(fn (IUser $user) => ($user->getUID() === 'user' ? $groups : []));
-
- $file = $this->createMock(File::class);
- $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturnSelf();
- $this->rootFolder->method('getFirstNodeById')->with($fileId)->willReturn($file);
-
- $share = $this->provider->getSharedWith('user', IShare::TYPE_GROUP, null, -1, 0);
- $this->assertCount(1, $share);
-
- $share = $share[0];
- $this->assertSame((string)$id, $share->getId());
- $this->assertSame('sharedWith', $share->getSharedWith());
- $this->assertSame('shareOwner', $share->getShareOwner());
- $this->assertSame('sharedBy', $share->getSharedBy());
- $this->assertSame(IShare::TYPE_GROUP, $share->getShareType());
- $this->assertSame(0, $share->getPermissions());
- $this->assertSame('userTarget', $share->getTarget());
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('storageAndFileNameProvider')]
- public function testGetSharedWithUserWithNode($storageStringId, $fileName1, $fileName2): void {
- $storageId = $this->createTestStorageEntry($storageStringId);
- $fileId = $this->createTestFileEntry($fileName1, $storageId);
- $fileId2 = $this->createTestFileEntry($fileName2, $storageId);
- $this->addShareToDB(IShare::TYPE_USER, 'user0', 'user1', 'user1',
- 'file', $fileId, 'myTarget', 31, null, null, null);
- $id = $this->addShareToDB(IShare::TYPE_USER, 'user0', 'user1', 'user1',
- 'file', $fileId2, 'myTarget', 31, null, null, null);
-
- $user0 = $this->createMock(IUser::class);
- $user0->method('getUID')->willReturn('user0');
- $user0->method('getDisplayName')->willReturn('user0');
- $user1 = $this->createMock(IUser::class);
- $user1->method('getUID')->willReturn('user1');
- $user0->method('getDisplayName')->willReturn('user0');
-
- $this->userManager->method('get')->willReturnMap([
- ['user0', $user0],
- ['user1', $user1],
- ]);
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn($fileId2);
-
- $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf();
- $this->rootFolder->method('getFirstNodeById')->with($fileId2)->willReturn($file);
-
- $share = $this->provider->getSharedWith('user0', IShare::TYPE_USER, $file, -1, 0);
- $this->assertCount(1, $share);
-
- $share = $share[0];
- $this->assertEquals($id, $share->getId());
- $this->assertSame('user0', $share->getSharedWith());
- $this->assertSame('user1', $share->getShareOwner());
- $this->assertSame('user1', $share->getSharedBy());
- $this->assertSame($file, $share->getNode());
- $this->assertEquals(IShare::TYPE_USER, $share->getShareType());
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('storageAndFileNameProvider')]
- public function testGetSharedWithGroupWithNode($storageStringId, $fileName1, $fileName2): void {
- $storageId = $this->createTestStorageEntry($storageStringId);
- $fileId = $this->createTestFileEntry($fileName1, $storageId);
- $fileId2 = $this->createTestFileEntry($fileName2, $storageId);
- $this->addShareToDB(IShare::TYPE_GROUP, 'group0', 'user1', 'user1',
- 'file', $fileId, 'myTarget', 31, null, null, null);
- $id = $this->addShareToDB(IShare::TYPE_GROUP, 'group0', 'user1', 'user1',
- 'file', $fileId2, 'myTarget', 31, null, null, null);
-
- $user0 = $this->createMock(IUser::class);
- $user0->method('getUID')->willReturn('user0');
- $user1 = $this->createMock(IUser::class);
- $user1->method('getUID')->willReturn('user1');
-
- $this->userManager->method('get')->willReturnMap([
- ['user0', $user0],
- ['user1', $user1],
- ]);
-
- $this->groupManager
- ->method('getUserGroupIds')
- ->willReturnCallback(fn (IUser $user) => ($user->getUID() === 'user0' ? ['group0'] : []));
-
- $node = $this->createMock(Folder::class);
- $node->method('getId')->willReturn($fileId2);
- $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf();
- $this->rootFolder->method('getFirstNodeById')->with($fileId2)->willReturn($node);
-
- $share = $this->provider->getSharedWith('user0', IShare::TYPE_GROUP, $node, -1, 0);
- $this->assertCount(1, $share);
-
- $share = $share[0];
- $this->assertEquals($id, $share->getId());
- $this->assertSame('group0', $share->getSharedWith());
- $this->assertSame('user1', $share->getShareOwner());
- $this->assertSame('user1', $share->getSharedBy());
- $this->assertSame($node, $share->getNode());
- $this->assertEquals(IShare::TYPE_GROUP, $share->getShareType());
- }
-
- public static function shareTypesProvider(): array {
- return [
- [IShare::TYPE_USER, false],
- [IShare::TYPE_GROUP, false],
- [IShare::TYPE_USER, true],
- [IShare::TYPE_GROUP, true],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('shareTypesProvider')]
- public function testGetSharedWithWithDeletedFile($shareType, $trashed): void {
- if ($trashed) {
- // exists in database but is in trash
- $storageId = $this->createTestStorageEntry('home::shareOwner');
- $deletedFileId = $this->createTestFileEntry('files_trashbin/files/test.txt.d1465553223', $storageId);
- } else {
- // fileid that doesn't exist in the database
- $deletedFileId = 123;
- }
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal($shareType),
- 'share_with' => $qb->expr()->literal('sharedWith'),
- 'uid_owner' => $qb->expr()->literal('shareOwner'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal($deletedFileId),
- 'file_target' => $qb->expr()->literal('myTarget'),
- 'permissions' => $qb->expr()->literal(13),
- ]);
- $this->assertEquals(1, $qb->executeStatement());
-
- $file = $this->createMock(File::class);
- $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturnSelf();
- $this->rootFolder->method('getFirstNodeById')->with($deletedFileId)->willReturn($file);
-
- $groups = [];
- foreach (range(0, 100) as $i) {
- $groups[] = 'group' . $i;
- }
-
- $groups[] = 'sharedWith';
-
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('sharedWith');
- $owner = $this->createMock(IUser::class);
- $owner->method('getUID')->willReturn('shareOwner');
- $initiator = $this->createMock(IUser::class);
- $initiator->method('getUID')->willReturn('sharedBy');
-
- $this->userManager->method('get')->willReturnMap([
- ['sharedWith', $user],
- ['shareOwner', $owner],
- ['sharedBy', $initiator],
- ]);
- $this->groupManager
- ->method('getUserGroupIds')
- ->willReturnCallback(fn (IUser $user) => ($user->getUID() === 'sharedWith' ? $groups : []));
-
- $share = $this->provider->getSharedWith('sharedWith', $shareType, null, 1, 0);
- $this->assertCount(0, $share);
- }
-
- public function testGetSharesBy(): void {
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_USER),
- 'share_with' => $qb->expr()->literal('sharedWith'),
- 'uid_owner' => $qb->expr()->literal('shareOwner'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(42),
- 'file_target' => $qb->expr()->literal('myTarget'),
- 'permissions' => $qb->expr()->literal(13),
- ]);
- $this->assertEquals(1, $qb->executeStatement());
- $id = $qb->getLastInsertId();
-
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_USER),
- 'share_with' => $qb->expr()->literal('sharedWith'),
- 'uid_owner' => $qb->expr()->literal('shareOwner'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy2'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(42),
- 'file_target' => $qb->expr()->literal('userTarget'),
- 'permissions' => $qb->expr()->literal(0),
- 'parent' => $qb->expr()->literal($id),
- ]);
- $this->assertEquals(1, $qb->executeStatement());
-
- $file = $this->createMock(File::class);
- $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturnSelf();
- $this->rootFolder->method('getFirstNodeById')->with(42)->willReturn($file);
-
- $share = $this->provider->getSharesBy('sharedBy', IShare::TYPE_USER, null, false, 1, 0);
- $this->assertCount(1, $share);
-
- /** @var IShare $share */
- $share = $share[0];
- $this->assertEquals($id, $share->getId());
- $this->assertEquals('sharedWith', $share->getSharedWith());
- $this->assertEquals('shareOwner', $share->getShareOwner());
- $this->assertEquals('sharedBy', $share->getSharedBy());
- $this->assertEquals(IShare::TYPE_USER, $share->getShareType());
- $this->assertEquals(13, $share->getPermissions());
- $this->assertEquals('myTarget', $share->getTarget());
- }
-
- public function testGetSharesNode(): void {
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_USER),
- 'share_with' => $qb->expr()->literal('sharedWith'),
- 'uid_owner' => $qb->expr()->literal('shareOwner'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(42),
- 'file_target' => $qb->expr()->literal('myTarget'),
- 'permissions' => $qb->expr()->literal(13),
- ]);
- $this->assertEquals(1, $qb->executeStatement());
- $id = $qb->getLastInsertId();
-
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_USER),
- 'share_with' => $qb->expr()->literal('sharedWith'),
- 'uid_owner' => $qb->expr()->literal('shareOwner'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(43),
- 'file_target' => $qb->expr()->literal('userTarget'),
- 'permissions' => $qb->expr()->literal(0),
- 'parent' => $qb->expr()->literal($id),
- ]);
- $this->assertEquals(1, $qb->executeStatement());
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(42);
- $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturnSelf();
- $this->rootFolder->method('getFirstNodeById')->with(42)->willReturn($file);
-
- $share = $this->provider->getSharesBy('sharedBy', IShare::TYPE_USER, $file, false, 1, 0);
- $this->assertCount(1, $share);
-
- /** @var IShare $share */
- $share = $share[0];
- $this->assertEquals($id, $share->getId());
- $this->assertEquals('sharedWith', $share->getSharedWith());
- $this->assertEquals('shareOwner', $share->getShareOwner());
- $this->assertEquals('sharedBy', $share->getSharedBy());
- $this->assertEquals(IShare::TYPE_USER, $share->getShareType());
- $this->assertEquals(13, $share->getPermissions());
- $this->assertEquals('myTarget', $share->getTarget());
- }
-
- public function testGetSharesReshare(): void {
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_USER),
- 'share_with' => $qb->expr()->literal('sharedWith'),
- 'uid_owner' => $qb->expr()->literal('shareOwner'),
- 'uid_initiator' => $qb->expr()->literal('shareOwner'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(42),
- 'file_target' => $qb->expr()->literal('myTarget'),
- 'permissions' => $qb->expr()->literal(13),
- ]);
- $this->assertEquals(1, $qb->executeStatement());
- $id1 = $qb->getLastInsertId();
-
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_USER),
- 'share_with' => $qb->expr()->literal('sharedWith'),
- 'uid_owner' => $qb->expr()->literal('shareOwner'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(42),
- 'file_target' => $qb->expr()->literal('userTarget'),
- 'permissions' => $qb->expr()->literal(0),
- ]);
- $this->assertEquals(1, $qb->executeStatement());
- $id2 = $qb->getLastInsertId();
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(42);
- $this->rootFolder->method('getUserFolder')->with('shareOwner')->willReturnSelf();
- $this->rootFolder->method('getFirstNodeById')->with(42)->willReturn($file);
-
- $shares = $this->provider->getSharesBy('shareOwner', IShare::TYPE_USER, null, true, -1, 0);
- $this->assertCount(2, $shares);
-
- /** @var IShare $share */
- $share = $shares[0];
- $this->assertEquals($id1, $share->getId());
- $this->assertSame('sharedWith', $share->getSharedWith());
- $this->assertSame('shareOwner', $share->getShareOwner());
- $this->assertSame('shareOwner', $share->getSharedBy());
- $this->assertEquals(IShare::TYPE_USER, $share->getShareType());
- $this->assertEquals(13, $share->getPermissions());
- $this->assertEquals('myTarget', $share->getTarget());
-
- $share = $shares[1];
- $this->assertEquals($id2, $share->getId());
- $this->assertSame('sharedWith', $share->getSharedWith());
- $this->assertSame('shareOwner', $share->getShareOwner());
- $this->assertSame('sharedBy', $share->getSharedBy());
- $this->assertEquals(IShare::TYPE_USER, $share->getShareType());
- $this->assertEquals(0, $share->getPermissions());
- $this->assertEquals('userTarget', $share->getTarget());
- }
-
- public function testDeleteFromSelfGroupNoCustomShare(): void {
- $qb = $this->dbConn->getQueryBuilder();
- $stmt = $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_GROUP),
- 'share_with' => $qb->expr()->literal('group'),
- 'uid_owner' => $qb->expr()->literal('user1'),
- 'uid_initiator' => $qb->expr()->literal('user1'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(1),
- 'file_target' => $qb->expr()->literal('myTarget1'),
- 'permissions' => $qb->expr()->literal(2)
- ])->executeStatement();
- $this->assertEquals(1, $stmt);
- $id = $qb->getLastInsertId();
-
- $user1 = $this->createMock(IUser::class);
- $user1->method('getUID')->willReturn('user1');
- $user2 = $this->createMock(IUser::class);
- $user2->method('getUID')->willReturn('user2');
- $this->userManager->method('get')->willReturnMap([
- ['user1', $user1],
- ['user2', $user2],
- ]);
-
- $group = $this->createMock(IGroup::class);
- $group->method('getGID')->willReturn('group');
- $group->method('inGroup')->with($user2)->willReturn(true);
- $group->method('getDisplayName')->willReturn('group-displayname');
- $this->groupManager->method('get')->with('group')->willReturn($group);
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(1);
-
- $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf();
- $this->rootFolder->method('getFirstNodeById')->with(1)->willReturn($file);
-
- $share = $this->provider->getShareById($id);
-
- $this->provider->deleteFromSelf($share, 'user2');
-
- $qb = $this->dbConn->getQueryBuilder();
- $stmt = $qb->select('*')
- ->from('share')
- ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(2)))
- ->executeQuery();
-
- $shares = $stmt->fetchAllAssociative();
- $stmt->closeCursor();
-
- $this->assertCount(1, $shares);
- $share2 = $shares[0];
- $this->assertEquals($id, $share2['parent']);
- $this->assertEquals(0, $share2['permissions']);
- $this->assertEquals('user2', $share2['share_with']);
- }
-
- public function testDeleteFromSelfGroupAlreadyCustomShare(): void {
- $qb = $this->dbConn->getQueryBuilder();
- $stmt = $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_GROUP),
- 'share_with' => $qb->expr()->literal('group'),
- 'uid_owner' => $qb->expr()->literal('user1'),
- 'uid_initiator' => $qb->expr()->literal('user1'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(1),
- 'file_target' => $qb->expr()->literal('myTarget1'),
- 'permissions' => $qb->expr()->literal(2)
- ])->executeStatement();
- $this->assertEquals(1, $stmt);
- $id = $qb->getLastInsertId();
-
- $qb = $this->dbConn->getQueryBuilder();
- $stmt = $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(2),
- 'share_with' => $qb->expr()->literal('user2'),
- 'uid_owner' => $qb->expr()->literal('user1'),
- 'uid_initiator' => $qb->expr()->literal('user1'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(1),
- 'file_target' => $qb->expr()->literal('myTarget1'),
- 'permissions' => $qb->expr()->literal(2),
- 'parent' => $qb->expr()->literal($id),
- ])->executeStatement();
- $this->assertEquals(1, $stmt);
-
- $user1 = $this->createMock(IUser::class);
- $user1->method('getUID')->willReturn('user1');
- $user2 = $this->createMock(IUser::class);
- $user2->method('getUID')->willReturn('user2');
- $this->userManager->method('get')->willReturnMap([
- ['user1', $user1],
- ['user2', $user2],
- ]);
-
- $group = $this->createMock(IGroup::class);
- $group->method('getGID')->willReturn('group');
- $group->method('inGroup')->with($user2)->willReturn(true);
- $group->method('getDisplayName')->willReturn('group-displayname');
- $this->groupManager->method('get')->with('group')->willReturn($group);
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(1);
-
- $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf();
- $this->rootFolder->method('getFirstNodeById')->with(1)->willReturn($file);
-
- $share = $this->provider->getShareById($id);
-
- $this->provider->deleteFromSelf($share, 'user2');
-
- $qb = $this->dbConn->getQueryBuilder();
- $stmt = $qb->select('*')
- ->from('share')
- ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(2)))
- ->executeQuery();
-
- $shares = $stmt->fetchAllAssociative();
- $stmt->closeCursor();
-
- $this->assertCount(1, $shares);
- $share2 = $shares[0];
- $this->assertEquals($id, $share2['parent']);
- $this->assertEquals(0, $share2['permissions']);
- $this->assertEquals('user2', $share2['share_with']);
- }
-
- public function testDeleteFromSelfGroupUserNotInGroup(): void {
- $qb = $this->dbConn->getQueryBuilder();
- $stmt = $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_GROUP),
- 'share_with' => $qb->expr()->literal('group'),
- 'uid_owner' => $qb->expr()->literal('user1'),
- 'uid_initiator' => $qb->expr()->literal('user1'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(1),
- 'file_target' => $qb->expr()->literal('myTarget1'),
- 'permissions' => $qb->expr()->literal(2)
- ])->executeStatement();
- $this->assertEquals(1, $stmt);
- $id = $qb->getLastInsertId();
-
- $user1 = $this->createMock(IUser::class);
- $user1->method('getUID')->willReturn('user1');
- $user2 = $this->createMock(IUser::class);
- $user2->method('getUID')->willReturn('user2');
- $this->userManager->method('get')->willReturnMap([
- ['user1', $user1],
- ['user2', $user2],
- ]);
-
- $group = $this->createMock(IGroup::class);
- $group->method('getGID')->willReturn('group');
- $group->method('inGroup')->with($user2)->willReturn(false);
- $group->method('getDisplayName')->willReturn('group-displayname');
- $this->groupManager->method('get')->with('group')->willReturn($group);
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(1);
-
- $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf();
- $this->rootFolder->method('getFirstNodeById')->with(1)->willReturn($file);
-
- $share = $this->provider->getShareById($id);
-
- $this->provider->deleteFromSelf($share, 'user2');
- }
-
- public function testDeleteFromSelfGroupDoesNotExist(): void {
- $this->expectException(ProviderException::class);
- $this->expectExceptionMessage('Group "group" does not exist');
-
- $qb = $this->dbConn->getQueryBuilder();
- $stmt = $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_GROUP),
- 'share_with' => $qb->expr()->literal('group'),
- 'uid_owner' => $qb->expr()->literal('user1'),
- 'uid_initiator' => $qb->expr()->literal('user1'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(1),
- 'file_target' => $qb->expr()->literal('myTarget1'),
- 'permissions' => $qb->expr()->literal(2)
- ])->executeStatement();
- $this->assertEquals(1, $stmt);
- $id = $qb->getLastInsertId();
-
- $user1 = $this->createMock(IUser::class);
- $user1->method('getUID')->willReturn('user1');
- $user2 = $this->createMock(IUser::class);
- $user2->method('getUID')->willReturn('user2');
- $this->userManager->method('get')->willReturnMap([
- ['user1', $user1],
- ['user2', $user2],
- ]);
-
- $this->groupManager->method('get')->with('group')->willReturn(null);
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(1);
-
- $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf();
- $this->rootFolder->method('getFirstNodeById')->with(1)->willReturn($file);
-
- $share = $this->provider->getShareById($id);
-
- $this->provider->deleteFromSelf($share, 'user2');
- }
-
- public function testDeleteFromSelfUser(): void {
- $qb = $this->dbConn->getQueryBuilder();
- $stmt = $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_USER),
- 'share_with' => $qb->expr()->literal('user2'),
- 'uid_owner' => $qb->expr()->literal('user1'),
- 'uid_initiator' => $qb->expr()->literal('user1'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(1),
- 'file_target' => $qb->expr()->literal('myTarget1'),
- 'permissions' => $qb->expr()->literal(2)
- ])->executeStatement();
- $this->assertEquals(1, $stmt);
- $id = $qb->getLastInsertId();
-
- $user1 = $this->createMock(IUser::class);
- $user1->method('getUID')->willReturn('user1');
- $user1->method('getDisplayName')->willReturn('user1');
- $user2 = $this->createMock(IUser::class);
- $user2->method('getUID')->willReturn('user2');
- $user2->method('getDisplayName')->willReturn('user2');
- $this->userManager->method('get')->willReturnMap([
- ['user1', $user1],
- ['user2', $user2],
- ]);
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(1);
-
- $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf();
- $this->rootFolder->method('getFirstNodeById')->with(1)->willReturn($file);
-
- $share = $this->provider->getShareById($id);
-
- $this->provider->deleteFromSelf($share, 'user2');
-
- $qb = $this->dbConn->getQueryBuilder();
- $stmt = $qb->select('*')
- ->from('share')
- ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
- ->executeQuery();
-
- $shares = $stmt->fetchAllAssociative();
- $stmt->closeCursor();
-
- $this->assertCount(0, $shares);
- }
-
- public function testDeleteFromSelfUserNotRecipient(): void {
- $this->expectException(ProviderException::class);
- $this->expectExceptionMessage('Recipient does not match');
-
- $qb = $this->dbConn->getQueryBuilder();
- $stmt = $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_USER),
- 'share_with' => $qb->expr()->literal('user2'),
- 'uid_owner' => $qb->expr()->literal('user1'),
- 'uid_initiator' => $qb->expr()->literal('user1'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(1),
- 'file_target' => $qb->expr()->literal('myTarget1'),
- 'permissions' => $qb->expr()->literal(2)
- ])->executeStatement();
- $this->assertEquals(1, $stmt);
- $id = $qb->getLastInsertId();
-
- $user1 = $this->createMock(IUser::class);
- $user1->method('getUID')->willReturn('user1');
- $user1->method('getDisplayName')->willReturn('user1');
- $user2 = $this->createMock(IUser::class);
- $user2->method('getUID')->willReturn('user2');
- $user2->method('getDisplayName')->willReturn('user2');
- $user3 = $this->createMock(IUser::class);
- $this->userManager->method('get')->willReturnMap([
- ['user1', $user1],
- ['user2', $user2],
- ]);
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(1);
-
- $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf();
- $this->rootFolder->method('getFirstNodeById')->with(1)->willReturn($file);
-
- $share = $this->provider->getShareById($id);
-
- $this->provider->deleteFromSelf($share, $user3);
- }
-
- public function testDeleteFromSelfLink(): void {
- $this->expectException(ProviderException::class);
- $this->expectExceptionMessage('Invalid shareType');
-
- $qb = $this->dbConn->getQueryBuilder();
- $stmt = $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_LINK),
- 'uid_owner' => $qb->expr()->literal('user1'),
- 'uid_initiator' => $qb->expr()->literal('user1'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(1),
- 'file_target' => $qb->expr()->literal('myTarget1'),
- 'permissions' => $qb->expr()->literal(2),
- 'token' => $qb->expr()->literal('token'),
- ])->executeStatement();
- $this->assertEquals(1, $stmt);
- $id = $qb->getLastInsertId();
-
- $user1 = $this->createMock(IUser::class);
- $user1->method('getUID')->willReturn('user1');
- $this->userManager->method('get')->willReturnMap([
- ['user1', $user1],
- ]);
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(1);
-
- $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf();
- $this->rootFolder->method('getFirstNodeById')->with(1)->willReturn($file);
-
- $share = $this->provider->getShareById($id);
-
- $this->provider->deleteFromSelf($share, $user1);
- }
-
- public function testUpdateUser(): void {
- $id = $this->addShareToDB(IShare::TYPE_USER, 'user0', 'user1', 'user2',
- 'file', 42, 'target', 31, null, null);
-
- $users = [];
- for ($i = 0; $i < 6; $i++) {
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('user' . $i);
- $user->method('getDisplayName')->willReturn('user' . $i);
- $users['user' . $i] = $user;
- }
-
- $this->userManager->method('get')->willReturnCallback(
- function ($userId) use ($users) {
- return $users[$userId];
- }
- );
-
- $file1 = $this->createMock(File::class);
- $file1->method('getId')->willReturn(42);
- $file2 = $this->createMock(File::class);
- $file2->method('getId')->willReturn(43);
-
- $folder1 = $this->createMock(Folder::class);
- $folder1->method('getFirstNodeById')->with(42)->willReturn($file1);
- $folder2 = $this->createMock(Folder::class);
- $folder2->method('getFirstNodeById')->with(43)->willReturn($file2);
-
- $this->rootFolder->method('getUserFolder')->willReturnMap([
- ['user2', $folder1],
- ['user5', $folder2],
- ]);
-
- $share = $this->provider->getShareById($id);
-
- $share->setSharedWith('user3');
- $share->setSharedBy('user4');
- $share->setShareOwner('user5');
- $share->setNode($file2);
- $share->setPermissions(1);
-
- $share2 = $this->provider->update($share);
-
- $this->assertEquals($id, $share2->getId());
- $this->assertSame('user3', $share2->getSharedWith());
- $this->assertSame('user4', $share2->getSharedBy());
- $this->assertSame('user5', $share2->getShareOwner());
- $this->assertSame(1, $share2->getPermissions());
-
- $share2 = $this->provider->getShareById($id);
-
- $this->assertEquals($id, $share2->getId());
- $this->assertSame('user3', $share2->getSharedWith());
- $this->assertSame('user4', $share2->getSharedBy());
- $this->assertSame('user5', $share2->getShareOwner());
- $this->assertSame(1, $share2->getPermissions());
- }
-
- public function testUpdateLink(): void {
- $passwordHash = Server::get(IHasher::class)->hash('password');
-
- $id = $this->addShareToDB(IShare::TYPE_LINK, null, 'user1', 'user2',
- 'file', 42, 'target', 31, null, null);
-
- $users = [];
- for ($i = 0; $i < 6; $i++) {
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('user' . $i);
- $users['user' . $i] = $user;
- }
-
- $this->userManager->method('get')->willReturnCallback(
- function ($userId) use ($users) {
- return $users[$userId];
- }
- );
-
- $file1 = $this->createMock(File::class);
- $file1->method('getId')->willReturn(42);
- $file2 = $this->createMock(File::class);
- $file2->method('getId')->willReturn(43);
-
- $folder1 = $this->createMock(Folder::class);
- $folder1->method('getFirstNodeById')->with(42)->willReturn($file1);
- $folder2 = $this->createMock(Folder::class);
- $folder2->method('getFirstNodeById')->with(43)->willReturn($file2);
-
- $this->rootFolder->method('getUserFolder')->willReturnMap([
- ['user2', $folder1],
- ['user5', $folder2],
- ]);
-
- $share = $this->provider->getShareById($id);
-
- $share->setPasswordHash($passwordHash);
- $share->setSendPasswordByTalk(true);
- $share->setSharedBy('user4');
- $share->setShareOwner('user5');
- $share->setNode($file2);
- $share->setPermissions(1);
-
- $share2 = $this->provider->update($share);
-
- $this->assertEquals($id, $share2->getId());
- $this->assertTrue($share2->isPasswordHashed());
- $this->assertEquals($passwordHash, $share2->getPassword());
- $this->assertSame(true, $share2->getSendPasswordByTalk());
- $this->assertSame('user4', $share2->getSharedBy());
- $this->assertSame('user5', $share2->getShareOwner());
- $this->assertSame(1, $share2->getPermissions());
-
- $share2 = $this->provider->getShareById($id);
-
- $this->assertEquals($id, $share2->getId());
- $this->assertTrue($share2->isPasswordHashed());
- $this->assertEquals($passwordHash, $share2->getPassword());
- $this->assertSame(true, $share2->getSendPasswordByTalk());
- $this->assertSame('user4', $share2->getSharedBy());
- $this->assertSame('user5', $share2->getShareOwner());
- $this->assertSame(1, $share2->getPermissions());
- }
-
- public function testUpdateLinkRemovePassword(): void {
- $passwordHash = Server::get(IHasher::class)->hash('password');
-
- $id = $this->addShareToDB(IShare::TYPE_LINK, 'foo', 'user1', 'user2',
- 'file', 42, 'target', 31, null, null);
-
- $qb = $this->dbConn->getQueryBuilder();
- $qb->update('share');
- $qb->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
- $qb->set('password', $qb->createNamedParameter($passwordHash));
- $this->assertEquals(1, $qb->executeStatement());
-
- $users = [];
- for ($i = 0; $i < 6; $i++) {
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('user' . $i);
- $users['user' . $i] = $user;
- }
-
- $this->userManager->method('get')->willReturnCallback(
- function ($userId) use ($users) {
- return $users[$userId];
- }
- );
-
- $file1 = $this->createMock(File::class);
- $file1->method('getId')->willReturn(42);
- $file2 = $this->createMock(File::class);
- $file2->method('getId')->willReturn(43);
-
- $folder1 = $this->createMock(Folder::class);
- $folder1->method('getFirstNodeById')->with(42)->willReturn($file1);
- $folder2 = $this->createMock(Folder::class);
- $folder2->method('getFirstNodeById')->with(43)->willReturn($file2);
-
- $this->rootFolder->method('getUserFolder')->willReturnMap([
- ['user2', $folder1],
- ['user5', $folder2],
- ]);
-
- $share = $this->provider->getShareById($id);
-
- $share->setPassword(null);
- $share->setSharedBy('user4');
- $share->setShareOwner('user5');
- $share->setNode($file2);
- $share->setPermissions(1);
-
- $share2 = $this->provider->update($share);
-
- $this->assertEquals($id, $share2->getId());
- $this->assertFalse($share2->isPasswordHashed());
- $this->assertEquals(null, $share2->getPassword());
- $this->assertSame('user4', $share2->getSharedBy());
- $this->assertSame('user5', $share2->getShareOwner());
- $this->assertSame(1, $share2->getPermissions());
-
- $share2 = $this->provider->getShareById($id);
-
- $this->assertEquals($id, $share2->getId());
- $this->assertFalse($share2->isPasswordHashed());
- $this->assertEquals(null, $share2->getPassword());
- $this->assertSame('user4', $share2->getSharedBy());
- $this->assertSame('user5', $share2->getShareOwner());
- $this->assertSame(1, $share2->getPermissions());
- }
-
- public function testUpdateGroupNoSub(): void {
- $id = $this->addShareToDB(IShare::TYPE_GROUP, 'group0', 'user1', 'user2',
- 'file', 42, 'target', 31, null, null);
-
- $users = [];
- for ($i = 0; $i < 6; $i++) {
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('user' . $i);
- $users['user' . $i] = $user;
- }
-
- $this->userManager->method('get')->willReturnCallback(
- function ($userId) use ($users) {
- return $users[$userId];
- }
- );
-
- $groups = [];
- for ($i = 0; $i < 2; $i++) {
- $group = $this->createMock(IGroup::class);
- $group->method('getGID')->willReturn('group' . $i);
- $group->method('getDisplayName')->willReturn('group-displayname' . $i);
- $groups['group' . $i] = $group;
- }
-
- $this->groupManager->method('get')->willReturnCallback(
- function ($groupId) use ($groups) {
- return $groups[$groupId];
- }
- );
-
- $file1 = $this->createMock(File::class);
- $file1->method('getId')->willReturn(42);
- $file2 = $this->createMock(File::class);
- $file2->method('getId')->willReturn(43);
-
- $folder1 = $this->createMock(Folder::class);
- $folder1->method('getFirstNodeById')->with(42)->willReturn($file1);
- $folder2 = $this->createMock(Folder::class);
- $folder2->method('getFirstNodeById')->with(43)->willReturn($file2);
-
- $this->rootFolder->method('getUserFolder')->willReturnMap([
- ['user2', $folder1],
- ['user5', $folder2],
- ]);
-
- $share = $this->provider->getShareById($id);
-
- $share->setSharedWith('group0');
- $share->setSharedBy('user4');
- $share->setShareOwner('user5');
- $share->setNode($file2);
- $share->setPermissions(1);
-
- $share2 = $this->provider->update($share);
-
- $this->assertEquals($id, $share2->getId());
- // Group shares do not allow updating the recipient
- $this->assertSame('group0', $share2->getSharedWith());
- $this->assertSame('user4', $share2->getSharedBy());
- $this->assertSame('user5', $share2->getShareOwner());
- $this->assertSame(1, $share2->getPermissions());
-
- $share2 = $this->provider->getShareById($id);
-
- $this->assertEquals($id, $share2->getId());
- // Group shares do not allow updating the recipient
- $this->assertSame('group0', $share2->getSharedWith());
- $this->assertSame('user4', $share2->getSharedBy());
- $this->assertSame('user5', $share2->getShareOwner());
- $this->assertSame(1, $share2->getPermissions());
- }
-
- public function testUpdateGroupSubShares(): void {
- $id = $this->addShareToDB(IShare::TYPE_GROUP, 'group0', 'user1', 'user2',
- 'file', 42, 'target', 31, null, null);
-
- $id2 = $this->addShareToDB(2, 'user0', 'user1', 'user2',
- 'file', 42, 'mytarget', 31, null, null, $id);
-
- $id3 = $this->addShareToDB(2, 'user3', 'user1', 'user2',
- 'file', 42, 'mytarget2', 0, null, null, $id);
-
- $users = [];
- for ($i = 0; $i < 6; $i++) {
- $user = $this->createMock(IUser::class);
- $user->method('getUID')->willReturn('user' . $i);
- $users['user' . $i] = $user;
- }
-
- $this->userManager->method('get')->willReturnCallback(
- function ($userId) use ($users) {
- return $users[$userId];
- }
- );
-
- $groups = [];
- for ($i = 0; $i < 2; $i++) {
- $group = $this->createMock(IGroup::class);
- $group->method('getGID')->willReturn('group' . $i);
- $group->method('getDisplayName')->willReturn('group-displayname' . $i);
- $groups['group' . $i] = $group;
- }
-
- $this->groupManager->method('get')->willReturnCallback(
- function ($groupId) use ($groups) {
- return $groups[$groupId];
- }
- );
-
- $file1 = $this->createMock(File::class);
- $file1->method('getId')->willReturn(42);
- $file2 = $this->createMock(File::class);
- $file2->method('getId')->willReturn(43);
-
- $folder1 = $this->createMock(Folder::class);
- $folder1->method('getFirstNodeById')->with(42)->willReturn($file1);
- $folder2 = $this->createMock(Folder::class);
- $folder2->method('getFirstNodeById')->with(43)->willReturn($file2);
-
- $this->rootFolder->method('getUserFolder')->willReturnMap([
- ['user2', $folder1],
- ['user5', $folder2],
- ]);
-
- $share = $this->provider->getShareById($id);
-
- $share->setSharedWith('group0');
- $share->setSharedBy('user4');
- $share->setShareOwner('user5');
- $share->setNode($file2);
- $share->setPermissions(1);
-
- $share2 = $this->provider->update($share);
-
- $this->assertEquals($id, $share2->getId());
- // Group shares do not allow updating the recipient
- $this->assertSame('group0', $share2->getSharedWith());
- $this->assertSame('user4', $share2->getSharedBy());
- $this->assertSame('user5', $share2->getShareOwner());
- $this->assertSame(1, $share2->getPermissions());
-
- $share2 = $this->provider->getShareById($id);
-
- $this->assertEquals($id, $share2->getId());
- // Group shares do not allow updating the recipient
- $this->assertSame('group0', $share2->getSharedWith());
- $this->assertSame('user4', $share2->getSharedBy());
- $this->assertSame('user5', $share2->getShareOwner());
- $this->assertSame(1, $share2->getPermissions());
-
- $qb = $this->dbConn->getQueryBuilder();
- $stmt = $qb->select('*')
- ->from('share')
- ->where($qb->expr()->eq('parent', $qb->createNamedParameter($id)))
- ->orderBy('id')
- ->executeQuery();
-
- $shares = $stmt->fetchAllAssociative();
-
- $this->assertSame('user0', $shares[0]['share_with']);
- $this->assertSame('user4', $shares[0]['uid_initiator']);
- $this->assertSame('user5', $shares[0]['uid_owner']);
- $this->assertSame(1, (int)$shares[0]['permissions']);
-
- $this->assertSame('user3', $shares[1]['share_with']);
- $this->assertSame('user4', $shares[1]['uid_initiator']);
- $this->assertSame('user5', $shares[1]['uid_owner']);
- $this->assertSame(0, (int)$shares[1]['permissions']);
-
- $stmt->closeCursor();
- }
-
- public function testMoveUserShare(): void {
- $id = $this->addShareToDB(IShare::TYPE_USER, 'user0', 'user1', 'user1', 'file',
- 42, 'mytaret', 31, null, null);
-
- $user0 = $this->createMock(IUser::class);
- $user0->method('getUID')->willReturn('user0');
- $user0->method('getDisplayName')->willReturn('user0');
- $user1 = $this->createMock(IUser::class);
- $user1->method('getUID')->willReturn('user1');
- $user1->method('getDisplayName')->willReturn('user1');
-
- $this->userManager->method('get')->willReturnMap([
- ['user0', $user0],
- ['user1', $user1],
- ]);
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(42);
-
- $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf();
- $this->rootFolder->method('getFirstNodeById')->willReturn($file);
-
- $share = $this->provider->getShareById($id, null);
-
- $share->setTarget('/newTarget');
- $this->provider->move($share, $user0);
-
- $share = $this->provider->getShareById($id, null);
- $this->assertSame('/newTarget', $share->getTarget());
- }
-
- public function testMoveGroupShare(): void {
- $id = $this->addShareToDB(IShare::TYPE_GROUP, 'group0', 'user1', 'user1', 'file',
- 42, 'mytaret', 31, null, null);
-
- $user0 = $this->createMock(IUser::class);
- $user0->method('getUID')->willReturn('user0');
- $user1 = $this->createMock(IUser::class);
- $user1->method('getUID')->willReturn('user1');
-
- $group0 = $this->createMock(IGroup::class);
- $group0->method('getGID')->willReturn('group0');
- $group0->method('inGroup')->with($user0)->willReturn(true);
- $group0->method('getDisplayName')->willReturn('group0-displayname');
-
- $this->groupManager->method('get')->with('group0')->willReturn($group0);
-
- $this->userManager->method('get')->willReturnMap([
- ['user0', $user0],
- ['user1', $user1],
- ]);
-
- $folder = $this->createMock(Folder::class);
- $folder->method('getId')->willReturn(42);
-
- $this->rootFolder->method('getUserFolder')->with('user1')->willReturnSelf();
- $this->rootFolder->method('getFirstNodeById')->willReturn($folder);
-
- $share = $this->provider->getShareById($id, 'user0');
-
- $share->setTarget('/newTarget');
- $this->provider->move($share, 'user0');
-
- $share = $this->provider->getShareById($id, 'user0');
- $this->assertSame('/newTarget', $share->getTarget());
- // The USERGROUP subshare created on first move must be STATUS_ACCEPTED so
- // MountProvider does not skip it (default DB value is STATUS_PENDING=0).
- $this->assertSame(IShare::STATUS_ACCEPTED, $share->getStatus());
-
- $share->setTarget('/ultraNewTarget');
- $this->provider->move($share, 'user0');
-
- $share = $this->provider->getShareById($id, 'user0');
- $this->assertSame('/ultraNewTarget', $share->getTarget());
- // Second move hits the UPDATE branch (USERGROUP subshare already exists).
- // STATUS_ACCEPTED must be preserved — the UPDATE only touches file_target.
- $this->assertSame(IShare::STATUS_ACCEPTED, $share->getStatus());
- }
-
- public static function dataDeleteUser(): array {
- return [
- [IShare::TYPE_USER, 'a', 'b', 'c', 'a', true],
- [IShare::TYPE_USER, 'a', 'b', 'c', 'b', false],
- [IShare::TYPE_USER, 'a', 'b', 'c', 'c', true],
- [IShare::TYPE_USER, 'a', 'b', 'c', 'd', false],
- [IShare::TYPE_GROUP, 'a', 'b', 'c', 'a', true],
- [IShare::TYPE_GROUP, 'a', 'b', 'c', 'b', false],
- // The group c is still valid but user c is deleted so group share stays
- [IShare::TYPE_GROUP, 'a', 'b', 'c', 'c', false],
- [IShare::TYPE_GROUP, 'a', 'b', 'c', 'd', false],
- [IShare::TYPE_LINK, 'a', 'b', 'c', 'a', true],
- // To avoid invisible link shares delete initiated link shares as well (see #22327)
- [IShare::TYPE_LINK, 'a', 'b', 'c', 'b', true],
- [IShare::TYPE_LINK, 'a', 'b', 'c', 'c', false],
- [IShare::TYPE_LINK, 'a', 'b', 'c', 'd', false],
- ];
- }
-
- /**
- *
- * @param int $type The shareType (user/group/link)
- * @param string $owner The owner of the share (uid)
- * @param string $initiator The initiator of the share (uid)
- * @param string $recipient The recipient of the share (uid/gid/pass)
- * @param string $deletedUser The user that is deleted
- * @param bool $rowDeleted Is the row deleted in this setup
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataDeleteUser')]
- public function testDeleteUser($type, $owner, $initiator, $recipient, $deletedUser, $rowDeleted): void {
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->setValue('share_type', $qb->createNamedParameter($type))
- ->setValue('uid_owner', $qb->createNamedParameter($owner))
- ->setValue('uid_initiator', $qb->createNamedParameter($initiator))
- ->setValue('share_with', $qb->createNamedParameter($recipient))
- ->setValue('item_type', $qb->createNamedParameter('file'))
- ->setValue('item_source', $qb->createNamedParameter(42))
- ->setValue('file_source', $qb->createNamedParameter(42))
- ->executeStatement();
-
- $id = $qb->getLastInsertId();
-
- $this->provider->userDeleted($deletedUser, $type);
-
- $qb = $this->dbConn->getQueryBuilder();
- $qb->select('*')
- ->from('share')
- ->where(
- $qb->expr()->eq('id', $qb->createNamedParameter($id))
- );
- $cursor = $qb->executeQuery();
- $data = $cursor->fetchAllAssociative();
- $cursor->closeCursor();
-
- $this->assertCount($rowDeleted ? 0 : 1, $data);
- }
-
- public static function dataDeleteUserGroup(): array {
- return [
- ['a', 'b', 'c', 'a', true, true],
- ['a', 'b', 'c', 'b', false, false],
- ['a', 'b', 'c', 'c', false, true],
- ['a', 'b', 'c', 'd', false, false],
- ];
- }
-
- /**
- *
- * @param string $owner The owner of the share (uid)
- * @param string $initiator The initiator of the share (uid)
- * @param string $recipient The recipient of the usergroup share (uid)
- * @param string $deletedUser The user that is deleted
- * @param bool $groupShareDeleted
- * @param bool $userGroupShareDeleted
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataDeleteUserGroup')]
- public function testDeleteUserGroup($owner, $initiator, $recipient, $deletedUser, $groupShareDeleted, $userGroupShareDeleted): void {
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->setValue('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP))
- ->setValue('uid_owner', $qb->createNamedParameter($owner))
- ->setValue('uid_initiator', $qb->createNamedParameter($initiator))
- ->setValue('share_with', $qb->createNamedParameter('group'))
- ->setValue('item_type', $qb->createNamedParameter('file'))
- ->setValue('item_source', $qb->createNamedParameter(42))
- ->setValue('file_source', $qb->createNamedParameter(42))
- ->executeStatement();
- $groupId = $qb->getLastInsertId();
-
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->setValue('share_type', $qb->createNamedParameter(2))
- ->setValue('uid_owner', $qb->createNamedParameter($owner))
- ->setValue('uid_initiator', $qb->createNamedParameter($initiator))
- ->setValue('share_with', $qb->createNamedParameter($recipient))
- ->setValue('item_type', $qb->createNamedParameter('file'))
- ->setValue('item_source', $qb->createNamedParameter(42))
- ->setValue('file_source', $qb->createNamedParameter(42))
- ->executeStatement();
- $userGroupId = $qb->getLastInsertId();
-
- $this->provider->userDeleted($deletedUser, IShare::TYPE_GROUP);
-
- $qb = $this->dbConn->getQueryBuilder();
- $qb->select('*')
- ->from('share')
- ->where(
- $qb->expr()->eq('id', $qb->createNamedParameter($userGroupId))
- );
- $cursor = $qb->executeQuery();
- $data = $cursor->fetchAllAssociative();
- $cursor->closeCursor();
- $this->assertCount($userGroupShareDeleted ? 0 : 1, $data);
-
- $qb = $this->dbConn->getQueryBuilder();
- $qb->select('*')
- ->from('share')
- ->where(
- $qb->expr()->eq('id', $qb->createNamedParameter($groupId))
- );
- $cursor = $qb->executeQuery();
- $data = $cursor->fetchAllAssociative();
- $cursor->closeCursor();
- $this->assertCount($groupShareDeleted ? 0 : 1, $data);
- }
-
- public static function dataGroupDeleted(): array {
- return [
- [
- [
- 'type' => IShare::TYPE_USER,
- 'recipient' => 'user',
- 'children' => []
- ], 'group', false
- ],
- [
- [
- 'type' => IShare::TYPE_USER,
- 'recipient' => 'user',
- 'children' => []
- ], 'user', false
- ],
- [
- [
- 'type' => IShare::TYPE_LINK,
- 'recipient' => 'user',
- 'children' => []
- ], 'group', false
- ],
- [
- [
- 'type' => IShare::TYPE_GROUP,
- 'recipient' => 'group1',
- 'children' => [
- 'foo',
- 'bar'
- ]
- ], 'group2', false
- ],
- [
- [
- 'type' => IShare::TYPE_GROUP,
- 'recipient' => 'group1',
- 'children' => [
- 'foo',
- 'bar'
- ]
- ], 'group1', true
- ],
- ];
- }
-
- /**
- *
- * @param $shares
- * @param $groupToDelete
- * @param $shouldBeDeleted
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGroupDeleted')]
- public function testGroupDeleted($shares, $groupToDelete, $shouldBeDeleted): void {
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->setValue('share_type', $qb->createNamedParameter($shares['type']))
- ->setValue('uid_owner', $qb->createNamedParameter('owner'))
- ->setValue('uid_initiator', $qb->createNamedParameter('initiator'))
- ->setValue('share_with', $qb->createNamedParameter($shares['recipient']))
- ->setValue('item_type', $qb->createNamedParameter('file'))
- ->setValue('item_source', $qb->createNamedParameter(42))
- ->setValue('file_source', $qb->createNamedParameter(42))
- ->executeStatement();
- $ids = [$qb->getLastInsertId()];
-
- foreach ($shares['children'] as $child) {
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->setValue('share_type', $qb->createNamedParameter(2))
- ->setValue('uid_owner', $qb->createNamedParameter('owner'))
- ->setValue('uid_initiator', $qb->createNamedParameter('initiator'))
- ->setValue('share_with', $qb->createNamedParameter($child))
- ->setValue('item_type', $qb->createNamedParameter('file'))
- ->setValue('item_source', $qb->createNamedParameter(42))
- ->setValue('file_source', $qb->createNamedParameter(42))
- ->setValue('parent', $qb->createNamedParameter($ids[0]))
- ->executeStatement();
- $ids[] = $qb->getLastInsertId();
- }
-
- $this->provider->groupDeleted($groupToDelete);
-
- $qb = $this->dbConn->getQueryBuilder();
- $cursor = $qb->select('*')
- ->from('share')
- ->where($qb->expr()->in('id', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
- ->executeQuery();
- $data = $cursor->fetchAllAssociative();
- $cursor->closeCursor();
-
- $this->assertCount($shouldBeDeleted ? 0 : count($ids), $data);
- }
-
- public static function dataUserDeletedFromGroup(): array {
- return [
- ['group1', 'user1', true],
- ['group1', 'user2', false],
- ['group2', 'user1', false],
- ];
- }
-
- /**
- * Given a group share with 'group1'
- * And a user specific group share with 'user1'.
- * User $user is deleted from group $gid.
- *
- *
- * @param string $group
- * @param string $user
- * @param bool $toDelete
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataUserDeletedFromGroup')]
- public function testUserDeletedFromGroup($group, $user, $toDelete): void {
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->setValue('share_type', $qb->createNamedParameter(IShare::TYPE_GROUP))
- ->setValue('uid_owner', $qb->createNamedParameter('owner'))
- ->setValue('uid_initiator', $qb->createNamedParameter('initiator'))
- ->setValue('share_with', $qb->createNamedParameter('group1'))
- ->setValue('item_type', $qb->createNamedParameter('file'))
- ->setValue('item_source', $qb->createNamedParameter(42))
- ->setValue('file_source', $qb->createNamedParameter(42));
- $qb->executeStatement();
- $id1 = $qb->getLastInsertId();
-
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share')
- ->setValue('share_type', $qb->createNamedParameter(2))
- ->setValue('uid_owner', $qb->createNamedParameter('owner'))
- ->setValue('uid_initiator', $qb->createNamedParameter('initiator'))
- ->setValue('share_with', $qb->createNamedParameter('user1'))
- ->setValue('item_type', $qb->createNamedParameter('file'))
- ->setValue('item_source', $qb->createNamedParameter(42))
- ->setValue('file_source', $qb->createNamedParameter(42))
- ->setValue('parent', $qb->createNamedParameter($id1));
- $qb->executeStatement();
- $id2 = $qb->getLastInsertId();
-
- $this->provider->userDeletedFromGroup($user, $group);
-
- $qb = $this->dbConn->getQueryBuilder();
- $qb->select('*')
- ->from('share')
- ->where($qb->expr()->eq('id', $qb->createNamedParameter($id2)));
- $cursor = $qb->executeQuery();
- $data = $cursor->fetchAllAssociative();
- $cursor->closeCursor();
-
- $this->assertCount($toDelete ? 0 : 1, $data);
- }
-
- public function testGetSharesInFolder(): void {
- $userManager = Server::get(IUserManager::class);
- $groupManager = Server::get(IGroupManager::class);
- $rootFolder = Server::get(IRootFolder::class);
-
- $provider = new DefaultShareProvider(
- $this->dbConn,
- $userManager,
- $groupManager,
- $rootFolder,
- $this->mailer,
- $this->defaults,
- $this->l10nFactory,
- $this->urlGenerator,
- $this->timeFactory,
- $this->logger,
- $this->shareManager,
- $this->config,
- );
-
- $password = md5(time());
-
- $u1 = $userManager->createUser('testShare1', $password);
- $u2 = $userManager->createUser('testShare2', $password);
- $u3 = $userManager->createUser('testShare3', $password);
-
- $g1 = $groupManager->createGroup('group1');
-
- $u1Folder = $rootFolder->getUserFolder($u1->getUID());
- $folder1 = $u1Folder->newFolder('foo');
- $file1 = $folder1->newFile('bar');
- $folder2 = $folder1->newFolder('baz');
-
- $shareManager = Server::get(IShareManager::class);
- $share1 = $shareManager->newShare();
- $share1->setNode($folder1)
- ->setSharedBy($u1->getUID())
- ->setSharedWith($u2->getUID())
- ->setShareOwner($u1->getUID())
- ->setShareType(IShare::TYPE_USER)
- ->setPermissions(Constants::PERMISSION_ALL);
- $share1 = $this->provider->create($share1);
-
- $share2 = $shareManager->newShare();
- $share2->setNode($file1)
- ->setSharedBy($u2->getUID())
- ->setSharedWith($u3->getUID())
- ->setShareOwner($u1->getUID())
- ->setShareType(IShare::TYPE_USER)
- ->setPermissions(Constants::PERMISSION_READ);
- $share2 = $this->provider->create($share2);
-
- $share3 = $shareManager->newShare();
- $share3->setNode($folder2)
- ->setSharedBy($u2->getUID())
- ->setShareOwner($u1->getUID())
- ->setShareType(IShare::TYPE_LINK)
- ->setPermissions(Constants::PERMISSION_READ);
- $share3 = $this->provider->create($share3);
-
- $share4 = $shareManager->newShare();
- $share4->setNode($folder2)
- ->setSharedBy($u1->getUID())
- ->setSharedWith($g1->getGID())
- ->setShareOwner($u1->getUID())
- ->setShareType(IShare::TYPE_GROUP)
- ->setPermissions(Constants::PERMISSION_READ);
- $share4 = $this->provider->create($share4);
-
- $result = $provider->getSharesInFolder($u1->getUID(), $folder1, false);
- $this->assertCount(1, $result);
- $shares = array_pop($result);
- $this->assertCount(1, $shares);
- $this->assertSame($folder2->getId(), $shares[0]->getNodeId());
-
- $result = $provider->getSharesInFolder($u1->getUID(), $folder1, true);
- $this->assertCount(2, $result);
-
- $file_shares = $result[$file1->getId()];
- $this->assertCount(1, $file_shares);
- $this->assertSame($file1->getId(), $file_shares[0]->getNodeId());
- $this->assertSame(IShare::TYPE_USER, $file_shares[0]->getShareType());
-
- $folder_shares = $result[$folder2->getId()];
- usort($folder_shares, fn (IShare $a, IShare $b) => $a->getId() <=> $b->getId());
- $this->assertCount(2, $folder_shares);
- $this->assertSame($folder2->getId(), $folder_shares[0]->getNodeId());
- $this->assertSame($folder2->getId(), $folder_shares[1]->getNodeId());
- $this->assertSame(IShare::TYPE_LINK, $folder_shares[0]->getShareType());
- $this->assertSame(IShare::TYPE_GROUP, $folder_shares[1]->getShareType());
-
- $provider->delete($share1);
- $provider->delete($share2);
- $provider->delete($share3);
- $provider->delete($share4);
-
- $u1->delete();
- $u2->delete();
- $u3->delete();
- $g1->delete();
- }
-
- public function testGetAccessListNoCurrentAccessRequired(): void {
- $userManager = Server::get(IUserManager::class);
- $groupManager = Server::get(IGroupManager::class);
- $rootFolder = Server::get(IRootFolder::class);
-
- $provider = new DefaultShareProvider(
- $this->dbConn,
- $userManager,
- $groupManager,
- $rootFolder,
- $this->mailer,
- $this->defaults,
- $this->l10nFactory,
- $this->urlGenerator,
- $this->timeFactory,
- $this->logger,
- $this->shareManager,
- $this->config,
- );
-
- $u1 = $userManager->createUser('testShare1', 'test');
- $u2 = $userManager->createUser('testShare2', 'test');
- $u3 = $userManager->createUser('testShare3', 'test');
- $u4 = $userManager->createUser('testShare4', 'test');
- $u5 = $userManager->createUser('testShare5', 'test');
-
- $g1 = $groupManager->createGroup('group1');
- $g1->addUser($u3);
- $g1->addUser($u4);
-
- $u1Folder = $rootFolder->getUserFolder($u1->getUID());
- $folder1 = $u1Folder->newFolder('foo');
- $folder2 = $folder1->newFolder('baz');
- $file1 = $folder2->newFile('bar');
-
- $result = $provider->getAccessList([$folder1, $folder2, $file1], false);
- $this->assertCount(0, $result['users']);
- $this->assertFalse($result['public']);
-
- $shareManager = Server::get(IShareManager::class);
- $share1 = $shareManager->newShare();
- $share1->setNode($folder1)
- ->setSharedBy($u1->getUID())
- ->setSharedWith($u2->getUID())
- ->setShareOwner($u1->getUID())
- ->setShareType(IShare::TYPE_USER)
- ->setPermissions(Constants::PERMISSION_ALL);
- $share1 = $this->provider->create($share1);
- $share1 = $provider->acceptShare($share1, $u2->getUid());
-
- $share2 = $shareManager->newShare();
- $share2->setNode($folder2)
- ->setSharedBy($u2->getUID())
- ->setSharedWith($g1->getGID())
- ->setShareOwner($u1->getUID())
- ->setShareType(IShare::TYPE_GROUP)
- ->setPermissions(Constants::PERMISSION_ALL);
- $share2 = $this->provider->create($share2);
-
- $shareManager->deleteFromSelf($share2, $u4->getUID());
-
- $share2 = $provider->acceptShare($share2, $u3->getUid());
- $share2 = $provider->acceptShare($share2, $u4->getUid());
-
- $share3 = $shareManager->newShare();
- $share3->setNode($file1)
- ->setSharedBy($u3->getUID())
- ->setShareOwner($u1->getUID())
- ->setShareType(IShare::TYPE_LINK)
- ->setPermissions(Constants::PERMISSION_READ);
- $share3 = $this->provider->create($share3);
-
- $share4 = $shareManager->newShare();
- $share4->setNode($file1)
- ->setSharedBy($u3->getUID())
- ->setSharedWith($u5->getUID())
- ->setShareOwner($u1->getUID())
- ->setShareType(IShare::TYPE_USER)
- ->setPermissions(Constants::PERMISSION_READ);
- $share4 = $this->provider->create($share4);
- $share4 = $provider->acceptShare($share4, $u5->getUid());
-
- $result = $provider->getAccessList([$folder1, $folder2, $file1], false);
-
- $this->assertCount(4, $result['users']);
- $this->assertContains('testShare2', $result['users']);
- $this->assertContains('testShare3', $result['users']);
- $this->assertContains('testShare4', $result['users']);
- $this->assertContains('testShare5', $result['users']);
- $this->assertTrue($result['public']);
-
- $provider->delete($share1);
- $provider->delete($share2);
- $provider->delete($share3);
- $provider->delete($share4);
-
- $u1->delete();
- $u2->delete();
- $u3->delete();
- $u4->delete();
- $u5->delete();
- $g1->delete();
- }
-
- public function testGetAccessListCurrentAccessRequired(): void {
- $userManager = Server::get(IUserManager::class);
- $groupManager = Server::get(IGroupManager::class);
- $rootFolder = Server::get(IRootFolder::class);
-
- $provider = new DefaultShareProvider(
- $this->dbConn,
- $userManager,
- $groupManager,
- $rootFolder,
- $this->mailer,
- $this->defaults,
- $this->l10nFactory,
- $this->urlGenerator,
- $this->timeFactory,
- $this->logger,
- $this->shareManager,
- $this->config,
- );
-
- $u1 = $userManager->createUser('testShare1', 'test');
- $u2 = $userManager->createUser('testShare2', 'test');
- $u3 = $userManager->createUser('testShare3', 'test');
- $u4 = $userManager->createUser('testShare4', 'test');
- $u5 = $userManager->createUser('testShare5', 'test');
-
- $g1 = $groupManager->createGroup('group1');
- $g1->addUser($u3);
- $g1->addUser($u4);
-
- $u1Folder = $rootFolder->getUserFolder($u1->getUID());
- $folder1 = $u1Folder->newFolder('foo');
- $folder2 = $folder1->newFolder('baz');
- $file1 = $folder2->newFile('bar');
-
- $result = $provider->getAccessList([$folder1, $folder2, $file1], false);
- $this->assertCount(0, $result['users']);
- $this->assertFalse($result['public']);
-
- $shareManager = Server::get(IShareManager::class);
- $share1 = $shareManager->newShare();
- $share1->setNode($folder1)
- ->setSharedBy($u1->getUID())
- ->setSharedWith($u2->getUID())
- ->setShareOwner($u1->getUID())
- ->setShareType(IShare::TYPE_USER)
- ->setPermissions(Constants::PERMISSION_ALL);
- $share1 = $this->provider->create($share1);
- $share1 = $provider->acceptShare($share1, $u2->getUid());
-
- $share2 = $shareManager->newShare();
- $share2->setNode($folder2)
- ->setSharedBy($u2->getUID())
- ->setSharedWith($g1->getGID())
- ->setShareOwner($u1->getUID())
- ->setShareType(IShare::TYPE_GROUP)
- ->setPermissions(Constants::PERMISSION_ALL);
- $share2 = $this->provider->create($share2);
- $share2 = $provider->acceptShare($share2, $u3->getUid());
- $share2 = $provider->acceptShare($share2, $u4->getUid());
-
- $shareManager->deleteFromSelf($share2, $u4->getUID());
-
- $share3 = $shareManager->newShare();
- $share3->setNode($file1)
- ->setSharedBy($u3->getUID())
- ->setShareOwner($u1->getUID())
- ->setShareType(IShare::TYPE_LINK)
- ->setPermissions(Constants::PERMISSION_READ);
- $share3 = $this->provider->create($share3);
-
- $share4 = $shareManager->newShare();
- $share4->setNode($file1)
- ->setSharedBy($u3->getUID())
- ->setSharedWith($u5->getUID())
- ->setShareOwner($u1->getUID())
- ->setShareType(IShare::TYPE_USER)
- ->setPermissions(Constants::PERMISSION_READ);
- $share4 = $this->provider->create($share4);
- $share4 = $provider->acceptShare($share4, $u5->getUid());
-
- $result = $provider->getAccessList([$folder1, $folder2, $file1], true);
-
- $this->assertCount(3, $result['users']);
- $this->assertArrayHasKey('testShare2', $result['users']);
- $this->assertArrayHasKey('testShare3', $result['users']);
- $this->assertArrayHasKey('testShare5', $result['users']);
- $this->assertTrue($result['public']);
-
- $provider->delete($share1);
- $provider->delete($share2);
- $provider->delete($share3);
- $provider->delete($share4);
-
- $u1->delete();
- $u2->delete();
- $u3->delete();
- $u4->delete();
- $u5->delete();
- $g1->delete();
- }
-
- public function testGetAllShares(): void {
- $qb = $this->dbConn->getQueryBuilder();
-
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_USER),
- 'share_with' => $qb->expr()->literal('sharedWith1'),
- 'uid_owner' => $qb->expr()->literal('shareOwner1'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy1'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(42),
- 'file_target' => $qb->expr()->literal('myTarget1'),
- 'permissions' => $qb->expr()->literal(13),
- ]);
- $qb->executeStatement();
-
- $id1 = $qb->getLastInsertId();
-
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_GROUP),
- 'share_with' => $qb->expr()->literal('sharedWith2'),
- 'uid_owner' => $qb->expr()->literal('shareOwner2'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy2'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(43),
- 'file_target' => $qb->expr()->literal('myTarget2'),
- 'permissions' => $qb->expr()->literal(14),
- ]);
- $qb->executeStatement();
-
- $id2 = $qb->getLastInsertId();
-
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_LINK),
- 'token' => $qb->expr()->literal('token3'),
- 'uid_owner' => $qb->expr()->literal('shareOwner3'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy3'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(44),
- 'file_target' => $qb->expr()->literal('myTarget3'),
- 'permissions' => $qb->expr()->literal(15),
- ]);
- $qb->executeStatement();
-
- $id3 = $qb->getLastInsertId();
-
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_EMAIL),
- 'share_with' => $qb->expr()->literal('shareOwner4'),
- 'token' => $qb->expr()->literal('token4'),
- 'uid_owner' => $qb->expr()->literal('shareOwner4'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy4'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(45),
- 'file_target' => $qb->expr()->literal('myTarget4'),
- 'permissions' => $qb->expr()->literal(16),
- ]);
- $qb->executeStatement();
-
- $id4 = $qb->getLastInsertId();
-
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_LINK),
- 'token' => $qb->expr()->literal('token5'),
- 'uid_owner' => $qb->expr()->literal('shareOwner5'),
- 'uid_initiator' => $qb->expr()->literal('sharedBy5'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(46),
- 'file_target' => $qb->expr()->literal('myTarget5'),
- 'permissions' => $qb->expr()->literal(17),
- ]);
- $qb->executeStatement();
-
- $id5 = $qb->getLastInsertId();
-
- $ownerPath1 = $this->createMock(File::class);
- $shareOwner1Folder = $this->createMock(Folder::class);
- $shareOwner1Folder->method('getFirstNodeById')->willReturn($ownerPath1);
-
- $ownerPath2 = $this->createMock(File::class);
- $shareOwner2Folder = $this->createMock(Folder::class);
- $shareOwner2Folder->method('getFirstNodeById')->willReturn($ownerPath2);
-
- $ownerPath3 = $this->createMock(File::class);
- $shareOwner3Folder = $this->createMock(Folder::class);
- $shareOwner3Folder->method('getFirstNodeById')->willReturn($ownerPath3);
-
- $ownerPath4 = $this->createMock(File::class);
- $shareOwner4Folder = $this->createMock(Folder::class);
- $shareOwner4Folder->method('getFirstNodeById')->willReturn($ownerPath4);
-
- $ownerPath5 = $this->createMock(File::class);
- $shareOwner5Folder = $this->createMock(Folder::class);
- $shareOwner5Folder->method('getFirstNodeById')->willReturn($ownerPath5);
-
- $this->rootFolder
- ->method('getUserFolder')
- ->willReturnMap(
- [
- ['shareOwner1', $shareOwner1Folder],
- ['shareOwner2', $shareOwner2Folder],
- ['shareOwner3', $shareOwner3Folder],
- ['shareOwner4', $shareOwner4Folder],
- ['shareOwner5', $shareOwner5Folder],
- ]
- );
-
- $shares = iterator_to_array($this->provider->getAllShares());
- $this->assertEquals(4, count($shares));
-
- $share = $shares[0];
-
- // We fetch the node so the root folder is eventually called
-
- $this->assertEquals($id1, $share->getId());
- $this->assertEquals(IShare::TYPE_USER, $share->getShareType());
- $this->assertEquals('sharedWith1', $share->getSharedWith());
- $this->assertEquals('sharedBy1', $share->getSharedBy());
- $this->assertEquals('shareOwner1', $share->getShareOwner());
- $this->assertEquals($ownerPath1, $share->getNode());
- $this->assertEquals(13, $share->getPermissions());
- $this->assertEquals(null, $share->getToken());
- $this->assertEquals('myTarget1', $share->getTarget());
-
- $share = $shares[1];
-
- $this->assertEquals($id2, $share->getId());
- $this->assertEquals(IShare::TYPE_GROUP, $share->getShareType());
- $this->assertEquals('sharedWith2', $share->getSharedWith());
- $this->assertEquals('sharedBy2', $share->getSharedBy());
- $this->assertEquals('shareOwner2', $share->getShareOwner());
- $this->assertEquals($ownerPath2, $share->getNode());
- $this->assertEquals(14, $share->getPermissions());
- $this->assertEquals(null, $share->getToken());
- $this->assertEquals('myTarget2', $share->getTarget());
-
- $share = $shares[2];
-
- $this->assertEquals($id3, $share->getId());
- $this->assertEquals(IShare::TYPE_LINK, $share->getShareType());
- $this->assertEquals(null, $share->getSharedWith());
- $this->assertEquals('sharedBy3', $share->getSharedBy());
- $this->assertEquals('shareOwner3', $share->getShareOwner());
- $this->assertEquals($ownerPath3, $share->getNode());
- $this->assertEquals(15, $share->getPermissions());
- $this->assertEquals('token3', $share->getToken());
- $this->assertEquals('myTarget3', $share->getTarget());
-
- $share = $shares[3];
-
- $this->assertEquals($id5, $share->getId());
- $this->assertEquals(IShare::TYPE_LINK, $share->getShareType());
- $this->assertEquals(null, $share->getSharedWith());
- $this->assertEquals('sharedBy5', $share->getSharedBy());
- $this->assertEquals('shareOwner5', $share->getShareOwner());
- $this->assertEquals($ownerPath5, $share->getNode());
- $this->assertEquals(17, $share->getPermissions());
- $this->assertEquals('token5', $share->getToken());
- $this->assertEquals('myTarget5', $share->getTarget());
- }
-
- public function testGetSharesByPath(): void {
- $qb = $this->dbConn->getQueryBuilder();
-
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_USER),
- 'uid_owner' => $qb->expr()->literal('user1'),
- 'uid_initiator' => $qb->expr()->literal('user1'),
- 'share_with' => $qb->expr()->literal('user2'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(1),
- ]);
- $qb->executeStatement();
-
- $id1 = $qb->getLastInsertId();
-
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_GROUP),
- 'uid_owner' => $qb->expr()->literal('user1'),
- 'uid_initiator' => $qb->expr()->literal('user1'),
- 'share_with' => $qb->expr()->literal('user2'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(1),
- ]);
- $qb->executeStatement();
-
- $id2 = $qb->getLastInsertId();
-
- $qb->insert('share')
- ->values([
- 'share_type' => $qb->expr()->literal(IShare::TYPE_LINK),
- 'uid_owner' => $qb->expr()->literal('user1'),
- 'uid_initiator' => $qb->expr()->literal('user1'),
- 'share_with' => $qb->expr()->literal('user2'),
- 'item_type' => $qb->expr()->literal('file'),
- 'file_source' => $qb->expr()->literal(1),
- ]);
- $qb->executeStatement();
-
- $id3 = $qb->getLastInsertId();
-
- $ownerPath1 = $this->createMock(File::class);
- $shareOwner1Folder = $this->createMock(Folder::class);
- $shareOwner1Folder->method('getFirstNodeById')->willReturn($ownerPath1);
-
- $ownerPath2 = $this->createMock(File::class);
- $shareOwner2Folder = $this->createMock(Folder::class);
- $shareOwner2Folder->method('getFirstNodeById')->willReturn($ownerPath2);
-
- $ownerPath3 = $this->createMock(File::class);
- $shareOwner3Folder = $this->createMock(Folder::class);
- $shareOwner3Folder->method('getFirstNodeById')->willReturn($ownerPath3);
-
- $this->rootFolder
- ->method('getUserFolder')
- ->willReturnMap(
- [
- ['shareOwner1', $shareOwner1Folder],
- ['shareOwner2', $shareOwner2Folder],
- ['shareOwner3', $shareOwner3Folder],
- ]
- );
-
- $node = $this->createMock(Node::class);
- $node
- ->expects($this->once())
- ->method('getId')
- ->willReturn(1);
-
- $shares = $this->provider->getSharesByPath($node);
- usort($shares, fn (IShare $a, IShare $b) => $a->getId() <=> $b->getId());
- $this->assertCount(3, $shares);
-
- $this->assertEquals($id1, $shares[0]->getId());
- $this->assertEquals(IShare::TYPE_USER, $shares[0]->getShareType());
-
- $this->assertEquals($id2, $shares[1]->getId());
- $this->assertEquals(IShare::TYPE_GROUP, $shares[1]->getShareType());
-
- $this->assertEquals($id3, $shares[2]->getId());
- $this->assertEquals(IShare::TYPE_LINK, $shares[2]->getShareType());
- }
-}
--- a/tests/lib/Share20/LegacyHooksTest.php
+++ /dev/null
@@ -1,357 +0,0 @@
-createMock(LoggerInterface::class);
- $this->eventDispatcher = new EventDispatcher($symfonyDispatcher, Server::get(ContainerInterface::class), $logger);
- $this->hooks = new LegacyHooks($this->eventDispatcher);
- $this->manager = Server::get(IShareManager::class);
- }
-
- public function testPreUnshare(): void {
- $path = $this->createMock(File::class);
- $path->method('getId')->willReturn(1);
-
- $info = $this->createMock(ICacheEntry::class);
- $info->method('getMimeType')->willReturn('text/plain');
-
- $share = $this->manager->newShare();
- $share->setId(42)
- ->setProviderId('prov')
- ->setShareType(IShare::TYPE_USER)
- ->setSharedWith('awesomeUser')
- ->setSharedBy('sharedBy')
- ->setNode($path)
- ->setTarget('myTarget')
- ->setNodeCacheEntry($info);
-
- $hookListner = $this->getMockBuilder(Dummy::class)->onlyMethods(['pre'])->getMock();
- Util::connectHook('OCP\Share', 'pre_unshare', $hookListner, 'pre');
-
- $hookListnerExpectsPre = [
- 'id' => 42,
- 'itemType' => 'file',
- 'itemSource' => 1,
- 'shareType' => IShare::TYPE_USER,
- 'shareWith' => 'awesomeUser',
- 'itemparent' => null,
- 'uidOwner' => 'sharedBy',
- 'fileSource' => 1,
- 'fileTarget' => 'myTarget',
- ];
-
- $hookListner
- ->expects($this->exactly(1))
- ->method('pre')
- ->with($hookListnerExpectsPre);
-
- $event = new BeforeShareDeletedEvent($share);
- $this->eventDispatcher->dispatchTyped($event);
- }
-
- public function testPostUnshare(): void {
- $path = $this->createMock(File::class);
- $path->method('getId')->willReturn(1);
-
- $info = $this->createMock(ICacheEntry::class);
- $info->method('getMimeType')->willReturn('text/plain');
-
- $share = $this->manager->newShare();
- $share->setId(42)
- ->setProviderId('prov')
- ->setShareType(IShare::TYPE_USER)
- ->setSharedWith('awesomeUser')
- ->setSharedBy('sharedBy')
- ->setNode($path)
- ->setTarget('myTarget')
- ->setNodeCacheEntry($info);
-
- $hookListner = $this->getMockBuilder(Dummy::class)->onlyMethods(['post'])->getMock();
- Util::connectHook('OCP\Share', 'post_unshare', $hookListner, 'post');
-
- $hookListnerExpectsPost = [
- 'id' => 42,
- 'itemType' => 'file',
- 'itemSource' => 1,
- 'shareType' => IShare::TYPE_USER,
- 'shareWith' => 'awesomeUser',
- 'itemparent' => null,
- 'uidOwner' => 'sharedBy',
- 'fileSource' => 1,
- 'fileTarget' => 'myTarget',
- 'deletedShares' => [
- [
- 'id' => 42,
- 'itemType' => 'file',
- 'itemSource' => 1,
- 'shareType' => IShare::TYPE_USER,
- 'shareWith' => 'awesomeUser',
- 'itemparent' => null,
- 'uidOwner' => 'sharedBy',
- 'fileSource' => 1,
- 'fileTarget' => 'myTarget',
- ],
- ],
- ];
-
- $hookListner
- ->expects($this->exactly(1))
- ->method('post')
- ->with($hookListnerExpectsPost);
-
- $event = new ShareDeletedEvent($share);
- $this->eventDispatcher->dispatchTyped($event);
- }
-
- public function testPostUnshareFromSelf(): void {
- $path = $this->createMock(File::class);
- $path->method('getId')->willReturn(1);
-
- $info = $this->createMock(ICacheEntry::class);
- $info->method('getMimeType')->willReturn('text/plain');
-
- $share = $this->manager->newShare();
- $share->setId(42)
- ->setProviderId('prov')
- ->setShareType(IShare::TYPE_USER)
- ->setSharedWith('awesomeUser')
- ->setSharedBy('sharedBy')
- ->setNode($path)
- ->setTarget('myTarget')
- ->setNodeCacheEntry($info);
-
- $hookListner = $this->getMockBuilder(Dummy::class)->onlyMethods(['postFromSelf'])->getMock();
- Util::connectHook('OCP\Share', 'post_unshareFromSelf', $hookListner, 'postFromSelf');
-
- $hookListnerExpectsPostFromSelf = [
- 'id' => 42,
- 'itemType' => 'file',
- 'itemSource' => 1,
- 'shareType' => IShare::TYPE_USER,
- 'shareWith' => 'awesomeUser',
- 'itemparent' => null,
- 'uidOwner' => 'sharedBy',
- 'fileSource' => 1,
- 'fileTarget' => 'myTarget',
- 'itemTarget' => 'myTarget',
- 'unsharedItems' => [
- [
- 'id' => 42,
- 'itemType' => 'file',
- 'itemSource' => 1,
- 'shareType' => IShare::TYPE_USER,
- 'shareWith' => 'awesomeUser',
- 'itemparent' => null,
- 'uidOwner' => 'sharedBy',
- 'fileSource' => 1,
- 'fileTarget' => 'myTarget',
- 'itemTarget' => 'myTarget',
- ],
- ],
- ];
-
- $hookListner
- ->expects($this->exactly(1))
- ->method('postFromSelf')
- ->with($hookListnerExpectsPostFromSelf);
-
- $event = new ShareDeletedFromSelfEvent($share);
- $this->eventDispatcher->dispatchTyped($event);
- }
-
- public function testPreShare(): void {
- $path = $this->createMock(File::class);
- $path->method('getId')->willReturn(1);
-
- $date = new \DateTime();
-
- $share = $this->manager->newShare();
- $share->setShareType(IShare::TYPE_LINK)
- ->setSharedWith('awesomeUser')
- ->setSharedBy('sharedBy')
- ->setNode($path)
- ->setTarget('myTarget')
- ->setPermissions(Constants::PERMISSION_ALL)
- ->setExpirationDate($date)
- ->setPassword('password')
- ->setToken('token');
-
- $hookListner = $this->getMockBuilder(Dummy::class)->onlyMethods(['preShare'])->getMock();
- Util::connectHook('OCP\Share', 'pre_shared', $hookListner, 'preShare');
-
- $run = true;
- $error = '';
-
- $expected = [
- 'itemType' => 'file',
- 'itemSource' => 1,
- 'shareType' => IShare::TYPE_LINK,
- 'shareWith' => 'awesomeUser',
- 'uidOwner' => 'sharedBy',
- 'fileSource' => 1,
- 'itemTarget' => 'myTarget',
- 'permissions' => Constants::PERMISSION_ALL,
- 'expiration' => $date,
- 'token' => 'token',
- 'run' => &$run,
- 'error' => &$error,
- ];
-
- $hookListner
- ->expects($this->exactly(1))
- ->method('preShare')
- ->with($expected);
-
- $event = new BeforeShareCreatedEvent($share);
- $this->eventDispatcher->dispatchTyped($event);
- }
-
- public function testPreShareError(): void {
- $path = $this->createMock(File::class);
- $path->method('getId')->willReturn(1);
-
- $date = new \DateTime();
-
- $share = $this->manager->newShare();
- $share->setShareType(IShare::TYPE_LINK)
- ->setSharedWith('awesomeUser')
- ->setSharedBy('sharedBy')
- ->setNode($path)
- ->setTarget('myTarget')
- ->setPermissions(Constants::PERMISSION_ALL)
- ->setExpirationDate($date)
- ->setPassword('password')
- ->setToken('token');
-
- $hookListner = $this->getMockBuilder(Dummy::class)->onlyMethods(['preShare'])->getMock();
- Util::connectHook('OCP\Share', 'pre_shared', $hookListner, 'preShare');
-
- $run = true;
- $error = '';
-
- $expected = [
- 'itemType' => 'file',
- 'itemSource' => 1,
- 'shareType' => IShare::TYPE_LINK,
- 'shareWith' => 'awesomeUser',
- 'uidOwner' => 'sharedBy',
- 'fileSource' => 1,
- 'itemTarget' => 'myTarget',
- 'permissions' => Constants::PERMISSION_ALL,
- 'expiration' => $date,
- 'token' => 'token',
- 'run' => &$run,
- 'error' => &$error,
- ];
-
- $hookListner
- ->expects($this->exactly(1))
- ->method('preShare')
- ->with($expected)
- ->willReturnCallback(function ($data): void {
- $data['run'] = false;
- $data['error'] = 'I error';
- });
-
- $event = new BeforeShareCreatedEvent($share);
- $this->eventDispatcher->dispatchTyped($event);
-
- $this->assertTrue($event->isPropagationStopped());
- $this->assertSame('I error', $event->getError());
- }
-
- public function testPostShare(): void {
- $path = $this->createMock(File::class);
- $path->method('getId')->willReturn(1);
-
- $date = new \DateTime();
-
- $share = $this->manager->newShare();
- $share->setId(42)
- ->setShareType(IShare::TYPE_LINK)
- ->setSharedWith('awesomeUser')
- ->setSharedBy('sharedBy')
- ->setNode($path)
- ->setTarget('myTarget')
- ->setPermissions(Constants::PERMISSION_ALL)
- ->setExpirationDate($date)
- ->setPassword('password')
- ->setToken('token');
-
- $hookListner = $this->getMockBuilder(Dummy::class)->onlyMethods(['postShare'])->getMock();
- Util::connectHook('OCP\Share', 'post_shared', $hookListner, 'postShare');
-
- $expected = [
- 'id' => 42,
- 'itemType' => 'file',
- 'itemSource' => 1,
- 'shareType' => IShare::TYPE_LINK,
- 'shareWith' => 'awesomeUser',
- 'uidOwner' => 'sharedBy',
- 'fileSource' => 1,
- 'itemTarget' => 'myTarget',
- 'fileTarget' => 'myTarget',
- 'permissions' => Constants::PERMISSION_ALL,
- 'expiration' => $date,
- 'token' => 'token',
- 'path' => null,
- ];
-
- $hookListner
- ->expects($this->exactly(1))
- ->method('postShare')
- ->with($expected);
-
- $event = new ShareCreatedEvent($share);
- $this->eventDispatcher->dispatchTyped($event);
- }
-}
--- a/tests/lib/Share20/ManagerTest.php
+++ /dev/null
@@ -1,5946 +0,0 @@
-logger = $this->createMock(LoggerInterface::class);
- $this->config = $this->createMock(IConfig::class);
- $this->secureRandom = $this->createMock(ISecureRandom::class);
- $this->mountManager = $this->createMock(IMountManager::class);
- $this->groupManager = $this->createMock(IGroupManager::class);
- $this->userManager = $this->createMock(IUserManager::class);
- $this->rootFolder = $this->createMock(IRootFolder::class);
- $this->dispatcher = $this->createMock(IEventDispatcher::class);
- $this->userSession = $this->createMock(IUserSession::class);
- $this->knownUserService = $this->createMock(KnownUserService::class);
- $this->connection = $this->createMock(IDBConnection::class);
-
- $this->shareDisabledChecker = new ShareDisableChecker($this->config, $this->userManager, $this->groupManager);
- $this->dateTimeZone = $this->createMock(IDateTimeZone::class);
- $this->timezone = new \DateTimeZone('Pacific/Auckland');
- $this->dateTimeZone->method('getTimeZone')->willReturnCallback(fn () => $this->timezone);
-
- $this->appConfig = $this->createMock(IAppConfig::class);
-
- $this->l10nFactory = $this->createMock(IFactory::class);
- $this->l = $this->createMock(IL10N::class);
- $this->l->method('t')
- ->willReturnCallback(function ($text, $parameters = []) {
- return vsprintf($text, $parameters);
- });
- $this->l->method('n')
- ->willReturnCallback(function ($singular, $plural, $count, $parameters = []) {
- return vsprintf(str_replace('%n', $count, ($count === 1) ? $singular : $plural), $parameters);
- });
- $this->l10nFactory->method('get')->willReturn($this->l);
-
- $this->factory = new DummyFactory(\OC::$server);
-
- $this->manager = $this->createManager($this->factory);
-
- $this->defaultProvider = $this->createMock(DefaultShareProvider::class);
- $this->defaultProvider->method('identifier')->willReturn('default');
- $this->factory->setProvider($this->defaultProvider);
-
- $this->overwriteService(IRootFolder::class, $this->rootFolder);
- $this->overwriteService(IUserManager::class, $this->userManager);
- $this->overwriteService(IGroupManager::class, $this->groupManager);
- self::invokePrivate(Server::get(\OC\Core\Listener\RestrictInteractionListener::class), 'manager', [$this->manager]);
- self::invokePrivate(Server::get(\OC\Core\Listener\RestrictInteractionListener::class), 'groupManager', [$this->groupManager]);
- self::invokePrivate(Server::get(RestrictInteractionListener::class), 'manager', [$this->manager]);
- self::invokePrivate(Server::get(RestrictInteractionListener::class), 'rootFolder', [$this->rootFolder]);
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->restoreService(IRootFolder::class);
- $this->restoreService(IUserManager::class);
- $this->restoreService(IGroupManager::class);
- self::invokePrivate(Server::get(\OC\Core\Listener\RestrictInteractionListener::class), 'manager', [Server::get(IManager::class)]);
- self::invokePrivate(Server::get(\OC\Core\Listener\RestrictInteractionListener::class), 'groupManager', [Server::get(IGroupManager::class)]);
- self::invokePrivate(Server::get(RestrictInteractionListener::class), 'manager', [Server::get(IManager::class)]);
- self::invokePrivate(Server::get(RestrictInteractionListener::class), 'rootFolder', [Server::get(IRootFolder::class)]);
-
- parent::tearDown();
- }
-
- private function createManager(IProviderFactory $factory): Manager {
- return new Manager(
- $this->logger,
- $this->config,
- $this->secureRandom,
- Server::get(IHasher::class),
- $this->mountManager,
- $this->groupManager,
- $this->l10nFactory,
- $factory,
- $this->userManager,
- $this->rootFolder,
- $this->dispatcher,
- $this->userSession,
- $this->knownUserService,
- $this->shareDisabledChecker,
- $this->dateTimeZone,
- $this->appConfig,
- $this->connection,
- );
- }
-
- /**
- * @return MockBuilder
- */
- private function createManagerMock(): MockBuilder {
- return $this->getMockBuilder(Manager::class)
- ->setConstructorArgs([
- $this->logger,
- $this->config,
- $this->secureRandom,
- Server::get(IHasher::class),
- $this->mountManager,
- $this->groupManager,
- $this->l10nFactory,
- $this->factory,
- $this->userManager,
- $this->rootFolder,
- $this->dispatcher,
- $this->userSession,
- $this->knownUserService,
- $this->shareDisabledChecker,
- $this->dateTimeZone,
- $this->appConfig,
- $this->connection,
- ]);
- }
-
- private function createFolderMock(string $folderPath): MockObject&Folder {
- $folder = $this->createMock(Folder::class);
- $folder->method('getPath')->willReturn($folderPath);
- $folder->method('getRelativePath')->willReturnCallback(
- fn (string $path): ?string => PathHelper::getRelativePath($folderPath, $path)
- );
- return $folder;
- }
-
- public function testDeleteNoShareId(): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $share = $this->manager->newShare();
-
- $this->manager->deleteShare($share);
- }
-
- public static function dataTestDelete(): array {
- return [
- [IShare::TYPE_USER, 'sharedWithUser'],
- [IShare::TYPE_GROUP, 'sharedWithGroup'],
- [IShare::TYPE_LINK, ''],
- [IShare::TYPE_REMOTE, 'foo@bar.com'],
- ];
- }
-
- #[DataProvider('dataTestDelete')]
- public function testDelete($shareType, $sharedWith): void {
- $manager = $this->createManagerMock()
- ->onlyMethods(['getShareById', 'deleteChildren', 'promoteReshares'])
- ->getMock();
-
- $manager->method('deleteChildren')->willReturn([]);
-
- $path = $this->createMock(File::class);
- $path->method('getId')->willReturn(1);
-
- $share = $this->manager->newShare();
- $share->setId(42)
- ->setProviderId('prov')
- ->setShareType($shareType)
- ->setSharedWith($sharedWith)
- ->setSharedBy('sharedBy')
- ->setNode($path)
- ->setTarget('myTarget');
-
- $manager->expects($this->once())->method('deleteChildren')->with($share);
- $manager->expects($this->once())->method('promoteReshares')->with($share);
-
- $this->defaultProvider
- ->expects($this->once())
- ->method('delete')
- ->with($share);
-
- $calls = [
- BeforeShareDeletedEvent::class,
- ShareDeletedEvent::class,
- ];
- $this->dispatcher->expects($this->exactly(2))
- ->method('dispatchTyped')
- ->willReturnCallback(function ($event) use (&$calls, $share): void {
- $expected = array_shift($calls);
- $this->assertInstanceOf($expected, $event);
- $this->assertEquals($share, $event->getShare());
- });
-
- $manager->deleteShare($share);
- }
-
- public function testDeleteLazyShare(): void {
- $manager = $this->createManagerMock()
- ->onlyMethods(['getShareById', 'deleteChildren', 'promoteReshares'])
- ->getMock();
-
- $manager->method('deleteChildren')->willReturn([]);
-
- $share = $this->manager->newShare();
- $share->setId(42)
- ->setProviderId('prov')
- ->setShareType(IShare::TYPE_USER)
- ->setSharedWith('sharedWith')
- ->setSharedBy('sharedBy')
- ->setShareOwner('shareOwner')
- ->setTarget('myTarget')
- ->setNodeId(1)
- ->setNodeType('file');
-
- $this->rootFolder->expects($this->never())->method($this->anything());
-
- $manager->expects($this->once())->method('deleteChildren')->with($share);
- $manager->expects($this->once())->method('promoteReshares')->with($share);
-
- $this->defaultProvider
- ->expects($this->once())
- ->method('delete')
- ->with($share);
-
- $calls = [
- BeforeShareDeletedEvent::class,
- ShareDeletedEvent::class,
- ];
- $this->dispatcher->expects($this->exactly(2))
- ->method('dispatchTyped')
- ->willReturnCallback(function ($event) use (&$calls, $share): void {
- $expected = array_shift($calls);
- $this->assertInstanceOf($expected, $event);
- $this->assertEquals($share, $event->getShare());
- });
-
- $manager->deleteShare($share);
- }
-
- public function testDeleteNested(): void {
- $manager = $this->createManagerMock()
- ->onlyMethods(['getShareById', 'promoteReshares'])
- ->getMock();
-
- $path = $this->createMock(File::class);
- $path->method('getId')->willReturn(1);
-
- $share1 = $this->manager->newShare();
- $share1->setId(42)
- ->setProviderId('prov')
- ->setShareType(IShare::TYPE_USER)
- ->setSharedWith('sharedWith1')
- ->setSharedBy('sharedBy1')
- ->setNode($path)
- ->setTarget('myTarget1');
-
- $share2 = $this->manager->newShare();
- $share2->setId(43)
- ->setProviderId('prov')
- ->setShareType(IShare::TYPE_GROUP)
- ->setSharedWith('sharedWith2')
- ->setSharedBy('sharedBy2')
- ->setNode($path)
- ->setTarget('myTarget2')
- ->setParent(42);
-
- $share3 = $this->manager->newShare();
- $share3->setId(44)
- ->setProviderId('prov')
- ->setShareType(IShare::TYPE_LINK)
- ->setSharedBy('sharedBy3')
- ->setNode($path)
- ->setTarget('myTarget3')
- ->setParent(43);
-
- $this->defaultProvider
- ->method('getChildren')
- ->willReturnMap([
- [$share1, [$share2]],
- [$share2, [$share3]],
- [$share3, []],
- ]);
-
- $deleteCalls = [
- $share3,
- $share2,
- $share1,
- ];
- $this->defaultProvider->expects($this->exactly(3))
- ->method('delete')
- ->willReturnCallback(function ($share) use (&$deleteCalls): void {
- $expected = array_shift($deleteCalls);
- $this->assertEquals($expected, $share);
- });
-
- $dispatchCalls = [
- [BeforeShareDeletedEvent::class, $share1],
- [BeforeShareDeletedEvent::class, $share2],
- [BeforeShareDeletedEvent::class, $share3],
- [ShareDeletedEvent::class, $share3],
- [ShareDeletedEvent::class, $share2],
- [ShareDeletedEvent::class, $share1],
- ];
- $this->dispatcher->expects($this->exactly(6))
- ->method('dispatchTyped')
- ->willReturnCallback(function ($event) use (&$dispatchCalls): void {
- $expected = array_shift($dispatchCalls);
- $this->assertInstanceOf($expected[0], $event);
- $this->assertEquals($expected[1]->getId(), $event->getShare()->getId());
- });
-
- $manager->deleteShare($share1);
- }
-
- public function testDeleteFromSelf(): void {
- $manager = $this->createManagerMock()
- ->onlyMethods(['getShareById'])
- ->getMock();
-
- $recipientId = 'unshareFrom';
- $share = $this->manager->newShare();
- $share->setId(42)
- ->setProviderId('prov')
- ->setShareType(IShare::TYPE_USER)
- ->setSharedWith('sharedWith')
- ->setSharedBy('sharedBy')
- ->setShareOwner('shareOwner')
- ->setTarget('myTarget')
- ->setNodeId(1)
- ->setNodeType('file');
-
- $this->defaultProvider
- ->expects($this->once())
- ->method('deleteFromSelf')
- ->with($share, $recipientId);
-
- $this->dispatcher->expects($this->once())
- ->method('dispatchTyped')
- ->with(
- $this->callBack(function (ShareDeletedFromSelfEvent $e) use ($share) {
- return $e->getShare() === $share;
- })
- );
-
- $manager->deleteFromSelf($share, $recipientId);
- }
-
- public function testDeleteChildren(): void {
- $manager = $this->createManagerMock()
- ->onlyMethods(['deleteShare'])
- ->getMock();
-
- $share = $this->createMock(IShare::class);
- $share->method('getShareType')->willReturn(IShare::TYPE_USER);
-
- $child1 = $this->createMock(IShare::class);
- $child1->method('getShareType')->willReturn(IShare::TYPE_USER);
- $child2 = $this->createMock(IShare::class);
- $child2->method('getShareType')->willReturn(IShare::TYPE_USER);
- $child3 = $this->createMock(IShare::class);
- $child3->method('getShareType')->willReturn(IShare::TYPE_USER);
-
- $shares = [
- $child1,
- $child2,
- $child3,
- ];
-
- $this->defaultProvider
- ->expects($this->exactly(4))
- ->method('getChildren')
- ->willReturnCallback(function ($_share) use ($share, $shares) {
- if ($_share === $share) {
- return $shares;
- }
- return [];
- });
-
- $calls = [
- $child1,
- $child2,
- $child3,
- ];
- $this->defaultProvider->expects($this->exactly(3))
- ->method('delete')
- ->willReturnCallback(function ($share) use (&$calls): void {
- $expected = array_shift($calls);
- $this->assertEquals($expected, $share);
- });
-
- $result = self::invokePrivate($manager, 'deleteChildren', [$share]);
- $this->assertSame($shares, $result);
- }
-
- public function testPromoteReshareFile(): void {
- $manager = $this->createManagerMock()
- ->onlyMethods(['updateShare', 'getSharesInFolder', 'generalChecks'])
- ->getMock();
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(42);
-
- $share = $this->createMock(IShare::class);
- $share->method('getShareType')->willReturn(IShare::TYPE_USER);
- $share->method('getNodeType')->willReturn('folder');
- $share->method('getSharedWith')->willReturn('userB');
- $share->method('getNode')->willReturn($file);
-
- $reShare = $this->createMock(IShare::class);
- $reShare->method('getShareType')->willReturn(IShare::TYPE_USER);
- $reShare->method('getSharedBy')->willReturn('userB');
- $reShare->method('getSharedWith')->willReturn('userC');
- $reShare->method('getNode')->willReturn($file);
-
- $this->defaultProvider->method('getSharesBy')
- ->willReturnCallback(function ($userId, $shareType, $node, $reshares, $limit, $offset) use ($reShare, $file) {
- $this->assertEquals($file, $node);
- if ($shareType === IShare::TYPE_USER) {
- return match ($userId) {
- 'userB' => [$reShare],
- };
- } else {
- return [];
- }
- });
- $manager->method('generalChecks')->willThrowException(new GenericShareException());
-
- $manager->expects($this->exactly(1))->method('updateShare')->with($reShare)->willReturn($reShare);
-
- $this->userManager->method('userExists')->willReturn(true);
- $userFolder = $this->createMock(Folder::class);
- $this->rootFolder->method('getUserFolder')->with('userA')->willReturn($userFolder);
- $userFolder->method('getFirstNodeById')
- ->with(42)
- ->willReturn($file);
-
- $qb = $this->createMock(IQueryBuilder::class);
- $result = $this->createMock(IResult::class);
- $qb->method('select')
- ->willReturn($qb);
- $qb->method('from')
- ->willReturn($qb);
- $qb->method('andWhere')
- ->willReturn($qb);
- $qb->method('expr')
- ->willReturn($this->createMock(IExpressionBuilder::class));
- $qb->method('executeQuery')
- ->willReturn($result);
- $this->connection->method('getQueryBuilder')
- ->willReturn($qb);
- $result->method('fetchAssociative')
- ->willReturnOnConsecutiveCalls(
- ['uid_initiator' => 'userB', 'share_type' => IShare::TYPE_USER, 'uid_owner' => 'userA', 'file_source' => 42],
- false,
- );
-
- self::invokePrivate($manager, 'promoteReshares', [$share]);
- }
-
- public function testPromoteReshare(): void {
- $manager = $this->createManagerMock()
- ->onlyMethods(['updateShare', 'getSharesInFolder', 'generalChecks'])
- ->getMock();
-
- $folder = $this->createFolderMock('/path/to/folder');
-
- $subFolder = $this->createFolderMock('/path/to/folder/sub');
-
- $otherFolder = $this->createFolderMock('/path/to/otherfolder/');
-
- $share = $this->createMock(IShare::class);
- $share->method('getShareType')->willReturn(IShare::TYPE_USER);
- $share->method('getNodeType')->willReturn('folder');
- $share->method('getSharedWith')->willReturn('userB');
- $share->method('getNode')->willReturn($folder);
-
- $reShare = $this->createMock(IShare::class);
- $reShare->method('getShareType')->willReturn(IShare::TYPE_USER);
- $reShare->method('getSharedBy')->willReturn('userB');
- $reShare->method('getSharedWith')->willReturn('userC');
- $reShare->method('getNode')->willReturn($folder);
-
- $reShareInSubFolder = $this->createMock(IShare::class);
- $reShareInSubFolder->method('getShareType')->willReturn(IShare::TYPE_USER);
- $reShareInSubFolder->method('getSharedBy')->willReturn('userB');
- $reShareInSubFolder->method('getNode')->willReturn($subFolder);
-
- $reShareInOtherFolder = $this->createMock(IShare::class);
- $reShareInOtherFolder->method('getShareType')->willReturn(IShare::TYPE_USER);
- $reShareInOtherFolder->method('getSharedBy')->willReturn('userB');
- $reShareInOtherFolder->method('getNode')->willReturn($otherFolder);
-
- $this->defaultProvider->method('getSharesBy')
- ->willReturnCallback(function ($userId, $shareType, $node, $reshares, $limit, $offset) use ($folder, $subFolder, $reShare, $reShareInSubFolder, $reShareInOtherFolder) {
- if ($shareType === IShare::TYPE_USER) {
- if ($userId === 'userB') {
- if ($node === $folder) {
- return [$reShare];
- }
- if ($node === $subFolder) {
- return [$reShareInSubFolder];
- }
- }
- }
- $this->fail();
- });
- $manager->method('generalChecks')->willThrowException(new GenericShareException());
-
- $calls = [
- $reShare,
- $reShareInSubFolder,
- ];
- $manager->expects($this->exactly(2))
- ->method('updateShare')
- ->willReturnCallback(function ($share) use (&$calls): IShare {
- $expected = array_shift($calls);
- $this->assertEquals($expected, $share);
- return $expected;
- });
-
- $this->userManager->method('userExists')->willReturn(true);
- $userFolder = $this->createMock(Folder::class);
- $this->rootFolder->method('getUserFolder')->with('userA')->willReturn($userFolder);
- $userFolder->method('getFirstNodeById')
- ->willReturnCallback(function ($id) use ($subFolder, $otherFolder, $folder) {
- return match ($id) {
- 41 => $subFolder,
- 42 => $otherFolder,
- 43 => $folder,
- };
- });
-
- $qb = $this->createMock(IQueryBuilder::class);
- $result = $this->createMock(IResult::class);
- $qb->method('select')
- ->willReturn($qb);
- $qb->method('from')
- ->willReturn($qb);
- $qb->method('andWhere')
- ->willReturn($qb);
- $qb->method('expr')
- ->willReturn($this->createMock(IExpressionBuilder::class));
- $qb->method('executeQuery')
- ->willReturn($result);
- $this->connection->method('getQueryBuilder')
- ->willReturn($qb);
- $result->method('fetchAssociative')
- ->willReturnOnConsecutiveCalls(
- ['uid_initiator' => 'userB', 'share_type' => IShare::TYPE_USER, 'uid_owner' => 'userA', 'file_source' => 41],
- ['uid_initiator' => 'userB', 'share_type' => IShare::TYPE_USER, 'uid_owner' => 'userA', 'file_source' => 42],
- ['uid_initiator' => 'userB', 'share_type' => IShare::TYPE_USER, 'uid_owner' => 'userA', 'file_source' => 43],
- false,
- );
-
- self::invokePrivate($manager, 'promoteReshares', [$share]);
- }
-
- public function testPromoteReshareWhenUserHasAnotherShare(): void {
- $manager = $this->createManagerMock()
- ->onlyMethods(['updateShare', 'getSharesInFolder', 'getSharedWith', 'generalChecks'])
- ->getMock();
-
- $folder = $this->createFolderMock('/path/to/folder');
-
- $share = $this->createMock(IShare::class);
- $share->method('getShareType')->willReturn(IShare::TYPE_USER);
- $share->method('getNodeType')->willReturn('folder');
- $share->method('getSharedWith')->willReturn('userB');
- $share->method('getNode')->willReturn($folder);
-
- $reShare = $this->createMock(IShare::class);
- $reShare->method('getShareType')->willReturn(IShare::TYPE_USER);
- $reShare->method('getNodeType')->willReturn('folder');
- $reShare->method('getSharedBy')->willReturn('userB');
- $reShare->method('getNode')->willReturn($folder);
-
- $this->defaultProvider->method('getSharesBy')->willReturn([$reShare]);
- $manager->method('generalChecks');
-
- /* No share is promoted because generalChecks does not throw */
- $manager->expects($this->never())->method('updateShare');
-
- $this->userManager->method('userExists')->willReturn(true);
- $userFolder = $this->createMock(Folder::class);
- $this->rootFolder->method('getUserFolder')->with('userA')->willReturn($userFolder);
- $userFolder->method('getFirstNodeById')
- ->with(42)
- ->willReturn($folder);
-
- $qb = $this->createMock(IQueryBuilder::class);
- $result = $this->createMock(IResult::class);
- $qb->method('select')
- ->willReturn($qb);
- $qb->method('from')
- ->willReturn($qb);
- $qb->method('andWhere')
- ->willReturn($qb);
- $qb->method('expr')
- ->willReturn($this->createMock(IExpressionBuilder::class));
- $qb->method('executeQuery')
- ->willReturn($result);
- $this->connection->method('getQueryBuilder')
- ->willReturn($qb);
- $result->method('fetchAssociative')
- ->willReturnOnConsecutiveCalls(
- ['uid_initiator' => 'userB', 'share_type' => IShare::TYPE_USER, 'uid_owner' => 'userA', 'file_source' => 42],
- false,
- );
-
- self::invokePrivate($manager, 'promoteReshares', [$share]);
- }
-
- public function testPromoteReshareOfUsersInGroupShare(): void {
- $manager = $this->createManagerMock()
- ->onlyMethods(['updateShare', 'getSharesInFolder', 'getSharedWith', 'generalChecks'])
- ->getMock();
-
- $folder = $this->createFolderMock('/path/to/folder');
-
- $userA = $this->createMock(IUser::class);
- $userA->method('getUID')->willReturn('userA');
-
- $share = $this->createMock(IShare::class);
- $share->method('getShareType')->willReturn(IShare::TYPE_GROUP);
- $share->method('getNodeType')->willReturn('folder');
- $share->method('getSharedWith')->willReturn('Group');
- $share->method('getNode')->willReturn($folder);
- $share->method('getShareOwner')->willReturn($userA);
-
- $reShare1 = $this->createMock(IShare::class);
- $reShare1->method('getShareType')->willReturn(IShare::TYPE_USER);
- $reShare1->method('getNodeType')->willReturn('folder');
- $reShare1->method('getSharedBy')->willReturn('userB');
- $reShare1->method('getNode')->willReturn($folder);
-
- $reShare2 = $this->createMock(IShare::class);
- $reShare2->method('getShareType')->willReturn(IShare::TYPE_USER);
- $reShare2->method('getNodeType')->willReturn('folder');
- $reShare2->method('getSharedBy')->willReturn('userC');
- $reShare2->method('getNode')->willReturn($folder);
-
- $userB = $this->createMock(IUser::class);
- $userB->method('getUID')->willReturn('userB');
- $userC = $this->createMock(IUser::class);
- $userC->method('getUID')->willReturn('userC');
- $group = $this->createMock(IGroup::class);
- $group->method('getUsers')->willReturn([$userB, $userC]);
- $this->groupManager->method('get')->with('Group')->willReturn($group);
-
- $this->defaultProvider->method('getSharesBy')
- ->willReturnCallback(function ($userId, $shareType, $node, $reshares, $limit, $offset) use ($reShare1, $reShare2) {
- if ($shareType === IShare::TYPE_USER) {
- return match ($userId) {
- 'userB' => [$reShare1],
- 'userC' => [$reShare2],
- };
- } else {
- return [];
- }
- });
- $manager->method('generalChecks')->willThrowException(new GenericShareException());
-
- $manager->method('getSharedWith')->willReturn([]);
-
- $this->userManager->method('userExists')->willReturn(true);
- $userFolder = $this->createMock(Folder::class);
- $this->rootFolder->method('getUserFolder')->with('userA')->willReturn($userFolder);
- $userFolder->method('getFirstNodeById')
- ->with(42)
- ->willReturn($folder);
-
- $calls = [
- $reShare1,
- $reShare2,
- ];
- $manager->expects($this->exactly(2))
- ->method('updateShare')
- ->willReturnCallback(function ($share) use (&$calls): IShare {
- $expected = array_shift($calls);
- $this->assertEquals($expected, $share);
- return $expected;
- });
-
- $qb = $this->createMock(IQueryBuilder::class);
- $result = $this->createMock(IResult::class);
- $qb->method('select')
- ->willReturn($qb);
- $qb->method('from')
- ->willReturn($qb);
- $qb->method('andWhere')
- ->willReturn($qb);
- $qb->method('expr')
- ->willReturn($this->createMock(IExpressionBuilder::class));
- $qb->method('executeQuery')
- ->willReturn($result);
- $this->connection->method('getQueryBuilder')
- ->willReturn($qb);
- $result->method('fetchAssociative')
- ->willReturnOnConsecutiveCalls(
- ['uid_initiator' => 'userB', 'share_type' => IShare::TYPE_USER, 'uid_owner' => 'userA', 'file_source' => 42],
- ['uid_initiator' => 'userC', 'share_type' => IShare::TYPE_USER, 'uid_owner' => 'userA', 'file_source' => 42],
- false,
- );
-
- self::invokePrivate($manager, 'promoteReshares', [$share]);
- }
-
- public function testGetShareById(): void {
- $share = $this->createMock(IShare::class);
-
- $this->defaultProvider
- ->expects($this->once())
- ->method('getShareById')
- ->with(42)
- ->willReturn($share);
-
- $this->assertEquals($share, $this->manager->getShareById('default:42'));
- }
-
- public function testGetShareByIdOfDisabledInitiatorAsShareOwner(): void {
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['files_sharing', 'hide_disabled_user_shares', 'yes', 'yes'],
- ]);
-
- $share = $this->manager->newShare();
- $share->setShareType(IShare::TYPE_USER)
- ->setShareOwner('owner')
- ->setSharedBy('initiator')
- ->setSharedWith('recipient');
-
- $this->userManager->method('get')->willReturnMap([
- ['owner', $this->createEnabledUser(true)],
- ['initiator', $this->createEnabledUser(false)],
- ]);
-
- $this->defaultProvider
- ->expects($this->once())
- ->method('getShareById')
- ->with('42', 'owner')
- ->willReturn($share);
-
- $this->assertSame($share, $this->manager->getShareById('default:42', 'owner'));
- }
-
- public function testGetShareByIdOfDisabledInitiatorAsOtherUser(): void {
- $this->expectException(ShareNotFound::class);
- $this->expectExceptionMessage('The requested share does not exist anymore');
-
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['files_sharing', 'hide_disabled_user_shares', 'yes', 'yes'],
- ]);
-
- $share = $this->manager->newShare();
- $share->setShareType(IShare::TYPE_USER)
- ->setShareOwner('owner')
- ->setSharedBy('initiator')
- ->setSharedWith('recipient');
-
- $this->userManager->method('get')->willReturnMap([
- ['owner', $this->createEnabledUser(true)],
- ['initiator', $this->createEnabledUser(false)],
- ]);
-
- $this->defaultProvider
- ->expects($this->once())
- ->method('getShareById')
- ->with('42', 'recipient')
- ->willReturn($share);
-
- $this->manager->getShareById('default:42', 'recipient');
- }
-
- public function testGetExpiredShareById(): void {
- $this->expectException(ShareNotFound::class);
-
- $manager = $this->createManagerMock()
- ->onlyMethods(['deleteShare'])
- ->getMock();
-
- $date = new \DateTime();
- $date->setTime(0, 0, 0);
-
- $share = $this->manager->newShare();
- $share->setExpirationDate($date)
- ->setShareType(IShare::TYPE_LINK);
-
- $this->defaultProvider->expects($this->once())
- ->method('getShareById')
- ->with('42')
- ->willReturn($share);
-
- $manager->expects($this->once())
- ->method('deleteShare')
- ->with($share);
-
- $manager->getShareById('default:42');
- }
-
- public function testVerifyPasswordNullButEnforced(): void {
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Passwords are enforced for link and mail shares');
-
- $this->config->method('getAppValue')->willReturnMap([
- ['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
- ]);
-
- $this->appConfig->method('getValueBool')->willReturnMap([
- ['core', 'shareapi_enforce_links_password', true],
- ]);
-
- self::invokePrivate($this->manager, 'verifyPassword', [null]);
- }
-
- public function testVerifyPasswordNotEnforcedGroup(): void {
- $this->config->method('getAppValue')->willReturnMap([
- ['core', 'shareapi_enforce_links_password_excluded_groups', '', '["admin"]'],
- ['core', 'shareapi_enforce_links_password', 'no', 'yes'],
- ]);
-
- // Create admin user
- $user = $this->createMock(IUser::class);
- $this->userSession->method('getUser')->willReturn($user);
- $this->groupManager->method('getUserGroupIds')->with($user)->willReturn(['admin']);
-
- $result = self::invokePrivate($this->manager, 'verifyPassword', [null]);
- $this->assertNull($result);
- }
-
- public function testVerifyPasswordNotEnforcedMultipleGroups(): void {
- $this->config->method('getAppValue')->willReturnMap([
- ['core', 'shareapi_enforce_links_password_excluded_groups', '', '["admin", "special"]'],
- ['core', 'shareapi_enforce_links_password', 'no', 'yes'],
- ]);
-
- // Create admin user
- $user = $this->createMock(IUser::class);
- $this->userSession->method('getUser')->willReturn($user);
- $this->groupManager->method('getUserGroupIds')->with($user)->willReturn(['special']);
-
- $result = self::invokePrivate($this->manager, 'verifyPassword', [null]);
- $this->assertNull($result);
- }
-
- public function testVerifyPasswordNull(): void {
- $this->config->method('getAppValue')->willReturnMap([
- ['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
- ['core', 'shareapi_enforce_links_password', 'no', 'no'],
- ]);
-
- $result = self::invokePrivate($this->manager, 'verifyPassword', [null]);
- $this->assertNull($result);
- }
-
- public function testVerifyPasswordHook(): void {
- $this->config->method('getAppValue')->willReturnMap([
- ['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
- ['core', 'shareapi_enforce_links_password', 'no', 'no'],
- ]);
-
- $this->dispatcher->expects($this->once())->method('dispatchTyped')
- ->willReturnCallback(function (Event $event): void {
- $this->assertInstanceOf(ValidatePasswordPolicyEvent::class, $event);
- /** @var ValidatePasswordPolicyEvent $event */
- $this->assertSame('password', $event->getPassword());
- }
- );
-
- $result = self::invokePrivate($this->manager, 'verifyPassword', ['password']);
- $this->assertNull($result);
- }
-
- public function testVerifyPasswordHookFails(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('password not accepted');
-
- $this->config->method('getAppValue')->willReturnMap([
- ['core', 'shareapi_enforce_links_password_excluded_groups', '', ''],
- ['core', 'shareapi_enforce_links_password', 'no', 'no'],
- ]);
-
- $this->dispatcher->expects($this->once())->method('dispatchTyped')
- ->willReturnCallback(function (Event $event): void {
- $this->assertInstanceOf(ValidatePasswordPolicyEvent::class, $event);
- /** @var ValidatePasswordPolicyEvent $event */
- $this->assertSame('password', $event->getPassword());
- throw new HintException('password not accepted');
- }
- );
-
- self::invokePrivate($this->manager, 'verifyPassword', ['password']);
- }
-
- public function createShare($id, int $type, ?Node $node, $sharedWith, $sharedBy, $shareOwner,
- $permissions, $expireDate = null, $password = null, $attributes = null): IShare&MockObject {
- $share = $this->createMock(IShare::class);
-
- $share->method('getShareType')->willReturn($type);
- $share->method('getSharedWith')->willReturn($sharedWith);
- $share->method('getSharedBy')->willReturn($sharedBy);
- $share->method('getShareOwner')->willReturn($shareOwner);
- if ($node) {
- $share->method('getNode')->willReturn($node);
- if ($node->getId()) {
- $share->method('getNodeId')->willReturn($node->getId());
- }
- } else {
- $share->method('getNode')->willReturnCallback(function (): never {
- throw new NotFoundException();
- });
- }
- $share->method('getPermissions')->willReturn($permissions);
- $share->method('getAttributes')->willReturn($attributes);
- $share->method('getExpirationDate')->willReturn($expireDate);
- $share->method('getPassword')->willReturn($password);
- $share->method('isPasswordProtected')->willReturn(!empty($password));
-
- return $share;
- }
-
- private function createEnabledUser(bool $enabled): IUser&MockObject {
- $user = $this->createMock(IUser::class);
- $user->method('isEnabled')->willReturn($enabled);
-
- return $user;
- }
-
- public static function dataGeneralChecks(): array {
- $user0 = 'user0';
- $user2 = 'user1';
- $group0 = 'group0';
-
- $file = [
- File::class,
- [
- 'getId' => 108,
- ],
- 'default',
- ];
-
- $node = [
- Node::class,
- [
- 'getId' => 108,
- ],
- 'default',
- ];
-
- $data = [
- [[null, IShare::TYPE_USER, $file, null, $user0, $user0, 31, null, null], 'Share recipient is not a valid user', true],
- [[null, IShare::TYPE_USER, $file, $group0, $user0, $user0, 31, null, null], 'Share recipient is not a valid user', true],
- [[null, IShare::TYPE_USER, $file, 'foo@bar.com', $user0, $user0, 31, null, null], 'Share recipient is not a valid user', true],
- [[null, IShare::TYPE_GROUP, $file, null, $user0, $user0, 31, null, null], 'Share recipient is not a valid group', true],
- [[null, IShare::TYPE_GROUP, $file, $user2, $user0, $user0, 31, null, null], 'Share recipient is not a valid group', true],
- [[null, IShare::TYPE_GROUP, $file, 'foo@bar.com', $user0, $user0, 31, null, null], 'Share recipient is not a valid group', true],
- [[null, IShare::TYPE_LINK, $file, $user2, $user0, $user0, 31, null, null], 'Share recipient should be empty', true],
- [[null, IShare::TYPE_LINK, $file, $group0, $user0, $user0, 31, null, null], 'Share recipient should be empty', true],
- [[null, IShare::TYPE_LINK, $file, 'foo@bar.com', $user0, $user0, 31, null, null], 'Share recipient should be empty', true],
- [[null, -1, $file, null, $user0, $user0, 31, null, null], 'Unknown share type', true],
-
- [[null, IShare::TYPE_USER, $file, $user2, null, $user0, 31, null, null], 'Share initiator must be set', true],
- [[null, IShare::TYPE_GROUP, $file, $group0, null, $user0, 31, null, null], 'Share initiator must be set', true],
- [[null, IShare::TYPE_LINK, $file, null, null, $user0, 31, null, null], 'Share initiator must be set', true],
-
- [[null, IShare::TYPE_USER, $file, $user0, $user0, $user0, 31, null, null], 'Cannot share with yourself', true],
-
- [[null, IShare::TYPE_USER, null, $user2, $user0, $user0, 31, null, null], 'Shared path must be set', true],
- [[null, IShare::TYPE_GROUP, null, $group0, $user0, $user0, 31, null, null], 'Shared path must be set', true],
- [[null, IShare::TYPE_LINK, null, null, $user0, $user0, 31, null, null], 'Shared path must be set', true],
-
- [[null, IShare::TYPE_USER, $node, $user2, $user0, $user0, 31, null, null], 'Shared path must be either a file or a folder', true],
- [[null, IShare::TYPE_GROUP, $node, $group0, $user0, $user0, 31, null, null], 'Shared path must be either a file or a folder', true],
- [[null, IShare::TYPE_LINK, $node, null, $user0, $user0, 31, null, null], 'Shared path must be either a file or a folder', true],
- ];
-
- $nonShareAble = [
- Folder::class,
- [
- 'getId' => 108,
- 'isShareable' => false,
- 'getPermissions' => Constants::PERMISSION_READ,
- 'getPath' => 'path',
- 'getName' => 'name',
- 'getOwner' => $user0,
- ],
- 'default',
- ];
-
- $data[] = [[null, IShare::TYPE_USER, $nonShareAble, $user2, $user0, $user0, 31, null, null], 'You are not allowed to share "name".', true];
- $data[] = [[null, IShare::TYPE_GROUP, $nonShareAble, $group0, $user0, $user0, 31, null, null], 'You are not allowed to share "name".', true];
- $data[] = [[null, IShare::TYPE_LINK, $nonShareAble, null, $user0, $user0, 31, null, null], 'You are not allowed to share "name".', true];
-
- $limitedPermissions = [
- File::class,
- [
- 'isShareable' => true,
- 'getPermissions' => Constants::PERMISSION_READ | Constants::PERMISSION_SHARE,
- 'getId' => 108,
- 'getPath' => 'path',
- 'getName' => 'name',
- 'getOwner' => $user0,
- ],
- 'default',
- ];
-
- $data[] = [[null, IShare::TYPE_USER, $limitedPermissions, $user2, $user0, $user0, null, null, null], 'Valid permissions are required for sharing', true];
- $data[] = [[null, IShare::TYPE_GROUP, $limitedPermissions, $group0, $user0, $user0, null, null, null], 'Valid permissions are required for sharing', true];
- $data[] = [[null, IShare::TYPE_LINK, $limitedPermissions, null, $user0, $user0, null, null, null], 'Valid permissions are required for sharing', true];
-
- $limitedPermissions[1]['getMountPoint'] = IMovableMount::class;
-
- // increase permissions of a re-share
- $data[] = [[null, IShare::TYPE_GROUP, $limitedPermissions, $group0, $user0, $user0, 19, null, null], 'You cannot share "path" with more permission than you have yourself.', true];
- $data[] = [[null, IShare::TYPE_USER, $limitedPermissions, $user2, $user0, $user0, 3, null, null], 'You cannot share "path" with more permission than you have yourself.', true];
-
- $nonMoveableMountPermissions = [
- Folder::class,
- [
- 'isShareable' => true,
- 'getPermissions' => Constants::PERMISSION_READ,
- 'getId' => 108,
- 'getPath' => 'path',
- 'getName' => 'name',
- 'getInternalPath' => '',
- 'getOwner' => $user0,
- ],
- 'allPermissions',
- ];
-
- $data[] = [[null, IShare::TYPE_USER, $nonMoveableMountPermissions, $user2, $user0, $user0, 11, null, null], 'You cannot share "path" with more permission than you have yourself.', false];
- $data[] = [[null, IShare::TYPE_GROUP, $nonMoveableMountPermissions, $group0, $user0, $user0, 11, null, null], 'You cannot share "path" with more permission than you have yourself.', false];
-
- $rootFolder = [
- Folder::class,
- [
- 'isShareable' => true,
- 'getPermissions' => Constants::PERMISSION_ALL,
- 'getId' => 42,
- ],
- 'none',
- ];
-
- $data[] = [[null, IShare::TYPE_USER, $rootFolder, $user2, $user0, $user0, 30, null, null], 'You cannot share your home folder.', true];
- $data[] = [[null, IShare::TYPE_GROUP, $rootFolder, $group0, $user0, $user0, 2, null, null], 'You cannot share your home folder.', true];
- $data[] = [[null, IShare::TYPE_LINK, $rootFolder, null, $user0, $user0, 16, null, null], 'You cannot share your home folder.', true];
-
- $allPermissionsFiles = [
- File::class,
- [
- 'isShareable' => true,
- 'getPermissions' => Constants::PERMISSION_ALL,
- 'getId' => 108,
- 'getOwner' => $user0,
- ],
- 'default',
- ];
-
- // test invalid CREATE or DELETE permissions
- $data[] = [[null, IShare::TYPE_USER, $allPermissionsFiles, $user2, $user0, $user0, Constants::PERMISSION_ALL, null, null], 'File cannot be shared with delete permission.', true];
- $data[] = [[null, IShare::TYPE_GROUP, $allPermissionsFiles, $group0, $user0, $user0, Constants::PERMISSION_READ | Constants::PERMISSION_CREATE, null, null], 'File cannot be shared with create permission.', true];
- $data[] = [[null, IShare::TYPE_LINK, $allPermissionsFiles, null, $user0, $user0, Constants::PERMISSION_READ | Constants::PERMISSION_DELETE, null, null], 'File cannot be shared with delete permission.', true];
-
- $allPermissions = [
- Folder::class,
- [
- 'isShareable' => true,
- 'getPermissions' => Constants::PERMISSION_ALL,
- 'getId' => 108,
- 'getOwner' => $user0,
- ],
- 'default',
- ];
-
- $data[] = [[null, IShare::TYPE_USER, $allPermissions, $user2, $user0, $user0, 30, null, null], 'File share needs at least read permission.', true];
- $data[] = [[null, IShare::TYPE_GROUP, $allPermissions, $group0, $user0, $user0, 2, null, null], 'File share needs at least read permission.', true];
-
- // test invalid permissions
- $data[] = [[null, IShare::TYPE_USER, $allPermissions, $user2, $user0, $user0, 32, null, null], 'Valid permissions are required for sharing', true];
- $data[] = [[null, IShare::TYPE_GROUP, $allPermissions, $group0, $user0, $user0, 63, null, null], 'Valid permissions are required for sharing', true];
- $data[] = [[null, IShare::TYPE_LINK, $allPermissions, null, $user0, $user0, -1, null, null], 'Valid permissions are required for sharing', true];
-
- // working shares
- $data[] = [[null, IShare::TYPE_USER, $allPermissions, $user2, $user0, $user0, 31, null, null], null, false];
- $data[] = [[null, IShare::TYPE_GROUP, $allPermissions, $group0, $user0, $user0, 3, null, null], null, false];
- $data[] = [[null, IShare::TYPE_LINK, $allPermissions, null, $user0, $user0, 17, null, null], null, false];
-
- $remoteFile = [
- Folder::class,
- [
- 'isShareable' => true,
- 'getPermissions' => Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE | Constants::PERMISSION_SHARE,
- 'getId' => 108,
- 'getOwner' => $user0,
- ],
- 'remote',
- ];
-
- $data[] = [[null, IShare::TYPE_REMOTE, $remoteFile, $user2, $user0, $user0, 1, null, null], null, false];
- $data[] = [[null, IShare::TYPE_REMOTE, $remoteFile, $user2, $user0, $user0, 3, null, null], null, false];
- $data[] = [[null, IShare::TYPE_REMOTE, $remoteFile, $user2, $user0, $user0, 31, null, null], 'You cannot share "" with more permission than you have yourself.', true];
-
- return $data;
- }
-
- private function createNodeMock(string $class, array $methods, string $storageType): MockObject {
- $mock = $this->createMock($class);
- foreach ($methods as $methodName => $return) {
- if ($methodName === 'getOwner') {
- $uid = $return;
- $return = $this->createMock(IUser::class);
- $return->method('getUID')
- ->willReturn($uid);
- } elseif ($methodName === 'getMountPoint') {
- $return = $this->createMock($return);
- }
- $mock->method($methodName)->willReturn($return);
- }
- switch ($storageType) {
- case 'default':
- $storage = $this->createMock(IStorage::class);
- $storage->method('instanceOfStorage')
- ->with('\OCA\Files_Sharing\External\Storage')
- ->willReturn(false);
- break;
- case 'allPermissions':
- $storage = $this->createMock(IStorage::class);
- $storage->method('instanceOfStorage')
- ->with('\OCA\Files_Sharing\External\Storage')
- ->willReturn(false);
- $storage->method('getPermissions')->willReturn(Constants::PERMISSION_ALL);
- break;
- case 'none':
- $storage = false;
- break;
- case 'remote':
- $storage = $this->createMock(IStorage::class);
- $storage->method('instanceOfStorage')
- ->with('\OCA\Files_Sharing\External\Storage')
- ->willReturn(true);
- break;
- default:
- throw new \Exception('Unknown storage type ' . $storageType);
- }
- if ($storage === false) {
- $mock->expects(self::never())->method('getStorage');
- } else {
- $mock->method('getStorage')
- ->willReturn($storage);
- }
-
- return $mock;
- }
-
- #[DataProvider('dataGeneralChecks')]
- public function testGeneralChecks(array $shareParams, ?string $exceptionMessage, bool $exception): void {
- if ($shareParams[2] !== null) {
- $shareParams[2] = $this->createNodeMock(...$shareParams[2]);
- }
- $share = $this->createShare(...$shareParams);
-
- $thrown = null;
-
- $this->userManager->method('userExists')->willReturnCallBack(fn (string $userId) => $userId === 'user0' || $userId === 'user1');
-
- $this->userManager->method('get')->willReturnMap([
- ['user0', $this->createMock(IUser::class)],
- ['user1', $this->createMock(IUser::class)],
- ]);
-
- $this->groupManager->method('groupExists')->willReturnMap([
- ['group0', true],
- ]);
-
- $userFolder = $this->createMock(Folder::class);
-
- $userFolder
- ->method('getId')
- ->willReturn(42);
-
- $userFolder
- ->method('getPermissions')
- ->willReturn(Constants::PERMISSION_ALL);
-
- // Id 108 is used in the data to refer to the node of the share.
- try {
- $node = $share->getNode();
- } catch (NotFoundException $e) {
- $node = null;
- }
-
- $userFolder
- ->method('getById')
- ->willReturnMap([
- [108, $node !== null ? [$node] : []],
- [42, [$userFolder]],
- ]);
-
- $userFolder
- ->method('getFirstNodeById')
- ->willReturnMap([
- [108, $node],
- [42, $userFolder],
- ]);
-
- $userFolder
- ->method('getRelativePath')
- ->willReturnArgument(0);
-
- $this->rootFolder
- ->method('getUserFolder')
- ->willReturn($userFolder);
-
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_enabled', 'yes', 'yes'],
- ['core', 'shareapi_allow_group_sharing', 'yes', 'yes'],
- ['core', 'shareapi_allow_links', 'yes', 'yes'],
- ['core', 'shareapi_allow_links_exclude_groups', '[]', '[]'],
- ['files_sharing', 'outgoing_server2server_share_enabled', 'yes', 'yes'],
- ]);
-
- try {
- self::invokePrivate($this->manager, 'generalChecks', [$share]);
- $thrown = false;
- } catch (GenericShareException $e) {
- $this->assertEquals($exceptionMessage, $e->getHint());
- $thrown = true;
- } catch (\InvalidArgumentException $e) {
- $this->assertEquals($exceptionMessage, $e->getMessage());
- $thrown = true;
- }
-
- $this->assertSame($exception, $thrown);
- }
-
- public function testGeneralCheckShareRoot(): void {
- $this->expectException(GenericShareException::class);
- $this->expectExceptionMessage('You cannot share your home folder.');
-
- $this->userManager->method('userExists')->willReturnMap([
- ['user0', true],
- ['user1', true],
- ]);
-
- $this->userManager->method('get')->willReturnMap([
- ['user0', $this->createMock(IUser::class)],
- ['user1', $this->createMock(IUser::class)],
- ]);
-
- $userFolder = $this->createMock(Folder::class);
- $userFolder
- ->method('isSubNode')
- ->with($userFolder)
- ->willReturn(false);
- $userFolder
- ->method('getId')
- ->willReturn(42);
- $userFolder
- ->method('getById')
- ->with(42)
- ->willReturn([$userFolder]);
- $userFolder
- ->method('getFirstNodeById')
- ->with(42)
- ->willReturn($userFolder);
- $userFolder
- ->method('getPermissions')
- ->willReturn(Constants::PERMISSION_ALL);
- $userFolder
- ->method('isShareable')
- ->willReturn(true);
-
- $this->rootFolder
- ->method('getUserFolder')
- ->willReturn($userFolder);
-
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_enabled', 'yes', 'yes'],
- ]);
-
- $share = $this->manager->newShare()
- ->setShareType(IShare::TYPE_USER)
- ->setSharedWith('user0')
- ->setSharedBy('user1')
- ->setNode($userFolder)
- ->setPermissions(Constants::PERMISSION_ALL);
-
- self::invokePrivate($this->manager, 'generalChecks', [$share]);
- }
-
- public static function validateExpirationDateInternalProvider() {
- return [[IShare::TYPE_USER], [IShare::TYPE_REMOTE], [IShare::TYPE_REMOTE_GROUP]];
- }
-
- #[DataProvider('validateExpirationDateInternalProvider')]
- public function testValidateExpirationDateInternalInPast($shareType): void {
- $this->expectException(GenericShareException::class);
- $this->expectExceptionMessage('Expiration date is in the past');
-
- // Expire date in the past
- $past = new \DateTime();
- $past->sub(new \DateInterval('P1D'));
-
- $share = $this->manager->newShare();
- $share->setShareType($shareType);
- $share->setExpirationDate($past);
-
- self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
- }
-
- #[DataProvider('validateExpirationDateInternalProvider')]
- public function testValidateExpirationDateInternalEnforceButNotSet($shareType): void {
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Expiration date is enforced');
-
- $share = $this->manager->newShare();
- $share->setProviderId('foo')->setId('bar');
- $share->setShareType($shareType);
- if ($shareType === IShare::TYPE_USER) {
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_default_internal_expire_date', 'no', 'yes'],
- ['core', 'shareapi_enforce_internal_expire_date', 'no', 'yes'],
- ]);
- } else {
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_default_remote_expire_date', 'no', 'yes'],
- ['core', 'shareapi_enforce_remote_expire_date', 'no', 'yes'],
- ]);
- }
-
- self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
- }
-
- #[DataProvider('validateExpirationDateInternalProvider')]
- public function testValidateExpirationDateInternalEnforceButNotEnabledAndNotSet($shareType): void {
- $share = $this->manager->newShare();
- $share->setProviderId('foo')->setId('bar');
- $share->setShareType($shareType);
-
- if ($shareType === IShare::TYPE_USER) {
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_enforce_internal_expire_date', 'no', 'yes'],
- ]);
- } else {
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_enforce_remote_expire_date', 'no', 'yes'],
- ]);
- }
-
- self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
-
- $this->assertNull($share->getExpirationDate());
- }
-
- #[DataProvider('validateExpirationDateInternalProvider')]
- public function testValidateExpirationDateInternalEnforceButNotSetNewShare($shareType): void {
- $share = $this->manager->newShare();
- $share->setShareType($shareType);
-
- if ($shareType === IShare::TYPE_USER) {
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_enforce_internal_expire_date', 'no', 'yes'],
- ['core', 'shareapi_internal_expire_after_n_days', '7', '3'],
- ['core', 'shareapi_default_internal_expire_date', 'no', 'yes'],
- ['core', 'internal_defaultExpDays', '3', '3'],
- ]);
- } else {
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_enforce_remote_expire_date', 'no', 'yes'],
- ['core', 'shareapi_remote_expire_after_n_days', '7', '3'],
- ['core', 'shareapi_default_remote_expire_date', 'no', 'yes'],
- ['core', 'remote_defaultExpDays', '3', '3'],
- ]);
- }
-
- $expected = new \DateTime('now', $this->timezone);
- $expected->setTime(23, 59, 59);
- $expected->add(new \DateInterval('P3D'));
-
- self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
-
- $this->assertNotNull($share->getExpirationDate());
- $this->assertEquals($expected, $share->getExpirationDate());
- }
-
- #[DataProvider('validateExpirationDateInternalProvider')]
- public function testValidateExpirationDateInternalEnforceRelaxedDefaultButNotSetNewShare($shareType): void {
- $share = $this->manager->newShare();
- $share->setShareType($shareType);
-
- if ($shareType === IShare::TYPE_USER) {
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_enforce_internal_expire_date', 'no', 'yes'],
- ['core', 'shareapi_internal_expire_after_n_days', '7', '3'],
- ['core', 'shareapi_default_internal_expire_date', 'no', 'yes'],
- ['core', 'internal_defaultExpDays', '3', '1'],
- ]);
- } else {
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_enforce_remote_expire_date', 'no', 'yes'],
- ['core', 'shareapi_remote_expire_after_n_days', '7', '3'],
- ['core', 'shareapi_default_remote_expire_date', 'no', 'yes'],
- ['core', 'remote_defaultExpDays', '3', '1'],
- ]);
- }
-
- $expected = new \DateTime('now', $this->timezone);
- $expected->setTime(23, 59, 59);
- $expected->add(new \DateInterval('P1D'));
-
- self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
-
- $this->assertNotNull($share->getExpirationDate());
- $this->assertEquals($expected, $share->getExpirationDate());
- }
-
- #[DataProvider('validateExpirationDateInternalProvider')]
- public function testValidateExpirationDateInternalEnforceTooFarIntoFuture($shareType): void {
- $this->expectException(GenericShareException::class);
- $this->expectExceptionMessage('Cannot set expiration date more than 3 days in the future');
-
- $future = new \DateTime();
- $future->add(new \DateInterval('P7D'));
-
- $share = $this->manager->newShare();
- $share->setShareType($shareType);
- $share->setExpirationDate($future);
-
- if ($shareType === IShare::TYPE_USER) {
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_enforce_internal_expire_date', 'no', 'yes'],
- ['core', 'shareapi_internal_expire_after_n_days', '7', '3'],
- ['core', 'shareapi_default_internal_expire_date', 'no', 'yes'],
- ]);
- } else {
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_enforce_remote_expire_date', 'no', 'yes'],
- ['core', 'shareapi_remote_expire_after_n_days', '7', '3'],
- ['core', 'shareapi_default_remote_expire_date', 'no', 'yes'],
- ]);
- }
-
- self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
- }
-
- #[DataProvider('validateExpirationDateInternalProvider')]
- public function testValidateExpirationDateInternalEnforceValid($shareType): void {
- $future = new \DateTime('now', $this->dateTimeZone->getTimeZone());
- $future->add(new \DateInterval('P2D'));
- $future->setTime(1, 2, 3);
-
- $expected = clone $future;
- $expected->setTime(23, 59, 59);
-
- $share = $this->manager->newShare();
- $share->setShareType($shareType);
- $share->setExpirationDate($future);
-
- if ($shareType === IShare::TYPE_USER) {
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_enforce_internal_expire_date', 'no', 'yes'],
- ['core', 'shareapi_internal_expire_after_n_days', '7', '3'],
- ['core', 'shareapi_default_internal_expire_date', 'no', 'yes'],
- ]);
- } else {
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_enforce_remote_expire_date', 'no', 'yes'],
- ['core', 'shareapi_remote_expire_after_n_days', '7', '3'],
- ['core', 'shareapi_default_remote_expire_date', 'no', 'yes'],
- ]);
- }
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
- $hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($future) {
- return $data['expirationDate'] == $future;
- }));
-
- self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
-
- $this->assertEquals($expected, $share->getExpirationDate());
- }
-
- #[DataProvider('validateExpirationDateInternalProvider')]
- public function testValidateExpirationDateInternalNoDefault($shareType): void {
- $date = new \DateTime('now', $this->dateTimeZone->getTimeZone());
- $date->add(new \DateInterval('P5D'));
- $date->setTime(1, 2, 3);
-
- $expected = clone $date;
- $expected->setTime(23, 59, 59);
-
- $share = $this->manager->newShare();
- $share->setShareType($shareType);
- $share->setExpirationDate($date);
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
- $hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($expected) {
- return $data['expirationDate'] == $expected && $data['passwordSet'] === false;
- }));
-
- self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
-
- $this->assertEquals($expected, $share->getExpirationDate());
- }
-
- #[DataProvider('validateExpirationDateInternalProvider')]
- public function testValidateExpirationDateInternalNoDateNoDefault($shareType): void {
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
- $hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) {
- return $data['expirationDate'] === null && $data['passwordSet'] === true;
- }));
-
- $share = $this->manager->newShare();
- $share->setShareType($shareType);
- $share->setPassword('password');
-
- self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
-
- $this->assertNull($share->getExpirationDate());
- }
-
- #[DataProvider('validateExpirationDateInternalProvider')]
- public function testValidateExpirationDateInternalNoDateDefault($shareType): void {
- $share = $this->manager->newShare();
- $share->setShareType($shareType);
-
- $expected = new \DateTime('now', $this->timezone);
- $expected->setTime(23, 59, 59);
- $expected->add(new \DateInterval('P3D'));
- $expected->setTimezone(new \DateTimeZone(date_default_timezone_get()));
-
- if ($shareType === IShare::TYPE_USER) {
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_default_internal_expire_date', 'no', 'yes'],
- ['core', 'shareapi_internal_expire_after_n_days', '7', '3'],
- ['core', 'internal_defaultExpDays', '3', '3'],
- ]);
- } else {
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_default_remote_expire_date', 'no', 'yes'],
- ['core', 'shareapi_remote_expire_after_n_days', '7', '3'],
- ['core', 'remote_defaultExpDays', '3', '3'],
- ]);
- }
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
- $hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($expected) {
- return $data['expirationDate'] == $expected;
- }));
-
- self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
-
- $this->assertEquals($expected, $share->getExpirationDate());
- }
-
- #[DataProvider('validateExpirationDateInternalProvider')]
- public function testValidateExpirationDateInternalDefault($shareType): void {
- $future = new \DateTime('now', $this->timezone);
- $future->add(new \DateInterval('P5D'));
- $future->setTime(1, 2, 3);
-
- $expected = clone $future;
- $expected->setTime(23, 59, 59);
-
- $share = $this->manager->newShare();
- $share->setShareType($shareType);
- $share->setExpirationDate($future);
-
- if ($shareType === IShare::TYPE_USER) {
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_default_internal_expire_date', 'no', 'yes'],
- ['core', 'shareapi_internal_expire_after_n_days', '7', '3'],
- ['core', 'internal_defaultExpDays', '3', '1'],
- ]);
- } else {
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_default_remote_expire_date', 'no', 'yes'],
- ['core', 'shareapi_remote_expire_after_n_days', '7', '3'],
- ['core', 'remote_defaultExpDays', '3', '1'],
- ]);
- }
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
- $hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($expected) {
- return $data['expirationDate'] == $expected;
- }));
-
- self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
-
- $this->assertEquals($expected, $share->getExpirationDate());
- }
-
- #[DataProvider('validateExpirationDateInternalProvider')]
- public function testValidateExpirationDateInternalHookModification($shareType): void {
- $nextWeek = new \DateTime('now', $this->timezone);
- $nextWeek->add(new \DateInterval('P7D'));
- $nextWeek->setTime(23, 59, 59);
-
- $save = clone $nextWeek;
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
- $hookListener->expects($this->once())->method('listener')->willReturnCallback(function ($data): void {
- $data['expirationDate']->sub(new \DateInterval('P2D'));
- });
-
- $share = $this->manager->newShare();
- $share->setShareType($shareType);
- $share->setExpirationDate($nextWeek);
-
- self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
-
- $save->sub(new \DateInterval('P2D'));
- $this->assertEquals($save, $share->getExpirationDate());
- }
-
- #[DataProvider('validateExpirationDateInternalProvider')]
- public function testValidateExpirationDateInternalHookException($shareType): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Invalid date!');
-
- $nextWeek = new \DateTime();
- $nextWeek->add(new \DateInterval('P7D'));
- $nextWeek->setTime(23, 59, 59);
-
- $share = $this->manager->newShare();
- $share->setShareType($shareType);
- $share->setExpirationDate($nextWeek);
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
- $hookListener->expects($this->once())->method('listener')->willReturnCallback(function ($data): void {
- $data['accepted'] = false;
- $data['message'] = 'Invalid date!';
- });
-
- self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
- }
-
- #[DataProvider('validateExpirationDateInternalProvider')]
- public function testValidateExpirationDateInternalExistingShareNoDefault($shareType): void {
- $share = $this->manager->newShare();
- $share->setShareType($shareType);
- $share->setId('42')->setProviderId('foo');
-
- if ($shareType === IShare::TYPE_USER) {
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_default_internal_expire_date', 'no', 'yes'],
- ['core', 'shareapi_internal_expire_after_n_days', '7', '6'],
- ]);
- } else {
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_default_remote_expire_date', 'no', 'yes'],
- ['core', 'shareapi_remote_expire_after_n_days', '7', '6'],
- ]);
- }
-
- self::invokePrivate($this->manager, 'validateExpirationDateInternal', [$share]);
-
- $this->assertEquals(null, $share->getExpirationDate());
- }
-
- public function testValidateExpirationDateInPast(): void {
- $this->expectException(GenericShareException::class);
- $this->expectExceptionMessage('Expiration date is in the past');
-
- // Expire date in the past
- $past = new \DateTime();
- $past->sub(new \DateInterval('P1D'));
-
- $share = $this->manager->newShare();
- $share->setExpirationDate($past);
-
- self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
- }
-
- public function testValidateExpirationDateEnforceButNotSet(): void {
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Expiration date is enforced');
-
- $share = $this->manager->newShare();
- $share->setProviderId('foo')->setId('bar');
-
- $this->appConfig->method('getValueBool')
- ->willReturnMap([
- ['core', 'shareapi_default_expire_date', true],
- ['core', 'shareapi_enforce_expire_date', true],
- ]);
-
- self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
- }
-
- public function testValidateExpirationDateEnforceButNotEnabledAndNotSet(): void {
- $share = $this->manager->newShare();
- $share->setProviderId('foo')->setId('bar');
-
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_enforce_expire_date', 'no', 'yes'],
- ]);
-
- self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
-
- $this->assertNull($share->getExpirationDate());
- }
-
- public function testValidateExpirationDateEnforceButNotSetNewShare(): void {
- $share = $this->manager->newShare();
-
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_expire_after_n_days', '7', '3'],
- ['core', 'link_defaultExpDays', '3', '3'],
- ]);
-
- $this->appConfig->method('getValueBool')
- ->willReturnMap([
- ['core', 'shareapi_default_expire_date', true],
- ['core', 'shareapi_enforce_expire_date', true],
- ]);
-
- $expected = new \DateTime('now', $this->timezone);
- $expected->setTime(23, 59, 59);
- $expected->add(new \DateInterval('P3D'));
-
- self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
-
- $this->assertNotNull($share->getExpirationDate());
- $this->assertEquals($expected, $share->getExpirationDate());
- }
-
- public function testValidateExpirationDateEnforceRelaxedDefaultButNotSetNewShare(): void {
- $share = $this->manager->newShare();
-
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_expire_after_n_days', '7', '3'],
- ['core', 'link_defaultExpDays', '3', '1'],
- ]);
-
- $this->appConfig->method('getValueBool')
- ->willReturnMap([
- ['core', 'shareapi_default_expire_date', true],
- ['core', 'shareapi_enforce_expire_date', true],
- ]);
-
- $expected = new \DateTime('now', $this->timezone);
- $expected->setTime(23, 59, 59);
- $expected->add(new \DateInterval('P1D'));
-
- self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
-
- $this->assertNotNull($share->getExpirationDate());
- $this->assertEquals($expected, $share->getExpirationDate());
- }
-
- public function testValidateExpirationDateEnforceTooFarIntoFuture(): void {
- $this->expectException(GenericShareException::class);
- $this->expectExceptionMessage('Cannot set expiration date more than 3 days in the future');
-
- $future = new \DateTime();
- $future->add(new \DateInterval('P7D'));
-
- $share = $this->manager->newShare();
- $share->setExpirationDate($future);
-
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_expire_after_n_days', '7', '3'],
- ]);
-
- $this->appConfig->method('getValueBool')
- ->willReturnMap([
- ['core', 'shareapi_default_expire_date', true],
- ['core', 'shareapi_enforce_expire_date', true],
- ]);
-
- self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
- }
-
- public function testValidateExpirationDateEnforceValid(): void {
- $future = new \DateTime('now', $this->timezone);
- $future->add(new \DateInterval('P2D'));
- $future->setTime(1, 2, 3);
-
- $expected = clone $future;
- $expected->setTime(23, 59, 59);
-
- $share = $this->manager->newShare();
- $share->setExpirationDate($future);
-
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_expire_after_n_days', '7', '3'],
- ]);
-
- $this->appConfig->method('getValueBool')
- ->willReturnMap([
- ['core', 'shareapi_default_expire_date', true],
- ['core', 'shareapi_enforce_expire_date', true],
- ]);
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
- $hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($future) {
- return $data['expirationDate'] == $future;
- }));
-
- self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
-
- $this->assertEquals($expected, $share->getExpirationDate());
- }
-
- public function testValidateExpirationDateNoDefault(): void {
- $date = new \DateTime('now', $this->timezone);
- $date->add(new \DateInterval('P5D'));
- $date->setTime(1, 2, 3);
-
- $expected = clone $date;
- $expected->setTime(23, 59, 59);
- $expected->setTimezone(new \DateTimeZone(date_default_timezone_get()));
-
- $share = $this->manager->newShare();
- $share->setExpirationDate($date);
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
- $hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($expected) {
- return $data['expirationDate'] == $expected && $data['passwordSet'] === false;
- }));
-
- self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
-
- $this->assertEquals($expected, $share->getExpirationDate());
- }
-
- public function testValidateExpirationDateNoDateNoDefault(): void {
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
- $hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) {
- return $data['expirationDate'] === null && $data['passwordSet'] === true;
- }));
-
- $share = $this->manager->newShare();
- $share->setPassword('password');
-
- self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
-
- $this->assertNull($share->getExpirationDate());
- }
-
- public function testValidateExpirationDateNoDateDefault(): void {
- $share = $this->manager->newShare();
-
- $expected = new \DateTime('now', $this->timezone);
- $expected->add(new \DateInterval('P3D'));
- $expected->setTime(23, 59, 59);
- $expected->setTimezone(new \DateTimeZone(date_default_timezone_get()));
-
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_expire_after_n_days', '7', '3'],
- ['core', 'link_defaultExpDays', '3', '3'],
- ]);
-
- $this->appConfig->method('getValueBool')
- ->willReturnMap([
- ['core', 'shareapi_default_expire_date', true],
- ['core', 'shareapi_enforce_expire_date', false],
- ]);
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
- $hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($expected) {
- return $data['expirationDate'] == $expected;
- }));
-
- self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
-
- $this->assertEquals($expected, $share->getExpirationDate());
- }
-
- public function testValidateExpirationDateDefault(): void {
- $future = new \DateTime('now', $this->timezone);
- $future->add(new \DateInterval('P5D'));
- $future->setTime(1, 2, 3);
-
- $expected = clone $future;
- $expected->setTime(23, 59, 59);
- $expected->setTimezone(new \DateTimeZone(date_default_timezone_get()));
-
- $share = $this->manager->newShare();
- $share->setExpirationDate($future);
-
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_expire_after_n_days', '7', '3'],
- ['core', 'link_defaultExpDays', '3', '1'],
- ]);
-
- $this->appConfig->method('getValueBool')
- ->willReturnMap([
- ['core', 'shareapi_default_expire_date', true],
- ['core', 'shareapi_enforce_expire_date', false],
- ]);
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
- $hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($expected) {
- return $data['expirationDate'] == $expected;
- }));
-
- self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
-
- $this->assertEquals($expected, $share->getExpirationDate());
- }
-
- public function testValidateExpirationNegativeOffsetTimezone(): void {
- $this->timezone = new \DateTimeZone('Pacific/Tahiti');
- $future = new \DateTime();
- $future->add(new \DateInterval('P5D'));
-
- $expected = clone $future;
- $expected->setTimezone($this->timezone);
- $expected->setTime(23, 59, 59);
- $expected->setTimezone(new \DateTimeZone(date_default_timezone_get()));
-
- $share = $this->manager->newShare();
- $share->setExpirationDate($future);
-
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_expire_after_n_days', '7', '3'],
- ['core', 'link_defaultExpDays', '3', '1'],
- ]);
-
- $this->appConfig->method('getValueBool')
- ->willReturnMap([
- ['core', 'shareapi_default_expire_date', true],
- ['core', 'shareapi_enforce_expire_date', false],
- ]);
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
- $hookListener->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($expected) {
- return $data['expirationDate'] == $expected;
- }));
-
- self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
-
- $this->assertEquals($expected, $share->getExpirationDate());
- }
-
- public function testValidateExpirationDateHookModification(): void {
- $nextWeek = new \DateTime('now', $this->timezone);
- $nextWeek->add(new \DateInterval('P7D'));
-
- $save = clone $nextWeek;
- $save->setTime(23, 59, 59);
- $save->sub(new \DateInterval('P2D'));
- $save->setTimezone(new \DateTimeZone(date_default_timezone_get()));
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
- $hookListener->expects($this->once())->method('listener')->willReturnCallback(function ($data): void {
- $data['expirationDate']->sub(new \DateInterval('P2D'));
- });
-
- $share = $this->manager->newShare();
- $share->setExpirationDate($nextWeek);
-
- self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
-
- $this->assertEquals($save, $share->getExpirationDate());
- }
-
- public function testValidateExpirationDateHookException(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Invalid date!');
-
- $nextWeek = new \DateTime();
- $nextWeek->add(new \DateInterval('P7D'));
- $nextWeek->setTime(23, 59, 59);
-
- $share = $this->manager->newShare();
- $share->setExpirationDate($nextWeek);
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListener, 'listener');
- $hookListener->expects($this->once())->method('listener')->willReturnCallback(function ($data): void {
- $data['accepted'] = false;
- $data['message'] = 'Invalid date!';
- });
-
- self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
- }
-
- public function testValidateExpirationDateExistingShareNoDefault(): void {
- $share = $this->manager->newShare();
-
- $share->setId('42')->setProviderId('foo');
-
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_expire_after_n_days', '7', '6'],
- ]);
-
- $this->appConfig->method('getValueBool')
- ->willReturnMap([
- ['core', 'shareapi_default_expire_date', true],
- ['core', 'shareapi_enforce_expire_date', false],
- ]);
- self::invokePrivate($this->manager, 'validateExpirationDateLink', [$share]);
-
- $this->assertEquals(null, $share->getExpirationDate());
- }
-
- public function testUserCreateChecksShareWithGroupMembersOnlyDifferentGroups(): void {
- $this->expectException(GenericShareException::class);
- $this->expectExceptionMessage('Sharing is only allowed with group members');
-
- $node = $this->createMock(Folder::class);
- $node
- ->method('getId')
- ->willReturn(108);
- $node
- ->method('getPermissions')
- ->willReturn(Constants::PERMISSION_ALL);
- $node
- ->method('isShareable')
- ->willReturn(true);
-
- $userFolder = $this->createMock(Folder::class);
- $userFolder
- ->method('getById')
- ->with(108)
- ->willReturn([$node]);
- $userFolder
- ->method('getFirstNodeById')
- ->with(108)
- ->willReturn($node);
-
- $this->rootFolder
- ->method('getUserFolder')
- ->with('sharedBy')
- ->willReturn($userFolder);
-
- $share = $this->manager->newShare()
- ->setShareType(IShare::TYPE_USER)
- ->setSharedBy('sharedBy')
- ->setSharedWith('sharedWith')
- ->setNode($node)
- ->setPermissions(Constants::PERMISSION_ALL);
-
- $sharedBy = $this->createMock(IUser::class);
- $sharedWith = $this->createMock(IUser::class);
-
- $this->groupManager
- ->method('getUserGroupIds')
- ->willReturnMap(
- [
- [$sharedBy, ['group1']],
- [$sharedWith, ['group2']],
- ]
- );
-
- $this->userManager->method('userExists')->willReturnMap([
- ['sharedBy', true],
- ['sharedWith', true],
- ]);
-
- $this->userManager->method('get')->willReturnMap([
- ['sharedBy', $sharedBy],
- ['sharedWith', $sharedWith],
- ]);
-
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_enabled', 'yes', 'yes'],
- ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
- ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
- ]);
-
- self::invokePrivate($this->manager, 'generalChecks', [$share]);
- }
-
- public function testUserCreateChecksShareWithGroupMembersOnlySharedGroup(): void {
- $node = $this->createMock(Folder::class);
- $node
- ->method('getId')
- ->willReturn(108);
- $node
- ->method('getPermissions')
- ->willReturn(Constants::PERMISSION_ALL);
- $node
- ->method('isShareable')
- ->willReturn(true);
-
- $userFolder = $this->createMock(Folder::class);
- $userFolder
- ->method('getById')
- ->with(108)
- ->willReturn([$node]);
- $userFolder
- ->method('getFirstNodeById')
- ->with(108)
- ->willReturn($node);
-
- $this->rootFolder
- ->method('getUserFolder')
- ->with('sharedBy')
- ->willReturn($userFolder);
-
- $share = $this->manager->newShare()
- ->setShareType(IShare::TYPE_USER)
- ->setSharedBy('sharedBy')
- ->setSharedWith('sharedWith')
- ->setNode($node)
- ->setPermissions(Constants::PERMISSION_ALL);
-
- $sharedBy = $this->createMock(IUser::class);
- $sharedBy
- ->method('getUID')
- ->willReturn('sharedBy');
- $sharedWith = $this->createMock(IUser::class);
-
- $this->groupManager
- ->method('getUserGroupIds')
- ->willReturnMap(
- [
- [$sharedBy, ['group1', 'group3']],
- [$sharedWith, ['group2', 'group3']],
- ]
- );
-
- $this->userManager
- ->method('get')
- ->willReturnMap([
- ['sharedBy', $sharedBy],
- ['sharedWith', $sharedWith],
- ]);
-
- $this->userManager
- ->method('userExists')
- ->willReturnMap([
- ['sharedBy', true],
- ['sharedWith', true],
- ]);
-
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_enabled', 'yes', 'yes'],
- ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
- ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
- ]);
-
- $this->defaultProvider
- ->method('getSharesByPath')
- ->with($node)
- ->willReturn([]);
-
- self::invokePrivate($this->manager, 'generalChecks', [$share]);
- }
-
- public function testUserCreateChecksIdenticalShareExists(): void {
- $this->expectException(AlreadySharedException::class);
- $this->expectExceptionMessage('Sharing name.txt failed, because this item is already shared with the account user');
-
- $share = $this->manager->newShare();
- $share->setSharedWithDisplayName('user');
- $share2 = $this->manager->newShare();
-
- $sharedWith = $this->createMock(IUser::class);
- $path = $this->createMock(Node::class);
-
- $share->setSharedWith('sharedWith')->setNode($path)
- ->setProviderId('foo')->setId('bar');
-
- $share2->setSharedWith('sharedWith')->setNode($path)
- ->setProviderId('foo')->setId('baz');
-
- $this->defaultProvider
- ->method('getSharesByPath')
- ->with($path)
- ->willReturn([$share2]);
-
- $path->method('getName')
- ->willReturn('name.txt');
-
- self::invokePrivate($this->manager, 'userCreateChecks', [$share]);
- }
-
- public function testUserCreateChecksIdenticalPathSharedViaGroup(): void {
- $this->expectException(AlreadySharedException::class);
- $this->expectExceptionMessage('Sharing name2.txt failed, because this item is already shared with the account userName');
-
- $share = $this->manager->newShare();
-
- $sharedWith = $this->createMock(IUser::class);
- $sharedWith->method('getUID')->willReturn('sharedWith');
-
- $this->userManager->method('get')->with('sharedWith')->willReturn($sharedWith);
-
- $path = $this->createMock(Node::class);
-
- $share->setSharedWith('sharedWith')
- ->setNode($path)
- ->setShareOwner('shareOwner')
- ->setSharedWithDisplayName('userName')
- ->setProviderId('foo')
- ->setId('bar');
-
- $share2 = $this->manager->newShare();
- $share2->setShareType(IShare::TYPE_GROUP)
- ->setShareOwner('shareOwner2')
- ->setProviderId('foo')
- ->setId('baz')
- ->setSharedWith('group');
-
- $group = $this->createMock(IGroup::class);
- $group->method('inGroup')
- ->with($sharedWith)
- ->willReturn(true);
-
- $this->groupManager->method('get')->with('group')->willReturn($group);
-
- $this->defaultProvider
- ->method('getSharesByPath')
- ->with($path)
- ->willReturn([$share2]);
-
- $path->method('getName')
- ->willReturn('name2.txt');
-
- self::invokePrivate($this->manager, 'userCreateChecks', [$share]);
- }
-
- public function testUserCreateChecksIdenticalPathSharedViaDeletedGroup(): void {
- $share = $this->manager->newShare();
-
- $sharedWith = $this->createMock(IUser::class);
- $sharedWith->method('getUID')->willReturn('sharedWith');
-
- $this->userManager->method('get')->with('sharedWith')->willReturn($sharedWith);
-
- $path = $this->createMock(Node::class);
-
- $share->setSharedWith('sharedWith')
- ->setNode($path)
- ->setShareOwner('shareOwner')
- ->setProviderId('foo')
- ->setId('bar');
-
- $share2 = $this->manager->newShare();
- $share2->setShareType(IShare::TYPE_GROUP)
- ->setShareOwner('shareOwner2')
- ->setProviderId('foo')
- ->setId('baz')
- ->setSharedWith('group');
-
- $this->groupManager->method('get')->with('group')->willReturn(null);
-
- $this->defaultProvider
- ->method('getSharesByPath')
- ->with($path)
- ->willReturn([$share2]);
-
- $this->assertNull($this->invokePrivate($this->manager, 'userCreateChecks', [$share]));
- }
-
- public function testUserCreateChecksIdenticalPathNotSharedWithUser(): void {
- $node = $this->createMock(Folder::class);
- $node
- ->method('getId')
- ->willReturn(108);
- $node
- ->method('getPermissions')
- ->willReturn(Constants::PERMISSION_ALL);
- $node
- ->method('isShareable')
- ->willReturn(true);
-
- $userFolder = $this->createMock(Folder::class);
- $userFolder
- ->method('getById')
- ->with(108)
- ->willReturn([$node]);
- $userFolder
- ->method('getFirstNodeById')
- ->with(108)
- ->willReturn($node);
-
- $this->rootFolder
- ->method('getUserFolder')
- ->with('sharedBy')
- ->willReturn($userFolder);
-
- $share = $this->manager->newShare();
- $share
- ->setShareType(IShare::TYPE_USER)
- ->setSharedBy('sharedBy')
- ->setSharedWith('sharedWith')
- ->setNode($node)
- ->setPermissions(Constants::PERMISSION_ALL)
- ->setProviderId('foo')
- ->setId('bar');
-
- $sharedWith = $this->createMock(IUser::class);
- $sharedBy = $this->createMock(IUser::class);
- $sharedBy
- ->method('getUID')
- ->willReturn('sharedBy');
-
- $this->userManager
- ->method('get')
- ->willReturnMap([
- ['sharedWith', $sharedWith],
- ['sharedBy', $sharedBy],
- ]);
-
- $this->userManager
- ->method('userExists')
- ->willReturnMap([
- ['sharedWith', true],
- ['sharedBy', true],
- ]);
-
- $share2 = $this->manager->newShare();
- $share2->setShareType(IShare::TYPE_GROUP)
- ->setShareOwner('shareOwner2')
- ->setProviderId('foo')
- ->setId('baz');
-
- $group = $this->createMock(IGroup::class);
- $group->method('inGroup')
- ->with($sharedWith)
- ->willReturn(false);
-
- $this->groupManager->method('get')->with('group')->willReturn($group);
-
- $share2->setSharedWith('group');
-
- $this->defaultProvider
- ->method('getSharesByPath')
- ->with($node)
- ->willReturn([$share2]);
-
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_enabled', 'yes', 'yes'],
- ]);
-
- self::invokePrivate($this->manager, 'generalChecks', [$share]);
- }
-
- public function testGroupCreateChecksShareWithGroupMembersGroupSharingNotAllowed(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Group sharing is not allowed');
-
- $node = $this->createMock(Folder::class);
- $node
- ->method('getId')
- ->willReturn(108);
- $node
- ->method('getPermissions')
- ->willReturn(Constants::PERMISSION_ALL);
- $node
- ->method('isShareable')
- ->willReturn(true);
-
- $userFolder = $this->createMock(Folder::class);
- $userFolder
- ->method('getById')
- ->with(108)
- ->willReturn([$node]);
- $userFolder
- ->method('getFirstNodeById')
- ->with(108)
- ->willReturn($node);
-
- $this->rootFolder
- ->method('getUserFolder')
- ->with('user1')
- ->willReturn($userFolder);
-
- $this->userManager
- ->method('userExists')
- ->willReturnMap([
- ['user1', true],
- ]);
-
- $this->userManager
- ->method('get')
- ->willReturnMap([
- ['user1', $this->createMock(IUser::class)],
- ]);
-
- $this->groupManager
- ->method('groupExists')
- ->willReturnMap([
- ['group1', true],
- ]);
-
- $share = $this->manager->newShare()
- ->setShareType(IShare::TYPE_GROUP)
- ->setSharedBy('user1')
- ->setSharedWith('group1')
- ->setNode($node)
- ->setPermissions(Constants::PERMISSION_ALL);
-
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_enabled', 'yes', 'yes'],
- ['core', 'shareapi_allow_group_sharing', 'yes', 'no'],
- ]);
-
- self::invokePrivate($this->manager, 'generalChecks', [$share]);
- }
-
- public function testGroupCreateChecksShareWithGroupMembersOnlyNotInGroup(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Sharing is only allowed within your own groups');
-
- $node = $this->createMock(Folder::class);
- $node
- ->method('getId')
- ->willReturn(108);
- $node
- ->method('getPermissions')
- ->willReturn(Constants::PERMISSION_ALL);
- $node
- ->method('isShareable')
- ->willReturn(true);
-
- $userFolder = $this->createMock(Folder::class);
- $userFolder
- ->method('getById')
- ->with(108)
- ->willReturn([$node]);
- $userFolder
- ->method('getFirstNodeById')
- ->with(108)
- ->willReturn($node);
-
- $this->rootFolder
- ->method('getUserFolder')
- ->with('user')
- ->willReturn($userFolder);
-
- $share = $this->manager->newShare()
- ->setShareType(IShare::TYPE_GROUP)
- ->setSharedBy('user')
- ->setSharedWith('group')
- ->setNode($node)
- ->setPermissions(Constants::PERMISSION_ALL);
-
- $user = $this->createMock(IUser::class);
-
- $group = $this->createMock(IGroup::class);
- $group
- ->method('inGroup')
- ->with($user)
- ->willReturn(false);
-
- $this->groupManager
- ->method('groupExists')
- ->with('group')
- ->willReturn(true);
-
- $this->groupManager
- ->method('get')
- ->with('group')
- ->willReturn($group);
-
- $this->userManager
- ->method('get')
- ->with('user')
- ->willReturn($user);
-
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_enabled', 'yes', 'yes'],
- ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
- ['core', 'shareapi_allow_group_sharing', 'yes', 'yes'],
- ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
- ]);
-
- self::invokePrivate($this->manager, 'generalChecks', [$share]);
- }
-
- public function testGroupCreateChecksShareWithGroupMembersOnlyInGroup(): void {
- $node = $this->createMock(Folder::class);
- $node
- ->method('getId')
- ->willReturn(108);
- $node
- ->method('getPermissions')
- ->willReturn(Constants::PERMISSION_ALL);
- $node
- ->method('isShareable')
- ->willReturn(true);
-
- $userFolder = $this->createMock(Folder::class);
- $userFolder
- ->method('getById')
- ->with(108)
- ->willReturn([$node]);
- $userFolder
- ->method('getFirstNodeById')
- ->with(108)
- ->willReturn($node);
-
- $this->rootFolder
- ->method('getUserFolder')
- ->with('user')
- ->willReturn($userFolder);
-
- $share = $this->manager->newShare();
- $share
- ->setShareType(IShare::TYPE_GROUP)
- ->setSharedBy('user')
- ->setSharedWith('group')
- ->setNode($node)
- ->setPermissions(Constants::PERMISSION_ALL);
-
- $user = $this->createMock(IUser::class);
- $user
- ->method('getUID')
- ->willReturn('user');
- $group = $this->createMock(IGroup::class);
-
- $this->userManager
- ->method('get')
- ->with('user')
- ->willReturn($user);
-
- $this->groupManager
- ->method('groupExists')
- ->with('group')
- ->willReturn(true);
-
- $this->groupManager
- ->method('get')
- ->with('group')
- ->willReturn($group);
-
- $group
- ->method('inGroup')
- ->with($user)
- ->willReturn(true);
-
- $this->defaultProvider->method('getSharesByPath')
- ->with($node)
- ->willReturn([]);
-
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_enabled', 'yes', 'yes'],
- ['core', 'shareapi_only_share_with_group_members', 'no', 'yes'],
- ['core', 'shareapi_allow_group_sharing', 'yes', 'yes'],
- ['core', 'shareapi_only_share_with_group_members_exclude_group_list', '', '[]'],
- ]);
-
- self::invokePrivate($this->manager, 'generalChecks', [$share]);
- }
-
- public function testGroupCreateChecksPathAlreadySharedWithSameGroup(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Path is already shared with this group');
-
- $share = $this->manager->newShare();
-
- $path = $this->createMock(Node::class);
- $share->setSharedWith('sharedWith')
- ->setNode($path)
- ->setProviderId('foo')
- ->setId('bar');
-
- $share2 = $this->manager->newShare();
- $share2->setSharedWith('sharedWith')
- ->setProviderId('foo')
- ->setId('baz');
-
- $this->defaultProvider->method('getSharesByPath')
- ->with($path)
- ->willReturn([$share2]);
-
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_allow_group_sharing', 'yes', 'yes'],
- ]);
-
- self::invokePrivate($this->manager, 'groupCreateChecks', [$share]);
- }
-
- public function testGroupCreateChecksPathAlreadySharedWithDifferentGroup(): void {
- $share = $this->manager->newShare();
-
- $share->setSharedWith('sharedWith');
-
- $path = $this->createMock(Node::class);
- $share->setNode($path);
-
- $share2 = $this->manager->newShare();
- $share2->setSharedWith('sharedWith2');
-
- $this->defaultProvider->method('getSharesByPath')
- ->with($path)
- ->willReturn([$share2]);
-
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_allow_group_sharing', 'yes', 'yes'],
- ]);
-
- self::invokePrivate($this->manager, 'groupCreateChecks', [$share]);
- }
-
- public function testLinkCreateChecksNoLinkSharesAllowed(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Public link sharing is not allowed.');
-
- $node = $this->createMock(File::class);
- $node
- ->method('getId')
- ->willReturn(108);
- $node
- ->method('getPermissions')
- ->willReturn(Constants::PERMISSION_ALL);
- $node
- ->method('isShareable')
- ->willReturn(true);
-
- $userFolder = $this->createMock(Folder::class);
- $userFolder
- ->method('getById')
- ->with(108)
- ->willReturn([$node]);
- $userFolder
- ->method('getFirstNodeById')
- ->with(108)
- ->willReturn($node);
-
- $this->rootFolder
- ->method('getUserFolder')
- ->with('user1')
- ->willReturn($userFolder);
-
- $this->userManager
- ->method('userExists')
- ->willReturnMap([
- ['user1', true],
- ]);
-
- $this->userManager
- ->method('get')
- ->willReturnMap([
- ['user1', $this->createMock(IUser::class)],
- ]);
-
- $share = $this->manager->newShare()
- ->setShareType(IShare::TYPE_LINK)
- ->setSharedBy('user1')
- ->setNode($node)
- ->setPermissions(Constants::PERMISSION_READ | Constants::PERMISSION_SHARE);
-
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_enabled', 'yes', 'yes'],
- ['core', 'shareapi_allow_links', 'yes', 'no'],
- ]);
-
- self::invokePrivate($this->manager, 'generalChecks', [$share]);
- }
-
- public function testFileLinkCreateChecksNoPublicUpload(): void {
- $node = $this->createMock(File::class);
- $node
- ->method('getId')
- ->willReturn(108);
- $node
- ->method('getPermissions')
- ->willReturn(Constants::PERMISSION_ALL);
- $node
- ->method('isShareable')
- ->willReturn(true);
-
- $userFolder = $this->createMock(Folder::class);
- $userFolder
- ->method('getById')
- ->with(108)
- ->willReturn([$node]);
- $userFolder
- ->method('getFirstNodeById')
- ->with(108)
- ->willReturn($node);
-
- $this->rootFolder
- ->method('getUserFolder')
- ->with('user1')
- ->willReturn($userFolder);
-
- $user1 = $this->createMock(IUser::class);
- $user1
- ->method('getUID')
- ->willReturn('user1');
-
- $this->userManager
- ->method('userExists')
- ->willReturnMap([
- ['user1', true],
- ]);
-
- $this->userManager
- ->method('get')
- ->willReturnMap([
- ['user1', $user1],
- ]);
-
- $share = $this->manager->newShare()
- ->setShareType(IShare::TYPE_LINK)
- ->setSharedBy('user1')
- ->setNode($node)
- ->setPermissions(Constants::PERMISSION_READ | Constants::PERMISSION_UPDATE);
-
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_enabled', 'yes', 'yes'],
- ['core', 'shareapi_allow_links', 'yes', 'yes'],
- ['core', 'shareapi_allow_links_exclude_groups', '[]', '[]'],
- ['core', 'shareapi_allow_public_upload', 'yes', 'no']
- ]);
-
- self::invokePrivate($this->manager, 'generalChecks', [$share]);
- }
-
- public function testFolderLinkCreateChecksNoPublicUpload(): void {
- $this->expectException(GenericShareException::class);
- $this->expectExceptionMessage('Public upload is not allowed.');
-
- $node = $this->createMock(Folder::class);
- $node
- ->method('getId')
- ->willReturn(108);
- $node
- ->method('getPermissions')
- ->willReturn(Constants::PERMISSION_ALL);
- $node
- ->method('isShareable')
- ->willReturn(true);
-
- $userFolder = $this->createMock(Folder::class);
- $userFolder
- ->method('getById')
- ->with(108)
- ->willReturn([$node]);
- $userFolder
- ->method('getFirstNodeById')
- ->with(108)
- ->willReturn($node);
-
- $this->rootFolder
- ->method('getUserFolder')
- ->with('user1')
- ->willReturn($userFolder);
-
- $user1 = $this->createMock(IUser::class);
- $user1
- ->method('getUID')
- ->willReturn('user1');
-
- $this->userManager
- ->method('userExists')
- ->willReturnMap([
- ['user1', true],
- ]);
-
- $this->userManager
- ->method('get')
- ->willReturnMap([
- ['user1', $user1],
- ]);
-
- $share = $this->manager->newShare()
- ->setShareType(IShare::TYPE_LINK)
- ->setSharedBy('user1')
- ->setNode($node)
- ->setPermissions(Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE);
-
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_enabled', 'yes', 'yes'],
- ['core', 'shareapi_allow_links', 'yes', 'yes'],
- ['core', 'shareapi_allow_links_exclude_groups', '[]', '[]'],
- ['core', 'shareapi_allow_public_upload', 'yes', 'no'],
- ]);
-
- self::invokePrivate($this->manager, 'generalChecks', [$share]);
- }
-
- public function testLinkCreateChecksPublicUpload(): void {
- $node = $this->createMock(Folder::class);
- $node
- ->method('getId')
- ->willReturn(108);
- $node
- ->method('getPermissions')
- ->willReturn(Constants::PERMISSION_ALL);
- $node
- ->method('isShareable')
- ->willReturn(true);
-
- $userFolder = $this->createMock(Folder::class);
- $userFolder
- ->method('getById')
- ->with(108)
- ->willReturn([$node]);
- $userFolder
- ->method('getFirstNodeById')
- ->with(108)
- ->willReturn($node);
-
- $this->rootFolder
- ->method('getUserFolder')
- ->with('user1')
- ->willReturn($userFolder);
-
- $user1 = $this->createMock(IUser::class);
- $user1
- ->method('getUID')
- ->willReturn('user1');
-
- $this->userManager
- ->method('userExists')
- ->willReturnMap([
- ['user1', true],
- ]);
-
- $this->userManager
- ->method('get')
- ->willReturnMap([
- ['user1', $user1],
- ]);
-
- $share = $this->manager->newShare()
- ->setShareType(IShare::TYPE_LINK)
- ->setSharedBy('user1')
- ->setNode($node)
- ->setPermissions(Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE);
-
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_enabled', 'yes', 'yes'],
- ['core', 'shareapi_allow_links', 'yes', 'yes'],
- ['core', 'shareapi_allow_links_exclude_groups', '[]', '[]'],
- ['core', 'shareapi_allow_public_upload', 'yes', 'yes']
- ]);
-
- self::invokePrivate($this->manager, 'generalChecks', [$share]);
- }
-
- public function testLinkCreateChecksReadOnly(): void {
- $node = $this->createMock(Folder::class);
- $node
- ->method('getId')
- ->willReturn(108);
- $node
- ->method('getPermissions')
- ->willReturn(Constants::PERMISSION_ALL);
- $node
- ->method('isShareable')
- ->willReturn(true);
-
- $userFolder = $this->createMock(Folder::class);
- $userFolder
- ->method('getById')
- ->with(108)
- ->willReturn([$node]);
- $userFolder
- ->method('getFirstNodeById')
- ->with(108)
- ->willReturn($node);
-
- $this->rootFolder
- ->method('getUserFolder')
- ->with('user1')
- ->willReturn($userFolder);
-
- $this->userManager
- ->method('userExists')
- ->willReturnMap([
- ['user1', true],
- ]);
-
- $user1 = $this->createMock(IUser::class);
- $user1
- ->method('getUID')
- ->willReturn('user1');
-
- $this->userManager
- ->method('get')
- ->willReturnMap([
- ['user1', $user1],
- ]);
-
- $share = $this->manager->newShare()
- ->setShareType(IShare::TYPE_LINK)
- ->setSharedBy('user1')
- ->setNode($node)
- ->setPermissions(Constants::PERMISSION_READ);
-
- $this->config
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_enabled', 'yes', 'yes'],
- ['core', 'shareapi_allow_links', 'yes', 'yes'],
- ['core', 'shareapi_allow_links_exclude_groups', '[]', '[]'],
- ['core', 'shareapi_allow_public_upload', 'yes', 'yes']
- ]);
-
- self::invokePrivate($this->manager, 'generalChecks', [$share]);
- }
-
- public function testPathCreateChecksContainsSharedMount(): void {
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('You cannot share a folder that contains other shares');
-
- $path = $this->createMock(Folder::class);
- $path->method('getPath')->willReturn('path');
-
- $mount = $this->createMock(IMountPoint::class);
- $storage = $this->createMock(IStorage::class);
- $mount->method('getStorage')->willReturn($storage);
- $storage->method('instanceOfStorage')->with('\OCA\Files_Sharing\ISharedStorage')->willReturn(true);
-
- $this->mountManager->method('findIn')->with('path')->willReturn([$mount]);
-
- self::invokePrivate($this->manager, 'pathCreateChecks', [$path]);
- }
-
- public function testPathCreateChecksContainsNoSharedMount(): void {
- $path = $this->createMock(Folder::class);
- $path->method('getPath')->willReturn('path');
-
- $mount = $this->createMock(IMountPoint::class);
- $storage = $this->createMock(IStorage::class);
- $mount->method('getStorage')->willReturn($storage);
- $storage->method('instanceOfStorage')->with('\OCA\Files_Sharing\ISharedStorage')->willReturn(false);
-
- $this->mountManager->method('findIn')->with('path')->willReturn([$mount]);
-
- self::invokePrivate($this->manager, 'pathCreateChecks', [$path]);
- }
-
- #[\PHPUnit\Framework\Attributes\DoesNotPerformAssertions]
- public function testPathCreateChecksContainsNoFolder(): void {
- $path = $this->createMock(File::class);
-
- self::invokePrivate($this->manager, 'pathCreateChecks', [$path]);
- }
-
- public static function dataIsSharingDisabledForUser(): array {
- $legacyList = 'group1,group2';
- $jsonList = json_encode(['group1', 'group2']);
-
- return [
- // No group restrictions configured
- 'no restrictions, user without groups' => ['no', null, null, [], false],
- 'no restrictions, user with groups' => ['no', null, null, ['group1'], false],
-
- // Empty legacy list, converted to json
- 'block list, empty list, user without groups' => ['yes', '', json_encode(['']), [], false],
- 'allow list, empty list, user without groups' => ['allow', '', json_encode(['']), [], true],
- 'block list, empty list, user with groups' => ['yes', '', json_encode(['']), ['group1', 'group2'], false],
- 'allow list, empty list, user with groups' => ['allow', '', json_encode(['']), ['group1', 'group2'], true],
-
- // Legacy comma separated list, converted to json
- 'block list, legacy list, user without groups' => ['yes', $legacyList, $jsonList, [], false],
- 'allow list, legacy list, user without groups' => ['allow', $legacyList, $jsonList, [], true],
- 'block list, legacy list, user not in list' => ['yes', $legacyList, $jsonList, ['group3'], false],
- 'allow list, legacy list, user not in list' => ['allow', $legacyList, $jsonList, ['group3'], true],
- 'block list, legacy list, user partly in list' => ['yes', $legacyList, $jsonList, ['group1', 'group3'], true],
- 'allow list, legacy list, user partly in list' => ['allow', $legacyList, $jsonList, ['group1', 'group3'], false],
- 'block list, legacy list, user only in list' => ['yes', $legacyList, $jsonList, ['group1'], true],
- 'allow list, legacy list, user only in list' => ['allow', $legacyList, $jsonList, ['group1'], false],
-
- // Json encoded list
- 'block list, user without groups' => ['yes', $jsonList, null, [], false],
- 'allow list, user without groups' => ['allow', $jsonList, null, [], true],
- 'block list, user not in list' => ['yes', $jsonList, null, ['group3'], false],
- 'allow list, user not in list' => ['allow', $jsonList, null, ['group3'], true],
- 'block list, user partly in list' => ['yes', $jsonList, null, ['group1', 'group3'], true],
- 'allow list, user partly in list' => ['allow', $jsonList, null, ['group1', 'group3'], false],
- 'block list, user only in list' => ['yes', $jsonList, null, ['group2'], true],
- 'allow list, user only in list' => ['allow', $jsonList, null, ['group2'], false],
- ];
- }
-
- /**
- * @param string $excludeGroups Value of the `shareapi_exclude_groups` setting
- * @param ?string $groupList Value of the `shareapi_exclude_groups_list` setting
- * @param ?string $setList Expected value the group list is migrated to, or null if no migration is expected
- * @param string[] $groupIds Groups of the user
- * @param bool $expected Whether sharing is expected to be disabled for the user
- */
- #[DataProvider('dataIsSharingDisabledForUser')]
- public function testIsSharingDisabledForUser(string $excludeGroups, ?string $groupList, ?string $setList, array $groupIds, bool $expected): void {
- $user = $this->createMock(IUser::class);
-
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_exclude_groups', 'no', $excludeGroups],
- ['core', 'shareapi_exclude_groups_list', '', $groupList],
- ]);
-
- if ($setList !== null) {
- $this->config->expects($this->once())
- ->method('setAppValue')
- ->with('core', 'shareapi_exclude_groups_list', $setList);
- } else {
- $this->config->expects($this->never())
- ->method('setAppValue');
- }
-
- $this->groupManager->method('getUserGroupIds')
- ->with($user)
- ->willReturn($groupIds);
-
- $this->userManager->method('get')->with('user')->willReturn($user);
-
- $res = $this->manager->sharingDisabledForUser('user');
- $this->assertEquals($expected, $res);
- }
-
- public static function dataCanShare() {
- $data = [];
-
- /*
- * [expected, sharing enabled, disabled for user]
- */
-
- $data[] = [false, 'no', false];
- $data[] = [false, 'no', true];
- $data[] = [true, 'yes', false];
- $data[] = [false, 'yes', true];
-
- return $data;
- }
-
- /**
- *
- * @param bool $expected
- * @param string $sharingEnabled
- * @param bool $disabledForUser
- */
- #[DataProvider('dataCanShare')]
- public function testCanShare($expected, $sharingEnabled, $disabledForUser): void {
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_enabled', 'yes', $sharingEnabled],
- ]);
-
- $manager = $this->createManagerMock()
- ->onlyMethods(['sharingDisabledForUser'])
- ->getMock();
-
- $manager->method('sharingDisabledForUser')
- ->with('user1')
- ->willReturn($disabledForUser);
-
- $this->overwriteService(IManager::class, $manager);
- self::invokePrivate(Server::get(\OC\Core\Listener\RestrictInteractionListener::class), 'manager', [$manager]);
-
- $node = $this->createMock(File::class);
- $node
- ->method('getId')
- ->willReturn(108);
- $node
- ->method('getPermissions')
- ->willReturn(Constants::PERMISSION_ALL);
- $node
- ->method('isShareable')
- ->willReturn(true);
-
- $userFolder = $this->createMock(Folder::class);
- $userFolder
- ->method('getById')
- ->with(108)
- ->willReturn([$node]);
- $userFolder
- ->method('getFirstNodeById')
- ->with(108)
- ->willReturn($node);
-
- $this->rootFolder
- ->method('getUserFolder')
- ->with('user1')
- ->willReturn($userFolder);
-
- $user1 = $this->createMock(IUser::class);
- $user1
- ->method('getUID')
- ->willReturn('user1');
-
- $this->userManager
- ->method('get')
- ->with('user1')
- ->willReturn($user1);
-
- $share = $this->manager->newShare()
- ->setShareType(IShare::TYPE_USER)
- ->setSharedBy('user1')
- ->setSharedWith('user2')
- ->setNode($node)
- ->setPermissions(Constants::PERMISSION_READ | Constants::PERMISSION_SHARE);
-
- $this->userManager
- ->method('userExists')
- ->willReturnMap([
- ['user1', true],
- ['user2', true],
- ]);
-
- $exception = false;
- try {
- $res = self::invokePrivate($manager, 'generalChecks', [$share]);
- } catch (\Exception $e) {
- $exception = true;
- }
-
- $this->assertEquals($expected, !$exception);
-
- $this->restoreService(IManager::class);
- self::invokePrivate(Server::get(\OC\Core\Listener\RestrictInteractionListener::class), 'manager', [Server::get(IManager::class)]);
- }
-
- public function testCreateShareUser(): void {
- /** @var Manager&MockObject $manager */
- $manager = $this->createManagerMock()
- ->onlyMethods(['generalChecks', 'userCreateChecks', 'pathCreateChecks', 'validateExpirationDateInternal'])
- ->getMock();
-
- $shareOwner = $this->createMock(IUser::class);
- $shareOwner->method('getUID')->willReturn('shareOwner');
-
- $storage = $this->createMock(IStorage::class);
- $path = $this->createMock(File::class);
- $path->method('getOwner')->willReturn($shareOwner);
- $path->method('getName')->willReturn('target');
- $path->method('getStorage')->willReturn($storage);
-
- $share = $this->createShare(
- null,
- IShare::TYPE_USER,
- $path,
- 'sharedWith',
- 'sharedBy',
- null,
- Constants::PERMISSION_ALL);
-
- $manager->expects($this->once())
- ->method('generalChecks')
- ->with($share);
- $manager->expects($this->once())
- ->method('userCreateChecks')
- ->with($share);
- $manager->expects($this->once())
- ->method('pathCreateChecks')
- ->with($path);
- $manager->expects($this->once())
- ->method('validateExpirationDateInternal')
- ->with($share)
- ->willReturnArgument(0);
-
- $this->defaultProvider
- ->expects($this->once())
- ->method('create')
- ->with($share)
- ->willReturnArgument(0);
-
- $share->expects($this->once())
- ->method('setShareOwner')
- ->with('shareOwner');
- $share->expects($this->once())
- ->method('setTarget')
- ->with('/target');
-
- $manager->createShare($share);
- }
-
- public function testCreateShareUserAlreadySharedReusesExistingShare(): void {
- /** @var Manager&MockObject $manager */
- $manager = $this->createManagerMock()
- ->onlyMethods(['generalChecks', 'userCreateChecks', 'pathCreateChecks'])
- ->getMock();
-
- $shareOwner = $this->createMock(IUser::class);
- $shareOwner->method('getUID')->willReturn('shareOwner');
-
- $path = $this->createMock(File::class);
- $path->method('getOwner')->willReturn($shareOwner);
- $path->method('getName')->willReturn('target');
- $path->method('getStorage')->willReturn($this->createMock(IStorage::class));
-
- $share = $this->createShare(null, IShare::TYPE_USER, $path, 'sharedWith', 'sharedBy', null, Constants::PERMISSION_ALL);
- $existingShare = $this->createShare('42', IShare::TYPE_USER, $path, 'sharedWith', 'shareOwner', 'shareOwner', Constants::PERMISSION_READ);
-
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['files_sharing', 'hide_disabled_user_shares', 'yes', 'yes'],
- ]);
- $this->userManager->method('get')->willReturnMap([
- ['shareOwner', $this->createEnabledUser(true)],
- ]);
-
- $manager->method('userCreateChecks')
- ->willThrowException(new AlreadySharedException('Already shared', $existingShare));
-
- $this->defaultProvider->expects($this->never())
- ->method('create');
-
- $existingShare->expects($this->once())
- ->method('setNode')
- ->with($path);
-
- $this->assertSame($existingShare, $manager->createShare($share));
- }
-
- public function testCreateShareUserAlreadySharedByDisabledInitiator(): void {
- $this->expectException(AlreadySharedException::class);
-
- /** @var Manager&MockObject $manager */
- $manager = $this->createManagerMock()
- ->onlyMethods(['generalChecks', 'userCreateChecks', 'pathCreateChecks'])
- ->getMock();
-
- $shareOwner = $this->createMock(IUser::class);
- $shareOwner->method('getUID')->willReturn('shareOwner');
-
- $path = $this->createMock(File::class);
- $path->method('getOwner')->willReturn($shareOwner);
- $path->method('getName')->willReturn('target');
- $path->method('getStorage')->willReturn($this->createMock(IStorage::class));
-
- $share = $this->createShare(null, IShare::TYPE_USER, $path, 'sharedWith', 'shareOwner', null, Constants::PERMISSION_ALL);
- $existingShare = $this->createShare('42', IShare::TYPE_USER, $path, 'sharedWith', 'initiator', 'shareOwner', Constants::PERMISSION_READ);
-
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['files_sharing', 'hide_disabled_user_shares', 'yes', 'yes'],
- ]);
- $this->userManager->method('get')->willReturnMap([
- ['shareOwner', $this->createEnabledUser(true)],
- ['initiator', $this->createEnabledUser(false)],
- ]);
-
- $manager->method('userCreateChecks')
- ->willThrowException(new AlreadySharedException('Already shared', $existingShare));
-
- $this->defaultProvider->expects($this->never())
- ->method('create');
- $this->dispatcher->expects($this->never())
- ->method('dispatchTyped');
-
- $manager->createShare($share);
- }
-
- public function testCreateShareGroup(): void {
- $manager = $this->createManagerMock()
- ->onlyMethods(['generalChecks', 'groupCreateChecks', 'pathCreateChecks', 'validateExpirationDateInternal'])
- ->getMock();
-
- $shareOwner = $this->createMock(IUser::class);
- $shareOwner->method('getUID')->willReturn('shareOwner');
-
- $storage = $this->createMock(IStorage::class);
- $path = $this->createMock(File::class);
- $path->method('getOwner')->willReturn($shareOwner);
- $path->method('getName')->willReturn('target');
- $path->method('getStorage')->willReturn($storage);
-
- $share = $this->createShare(
- null,
- IShare::TYPE_GROUP,
- $path,
- 'sharedWith',
- 'sharedBy',
- null,
- Constants::PERMISSION_ALL);
-
- $manager->expects($this->once())
- ->method('generalChecks')
- ->with($share);
- $manager->expects($this->once())
- ->method('groupCreateChecks')
- ->with($share);
- $manager->expects($this->once())
- ->method('pathCreateChecks')
- ->with($path);
- $manager->expects($this->once())
- ->method('validateExpirationDateInternal')
- ->with($share)
- ->willReturnArgument(0);
-
- $this->defaultProvider
- ->expects($this->once())
- ->method('create')
- ->with($share)
- ->willReturnArgument(0);
-
- $share->expects($this->once())
- ->method('setShareOwner')
- ->with('shareOwner');
- $share->expects($this->once())
- ->method('setTarget')
- ->with('/target');
-
- $manager->createShare($share);
- }
-
- public function testCreateShareLink(): void {
- $manager = $this->createManagerMock()
- ->onlyMethods([
- 'generalChecks',
- 'pathCreateChecks',
- 'validateExpirationDateLink',
- 'verifyPassword',
- 'setLinkParent',
- ])
- ->getMock();
-
- $shareOwner = $this->createMock(IUser::class);
- $shareOwner->method('getUID')->willReturn('shareOwner');
-
- $storage = $this->createMock(IStorage::class);
- $path = $this->createMock(File::class);
- $path->method('getOwner')->willReturn($shareOwner);
- $path->method('getName')->willReturn('target');
- $path->method('getId')->willReturn(1);
- $path->method('getStorage')->willReturn($storage);
-
- $date = new \DateTime();
-
- $share = $this->manager->newShare();
- $share->setShareType(IShare::TYPE_LINK)
- ->setNode($path)
- ->setSharedBy('sharedBy')
- ->setPermissions(Constants::PERMISSION_ALL)
- ->setExpirationDate($date)
- ->setPassword('password');
-
- $manager->expects($this->once())
- ->method('generalChecks')
- ->with($share);
- $manager->expects($this->once())
- ->method('pathCreateChecks')
- ->with($path);
- $manager->expects($this->once())
- ->method('validateExpirationDateLink')
- ->with($share)
- ->willReturn($share);
- $manager->expects($this->once())
- ->method('verifyPassword')
- ->with('password');
- $manager->expects($this->once())
- ->method('setLinkParent')
- ->with($share);
-
- $this->secureRandom->method('generate')
- ->willReturn('token');
-
- $this->defaultProvider
- ->expects($this->once())
- ->method('create')
- ->with($share)
- ->willReturnCallback(function (Share $share) {
- return $share->setId(42);
- });
-
- $calls = [
- BeforeShareCreatedEvent::class,
- ShareCreatedEvent::class,
- ];
- $this->dispatcher->expects($this->exactly(2))
- ->method('dispatchTyped')
- ->willReturnCallback(function ($event) use (&$calls, $date, $path): void {
- $expected = array_shift($calls);
- $this->assertInstanceOf($expected, $event);
- $share = $event->getShare();
-
- $this->assertEquals(IShare::TYPE_LINK, $share->getShareType(), 'getShareType');
- $this->assertEquals($path, $share->getNode(), 'getNode');
- $this->assertEquals('sharedBy', $share->getSharedBy(), 'getSharedBy');
- $this->assertEquals(Constants::PERMISSION_ALL, $share->getPermissions(), 'getPermissions');
- $this->assertEquals($date, $share->getExpirationDate(), 'getExpirationDate');
- $this->assertEquals('hashed', $share->getPassword(), 'getPassword');
- $this->assertEquals('token', $share->getToken(), 'getToken');
-
- if ($expected === ShareCreatedEvent::class) {
- $this->assertEquals('42', $share->getId(), 'getId');
- $this->assertEquals('/target', $share->getTarget(), 'getTarget');
- }
- });
-
- /** @var IShare $share */
- $share = $manager->createShare($share);
-
- $this->assertSame('shareOwner', $share->getShareOwner());
- $this->assertEquals('/target', $share->getTarget());
- $this->assertSame($date, $share->getExpirationDate());
- $this->assertEquals('token', $share->getToken());
- $this->assertTrue($share->isPasswordHashed());
- $this->assertNotEmpty($share->getPassword());
- }
-
- public function testCreateShareMail(): void {
- $manager = $this->createManagerMock()
- ->onlyMethods([
- 'generalChecks',
- 'pathCreateChecks',
- 'validateExpirationDateLink',
- 'verifyPassword',
- 'setLinkParent',
- ])
- ->getMock();
-
- $shareOwner = $this->createMock(IUser::class);
- $shareOwner->method('getUID')->willReturn('shareOwner');
-
- $storage = $this->createMock(IStorage::class);
- $path = $this->createMock(File::class);
- $path->method('getOwner')->willReturn($shareOwner);
- $path->method('getName')->willReturn('target');
- $path->method('getId')->willReturn(1);
- $path->method('getStorage')->willReturn($storage);
-
- $share = $this->manager->newShare();
- $share->setShareType(IShare::TYPE_EMAIL)
- ->setNode($path)
- ->setSharedBy('sharedBy')
- ->setPermissions(Constants::PERMISSION_ALL);
-
- $manager->expects($this->once())
- ->method('generalChecks')
- ->with($share);
-
- $manager->expects($this->once())
- ->method('pathCreateChecks')
- ->with($path);
- $manager->expects($this->once())
- ->method('validateExpirationDateLink')
- ->with($share)
- ->willReturn($share);
- $manager->expects($this->once())
- ->method('verifyPassword');
- $manager->expects($this->once())
- ->method('setLinkParent');
-
- $this->secureRandom->method('generate')
- ->willReturn('token');
-
- $this->defaultProvider
- ->expects($this->once())
- ->method('create')
- ->with($share)
- ->willReturnCallback(function (Share $share) {
- return $share->setId(42);
- });
-
- $calls = [
- BeforeShareCreatedEvent::class,
- ShareCreatedEvent::class,
- ];
- $this->dispatcher->expects($this->exactly(2))
- ->method('dispatchTyped')
- ->willReturnCallback(function ($event) use (&$calls, $path): void {
- $expected = array_shift($calls);
- $this->assertInstanceOf($expected, $event);
- $share = $event->getShare();
-
- $this->assertEquals(IShare::TYPE_EMAIL, $share->getShareType(), 'getShareType');
- $this->assertEquals($path, $share->getNode(), 'getNode');
- $this->assertEquals('sharedBy', $share->getSharedBy(), 'getSharedBy');
- $this->assertEquals(Constants::PERMISSION_ALL, $share->getPermissions(), 'getPermissions');
- $this->assertNull($share->getExpirationDate(), 'getExpirationDate');
- $this->assertNull($share->getPassword(), 'getPassword');
- $this->assertEquals('token', $share->getToken(), 'getToken');
-
- if ($expected === ShareCreatedEvent::class) {
- $this->assertEquals('42', $share->getId(), 'getId');
- $this->assertEquals('/target', $share->getTarget(), 'getTarget');
- }
- });
-
- /** @var IShare $share */
- $share = $manager->createShare($share);
-
- $this->assertSame('shareOwner', $share->getShareOwner());
- $this->assertEquals('/target', $share->getTarget());
- $this->assertEquals('token', $share->getToken());
- }
-
- public function testCreateShareHookError(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('I won\'t let you share');
-
- $manager = $this->createManagerMock()
- ->onlyMethods([
- 'generalChecks',
- 'userCreateChecks',
- 'pathCreateChecks',
- 'validateExpirationDateInternal',
- ])
- ->getMock();
-
- $shareOwner = $this->createMock(IUser::class);
- $shareOwner->method('getUID')->willReturn('shareOwner');
-
- $storage = $this->createMock(IStorage::class);
- $path = $this->createMock(File::class);
- $path->method('getOwner')->willReturn($shareOwner);
- $path->method('getName')->willReturn('target');
- $path->method('getStorage')->willReturn($storage);
-
- $share = $this->createShare(
- null,
- IShare::TYPE_USER,
- $path,
- 'sharedWith',
- 'sharedBy',
- null,
- Constants::PERMISSION_ALL);
-
- $manager->expects($this->once())
- ->method('generalChecks')
- ->with($share);
- $manager->expects($this->once())
- ->method('userCreateChecks')
- ->with($share);
- $manager->expects($this->once())
- ->method('pathCreateChecks')
- ->with($path);
- $manager->expects($this->once())
- ->method('validateExpirationDateInternal')
- ->with($share)
- ->willReturnArgument(0);
-
- $share->expects($this->once())
- ->method('setShareOwner')
- ->with('shareOwner');
- $share->expects($this->once())
- ->method('setTarget')
- ->with('/target');
-
- // Pre share
- $this->dispatcher->expects($this->once())
- ->method('dispatchTyped')
- ->with(
- $this->isInstanceOf(BeforeShareCreatedEvent::class)
- )->willReturnCallback(function (BeforeShareCreatedEvent $e): void {
- $e->setError('I won\'t let you share!');
- $e->stopPropagation();
- }
- );
-
- $manager->createShare($share);
- }
-
- public function testCreateShareOfIncomingFederatedShare(): void {
- $manager = $this->createManagerMock()
- ->onlyMethods(['generalChecks', 'userCreateChecks', 'pathCreateChecks', 'validateExpirationDateInternal'])
- ->getMock();
-
- $shareOwner = $this->createMock(IUser::class);
- $shareOwner->method('getUID')->willReturn('shareOwner');
-
- $storage = $this->createMock(IStorage::class);
- $storage->method('instanceOfStorage')
- ->with('OCA\Files_Sharing\External\Storage')
- ->willReturn(true);
-
- $storage2 = $this->createMock(IStorage::class);
- $storage2->method('instanceOfStorage')
- ->with('OCA\Files_Sharing\External\Storage')
- ->willReturn(false);
-
- $path = $this->createMock(File::class);
- $path->expects($this->never())->method('getOwner');
- $path->method('getName')->willReturn('target');
- $path->method('getStorage')->willReturn($storage);
-
- $parent = $this->createMock(Folder::class);
- $parent->method('getStorage')->willReturn($storage);
-
- $parentParent = $this->createMock(Folder::class);
- $parentParent->method('getStorage')->willReturn($storage2);
- $parentParent->method('getOwner')->willReturn($shareOwner);
-
- $path->method('getParent')->willReturn($parent);
- $parent->method('getParent')->willReturn($parentParent);
-
- $share = $this->createShare(
- null,
- IShare::TYPE_USER,
- $path,
- 'sharedWith',
- 'sharedBy',
- null,
- Constants::PERMISSION_ALL);
-
- $manager->expects($this->once())
- ->method('generalChecks')
- ->with($share);
- $manager->expects($this->once())
- ->method('userCreateChecks')
- ->with($share);
- $manager->expects($this->once())
- ->method('pathCreateChecks')
- ->with($path);
- $manager->expects($this->once())
- ->method('validateExpirationDateInternal')
- ->with($share)
- ->willReturnArgument(0);
-
- $this->defaultProvider
- ->expects($this->once())
- ->method('create')
- ->with($share)
- ->willReturnArgument(0);
-
- $share->expects($this->once())
- ->method('setShareOwner')
- ->with('shareOwner');
- $share->expects($this->once())
- ->method('setTarget')
- ->with('/target');
-
- $manager->createShare($share);
- }
-
- public function testGetSharesBy(): void {
- $share = $this->manager->newShare();
-
- $node = $this->createMock(Folder::class);
-
- $this->defaultProvider->expects($this->once())
- ->method('getSharesBy')
- ->with(
- $this->equalTo('user'),
- $this->equalTo(IShare::TYPE_USER),
- $this->equalTo($node),
- $this->equalTo(true),
- $this->equalTo(1),
- $this->equalTo(1)
- )->willReturn([$share]);
-
- $shares = $this->manager->getSharesBy('user', IShare::TYPE_USER, $node, true, 1, 1);
-
- $this->assertCount(1, $shares);
- $this->assertSame($share, $shares[0]);
- }
-
- public function testGetSharesByOwnerless(): void {
- $mount = $this->createMock(IShareOwnerlessMount::class);
-
- $node = $this->createMock(Folder::class);
- $node
- ->expects($this->once())
- ->method('getMountPoint')
- ->willReturn($mount);
-
- $share = $this->manager->newShare();
- $share->setNode($node);
- $share->setShareType(IShare::TYPE_USER);
-
- $this->defaultProvider
- ->expects($this->once())
- ->method('getSharesByPath')
- ->with($this->equalTo($node))
- ->willReturn([$share]);
-
- $shares = $this->manager->getSharesBy('user', IShare::TYPE_USER, $node, true, 1, 1);
-
- $this->assertCount(1, $shares);
- $this->assertSame($share, $shares[0]);
- }
-
- public function testGetSharesByOfDisabledInitiatorAsShareOwner(): void {
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['files_sharing', 'hide_disabled_user_shares', 'yes', 'yes'],
- ]);
-
- $share = $this->manager->newShare();
- $share->setShareType(IShare::TYPE_USER)
- ->setShareOwner('owner')
- ->setSharedBy('initiator')
- ->setSharedWith('recipient');
-
- $this->userManager->method('get')->willReturnMap([
- ['owner', $this->createEnabledUser(true)],
- ['initiator', $this->createEnabledUser(false)],
- ]);
-
- $this->defaultProvider->expects($this->once())
- ->method('getSharesBy')
- ->willReturn([$share]);
-
- $shares = $this->manager->getSharesBy('owner', IShare::TYPE_USER, $this->createMock(Folder::class), true, -1, 0);
-
- $this->assertCount(1, $shares);
- $this->assertSame($share, $shares[0]);
- }
-
- public function testGetSharesByOfDisabledInitiatorAsOtherUser(): void {
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['files_sharing', 'hide_disabled_user_shares', 'yes', 'yes'],
- ]);
-
- $share = $this->manager->newShare();
- $share->setShareType(IShare::TYPE_USER)
- ->setShareOwner('owner')
- ->setSharedBy('initiator')
- ->setSharedWith('recipient');
-
- $this->userManager->method('get')->willReturnMap([
- ['owner', $this->createEnabledUser(true)],
- ['initiator', $this->createEnabledUser(false)],
- ['resharer', $this->createEnabledUser(true)],
- ]);
-
- $this->defaultProvider->expects($this->once())
- ->method('getSharesBy')
- ->willReturn([$share]);
-
- $shares = $this->manager->getSharesBy('resharer', IShare::TYPE_USER, $this->createMock(Folder::class), true, -1, 0);
-
- $this->assertCount(0, $shares);
- }
-
- public function testGetSharedWithOfDisabledInitiator(): void {
- $this->config->method('getAppValue')
- ->willReturnMap([
- ['files_sharing', 'hide_disabled_user_shares', 'yes', 'yes'],
- ]);
-
- $share = $this->manager->newShare();
- $share->setShareType(IShare::TYPE_USER)
- ->setShareOwner('owner')
- ->setSharedBy('initiator')
- ->setSharedWith('recipient');
-
- $this->userManager->method('get')->willReturnMap([
- ['owner', $this->createEnabledUser(true)],
- ['initiator', $this->createEnabledUser(false)],
- ]);
-
- $this->defaultProvider->expects($this->once())
- ->method('getSharedWith')
- ->willReturn([$share]);
-
- $shares = $this->manager->getSharedWith('recipient', IShare::TYPE_USER);
-
- $this->assertCount(0, $shares);
- }
-
- /**
- * Test to ensure we correctly remove expired link shares
- *
- * We have 8 Shares and we want the 3 first valid shares.
- * share 3-6 and 8 are expired. Thus at the end of this test we should
- * have received share 1,2 and 7. And from the manager. Share 3-6 should be
- * deleted (as they are evaluated). but share 8 should still be there.
- */
- public function testGetSharesByExpiredLinkShares(): void {
- $manager = $this->createManagerMock()
- ->onlyMethods(['deleteShare'])
- ->getMock();
-
- /** @var IShare[] $shares */
- $shares = [];
-
- /*
- * This results in an array of 8 IShare elements
- */
- for ($i = 0; $i < 8; $i++) {
- $share = $this->manager->newShare();
- $share->setId($i);
- $shares[] = $share;
- }
-
- $today = new \DateTime();
- $today->setTime(0, 0, 0);
-
- /*
- * Set the expiration date to today for some shares
- */
- $shares[2]->setExpirationDate($today);
- $shares[3]->setExpirationDate($today);
- $shares[4]->setExpirationDate($today);
- $shares[5]->setExpirationDate($today);
-
- /** @var IShare[] $i */
- $shares2 = [];
- for ($i = 0; $i < 8; $i++) {
- $shares2[] = clone $shares[$i];
- }
-
- $node = $this->createMock(File::class);
-
- /*
- * Simulate the getSharesBy call.
- */
- $this->defaultProvider
- ->method('getSharesBy')
- ->willReturnCallback(function ($uid, $type, $node, $reshares, $limit, $offset) use (&$shares2) {
- return array_slice($shares2, $offset, $limit);
- });
-
- /*
- * Simulate the deleteShare call.
- */
- $manager->method('deleteShare')
- ->willReturnCallback(function ($share) use (&$shares2): void {
- for ($i = 0; $i < count($shares2); $i++) {
- if ($shares2[$i]->getId() === $share->getId()) {
- array_splice($shares2, $i, 1);
- break;
- }
- }
- });
-
- $res = $manager->getSharesBy('user', IShare::TYPE_LINK, $node, true, 3, 0);
-
- $this->assertCount(3, $res);
- $this->assertEquals($shares[0]->getId(), $res[0]->getId());
- $this->assertEquals($shares[1]->getId(), $res[1]->getId());
- $this->assertEquals($shares[6]->getId(), $res[2]->getId());
-
- $this->assertCount(4, $shares2);
- $this->assertEquals(0, $shares2[0]->getId());
- $this->assertEquals(1, $shares2[1]->getId());
- $this->assertEquals(6, $shares2[2]->getId());
- $this->assertEquals(7, $shares2[3]->getId());
- $this->assertSame($today, $shares[3]->getExpirationDate());
- }
-
- public function testGetShareByToken(): void {
- $this->config
- ->expects($this->exactly(2))
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_allow_links', 'yes', 'yes'],
- ['files_sharing', 'hide_disabled_user_shares', 'no', 'no'],
- ]);
-
- $factory = $this->createMock(IProviderFactory::class);
-
- $manager = $this->createManager($factory);
-
- $share = $this->createMock(IShare::class);
-
- $factory->expects($this->once())
- ->method('getProviderForType')
- ->with(IShare::TYPE_LINK)
- ->willReturn($this->defaultProvider);
-
- $this->defaultProvider->expects($this->once())
- ->method('getShareByToken')
- ->with('token')
- ->willReturn($share);
-
- $ret = $manager->getShareByToken('token');
- $this->assertSame($share, $ret);
- }
-
- public function testGetShareByTokenRoom(): void {
- $this->config
- ->expects($this->exactly(2))
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_allow_links', 'yes', 'no'],
- ['files_sharing', 'hide_disabled_user_shares', 'no', 'no'],
- ]);
-
- $factory = $this->createMock(IProviderFactory::class);
-
- $manager = $this->createManager($factory);
-
- $share = $this->createMock(IShare::class);
-
- $roomShareProvider = $this->createMock(IShareProvider::class);
-
- $factory->expects($this->any())
- ->method('getProviderForType')
- ->willReturnCallback(function ($shareType) use ($roomShareProvider) {
- if ($shareType !== IShare::TYPE_ROOM) {
- throw new ProviderException();
- }
-
- return $roomShareProvider;
- });
-
- $roomShareProvider->expects($this->once())
- ->method('getShareByToken')
- ->with('token')
- ->willReturn($share);
-
- $ret = $manager->getShareByToken('token');
- $this->assertSame($share, $ret);
- }
-
- public function testGetShareByTokenWithException(): void {
- $this->config
- ->expects($this->exactly(2))
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_allow_links', 'yes', 'yes'],
- ['files_sharing', 'hide_disabled_user_shares', 'no', 'no'],
- ]);
-
- $factory = $this->createMock(IProviderFactory::class);
-
- $manager = $this->createManager($factory);
-
- $share = $this->createMock(IShare::class);
-
- $calls = [
- [IShare::TYPE_LINK],
- [IShare::TYPE_REMOTE],
- ];
- $factory->expects($this->exactly(2))
- ->method('getProviderForType')
- ->willReturnCallback(function () use (&$calls) {
- $expected = array_shift($calls);
- $this->assertEquals($expected, func_get_args());
- return $this->defaultProvider;
- });
-
- $this->defaultProvider->expects($this->exactly(2))
- ->method('getShareByToken')
- ->with('token')
- ->willReturnOnConsecutiveCalls(
- $this->throwException(new ShareNotFound()),
- $share
- );
-
- $ret = $manager->getShareByToken('token');
- $this->assertSame($share, $ret);
- }
-
- public function testGetShareByTokenHideDisabledUser(): void {
- $this->expectException(ShareNotFound::class);
- $this->expectExceptionMessage('The requested share does not exist anymore');
-
- $this->config
- ->expects($this->exactly(2))
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_allow_links', 'yes', 'yes'],
- ['files_sharing', 'hide_disabled_user_shares', 'yes', 'yes'],
- ]);
-
- $this->l->expects($this->once())
- ->method('t')
- ->willReturnArgument(0);
-
- $manager = $this->createManagerMock()
- ->onlyMethods(['deleteShare'])
- ->getMock();
-
- $date = new \DateTime();
- $date->setTime(0, 0, 0);
- $date->add(new \DateInterval('P2D'));
- $share = $this->manager->newShare();
- $share->setExpirationDate($date);
- $share->setShareOwner('owner');
- $share->setSharedBy('sharedBy');
-
- $sharedBy = $this->createMock(IUser::class);
- $owner = $this->createMock(IUser::class);
-
- $this->userManager->method('get')->willReturnMap([
- ['sharedBy', $sharedBy],
- ['owner', $owner],
- ]);
-
- $owner->expects($this->once())
- ->method('isEnabled')
- ->willReturn(true);
- $sharedBy->expects($this->once())
- ->method('isEnabled')
- ->willReturn(false);
-
- $this->defaultProvider->expects($this->once())
- ->method('getShareByToken')
- ->with('expiredToken')
- ->willReturn($share);
-
- $manager->expects($this->never())
- ->method('deleteShare');
-
- $manager->getShareByToken('expiredToken');
- }
-
- public function testGetShareByTokenExpired(): void {
- $this->expectException(ShareNotFound::class);
- $this->expectExceptionMessage('The requested share does not exist anymore');
-
- $this->config
- ->expects($this->once())
- ->method('getAppValue')
- ->with('core', 'shareapi_allow_links', 'yes')
- ->willReturn('yes');
-
- $this->l->expects($this->once())
- ->method('t')
- ->willReturnArgument(0);
-
- $manager = $this->createManagerMock()
- ->onlyMethods(['deleteShare'])
- ->getMock();
-
- $date = new \DateTime();
- $date->setTime(0, 0, 0);
- $share = $this->manager->newShare();
- $share->setExpirationDate($date);
-
- $this->defaultProvider->expects($this->once())
- ->method('getShareByToken')
- ->with('expiredToken')
- ->willReturn($share);
-
- $manager->expects($this->once())
- ->method('deleteShare')
- ->with($this->equalTo($share));
-
- $manager->getShareByToken('expiredToken');
- }
-
- public function testGetShareByTokenNotExpired(): void {
- $this->config
- ->expects($this->exactly(2))
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_allow_links', 'yes', 'yes'],
- ['files_sharing', 'hide_disabled_user_shares', 'no', 'no'],
- ]);
-
- $date = new \DateTime();
- $date->setTime(0, 0, 0);
- $date->add(new \DateInterval('P2D'));
- $share = $this->manager->newShare();
- $share->setExpirationDate($date);
-
- $this->defaultProvider->expects($this->once())
- ->method('getShareByToken')
- ->with('expiredToken')
- ->willReturn($share);
-
- $res = $this->manager->getShareByToken('expiredToken');
-
- $this->assertSame($share, $res);
- }
-
- public function testGetShareByTokenWithPublicLinksDisabled(): void {
- $this->expectException(ShareNotFound::class);
-
- $this->config
- ->expects($this->once())
- ->method('getAppValue')
- ->with('core', 'shareapi_allow_links', 'yes')
- ->willReturn('no');
- $this->manager->getShareByToken('validToken');
- }
-
- public function testGetShareByTokenPublicUploadDisabled(): void {
- $this->config
- ->expects($this->exactly(5))
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_allow_links', 'yes', 'yes'],
- ['core', 'shareapi_allow_public_upload', 'yes', 'no'],
- ['files_sharing', 'hide_disabled_user_shares', 'no', 'no'],
- ['core', 'shareapi_allow_links_exclude_groups', '[]', '[]'],
- ]);
-
- $share = $this->manager->newShare();
- $share->setShareType(IShare::TYPE_LINK)
- ->setPermissions(Constants::PERMISSION_READ | Constants::PERMISSION_CREATE | Constants::PERMISSION_UPDATE);
- $share->setSharedWith('sharedWith');
- $share->setShareOwner('shareOwner');
- $folder = $this->createMock(\OC\Files\Node\Folder::class);
- $share->setNode($folder);
-
- $shareOwner = $this->createMock(IUser::class);
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('shareOwner')
- ->willReturn($shareOwner);
-
- $this->defaultProvider->expects($this->once())
- ->method('getShareByToken')
- ->willReturn('validToken')
- ->willReturn($share);
-
- $res = $this->manager->getShareByToken('validToken');
-
- $this->assertSame(Constants::PERMISSION_READ, $res->getPermissions());
- }
-
- public function testGetShareByTokenShareOwnerExcludedFromLinkShares(): void {
- $this->expectException(ShareNotFound::class);
- $this->expectExceptionMessage('The requested share does not exist anymore');
-
- $this->config
- ->expects($this->exactly(4))
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_allow_links', 'yes', 'yes'],
- ['files_sharing', 'hide_disabled_user_shares', 'no', 'no'],
- ['core', 'shareapi_allow_links_exclude_groups', '[]', '["excludedGroup"]'],
- ]);
-
- $this->l->expects($this->once())
- ->method('t')
- ->willReturnArgument(0);
-
- $share = $this->manager->newShare();
- $share->setShareType(IShare::TYPE_LINK)
- ->setPermissions(Constants::PERMISSION_READ);
- $share->setShareOwner('shareOwner');
- $file = $this->createMock(File::class);
- $share->setNode($file);
-
- $shareOwner = $this->createMock(IUser::class);
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('shareOwner')
- ->willReturn($shareOwner);
-
- $this->groupManager->expects($this->once())
- ->method('getUserGroupIds')
- ->with($shareOwner)
- ->willReturn(['excludedGroup', 'otherGroup']);
-
- $this->defaultProvider->expects($this->once())
- ->method('getShareByToken')
- ->with('token')
- ->willReturn($share);
-
- $this->manager->getShareByToken('token');
- }
-
- public function testGetShareByTokenShareOwnerNotExcludedFromLinkShares(): void {
- $this->config
- ->expects($this->exactly(4))
- ->method('getAppValue')
- ->willReturnMap([
- ['core', 'shareapi_allow_links', 'yes', 'yes'],
- ['files_sharing', 'hide_disabled_user_shares', 'no', 'no'],
- ['core', 'shareapi_allow_links_exclude_groups', '[]', '["excludedGroup"]'],
- ]);
-
- $share = $this->manager->newShare();
- $share->setShareType(IShare::TYPE_LINK)
- ->setPermissions(Constants::PERMISSION_READ);
- $share->setShareOwner('shareOwner');
- $file = $this->createMock(File::class);
- $share->setNode($file);
-
- $shareOwner = $this->createMock(IUser::class);
- $this->userManager->expects($this->once())
- ->method('get')
- ->with('shareOwner')
- ->willReturn($shareOwner);
-
- $this->groupManager->expects($this->once())
- ->method('getUserGroupIds')
- ->with($shareOwner)
- ->willReturn(['allowedGroup', 'otherGroup']);
-
- $this->defaultProvider->expects($this->once())
- ->method('getShareByToken')
- ->with('token')
- ->willReturn($share);
-
- $res = $this->manager->getShareByToken('token');
-
- $this->assertSame($share, $res);
- }
-
- public function testCheckPasswordNoLinkShare(): void {
- $share = $this->createMock(IShare::class);
- $share->method('getShareType')->willReturn(IShare::TYPE_USER);
- $this->assertFalse($this->manager->checkPassword($share, 'password'));
- }
-
- public function testCheckPasswordNoPassword(): void {
- $share = $this->createMock(IShare::class);
- $share->method('getShareType')->willReturn(IShare::TYPE_LINK);
- $this->assertFalse($this->manager->checkPassword($share, 'password'));
-
- $share->method('getPassword')->willReturn('password');
- $share->method('isPasswordProtected')->willReturn(true);
- $this->assertFalse($this->manager->checkPassword($share, null));
- }
-
- public function testCheckPasswordInvalidPassword(): void {
- $passwordHash = Server::get(IHasher::class)->hash('password');
-
- $share = $this->manager->newShare()
- ->setShareType(IShare::TYPE_LINK)
- ->setPasswordHash($passwordHash);
-
- $this->assertFalse($this->manager->checkPassword($share, 'invalidpassword'));
- }
-
- public function testCheckPasswordValidPassword(): void {
- $passwordHash = Server::get(IHasher::class)->hash('password');
-
- $share = $this->manager->newShare()
- ->setShareType(IShare::TYPE_LINK)
- ->setPasswordHash($passwordHash);
-
- $this->assertTrue($this->manager->checkPassword($share, 'password'));
- }
-
- public static function dataHasherAlgorithm(): array {
- $algorithms = [
- PASSWORD_BCRYPT,
- ];
-
- if (\defined('PASSWORD_ARGON2I')) {
- $algorithms[] = PASSWORD_ARGON2I;
- }
-
- if (\defined('PASSWORD_ARGON2ID')) {
- $algorithms[] = PASSWORD_ARGON2ID;
- }
-
- return array_map(static fn (string $algorithm): array => [$algorithm], $algorithms);
- }
-
- #[DataProvider('dataHasherAlgorithm')]
- public function testCheckPasswordUpdateShareNoRehash(string $algorithm): void {
- $hasher = Server::get(IHasher::class);
- $this->invokePrivate($hasher, 'forcedAlgorithm', [$algorithm]);
-
- $passwordHash = $hasher->hash('password');
- $this->assertEquals((match ($algorithm) {
- PASSWORD_ARGON2ID => 3,
- PASSWORD_ARGON2I => 2,
- PASSWORD_BCRYPT => 1,
- }), (int)(explode('|', $passwordHash)[0]));
-
- $share = $this->manager->newShare()
- ->setShareType(IShare::TYPE_LINK)
- ->setPasswordHash($passwordHash);
-
- $this->defaultProvider->expects($this->never())->method('update');
-
- $this->assertTrue($this->manager->checkPassword($share, 'password'));
-
- $this->invokePrivate($hasher, 'forcedAlgorithm', [null]);
- }
-
- #[DataProvider('dataHasherAlgorithm')]
- public function testCheckPasswordUpdateShare(string $algorithm): void {
- $hasher = Server::get(IHasher::class);
- $this->invokePrivate($hasher, 'forcedAlgorithm', [$algorithm]);
-
- $passwordHash = $hasher->hash('password');
- $this->assertEquals((match ($algorithm) {
- PASSWORD_ARGON2ID => 3,
- PASSWORD_ARGON2I => 2,
- PASSWORD_BCRYPT => 1,
- }), (int)(explode('|', $passwordHash)[0]));
-
- $previousHasherOptions = $this->invokePrivate($hasher, 'options');
-
- // Make sure the hasher wants to rehash the password
- $this->invokePrivate(
- $hasher,
- 'options',
- [
- [
- 'threads' => $previousHasherOptions['threads'] ?? 1,
- 'memory_cost' => ($previousHasherOptions['memory_cost'] ?? PASSWORD_ARGON2_DEFAULT_MEMORY_COST) + 1,
- 'time_cost' => ($previousHasherOptions['time_cost'] ?? PASSWORD_ARGON2_DEFAULT_TIME_COST) + 1,
- 'cost' => ($previousHasherOptions['cost'] ?? PASSWORD_BCRYPT_DEFAULT_COST) + 1,
- ],
- ],
- );
-
- $share = $this->manager->newShare()
- ->setShareType(IShare::TYPE_LINK)
- ->setPasswordHash($passwordHash);
-
- $this->defaultProvider->expects($this->once())
- ->method('update')
- ->with($this->callback(function (IShare $share) use ($passwordHash) {
- return $share->getPassword() !== null && $share->isPasswordHashed() && $share->getPassword() !== $passwordHash;
- }));
-
- $this->assertTrue($this->manager->checkPassword($share, 'password'));
-
- $this->invokePrivate($hasher, 'options', [$previousHasherOptions]);
- $this->invokePrivate($hasher, 'forcedAlgorithm', [null]);
- }
-
- public function testUpdateShareCantChangeShareType(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Cannot change share type');
-
- $manager = $this->createManagerMock()
- ->onlyMethods([
- 'getShareById'
- ])
- ->getMock();
-
- $originalShare = $this->manager->newShare();
- $originalShare->setShareType(IShare::TYPE_GROUP);
-
- $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
-
- $share = $this->manager->newShare();
- $attrs = $this->manager->newShare()->newAttributes();
- $attrs->setAttribute('app1', 'perm1', true);
- $share->setProviderId('foo')
- ->setId('42')
- ->setShareType(IShare::TYPE_USER);
-
- $manager->updateShare($share);
- }
-
- public function testUpdateShareCantChangeRecipientForGroupShare(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Can only update recipient on user shares');
-
- $manager = $this->createManagerMock()
- ->onlyMethods([
- 'getShareById'
- ])
- ->getMock();
-
- $originalShare = $this->manager->newShare();
- $originalShare->setShareType(IShare::TYPE_GROUP)
- ->setSharedWith('origGroup');
-
- $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
-
- $share = $this->manager->newShare();
- $share->setProviderId('foo')
- ->setId('42')
- ->setShareType(IShare::TYPE_GROUP)
- ->setSharedWith('newGroup');
-
- $manager->updateShare($share);
- }
-
- public function testUpdateShareCantShareWithOwner(): void {
- $this->expectException(\Exception::class);
- $this->expectExceptionMessage('Cannot share with the share owner');
-
- $manager = $this->createManagerMock()
- ->onlyMethods([
- 'getShareById'
- ])
- ->getMock();
-
- $originalShare = $this->manager->newShare();
- $originalShare->setShareType(IShare::TYPE_USER)
- ->setSharedWith('sharedWith');
-
- $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
-
- $share = $this->manager->newShare();
- $share->setProviderId('foo')
- ->setId('42')
- ->setShareType(IShare::TYPE_USER)
- ->setSharedWith('newUser')
- ->setShareOwner('newUser');
-
- $manager->updateShare($share);
- }
-
- public function testUpdateShareUser(): void {
- $this->userManager->expects($this->any())->method('userExists')->willReturn(true);
-
- $manager = $this->createManagerMock()
- ->onlyMethods([
- 'getShareById',
- 'generalChecks',
- 'userCreateChecks',
- 'pathCreateChecks',
- ])
- ->getMock();
-
- $originalShare = $this->manager->newShare();
- $originalShare->setShareType(IShare::TYPE_USER)
- ->setSharedWith('origUser')
- ->setPermissions(1);
-
- $node = $this->createMock(File::class);
- $node->method('getId')->willReturn(100);
- $node->method('getPath')->willReturn('/newUser/files/myPath');
-
- $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
-
- $share = $this->manager->newShare();
- $attrs = $this->manager->newShare()->newAttributes();
- $attrs->setAttribute('app1', 'perm1', true);
- $share->setProviderId('foo')
- ->setId('42')
- ->setShareType(IShare::TYPE_USER)
- ->setSharedWith('origUser')
- ->setShareOwner('newUser')
- ->setSharedBy('sharer')
- ->setPermissions(31)
- ->setAttributes($attrs)
- ->setNode($node);
-
- $this->defaultProvider->expects($this->once())
- ->method('update')
- ->with($share)
- ->willReturn($share);
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
- $hookListener->expects($this->never())->method('post');
-
- $this->rootFolder->method('getUserFolder')->with('newUser')->willReturnSelf();
- $this->rootFolder->method('getRelativePath')->with('/newUser/files/myPath')->willReturn('/myPath');
-
- $hookListener2 = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener2, 'post');
- $hookListener2->expects($this->once())->method('post')->with([
- 'itemType' => 'file',
- 'itemSource' => 100,
- 'shareType' => IShare::TYPE_USER,
- 'shareWith' => 'origUser',
- 'uidOwner' => 'sharer',
- 'permissions' => 31,
- 'path' => '/myPath',
- 'attributes' => $attrs->toArray(),
- ]);
-
- $manager->updateShare($share);
- }
-
- public function testUpdateShareGroup(): void {
- $manager = $this->createManagerMock()
- ->onlyMethods([
- 'getShareById',
- 'generalChecks',
- 'groupCreateChecks',
- 'pathCreateChecks',
- ])
- ->getMock();
-
- $originalShare = $this->manager->newShare();
- $originalShare->setShareType(IShare::TYPE_GROUP)
- ->setSharedWith('origUser')
- ->setPermissions(31);
-
- $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
-
- $node = $this->createMock(File::class);
-
- $share = $this->manager->newShare();
- $share->setProviderId('foo')
- ->setId('42')
- ->setShareType(IShare::TYPE_GROUP)
- ->setSharedWith('origUser')
- ->setShareOwner('owner')
- ->setNode($node)
- ->setPermissions(31);
-
- $this->defaultProvider->expects($this->once())
- ->method('update')
- ->with($share)
- ->willReturn($share);
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
- $hookListener->expects($this->never())->method('post');
-
- $hookListener2 = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener2, 'post');
- $hookListener2->expects($this->never())->method('post');
-
- $manager->updateShare($share);
- }
-
- public function testUpdateShareLink(): void {
- $manager = $this->createManagerMock()
- ->onlyMethods([
- 'getShareById',
- 'generalChecks',
- 'pathCreateChecks',
- 'verifyPassword',
- 'validateExpirationDateLink',
- ])
- ->getMock();
-
- $originalShare = $this->manager->newShare();
- $originalShare->setShareType(IShare::TYPE_LINK)
- ->setPermissions(15);
-
- $tomorrow = new \DateTime();
- $tomorrow->setTime(0, 0, 0);
- $tomorrow->add(new \DateInterval('P1D'));
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(100);
-
- $share = $this->manager->newShare();
- $share->setProviderId('foo')
- ->setId('42')
- ->setShareType(IShare::TYPE_LINK)
- ->setToken('token')
- ->setSharedBy('owner')
- ->setShareOwner('owner')
- ->setPassword('password')
- ->setExpirationDate($tomorrow)
- ->setNode($file)
- ->setPermissions(15);
-
- $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
- $manager->expects($this->once())->method('validateExpirationDateLink')->with($share);
- $manager->expects($this->once())->method('verifyPassword')->with('password');
-
- $this->defaultProvider->expects($this->once())
- ->method('update')
- ->with($share)
- ->willReturn($share);
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
- $hookListener->expects($this->once())->method('post')->with([
- 'itemType' => 'file',
- 'itemSource' => 100,
- 'date' => $tomorrow,
- 'uidOwner' => 'owner',
- ]);
-
- $hookListener2 = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
- $hookListener2->expects($this->once())->method('post')->with([
- 'itemType' => 'file',
- 'itemSource' => 100,
- 'uidOwner' => 'owner',
- 'token' => 'token',
- 'disabled' => false,
- ]);
-
- $hookListener3 = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
- $hookListener3->expects($this->never())->method('post');
-
- $manager->updateShare($share);
- }
-
- public function testUpdateShareLinkEnableSendPasswordByTalkWithNoPassword(): void {
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Cannot enable sending the password by Talk with an empty password');
-
- $manager = $this->createManagerMock()
- ->onlyMethods([
- 'getShareById',
- 'generalChecks',
- 'pathCreateChecks',
- 'verifyPassword',
- 'validateExpirationDateLink',
- ])
- ->getMock();
-
- $originalShare = $this->manager->newShare();
- $originalShare->setShareType(IShare::TYPE_LINK)
- ->setPermissions(15);
-
- $tomorrow = new \DateTime();
- $tomorrow->setTime(0, 0, 0);
- $tomorrow->add(new \DateInterval('P1D'));
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(100);
-
- $share = $this->manager->newShare();
- $share->setProviderId('foo')
- ->setId('42')
- ->setShareType(IShare::TYPE_LINK)
- ->setToken('token')
- ->setSharedBy('owner')
- ->setShareOwner('owner')
- ->setPassword(null)
- ->setSendPasswordByTalk(true)
- ->setExpirationDate($tomorrow)
- ->setNode($file)
- ->setPermissions(15);
-
- $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
- $manager->expects($this->once())->method('generalChecks')->with($share);
- $manager->expects($this->never())->method('verifyPassword');
- $manager->expects($this->never())->method('pathCreateChecks');
- $manager->expects($this->never())->method('validateExpirationDateLink');
-
- $this->defaultProvider->expects($this->never())
- ->method('update');
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
- $hookListener->expects($this->never())->method('post');
-
- $hookListener2 = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
- $hookListener2->expects($this->never())->method('post');
-
- $hookListener3 = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
- $hookListener3->expects($this->never())->method('post');
-
- $manager->updateShare($share);
- }
-
- public function testUpdateShareMail(): void {
- $manager = $this->createManagerMock()
- ->onlyMethods([
- 'getShareById',
- 'generalChecks',
- 'verifyPassword',
- 'pathCreateChecks',
- 'validateExpirationDateLink',
- ])
- ->getMock();
-
- $originalShare = $this->manager->newShare();
- $originalShare->setShareType(IShare::TYPE_EMAIL)
- ->setPermissions(Constants::PERMISSION_ALL);
-
- $tomorrow = new \DateTime();
- $tomorrow->setTime(0, 0, 0);
- $tomorrow->add(new \DateInterval('P1D'));
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(100);
-
- $share = $this->manager->newShare();
- $share->setProviderId('foo')
- ->setId('42')
- ->setShareType(IShare::TYPE_EMAIL)
- ->setToken('token')
- ->setSharedBy('owner')
- ->setShareOwner('owner')
- ->setPassword('password')
- ->setExpirationDate($tomorrow)
- ->setNode($file)
- ->setPermissions(Constants::PERMISSION_ALL);
-
- $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
- $manager->expects($this->once())->method('generalChecks')->with($share);
- $manager->expects($this->once())->method('verifyPassword')->with('password');
- $manager->expects($this->once())->method('pathCreateChecks')->with($file);
- $manager->expects($this->once())->method('validateExpirationDateLink');
-
- $this->defaultProvider->expects($this->once())
- ->method('update')
- ->with($share, 'password')
- ->willReturn($share);
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
- $hookListener->expects($this->once())->method('post')->with([
- 'itemType' => 'file',
- 'itemSource' => 100,
- 'date' => $tomorrow,
- 'uidOwner' => 'owner',
- ]);
-
- $hookListener2 = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
- $hookListener2->expects($this->once())->method('post')->with([
- 'itemType' => 'file',
- 'itemSource' => 100,
- 'uidOwner' => 'owner',
- 'token' => 'token',
- 'disabled' => false,
- ]);
-
- $hookListener3 = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
- $hookListener3->expects($this->never())->method('post');
-
- $manager->updateShare($share);
- }
-
- public function testUpdateShareMailEnableSendPasswordByTalk(): void {
- $manager = $this->createManagerMock()
- ->onlyMethods([
- 'getShareById',
- 'generalChecks',
- 'verifyPassword',
- 'pathCreateChecks',
- 'validateExpirationDateLink',
- ])
- ->getMock();
-
- $originalShare = $this->manager->newShare();
- $originalShare->setShareType(IShare::TYPE_EMAIL)
- ->setPermissions(Constants::PERMISSION_ALL)
- ->setPassword(null)
- ->setSendPasswordByTalk(false);
-
- $tomorrow = new \DateTime();
- $tomorrow->setTime(0, 0, 0);
- $tomorrow->add(new \DateInterval('P1D'));
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(100);
-
- $share = $this->manager->newShare();
- $share->setProviderId('foo')
- ->setId('42')
- ->setShareType(IShare::TYPE_EMAIL)
- ->setToken('token')
- ->setSharedBy('owner')
- ->setShareOwner('owner')
- ->setPassword('password')
- ->setSendPasswordByTalk(true)
- ->setExpirationDate($tomorrow)
- ->setNode($file)
- ->setPermissions(Constants::PERMISSION_ALL);
-
- $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
- $manager->expects($this->once())->method('generalChecks')->with($share);
- $manager->expects($this->once())->method('verifyPassword')->with('password');
- $manager->expects($this->once())->method('pathCreateChecks')->with($file);
- $manager->expects($this->once())->method('validateExpirationDateLink');
-
- $this->defaultProvider->expects($this->once())
- ->method('update')
- ->with($share, 'password')
- ->willReturn($share);
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
- $hookListener->expects($this->once())->method('post')->with([
- 'itemType' => 'file',
- 'itemSource' => 100,
- 'date' => $tomorrow,
- 'uidOwner' => 'owner',
- ]);
-
- $hookListener2 = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
- $hookListener2->expects($this->once())->method('post')->with([
- 'itemType' => 'file',
- 'itemSource' => 100,
- 'uidOwner' => 'owner',
- 'token' => 'token',
- 'disabled' => false,
- ]);
-
- $hookListener3 = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
- $hookListener3->expects($this->never())->method('post');
-
- $manager->updateShare($share);
- }
-
- public function testUpdateShareMailEnableSendPasswordByTalkWithDifferentPassword(): void {
- $manager = $this->createManagerMock()
- ->onlyMethods([
- 'getShareById',
- 'generalChecks',
- 'verifyPassword',
- 'pathCreateChecks',
- 'validateExpirationDateLink',
- ])
- ->getMock();
-
- $originalShare = $this->manager->newShare();
- $originalShare->setShareType(IShare::TYPE_EMAIL)
- ->setPermissions(Constants::PERMISSION_ALL)
- ->setPassword('anotherPasswordHash')
- ->setSendPasswordByTalk(false);
-
- $tomorrow = new \DateTime();
- $tomorrow->setTime(0, 0, 0);
- $tomorrow->add(new \DateInterval('P1D'));
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(100);
-
- $share = $this->manager->newShare();
- $share->setProviderId('foo')
- ->setId('42')
- ->setShareType(IShare::TYPE_EMAIL)
- ->setToken('token')
- ->setSharedBy('owner')
- ->setShareOwner('owner')
- ->setPassword('password')
- ->setSendPasswordByTalk(true)
- ->setExpirationDate($tomorrow)
- ->setNode($file)
- ->setPermissions(Constants::PERMISSION_ALL);
-
- $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
- $manager->expects($this->once())->method('generalChecks')->with($share);
- $manager->expects($this->once())->method('verifyPassword')->with('password');
- $manager->expects($this->once())->method('pathCreateChecks')->with($file);
- $manager->expects($this->once())->method('validateExpirationDateLink');
-
- $this->defaultProvider->expects($this->once())
- ->method('update')
- ->with($share, 'password')
- ->willReturn($share);
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
- $hookListener->expects($this->once())->method('post')->with([
- 'itemType' => 'file',
- 'itemSource' => 100,
- 'date' => $tomorrow,
- 'uidOwner' => 'owner',
- ]);
-
- $hookListener2 = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
- $hookListener2->expects($this->once())->method('post')->with([
- 'itemType' => 'file',
- 'itemSource' => 100,
- 'uidOwner' => 'owner',
- 'token' => 'token',
- 'disabled' => false,
- ]);
-
- $hookListener3 = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
- $hookListener3->expects($this->never())->method('post');
-
- $manager->updateShare($share);
- }
-
- public function testUpdateShareMailEnableSendPasswordByTalkWithNoPassword(): void {
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Cannot enable sending the password by Talk with an empty password');
-
- $manager = $this->createManagerMock()
- ->onlyMethods([
- 'getShareById',
- 'generalChecks',
- 'verifyPassword',
- 'pathCreateChecks',
- 'validateExpirationDateLink',
- ])
- ->getMock();
-
- $originalShare = $this->manager->newShare();
- $originalShare->setShareType(IShare::TYPE_EMAIL)
- ->setPermissions(Constants::PERMISSION_ALL)
- ->setPassword(null)
- ->setSendPasswordByTalk(false);
-
- $tomorrow = new \DateTime();
- $tomorrow->setTime(0, 0, 0);
- $tomorrow->add(new \DateInterval('P1D'));
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(100);
-
- $share = $this->manager->newShare();
- $share->setProviderId('foo')
- ->setId('42')
- ->setShareType(IShare::TYPE_EMAIL)
- ->setToken('token')
- ->setSharedBy('owner')
- ->setShareOwner('owner')
- ->setPassword(null)
- ->setSendPasswordByTalk(true)
- ->setExpirationDate($tomorrow)
- ->setNode($file)
- ->setPermissions(Constants::PERMISSION_ALL);
-
- $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
- $manager->expects($this->once())->method('generalChecks')->with($share);
- $manager->expects($this->never())->method('verifyPassword');
- $manager->expects($this->never())->method('pathCreateChecks');
- $manager->expects($this->never())->method('validateExpirationDateLink');
-
- $this->defaultProvider->expects($this->never())
- ->method('update');
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
- $hookListener->expects($this->never())->method('post');
-
- $hookListener2 = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
- $hookListener2->expects($this->never())->method('post');
-
- $hookListener3 = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
- $hookListener3->expects($this->never())->method('post');
-
- $manager->updateShare($share);
- }
-
- public function testUpdateShareMailEnableSendPasswordByTalkRemovingPassword(): void {
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Cannot enable sending the password by Talk with an empty password');
-
- $manager = $this->createManagerMock()
- ->onlyMethods([
- 'getShareById',
- 'generalChecks',
- 'verifyPassword',
- 'pathCreateChecks',
- 'validateExpirationDateLink',
- ])
- ->getMock();
-
- $originalShare = $this->manager->newShare();
- $originalShare->setShareType(IShare::TYPE_EMAIL)
- ->setPermissions(Constants::PERMISSION_ALL)
- ->setPassword('passwordHash')
- ->setSendPasswordByTalk(false);
-
- $tomorrow = new \DateTime();
- $tomorrow->setTime(0, 0, 0);
- $tomorrow->add(new \DateInterval('P1D'));
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(100);
-
- $share = $this->manager->newShare();
- $share->setProviderId('foo')
- ->setId('42')
- ->setShareType(IShare::TYPE_EMAIL)
- ->setToken('token')
- ->setSharedBy('owner')
- ->setShareOwner('owner')
- ->setPassword(null)
- ->setSendPasswordByTalk(true)
- ->setExpirationDate($tomorrow)
- ->setNode($file)
- ->setPermissions(Constants::PERMISSION_ALL);
-
- $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
- $manager->expects($this->once())->method('generalChecks')->with($share);
- $manager->expects($this->once())->method('verifyPassword');
- $manager->expects($this->never())->method('pathCreateChecks');
- $manager->expects($this->never())->method('validateExpirationDateLink');
-
- $this->defaultProvider->expects($this->never())
- ->method('update');
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
- $hookListener->expects($this->never())->method('post');
-
- $hookListener2 = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
- $hookListener2->expects($this->never())->method('post');
-
- $hookListener3 = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
- $hookListener3->expects($this->never())->method('post');
-
- $manager->updateShare($share);
- }
-
- public function testUpdateShareMailEnableSendPasswordByTalkRemovingPasswordWithEmptyString(): void {
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Cannot enable sending the password by Talk with an empty password');
-
- $manager = $this->createManagerMock()
- ->onlyMethods([
- 'getShareById',
- 'generalChecks',
- 'verifyPassword',
- 'pathCreateChecks',
- 'validateExpirationDateLink',
- ])
- ->getMock();
-
- $originalShare = $this->manager->newShare();
- $originalShare->setShareType(IShare::TYPE_EMAIL)
- ->setPermissions(Constants::PERMISSION_ALL)
- ->setPassword('passwordHash')
- ->setSendPasswordByTalk(false);
-
- $tomorrow = new \DateTime();
- $tomorrow->setTime(0, 0, 0);
- $tomorrow->add(new \DateInterval('P1D'));
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(100);
-
- $share = $this->manager->newShare();
- $share->setProviderId('foo')
- ->setId('42')
- ->setShareType(IShare::TYPE_EMAIL)
- ->setToken('token')
- ->setSharedBy('owner')
- ->setShareOwner('owner')
- ->setPassword('')
- ->setSendPasswordByTalk(true)
- ->setExpirationDate($tomorrow)
- ->setNode($file)
- ->setPermissions(Constants::PERMISSION_ALL);
-
- $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
- $manager->expects($this->once())->method('generalChecks')->with($share);
- $manager->expects($this->once())->method('verifyPassword');
- $manager->expects($this->never())->method('pathCreateChecks');
- $manager->expects($this->never())->method('validateExpirationDateLink');
-
- $this->defaultProvider->expects($this->never())
- ->method('update');
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
- $hookListener->expects($this->never())->method('post');
-
- $hookListener2 = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
- $hookListener2->expects($this->never())->method('post');
-
- $hookListener3 = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
- $hookListener3->expects($this->never())->method('post');
-
- $manager->updateShare($share);
- }
-
- public function testUpdateShareMailEnableSendPasswordByTalkWithPreviousPassword(): void {
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Cannot enable sending the password by Talk without setting a new password');
-
- $manager = $this->createManagerMock()
- ->onlyMethods([
- 'getShareById',
- 'generalChecks',
- 'verifyPassword',
- 'pathCreateChecks',
- 'validateExpirationDateLink',
- ])
- ->getMock();
-
- $originalShare = $this->manager->newShare();
- $originalShare->setShareType(IShare::TYPE_EMAIL)
- ->setPermissions(Constants::PERMISSION_ALL)
- ->setPassword('password')
- ->setSendPasswordByTalk(false);
-
- $tomorrow = new \DateTime();
- $tomorrow->setTime(0, 0, 0);
- $tomorrow->add(new \DateInterval('P1D'));
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(100);
-
- $share = $this->manager->newShare();
- $share->setProviderId('foo')
- ->setId('42')
- ->setShareType(IShare::TYPE_EMAIL)
- ->setToken('token')
- ->setSharedBy('owner')
- ->setShareOwner('owner')
- ->setPassword('password')
- ->setSendPasswordByTalk(true)
- ->setExpirationDate($tomorrow)
- ->setNode($file)
- ->setPermissions(Constants::PERMISSION_ALL);
-
- $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
- $manager->expects($this->once())->method('generalChecks')->with($share);
- $manager->expects($this->never())->method('verifyPassword');
- $manager->expects($this->never())->method('pathCreateChecks');
- $manager->expects($this->never())->method('validateExpirationDateLink');
-
- $this->defaultProvider->expects($this->never())
- ->method('update');
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
- $hookListener->expects($this->never())->method('post');
-
- $hookListener2 = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
- $hookListener2->expects($this->never())->method('post');
-
- $hookListener3 = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
- $hookListener3->expects($this->never())->method('post');
-
- $manager->updateShare($share);
- }
-
- public function testUpdateShareMailDisableSendPasswordByTalkWithPreviousPassword(): void {
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Cannot disable sending the password by Talk without setting a new password');
-
- $manager = $this->createManagerMock()
- ->onlyMethods([
- 'getShareById',
- 'generalChecks',
- 'verifyPassword',
- 'pathCreateChecks',
- 'validateExpirationDateLink',
- ])
- ->getMock();
-
- $originalShare = $this->manager->newShare();
- $originalShare->setShareType(IShare::TYPE_EMAIL)
- ->setPermissions(Constants::PERMISSION_ALL)
- ->setPassword('passwordHash')
- ->setSendPasswordByTalk(true);
-
- $tomorrow = new \DateTime();
- $tomorrow->setTime(0, 0, 0);
- $tomorrow->add(new \DateInterval('P1D'));
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(100);
-
- $share = $this->manager->newShare();
- $share->setProviderId('foo')
- ->setId('42')
- ->setShareType(IShare::TYPE_EMAIL)
- ->setToken('token')
- ->setSharedBy('owner')
- ->setShareOwner('owner')
- ->setPassword('passwordHash')
- ->setSendPasswordByTalk(false)
- ->setExpirationDate($tomorrow)
- ->setNode($file)
- ->setPermissions(Constants::PERMISSION_ALL);
-
- $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
- $manager->expects($this->once())->method('generalChecks')->with($share);
- $manager->expects($this->never())->method('verifyPassword');
- $manager->expects($this->never())->method('pathCreateChecks');
- $manager->expects($this->never())->method('validateExpirationDateLink');
-
- $this->defaultProvider->expects($this->never())
- ->method('update');
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
- $hookListener->expects($this->never())->method('post');
-
- $hookListener2 = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
- $hookListener2->expects($this->never())->method('post');
-
- $hookListener3 = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
- $hookListener3->expects($this->never())->method('post');
-
- $manager->updateShare($share);
- }
-
- public function testUpdateShareMailDisableSendPasswordByTalkWithoutChangingPassword(): void {
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Cannot disable sending the password by Talk without setting a new password');
-
- $manager = $this->createManagerMock()
- ->onlyMethods([
- 'getShareById',
- 'generalChecks',
- 'verifyPassword',
- 'pathCreateChecks',
- 'validateExpirationDateLink',
- ])
- ->getMock();
-
- $originalShare = $this->manager->newShare();
- $originalShare->setShareType(IShare::TYPE_EMAIL)
- ->setPermissions(Constants::PERMISSION_ALL)
- ->setPassword('passwordHash')
- ->setSendPasswordByTalk(true);
-
- $tomorrow = new \DateTime();
- $tomorrow->setTime(0, 0, 0);
- $tomorrow->add(new \DateInterval('P1D'));
-
- $file = $this->createMock(File::class);
- $file->method('getId')->willReturn(100);
-
- $share = $this->manager->newShare();
- $share->setProviderId('foo')
- ->setId('42')
- ->setShareType(IShare::TYPE_EMAIL)
- ->setToken('token')
- ->setSharedBy('owner')
- ->setShareOwner('owner')
- ->setPassword('passwordHash')
- ->setSendPasswordByTalk(false)
- ->setExpirationDate($tomorrow)
- ->setNode($file)
- ->setPermissions(Constants::PERMISSION_ALL);
-
- $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
- $manager->expects($this->once())->method('generalChecks')->with($share);
- $manager->expects($this->never())->method('verifyPassword');
- $manager->expects($this->never())->method('pathCreateChecks');
- $manager->expects($this->never())->method('validateExpirationDateLink');
-
- $this->defaultProvider->expects($this->never())
- ->method('update');
-
- $hookListener = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListener, 'post');
- $hookListener->expects($this->never())->method('post');
-
- $hookListener2 = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_update_password', $hookListener2, 'post');
- $hookListener2->expects($this->never())->method('post');
-
- $hookListener3 = $this->createMock(DummyShareManagerListener::class);
- Util::connectHook('OCP\Share', 'post_update_permissions', $hookListener3, 'post');
- $hookListener3->expects($this->never())->method('post');
-
- $manager->updateShare($share);
- }
-
- public function testMoveShareLink(): void {
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Cannot change target of link share');
-
- $share = $this->manager->newShare();
- $share->setShareType(IShare::TYPE_LINK);
-
- $this->manager->moveShare($share, 'recipient');
- }
-
- public function testMoveShareUserNotRecipient(): void {
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Invalid share recipient');
-
- $share = $this->manager->newShare();
- $share->setShareType(IShare::TYPE_USER);
-
- $share->setSharedWith('sharedWith');
-
- $this->manager->moveShare($share, 'recipient');
- }
-
- public function testMoveShareUser(): void {
- $share = $this->manager->newShare();
- $share->setShareType(IShare::TYPE_USER)
- ->setId('42')
- ->setProviderId('foo');
- $this->userManager->method('get')
- ->with('recipient')
- ->willReturn($this->createMock(IUser::class));
-
- $share->setSharedWith('recipient');
-
- $this->defaultProvider->method('move')->with($share, 'recipient')->willReturnArgument(0);
-
- $this->manager->moveShare($share, 'recipient');
- }
-
- public function testMoveShareGroupNotRecipient(): void {
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Invalid share recipient');
-
- $share = $this->manager->newShare();
- $share->setShareType(IShare::TYPE_GROUP);
-
- $sharedWith = $this->createMock(IGroup::class);
- $share->setSharedWith('shareWith');
-
- $recipient = $this->createMock(IUser::class);
- $sharedWith->method('inGroup')->with($recipient)->willReturn(false);
-
- $this->groupManager->method('get')->with('shareWith')->willReturn($sharedWith);
- $this->userManager->method('get')->with('recipient')->willReturn($recipient);
-
- $this->manager->moveShare($share, 'recipient');
- }
-
- public function testMoveShareGroupNull(): void {
- $this->expectException(\InvalidArgumentException::class);
- $this->expectExceptionMessage('Group "shareWith" does not exist');
-
- $share = $this->manager->newShare();
- $share->setShareType(IShare::TYPE_GROUP);
- $share->setSharedWith('shareWith');
-
- $recipient = $this->createMock(IUser::class);
-
- $this->groupManager->method('get')->with('shareWith')->willReturn(null);
- $this->userManager->method('get')->with('recipient')->willReturn($recipient);
-
- $this->manager->moveShare($share, 'recipient');
- }
-
- public function testMoveShareGroup(): void {
- $share = $this->manager->newShare();
- $share->setShareType(IShare::TYPE_GROUP)
- ->setId('42')
- ->setProviderId('foo');
-
- $group = $this->createMock(IGroup::class);
- $share->setSharedWith('group');
-
- $recipient = $this->createMock(IUser::class);
- $group->method('inGroup')->with($recipient)->willReturn(true);
-
- $this->groupManager->method('get')->with('group')->willReturn($group);
- $this->userManager->method('get')->with('recipient')->willReturn($recipient);
-
- $this->defaultProvider->method('move')->with($share, 'recipient')->willReturnArgument(0);
-
- $this->manager->moveShare($share, 'recipient');
- }
-
- #[DataProvider('dataTestShareProviderExists')]
- public function testShareProviderExists($shareType, $expected): void {
- $factory = $this->getMockBuilder('OCP\Share\IProviderFactory')->getMock();
- $factory->expects($this->any())->method('getProviderForType')
- ->willReturnCallback(function ($id) {
- if ($id === IShare::TYPE_USER) {
- return true;
- }
- throw new ProviderException();
- });
-
- $manager = $this->createManager($factory);
- $this->assertSame($expected,
- $manager->shareProviderExists($shareType)
- );
- }
-
- public static function dataTestShareProviderExists() {
- return [
- [IShare::TYPE_USER, true],
- [42, false],
- ];
- }
-
- public function testGetSharesInFolder(): void {
- $factory = new DummyFactory2($this->createMock(ContainerInterface::class));
-
- $manager = $this->createManager($factory);
-
- $factory->setProvider($this->defaultProvider);
- $extraProvider = $this->createMock(IShareProvider::class);
- $factory->setSecondProvider($extraProvider);
-
- $share1 = $this->createMock(IShare::class);
- $share2 = $this->createMock(IShare::class);
- $share3 = $this->createMock(IShare::class);
- $share4 = $this->createMock(IShare::class);
-
- $folder = $this->createMock(Folder::class);
-
- $this->defaultProvider->method('getSharesInFolder')
- ->with(
- $this->equalTo('user'),
- $this->equalTo($folder),
- $this->equalTo(false)
- )->willReturn([
- 1 => [$share1],
- 2 => [$share2],
- ]);
-
- $extraProvider->method('getSharesInFolder')
- ->with(
- $this->equalTo('user'),
- $this->equalTo($folder),
- $this->equalTo(false)
- )->willReturn([
- 2 => [$share3],
- 3 => [$share4],
- ]);
-
- $result = $manager->getSharesInFolder('user', $folder, false);
-
- $expects = [
- 1 => [$share1],
- 2 => [$share2, $share3],
- 3 => [$share4],
- ];
-
- $this->assertSame($expects, $result);
- }
-
- public function testGetSharesInFolderOwnerless(): void {
- $factory = new DummyFactory2($this->createMock(ContainerInterface::class));
-
- $manager = $this->createManager($factory);
-
- $factory->setProvider($this->defaultProvider);
- $extraProvider = $this->createMock(IShareProviderSupportsAllSharesInFolder::class);
- $factory->setSecondProvider($extraProvider);
-
- $share1 = $this->createMock(IShare::class);
- $share2 = $this->createMock(IShare::class);
-
- $mount = $this->createMock(IShareOwnerlessMount::class);
-
- $folder = $this->createMock(Folder::class);
- $folder
- ->method('getMountPoint')
- ->willReturn($mount);
-
- $this->defaultProvider
- ->method('getAllSharesInFolder')
- ->with($folder)
- ->willReturn([1 => [$share1]]);
-
- $extraProvider
- ->method('getAllSharesInFolder')
- ->with($folder)
- ->willReturn([1 => [$share2]]);
-
- $this->assertSame([
- 1 => [$share1, $share2],
- ], $manager->getSharesInFolder('user', $folder));
- }
-
- public function testGetAccessList(): void {
- $factory = new DummyFactory2($this->createMock(ContainerInterface::class));
-
- $manager = $this->createManager($factory);
-
- $factory->setProvider($this->defaultProvider);
- $extraProvider = $this->createMock(IShareProvider::class);
- $factory->setSecondProvider($extraProvider);
-
- $nodeOwner = $this->createMock(IUser::class);
- $nodeOwner->expects($this->once())
- ->method('getUID')
- ->willReturn('user1');
-
- $node = $this->createMock(Node::class);
- $node->expects($this->once())
- ->method('getOwner')
- ->willReturn($nodeOwner);
- $node->method('getId')
- ->willReturn(42);
-
- $userFolder = $this->createMock(Folder::class);
- $file = $this->createMock(File::class);
- $folder = $this->createMock(Folder::class);
-
- $owner = $this->createMock(IUser::class);
- $owner->expects($this->once())
- ->method('getUID')
- ->willReturn('owner');
-
- $file->method('getParent')
- ->willReturn($folder);
- $file->method('getPath')
- ->willReturn('/owner/files/folder/file');
- $file->method('getOwner')
- ->willReturn($owner);
- $file->method('getId')
- ->willReturn(23);
- $folder->method('getParent')
- ->willReturn($userFolder);
- $folder->method('getPath')
- ->willReturn('/owner/files/folder');
- $userFolder->method('getFirstNodeById')
- ->with($this->equalTo(42))
- ->willReturn($file);
- $userFolder->method('getPath')
- ->willReturn('/user1/files');
-
- $this->userManager->method('userExists')
- ->with($this->equalTo('user1'))
- ->willReturn(true);
-
- $this->defaultProvider->method('getAccessList')
- ->with(
- $this->equalTo([$file, $folder]),
- false
- )
- ->willReturn([
- 'users' => [
- 'user1',
- 'user2',
- 'user3',
- '123456',
- ],
- 'public' => true,
- ]);
-
- $extraProvider->method('getAccessList')
- ->with(
- $this->equalTo([$file, $folder]),
- false
- )
- ->willReturn([
- 'users' => [
- 'user3',
- 'user4',
- 'user5',
- '234567',
- ],
- 'remote' => true,
- ]);
-
- $this->rootFolder->method('getUserFolder')
- ->with($this->equalTo('user1'))
- ->willReturn($userFolder);
-
- $expected = [
- 'users' => ['owner', 'user1', 'user2', 'user3', '123456', 'user4', 'user5', '234567'],
- 'remote' => true,
- 'public' => true,
- ];
-
- $result = $manager->getAccessList($node, true, false);
-
- $this->assertSame($expected['public'], $result['public']);
- $this->assertSame($expected['remote'], $result['remote']);
- $this->assertSame($expected['users'], $result['users']);
- }
-
- public function testGetAccessListWithCurrentAccess(): void {
- $factory = new DummyFactory2($this->createMock(ContainerInterface::class));
-
- $manager = $this->createManager($factory);
-
- $factory->setProvider($this->defaultProvider);
- $extraProvider = $this->createMock(IShareProvider::class);
- $factory->setSecondProvider($extraProvider);
-
- $nodeOwner = $this->createMock(IUser::class);
- $nodeOwner->expects($this->once())
- ->method('getUID')
- ->willReturn('user1');
-
- $node = $this->createMock(Node::class);
- $node->expects($this->once())
- ->method('getOwner')
- ->willReturn($nodeOwner);
- $node->method('getId')
- ->willReturn(42);
-
- $userFolder = $this->createMock(Folder::class);
- $file = $this->createMock(File::class);
-
- $owner = $this->createMock(IUser::class);
- $owner->expects($this->once())
- ->method('getUID')
- ->willReturn('owner');
- $folder = $this->createMock(Folder::class);
-
- $file->method('getParent')
- ->willReturn($folder);
- $file->method('getPath')
- ->willReturn('/owner/files/folder/file');
- $file->method('getOwner')
- ->willReturn($owner);
- $file->method('getId')
- ->willReturn(23);
- $folder->method('getParent')
- ->willReturn($userFolder);
- $folder->method('getPath')
- ->willReturn('/owner/files/folder');
- $userFolder->method('getFirstNodeById')
- ->with($this->equalTo(42))
- ->willReturn($file);
- $userFolder->method('getPath')
- ->willReturn('/user1/files');
-
- $this->userManager->method('userExists')
- ->with($this->equalTo('user1'))
- ->willReturn(true);
-
- $this->defaultProvider->method('getAccessList')
- ->with(
- $this->equalTo([$file, $folder]),
- true
- )
- ->willReturn([
- 'users' => [
- 'user1' => [],
- 'user2' => [],
- 'user3' => [],
- '123456' => [],
- ],
- 'public' => true,
- ]);
-
- $extraProvider->method('getAccessList')
- ->with(
- $this->equalTo([$file, $folder]),
- true
- )
- ->willReturn([
- 'users' => [
- 'user3' => [],
- 'user4' => [],
- 'user5' => [],
- '234567' => [],
- ],
- 'remote' => [
- 'remote1',
- ],
- ]);
-
- $this->rootFolder->method('getUserFolder')
- ->with($this->equalTo('user1'))
- ->willReturn($userFolder);
-
- $expected = [
- 'users' => [
- 'owner' => [
- 'node_id' => 23,
- 'node_path' => '/folder/file'
- ]
- , 'user1' => [], 'user2' => [], 'user3' => [], '123456' => [], 'user4' => [], 'user5' => [], '234567' => []],
- 'remote' => [
- 'remote1',
- ],
- 'public' => true,
- ];
-
- $result = $manager->getAccessList($node, true, true);
-
- $this->assertSame($expected['public'], $result['public']);
- $this->assertSame($expected['remote'], $result['remote']);
- $this->assertSame($expected['users'], $result['users']);
- }
-
- public function testGetAllShares(): void {
- $factory = new DummyFactory2($this->createMock(ContainerInterface::class));
-
- $manager = $this->createManager($factory);
-
- $factory->setProvider($this->defaultProvider);
- $extraProvider = $this->createMock(IShareProvider::class);
- $factory->setSecondProvider($extraProvider);
-
- $share1 = $this->createMock(IShare::class);
- $share2 = $this->createMock(IShare::class);
- $share3 = $this->createMock(IShare::class);
- $share4 = $this->createMock(IShare::class);
-
- $this->defaultProvider->method('getAllShares')
- ->willReturnCallback(function () use ($share1, $share2) {
- yield $share1;
- yield $share2;
- });
- $extraProvider->method('getAllShares')
- ->willReturnCallback(function () use ($share3, $share4) {
- yield $share3;
- yield $share4;
- });
-
- // "yield from", used in "getAllShares()", does not reset the keys, so
- // "use_keys" has to be disabled to collect all the values while
- // ignoring the keys returned by the generator.
- $result = iterator_to_array($manager->getAllShares(), $use_keys = false);
-
- $expects = [$share1, $share2, $share3, $share4];
-
- $this->assertSame($expects, $result);
- }
-
- public static function dataCurrentUserCanEnumerateTargetUser(): array {
- return [
- 'Full match guest' => [true, true, false, false, false, false, false, true],
- 'Full match user' => [false, true, false, false, false, false, false, true],
- 'Enumeration off guest' => [true, false, false, false, false, false, false, false],
- 'Enumeration off user' => [false, false, false, false, false, false, false, false],
- 'Enumeration guest' => [true, false, true, false, false, false, false, true],
- 'Enumeration user' => [false, false, true, false, false, false, false, true],
-
- // Restricted enumerations guests never works
- 'Guest phone' => [true, false, true, true, false, false, false, false],
- 'Guest group' => [true, false, true, false, true, false, false, false],
- 'Guest both' => [true, false, true, true, true, false, false, false],
-
- // Restricted enumerations users
- 'User phone but not known' => [false, false, true, true, false, false, false, false],
- 'User phone known' => [false, false, true, true, false, true, false, true],
- 'User group but no match' => [false, false, true, false, true, false, false, false],
- 'User group with match' => [false, false, true, false, true, false, true, true],
- ];
- }
-
- /**
- * @param bool $expected
- */
- #[DataProvider('dataCurrentUserCanEnumerateTargetUser')]
- public function testCurrentUserCanEnumerateTargetUser(bool $currentUserIsGuest, bool $allowEnumerationFullMatch, bool $allowEnumeration, bool $limitEnumerationToPhone, bool $limitEnumerationToGroups, bool $isKnownToUser, bool $haveCommonGroup, bool $expected): void {
- /** @var IManager|MockObject $manager */
- $manager = $this->createManagerMock()
- ->onlyMethods([
- 'allowEnumerationFullMatch',
- 'allowEnumeration',
- 'limitEnumerationToPhone',
- 'limitEnumerationToGroups',
- ])
- ->getMock();
-
- $manager->method('allowEnumerationFullMatch')
- ->willReturn($allowEnumerationFullMatch);
- $manager->method('allowEnumeration')
- ->willReturn($allowEnumeration);
- $manager->method('limitEnumerationToPhone')
- ->willReturn($limitEnumerationToPhone);
- $manager->method('limitEnumerationToGroups')
- ->willReturn($limitEnumerationToGroups);
-
- $this->knownUserService->method('isKnownToUser')
- ->with('current', 'target')
- ->willReturn($isKnownToUser);
-
- $currentUser = null;
- if (!$currentUserIsGuest) {
- $currentUser = $this->createMock(IUser::class);
- $currentUser->method('getUID')
- ->willReturn('current');
- }
- $targetUser = $this->createMock(IUser::class);
- $targetUser->method('getUID')
- ->willReturn('target');
-
- if ($haveCommonGroup) {
- $this->groupManager->method('getUserGroupIds')
- ->willReturnMap([
- [$targetUser, ['gid1', 'gid2']],
- [$currentUser, ['gid2', 'gid3']],
- ]);
- } else {
- $this->groupManager->method('getUserGroupIds')
- ->willReturnMap([
- [$targetUser, ['gid1', 'gid2']],
- [$currentUser, ['gid3', 'gid4']],
- ]);
- }
-
- $this->assertSame($expected, $manager->currentUserCanEnumerateTargetUser($currentUser, $targetUser));
- }
-}
-
-class DummyFactory implements IProviderFactory {
- /** @var IShareProvider */
- protected $provider;
-
- public function __construct(ContainerInterface $serverContainer) {
- }
-
- /**
- * @param IShareProvider $provider
- */
- public function setProvider($provider) {
- $this->provider = $provider;
- }
-
- /**
- * @param string $id
- * @return IShareProvider
- */
- #[\Override]
- public function getProvider($id) {
- return $this->provider;
- }
-
- /**
- * @param int $shareType
- * @return IShareProvider
- */
- #[\Override]
- public function getProviderForType($shareType) {
- return $this->provider;
- }
-
- /**
- * @return IShareProvider[]
- */
- #[\Override]
- public function getAllProviders() {
- return [$this->provider];
- }
-
- #[\Override]
- public function registerProvider(string $shareProvier): void {
- }
-}
-
-class DummyFactory2 extends DummyFactory {
- /** @var IShareProvider */
- private $provider2;
-
- /**
- * @param IShareProvider $provider
- */
- public function setSecondProvider($provider) {
- $this->provider2 = $provider;
- }
-
- #[\Override]
- public function getAllProviders() {
- return [$this->provider, $this->provider2];
- }
-
- #[\Override]
- public function registerProvider(string $shareProvier): void {
- }
-}
--- a/tests/lib/Share20/ShareByMailProviderTest.php
+++ /dev/null
@@ -1,305 +0,0 @@
-dbConn = Server::get(IDBConnection::class);
- $this->userManager = $this->createMock(IUserManager::class);
- $this->rootFolder = $this->createMock(IRootFolder::class);
- $this->mailer = $this->createMock(IMailer::class);
- $this->l10n = $this->createMock(IL10N::class);
- $this->defaults = $this->getMockBuilder(Defaults::class)->disableOriginalConstructor()->getMock();
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->activityManager = $this->createMock(\OCP\Activity\IManager::class);
- $this->settingsManager = $this->createMock(SettingsManager::class);
- $this->hasher = $this->createMock(IHasher::class);
- $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
- $this->shareManager = $this->createMock(\OCP\Share\IManager::class);
- $this->secureRandom = $this->createMock(ISecureRandom::class);
- $this->config = $this->createMock(IConfig::class);
-
- // Empty share table
- $this->dbConn->getQueryBuilder()->delete('share')->executeStatement();
-
- $this->provider = new ShareByMailProvider(
- $this->config,
- $this->dbConn,
- $this->secureRandom,
- $this->userManager,
- $this->rootFolder,
- $this->l10n,
- $this->logger,
- $this->mailer,
- $this->urlGenerator,
- $this->activityManager,
- $this->settingsManager,
- $this->defaults,
- $this->hasher,
- $this->eventDispatcher,
- $this->shareManager,
- $this->getEmailValidatorWithStrictEmailCheck(),
- );
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->dbConn->getQueryBuilder()->delete('share')->executeStatement();
- $this->dbConn->getQueryBuilder()->delete('filecache')->runAcrossAllShards()->executeStatement();
- $this->dbConn->getQueryBuilder()->delete('storages')->executeStatement();
- parent::tearDown();
- }
-
- /**
- * @param int $shareType
- * @param string $sharedWith
- * @param string $sharedBy
- * @param string $shareOwner
- * @param string $itemType
- * @param int $fileSource
- * @param string $fileTarget
- * @param int $permissions
- * @param $token
- * @param $expiration
- * @param $parent
- * @return int
- *
- * @throws \OCP\DB\Exception
- */
- private function addShareToDB($shareType, $sharedWith, $sharedBy, $shareOwner,
- $itemType, $fileSource, $fileTarget, $permissions, $token, $expiration,
- $parent) {
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('share');
-
- if ($shareType) {
- $qb->setValue('share_type', $qb->expr()->literal($shareType));
- }
- if ($sharedWith) {
- $qb->setValue('share_with', $qb->expr()->literal($sharedWith));
- }
- if ($sharedBy) {
- $qb->setValue('uid_initiator', $qb->expr()->literal($sharedBy));
- }
- if ($shareOwner) {
- $qb->setValue('uid_owner', $qb->expr()->literal($shareOwner));
- }
- if ($itemType) {
- $qb->setValue('item_type', $qb->expr()->literal($itemType));
- }
- if ($fileSource) {
- $qb->setValue('file_source', $qb->expr()->literal($fileSource));
- }
- if ($fileTarget) {
- $qb->setValue('file_target', $qb->expr()->literal($fileTarget));
- }
- if ($permissions) {
- $qb->setValue('permissions', $qb->expr()->literal($permissions));
- }
- if ($token) {
- $qb->setValue('token', $qb->expr()->literal($token));
- }
- if ($expiration) {
- $qb->setValue('expiration', $qb->createNamedParameter($expiration, IQueryBuilder::PARAM_DATETIME_MUTABLE));
- }
- if ($parent) {
- $qb->setValue('parent', $qb->expr()->literal($parent));
- }
-
- $this->assertEquals(1, $qb->executeStatement());
- return $qb->getLastInsertId();
- }
-
- public function testGetSharesByWithResharesAndNoNode(): void {
- $this->addShareToDB(
- IShare::TYPE_EMAIL,
- 'external.one@domain.tld',
- 'user1',
- 'user1',
- 'folder',
- 42,
- null,
- 17,
- 'foobar',
- null,
- null,
- );
- $this->addShareToDB(
- IShare::TYPE_EMAIL,
- 'external.two@domain.tld',
- 'user2',
- 'user2',
- 'folder',
- 42,
- null,
- 17,
- 'barfoo',
- null,
- null,
- );
-
- // Return own shares only if not asked for a specific node
- /** @var IShare[] $actual */
- $actual = $this->provider->getSharesBy(
- 'user1',
- IShare::TYPE_EMAIL,
- null,
- true,
- -1,
- 0,
- );
-
- $this->assertCount(1, $actual);
-
- $this->assertEquals(IShare::TYPE_EMAIL, $actual[0]->getShareType());
- $this->assertEquals('user1', $actual[0]->getSharedBy());
- $this->assertEquals('user1', $actual[0]->getShareOwner());
- $this->assertEquals('external.one@domain.tld', $actual[0]->getSharedWith());
- }
-
- public function testGetSharesByWithResharesAndNode(): void {
- $this->addShareToDB(
- IShare::TYPE_EMAIL,
- 'external.one@domain.tld',
- 'user1',
- 'user1',
- 'folder',
- 42,
- null,
- 17,
- 'foobar',
- null,
- null,
- );
- $this->addShareToDB(
- IShare::TYPE_EMAIL,
- 'external.two@domain.tld',
- 'user2',
- 'user2',
- 'folder',
- 42,
- null,
- 17,
- 'barfoo',
- null,
- null,
- );
-
- $node = $this->createMock(Node::class);
- $node->expects($this->once())
- ->method('getId')
- ->willReturn(42);
-
- // Return all shares if asked for specific node
- /** @var IShare[] $actual */
- $actual = $this->provider->getSharesBy(
- 'user1',
- IShare::TYPE_EMAIL,
- $node,
- true,
- -1,
- 0,
- );
-
- $this->assertCount(2, $actual);
-
- $this->assertEquals(IShare::TYPE_EMAIL, $actual[0]->getShareType());
- $this->assertEquals('user1', $actual[0]->getSharedBy());
- $this->assertEquals('user1', $actual[0]->getShareOwner());
- $this->assertEquals('external.one@domain.tld', $actual[0]->getSharedWith());
-
- $this->assertEquals(IShare::TYPE_EMAIL, $actual[1]->getShareType());
- $this->assertEquals('user2', $actual[1]->getSharedBy());
- $this->assertEquals('user2', $actual[1]->getShareOwner());
- $this->assertEquals('external.two@domain.tld', $actual[1]->getSharedWith());
- }
-}
--- a/tests/lib/Share20/ShareHelperTest.php
+++ /dev/null
@@ -1,214 +0,0 @@
-manager = $this->createMock(IManager::class);
-
- $this->helper = new ShareHelper($this->manager);
- }
-
- public static function dataGetPathsForAccessList(): array {
- return [
- [[], [], false, [], [], false, [
- 'users' => [],
- 'remotes' => [],
- ]],
- [['user1', 'user2'], ['user1' => 'foo', 'user2' => 'bar'], true, [], [], false, [
- 'users' => ['user1' => 'foo', 'user2' => 'bar'],
- 'remotes' => [],
- ]],
- [[], [], false, ['remote1', 'remote2'], ['remote1' => 'qwe', 'remote2' => 'rtz'], true, [
- 'users' => [],
- 'remotes' => ['remote1' => 'qwe', 'remote2' => 'rtz'],
- ]],
- [['user1', 'user2'], ['user1' => 'foo', 'user2' => 'bar'], true, ['remote1', 'remote2'], ['remote1' => 'qwe', 'remote2' => 'rtz'], true, [
- 'users' => ['user1' => 'foo', 'user2' => 'bar'],
- 'remotes' => ['remote1' => 'qwe', 'remote2' => 'rtz'],
- ]],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetPathsForAccessList')]
- public function testGetPathsForAccessList(array $userList, array $userMap, $resolveUsers, array $remoteList, array $remoteMap, $resolveRemotes, array $expected): void {
- $this->manager->expects($this->once())
- ->method('getAccessList')
- ->willReturn([
- 'users' => $userList,
- 'remote' => $remoteList,
- ]);
-
- /** @var Node|\PHPUnit\Framework\MockObject\MockObject $node */
- $node = $this->createMock(Node::class);
- /** @var ShareHelper|\PHPUnit\Framework\MockObject\MockObject $helper */
- $helper = $this->getMockBuilder(ShareHelper::class)
- ->setConstructorArgs([$this->manager])
- ->onlyMethods(['getPathsForUsers', 'getPathsForRemotes'])
- ->getMock();
-
- $helper->expects($resolveUsers ? $this->once() : $this->never())
- ->method('getPathsForUsers')
- ->with($node, $userList)
- ->willReturn($userMap);
-
- $helper->expects($resolveRemotes ? $this->once() : $this->never())
- ->method('getPathsForRemotes')
- ->with($node, $remoteList)
- ->willReturn($remoteMap);
-
- $this->assertSame($expected, $helper->getPathsForAccessList($node));
- }
-
- public static function dataGetPathsForUsers(): array {
- return [
- [[], [23 => 'TwentyThree', 42 => 'FortyTwo'], []],
- [
- [
- 'test1' => ['node_id' => 16, 'node_path' => '/foo'],
- 'test2' => ['node_id' => 23, 'node_path' => '/bar'],
- 'test3' => ['node_id' => 42, 'node_path' => '/cat'],
- 'test4' => ['node_id' => 48, 'node_path' => '/dog'],
- ],
- [16 => 'SixTeen', 23 => 'TwentyThree', 42 => 'FortyTwo'],
- [
- 'test1' => '/foo/TwentyThree/FortyTwo',
- 'test2' => '/bar/FortyTwo',
- 'test3' => '/cat',
- ],
- ],
- ];
- }
-
- /**
- *
- * @param array $users
- * @param array $nodes
- * @param array $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetPathsForUsers')]
- public function testGetPathsForUsers(array $users, array $nodes, array $expected): void {
- $lastNode = null;
- foreach ($nodes as $nodeId => $nodeName) {
- /** @var Node|\PHPUnit\Framework\MockObject\MockObject $node */
- $node = $this->createMock(Node::class);
- $node->expects($this->any())
- ->method('getId')
- ->willReturn($nodeId);
- $node->expects($this->any())
- ->method('getName')
- ->willReturn($nodeName);
- if ($lastNode === null) {
- $node->expects($this->any())
- ->method('getParent')
- ->willThrowException(new NotFoundException());
- } else {
- $node->expects($this->any())
- ->method('getParent')
- ->willReturn($lastNode);
- }
- $lastNode = $node;
- }
-
- $this->assertEquals($expected, self::invokePrivate($this->helper, 'getPathsForUsers', [$lastNode, $users]));
- }
-
- public static function dataGetPathsForRemotes(): array {
- return [
- [[], [23 => 'TwentyThree', 42 => 'FortyTwo'], []],
- [
- [
- 'test1' => ['node_id' => 16, 'token' => 't1'],
- 'test2' => ['node_id' => 23, 'token' => 't2'],
- 'test3' => ['node_id' => 42, 'token' => 't3'],
- 'test4' => ['node_id' => 48, 'token' => 't4'],
- ],
- [
- 16 => '/admin/files/SixTeen',
- 23 => '/admin/files/SixTeen/TwentyThree',
- 42 => '/admin/files/SixTeen/TwentyThree/FortyTwo',
- ],
- [
- 'test1' => ['token' => 't1', 'node_path' => '/SixTeen'],
- 'test2' => ['token' => 't2', 'node_path' => '/SixTeen/TwentyThree'],
- 'test3' => ['token' => 't3', 'node_path' => '/SixTeen/TwentyThree/FortyTwo'],
- ],
- ],
- ];
- }
-
- /**
- *
- * @param array $remotes
- * @param array $nodes
- * @param array $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetPathsForRemotes')]
- public function testGetPathsForRemotes(array $remotes, array $nodes, array $expected): void {
- $lastNode = null;
- foreach ($nodes as $nodeId => $nodePath) {
- /** @var Node|\PHPUnit\Framework\MockObject\MockObject $node */
- $node = $this->createMock(Node::class);
- $node->expects($this->any())
- ->method('getId')
- ->willReturn($nodeId);
- $node->expects($this->any())
- ->method('getPath')
- ->willReturn($nodePath);
- if ($lastNode === null) {
- $node->expects($this->any())
- ->method('getParent')
- ->willThrowException(new NotFoundException());
- } else {
- $node->expects($this->any())
- ->method('getParent')
- ->willReturn($lastNode);
- }
- $lastNode = $node;
- }
-
- $this->assertEquals($expected, self::invokePrivate($this->helper, 'getPathsForRemotes', [$lastNode, $remotes]));
- }
-
- public static function dataGetMountedPath(): array {
- return [
- ['/admin/files/foobar', '/foobar'],
- ['/admin/files/foo/bar', '/foo/bar'],
- ];
- }
-
- /**
- * @param string $path
- * @param string $expected
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetMountedPath')]
- public function testGetMountedPath($path, $expected): void {
- /** @var Node|\PHPUnit\Framework\MockObject\MockObject $node */
- $node = $this->createMock(Node::class);
- $node->expects($this->once())
- ->method('getPath')
- ->willReturn($path);
-
- $this->assertSame($expected, self::invokePrivate($this->helper, 'getMountedPath', [$node]));
- }
-}
--- a/tests/lib/Share20/ShareReview/Events/ShareReviewAccessCheckEventTest.php
+++ /dev/null
@@ -1,96 +0,0 @@
-makeEvent();
-
- $this->assertFalse($event->isHandled());
- $this->assertFalse($event->isGranted());
- $this->assertNull($event->getReason());
- }
-
- public function testConstructorPayload(): void {
- $event = new ShareReviewAccessCheckEvent('Deck', '99');
-
- $this->assertSame('Deck', $event->getSourceName());
- $this->assertSame('99', $event->getShareId());
- }
-
- public function testGrantAccess(): void {
- $event = $this->makeEvent();
- $event->grantAccess();
-
- $this->assertTrue($event->isHandled());
- $this->assertTrue($event->isGranted());
- $this->assertNull($event->getReason());
- $this->assertFalse($event->isPropagationStopped());
- }
-
- public function testDenyAccess(): void {
- $event = $this->makeEvent();
- $event->denyAccess('not in group');
-
- $this->assertTrue($event->isHandled());
- $this->assertFalse($event->isGranted());
- $this->assertSame('not in group', $event->getReason());
- }
-
- public function testDenyStopsPropagation(): void {
- $event = $this->makeEvent();
- $event->denyAccess('no access');
-
- $this->assertTrue($event->isPropagationStopped());
- }
-
- public function testGrantDoesNotStopPropagation(): void {
- $event = $this->makeEvent();
- $event->grantAccess();
-
- $this->assertFalse($event->isPropagationStopped());
- }
-
- public function testGrantThenDenyIsDenied(): void {
- $event = $this->makeEvent();
- $event->grantAccess();
- $event->denyAccess('revoked');
-
- $this->assertFalse($event->isGranted());
- $this->assertSame('revoked', $event->getReason());
- $this->assertTrue($event->isPropagationStopped());
- }
-
- public function testDenyThenGrantRemainesDenied(): void {
- $event = $this->makeEvent();
- $event->denyAccess('not allowed');
- $event->grantAccess(); // must be ignored — deny wins
-
- $this->assertFalse($event->isGranted());
- $this->assertSame('not allowed', $event->getReason());
- }
-
- public function testMultipleGrantsAreIdempotent(): void {
- $event = $this->makeEvent();
- $event->grantAccess();
- $event->grantAccess();
-
- $this->assertTrue($event->isGranted());
- $this->assertFalse($event->isPropagationStopped());
- }
-}
--- a/tests/lib/Share20/ShareReview/RegisterShareReviewSourceEventTest.php
+++ /dev/null
@@ -1,42 +0,0 @@
-assertSame([], $event->getSources());
- }
-
- public function testRegisterSource(): void {
- $sourceClass = $this->createMock(IShareReviewSource::class)::class;
-
- $event = new RegisterShareReviewSourceEvent();
- $event->registerSource($sourceClass);
-
- $this->assertSame([$sourceClass], $event->getSources());
- }
-
- public function testRegisterSourceKeepsDuplicates(): void {
- $sourceClass = $this->createMock(IShareReviewSource::class)::class;
-
- $event = new RegisterShareReviewSourceEvent();
- $event->registerSource($sourceClass);
- $event->registerSource($sourceClass);
-
- $this->assertSame([$sourceClass, $sourceClass], $event->getSources());
- }
-}
--- a/tests/lib/Share20/ShareReview/ShareReviewEntryTest.php
+++ /dev/null
@@ -1,69 +0,0 @@
-assertSame('42', $entry->id);
- $this->assertSame('Board "Roadmap"', $entry->object);
- $this->assertSame('alice', $entry->initiator);
- $this->assertSame(IShare::TYPE_USER, $entry->type);
- $this->assertSame('bob', $entry->recipient);
- $this->assertSame(1783764000, $entry->lastModifiedTimestamp);
- $this->assertSame($permissions, $entry->permissions);
- $this->assertSame('board-share-42', $entry->action);
- $this->assertTrue($entry->hasPassword);
- $this->assertSame(1785837600, $entry->expirationTimestamp);
- $this->assertSame('23', $entry->parent);
- }
-
- public function testDefaults(): void {
- $entry = new ShareReviewEntry(
- id: '42',
- object: '/folder/file.txt',
- initiator: 'alice',
- type: IShare::TYPE_LINK,
- recipient: 'sToKeN',
- lastModifiedTimestamp: 0,
- );
-
- $this->assertSame(0, $entry->lastModifiedTimestamp);
- $this->assertSame([], $entry->permissions);
- $this->assertSame('', $entry->action);
- $this->assertFalse($entry->hasPassword);
- $this->assertNull($entry->expirationTimestamp);
- $this->assertNull($entry->parent);
- }
-}
--- a/tests/lib/Share20/ShareReview/ShareReviewPermissionTest.php
+++ /dev/null
@@ -1,47 +0,0 @@
-assertSame('deck:manage', $permission->id);
- $this->assertSame('Manage board', $permission->displayName);
- $this->assertSame('Administer participants and board settings', $permission->hint);
- $this->assertSame(30, $permission->priority);
- }
-
- public function testDefaults(): void {
- $permission = new ShareReviewPermission(ShareReviewPermission::FILES_READ, 'Read');
-
- $this->assertSame('files:read', $permission->id);
- $this->assertSame('Read', $permission->displayName);
- $this->assertNull($permission->hint);
- $this->assertSame(50, $permission->priority);
- }
-
- public function testFilesIdentifiers(): void {
- $this->assertSame('files:read', ShareReviewPermission::FILES_READ);
- $this->assertSame('files:update', ShareReviewPermission::FILES_UPDATE);
- $this->assertSame('files:create', ShareReviewPermission::FILES_CREATE);
- $this->assertSame('files:delete', ShareReviewPermission::FILES_DELETE);
- $this->assertSame('files:reshare', ShareReviewPermission::FILES_RESHARE);
- }
-}
--- a/tests/lib/Share20/ShareTest.php
+++ /dev/null
@@ -1,68 +0,0 @@
-rootFolder = $this->createMock(IRootFolder::class);
- $this->userManager = $this->createMock(IUserManager::class);
- $this->share = new Share($this->rootFolder, $this->userManager);
- }
-
- public function testSetIdInt(): void {
- $this->share->setId(42);
- $this->assertEquals('42', $this->share->getId());
- }
-
- public function testSetIdString(): void {
- $this->share->setId('foo');
- $this->assertEquals('foo', $this->share->getId());
- }
-
- public function testSetIdOnce(): void {
- $this->expectException(IllegalIDChangeException::class);
- $this->expectExceptionMessage('Not allowed to assign a new internal id to a share');
-
- $this->share->setId('foo');
- $this->share->setId('bar');
- }
-
- public function testSetProviderIdString(): void {
- $this->share->setProviderId('foo');
- $this->share->setId('bar');
- $this->assertEquals('foo:bar', $this->share->getFullId());
- }
-
- public function testSetProviderIdOnce(): void {
- $this->expectException(IllegalIDChangeException::class);
- $this->expectExceptionMessage('Not allowed to assign a new provider id to a share');
-
- $this->share->setProviderId('foo');
- $this->share->setProviderId('bar');
- }
-}
--- a/tests/lib/Sharing/AbstractSharingManagerTests.php
+++ /dev/null
@@ -1,4897 +0,0 @@
-assertGreaterThanOrEqual(SharingManager::timeToMs($before), SharingManager::timeToMs($time));
- $this->assertLessThanOrEqual(SharingManager::timeToMs($after), SharingManager::timeToMs($time));
- }
-
- #[\Override]
- public function setUp(): void {
- parent::setUp();
-
- $this->dbConnection = Server::get(IDBConnection::class);
-
- $this->manager = Server::get(ISharingManager::class);
-
- $this->l10nFactory = Server::get(IFactory::class);
-
- $userManager = Server::get(IUserManager::class);
-
- $owner = $userManager->createUser('owner', 'password');
- $this->assertNotFalse($owner);
- $this->owner = $owner;
- $this->owner->setDisplayName('Owner');
-
- $user1 = $userManager->createUser('user1', 'password');
- $this->assertNotFalse($user1);
- $this->user1 = $user1;
- $this->user1->setDisplayName('User 1');
-
- $user2 = $userManager->createUser('user2', 'password');
- $this->assertNotFalse($user2);
- $this->user2 = $user2;
- $this->user2->setDisplayName('User 2');
-
- $this->registry = Server::get(ISharingRegistry::class);
- $this->registry->clear();
-
- $this->shareSourceType1 = new TestShareSourceType1(['source1' => 'Source 1']);
- $this->registry->registerSourceType($this->shareSourceType1);
- $this->shareSourceType2 = new TestShareSourceType2(['source2' => 'Source 2']);
- $this->registry->registerSourceType($this->shareSourceType2);
- $this->registry->registerRecipientType(
- new TestShareRecipientType1(
- [
- 'recipient1' => 'Recipient 1',
- ],
- [
- $this->user1->getUID() => ['recipient1'],
- ],
- [
- new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null),
- ],
- )
- );
- $this->registry->registerRecipientType(
- new TestShareRecipientType2(
- [
- 'recipient2' => 'Recipient 2',
- 'recipient3' => 'Recipient 3',
- ],
- [
- $this->user2->getUID() => ['recipient2'],
- ],
- [
- new ShareRecipient(TestShareRecipientType2::class, 'recipient2', null),
- ],
- )
- );
- $this->registry->registerPropertyType(new TestSharePropertyType1(['valid1']));
- $this->registry->markPropertyTypeCompatibleWithSourceType(TestSharePropertyType1::class, TestShareSourceType1::class);
- $this->registry->markPropertyTypeCompatibleWithRecipientType(TestSharePropertyType1::class, TestShareRecipientType1::class);
- $this->registry->registerPropertyType(new TestSharePropertyType2(['valid2']));
- $this->registry->markPropertyTypeCompatibleWithSourceType(TestSharePropertyType2::class, TestShareSourceType2::class);
- $this->registry->markPropertyTypeCompatibleWithRecipientType(TestSharePropertyType2::class, TestShareRecipientType2::class);
- $this->registry->registerPermissionPreset(new TestSharePermissionPreset1());
- $this->registry->registerPermissionPreset(new TestSharePermissionPreset2());
- $this->registry->registerPermissionType(TestShareSourceType1::class, new TestSharePermissionType1());
- $this->registry->markPermissionTypeCompatibleWithPermissionPreset(TestSharePermissionType1::class, TestSharePermissionPreset1::class);
- $this->registry->markPermissionTypeCompatibleWithPermissionPreset(TestSharePermissionType1::class, TestSharePermissionPreset2::class);
- $this->registry->registerPermissionType(TestShareSourceType2::class, new TestSharePermissionType2());
- $this->registry->markPermissionTypeCompatibleWithPermissionPreset(TestSharePermissionType2::class, TestSharePermissionPreset2::class);
- $this->registry->registerPermissionType(null, Server::get(ReshareSharePermissionType::class));
- }
-
- #[\Override]
- protected function tearDown(): void {
- $openTransaction = false;
- if ($this->dbConnection->inTransaction()) {
- $this->dbConnection->rollBack();
- $openTransaction = true;
- }
-
- $accessContext = new ShareAccessContext(overrideChecks: true);
-
- $this->dbConnection->beginTransaction();
-
- foreach ($this->manager->getShares($accessContext, null, null, null, null, null, null) as $share) {
- $this->manager->deleteShare($accessContext, $share);
- }
-
- $this->dbConnection->commit();
-
- $this->owner->delete();
- $this->user1->delete();
- $this->user2->delete();
-
- $this->registry->clear();
-
- foreach ([
- 'sharing_share',
- 'sharing_share_permissions',
- 'sharing_share_properties',
- 'sharing_share_recipients',
- 'sharing_share_recipient_permissions',
- 'sharing_share_sources',
- 'sharing_share_user_status',
- ] as $table) {
- $qb = $this->dbConnection->getQueryBuilder();
- $qb
- ->select($qb->func()->count('*'))
- ->from($table);
- $this->assertEquals(0, $qb->executeQuery()->fetchOne(), $table);
- }
-
- parent::tearDown();
-
- if ($openTransaction) {
- $this->fail('Open transaction was not committed.');
- }
- }
-
- private function reloadShare(ShareAccessContext $accessContext, Share $share): Share {
- $this->dbConnection->beginTransaction();
- $share = $this->manager->getShare($accessContext, $share->id);
- $this->dbConnection->commit();
- return $share;
- }
-
- public function testSearchRecipients(): void {
- $this->registry->clear();
- $this->registry->registerRecipientType(
- new TestShareRecipientType1(
- [
- 'recipient1a' => 'Recipient 1A',
- 'recipient1b' => 'Recipient 1B',
- 'recipient1c' => 'Recipient 1C',
- ],
- [],
- [
- new ShareRecipient(TestShareRecipientType1::class, 'recipient1a', null),
- new ShareRecipient(TestShareRecipientType1::class, 'recipient1b', null),
- new ShareRecipient(TestShareRecipientType1::class, 'recipient1c', null),
- ],
- )
- );
- $this->registry->registerRecipientType(
- new TestShareRecipientType2(
- [
- 'recipient2a' => 'Recipient 2A',
- 'recipient2b' => 'Recipient 2B',
- 'recipient2c' => 'Recipient 2C',
- ],
- [],
- [
- new ShareRecipient(TestShareRecipientType2::class, 'recipient2a', null),
- new ShareRecipient(TestShareRecipientType2::class, 'recipient2b', null),
- new ShareRecipient(TestShareRecipientType2::class, 'recipient2c', null),
- ],
- )
- );
-
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->assertEquals([
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1a',
- 'instance' => null,
- 'display_name' => 'Recipient 1A',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => null,
- 'permissions' => [],
- ],
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1b',
- 'instance' => null,
- 'display_name' => 'Recipient 1B',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => null,
- 'permissions' => [],
- ],
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1c',
- 'instance' => null,
- 'display_name' => 'Recipient 1C',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => null,
- 'permissions' => [],
- ],
- [
- 'class' => TestShareRecipientType2::class,
- 'value' => 'recipient2a',
- 'instance' => null,
- 'display_name' => 'Recipient 2A',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => null,
- 'permissions' => [],
- ],
- [
- 'class' => TestShareRecipientType2::class,
- 'value' => 'recipient2b',
- 'instance' => null,
- 'display_name' => 'Recipient 2B',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => null,
- 'permissions' => [],
- ],
- [
- 'class' => TestShareRecipientType2::class,
- 'value' => 'recipient2c',
- 'instance' => null,
- 'display_name' => 'Recipient 2C',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => null,
- 'permissions' => [],
- ],
- ], $this->searchRecipients($accessContext, null, 'recipient', 10, 0));
-
- $this->assertEquals([
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1a',
- 'instance' => null,
- 'display_name' => 'Recipient 1A',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => null,
- 'permissions' => [],
- ],
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1b',
- 'instance' => null,
- 'display_name' => 'Recipient 1B',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => null,
- 'permissions' => [],
- ],
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1c',
- 'instance' => null,
- 'display_name' => 'Recipient 1C',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => null,
- 'permissions' => [],
- ],
- ], $this->searchRecipients($accessContext, [TestShareRecipientType1::class], 'recipient', 10, 0));
-
- $this->assertEquals([
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1a',
- 'instance' => null,
- 'display_name' => 'Recipient 1A',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => null,
- 'permissions' => [],
- ],
- ], $this->searchRecipients($accessContext, [TestShareRecipientType1::class], 'recipient', 1, 0));
-
- $this->assertEquals([
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1b',
- 'instance' => null,
- 'display_name' => 'Recipient 1B',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => null,
- 'permissions' => [],
- ],
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1c',
- 'instance' => null,
- 'display_name' => 'Recipient 1C',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => null,
- 'permissions' => [],
- ],
- ], $this->searchRecipients($accessContext, [TestShareRecipientType1::class], 'recipient', 10, 1));
- }
-
- public function testSearchRecipientsUniqueDisplayNames(): void {
- $this->registry->clear();
- $this->registry->registerRecipientType(
- new TestShareRecipientType1(
- [
- 'recipient1' => 'Recipient',
- ],
- [],
- [
- new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null),
- ],
- )
- );
- $this->registry->registerRecipientType(
- new TestShareRecipientType2(
- [
- 'recipient2' => 'Recipient',
- 'recipient3' => 'Other',
- ],
- [],
- [
- new ShareRecipient(TestShareRecipientType2::class, 'recipient2', null),
- new ShareRecipient(TestShareRecipientType2::class, 'recipient3', null),
- ],
- )
- );
-
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->assertEquals([
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1',
- 'instance' => null,
- 'display_name' => 'Recipient (TestShareRecipientType1: recipient1)',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => null,
- 'permissions' => [],
- ],
- [
- 'class' => TestShareRecipientType2::class,
- 'value' => 'recipient2',
- 'instance' => null,
- 'display_name' => 'Recipient (TestShareRecipientType2: recipient2)',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => null,
- 'permissions' => [],
- ],
- [
- 'class' => TestShareRecipientType2::class,
- 'value' => 'recipient3',
- 'instance' => null,
- 'display_name' => 'Other',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => null,
- 'permissions' => [],
- ],
- ], $this->searchRecipients($accessContext, null, 'recipient', 10, 0));
- }
-
- public function testSearchRecipientsIcons(): void {
- $this->registry->clear();
- $this->registry->registerRecipientType(
- new TestShareRecipientType1(
- [
- 'svg' => 'SVG',
- 'url' => 'URL',
- ],
- [],
- [
- new ShareRecipient(TestShareRecipientType1::class, 'svg', null),
- new ShareRecipient(TestShareRecipientType1::class, 'url', null),
- ],
- )
- );
-
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->assertEquals([
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'svg',
- 'instance' => null,
- 'display_name' => 'SVG',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => null,
- 'permissions' => [],
- ],
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'url',
- 'instance' => null,
- 'display_name' => 'URL',
- 'icon' => [
- 'light' => 'https://example.com/light.png',
- 'dark' => 'https://example.com/dark.png',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => null,
- 'permissions' => [],
- ],
- ], $this->searchRecipients($accessContext, null, 'icon', 10, 0));
- }
-
- public function testSearchRecipientsOmitExisting(): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $this->dbConnection->commit();
-
- $this->assertEquals([
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1',
- 'instance' => null,
- 'display_name' => 'Recipient 1',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => null,
- 'permissions' => [],
- ],
- [
- 'class' => TestShareRecipientType2::class,
- 'value' => 'recipient2',
- 'instance' => null,
- 'display_name' => 'Recipient 2',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => null,
- 'permissions' => [],
- ],
- ], $this->searchRecipients($accessContext, null, 'recipient', 3, 0, $share));
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
- $this->dbConnection->commit();
-
- $this->assertEquals([
- [
- 'class' => TestShareRecipientType2::class,
- 'value' => 'recipient2',
- 'instance' => null,
- 'display_name' => 'Recipient 2',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => null,
- 'permissions' => [],
- ],
- ], $this->searchRecipients($accessContext, null, 'recipient', 3, 0, $share));
- }
-
- public function testCreateShare(): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $before = $this->manager->getTime();
- $share = $this->createShare($accessContext);
- $after = $this->manager->getTime();
- unset($share['id']);
- $this->assertDateBetween($before, $after, $this->parseTime($share['last_updated']));
- unset($share['last_updated']);
- $this->assertEquals([
- 'owner' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'state' => ShareState::Draft->value,
- 'user_status' => null,
- 'sources' => [],
- 'recipients' => [],
- 'properties' => [],
- 'permissions' => [
- [
- 'class' => ReshareSharePermissionType::class,
- 'source_class' => null,
- 'display_name' => 'Share with others',
- 'hint' => null,
- 'presets' => [],
- 'enabled' => false,
- 'priority' => 90,
- ],
- ],
- 'permission_preset' => null,
- ], $share);
- }
-
- /**
- * @return list, list, list, list, ?string}>
- */
- public static function dataProviderUpdateShareState(): array {
- return [
- [
- [new ShareSource(TestShareSourceType1::class, 'source1')],
- [new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null)],
- [new ShareProperty(TestSharePropertyTypeRequired::class, 'valid1')],
- [new SharePermission(ReshareSharePermissionType::class, true)],
- null,
- ],
- [
- [],
- [new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null)],
- [],
- [new SharePermission(ReshareSharePermissionType::class, true)],
- 'You need to add at least one source to make the share available.',
- ],
- [
- [new ShareSource(TestShareSourceType1::class, 'source1')],
- [],
- [],
- [new SharePermission(ReshareSharePermissionType::class, true)],
- 'You need to add at least one recipient to make the share available.',
- ],
- [
- [new ShareSource(TestShareSourceType1::class, 'source1')],
- [new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null)],
- [new ShareProperty(TestSharePropertyTypeRequired::class, null)],
- [new SharePermission(ReshareSharePermissionType::class, true)],
- 'You need to set a value for the TestSharePropertyTypeRequired',
- ],
- [
- [new ShareSource(TestShareSourceType1::class, 'source1')],
- [new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null)],
- [new ShareProperty(TestSharePropertyTypeRequired::class, 'valid1')],
- [new SharePermission(ReshareSharePermissionType::class, false)],
- 'You need to allow at least one permission to make the share available.',
- ],
- ];
- }
-
- /**
- * @param list $sources
- * @param list $recipients
- * @param list $properties
- * @param list $permissions
- */
- #[DataProvider('dataProviderUpdateShareState')]
- public function testUpdateShareState(array $sources, array $recipients, array $properties, array $permissions, ?string $errorMessage): void {
- $this->registry->registerPropertyType(new TestSharePropertyTypeRequired(['valid1']));
- $this->registry->markPropertyTypeCompatibleWithSourceType(TestSharePropertyTypeRequired::class, TestShareSourceType1::class);
- $this->registry->markPropertyTypeCompatibleWithRecipientType(TestSharePropertyTypeRequired::class, TestShareRecipientType1::class);
-
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- foreach ($sources as $source) {
- $share = $this->manager->addShareSource($accessContext, $share, $source);
- }
-
- foreach ($recipients as $recipient) {
- $share = $this->manager->addShareRecipient($accessContext, $share, $recipient);
- }
-
- foreach ($properties as $property) {
- $share = $this->manager->updateShareProperty($accessContext, $share, $property);
- }
-
- foreach ($permissions as $permission) {
- $share = $this->manager->updateSharePermission($accessContext, $share, $permission);
- }
-
- $this->dbConnection->commit();
-
- if ($errorMessage !== null) {
- try {
- $this->updateShareState($accessContext, $share, ShareState::Active);
- $this->fail('Allowed to set share state active.');
- } catch (HintException $exception) {
- $this->assertEquals($errorMessage, $exception->getHint());
- }
- } else {
- $before = $this->manager->getTime();
- $formatted = $this->updateShareState($accessContext, $share, ShareState::Active);
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals(ShareState::Active->value, $formatted['state']);
- }
- }
-
- public function testUpdateShareUserStatus(): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $before = $this->manager->getTime();
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(TestSharePermissionType1::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
-
- $this->dbConnection->commit();
- $after = $this->manager->getTime();
-
- $this->assertEquals(null, $share->userStatus);
-
- try {
- $this->updateShareUserStatus($accessContext, $share, ShareUserStatus::Accepted);
- $this->fail('Not allowed.');
- } catch (HintException $hintException) {
- $this->assertEquals('Cannot set user status for the owner of the share.', $hintException->getHint());
- }
-
- $accessContext2 = new ShareAccessContext($this->user1);
-
- $this->dbConnection->beginTransaction();
- $share2 = $this->manager->getShare($accessContext2, $share->id);
- $this->dbConnection->commit();
- $this->assertDateBetween($before, $after, $share->lastUpdated);
- $this->assertEquals(ShareUserStatus::Pending, $share2->userStatus);
-
- foreach (ShareUserStatus::cases() as $case) {
- $userStatus = ShareUserStatus::from($case->value);
-
- $formatted = $this->updateShareUserStatus($accessContext2, $share2, $userStatus);
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals($userStatus->value, $formatted['user_status']);
- }
- }
-
- public function testAddShareSource(): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $this->dbConnection->commit();
-
- $before = $this->manager->getTime();
- $formatted = $this->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals([
- [
- 'class' => TestShareSourceType1::class,
- 'value' => 'source1',
- 'display_name' => 'Source 1',
- 'icon' => [
- 'svg' => '',
- ],
- ],
- ], $formatted['sources']);
- }
-
- public function testAddShareSourceInteractionRestricted(): void {
- $listener = function (RestrictInteractionEvent $event): void {
- foreach ($event->resources as $resource) {
- if ($resource instanceof TestInteractionResource && $resource->getID() === 'source1') {
- throw new InteractionRestrictedException('Source not allowed.', 'You are not allowed to add this source.');
- }
- }
- };
- $eventDispatcher = Server::get(IEventDispatcher::class);
- $eventDispatcher->addListener(RestrictInteractionEvent::class, $listener);
-
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
- $share = $this->reloadShare($accessContext, $share);
- $this->dbConnection->commit();
-
- try {
- $this->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $this->fail('Not restricted.');
- } catch (HintException $hintException) {
- $this->assertEquals('You are not allowed to add this source.', $hintException->getHint());
- }
-
- $eventDispatcher->removeListener(RestrictInteractionEvent::class, $listener);
- }
-
- public function testRemoveShareSource(): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType2::class, 'source2'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
-
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(TestSharePermissionType2::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
-
- $this->dbConnection->commit();
-
- $before = $this->manager->getTime();
- $formatted = $this->removeShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->reloadShare($accessContext, $share);
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals(ShareState::Active->value, $formatted['state']);
- $this->assertEquals([
- [
- 'class' => TestShareSourceType2::class,
- 'value' => 'source2',
- 'display_name' => 'Source 2',
- 'icon' => [
- 'svg' => '',
- ],
- ],
- ], $formatted['sources']);
-
- $before = $this->manager->getTime();
- $formatted = $this->removeShareSource($accessContext, $share, new ShareSource(TestShareSourceType2::class, 'source2'));
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals(ShareState::Draft->value, $formatted['state']);
- $this->assertEquals([], $formatted['sources']);
- }
-
- /**
- * @return list
- */
- public static function dataRecipient1(): array {
- return [
- [new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null), 'Recipient 1'],
- [new ShareRecipient(TestShareRecipientType1::class, 'recipient1', 'https://example.com'), 'recipient1'],
- ];
- }
-
- /**
- * @return list
- */
- public static function dataRecipient2(): array {
- return [
- [new ShareRecipient(TestShareRecipientType2::class, 'recipient2', null), 'Recipient 2'],
- [new ShareRecipient(TestShareRecipientType2::class, 'recipient2', 'https://example.org'), 'recipient2'],
- ];
- }
-
- /**
- * @return list
- */
- public static function dataRecipient12(): array {
- return [
- [new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null), 'Recipient 1', new ShareRecipient(TestShareRecipientType2::class, 'recipient2', null), 'Recipient 2'],
- [new ShareRecipient(TestShareRecipientType1::class, 'recipient1', 'https://example.com'), 'recipient1', new ShareRecipient(TestShareRecipientType2::class, 'recipient2', null), 'Recipient 2'],
- [new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null), 'Recipient 1', new ShareRecipient(TestShareRecipientType2::class, 'recipient2', 'https://example.org'), 'recipient2'],
- [new ShareRecipient(TestShareRecipientType1::class, 'recipient1', 'https://example.com'), 'recipient1', new ShareRecipient(TestShareRecipientType2::class, 'recipient2', 'https://example.org'), 'recipient2'],
- ];
- }
-
- #[DataProvider(methodName: 'dataRecipient1')]
- public function testAddShareRecipient(ShareRecipient $recipient1, string $recipient1DisplayName): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $this->dbConnection->commit();
-
- $before = $this->manager->getTime();
- $formatted = $this->addShareRecipient($accessContext, $share, $recipient1);
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals([
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => $recipient1->value,
- 'instance' => $recipient1->instance,
- 'display_name' => $recipient1DisplayName,
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- ], $formatted['recipients']);
- }
-
- #[DataProvider(methodName: 'dataRecipient1')]
- public function testAddShareRecipientInteractionRestricted(ShareRecipient $recipient1, string $recipient1DisplayName): void {
- $listener = function (RestrictInteractionEvent $event): void {
- foreach ($event->receivers as $receiver) {
- if ($receiver instanceof TestInteractionReceiver && $receiver->getID() === 'recipient1') {
- throw new InteractionRestrictedException('Recipient not allowed.', 'You are not allowed to add this recipient.');
- }
- }
- };
- $eventDispatcher = Server::get(IEventDispatcher::class);
- $eventDispatcher->addListener(RestrictInteractionEvent::class, $listener);
-
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
-
- $this->dbConnection->commit();
-
- try {
- $this->addShareRecipient($accessContext, $share, $recipient1);
- $this->fail('Interaction not restricted.');
- } catch (HintException $hintException) {
- $this->assertEquals('You are not allowed to add this recipient.', $hintException->getHint());
- }
-
- $eventDispatcher->removeListener(RestrictInteractionEvent::class, $listener);
- }
-
- #[DataProvider(methodName: 'dataRecipient2')]
- public function testAddChildShareRecipientWithoutResharePermission(ShareRecipient $recipient2, string $recipient2DisplayName): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
-
- $share = $this->manager->getShare($accessContext, $share->id);
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(TestSharePermissionType1::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
-
- $this->dbConnection->commit();
-
- try {
- $this->addShareRecipient(new ShareAccessContext($this->user1), $share, $recipient2);
- $this->fail('Able to add child recipient without reshare permission.');
- } catch (HintException $hintException) {
- $this->assertEquals('You are not allowed to edit this share.', $hintException->getHint());
- }
- }
-
- #[DataProvider(methodName: 'dataRecipient2')]
- public function testAddChildShareRecipientWithResharePermission(ShareRecipient $recipient2, string $recipient2DisplayName): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
-
- $share = $this->manager->getShare($accessContext, $share->id);
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(ReshareSharePermissionType::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
-
- $this->dbConnection->commit();
-
- $accessContext2 = new ShareAccessContext($this->user1);
-
- $this->dbConnection->beginTransaction();
- $share2 = $this->manager->getShare($accessContext2, $share->id);
- $this->dbConnection->commit();
-
- $before = $this->manager->getTime();
- $formatted = $this->addShareRecipient($accessContext2, $share2, $recipient2);
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals([
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1',
- 'instance' => null,
- 'display_name' => 'Recipient 1',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- [
- 'class' => TestShareRecipientType2::class,
- 'value' => $recipient2->value,
- 'instance' => $recipient2->instance,
- 'display_name' => $recipient2DisplayName,
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'user1',
- 'instance' => null,
- 'display_name' => 'User 1',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/user1/64',
- 'dark' => 'http://localhost/index.php/avatar/user1/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- ], $formatted['recipients']);
- }
-
- #[DataProvider(methodName: 'dataRecipient12')]
- public function testRemoveShareRecipient(ShareRecipient $recipient1, string $recipient1DisplayName, ShareRecipient $recipient2, string $recipient2DisplayName): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, $recipient1);
- $share = $this->manager->addShareRecipient($accessContext, $share, $recipient2);
-
- $share = $this->manager->getShare($accessContext, $share->id);
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(TestSharePermissionType1::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
-
- $this->dbConnection->commit();
-
- $before = $this->manager->getTime();
- $formatted = $this->removeShareRecipient($accessContext, $share, $recipient1);
- $share = $this->reloadShare($accessContext, $share);
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals(ShareState::Active->value, $formatted['state']);
- $this->assertEquals([
- [
- 'class' => TestShareRecipientType2::class,
- 'value' => $recipient2->value,
- 'instance' => $recipient2->instance,
- 'display_name' => $recipient2DisplayName,
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- ], $formatted['recipients']);
-
- $before = $this->manager->getTime();
- $formatted = $this->removeShareRecipient($accessContext, $share, $recipient2);
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals(ShareState::Draft->value, $formatted['state']);
- $this->assertEquals([], $formatted['recipients']);
- }
-
- public function testRemoveSelfShareRecipientWithoutResharePermission(): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
-
- $share = $this->manager->getShare($accessContext, $share->id);
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(TestSharePermissionType1::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
-
- $this->dbConnection->commit();
-
- try {
- $this->removeShareRecipient(new ShareAccessContext($this->user1), $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
- $this->fail('Able to remove self recipient.');
- } catch (HintException $hintException) {
- $this->assertEquals('You are not allowed to edit this share.', $hintException->getHint());
- }
- }
-
- public function testRemoveSelfShareRecipientWithResharePermission(): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
-
- $share = $this->manager->getShare($accessContext, $share->id);
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(ReshareSharePermissionType::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
-
- $this->dbConnection->commit();
-
- try {
- $this->removeShareRecipient(new ShareAccessContext($this->user1), $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
- $this->fail('Able to remove self recipient.');
- } catch (HintException $hintException) {
- $this->assertEquals('You are not allowed to edit this share.', $hintException->getHint());
- }
- }
-
- #[DataProvider(methodName: 'dataRecipient2')]
- public function testRemoveChildShareRecipientWithoutResharePermission(ShareRecipient $recipient2, string $recipient2DisplayName): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
-
- $share = $this->manager->getShare($accessContext, $share->id);
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(TestSharePermissionType1::class, true));
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(ReshareSharePermissionType::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
- $share = $this->manager->addShareRecipient(new ShareAccessContext($this->user1), $share, $recipient2);
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(ReshareSharePermissionType::class, false));
-
- $this->dbConnection->commit();
-
- try {
- $this->removeShareRecipient(new ShareAccessContext($this->user1), $share, $recipient2);
- $this->fail('Able to remove child recipient without reshare permission.');
- } catch (HintException $hintException) {
- $this->assertEquals('You are not allowed to edit this share.', $hintException->getHint());
- }
- }
-
- #[DataProvider(methodName: 'dataRecipient2')]
- public function testRemoveChildShareRecipientWithResharePermission(ShareRecipient $recipient2, string $recipient2DisplayName): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
-
- $share = $this->manager->getShare($accessContext, $share->id);
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(ReshareSharePermissionType::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
- $share = $this->manager->addShareRecipient(new ShareAccessContext($this->user1), $share, $recipient2);
-
- $this->dbConnection->commit();
-
- $accessContext2 = new ShareAccessContext($this->user1);
-
- $this->dbConnection->beginTransaction();
- $share2 = $this->manager->getShare($accessContext2, $share->id);
- $this->dbConnection->commit();
-
- $before = $this->manager->getTime();
- $formatted = $this->removeShareRecipient($accessContext2, $share2, $recipient2);
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals([
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1',
- 'instance' => null,
- 'display_name' => 'Recipient 1',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- ], $formatted['recipients']);
- }
-
- #[DataProvider(methodName: 'dataRecipient2')]
- public function testRemoveSiblingShareRecipientWithoutResharePermission(ShareRecipient $recipient2, string $recipient2DisplayName): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
- $share = $this->manager->addShareRecipient($accessContext, $share, $recipient2);
-
- $share = $this->manager->getShare($accessContext, $share->id);
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(TestSharePermissionType1::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
-
- $this->dbConnection->commit();
-
- try {
- $this->removeShareRecipient(new ShareAccessContext($this->user1), $share, $recipient2);
- $this->fail('Able to remove sibling recipient.');
- } catch (HintException $hintException) {
- $this->assertEquals('You are not allowed to edit this share.', $hintException->getHint());
- }
- }
-
- #[DataProvider(methodName: 'dataRecipient2')]
- public function testRemoveSiblingShareRecipientWithResharePermission(ShareRecipient $recipient2, string $recipient2DisplayName): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
- $share = $this->manager->addShareRecipient($accessContext, $share, $recipient2);
- $share = $this->manager->getShare($accessContext, $share->id);
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(ReshareSharePermissionType::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
-
- $this->dbConnection->commit();
-
- try {
- $this->removeShareRecipient(new ShareAccessContext($this->user1), $share, $recipient2);
- $this->fail('Able to remove sibling recipient.');
- } catch (HintException $hintException) {
- $this->assertEquals('You are not allowed to edit this share.', $hintException->getHint());
- }
- }
-
- public function testRemoveParentShareRecipientWithoutResharePermission(): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
- $share = $this->manager->getShare($accessContext, $share->id);
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(TestSharePermissionType1::class, true));
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(ReshareSharePermissionType::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
- $share = $this->manager->addShareRecipient(
- new ShareAccessContext($this->user1), $share, new ShareRecipient(TestShareRecipientType2::class, 'recipient2', null)
- );
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(ReshareSharePermissionType::class, false));
-
- $this->dbConnection->commit();
-
- try {
- $this->removeShareRecipient(new ShareAccessContext($this->user2), $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
- $this->fail('Able to remove parent recipient.');
- } catch (HintException $hintException) {
- $this->assertEquals('You are not allowed to edit this share.', $hintException->getHint());
- }
- }
-
- public function testRemoveParentShareRecipientWithResharePermission(): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
- $share = $this->manager->getShare($accessContext, $share->id);
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(ReshareSharePermissionType::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
- $share = $this->manager->addShareRecipient(
- new ShareAccessContext($this->user1), $share, new ShareRecipient(TestShareRecipientType2::class, 'recipient2', null)
- );
-
- $this->dbConnection->commit();
-
- try {
- $this->removeShareRecipient(new ShareAccessContext($this->user2), $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
- $this->fail('Able to remove parent recipient.');
- } catch (HintException $hintException) {
- $this->assertEquals('You are not allowed to edit this share.', $hintException->getHint());
- }
- }
-
- /**
- * @return list
- */
- public static function dataUpdateShareRecipientSecret(): array {
- return [
- [true],
- [false],
- ];
- }
-
- #[DataProvider('dataUpdateShareRecipientSecret')]
- public function testUpdateShareRecipientSecret(bool $isSecretUpdatable): void {
- $this->registry->registerRecipientType(
- new TestShareRecipientTypePublicSecret(
- [
- 'recipient1' => 'Recipient 1',
- ],
- [],
- true,
- $isSecretUpdatable,
- )
- );
-
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $recipient = new ShareRecipient(TestShareRecipientTypePublicSecret::class, 'recipient1', null);
- $share = $this->manager->addShareRecipient($accessContext, $share, $recipient);
- $this->dbConnection->commit();
-
- if (!$isSecretUpdatable) {
- try {
- $this->updateShareRecipientSecret($accessContext, $share, $recipient, 'mysecret');
- $this->fail('Able to update recipient secret.');
- } catch (HintException $exception) {
- $this->assertEquals('You are not allowed to edit this share.', $exception->getHint());
- }
- } else {
- $before = $this->manager->getTime();
- $formatted = $this->updateShareRecipientSecret($accessContext, $share, $recipient, 'mysecret');
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- unset($formatted['last_updated']);
- $this->assertEquals([
- [
- 'class' => TestShareRecipientTypePublicSecret::class,
- 'value' => 'recipient1',
- 'instance' => null,
- 'display_name' => 'Recipient 1',
- 'icon' => [
- 'svg' => ''
- ],
- 'secret' => [
- 'updatable' => true,
- 'value' => 'mysecret',
- 'url' => 'http://localhost/index.php/s/mysecret',
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- ], $formatted['recipients']);
- }
- }
-
- /**
- * @return list}>
- */
- public static function dataProviderUpdateShareProperty(): array {
- return [
- [[null, 'valid1']],
- [['valid1', null]],
- ];
- }
-
- /**
- * @param list $values
- */
- #[DataProvider('dataProviderUpdateShareProperty')]
- public function testUpdateShareProperty(array $values): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
- $share = $this->manager->getShare($accessContext, $share->id);
-
- $this->dbConnection->commit();
-
- foreach ($values as $value) {
- $before = $this->manager->getTime();
- $formatted = $this->updateShareProperty($accessContext, $share, new ShareProperty(TestSharePropertyType1::class, $value));
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals([
- [
- 'class' => TestSharePropertyType1::class,
- 'display_name' => 'TestSharePropertyType1',
- 'hint' => 'hint TestSharePropertyType1',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => $value,
- 'type' => 'enum',
- 'valid_values' => ['valid1'],
- ],
- ], $formatted['properties']);
- }
- }
-
- public function testUpdateSharePropertyRequired(): void {
- $this->registry->registerPropertyType(new TestSharePropertyTypeRequired(['valid1', 'valid2']));
- $this->registry->markPropertyTypeCompatibleWithSourceType(TestSharePropertyTypeRequired::class, TestShareSourceType1::class);
- $this->registry->markPropertyTypeCompatibleWithRecipientType(TestSharePropertyTypeRequired::class, TestShareRecipientType1::class);
-
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
-
- $share = $this->manager->getShare($accessContext, $share->id);
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(TestSharePermissionType1::class, true));
-
- $this->dbConnection->commit();
-
- $before = $this->manager->getTime();
- $formatted = $this->updateShareProperty($accessContext, $share, new ShareProperty(TestSharePropertyTypeRequired::class, 'valid1'));
- $share = $this->reloadShare($accessContext, $share);
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals(ShareState::Draft->value, $formatted['state']);
- $this->assertEquals([
- [
- 'class' => TestSharePropertyType1::class,
- 'display_name' => 'TestSharePropertyType1',
- 'hint' => 'hint TestSharePropertyType1',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => null,
- 'type' => 'enum',
- 'valid_values' => ['valid1'],
- ],
- [
- 'class' => TestSharePropertyTypeRequired::class,
- 'display_name' => 'TestSharePropertyTypeRequired',
- 'hint' => 'hint TestSharePropertyTypeRequired',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => true,
- 'value' => 'valid1',
- 'type' => 'enum',
- 'valid_values' => ['valid1', 'valid2'],
- ],
- ], $formatted['properties']);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
- $this->dbConnection->commit();
-
- $before = $this->manager->getTime();
- $formatted = $this->updateShareProperty($accessContext, $share, new ShareProperty(TestSharePropertyTypeRequired::class, 'valid2'));
- $share = $this->reloadShare($accessContext, $share);
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals(ShareState::Active->value, $formatted['state']);
- $this->assertEquals([
- [
- 'class' => TestSharePropertyType1::class,
- 'display_name' => 'TestSharePropertyType1',
- 'hint' => 'hint TestSharePropertyType1',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => null,
- 'type' => 'enum',
- 'valid_values' => ['valid1'],
- ],
- [
- 'class' => TestSharePropertyTypeRequired::class,
- 'display_name' => 'TestSharePropertyTypeRequired',
- 'hint' => 'hint TestSharePropertyTypeRequired',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => true,
- 'value' => 'valid2',
- 'type' => 'enum',
- 'valid_values' => ['valid1', 'valid2'],
- ],
- ], $formatted['properties']);
-
- $before = $this->manager->getTime();
- $formatted = $this->updateShareProperty($accessContext, $share, new ShareProperty(TestSharePropertyTypeRequired::class, null));
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals(ShareState::Draft->value, $formatted['state']);
- $this->assertEquals([
- [
- 'class' => TestSharePropertyType1::class,
- 'display_name' => 'TestSharePropertyType1',
- 'hint' => 'hint TestSharePropertyType1',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => null,
- 'type' => 'enum',
- 'valid_values' => ['valid1'],
- ],
- [
- 'class' => TestSharePropertyTypeRequired::class,
- 'display_name' => 'TestSharePropertyTypeRequired',
- 'hint' => 'hint TestSharePropertyTypeRequired',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => true,
- 'value' => null,
- 'type' => 'enum',
- 'valid_values' => ['valid1', 'valid2'],
- ],
- ], $formatted['properties']);
- }
-
- public function testUpdateSharePropertyModifyProperties(): void {
- $this->registry->registerPropertyType(
- new TestSharePropertyTypeModifyValue(['old-value', 'modify-on-save-old-value', 'modify-on-save', 'modify-on-load'])
- );
- $this->registry->markPropertyTypeCompatibleWithSourceType(TestSharePropertyTypeModifyValue::class, TestShareSourceType1::class);
- $this->registry->markPropertyTypeCompatibleWithRecipientType(TestSharePropertyTypeModifyValue::class, TestShareRecipientType1::class);
-
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
-
- $this->dbConnection->commit();
-
- // We cannot test for modify-on-save, because we will always see modified-on-save as the returned value.
-
- $formatted = $this->getShare($accessContext, $share->id);
- $this->assertEquals([
- [
- 'class' => TestSharePropertyType1::class,
- 'display_name' => 'TestSharePropertyType1',
- 'hint' => 'hint TestSharePropertyType1',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => null,
- 'type' => 'enum',
- 'valid_values' => ['valid1'],
- ],
- [
- 'class' => TestSharePropertyTypeModifyValue::class,
- 'display_name' => 'TestSharePropertyTypeModifyValue',
- 'hint' => 'hint TestSharePropertyTypeModifyValue',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => 'modified-on-save',
- 'type' => 'enum',
- 'valid_values' => ['old-value', 'modify-on-save-old-value', 'modify-on-save', 'modify-on-load'],
- ],
- ], $formatted['properties']);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->updateShareProperty($accessContext, $share, new ShareProperty(TestSharePropertyTypeModifyValue::class, 'old-value'));
- $this->dbConnection->commit();
-
- $before = $this->manager->getTime();
- $formatted = $this->updateShareProperty($accessContext, $share, new ShareProperty(TestSharePropertyTypeModifyValue::class, 'modify-on-save-old-value'));
- $share = $this->reloadShare($accessContext, $share);
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals([
- [
- 'class' => TestSharePropertyType1::class,
- 'display_name' => 'TestSharePropertyType1',
- 'hint' => 'hint TestSharePropertyType1',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => null,
- 'type' => 'enum',
- 'valid_values' => ['valid1'],
- ],
- [
- 'class' => TestSharePropertyTypeModifyValue::class,
- 'display_name' => 'TestSharePropertyTypeModifyValue',
- 'hint' => 'hint TestSharePropertyTypeModifyValue',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => 'old-value',
- 'type' => 'enum',
- 'valid_values' => ['old-value', 'modify-on-save-old-value', 'modify-on-save', 'modify-on-load'],
- ],
- ], $formatted['properties']);
-
- $before = $this->manager->getTime();
- $formatted = $this->updateShareProperty($accessContext, $share, new ShareProperty(TestSharePropertyTypeModifyValue::class, 'modify-on-save'));
- $share = $this->reloadShare($accessContext, $share);
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals([
- [
- 'class' => TestSharePropertyType1::class,
- 'display_name' => 'TestSharePropertyType1',
- 'hint' => 'hint TestSharePropertyType1',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => null,
- 'type' => 'enum',
- 'valid_values' => ['valid1'],
- ],
- [
- 'class' => TestSharePropertyTypeModifyValue::class,
- 'display_name' => 'TestSharePropertyTypeModifyValue',
- 'hint' => 'hint TestSharePropertyTypeModifyValue',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => 'modified-on-save',
- 'type' => 'enum',
- 'valid_values' => ['old-value', 'modify-on-save-old-value', 'modify-on-save', 'modify-on-load'],
- ],
- ], $formatted['properties']);
-
- $before = $this->manager->getTime();
- $formatted = $this->updateShareProperty($accessContext, $share, new ShareProperty(TestSharePropertyTypeModifyValue::class, 'modify-on-load'));
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals([
- [
- 'class' => TestSharePropertyType1::class,
- 'display_name' => 'TestSharePropertyType1',
- 'hint' => 'hint TestSharePropertyType1',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => null,
- 'type' => 'enum',
- 'valid_values' => ['valid1'],
- ],
- [
- 'class' => TestSharePropertyTypeModifyValue::class,
- 'display_name' => 'TestSharePropertyTypeModifyValue',
- 'hint' => 'hint TestSharePropertyTypeModifyValue',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => 'modified-on-load',
- 'type' => 'enum',
- 'valid_values' => ['old-value', 'modify-on-save-old-value', 'modify-on-save', 'modify-on-load'],
- ],
- ], $formatted['properties']);
- }
-
- public function testUpdateSharePermission(): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
- $share = $this->manager->getShare($accessContext, $share->id);
-
- $this->dbConnection->commit();
-
- $before = $this->manager->getTime();
- $this->updateSharePermission($accessContext, $share, new SharePermission(ReshareSharePermissionType::class, true));
- $share = $this->reloadShare($accessContext, $share);
- $formatted = $this->updateSharePermission($accessContext, $share, new SharePermission(TestSharePermissionType1::class, true));
- $share = $this->reloadShare($accessContext, $share);
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals(ShareState::Draft->value, $formatted['state']);
- $this->assertEquals([
- [
- 'class' => ReshareSharePermissionType::class,
- 'source_class' => null,
- 'display_name' => 'Share with others',
- 'hint' => null,
- 'presets' => [],
- 'enabled' => true,
- 'priority' => 90,
- ],
- [
- 'class' => TestSharePermissionType1::class,
- 'source_class' => TestShareSourceType1::class,
- 'display_name' => 'TestSharePermissionType1',
- 'hint' => 'hint TestSharePermissionType1',
- 'presets' => [TestSharePermissionPreset1::class, TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- ], $formatted['permissions']);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
- $this->dbConnection->commit();
-
- $before = $this->manager->getTime();
- $formatted = $this->updateSharePermission($accessContext, $share, new SharePermission(ReshareSharePermissionType::class, false));
- $share = $this->reloadShare($accessContext, $share);
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals(ShareState::Active->value, $formatted['state']);
- $this->assertEquals([
- [
- 'class' => ReshareSharePermissionType::class,
- 'source_class' => null,
- 'display_name' => 'Share with others',
- 'hint' => null,
- 'presets' => [],
- 'enabled' => false,
- 'priority' => 90,
- ],
- [
- 'class' => TestSharePermissionType1::class,
- 'source_class' => TestShareSourceType1::class,
- 'display_name' => 'TestSharePermissionType1',
- 'hint' => 'hint TestSharePermissionType1',
- 'presets' => [TestSharePermissionPreset1::class, TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- ], $formatted['permissions']);
-
- $before = $this->manager->getTime();
- $formatted = $this->updateSharePermission($accessContext, $share, new SharePermission(TestSharePermissionType1::class, false));
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals(ShareState::Draft->value, $formatted['state']);
- $this->assertEquals([
- [
- 'class' => ReshareSharePermissionType::class,
- 'source_class' => null,
- 'display_name' => 'Share with others',
- 'hint' => null,
- 'presets' => [],
- 'enabled' => false,
- 'priority' => 90,
- ],
- [
- 'class' => TestSharePermissionType1::class,
- 'source_class' => TestShareSourceType1::class,
- 'display_name' => 'TestSharePermissionType1',
- 'hint' => 'hint TestSharePermissionType1',
- 'presets' => [TestSharePermissionPreset1::class, TestSharePermissionPreset2::class],
- 'enabled' => false,
- 'priority' => 1,
- ],
- ], $formatted['permissions']);
- }
-
- public function testUpdateSharePermissionInteractionRestricted(): void {
- $listener = function (RestrictInteractionEvent $event): void {
- if ($event->action instanceof ShareAction && $event->action->unifiedSharingPermissions !== null && in_array(
- TestSharePermissionType1::class, $event->action->unifiedSharingPermissions, true
- )) {
- throw new InteractionRestrictedException('Permission not allowed.', 'You are not allowed to enable this permission.');
- }
- };
- $eventDispatcher = Server::get(IEventDispatcher::class);
- $eventDispatcher->addListener(RestrictInteractionEvent::class, $listener);
-
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
-
- $this->dbConnection->commit();
-
- try {
- $this->updateSharePermission($accessContext, $share, new SharePermission(TestSharePermissionType1::class, true));
- $this->fail('Not restricted.');
- } catch (HintException $hintException) {
- $this->assertEquals('You are not allowed to enable this permission.', $hintException->getHint());
- }
-
- $eventDispatcher->removeListener(RestrictInteractionEvent::class, $listener);
- }
-
- public function testUpdateShareRecipientPermission(): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(ReshareSharePermissionType::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
-
- $this->dbConnection->commit();
-
- $accessContext1 = new ShareAccessContext($this->user1);
- $recipient2 = new ShareRecipient(TestShareRecipientType2::class, 'recipient2', null);
- $this->dbConnection->beginTransaction();
- $share1 = $this->manager->getShare($accessContext1, $share->id);
- $share1 = $this->manager->addShareRecipient($accessContext1, $share1, $recipient2);
-
- $this->dbConnection->commit();
- $formatted = $this->updateShareRecipientPermission(
- $accessContext1, $share1, $recipient2, new SharePermission(ReshareSharePermissionType::class, false)
- );
- $this->assertEquals([
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1',
- 'instance' => null,
- 'display_name' => 'Recipient 1',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- [
- 'class' => TestShareRecipientType2::class,
- 'value' => 'recipient2',
- 'instance' => null,
- 'display_name' => 'Recipient 2',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'user1',
- 'instance' => null,
- 'display_name' => 'User 1',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/user1/64',
- 'dark' => 'http://localhost/index.php/avatar/user1/64/dark',
- ],
- ],
- 'permissions' => [
- [
- 'class' => ReshareSharePermissionType::class,
- 'source_class' => null,
- 'display_name' => 'Share with others',
- 'hint' => null,
- 'presets' => [],
- 'enabled' => false,
- 'priority' => 90,
- ],
- ],
- ],
- ], $formatted['recipients']);
-
- $accessContext2 = new ShareAccessContext($this->user2);
- $this->dbConnection->beginTransaction();
- $share2 = $this->manager->getShare($accessContext2, $share->id);
- try {
- $this->manager->addShareRecipient($accessContext2, $share2, new ShareRecipient(TestShareRecipientType2::class, 'recipient3', null));
- $this->fail('Adding share recipient without reshare permission should fail.');
- } catch (HintException $hintException) {
- $this->assertEquals('You are not allowed to edit this share.', $hintException->getHint());
- } finally {
- $this->dbConnection->commit();
- }
-
- $formatted = $this->updateShareRecipientPermission($accessContext1, $share1, $recipient2, new SharePermission(ReshareSharePermissionType::class, true));
- $this->assertEquals([
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1',
- 'instance' => null,
- 'display_name' => 'Recipient 1',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- [
- 'class' => TestShareRecipientType2::class,
- 'value' => 'recipient2',
- 'instance' => null,
- 'display_name' => 'Recipient 2',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'user1',
- 'instance' => null,
- 'display_name' => 'User 1',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/user1/64',
- 'dark' => 'http://localhost/index.php/avatar/user1/64/dark',
- ],
- ],
- 'permissions' => [
- [
- 'class' => ReshareSharePermissionType::class,
- 'source_class' => null,
- 'display_name' => 'Share with others',
- 'hint' => null,
- 'presets' => [],
- 'enabled' => true,
- 'priority' => 90,
- ],
- ],
- ],
- ], $formatted['recipients']);
-
- $this->dbConnection->beginTransaction();
- $share2 = $this->manager->getShare($accessContext2, $share->id);
- $this->manager->addShareRecipient($accessContext2, $share2, new ShareRecipient(TestShareRecipientType2::class, 'recipient3', null));
- $this->dbConnection->commit();
- }
-
- public function testSelectSharePermissionPreset(): void {
- $this->registry->clear();
- $this->registry->registerPermissionPreset(new TestSharePermissionPreset1());
- $this->registry->registerPermissionPreset(new TestSharePermissionPreset2());
- $this->registry->registerPermissionType(null, new TestSharePermissionType1());
- $this->registry->markPermissionTypeCompatibleWithPermissionPreset(TestSharePermissionType1::class, TestSharePermissionPreset1::class);
- $this->registry->markPermissionTypeCompatibleWithPermissionPreset(TestSharePermissionType1::class, TestSharePermissionPreset2::class);
- $this->registry->registerPermissionType(null, new TestSharePermissionType2());
- $this->registry->markPermissionTypeCompatibleWithPermissionPreset(TestSharePermissionType2::class, TestSharePermissionPreset2::class);
- $this->registry->registerPermissionType(null, new TestSharePermissionType3());
-
- $accessContext = new ShareAccessContext($this->owner);
-
- $before = $this->manager->getTime();
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $this->manager->getShare($accessContext, $share->id);
- $this->dbConnection->commit();
- $after = $this->manager->getTime();
-
- $formatted = $this->getShare($accessContext, $share->id);
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertNull($formatted['permission_preset']);
- $this->assertEquals([
- [
- 'class' => TestSharePermissionType1::class,
- 'source_class' => null,
- 'display_name' => 'TestSharePermissionType1',
- 'hint' => 'hint TestSharePermissionType1',
- 'presets' => [TestSharePermissionPreset1::class, TestSharePermissionPreset2::class],
- 'enabled' => false,
- 'priority' => 1,
- ],
- [
- 'class' => TestSharePermissionType2::class,
- 'source_class' => null,
- 'display_name' => 'TestSharePermissionType2',
- 'hint' => 'hint TestSharePermissionType2',
- 'presets' => [TestSharePermissionPreset2::class],
- 'enabled' => false,
- 'priority' => 1,
- ],
- [
- 'class' => TestSharePermissionType3::class,
- 'source_class' => null,
- 'display_name' => 'TestSharePermissionType3',
- 'hint' => 'hint TestSharePermissionType3',
- 'presets' => [],
- 'enabled' => false,
- 'priority' => 1,
- ],
- ], $formatted['permissions']);
-
- $before = $this->manager->getTime();
- $formatted = $this->selectSharePermissionPreset($accessContext, $share, TestSharePermissionPreset2::class);
- $share = $this->reloadShare($accessContext, $share);
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals(TestSharePermissionPreset2::class, $formatted['permission_preset']);
- $this->assertEquals([
- [
- 'class' => TestSharePermissionType1::class,
- 'source_class' => null,
- 'display_name' => 'TestSharePermissionType1',
- 'hint' => 'hint TestSharePermissionType1',
- 'presets' => [TestSharePermissionPreset1::class, TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- [
- 'class' => TestSharePermissionType2::class,
- 'source_class' => null,
- 'display_name' => 'TestSharePermissionType2',
- 'hint' => 'hint TestSharePermissionType2',
- 'presets' => [TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- [
- 'class' => TestSharePermissionType3::class,
- 'source_class' => null,
- 'display_name' => 'TestSharePermissionType3',
- 'hint' => 'hint TestSharePermissionType3',
- 'presets' => [],
- 'enabled' => false,
- 'priority' => 1,
- ],
- ], $formatted['permissions']);
-
- $before = $this->manager->getTime();
- $formatted = $this->updateSharePermission($accessContext, $share, new SharePermission(TestSharePermissionType3::class, true));
- $share = $this->reloadShare($accessContext, $share);
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertNull($formatted['permission_preset']);
- $this->assertEquals([
- [
- 'class' => TestSharePermissionType1::class,
- 'source_class' => null,
- 'display_name' => 'TestSharePermissionType1',
- 'hint' => 'hint TestSharePermissionType1',
- 'presets' => [TestSharePermissionPreset1::class, TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- [
- 'class' => TestSharePermissionType2::class,
- 'source_class' => null,
- 'display_name' => 'TestSharePermissionType2',
- 'hint' => 'hint TestSharePermissionType2',
- 'presets' => [TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- [
- 'class' => TestSharePermissionType3::class,
- 'source_class' => null,
- 'display_name' => 'TestSharePermissionType3',
- 'hint' => 'hint TestSharePermissionType3',
- 'presets' => [],
- 'enabled' => true,
- 'priority' => 1,
- ],
- ], $formatted['permissions']);
-
- $before = $this->manager->getTime();
- $formatted = $this->selectSharePermissionPreset($accessContext, $share, TestSharePermissionPreset1::class);
- $share = $this->reloadShare($accessContext, $share);
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals(TestSharePermissionPreset1::class, $formatted['permission_preset']);
- $this->assertEquals([
- [
- 'class' => TestSharePermissionType1::class,
- 'source_class' => null,
- 'display_name' => 'TestSharePermissionType1',
- 'hint' => 'hint TestSharePermissionType1',
- 'presets' => [TestSharePermissionPreset1::class, TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- [
- 'class' => TestSharePermissionType2::class,
- 'source_class' => null,
- 'display_name' => 'TestSharePermissionType2',
- 'hint' => 'hint TestSharePermissionType2',
- 'presets' => [TestSharePermissionPreset2::class],
- 'enabled' => false,
- 'priority' => 1,
- ],
- [
- 'class' => TestSharePermissionType3::class,
- 'source_class' => null,
- 'display_name' => 'TestSharePermissionType3',
- 'hint' => 'hint TestSharePermissionType3',
- 'presets' => [],
- 'enabled' => false,
- 'priority' => 1,
- ],
- ], $formatted['permissions']);
-
- $before = $this->manager->getTime();
- $formatted = $this->updateSharePermission($accessContext, $share, new SharePermission(TestSharePermissionType1::class, false));
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertNull($formatted['permission_preset']);
- $this->assertEquals([
- [
- 'class' => TestSharePermissionType1::class,
- 'source_class' => null,
- 'display_name' => 'TestSharePermissionType1',
- 'hint' => 'hint TestSharePermissionType1',
- 'presets' => [TestSharePermissionPreset1::class, TestSharePermissionPreset2::class],
- 'enabled' => false,
- 'priority' => 1,
- ],
- [
- 'class' => TestSharePermissionType2::class,
- 'source_class' => null,
- 'display_name' => 'TestSharePermissionType2',
- 'hint' => 'hint TestSharePermissionType2',
- 'presets' => [TestSharePermissionPreset2::class],
- 'enabled' => false,
- 'priority' => 1,
- ],
- [
- 'class' => TestSharePermissionType3::class,
- 'source_class' => null,
- 'display_name' => 'TestSharePermissionType3',
- 'hint' => 'hint TestSharePermissionType3',
- 'presets' => [],
- 'enabled' => false,
- 'priority' => 1,
- ],
- ], $formatted['permissions']);
- }
-
- public function testSelectSharePermissionPresetCompatible(): void {
- $this->registry->clear();
- $this->registry->registerSourceType(new TestShareSourceType1(['source1' => 'Source 1']));
- $this->registry->registerPermissionPreset(new TestSharePermissionPreset1());
- $this->registry->registerPermissionPreset(new TestSharePermissionPreset2());
- $this->registry->registerPermissionType(TestShareSourceType1::class, new TestSharePermissionType1());
- $this->registry->markPermissionTypeCompatibleWithPermissionPreset(TestSharePermissionType1::class, TestSharePermissionPreset1::class);
- $this->registry->markPermissionTypeCompatibleWithPermissionPreset(TestSharePermissionType1::class, TestSharePermissionPreset2::class);
- $this->registry->registerPermissionType(null, new TestSharePermissionType2());
- $this->registry->markPermissionTypeCompatibleWithPermissionPreset(TestSharePermissionType2::class, TestSharePermissionPreset2::class);
-
- $accessContext = new ShareAccessContext($this->owner);
-
- $before = $this->manager->getTime();
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $this->manager->getShare($accessContext, $share->id);
- $this->dbConnection->commit();
- $after = $this->manager->getTime();
-
- $formatted = $this->getShare($accessContext, $share->id);
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertNull($formatted['permission_preset']);
- $this->assertEquals([
- [
- 'class' => TestSharePermissionType2::class,
- 'source_class' => null,
- 'display_name' => 'TestSharePermissionType2',
- 'hint' => 'hint TestSharePermissionType2',
- 'presets' => [TestSharePermissionPreset2::class],
- 'enabled' => false,
- 'priority' => 1,
- ],
- ], $formatted['permissions']);
-
- $before = $this->manager->getTime();
- $formatted = $this->selectSharePermissionPreset($accessContext, $share, TestSharePermissionPreset2::class);
- $share = $this->reloadShare($accessContext, $share);
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals(TestSharePermissionPreset2::class, $formatted['permission_preset']);
- $this->assertEquals([
- [
- 'class' => TestSharePermissionType2::class,
- 'source_class' => null,
- 'display_name' => 'TestSharePermissionType2',
- 'hint' => 'hint TestSharePermissionType2',
- 'presets' => [TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- ], $formatted['permissions']);
-
- $before = $this->manager->getTime();
- $formatted = $this->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->reloadShare($accessContext, $share);
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertNull($formatted['permission_preset']);
- $this->assertEquals([
- [
- 'class' => TestSharePermissionType1::class,
- 'source_class' => TestShareSourceType1::class,
- 'display_name' => 'TestSharePermissionType1',
- 'hint' => 'hint TestSharePermissionType1',
- 'presets' => [TestSharePermissionPreset1::class, TestSharePermissionPreset2::class],
- 'enabled' => false,
- 'priority' => 1,
- ],
- [
- 'class' => TestSharePermissionType2::class,
- 'source_class' => null,
- 'display_name' => 'TestSharePermissionType2',
- 'hint' => 'hint TestSharePermissionType2',
- 'presets' => [TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- ], $formatted['permissions']);
-
- $before = $this->manager->getTime();
- $formatted = $this->selectSharePermissionPreset($accessContext, $share, TestSharePermissionPreset2::class);
- $after = $this->manager->getTime();
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals(TestSharePermissionPreset2::class, $formatted['permission_preset']);
- $this->assertEquals([
- [
- 'class' => TestSharePermissionType1::class,
- 'source_class' => TestShareSourceType1::class,
- 'display_name' => 'TestSharePermissionType1',
- 'hint' => 'hint TestSharePermissionType1',
- 'presets' => [TestSharePermissionPreset1::class, TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- [
- 'class' => TestSharePermissionType2::class,
- 'source_class' => null,
- 'display_name' => 'TestSharePermissionType2',
- 'hint' => 'hint TestSharePermissionType2',
- 'presets' => [TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- ], $formatted['permissions']);
- }
-
- public function testDeleteShare(): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
-
- $this->deleteShare($accessContext, $share);
-
- try {
- $this->manager->getShare(new ShareAccessContext(overrideChecks: true), $share->id);
- $this->fail('Share not deleted.');
- } catch (HintException $hintException) {
- $this->assertEquals('Share not found.', $hintException->getHint());
- } finally {
- $this->dbConnection->commit();
- }
- }
-
- public function testGetShare(): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $before = $this->manager->getTime();
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
- $this->manager->getShare($accessContext, $share->id);
-
- $this->dbConnection->commit();
- $after = $this->manager->getTime();
-
- $formatted = $this->getShare($accessContext, $share->id);
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- unset($formatted['last_updated']);
- $this->assertEquals([
- 'id' => $share->id,
- 'owner' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'state' => ShareState::Draft->value,
- 'user_status' => null,
- 'sources' => [
- [
- 'class' => TestShareSourceType1::class,
- 'value' => 'source1',
- 'display_name' => 'Source 1',
- 'icon' => [
- 'svg' => '',
- ],
- ],
- ],
- 'recipients' => [
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1',
- 'instance' => null,
- 'display_name' => 'Recipient 1',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- ],
- 'properties' => [
- [
- 'class' => TestSharePropertyType1::class,
- 'display_name' => 'TestSharePropertyType1',
- 'hint' => 'hint TestSharePropertyType1',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => null,
- 'type' => 'enum',
- 'valid_values' => ['valid1'],
- ],
- ],
- 'permissions' => [
- [
- 'class' => ReshareSharePermissionType::class,
- 'source_class' => null,
- 'display_name' => 'Share with others',
- 'hint' => null,
- 'presets' => [],
- 'enabled' => false,
- 'priority' => 90,
- ],
- [
- 'class' => TestSharePermissionType1::class,
- 'source_class' => TestShareSourceType1::class,
- 'display_name' => 'TestSharePermissionType1',
- 'hint' => 'hint TestSharePermissionType1',
- 'presets' => [TestSharePermissionPreset1::class, TestSharePermissionPreset2::class],
- 'enabled' => false,
- 'priority' => 1,
- ],
- ],
- 'permission_preset' => null,
- ], $formatted);
- }
-
- public function testGetShareAsRecipientNotActive(): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
-
- $this->dbConnection->commit();
-
- try {
- $this->getShare(new ShareAccessContext(currentUser: $this->user1), $share->id);
- $this->fail('Draft share visible.');
- } catch (HintException $hintException) {
- $this->assertEquals('Share not found.', $hintException->getHint());
- }
- }
-
- public function testGetShareAsRecipientActive(): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $before = $this->manager->getTime();
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType2::class, 'recipient2', null));
-
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(TestSharePermissionType1::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
-
- $this->dbConnection->commit();
- $after = $this->manager->getTime();
-
- $formatted = $this->getShare(new ShareAccessContext(currentUser: $this->user1), $share->id);
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- unset($formatted['last_updated']);
- $this->assertEquals([
- 'id' => $share->id,
- 'owner' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'state' => ShareState::Active->value,
- 'user_status' => ShareUserStatus::Pending->value,
- 'sources' => [
- [
- 'class' => TestShareSourceType1::class,
- 'value' => 'source1',
- 'display_name' => 'Source 1',
- 'icon' => [
- 'svg' => '',
- ],
- ],
- ],
- 'recipients' => [
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1',
- 'instance' => null,
- 'display_name' => 'Recipient 1',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- ],
- 'properties' => [
- [
- 'class' => TestSharePropertyType1::class,
- 'display_name' => 'TestSharePropertyType1',
- 'hint' => 'hint TestSharePropertyType1',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => null,
- 'type' => 'enum',
- 'valid_values' => ['valid1'],
- ],
- ],
- 'permissions' => [
- [
- 'class' => ReshareSharePermissionType::class,
- 'source_class' => null,
- 'display_name' => 'Share with others',
- 'hint' => null,
- 'presets' => [],
- 'enabled' => false,
- 'priority' => 90,
- ],
- [
- 'class' => TestSharePermissionType1::class,
- 'source_class' => TestShareSourceType1::class,
- 'display_name' => 'TestSharePermissionType1',
- 'hint' => 'hint TestSharePermissionType1',
- 'presets' => [TestSharePermissionPreset1::class, TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- ],
- 'permission_preset' => TestSharePermissionPreset1::class,
- ], $formatted);
- }
-
- public function testGetShareAsRecipientWithArguments(): void {
- $this->registry->registerRecipientType(new TestShareRecipientTypeArguments());
-
- $accessContext = new ShareAccessContext($this->owner);
-
- $before = $this->manager->getTime();
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientTypeArguments::class, 'secret', null));
-
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(TestSharePermissionType1::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
-
- $this->dbConnection->commit();
- $after = $this->manager->getTime();
-
- $formatted = $this->getShare(new ShareAccessContext(currentUser: $this->user1, arguments: [TestShareRecipientTypeArguments::class => 'secret']),
- $share->id);
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- unset($formatted['last_updated']);
- $this->assertEquals([
- 'id' => $share->id,
- 'owner' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'state' => ShareState::Active->value,
- 'user_status' => ShareUserStatus::Pending->value,
- 'sources' => [
- [
- 'class' => TestShareSourceType1::class,
- 'value' => 'source1',
- 'display_name' => 'Source 1',
- 'icon' => [
- 'svg' => '',
- ],
- ],
- ],
- 'recipients' => [
- [
- 'class' => TestShareRecipientTypeArguments::class,
- 'value' => 'secret',
- 'instance' => null,
- 'display_name' => 'secret',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/guest/secret/64',
- 'dark' => 'http://localhost/index.php/avatar/guest/secret/64?darkTheme=1',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- ],
- 'properties' => [],
- 'permissions' => [
- [
- 'class' => ReshareSharePermissionType::class,
- 'source_class' => null,
- 'display_name' => 'Share with others',
- 'hint' => null,
- 'presets' => [],
- 'enabled' => false,
- 'priority' => 90,
- ],
- [
- 'class' => TestSharePermissionType1::class,
- 'source_class' => TestShareSourceType1::class,
- 'display_name' => 'TestSharePermissionType1',
- 'hint' => 'hint TestSharePermissionType1',
- 'presets' => [TestSharePermissionPreset1::class, TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- ],
- 'permission_preset' => TestSharePermissionPreset1::class,
- ], $formatted);
-
- try {
- $this->getShare(new ShareAccessContext(currentUser: $this->user1), $share->id);
- $this->fail('Share visible without arguments.');
- } catch (HintException $hintException) {
- $this->assertEquals('Share not found.', $hintException->getHint());
- }
- }
-
- public function testGetShareWithSecretNotActive(): void {
- $this->registry->registerRecipientType(new TestShareRecipientTypeArguments());
-
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientTypeArguments::class, 'secret', null));
-
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(TestSharePermissionType1::class, true));
-
- $this->dbConnection->commit();
- $secret = $share->recipients[0]->secret;
- $this->assertNotNull($secret);
-
- try {
- $this->getShare(new ShareAccessContext(secret: $secret), $share->id);
- $this->fail('Draft share visible with secret.');
- } catch (HintException $hintException) {
- $this->assertEquals('Share not found.', $hintException->getHint());
- }
- }
-
- public function testGetShareWithSecretActive(): void {
- $this->registry->registerRecipientType(new TestShareRecipientTypeArguments());
-
- $accessContext = new ShareAccessContext($this->owner);
-
- $before = $this->manager->getTime();
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientTypeArguments::class, 'secret', null));
-
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(TestSharePermissionType1::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
-
- $after = $this->manager->getTime();
-
- $this->dbConnection->commit();
- $secret = $share->recipients[0]->secret;
- $this->assertNotNull($secret);
-
- $formatted = $this->getShare(new ShareAccessContext(secret: $secret), $share->id);
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- unset($formatted['last_updated']);
- $this->assertEquals([
- 'id' => $share->id,
- 'owner' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'state' => ShareState::Active->value,
- 'user_status' => null,
- 'sources' => [
- [
- 'class' => TestShareSourceType1::class,
- 'value' => 'source1',
- 'display_name' => 'Source 1',
- 'icon' => [
- 'svg' => '',
- ],
- ],
- ],
- 'recipients' => [
- [
- 'class' => TestShareRecipientTypeArguments::class,
- 'value' => 'secret',
- 'instance' => null,
- 'display_name' => 'secret',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/guest/secret/64',
- 'dark' => 'http://localhost/index.php/avatar/guest/secret/64?darkTheme=1',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- ],
- 'properties' => [],
- 'permissions' => [
- [
- 'class' => ReshareSharePermissionType::class,
- 'source_class' => null,
- 'display_name' => 'Share with others',
- 'hint' => null,
- 'presets' => [],
- 'enabled' => false,
- 'priority' => 90,
- ],
- [
- 'class' => TestSharePermissionType1::class,
- 'source_class' => TestShareSourceType1::class,
- 'display_name' => 'TestSharePermissionType1',
- 'hint' => 'hint TestSharePermissionType1',
- 'presets' => [TestSharePermissionPreset1::class, TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- ],
- 'permission_preset' => TestSharePermissionPreset1::class,
- ], $formatted);
-
- try {
- $this->getShare(new ShareAccessContext(), $share->id);
- $this->fail('Share visible without secret.');
- } catch (HintException $hintException) {
- $this->assertEquals('Share not found.', $hintException->getHint());
- }
- }
-
- public function testGetShareAsNonRecipient(): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType2::class, 'recipient2', null));
-
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(TestSharePermissionType1::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
-
- $this->dbConnection->commit();
-
- try {
- $this->getShare(new ShareAccessContext(currentUser: $this->user1), $share->id);
- $this->fail('Share visible as non-recipient.');
- } catch (HintException $hintException) {
- $this->assertEquals('Share not found.', $hintException->getHint());
- }
- }
-
- public function testGetShareAsRecipientFilteredProperties(): void {
- $this->registry->registerPropertyType(new TestSharePropertyTypeFilter(['visible', 'filtered']));
- $this->registry->markPropertyTypeCompatibleWithSourceType(TestSharePropertyTypeFilter::class, TestShareSourceType1::class);
- $this->registry->markPropertyTypeCompatibleWithRecipientType(TestSharePropertyTypeFilter::class, TestShareRecipientType1::class);
-
- $accessContext = new ShareAccessContext($this->owner);
-
- $before = $this->manager->getTime();
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
-
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(TestSharePermissionType1::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
- $share = $this->manager->updateShareProperty($accessContext, $share, new ShareProperty(TestSharePropertyTypeFilter::class, 'visible'));
-
- $this->dbConnection->commit();
- $after = $this->manager->getTime();
-
- $formatted = $this->getShare(new ShareAccessContext(currentUser: $this->user1), $share->id);
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- unset($formatted['last_updated']);
- $this->assertEquals([
- 'id' => $share->id,
- 'owner' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'state' => ShareState::Active->value,
- 'user_status' => ShareUserStatus::Pending->value,
- 'sources' => [
- [
- 'class' => TestShareSourceType1::class,
- 'value' => 'source1',
- 'display_name' => 'Source 1',
- 'icon' => [
- 'svg' => '',
- ],
- ],
- ],
- 'recipients' => [
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1',
- 'instance' => null,
- 'display_name' => 'Recipient 1',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- ],
- 'properties' => [
- [
- 'class' => TestSharePropertyType1::class,
- 'display_name' => 'TestSharePropertyType1',
- 'hint' => 'hint TestSharePropertyType1',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => null,
- 'type' => 'enum',
- 'valid_values' => ['valid1'],
- ],
- [
- 'class' => TestSharePropertyTypeFilter::class,
- 'display_name' => 'TestSharePropertyTypeFilter',
- 'hint' => 'hint TestSharePropertyTypeFilter',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => 'visible',
- 'type' => 'enum',
- 'valid_values' => ['visible', 'filtered'],
- ],
- ],
- 'permissions' => [
- [
- 'class' => ReshareSharePermissionType::class,
- 'source_class' => null,
- 'display_name' => 'Share with others',
- 'hint' => null,
- 'presets' => [],
- 'enabled' => false,
- 'priority' => 90,
- ],
- [
- 'class' => TestSharePermissionType1::class,
- 'source_class' => TestShareSourceType1::class,
- 'display_name' => 'TestSharePermissionType1',
- 'hint' => 'hint TestSharePermissionType1',
- 'presets' => [TestSharePermissionPreset1::class, TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- ],
- 'permission_preset' => TestSharePermissionPreset1::class,
- ], $formatted);
-
- $before = $this->manager->getTime();
- $this->dbConnection->beginTransaction();
- $this->manager->updateShareProperty($accessContext, $share, new ShareProperty(TestSharePropertyTypeFilter::class, 'filtered'));
- $share = $this->reloadShare($accessContext, $share);
- $this->dbConnection->commit();
- $after = $this->manager->getTime();
-
- $formatted = $this->getShare(new ShareAccessContext(currentUser: $this->owner), $share->id);
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- unset($formatted['last_updated']);
- $this->assertEquals([
- 'id' => $share->id,
- 'owner' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'state' => ShareState::Active->value,
- 'user_status' => null,
- 'sources' => [
- [
- 'class' => TestShareSourceType1::class,
- 'value' => 'source1',
- 'display_name' => 'Source 1',
- 'icon' => [
- 'svg' => '',
- ],
- ],
- ],
- 'recipients' => [
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1',
- 'instance' => null,
- 'display_name' => 'Recipient 1',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- ],
- 'properties' => [
- [
- 'class' => TestSharePropertyType1::class,
- 'display_name' => 'TestSharePropertyType1',
- 'hint' => 'hint TestSharePropertyType1',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => null,
- 'type' => 'enum',
- 'valid_values' => ['valid1'],
- ],
- [
- 'class' => TestSharePropertyTypeFilter::class,
- 'display_name' => 'TestSharePropertyTypeFilter',
- 'hint' => 'hint TestSharePropertyTypeFilter',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => 'filtered',
- 'type' => 'enum',
- 'valid_values' => ['visible', 'filtered'],
- ],
- ],
- 'permissions' => [
- [
- 'class' => ReshareSharePermissionType::class,
- 'source_class' => null,
- 'display_name' => 'Share with others',
- 'hint' => null,
- 'presets' => [],
- 'enabled' => false,
- 'priority' => 90,
- ],
- [
- 'class' => TestSharePermissionType1::class,
- 'source_class' => TestShareSourceType1::class,
- 'display_name' => 'TestSharePermissionType1',
- 'hint' => 'hint TestSharePermissionType1',
- 'presets' => [TestSharePermissionPreset1::class, TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- ],
- 'permission_preset' => TestSharePermissionPreset1::class,
- ], $formatted);
-
- try {
- $this->getShare(new ShareAccessContext(currentUser: $this->user1), $share->id);
- $this->fail('Share visible with active filter property.');
- } catch (HintException $hintException) {
- $this->assertEquals('Share not found.', $hintException->getHint());
- }
- }
-
- public function testGetShareAsRecipientFilteredArguments(): void {
- $this->registry->registerPropertyType(new TestSharePropertyTypeFilter(['visible', 'filtered']));
- $this->registry->markPropertyTypeCompatibleWithSourceType(TestSharePropertyTypeFilter::class, TestShareSourceType1::class);
- $this->registry->markPropertyTypeCompatibleWithRecipientType(TestSharePropertyTypeFilter::class, TestShareRecipientType1::class);
-
- $accessContext = new ShareAccessContext($this->owner);
-
- $before = $this->manager->getTime();
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
-
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(TestSharePermissionType1::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
-
- $this->dbConnection->commit();
- $after = $this->manager->getTime();
-
- $formatted = $this->getShare(new ShareAccessContext(currentUser: $this->user1), $share->id);
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- unset($formatted['last_updated']);
- $this->assertEquals([
- 'id' => $share->id,
- 'owner' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'state' => ShareState::Active->value,
- 'user_status' => ShareUserStatus::Pending->value,
- 'sources' => [
- [
- 'class' => TestShareSourceType1::class,
- 'value' => 'source1',
- 'display_name' => 'Source 1',
- 'icon' => [
- 'svg' => '',
- ],
- ],
- ],
- 'recipients' => [
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1',
- 'instance' => null,
- 'display_name' => 'Recipient 1',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- ],
- 'properties' => [
- [
- 'class' => TestSharePropertyType1::class,
- 'display_name' => 'TestSharePropertyType1',
- 'hint' => 'hint TestSharePropertyType1',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => null,
- 'type' => 'enum',
- 'valid_values' => ['valid1'],
- ],
- [
- 'class' => TestSharePropertyTypeFilter::class,
- 'display_name' => 'TestSharePropertyTypeFilter',
- 'hint' => 'hint TestSharePropertyTypeFilter',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => null,
- 'type' => 'enum',
- 'valid_values' => ['visible', 'filtered'],
- ],
- ],
- 'permissions' => [
- [
- 'class' => ReshareSharePermissionType::class,
- 'source_class' => null,
- 'display_name' => 'Share with others',
- 'hint' => null,
- 'presets' => [],
- 'enabled' => false,
- 'priority' => 90,
- ],
- [
- 'class' => TestSharePermissionType1::class,
- 'source_class' => TestShareSourceType1::class,
- 'display_name' => 'TestSharePermissionType1',
- 'hint' => 'hint TestSharePermissionType1',
- 'presets' => [TestSharePermissionPreset1::class, TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- ],
- 'permission_preset' => TestSharePermissionPreset1::class,
- ], $formatted);
-
- $formatted = $this->getShare(new ShareAccessContext(currentUser: $this->owner, arguments: [TestSharePropertyTypeFilter::class => 'filtered']),
- $share->id);
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- unset($formatted['last_updated']);
- $this->assertEquals([
- 'id' => $share->id,
- 'owner' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'state' => ShareState::Active->value,
- 'user_status' => null,
- 'sources' => [
- [
- 'class' => TestShareSourceType1::class,
- 'value' => 'source1',
- 'display_name' => 'Source 1',
- 'icon' => [
- 'svg' => '',
- ],
- ],
- ],
- 'recipients' => [
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1',
- 'instance' => null,
- 'display_name' => 'Recipient 1',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- ],
- 'properties' => [
- [
- 'class' => TestSharePropertyType1::class,
- 'display_name' => 'TestSharePropertyType1',
- 'hint' => 'hint TestSharePropertyType1',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => null,
- 'type' => 'enum',
- 'valid_values' => ['valid1'],
- ],
- [
- 'class' => TestSharePropertyTypeFilter::class,
- 'display_name' => 'TestSharePropertyTypeFilter',
- 'hint' => 'hint TestSharePropertyTypeFilter',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => null,
- 'type' => 'enum',
- 'valid_values' => ['visible', 'filtered'],
- ],
- ],
- 'permissions' => [
- [
- 'class' => ReshareSharePermissionType::class,
- 'source_class' => null,
- 'display_name' => 'Share with others',
- 'hint' => null,
- 'presets' => [],
- 'enabled' => false,
- 'priority' => 90,
- ],
- [
- 'class' => TestSharePermissionType1::class,
- 'source_class' => TestShareSourceType1::class,
- 'display_name' => 'TestSharePermissionType1',
- 'hint' => 'hint TestSharePermissionType1',
- 'presets' => [TestSharePermissionPreset1::class, TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- ],
- 'permission_preset' => TestSharePermissionPreset1::class,
- ], $formatted);
-
- try {
- $this->getShare(new ShareAccessContext(currentUser: $this->user1, arguments: [TestSharePropertyTypeFilter::class => 'filtered']), $share->id);
- $this->fail('Share visible with filtered value as recipient.');
- } catch (HintException $hintException) {
- $this->assertEquals('Share not found.', $hintException->getHint());
- }
- }
-
- /**
- * @return list
- */
- public static function dataGetShareWithPublicSecret(): array {
- return [
- [true],
- [false],
- ];
- }
-
- #[DataProvider('dataGetShareWithPublicSecret')]
- public function testGetShareWithPublicSecret(bool $isSecretPublic): void {
- $this->registry->clear();
- $this->registry->registerRecipientType(
- new TestShareRecipientType1(
- [
- 'recipient1' => 'Recipient 1',
- ],
- [],
- [],
- )
- );
- $this->registry->registerRecipientType(
- new TestShareRecipientTypePublicSecret(
- [
- 'recipient2' => 'Recipient 2',
- ],
- [],
- $isSecretPublic,
- false,
- )
- );
-
- $accessContext = new ShareAccessContext($this->owner);
-
- $before = $this->manager->getTime();
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
- $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientTypePublicSecret::class, 'recipient2', null));
-
- $this->dbConnection->commit();
- $after = $this->manager->getTime();
-
- $formatted = $this->getShare($accessContext, $share->id);
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- unset($formatted['last_updated']);
- $this->assertIsList($formatted['recipients']);
- $this->assertCount(2, $formatted['recipients']);
- $this->assertEquals([
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1',
- 'instance' => null,
- 'display_name' => 'Recipient 1',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ], $formatted['recipients'][0]);
- $this->assertIsArray($formatted['recipients'][1]);
- if ($isSecretPublic) {
- $this->assertArrayHasKey('secret', $formatted['recipients'][1]);
- $this->assertIsArray($formatted['recipients'][1]['secret']);
-
- $this->assertArrayHasKey('updatable', $formatted['recipients'][1]['secret']);
- $this->assertFalse($formatted['recipients'][1]['secret']['updatable']);
-
- $this->assertArrayHasKey('value', $formatted['recipients'][1]['secret']);
- $this->assertIsString($formatted['recipients'][1]['secret']['value']);
- $this->assertNotEmpty($formatted['recipients'][1]['secret']['value']);
-
- $this->assertArrayHasKey('url', $formatted['recipients'][1]['secret']);
- $this->assertIsString($formatted['recipients'][1]['secret']['url']);
- $this->assertMatchesRegularExpression('/http:\/\/localhost\/index\.php\/s\/.+/', $formatted['recipients'][1]['secret']['url']);
- } else {
- $this->assertArrayNotHasKey('url', $formatted['recipients'][1]);
- }
- }
-
- public function testGetShareWithSecret(): void {
- $this->registry->clear();
- $this->registry->registerSourceType(new TestShareSourceType1(['source1' => 'Source']));
- $this->registry->registerRecipientType(
- new TestShareRecipientTypePublicSecret(
- [
- 'recipient1' => 'Recipient 1',
- 'recipient2' => 'Recipient 2',
- 'recipient3' => 'Recipient 3',
- 'recipient4' => 'Recipient 4',
- ],
- [
- $this->user1->getUID() => ['recipient1'],
- $this->user2->getUID() => ['recipient2'],
- ],
- true,
- false,
- )
- );
- $this->registry->registerPermissionType(null, Server::get(ReshareSharePermissionType::class));
-
- $accessContext = new ShareAccessContext($this->owner);
-
- $before = $this->manager->getTime();
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientTypePublicSecret::class, 'recipient1', null));
-
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(ReshareSharePermissionType::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
- $share = $this->manager->addShareRecipient(
- new ShareAccessContext($this->user1), $share, new ShareRecipient(TestShareRecipientTypePublicSecret::class, 'recipient2', null)
- );
- $share = $this->manager->addShareRecipient(
- new ShareAccessContext($this->user1), $share, new ShareRecipient(TestShareRecipientTypePublicSecret::class, 'recipient3', null)
- );
- $share = $this->manager->addShareRecipient(
- new ShareAccessContext($this->user2), $share, new ShareRecipient(TestShareRecipientTypePublicSecret::class, 'recipient4', null)
- );
-
- $this->dbConnection->commit();
- $after = $this->manager->getTime();
-
- $formatted = $this->getShare(new ShareAccessContext($this->user2), $share->id);
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
-
- $this->assertArrayHasKey('recipients', $formatted);
- $this->assertIsArray($formatted['recipients']);
- $this->assertCount(2, $formatted['recipients']);
-
- // Parent is not visible
-
- // Self
- $this->assertIsArray($formatted['recipients'][0]);
- $this->assertArrayHasKey('value', $formatted['recipients'][0]);
- $this->assertEquals('recipient2', $formatted['recipients'][0]['value']);
- $this->assertArrayHasKey('secret', $formatted['recipients'][0]);
- $this->assertIsArray($formatted['recipients'][0]['secret']);
- $this->assertNotEmpty($formatted['recipients'][0]['secret']['value']);
-
- // Sibling is not visible
-
- // Child
- $this->assertIsArray($formatted['recipients'][1]);
- $this->assertArrayHasKey('value', $formatted['recipients'][1]);
- $this->assertEquals('recipient4', $formatted['recipients'][1]['value']);
- $this->assertArrayHasKey('secret', $formatted['recipients'][1]);
- $this->assertIsArray($formatted['recipients'][1]['secret']);
- $this->assertNotEmpty($formatted['recipients'][1]['secret']['value']);
- }
-
- public function testGetShareUniqueDisplayNames(): void {
- $this->registry->clear();
- $this->registry->registerSourceType(new TestShareSourceType1(['source1' => 'Source']));
- $this->registry->registerSourceType(new TestShareSourceType2(['source2' => 'Source', 'source3' => 'Other']));
- $this->registry->registerRecipientType(new TestShareRecipientType1(['recipient1' => 'Recipient'], [], []));
- $this->registry->registerRecipientType(new TestShareRecipientType2(['recipient2' => 'Recipient', 'recipient3' => 'Other'], [], []));
-
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType2::class, 'source2'));
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType2::class, 'source3'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType2::class, 'recipient2', null));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType2::class, 'recipient3', null));
-
- $this->dbConnection->commit();
-
- $formatted = $this->getShare($accessContext, $share->id);
-
- $this->assertEquals([
- [
- 'class' => TestShareSourceType1::class,
- 'value' => 'source1',
- 'display_name' => 'Source (TestShareSourceType1: source1)',
- 'icon' => [
- 'svg' => '',
- ],
- ],
- [
- 'class' => TestShareSourceType2::class,
- 'value' => 'source2',
- 'display_name' => 'Source (TestShareSourceType2: source2)',
- 'icon' => [
- 'svg' => '',
- ],
- ],
- [
- 'class' => TestShareSourceType2::class,
- 'value' => 'source3',
- 'display_name' => 'Other',
- 'icon' => [
- 'svg' => '',
- ],
- ],
- ], $formatted['sources']);
- $this->assertEquals([
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1',
- 'instance' => null,
- 'display_name' => 'Recipient (TestShareRecipientType1: recipient1)',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- [
- 'class' => TestShareRecipientType2::class,
- 'value' => 'recipient2',
- 'instance' => null,
- 'display_name' => 'Recipient (TestShareRecipientType2: recipient2)',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- [
- 'class' => TestShareRecipientType2::class,
- 'value' => 'recipient3',
- 'instance' => null,
- 'display_name' => 'Other',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- ], $formatted['recipients']);
- }
-
- public function testGetShareDisabledOwner(): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $before = $this->manager->getTime();
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType2::class, 'recipient2', null));
-
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(TestSharePermissionType1::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
-
- $this->dbConnection->commit();
- $after = $this->manager->getTime();
-
- $this->owner->setEnabled(false);
-
- try {
- $this->getShare(new ShareAccessContext(currentUser: $this->user1), $share->id);
- $this->fail('Share still visible.');
- } catch (HintException $hintException) {
- $this->assertEquals('Share not found.', $hintException->getHint());
- }
-
- $formatted = $this->getShare(new ShareAccessContext(overrideChecks: true), $share->id);
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals([
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ], $formatted['owner']);
- }
-
- public function testGetShareDisabledInitiator(): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $before = $this->manager->getTime();
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(ReshareSharePermissionType::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
- $share = $this->manager->addShareRecipient(
- new ShareAccessContext(currentUser: $this->user1), $share, new ShareRecipient(TestShareRecipientType2::class, 'recipient2', null)
- );
-
- $this->dbConnection->commit();
- $after = $this->manager->getTime();
-
- $this->user1->setEnabled(false);
-
- try {
- $this->getShare(new ShareAccessContext(currentUser: $this->user2), $share->id);
- $this->fail('Share still visible.');
- } catch (HintException $hintException) {
- $this->assertEquals('Share not found.', $hintException->getHint());
- }
-
- $formatted = $this->getShare($accessContext, $share->id);
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals([
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1',
- 'instance' => null,
- 'display_name' => 'Recipient 1',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- [
- 'class' => TestShareRecipientType2::class,
- 'value' => 'recipient2',
- 'instance' => null,
- 'display_name' => 'Recipient 2',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'user1',
- 'instance' => null,
- 'display_name' => 'User 1',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/user1/64',
- 'dark' => 'http://localhost/index.php/avatar/user1/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- ], $formatted['recipients']);
- }
-
- public function testGetShares(): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $before1 = $this->manager->getTime();
- $this->dbConnection->beginTransaction();
- $share1 = $this->manager->createShare($accessContext);
- $share1 = $this->manager->addShareSource($accessContext, $share1, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share1 = $this->manager->addShareRecipient($accessContext, $share1, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
- $share1 = $this->manager->updateSharePermission($accessContext, $share1, new SharePermission(TestSharePermissionType1::class, true));
-
- $this->dbConnection->commit();
- $after1 = $this->manager->getTime();
-
- $before2 = $this->manager->getTime();
- $this->dbConnection->beginTransaction();
- $share2 = $this->manager->createShare($accessContext);
- $share2 = $this->manager->addShareSource($accessContext, $share2, new ShareSource(TestShareSourceType2::class, 'source2'));
- $share2 = $this->manager->addShareRecipient($accessContext, $share2, new ShareRecipient(TestShareRecipientType2::class, 'recipient2', null));
- $share2 = $this->manager->updateSharePermission($accessContext, $share2, new SharePermission(TestSharePermissionType2::class, true));
- $share2 = $this->manager->updateShareState($accessContext, $share2, ShareState::Active);
-
- $this->dbConnection->commit();
- $after2 = $this->manager->getTime();
-
- $accessContext2 = new ShareAccessContext($this->user2);
- $this->dbConnection->beginTransaction();
- $this->manager->updateShareUserStatus($accessContext2, $this->manager->getShare($accessContext2, $share2->id), ShareUserStatus::Accepted);
- $this->dbConnection->commit();
-
- $formatted = $this->getShares($accessContext, null, null, null, null, null, null);
- $this->assertCount(2, $formatted);
- $this->assertIsArray($formatted[0]);
- $this->assertDateBetween($before1, $after1, $this->parseTime($formatted[0]['last_updated']));
- $this->assertIsArray($formatted[1]);
- $this->assertDateBetween($before2, $after2, $this->parseTime($formatted[1]['last_updated']));
- unset($formatted[0]['last_updated'], $formatted[1]['last_updated']);
- $this->assertEquals([
- [
- 'id' => $share1->id,
- 'owner' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'state' => ShareState::Draft->value,
- 'user_status' => null,
- 'sources' => [
- [
- 'class' => TestShareSourceType1::class,
- 'value' => 'source1',
- 'display_name' => 'Source 1',
- 'icon' => [
- 'svg' => '',
- ],
- ],
- ],
- 'recipients' => [
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1',
- 'instance' => null,
- 'display_name' => 'Recipient 1',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- ],
- 'properties' => [
- [
- 'class' => TestSharePropertyType1::class,
- 'display_name' => 'TestSharePropertyType1',
- 'hint' => 'hint TestSharePropertyType1',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => null,
- 'type' => 'enum',
- 'valid_values' => ['valid1'],
- ],
- ],
- 'permissions' => [
- [
- 'class' => ReshareSharePermissionType::class,
- 'source_class' => null,
- 'display_name' => 'Share with others',
- 'hint' => null,
- 'presets' => [],
- 'enabled' => false,
- 'priority' => 90,
- ],
- [
- 'class' => TestSharePermissionType1::class,
- 'source_class' => TestShareSourceType1::class,
- 'display_name' => 'TestSharePermissionType1',
- 'hint' => 'hint TestSharePermissionType1',
- 'presets' => [TestSharePermissionPreset1::class, TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- ],
- 'permission_preset' => TestSharePermissionPreset1::class,
- ],
- [
- 'id' => $share2->id,
- 'owner' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'state' => ShareState::Active->value,
- 'user_status' => null,
- 'sources' => [
- [
- 'class' => TestShareSourceType2::class,
- 'value' => 'source2',
- 'display_name' => 'Source 2',
- 'icon' => [
- 'svg' => '',
- ],
- ],
- ],
- 'recipients' => [
- [
- 'class' => TestShareRecipientType2::class,
- 'value' => 'recipient2',
- 'instance' => null,
- 'display_name' => 'Recipient 2',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- ],
- 'properties' => [
- [
- 'class' => TestSharePropertyType2::class,
- 'display_name' => 'TestSharePropertyType2',
- 'hint' => 'hint TestSharePropertyType2',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => null,
- 'type' => 'enum',
- 'valid_values' => ['valid2'],
- ],
- ],
- 'permissions' => [
- [
- 'class' => ReshareSharePermissionType::class,
- 'source_class' => null,
- 'display_name' => 'Share with others',
- 'hint' => null,
- 'presets' => [],
- 'enabled' => false,
- 'priority' => 90,
- ],
- [
- 'class' => TestSharePermissionType2::class,
- 'source_class' => TestShareSourceType2::class,
- 'display_name' => 'TestSharePermissionType2',
- 'hint' => 'hint TestSharePermissionType2',
- 'presets' => [TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- ],
- 'permission_preset' => TestSharePermissionPreset2::class,
- ],
- ], $formatted);
-
- $formatted = $this->getShares($accessContext, TestShareSourceType1::class, null, null, null, null, null);
- $this->assertCount(1, $formatted);
- $this->assertIsArray($formatted[0]);
- $this->assertDateBetween($before1, $after1, $this->parseTime($formatted[0]['last_updated']));
- unset($formatted[0]['last_updated']);
- $this->assertEquals([
- [
- 'id' => $share1->id,
- 'owner' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'state' => ShareState::Draft->value,
- 'user_status' => null,
- 'sources' => [
- [
- 'class' => TestShareSourceType1::class,
- 'value' => 'source1',
- 'display_name' => 'Source 1',
- 'icon' => [
- 'svg' => '',
- ],
- ],
- ],
- 'recipients' => [
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1',
- 'instance' => null,
- 'display_name' => 'Recipient 1',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- ],
- 'properties' => [
- [
- 'class' => TestSharePropertyType1::class,
- 'display_name' => 'TestSharePropertyType1',
- 'hint' => 'hint TestSharePropertyType1',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => null,
- 'type' => 'enum',
- 'valid_values' => ['valid1'],
- ],
- ],
- 'permissions' => [
- [
- 'class' => ReshareSharePermissionType::class,
- 'source_class' => null,
- 'display_name' => 'Share with others',
- 'hint' => null,
- 'presets' => [],
- 'enabled' => false,
- 'priority' => 90,
- ],
- [
- 'class' => TestSharePermissionType1::class,
- 'source_class' => TestShareSourceType1::class,
- 'display_name' => 'TestSharePermissionType1',
- 'hint' => 'hint TestSharePermissionType1',
- 'presets' => [TestSharePermissionPreset1::class, TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- ],
- 'permission_preset' => TestSharePermissionPreset1::class,
- ],
- ], $formatted);
-
- $formatted = $this->getShares($accessContext, TestShareSourceType1::class, 'source1', null, null, null, null);
- $this->assertCount(1, $formatted);
- $this->assertIsArray($formatted[0]);
- $this->assertDateBetween($before1, $after1, $this->parseTime($formatted[0]['last_updated']));
- unset($formatted[0]['last_updated']);
- $this->assertEquals([
- [
- 'id' => $share1->id,
- 'owner' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'state' => ShareState::Draft->value,
- 'user_status' => null,
- 'sources' => [
- [
- 'class' => TestShareSourceType1::class,
- 'value' => 'source1',
- 'display_name' => 'Source 1',
- 'icon' => [
- 'svg' => '',
- ],
- ],
- ],
- 'recipients' => [
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1',
- 'instance' => null,
- 'display_name' => 'Recipient 1',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- ],
- 'properties' => [
- [
- 'class' => TestSharePropertyType1::class,
- 'display_name' => 'TestSharePropertyType1',
- 'hint' => 'hint TestSharePropertyType1',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => null,
- 'type' => 'enum',
- 'valid_values' => ['valid1'],
- ],
- ],
- 'permissions' => [
- [
- 'class' => ReshareSharePermissionType::class,
- 'source_class' => null,
- 'display_name' => 'Share with others',
- 'hint' => null,
- 'presets' => [],
- 'enabled' => false,
- 'priority' => 90,
- ],
- [
- 'class' => TestSharePermissionType1::class,
- 'source_class' => TestShareSourceType1::class,
- 'display_name' => 'TestSharePermissionType1',
- 'hint' => 'hint TestSharePermissionType1',
- 'presets' => [TestSharePermissionPreset1::class, TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- ],
- 'permission_preset' => TestSharePermissionPreset1::class,
- ],
- ], $formatted);
-
- $formatted = $this->getShares($accessContext, TestShareSourceType1::class, 'non-existent', null, null, null, null);
- $this->assertCount(0, $formatted);
-
- $formatted = $this->getShares($accessContext, null, null, ShareState::Draft, null, null, null);
- $this->assertCount(1, $formatted);
- $this->assertIsArray($formatted[0]);
- $this->assertDateBetween($before1, $after1, $this->parseTime($formatted[0]['last_updated']));
- unset($formatted[0]['last_updated']);
- $this->assertEquals([
- [
- 'id' => $share1->id,
- 'owner' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'state' => ShareState::Draft->value,
- 'user_status' => null,
- 'sources' => [
- [
- 'class' => TestShareSourceType1::class,
- 'value' => 'source1',
- 'display_name' => 'Source 1',
- 'icon' => [
- 'svg' => '',
- ],
- ],
- ],
- 'recipients' => [
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1',
- 'instance' => null,
- 'display_name' => 'Recipient 1',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- ],
- 'properties' => [
- [
- 'class' => TestSharePropertyType1::class,
- 'display_name' => 'TestSharePropertyType1',
- 'hint' => 'hint TestSharePropertyType1',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => null,
- 'type' => 'enum',
- 'valid_values' => ['valid1'],
- ],
- ],
- 'permissions' => [
- [
- 'class' => ReshareSharePermissionType::class,
- 'source_class' => null,
- 'display_name' => 'Share with others',
- 'hint' => null,
- 'presets' => [],
- 'enabled' => false,
- 'priority' => 90,
- ],
- [
- 'class' => TestSharePermissionType1::class,
- 'source_class' => TestShareSourceType1::class,
- 'display_name' => 'TestSharePermissionType1',
- 'hint' => 'hint TestSharePermissionType1',
- 'presets' => [TestSharePermissionPreset1::class, TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- ],
- 'permission_preset' => TestSharePermissionPreset1::class,
- ],
- ], $formatted);
-
- $formatted = $this->getShares($accessContext, null, null, ShareState::Active, null, null, null);
- $this->assertCount(1, $formatted);
- $this->assertIsArray($formatted[0]);
- $this->assertDateBetween($before2, $after2, $this->parseTime($formatted[0]['last_updated']));
- unset($formatted[0]['last_updated']);
- $this->assertEquals([
- [
- 'id' => $share2->id,
- 'owner' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'state' => ShareState::Active->value,
- 'user_status' => null,
- 'sources' => [
- [
- 'class' => TestShareSourceType2::class,
- 'value' => 'source2',
- 'display_name' => 'Source 2',
- 'icon' => [
- 'svg' => '',
- ],
- ],
- ],
- 'recipients' => [
- [
- 'class' => TestShareRecipientType2::class,
- 'value' => 'recipient2',
- 'instance' => null,
- 'display_name' => 'Recipient 2',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- ],
- 'properties' => [
- [
- 'class' => TestSharePropertyType2::class,
- 'display_name' => 'TestSharePropertyType2',
- 'hint' => 'hint TestSharePropertyType2',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => null,
- 'type' => 'enum',
- 'valid_values' => ['valid2'],
- ],
- ],
- 'permissions' => [
- [
- 'class' => ReshareSharePermissionType::class,
- 'source_class' => null,
- 'display_name' => 'Share with others',
- 'hint' => null,
- 'presets' => [],
- 'enabled' => false,
- 'priority' => 90,
- ],
- [
- 'class' => TestSharePermissionType2::class,
- 'source_class' => TestShareSourceType2::class,
- 'display_name' => 'TestSharePermissionType2',
- 'hint' => 'hint TestSharePermissionType2',
- 'presets' => [TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- ],
- 'permission_preset' => TestSharePermissionPreset2::class,
- ],
- ], $formatted);
-
- $formatted = $this->getShares($accessContext2, null, null, null, ShareUserStatus::Pending, null, null);
- $this->assertCount(0, $formatted);
-
- $formatted = $this->getShares($accessContext2, null, null, null, ShareUserStatus::Accepted, null, null);
- $this->assertCount(1, $formatted);
- $this->assertIsArray($formatted[0]);
- $this->assertDateBetween($before2, $after2, $this->parseTime($formatted[0]['last_updated']));
- unset($formatted[0]['last_updated']);
- $this->assertEquals([
- [
- 'id' => $share2->id,
- 'owner' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'state' => ShareState::Active->value,
- 'user_status' => ShareUserStatus::Accepted->value,
- 'sources' => [
- [
- 'class' => TestShareSourceType2::class,
- 'value' => 'source2',
- 'display_name' => 'Source 2',
- 'icon' => [
- 'svg' => '',
- ],
- ],
- ],
- 'recipients' => [
- [
- 'class' => TestShareRecipientType2::class,
- 'value' => 'recipient2',
- 'instance' => null,
- 'display_name' => 'Recipient 2',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- ],
- 'properties' => [
- [
- 'class' => TestSharePropertyType2::class,
- 'display_name' => 'TestSharePropertyType2',
- 'hint' => 'hint TestSharePropertyType2',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => null,
- 'type' => 'enum',
- 'valid_values' => ['valid2'],
- ],
- ],
- 'permissions' => [
- [
- 'class' => ReshareSharePermissionType::class,
- 'source_class' => null,
- 'display_name' => 'Share with others',
- 'hint' => null,
- 'presets' => [],
- 'enabled' => false,
- 'priority' => 90,
- ],
- [
- 'class' => TestSharePermissionType2::class,
- 'source_class' => TestShareSourceType2::class,
- 'display_name' => 'TestSharePermissionType2',
- 'hint' => 'hint TestSharePermissionType2',
- 'presets' => [TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- ],
- 'permission_preset' => TestSharePermissionPreset2::class,
- ],
- ], $formatted);
-
- $formatted = $this->getShares($accessContext2, null, null, null, ShareUserStatus::Rejected, null, null);
- $this->assertCount(0, $formatted);
-
- $formatted = $this->getShares($accessContext, null, null, null, null, $share1->id, null);
- $this->assertCount(1, $formatted);
- $this->assertIsArray($formatted[0]);
- $this->assertDateBetween($before2, $after2, $this->parseTime($formatted[0]['last_updated']));
- unset($formatted[0]['last_updated']);
- $this->assertEquals([
- [
- 'id' => $share2->id,
- 'owner' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'state' => ShareState::Active->value,
- 'user_status' => null,
- 'sources' => [
- [
- 'class' => TestShareSourceType2::class,
- 'value' => 'source2',
- 'display_name' => 'Source 2',
- 'icon' => [
- 'svg' => '',
- ],
- ],
- ],
- 'recipients' => [
- [
- 'class' => TestShareRecipientType2::class,
- 'value' => 'recipient2',
- 'instance' => null,
- 'display_name' => 'Recipient 2',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- ],
- 'properties' => [
- [
- 'class' => TestSharePropertyType2::class,
- 'display_name' => 'TestSharePropertyType2',
- 'hint' => 'hint TestSharePropertyType2',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => null,
- 'type' => 'enum',
- 'valid_values' => ['valid2'],
- ],
- ],
- 'permissions' => [
- [
- 'class' => ReshareSharePermissionType::class,
- 'source_class' => null,
- 'display_name' => 'Share with others',
- 'hint' => null,
- 'presets' => [],
- 'enabled' => false,
- 'priority' => 90,
- ],
- [
- 'class' => TestSharePermissionType2::class,
- 'source_class' => TestShareSourceType2::class,
- 'display_name' => 'TestSharePermissionType2',
- 'hint' => 'hint TestSharePermissionType2',
- 'presets' => [TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- ],
- 'permission_preset' => TestSharePermissionPreset2::class,
- ],
- ], $formatted);
-
- $formatted = $this->getShares($accessContext, null, null, null, null, null, 1);
- $this->assertCount(1, $formatted);
- $this->assertIsArray($formatted[0]);
- $this->assertDateBetween($before1, $after1, $this->parseTime($formatted[0]['last_updated']));
- unset($formatted[0]['last_updated']);
- $this->assertEquals([
- [
- 'id' => $share1->id,
- 'owner' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'state' => ShareState::Draft->value,
- 'user_status' => null,
- 'sources' => [
- [
- 'class' => TestShareSourceType1::class,
- 'value' => 'source1',
- 'display_name' => 'Source 1',
- 'icon' => [
- 'svg' => '',
- ],
- ],
- ],
- 'recipients' => [
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1',
- 'instance' => null,
- 'display_name' => 'Recipient 1',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- ],
- 'properties' => [
- [
- 'class' => TestSharePropertyType1::class,
- 'display_name' => 'TestSharePropertyType1',
- 'hint' => 'hint TestSharePropertyType1',
- 'priority' => 1,
- 'advanced' => false,
- 'required' => false,
- 'value' => null,
- 'type' => 'enum',
- 'valid_values' => ['valid1'],
- ],
- ],
- 'permissions' => [
- [
- 'class' => ReshareSharePermissionType::class,
- 'source_class' => null,
- 'display_name' => 'Share with others',
- 'hint' => null,
- 'presets' => [],
- 'enabled' => false,
- 'priority' => 90,
- ],
- [
- 'class' => TestSharePermissionType1::class,
- 'source_class' => TestShareSourceType1::class,
- 'display_name' => 'TestSharePermissionType1',
- 'hint' => 'hint TestSharePermissionType1',
- 'presets' => [TestSharePermissionPreset1::class, TestSharePermissionPreset2::class],
- 'enabled' => true,
- 'priority' => 1,
- ],
- ],
- 'permission_preset' => TestSharePermissionPreset1::class,
- ],
- ], $formatted);
- }
-
- public function testGetSharesSorted(): void {
- $accessContext = new ShareAccessContext(currentUser: $this->owner);
-
- $this->dbConnection->beginTransaction();
- $share1 = $this->manager->createShare($accessContext);
- $share2 = $this->manager->createShare($accessContext);
- $this->dbConnection->commit();
-
- $shares = $this->getShares($accessContext, null, null, null, null, null, null);
- $this->assertIsArray($shares[0]);
- $this->assertArrayHasKey('id', $shares[0]);
- $this->assertIsArray($shares[1]);
- $this->assertArrayHasKey('id', $shares[1]);
- $this->assertEquals($share1->id, $shares[0]['id']);
- $this->assertEquals($share2->id, $shares[1]['id']);
-
- $this->dbConnection->beginTransaction();
- $this->manager->addShareSource($accessContext, $share2, new ShareSource(TestShareSourceType2::class, 'source2'));
- $this->manager->getShare($accessContext, $share2->id);
- $this->manager->updateSharePermission($accessContext, $share2, new SharePermission(TestSharePermissionType2::class, true));
-
- $this->dbConnection->commit();
-
- $formatted = $this->getShares($accessContext, null, null, null, null, null, null);
- $this->assertIsArray($formatted[0]);
- $this->assertArrayHasKey('id', $formatted[0]);
- $this->assertIsArray($formatted[1]);
- $this->assertArrayHasKey('id', $formatted[1]);
- $this->assertEquals($share2->id, $formatted[0]['id']);
- $this->assertEquals($share1->id, $formatted[1]['id']);
- }
-
- public function testOwnerDeleted(): void {
- $accessContext = new ShareAccessContext(currentUser: $this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $this->owner->delete();
- $this->dbConnection->commit();
-
- try {
- $this->getShare(new ShareAccessContext(overrideChecks: true), $share->id);
- $this->fail('Share still exists.');
- } catch (HintException $hintException) {
- $this->assertEquals('Share not found.', $hintException->getHint());
- }
- }
-
- public function testInitiatorDeleted(): void {
- $accessContext = new ShareAccessContext($this->owner);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(ReshareSharePermissionType::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
- $share = $this->manager->addShareRecipient(
- new ShareAccessContext(currentUser: $this->user1), $share, new ShareRecipient(TestShareRecipientType2::class, 'recipient2', null)
- );
-
- $before = $this->manager->getTime();
- $this->user1->delete();
- $after = $this->manager->getTime();
- $this->dbConnection->commit();
-
- $formatted = $this->getShare(new ShareAccessContext(overrideChecks: true), $share->id);
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals([
- [
- 'class' => TestShareRecipientType1::class,
- 'value' => 'recipient1',
- 'instance' => null,
- 'display_name' => 'Recipient 1',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- [
- 'class' => TestShareRecipientType2::class,
- 'value' => 'recipient2',
- 'instance' => null,
- 'display_name' => 'Recipient 2',
- 'icon' => [
- 'svg' => '',
- ],
- 'secret' => [
- 'updatable' => false,
- ],
- 'initiator' => [
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ],
- 'permissions' => [],
- ],
- ], $formatted['recipients']);
- }
-
- public function testGetWithDirectAccess(): void {
- $accessContext = new ShareAccessContext($this->owner);
- $accessContext2 = new ShareAccessContext(currentUser: $this->user2);
-
- $before = $this->manager->getTime();
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
-
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(TestSharePermissionType1::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
-
- $this->dbConnection->commit();
-
- $after = $this->manager->getTime();
-
- // user2 has no direct access, no shares
- $formattedShares = $this->getShares($accessContext2, TestShareSourceType1::class, 'source1', null, null, null, null);
- $this->assertCount(0, $formattedShares);
-
- try {
- $this->getShare($accessContext2, $share->id);
- $this->fail('user has invalid share access');
- } catch (HintException) {
-
- }
-
- // give user2 direct access, can see shares
- $this->shareSourceType1->userAccess[$this->user2->getUID()] = ['source1'];
- $formattedShares = $this->getShares($accessContext2, TestShareSourceType1::class, 'source1', null, null, null, null);
-
- $this->assertCount(1, $formattedShares);
- $formatted = $formattedShares[0];
-
- $this->assertDateBetween($before, $after, $this->parseTime($formatted['last_updated']));
- $this->assertEquals([
- 'user_id' => 'owner',
- 'instance' => null,
- 'display_name' => 'Owner',
- 'icon' => [
- 'light' => 'http://localhost/index.php/avatar/owner/64',
- 'dark' => 'http://localhost/index.php/avatar/owner/64/dark',
- ],
- ], $formatted['owner']);
-
- $singleFormatted = $this->getShare($accessContext2, $share->id);
- $this->assertEquals($singleFormatted, $formatted);
-
- // add a source that user2 doesn't have access to, can't see share anymore
- $this->dbConnection->beginTransaction();
- $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType2::class, 'source2'));
- $this->dbConnection->commit();
- $formattedShares = $this->getShares(new ShareAccessContext(currentUser: $this->user2), TestShareSourceType1::class, 'source1', null, null, null, null);
- $this->assertCount(0, $formattedShares);
- }
-
- public function testEditWithDirectAccess(): void {
- $accessContext = new ShareAccessContext($this->owner);
- $accessContext2 = new ShareAccessContext(currentUser: $this->user2);
-
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $share = $this->manager->addShareSource($accessContext, $share, new ShareSource(TestShareSourceType1::class, 'source1'));
- $share = $this->manager->addShareRecipient($accessContext, $share, new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null));
-
- $share = $this->manager->updateSharePermission($accessContext, $share, new SharePermission(TestSharePermissionType1::class, true));
- $share = $this->manager->updateShareState($accessContext, $share, ShareState::Active);
-
- $this->dbConnection->commit();
-
- try {
- $this->updateShareProperty($accessContext2, $share, new ShareProperty(TestSharePropertyType1::class, 'valid1'));
- $this->fail('update allowed');
- } catch (HintException) {
-
- }
-
- // give user2 direct access
- $this->shareSourceType1->userAccess[$this->user2->getUID()] = ['source1'];
- $user2Share = $this->reloadShare($accessContext2, $share);
- $this->assertEmpty($user2Share->recipients);
- $formatted = $this->updateShareProperty($accessContext2, $user2Share, new ShareProperty(TestSharePropertyType1::class, 'valid1'));
-
- $this->assertEquals([
- [
- 'display_name' => 'TestSharePropertyType1',
- 'hint' => 'hint TestSharePropertyType1',
- 'priority' => 1,
- 'required' => false,
- 'advanced' => false,
- 'value' => 'valid1',
- 'class' => \Test\Sharing\TestSharePropertyType1::class,
- 'type' => 'enum',
- 'valid_values' => ['valid1'],
- ]
- ], $formatted['properties']);
- }
-
- /**
- * @return array{
- * shares: array{recipients: ShareRecipient[], owner: string}[],
- * user: ShareUser,
- * filterRecipientTypeClasses?: ?array,
- * notInShare?: ?string,
- * count?: int,
- * offset?: int,
- * expected?: string[]
- * }[]
- */
- public static function getRecipientsForUsersData(): array {
- $initiator = new ShareUser('user1', null);
- $initiator2 = new ShareUser('user2', null);
- $recipient1 = new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null, null, $initiator);
- $recipient2 = new ShareRecipient(TestShareRecipientType2::class, 'recipient2', null, null, $initiator);
- $recipient1By2 = new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null, null, $initiator2);
- $owner = new ShareUser('owner', null);
-
- return [
- // querying for the owner, with no further filters
- ['shares' => [
- ['recipients' => [$recipient1, $recipient2], 'owner' => 'owner']
- ], 'user' => $owner, 'expected' => ['recipient1', 'recipient2']],
- // querying for the owner, limit
- ['shares' => [
- ['recipients' => [$recipient1, $recipient2], 'owner' => 'owner']
- ], 'user' => $owner, 'count' => 1, 'expected' => ['recipient1']],
- // querying for the owner, offset
- ['shares' => [
- ['recipients' => [$recipient1, $recipient2], 'owner' => 'owner']
- ], 'user' => $owner, 'offset' => 1, 'expected' => ['recipient2']],
- // querying for the owner, offset past end
- ['shares' => [
- ['recipients' => [$recipient1, $recipient2], 'owner' => 'owner']
- ], 'user' => $owner, 'offset' => 2, 'expected' => []],
- // querying for the initiator, with no further filters
- ['shares' => [
- ['recipients' => [$recipient1], 'owner' => 'owner']
- ], 'user' => $initiator, 'expected' => ['recipient1']],
- // querying for the wrong initiator
- ['shares' => [
- ['recipients' => [$recipient1], 'owner' => 'owner']
- ], 'user' => $initiator2, 'expected' => []],
- // querying for the owner, filter type
- ['shares' => [
- ['recipients' => [$recipient1, $recipient2], 'owner' => 'owner']
- ], 'user' => $owner, 'filterRecipientTypeClasses' => [TestShareRecipientType2::class], 'expected' => ['recipient2']],
- // querying for the owner, multiple shares
- ['shares' => [
- ['recipients' => [$recipient1, $recipient2], 'owner' => 'owner'],
- ['recipients' => [$recipient2], 'owner' => 'owner']
- ], 'user' => $owner, 'expected' => ['recipient2', 'recipient1']],
- // querying for the owner, multiple shares, not in share
- ['shares' => [
- ['recipients' => [$recipient1, $recipient2], 'owner' => 'owner'],
- 'excluded' => ['recipients' => [$recipient2], 'owner' => 'owner']
- ], 'user' => $owner, 'notInShare' => 'excluded', 'expected' => ['recipient1']],
- // querying for the owner, multiple shares, offset
- ['shares' => [
- ['recipients' => [$recipient1, $recipient2], 'owner' => 'owner'],
- ['recipients' => [$recipient2], 'owner' => 'owner']
- ], 'user' => $owner, 'offset' => 1, 'expected' => ['recipient1']],
- // user has both owned share and initiated recipients
- ['shares' => [
- ['recipients' => [$recipient1], 'owner' => 'user2'],
- ['recipients' => [$recipient1By2], 'owner' => 'owner']
- ], 'user' => $initiator2, 'offset' => 0, 'expected' => ['recipient1']],
- ];
- }
-
- /**
- * @param array{recipients: ShareRecipient[], owner: string}[] $shares
- * @param string[] $expected ,
- */
- #[DataProvider('getRecipientsForUsersData')]
- public function testGetRecipientsForUsers(
- array $shares,
- ShareUser $user,
- ?array $filterRecipientTypeClasses = null,
- ?string $notInShare = null,
- int $count = 5,
- int $offset = 0,
- array $expected = [],
- ): void {
- /** @var Share[] $share */
- $createdShares = [];
- $this->dbConnection->beginTransaction();
- foreach ($shares as $key => $shareData) {
- $owner = $this->createMock(IUser::class);
- $owner->method('getUID')->willReturn($shareData['owner']);
- $ownerAccessContext = new ShareAccessContext($owner);
-
- $share = $this->manager->createShare($ownerAccessContext);
- foreach ($shareData['recipients'] as $recipient) {
- $share = $this->manager->addShareRecipient($ownerAccessContext, $share, $recipient);
- }
-
- $createdShares[$key] = $share;
- }
-
- $this->dbConnection->commit();
-
- $accessUser = $this->createMock(IUser::class);
- $accessUser->method('getUID')->willReturn($user->userId);
-
- $accessContext = new ShareAccessContext($accessUser);
-
- $notInShareId = ($notInShare !== null) ? $createdShares[$notInShare] : null;
- $recipients = $this->searchRecipients($accessContext, $filterRecipientTypeClasses, '', $count, $offset, $notInShareId);
- $recipientValues = array_map(fn (array $recipient): string => $recipient['value'], $recipients);
- $this->assertEquals($expected, $recipientValues);
- }
-}
--- a/tests/lib/Sharing/ClassMapperTest.php
+++ /dev/null
@@ -1,68 +0,0 @@
-connection = Server::get(IDBConnection::class);
- $this->clearMappings();
- $this->classMapper = $this->getMapper();
- }
-
- private function clearMappings(): void {
- $query = $this->connection->getTypedQueryBuilder();
- $cleanupClasses = [IDBConnection::class, ClassMapper::class];
- $query->delete('sharing_classmap')
- ->where($query->expr()->in('class_name', $query->createNamedParameter($cleanupClasses, IQueryBuilder::PARAM_STR_ARRAY)));
- $query->executeStatement();
- }
-
- private function getMapper(): ClassMapper {
- return new ClassMapper($this->connection);
- }
-
- public function testGetInsert(): void {
- $id = $this->classMapper->getClassId(IDBConnection::class);
- $this->assertEquals($id, $this->classMapper->getClassId(IDBConnection::class));
- $this->assertEquals(IDBConnection::class, $this->classMapper->getClassName($id));
-
- $this->assertEquals($id, $this->getMapper()->getClassId(IDBConnection::class));
- }
-
- public function testInsertConcurrent(): void {
- $concurrentMapper = $this->getMapper();
-
- // trigger a load
- $this->classMapper->getClassId(ClassMapper::class);
-
- $id = $concurrentMapper->getClassId(IDBConnection::class);
- $this->assertEquals($id, $this->classMapper->getClassId(IDBConnection::class));
- $this->assertEquals(IDBConnection::class, $this->classMapper->getClassName($id));
- }
-
- public function testGetUnknownId(): void {
- $this->expectException(\Exception::class);
- $this->classMapper->getClassName(PHP_INT_MAX - 1);
- }
-}
--- a/tests/lib/Sharing/Property/ABooleanSharePropertyTypeTest.php
+++ /dev/null
@@ -1,81 +0,0 @@
-propertyType = new TestBooleanSharePropertyType();
- }
-
- public function testValidateValue(): void {
- $l10nFactory = Server::get(IFactory::class);
- $share = new Share(
- '123',
- new ShareUser('user', null),
- new DateTimeImmutable(),
- ShareState::Active,
- null,
- [],
- [],
- [],
- [],
- );
- $this->assertTrue($this->propertyType->validateValue($l10nFactory, $share, 'true'));
- $this->assertTrue($this->propertyType->validateValue($l10nFactory, $share, 'false'));
- $this->assertIsString($this->propertyType->validateValue($l10nFactory, $share, ''));
- $this->assertIsString($this->propertyType->validateValue($l10nFactory, $share, 'invalid'));
- }
-}
--- a/tests/lib/Sharing/Property/ADateSharePropertyTypeTest.php
+++ /dev/null
@@ -1,104 +0,0 @@
-minDate;
- }
-
- #[\Override]
- public function getMaxDate(Share $share): ?DateTimeImmutable {
- return $this->maxDate;
- }
-
- #[\Override]
- public function getDisplayName(IFactory $l10nFactory): string {
- throw new \RuntimeException();
- }
-
- #[\Override]
- public function getHint(IFactory $l10nFactory, Share $share): ?string {
- throw new \RuntimeException();
- }
-
- #[\Override]
- public function getPriority(): int {
- throw new \RuntimeException();
- }
-
- #[\Override]
- public function isAdvanced(): bool {
- throw new \RuntimeException();
- }
-
- #[\Override]
- public function isRequired(Share $share): bool {
- throw new \RuntimeException();
- }
-
- #[\Override]
- public function getDefaultValue(Share $share): ?string {
- throw new \RuntimeException();
- }
-}
-
-final class ADateSharePropertyTypeTest extends TestCase {
- private DateTimeImmutable $now;
-
- private ADateSharePropertyType $propertyType;
-
- #[\Override]
- public function setUp(): void {
- parent::setUp();
-
- $this->now = new DateTimeImmutable();
- $this->propertyType = new TestDateSharePropertyType(
- $this->now->sub(new DateInterval('PT1M')),
- $this->now->add(new DateInterval('PT1M')),
- );
- }
-
- public function testValidateValue(): void {
- $l10nFactory = Server::get(IFactory::class);
- $share = new Share(
- '123',
- new ShareUser('user', null),
- new DateTimeImmutable(),
- ShareState::Active,
- null,
- [],
- [],
- [],
- [],
- );
- $this->assertTrue($this->propertyType->validateValue($l10nFactory, $share, $this->now->format(DateTimeInterface::ATOM)));
- $this->assertIsString($this->propertyType->validateValue($l10nFactory, $share, $this->now->sub(new DateInterval('PT2M'))->format(DateTimeInterface::ATOM)));
- $this->assertIsString($this->propertyType->validateValue($l10nFactory, $share, $this->now->add(new DateInterval('PT2M'))->format(DateTimeInterface::ATOM)));
- }
-}
--- a/tests/lib/Sharing/Property/AEnumSharePropertyTypeTest.php
+++ /dev/null
@@ -1,93 +0,0 @@
- $validValues */
- public array $validValues,
- ) {
- }
-
- /**
- * @return non-empty-list
- */
- #[\Override]
- public function getValidValues(): array {
- return $this->validValues;
- }
-
- #[\Override]
- public function getDisplayName(IFactory $l10nFactory): string {
- throw new \RuntimeException();
- }
-
- #[\Override]
- public function getHint(IFactory $l10nFactory, Share $share): ?string {
- throw new \RuntimeException();
- }
-
- #[\Override]
- public function getPriority(): int {
- throw new \RuntimeException();
- }
-
- #[\Override]
- public function isAdvanced(): bool {
- throw new \RuntimeException();
- }
-
- #[\Override]
- public function isRequired(Share $share): bool {
- throw new \RuntimeException();
- }
-
- #[\Override]
- public function getDefaultValue(Share $share): ?string {
- throw new \RuntimeException();
- }
-}
-
-final class AEnumSharePropertyTypeTest extends TestCase {
- private AEnumSharePropertyType $propertyType;
-
- #[\Override]
- public function setUp(): void {
- parent::setUp();
-
- $this->propertyType = new TestEnumSharePropertyType(['valid']);
- }
-
- public function testValidateValue(): void {
- $l10nFactory = Server::get(IFactory::class);
- $share = new Share(
- '123',
- new ShareUser('user', null),
- new DateTimeImmutable(),
- ShareState::Active,
- null,
- [],
- [],
- [],
- [],
- );
- $this->assertTrue($this->propertyType->validateValue($l10nFactory, $share, 'valid'));
- $this->assertIsString($this->propertyType->validateValue($l10nFactory, $share, 'invalid'));
- }
-}
--- a/tests/lib/Sharing/Property/APasswordSharePropertyTypeTest.php
+++ /dev/null
@@ -1,123 +0,0 @@
-propertyType = new TestPasswordSharePropertyType();
-
- $this->eventDispatcher = Server::get(IEventDispatcher::class);
- $this->validatePasswordPolicyEventListener = static function (ValidatePasswordPolicyEvent $event): void {
- if ($event->getPassword() !== 'secure') {
- throw new HintException('insecure message', 'insecure hint');
- }
- };
-
- $this->eventDispatcher->addListener(ValidatePasswordPolicyEvent::class, $this->validatePasswordPolicyEventListener);
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->eventDispatcher->removeListener(ValidatePasswordPolicyEvent::class, $this->validatePasswordPolicyEventListener);
-
- parent::tearDown();
- }
-
- public function testValidateValue(): void {
- $l10nFactory = Server::get(IFactory::class);
- $share = new Share(
- '123',
- new ShareUser('user', null),
- new DateTimeImmutable(),
- ShareState::Active,
- null,
- [],
- [],
- [],
- [],
- );
- $this->assertTrue($this->propertyType->validateValue($l10nFactory, $share, 'secure'));
- $this->assertIsString($this->propertyType->validateValue($l10nFactory, $share, '123'));
- }
-
- public function testModifyValueOnFetch(): void {
- $this->assertNull($this->propertyType->modifyValueOnLoad(null));
- $this->assertEquals(APasswordSharePropertyType::PLACEHOLDER, $this->propertyType->modifyValueOnLoad(''));
- }
-
- public function testModifyValueOnSave(): void {
- $this->assertNull($this->propertyType->modifyValueOnSave('old hash', null));
-
- $this->assertEquals('old hash', $this->propertyType->modifyValueOnSave('old hash', APasswordSharePropertyType::PLACEHOLDER));
-
- $newHash = $this->propertyType->modifyValueOnSave('old hash', 'password');
- $this->assertNotNull($newHash);
- $this->assertNotEquals('old hash', $newHash);
- $this->assertTrue(Server::get(IHasher::class)->verify('password', $newHash));
- }
-}
--- a/tests/lib/Sharing/Property/AStringSharePropertyTypeTest.php
+++ /dev/null
@@ -1,103 +0,0 @@
-minLength;
- }
-
- #[\Override]
- public function getMaxLength(): ?int {
- return $this->maxLength;
- }
-
- #[\Override]
- public function getDisplayName(IFactory $l10nFactory): string {
- throw new \RuntimeException();
- }
-
- #[\Override]
- public function getHint(IFactory $l10nFactory, Share $share): ?string {
- throw new \RuntimeException();
- }
-
- #[\Override]
- public function getPriority(): int {
- throw new \RuntimeException();
- }
-
- #[\Override]
- public function isAdvanced(): bool {
- throw new \RuntimeException();
- }
-
- #[\Override]
- public function isRequired(Share $share): bool {
- throw new \RuntimeException();
- }
-
- #[\Override]
- public function getDefaultValue(Share $share): ?string {
- throw new \RuntimeException();
- }
-}
-
-final class AStringSharePropertyTypeTest extends TestCase {
- private AStringSharePropertyType $propertyType;
-
- #[\Override]
- public function setUp(): void {
- parent::setUp();
-
- $this->propertyType = new TestStringSharePropertyType(
- 3,
- 5,
- );
- }
-
- public function testValiStringValue(): void {
- $l10nFactory = Server::get(IFactory::class);
- $share = new Share(
- '123',
- new ShareUser('user', null),
- new DateTimeImmutable(),
- ShareState::Active,
- null,
- [],
- [],
- [],
- [],
- );
- $this->assertIsString($this->propertyType->validateValue($l10nFactory, $share, 'ab'));
- $this->assertTrue($this->propertyType->validateValue($l10nFactory, $share, 'abc'));
- $this->assertTrue($this->propertyType->validateValue($l10nFactory, $share, 'abcd'));
- $this->assertTrue($this->propertyType->validateValue($l10nFactory, $share, 'abcde'));
- $this->assertIsString($this->propertyType->validateValue($l10nFactory, $share, 'abcdef'));
- }
-}
--- a/tests/lib/Sharing/ShareTest.php
+++ /dev/null
@@ -1,211 +0,0 @@
-registry = Server::get(ISharingRegistry::class);
- $this->registry->clear();
-
- $this->owner = $this->createUser('owner', 'password');
- $this->user1 = $this->createUser('user1', 'password');
- $this->user2 = $this->createUser('user2', 'password');
- }
-
- #[\Override]
- public function tearDown(): void {
- $this->registry->clear();
-
- parent::tearDown();
- }
-
- public function testGetEffectiveRecipients(): void {
- $recipient1 = new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null);
- $recipient2 = new ShareRecipient(TestShareRecipientType1::class, 'recipient2', null);
- $recipient3 = new ShareRecipient(TestShareRecipientType1::class, 'recipient3', null, 'secret');
-
- $this->registry->registerRecipientType(
- new TestShareRecipientType1(
- [
- 'recipient1' => 'Recipient 1',
- 'recipient2' => 'Recipient 2',
- 'recipient3' => 'Recipient 3',
- ],
- [
- $this->user1->getUID() => ['recipient1'],
- $this->user2->getUID() => ['recipient2'],
- ],
- [],
- )
- );
-
- $share = new Share(
- '123',
- new ShareUser($this->owner->getUID(), null),
- new DateTimeImmutable(),
- ShareState::Active,
- null,
- [],
- [
- $recipient1,
- $recipient2,
- $recipient3,
- ],
- [],
- [],
- );
-
- $this->assertEquals([$recipient1], $share->getEffectiveRecipients(new ShareAccessContext($this->user1)));
- $this->assertEquals([$recipient2], $share->getEffectiveRecipients(new ShareAccessContext($this->user2)));
- $this->assertEquals([$recipient3], $share->getEffectiveRecipients(new ShareAccessContext(secret: 'secret')));
- $this->assertEquals([$recipient1, $recipient3], $share->getEffectiveRecipients(new ShareAccessContext($this->user1, 'secret')));
- }
-
- /**
- * @return list, array, SharePermission>, bool}>
- */
- public static function dataGetEffectiveEnabledPermissions(): array {
- $recipient1 = new ShareRecipient(TestShareRecipientType1::class, 'recipient1', null, null, null, [TestSharePermissionType1::class => new SharePermission(TestSharePermissionType1::class, true)]);
- $recipient2 = new ShareRecipient(TestShareRecipientType1::class, 'recipient2', null, null, null, [TestSharePermissionType1::class => new SharePermission(TestSharePermissionType1::class, false)]);
-
- return [
- [
- [$recipient1],
- [TestSharePermissionType1::class => new SharePermission(TestSharePermissionType1::class, true)],
- true,
- ],
- [
- [$recipient1],
- [TestSharePermissionType1::class => new SharePermission(TestSharePermissionType1::class, false)],
- false,
- ],
- [
- [$recipient1],
- [],
- false,
- ],
- //
- [
- [$recipient2],
- [TestSharePermissionType1::class => new SharePermission(TestSharePermissionType1::class, true)],
- false,
- ],
- [
- [$recipient2],
- [TestSharePermissionType1::class => new SharePermission(TestSharePermissionType1::class, false)],
- false,
- ],
- [
- [$recipient2],
- [],
- false,
- ],
- //
- [
- [$recipient1, $recipient2],
- [TestSharePermissionType1::class => new SharePermission(TestSharePermissionType1::class, true)],
- true,
- ],
- [
- [$recipient1, $recipient2],
- [TestSharePermissionType1::class => new SharePermission(TestSharePermissionType1::class, false)],
- false,
- ],
- [
- [$recipient1, $recipient2],
- [],
- false,
- ],
- //
- [
- [$recipient2, $recipient1],
- [TestSharePermissionType1::class => new SharePermission(TestSharePermissionType1::class, true)],
- true,
- ],
- [
- [$recipient2, $recipient1],
- [TestSharePermissionType1::class => new SharePermission(TestSharePermissionType1::class, false)],
- false,
- ],
- [
- [$recipient2, $recipient1],
- [],
- false,
- ],
- ];
- }
-
- /**
- * @param list $recipients
- * @param array, SharePermission> $permissions
- */
- #[DataProvider('dataGetEffectiveEnabledPermissions')]
- public function testGetEffectiveEnabledPermissions(array $recipients, array $permissions, bool $enabled): void {
- $this->registry->registerRecipientType(
- new TestShareRecipientType1(
- [
- 'recipient1' => 'Recipient 1',
- 'recipient2' => 'Recipient 2',
- ],
- [
- $this->user1->getUID() => ['recipient1', 'recipient2'],
- ],
- [],
- )
- );
-
- $share = new Share(
- '123',
- new ShareUser($this->owner->getUID(), null),
- new DateTimeImmutable(),
- ShareState::Active,
- null,
- [],
- $recipients,
- [],
- $permissions,
- );
-
- $this->assertEquals(
- $enabled
- ? [TestSharePermissionType1::class => new SharePermission(TestSharePermissionType1::class, true)]
- : [],
- $share->getEffectiveEnabledPermissions(new ShareAccessContext($this->user1)),
- );
- }
-}
--- a/tests/lib/Sharing/SharingManagerTest.php
+++ /dev/null
@@ -1,306 +0,0 @@
-manager->getShare($accessContext, $share->id);
- // don't compare time at sub-ms accuracy
- $this->assertEquals(SharingManager::timeToMs($retrieved->lastUpdated), SharingManager::timeToMs($share->lastUpdated));
-
- // now we compared the lastUpdated, make them the same to not fail the full comparison when there is a sub-ms lastUpdate difference
- $retrieved = new Share(
- $retrieved->id,
- $retrieved->owner,
- $share->lastUpdated,
- $retrieved->state,
- $retrieved->userStatus,
- $retrieved->sources,
- $retrieved->recipients,
- $retrieved->properties,
- $retrieved->permissions
- );
-
- // cache the enabled permissions for both
- foreach ([$retrieved, $share] as $item) {
- $item->getEffectiveEnabledPermissions(new ShareAccessContext(overrideChecks: true));
- $item->getEffectiveEnabledPermissions(new ShareAccessContext($this->owner));
- $item->getEffectiveEnabledPermissions(new ShareAccessContext($this->user1));
- $item->getEffectiveEnabledPermissions(new ShareAccessContext($this->user2));
- foreach ($item->recipients as $recipient) {
- $recipient->getDisabledPermissions();
- }
-
- // ensure source metadata is loaded
- foreach ($item->sources as $source) {
- $source->format($this->registry, $this->l10nFactory, false);
- }
- }
-
- $this->assertEquals($retrieved, $share, 'share object not in sync with database');
- }
-
- #[\Override]
- protected function searchRecipients(ShareAccessContext $accessContext, ?array $filterRecipientTypeClasses, string $query, int $limit, int $offset, ?Share $forShare = null): array {
- try {
- $this->dbConnection->beginTransaction();
- if ($forShare instanceof Share) {
- $this->assertShareSyncedWithDb($accessContext, $forShare);
- }
-
- /** @psalm-suppress ArgumentTypeCoercion */
- $recipients = ShareRecipient::formatMultiple($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class), $this->manager->searchRecipients($accessContext, $filterRecipientTypeClasses, $query, $limit, $offset, $forShare));
- $this->dbConnection->commit();
- return $recipients;
- } catch (Exception $exception) {
- $this->dbConnection->rollBack();
- throw $exception;
- }
- }
-
- #[\Override]
- protected function createShare(ShareAccessContext $accessContext): array {
- try {
- $this->dbConnection->beginTransaction();
- $share = $this->manager->createShare($accessContext);
- $this->assertShareSyncedWithDb($accessContext, $share);
- $share = $share->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class), $accessContext);
- $this->dbConnection->commit();
- return $share;
- } catch (Exception $exception) {
- $this->dbConnection->rollBack();
- throw $exception;
- }
- }
-
- #[\Override]
- protected function updateShareState(ShareAccessContext $accessContext, Share $share, ShareState $state): array {
- try {
- $this->dbConnection->beginTransaction();
- $share = $this->manager->updateShareState($accessContext, $share, $state);
- $this->assertShareSyncedWithDb($accessContext, $share);
- $share = $share->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class), $accessContext);
- $this->dbConnection->commit();
- return $share;
- } catch (Exception $exception) {
- $this->dbConnection->rollBack();
- throw $exception;
- }
- }
-
- #[\Override]
- protected function updateShareUserStatus(ShareAccessContext $accessContext, Share $share, ShareUserStatus $userStatus): array {
- try {
- $this->dbConnection->beginTransaction();
- $share = $this->manager->updateShareUserStatus($accessContext, $share, $userStatus);
- $this->assertShareSyncedWithDb($accessContext, $share);
- $share = $share->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class), $accessContext);
- $this->dbConnection->commit();
- return $share;
- } catch (Exception $exception) {
- $this->dbConnection->rollBack();
- throw $exception;
- }
- }
-
- #[\Override]
- protected function addShareSource(ShareAccessContext $accessContext, Share $share, ShareSource $source): array {
- try {
- $this->dbConnection->beginTransaction();
- $share = $this->manager->addShareSource($accessContext, $share, $source);
- $this->assertShareSyncedWithDb($accessContext, $share);
- $share = $share->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class), $accessContext);
- $this->dbConnection->commit();
- return $share;
- } catch (Exception $exception) {
- $this->dbConnection->rollBack();
- throw $exception;
- }
- }
-
- #[\Override]
- protected function removeShareSource(ShareAccessContext $accessContext, Share $share, ShareSource $source): array {
- try {
- $this->dbConnection->beginTransaction();
- $this->assertShareSyncedWithDb($accessContext, $share);
- $share = $this->manager->removeShareSource($accessContext, $share, $source);
- $this->assertShareSyncedWithDb($accessContext, $share);
- $share = $share->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class), $accessContext);
- $this->dbConnection->commit();
- return $share;
- } catch (Exception $exception) {
- $this->dbConnection->rollBack();
- throw $exception;
- }
- }
-
- #[\Override]
- protected function addShareRecipient(ShareAccessContext $accessContext, Share $share, ShareRecipient $recipient): array {
- try {
- $this->dbConnection->beginTransaction();
- $share = $this->manager->addShareRecipient($accessContext, $share, $recipient);
- $this->assertShareSyncedWithDb($accessContext, $share);
- $share = $share->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class), $accessContext);
- $this->dbConnection->commit();
- return $share;
- } catch (Exception $exception) {
- $this->dbConnection->rollBack();
- throw $exception;
- }
- }
-
- #[\Override]
- protected function removeShareRecipient(ShareAccessContext $accessContext, Share $share, ShareRecipient $recipient): array {
- try {
- $this->dbConnection->beginTransaction();
- $share = $this->manager->removeShareRecipient($accessContext, $share, $recipient);
- $this->assertShareSyncedWithDb($accessContext, $share);
- $share = $share->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class), $accessContext);
- $this->dbConnection->commit();
- return $share;
- } catch (Exception $exception) {
- $this->dbConnection->rollBack();
- throw $exception;
- }
- }
-
- #[\Override]
- protected function updateShareRecipientSecret(ShareAccessContext $accessContext, Share $share, ShareRecipient $recipient, string $secret): array {
- try {
- $this->dbConnection->beginTransaction();
- $share = $this->manager->updateShareRecipientSecret($accessContext, $share, $recipient, $secret);
- $this->assertShareSyncedWithDb($accessContext, $share);
- $share = $share->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class), $accessContext);
- $this->dbConnection->commit();
- return $share;
- } catch (Exception $exception) {
- $this->dbConnection->rollBack();
- throw $exception;
- }
- }
-
- #[\Override]
- protected function updateShareProperty(ShareAccessContext $accessContext, Share $share, ShareProperty $property): array {
- try {
- $this->dbConnection->beginTransaction();
- $share = $this->manager->updateShareProperty($accessContext, $share, $property);
- $this->assertShareSyncedWithDb($accessContext, $share);
- $share = $share->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class), $accessContext);
- $this->dbConnection->commit();
- return $share;
- } catch (Exception $exception) {
- $this->dbConnection->rollBack();
- throw $exception;
- }
- }
-
- #[\Override]
- protected function updateSharePermission(ShareAccessContext $accessContext, Share $share, SharePermission $permission): array {
- try {
- $this->dbConnection->beginTransaction();
- $share = $this->manager->updateSharePermission($accessContext, $share, $permission);
- $this->assertShareSyncedWithDb($accessContext, $share);
- $share = $share->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class), $accessContext);
- $this->dbConnection->commit();
- return $share;
- } catch (Exception $exception) {
- $this->dbConnection->rollBack();
- throw $exception;
- }
- }
-
- #[\Override]
- protected function updateShareRecipientPermission(ShareAccessContext $accessContext, Share $share, ShareRecipient $recipient, SharePermission $permission): array {
- try {
- $this->dbConnection->beginTransaction();
- $share = $this->manager->updateShareRecipientPermission($accessContext, $share, $recipient, $permission);
- $this->assertShareSyncedWithDb($accessContext, $share);
- $share = $share->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class), $accessContext);
- $this->dbConnection->commit();
- return $share;
- } catch (Exception $exception) {
- $this->dbConnection->rollBack();
- throw $exception;
- }
- }
-
- #[\Override]
- protected function selectSharePermissionPreset(ShareAccessContext $accessContext, Share $share, string $permissionPresetClass): array {
- try {
- $this->dbConnection->beginTransaction();
- /** @psalm-suppress ArgumentTypeCoercion */
- $share = $this->manager->selectSharePermissionPreset($accessContext, $share, $permissionPresetClass);
- $this->assertShareSyncedWithDb($accessContext, $share);
- $share = $share->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class), $accessContext);
- $this->dbConnection->commit();
- return $share;
- } catch (Exception $exception) {
- $this->dbConnection->rollBack();
- throw $exception;
- }
- }
-
- #[\Override]
- protected function deleteShare(ShareAccessContext $accessContext, Share $share): void {
- try {
- $this->dbConnection->beginTransaction();
- $this->assertShareSyncedWithDb($accessContext, $share);
- $this->manager->deleteShare($accessContext, $share);
- $this->dbConnection->commit();
- } catch (Exception $exception) {
- $this->dbConnection->rollBack();
- throw $exception;
- }
- }
-
- #[\Override]
- protected function getShare(ShareAccessContext $accessContext, string $id): array {
- try {
- $this->dbConnection->beginTransaction();
- $share = $this->manager->getShare($accessContext, $id)->format($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class), $accessContext);
- $this->dbConnection->commit();
- return $share;
- } catch (Exception $exception) {
- $this->dbConnection->rollBack();
- throw $exception;
- }
- }
-
- #[\Override]
- protected function getShares(ShareAccessContext $accessContext, ?string $filterSourceTypeClass, ?string $filterSourceTypeValue, ?ShareState $filterState, ?ShareUserStatus $filterUserStatus, ?string $lastShareID, ?int $limit): array {
- try {
- $this->dbConnection->beginTransaction();
- /** @psalm-suppress ArgumentTypeCoercion */
- $shares = $this->manager->getShares($accessContext, $filterSourceTypeClass, $filterSourceTypeValue, $filterState, $filterUserStatus, $lastShareID, $limit);
- $this->dbConnection->commit();
- return Share::formatMultiple($this->registry, Server::get(IFactory::class), Server::get(IURLGenerator::class), Server::get(IUserManager::class), $accessContext, $shares);
- } catch (Exception $exception) {
- $this->dbConnection->rollBack();
- throw $exception;
- }
- }
-}
--- a/tests/lib/Sharing/SharingRegistryTest.php
+++ /dev/null
@@ -1,171 +0,0 @@
-registry = Server::get(ISharingRegistry::class);
- $this->registry->clear();
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->registry->clear();
-
- parent::tearDown();
- }
-
- public function testClear(): void {
- $this->registry->registerSourceType(new TestShareSourceType1([]));
- $this->registry->registerRecipientType(new TestShareRecipientType1([], [], []));
- $this->registry->registerPropertyType(new TestSharePropertyType1(['']));
- $this->registry->markPropertyTypeCompatibleWithSourceType(TestSharePropertyType1::class, TestShareSourceType1::class);
- $this->registry->markPropertyTypeCompatibleWithRecipientType(TestSharePropertyType1::class, TestShareRecipientType1::class);
- $this->registry->registerPermissionType(TestShareSourceType1::class, new TestSharePermissionType1());
- $this->registry->registerPermissionPreset(new TestSharePermissionPreset1());
- $this->registry->markPermissionTypeCompatibleWithPermissionPreset(TestSharePermissionType1::class, TestSharePermissionPreset1::class);
-
- $this->registry->clear();
-
- $this->assertEquals([], $this->registry->getSourceTypes());
- $this->assertEquals([], $this->registry->getRecipientTypes());
- $this->assertEquals([], $this->registry->getPropertyTypes());
- $this->assertEquals([], $this->registry->getPropertyTypeCompatibleSourceTypeClasses());
- $this->assertEquals([], $this->registry->getPropertyTypeCompatibleRecipientTypes());
- $this->assertEquals([], $this->registry->getPermissionTypes());
- $this->assertEquals([], $this->registry->getPermissionPresets());
- $this->assertEquals([], $this->registry->getPermissionTypeCompatiblePermissionPresetClasses());
- $this->assertEquals([], $this->registry->getPermissionPresetCompatiblePermissionTypeClasses());
- }
-
- public function testRegisterSourceType(): void {
- $sourceType = new TestShareSourceType1([]);
- $this->registry->registerSourceType($sourceType);
-
- $this->assertEquals([$sourceType::class => $sourceType], $this->registry->getSourceTypes());
-
- $this->expectException(RuntimeException::class);
- $this->registry->registerSourceType($sourceType);
- }
-
- public function testRegisterRecipientType(): void {
- $recipientType = new TestShareRecipientType1([], [], []);
- $this->registry->registerRecipientType($recipientType);
-
- $this->assertEquals([$recipientType::class => $recipientType], $this->registry->getRecipientTypes());
-
- $this->expectException(RuntimeException::class);
- $this->registry->registerRecipientType($recipientType);
- }
-
- public function testRegisterProperty(): void {
- $sourceType = new TestShareSourceType1([]);
- $this->registry->registerSourceType($sourceType);
-
- $recipientType = new TestShareRecipientType1([], [], []);
- $this->registry->registerRecipientType($recipientType);
-
- $propertyType = new TestSharePropertyType1(['']);
- $this->registry->registerPropertyType($propertyType);
- $this->registry->markPropertyTypeCompatibleWithSourceType($propertyType::class, $sourceType::class);
- $this->registry->markPropertyTypeCompatibleWithRecipientType($propertyType::class, $recipientType::class);
-
- $this->assertEquals([$propertyType::class => $propertyType], $this->registry->getPropertyTypes());
-
- $this->expectException(RuntimeException::class);
- $this->registry->registerPropertyType($propertyType);
- }
-
- public function testMarkPropertyCompatibleWithSourceType(): void {
- $sourceType = new TestShareSourceType1([]);
- $this->registry->registerSourceType($sourceType);
-
- $propertyType = new TestSharePropertyType1(['']);
- $this->registry->registerPropertyType($propertyType);
- $this->registry->markPropertyTypeCompatibleWithSourceType($propertyType::class, $sourceType::class);
- $this->registry->markPropertyTypeCompatibleWithSourceType($propertyType::class, $sourceType::class);
-
- $this->assertEquals([$propertyType::class => [$sourceType::class]], $this->registry->getPropertyTypeCompatibleSourceTypeClasses());
-
- $this->registry->markPropertyTypeCompatibleWithSourceType($propertyType::class, TestShareSourceType2::class);
- $this->expectException(RuntimeException::class);
- $this->registry->getPropertyTypeCompatibleSourceTypeClasses();
- }
-
- public function testMarkPropertyCompatibleWithRecipientType(): void {
- $recipientType = new TestShareRecipientType1([], [], []);
- $this->registry->registerRecipientType($recipientType);
-
- $propertyType = new TestSharePropertyType1(['']);
- $this->registry->registerPropertyType($propertyType);
- $this->registry->markPropertyTypeCompatibleWithRecipientType($propertyType::class, $recipientType::class);
- $this->registry->markPropertyTypeCompatibleWithRecipientType($propertyType::class, $recipientType::class);
-
- $this->assertEquals([$propertyType::class => [$recipientType::class]], $this->registry->getPropertyTypeCompatibleRecipientTypes());
-
- $this->registry->markPropertyTypeCompatibleWithRecipientType($propertyType::class, TestShareRecipientType2::class);
- $this->expectException(RuntimeException::class);
- $this->registry->getPropertyTypeCompatibleRecipientTypes();
- }
-
- public function testRegisterPermission(): void {
- $sourceType = new TestShareSourceType1([]);
- $this->registry->registerSourceType($sourceType);
-
- $permissionType1 = new TestSharePermissionType1();
- $this->registry->registerPermissionType($sourceType::class, $permissionType1);
- $permissionType2 = new TestSharePermissionType2();
- $this->registry->registerPermissionType(null, $permissionType2);
-
- $this->assertEquals([$permissionType1::class => $permissionType1, $permissionType2::class => $permissionType2], $this->registry->getPermissionTypes());
- $this->assertEquals([$sourceType::class => [$permissionType1::class]], $this->registry->getSourceTypePermissionTypeClasses());
- $this->assertEquals([$permissionType2::class], $this->registry->getGenericPermissionTypeClasses());
-
- $this->expectException(RuntimeException::class);
- $this->registry->registerPermissionType($sourceType::class, $permissionType1);
- }
-
- public function testRegisterPermissionPreset(): void {
- $permissionPreset = new TestSharePermissionPreset1();
- $this->registry->registerPermissionPreset($permissionPreset);
-
- $this->assertEquals([$permissionPreset::class => $permissionPreset], $this->registry->getPermissionPresets());
-
- $this->expectException(RuntimeException::class);
- $this->registry->registerPermissionPreset($permissionPreset);
- }
-
- public function testMarkPermissionTypeCompatibleWithPermissionPreset(): void {
- $permissionPreset = new TestSharePermissionPreset1();
- $this->registry->registerPermissionPreset($permissionPreset);
-
- $permissionType = new TestSharePermissionType1();
- $this->registry->registerPermissionType(null, $permissionType);
- $this->registry->markPermissionTypeCompatibleWithPermissionPreset($permissionType::class, $permissionPreset::class);
- $this->registry->markPermissionTypeCompatibleWithPermissionPreset($permissionType::class, $permissionPreset::class);
-
- $this->assertEquals([$permissionType::class => [$permissionPreset::class]], $this->registry->getPermissionTypeCompatiblePermissionPresetClasses());
- $this->assertEquals([$permissionPreset::class => [$permissionType::class]], $this->registry->getPermissionPresetCompatiblePermissionTypeClasses());
-
- $this->registry->markPermissionTypeCompatibleWithPermissionPreset($permissionType::class, TestSharePermissionPreset2::class);
- $this->expectException(RuntimeException::class);
- $this->registry->getPermissionTypeCompatiblePermissionPresetClasses();
- }
-}
--- a/tests/lib/Sharing/TestInteractionReceiver.php
+++ /dev/null
@@ -1,24 +0,0 @@
-recipient;
- }
-}
--- a/tests/lib/Sharing/TestInteractionResource.php
+++ /dev/null
@@ -1,24 +0,0 @@
-source;
- }
-}
--- a/tests/lib/Sharing/TestSharePermissionPreset1.php
+++ /dev/null
@@ -1,27 +0,0 @@
- $parts */
- $parts = explode('\\', static::class);
- return end($parts);
- }
-
- #[\Override]
- public function getHint(IFactory $l10nFactory): ?string {
- return 'hint ' . $this->getDisplayName($l10nFactory);
- }
-}
--- a/tests/lib/Sharing/TestSharePermissionPreset2.php
+++ /dev/null
@@ -1,13 +0,0 @@
- $parts */
- $parts = explode('\\', static::class);
- return end($parts);
- }
-
- #[\Override]
- public function getHint(IFactory $l10nFactory): string {
- return 'hint ' . $this->getDisplayName($l10nFactory);
- }
-
- #[\Override]
- public function getPriority(): int {
- return 1;
- }
-
- #[\Override]
- public function isEnabledByDefault(): bool {
- return false;
- }
-}
--- a/tests/lib/Sharing/TestSharePermissionType2.php
+++ /dev/null
@@ -1,13 +0,0 @@
- $validValues */
- private readonly array $validValues,
- ) {
- }
-
- #[\Override]
- public function getDisplayName(IFactory $l10nFactory): string {
- /** @var non-empty-list $parts */
- $parts = explode('\\', static::class);
- return end($parts);
- }
-
- #[\Override]
- public function getHint(IFactory $l10nFactory, Share $share): string {
- return 'hint ' . $this->getDisplayName($l10nFactory);
- }
-
- #[\Override]
- public function getPriority(): int {
- return 1;
- }
-
- #[\Override]
- public function isAdvanced(): bool {
- return false;
- }
-
- #[\Override]
- public function isRequired(Share $share): bool {
- return false;
- }
-
- #[\Override]
- public function getDefaultValue(Share $share): ?string {
- return null;
- }
-
- /**
- * @return non-empty-list
- */
- #[\Override]
- public function getValidValues(): array {
- return $this->validValues;
- }
-}
--- a/tests/lib/Sharing/TestSharePropertyType2.php
+++ /dev/null
@@ -1,13 +0,0 @@
-arguments[self::class] ?? null) === 'filtered') {
- return true;
- }
-
- return ($property = $share->properties[self::class] ?? null) !== null && $property->value === 'filtered';
- }
-}
--- a/tests/lib/Sharing/TestSharePropertyTypeModifyValue.php
+++ /dev/null
@@ -1,43 +0,0 @@
-getValidValues()[0];
- }
-
- #[\Override]
- public function isRequired(Share $share): bool {
- return true;
- }
-}
--- a/tests/lib/Sharing/TestShareRecipientType1.php
+++ /dev/null
@@ -1,83 +0,0 @@
- $validRecipients */
- private readonly array $validRecipients,
- /** @var array> $recipientValues */
- private readonly array $recipientValues,
- /** @var list $searchRecipients */
- public array $searchRecipients,
- ) {
- }
-
- #[\Override]
- public function getDisplayName(IFactory $l10nFactory): string {
- /** @var non-empty-list $parts */
- $parts = explode('\\', static::class);
- return end($parts);
- }
-
- #[\Override]
- public function validateRecipient(string $recipient): bool {
- return array_key_exists($recipient, $this->validRecipients);
- }
-
- /**
- * @return list
- */
- #[\Override]
- public function getRecipients(?IUser $currentUser, mixed $arguments): array {
- if (!$currentUser instanceof IUser) {
- return [];
- }
-
- return $this->recipientValues[$currentUser->getUID()] ?? [];
- }
-
- #[\Override]
- public function getRecipientDisplayName(string $recipient, ?string $instance): ?string {
- if ($instance !== null) {
- return null;
- }
-
- return $this->validRecipients[$recipient];
- }
-
- #[\Override]
- public function getRecipientIcon(string $recipient, ?string $instance): null|ShareIconSVG|ShareIconURL {
- return match ($recipient) {
- 'url' => new ShareIconURL('https://example.com/light.png', 'https://example.com/dark.png'),
- default => new ShareIconSVG(''),
- };
- }
-
- #[\Override]
- public function getRecipientInteractionReceiver(string $recipient): InteractionReceiver {
- return new TestInteractionReceiver($recipient);
- }
-
- #[\Override]
- public function searchRecipients(ShareAccessContext $accessContext, string $query, int $limit, int $offset): array {
- return array_slice($this->searchRecipients, $offset, $limit);
- }
-}
--- a/tests/lib/Sharing/TestShareRecipientType2.php
+++ /dev/null
@@ -1,13 +0,0 @@
- $parts */
- $parts = explode('\\', self::class);
- return end($parts);
- }
-
- #[\Override]
- public function validateRecipient(string $recipient): bool {
- return true;
- }
-
- #[\Override]
- public function getRecipients(?IUser $currentUser, mixed $arguments): array {
- if (is_string($arguments)) {
- return [$arguments];
- }
-
- return [];
- }
-
- #[\Override]
- public function getRecipientDisplayName(string $recipient, ?string $instance): ?string {
- return null;
- }
-
- #[\Override]
- public function getRecipientIcon(string $recipient, ?string $instance): null|ShareIconSVG|ShareIconURL {
- return null;
- }
-
- #[\Override]
- public function getRecipientInteractionReceiver(string $recipient): InteractionReceiver {
- return new TestInteractionReceiver($recipient);
- }
-}
--- a/tests/lib/Sharing/TestShareRecipientTypePublicSecret.php
+++ /dev/null
@@ -1,82 +0,0 @@
- $validRecipients */
- private array $validRecipients,
- /** @var array> $recipientValues */
- private array $recipientValues,
- private bool $isSecretPublic,
- private bool $isSecretUpdatable,
- ) {
- }
-
- #[\Override]
- public function getDisplayName(IFactory $l10nFactory): string {
- /** @var non-empty-list $parts */
- $parts = explode('\\', self::class);
- return end($parts);
- }
-
- #[\Override]
- public function validateRecipient(string $recipient): bool {
- return array_key_exists($recipient, $this->validRecipients);
- }
-
- /**
- * @return list
- */
- #[\Override]
- public function getRecipients(?IUser $currentUser, mixed $arguments): array {
- if (!$currentUser instanceof IUser) {
- return [];
- }
-
- return $this->recipientValues[$currentUser->getUID()] ?? [];
- }
-
- #[\Override]
- public function getRecipientDisplayName(string $recipient, ?string $instance): ?string {
- return $this->validRecipients[$recipient];
- }
-
- #[\Override]
- public function getRecipientIcon(string $recipient, ?string $instance): ShareIconSVG|ShareIconURL {
- return match ($recipient) {
- 'url' => new ShareIconURL('https://example.com/light.png', 'https://example.com/dark.png'),
- default => new ShareIconSVG(''),
- };
- }
-
- #[\Override]
- public function isSecretPublic(string $recipient): bool {
- return $this->isSecretPublic;
- }
-
- #[\Override]
- public function isSecretUpdatable(string $recipient): bool {
- return $this->isSecretUpdatable;
- }
-
- #[\Override]
- public function getRecipientInteractionReceiver(string $recipient): InteractionReceiver {
- return new TestInteractionReceiver($recipient);
- }
-}
--- a/tests/lib/Sharing/TestShareSourceType1.php
+++ /dev/null
@@ -1,70 +0,0 @@
- $validSources */
- private readonly array $validSources,
- /** @var array $validSources */
- public array $userAccess = [],
- ) {
- }
-
- #[\Override]
- public function getDisplayName(IFactory $l10nFactory): string {
- /** @var non-empty-list $parts */
- $parts = explode('\\', static::class);
- return end($parts);
- }
-
- #[\Override]
- public function validateSource(IUser $owner, string $source): bool {
- return array_key_exists($source, $this->validSources);
- }
-
- #[\Override]
- public function getSourceMetadata(string $source): ?IShareSourceMetadata {
- if (isset($this->validSources[$source])) {
- return new ShareSourceMetadata(
- $this->validSources[$source],
- new ShareIconSVG(''),
- );
- }
-
- return null;
- }
-
- #[\Override]
- public function getSourcesMetadata(array $sources): array {
- $metas = array_map($this->getSourceMetadata(...), $sources);
- $metas = array_combine($sources, $metas);
- return array_filter($metas);
- }
-
- #[\Override]
- public function getSourceInteractionResource(IUser $user, string $source): InteractionResource {
- return new TestInteractionResource($source);
- }
-
- #[\Override]
- public function userHasDirectSharingAccessToSource(IUser $user, string $source): bool {
- $userSources = $this->userAccess[$user->getUID()] ?? [];
- return in_array($source, $userSources);
- }
-}
--- a/tests/lib/Sharing/TestShareSourceType2.php
+++ /dev/null
@@ -1,13 +0,0 @@
-sequence = new APCuSequence();
- }
-}
--- a/tests/lib/Snowflake/DecoderTest.php
+++ /dev/null
@@ -1,72 +0,0 @@
-decoder = new SnowflakeDecoder();
- }
-
- #[DataProvider('provideSnowflakeIds')]
- public function testDecode(
- string $snowflakeId,
- float $timestamp,
- int $serverId,
- int $sequenceId,
- bool $isCli,
- ): void {
- $data = $this->decoder->decode($snowflakeId);
-
- $this->assertEquals($timestamp, (float)$data->getCreatedAt()->format('U.v'));
- $this->assertEquals($serverId, $data->getServerId());
- $this->assertEquals($sequenceId, $data->getSequenceId());
- $this->assertEquals($isCli, $data->isCli());
- }
-
- public static function provideSnowflakeIds(): array {
- $data = [
- ['4688076898113587', 1760368327.984, 392, 2099, true],
- // Max milliseconds
- ['4190109696', 1759276800.999, 0, 0, false],
- // Max serverId
- ['4186112', 1759276800.0, 511, 0, false],
- // Max sequenceId
- ['4095', 1759276800.0, 0, 4095, false],
- // Max isCli
- ['4096', 1759276800.0, 0, 0, true],
- // Min
- ['0', 1759276800, 0, 0, false],
- // Other
- ['250159983611680096', 1817521710, 392, 1376, true],
- ];
-
- // 32 bits can't handle large timestamps correctly
- if (PHP_INT_SIZE === 8) {
- // Max all (can't happen because ms are up to 999)
- $data[] = ['9223372036854775807', 3906760448.023, 511, 4095, true];
- // Max all (real)
- $data[] = ['9223372036754112511', 3906760447.999, 511, 4095, true];
- // Max seconds
- $data[] = ['9223372032559808512', 3906760447, 0, 0, false];
- }
-
- return $data;
- }
-}
--- a/tests/lib/Snowflake/FileSequenceTest.php
+++ /dev/null
@@ -1,40 +0,0 @@
-createMock(ITempManager::class);
- $this->path = sys_get_temp_dir();
- $tempManager->method('getTempBaseDir')->willReturn($this->path);
- $this->sequence = new FileSequence($tempManager);
- }
-
- #[\Override]
- public function tearDown():void {
- $lockDirectory = $this->path . '/' . FileSequence::LOCK_FILE_DIRECTORY;
- foreach (glob($lockDirectory . '/*') as $file) {
- unlink($file);
- }
-
- parent::tearDown();
- }
-}
--- a/tests/lib/Snowflake/GeneratorTest.php
+++ /dev/null
@@ -1,131 +0,0 @@
-decoder = new SnowflakeDecoder();
-
- $this->sequence = $this->createMock(ISequence::class);
- $this->sequence->method('isAvailable')->willReturn(true);
- $this->sequence->method('nextId')->willReturn(421);
-
- $this->serverInfo = Server::get(IServerInfo::class);
- }
-
- public function testGenerator(): void {
- $generator = new SnowflakeGenerator(new TimeFactory(), $this->sequence, $this->serverInfo);
- $snowflakeId = $generator->nextId();
- $data = $this->decoder->decode($generator->nextId());
-
- $this->assertIsString($snowflakeId);
- // Check timestamp
- $this->assertGreaterThan(time() - 30, $data->getCreatedAt()->format('U'));
-
- // Check serverId
- $this->assertGreaterThanOrEqual(0, $data->getServerId());
- $this->assertLessThanOrEqual(511, $data->getServerId());
-
- // Check sequenceId
- $this->assertGreaterThanOrEqual(0, $data->getSequenceId());
- $this->assertLessThanOrEqual(4095, $data->getSequenceId());
-
- // Check CLI
- $this->assertTrue($data->isCli());
-
- // Check serverId
- $this->assertEquals($this->serverInfo->getServerId(), $data->getServerId());
- }
-
- public function testMinForTime(): void {
- $generator = new SnowflakeGenerator(new TimeFactory(), $this->sequence, $this->serverInfo);
- $now = time();
- $snowflakeId = $generator->minForTimeId($now);
- $data = $this->decoder->decode($snowflakeId);
-
- $this->assertIsString($snowflakeId);
-
- // Check timestamp
- $this->assertEquals($now - ISnowflakeGenerator::TS_OFFSET, $data->getSeconds());
-
- // Check all other fields are at zero
- $this->assertEquals(0, $data->getMilliseconds());
- $this->assertEquals(0, $data->getServerId());
- $this->assertEquals(0, $data->getSequenceId());
- $this->assertFalse($data->isCli());
- $this->assertEquals(0, $data->getServerId());
- }
-
- #[DataProvider('provideSnowflakeData')]
- public function testGeneratorWithFixedTime(string $date, int $expectedSeconds, int $expectedMilliseconds): void {
- $dt = new \DateTimeImmutable($date);
- $timeFactory = $this->createMock(ITimeFactory::class);
- $timeFactory->method('now')->willReturn($dt);
-
- $generator = new SnowflakeGenerator($timeFactory, $this->sequence, $this->serverInfo);
- $data = $this->decoder->decode($generator->nextId());
-
- $this->assertEquals($expectedSeconds, $data->getCreatedAt()->format('U') - ISnowflakeGenerator::TS_OFFSET);
- $this->assertEquals($expectedMilliseconds, (int)$data->getCreatedAt()->format('v'));
- $this->assertEquals($this->serverInfo->getServerId(), $data->getServerId());
- }
-
- #[DataProvider('provideSnowflakeData')]
- public function testGeneratorWithTimestampParameter(string $date, int $expectedSeconds, int $expectedMilliseconds): void {
- $dt = new \DateTimeImmutable($date);
-
- $generator = new SnowflakeGenerator(new TimeFactory(), $this->sequence, $this->serverInfo);
- $data = $this->decoder->decode($generator->nextId($dt));
-
- $this->assertEquals($expectedSeconds, $data->getCreatedAt()->format('U') - ISnowflakeGenerator::TS_OFFSET);
- $this->assertEquals($expectedMilliseconds, (int)$data->getCreatedAt()->format('v'));
- $this->assertEquals($this->serverInfo->getServerId(), $data->getServerId());
- }
-
- public static function provideSnowflakeData(): array {
- $tests = [
- ['2025-10-01 00:00:00.000000', 0, 0],
- ['2025-10-01 00:00:01.000000', 1, 0],
- ['2025-10-01 00:00:00.001000', 0, 1],
- ['2027-08-06 03:08:30.000975', 58244910, 0],
- ['2030-06-21 12:59:33.100875', 149000373, 100],
- ['2038-01-18 13:33:37.666666', 388157617, 666],
- ];
- // Timestamp in 32 bits can't go after 2038. Add few cases for 64 bits.
- if (PHP_INT_SIZE === 8) {
- $tests[] = ['2039-12-31 23:59:59.999999', 449711999, 999];
- $tests[] = ['2086-06-21 12:59:33.010875', 1916225973, 10];
- }
-
- return $tests;
- }
-}
--- a/tests/lib/Snowflake/ISequenceBase.php
+++ /dev/null
@@ -1,45 +0,0 @@
-sequence->isAvailable()) {
- $this->markTestSkipped('Sequence ID generator ' . get_class($this->sequence) . 'is’nt available. Skip');
- }
-
- $nb = 1000;
- $ids = [];
- $server = 42;
- for ($i = 0; $i < $nb; ++$i) {
- $time = explode('.', (string)microtime(true));
- $seconds = (int)$time[0];
- $milliseconds = str_pad(substr($time[1] ?? '0', 0, 3), 3, '0');
- $id = $this->sequence->nextId($server, $seconds, (int)$milliseconds);
- $ids[] = sprintf('%d_%s_%d', $seconds, $milliseconds, $id);
- usleep(100);
- }
-
- // Is it unique?
- $this->assertCount($nb, array_unique($ids));
- // Is it sequential?
- $sortedIds = $ids;
- natsort($sortedIds);
- $this->assertSame($sortedIds, $ids);
- }
-}
--- a/tests/lib/SubAdminTest.php
+++ /dev/null
@@ -1,301 +0,0 @@
-users = [];
- $this->groups = [];
-
- $this->userManager = Server::get(IUserManager::class);
- $this->groupManager = Server::get(IGroupManager::class);
- $this->dbConn = Server::get(IDBConnection::class);
- $this->eventDispatcher = Server::get(IEventDispatcher::class);
-
- // Create 3 users and 3 groups
- for ($i = 0; $i < 3; $i++) {
- $this->users[] = $this->userManager->createUser('user' . $i, 'user');
- $this->groups[] = $this->groupManager->createGroup('group' . $i);
- }
-
- // Create admin group
- if (!$this->groupManager->groupExists('admin')) {
- $this->groupManager->createGroup('admin');
- }
-
- // Create "orphaned" users and groups (scenario: temporarily disabled
- // backend)
- $qb = $this->dbConn->getQueryBuilder();
- $qb->insert('group_admin')
- ->values([
- 'gid' => $qb->createNamedParameter($this->groups[0]->getGID()),
- 'uid' => $qb->createNamedParameter('orphanedUser')
- ])
- ->executeStatement();
- $qb->insert('group_admin')
- ->values([
- 'gid' => $qb->createNamedParameter('orphanedGroup'),
- 'uid' => $qb->createNamedParameter('orphanedUser')
- ])
- ->executeStatement();
- $qb->insert('group_admin')
- ->values([
- 'gid' => $qb->createNamedParameter('orphanedGroup'),
- 'uid' => $qb->createNamedParameter($this->users[0]->getUID())
- ])
- ->executeStatement();
- }
-
- #[\Override]
- protected function tearDown(): void {
- foreach ($this->users as $user) {
- $user->delete();
- }
-
- foreach ($this->groups as $group) {
- $group->delete();
- }
-
- $qb = $this->dbConn->getQueryBuilder();
- $qb->delete('group_admin')
- ->where($qb->expr()->eq('uid', $qb->createNamedParameter('orphanedUser')))
- ->orWhere($qb->expr()->eq('gid', $qb->createNamedParameter('orphanedGroup')))
- ->executeStatement();
- parent::tearDown();
- }
-
- public function testCreateSubAdmin(): void {
- $subAdmin = new SubAdmin($this->userManager, $this->groupManager, $this->dbConn, $this->eventDispatcher);
- $subAdmin->createSubAdmin($this->users[0], $this->groups[0]);
-
- // Look for subadmin in the database
- $qb = $this->dbConn->getQueryBuilder();
- $result = $qb->select(['gid', 'uid'])
- ->from('group_admin')
- ->where($qb->expr()->eq('gid', $qb->createNamedParameter($this->groups[0]->getGID())))
- ->andWHere($qb->expr()->eq('uid', $qb->createNamedParameter($this->users[0]->getUID())))
- ->executeQuery()
- ->fetchAssociative();
- $this->assertEquals(
- [
- 'gid' => $this->groups[0]->getGID(),
- 'uid' => $this->users[0]->getUID()
- ], $result);
-
- // Delete subadmin
- $qb->delete('group_admin')
- ->where($qb->expr()->eq('gid', $qb->createNamedParameter($this->groups[0]->getGID())))
- ->andWHere($qb->expr()->eq('uid', $qb->createNamedParameter($this->users[0]->getUID())))
- ->executeStatement();
- }
-
- public function testDeleteSubAdmin(): void {
- $subAdmin = new SubAdmin($this->userManager, $this->groupManager, $this->dbConn, $this->eventDispatcher);
- $subAdmin->createSubAdmin($this->users[0], $this->groups[0]);
- $subAdmin->deleteSubAdmin($this->users[0], $this->groups[0]);
-
- // DB query should be empty
- $qb = $this->dbConn->getQueryBuilder();
- $result = $qb->select(['gid', 'uid'])
- ->from('group_admin')
- ->where($qb->expr()->eq('gid', $qb->createNamedParameter($this->groups[0]->getGID())))
- ->andWHere($qb->expr()->eq('uid', $qb->createNamedParameter($this->users[0]->getUID())))
- ->executeQuery()
- ->fetchAssociative();
- $this->assertEmpty($result);
- }
-
- public function testGetSubAdminsGroups(): void {
- $subAdmin = new SubAdmin($this->userManager, $this->groupManager, $this->dbConn, $this->eventDispatcher);
- $subAdmin->createSubAdmin($this->users[0], $this->groups[0]);
- $subAdmin->createSubAdmin($this->users[0], $this->groups[1]);
-
- $result = $subAdmin->getSubAdminsGroups($this->users[0]);
-
- $this->assertContains($this->groups[0], $result);
- $this->assertContains($this->groups[1], $result);
- $this->assertNotContains($this->groups[2], $result);
- $this->assertNotContains(null, $result);
-
- $subAdmin->deleteSubAdmin($this->users[0], $this->groups[0]);
- $subAdmin->deleteSubAdmin($this->users[0], $this->groups[1]);
- }
-
- public function testGetGroupsSubAdmins(): void {
- $subAdmin = new SubAdmin($this->userManager, $this->groupManager, $this->dbConn, $this->eventDispatcher);
- $subAdmin->createSubAdmin($this->users[0], $this->groups[0]);
- $subAdmin->createSubAdmin($this->users[1], $this->groups[0]);
-
- $result = $subAdmin->getGroupsSubAdmins($this->groups[0]);
-
- $this->assertContains($this->users[0], $result);
- $this->assertContains($this->users[1], $result);
- $this->assertNotContains($this->users[2], $result);
- $this->assertNotContains(null, $result);
-
- $subAdmin->deleteSubAdmin($this->users[0], $this->groups[0]);
- $subAdmin->deleteSubAdmin($this->users[1], $this->groups[0]);
- }
-
- public function testGetAllSubAdmin(): void {
- $subAdmin = new SubAdmin($this->userManager, $this->groupManager, $this->dbConn, $this->eventDispatcher);
-
- $subAdmin->createSubAdmin($this->users[0], $this->groups[0]);
- $subAdmin->createSubAdmin($this->users[1], $this->groups[1]);
- $subAdmin->createSubAdmin($this->users[2], $this->groups[1]);
-
- $result = $subAdmin->getAllSubAdmins();
-
- $this->assertContains(['user' => $this->users[0], 'group' => $this->groups[0]], $result);
- $this->assertContains(['user' => $this->users[1], 'group' => $this->groups[1]], $result);
- $this->assertContains(['user' => $this->users[2], 'group' => $this->groups[1]], $result);
- $this->assertNotContains(['user' => null, 'group' => null], $result);
- }
-
- public function testIsSubAdminofGroup(): void {
- $subAdmin = new SubAdmin($this->userManager, $this->groupManager, $this->dbConn, $this->eventDispatcher);
- $subAdmin->createSubAdmin($this->users[0], $this->groups[0]);
-
- $this->assertTrue($subAdmin->isSubAdminOfGroup($this->users[0], $this->groups[0]));
- $this->assertFalse($subAdmin->isSubAdminOfGroup($this->users[0], $this->groups[1]));
- $this->assertFalse($subAdmin->isSubAdminOfGroup($this->users[1], $this->groups[0]));
-
- $subAdmin->deleteSubAdmin($this->users[0], $this->groups[0]);
- }
-
- public function testIsSubAdmin(): void {
- $subAdmin = new SubAdmin($this->userManager, $this->groupManager, $this->dbConn, $this->eventDispatcher);
- $subAdmin->createSubAdmin($this->users[0], $this->groups[0]);
-
- $this->assertTrue($subAdmin->isSubAdmin($this->users[0]));
- $this->assertFalse($subAdmin->isSubAdmin($this->users[1]));
-
- $subAdmin->deleteSubAdmin($this->users[0], $this->groups[0]);
- }
-
- public function testIsSubAdminAsAdmin(): void {
- $subAdmin = new SubAdmin($this->userManager, $this->groupManager, $this->dbConn, $this->eventDispatcher);
- $this->groupManager->get('admin')->addUser($this->users[0]);
-
- $this->assertTrue($subAdmin->isSubAdmin($this->users[0]));
- }
-
- public function testIsUserAccessible(): void {
- $subAdmin = new SubAdmin($this->userManager, $this->groupManager, $this->dbConn, $this->eventDispatcher);
- $this->groups[0]->addUser($this->users[1]);
- $this->groups[1]->addUser($this->users[1]);
- $this->groups[1]->addUser($this->users[2]);
- $subAdmin->createSubAdmin($this->users[0], $this->groups[0]);
- $subAdmin->createSubAdmin($this->users[2], $this->groups[2]);
-
- $this->assertTrue($subAdmin->isUserAccessible($this->users[0], $this->users[1]));
- $this->assertFalse($subAdmin->isUserAccessible($this->users[0], $this->users[2]));
- $this->assertFalse($subAdmin->isUserAccessible($this->users[2], $this->users[0]));
-
- $subAdmin->deleteSubAdmin($this->users[0], $this->groups[0]);
- $subAdmin->deleteSubAdmin($this->users[2], $this->groups[2]);
- }
-
- public function testIsUserAccessibleAsUser(): void {
- $subAdmin = new SubAdmin($this->userManager, $this->groupManager, $this->dbConn, $this->eventDispatcher);
- $this->assertFalse($subAdmin->isUserAccessible($this->users[0], $this->users[1]));
- }
-
- public function testIsUserAccessibleAdmin(): void {
- $subAdmin = new SubAdmin($this->userManager, $this->groupManager, $this->dbConn, $this->eventDispatcher);
- $subAdmin->createSubAdmin($this->users[0], $this->groups[0]);
- $this->groupManager->get('admin')->addUser($this->users[1]);
-
- $this->assertFalse($subAdmin->isUserAccessible($this->users[0], $this->users[1]));
- }
-
- public function testPostDeleteUser(): void {
- $subAdmin = new SubAdmin($this->userManager, $this->groupManager, $this->dbConn, $this->eventDispatcher);
-
- $user = array_shift($this->users);
- foreach ($this->groups as $group) {
- $subAdmin->createSubAdmin($user, $group);
- }
-
- $user->delete();
- $this->assertEmpty($subAdmin->getAllSubAdmins());
- }
-
- public function testPostDeleteGroup(): void {
- $subAdmin = new SubAdmin($this->userManager, $this->groupManager, $this->dbConn, $this->eventDispatcher);
-
- $group = array_shift($this->groups);
- foreach ($this->users as $user) {
- $subAdmin->createSubAdmin($user, $group);
- }
-
- $group->delete();
- $this->assertEmpty($subAdmin->getAllSubAdmins());
- }
-
- public function testHooks(): void {
- $subAdmin = new SubAdmin($this->userManager, $this->groupManager, $this->dbConn, $this->eventDispatcher);
-
- $test = $this;
- $u = $this->users[0];
- $g = $this->groups[0];
- $count = 0;
-
- $this->eventDispatcher->addListener(SubAdminAddedEvent::class, function (SubAdminAddedEvent $event) use ($test, $u, $g, &$count): void {
- $test->assertEquals($u->getUID(), $event->getUser()->getUID());
- $test->assertEquals($g->getGID(), $event->getGroup()->getGID());
- $count++;
- });
-
- $this->eventDispatcher->addListener(SubAdminRemovedEvent::class, function ($event) use ($test, $u, $g, &$count): void {
- $test->assertEquals($u->getUID(), $event->getUser()->getUID());
- $test->assertEquals($g->getGID(), $event->getGroup()->getGID());
- $count++;
- });
-
- $subAdmin->createSubAdmin($u, $g);
- $this->assertEquals(1, $count);
-
- $subAdmin->deleteSubAdmin($u, $g);
- $this->assertEquals(2, $count);
- }
-}
--- a/tests/lib/Support/CrashReport/RegistryTest.php
+++ /dev/null
@@ -1,106 +0,0 @@
-registry = new Registry();
- }
-
- /**
- * Doesn't assert anything, just checks whether anything "explodes"
- */
- public function testDelegateToNone(): void {
- $exception = new Exception('test');
-
- $this->registry->delegateReport($exception);
- $this->addToAssertionCount(1);
- }
-
- public function testRegisterLazy(): void {
- $reporterClass = '\OCA\MyApp\Reporter';
- $reporter = $this->createMock(IReporter::class);
- $this->overwriteService($reporterClass, $reporter);
- $reporter->expects($this->once())
- ->method('report');
- $exception = new Exception('test');
-
- $this->registry->registerLazy($reporterClass);
- $this->registry->delegateReport($exception);
- }
-
- /**
- * Doesn't assert anything, just checks whether anything "explodes"
- */
- public function testRegisterLazyCantLoad(): void {
- $reporterClass = '\OCA\MyApp\Reporter';
- /* We do not register reporterClass in DI, so it will throw a QueryException queried */
- $exception = new Exception('test');
-
- $this->registry->registerLazy($reporterClass);
- $this->registry->delegateReport($exception);
- $this->addToAssertionCount(1);
- }
-
- public function testDelegateBreadcrumbCollection(): void {
- $reporter1 = $this->createMock(IReporter::class);
- $reporter2 = $this->createMock(ICollectBreadcrumbs::class);
- $message = 'hello';
- $category = 'log';
- $reporter2->expects($this->once())
- ->method('collect')
- ->with($message, $category);
-
- $this->registry->register($reporter1);
- $this->registry->register($reporter2);
- $this->registry->delegateBreadcrumb($message, $category);
- }
-
- public function testDelegateToAll(): void {
- $reporter1 = $this->createMock(IReporter::class);
- $reporter2 = $this->createMock(IReporter::class);
- $exception = new Exception('test');
- $reporter1->expects($this->once())
- ->method('report')
- ->with($exception);
- $reporter2->expects($this->once())
- ->method('report')
- ->with($exception);
-
- $this->registry->register($reporter1);
- $this->registry->register($reporter2);
- $this->registry->delegateReport($exception);
- }
-
- public function testDelegateMessage(): void {
- $reporter1 = $this->createMock(IReporter::class);
- $reporter2 = $this->createMock(IMessageReporter::class);
- $message = 'hello';
- $reporter2->expects($this->once())
- ->method('reportMessage')
- ->with($message, []);
-
- $this->registry->register($reporter1);
- $this->registry->register($reporter2);
- $this->registry->delegateMessage($message);
- }
-}
--- a/tests/lib/Support/Subscription/DummySubscription.php
+++ /dev/null
@@ -1,48 +0,0 @@
-hasValidSubscription;
- }
-
- /**
- * @inheritDoc
- */
- #[\Override]
- public function hasExtendedSupport(): bool {
- return $this->hasExtendedSupport;
- }
-
- #[\Override]
- public function isHardUserLimitReached(): bool {
- return $this->isHardUserLimitReached;
- }
-}
--- a/tests/lib/Support/Subscription/RegistryTest.php
+++ /dev/null
@@ -1,199 +0,0 @@
-config = $this->createMock(IConfig::class);
- $this->serverContainer = $this->createMock(ContainerInterface::class);
- $this->userManager = $this->createMock(IUserManager::class);
- $this->groupManager = $this->createMock(IGroupManager::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->notificationManager = $this->createMock(IManager::class);
- $this->registry = new Registry(
- $this->config,
- $this->serverContainer,
- $this->userManager,
- $this->groupManager,
- $this->logger
- );
- }
-
- /**
- * Doesn't assert anything, just checks whether anything "explodes"
- */
- public function testDelegateToNone(): void {
- $this->registry->delegateHasValidSubscription();
- $this->addToAssertionCount(1);
- }
-
- public function testDoubleRegistration(): void {
- $this->expectException(AlreadyRegisteredException::class);
-
- /* @var ISubscription $subscription1 */
- $subscription1 = $this->createMock(ISubscription::class);
- /* @var ISubscription $subscription2 */
- $subscription2 = $this->createMock(ISubscription::class);
- $this->registry->register($subscription1);
- $this->registry->register($subscription2);
- }
-
- public function testNoSupportApp(): void {
- $this->assertSame([], $this->registry->delegateGetSupportedApps());
- $this->assertSame(false, $this->registry->delegateHasValidSubscription());
- }
-
- public function testDelegateHasValidSubscription(): void {
- /* @var ISubscription|\PHPUnit\Framework\MockObject\MockObject $subscription */
- $subscription = $this->createMock(ISubscription::class);
- $subscription->expects($this->once())
- ->method('hasValidSubscription')
- ->willReturn(true);
- $this->registry->register($subscription);
-
- $this->assertSame(true, $this->registry->delegateHasValidSubscription());
- }
-
- public function testDelegateHasValidSubscriptionConfig(): void {
- /* @var ISubscription|\PHPUnit\Framework\MockObject\MockObject $subscription */
- $this->config->expects($this->once())
- ->method('getSystemValueBool')
- ->with('has_valid_subscription')
- ->willReturn(true);
-
- $this->assertSame(true, $this->registry->delegateHasValidSubscription());
- }
-
- public function testDelegateHasExtendedSupport(): void {
- /* @var ISubscription|\PHPUnit\Framework\MockObject\MockObject $subscription */
- $subscription = $this->createMock(ISubscription::class);
- $subscription->expects($this->once())
- ->method('hasExtendedSupport')
- ->willReturn(true);
- $this->registry->register($subscription);
-
- $this->assertSame(true, $this->registry->delegateHasExtendedSupport());
- }
-
- public function testDelegateGetSupportedApps(): void {
- /* @var ISupportedApps|\PHPUnit\Framework\MockObject\MockObject $subscription */
- $subscription = $this->createMock(ISupportedApps::class);
- $subscription->expects($this->once())
- ->method('getSupportedApps')
- ->willReturn(['abc']);
- $this->registry->register($subscription);
-
- $this->assertSame(['abc'], $this->registry->delegateGetSupportedApps());
- }
-
- public function testSubscriptionService(): void {
- $this->serverContainer->method('get')
- ->with(DummySubscription::class)
- ->willReturn(new DummySubscription(true, false, false));
- $this->registry->registerService(DummySubscription::class);
-
- $this->assertTrue($this->registry->delegateHasValidSubscription());
- $this->assertFalse($this->registry->delegateHasExtendedSupport());
- }
-
- public function testDelegateIsHardUserLimitReached(): void {
- /* @var ISubscription|\PHPUnit\Framework\MockObject\MockObject $subscription */
- $subscription = $this->createMock(ISubscription::class);
- $subscription->expects($this->once())
- ->method('hasValidSubscription')
- ->willReturn(true);
- $subscription->expects($this->once())
- ->method('isHardUserLimitReached')
- ->willReturn(true);
- $this->registry->register($subscription);
- $dummyGroup = $this->createMock(IGroup::class);
- $dummyGroup->expects($this->once())
- ->method('getUsers')
- ->willReturn([]);
- $this->groupManager->expects($this->once())
- ->method('get')
- ->willReturn($dummyGroup);
-
- $this->assertSame(true, $this->registry->delegateIsHardUserLimitReached($this->notificationManager));
- }
-
- public function testDelegateIsHardUserLimitReachedWithoutSupportApp(): void {
- $this->config->expects($this->once())
- ->method('getSystemValueBool')
- ->with('one-click-instance')
- ->willReturn(false);
-
- $this->assertSame(false, $this->registry->delegateIsHardUserLimitReached($this->notificationManager));
- }
-
- public static function dataForUserLimitCheck(): array {
- return [
- // $userLimit, $userCount, $disabledUsers, $expectedResult
- [35, 15, 2, false],
- [35, 45, 15, false],
- [35, 45, 5, true],
- [35, 45, 55, false],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('dataForUserLimitCheck')]
- public function testDelegateIsHardUserLimitReachedWithoutSupportAppAndUserCount($userLimit, $userCount, $disabledUsers, $expectedResult): void {
- $this->config->expects($this->once())
- ->method('getSystemValueBool')
- ->with('one-click-instance')
- ->willReturn(true);
- $this->config->expects($this->once())
- ->method('getSystemValueInt')
- ->with('one-click-instance.user-limit')
- ->willReturn($userLimit);
- $this->config->expects($this->once())
- ->method('getUsersForUserValue')
- ->with('core', 'enabled', 'false')
- ->willReturn(array_fill(0, $disabledUsers, ''));
- $this->userManager->expects($this->once())
- ->method('countUsersTotal')
- ->willReturn($userCount);
-
- if ($expectedResult) {
- $dummyGroup = $this->createMock(IGroup::class);
- $dummyGroup->expects($this->once())
- ->method('getUsers')
- ->willReturn([]);
- $this->groupManager->expects($this->once())
- ->method('get')
- ->willReturn($dummyGroup);
- }
-
- $this->assertSame($expectedResult, $this->registry->delegateIsHardUserLimitReached($this->notificationManager));
- }
-}
--- a/tests/lib/SystemConfigTest.php
+++ /dev/null
@@ -1,50 +0,0 @@
-config = $this->createMock(Config::class);
- }
-
- public function testGetFilteredValueMasksTheEuroOfficeSecret(): void {
- $this->config->method('getValue')
- ->willReturnMap([
- ['config_extra_sensitive_values', [], []],
- ['eurooffice', '', [
- 'editors_check_interval' => 0,
- 'jwt_secret' => 'a-document-server-signing-key',
- 'jwt_header' => 'AuthorizationJwt',
- ]],
- ]);
-
- $systemConfig = new SystemConfig($this->config);
-
- $this->assertSame([
- 'editors_check_interval' => 0,
- 'jwt_secret' => IConfig::SENSITIVE_VALUE,
- 'jwt_header' => 'AuthorizationJwt',
- ], $systemConfig->getFilteredValue('eurooffice'));
- }
-}
--- a/tests/lib/SystemReport/SystemReportManagerTest.php
+++ /dev/null
@@ -1,117 +0,0 @@
-coordinator = $this->createMock(Coordinator::class);
- $this->container = $this->createMock(ContainerInterface::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->manager = new SystemReportManager(
- $this->coordinator,
- $this->container,
- $this->logger,
- );
- }
-
- /**
- * @param \OC\AppFramework\Bootstrap\ServiceRegistration[] $registrations
- */
- private function withRegisteredSections(array $registrations): void {
- $context = $this->createMock(RegistrationContext::class);
- $context->expects(self::atLeastOnce())
- ->method('getSystemReportSections')
- ->willReturn($registrations);
-
- $this->coordinator->expects(self::atLeastOnce())
- ->method('getRegistrationContext')
- ->willReturn($context);
- }
-
- public function testGetSectionsReturnsNoneWhenNoneRegistered(): void {
- $this->withRegisteredSections([]);
-
- $this->assertSame([], $this->manager->getSections());
- }
-
- public function testGetSectionsResolvesAndReturnsRegisteredSections(): void {
- $section = $this->createMock(ISystemReportSection::class);
- $section->expects(self::once())
- ->method('getDetails')
- ->willReturn([]);
-
- $this->container->expects(self::once())
- ->method('get')
- ->with($section::class)
- ->willReturn($section);
-
- $this->withRegisteredSections([
- new ServiceRegistration('testing', $section::class),
- ]);
-
- $this->assertSame([$section], $this->manager->getSections());
- }
-
- public function testGetSectionsSkipsSectionThrowingDuringCollection(): void {
- $section = $this->createMock(ISystemReportSection::class);
- $section->method('getDetails')
- ->willThrowException(new \RuntimeException('boom'));
-
- $this->container->method('get')
- ->with($section::class)
- ->willReturn($section);
-
- $this->withRegisteredSections([
- new ServiceRegistration('testing', $section::class),
- ]);
-
- $this->logger->expects(self::once())
- ->method('error');
-
- $this->assertSame([], $this->manager->getSections());
- }
-
- public function testGetSectionsSkipsUnresolvableClass(): void {
- $this->container->method('get')
- ->with(\stdClass::class)
- ->willThrowException($this->createStub(NotFoundExceptionInterface::class));
-
- $this->withRegisteredSections([
- new ServiceRegistration('testing', \stdClass::class),
- ]);
-
- $this->logger->expects(self::once())
- ->method('error');
-
- $this->assertSame([], $this->manager->getSections());
- }
-}
--- a/tests/lib/SystemTag/SystemTagManagerTest.php
+++ /dev/null
@@ -1,586 +0,0 @@
-connection = Server::get(IDBConnection::class);
-
- $this->dispatcher = $this->createMock(IEventDispatcher::class);
- $this->groupManager = $this->createMock(IGroupManager::class);
- $this->userSession = $this->createMock(IUserSession::class);
- $this->appConfig = $this->createMock(IAppConfig::class);
-
- $this->tagManager = new SystemTagManager(
- $this->connection,
- $this->groupManager,
- $this->dispatcher,
- $this->userSession,
- $this->appConfig,
- );
- $this->pruneTagsTables();
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->pruneTagsTables();
- \OC::$CLI = true;
- parent::tearDown();
- }
-
- protected function pruneTagsTables() {
- $query = $this->connection->getQueryBuilder();
- $query->delete(SystemTagObjectMapper::RELATION_TABLE)->executeStatement();
- $query->delete(SystemTagManager::TAG_TABLE)->executeStatement();
- }
-
- public static function getAllTagsDataProvider(): array {
- return [
- [
- // no tags at all
- []
- ],
- [
- // simple
- [
- ['one', false, false],
- ['two', false, false],
- ]
- ],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('getAllTagsDataProvider')]
- public function testGetAllTags($testTags): void {
- $testTagsById = [];
- foreach ($testTags as $testTag) {
- $tag = $this->tagManager->createTag($testTag[0], $testTag[1], $testTag[2]);
- $testTagsById[$tag->getId()] = $tag;
- }
-
- $tagList = $this->tagManager->getAllTags();
-
- $this->assertCount(count($testTags), $tagList);
-
- foreach ($testTagsById as $testTagId => $testTag) {
- $this->assertTrue(isset($tagList[$testTagId]));
- $this->assertSameTag($tagList[$testTagId], $testTag);
- }
- }
-
- public static function getAllTagsFilteredDataProvider(): array {
- return [
- [
- [
- // no tags at all
- ],
- null,
- null,
- []
- ],
- // filter by visible only
- [
- // none visible
- [
- ['one', false, false],
- ['two', false, false],
- ],
- true,
- null,
- []
- ],
- [
- // one visible
- [
- ['one', true, false],
- ['two', false, false],
- ],
- true,
- null,
- [
- ['one', true, false],
- ]
- ],
- [
- // one invisible
- [
- ['one', true, false],
- ['two', false, false],
- ],
- false,
- null,
- [
- ['two', false, false],
- ]
- ],
- // filter by name pattern
- [
- [
- ['one', true, false],
- ['one_different', false, false],
- ['two', true, false],
- ],
- null,
- 'on',
- [
- ['one', true, false],
- ['one_different', false, false],
- ]
- ],
- // filter by name pattern and visibility
- [
- // one visible
- [
- ['one', true, false],
- ['two', true, false],
- ['one_different', false, false],
- ],
- true,
- 'on',
- [
- ['one', true, false],
- ]
- ],
- // filter by name pattern in the middle
- [
- // one visible
- [
- ['abcdefghi', true, false],
- ['two', true, false],
- ],
- null,
- 'def',
- [
- ['abcdefghi', true, false],
- ]
- ]
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('getAllTagsFilteredDataProvider')]
- public function testGetAllTagsFiltered($testTags, $visibilityFilter, $nameSearch, $expectedResults): void {
- foreach ($testTags as $testTag) {
- $this->tagManager->createTag($testTag[0], $testTag[1], $testTag[2]);
- }
-
- $testTagsById = [];
- foreach ($expectedResults as $expectedTag) {
- $tag = $this->tagManager->getTag($expectedTag[0], $expectedTag[1], $expectedTag[2]);
- $testTagsById[$tag->getId()] = $tag;
- }
-
- $tagList = $this->tagManager->getAllTags($visibilityFilter, $nameSearch);
-
- $this->assertCount(count($testTagsById), $tagList);
-
- foreach ($testTagsById as $testTagId => $testTag) {
- $this->assertTrue(isset($tagList[$testTagId]));
- $this->assertSameTag($tagList[$testTagId], $testTag);
- }
- }
-
- public static function oneTagMultipleFlagsProvider(): array {
- return [
- ['one', false, false],
- ['one', true, false],
- ['one', false, true],
- ['one', true, true],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('oneTagMultipleFlagsProvider')]
- public function testCreateDuplicate($name, $userVisible, $userAssignable): void {
- $this->expectException(TagAlreadyExistsException::class);
-
- try {
- $this->tagManager->createTag($name, $userVisible, $userAssignable);
- } catch (\Exception $e) {
- $this->assertTrue(false, 'No exception thrown for the first create call');
- }
- $this->tagManager->createTag($name, $userVisible, $userAssignable);
- }
-
- public function testCreateDuplicateWithDifferentFlags(): void {
- $this->expectException(TagAlreadyExistsException::class);
-
- // Create a tag with specific flags
- $this->tagManager->createTag('duplicate', true, false);
- // Try to create a tag with the same name but different flags - should fail
- $this->tagManager->createTag('duplicate', false, true);
- }
-
- public function testCreateOverlongName(): void {
- $tag = $this->tagManager->createTag('Zona circundante do Palácio Nacional da Ajuda (Jardim das Damas, Salão de Física, Torre Sineira, Paço Velho e Jardim Botânico)', true, true);
- $this->assertSame('Zona circundante do Palácio Nacional da Ajuda (Jardim das Damas', $tag->getName()); // 63 characters but 64 bytes due to "á"
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('oneTagMultipleFlagsProvider')]
- public function testGetExistingTag($name, $userVisible, $userAssignable): void {
- $tag1 = $this->tagManager->createTag($name, $userVisible, $userAssignable);
- $tag2 = $this->tagManager->getTag($name, $userVisible, $userAssignable);
-
- $this->assertSameTag($tag1, $tag2);
- }
-
- public function testGetExistingTagById(): void {
- $tag1 = $this->tagManager->createTag('one', true, false);
- $tag2 = $this->tagManager->createTag('two', false, true);
-
- $tagList = $this->tagManager->getTagsByIds([$tag1->getId(), $tag2->getId()]);
-
- $this->assertCount(2, $tagList);
-
- $this->assertSameTag($tag1, $tagList[$tag1->getId()]);
- $this->assertSameTag($tag2, $tagList[$tag2->getId()]);
- }
-
- public function testGetNonExistingTag(): void {
- $this->expectException(TagNotFoundException::class);
-
- $this->tagManager->getTag('nonexist', false, false);
- }
-
- public function testGetNonExistingTagsById(): void {
- $this->expectException(TagNotFoundException::class);
-
- $tag1 = $this->tagManager->createTag('one', true, false);
- $this->tagManager->getTagsByIds([$tag1->getId(), 100, 101]);
- }
-
- public function testGetInvalidTagIdFormat(): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $tag1 = $this->tagManager->createTag('one', true, false);
- $this->tagManager->getTagsByIds([$tag1->getId() . 'suffix']);
- }
-
- public static function updateTagProvider(): array {
- return [
- [
- // update name
- ['one', true, true],
- ['two', true, true, '0082c9']
- ],
- [
- // update one flag
- ['one', false, true],
- ['one', true, true, '0082c9']
- ],
- [
- // update all flags
- ['one', false, false],
- ['one', true, true, null]
- ],
- [
- // update all
- ['one', false, false],
- ['two', true, true, '0082c9']
- ],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('updateTagProvider')]
- public function testUpdateTag($tagCreate, $tagUpdated): void {
- $tag1 = $this->tagManager->createTag(
- $tagCreate[0],
- $tagCreate[1],
- $tagCreate[2],
- );
- $this->tagManager->updateTag(
- $tag1->getId(),
- $tagUpdated[0],
- $tagUpdated[1],
- $tagUpdated[2],
- $tagUpdated[3],
- );
- $tag2 = $this->tagManager->getTag(
- $tagUpdated[0],
- $tagUpdated[1],
- $tagUpdated[2],
- );
-
- $this->assertEquals($tag2->getId(), $tag1->getId());
- $this->assertEquals($tag2->getName(), $tagUpdated[0]);
- $this->assertEquals($tag2->isUserVisible(), $tagUpdated[1]);
- $this->assertEquals($tag2->isUserAssignable(), $tagUpdated[2]);
- $this->assertEquals($tag2->getColor(), $tagUpdated[3]);
-
- }
-
- public function testUpdateTagToExistingName(): void {
- $this->expectException(TagAlreadyExistsException::class);
-
- // Create two different tags
- $tag1 = $this->tagManager->createTag('first', true, true);
- $tag2 = $this->tagManager->createTag('second', false, false);
-
- // Try to update tag2 to have the same name as tag1 - should fail
- $this->tagManager->updateTag(
- $tag2->getId(),
- 'first',
- false,
- false,
- null
- );
- }
-
- public function testDeleteTags(): void {
- $tag1 = $this->tagManager->createTag('one', true, false);
- $tag2 = $this->tagManager->createTag('two', false, true);
-
- $this->tagManager->deleteTags([$tag1->getId(), $tag2->getId()]);
-
- $this->assertEmpty($this->tagManager->getAllTags());
- }
-
- public function testDeleteNonExistingTag(): void {
- $this->expectException(TagNotFoundException::class);
-
- $this->tagManager->deleteTags([100]);
- }
-
- public function testDeleteTagRemovesRelations(): void {
- $tag1 = $this->tagManager->createTag('one', true, false);
- $tag2 = $this->tagManager->createTag('two', true, true);
-
- $tagMapper = new SystemTagObjectMapper($this->connection, $this->tagManager, $this->dispatcher);
-
- $tagMapper->assignTags(1, 'testtype', $tag1->getId());
- $tagMapper->assignTags(1, 'testtype', $tag2->getId());
- $tagMapper->assignTags(2, 'testtype', $tag1->getId());
-
- $this->tagManager->deleteTags($tag1->getId());
-
- $tagIdMapping = $tagMapper->getTagIdsForObjects(
- [1, 2],
- 'testtype'
- );
-
- $this->assertEquals([
- 1 => [$tag2->getId()],
- 2 => [],
- ], $tagIdMapping);
- }
-
- public static function visibilityCheckProvider(): array {
- return [
- [false, false, false, false],
- [true, false, false, true],
- [false, false, true, true],
- [true, false, true, true],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('visibilityCheckProvider')]
- public function testVisibilityCheck($userVisible, $userAssignable, $isAdmin, $expectedResult): void {
- $user = $this->getMockBuilder(IUser::class)->getMock();
- $user->expects($this->any())
- ->method('getUID')
- ->willReturn('test');
- $tag1 = $this->tagManager->createTag('one', $userVisible, $userAssignable);
-
- $this->groupManager->expects($this->any())
- ->method('isAdmin')
- ->with('test')
- ->willReturn($isAdmin);
-
- $this->assertEquals($expectedResult, $this->tagManager->canUserSeeTag($tag1, $user));
- }
-
- public static function assignabilityCheckProvider(): array {
- return [
- // no groups
- [false, false, false, false],
- [true, false, false, false],
- [true, true, false, true],
- [false, true, false, false],
- // admin rulez
- [false, false, true, true],
- [false, true, true, true],
- [true, false, true, true],
- [true, true, true, true],
- // ignored groups
- [false, false, false, false, ['group1'], ['group1']],
- [true, true, false, true, ['group1'], ['group1']],
- [true, true, false, true, ['group1'], ['anothergroup']],
- [false, true, false, false, ['group1'], ['group1']],
- // admin has precedence over groups
- [false, false, true, true, ['group1'], ['anothergroup']],
- [false, true, true, true, ['group1'], ['anothergroup']],
- [true, false, true, true, ['group1'], ['anothergroup']],
- [true, true, true, true, ['group1'], ['anothergroup']],
- // groups only checked when visible and user non-assignable and non-admin
- [true, false, false, false, ['group1'], ['anothergroup1']],
- [true, false, false, true, ['group1'], ['group1']],
- [true, false, false, true, ['group1', 'group2'], ['group2', 'group3']],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('assignabilityCheckProvider')]
- public function testAssignabilityCheck($userVisible, $userAssignable, $isAdmin, $expectedResult, $userGroupIds = [], $tagGroupIds = []): void {
- $user = $this->getMockBuilder(IUser::class)->getMock();
- $user->expects($this->any())
- ->method('getUID')
- ->willReturn('test');
- $tag1 = $this->tagManager->createTag('one', $userVisible, $userAssignable);
- $this->tagManager->setTagGroups($tag1, $tagGroupIds);
-
- $this->groupManager->expects($this->any())
- ->method('isAdmin')
- ->with('test')
- ->willReturn($isAdmin);
- $this->groupManager->expects($this->any())
- ->method('getUserGroupIds')
- ->with($user)
- ->willReturn($userGroupIds);
-
- $this->assertEquals($expectedResult, $this->tagManager->canUserAssignTag($tag1, $user));
- }
-
- public function testTagGroups(): void {
- $tag1 = $this->tagManager->createTag('tag1', true, false);
- $tag2 = $this->tagManager->createTag('tag2', true, false);
- $this->tagManager->setTagGroups($tag1, ['group1', 'group2']);
- $this->tagManager->setTagGroups($tag2, ['group2', 'group3']);
-
- $this->assertEquals(['group1', 'group2'], $this->tagManager->getTagGroups($tag1));
- $this->assertEquals(['group2', 'group3'], $this->tagManager->getTagGroups($tag2));
-
- // change groups
- $this->tagManager->setTagGroups($tag1, ['group3', 'group4']);
- $this->tagManager->setTagGroups($tag2, []);
-
- $this->assertEquals(['group3', 'group4'], $this->tagManager->getTagGroups($tag1));
- $this->assertEquals([], $this->tagManager->getTagGroups($tag2));
- }
-
- /**
- * empty groupIds should be ignored
- */
- public function testEmptyTagGroup(): void {
- $tag1 = $this->tagManager->createTag('tag1', true, false);
- $this->tagManager->setTagGroups($tag1, ['']);
- $this->assertEquals([], $this->tagManager->getTagGroups($tag1));
- }
-
- public static function allowedToCreateProvider(): array {
- return [
- [true, null, true],
- [true, null, false],
- [false, true, true],
- [false, true, false],
- [false, false, false],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('allowedToCreateProvider')]
- public function testAllowedToCreateTag(bool $isCli, ?bool $isAdmin, bool $isRestricted): void {
- $oldCli = \OC::$CLI;
- \OC::$CLI = $isCli;
-
- $user = $this->getMockBuilder(IUser::class)->getMock();
- $user->expects($this->any())
- ->method('getUID')
- ->willReturn('test');
- $this->userSession->expects($this->any())
- ->method('getUser')
- ->willReturn($isAdmin === null ? null : $user);
- $this->groupManager->expects($this->any())
- ->method('isAdmin')
- ->with('test')
- ->willReturn($isAdmin);
- $this->appConfig->expects($this->any())
- ->method('getValueBool')
- ->with('systemtags', 'restrict_creation_to_admin')
- ->willReturn($isRestricted);
-
- $name = uniqid('tag_', true);
- $tag = $this->tagManager->createTag($name, true, true);
- $this->assertEquals($tag->getName(), $name);
- $this->tagManager->deleteTags($tag->getId());
-
- \OC::$CLI = $oldCli;
- }
-
- public static function disallowedToCreateProvider(): array {
- return [
- [false],
- [null],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('disallowedToCreateProvider')]
- public function testDisallowedToCreateTag(?bool $isAdmin): void {
- $oldCli = \OC::$CLI;
- \OC::$CLI = false;
-
- $user = $this->getMockBuilder(IUser::class)->getMock();
- $user->expects($this->any())
- ->method('getUID')
- ->willReturn('test');
- $this->userSession->expects($this->any())
- ->method('getUser')
- ->willReturn($isAdmin === null ? null : $user);
- $this->groupManager->expects($this->any())
- ->method('isAdmin')
- ->with('test')
- ->willReturn($isAdmin);
- $this->appConfig->expects($this->any())
- ->method('getValueBool')
- ->with('systemtags', 'restrict_creation_to_admin')
- ->willReturn(true);
-
- $this->expectException(\Exception::class);
- $tag = $this->tagManager->createTag(uniqid('tag_', true), true, true);
-
- \OC::$CLI = $oldCli;
- }
-
- /**
- * @param ISystemTag $tag1
- * @param ISystemTag $tag2
- */
- private function assertSameTag($tag1, $tag2) {
- $this->assertEquals($tag1->getId(), $tag2->getId());
- $this->assertEquals($tag1->getName(), $tag2->getName());
- $this->assertEquals($tag1->isUserVisible(), $tag2->isUserVisible());
- $this->assertEquals($tag1->isUserAssignable(), $tag2->isUserAssignable());
- }
-}
--- a/tests/lib/SystemTag/SystemTagObjectMapperTest.php
+++ /dev/null
@@ -1,459 +0,0 @@
-connection = Server::get(IDBConnection::class);
- $this->pruneTagsTables();
-
- $this->tagManager = $this->createMock(ISystemTagManager::class);
- $this->dispatcher = $this->createMock(IEventDispatcher::class);
-
- $this->tagMapper = new SystemTagObjectMapper(
- $this->connection,
- $this->tagManager,
- $this->dispatcher
- );
-
- $this->tag1 = new SystemTag(1, 'testtag1', false, false);
- $this->tag2 = new SystemTag(2, 'testtag2', true, false);
- $this->tag3 = new SystemTag(3, 'testtag3', false, false);
-
- $this->tagManager->expects($this->any())
- ->method('getTagsByIds')
- ->willReturnCallback(function ($tagIds) {
- $result = [];
- if (in_array(1, $tagIds)) {
- $result[1] = $this->tag1;
- }
- if (in_array(2, $tagIds)) {
- $result[2] = $this->tag2;
- }
- if (in_array(3, $tagIds)) {
- $result[3] = $this->tag3;
- }
- return $result;
- });
-
- $this->tagMapper->assignTags('1', 'testtype', $this->tag1->getId());
- $this->tagMapper->assignTags('1', 'testtype', $this->tag2->getId());
- $this->tagMapper->assignTags('2', 'testtype', $this->tag1->getId());
- $this->tagMapper->assignTags('3', 'anothertype', $this->tag1->getId());
- }
-
- #[\Override]
- protected function tearDown(): void {
- $this->pruneTagsTables();
- parent::tearDown();
- }
-
- protected function pruneTagsTables() {
- $query = $this->connection->getQueryBuilder();
- $query->delete(SystemTagObjectMapper::RELATION_TABLE)->executeStatement();
- $query->delete(SystemTagManager::TAG_TABLE)->executeStatement();
- }
-
- public function testGetTagIdsForObjects(): void {
- $tagIdMapping = $this->tagMapper->getTagIdsForObjects(
- ['1', '2', '3', '4'],
- 'testtype'
- );
-
- $this->assertEquals([
- '1' => [$this->tag1->getId(), $this->tag2->getId()],
- '2' => [$this->tag1->getId()],
- '3' => [],
- '4' => [],
- ], $tagIdMapping);
- }
-
- public function testGetTagIdsForNoObjects(): void {
- $tagIdMapping = $this->tagMapper->getTagIdsForObjects(
- [],
- 'testtype'
- );
-
- $this->assertEquals([], $tagIdMapping);
- }
-
- public function testGetTagIdsForALotOfObjects(): void {
- $ids = range(1, 10500);
- $tagIdMapping = $this->tagMapper->getTagIdsForObjects(
- $ids,
- 'testtype'
- );
-
- $this->assertEquals(10500, count($tagIdMapping));
- $this->assertEquals([$this->tag1->getId(), $this->tag2->getId()], $tagIdMapping[1]);
- }
-
- public function testGetObjectsForTags(): void {
- $objectIds = $this->tagMapper->getObjectIdsForTags(
- [$this->tag1->getId(), $this->tag2->getId(), $this->tag3->getId()],
- 'testtype'
- );
- sort($objectIds);
-
- $this->assertEquals([
- '1',
- '2',
- ], $objectIds);
- }
-
- public function testGetObjectsForTagsLimit(): void {
- $objectIds = $this->tagMapper->getObjectIdsForTags(
- [$this->tag1->getId()],
- 'testtype',
- 1
- );
-
- $this->assertEquals([
- 1,
- ], $objectIds);
- }
-
- public function testGetObjectsForTagsLimitWithMultipleTags(): void {
- $this->expectException(\InvalidArgumentException::class);
-
- $this->tagMapper->getObjectIdsForTags(
- [$this->tag1->getId(), $this->tag2->getId(), $this->tag3->getId()],
- 'testtype',
- 1
- );
- }
-
- public function testGetObjectsForTagsLimitOffset(): void {
- $objectIds = $this->tagMapper->getObjectIdsForTags(
- [$this->tag1->getId()],
- 'testtype',
- 1,
- '1'
- );
-
- $this->assertEquals([
- 2,
- ], $objectIds);
- }
-
- public function testGetObjectsForNonExistingTag(): void {
- $this->expectException(TagNotFoundException::class);
-
- $this->tagMapper->getObjectIdsForTags(
- [100],
- 'testtype'
- );
- }
-
- public function testAssignUnassignTags(): void {
- $event = null;
- $this->dispatcher->expects($this->any())->method('dispatchTyped')->willReturnCallback(function (Event $e) use (&$event): void {
- $event = $e;
- });
-
- $this->tagMapper->unassignTags('1', 'testtype', [$this->tag1->getId()]);
-
- $this->assertNotNull($event);
- $this->assertEquals(TagUnassignedEvent::class, $event::class);
- $this->assertEquals('testtype', $event->getObjectType());
- $this->assertCount(1, $event->getObjectIds());
- $this->assertEquals('1', current($event->getObjectIds()));
- $this->assertCount(1, $event->getTags());
- $this->assertEquals($this->tag1->getId(), current($event->getTags()));
-
- $tagIdMapping = $this->tagMapper->getTagIdsForObjects('1', 'testtype');
- $this->assertEquals([
- 1 => [$this->tag2->getId()],
- ], $tagIdMapping);
-
- $this->tagMapper->assignTags('1', 'testtype', [$this->tag1->getId()]);
-
- $this->assertNotNull($event);
- $this->assertEquals(TagAssignedEvent::class, $event::class);
- $this->assertEquals('testtype', $event->getObjectType());
- $this->assertCount(1, $event->getObjectIds());
- $this->assertEquals('1', current($event->getObjectIds()));
- $this->assertCount(1, $event->getTags());
- $this->assertEquals($this->tag1->getId(), current($event->getTags()));
-
- $this->tagMapper->assignTags('1', 'testtype', $this->tag3->getId());
-
- $this->assertNotNull($event);
- $this->assertEquals(TagAssignedEvent::class, $event::class);
- $this->assertEquals('testtype', $event->getObjectType());
- $this->assertCount(1, $event->getObjectIds());
- $this->assertEquals('1', current($event->getObjectIds()));
- $this->assertCount(1, $event->getTags());
- $this->assertEquals($this->tag3->getId(), current($event->getTags()));
-
- $tagIdMapping = $this->tagMapper->getTagIdsForObjects('1', 'testtype');
-
- $this->assertEquals([
- '1' => [$this->tag1->getId(), $this->tag2->getId(), $this->tag3->getId()],
- ], $tagIdMapping);
- }
-
- public function testSetObjectIdsForTagDispatchesUnassignEventOncePerObject(): void {
- $unassignedObjectIds = [];
- $this->dispatcher->expects($this->any())->method('dispatch')->willReturnCallback(
- function (string $eventName, Event $event) use (&$unassignedObjectIds): void {
- if ($eventName === MapperEvent::EVENT_UNASSIGN) {
- $unassignedObjectIds[] = $event->getObjectId();
- }
- }
- );
-
- // tag1 is assigned to objects '1' and '2', keep only '2'
- $this->tagMapper->setObjectIdsForTag((string)$this->tag1->getId(), 'testtype', ['2']);
-
- $this->assertEquals(['1'], $unassignedObjectIds);
-
- // same expectation when the new object list is empty
- $unassignedObjectIds = [];
- $this->tagMapper->setObjectIdsForTag((string)$this->tag1->getId(), 'testtype', []);
-
- $this->assertEquals(['2'], $unassignedObjectIds);
- }
-
- public function testReAssignUnassignTags(): void {
- // reassign tag1
- $this->tagMapper->assignTags('1', 'testtype', [$this->tag1->getId()]);
-
- // tag 3 was never assigned
- $this->tagMapper->unassignTags('1', 'testtype', [$this->tag3->getId()]);
-
- $this->assertTrue(true, 'No error when reassigning/unassigning');
- }
-
- public function testAssignNonExistingTags(): void {
- $this->expectException(TagNotFoundException::class);
-
- $this->tagMapper->assignTags('1', 'testtype', [100]);
- }
-
- public function testAssignNonExistingTagInArray(): void {
- $caught = false;
- try {
- $this->tagMapper->assignTags('1', 'testtype', [100, $this->tag3->getId()]);
- } catch (TagNotFoundException $e) {
- $caught = true;
- }
-
- $this->assertTrue($caught, 'Exception thrown');
-
- $tagIdMapping = $this->tagMapper->getTagIdsForObjects(
- ['1'],
- 'testtype'
- );
-
- $this->assertEquals([
- '1' => [$this->tag1->getId(), $this->tag2->getId()],
- ], $tagIdMapping, 'None of the tags got assigned');
- }
-
- public function testUnassignNonExistingTags(): void {
- $this->expectException(TagNotFoundException::class);
-
- $this->tagMapper->unassignTags('1', 'testtype', [100]);
- }
-
- public function testUnassignNonExistingTagsInArray(): void {
- $caught = false;
- try {
- $this->tagMapper->unassignTags('1', 'testtype', [100, $this->tag1->getId()]);
- } catch (TagNotFoundException $e) {
- $caught = true;
- }
-
- $this->assertTrue($caught, 'Exception thrown');
-
- $tagIdMapping = $this->tagMapper->getTagIdsForObjects(
- [1],
- 'testtype'
- );
-
- $this->assertEquals([
- '1' => [$this->tag1->getId(), $this->tag2->getId()],
- ], $tagIdMapping, 'None of the tags got unassigned');
- }
-
- public function testHaveTagAllMatches(): void {
- $this->assertTrue(
- $this->tagMapper->haveTag(
- ['1'],
- 'testtype',
- $this->tag1->getId(),
- true
- ),
- 'object 1 has the tag tag1'
- );
-
- $this->assertTrue(
- $this->tagMapper->haveTag(
- ['1', '2'],
- 'testtype',
- $this->tag1->getId(),
- true
- ),
- 'object 1 and object 2 ALL have the tag tag1'
- );
-
- $this->assertFalse(
- $this->tagMapper->haveTag(
- ['1', '2'],
- 'testtype',
- $this->tag2->getId(),
- true
- ),
- 'object 1 has tag2 but not object 2, so not ALL of them'
- );
-
- $this->assertFalse(
- $this->tagMapper->haveTag(
- ['2'],
- 'testtype',
- $this->tag2->getId(),
- true
- ),
- 'object 2 does not have tag2'
- );
-
- $this->assertFalse(
- $this->tagMapper->haveTag(
- ['3'],
- 'testtype',
- $this->tag2->getId(),
- true
- ),
- 'object 3 does not have tag1 due to different type'
- );
- }
-
- public function testHaveTagAtLeastOneMatch(): void {
- $this->assertTrue(
- $this->tagMapper->haveTag(
- ['1'],
- 'testtype',
- $this->tag1->getId(),
- false
- ),
- 'object1 has the tag tag1'
- );
-
- $this->assertTrue(
- $this->tagMapper->haveTag(
- ['1', '2'],
- 'testtype',
- $this->tag1->getId(),
- false
- ),
- 'object 1 and object 2 both the tag tag1'
- );
-
- $this->assertTrue(
- $this->tagMapper->haveTag(
- ['1', '2'],
- 'testtype',
- $this->tag2->getId(),
- false
- ),
- 'at least object 1 has the tag tag2'
- );
-
- $this->assertFalse(
- $this->tagMapper->haveTag(
- ['2'],
- 'testtype',
- $this->tag2->getId(),
- false
- ),
- 'object 2 does not have tag2'
- );
-
- $this->assertFalse(
- $this->tagMapper->haveTag(
- ['3'],
- 'testtype',
- $this->tag2->getId(),
- false
- ),
- 'object 3 does not have tag1 due to different type'
- );
- }
-
- public function testHaveTagNonExisting(): void {
- $this->expectException(TagNotFoundException::class);
-
- $this->tagMapper->haveTag(
- ['1'],
- 'testtype',
- 100
- );
- }
-}
--- a/tests/lib/TagsTest.php
+++ /dev/null
@@ -1,319 +0,0 @@
-clearBackends();
- Server::get(IUserManager::class)->registerBackend(new \Test\Util\User\Dummy());
- $userId = $this->getUniqueID('user_');
- Server::get(IUserManager::class)->createUser($userId, 'pass');
- \OC_User::setUserId($userId);
- $this->user = $this->createMock(IUser::class);
- $this->user->method('getUID')
- ->willReturn($userId);
- $this->userManager = $this->createMock(IUserManager::class);
- $this->userManager
- ->expects($this->any())
- ->method('getExistingUser')
- ->willReturn($this->user);
- $this->userSession = $this->createMock(IUserSession::class);
- $this->userSession
- ->expects($this->any())
- ->method('getUser')
- ->willReturn($this->user);
- $userFolder = $this->createMock(Folder::class);
- $node = $this->createMock(Node::class);
- $this->rootFolder = $this->createMock(IRootFolder::class);
- $this->rootFolder
- ->method('getUserFolder')
- ->willReturnCallback(fn () => $userFolder);
- $userFolder
- ->method('getFirstNodeById')
- ->willReturnCallback(fn () => $node);
- $node
- ->method('getPath')
- ->willReturnCallback(fn () => 'file.txt');
-
- $this->objectType = $this->getUniqueID('type_');
- $this->tagMapper = Server::get(TagMapper::class);
- $this->tagMgr = new TagManager(
- $this->tagMapper,
- $this->userSession,
- $this->userManager,
- Server::get(IDBConnection::class),
- Server::get(LoggerInterface::class),
- Server::get(IEventDispatcher::class),
- $this->rootFolder
- );
- }
-
- #[\Override]
- protected function tearDown(): void {
- $conn = Server::get(IDBConnection::class);
- $conn->getQueryBuilder()->delete('vcategory_to_object')->executeStatement();
- $conn->getQueryBuilder()->delete('vcategory')->executeStatement();
-
- parent::tearDown();
- }
-
- public function testTagManagerWithoutUserReturnsNull(): void {
- $this->userSession = $this->createMock(IUserSession::class);
- $this->userSession
- ->expects($this->any())
- ->method('getUser')
- ->willReturn(null);
- $this->tagMgr = new TagManager(
- $this->tagMapper,
- $this->userSession,
- $this->userManager,
- Server::get(IDBConnection::class),
- Server::get(LoggerInterface::class),
- Server::get(IEventDispatcher::class),
- $this->rootFolder
- );
- $this->assertNull($this->tagMgr->load($this->objectType));
- }
-
- public function testInstantiateWithDefaults(): void {
- $defaultTags = ['Friends', 'Family', 'Work', 'Other'];
-
- $tagger = $this->tagMgr->load($this->objectType, $defaultTags);
-
- $this->assertEquals(4, count($tagger->getTags()));
- }
-
- public function testAddTags(): void {
- $tags = ['Friends', 'Family', 'Work', 'Other'];
-
- $tagger = $this->tagMgr->load($this->objectType);
-
- foreach ($tags as $tag) {
- $result = $tagger->add($tag);
- $this->assertGreaterThan(0, $result, 'add() returned an ID <= 0');
- $this->assertTrue((bool)$result);
- }
-
- $this->assertFalse($tagger->add('Family'));
- $this->assertFalse($tagger->add('fAMILY'));
-
- $this->assertCount(4, $tagger->getTags(), 'Wrong number of added tags');
- }
-
- public function testAddMultiple(): void {
- $tags = ['Friends', 'Family', 'Work', 'Other'];
-
- $tagger = $this->tagMgr->load($this->objectType);
-
- foreach ($tags as $tag) {
- $this->assertFalse($tagger->hasTag($tag));
- }
-
- $result = $tagger->addMultiple($tags);
- $this->assertTrue((bool)$result);
-
- foreach ($tags as $tag) {
- $this->assertTrue($tagger->hasTag($tag));
- }
-
- $tagMaps = $tagger->getTags();
- $this->assertCount(4, $tagMaps, 'Not all tags added');
- foreach ($tagMaps as $tagMap) {
- $this->assertEquals(null, $tagMap['id']);
- }
-
- // As addMultiple has been called without $sync=true, the tags aren't
- // saved to the database, so they're gone when we reload $tagger:
-
- $tagger = $this->tagMgr->load($this->objectType);
- $this->assertEquals(0, count($tagger->getTags()));
-
- // Now, we call addMultiple() with $sync=true so the tags will be
- // be saved to the database.
- $result = $tagger->addMultiple($tags, true);
- $this->assertTrue((bool)$result);
-
- $tagMaps = $tagger->getTags();
- foreach ($tagMaps as $tagMap) {
- $this->assertNotEquals(null, $tagMap['id']);
- }
-
- // Reload the tagger.
- $tagger = $this->tagMgr->load($this->objectType);
-
- foreach ($tags as $tag) {
- $this->assertTrue($tagger->hasTag($tag));
- }
-
- $this->assertCount(4, $tagger->getTags(), 'Not all previously saved tags found');
- }
-
- public function testIsEmpty(): void {
- $tagger = $this->tagMgr->load($this->objectType);
-
- $this->assertEquals(0, count($tagger->getTags()));
- $this->assertTrue($tagger->isEmpty());
-
- $result = $tagger->add('Tag');
- $this->assertGreaterThan(0, $result, 'add() returned an ID <= 0');
- $this->assertNotEquals(false, $result, 'add() returned false');
- $this->assertFalse($tagger->isEmpty());
- }
-
- public function testGetTagsForObjects(): void {
- $defaultTags = ['Friends', 'Family', 'Work', 'Other'];
- $tagger = $this->tagMgr->load($this->objectType, $defaultTags);
-
- $tagger->tagAs(1, 'Friends');
- $tagger->tagAs(1, 'Other');
- $tagger->tagAs(2, 'Family');
-
- $tags = $tagger->getTagsForObjects([1]);
- $this->assertEquals(1, count($tags));
- $tags = current($tags);
- sort($tags);
- $this->assertSame(['Friends', 'Other'], $tags);
-
- $tags = $tagger->getTagsForObjects([1, 2]);
- $this->assertEquals(2, count($tags));
- $tags1 = $tags[1];
- sort($tags1);
- $this->assertSame(['Friends', 'Other'], $tags1);
- $this->assertSame(['Family'], $tags[2]);
- $this->assertEquals(
- [],
- $tagger->getTagsForObjects([4])
- );
- $this->assertEquals(
- [],
- $tagger->getTagsForObjects([4, 5])
- );
- }
-
- public function testGetTagsForObjectsMassiveResults(): void {
- $defaultTags = ['tag1'];
- $tagger = $this->tagMgr->load($this->objectType, $defaultTags);
- $tagData = $tagger->getTags();
- $tagId = $tagData[0]['id'];
- $tagType = $tagData[0]['type'];
-
- $conn = Server::get(IDBConnection::class);
-
- // insert lots of entries
- $idsArray = [];
- for ($i = 1; $i <= 1500; $i++) {
- $qb = $conn->getQueryBuilder();
- $qb->insert('vcategory_to_object')
- ->values([
- 'objid' => $qb->createNamedParameter($i, IQueryBuilder::PARAM_INT),
- 'categoryid' => $qb->createNamedParameter($tagId, IQueryBuilder::PARAM_INT),
- 'type' => $qb->createNamedParameter($tagType),
- ])
- ->executeStatement();
- $idsArray[] = $i;
- }
-
- $tags = $tagger->getTagsForObjects($idsArray);
- $this->assertEquals(1500, count($tags));
- }
-
- public function testDeleteTags(): void {
- $defaultTags = ['Friends', 'Family', 'Work', 'Other'];
- $tagger = $this->tagMgr->load($this->objectType, $defaultTags);
-
- $this->assertEquals(4, count($tagger->getTags()));
-
- $tagger->delete('family');
- $this->assertEquals(3, count($tagger->getTags()));
-
- $tagger->delete(['Friends', 'Work', 'Other']);
- $this->assertEquals(0, count($tagger->getTags()));
- }
-
- public function testRenameTag(): void {
- $defaultTags = ['Friends', 'Family', 'Wrok', 'Other'];
- $tagger = $this->tagMgr->load($this->objectType, $defaultTags);
-
- $this->assertTrue($tagger->rename('Wrok', 'Work'));
- $this->assertTrue($tagger->hasTag('Work'));
- $this->assertFalse($tagger->hasTag('Wrok'));
- $this->assertFalse($tagger->rename('Wrok', 'Work')); // Rename non-existant tag.
- $this->assertFalse($tagger->rename('Work', 'Family')); // Collide with existing tag.
- }
-
- public function testTagAs(): void {
- $objids = [1, 2, 3, 4, 5, 6, 7, 8, 9];
-
- $tagger = $this->tagMgr->load($this->objectType);
-
- foreach ($objids as $id) {
- $this->assertTrue($tagger->tagAs($id, 'Family'));
- }
-
- $this->assertEquals(1, count($tagger->getTags()));
- $this->assertEquals(9, count($tagger->getIdsForTag('Family')));
- }
-
- #[\PHPUnit\Framework\Attributes\Depends('testTagAs')]
- public function testUnTag(): void {
- $objIds = [1, 2, 3, 4, 5, 6, 7, 8, 9];
-
- // Is this "legal"?
- $this->testTagAs();
- $tagger = $this->tagMgr->load($this->objectType);
-
- foreach ($objIds as $id) {
- $this->assertTrue(in_array($id, $tagger->getIdsForTag('Family')));
- $tagger->unTag($id, 'Family');
- $this->assertFalse(in_array($id, $tagger->getIdsForTag('Family')));
- }
-
- $this->assertEquals(1, count($tagger->getTags()));
- $this->assertEquals(0, count($tagger->getIdsForTag('Family')));
- }
-
- public function testFavorite(): void {
- $tagger = $this->tagMgr->load($this->objectType);
- $this->assertTrue($tagger->addToFavorites(1));
- $this->assertEquals([1], $tagger->getFavorites());
- $this->assertTrue($tagger->removeFromFavorites(1));
- $this->assertEquals([], $tagger->getFavorites());
- }
-}
--- a/tests/lib/Talk/BrokerTest.php
+++ /dev/null
@@ -1,248 +0,0 @@
-coordinator = $this->createMock(Coordinator::class);
- $this->container = $this->createMock(ContainerInterface::class);
- $this->logger = $this->createMock(LoggerInterface::class);
-
- $this->broker = new Broker(
- $this->coordinator,
- $this->container,
- $this->logger,
- );
- }
-
- public function testHasNoBackendCalledTooEarly(): void {
- $this->expectException(RuntimeException::class);
-
- $this->broker->hasBackend();
- }
-
- public function testHasNoBackend(): void {
- $this->coordinator->expects($this->once())
- ->method('getRegistrationContext')
- ->willReturn($this->createMock(RegistrationContext::class));
-
- self::assertFalse(
- $this->broker->hasBackend()
- );
- }
-
- public function testHasFaultyBackend(): void {
- $fakeTalkServiceClass = '\\OCA\\Spreed\\TalkBackend';
- $registrationContext = $this->createMock(RegistrationContext::class);
- $this->coordinator->expects($this->once())
- ->method('getRegistrationContext')
- ->willReturn($registrationContext);
- $registrationContext->expects($this->once())
- ->method('getTalkBackendRegistration')
- ->willReturn(new ServiceRegistration('spreed', $fakeTalkServiceClass));
- $this->container->expects($this->once())
- ->method('get')
- ->willThrowException(new QueryException());
- $this->logger->expects($this->once())
- ->method('error');
-
- self::assertFalse(
- $this->broker->hasBackend()
- );
- }
-
- public function testHasBackend(): void {
- $fakeTalkServiceClass = '\\OCA\\Spreed\\TalkBackend';
- $registrationContext = $this->createMock(RegistrationContext::class);
- $this->coordinator->expects($this->once())
- ->method('getRegistrationContext')
- ->willReturn($registrationContext);
- $registrationContext->expects($this->once())
- ->method('getTalkBackendRegistration')
- ->willReturn(new ServiceRegistration('spreed', $fakeTalkServiceClass));
- $talkService = $this->createMock(ITalkBackend::class);
- $this->container->expects($this->once())
- ->method('get')
- ->with($fakeTalkServiceClass)
- ->willReturn($talkService);
-
- self::assertTrue(
- $this->broker->hasBackend()
- );
- }
-
- public function testNewConversationOptions(): void {
- $options = $this->broker->newConversationOptions();
-
- $this->assertInstanceOf(IConversationOptions::class, $options);
- }
-
- public function testCreateConversation(): void {
- $fakeTalkServiceClass = '\\OCA\\Spreed\\TalkBackend';
- $registrationContext = $this->createMock(RegistrationContext::class);
- $this->coordinator->expects($this->once())
- ->method('getRegistrationContext')
- ->willReturn($registrationContext);
- $registrationContext->expects($this->once())
- ->method('getTalkBackendRegistration')
- ->willReturn(new ServiceRegistration('spreed', $fakeTalkServiceClass));
- $talkService = $this->createMock(ITalkBackend::class);
- $this->container->expects($this->once())
- ->method('get')
- ->with($fakeTalkServiceClass)
- ->willReturn($talkService);
- $options = $this->createMock(IConversationOptions::class);
- $talkService->expects($this->once())
- ->method('createConversation')
- ->with('Watercooler', [], $options);
-
- $this->broker->createConversation(
- 'Watercooler',
- [],
- $options
- );
- }
-
- public function testIsEnabledForUserNoBackend(): void {
- $this->coordinator->expects($this->once())
- ->method('getRegistrationContext')
- ->willReturn($this->createMock(RegistrationContext::class));
-
- self::assertFalse(
- $this->broker->isEnabledForUser()
- );
- }
-
- public static function dataIsEnabledForUser(): array {
- return [
- [true],
- [false],
- ];
- }
-
- #[DataProvider('dataIsEnabledForUser')]
- public function testIsEnabledForUser(bool $enabled): void {
- $fakeTalkServiceClass = '\\OCA\\Spreed\\TalkBackend';
- $registrationContext = $this->createMock(RegistrationContext::class);
- $this->coordinator->expects($this->once())
- ->method('getRegistrationContext')
- ->willReturn($registrationContext);
- $registrationContext->expects($this->once())
- ->method('getTalkBackendRegistration')
- ->willReturn(new ServiceRegistration('spreed', $fakeTalkServiceClass));
- $talkService = $this->createMock(ITalkBackend::class);
- $this->container->expects($this->once())
- ->method('get')
- ->with($fakeTalkServiceClass)
- ->willReturn($talkService);
- $talkService->expects($this->once())
- ->method('isEnabledForUser')
- ->willReturn($enabled);
-
- self::assertSame(
- $enabled,
- $this->broker->isEnabledForUser()
- );
- }
-
- public function testIsAllowedToCreateConversationsNoBackend(): void {
- $this->coordinator->expects($this->once())
- ->method('getRegistrationContext')
- ->willReturn($this->createMock(RegistrationContext::class));
-
- self::assertFalse(
- $this->broker->isAllowedToCreateConversations()
- );
- }
-
- public function testIsAllowedToCreateConversationsBackendDisabled(): void {
- $fakeTalkServiceClass = '\\OCA\\Spreed\\TalkBackend';
- $registrationContext = $this->createMock(RegistrationContext::class);
- $this->coordinator->expects($this->once())
- ->method('getRegistrationContext')
- ->willReturn($registrationContext);
- $registrationContext->expects($this->once())
- ->method('getTalkBackendRegistration')
- ->willReturn(new ServiceRegistration('spreed', $fakeTalkServiceClass));
- $talkService = $this->createMock(ITalkBackend::class);
- $this->container->expects($this->once())
- ->method('get')
- ->with($fakeTalkServiceClass)
- ->willReturn($talkService);
- $talkService->expects($this->once())
- ->method('isEnabledForUser')
- ->willReturn(false);
- $talkService->expects($this->never())
- ->method('isAllowedToCreateConversations');
-
- self::assertFalse(
- $this->broker->isAllowedToCreateConversations()
- );
- }
-
- public static function dataIsAllowedToCreateConversations(): array {
- return [
- [true],
- [false],
- ];
- }
-
- #[DataProvider('dataIsAllowedToCreateConversations')]
- public function testIsAllowedToCreateConversations(bool $allowed): void {
- $fakeTalkServiceClass = '\\OCA\\Spreed\\TalkBackend';
- $registrationContext = $this->createMock(RegistrationContext::class);
- $this->coordinator->expects($this->once())
- ->method('getRegistrationContext')
- ->willReturn($registrationContext);
- $registrationContext->expects($this->once())
- ->method('getTalkBackendRegistration')
- ->willReturn(new ServiceRegistration('spreed', $fakeTalkServiceClass));
- $talkService = $this->createMock(ITalkBackend::class);
- $this->container->expects($this->once())
- ->method('get')
- ->with($fakeTalkServiceClass)
- ->willReturn($talkService);
- $talkService->expects($this->once())
- ->method('isEnabledForUser')
- ->willReturn(true);
- $talkService->expects($this->once())
- ->method('isAllowedToCreateConversations')
- ->willReturn($allowed);
-
- self::assertSame(
- $allowed,
- $this->broker->isAllowedToCreateConversations()
- );
- }
-}
--- a/tests/lib/Talk/ConversationOptionsTest.php
+++ /dev/null
@@ -1,21 +0,0 @@
-addToAssertionCount(1);
- }
-}
--- a/tests/lib/TaskProcessing/SkipLockedSqlShapeTest.php
+++ /dev/null
@@ -1,90 +0,0 @@
-systemConfig = $this->createMock(SystemConfig::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- }
-
- /**
- * Build a QueryBuilder backed by a non-SQLite (MySQL 8) platform so the
- * generated SQL exposes the locking clause the way it would in production.
- */
- private function newMysqlQueryBuilder(): QueryBuilder {
- $inner = $this->createMock(Connection::class);
- $inner->method('getDatabasePlatform')->willReturn(new MySQL80Platform());
-
- $adapter = $this->createMock(ConnectionAdapter::class);
- $adapter->method('getInner')->willReturn($inner);
- $adapter->method('getDatabaseProvider')->willReturn(IDBConnection::PLATFORM_MYSQL);
-
- return new QueryBuilder($adapter, $this->systemConfig, $this->logger);
- }
-
- public function testClaimQueryContainsForUpdateSkipLocked(): void {
- $qb = $this->newMysqlQueryBuilder();
- $qb->select('id', 'status', 'type', 'last_updated')
- ->from('taskprocessing_tasks')
- ->where($qb->expr()->eq('status', $qb->createPositionalParameter(1, IQueryBuilder::PARAM_INT)))
- ->orderBy('last_updated', 'ASC')
- ->setMaxResults(1)
- ->forUpdate(ConflictResolutionMode::SkipLocked);
-
- $sql = $qb->getSQL();
-
- self::assertStringContainsString('FOR UPDATE', $sql);
- self::assertStringContainsString('SKIP LOCKED', $sql);
- }
-
- public function testOrdinaryForUpdateHasNoSkipLocked(): void {
- // Sanity check: only the SkipLocked mode adds the SKIP LOCKED clause.
- $qb = $this->newMysqlQueryBuilder();
- $qb->select('id')
- ->from('taskprocessing_tasks')
- ->setMaxResults(1)
- ->forUpdate(ConflictResolutionMode::Ordinary);
-
- $sql = $qb->getSQL();
-
- self::assertStringContainsString('FOR UPDATE', $sql);
- self::assertStringNotContainsString('SKIP LOCKED', $sql);
- }
-}
--- a/tests/lib/TaskProcessing/TaskProcessingTest.php
+++ /dev/null
@@ -1,2005 +0,0 @@
- new ShapeDescriptor('Audio', 'The audio', EShapeType::Audio),
- ];
- }
-
- #[\Override]
- public function getOutputShape(): array {
- return [
- 'spectrogram' => new ShapeDescriptor('Spectrogram', 'The audio spectrogram', EShapeType::Image),
- ];
- }
-}
-
-class AsyncProvider implements IProvider {
- #[\Override]
- public function getId(): string {
- return 'test:sync:success';
- }
-
- #[\Override]
- public function getName(): string {
- return self::class;
- }
-
- #[\Override]
- public function getTaskTypeId(): string {
- return AudioToImage::ID;
- }
-
- #[\Override]
- public function getExpectedRuntime(): int {
- return 10;
- }
-
- #[\Override]
- public function getOptionalInputShape(): array {
- return [
- 'optionalKey' => new ShapeDescriptor('optional Key', 'AN optional key', EShapeType::Text),
- ];
- }
-
- #[\Override]
- public function getOptionalOutputShape(): array {
- return [
- 'optionalKey' => new ShapeDescriptor('optional Key', 'AN optional key', EShapeType::Text),
- ];
- }
-
- #[\Override]
- public function getInputShapeEnumValues(): array {
- return [];
- }
-
- #[\Override]
- public function getInputShapeDefaults(): array {
- return [];
- }
-
- #[\Override]
- public function getOptionalInputShapeEnumValues(): array {
- return [];
- }
-
- #[\Override]
- public function getOptionalInputShapeDefaults(): array {
- return [];
- }
-
- #[\Override]
- public function getOutputShapeEnumValues(): array {
- return [];
- }
-
- #[\Override]
- public function getOptionalOutputShapeEnumValues(): array {
- return [];
- }
-}
-
-class SuccessfulSyncProvider implements IProvider, ISynchronousProvider {
- public const ID = 'test:sync:success';
-
- #[\Override]
- public function getId(): string {
- return self::ID;
- }
-
- #[\Override]
- public function getName(): string {
- return self::class;
- }
-
- #[\Override]
- public function getTaskTypeId(): string {
- return TextToText::ID;
- }
-
- #[\Override]
- public function getExpectedRuntime(): int {
- return 10;
- }
-
- #[\Override]
- public function getOptionalInputShape(): array {
- return [
- 'optionalKey' => new ShapeDescriptor('optional Key', 'AN optional key', EShapeType::Text),
- ];
- }
-
- #[\Override]
- public function getOptionalOutputShape(): array {
- return [
- 'optionalKey' => new ShapeDescriptor('optional Key', 'AN optional key', EShapeType::Text),
- ];
- }
-
- #[\Override]
- public function process(?string $userId, array $input, callable $reportProgress): array {
- return ['output' => $input['input']];
- }
-
- #[\Override]
- public function getInputShapeEnumValues(): array {
- return [];
- }
-
- #[\Override]
- public function getInputShapeDefaults(): array {
- return [];
- }
-
- #[\Override]
- public function getOptionalInputShapeEnumValues(): array {
- return [];
- }
-
- #[\Override]
- public function getOptionalInputShapeDefaults(): array {
- return [];
- }
-
- #[\Override]
- public function getOutputShapeEnumValues(): array {
- return [];
- }
-
- #[\Override]
- public function getOptionalOutputShapeEnumValues(): array {
- return [];
- }
-}
-
-class FailingSyncProvider implements IProvider, ISynchronousProvider {
- public const ERROR_MESSAGE = 'Failure';
- #[\Override]
- public function getId(): string {
- return 'test:sync:fail';
- }
-
- #[\Override]
- public function getName(): string {
- return self::class;
- }
-
- #[\Override]
- public function getTaskTypeId(): string {
- return TextToText::ID;
- }
-
- #[\Override]
- public function getExpectedRuntime(): int {
- return 10;
- }
-
- #[\Override]
- public function getOptionalInputShape(): array {
- return [
- 'optionalKey' => new ShapeDescriptor('optional Key', 'AN optional key', EShapeType::Text),
- ];
- }
-
- #[\Override]
- public function getOptionalOutputShape(): array {
- return [
- 'optionalKey' => new ShapeDescriptor('optional Key', 'AN optional key', EShapeType::Text),
- ];
- }
-
- #[\Override]
- public function process(?string $userId, array $input, callable $reportProgress): array {
- throw new ProcessingException(self::ERROR_MESSAGE);
- }
-
- #[\Override]
- public function getInputShapeEnumValues(): array {
- return [];
- }
-
- #[\Override]
- public function getInputShapeDefaults(): array {
- return [];
- }
-
- #[\Override]
- public function getOptionalInputShapeEnumValues(): array {
- return [];
- }
-
- #[\Override]
- public function getOptionalInputShapeDefaults(): array {
- return [];
- }
-
- #[\Override]
- public function getOutputShapeEnumValues(): array {
- return [];
- }
-
- #[\Override]
- public function getOptionalOutputShapeEnumValues(): array {
- return [];
- }
-}
-
-class FailingSyncProviderWithUserFacingError implements IProvider, ISynchronousProvider {
- public const ERROR_MESSAGE = 'Failure';
- public const USER_FACING_ERROR_MESSAGE = 'User-facing Failure';
- #[\Override]
- public function getId(): string {
- return 'test:sync:fail:user-facing';
- }
-
- #[\Override]
- public function getName(): string {
- return self::class;
- }
-
- #[\Override]
- public function getTaskTypeId(): string {
- return TextToText::ID;
- }
-
- #[\Override]
- public function getExpectedRuntime(): int {
- return 10;
- }
-
- #[\Override]
- public function getOptionalInputShape(): array {
- return [
- 'optionalKey' => new ShapeDescriptor('optional Key', 'AN optional key', EShapeType::Text),
- ];
- }
-
- #[\Override]
- public function getOptionalOutputShape(): array {
- return [
- 'optionalKey' => new ShapeDescriptor('optional Key', 'AN optional key', EShapeType::Text),
- ];
- }
-
- #[\Override]
- public function process(?string $userId, array $input, callable $reportProgress): array {
- throw new UserFacingProcessingException(self::ERROR_MESSAGE, userFacingMessage: self::USER_FACING_ERROR_MESSAGE);
- }
-
- #[\Override]
- public function getInputShapeEnumValues(): array {
- return [];
- }
-
- #[\Override]
- public function getInputShapeDefaults(): array {
- return [];
- }
-
- #[\Override]
- public function getOptionalInputShapeEnumValues(): array {
- return [];
- }
-
- #[\Override]
- public function getOptionalInputShapeDefaults(): array {
- return [];
- }
-
- #[\Override]
- public function getOutputShapeEnumValues(): array {
- return [];
- }
-
- #[\Override]
- public function getOptionalOutputShapeEnumValues(): array {
- return [];
- }
-}
-
-class BrokenSyncProvider implements IProvider, ISynchronousProvider {
- #[\Override]
- public function getId(): string {
- return 'test:sync:broken-output';
- }
-
- #[\Override]
- public function getName(): string {
- return self::class;
- }
-
- #[\Override]
- public function getTaskTypeId(): string {
- return TextToText::ID;
- }
-
- #[\Override]
- public function getExpectedRuntime(): int {
- return 10;
- }
-
- #[\Override]
- public function getOptionalInputShape(): array {
- return [
- 'optionalKey' => new ShapeDescriptor('optional Key', 'AN optional key', EShapeType::Text),
- ];
- }
-
- #[\Override]
- public function getOptionalOutputShape(): array {
- return [
- 'optionalKey' => new ShapeDescriptor('optional Key', 'AN optional key', EShapeType::Text),
- ];
- }
-
- #[\Override]
- public function process(?string $userId, array $input, callable $reportProgress): array {
- return [];
- }
-
- #[\Override]
- public function getInputShapeEnumValues(): array {
- return [];
- }
-
- #[\Override]
- public function getInputShapeDefaults(): array {
- return [];
- }
-
- #[\Override]
- public function getOptionalInputShapeEnumValues(): array {
- return [];
- }
-
- #[\Override]
- public function getOptionalInputShapeDefaults(): array {
- return [];
- }
-
- #[\Override]
- public function getOutputShapeEnumValues(): array {
- return [];
- }
-
- #[\Override]
- public function getOptionalOutputShapeEnumValues(): array {
- return [];
- }
-}
-
-class SuccessfulTextProcessingSummaryProvider implements \OCP\TextProcessing\IProvider {
- public bool $ran = false;
-
- #[\Override]
- public function getName(): string {
- return 'TEST Vanilla LLM Provider';
- }
-
- #[\Override]
- public function process(string $prompt): string {
- $this->ran = true;
- return $prompt . ' Summarize';
- }
-
- #[\Override]
- public function getTaskType(): string {
- return SummaryTaskType::class;
- }
-}
-
-class FailingTextProcessingSummaryProvider implements \OCP\TextProcessing\IProvider {
- public bool $ran = false;
-
- #[\Override]
- public function getName(): string {
- return 'TEST Vanilla LLM Provider';
- }
-
- #[\Override]
- public function process(string $prompt): string {
- $this->ran = true;
- throw new \Exception('ERROR');
- }
-
- #[\Override]
- public function getTaskType(): string {
- return SummaryTaskType::class;
- }
-}
-
-class SuccessfulTextToImageProvider implements \OCP\TextToImage\IProvider {
- public bool $ran = false;
-
- #[\Override]
- public function getId(): string {
- return 'test:successful';
- }
-
- #[\Override]
- public function getName(): string {
- return 'TEST Provider';
- }
-
- #[\Override]
- public function generate(string $prompt, array $resources): void {
- $this->ran = true;
- foreach ($resources as $resource) {
- fwrite($resource, 'test');
- }
- }
-
- #[\Override]
- public function getExpectedRuntime(): int {
- return 1;
- }
-}
-
-class FailingTextToImageProvider implements \OCP\TextToImage\IProvider {
- public bool $ran = false;
-
- #[\Override]
- public function getId(): string {
- return 'test:failing';
- }
-
- #[\Override]
- public function getName(): string {
- return 'TEST Provider';
- }
-
- #[\Override]
- public function generate(string $prompt, array $resources): void {
- $this->ran = true;
- throw new \RuntimeException('ERROR');
- }
-
- #[\Override]
- public function getExpectedRuntime(): int {
- return 1;
- }
-}
-
-class ExternalProvider implements IProvider {
- public const ID = 'event:external:provider';
- public const TASK_TYPE_ID = 'event:external:tasktype';
-
- #[\Override]
- public function getId(): string {
- return self::ID;
- }
- #[\Override]
- public function getName(): string {
- return 'External Provider via Event';
- }
- #[\Override]
- public function getTaskTypeId(): string {
- return self::TASK_TYPE_ID;
- }
- #[\Override]
- public function getExpectedRuntime(): int {
- return 5;
- }
- #[\Override]
- public function getOptionalInputShape(): array {
- return [];
- }
- #[\Override]
- public function getOptionalOutputShape(): array {
- return [];
- }
- #[\Override]
- public function getInputShapeEnumValues(): array {
- return [];
- }
- #[\Override]
- public function getInputShapeDefaults(): array {
- return [];
- }
- #[\Override]
- public function getOptionalInputShapeEnumValues(): array {
- return [];
- }
- #[\Override]
- public function getOptionalInputShapeDefaults(): array {
- return [];
- }
- #[\Override]
- public function getOutputShapeEnumValues(): array {
- return [];
- }
- #[\Override]
- public function getOptionalOutputShapeEnumValues(): array {
- return [];
- }
-}
-
-class ExternalTriggerableProvider implements ITriggerableProvider {
- public const ID = 'event:external:provider:triggerable';
- public const TASK_TYPE_ID = TextToText::ID;
-
- #[\Override]
- public function getId(): string {
- return self::ID;
- }
- #[\Override]
- public function getName(): string {
- return 'External Triggerable Provider via Event';
- }
-
- #[\Override]
- public function getTaskTypeId(): string {
- return self::TASK_TYPE_ID;
- }
-
- #[\Override]
- public function trigger(): void {
- }
- #[\Override]
- public function getExpectedRuntime(): int {
- return 5;
- }
- #[\Override]
- public function getOptionalInputShape(): array {
- return [];
- }
- #[\Override]
- public function getOptionalOutputShape(): array {
- return [];
- }
- #[\Override]
- public function getInputShapeEnumValues(): array {
- return [];
- }
- #[\Override]
- public function getInputShapeDefaults(): array {
- return [];
- }
- #[\Override]
- public function getOptionalInputShapeEnumValues(): array {
- return [];
- }
- #[\Override]
- public function getOptionalInputShapeDefaults(): array {
- return [];
- }
- #[\Override]
- public function getOutputShapeEnumValues(): array {
- return [];
- }
- #[\Override]
- public function getOptionalOutputShapeEnumValues(): array {
- return [];
- }
-}
-
-class ConflictingExternalProvider implements IProvider {
- // Same ID as SuccessfulSyncProvider
- public const ID = 'test:sync:success';
- public const TASK_TYPE_ID = 'event:external:tasktype'; // Can be different task type
-
- #[\Override]
- public function getId(): string {
- return self::ID;
- }
- #[\Override]
- public function getName(): string {
- return 'Conflicting External Provider';
- }
- #[\Override]
- public function getTaskTypeId(): string {
- return self::TASK_TYPE_ID;
- }
- #[\Override]
- public function getExpectedRuntime(): int {
- return 50;
- }
- #[\Override]
- public function getOptionalInputShape(): array {
- return [];
- }
- #[\Override]
- public function getOptionalOutputShape(): array {
- return [];
- }
- #[\Override]
- public function getInputShapeEnumValues(): array {
- return [];
- }
- #[\Override]
- public function getInputShapeDefaults(): array {
- return [];
- }
- #[\Override]
- public function getOptionalInputShapeEnumValues(): array {
- return [];
- }
- #[\Override]
- public function getOptionalInputShapeDefaults(): array {
- return [];
- }
- #[\Override]
- public function getOutputShapeEnumValues(): array {
- return [];
- }
- #[\Override]
- public function getOptionalOutputShapeEnumValues(): array {
- return [];
- }
-}
-
-class ExternalTaskType implements ITaskType {
- public const ID = 'event:external:tasktype';
-
- #[\Override]
- public function getId(): string {
- return self::ID;
- }
- #[\Override]
- public function getName(): string {
- return 'External Task Type via Event';
- }
- #[\Override]
- public function getDescription(): string {
- return 'A task type added via event';
- }
- #[\Override]
- public function getInputShape(): array {
- return ['external_input' => new ShapeDescriptor('Ext In', '', EShapeType::Text)];
- }
- #[\Override]
- public function getOutputShape(): array {
- return ['external_output' => new ShapeDescriptor('Ext Out', '', EShapeType::Text)];
- }
-}
-
-class ConflictingExternalTaskType implements ITaskType {
- // Same ID as built-in TextToText
- public const ID = TextToText::ID;
-
- #[\Override]
- public function getId(): string {
- return self::ID;
- }
- #[\Override]
- public function getName(): string {
- return 'Conflicting External Task Type';
- }
- #[\Override]
- public function getDescription(): string {
- return 'Overrides built-in TextToText';
- }
- #[\Override]
- public function getInputShape(): array {
- return ['override_input' => new ShapeDescriptor('Override In', '', EShapeType::Number)];
- }
- #[\Override]
- public function getOutputShape(): array {
- return ['override_output' => new ShapeDescriptor('Override Out', '', EShapeType::Number)];
- }
-}
-
-#[\PHPUnit\Framework\Attributes\Group('DB')]
-class TaskProcessingTest extends \Test\TestCase {
- private Coordinator&MockObject $coordinator;
- private ContainerInterface&MockObject $serverContainer;
- private IEventDispatcher&MockObject $eventDispatcher;
- private IJobList&MockObject $jobList;
- private IUserMountCache&MockObject $userMountCache;
- private RegistrationContext&MockObject $registrationContext;
- private IRemoteHostValidator&MockObject $remoteHostValidator;
-
- /** @var list hosts the mocked IRemoteHostValidator rejects */
- private array $invalidRemoteHosts = [];
- /** Makes the mocked IRemoteHostValidator reject every host */
- private bool $rejectAllRemoteHosts = false;
-
- /** @var array */
- private array $providers;
- private IAppConfig $appConfig;
- private IConfig $config;
- private IRootFolder $rootFolder;
- private TaskMapper $taskMapper;
- private IManager $manager;
-
- public const TEST_USER = 'testuser';
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- $this->providers = [
- SuccessfulSyncProvider::class => new SuccessfulSyncProvider(),
- FailingSyncProvider::class => new FailingSyncProvider(),
- FailingSyncProviderWithUserFacingError::class => new FailingSyncProviderWithUserFacingError(),
- BrokenSyncProvider::class => new BrokenSyncProvider(),
- AsyncProvider::class => new AsyncProvider(),
- AudioToImage::class => new AudioToImage(),
- SuccessfulTextProcessingSummaryProvider::class => new SuccessfulTextProcessingSummaryProvider(),
- FailingTextProcessingSummaryProvider::class => new FailingTextProcessingSummaryProvider(),
- SuccessfulTextToImageProvider::class => new SuccessfulTextToImageProvider(),
- FailingTextToImageProvider::class => new FailingTextToImageProvider(),
- ExternalProvider::class => new ExternalProvider(),
- ExternalTriggerableProvider::class => new ExternalTriggerableProvider(),
- ConflictingExternalProvider::class => new ConflictingExternalProvider(),
- ExternalTaskType::class => new ExternalTaskType(),
- ConflictingExternalTaskType::class => new ConflictingExternalTaskType(),
- ];
-
- $userManager = Server::get(IUserManager::class);
- if (!$userManager->userExists(self::TEST_USER)) {
- $userManager->createUser(self::TEST_USER, 'test');
- }
-
- $this->serverContainer = $this->createMock(ContainerInterface::class);
- $this->serverContainer->expects($this->any())->method('get')->willReturnCallback(function ($class) {
- return $this->providers[$class];
- });
-
- $this->registrationContext = $this->createMock(RegistrationContext::class);
- $this->coordinator = $this->createMock(Coordinator::class);
- $this->coordinator->expects($this->any())->method('getRegistrationContext')->willReturn($this->registrationContext);
-
- $this->rootFolder = Server::get(IRootFolder::class);
- $this->taskMapper = Server::get(TaskMapper::class);
-
- $this->jobList = $this->createPartialMock(DummyJobList::class, ['add']);
- $this->jobList->expects($this->any())->method('add')->willReturnCallback(function (): void {
- });
-
- $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
- $this->configureEventDispatcherMock();
-
- $text2imageManager = new \OC\TextToImage\Manager(
- $this->serverContainer,
- $this->coordinator,
- Server::get(LoggerInterface::class),
- $this->jobList,
- Server::get(\OC\TextToImage\Db\TaskMapper::class),
- Server::get(IConfig::class),
- Server::get(IAppDataFactory::class),
- );
-
- $this->userMountCache = $this->createMock(IUserMountCache::class);
- $this->invalidRemoteHosts = [];
- $this->rejectAllRemoteHosts = false;
- $this->remoteHostValidator = $this->createMock(IRemoteHostValidator::class);
- $this->remoteHostValidator->expects($this->any())->method('isValid')
- ->willReturnCallback(fn (string $host): bool => !$this->rejectAllRemoteHosts && !in_array($host, $this->invalidRemoteHosts, true));
- $this->config = Server::get(IConfig::class);
- $this->appConfig = Server::get(IAppConfig::class);
- $this->manager = new Manager(
- $this->appConfig,
- $this->coordinator,
- $this->serverContainer,
- Server::get(LoggerInterface::class),
- $this->taskMapper,
- $this->jobList,
- $this->eventDispatcher,
- Server::get(IAppDataFactory::class),
- Server::get(IRootFolder::class),
- $text2imageManager,
- $this->userMountCache,
- Server::get(IClientService::class),
- Server::get(IAppManager::class),
- $userManager,
- Server::get(IUserSession::class),
- Server::get(ICacheFactory::class),
- Server::get(IFactory::class),
- Server::get(ITimeFactory::class),
- $this->remoteHostValidator,
- );
- }
-
- private function getFile(string $name, string $content): File {
- $folder = $this->rootFolder->getUserFolder(self::TEST_USER);
- $file = $folder->newFile($name, $content);
- return $file;
- }
-
- public function testShouldNotHaveAnyProviders(): void {
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([]);
- self::assertCount(0, $this->manager->getAvailableTaskTypes());
- self::assertCount(0, $this->manager->getAvailableTaskTypeIds());
- self::assertFalse($this->manager->hasProviders());
- self::expectException(PreConditionNotMetException::class);
- $this->manager->scheduleTask(new Task(TextToText::ID, ['input' => 'Hello'], 'test', null));
- }
-
- public function testProviderShouldBeRegisteredAndTaskTypeDisabled(): void {
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([
- new ServiceRegistration('test', SuccessfulSyncProvider::class)
- ]);
- $taskProcessingTypeSettings = [
- TextToText::ID => false,
- ];
- $this->appConfig->setValueString('core', 'ai.taskprocessing_type_preferences', json_encode($taskProcessingTypeSettings), lazy: true);
- self::assertCount(0, $this->manager->getAvailableTaskTypes());
- self::assertCount(1, $this->manager->getAvailableTaskTypes(true));
- self::assertCount(0, $this->manager->getAvailableTaskTypeIds());
- self::assertCount(1, $this->manager->getAvailableTaskTypeIds(true));
- self::assertTrue($this->manager->hasProviders());
- self::expectException(PreConditionNotMetException::class);
- $this->manager->scheduleTask(new Task(TextToText::ID, ['input' => 'Hello'], 'test', null));
- }
-
- public function testProviderShouldBeRegisteredAndTaskFailValidation(): void {
- $this->appConfig->setValueString('core', 'ai.taskprocessing_type_preferences', '', lazy: true);
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([
- new ServiceRegistration('test', BrokenSyncProvider::class)
- ]);
- self::assertCount(1, $this->manager->getAvailableTaskTypes());
- self::assertCount(1, $this->manager->getAvailableTaskTypeIds());
- self::assertTrue($this->manager->hasProviders());
- $task = new Task(TextToText::ID, ['wrongInputKey' => 'Hello'], 'test', null);
- self::assertNull($task->getId());
- self::expectException(ValidationException::class);
- $this->manager->scheduleTask($task);
- }
-
- public static function invalidWebhookDataProvider(): array {
- return [
- 'uri without method' => ['https://example.com/hook', null],
- 'method without uri' => [null, 'HTTP:POST'],
- 'empty uri with method' => ['', 'HTTP:POST'],
- 'uri with empty method' => ['https://example.com/hook', ''],
- 'unknown method prefix' => ['https://example.com/hook', 'FTP:GET'],
- 'unknown http verb' => ['https://example.com/hook', 'HTTP:PATCH'],
- 'lowercase http verb' => ['https://example.com/hook', 'HTTP:post'],
- 'unsupported uri scheme' => ['file:///etc/passwd', 'HTTP:GET'],
- 'relative uri for http method' => ['/some/path', 'HTTP:POST'],
- 'malformed uri' => ['https://', 'HTTP:POST'],
- 'appapi method without exapp id' => ['/some/path', 'AppAPI:POST'],
- 'appapi method with too many parts' => ['/some/path', 'AppAPI:my_app:POST:extra'],
- 'appapi method with invalid exapp id' => ['/some/path', 'AppAPI:My App:POST'],
- 'appapi method with unknown http verb' => ['/some/path', 'AppAPI:my_app:PATCH'],
- 'absolute uri for appapi method' => ['https://example.com/hook', 'AppAPI:my_app:POST'],
- 'uri too long' => ['https://example.com/' . str_repeat('a', 4000), 'HTTP:POST'],
- 'method too long' => ['/some/path', 'AppAPI:' . str_repeat('a', 64) . ':POST'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('invalidWebhookDataProvider')]
- public function testProviderShouldBeRegisteredAndWebhookFailValidation(?string $webhookUri, ?string $webhookMethod): void {
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([
- new ServiceRegistration('test', SuccessfulSyncProvider::class)
- ]);
- $task = new Task(TextToText::ID, ['input' => 'Hello'], 'test', null);
- $task->setWebhookUri($webhookUri);
- $task->setWebhookMethod($webhookMethod);
- self::expectException(ValidationException::class);
- $this->manager->scheduleTask($task);
- }
-
- public static function validWebhookDataProvider(): array {
- return [
- 'no webhook' => [null, null],
- 'empty webhook' => ['', ''],
- 'http get' => ['http://example.com/hook', 'HTTP:GET'],
- 'https post' => ['https://example.com/hook?foo=bar', 'HTTP:POST'],
- 'https put' => ['https://example.com/hook', 'HTTP:PUT'],
- 'https delete' => ['https://example.com/hook', 'HTTP:DELETE'],
- 'appapi post' => ['/some/path', 'AppAPI:my_app:POST'],
- 'appapi get' => ['/', 'AppAPI:my-app2:GET'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('validWebhookDataProvider')]
- public function testProviderShouldBeRegisteredAndWebhookPassValidation(?string $webhookUri, ?string $webhookMethod): void {
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([
- new ServiceRegistration('test', SuccessfulSyncProvider::class)
- ]);
- $task = new Task(TextToText::ID, ['input' => 'Hello'], 'test', null);
- $task->setWebhookUri($webhookUri);
- $task->setWebhookMethod($webhookMethod);
- $this->manager->scheduleTask($task);
- self::assertNotNull($task->getId());
- self::assertEquals(Task::STATUS_SCHEDULED, $task->getStatus());
- // clean up so the scheduled task does not interfere with other tests
- $this->manager->deleteTask($task);
- }
-
- public static function localWebhookHostDataProvider(): array {
- return [
- 'localhost' => ['http://localhost/hook', 'localhost'],
- 'ipv4 loopback' => ['http://127.0.0.1:8080/hook', '127.0.0.1'],
- 'ipv6 loopback' => ['http://[::1]/hook', '[::1]'],
- 'private network' => ['https://192.168.1.1/hook', '192.168.1.1'],
- 'local hostname' => ['https://server.local/hook', 'server.local'],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('localWebhookHostDataProvider')]
- public function testProviderShouldBeRegisteredAndLocalWebhookHostFailValidation(string $webhookUri, string $host): void {
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([
- new ServiceRegistration('test', SuccessfulSyncProvider::class)
- ]);
- $this->invalidRemoteHosts = [$host];
- $task = new Task(TextToText::ID, ['input' => 'Hello'], 'test', null);
- $task->setWebhookUri($webhookUri);
- $task->setWebhookMethod('HTTP:POST');
- self::expectException(ValidationException::class);
- $this->manager->scheduleTask($task);
- }
-
- public function testProviderShouldBeRegisteredAndAppApiWebhookSkipsHostValidation(): void {
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([
- new ServiceRegistration('test', SuccessfulSyncProvider::class)
- ]);
- // AppAPI webhooks use an absolute path, so no remote host is involved
- $this->rejectAllRemoteHosts = true;
- $task = new Task(TextToText::ID, ['input' => 'Hello'], 'test', null);
- $task->setWebhookUri('/some/path');
- $task->setWebhookMethod('AppAPI:my_app:POST');
- $this->manager->scheduleTask($task);
- self::assertEquals(Task::STATUS_SCHEDULED, $task->getStatus());
- // clean up so the scheduled task does not interfere with other tests
- $this->manager->deleteTask($task);
- }
-
- public function testProviderShouldBeRegisteredAndTaskWithFilesFailValidation(): void {
- $this->registrationContext->expects($this->any())->method('getTaskProcessingTaskTypes')->willReturn([
- new ServiceRegistration('test', AudioToImage::class)
- ]);
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([
- new ServiceRegistration('test', AsyncProvider::class)
- ]);
- $user = $this->createMock(IUser::class);
- $user->expects($this->any())->method('getUID')->willReturn('uid');
- $mount = $this->createMock(ICachedMountInfo::class);
- $mount->expects($this->any())->method('getUser')->willReturn($user);
- $this->userMountCache->expects($this->any())->method('getMountsForFileId')->willReturn([$mount]);
-
- self::assertCount(1, $this->manager->getAvailableTaskTypes());
- self::assertCount(1, $this->manager->getAvailableTaskTypeIds());
- self::assertTrue($this->manager->hasProviders());
-
- $audioId = $this->getFile('audioInput', 'Hello')->getId();
- $task = new Task(AudioToImage::ID, ['audio' => $audioId], 'test', null);
- self::assertNull($task->getId());
- self::assertEquals(Task::STATUS_UNKNOWN, $task->getStatus());
- self::expectException(UnauthorizedException::class);
- $this->manager->scheduleTask($task);
- }
-
- public function testProviderShouldBeRegisteredAndFail(): void {
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([
- new ServiceRegistration('test', FailingSyncProvider::class)
- ]);
- self::assertCount(1, $this->manager->getAvailableTaskTypes());
- self::assertCount(1, $this->manager->getAvailableTaskTypeIds());
- self::assertTrue($this->manager->hasProviders());
- $task = new Task(TextToText::ID, ['input' => 'Hello'], 'test', null);
- self::assertNull($task->getId());
- self::assertEquals(Task::STATUS_UNKNOWN, $task->getStatus());
- $this->manager->scheduleTask($task);
- self::assertNotNull($task->getId());
- self::assertEquals(Task::STATUS_SCHEDULED, $task->getStatus());
-
- $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new IsInstanceOf(TaskFailedEvent::class));
-
- $backgroundJob = new SynchronousBackgroundJob(
- Server::get(ITimeFactory::class),
- $this->manager,
- $this->jobList,
- Server::get(LoggerInterface::class),
- );
- $backgroundJob->start($this->jobList);
-
- $task = $this->manager->getTask($task->getId());
- self::assertEquals(Task::STATUS_FAILED, $task->getStatus());
- self::assertEquals(FailingSyncProvider::ERROR_MESSAGE, $task->getErrorMessage());
- }
-
- public function testProviderShouldBeRegisteredAndFailWithUserFacingMessage(): void {
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([
- new ServiceRegistration('test', FailingSyncProviderWithUserFacingError::class)
- ]);
- self::assertCount(1, $this->manager->getAvailableTaskTypes());
- self::assertCount(1, $this->manager->getAvailableTaskTypeIds());
- self::assertTrue($this->manager->hasProviders());
- $task = new Task(TextToText::ID, ['input' => 'Hello'], 'test', null);
- self::assertNull($task->getId());
- self::assertEquals(Task::STATUS_UNKNOWN, $task->getStatus());
- $this->manager->scheduleTask($task);
- self::assertNotNull($task->getId());
- self::assertEquals(Task::STATUS_SCHEDULED, $task->getStatus());
-
- $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new IsInstanceOf(TaskFailedEvent::class));
-
- $backgroundJob = new SynchronousBackgroundJob(
- Server::get(ITimeFactory::class),
- $this->manager,
- $this->jobList,
- Server::get(LoggerInterface::class),
- );
- $backgroundJob->start($this->jobList);
-
- $task = $this->manager->getTask($task->getId());
- self::assertEquals(Task::STATUS_FAILED, $task->getStatus());
- self::assertEquals(FailingSyncProviderWithUserFacingError::ERROR_MESSAGE, $task->getErrorMessage());
- self::assertEquals(FailingSyncProviderWithUserFacingError::USER_FACING_ERROR_MESSAGE, $task->getUserFacingErrorMessage());
- }
-
- public function testProviderShouldBeRegisteredAndFailOutputValidation(): void {
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([
- new ServiceRegistration('test', BrokenSyncProvider::class)
- ]);
- self::assertCount(1, $this->manager->getAvailableTaskTypes());
- self::assertCount(1, $this->manager->getAvailableTaskTypeIds());
- self::assertTrue($this->manager->hasProviders());
- $task = new Task(TextToText::ID, ['input' => 'Hello'], 'test', null);
- self::assertNull($task->getId());
- self::assertEquals(Task::STATUS_UNKNOWN, $task->getStatus());
- $this->manager->scheduleTask($task);
- self::assertNotNull($task->getId());
- self::assertEquals(Task::STATUS_SCHEDULED, $task->getStatus());
-
- $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new IsInstanceOf(TaskFailedEvent::class));
-
- $backgroundJob = new SynchronousBackgroundJob(
- Server::get(ITimeFactory::class),
- $this->manager,
- $this->jobList,
- Server::get(LoggerInterface::class),
- );
- $backgroundJob->start($this->jobList);
-
- $task = $this->manager->getTask($task->getId());
- self::assertEquals(Task::STATUS_FAILED, $task->getStatus());
- self::assertEquals('The task was processed successfully but the provider\'s output doesn\'t pass validation against the task type\'s outputShape spec and/or the provider\'s own optionalOutputShape spec', $task->getErrorMessage());
- }
-
- public function testProviderShouldBeRegisteredAndRun(): void {
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([
- new ServiceRegistration('test', SuccessfulSyncProvider::class)
- ]);
- self::assertCount(1, $this->manager->getAvailableTaskTypes());
- self::assertCount(1, $this->manager->getAvailableTaskTypeIds());
- $taskTypeStruct = $this->manager->getAvailableTaskTypes()[array_keys($this->manager->getAvailableTaskTypes())[0]];
- self::assertTrue(isset($taskTypeStruct['inputShape']['input']));
- self::assertEquals(EShapeType::Text, $taskTypeStruct['inputShape']['input']->getShapeType());
- self::assertTrue(isset($taskTypeStruct['optionalInputShape']['optionalKey']));
- self::assertEquals(EShapeType::Text, $taskTypeStruct['optionalInputShape']['optionalKey']->getShapeType());
- self::assertTrue(isset($taskTypeStruct['outputShape']['output']));
- self::assertEquals(EShapeType::Text, $taskTypeStruct['outputShape']['output']->getShapeType());
- self::assertTrue(isset($taskTypeStruct['optionalOutputShape']['optionalKey']));
- self::assertEquals(EShapeType::Text, $taskTypeStruct['optionalOutputShape']['optionalKey']->getShapeType());
-
- self::assertTrue($this->manager->hasProviders());
- $task = new Task(TextToText::ID, ['input' => 'Hello'], 'test', null);
- self::assertNull($task->getId());
- self::assertEquals(Task::STATUS_UNKNOWN, $task->getStatus());
- $this->manager->scheduleTask($task);
- self::assertNotNull($task->getId());
- self::assertEquals(Task::STATUS_SCHEDULED, $task->getStatus());
-
- // Task object retrieved from db is up-to-date
- $task2 = $this->manager->getTask($task->getId());
- self::assertEquals($task->getId(), $task2->getId());
- self::assertEquals(['input' => 'Hello'], $task2->getInput());
- self::assertNull($task2->getOutput());
- self::assertEquals(Task::STATUS_SCHEDULED, $task2->getStatus());
-
- $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new IsInstanceOf(TaskSuccessfulEvent::class));
-
- $backgroundJob = new SynchronousBackgroundJob(
- Server::get(ITimeFactory::class),
- $this->manager,
- $this->jobList,
- Server::get(LoggerInterface::class),
- );
- $backgroundJob->start($this->jobList);
-
- $task = $this->manager->getTask($task->getId());
- self::assertEquals(Task::STATUS_SUCCESSFUL, $task->getStatus(), 'Status is ' . $task->getStatus() . ' with error message: ' . $task->getErrorMessage());
- self::assertEquals(['output' => 'Hello'], $task->getOutput());
- self::assertEquals(1, $task->getProgress());
- }
-
- public function testTaskTypeExplicitlyEnabled(): void {
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([
- new ServiceRegistration('test', SuccessfulSyncProvider::class)
- ]);
-
- $taskProcessingTypeSettings = [
- TextToText::ID => true,
- ];
- $this->appConfig->setValueString('core', 'ai.taskprocessing_type_preferences', json_encode($taskProcessingTypeSettings), lazy: true);
-
- self::assertCount(1, $this->manager->getAvailableTaskTypes());
- self::assertCount(1, $this->manager->getAvailableTaskTypeIds());
-
- self::assertTrue($this->manager->hasProviders());
- $task = new Task(TextToText::ID, ['input' => 'Hello'], 'test', null);
- self::assertNull($task->getId());
- self::assertEquals(Task::STATUS_UNKNOWN, $task->getStatus());
- $this->manager->scheduleTask($task);
- self::assertNotNull($task->getId());
- self::assertEquals(Task::STATUS_SCHEDULED, $task->getStatus());
-
- $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new IsInstanceOf(TaskSuccessfulEvent::class));
-
- $backgroundJob = new SynchronousBackgroundJob(
- Server::get(ITimeFactory::class),
- $this->manager,
- $this->jobList,
- Server::get(LoggerInterface::class),
- );
- $backgroundJob->start($this->jobList);
-
- $task = $this->manager->getTask($task->getId());
- self::assertEquals(Task::STATUS_SUCCESSFUL, $task->getStatus(), 'Status is ' . $task->getStatus() . ' with error message: ' . $task->getErrorMessage());
- self::assertEquals(['output' => 'Hello'], $task->getOutput());
- self::assertEquals(1, $task->getProgress());
- }
-
- public function testAsyncProviderWithFilesShouldBeRegisteredAndRunReturningRawFileData(): void {
- $this->registrationContext->expects($this->any())->method('getTaskProcessingTaskTypes')->willReturn([
- new ServiceRegistration('test', AudioToImage::class)
- ]);
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([
- new ServiceRegistration('test', AsyncProvider::class)
- ]);
-
- $user = $this->createMock(IUser::class);
- $user->expects($this->any())->method('getUID')->willReturn('testuser');
- $mount = $this->createMock(ICachedMountInfo::class);
- $mount->expects($this->any())->method('getUser')->willReturn($user);
- $this->userMountCache->expects($this->any())->method('getMountsForFileId')->willReturn([$mount]);
-
- self::assertCount(1, $this->manager->getAvailableTaskTypes());
- self::assertCount(1, $this->manager->getAvailableTaskTypeIds());
-
- self::assertTrue($this->manager->hasProviders());
- $audioId = $this->getFile('audioInput', 'Hello')->getId();
- $task = new Task(AudioToImage::ID, ['audio' => $audioId], 'test', 'testuser');
- self::assertNull($task->getId());
- self::assertEquals(Task::STATUS_UNKNOWN, $task->getStatus());
- $this->manager->scheduleTask($task);
- self::assertNotNull($task->getId());
- self::assertEquals(Task::STATUS_SCHEDULED, $task->getStatus());
-
- // Task object retrieved from db is up-to-date
- $task2 = $this->manager->getTask($task->getId());
- self::assertEquals($task->getId(), $task2->getId());
- self::assertEquals(['audio' => $audioId], $task2->getInput());
- self::assertNull($task2->getOutput());
- self::assertEquals(Task::STATUS_SCHEDULED, $task2->getStatus());
-
- $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new IsInstanceOf(TaskSuccessfulEvent::class));
-
- $this->manager->setTaskProgress($task2->getId(), 0.1);
- $input = $this->manager->prepareInputData($task2);
- self::assertTrue(isset($input['audio']));
- self::assertInstanceOf(File::class, $input['audio']);
- self::assertEquals($audioId, $input['audio']->getId());
-
- $this->manager->setTaskResult($task2->getId(), null, ['spectrogram' => 'World']);
-
- $task = $this->manager->getTask($task->getId());
- self::assertEquals(Task::STATUS_SUCCESSFUL, $task->getStatus());
- self::assertEquals(1, $task->getProgress());
- self::assertTrue(isset($task->getOutput()['spectrogram']));
- $node = $this->rootFolder->getFirstNodeByIdInPath($task->getOutput()['spectrogram'], '/' . $this->rootFolder->getAppDataDirectoryName() . '/');
- self::assertNotNull($node);
- self::assertInstanceOf(File::class, $node);
- self::assertEquals('World', $node->getContent());
- }
-
- public function testAsyncProviderWithFilesShouldBeRegisteredAndRunReturningFileShapedData(): void {
- $this->registrationContext->expects($this->any())->method('getTaskProcessingTaskTypes')->willReturn([
- new ServiceRegistration('test', AudioToImage::class)
- ]);
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([
- new ServiceRegistration('test', AsyncProvider::class)
- ]);
-
- $user = $this->createMock(IUser::class);
- $user->expects($this->any())->method('getUID')->willReturn('testuser');
- $mount = $this->createMock(ICachedMountInfo::class);
- $mount->expects($this->any())->method('getUser')->willReturn($user);
- $this->userMountCache->expects($this->any())->method('getMountsForFileId')->willReturn([$mount]);
-
- self::assertCount(1, $this->manager->getAvailableTaskTypes());
- self::assertCount(1, $this->manager->getAvailableTaskTypeIds());
-
- self::assertTrue($this->manager->hasProviders());
- $audioId = $this->getFile('audioInput', 'Hello')->getId();
- $task = new Task(AudioToImage::ID, ['audio' => $audioId], 'test', 'testuser');
- self::assertNull($task->getId());
- self::assertEquals(Task::STATUS_UNKNOWN, $task->getStatus());
- $this->manager->scheduleTask($task);
- self::assertNotNull($task->getId());
- self::assertEquals(Task::STATUS_SCHEDULED, $task->getStatus());
-
- // Task object retrieved from db is up-to-date
- $task2 = $this->manager->getTask($task->getId());
- self::assertEquals($task->getId(), $task2->getId());
- self::assertEquals(['audio' => $audioId], $task2->getInput());
- self::assertNull($task2->getOutput());
- self::assertEquals(Task::STATUS_SCHEDULED, $task2->getStatus());
-
- $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new IsInstanceOf(TaskSuccessfulEvent::class));
-
- $this->manager->setTaskProgress($task2->getId(), 0.1);
- $input = $this->manager->prepareInputData($task2);
- self::assertTrue(isset($input['audio']));
- self::assertInstanceOf(File::class, $input['audio']);
- self::assertEquals($audioId, $input['audio']->getId());
-
- // Provider returns the raw file contents wrapped in a FileShaped object, including a file extension
- $this->manager->setTaskResult($task2->getId(), null, ['spectrogram' => new FileShaped(EShapeType::Image, 'World', 'png')]);
-
- $task = $this->manager->getTask($task->getId());
- self::assertEquals(Task::STATUS_SUCCESSFUL, $task->getStatus());
- self::assertEquals(1, $task->getProgress());
- self::assertTrue(isset($task->getOutput()['spectrogram']));
- $node = $this->rootFolder->getFirstNodeByIdInPath($task->getOutput()['spectrogram'], '/' . $this->rootFolder->getAppDataDirectoryName() . '/');
- self::assertNotNull($node);
- self::assertInstanceOf(File::class, $node);
- self::assertEquals('World', $node->getContent());
- // The extension carried by the FileShaped object is applied to the stored file name
- self::assertStringEndsWith('.png', $node->getName());
- }
-
- public function testAsyncProviderWithFilesShouldBeRegisteredAndRunReturningFileIds(): void {
- $this->registrationContext->expects($this->any())->method('getTaskProcessingTaskTypes')->willReturn([
- new ServiceRegistration('test', AudioToImage::class)
- ]);
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([
- new ServiceRegistration('test', AsyncProvider::class)
- ]);
- $user = $this->createMock(IUser::class);
- $user->expects($this->any())->method('getUID')->willReturn('testuser');
- $mount = $this->createMock(ICachedMountInfo::class);
- $mount->expects($this->any())->method('getUser')->willReturn($user);
- $this->userMountCache->expects($this->any())->method('getMountsForFileId')->willReturn([$mount]);
- self::assertCount(1, $this->manager->getAvailableTaskTypes());
- self::assertCount(1, $this->manager->getAvailableTaskTypeIds());
-
- self::assertTrue($this->manager->hasProviders());
- $audioId = $this->getFile('audioInput', 'Hello')->getId();
- $task = new Task(AudioToImage::ID, ['audio' => $audioId], 'test', 'testuser');
- self::assertNull($task->getId());
- self::assertEquals(Task::STATUS_UNKNOWN, $task->getStatus());
- $this->manager->scheduleTask($task);
- self::assertNotNull($task->getId());
- self::assertEquals(Task::STATUS_SCHEDULED, $task->getStatus());
-
- // Task object retrieved from db is up-to-date
- $task2 = $this->manager->getTask($task->getId());
- self::assertEquals($task->getId(), $task2->getId());
- self::assertEquals(['audio' => $audioId], $task2->getInput());
- self::assertNull($task2->getOutput());
- self::assertEquals(Task::STATUS_SCHEDULED, $task2->getStatus());
-
- $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new IsInstanceOf(TaskSuccessfulEvent::class));
-
- $this->manager->setTaskProgress($task2->getId(), 0.1);
- $input = $this->manager->prepareInputData($task2);
- self::assertTrue(isset($input['audio']));
- self::assertInstanceOf(File::class, $input['audio']);
- self::assertEquals($audioId, $input['audio']->getId());
-
- $outputFileId = $this->getFile('audioOutput', 'World')->getId();
-
- $this->manager->setTaskResult($task2->getId(), null, ['spectrogram' => $outputFileId], true);
-
- $task = $this->manager->getTask($task->getId());
- self::assertEquals(Task::STATUS_SUCCESSFUL, $task->getStatus());
- self::assertEquals(1, $task->getProgress());
- self::assertTrue(isset($task->getOutput()['spectrogram']));
- $node = $this->rootFolder->getFirstNodeById($task->getOutput()['spectrogram']);
- self::assertNotNull($node, 'fileId:' . $task->getOutput()['spectrogram']);
- self::assertInstanceOf(File::class, $node);
- self::assertEquals('World', $node->getContent());
- }
-
- public function testNonexistentTask(): void {
- $this->expectException(NotFoundException::class);
- $this->manager->getTask(2147483646);
- }
-
- /**
- * Insert a task without going through a provider, to control its timestamps and status.
- */
- private function insertTask(int $status, ?int $scheduledAt, ?int $startedAt): DbTask {
- $task = new Task(TextToText::ID, ['input' => 'Hello'], 'test', null);
- $task->setStatus($status);
- $task->setScheduledAt($scheduledAt);
- $task->setStartedAt($startedAt);
- /** @var DbTask $entity */
- $entity = $this->taskMapper->insert(DbTask::fromPublicTask($task));
- return $entity;
- }
-
- public function testCountTasks(): void {
- // Far in the future, so that tasks of other tests are outside of the window
- $now = time() + 365 * 24 * 3600;
- $window = $now - 7200;
- $totalBefore = $this->manager->countTasks();
- $entities = [];
-
- try {
- // Scheduled within the window, picked up after 1 minute
- $entities[] = $this->insertTask(Task::STATUS_SUCCESSFUL, $now - 3600, $now - 3540);
- // Scheduled within the window, picked up after 10 minutes
- $entities[] = $this->insertTask(Task::STATUS_SUCCESSFUL, $now - 3600, $now - 3000);
- // Scheduled within the window, picked up after 10 minutes, but failed
- $entities[] = $this->insertTask(Task::STATUS_FAILED, $now - 3600, $now - 3000);
- // Scheduled within the window, never picked up
- $entities[] = $this->insertTask(Task::STATUS_CANCELLED, $now - 3600, null);
- // Scheduled before the window, picked up after 10 minutes
- $entities[] = $this->insertTask(Task::STATUS_SUCCESSFUL, $now - 90000, $now - 89400);
-
- self::assertEquals($totalBefore + 5, $this->manager->countTasks());
- self::assertEquals(4, $this->manager->countTasks(scheduleAfter: $window));
- self::assertEquals(1, $this->manager->countTasks(status: Task::STATUS_FAILED, scheduleAfter: $window));
- // Tasks that were never picked up are not counted as slow
- self::assertEquals(2, $this->manager->countTasks(scheduleAfter: $window, minPickupDelay: 60 * 4));
- self::assertEquals(1, $this->manager->countTasks(status: Task::STATUS_SUCCESSFUL, scheduleAfter: $window, minPickupDelay: 60 * 4));
- self::assertEquals(0, $this->manager->countTasks(scheduleAfter: $window, minPickupDelay: 60 * 20));
- self::assertEquals(4, $this->manager->countTasks(taskTypeIds: [TextToText::ID], scheduleAfter: $window));
- self::assertEquals(0, $this->manager->countTasks(taskTypeIds: [TextToImage::ID], scheduleAfter: $window));
- } finally {
- foreach ($entities as $entity) {
- $this->taskMapper->delete($entity);
- }
- }
- }
-
- public function testOldTasksShouldBeCleanedUp(): void {
- $currentTime = new \DateTime('now');
- $timeFactory = $this->createMock(ITimeFactory::class);
- $timeFactory->expects($this->any())->method('getDateTime')->willReturnCallback(fn () => $currentTime);
- $timeFactory->expects($this->any())->method('getTime')->willReturnCallback(fn () => $currentTime->getTimestamp());
-
- $this->taskMapper = new TaskMapper(
- Server::get(IDBConnection::class),
- $timeFactory,
- );
-
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([
- new ServiceRegistration('test', SuccessfulSyncProvider::class)
- ]);
- self::assertCount(1, $this->manager->getAvailableTaskTypes());
- self::assertCount(1, $this->manager->getAvailableTaskTypeIds());
- self::assertTrue($this->manager->hasProviders());
- $task = new Task(TextToText::ID, ['input' => 'Hello'], 'test', null);
- $this->manager->scheduleTask($task);
-
- $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new IsInstanceOf(TaskSuccessfulEvent::class));
-
- $backgroundJob = new SynchronousBackgroundJob(
- Server::get(ITimeFactory::class),
- $this->manager,
- $this->jobList,
- Server::get(LoggerInterface::class),
- );
- $backgroundJob->start($this->jobList);
-
- $task = $this->manager->getTask($task->getId());
-
- $currentTime = $currentTime->add(new \DateInterval('P1Y'));
- // run background job
- $bgJob = new RemoveOldTasksBackgroundJob(
- $timeFactory,
- $this->manager,
- $this->taskMapper,
- Server::get(LoggerInterface::class),
- Server::get(IAppDataFactory::class),
- );
- $bgJob->setArgument([]);
- $bgJob->start($this->jobList);
-
- $this->expectException(NotFoundException::class);
- $this->manager->getTask($task->getId());
- }
-
- public function testShouldTransparentlyHandleTextProcessingProviders(): void {
- $this->registrationContext->expects($this->any())->method('getTextProcessingProviders')->willReturn([
- new ServiceRegistration('test', SuccessfulTextProcessingSummaryProvider::class)
- ]);
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([
- ]);
- $taskTypes = $this->manager->getAvailableTaskTypes();
- self::assertCount(1, $taskTypes);
- self::assertCount(1, $this->manager->getAvailableTaskTypeIds());
- self::assertTrue(isset($taskTypes[TextToTextSummary::ID]));
- self::assertTrue($this->manager->hasProviders());
- $task = new Task(TextToTextSummary::ID, ['input' => 'Hello'], 'test', null);
- $this->manager->scheduleTask($task);
-
- $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new IsInstanceOf(TaskSuccessfulEvent::class));
-
- $backgroundJob = new SynchronousBackgroundJob(
- Server::get(ITimeFactory::class),
- $this->manager,
- $this->jobList,
- Server::get(LoggerInterface::class),
- );
- $backgroundJob->start($this->jobList);
-
- $task = $this->manager->getTask($task->getId());
- self::assertEquals(Task::STATUS_SUCCESSFUL, $task->getStatus());
- self::assertIsArray($task->getOutput());
- self::assertTrue(isset($task->getOutput()['output']));
- self::assertEquals('Hello Summarize', $task->getOutput()['output']);
- self::assertTrue($this->providers[SuccessfulTextProcessingSummaryProvider::class]->ran);
- }
-
- public function testShouldTransparentlyHandleFailingTextProcessingProviders(): void {
- $this->registrationContext->expects($this->any())->method('getTextProcessingProviders')->willReturn([
- new ServiceRegistration('test', FailingTextProcessingSummaryProvider::class)
- ]);
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([
- ]);
- $taskTypes = $this->manager->getAvailableTaskTypes();
- self::assertCount(1, $taskTypes);
- self::assertCount(1, $this->manager->getAvailableTaskTypeIds());
- self::assertTrue(isset($taskTypes[TextToTextSummary::ID]));
- self::assertTrue($this->manager->hasProviders());
- $task = new Task(TextToTextSummary::ID, ['input' => 'Hello'], 'test', null);
- $this->manager->scheduleTask($task);
-
- $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new IsInstanceOf(TaskFailedEvent::class));
-
- $backgroundJob = new SynchronousBackgroundJob(
- Server::get(ITimeFactory::class),
- $this->manager,
- $this->jobList,
- Server::get(LoggerInterface::class),
- );
- $backgroundJob->start($this->jobList);
-
- $task = $this->manager->getTask($task->getId());
- self::assertEquals(Task::STATUS_FAILED, $task->getStatus());
- self::assertTrue($task->getOutput() === null);
- self::assertEquals('ERROR', $task->getErrorMessage());
- self::assertTrue($this->providers[FailingTextProcessingSummaryProvider::class]->ran);
- }
-
- public function testShouldTransparentlyHandleText2ImageProviders(): void {
- $this->registrationContext->expects($this->any())->method('getTextToImageProviders')->willReturn([
- new ServiceRegistration('test', SuccessfulTextToImageProvider::class)
- ]);
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([
- ]);
- $taskTypes = $this->manager->getAvailableTaskTypes();
- self::assertCount(1, $taskTypes);
- self::assertCount(1, $this->manager->getAvailableTaskTypeIds());
- self::assertTrue(isset($taskTypes[TextToImage::ID]));
- self::assertTrue($this->manager->hasProviders());
- $task = new Task(TextToImage::ID, ['input' => 'Hello', 'numberOfImages' => 3], 'test', null);
- $this->manager->scheduleTask($task);
-
- $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new IsInstanceOf(TaskSuccessfulEvent::class));
-
- $backgroundJob = new SynchronousBackgroundJob(
- Server::get(ITimeFactory::class),
- $this->manager,
- $this->jobList,
- Server::get(LoggerInterface::class),
- );
- $backgroundJob->start($this->jobList);
-
- $task = $this->manager->getTask($task->getId());
- self::assertEquals(Task::STATUS_SUCCESSFUL, $task->getStatus());
- self::assertIsArray($task->getOutput());
- self::assertTrue(isset($task->getOutput()['images']));
- self::assertIsArray($task->getOutput()['images']);
- self::assertCount(3, $task->getOutput()['images']);
- self::assertTrue($this->providers[SuccessfulTextToImageProvider::class]->ran);
- $node = $this->rootFolder->getFirstNodeByIdInPath($task->getOutput()['images'][0], '/' . $this->rootFolder->getAppDataDirectoryName() . '/');
- self::assertNotNull($node);
- self::assertInstanceOf(File::class, $node);
- self::assertEquals('test', $node->getContent());
- }
-
- public function testShouldTransparentlyHandleFailingText2ImageProviders(): void {
- $this->registrationContext->expects($this->any())->method('getTextToImageProviders')->willReturn([
- new ServiceRegistration('test', FailingTextToImageProvider::class)
- ]);
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([
- ]);
- $taskTypes = $this->manager->getAvailableTaskTypes();
- self::assertCount(1, $taskTypes);
- self::assertCount(1, $this->manager->getAvailableTaskTypeIds());
- self::assertTrue(isset($taskTypes[TextToImage::ID]));
- self::assertTrue($this->manager->hasProviders());
- $task = new Task(TextToImage::ID, ['input' => 'Hello', 'numberOfImages' => 3], 'test', null);
- $this->manager->scheduleTask($task);
-
- $this->eventDispatcher->expects($this->once())->method('dispatchTyped')->with(new IsInstanceOf(TaskFailedEvent::class));
-
- $backgroundJob = new SynchronousBackgroundJob(
- Server::get(ITimeFactory::class),
- $this->manager,
- $this->jobList,
- Server::get(LoggerInterface::class),
- );
- $backgroundJob->start($this->jobList);
-
- $task = $this->manager->getTask($task->getId());
- self::assertEquals(Task::STATUS_FAILED, $task->getStatus());
- self::assertTrue($task->getOutput() === null);
- self::assertEquals('ERROR', $task->getErrorMessage());
- self::assertTrue($this->providers[FailingTextToImageProvider::class]->ran);
- }
-
- public function testMergeProvidersLocalAndEvent() {
- // Arrange: Local provider registered, DIFFERENT external provider via event
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([
- new ServiceRegistration('test', SuccessfulSyncProvider::class)
- ]);
- $this->registrationContext->expects($this->any())->method('getTextProcessingProviders')->willReturn([]);
- $this->registrationContext->expects($this->any())->method('getTextToImageProviders')->willReturn([]);
- $this->registrationContext->expects($this->any())->method('getSpeechToTextProviders')->willReturn([]);
-
- $externalProvider = new ExternalProvider(); // ID = 'event:external:provider'
- $this->configureEventDispatcherMock(providersToAdd: [$externalProvider]);
- $this->manager = $this->createManagerInstance();
-
- // Act
- $providers = $this->manager->getProviders();
-
- // Assert: Both providers should be present
- self::assertArrayHasKey(SuccessfulSyncProvider::ID, $providers);
- self::assertInstanceOf(SuccessfulSyncProvider::class, $providers[SuccessfulSyncProvider::ID]);
- self::assertArrayHasKey(ExternalProvider::ID, $providers);
- self::assertInstanceOf(ExternalProvider::class, $providers[ExternalProvider::ID]);
- self::assertCount(2, $providers);
- }
-
- public function testGetProvidersIncludesExternalViaEvent() {
- // Arrange: No local providers, one external provider via event
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([]);
- $this->registrationContext->expects($this->any())->method('getTextProcessingProviders')->willReturn([]);
- $this->registrationContext->expects($this->any())->method('getTextToImageProviders')->willReturn([]);
- $this->registrationContext->expects($this->any())->method('getSpeechToTextProviders')->willReturn([]);
-
- $externalProvider = new ExternalProvider();
- $this->configureEventDispatcherMock(providersToAdd: [$externalProvider]);
- $this->manager = $this->createManagerInstance(); // Create manager with configured mocks
-
- // Act
- $providers = $this->manager->getProviders(); // Returns ID-indexed array
-
- // Assert
- self::assertArrayHasKey(ExternalProvider::ID, $providers);
- self::assertInstanceOf(ExternalProvider::class, $providers[ExternalProvider::ID]);
- self::assertCount(1, $providers);
- self::assertTrue($this->manager->hasProviders());
- }
-
- public function testGetAvailableTaskTypesIncludesExternalViaEvent() {
- // Arrange: No local types/providers, one external type and provider via event
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([]);
- $this->registrationContext->expects($this->any())->method('getTaskProcessingTaskTypes')->willReturn([]);
- $this->registrationContext->expects($this->any())->method('getTextProcessingProviders')->willReturn([]);
- $this->registrationContext->expects($this->any())->method('getTextToImageProviders')->willReturn([]);
- $this->registrationContext->expects($this->any())->method('getSpeechToTextProviders')->willReturn([]);
-
- $externalProvider = new ExternalProvider(); // Provides ExternalTaskType
- $externalTaskType = new ExternalTaskType();
- $this->configureEventDispatcherMock(
- providersToAdd: [$externalProvider],
- taskTypesToAdd: [$externalTaskType]
- );
- $this->manager = $this->createManagerInstance();
-
- // Act
- $availableTypes = $this->manager->getAvailableTaskTypes();
-
- // Assert
- self::assertArrayHasKey(ExternalTaskType::ID, $availableTypes);
- self::assertContains(ExternalTaskType::ID, $this->manager->getAvailableTaskTypeIds());
- self::assertEquals(ExternalTaskType::ID, $externalProvider->getTaskTypeId(), 'Test Sanity: Provider must handle the Task Type');
- self::assertEquals('External Task Type via Event', $availableTypes[ExternalTaskType::ID]['name']);
- // Check if shapes match the external type/provider
- self::assertArrayHasKey('external_input', $availableTypes[ExternalTaskType::ID]['inputShape']);
- self::assertArrayHasKey('external_output', $availableTypes[ExternalTaskType::ID]['outputShape']);
- self::assertEmpty($availableTypes[ExternalTaskType::ID]['optionalInputShape']); // From ExternalProvider
- }
-
- public function testLocalProviderWinsConflictWithEvent() {
- // Arrange: Local provider registered, conflicting external provider via event
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([
- new ServiceRegistration('test', SuccessfulSyncProvider::class)
- ]);
- $this->registrationContext->expects($this->any())->method('getTextProcessingProviders')->willReturn([]);
- $this->registrationContext->expects($this->any())->method('getTextToImageProviders')->willReturn([]);
- $this->registrationContext->expects($this->any())->method('getSpeechToTextProviders')->willReturn([]);
-
- $conflictingExternalProvider = new ConflictingExternalProvider(); // ID = 'test:sync:success'
- $this->configureEventDispatcherMock(providersToAdd: [$conflictingExternalProvider]);
- $this->manager = $this->createManagerInstance();
-
- // Act
- $providers = $this->manager->getProviders();
-
- // Assert: Only the local provider should be present for the conflicting ID
- self::assertArrayHasKey(SuccessfulSyncProvider::ID, $providers);
- self::assertInstanceOf(SuccessfulSyncProvider::class, $providers[SuccessfulSyncProvider::ID]);
- self::assertCount(1, $providers); // Ensure no extra provider was added
- }
-
- public function testTriggerableProviderWithNoOtherRunningTasks() {
- // Arrange: Local provider registered, conflicting external provider via event
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([]);
- $this->registrationContext->expects($this->any())->method('getTextProcessingProviders')->willReturn([]);
- $this->registrationContext->expects($this->any())->method('getTextToImageProviders')->willReturn([]);
- $this->registrationContext->expects($this->any())->method('getSpeechToTextProviders')->willReturn([]);
-
- $externalProvider = $this->createPartialMock(ExternalTriggerableProvider::class, ['trigger']);
- $externalProvider->expects($this->once())->method('trigger');
- $this->configureEventDispatcherMock(providersToAdd: [$externalProvider]);
- $this->manager = $this->createManagerInstance();
-
- // Act
- $task = new Task($externalProvider->getTaskTypeId(), ['input' => ''], 'tests', null);
- $this->manager->scheduleTask($task);
- }
-
- public function testTriggerableProviderWithOtherRunningTasks() {
- // Arrange: Local provider registered, conflicting external provider via event
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([]);
- $this->registrationContext->expects($this->any())->method('getTextProcessingProviders')->willReturn([]);
- $this->registrationContext->expects($this->any())->method('getTextToImageProviders')->willReturn([]);
- $this->registrationContext->expects($this->any())->method('getSpeechToTextProviders')->willReturn([]);
-
- $externalProvider = $this->createPartialMock(ExternalTriggerableProvider::class, ['trigger']);
- $externalProvider->expects($this->once())->method('trigger');
- $this->configureEventDispatcherMock(providersToAdd: [$externalProvider]);
- $this->manager = $this->createManagerInstance();
-
- $task = new Task($externalProvider->getTaskTypeId(), ['input' => ''], 'tests', null);
- $this->manager->scheduleTask($task);
- $this->manager->lockTask($task);
-
- // Act
- $task = new Task($externalProvider->getTaskTypeId(), ['input' => ''], 'tests', null);
- $this->manager->scheduleTask($task);
- }
-
- public function testMergeTaskTypesLocalAndEvent() {
- // Arrange: Local type registered, DIFFERENT external type via event
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([
- new ServiceRegistration('test', AsyncProvider::class)
- ]);
- $this->registrationContext->expects($this->any())->method('getTaskProcessingTaskTypes')->willReturn([
- new ServiceRegistration('test', AudioToImage::class)
- ]);
- $this->registrationContext->expects($this->any())->method('getTextProcessingProviders')->willReturn([]);
- $this->registrationContext->expects($this->any())->method('getTextToImageProviders')->willReturn([]);
- $this->registrationContext->expects($this->any())->method('getSpeechToTextProviders')->willReturn([]);
-
- $externalTaskType = new ExternalTaskType(); // ID = 'event:external:tasktype'
- $externalProvider = new ExternalProvider(); // Handles 'event:external:tasktype'
- $this->configureEventDispatcherMock(
- providersToAdd: [$externalProvider],
- taskTypesToAdd: [$externalTaskType]
- );
- $this->manager = $this->createManagerInstance();
-
- // Act
- $availableTypes = $this->manager->getAvailableTaskTypes();
- $availableTypeIds = $this->manager->getAvailableTaskTypeIds();
-
- // Assert: Both task types should be available
- self::assertContains(AudioToImage::ID, $availableTypeIds);
- self::assertArrayHasKey(AudioToImage::ID, $availableTypes);
- self::assertEquals(AudioToImage::class, $availableTypes[AudioToImage::ID]['name']);
-
- self::assertContains(ExternalTaskType::ID, $availableTypeIds);
- self::assertArrayHasKey(ExternalTaskType::ID, $availableTypes);
- self::assertEquals('External Task Type via Event', $availableTypes[ExternalTaskType::ID]['name']);
-
- self::assertCount(2, $availableTypes);
- }
-
- private function createManagerInstance(): Manager {
- // Clear potentially cached config values if needed
- $this->appConfig->deleteKey('core', 'ai.taskprocessing_type_preferences');
-
- // Re-create Text2ImageManager if its state matters or mocks change
- $text2imageManager = new \OC\TextToImage\Manager(
- $this->serverContainer,
- $this->coordinator,
- Server::get(LoggerInterface::class),
- $this->jobList,
- Server::get(\OC\TextToImage\Db\TaskMapper::class),
- $this->config, // Use the shared config mock
- Server::get(IAppDataFactory::class),
- );
-
- return new Manager(
- $this->appConfig,
- $this->coordinator,
- $this->serverContainer,
- Server::get(LoggerInterface::class),
- $this->taskMapper,
- $this->jobList,
- $this->eventDispatcher, // Use the potentially reconfigured mock
- Server::get(IAppDataFactory::class),
- $this->rootFolder,
- $text2imageManager,
- $this->userMountCache,
- Server::get(IClientService::class),
- Server::get(IAppManager::class),
- Server::get(IUserManager::class),
- Server::get(IUserSession::class),
- Server::get(ICacheFactory::class),
- Server::get(IFactory::class),
- Server::get(ITimeFactory::class),
- $this->remoteHostValidator,
- );
- }
-
- private function configureEventDispatcherMock(
- array $providersToAdd = [],
- array $taskTypesToAdd = [],
- ?int $expectedCalls = null,
- ): void {
- $dispatchExpectation = $expectedCalls === null ? $this->any() : $this->exactly($expectedCalls);
-
- $this->eventDispatcher->expects($dispatchExpectation)
- ->method('dispatchTyped')
- ->willReturnCallback(function (object $event) use ($providersToAdd, $taskTypesToAdd): void {
- if ($event instanceof GetTaskProcessingProvidersEvent) {
- foreach ($providersToAdd as $providerInstance) {
- $event->addProvider($providerInstance);
- }
- foreach ($taskTypesToAdd as $taskTypeInstance) {
- $event->addTaskType($taskTypeInstance);
- }
- }
- });
- }
-
- /**
- * Register a single synchronous provider for TextToText so tasks can be scheduled.
- *
- * The integration suite shares one database across tests and does not truncate
- * between them, so we clear the tasks table first to make the oldest-scheduled
- * ordering of the claim deterministic for these focused tests.
- */
- private function registerTextToTextProvider(): void {
- $db = Server::get(IDBConnection::class);
- $db->getQueryBuilder()->delete('taskprocessing_tasks')->executeStatement();
-
- $this->appConfig->setValueString('core', 'ai.taskprocessing_type_preferences', '', lazy: true);
- $this->registrationContext->expects($this->any())->method('getTaskProcessingProviders')->willReturn([
- new ServiceRegistration('test', SuccessfulSyncProvider::class)
- ]);
- self::assertTrue($this->manager->hasProviders());
- }
-
- public function testClaimReturnsNullWhenNoScheduledTask(): void {
- $this->registerTextToTextProvider();
-
- // No task scheduled => nothing to claim.
- self::assertNull($this->manager->claimNextScheduledTask([TextToText::ID]));
- self::assertNull($this->manager->claimNextScheduledTask());
- }
-
- public function testClaimReturnsTaskAndSetsItRunning(): void {
- $this->registerTextToTextProvider();
-
- $task = new Task(TextToText::ID, ['input' => 'Hello'], 'test', null);
- $this->manager->scheduleTask($task);
- self::assertEquals(Task::STATUS_SCHEDULED, $task->getStatus());
- $scheduledId = $task->getId();
-
- $claimed = $this->manager->claimNextScheduledTask([TextToText::ID]);
- self::assertNotNull($claimed);
- self::assertEquals($scheduledId, $claimed->getId());
- // The returned task object reports RUNNING ...
- self::assertEquals(Task::STATUS_RUNNING, $claimed->getStatus());
- // ... and the change is persisted in the database.
- $persisted = $this->manager->getTask($scheduledId);
- self::assertEquals(Task::STATUS_RUNNING, $persisted->getStatus());
- }
-
- public function testClaimNeverReturnsTheSameTaskTwice(): void {
- // No-duplicate invariant. We cannot run two truly concurrent DB transactions
- // inside one PHPUnit process, but the structural guarantee is the same: once a
- // task is claimed it is RUNNING (no longer SCHEDULED), so a second claim can
- // never return it again. Under real concurrency, FOR UPDATE SKIP LOCKED enforces
- // the same property by skipping rows another transaction has locked; that path
- // is additionally validated live on nc-ai.
- $this->registerTextToTextProvider();
-
- $taskA = new Task(TextToText::ID, ['input' => 'A'], 'test', null);
- $this->manager->scheduleTask($taskA);
- $taskB = new Task(TextToText::ID, ['input' => 'B'], 'test', null);
- $this->manager->scheduleTask($taskB);
-
- $firstClaim = $this->manager->claimNextScheduledTask([TextToText::ID]);
- $secondClaim = $this->manager->claimNextScheduledTask([TextToText::ID]);
-
- self::assertNotNull($firstClaim);
- self::assertNotNull($secondClaim);
- // Two distinct tasks were handed out, never the same one twice.
- self::assertNotEquals($firstClaim->getId(), $secondClaim->getId());
- self::assertEqualsCanonicalizing(
- [$taskA->getId(), $taskB->getId()],
- [$firstClaim->getId(), $secondClaim->getId()],
- );
-
- // Both are now RUNNING and the queue is drained.
- self::assertEquals(Task::STATUS_RUNNING, $this->manager->getTask($taskA->getId())->getStatus());
- self::assertEquals(Task::STATUS_RUNNING, $this->manager->getTask($taskB->getId())->getStatus());
- self::assertNull($this->manager->claimNextScheduledTask([TextToText::ID]));
- }
-
- public function testClaimNeverMarksTaskFailed(): void {
- $this->registerTextToTextProvider();
-
- $task = new Task(TextToText::ID, ['input' => 'Hello'], 'test', null);
- $this->manager->scheduleTask($task);
- $id = $task->getId();
-
- $claimed = $this->manager->claimNextScheduledTask([TextToText::ID]);
- self::assertNotNull($claimed);
-
- // Claiming only ever transitions SCHEDULED -> RUNNING, never to FAILED/CANCELLED.
- self::assertNotEquals(Task::STATUS_FAILED, $claimed->getStatus());
- self::assertNotEquals(Task::STATUS_CANCELLED, $claimed->getStatus());
- $persisted = $this->manager->getTask($id);
- self::assertNotEquals(Task::STATUS_FAILED, $persisted->getStatus());
- self::assertNotEquals(Task::STATUS_CANCELLED, $persisted->getStatus());
- self::assertEquals(Task::STATUS_RUNNING, $persisted->getStatus());
- self::assertNull($persisted->getErrorMessage());
- }
-
- public function testClaimRespectsTaskTypeFilter(): void {
- $this->registerTextToTextProvider();
-
- $task = new Task(TextToText::ID, ['input' => 'Hello'], 'test', null);
- $this->manager->scheduleTask($task);
-
- // A type that does not match the only scheduled task must not be claimed.
- self::assertNull($this->manager->claimNextScheduledTask(['some:other:tasktype']));
- // The task is still SCHEDULED and claimable without a filter.
- self::assertEquals(Task::STATUS_SCHEDULED, $this->manager->getTask($task->getId())->getStatus());
- $claimed = $this->manager->claimNextScheduledTask();
- self::assertNotNull($claimed);
- self::assertEquals($task->getId(), $claimed->getId());
- }
-
- public function testClaimRecordsStartedAt(): void {
- $this->registerTextToTextProvider();
-
- $task = new Task(TextToText::ID, ['input' => 'Hello'], 'test', null);
- $this->manager->scheduleTask($task);
- // A scheduled task has not started yet.
- self::assertNull($this->manager->getTask($task->getId())->getStartedAt());
-
- $before = time();
- $claimed = $this->manager->claimNextScheduledTask([TextToText::ID]);
- $after = time();
-
- self::assertNotNull($claimed);
- // started_at is recorded at claim time on the returned task ...
- self::assertNotNull($claimed->getStartedAt());
- self::assertGreaterThanOrEqual($before, $claimed->getStartedAt());
- self::assertLessThanOrEqual($after, $claimed->getStartedAt());
- // ... and persisted in the database (since the worker receives the task already
- // RUNNING, the later setTaskStatus SCHEDULED -> RUNNING edge is skipped and would
- // otherwise never write started_at).
- $persisted = $this->manager->getTask($task->getId());
- self::assertNotNull($persisted->getStartedAt());
- self::assertGreaterThanOrEqual($before, $persisted->getStartedAt());
- self::assertLessThanOrEqual($after, $persisted->getStartedAt());
- }
-
- public function testLockTaskDoesNotResurrectFinishedTask(): void {
- // Regression guard for the lockTask claim path (used by the SQLite fallback and the
- // external-provider API claim). lockTask must only ever transition SCHEDULED -> RUNNING.
- // If another worker finished a task (SUCCESSFUL/FAILED) between the SELECT and this
- // UPDATE, lockTask must NOT flip it back to RUNNING -- otherwise a completed task is
- // resurrected and processed twice. (The previous `status != RUNNING` guard let a
- // SUCCESSFUL/FAILED row be re-locked.)
- $this->registerTextToTextProvider();
-
- $task = new Task(TextToText::ID, ['input' => 'Hello'], 'test', null);
- $this->manager->scheduleTask($task);
- $id = $task->getId();
-
- // Simulate another worker having already finished the task.
- $entity = $this->taskMapper->find($id);
- $entity->setStatus(Task::STATUS_SUCCESSFUL);
- $this->taskMapper->update($entity);
-
- // Attempting to claim the (now SUCCESSFUL) task must be a no-op.
- $affected = $this->taskMapper->lockTask($this->taskMapper->find($id));
-
- self::assertSame(0, $affected, 'lockTask must not claim a task that is no longer SCHEDULED');
- self::assertEquals(Task::STATUS_SUCCESSFUL, $this->manager->getTask($id)->getStatus());
- }
-}
--- a/tests/lib/Teams/TeamFolderTest.php
+++ /dev/null
@@ -1,31 +0,0 @@
-assertSame(42, $folder->getId());
- $this->assertSame('Engineering', $folder->getMountPoint());
- $this->assertNull($folder->getQuota());
- $this->assertSame(['id' => 42, 'quota' => null, 'mountPoint' => 'Engineering'], $folder->jsonSerialize());
- }
-
- public function testSerializesFolderQuota(): void {
- $folder = new TeamFolder(42, 'Engineering', 1024);
-
- $this->assertSame(1024, $folder->getQuota());
- $this->assertSame(['id' => 42, 'quota' => 1024, 'mountPoint' => 'Engineering'], $folder->jsonSerialize());
- }
-}
--- a/tests/lib/Teams/TeamManagerTest.php
+++ /dev/null
@@ -1,70 +0,0 @@
-createTeamManager();
-
- $this->assertNull($teamManager->getTeamFolderProvider());
- }
-
- public function testGetTeamFolderProviderReturnsNullWithoutFolderProvider(): void {
- $teamManager = $this->createTeamManager(true);
- $this->setProviders($teamManager, [
- 'other' => $this->createMock(ITeamResourceProvider::class),
- ]);
-
- $this->assertNull($teamManager->getTeamFolderProvider());
- }
-
- public function testGetTeamFolderProviderReturnsRegisteredFolderProvider(): void {
- $teamManager = $this->createTeamManager(true);
- $folderProvider = $this->createMock(ITeamFolderProvider::class);
- $this->setProviders($teamManager, [
- 'other' => $this->createMock(ITeamResourceProvider::class),
- 'folder' => $folderProvider,
- ]);
-
- $this->assertSame($folderProvider, $teamManager->getTeamFolderProvider());
- }
-
- private function createTeamManager(bool $hasTeamSupport = false): TeamManager {
- return new class($this->createMock(Coordinator::class), $this->createMock(IURLGenerator::class), null, $hasTeamSupport, ) extends TeamManager {
- public function __construct(
- Coordinator $bootContext,
- IURLGenerator $urlGenerator,
- null $circlesManager,
- private bool $hasTeamSupport,
- ) {
- parent::__construct($bootContext, $urlGenerator, $circlesManager);
- }
-
- #[\Override]
- public function hasTeamSupport(): bool {
- return $this->hasTeamSupport;
- }
- };
- }
-
- /**
- * @param array $providers
- */
- private function setProviders(TeamManager $teamManager, array $providers): void {
- (new \ReflectionProperty(TeamManager::class, 'providers'))->setValue($teamManager, $providers);
- }
-}
--- a/tests/lib/TempManagerTest.php
+++ /dev/null
@@ -1,189 +0,0 @@
-baseDir = $this->getManager()->getTempBaseDir() . $this->getUniqueID('/oc_tmp_test');
- if (!is_dir($this->baseDir)) {
- mkdir($this->baseDir);
- }
- }
-
- #[\Override]
- protected function tearDown(): void {
- if ($this->baseDir !== null) {
- Files::rmdirr($this->baseDir);
- }
- $this->baseDir = null;
- parent::tearDown();
- }
-
- protected function getManager(?LoggerInterface $logger = null, ?IConfig $config = null): TempManager {
- if (!$logger) {
- $logger = $this->createMock(LoggerInterface::class);
- }
- if (!$config) {
- $config = $this->createMock(IConfig::class);
- $config->method('getSystemValue')
- ->with('tempdirectory', null)
- ->willReturn('/dev/shm');
- }
- $iniGetWrapper = $this->createMock(IniGetWrapper::class);
- $manager = new TempManager($logger, $config, $iniGetWrapper);
- if ($this->baseDir) {
- $manager->overrideTempBaseDir($this->baseDir);
- }
- return $manager;
- }
-
- public function testGetFile(): void {
- $manager = $this->getManager();
- $file = $manager->getTemporaryFile('txt');
- $this->assertStringEndsWith('.txt', $file);
- $this->assertTrue(is_file($file));
- $this->assertTrue(is_writable($file));
-
- file_put_contents($file, 'bar');
- $this->assertEquals('bar', file_get_contents($file));
- }
-
- public function testGetFolder(): void {
- $manager = $this->getManager();
- $folder = $manager->getTemporaryFolder();
- $this->assertStringEndsWith('/', $folder);
- $this->assertTrue(is_dir($folder));
- $this->assertTrue(is_writable($folder));
-
- file_put_contents($folder . 'foo.txt', 'bar');
- $this->assertEquals('bar', file_get_contents($folder . 'foo.txt'));
- }
-
- public function testCleanFiles(): void {
- $manager = $this->getManager();
- $file1 = $manager->getTemporaryFile('txt');
- $file2 = $manager->getTemporaryFile('txt');
- $this->assertTrue(file_exists($file1));
- $this->assertTrue(file_exists($file2));
-
- $manager->clean();
-
- $this->assertFalse(file_exists($file1));
- $this->assertFalse(file_exists($file2));
- }
-
- public function testCleanFolder(): void {
- $manager = $this->getManager();
- $folder1 = $manager->getTemporaryFolder();
- $folder2 = $manager->getTemporaryFolder();
- touch($folder1 . 'foo.txt');
- touch($folder1 . 'bar.txt');
- $this->assertTrue(file_exists($folder1));
- $this->assertTrue(file_exists($folder2));
- $this->assertTrue(file_exists($folder1 . 'foo.txt'));
- $this->assertTrue(file_exists($folder1 . 'bar.txt'));
-
- $manager->clean();
-
- $this->assertFalse(file_exists($folder1));
- $this->assertFalse(file_exists($folder2));
- $this->assertFalse(file_exists($folder1 . 'foo.txt'));
- $this->assertFalse(file_exists($folder1 . 'bar.txt'));
- }
-
- public function testCleanOld(): void {
- $manager = $this->getManager();
- $oldFile = $manager->getTemporaryFile('txt');
- $newFile = $manager->getTemporaryFile('txt');
- $folder = $manager->getTemporaryFolder();
- $nonOcFile = $this->baseDir . '/foo.txt';
- file_put_contents($nonOcFile, 'bar');
-
- $past = time() - 2 * 3600;
- touch($oldFile, $past);
- touch($folder, $past);
- touch($nonOcFile, $past);
-
- $manager2 = $this->getManager();
- $manager2->cleanOld();
- $this->assertFalse(file_exists($oldFile));
- $this->assertFalse(file_exists($folder));
- $this->assertTrue(file_exists($nonOcFile));
- $this->assertTrue(file_exists($newFile));
- }
-
- public function testLogCantCreateFile(): void {
- $logger = $this->createMock(LoggerInterface::class);
- $manager = $this->getManager($logger);
- chmod($this->baseDir, 0500);
- $logger->expects($this->once())
- ->method('warning')
- ->with($this->stringContains('Can not create a temporary file in directory'));
- $this->assertFalse($manager->getTemporaryFile('txt'));
- }
-
- public function testLogCantCreateFolder(): void {
- $logger = $this->createMock(LoggerInterface::class);
- $manager = $this->getManager($logger);
- chmod($this->baseDir, 0500);
- $logger->expects($this->once())
- ->method('warning')
- ->with($this->stringContains('Can not create a temporary folder in directory'));
- $this->assertFalse($manager->getTemporaryFolder());
- }
-
- public function testGenerateTemporaryPathWithPostfix(): void {
- $logger = $this->createMock(LoggerInterface::class);
- $tmpManager = self::invokePrivate(
- $this->getManager($logger),
- 'generateTemporaryPath',
- ['postfix']
- );
-
- $this->assertStringEndsWith('.postfix', $tmpManager);
- }
-
- public function testGenerateTemporaryPathTraversal(): void {
- $logger = $this->createMock(LoggerInterface::class);
- $tmpManager = self::invokePrivate(
- $this->getManager($logger),
- 'generateTemporaryPath',
- ['../Traversal\\../FileName']
- );
-
- $this->assertStringEndsNotWith('./Traversal\\../FileName', $tmpManager);
- $this->assertStringEndsWith('.Traversal..FileName', $tmpManager);
- }
-
- public function testGetTempBaseDirFromConfig(): void {
- $dir = $this->getManager()->getTemporaryFolder();
-
- $config = $this->createMock(IConfig::class);
- $config->expects($this->once())
- ->method('getSystemValue')
- ->with('tempdirectory', null)
- ->willReturn($dir);
-
- $this->baseDir = null; // prevent override
- $tmpManager = $this->getManager(null, $config);
-
- $this->assertEquals($dir, $tmpManager->getTempBaseDir());
- }
-}
--- a/tests/lib/Template/CSSResourceLocatorTest.php
+++ /dev/null
@@ -1,123 +0,0 @@
-logger = $this->createMock(LoggerInterface::class);
- $this->appData = $this->createMock(AppData::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->config = $this->createMock(IConfig::class);
- $this->cacheFactory = $this->createMock(ICacheFactory::class);
- $this->themingDefaults = $this->createMock(ThemingDefaults::class);
- $this->timeFactory = $this->createMock(ITimeFactory::class);
- $this->appConfig = $this->createMock(AppConfig::class);
- }
-
- private function cssResourceLocator() {
- /** @var Factory|\PHPUnit\Framework\MockObject\MockObject $factory */
- $factory = $this->createMock(Factory::class);
- $factory->method('get')->with('css')->willReturn($this->appData);
- return new CSSResourceLocator(
- $this->logger,
- $this->createMock(IConfig::class),
- Server::get(IAppManager::class),
- 'theme',
- ['core' => 'map'],
- ['3rd' => 'party'],
- );
- }
-
- private function rrmdir($directory) {
- $files = array_diff(scandir($directory), ['.','..']);
- foreach ($files as $file) {
- if (is_dir($directory . '/' . $file)) {
- $this->rrmdir($directory . '/' . $file);
- } else {
- unlink($directory . '/' . $file);
- }
- }
- return rmdir($directory);
- }
-
- private function randomString(): string {
- return sha1(random_bytes(10));
- }
-
- public function testFindWithAppPathSymlink(): void {
- // First create new apps path, and a symlink to it
- $apps_dirname = $this->randomString();
- $new_apps_path = sys_get_temp_dir() . '/' . $apps_dirname;
- $new_apps_path_symlink = $new_apps_path . '_link';
- mkdir($new_apps_path);
- symlink($apps_dirname, $new_apps_path_symlink);
-
- // Create an app within that path
- mkdir($new_apps_path . '/' . 'test_css_app');
-
- // Use the symlink as the app path
- \OC::$APPSROOTS[] = [
- 'path' => $new_apps_path_symlink,
- 'url' => '/css-apps-test',
- 'writable' => false,
- ];
-
- $locator = $this->cssResourceLocator();
- $locator->find(['test_css_app/test-file']);
-
- $resources = $locator->getResources();
- $this->assertCount(1, $resources);
- $resource = $resources[0];
- $this->assertCount(3, $resource);
- $root = $resource[0];
- $webRoot = $resource[1];
- $file = $resource[2];
-
- $expectedRoot = $new_apps_path . '/test_css_app';
- $expectedWebRoot = \OC::$WEBROOT . '/css-apps-test/test_css_app';
- $expectedFile = 'test-file.css';
-
- $this->assertEquals($expectedRoot, $root,
- 'Ensure the app path symlink is resolved into the real path');
- $this->assertEquals($expectedWebRoot, $webRoot);
- $this->assertEquals($expectedFile, $file);
-
- array_pop(\OC::$APPSROOTS);
- unlink($new_apps_path_symlink);
- $this->rrmdir($new_apps_path);
- }
-}
--- a/tests/lib/Template/JSCombinerTest.php
+++ /dev/null
@@ -1,535 +0,0 @@
-appData = $this->createMock(IAppData::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->config = $this->createMock(IConfig::class);
- $this->cacheFactory = $this->createMock(ICacheFactory::class);
- $this->depsCache = $this->createMock(ICache::class);
- $this->cacheFactory->expects($this->atLeastOnce())
- ->method('createDistributed')
- ->willReturn($this->depsCache);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->jsCombiner = new JSCombiner(
- $this->appData,
- $this->urlGenerator,
- $this->cacheFactory,
- $this->config,
- $this->logger
- );
- }
-
- public function testProcessDebugMode(): void {
- $this->config
- ->expects($this->once())
- ->method('getSystemValueBool')
- ->with('debug')
- ->willReturn(true);
-
- $actual = $this->jsCombiner->process(__DIR__, '/data/combine.json', 'awesomeapp');
- $this->assertFalse($actual);
- }
-
- public function testProcessNotInstalled(): void {
- $this->config
- ->expects($this->exactly(2))
- ->method('getSystemValueBool')
- ->willReturnMap([
- ['debug', false],
- ['installed', false]
- ]);
-
- $actual = $this->jsCombiner->process(__DIR__, '/data/combine.json', 'awesomeapp');
- $this->assertFalse($actual);
- }
-
- public function testProcessUncachedFileNoAppDataFolder(): void {
- $this->config
- ->expects($this->exactly(2))
- ->method('getSystemValueBool')
- ->willReturnMap([
- ['debug', false],
- ['installed', true],
- ]);
- $folder = $this->createMock(ISimpleFolder::class);
- $this->appData->expects($this->once())->method('getFolder')->with('awesomeapp')->willThrowException(new NotFoundException());
- $this->appData->expects($this->once())->method('newFolder')->with('awesomeapp')->willReturn($folder);
- $file = $this->createMock(ISimpleFile::class);
- $gzfile = $this->createMock(ISimpleFile::class);
-
- $fileDeps = $this->createMock(ISimpleFile::class);
-
- $folder->method('getFile')
- ->willReturnCallback(function ($path) use ($file, $gzfile) {
- if ($path === 'combine.js') {
- return $file;
- } elseif ($path === 'combine.js.deps') {
- throw new NotFoundException();
- } elseif ($path === 'combine.js.gzip') {
- return $gzfile;
- }
- $this->fail();
- });
- $folder->expects($this->once())
- ->method('newFile')
- ->with('combine.js.deps')
- ->willReturn($fileDeps);
-
- $actual = $this->jsCombiner->process(__DIR__, '/data/combine.json', 'awesomeapp');
- $this->assertTrue($actual);
- }
-
- public function testProcessUncachedFile(): void {
- $this->config
- ->expects($this->exactly(2))
- ->method('getSystemValueBool')
- ->willReturnMap([
- ['debug', false],
- ['installed', true],
- ]);
- $folder = $this->createMock(ISimpleFolder::class);
- $this->appData->expects($this->once())->method('getFolder')->with('awesomeapp')->willReturn($folder);
- $file = $this->createMock(ISimpleFile::class);
- $fileDeps = $this->createMock(ISimpleFile::class);
- $gzfile = $this->createMock(ISimpleFile::class);
-
- $folder->method('getFile')
- ->willReturnCallback(function ($path) use ($file, $gzfile) {
- if ($path === 'combine.js') {
- return $file;
- } elseif ($path === 'combine.js.deps') {
- throw new NotFoundException();
- } elseif ($path === 'combine.js.gzip') {
- return $gzfile;
- }
- $this->fail();
- });
- $folder->expects($this->once())
- ->method('newFile')
- ->with('combine.js.deps')
- ->willReturn($fileDeps);
-
- $actual = $this->jsCombiner->process(__DIR__, '/data/combine.json', 'awesomeapp');
- $this->assertTrue($actual);
- }
-
- public function testProcessCachedFile(): void {
- $this->config
- ->expects($this->exactly(2))
- ->method('getSystemValueBool')
- ->willReturnMap([
- ['debug', false],
- ['installed', true],
- ]);
- $folder = $this->createMock(ISimpleFolder::class);
- $this->appData->expects($this->once())->method('getFolder')->with('awesomeapp')->willReturn($folder);
- $file = $this->createMock(ISimpleFile::class);
-
- $fileDeps = $this->createMock(ISimpleFile::class);
-
- $fileDeps->expects($this->once())->method('getContent')->willReturn('{}');
-
- $folder->method('fileExists')
- ->with('combine.js')
- ->willReturn(true);
-
- $folder->method('getFile')
- ->willReturnCallback(function ($path) use ($file, $fileDeps) {
- if ($path === 'combine.js') {
- return $file;
- }
-
- if ($path === 'combine.js.deps') {
- return $fileDeps;
- }
-
- $this->fail();
- });
-
- $actual = $this->jsCombiner->process(__DIR__, '/data/combine.json', 'awesomeapp');
- $this->assertTrue($actual);
- }
-
- public function testProcessCachedFileMemcache(): void {
- $this->config
- ->expects($this->exactly(2))
- ->method('getSystemValueBool')
- ->willReturnMap([
- ['debug', false],
- ['installed', true],
- ]);
- $folder = $this->createMock(ISimpleFolder::class);
- $this->appData->expects($this->once())
- ->method('getFolder')
- ->with('awesomeapp')
- ->willReturn($folder);
- $folder->method('getName')
- ->willReturn('awesomeapp');
- $folder->method('fileExists')
- ->with('combine.js')
- ->willReturn(true);
-
- $file = $this->createMock(ISimpleFile::class);
-
- $this->depsCache->method('get')
- ->with('awesomeapp-combine.js.deps')
- ->willReturn('{}');
-
- $folder->method('getFile')
- ->willReturnCallback(function ($path) use ($file) {
- if ($path === 'combine.js') {
- return $file;
- }
- $this->fail();
- });
-
- $actual = $this->jsCombiner->process(__DIR__, '/data/combine.json', 'awesomeapp');
- $this->assertTrue($actual);
- }
-
- public function testIsCachedNoDepsFile(): void {
- $fileName = 'combine.json';
- $folder = $this->createMock(ISimpleFolder::class);
- $file = $this->createMock(ISimpleFile::class);
-
- $folder->method('getFile')
- ->willReturnCallback(function ($path) use ($file) {
- if ($path === 'combine.js') {
- return $file;
- }
- if ($path === 'combine.js.deps') {
- throw new NotFoundException();
- }
- $this->fail();
- });
-
- $actual = self::invokePrivate($this->jsCombiner, 'isCached', [$fileName, $folder]);
- $this->assertFalse($actual);
- }
-
- public function testIsCachedWithNotExistingFile(): void {
- $fileName = 'combine.json';
- $folder = $this->createMock(ISimpleFolder::class);
- $folder->method('fileExists')
- ->with('combine.js')
- ->willReturn(true);
- $file = $this->createMock(ISimpleFile::class);
- $folder->method('getFile')
- ->with('combine.js.deps')
- ->willReturn($file);
- $file->expects($this->once())
- ->method('getContent')
- ->willReturn(json_encode(['/etc/certainlynotexisting/file/ihope' => 10000]));
-
- $actual = self::invokePrivate($this->jsCombiner, 'isCached', [$fileName, $folder]);
- $this->assertFalse($actual);
- }
-
- public function testIsCachedWithOlderMtime(): void {
- $fileName = 'combine.json';
- $folder = $this->createMock(ISimpleFolder::class);
- $folder->method('fileExists')
- ->with('combine.js')
- ->willReturn(true);
- $file = $this->createMock(ISimpleFile::class);
- $folder->method('getFile')
- ->with('combine.js.deps')
- ->willReturn($file);
- $file->expects($this->once())
- ->method('getContent')
- ->willReturn(json_encode([__FILE__ => 1234]));
-
- $actual = self::invokePrivate($this->jsCombiner, 'isCached', [$fileName, $folder]);
- $this->assertFalse($actual);
- }
-
- public function testIsCachedWithoutContent(): void {
- $fileName = 'combine.json';
- $folder = $this->createMock(ISimpleFolder::class);
- $folder->method('fileExists')
- ->with('combine.js')
- ->willReturn(true);
- $file = $this->createMock(ISimpleFile::class);
- $folder->method('getFile')
- ->with('combine.js.deps')
- ->willReturn($file);
- $file->expects($this->once())
- ->method('getContent')
- ->willReturn('');
- $this->logger->expects($this->once())
- ->method('info')
- ->with('JSCombiner: deps file empty: combine.js.deps');
- $actual = self::invokePrivate($this->jsCombiner, 'isCached', [$fileName, $folder]);
- $this->assertFalse($actual);
- }
-
- public function testCacheNoFile(): void {
- $fileName = 'combine.js';
-
- $folder = $this->createMock(ISimpleFolder::class);
- $file = $this->createMock(ISimpleFile::class);
- $depsFile = $this->createMock(ISimpleFile::class);
- $gzFile = $this->createMock(ISimpleFile::class);
-
- $path = __DIR__ . '/data/';
-
- $folder->method('getFile')->willThrowException(new NotFoundException());
-
- $folder->method('newFile')->willReturnCallback(
- function ($filename) use ($file, $depsFile, $gzFile) {
- if ($filename === 'combine.js') {
- return $file;
- } elseif ($filename === 'combine.js.deps') {
- return $depsFile;
- } elseif ($filename === 'combine.js.gzip') {
- return $gzFile;
- }
- $this->fail();
- }
- );
-
- $file->expects($this->once())->method('putContent');
- $depsFile->expects($this->once())->method('putContent');
- $gzFile->expects($this->once())->method('putContent');
-
- $actual = self::invokePrivate($this->jsCombiner, 'cache', [$path, 'combine.json', $folder]);
- $this->assertTrue($actual);
- }
-
- public function testCache(): void {
- $fileName = 'combine.js';
-
- $folder = $this->createMock(ISimpleFolder::class);
- $file = $this->createMock(ISimpleFile::class);
- $depsFile = $this->createMock(ISimpleFile::class);
- $gzFile = $this->createMock(ISimpleFile::class);
-
- $path = __DIR__ . '/data/';
-
- $folder->method('getFile')->willReturnCallback(
- function ($filename) use ($file, $depsFile, $gzFile) {
- if ($filename === 'combine.js') {
- return $file;
- } elseif ($filename === 'combine.js.deps') {
- return $depsFile;
- } elseif ($filename === 'combine.js.gzip') {
- return $gzFile;
- }
- $this->fail();
- }
- );
-
- $file->expects($this->once())->method('putContent');
- $depsFile->expects($this->once())->method('putContent');
- $gzFile->expects($this->once())->method('putContent');
-
- $actual = self::invokePrivate($this->jsCombiner, 'cache', [$path, 'combine.json', $folder]);
- $this->assertTrue($actual);
- }
-
- public function testCacheNotPermittedException(): void {
- $fileName = 'combine.js';
-
- $folder = $this->createMock(ISimpleFolder::class);
- $file = $this->createMock(ISimpleFile::class);
- $depsFile = $this->createMock(ISimpleFile::class);
- $gzFile = $this->createMock(ISimpleFile::class);
-
- $path = __DIR__ . '/data/';
-
- $folder->expects($this->exactly(3))
- ->method('getFile')
- ->willReturnMap([
- [$fileName, $file],
- [$fileName . '.deps', $depsFile],
- [$fileName . '.gzip', $gzFile]
- ]);
-
- $file->expects($this->once())
- ->method('putContent')
- ->with('var a = \'hello\';
-
-
-var b = \'world\';
-
-
-');
- $depsFile
- ->expects($this->once())
- ->method('putContent')
- ->with($this->callback(
- function ($content) {
- $deps = json_decode($content, true);
- return array_key_exists(__DIR__ . '/data//1.js', $deps)
- && array_key_exists(__DIR__ . '/data//2.js', $deps);
- }))
- ->willThrowException(new NotPermittedException());
-
- $actual = self::invokePrivate($this->jsCombiner, 'cache', [$path, 'combine.json', $folder]);
- $this->assertFalse($actual);
- }
-
- public function testCacheSuccess(): void {
- $fileName = 'combine.js';
-
- $folder = $this->createMock(ISimpleFolder::class);
- $file = $this->createMock(ISimpleFile::class);
- $depsFile = $this->createMock(ISimpleFile::class);
- $gzFile = $this->createMock(ISimpleFile::class);
-
- $path = __DIR__ . '/data/';
-
- $folder->method('getFile')->willReturnCallback(
- function ($filename) use ($file, $depsFile, $gzFile) {
- if ($filename === 'combine.js') {
- return $file;
- } elseif ($filename === 'combine.js.deps') {
- return $depsFile;
- } elseif ($filename === 'combine.js.gzip') {
- return $gzFile;
- }
- $this->fail();
- }
- );
-
- $file->expects($this->once())
- ->method('putContent')
- ->with('var a = \'hello\';
-
-
-var b = \'world\';
-
-
-');
- $depsFile->expects($this->once())->method('putContent')->with($this->callback(
- function ($content) {
- $deps = json_decode($content, true);
- return array_key_exists(__DIR__ . '/data//1.js', $deps)
- && array_key_exists(__DIR__ . '/data//2.js', $deps);
- }));
- $gzFile->expects($this->once())->method('putContent')->with($this->callback(
- function ($content) {
- return gzdecode($content) === 'var a = \'hello\';
-
-
-var b = \'world\';
-
-
-';
- }
- ));
-
- $actual = self::invokePrivate($this->jsCombiner, 'cache', [$path, 'combine.json', $folder]);
- $this->assertTrue($actual);
- }
-
- public static function dataGetCachedSCSS(): array {
- return [
- ['awesomeapp', 'core/js/foo.json', '/js/core/foo.js'],
- ['files', 'apps/files/js/foo.json', '/js/files/foo.js']
- ];
- }
-
- /**
- * @param $appName
- * @param $fileName
- * @param $result
- */
- #[\PHPUnit\Framework\Attributes\DataProvider('dataGetCachedSCSS')]
- public function testGetCachedSCSS($appName, $fileName, $result): void {
- $this->urlGenerator->expects($this->once())
- ->method('linkToRoute')
- ->with('core.Js.getJs', [
- 'fileName' => 'foo.js',
- 'appName' => $appName
- ])
- ->willReturn(\OC::$WEBROOT . $result);
-
- $actual = $this->jsCombiner->getCachedJS($appName, $fileName);
- $this->assertEquals(substr($result, 1), $actual);
- }
-
- public function testGetContent(): void {
- // Create temporary file with some content
- $tmpFile = Server::get(ITempManager::class)->getTemporaryFile('JSCombinerTest');
- $pathInfo = pathinfo($tmpFile);
- file_put_contents($tmpFile, json_encode(['/foo/bar/test', $pathInfo['dirname'] . '/js/mytest.js']));
- $tmpFilePathArray = explode('/', $pathInfo['basename']);
- array_pop($tmpFilePathArray);
-
- $expected = [
- '//foo/bar/test',
- '/' . implode('/', $tmpFilePathArray) . $pathInfo['dirname'] . '/js/mytest.js',
- ];
- $this->assertEquals($expected, $this->jsCombiner->getContent($pathInfo['dirname'], $pathInfo['basename']));
- }
-
- public function testGetContentInvalidJson(): void {
- // Create temporary file with some content
- $tmpFile = Server::get(ITempManager::class)->getTemporaryFile('JSCombinerTest');
- $pathInfo = pathinfo($tmpFile);
- file_put_contents($tmpFile, 'CertainlyNotJson');
- $expected = [];
- $this->assertEquals($expected, $this->jsCombiner->getContent($pathInfo['dirname'], $pathInfo['basename']));
- }
-
- public function testResetCache(): void {
- $file = $this->createMock(ISimpleFile::class);
- $file->expects($this->once())
- ->method('delete');
-
- $folder = $this->createMock(ISimpleFolder::class);
- $folder->expects($this->once())
- ->method('getDirectoryListing')
- ->willReturn([$file]);
-
- $cache = $this->createMock(ICache::class);
- $this->cacheFactory->expects($this->once())
- ->method('createDistributed')
- ->willReturn($cache);
- $cache->expects($this->never())
- ->method('clear');
- $this->appData->expects($this->once())
- ->method('getDirectoryListing')
- ->willReturn([$folder]);
-
- $this->jsCombiner->resetCache();
- }
-}
--- a/tests/lib/Template/JSResourceLocatorTest.php
+++ /dev/null
@@ -1,182 +0,0 @@
-appData = $this->createMock(IAppData::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->config = $this->createMock(IConfig::class);
- $this->cacheFactory = $this->createMock(ICacheFactory::class);
- $this->logger = $this->createMock(LoggerInterface::class);
- $this->appManager = $this->createMock(IAppManager::class);
- }
-
- private function jsResourceLocator(): JSResourceLocator {
- $jsCombiner = new JSCombiner(
- $this->appData,
- $this->urlGenerator,
- $this->cacheFactory,
- $this->config,
- $this->logger
- );
- return new JSResourceLocator(
- $this->logger,
- $this->config,
- $jsCombiner,
- $this->appManager,
- );
- }
-
- private function rrmdir($directory) {
- $files = array_diff(scandir($directory), ['.','..']);
- foreach ($files as $file) {
- if (is_dir($directory . '/' . $file)) {
- $this->rrmdir($directory . '/' . $file);
- } else {
- unlink($directory . '/' . $file);
- }
- }
- return rmdir($directory);
- }
-
- private function randomString(): string {
- return sha1(random_bytes(10));
- }
-
- public function testFindWithAppPathSymlink(): void {
- $appName = 'test-js-app';
-
- // First create new apps path, and a symlink to it
- $apps_dirname = $this->randomString();
- $new_apps_path = sys_get_temp_dir() . '/' . $apps_dirname;
- $new_apps_path_symlink = $new_apps_path . '_link';
- $this->assertTrue((
- mkdir($new_apps_path) && symlink($apps_dirname, $new_apps_path_symlink)
- ), 'Setup of apps path failed');
-
- // Create an app within that path
- $this->assertTrue((
- mkdir($new_apps_path . '/' . $appName) && touch($new_apps_path . '/' . $appName . '/' . 'test-file.js')
- ), 'Setup of app within the new apps path failed');
-
- // Use the symlink as the app path
- $this->appManager->expects($this->once())
- ->method('getAppPath')
- ->with($appName)
- ->willReturn("$new_apps_path_symlink/$appName");
- $this->appManager->expects($this->once())
- ->method('getAppWebPath')
- ->with($appName)
- ->willReturn("/js-apps-test/$appName");
-
- // Run the tests
- $locator = $this->jsResourceLocator();
- $locator->find(["$appName/test-file"]);
-
- $resources = $locator->getResources();
- $this->assertCount(1, $resources);
- $resource = $resources[0];
- $this->assertCount(3, $resource);
- $root = $resource[0];
- $webRoot = $resource[1];
- $file = $resource[2];
-
- $expectedRoot = $new_apps_path;
- $expectedWebRoot = \OC::$WEBROOT . '/js-apps-test';
- $expectedFile = $appName . '/test-file.js';
-
- $this->assertEquals($expectedRoot, $root,
- 'Ensure the app path symlink is resolved into the real path');
- $this->assertEquals($expectedWebRoot, $webRoot);
- $this->assertEquals($expectedFile, $file);
-
- unlink($new_apps_path_symlink);
- $this->rrmdir($new_apps_path);
- }
-
- public function testNotExistingTranslationHandledSilent(): void {
- $this->appManager->expects($this->once())
- ->method('getAppPath')
- ->with('core')
- ->willThrowException(new AppPathNotFoundException());
- $this->appManager->expects($this->atMost(1))
- ->method('getAppWebPath')
- ->with('core')
- ->willThrowException(new AppPathNotFoundException());
- // Assert logger is not called
- $this->logger->expects($this->never())
- ->method('error');
-
- // Run the tests
- $locator = $this->jsResourceLocator();
- $locator->find(['core/l10n/en.js']);
-
- $resources = $locator->getResources();
- $this->assertCount(0, $resources);
- }
-
- public function testFindModuleJSWithFallback(): void {
- // First create new apps path, and a symlink to it
- $apps_dirname = $this->randomString();
- $new_apps_path = sys_get_temp_dir() . '/' . $apps_dirname;
- mkdir($new_apps_path);
-
- // Create an app within that path
- mkdir("$new_apps_path/test-js-app");
- touch("$new_apps_path/test-js-app/module.mjs");
- touch("$new_apps_path/test-js-app/both.mjs");
- touch("$new_apps_path/test-js-app/both.js");
- touch("$new_apps_path/test-js-app/plain.js");
-
- // Use the app path
- $this->appManager->expects($this->any())
- ->method('getAppPath')
- ->with('test-js-app')
- ->willReturn("$new_apps_path/test-js-app");
-
- $locator = $this->jsResourceLocator();
- $locator->find(['test-js-app/module', 'test-js-app/both', 'test-js-app/plain']);
-
- $resources = $locator->getResources();
- $this->assertCount(3, $resources);
-
- $expectedWebRoot = \OC::$WEBROOT . '/js-apps-test/test-js-app';
- $expectedFiles = ['module.mjs', 'both.mjs', 'plain.js'];
-
- for ($idx = 0; $idx++; $idx < 3) {
- $this->assertEquals($expectedWebRoot, $resources[$idx][1]);
- $this->assertEquals($expectedFiles[$idx], $resources[$idx][2]);
- }
-
- $this->rrmdir($new_apps_path);
- }
-}
--- a/tests/lib/Template/ResourceLocatorTest.php
+++ /dev/null
@@ -1,84 +0,0 @@
-logger = $this->createMock(LoggerInterface::class);
- $this->config = $this->createMock(IConfig::class);
- }
-
- public function getResourceLocator(string $theme): ResourceLocator&MockObject {
- $this->config
- ->expects($this->any())
- ->method('getSystemValueString')
- ->with('theme', '')
- ->willReturn($theme);
- return $this->getMockBuilder(ResourceLocator::class)
- ->onlyMethods(['doFind', 'doFindTheme'])
- ->setConstructorArgs(
- [$this->logger, $this->config],
- '', true, true, true, []
- )
- ->getMock();
- }
-
- public function testFind(): void {
- $locator = $this->getResourceLocator('theme');
- $locator->expects($this->once())
- ->method('doFind')
- ->with('foo');
- $locator->expects($this->once())
- ->method('doFindTheme')
- ->with('foo');
- $locator->find(['foo']);
- }
-
- public function testFindNotFound(): void {
- $locator = $this->getResourceLocator('theme',
- ['core' => 'map'], ['3rd' => 'party'], ['foo' => 'bar']);
- $locator->expects($this->once())
- ->method('doFind')
- ->with('foo')
- ->willThrowException(new ResourceNotFoundException('foo', 'map'));
- $locator->expects($this->once())
- ->method('doFindTheme')
- ->with('foo')
- ->willThrowException(new ResourceNotFoundException('foo', 'map'));
- $this->logger->expects($this->exactly(2))
- ->method('debug')
- ->with($this->stringContains('map/foo'));
- $locator->find(['foo']);
- }
-
- public function testAppendIfExist(): void {
- $locator = $this->getResourceLocator('theme');
- $method = new \ReflectionMethod($locator, 'appendIfExist');
-
- $method->invoke($locator, __DIR__, basename(__FILE__), 'webroot');
- $resource1 = [__DIR__, 'webroot', basename(__FILE__)];
- $this->assertEquals([$resource1], $locator->getResources());
-
- $method->invoke($locator, __DIR__, 'does-not-exist');
- $this->assertEquals([$resource1], $locator->getResources());
- }
-}
--- a/tests/lib/Template/data/1.js
+++ /dev/null
@@ -1 +0,0 @@
-var a = 'hello';
--- a/tests/lib/Template/data/1.js.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
-SPDX-License-Identifier: AGPL-3.0-or-later
--- a/tests/lib/Template/data/2.js
+++ /dev/null
@@ -1 +0,0 @@
-var b = 'world';
--- a/tests/lib/Template/data/2.js.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
-SPDX-License-Identifier: AGPL-3.0-or-later
--- a/tests/lib/Template/data/combine.json
+++ /dev/null
@@ -1,4 +0,0 @@
-[
- "1.js",
- "2.js"
-]
--- a/tests/lib/Template/data/combine.json.license
+++ /dev/null
@@ -1,2 +0,0 @@
-SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
-SPDX-License-Identifier: AGPL-3.0-or-later
--- a/tests/lib/TemplateFunctionsTest.php
+++ /dev/null
@@ -1,299 +0,0 @@
-expectOutputString('<img onload="alert(1)" />');
- p('
');
- }
-
- public function testPJavaScriptWithScriptTags(): void {
- $this->expectOutputString('<script>alert('Hacked!');</script>');
- p("");
- }
-
- public function testPNormalString(): void {
- $string = 'This is a good string without HTML.';
- $this->expectOutputString($string);
- p($string);
- }
-
- public function testPrintUnescaped(): void {
- $htmlString = "";
- $this->expectOutputString($htmlString);
- print_unescaped($htmlString);
- }
-
- public function testPrintUnescapedNormalString(): void {
- $string = 'This is a good string!';
- $this->expectOutputString($string);
- print_unescaped($string);
- }
-
- public function testEmitScriptTagWithContent(): void {
- $this->expectOutputRegex('/ for it!',
- ];
- $goodArray = [
- 'While it is unusual to pass an array',
- 'this function actually <blink>supports</blink> it.',
- 'And therefore there needs to be a <script>alert("Unit"+'test')</script> for it!',
- ];
- $result = Util::sanitizeHTML($badArray);
- $this->assertEquals($goodArray, $result);
-
- $badString = '
';
- $result = Util::sanitizeHTML($badString);
- $this->assertEquals('<img onload="alert(1)" />', $result);
-
- $badString = "";
- $result = Util::sanitizeHTML($badString);
- $this->assertEquals('<script>alert('Hacked!');</script>', $result);
-
- $goodString = 'This is a good string without HTML.';
- $result = Util::sanitizeHTML($goodString);
- $this->assertEquals('This is a good string without HTML.', $result);
- }
-
- public function testEncodePath(): void {
- $component = '/§#@test%&^ä/-child';
- $result = Util::encodePath($component);
- $this->assertEquals('/%C2%A7%23%40test%25%26%5E%C3%A4/-child', $result);
- }
-
- public function testIsNonUTF8Locale(): void {
- // OC_Util::isNonUTF8Locale() assumes escapeshellcmd('§') returns '' with non-UTF-8 locale.
- $locale = setlocale(LC_CTYPE, 0);
- setlocale(LC_CTYPE, 'C');
- $this->assertEquals('', escapeshellcmd('§'));
- $this->assertEquals('\'\'', escapeshellarg('§'));
- setlocale(LC_CTYPE, 'C.UTF-8');
- $this->assertEquals('§', escapeshellcmd('§'));
- $this->assertEquals('\'§\'', escapeshellarg('§'));
- setlocale(LC_CTYPE, $locale);
- }
-
- public function testFileInfoLoaded(): void {
- $expected = function_exists('finfo_open');
- $this->assertEquals($expected, \OC_Util::fileInfoLoaded());
- }
-
- /**
- * Host is "localhost" this is a valid for emails,
- * but not for default strict email verification that requires a top level domain.
- * So we check that with strict email verification we fallback to the default
- */
- public function testGetDefaultEmailAddressStrict(): void {
- $email = Util::getDefaultEmailAddress('no-reply');
- $this->assertEquals('no-reply@localhost.localdomain', $email);
- }
-
- /**
- * If no strict email check is enabled "localhost" should validate as a valid email domain
- */
- public function testGetDefaultEmailAddress(): void {
- $config = Server::get(IConfig::class);
- $config->setAppValue('core', 'enforce_strict_email_check', 'no');
- $email = Util::getDefaultEmailAddress('no-reply');
- $this->assertEquals('no-reply@localhost', $email);
- $config->deleteAppValue('core', 'enforce_strict_email_check');
- }
-
- public function testGetDefaultEmailAddressFromConfig(): void {
- $config = Server::get(IConfig::class);
- $config->setSystemValue('mail_domain', 'example.com');
- $email = Util::getDefaultEmailAddress('no-reply');
- $this->assertEquals('no-reply@example.com', $email);
- $config->deleteSystemValue('mail_domain');
- }
-
- public function testGetConfiguredEmailAddressFromConfig(): void {
- $config = Server::get(IConfig::class);
- $config->setSystemValue('mail_domain', 'example.com');
- $config->setSystemValue('mail_from_address', 'owncloud');
- $email = Util::getDefaultEmailAddress('no-reply');
- $this->assertEquals('owncloud@example.com', $email);
- $config->deleteSystemValue('mail_domain');
- $config->deleteSystemValue('mail_from_address');
- }
-
- public function testGetInstanceIdGeneratesValidId(): void {
- Server::get(IConfig::class)->deleteSystemValue('instanceid');
- $instanceId = OC_Util::getInstanceId();
- $this->assertStringStartsWith('oc', $instanceId);
- $matchesRegex = preg_match('/^[a-z0-9]+$/', $instanceId);
- $this->assertSame(1, $matchesRegex);
- }
-
- /**
- * Test needUpgrade() when the core version is increased
- */
- public function testNeedUpgradeCore(): void {
- $config = Server::get(IConfig::class);
- $oldConfigVersion = $config->getSystemValue('version', '0.0.0');
- $oldSessionVersion = Server::get(ISession::class)->get('OC_Version');
-
- $this->assertFalse(Util::needUpgrade());
-
- $config->setSystemValue('version', '7.0.0.0');
- Server::get(ISession::class)->set('OC_Version', [7, 0, 0, 1]);
- self::invokePrivate(new Util, 'needUpgradeCache', [null]);
-
- $this->assertTrue(Util::needUpgrade());
-
- $config->setSystemValue('version', $oldConfigVersion);
- Server::get(ISession::class)->set('OC_Version', $oldSessionVersion);
- self::invokePrivate(new Util, 'needUpgradeCache', [null]);
-
- $this->assertFalse(Util::needUpgrade());
- }
-
- public function testCheckDataDirectoryValidity(): void {
- $dataDir = Server::get(ITempManager::class)->getTemporaryFolder();
- touch($dataDir . '/.ncdata');
- $errors = \OC_Util::checkDataDirectoryValidity($dataDir);
- $this->assertEmpty($errors);
- Files::rmdirr($dataDir);
-
- $dataDir = Server::get(ITempManager::class)->getTemporaryFolder();
- // no touch
- $errors = \OC_Util::checkDataDirectoryValidity($dataDir);
- $this->assertNotEmpty($errors);
- Files::rmdirr($dataDir);
-
- $errors = \OC_Util::checkDataDirectoryValidity('relative/path');
- $this->assertNotEmpty($errors);
- }
-
- #[\Override]
- protected function setUp(): void {
- parent::setUp();
-
- \OC_Util::$styles = [];
- self::invokePrivate(Util::class, 'scripts', [[]]);
- self::invokePrivate(Util::class, 'scriptDeps', [[]]);
- }
- #[\Override]
- protected function tearDown(): void {
- parent::tearDown();
-
- \OC_Util::$styles = [];
- self::invokePrivate(Util::class, 'scripts', [[]]);
- self::invokePrivate(Util::class, 'scriptDeps', [[]]);
- }
-
- public function testAddScript(): void {
- Util::addScript('first', 'myFirstJSFile');
- Util::addScript('core', 'myFancyJSFile1');
- Util::addScript('files', 'myFancyJSFile2', 'core');
- Util::addScript('myApp5', 'myApp5JSFile', 'myApp2');
- Util::addScript('myApp', 'myFancyJSFile3');
- Util::addScript('core', 'myFancyJSFile4');
- // after itself
- Util::addScript('core', 'myFancyJSFile5', 'core');
- // add duplicate
- Util::addScript('core', 'myFancyJSFile1');
- // dependency chain
- Util::addScript('myApp4', 'myApp4JSFile', 'myApp3');
- Util::addScript('myApp3', 'myApp3JSFile', 'myApp2');
- Util::addScript('myApp2', 'myApp2JSFile', 'myApp');
- Util::addScript('core', 'common');
- Util::addScript('core', 'main');
-
- $scripts = Util::getScripts();
-
- // Core should appear first
- $this->assertEquals(
- 0,
- array_search('core/js/common', $scripts, true)
- );
- $this->assertEquals(
- 1,
- array_search('core/js/main', $scripts, true)
- );
- $this->assertEquals(
- 2,
- array_search('core/js/myFancyJSFile1', $scripts, true)
- );
- $this->assertEquals(
- 3,
- array_search('core/js/myFancyJSFile4', $scripts, true)
- );
-
- // Dependencies should appear before their children
- $this->assertLessThan(
- array_search('files/js/myFancyJSFile2', $scripts, true),
- array_search('core/js/myFancyJSFile3', $scripts, true)
- );
- $this->assertLessThan(
- array_search('myApp2/js/myApp2JSFile', $scripts, true),
- array_search('myApp/js/myFancyJSFile3', $scripts, true)
- );
- $this->assertLessThan(
- array_search('myApp3/js/myApp3JSFile', $scripts, true),
- array_search('myApp2/js/myApp2JSFile', $scripts, true)
- );
- $this->assertLessThan(
- array_search('myApp4/js/myApp4JSFile', $scripts, true),
- array_search('myApp3/js/myApp3JSFile', $scripts, true)
- );
- $this->assertLessThan(
- array_search('myApp5/js/myApp5JSFile', $scripts, true),
- array_search('myApp2/js/myApp2JSFile', $scripts, true)
- );
-
- // No duplicates
- $this->assertEquals(
- $scripts,
- array_unique($scripts)
- );
-
- // All scripts still there
- $scripts = [
- 'core/js/common',
- 'core/js/main',
- 'core/js/myFancyJSFile1',
- 'core/js/myFancyJSFile4',
- 'core/js/myFancyJSFile5',
- 'first/l10n/en',
- 'first/js/myFirstJSFile',
- 'files/l10n/en',
- 'files/js/myFancyJSFile2',
- 'myApp/l10n/en',
- 'myApp/js/myFancyJSFile3',
- 'myApp2/l10n/en',
- 'myApp2/js/myApp2JSFile',
- 'myApp5/l10n/en',
- 'myApp5/js/myApp5JSFile',
- 'myApp3/l10n/en',
- 'myApp3/js/myApp3JSFile',
- 'myApp4/l10n/en',
- 'myApp4/js/myApp4JSFile',
- ];
- foreach ($scripts as $script) {
- $this->assertContains($script, $scripts);
- }
- }
-
- public function testAddScriptCircularDependency(): void {
- Util::addScript('circular', 'file1', 'dependency');
- Util::addScript('dependency', 'file2', 'circular');
-
- $scripts = Util::getScripts();
- $this->assertContains('circular/js/file1', $scripts);
- $this->assertContains('dependency/js/file2', $scripts);
- }
-
- public function testAddTranslations(): void {
- Util::addTranslations('appId', 'de');
-
- $this->assertEquals([
- 'appId/l10n/de'
- ], Util::getScripts());
- $this->assertEquals([], \OC_Util::$styles);
- }
-
- public function testAddStyle(): void {
- Util::addStyle('core', 'myFancyCSSFile1');
- Util::addStyle('myApp', 'myFancyCSSFile2');
- Util::addStyle('core', 'myFancyCSSFile0', true);
- Util::addStyle('core', 'myFancyCSSFile10', true);
- // add duplicate
- Util::addStyle('core', 'myFancyCSSFile1');
-
- $this->assertEquals([], Util::getScripts());
- $this->assertEquals([
- 'core/css/myFancyCSSFile10',
- 'core/css/myFancyCSSFile0',
- 'core/css/myFancyCSSFile1',
- 'myApp/css/myFancyCSSFile2',
- ], \OC_Util::$styles);
- }
-
- public function testAddVendorStyle(): void {
- \OC_Util::addVendorStyle('core', 'myFancyCSSFile1');
- \OC_Util::addVendorStyle('myApp', 'myFancyCSSFile2');
- \OC_Util::addVendorStyle('core', 'myFancyCSSFile0', true);
- \OC_Util::addVendorStyle('core', 'myFancyCSSFile10', true);
- // add duplicate
- \OC_Util::addVendorStyle('core', 'myFancyCSSFile1');
-
- $this->assertEquals([], Util::getScripts());
- $this->assertEquals([
- 'core/vendor/myFancyCSSFile10',
- 'core/vendor/myFancyCSSFile0',
- 'core/vendor/myFancyCSSFile1',
- 'myApp/vendor/myFancyCSSFile2',
- ], \OC_Util::$styles);
- }
-
- public function testShortenMultibyteString(): void {
- $this->assertEquals('Short nuff', Util::shortenMultibyteString('Short nuff', 255));
- $this->assertEquals('ABC', Util::shortenMultibyteString('ABCDEF', 3));
- // each of the characters is 12 bytes
- $this->assertEquals('🙈', Util::shortenMultibyteString('🙈🙊🙉', 16, 2));
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('humanFileSizeProvider')]
- public function testHumanFileSize($expected, $input): void {
- $result = Util::humanFileSize($input);
- $this->assertEquals($expected, $result);
- }
-
- public static function humanFileSizeProvider(): array {
- return [
- ['0 B', 0],
- ['1 KB', 1024],
- ['9.5 MB', 10000000],
- ['1.3 GB', 1395864371],
- ['465.7 GB', 500000000000],
- ['454.7 TB', 500000000000000],
- ['444.1 PB', 500000000000000000],
- ];
- }
-
- #[\PHPUnit\Framework\Attributes\DataProvider('providesComputerFileSize')]
- public function testComputerFileSize($expected, $input): void {
- $result = Util::computerFileSize($input);
- $this->assertEquals($expected, $result);
- }
-
- public static function providesComputerFileSize(): array {
- return [
- [0.0, '0 B'],
- [1024.0, '1 KB'],
- [1395864371.0, '1.3 GB'],
- [9961472.0, '9.5 MB'],
- [500041567437.0, '465.7 GB'],
- [false, '12 GB etfrhzui']
- ];
- }
-
- public function testMb_array_change_key_case(): void {
- $arrayStart = [
- 'Foo' => 'bar',
- 'Bar' => 'foo',
- ];
- $arrayResult = [
- 'foo' => 'bar',
- 'bar' => 'foo',
- ];
- $result = Util::mb_array_change_key_case($arrayStart);
- $expected = $arrayResult;
- $this->assertEquals($result, $expected);
-
- $arrayStart = [
- 'foo' => 'bar',
- 'bar' => 'foo',
- ];
- $arrayResult = [
- 'FOO' => 'bar',
- 'BAR' => 'foo',
- ];
- $result = Util::mb_array_change_key_case($arrayStart, MB_CASE_UPPER);
- $expected = $arrayResult;
- $this->assertEquals($result, $expected);
- }
-
- public static function sanitizeProvider(): array {
- return [
- // Basic spaces and line controls
- ['Hello World', 'Hello World'],
- [' Hello World ', 'Hello World'],
- ["Hello\t World \nAgain", 'Hello World Again'],
- ["Hello\rWorld", 'HelloWorld'],
- ["Hello\r\nWorld", 'HelloWorld'],
- ["Hello\u{200B}World", 'HelloWorld'], // zero-width space removed
- ["Hello\t\n\r World", 'Hello World'],
-
- // Unicode, emoji, and CJK
- ['テスト 😃 💬', 'テスト 😃 💬'],
- ['中文測試 ✅', '中文測試 ✅'],
- ['Русский текст 😁', 'Русский текст 😁'],
- ['Café crème ☕', 'Café crème ☕'],
-
- // Punctuation and filename-like
- ['Hello-World_123.', 'Hello-World_123.'],
- ['File.name, with commas', 'File.name, with commas'],
- ['Smile — dash', 'Smile — dash'],
- ['Invalid:/\\?%*|<>name', 'Invalid:/\\?%*|<>name'], // kept as is
- ['test@example.com', 'test@example.com'],
-
- // Control and invisible chars
- ["Bad\0Name", 'BadName'],
- ["Hello\u{0007}World", 'HelloWorld'],
- ["Line\r\nbreaks", 'Linebreaks'],
- ["\x1F Hidden control", 'Hidden control'],
-
- // Whitespace and normalization
- [" Multiple spaces\t and \nnewlines ", 'Multiple spaces and newlines'],
- ["No-break\u{00A0}space", 'No-break space'], // NBSP normalized
- ["Zero\u{2003}width\u{2009}spaces", 'Zero width spaces'], // various spaces
-
- // Complex mixes
- ['テスト 💬.png', 'テスト 💬.png'],
- [' Mix 😎 emojis 🎉 and 123 numbers ', 'Mix 😎 emojis 🎉 and 123 numbers'],
- ["Hello \u{200B}\n World", 'Hello World'],
- ['Path ../etc/passwd', 'Path ../etc/passwd'],
- ['Symbols! @ # % ^ & * ( )', 'Symbols! @ # % ^ & * ( )'],
- ['Special chars