diff options
Diffstat (limited to 'build/integration/features/bootstrap/BasicStructure.php')
-rw-r--r-- | build/integration/features/bootstrap/BasicStructure.php | 404 |
1 files changed, 333 insertions, 71 deletions
diff --git a/build/integration/features/bootstrap/BasicStructure.php b/build/integration/features/bootstrap/BasicStructure.php index d2aed82055a..59a4312913e 100644 --- a/build/integration/features/bootstrap/BasicStructure.php +++ b/build/integration/features/bootstrap/BasicStructure.php @@ -1,11 +1,27 @@ <?php +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +use Behat\Gherkin\Node\TableNode; use GuzzleHttp\Client; -use GuzzleHttp\Message\ResponseInterface; +use GuzzleHttp\Cookie\CookieJar; +use GuzzleHttp\Exception\ClientException; +use GuzzleHttp\Exception\ServerException; +use PHPUnit\Framework\Assert; +use Psr\Http\Message\ResponseInterface; require __DIR__ . '/../../vendor/autoload.php'; trait BasicStructure { + use Auth; + use Avatar; + use Download; + use Mail; + use Theming; + /** @var string */ private $currentUser = ''; @@ -21,22 +37,26 @@ trait BasicStructure { /** @var ResponseInterface */ private $response = null; - /** @var \GuzzleHttp\Cookie\CookieJar */ + /** @var CookieJar */ private $cookieJar; /** @var string */ - private $requesttoken; + private $requestToken; - public function __construct($baseUrl, $admin, $regular_user_password) { + protected $adminUser; + protected $regularUser; + protected $localBaseUrl; + protected $remoteBaseUrl; + public function __construct($baseUrl, $admin, $regular_user_password) { // Initialize your context here $this->baseUrl = $baseUrl; $this->adminUser = $admin; $this->regularUser = $regular_user_password; - $this->localBaseUrl = substr($this->baseUrl, 0, -4); - $this->remoteBaseUrl = substr($this->baseUrl, 0, -4); + $this->localBaseUrl = $this->baseUrl; + $this->remoteBaseUrl = $this->baseUrl; $this->currentServer = 'LOCAL'; - $this->cookieJar = new \GuzzleHttp\Cookie\CookieJar(); + $this->cookieJar = new CookieJar(); // in case of ci deployment we take the server url from the environment $testServerUrl = getenv('TEST_SERVER_URL'); @@ -53,11 +73,11 @@ trait BasicStructure { } /** - * @Given /^using api version "([^"]*)"$/ + * @Given /^using api version "(\d+)"$/ * @param string $version */ public function usingApiVersion($version) { - $this->apiVersion = $version; + $this->apiVersion = (int)$version; } /** @@ -75,7 +95,7 @@ trait BasicStructure { */ public function usingServer($server) { $previousServer = $this->currentServer; - if ($server === 'LOCAL'){ + if ($server === 'LOCAL') { $this->baseUrl = $this->localBaseUrl; $this->currentServer = 'LOCAL'; return $previousServer; @@ -98,20 +118,28 @@ trait BasicStructure { /** * Parses the xml answer to get ocs response which doesn't match with * http one in v1 of the api. + * * @param ResponseInterface $response * @return string */ public function getOCSResponse($response) { - return $response->xml()->meta[0]->statuscode; + $body = simplexml_load_string((string)$response->getBody()); + if ($body === false) { + throw new \RuntimeException('Could not parse OCS response, body is not valid XML'); + } + return $body->meta[0]->statuscode; } /** * This function is needed to use a vertical fashion in the gherkin tables. + * * @param array $arrayOfArrays * @return array */ - public function simplifyArray($arrayOfArrays){ - $a = array_map(function($subArray) { return $subArray[0]; }, $arrayOfArrays); + public function simplifyArray($arrayOfArrays) { + $a = array_map(function ($subArray) { + return $subArray[0]; + }, $arrayOfArrays); return $a; } @@ -119,7 +147,7 @@ trait BasicStructure { * @When /^sending "([^"]*)" to "([^"]*)" with$/ * @param string $verb * @param string $url - * @param \Behat\Gherkin\Node\TableNode $body + * @param TableNode $body */ public function sendingToWith($verb, $url, $body) { $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php" . $url; @@ -127,25 +155,101 @@ trait BasicStructure { $options = []; if ($this->currentUser === 'admin') { $options['auth'] = $this->adminUser; - } else { + } elseif (strpos($this->currentUser, 'anonymous') !== 0) { $options['auth'] = [$this->currentUser, $this->regularUser]; } - if ($body instanceof \Behat\Gherkin\Node\TableNode) { + $options['headers'] = [ + 'OCS-APIRequest' => 'true' + ]; + if ($body instanceof TableNode) { $fd = $body->getRowsHash(); - $options['body'] = $fd; + $options['form_params'] = $fd; + } + + // TODO: Fix this hack! + if ($verb === 'PUT' && $body === null) { + $options['form_params'] = [ + 'foo' => 'bar', + ]; } try { - $this->response = $client->send($client->createRequest($verb, $fullUrl, $options)); - } catch (\GuzzleHttp\Exception\ClientException $ex) { + $this->response = $client->request($verb, $fullUrl, $options); + } catch (ClientException $ex) { + $this->response = $ex->getResponse(); + } catch (ServerException $ex) { $this->response = $ex->getResponse(); } } - public function isExpectedUrl($possibleUrl, $finalPart){ + /** + * @param string $verb + * @param string $url + * @param TableNode|array|null $body + * @param array $headers + */ + protected function sendRequestForJSON(string $verb, string $url, $body = null, array $headers = []): void { + $fullUrl = $this->baseUrl . "v{$this->apiVersion}.php" . $url; + $client = new Client(); + $options = []; + if ($this->currentUser === 'admin') { + $options['auth'] = ['admin', 'admin']; + } elseif (strpos($this->currentUser, 'anonymous') !== 0) { + $options['auth'] = [$this->currentUser, $this->regularUser]; + } + if ($body instanceof TableNode) { + $fd = $body->getRowsHash(); + $options['form_params'] = $fd; + } elseif (is_array($body)) { + $options['form_params'] = $body; + } + + $options['headers'] = array_merge($headers, [ + 'OCS-ApiRequest' => 'true', + 'Accept' => 'application/json', + ]); + + try { + $this->response = $client->{$verb}($fullUrl, $options); + } catch (ClientException $ex) { + $this->response = $ex->getResponse(); + } + } + + /** + * @When /^sending "([^"]*)" with exact url to "([^"]*)"$/ + * @param string $verb + * @param string $url + */ + public function sendingToDirectUrl($verb, $url) { + $this->sendingToWithDirectUrl($verb, $url, null); + } + + public function sendingToWithDirectUrl($verb, $url, $body) { + $fullUrl = substr($this->baseUrl, 0, -5) . $url; + $client = new Client(); + $options = []; + if ($this->currentUser === 'admin') { + $options['auth'] = $this->adminUser; + } elseif (strpos($this->currentUser, 'anonymous') !== 0) { + $options['auth'] = [$this->currentUser, $this->regularUser]; + } + if ($body instanceof TableNode) { + $fd = $body->getRowsHash(); + $options['form_params'] = $fd; + } + + try { + $this->response = $client->request($verb, $fullUrl, $options); + } catch (ClientException $ex) { + $this->response = $ex->getResponse(); + } + } + + public function isExpectedUrl($possibleUrl, $finalPart) { $baseUrlChopped = substr($this->baseUrl, 0, -4); $endCharacter = strlen($baseUrlChopped) + strlen($finalPart); - return (substr($possibleUrl,0,$endCharacter) == "$baseUrlChopped" . "$finalPart"); + return (substr($possibleUrl, 0, $endCharacter) == "$baseUrlChopped" . "$finalPart"); } /** @@ -153,7 +257,7 @@ trait BasicStructure { * @param int $statusCode */ public function theOCSStatusCodeShouldBe($statusCode) { - PHPUnit_Framework_Assert::assertEquals($statusCode, $this->getOCSResponse($this->response)); + Assert::assertEquals($statusCode, $this->getOCSResponse($this->response)); } /** @@ -161,14 +265,22 @@ trait BasicStructure { * @param int $statusCode */ public function theHTTPStatusCodeShouldBe($statusCode) { - PHPUnit_Framework_Assert::assertEquals($statusCode, $this->response->getStatusCode()); + Assert::assertEquals($statusCode, $this->response->getStatusCode()); + } + + /** + * @Then /^the Content-Type should be "([^"]*)"$/ + * @param string $contentType + */ + public function theContentTypeShouldbe($contentType) { + Assert::assertEquals($contentType, $this->response->getHeader('Content-Type')[0]); } /** * @param ResponseInterface $response */ private function extracRequestTokenFromResponse(ResponseInterface $response) { - $this->requesttoken = substr(preg_replace('/(.*)data-requesttoken="(.*)">(.*)/sm', '\2', $response->getBody()->getContents()), 0, 89); + $this->requestToken = substr(preg_replace('/(.*)data-requesttoken="(.*)">(.*)/sm', '\2', $response->getBody()->getContents()), 0, 89); } /** @@ -176,7 +288,8 @@ trait BasicStructure { * @param string $user */ public function loggingInUsingWebAs($user) { - $loginUrl = substr($this->baseUrl, 0, -5); + $baseUrl = substr($this->baseUrl, 0, -5); + $loginUrl = $baseUrl . '/index.php/login'; // Request a new session and extract CSRF token $client = new Client(); $response = $client->get( @@ -193,12 +306,15 @@ trait BasicStructure { $response = $client->post( $loginUrl, [ - 'body' => [ + 'form_params' => [ 'user' => $user, 'password' => $password, - 'requesttoken' => $this->requesttoken, + 'requesttoken' => $this->requestToken, ], 'cookies' => $this->cookieJar, + 'headers' => [ + 'Origin' => $baseUrl, + ], ] ); $this->extracRequestTokenFromResponse($response); @@ -208,22 +324,33 @@ trait BasicStructure { * @When Sending a :method to :url with requesttoken * @param string $method * @param string $url + * @param TableNode|array|null $body */ - public function sendingAToWithRequesttoken($method, $url) { + public function sendingAToWithRequesttoken($method, $url, $body = null) { $baseUrl = substr($this->baseUrl, 0, -5); + $options = [ + 'cookies' => $this->cookieJar, + 'headers' => [ + 'requesttoken' => $this->requestToken + ], + ]; + + if ($body instanceof TableNode) { + $fd = $body->getRowsHash(); + $options['form_params'] = $fd; + } elseif ($body) { + $options = array_merge_recursive($options, $body); + } + $client = new Client(); - $request = $client->createRequest( - $method, - $baseUrl . $url, - [ - 'cookies' => $this->cookieJar, - ] - ); - $request->addHeader('requesttoken', $this->requesttoken); try { - $this->response = $client->send($request); - } catch (\GuzzleHttp\Exception\ClientException $e) { + $this->response = $client->request( + $method, + $baseUrl . $url, + $options + ); + } catch (ClientException $e) { $this->response = $e->getResponse(); } } @@ -237,64 +364,199 @@ trait BasicStructure { $baseUrl = substr($this->baseUrl, 0, -5); $client = new Client(); - $request = $client->createRequest( - $method, - $baseUrl . $url, - [ - 'cookies' => $this->cookieJar, - ] - ); try { - $this->response = $client->send($request); - } catch (\GuzzleHttp\Exception\ClientException $e) { + $this->response = $client->request( + $method, + $baseUrl . $url, + [ + 'cookies' => $this->cookieJar + ] + ); + } catch (ClientException $e) { $this->response = $e->getResponse(); } } - public static function removeFile($path, $filename){ + public static function removeFile($path, $filename) { if (file_exists("$path" . "$filename")) { unlink("$path" . "$filename"); } } /** + * @Given User :user modifies text of :filename with text :text + * @param string $user + * @param string $filename + * @param string $text + */ + public function modifyTextOfFile($user, $filename, $text) { + self::removeFile($this->getDataDirectory() . "/$user/files", "$filename"); + file_put_contents($this->getDataDirectory() . "/$user/files" . "$filename", "$text"); + } + + private function getDataDirectory() { + // Based on "runOcc" from CommandLine trait + $args = ['config:system:get', 'datadirectory']; + $args = array_map(function ($arg) { + return escapeshellarg($arg); + }, $args); + $args[] = '--no-ansi --no-warnings'; + $args = implode(' ', $args); + + $descriptor = [ + 0 => ['pipe', 'r'], + 1 => ['pipe', 'w'], + 2 => ['pipe', 'w'], + ]; + $process = proc_open('php console.php ' . $args, $descriptor, $pipes, $ocPath = '../..'); + $lastStdOut = stream_get_contents($pipes[1]); + proc_close($process); + + return trim($lastStdOut); + } + + /** + * @Given file :filename is created :times times in :user user data + * @param string $filename + * @param string $times + * @param string $user + */ + public function fileIsCreatedTimesInUserData($filename, $times, $user) { + for ($i = 0; $i < $times; $i++) { + file_put_contents($this->getDataDirectory() . "/$user/files" . "$filename-$i", "content-$i"); + } + } + + public function createFileSpecificSize($name, $size) { + $file = fopen('work/' . "$name", 'w'); + fseek($file, $size - 1, SEEK_CUR); + fwrite($file, 'a'); // write a dummy char at SIZE position + fclose($file); + } + + public function createFileWithText($name, $text) { + $file = fopen('work/' . "$name", 'w'); + fwrite($file, $text); + fclose($file); + } + + /** + * @Given file :filename of size :size is created in local storage + * @param string $filename + * @param string $size + */ + public function fileIsCreatedInLocalStorageWithSize($filename, $size) { + $this->createFileSpecificSize("local_storage/$filename", $size); + } + + /** + * @Given file :filename with text :text is created in local storage + * @param string $filename + * @param string $text + */ + public function fileIsCreatedInLocalStorageWithText($filename, $text) { + $this->createFileWithText("local_storage/$filename", $text); + } + + /** + * @When Sleep for :seconds seconds + * @param int $seconds + */ + public function sleepForSeconds($seconds) { + sleep((int)$seconds); + } + + /** * @BeforeSuite */ - public static function addFilesToSkeleton(){ - for ($i=0; $i<5; $i++){ - file_put_contents("../../core/skeleton/" . "textfile" . "$i" . ".txt", "ownCloud test text file\n"); + public static function addFilesToSkeleton() { + for ($i = 0; $i < 5; $i++) { + file_put_contents('../../core/skeleton/' . 'textfile' . "$i" . '.txt', "Nextcloud test text file\n"); } - if (!file_exists("../../core/skeleton/FOLDER")) { - mkdir("../../core/skeleton/FOLDER", 0777, true); + if (!file_exists('../../core/skeleton/FOLDER')) { + mkdir('../../core/skeleton/FOLDER', 0777, true); } - if (!file_exists("../../core/skeleton/PARENT")) { - mkdir("../../core/skeleton/PARENT", 0777, true); + if (!file_exists('../../core/skeleton/PARENT')) { + mkdir('../../core/skeleton/PARENT', 0777, true); } - file_put_contents("../../core/skeleton/PARENT/" . "parent.txt", "ownCloud test text file\n"); - if (!file_exists("../../core/skeleton/PARENT/CHILD")) { - mkdir("../../core/skeleton/PARENT/CHILD", 0777, true); + file_put_contents('../../core/skeleton/PARENT/' . 'parent.txt', "Nextcloud test text file\n"); + if (!file_exists('../../core/skeleton/PARENT/CHILD')) { + mkdir('../../core/skeleton/PARENT/CHILD', 0777, true); } - file_put_contents("../../core/skeleton/PARENT/CHILD/" . "child.txt", "ownCloud test text file\n"); + file_put_contents('../../core/skeleton/PARENT/CHILD/' . 'child.txt', "Nextcloud test text file\n"); } /** * @AfterSuite */ - public static function removeFilesFromSkeleton(){ - for ($i=0; $i<5; $i++){ - self::removeFile("../../core/skeleton/", "textfile" . "$i" . ".txt"); + public static function removeFilesFromSkeleton() { + for ($i = 0; $i < 5; $i++) { + self::removeFile('../../core/skeleton/', 'textfile' . "$i" . '.txt'); } - if (is_dir("../../core/skeleton/FOLDER")) { - rmdir("../../core/skeleton/FOLDER"); + if (is_dir('../../core/skeleton/FOLDER')) { + rmdir('../../core/skeleton/FOLDER'); } - self::removeFile("../../core/skeleton/PARENT/CHILD/", "child.txt"); - if (is_dir("../../core/skeleton/PARENT/CHILD")) { - rmdir("../../core/skeleton/PARENT/CHILD"); + self::removeFile('../../core/skeleton/PARENT/CHILD/', 'child.txt'); + if (is_dir('../../core/skeleton/PARENT/CHILD')) { + rmdir('../../core/skeleton/PARENT/CHILD'); } - self::removeFile("../../core/skeleton/PARENT/", "parent.txt"); - if (is_dir("../../core/skeleton/PARENT")) { - rmdir("../../core/skeleton/PARENT"); + self::removeFile('../../core/skeleton/PARENT/', 'parent.txt'); + if (is_dir('../../core/skeleton/PARENT')) { + rmdir('../../core/skeleton/PARENT'); + } + } + + /** + * @BeforeScenario @local_storage + */ + public static function removeFilesFromLocalStorageBefore() { + $dir = './work/local_storage/'; + $di = new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS); + $ri = new RecursiveIteratorIterator($di, RecursiveIteratorIterator::CHILD_FIRST); + foreach ($ri as $file) { + $file->isDir() ? rmdir($file) : unlink($file); } } -} + /** + * @AfterScenario @local_storage + */ + public static function removeFilesFromLocalStorageAfter() { + $dir = './work/local_storage/'; + $di = new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS); + $ri = new RecursiveIteratorIterator($di, RecursiveIteratorIterator::CHILD_FIRST); + foreach ($ri as $file) { + $file->isDir() ? rmdir($file) : unlink($file); + } + } + + /** + * @Given /^cookies are reset$/ + */ + public function cookiesAreReset() { + $this->cookieJar = new CookieJar(); + } + + /** + * @Then The following headers should be set + * @param TableNode $table + * @throws \Exception + */ + public function theFollowingHeadersShouldBeSet(TableNode $table) { + foreach ($table->getTable() as $header) { + $headerName = $header[0]; + $expectedHeaderValue = $header[1]; + $returnedHeader = $this->response->getHeader($headerName)[0]; + if ($returnedHeader !== $expectedHeaderValue) { + throw new \Exception( + sprintf( + "Expected value '%s' for header '%s', got '%s'", + $expectedHeaderValue, + $headerName, + $returnedHeader + ) + ); + } + } + } +} |