From 084d892ce601fa67b298987d8c455e5a1d099aad Mon Sep 17 00:00:00 2001 From: Julius Haertl Date: Sat, 4 Mar 2017 12:22:12 +0100 Subject: [PATCH] Add basic behat test structure Signed-off-by: Julius Haertl --- tests/integration/composer.json | 23 +++ tests/integration/config/behat.yml | 12 ++ tests/integration/features/acl.feature | 36 ++++ .../features/bootstrap/FeatureContext.php | 189 ++++++++++++++++++ tests/integration/features/decks.feature | 37 ++++ tests/integration/run.sh | 44 ++++ tests/phpunit.integration.xml | 4 +- 7 files changed, 343 insertions(+), 2 deletions(-) create mode 100644 tests/integration/composer.json create mode 100644 tests/integration/config/behat.yml create mode 100644 tests/integration/features/acl.feature create mode 100644 tests/integration/features/bootstrap/FeatureContext.php create mode 100644 tests/integration/features/decks.feature create mode 100755 tests/integration/run.sh diff --git a/tests/integration/composer.json b/tests/integration/composer.json new file mode 100644 index 000000000..4727d7872 --- /dev/null +++ b/tests/integration/composer.json @@ -0,0 +1,23 @@ +{ + "require-dev": { + "phpunit/phpunit": "~4.6", + "behat/behat": "^3.0", + "guzzlehttp/guzzle": "~5.0", + "jarnaiz/behat-junit-formatter": "^1.3", + "sabre/dav": "3.2" + }, + "autoload": { + "files": [ + "../../../../build/integration/features/bootstrap/Auth.php", + "../../../../build/integration/features/bootstrap/BasicStructure.php", + "../../../../build/integration/features/bootstrap/Provisioning.php", + "../../../../build/integration/features/bootstrap/Sharing.php", + "../../../../build/integration/features/bootstrap/WebDav.php" + ], + "psr-0": { + "": [ + "features/bootstrap/" + ] + } + } +} diff --git a/tests/integration/config/behat.yml b/tests/integration/config/behat.yml new file mode 100644 index 000000000..37aaed8f4 --- /dev/null +++ b/tests/integration/config/behat.yml @@ -0,0 +1,12 @@ +default: + suites: + test: + paths: + - %paths.base%/../features/ + contexts: + - FeatureContext: + baseUrl: http://localhost:8080/index.php/ocs/ + admin: + - admin + - admin + regular_user_password: 123456 \ No newline at end of file diff --git a/tests/integration/features/acl.feature b/tests/integration/features/acl.feature new file mode 100644 index 000000000..9ee4c1ebe --- /dev/null +++ b/tests/integration/features/acl.feature @@ -0,0 +1,36 @@ +Feature: acl + Routes should check for permissions when a user sends a requests + + Background: + Given user "admin" exists + And user "user0" exists + And user "user1" exists + And user "user2" exists + Given group "group0" exists + And group "group1" exists + Given user "user1" belongs to group "group1" + + Scenario: Request the main frontend page + Given Logging in using web as "user0" + When Sending a "GET" to "/index.php/apps/deck" without requesttoken + Then the HTTP status code should be "200" + + Scenario: Fetch the board list + Given Logging in using web as "user0" + When Sending a "GET" to "/index.php/apps/deck/boards" with requesttoken + Then the HTTP status code should be "200" + And the Content-Type should be "application/json; charset=utf-8" + + Scenario: Fetch board details of owned board + Given Logging in using web as "admin" + And creates a board named "MyPrivateAdminBoard" with color "fafafa" + When "admin" fetches the board named "MyPrivateAdminBoard" + Then the HTTP status code should be "200" + And the Content-Type should be "application/json; charset=utf-8" + + Scenario: Fetch board details of an other users board + Given Logging in using web as "admin" + And creates a board named "MyPrivateAdminBoard" with color "fafafa" + When "user0" fetches the board named "MyPrivateAdminBoard" + Then the HTTP status code should be "403" + And the Content-Type should be "application/json; charset=utf-8" \ No newline at end of file diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php new file mode 100644 index 000000000..f63cdcf30 --- /dev/null +++ b/tests/integration/features/bootstrap/FeatureContext.php @@ -0,0 +1,189 @@ +baseUrl, 0, -5); + + $client = new Client; + $request = $client->createRequest( + $method, + $baseUrl . $url, + [ + 'cookies' => $this->cookieJar, + 'json' => json_decode($data) + ] + ); + $request->addHeader('requesttoken', $this->requestToken); + try { + $this->response = $client->send($request); + } catch (ClientException $e) { + $this->response = $e->getResponse(); + } + } + + + /** + * @When :user creates a new deck with name :name + */ + public function createsANewDeckWithName($user, $content) { + $client = new GuzzleHttp\Client(); + $this->response = $client->post( + 'http://localhost:8080/index.php/apps/deck/boards', + [ + 'form_params' => [ + 'name' => $name, + ], + 'auth' => [ + $this->mappedUserId, + 'test', + ], + ] + ); + } + + /** + * @Then the response should have a status code :code + * @param string $code + * @throws InvalidArgumentException + */ + public function theResponseShouldHaveAStatusCode($code) { + $currentCode = $this->response->getStatusCode(); + if ($currentCode !== (int)$code) { + throw new InvalidArgumentException( + sprintf( + 'Expected %s as code got %s', + $code, + $currentCode + ) + ); + } + } + + /** + * @Then the response should be a JSON array with the following mandatory values + * @param TableNode $table + * @throws InvalidArgumentException + */ + public function theResponseShouldBeAJsonArrayWithTheFollowingMandatoryValues(TableNode $table) { + $expectedValues = $table->getColumnsHash(); + $realResponseArray = json_decode($this->response->getBody()->getContents(), true); + + foreach ($expectedValues as $value) { + if ((string)$realResponseArray[$value['key']] !== (string)$value['value']) { + throw new InvalidArgumentException( + sprintf( + 'Expected %s for key %s got %s', + (string)$value['value'], + $value['key'], + (string)$realResponseArray[$value['key']] + ) + ); + } + } + } + + /** + * @Then the response should be a JSON array with a length of :length + * @param int $length + * @throws InvalidArgumentException + */ + public function theResponseShouldBeAJsonArrayWithALengthOf($length) { + $realResponseArray = json_decode($this->response->getBody()->getContents(), true); + PHPUnit_Framework_Assert::assertEquals($realResponseArray, "foo"); + if((int)count($realResponseArray) !== (int)$length) { + throw new InvalidArgumentException( + sprintf( + 'Expected %d as length got %d', + $length, + count($realResponseArray) + ) + ); + } + } + + /** + * @Then /^I should get:$/ + * + * @param PyStringNode $string + * @throws \Exception + */ + public function iShouldGet(PyStringNode $string) + { + if ((string) $string !== trim($this->cliOutput)) { + throw new Exception(sprintf( + 'Expected "%s" but received "%s".', + $string, + $this->cliOutput + )); + } + + return; + } + + private function sendJSONrequest($method, $url, $data) { + $baseUrl = substr($this->baseUrl, 0, -5); + + $client = new Client; + $request = $client->createRequest( + $method, + $baseUrl . $url, + [ + 'cookies' => $this->cookieJar, + 'json' => $data + ] + ); + $request->addHeader('requesttoken', $this->requestToken); + try { + $this->response = $client->send($request); + } catch (ClientException $e) { + $this->response = $e->getResponse(); + } + } + + /** + * @Given /^creates a board named "([^"]*)" with color "([^"]*)"$/ + */ + public function createsABoardNamedWithColor($title, $color) { + + $this->sendJSONrequest('POST', '/index.php/apps/deck/boards', [ + 'title' => $title, + 'color' => $color + ] + ); + $response = json_decode($this->response->getBody()->getContents(), true); + $this->lastInsertIds[$title] = $response['id']; + } + + /** + * @When /^"([^"]*)" fetches the board named "([^"]*)"$/ + */ + public function fetchesTheBoardNamed($user, $boardName) { + $this->loggingInUsingWebAs($user); + $id = $this->lastInsertIds[$boardName]; + $this->sendJSONrequest('GET', '/index.php/apps/deck/boards/'.$id, []); + } + +} diff --git a/tests/integration/features/decks.feature b/tests/integration/features/decks.feature new file mode 100644 index 000000000..405e0b823 --- /dev/null +++ b/tests/integration/features/decks.feature @@ -0,0 +1,37 @@ +Feature: decks + + Background: + Given user "user0" exists + + Scenario: Request the main frontend page + Given Logging in using web as "admin" + When Sending a "GET" to "/index.php/apps/deck" without requesttoken + Then the HTTP status code should be "200" + + Scenario: Fetch the board list + Given Logging in using web as "admin" + When Sending a "GET" to "/index.php/apps/deck/boards" with requesttoken + Then the HTTP status code should be "200" + And the Content-Type should be "application/json; charset=utf-8" + + Scenario: Fetch board details of a nonexisting board + Given Logging in using web as "admin" + When Sending a "GET" to "/index.php/apps/deck/boards/13379" with requesttoken + Then the HTTP status code should be "403" + And the Content-Type should be "application/json; charset=utf-8" + + Scenario: Create a new board + Given Logging in using web as "admin" + When Sending a "POST" to "/index.php/apps/deck/boards" with JSON + """ + { + "title": "MyBoard", + "color": "000000" + } + """ + Then the HTTP status code should be "200" + And the Content-Type should be "application/json; charset=utf-8" + And the response should be a JSON array with the following mandatory values + |key|value| + |title|MyBoard| + |color|000000| diff --git a/tests/integration/run.sh b/tests/integration/run.sh new file mode 100755 index 000000000..345bb810a --- /dev/null +++ b/tests/integration/run.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash + +OC_PATH=../../../../ +OCC=${OC_PATH}occ +SCENARIO_TO_RUN=$1 +HIDE_OC_LOGS=$2 + +INSTALLED=$($OCC status | grep installed: | cut -d " " -f 5) + +if [ "$INSTALLED" == "true" ]; then + $OCC app:enable deck +else + if [ "$SCENARIO_TO_RUN" != "setup_features/setup.feature" ]; then + echo "Nextcloud instance needs to be installed" >&2 + exit 1 + fi +fi + +composer install +composer dump-autoload + +# avoid port collision on jenkins - use $EXECUTOR_NUMBER +if [ -z "$EXECUTOR_NUMBER" ]; then + EXECUTOR_NUMBER=0 +fi +PORT=$((8080 + $EXECUTOR_NUMBER)) +echo $PORT +php -S localhost:$PORT -t $OC_PATH & +PHPPID=$! +echo $PHPPID + +export TEST_SERVER_URL="http://localhost:$PORT/ocs/" + +vendor/bin/behat +RESULT=$? + +kill $PHPPID + +if [ -z $HIDE_OC_LOGS ]; then + tail "${OC_PATH}/data/nextcloud.log" +fi + +echo "runsh: Exit code: $RESULT" +exit $RESULT diff --git a/tests/phpunit.integration.xml b/tests/phpunit.integration.xml index 4a7eddb02..b6a61a26a 100644 --- a/tests/phpunit.integration.xml +++ b/tests/phpunit.integration.xml @@ -1,7 +1,7 @@ - - ./integration + + ./integration/database