mirror of
https://github.com/owncloud/ocis.git
synced 2025-04-18 23:44:07 +03:00
2879 lines
74 KiB
PHP
2879 lines
74 KiB
PHP
<?php declare(strict_types=1);
|
|
|
|
/**
|
|
* ownCloud
|
|
*
|
|
* @author Sergio Bertolin <sbertolin@owncloud.com>
|
|
* @author Phillip Davis <phil@jankaritech.com>
|
|
* @copyright Copyright (c) 2018, ownCloud GmbH
|
|
*
|
|
* This code is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License,
|
|
* as published by the Free Software Foundation;
|
|
* either version 3 of the License, or any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
*
|
|
*/
|
|
|
|
use Behat\Behat\Hook\Scope\BeforeStepScope;
|
|
use GuzzleHttp\Exception\GuzzleException;
|
|
use rdx\behatvars\BehatVariablesContext;
|
|
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
|
|
use Behat\Behat\Hook\Scope\AfterScenarioScope;
|
|
use Behat\Gherkin\Node\PyStringNode;
|
|
use Behat\Gherkin\Node\TableNode;
|
|
use Behat\Testwork\Hook\Scope\BeforeSuiteScope;
|
|
use GuzzleHttp\Cookie\CookieJar;
|
|
use Psr\Http\Message\ResponseInterface;
|
|
use PHPUnit\Framework\Assert;
|
|
use Swaggest\JsonSchema\Schema as JsonSchema;
|
|
use TestHelpers\OcsApiHelper;
|
|
use Laminas\Ldap\Ldap;
|
|
use TestHelpers\SetupHelper;
|
|
use TestHelpers\HttpRequestHelper;
|
|
use TestHelpers\HttpLogger;
|
|
use TestHelpers\OcisHelper;
|
|
use TestHelpers\GraphHelper;
|
|
use TestHelpers\WebDavHelper;
|
|
use TestHelpers\SettingsHelper;
|
|
use TestHelpers\OcisConfigHelper;
|
|
|
|
require_once 'bootstrap.php';
|
|
|
|
/**
|
|
* Features context.
|
|
*/
|
|
class FeatureContext extends BehatVariablesContext {
|
|
use Provisioning;
|
|
use Sharing;
|
|
use WebDav;
|
|
|
|
/**
|
|
* json schema validator keywords
|
|
* See: https://json-schema.org/draft-06/draft-wright-json-schema-validation-01#rfc.section.6
|
|
*/
|
|
private array $jsonSchemaValidators;
|
|
|
|
/**
|
|
* Unix timestamp seconds
|
|
*/
|
|
private int $scenarioStartTime;
|
|
private string $adminUsername;
|
|
private string $adminPassword;
|
|
private string $originalAdminPassword;
|
|
|
|
/**
|
|
* An array of values of replacement values of user attributes.
|
|
* These are only referenced when creating a user. After that, the
|
|
* run-time values are maintained and referenced in the $createdUsers array.
|
|
*
|
|
* Key is the username, value is an array of user attributes
|
|
*/
|
|
private ?array $userReplacements = null;
|
|
private string $regularUserPassword;
|
|
private string $alt1UserPassword;
|
|
private string $alt2UserPassword;
|
|
private string $alt3UserPassword;
|
|
private string $alt4UserPassword;
|
|
|
|
/**
|
|
* The password to use in tests that create a sub-admin user
|
|
*/
|
|
private string $subAdminPassword;
|
|
|
|
/**
|
|
* The password to use in tests that create another admin user
|
|
*/
|
|
private string $alternateAdminPassword;
|
|
|
|
/**
|
|
* The password to use in tests that create public link shares
|
|
*/
|
|
private string $publicLinkSharePassword;
|
|
private string $ocPath;
|
|
private string $currentUser = '';
|
|
private string $currentServer;
|
|
|
|
/**
|
|
* The base URL of the current server under test,
|
|
* without any terminating slash
|
|
* e.g. http://localhost:8080
|
|
*/
|
|
private string $baseUrl;
|
|
|
|
/**
|
|
* The base URL of the local server under test,
|
|
* without any terminating slash
|
|
* e.g. http://localhost:8080
|
|
*/
|
|
private string $localBaseUrl;
|
|
|
|
/**
|
|
* The base URL of the remote (federated) server under test,
|
|
* without any terminating slash
|
|
* e.g. http://localhost:8180
|
|
*/
|
|
private string $remoteBaseUrl;
|
|
|
|
/**
|
|
* The suite name, feature name and scenario line number.
|
|
* Example: apiComments/createComments.feature:24
|
|
*/
|
|
private string $scenarioString = '';
|
|
|
|
/**
|
|
* A full unique reference to the step that is currently executing.
|
|
* Example: apiComments/createComments.feature:24-28
|
|
* That is line 28, in the scenario at line 24, in the createComments feature
|
|
* in the apiComments suite.
|
|
*/
|
|
private string $stepLineRef = '';
|
|
private bool $sendStepLineRef = false;
|
|
private bool $sendStepLineRefHasBeenChecked = false;
|
|
|
|
/**
|
|
* @var boolean true if TEST_SERVER_FED_URL is defined
|
|
*/
|
|
private bool $federatedServerExists;
|
|
private int $ocsApiVersion = 1;
|
|
private ?ResponseInterface $response = null;
|
|
private string $responseUser = '';
|
|
private ?string $responseBodyContent = null;
|
|
public array $emailRecipients = [];
|
|
private CookieJar $cookieJar;
|
|
private string $requestToken;
|
|
private array $createdFiles = [];
|
|
|
|
/**
|
|
* The local source IP address from which to initiate API actions.
|
|
* Defaults to system-selected address matching IP address family and scope.
|
|
*/
|
|
private ?string $sourceIpAddress = null;
|
|
private array $guzzleClientHeaders = [];
|
|
public OCSContext $ocsContext;
|
|
public AuthContext $authContext;
|
|
public TUSContext $tusContext;
|
|
public GraphContext $graphContext;
|
|
public SpacesContext $spacesContext;
|
|
|
|
/**
|
|
* The codes are stored as strings, even though they are numbers
|
|
*/
|
|
private array $lastHttpStatusCodesArray = [];
|
|
private array $lastOCSStatusCodesArray = [];
|
|
|
|
/**
|
|
* Store for auto-sync settings for users
|
|
*/
|
|
private array $autoSyncSettings = [];
|
|
|
|
/**
|
|
* @param string $user
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function getUserAutoSyncSetting(string $user): bool {
|
|
if (\array_key_exists($user, $this->autoSyncSettings)) {
|
|
return $this->autoSyncSettings[$user];
|
|
}
|
|
$autoSyncSetting = SettingsHelper::getAutoAcceptSharesSettingValue(
|
|
$this->baseUrl,
|
|
$user,
|
|
$this->getPasswordForUser($user),
|
|
$this->getStepLineRef()
|
|
);
|
|
$this->autoSyncSettings[$user] = $autoSyncSetting;
|
|
|
|
return $autoSyncSetting;
|
|
}
|
|
|
|
/**
|
|
* @param string $user
|
|
* @param bool $value
|
|
*
|
|
* @return void
|
|
*/
|
|
public function rememberUserAutoSyncSetting(string $user, bool $value): void {
|
|
$this->autoSyncSettings[$user] = $value;
|
|
}
|
|
|
|
public const SHARES_SPACE_ID = 'a0ca6a90-a365-4782-871e-d44447bbc668$a0ca6a90-a365-4782-871e-d44447bbc668';
|
|
private bool $useSharingNG = false;
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
public function isUsingSharingNG(): bool {
|
|
return $this->useSharingNG;
|
|
}
|
|
|
|
private string $oCSelector;
|
|
|
|
/**
|
|
* @param string $selector
|
|
*
|
|
* @return void
|
|
*/
|
|
public function setOCSelector(string $selector): void {
|
|
$this->oCSelector = $selector;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getOCSelector(): string {
|
|
return $this->oCSelector;
|
|
}
|
|
|
|
/**
|
|
* @param string|null $httpStatusCode
|
|
*
|
|
* @return void
|
|
*/
|
|
public function pushToLastHttpStatusCodesArray(?string $httpStatusCode = null): void {
|
|
if ($httpStatusCode !== null) {
|
|
$this->lastHttpStatusCodesArray[] = $httpStatusCode;
|
|
} elseif ($this->getResponse()->getStatusCode() !== null) {
|
|
$this->lastHttpStatusCodesArray[] = (string)$this->getResponse()->getStatusCode();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function emptyLastHTTPStatusCodesArray(): void {
|
|
$this->lastHttpStatusCodesArray = [];
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function emptyLastOCSStatusCodesArray(): void {
|
|
$this->lastOCSStatusCodesArray = [];
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function clearStatusCodeArrays(): void {
|
|
$this->emptyLastHTTPStatusCodesArray();
|
|
$this->emptyLastOCSStatusCodesArray();
|
|
}
|
|
|
|
/**
|
|
* @param string $ocsStatusCode
|
|
*
|
|
* @return void
|
|
*/
|
|
public function pushToLastOcsCodesArray(string $ocsStatusCode): void {
|
|
$this->lastOCSStatusCodesArray[] = $ocsStatusCode;
|
|
}
|
|
|
|
/**
|
|
* Add HTTP and OCS status code of the last response to the respective status code array
|
|
*
|
|
* @return void
|
|
*/
|
|
public function pushToLastStatusCodesArrays(): void {
|
|
$this->pushToLastHttpStatusCodesArray(
|
|
(string)$this->getResponse()->getStatusCode()
|
|
);
|
|
try {
|
|
$this->pushToLastOcsCodesArray(
|
|
$this->ocsContext->getOCSResponseStatusCode(
|
|
$this->getResponse()
|
|
)
|
|
);
|
|
} catch (Exception $exception) {
|
|
// if response couldn't be converted into xml then push "notset" to last ocs status codes array
|
|
$this->pushToLastOcsCodesArray("notset");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string $emailAddress
|
|
*
|
|
* @return void
|
|
*/
|
|
public function pushEmailRecipientAsMailBox(string $emailAddress): void {
|
|
$mailBox = explode("@", $emailAddress)[0];
|
|
if (!\in_array($mailBox, $this->emailRecipients)) {
|
|
$this->emailRecipients[] = $mailBox;
|
|
}
|
|
}
|
|
|
|
private Ldap $ldap;
|
|
private string $ldapBaseDN;
|
|
private string $ldapHost;
|
|
private int $ldapPort;
|
|
private string $ldapAdminUser;
|
|
private string $ldapAdminPassword = "";
|
|
private string $ldapUsersOU;
|
|
private string $ldapGroupsOU;
|
|
private string $ldapGroupSchema;
|
|
private bool $skipImportLdif;
|
|
private array $toDeleteDNs = [];
|
|
private array $ldapCreatedUsers = [];
|
|
private array $ldapCreatedGroups = [];
|
|
private array $toDeleteLdapConfigs = [];
|
|
private array $oldLdapConfig = [];
|
|
|
|
/**
|
|
* @return Ldap
|
|
*/
|
|
public function getLdap(): Ldap {
|
|
return $this->ldap;
|
|
}
|
|
|
|
/**
|
|
* @param string $configId
|
|
*
|
|
* @return void
|
|
*/
|
|
public function setToDeleteLdapConfigs(string $configId): void {
|
|
$this->toDeleteLdapConfigs[] = $configId;
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
public function getToDeleteLdapConfigs(): array {
|
|
return $this->toDeleteLdapConfigs;
|
|
}
|
|
|
|
/**
|
|
* @param string $setValue
|
|
*
|
|
* @return void
|
|
*/
|
|
public function setToDeleteDNs(string $setValue): void {
|
|
$this->toDeleteDNs[] = $setValue;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getLdapBaseDN(): string {
|
|
return $this->ldapBaseDN;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getLdapUsersOU(): string {
|
|
return $this->ldapUsersOU;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getLdapGroupsOU(): string {
|
|
return $this->ldapGroupsOU;
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
public function getOldLdapConfig(): array {
|
|
return $this->oldLdapConfig;
|
|
}
|
|
|
|
/**
|
|
* @param string $configId
|
|
* @param string $configKey
|
|
* @param string $value
|
|
*
|
|
* @return void
|
|
*/
|
|
public function setOldLdapConfig(string $configId, string $configKey, string $value): void {
|
|
$this->oldLdapConfig[$configId][$configKey] = $value;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getLdapHost(): string {
|
|
return $this->ldapHost;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getLdapHostWithoutScheme(): string {
|
|
return $this->removeSchemeFromUrl($this->ldapHost);
|
|
}
|
|
|
|
/**
|
|
* @return integer
|
|
*/
|
|
public function getLdapPort(): int {
|
|
return $this->ldapPort;
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
public function isTestingWithLdap(): bool {
|
|
return (\getenv("TEST_WITH_LDAP") === "true");
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
public function sendScenarioLineReferencesInXRequestId(): ?bool {
|
|
if ($this->sendStepLineRefHasBeenChecked === false) {
|
|
$this->sendStepLineRef = (\getenv("SEND_SCENARIO_LINE_REFERENCES") === "true");
|
|
$this->sendStepLineRefHasBeenChecked = true;
|
|
}
|
|
return $this->sendStepLineRef;
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
public function isTestingReplacingUsernames(): bool {
|
|
return (\getenv('REPLACE_USERNAMES') === "true");
|
|
}
|
|
|
|
/**
|
|
* @return array|null
|
|
*/
|
|
public function usersToBeReplaced(): ?array {
|
|
if (($this->userReplacements === null) && $this->isTestingReplacingUsernames()) {
|
|
$this->userReplacements = \json_decode(
|
|
\file_get_contents("./tests/acceptance/usernames.json"),
|
|
true
|
|
);
|
|
// Loop through the user replacements, and make entries for the lower
|
|
// and upper case forms. This allows for steps that specifically
|
|
// want to test that usernames like "alice", "Alice" and "ALICE" all work.
|
|
// Such steps will make useful replacements for each form.
|
|
foreach ($this->userReplacements as $key => $value) {
|
|
$lowerKey = \strtolower($key);
|
|
if ($lowerKey !== $key) {
|
|
$this->userReplacements[$lowerKey] = $value;
|
|
$this->userReplacements[$lowerKey]['username'] = \strtolower(
|
|
$this->userReplacements[$lowerKey]['username']
|
|
);
|
|
}
|
|
$upperKey = \strtoupper($key);
|
|
if ($upperKey !== $key) {
|
|
$this->userReplacements[$upperKey] = $value;
|
|
$this->userReplacements[$upperKey]['username'] = \strtoupper(
|
|
$this->userReplacements[$upperKey]['username']
|
|
);
|
|
}
|
|
}
|
|
}
|
|
return $this->userReplacements;
|
|
}
|
|
|
|
/**
|
|
* BasicStructure constructor.
|
|
*
|
|
* @param string $baseUrl
|
|
* @param string $adminUsername
|
|
* @param string $adminPassword
|
|
* @param string $regularUserPassword
|
|
* @param string $ocPath
|
|
*
|
|
*/
|
|
public function __construct(
|
|
string $baseUrl,
|
|
string $adminUsername,
|
|
string $adminPassword,
|
|
string $regularUserPassword,
|
|
string $ocPath
|
|
) {
|
|
// Initialize your context here
|
|
$this->baseUrl = \rtrim($baseUrl, '/');
|
|
$this->adminUsername = $adminUsername;
|
|
$this->adminPassword = $adminPassword;
|
|
$this->regularUserPassword = $regularUserPassword;
|
|
$this->localBaseUrl = $this->baseUrl;
|
|
$this->currentServer = 'LOCAL';
|
|
$this->cookieJar = new CookieJar();
|
|
$this->ocPath = $ocPath;
|
|
|
|
// PARALLEL DEPLOYMENT: ownCloud selector
|
|
$this->oCSelector = "oc10";
|
|
|
|
// These passwords are referenced in tests and can be overridden by
|
|
// setting environment variables.
|
|
$this->alt1UserPassword = "1234";
|
|
$this->alt2UserPassword = "AaBb2Cc3Dd4";
|
|
$this->alt3UserPassword = "aVeryLongPassword42TheMeaningOfLife";
|
|
$this->alt4UserPassword = "ThisIsThe4thAlternatePwd";
|
|
$this->subAdminPassword = "IamAJuniorAdmin42";
|
|
$this->alternateAdminPassword = "IHave99LotsOfPriv";
|
|
$this->publicLinkSharePassword = "publicPwd:1";
|
|
|
|
// in case of CI deployment we take the server url from the environment
|
|
$testServerUrl = \getenv('TEST_SERVER_URL');
|
|
if ($testServerUrl !== false) {
|
|
$this->baseUrl = \rtrim($testServerUrl, '/');
|
|
$this->localBaseUrl = $this->baseUrl;
|
|
}
|
|
|
|
// federated server url from the environment
|
|
$testRemoteServerUrl = \getenv('TEST_SERVER_FED_URL');
|
|
if ($testRemoteServerUrl !== false) {
|
|
$this->remoteBaseUrl = \rtrim($testRemoteServerUrl, '/');
|
|
$this->federatedServerExists = true;
|
|
} else {
|
|
$this->remoteBaseUrl = $this->localBaseUrl;
|
|
$this->federatedServerExists = false;
|
|
}
|
|
|
|
// get the admin username from the environment (if defined)
|
|
$adminUsernameFromEnvironment = $this->getAdminUsernameFromEnvironment();
|
|
if ($adminUsernameFromEnvironment !== false) {
|
|
$this->adminUsername = $adminUsernameFromEnvironment;
|
|
}
|
|
|
|
// get the admin password from the environment (if defined)
|
|
$adminPasswordFromEnvironment = $this->getAdminPasswordFromEnvironment();
|
|
if ($adminPasswordFromEnvironment !== false) {
|
|
$this->adminPassword = $adminPasswordFromEnvironment;
|
|
}
|
|
|
|
// get the regular user password from the environment (if defined)
|
|
$regularUserPasswordFromEnvironment = $this->getRegularUserPasswordFromEnvironment();
|
|
if ($regularUserPasswordFromEnvironment !== false) {
|
|
$this->regularUserPassword = $regularUserPasswordFromEnvironment;
|
|
}
|
|
|
|
// get the alternate(1) user password from the environment (if defined)
|
|
$alt1UserPasswordFromEnvironment = $this->getAlt1UserPasswordFromEnvironment();
|
|
if ($alt1UserPasswordFromEnvironment !== false) {
|
|
$this->alt1UserPassword = $alt1UserPasswordFromEnvironment;
|
|
}
|
|
|
|
// get the alternate(2) user password from the environment (if defined)
|
|
$alt2UserPasswordFromEnvironment = $this->getAlt2UserPasswordFromEnvironment();
|
|
if ($alt2UserPasswordFromEnvironment !== false) {
|
|
$this->alt2UserPassword = $alt2UserPasswordFromEnvironment;
|
|
}
|
|
|
|
// get the alternate(3) user password from the environment (if defined)
|
|
$alt3UserPasswordFromEnvironment = $this->getAlt3UserPasswordFromEnvironment();
|
|
if ($alt3UserPasswordFromEnvironment !== false) {
|
|
$this->alt3UserPassword = $alt3UserPasswordFromEnvironment;
|
|
}
|
|
|
|
// get the alternate(4) user password from the environment (if defined)
|
|
$alt4UserPasswordFromEnvironment = $this->getAlt4UserPasswordFromEnvironment();
|
|
if ($alt4UserPasswordFromEnvironment !== false) {
|
|
$this->alt4UserPassword = $alt4UserPasswordFromEnvironment;
|
|
}
|
|
|
|
// get the sub-admin password from the environment (if defined)
|
|
$subAdminPasswordFromEnvironment = $this->getSubAdminPasswordFromEnvironment();
|
|
if ($subAdminPasswordFromEnvironment !== false) {
|
|
$this->subAdminPassword = $subAdminPasswordFromEnvironment;
|
|
}
|
|
|
|
// get the alternate admin password from the environment (if defined)
|
|
$alternateAdminPasswordFromEnvironment = $this->getAlternateAdminPasswordFromEnvironment();
|
|
if ($alternateAdminPasswordFromEnvironment !== false) {
|
|
$this->alternateAdminPassword = $alternateAdminPasswordFromEnvironment;
|
|
}
|
|
|
|
// get the public link share password from the environment (if defined)
|
|
$publicLinkSharePasswordFromEnvironment = $this->getPublicLinkSharePasswordFromEnvironment();
|
|
if ($publicLinkSharePasswordFromEnvironment !== false) {
|
|
$this->publicLinkSharePassword = $publicLinkSharePasswordFromEnvironment;
|
|
}
|
|
$this->originalAdminPassword = $this->adminPassword;
|
|
|
|
$this->jsonSchemaValidators = \array_keys(JsonSchema::properties()->getDataKeyMap());
|
|
}
|
|
|
|
/**
|
|
* Create log directory if it doesn't exist
|
|
*
|
|
* @BeforeSuite
|
|
*
|
|
* @param BeforeSuiteScope $scope
|
|
*
|
|
* @return void
|
|
* @throws Exception
|
|
*/
|
|
public static function setupLogDir(BeforeSuiteScope $scope): void {
|
|
if (!\file_exists(HttpLogger::getLogDir())) {
|
|
\mkdir(HttpLogger::getLogDir(), 0777, true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @BeforeScenario
|
|
*
|
|
* @param BeforeScenarioScope $scope
|
|
*
|
|
* @return void
|
|
* @throws Exception
|
|
*/
|
|
public static function logScenario(BeforeScenarioScope $scope): void {
|
|
$scenarioLine = self::getScenarioLine($scope);
|
|
|
|
if ($scope->getScenario()->getNodeType() === "Example") {
|
|
$scenario = "Scenario Outline: " . $scope->getScenario()->getOutlineTitle();
|
|
} else {
|
|
$scenario = $scope->getScenario()->getNodeType() . ": " . $scope->getScenario()->getTitle();
|
|
}
|
|
|
|
$logMessage = "## $scenario ($scenarioLine)\n";
|
|
|
|
// Delete previous scenario's log file
|
|
if (\file_exists(HttpLogger::getScenarioLogPath())) {
|
|
\unlink(HttpLogger::getScenarioLogPath());
|
|
}
|
|
|
|
// Write the scenario log
|
|
HttpLogger::writeLog(HttpLogger::getScenarioLogPath(), $logMessage);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @BeforeStep
|
|
*
|
|
* @param BeforeStepScope $scope
|
|
*
|
|
* @return void
|
|
* @throws Exception
|
|
*/
|
|
public static function logStep(BeforeStepScope $scope): void {
|
|
$step = $scope->getStep()->getKeyword() . " " . $scope->getStep()->getText();
|
|
$logMessage = "\t### $step\n";
|
|
HttpLogger::writeLog(HttpLogger::getScenarioLogPath(), $logMessage);
|
|
}
|
|
|
|
/**
|
|
* FIRST AfterScenario HOOK
|
|
*
|
|
* NOTE: This method is called after each scenario having the @env-config tag
|
|
* This ensures that the server is running for clean-up purposes
|
|
*
|
|
* @AfterScenario @env-config
|
|
*
|
|
* @return void
|
|
*/
|
|
public function startOcisServer(): void {
|
|
$response = OcisConfigHelper::startOcis();
|
|
// 409 is returned if the server is already running
|
|
$this->theHTTPStatusCodeShouldBe([200, 409], 'Starting oCIS server', $response);
|
|
}
|
|
|
|
/**
|
|
* Get the externally-defined admin username, if any
|
|
*
|
|
* @return string|false
|
|
*/
|
|
private static function getAdminUsernameFromEnvironment() {
|
|
return \getenv('ADMIN_USERNAME');
|
|
}
|
|
|
|
/**
|
|
* Get the externally-defined admin password, if any
|
|
*
|
|
* @return string|false
|
|
*/
|
|
private static function getAdminPasswordFromEnvironment() {
|
|
return \getenv('ADMIN_PASSWORD');
|
|
}
|
|
|
|
/**
|
|
* Get the externally-defined regular user password, if any
|
|
*
|
|
* @return string|false
|
|
*/
|
|
private static function getRegularUserPasswordFromEnvironment() {
|
|
return \getenv('REGULAR_USER_PASSWORD');
|
|
}
|
|
|
|
/**
|
|
* Get the externally-defined alternate(1) user password, if any
|
|
*
|
|
* @return string|false
|
|
*/
|
|
private static function getAlt1UserPasswordFromEnvironment() {
|
|
return \getenv('ALT1_USER_PASSWORD');
|
|
}
|
|
|
|
/**
|
|
* Get the externally-defined alternate(2) user password, if any
|
|
*
|
|
* @return string|false
|
|
*/
|
|
private static function getAlt2UserPasswordFromEnvironment() {
|
|
return \getenv('ALT2_USER_PASSWORD');
|
|
}
|
|
|
|
/**
|
|
* Get the externally-defined alternate(3) user password, if any
|
|
*
|
|
* @return string|false
|
|
*/
|
|
private static function getAlt3UserPasswordFromEnvironment() {
|
|
return \getenv('ALT3_USER_PASSWORD');
|
|
}
|
|
|
|
/**
|
|
* Get the externally-defined alternate(4) user password, if any
|
|
*
|
|
* @return string|false
|
|
*/
|
|
private static function getAlt4UserPasswordFromEnvironment() {
|
|
return \getenv('ALT4_USER_PASSWORD');
|
|
}
|
|
|
|
/**
|
|
* Get the externally-defined sub-admin password, if any
|
|
*
|
|
* @return string|false
|
|
*/
|
|
private static function getSubAdminPasswordFromEnvironment() {
|
|
return \getenv('SUB_ADMIN_PASSWORD');
|
|
}
|
|
|
|
/**
|
|
* Get the externally-defined alternate admin password, if any
|
|
*
|
|
* @return string|false
|
|
*/
|
|
private static function getAlternateAdminPasswordFromEnvironment() {
|
|
return \getenv('ALTERNATE_ADMIN_PASSWORD');
|
|
}
|
|
|
|
/**
|
|
* Get the externally-defined public link share password, if any
|
|
*
|
|
* @return string|false
|
|
*/
|
|
private static function getPublicLinkSharePasswordFromEnvironment() {
|
|
return \getenv('PUBLIC_LINK_SHARE_PASSWORD');
|
|
}
|
|
|
|
/**
|
|
* removes the scheme "http(s)://" (if any) from the front of a URL
|
|
* note: only needs to handle http or https
|
|
*
|
|
* @param string $url
|
|
*
|
|
* @return string
|
|
*/
|
|
public function removeSchemeFromUrl(string $url): string {
|
|
return \preg_replace(
|
|
"(^https?://)",
|
|
"",
|
|
$url
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getOcPath(): string {
|
|
return $this->ocPath;
|
|
}
|
|
|
|
/**
|
|
* returns the base URL (which is without a slash at the end)
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getBaseUrl(): string {
|
|
return $this->baseUrl;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getStorageUsersRoot(): string {
|
|
$ocisDataPath = getenv("OCIS_BASE_DATA_PATH") ? getenv("OCIS_BASE_DATA_PATH") : getenv("HOME") . '/.ocis';
|
|
return getenv("STORAGE_USERS_OCIS_ROOT") ? getenv("STORAGE_USERS_OCIS_ROOT") : $ocisDataPath . "/storage/users";
|
|
}
|
|
|
|
/**
|
|
* returns the path of the base URL
|
|
* e.g. owncloud-core/10 if the baseUrl is http://localhost/owncloud-core/10
|
|
* the path is without a slash at the end and without a slash at the beginning
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getBasePath(): string {
|
|
$parsedUrl = \parse_url($this->getBaseUrl(), PHP_URL_PATH);
|
|
// If the server-under-test is at the "top" of the domain then parse_url returns null.
|
|
// For example, testing a server at http://localhost:8080 or http://example.com
|
|
if ($parsedUrl === null) {
|
|
$parsedUrl = '';
|
|
}
|
|
return \ltrim($parsedUrl, "/");
|
|
}
|
|
|
|
/**
|
|
* returns the OCS path
|
|
* the path is without a slash at the end and without a slash at the beginning
|
|
*
|
|
* @param string $ocsApiVersion
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getOCSPath(string $ocsApiVersion): string {
|
|
return \ltrim($this->getBasePath() . "/ocs/v$ocsApiVersion.php", "/");
|
|
}
|
|
|
|
/**
|
|
* returns the complete DAV path including the base path e.g. owncloud-core/remote.php/dav
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getDAVPathIncludingBasePath(): string {
|
|
return \ltrim($this->getBasePath() . "/" . $this->getDavPath(), "/");
|
|
}
|
|
|
|
/**
|
|
* returns the base URL but without "http(s)://" in front of it
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getBaseUrlWithoutScheme(): string {
|
|
return $this->removeSchemeFromUrl($this->getBaseUrl());
|
|
}
|
|
|
|
/**
|
|
* returns the local base URL (which is without a slash at the end)
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getLocalBaseUrl(): string {
|
|
return $this->localBaseUrl;
|
|
}
|
|
|
|
/**
|
|
* returns the local base URL but without "http(s)://" in front of it
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getLocalBaseUrlWithoutScheme(): string {
|
|
return $this->removeSchemeFromUrl($this->getLocalBaseUrl());
|
|
}
|
|
|
|
/**
|
|
* returns the remote base URL (which is without a slash at the end)
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getRemoteBaseUrl(): string {
|
|
return $this->remoteBaseUrl;
|
|
}
|
|
|
|
/**
|
|
* returns the remote base URL but without "http(s)://" in front of it
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getRemoteBaseUrlWithoutScheme(): string {
|
|
return $this->removeSchemeFromUrl($this->getRemoteBaseUrl());
|
|
}
|
|
|
|
/**
|
|
* returns the reference to the current line being executed.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getStepLineRef(): string {
|
|
if (!$this->sendStepLineRef) {
|
|
return '';
|
|
}
|
|
|
|
// If we are in BeforeScenario and possibly before any particular step
|
|
// is being executed, then stepLineRef might be empty. In that case
|
|
// return just the string for the scenario.
|
|
if ($this->stepLineRef === '') {
|
|
return $this->scenarioString;
|
|
}
|
|
return $this->stepLineRef;
|
|
}
|
|
|
|
/**
|
|
* returns the base URL without any sub-path e.g. http://localhost:8080
|
|
* of the base URL http://localhost:8080/owncloud
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getBaseUrlWithoutPath(): string {
|
|
$parts = \parse_url($this->getBaseUrl());
|
|
$url = $parts ["scheme"] . "://" . $parts["host"];
|
|
if (isset($parts["port"])) {
|
|
$url = "$url:" . $parts["port"];
|
|
}
|
|
return $url;
|
|
}
|
|
|
|
/**
|
|
* @return int
|
|
*/
|
|
public function getOcsApiVersion(): int {
|
|
return $this->ocsApiVersion;
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
public function getGuzzleClientHeaders(): array {
|
|
return $this->guzzleClientHeaders;
|
|
}
|
|
|
|
/**
|
|
* @param array $guzzleClientHeaders ['X-Foo' => 'Bar']
|
|
*
|
|
* @return void
|
|
*/
|
|
public function setGuzzleClientHeaders(array $guzzleClientHeaders): void {
|
|
$this->guzzleClientHeaders = $guzzleClientHeaders;
|
|
}
|
|
|
|
/**
|
|
* @param array $guzzleClientHeaders ['X-Foo' => 'Bar']
|
|
*
|
|
* @return void
|
|
*/
|
|
public function addGuzzleClientHeaders(array $guzzleClientHeaders): void {
|
|
$this->guzzleClientHeaders = \array_merge(
|
|
$this->guzzleClientHeaders,
|
|
$guzzleClientHeaders
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @Given using SharingNG
|
|
*
|
|
* @return void
|
|
*/
|
|
public function usingSharingNG(): void {
|
|
$this->useSharingNG = true;
|
|
}
|
|
|
|
/**
|
|
* @Given /^using OCS API version "([^"]*)"$/
|
|
*
|
|
* @param string $version
|
|
*
|
|
* @return void
|
|
*/
|
|
public function usingOcsApiVersion(string $version): void {
|
|
$this->ocsApiVersion = (int)$version;
|
|
}
|
|
|
|
/**
|
|
* @Given /^as user "([^"]*)"$/
|
|
*
|
|
* @param string $user
|
|
*
|
|
* @return void
|
|
*/
|
|
public function asUser(string $user): void {
|
|
$this->currentUser = $this->getActualUsername($user);
|
|
}
|
|
|
|
/**
|
|
* @Given as the administrator
|
|
*
|
|
* @return void
|
|
*/
|
|
public function asTheAdministrator(): void {
|
|
$this->currentUser = $this->getAdminUsername();
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getCurrentUser(): string {
|
|
return $this->currentUser;
|
|
}
|
|
|
|
/**
|
|
* @param string $user
|
|
*
|
|
* @return void
|
|
*/
|
|
public function setCurrentUser(string $user): void {
|
|
$this->currentUser = $user;
|
|
}
|
|
|
|
/**
|
|
* returns $this->response
|
|
* some steps use that private var to store the response for other steps
|
|
*
|
|
* @return ResponseInterface
|
|
*/
|
|
public function getResponse(): ?ResponseInterface {
|
|
return $this->response;
|
|
}
|
|
|
|
/**
|
|
* let this class remember a response that was received elsewhere
|
|
* so that steps in this class can be used to examine the response
|
|
*
|
|
* @param ResponseInterface|null $response
|
|
* @param string $username of the user that received the response
|
|
*
|
|
* @return void
|
|
*/
|
|
public function setResponse(
|
|
?ResponseInterface $response,
|
|
string $username = ""
|
|
): void {
|
|
$this->response = $response;
|
|
//after a new response reset the response xml
|
|
$this->responseXml = [];
|
|
//after a new response reset the response xml object
|
|
$this->responseXmlObject = null;
|
|
// remember the user that received the response
|
|
$this->responseUser = $username;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getCurrentServer(): string {
|
|
return $this->currentServer;
|
|
}
|
|
|
|
/**
|
|
* @Given /^using server "(LOCAL|REMOTE)"$/
|
|
*
|
|
* @param string|null $server
|
|
*
|
|
* @return string Previous used server
|
|
*/
|
|
public function usingServer(?string $server): string {
|
|
$previousServer = $this->currentServer;
|
|
if ($server === 'LOCAL') {
|
|
$this->baseUrl = $this->localBaseUrl;
|
|
$this->currentServer = 'LOCAL';
|
|
} else {
|
|
$this->baseUrl = $this->remoteBaseUrl;
|
|
$this->currentServer = 'REMOTE';
|
|
}
|
|
return $previousServer;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function federatedServerExists(): bool {
|
|
return $this->federatedServerExists;
|
|
}
|
|
|
|
/**
|
|
* Parses the response as XML
|
|
*
|
|
* @param ResponseInterface|null $response
|
|
* @param string|null $exceptionText text to put at the front of exception messages
|
|
*
|
|
* @return SimpleXMLElement
|
|
* @throws Exception
|
|
*/
|
|
public function getResponseXml(?ResponseInterface $response = null, ?string $exceptionText = ''): SimpleXMLElement {
|
|
if ($response === null) {
|
|
$response = $this->response;
|
|
}
|
|
|
|
if ($exceptionText === '') {
|
|
$exceptionText = __METHOD__;
|
|
}
|
|
return HttpRequestHelper::getResponseXml($response, $exceptionText);
|
|
}
|
|
|
|
/**
|
|
* @param JsonSchema $schemaObj
|
|
*
|
|
* @return void
|
|
* @throws Exception
|
|
*/
|
|
private function checkInvalidValidator(JsonSchema $schemaObj): void {
|
|
$validators = \array_keys((array)$schemaObj->jsonSerialize());
|
|
foreach ($validators as $validator) {
|
|
Assert::assertContains(\ltrim($validator, "$"), $this->jsonSchemaValidators, "Invalid schema validator: '$validator'");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validates against the requirements that object schema should adhere to
|
|
*
|
|
* @param JsonSchema $schemaObj
|
|
*
|
|
* @return void
|
|
* @throws Exception
|
|
*/
|
|
public function validateSchemaObject(JsonSchema $schemaObj): void {
|
|
$this->checkInvalidValidator($schemaObj);
|
|
|
|
if ($schemaObj->type && $schemaObj->type !== "object") {
|
|
return;
|
|
}
|
|
|
|
$notAllowedValidators = ["items", "maxItems", "minItems", "uniqueItems"];
|
|
|
|
// check invalid validators
|
|
foreach ($notAllowedValidators as $validator) {
|
|
Assert::assertTrue(null === $schemaObj->$validator, "'$validator' should not be used with object type");
|
|
}
|
|
|
|
$propNames = $schemaObj->getPropertyNames();
|
|
$props = $schemaObj->getProperties();
|
|
foreach ($propNames as $propName) {
|
|
$schema = $props->$propName;
|
|
switch ($schema->type) {
|
|
case "array":
|
|
$this->validateSchemaArray($schema);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
// traverse for nested properties
|
|
$this->validateSchemaObject($schema);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validates against the requirements that array schema should adhere to
|
|
*
|
|
* @param JsonSchema $schemaObj
|
|
*
|
|
* @return void
|
|
* @throws Exception
|
|
*/
|
|
private function validateSchemaArray(JsonSchema $schemaObj): void {
|
|
$this->checkInvalidValidator($schemaObj);
|
|
|
|
if ($schemaObj->type && $schemaObj->type !== "array") {
|
|
return;
|
|
}
|
|
|
|
$hasTwoElementValidator = ($schemaObj->enum && $schemaObj->const) || ($schemaObj->enum && $schemaObj->items) || ($schemaObj->const && $schemaObj->items);
|
|
Assert::assertFalse($hasTwoElementValidator, "'items', 'enum' and 'const' should not be used together");
|
|
if ($schemaObj->enum || $schemaObj->const) {
|
|
// do not try to validate of enum or const is present
|
|
return;
|
|
}
|
|
|
|
$requiredValidators = ["maxItems", "minItems"];
|
|
$optionalValidators = ["items", "uniqueItems"];
|
|
$notAllowedValidators = ["properties", "minProperties", "maxProperties", "required"];
|
|
$errMsg = "'%s' is required for array assertion";
|
|
|
|
// check invalid validators
|
|
foreach ($notAllowedValidators as $validator) {
|
|
Assert::assertTrue($schemaObj->$validator === null, "'$validator' should not be used with array type");
|
|
}
|
|
|
|
// check required validators
|
|
foreach ($requiredValidators as $validator) {
|
|
Assert::assertNotNull($schemaObj->$validator, \sprintf($errMsg, $validator));
|
|
}
|
|
|
|
Assert::assertEquals($schemaObj->minItems, $schemaObj->maxItems, "'minItems' and 'maxItems' should be equal for strict assertion");
|
|
|
|
// check optional validators
|
|
foreach ($optionalValidators as $validator) {
|
|
$value = $schemaObj->$validator;
|
|
switch ($validator) {
|
|
case "items":
|
|
if ($schemaObj->maxItems === 0) {
|
|
break;
|
|
}
|
|
Assert::assertNotNull($schemaObj->$validator, \sprintf($errMsg, $validator));
|
|
if ($schemaObj->maxItems > 1) {
|
|
if (\is_array($value)) {
|
|
foreach ($value as $element) {
|
|
Assert::assertNotNull($element->oneOf, "'oneOf' is required to assert more than one elements");
|
|
}
|
|
Assert::fail("'$validator' should be an object not an array");
|
|
}
|
|
Assert::assertFalse($value->allOf || $value->anyOf, "'allOf' and 'anyOf' are not allowed in array");
|
|
Assert::assertNotNull($value->oneOf, "'oneOf' is required to assert more than one elements");
|
|
Assert::assertTrue(\is_array($value->oneOf), "'oneOf' should be an array");
|
|
Assert::assertEquals($schemaObj->maxItems, \count($value->oneOf), "Expected " . $schemaObj->maxItems . " 'oneOf' items but got " . \count($value->oneOf));
|
|
}
|
|
Assert::assertTrue(\is_object($value), "'$validator' should be an object when expecting 1 element");
|
|
break;
|
|
case "uniqueItems":
|
|
if ($schemaObj->minItems > 1) {
|
|
$errMsg = $value === null ? \sprintf($errMsg, $validator) : "'$validator' should be true";
|
|
Assert::assertTrue($value, $errMsg);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
$items = $schemaObj->items;
|
|
if ($items !== null && $items->oneOf !== null) {
|
|
foreach ($items->oneOf as $oneOfItem) {
|
|
$this->validateSchemaObject($oneOfItem);
|
|
}
|
|
} elseif ($items !== null) {
|
|
$this->validateSchemaObject($items);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validates the json schema requirements
|
|
*
|
|
* @param JsonSchema $schema
|
|
*
|
|
* @return void
|
|
* @throws Exception
|
|
*/
|
|
public function validateSchemaRequirements(JsonSchema $schema): void {
|
|
Assert::assertNotNull($schema->type, "'type' is required for root level schema");
|
|
|
|
switch ($schema->type) {
|
|
case "object":
|
|
$this->validateSchemaObject($schema);
|
|
break;
|
|
case "array":
|
|
$this->validateSchemaArray($schema);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param object|array $json
|
|
* @param object $schema
|
|
*
|
|
* @return void
|
|
* @throws Exception
|
|
*/
|
|
public function assertJsonDocumentMatchesSchema(object|array $json, object $schema): void {
|
|
$schema = JsonSchema::import($schema);
|
|
$this->validateSchemaRequirements($schema);
|
|
$schema->in($json);
|
|
}
|
|
|
|
/**
|
|
* @When /^user "([^"]*)" sends HTTP method "([^"]*)" to URL "([^"]*)"$/
|
|
*
|
|
* @param string $user
|
|
* @param string $verb
|
|
* @param string $url
|
|
*
|
|
* @return void
|
|
*/
|
|
public function userSendsHTTPMethodToUrl(string $user, string $verb, string $url): void {
|
|
$user = $this->getActualUsername($user);
|
|
$url = $this->substituteInLineCodes($url, $user);
|
|
$this->setResponse($this->sendingToWithDirectUrl($user, $verb, $url));
|
|
}
|
|
|
|
/**
|
|
* @When /^user "([^"]*)" sends HTTP method "([^"]*)" to URL "([^"]*)" with headers$/
|
|
*
|
|
* @param string $user
|
|
* @param string $verb
|
|
* @param string $url
|
|
* @param TableNode $headersTable
|
|
*
|
|
* @return void
|
|
* @throws GuzzleException
|
|
* @throws JsonException
|
|
*/
|
|
public function userSendsHTTPMethodToUrlWithHeaders(string $user, string $verb, string $url, TableNode $headersTable): void {
|
|
$this->verifyTableNodeColumns(
|
|
$headersTable,
|
|
['header', 'value']
|
|
);
|
|
|
|
$user = $this->getActualUsername($user);
|
|
$url = $this->substituteInLineCodes($url, $user);
|
|
$url = "/" . \ltrim(\str_replace($this->getBaseUrl(), "", $url), "/");
|
|
|
|
$headers = [];
|
|
foreach ($headersTable as $row) {
|
|
$headers[$row['header']] = $row['value'];
|
|
}
|
|
$response = $this->sendingToWithDirectUrl($user, $verb, $url, null, null, $headers);
|
|
$this->setResponse($response);
|
|
}
|
|
|
|
/**
|
|
* This function is needed to use a vertical fashion in the gherkin tables.
|
|
*
|
|
* @param array $arrayOfArrays
|
|
*
|
|
* @return array
|
|
*/
|
|
public function simplifyArray(array $arrayOfArrays): array {
|
|
$a = \array_map(
|
|
function ($subArray) {
|
|
return $subArray[0];
|
|
},
|
|
$arrayOfArrays
|
|
);
|
|
return $a;
|
|
}
|
|
|
|
/**
|
|
* @When user :user sends HTTP method :method to URL :davPath with content :content
|
|
*
|
|
* @param string $user
|
|
* @param string $method
|
|
* @param string $davPath
|
|
* @param string $content
|
|
*
|
|
* @return void
|
|
*/
|
|
public function userSendsHttpMethodToUrlWithContent(string $user, string $method, string $davPath, string $content): void {
|
|
$this->setResponse($this->sendingToWithDirectUrl($user, $method, $davPath, $content));
|
|
}
|
|
|
|
/**
|
|
* @When /^user "([^"]*)" sends HTTP method "([^"]*)" to URL "([^"]*)" with password "([^"]*)"$/
|
|
*
|
|
* @param string $user
|
|
* @param string $verb
|
|
* @param string $url
|
|
* @param string $password
|
|
*
|
|
* @return void
|
|
*/
|
|
public function userSendsHTTPMethodToUrlWithPassword(string $user, string $verb, string $url, string $password): void {
|
|
$this->setResponse($this->sendingToWithDirectUrl($user, $verb, $url, null, $password));
|
|
}
|
|
|
|
/**
|
|
* @param string $user
|
|
* @param string $verb
|
|
* @param string $url
|
|
* @param string|null $body
|
|
* @param string|null $password
|
|
* @param array|null $headers
|
|
*
|
|
* @return ResponseInterface
|
|
* @throws GuzzleException
|
|
*/
|
|
public function sendingToWithDirectUrl(string $user, string $verb, string $url, ?string $body = null, ?string $password = null, ?array $headers = null): ResponseInterface {
|
|
$fullUrl = $this->getBaseUrl() . $url;
|
|
|
|
if ($password === null) {
|
|
$password = $this->getPasswordForUser($user);
|
|
}
|
|
|
|
$reqHeaders = $this->guzzleClientHeaders;
|
|
|
|
$config = null;
|
|
if ($this->sourceIpAddress !== null) {
|
|
$config = [
|
|
'curl' => [
|
|
CURLOPT_INTERFACE => $this->sourceIpAddress
|
|
]
|
|
];
|
|
}
|
|
|
|
$cookies = null;
|
|
if (!empty($this->cookieJar->toArray())) {
|
|
$cookies = $this->cookieJar;
|
|
}
|
|
|
|
if (isset($this->requestToken)) {
|
|
$reqHeaders['requesttoken'] = $this->requestToken;
|
|
}
|
|
|
|
if ($headers) {
|
|
$reqHeaders = \array_merge($headers, $reqHeaders);
|
|
}
|
|
|
|
return HttpRequestHelper::sendRequest(
|
|
$fullUrl,
|
|
$this->getStepLineRef(),
|
|
$verb,
|
|
$user,
|
|
$password,
|
|
$reqHeaders,
|
|
$body,
|
|
$config,
|
|
$cookies
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param string $url
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function isAPublicLinkUrl(string $url): bool {
|
|
if (OcisHelper::isTestingOnReva()) {
|
|
$urlEnding = \ltrim($url, '/');
|
|
} else {
|
|
if (\substr($url, 0, 4) !== "http") {
|
|
return false;
|
|
}
|
|
$urlEnding = \substr($url, \strlen($this->getBaseUrl() . '/'));
|
|
}
|
|
|
|
$matchResult = \preg_match("%^(#/)?s/([a-zA-Z0-9]{15})$%", $urlEnding);
|
|
|
|
// preg_match returns (int) 1 for a match, we want to return a boolean.
|
|
if ($matchResult === 1) {
|
|
$isPublicLinkUrl = true;
|
|
} else {
|
|
$isPublicLinkUrl = false;
|
|
}
|
|
return $isPublicLinkUrl;
|
|
}
|
|
|
|
/**
|
|
* Check that the status code in the saved response is the expected status
|
|
* code, or one of the expected status codes.
|
|
*
|
|
* @param int|int[]|string|string[] $expectedStatusCode
|
|
* @param string|null $message
|
|
* @param ResponseInterface|null $response
|
|
*
|
|
* @return void
|
|
*/
|
|
public function theHTTPStatusCodeShouldBe($expectedStatusCode, ?string $message = "", ?ResponseInterface $response = null): void {
|
|
$response = $response ?? $this->response;
|
|
$actualStatusCode = $response->getStatusCode();
|
|
if (\is_array($expectedStatusCode)) {
|
|
if ($message === "") {
|
|
$message = "HTTP status code $actualStatusCode is not one of the expected values " . \implode(" or ", $expectedStatusCode);
|
|
}
|
|
|
|
Assert::assertContainsEquals(
|
|
$actualStatusCode,
|
|
$expectedStatusCode,
|
|
$message
|
|
);
|
|
} else {
|
|
if ($message === "") {
|
|
$message = "HTTP status code $actualStatusCode is not the expected value $expectedStatusCode";
|
|
}
|
|
|
|
Assert::assertEquals(
|
|
$expectedStatusCode,
|
|
$actualStatusCode,
|
|
$message
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param PyStringNode|string $schemaString
|
|
*
|
|
* @return mixed
|
|
*/
|
|
public function getJSONSchema($schemaString) {
|
|
if (\gettype($schemaString) !== 'string') {
|
|
$schemaString = $schemaString->getRaw();
|
|
}
|
|
$schemaString = $this->substituteInLineCodes($schemaString);
|
|
$schema = \json_decode($schemaString);
|
|
Assert::assertNotNull($schema, 'schema is not valid JSON');
|
|
return $schema;
|
|
}
|
|
|
|
/**
|
|
* returns json decoded body content of a json response as an object
|
|
*
|
|
* @param ResponseInterface|null $response
|
|
*
|
|
* @return mixed
|
|
*/
|
|
public function getJsonDecodedResponseBodyContent(ResponseInterface $response = null): mixed {
|
|
$response = $response ?? $this->response;
|
|
$response->getBody()->rewind();
|
|
return HttpRequestHelper::getJsonDecodedResponseBodyContent($response);
|
|
}
|
|
|
|
/**
|
|
* @Then the ocs JSON data of the response should match
|
|
*
|
|
* @param PyStringNode $schemaString
|
|
*
|
|
* @return void
|
|
*
|
|
* @throws Exception
|
|
*/
|
|
public function theOcsDataOfTheResponseShouldMatch(
|
|
PyStringNode $schemaString
|
|
): void {
|
|
$jsonResponse = $this->getJsonDecodedResponseBodyContent();
|
|
$this->assertJsonDocumentMatchesSchema(
|
|
$jsonResponse->ocs->data,
|
|
$this->getJSONSchema($schemaString)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @Then the JSON data of the response should match
|
|
*
|
|
* @param PyStringNode $schemaString
|
|
*
|
|
* @return void
|
|
* @throws Exception
|
|
*/
|
|
public function theJsonDataOfTheResponseShouldMatch(PyStringNode $schemaString): void {
|
|
$responseBody = $this->getJsonDecodedResponseBodyContent();
|
|
$this->assertJsonDocumentMatchesSchema(
|
|
$responseBody,
|
|
$this->getJSONSchema($schemaString)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @Then /^the HTTP status code should be "([^"]*)"$/
|
|
*
|
|
* @param int|string $statusCode
|
|
*
|
|
* @return void
|
|
*/
|
|
public function thenTheHTTPStatusCodeShouldBe($statusCode): void {
|
|
$this->theHTTPStatusCodeShouldBe($statusCode);
|
|
}
|
|
|
|
/**
|
|
* @Then /^the HTTP status code should be "([^"]*)" or "([^"]*)"$/
|
|
*
|
|
* @param int|string $statusCode1
|
|
* @param int|string $statusCode2
|
|
*
|
|
* @return void
|
|
*/
|
|
public function theHTTPStatusCodeShouldBeOr($statusCode1, $statusCode2): void {
|
|
$this->theHTTPStatusCodeShouldBe(
|
|
[$statusCode1, $statusCode2]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @Then /^the HTTP status code should be between "(\d+)" and "(\d+)"$/
|
|
*
|
|
* @param int|string $minStatusCode
|
|
* @param int|string $maxStatusCode
|
|
* @param ResponseInterface|null $response
|
|
*
|
|
* @return void
|
|
*/
|
|
public function theHTTPStatusCodeShouldBeBetween(
|
|
$minStatusCode,
|
|
$maxStatusCode,
|
|
?ResponseInterface $response= null
|
|
): void {
|
|
$response = $response ?? $this->response;
|
|
$statusCode = $response->getStatusCode();
|
|
$message = "The HTTP status code $statusCode is not between $minStatusCode and $maxStatusCode";
|
|
Assert::assertGreaterThanOrEqual(
|
|
$minStatusCode,
|
|
$statusCode,
|
|
$message
|
|
);
|
|
Assert::assertLessThanOrEqual(
|
|
$maxStatusCode,
|
|
$statusCode,
|
|
$message
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @Then the HTTP status code should be failure
|
|
*
|
|
* @return void
|
|
*/
|
|
public function theHTTPStatusCodeShouldBeFailure(): void {
|
|
$statusCode = $this->response->getStatusCode();
|
|
$message = "The HTTP status code $statusCode is not greater than or equals to 400";
|
|
Assert::assertGreaterThanOrEqual(
|
|
400,
|
|
$statusCode,
|
|
$message
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param string $path
|
|
* @param string $filename
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function removeFile(string $path, string $filename): void {
|
|
if (\file_exists("$path$filename")) {
|
|
\unlink("$path$filename");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a file locally in the file system of the test runner
|
|
* The file will be available to upload to the server
|
|
*
|
|
* @param string $name
|
|
* @param string $size
|
|
* @param string $endData
|
|
*
|
|
* @return void
|
|
*/
|
|
public function createLocalFileOfSpecificSize(string $name, string $size, string $endData = 'a'): void {
|
|
$folder = $this->workStorageDirLocation();
|
|
if (!\is_dir($folder)) {
|
|
\mkDir($folder);
|
|
}
|
|
$file = \fopen($folder . $name, 'w');
|
|
\fseek($file, $size - \strlen($endData), SEEK_CUR);
|
|
\fwrite($file, $endData); // write the end data to force the file size
|
|
\fclose($file);
|
|
}
|
|
|
|
/**
|
|
* Make a directory under the server root on the ownCloud server
|
|
*
|
|
* @param string $dirPathFromServerRoot e.g. 'apps2/myapp/appinfo'
|
|
*
|
|
* @return void
|
|
* @throws Exception
|
|
* @throws GuzzleException
|
|
*/
|
|
public function mkDirOnServer(string $dirPathFromServerRoot): void {
|
|
SetupHelper::mkDirOnServer(
|
|
$dirPathFromServerRoot,
|
|
$this->getStepLineRef(),
|
|
$this->getBaseUrl(),
|
|
$this->getAdminUsername(),
|
|
$this->getAdminPassword()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getAdminUsername(): string {
|
|
return $this->adminUsername;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getAdminPassword(): string {
|
|
return $this->adminPassword;
|
|
}
|
|
|
|
/**
|
|
* @param string|null $userName
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getPasswordForUser(?string $userName): string {
|
|
$userNameNormalized = $this->normalizeUsername($userName);
|
|
$username = $this->getActualUsername($userNameNormalized);
|
|
if ($username === $this->getAdminUsername()) {
|
|
return $this->getAdminPassword();
|
|
} elseif (\array_key_exists($username, $this->createdUsers)) {
|
|
return (string)$this->createdUsers[$username]['password'];
|
|
} elseif (\array_key_exists($username, $this->createdRemoteUsers)) {
|
|
return (string)$this->createdRemoteUsers[$username]['password'];
|
|
}
|
|
|
|
// The user has not been created yet, see if there is a replacement
|
|
// defined for the user.
|
|
$usernameReplacements = $this->usersToBeReplaced();
|
|
if (isset($usernameReplacements)) {
|
|
if (isset($usernameReplacements[$userNameNormalized])) {
|
|
return $usernameReplacements[$userNameNormalized]['password'];
|
|
}
|
|
}
|
|
|
|
// Fall back to the default password used for the well-known users.
|
|
if ($username === 'regularuser') {
|
|
return $this->regularUserPassword;
|
|
} elseif ($username === 'alice') {
|
|
return $this->regularUserPassword;
|
|
} elseif ($username === 'brian') {
|
|
return $this->alt1UserPassword;
|
|
} elseif ($username === 'carol') {
|
|
return $this->alt2UserPassword;
|
|
} elseif ($username === 'david') {
|
|
return $this->alt3UserPassword;
|
|
} elseif ($username === 'emily') {
|
|
return $this->alt4UserPassword;
|
|
} elseif ($username === 'usergrp') {
|
|
return $this->regularUserPassword;
|
|
} elseif ($username === 'sharee1') {
|
|
return $this->regularUserPassword;
|
|
}
|
|
|
|
// The user has not been created yet and is not one of the pre-known
|
|
// users. So let the caller have the default password.
|
|
return (string)$this->getActualPassword($this->regularUserPassword);
|
|
}
|
|
|
|
/**
|
|
* @param string $username
|
|
* @param string $password
|
|
*
|
|
* @return void
|
|
* @throws Exception
|
|
*/
|
|
public function updateUserPassword(string $username, string $password): void {
|
|
$username = $this->normalizeUsername($username);
|
|
if ($username === $this->getAdminUsername()) {
|
|
$this->adminPassword = $password;
|
|
} elseif (\array_key_exists($username, $this->createdUsers)) {
|
|
$this->createdUsers[$username]['password'] = $password;
|
|
} else {
|
|
throw new Exception("User '$username' not found");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the display name of the user.
|
|
*
|
|
* For users that have already been created, return their display name.
|
|
* For special known usernames, return the display name that is also used by LDAP tests.
|
|
* For other users, return null. They will not be assigned any particular
|
|
* display name by this function.
|
|
*
|
|
* @param string $userName
|
|
*
|
|
* @return string|null
|
|
*/
|
|
public function getDisplayNameForUser(string $userName): ?string {
|
|
$userNameNormalized = $this->normalizeUsername($userName);
|
|
$username = $this->getActualUsername($userNameNormalized);
|
|
if (\array_key_exists($username, $this->createdUsers)) {
|
|
if (isset($this->createdUsers[$username]['displayname'])) {
|
|
return (string)$this->createdUsers[$username]['displayname'];
|
|
}
|
|
return $userName;
|
|
}
|
|
if (\array_key_exists($username, $this->createdRemoteUsers)) {
|
|
if (isset($this->createdRemoteUsers[$username]['displayname'])) {
|
|
return (string)$this->createdRemoteUsers[$username]['displayname'];
|
|
}
|
|
return $userName;
|
|
}
|
|
|
|
// The user has not been created yet, see if there is a replacement
|
|
// defined for the user.
|
|
$usernameReplacements = $this->usersToBeReplaced();
|
|
if (isset($usernameReplacements)) {
|
|
if (isset($usernameReplacements[$userNameNormalized])) {
|
|
return $usernameReplacements[$userNameNormalized]['displayname'];
|
|
} elseif (isset($usernameReplacements[$userName])) {
|
|
return $usernameReplacements[$userName]['displayname'];
|
|
}
|
|
}
|
|
|
|
// Fall back to the default display name used for the well-known users.
|
|
if ($username === 'regularuser') {
|
|
return 'Regular User';
|
|
} elseif ($username === 'alice') {
|
|
return 'Alice Hansen';
|
|
} elseif ($username === 'brian') {
|
|
return 'Brian Murphy';
|
|
} elseif ($username === 'carol') {
|
|
return 'Carol King';
|
|
} elseif ($username === 'david') {
|
|
return 'David Lopez';
|
|
} elseif ($username === 'emily') {
|
|
return 'Emily Wagner';
|
|
} elseif ($username === 'usergrp') {
|
|
return 'User Grp';
|
|
} elseif ($username === 'sharee1') {
|
|
return 'Sharee One';
|
|
} elseif ($username === 'sharee2') {
|
|
return 'Sharee Two';
|
|
} elseif (\in_array($username, ["grp1", "***redacted***"])) {
|
|
return $username;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get the email address of the user.
|
|
*
|
|
* For users that have already been created, return their email address.
|
|
* For special known usernames, return the email address that is also used by LDAP tests.
|
|
* For other users, return null. They will not be assigned any particular
|
|
* email address by this function.
|
|
*
|
|
* @param string $userName
|
|
*
|
|
* @return string|null
|
|
*/
|
|
public function getEmailAddressForUser(string $userName): ?string {
|
|
$userNameNormalized = $this->normalizeUsername($userName);
|
|
$username = $this->getActualUsername($userNameNormalized);
|
|
if (\array_key_exists($username, $this->createdUsers)) {
|
|
return (string)$this->createdUsers[$username]['email'];
|
|
}
|
|
if (\array_key_exists($username, $this->createdRemoteUsers)) {
|
|
return (string)$this->createdRemoteUsers[$username]['email'];
|
|
}
|
|
|
|
// The user has not been created yet, see if there is a replacement
|
|
// defined for the user.
|
|
$usernameReplacements = $this->usersToBeReplaced();
|
|
if (isset($usernameReplacements)) {
|
|
if (isset($usernameReplacements[$userNameNormalized])) {
|
|
return $usernameReplacements[$userNameNormalized]['email'];
|
|
} elseif (isset($usernameReplacements[$userName])) {
|
|
return $usernameReplacements[$userName]['email'];
|
|
}
|
|
}
|
|
|
|
// Fall back to the default display name used for the well-known users.
|
|
if ($username === 'regularuser') {
|
|
return 'regularuser@example.org';
|
|
} elseif ($username === 'alice') {
|
|
return 'alice@example.org';
|
|
} elseif ($username === 'brian') {
|
|
return 'brian@example.org';
|
|
} elseif ($username === 'carol') {
|
|
return 'carol@example.org';
|
|
} elseif ($username === 'david') {
|
|
return 'david@example.org';
|
|
} elseif ($username === 'emily') {
|
|
return 'emily@example.org';
|
|
} elseif ($username === 'usergrp') {
|
|
return 'usergrp@example.org';
|
|
} elseif ($username === 'sharee1') {
|
|
return 'sharee1@example.org';
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// TODO do similar for other usernames for e.g. %regularuser% or %test-user-1%
|
|
|
|
/**
|
|
* @param string|null $functionalUsername
|
|
*
|
|
* @return string|null
|
|
* @throws JsonException
|
|
*/
|
|
public function getActualUsername(?string $functionalUsername): ?string {
|
|
if ($functionalUsername === null) {
|
|
return null;
|
|
}
|
|
$usernames = $this->usersToBeReplaced();
|
|
if (isset($usernames)) {
|
|
if (isset($usernames[$functionalUsername])) {
|
|
return $usernames[$functionalUsername]['username'];
|
|
}
|
|
$normalizedUsername = $this->normalizeUsername($functionalUsername);
|
|
if (isset($usernames[$normalizedUsername])) {
|
|
return $usernames[$normalizedUsername]['username'];
|
|
}
|
|
}
|
|
if ($functionalUsername === "%admin%") {
|
|
return $this->getAdminUsername();
|
|
}
|
|
return $functionalUsername;
|
|
}
|
|
|
|
/**
|
|
* @param string|null $functionalPassword
|
|
*
|
|
* @return string|null
|
|
*/
|
|
public function getActualPassword(?string $functionalPassword): ?string {
|
|
if ($functionalPassword === "%regular%") {
|
|
return $this->regularUserPassword;
|
|
} elseif ($functionalPassword === "%alt1%") {
|
|
return $this->alt1UserPassword;
|
|
} elseif ($functionalPassword === "%alt2%") {
|
|
return $this->alt2UserPassword;
|
|
} elseif ($functionalPassword === "%alt3%") {
|
|
return $this->alt3UserPassword;
|
|
} elseif ($functionalPassword === "%alt4%") {
|
|
return $this->alt4UserPassword;
|
|
} elseif ($functionalPassword === "%subadmin%") {
|
|
return $this->subAdminPassword;
|
|
} elseif ($functionalPassword === "%admin%") {
|
|
return $this->getAdminPassword();
|
|
} elseif ($functionalPassword === "%altadmin%") {
|
|
return $this->alternateAdminPassword;
|
|
} elseif ($functionalPassword === "%public%") {
|
|
return $this->publicLinkSharePassword;
|
|
} elseif ($functionalPassword === "%remove%") {
|
|
return "";
|
|
} else {
|
|
return $functionalPassword;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @When the administrator requests status.php
|
|
*
|
|
* @return void
|
|
*/
|
|
public function theAdministratorRequestsStatusPhp(): void {
|
|
$this->response = $this->getStatusPhp();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @return ResponseInterface
|
|
*/
|
|
public function getStatusPhp(): ResponseInterface {
|
|
$fullUrl = $this->getBaseUrl() . "/status.php";
|
|
|
|
$config = null;
|
|
if ($this->sourceIpAddress !== null) {
|
|
$config = [
|
|
'curl' => [
|
|
CURLOPT_INTERFACE => $this->sourceIpAddress
|
|
]
|
|
];
|
|
}
|
|
|
|
return HttpRequestHelper::get(
|
|
$fullUrl,
|
|
$this->getStepLineRef(),
|
|
$this->getAdminUsername(),
|
|
$this->getAdminPassword(),
|
|
$this->guzzleClientHeaders,
|
|
null,
|
|
$config
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param ResponseInterface|null $response
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getJsonDecodedResponse(?ResponseInterface $response = null): array {
|
|
if ($response === null) {
|
|
$response = $this->getResponse();
|
|
}
|
|
return \json_decode(
|
|
(string)$response->getBody(),
|
|
true
|
|
);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getJsonDecodedStatusPhp(): array {
|
|
return $this->getJsonDecodedResponse(
|
|
$this->getStatusPhp()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getEditionFromStatus(): string {
|
|
$decodedResponse = $this->getJsonDecodedStatusPhp();
|
|
if (isset($decodedResponse['edition'])) {
|
|
return $decodedResponse['edition'];
|
|
}
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* @return string|null
|
|
*/
|
|
public function getProductNameFromStatus(): ?string {
|
|
$decodedResponse = $this->getJsonDecodedStatusPhp();
|
|
if (isset($decodedResponse['productname'])) {
|
|
return $decodedResponse['productname'];
|
|
}
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* @return string|null
|
|
*/
|
|
public function getVersionFromStatus(): ?string {
|
|
$decodedResponse = $this->getJsonDecodedStatusPhp();
|
|
if (isset($decodedResponse['version'])) {
|
|
return $decodedResponse['version'];
|
|
}
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* @return string|null
|
|
*/
|
|
public function getVersionStringFromStatus(): ?string {
|
|
$decodedResponse = $this->getJsonDecodedStatusPhp();
|
|
if (isset($decodedResponse['versionstring'])) {
|
|
return $decodedResponse['versionstring'];
|
|
}
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* returns a string that can be used to check a URL of comments with
|
|
* regular expression (without delimiter)
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getCommentUrlRegExp(): string {
|
|
$basePath = \ltrim($this->getBasePath() . "/", "/");
|
|
return "/{$basePath}remote.php/dav/comments/files/([0-9]+)";
|
|
}
|
|
|
|
/**
|
|
* substitutes codes like %base_url% with the value
|
|
* if the given value does not have anything to be substituted
|
|
* then it is returned unmodified
|
|
*
|
|
* @param string|null $value
|
|
* @param string|null $user
|
|
* @param array|null $functions associative array of functions and parameters to be
|
|
* called on every replacement string before the
|
|
* replacement
|
|
* function name has to be the key and the parameters an
|
|
* own array
|
|
* the replacement itself will be used as first parameter
|
|
* e.g. substituteInLineCodes($value, ['preg_quote' => ['/']])
|
|
* @param array|null $additionalSubstitutions
|
|
* array of additional substitution configurations
|
|
* [
|
|
* [
|
|
* "code" => "%my_code%",
|
|
* "function" => [
|
|
* $myClass,
|
|
* "myFunction"
|
|
* ],
|
|
* "parameter" => []
|
|
* ],
|
|
* ]
|
|
* @param string|null $group
|
|
* @param string|null $userName
|
|
*
|
|
* @return string
|
|
*/
|
|
public function substituteInLineCodes(
|
|
?string $value,
|
|
?string $user = null,
|
|
?array $functions = [],
|
|
?array $additionalSubstitutions = [],
|
|
?string $group = null,
|
|
?string $userName = null
|
|
): ?string {
|
|
$substitutions = [
|
|
[
|
|
"code" => "%base_url%",
|
|
"function" => [
|
|
$this,
|
|
"getBaseUrl"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%storage_path%",
|
|
"function" => [
|
|
$this,
|
|
"getStorageUsersRoot"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%base_url_without_scheme%",
|
|
"function" => [
|
|
$this,
|
|
"getBaseUrlWithoutScheme"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%remote_server%",
|
|
"function" => [
|
|
$this,
|
|
"getRemoteBaseUrl"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%remote_server_without_scheme%",
|
|
"function" => [
|
|
$this,
|
|
"getRemoteBaseUrlWithoutScheme"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%local_server%",
|
|
"function" => [
|
|
$this,
|
|
"getLocalBaseUrl"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%local_server_without_scheme%",
|
|
"function" => [
|
|
$this,
|
|
"getLocalBaseUrlWithoutScheme"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%base_path%",
|
|
"function" => [
|
|
$this,
|
|
"getBasePath"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%dav_path%",
|
|
"function" => [
|
|
$this,
|
|
"getDAVPathIncludingBasePath"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%ocs_path_v1%",
|
|
"function" => [
|
|
$this,
|
|
"getOCSPath"
|
|
],
|
|
"parameter" => ["1"]
|
|
],
|
|
[
|
|
"code" => "%ocs_path_v2%",
|
|
"function" => [
|
|
$this,
|
|
"getOCSPath"
|
|
],
|
|
"parameter" => ["2"]
|
|
],
|
|
[
|
|
"code" => "%productname%",
|
|
"function" => [
|
|
$this,
|
|
"getProductNameFromStatus"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%edition%",
|
|
"function" => [
|
|
$this,
|
|
"getEditionFromStatus"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%version%",
|
|
"function" => [
|
|
$this,
|
|
"getVersionFromStatus"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%versionstring%",
|
|
"function" => [
|
|
$this,
|
|
"getVersionStringFromStatus"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%a_comment_url%",
|
|
"function" => [
|
|
$this,
|
|
"getCommentUrlRegExp"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%last_share_id%",
|
|
"function" => [
|
|
$this,
|
|
"getLastCreatedUserGroupShareId"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%last_public_share_token%",
|
|
"function" => [
|
|
$this,
|
|
"getLastCreatedPublicShareToken"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%user_id%",
|
|
"function" => [
|
|
$this, "getUserIdByUserName"
|
|
],
|
|
"parameter" => [$userName]
|
|
],
|
|
[
|
|
"code" => "%group_id%",
|
|
"function" => [
|
|
$this, "getGroupIdByGroupName"
|
|
],
|
|
"parameter" => [$group]
|
|
],
|
|
[
|
|
"code" => "%user_id_pattern%",
|
|
"function" => [
|
|
__NAMESPACE__ . '\TestHelpers\GraphHelper',
|
|
"getUUIDv4Regex"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%group_id_pattern%",
|
|
"function" => [
|
|
__NAMESPACE__ . '\TestHelpers\GraphHelper',
|
|
"getUUIDv4Regex"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%role_id_pattern%",
|
|
"function" => [
|
|
__NAMESPACE__ . '\TestHelpers\GraphHelper',
|
|
"getUUIDv4Regex"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%permissions_id_pattern%",
|
|
"function" => [
|
|
__NAMESPACE__ . '\TestHelpers\GraphHelper',
|
|
"getPermissionsIdRegex"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%file_id_pattern%",
|
|
"function" => [
|
|
__NAMESPACE__ . '\TestHelpers\GraphHelper',
|
|
"getFileIdRegex"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%space_id_pattern%",
|
|
"function" => [
|
|
__NAMESPACE__ . '\TestHelpers\GraphHelper',
|
|
"getSpaceIdRegex"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%share_id_pattern%",
|
|
"function" => [
|
|
__NAMESPACE__ . '\TestHelpers\GraphHelper',
|
|
"getShareIdRegex"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%etag_pattern%",
|
|
"function" => [
|
|
__NAMESPACE__ . '\TestHelpers\GraphHelper',
|
|
"getEtagRegex"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%tus_upload_location%",
|
|
"function" => [
|
|
$this->tusContext,
|
|
"getTusResourceLocation"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%fed_invitation_token_pattern%",
|
|
"function" => [
|
|
__NAMESPACE__ . '\TestHelpers\GraphHelper',
|
|
"getUUIDv4Regex"
|
|
],
|
|
"parameter" => []
|
|
],
|
|
[
|
|
"code" => "%identities_issuer_id_pattern%",
|
|
"function" => [
|
|
__NAMESPACE__ . '\TestHelpers\GraphHelper',
|
|
"getUUIDv4Regex"
|
|
],
|
|
"parameter" => []
|
|
]
|
|
];
|
|
if ($user !== null) {
|
|
array_push(
|
|
$substitutions,
|
|
[
|
|
"code" => "%username%",
|
|
"function" => [
|
|
$this,
|
|
"getActualUsername"
|
|
],
|
|
"parameter" => [$user]
|
|
],
|
|
[
|
|
"code" => "%displayname%",
|
|
"function" => [
|
|
$this,
|
|
"getDisplayNameForUser"
|
|
],
|
|
"parameter" => [$user]
|
|
],
|
|
[
|
|
"code" => "%password%",
|
|
"function" => [
|
|
$this,
|
|
"getPasswordForUser"
|
|
],
|
|
"parameter" => [$user]
|
|
],
|
|
[
|
|
"code" => "%emailaddress%",
|
|
"function" => [
|
|
$this,
|
|
"getEmailAddressForUser"
|
|
],
|
|
"parameter" => [$user]
|
|
],
|
|
[
|
|
"code" => "%spaceid%",
|
|
"function" => [
|
|
$this,
|
|
"getPersonalSpaceIdForUser",
|
|
],
|
|
"parameter" => [$user, true]
|
|
],
|
|
[
|
|
"code" => "%user_id%",
|
|
"function" =>
|
|
[$this, "getUserIdByUserName"],
|
|
"parameter" => [$userName]
|
|
],
|
|
[
|
|
"code" => "%group_id%",
|
|
"function" =>
|
|
[$this, "getGroupIdByGroupName"],
|
|
"parameter" => [$group]
|
|
]
|
|
);
|
|
|
|
if (!OcisHelper::isTestingOnReva()) {
|
|
array_push(
|
|
$substitutions,
|
|
[
|
|
"code" => "%shares_drive_id%",
|
|
"function" => [
|
|
$this->spacesContext,
|
|
"getSpaceIdByName"
|
|
],
|
|
"parameter" => [$user, "Shares"]
|
|
]
|
|
);
|
|
}
|
|
}
|
|
|
|
if (!empty($additionalSubstitutions)) {
|
|
$substitutions = \array_merge($substitutions, $additionalSubstitutions);
|
|
}
|
|
|
|
foreach ($substitutions as $substitution) {
|
|
if (strpos($value, $substitution['code']) === false) {
|
|
continue;
|
|
}
|
|
|
|
$replacement = \call_user_func_array(
|
|
$substitution["function"],
|
|
$substitution["parameter"]
|
|
);
|
|
foreach ($functions as $function => $parameters) {
|
|
$replacement = \call_user_func_array(
|
|
$function,
|
|
\array_merge([$replacement], $parameters)
|
|
);
|
|
}
|
|
$value = \str_replace(
|
|
$substitution["code"],
|
|
$replacement,
|
|
$value
|
|
);
|
|
}
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* returns personal space id for user if the test is using the spaces dav path
|
|
* or if alwaysDoIt is set to true,
|
|
* otherwise it returns null.
|
|
*
|
|
* @param string $user
|
|
* @param bool $alwaysDoIt default false. Set to true
|
|
*
|
|
* @return string|null
|
|
* @throws GuzzleException
|
|
*/
|
|
public function getPersonalSpaceIdForUser(string $user, bool $alwaysDoIt = false): ?string {
|
|
if ($alwaysDoIt || ($this->getDavPathVersion() === WebDavHelper::DAV_VERSION_SPACES)) {
|
|
return WebDavHelper::getPersonalSpaceIdForUserOrFakeIfNotFound(
|
|
$this->getBaseUrl(),
|
|
$user,
|
|
$this->getPasswordForUser($user),
|
|
$this->getStepLineRef()
|
|
);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function temporaryStorageSubfolderName(): string {
|
|
return "work_tmp";
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function acceptanceTestsDirLocation(): string {
|
|
return \dirname(__FILE__) . "/../";
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function workStorageDirLocation(): string {
|
|
return $this->acceptanceTestsDirLocation() . $this->temporaryStorageSubfolderName() . "/";
|
|
}
|
|
|
|
/**
|
|
* Parse list of config keys from the given XML response
|
|
*
|
|
* @param SimpleXMLElement $responseXml
|
|
*
|
|
* @return array
|
|
*/
|
|
public function parseConfigListFromResponseXml(SimpleXMLElement $responseXml): array {
|
|
$configkeyData = \json_decode(\json_encode($responseXml->data), true);
|
|
if (isset($configkeyData['element'])) {
|
|
$configkeyData = $configkeyData['element'];
|
|
} else {
|
|
// There are no keys for the app
|
|
return [];
|
|
}
|
|
if (isset($configkeyData[0])) {
|
|
$configkeyValues = $configkeyData;
|
|
} else {
|
|
// There is just 1 key for the app
|
|
$configkeyValues[0] = $configkeyData;
|
|
}
|
|
return $configkeyValues;
|
|
}
|
|
|
|
/**
|
|
* This will run before EVERY scenario.
|
|
* It will set the properties for this object.
|
|
*
|
|
* @BeforeScenario
|
|
*
|
|
* @param BeforeScenarioScope $scope
|
|
*
|
|
* @return void
|
|
* @throws Exception
|
|
*/
|
|
public function before(BeforeScenarioScope $scope): void {
|
|
$this->scenarioStartTime = \time();
|
|
// Get the environment
|
|
$environment = $scope->getEnvironment();
|
|
// registers context in every suite, as every suite has FeatureContext
|
|
// that calls BasicStructure.php
|
|
$this->ocsContext = new OCSContext();
|
|
$this->authContext = new AuthContext();
|
|
$this->tusContext = new TUSContext();
|
|
$this->ocsContext->before($scope);
|
|
$this->authContext->setUpScenario($scope);
|
|
$this->tusContext->setUpScenario($scope);
|
|
$environment->registerContext($this->ocsContext);
|
|
$environment->registerContext($this->authContext);
|
|
$environment->registerContext($this->tusContext);
|
|
$scenarioLine = $scope->getScenario()->getLine();
|
|
$featureFile = $scope->getFeature()->getFile();
|
|
$suiteName = $scope->getSuite()->getName();
|
|
$featureFileName = \basename($featureFile);
|
|
|
|
if (!OcisHelper::isTestingOnReva()) {
|
|
$this->spacesContext = new SpacesContext();
|
|
$this->spacesContext->setUpScenario($scope);
|
|
$environment->registerContext($this->spacesContext);
|
|
}
|
|
|
|
if ($this->sendScenarioLineReferencesInXRequestId()) {
|
|
$this->scenarioString = $suiteName . '/' . $featureFileName . ':' . $scenarioLine;
|
|
} else {
|
|
$this->scenarioString = '';
|
|
}
|
|
|
|
// Initialize SetupHelper
|
|
SetupHelper::init(
|
|
$this->getAdminUsername(),
|
|
$this->getAdminPassword(),
|
|
$this->getBaseUrl(),
|
|
$this->getOcPath()
|
|
);
|
|
|
|
if ($this->isTestingWithLdap()) {
|
|
$suiteParameters = SetupHelper::getSuiteParameters($scope);
|
|
$this->connectToLdap($suiteParameters);
|
|
}
|
|
|
|
$this->graphContext = new GraphContext();
|
|
$this->graphContext->before($scope);
|
|
$environment->registerContext($this->graphContext);
|
|
}
|
|
|
|
/**
|
|
* This will run before EVERY step.
|
|
*
|
|
* @BeforeStep
|
|
*
|
|
* @param BeforeStepScope $scope
|
|
*
|
|
* @return void
|
|
*/
|
|
public function beforeEachStep(BeforeStepScope $scope): void {
|
|
if ($this->sendScenarioLineReferencesInXRequestId()) {
|
|
$this->stepLineRef = $this->scenarioString . '-' . $scope->getStep()->getLine();
|
|
} else {
|
|
$this->stepLineRef = '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @AfterScenario
|
|
*
|
|
* @return void
|
|
*/
|
|
public function restoreAdminPassword(): void {
|
|
if ($this->adminPassword !== $this->originalAdminPassword) {
|
|
$this->resetUserPasswordAsAdminUsingTheProvisioningApi(
|
|
$this->getAdminUsername(),
|
|
$this->originalAdminPassword
|
|
);
|
|
$this->adminPassword = $this->originalAdminPassword;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @AfterScenario
|
|
*
|
|
* @return void
|
|
*/
|
|
public function deleteAllResourceCreatedByAdmin(): void {
|
|
foreach ($this->adminResources as $resource) {
|
|
$this->deleteFile("admin", $resource);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @BeforeScenario @temporary_storage_on_server
|
|
*
|
|
* @return void
|
|
* @throws Exception
|
|
*/
|
|
public function makeTemporaryStorageOnServerBefore(): void {
|
|
$this->mkDirOnServer(
|
|
TEMPORARY_STORAGE_DIR_ON_REMOTE_SERVER
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @AfterScenario @temporary_storage_on_server
|
|
*
|
|
* @return void
|
|
* @throws Exception
|
|
*/
|
|
public function removeTemporaryStorageOnServerAfter(): void {
|
|
SetupHelper::rmDirOnServer(
|
|
TEMPORARY_STORAGE_DIR_ON_REMOTE_SERVER,
|
|
$this->getStepLineRef()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @AfterScenario
|
|
*
|
|
* @return void
|
|
*/
|
|
public function removeCreatedFilesAfter(): void {
|
|
foreach ($this->createdFiles as $file) {
|
|
\unlink($file);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @AfterScenario
|
|
*
|
|
* clear space id reference
|
|
*
|
|
* @return void
|
|
* @throws Exception
|
|
*/
|
|
public function clearSpaceId(): void {
|
|
if (\count(WebDavHelper::$spacesIdRef) > 0) {
|
|
WebDavHelper::$spacesIdRef = [];
|
|
}
|
|
WebDavHelper::$SPACE_ID_FROM_OCIS = '';
|
|
}
|
|
|
|
/**
|
|
* Verify that the tableNode contains expected headers
|
|
*
|
|
* @param TableNode|null $table
|
|
* @param array|null $requiredHeader
|
|
* @param array|null $allowedHeader
|
|
*
|
|
* @return void
|
|
* @throws Exception
|
|
*/
|
|
public function verifyTableNodeColumns(?TableNode $table, ?array $requiredHeader = [], ?array $allowedHeader = []): void {
|
|
if ($table === null || \count($table->getHash()) < 1) {
|
|
throw new Exception("Table should have at least one row.");
|
|
}
|
|
$tableHeaders = $table->getRows()[0];
|
|
$allowedHeader = \array_unique(\array_merge($requiredHeader, $allowedHeader));
|
|
if ($requiredHeader != []) {
|
|
foreach ($requiredHeader as $element) {
|
|
if (!\in_array($element, $tableHeaders)) {
|
|
throw new Exception("Row with header '$element' expected to be in table but not found");
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($allowedHeader != []) {
|
|
foreach ($tableHeaders as $element) {
|
|
if (!\in_array($element, $allowedHeader)) {
|
|
throw new Exception("Row with header '$element' is not allowed in table but found");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Verify that the tableNode contains expected rows
|
|
*
|
|
* @param TableNode $table
|
|
* @param array $requiredRows
|
|
* @param array $allowedRows
|
|
*
|
|
* @return void
|
|
* @throws Exception
|
|
*/
|
|
public function verifyTableNodeRows(TableNode $table, array $requiredRows = [], array $allowedRows = []): void {
|
|
if (\count($table->getRows()) < 1) {
|
|
throw new Exception("Table should have at least one row.");
|
|
}
|
|
$tableHeaders = $table->getColumn(0);
|
|
$allowedRows = \array_unique(\array_merge($requiredRows, $allowedRows));
|
|
if ($requiredRows != []) {
|
|
foreach ($requiredRows as $element) {
|
|
if (!\in_array($element, $tableHeaders)) {
|
|
throw new Exception("Row with name '$element' expected to be in table but not found");
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($allowedRows != []) {
|
|
foreach ($tableHeaders as $element) {
|
|
if (!\in_array($element, $allowedRows)) {
|
|
throw new Exception("Row with name '$element' is not allowed in table but found");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Verify that the tableNode contains expected number of columns
|
|
*
|
|
* @param TableNode $table
|
|
* @param int $count
|
|
*
|
|
* @return void
|
|
* @throws Exception
|
|
*/
|
|
public function verifyTableNodeColumnsCount(TableNode $table, int $count): void {
|
|
if (\count($table->getRows()) < 1) {
|
|
throw new Exception("Table should have at least one row.");
|
|
}
|
|
$rowCount = \count($table->getRows()[0]);
|
|
if ($count !== $rowCount) {
|
|
throw new Exception("Table expected to have $count rows but found $rowCount");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param string $method http request method
|
|
* @param string $property property in form d:getetag
|
|
* if property is `doesnotmatter` body is also set `doesnotmatter`
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getBodyForOCSRequest(string $method, string $property): ?string {
|
|
$body = null;
|
|
if ($method === 'PROPFIND') {
|
|
$body = '<?xml version="1.0"?><d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns"><d:prop><' . $property . '/></d:prop></d:propfind>';
|
|
} elseif ($method === 'LOCK') {
|
|
$body = "<?xml version='1.0' encoding='UTF-8'?><d:lockinfo xmlns:d='DAV:'> <d:lockscope><" . $property . " /></d:lockscope></d:lockinfo>";
|
|
} elseif ($method === 'PROPPATCH') {
|
|
if ($property === 'favorite') {
|
|
$property = '<oc:favorite xmlns:oc="http://owncloud.org/ns">1</oc:favorite>';
|
|
}
|
|
$body = '<?xml version="1.0"?><d:propertyupdate xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns"><d:set><d:prop>' . $property . '</d:prop></d:set></d:propertyupdate>';
|
|
}
|
|
if ($property === '') {
|
|
$body = '';
|
|
}
|
|
return $body;
|
|
}
|
|
|
|
/**
|
|
* The method returns userId
|
|
*
|
|
* @param string $userName
|
|
*
|
|
* @return string
|
|
* @throws Exception|GuzzleException
|
|
*/
|
|
public function getUserIdByUserName(string $userName): string {
|
|
$response = GraphHelper::getUser(
|
|
$this->getBaseUrl(),
|
|
$this->getStepLineRef(),
|
|
$this->getAdminUsername(),
|
|
$this->getAdminPassword(),
|
|
$userName
|
|
);
|
|
$data = \json_decode($response->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR);
|
|
if (isset($data["id"])) {
|
|
return $data["id"];
|
|
} else {
|
|
throw new Exception(__METHOD__ . " accounts-list is empty");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The method returns groupId
|
|
*
|
|
* @param string $groupName
|
|
*
|
|
* @return string
|
|
* @throws Exception|GuzzleException
|
|
*/
|
|
public function getGroupIdByGroupName(string $groupName):string {
|
|
$response = GraphHelper::getGroup(
|
|
$this->getBaseUrl(),
|
|
$this->getStepLineRef(),
|
|
$this->getAdminUsername(),
|
|
$this->getAdminPassword(),
|
|
$groupName
|
|
);
|
|
$data = $this->getJsonDecodedResponse($response);
|
|
if (isset($data["id"])) {
|
|
return $data["id"];
|
|
} else {
|
|
throw new Exception(__METHOD__ . " accounts-list is empty");
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @AfterSuite
|
|
*
|
|
* @return void
|
|
* @throws Exception
|
|
*/
|
|
public static function clearScenarioLog(): void {
|
|
if (\file_exists(HttpLogger::getScenarioLogPath())) {
|
|
\unlink(HttpLogger::getScenarioLogPath());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Log request and response logs if scenario fails
|
|
*
|
|
* @AfterScenario
|
|
*
|
|
* @param AfterScenarioScope $scope
|
|
*
|
|
* @return void
|
|
* @throws Exception
|
|
*/
|
|
public static function checkScenario(AfterScenarioScope $scope): void {
|
|
if (($scope->getTestResult()->getResultCode() !== 0)
|
|
&& (!self::isExpectedToFail(self::getScenarioLine($scope)))
|
|
) {
|
|
$logs = \file_get_contents(HttpLogger::getScenarioLogPath());
|
|
// add new lines
|
|
$logs = \rtrim($logs, "\n") . "\n\n\n";
|
|
HttpLogger::writeLog(HttpLogger::getFailedLogPath(), $logs);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param BeforeScenarioScope|AfterScenarioScope $scope
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function getScenarioLine($scope): string {
|
|
$feature = $scope->getFeature()->getFile();
|
|
$feature = \explode('/', $feature);
|
|
$feature = \array_slice($feature, -2);
|
|
$feature = \implode('/', $feature);
|
|
$scenarioLine = $scope->getScenario()->getLine();
|
|
// Example: apiGraph/createUser.feature:24
|
|
return $feature . ':' . $scenarioLine;
|
|
}
|
|
|
|
/**
|
|
* @param string $scenarioLine
|
|
*
|
|
* @return bool
|
|
*/
|
|
public static function isExpectedToFail(string $scenarioLine): bool {
|
|
$expectedFailFile = \getenv('EXPECTED_FAILURES_FILE');
|
|
if (!$expectedFailFile) {
|
|
$expectedFailFile = __DIR__ . '/../expected-failures-localAPI-on-OCIS-storage.md';
|
|
if (\strpos($scenarioLine, "coreApi") === 0) {
|
|
$expectedFailFile = __DIR__ . '/../expected-failures-API-on-OCIS-storage.md';
|
|
}
|
|
}
|
|
|
|
$reader = \fopen($expectedFailFile, 'r');
|
|
if ($reader) {
|
|
while (($line = \fgets($reader)) !== false) {
|
|
if (\strpos($line, $scenarioLine) !== false) {
|
|
\fclose($reader);
|
|
return true;
|
|
}
|
|
}
|
|
\fclose($reader);
|
|
}
|
|
return false;
|
|
}
|
|
}
|