Merge pull request #2639 from owncloud/add-api-steps

[tests-only] add some more steps
This commit is contained in:
Michael Barz
2021-10-21 15:31:05 +02:00
committed by GitHub
4 changed files with 554 additions and 217 deletions
+1 -1
View File
@@ -32,7 +32,7 @@ default:
paths:
- '%paths.base%/../features/apiSpaces'
contexts:
- GraphApiContext:
- SpacesContext:
- OccContext:
- FeatureContext: &common_feature_context_params
baseUrl: http://localhost:8080
@@ -8,7 +8,21 @@ Feature: List and create spaces
Scenario: list own spaces
Given user "Alice" has been created with default attributes and without skeleton files
And user "Alice" lists all available spaces via the GraphApi
When user "Alice" lists all available spaces via the GraphApi
Then the HTTP status code should be "200"
And user "Alice" lists the content of the personal space root using the WebDav Api
And the HTTP status code should be "207"
When user "Alice" lists the content of the space with the name "Alice Hansen" using the WebDav Api
Then the HTTP status code should be "207"
#Then the propfind result of the space should contain these entries:
#| .space/ |
Then the propfind result of the space should not contain these entries:
| file |
When user "Alice" creates a space "Project Mars" of type "project" with the default quota using the GraphApi
Then the HTTP status code should be "401"
When the administrator gives "Alice" the role "Admin" using the settings api
When user "Alice" creates a space "Project Mars" of type "project" with the default quota using the GraphApi
Then the HTTP status code should be "201"
Then the json responded should contain these key and value pairs
| key | value |
| driveType | project |
| name | Project Mars |
When user "Alice" creates a space "Project Venus" of type "project" with quota "2000" using the GraphApi
@@ -1,213 +0,0 @@
<?php
use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Psr\Http\Message\ResponseInterface;
use TestHelpers\HttpRequestHelper;
use TestHelpers\SetupHelper;
use PHPUnit\Framework\Assert;
require_once 'bootstrap.php';
/**
* Context for GraphApi specific steps
*/
class GraphApiContext implements Context {
/**
* @var FeatureContext
*/
private FeatureContext $featureContext;
/**
* @var string
*/
private string $personalDriveWebDavUrl;
/**
* @return string
*/
public function getPersonalDriveWebDavUrl(): string
{
return $this->personalDriveWebDavUrl;
}
/**
* @param string $personalDriveWebDavUrl
*/
public function setPersonalDriveWebDavUrl(string $personalDriveWebDavUrl): void
{
$this->personalDriveWebDavUrl = $personalDriveWebDavUrl;
}
/**
* @BeforeScenario
*
* @param BeforeScenarioScope $scope
*
* @return void
* @throws Exception
*/
public function setUpScenario(BeforeScenarioScope $scope): void
{
// Get the environment
$environment = $scope->getEnvironment();
// Get all the contexts you need in this context
$this->featureContext = $environment->getContext('FeatureContext');
SetupHelper::init(
$this->featureContext->getAdminUsername(),
$this->featureContext->getAdminPassword(),
$this->featureContext->getBaseUrl(),
$this->featureContext->getOcPath()
);
}
/**
* Send Graph List Drives Request
*
* @param $baseUrl
* @param $user
* @param $password
* @param $arguments
* @param string $xRequestId
* @param array $body
* @param array $headers
* @return ResponseInterface
*/
public function listSpacesRequest(
$baseUrl,
$user,
$password,
$arguments,
string $xRequestId = '',
array $body = [],
array $headers = []
) {
$fullUrl = $baseUrl;
if (!str_ends_with($fullUrl, '/')) {
$fullUrl .= '/';
}
$fullUrl .= "graph/v1.0/me/drives/" . $arguments;
return HttpRequestHelper::get($fullUrl, $xRequestId, $user, $password, $headers, $body);
}
/**
* Send Graph List Drives Request
*
* @param $baseUrl
* @param $user
* @param $password
* @param string $spaceName
* @param string $xRequestId
* @param array $headers
* @return ResponseInterface
*/
public function sendCreateSpaceRequest(
$baseUrl,
$user,
$password,
string $spaceName,
string $xRequestId = '',
array $headers = []
): ResponseInterface
{
$fullUrl = $baseUrl;
if (!str_ends_with($fullUrl, '/')) {
$fullUrl .= '/';
}
$fullUrl .= "drives/" . $spaceName;
return HttpRequestHelper::sendRequest($fullUrl, $xRequestId, 'POST', $user, $password, $headers);
}
/**
* Send Propfind Request to Url
*
* @param $fullUrl
* @param $user
* @param $password
* @param string $xRequestId
* @param array $headers
* @return ResponseInterface
*/
public function sendPropfindRequestToUrl(
$fullUrl,
$user,
$password,
string $xRequestId = '',
array $headers = []
): ResponseInterface
{
return HttpRequestHelper::sendRequest($fullUrl, $xRequestId, 'PROPFIND', $user, $password, $headers);
}
/**
* @When /^user "([^"]*)" lists all available spaces via the GraphApi$/
*
* @param $user
* @return void
*/
public function theUserListsAllHisAvailableSpacesUsingTheGraphApi($user): void
{
$this->featureContext->setResponse(
$this->listSpacesRequest(
$this->featureContext->getBaseUrl(),
$user,
$this->featureContext->getPasswordForUser($user),
"",
""
)
);
}
/**
* Get the webDavUrl of the personal space has been found
*
* @return void
*/
public function theWebDavUrlOfThePersonalSpaceHasBeenFound(): void
{
$rawBody = $this->featureContext->getResponse()->getBody()->getContents();
$drives = [];
if (isset(\json_decode($rawBody, true)["value"])) {
$drives = \json_decode($rawBody, true)["value"];
}
Assert::assertArrayHasKey(0, $drives, "No drives were found on that endpoint");
foreach($drives as $drive) {
if (isset($drive["driveType"]) && $drive["driveType"] === "personal") {
$this->setPersonalDriveWebDavUrl($drive["root"]["webDavUrl"]);
Assert::assertNotEmpty(
$drive["root"]["webDavUrl"],
"The personal space attributes contain no webDavUrl"
);
}
}
}
/**
* @When /^user "([^"]*)" lists the content of the personal space root using the WebDav Api$/
*
* @param $user
*
* @return void
*/
public function theUserListsTheContentOfAPersonalSpaceRootUsingTheWebDAvApi($user): void
{
$this->theUserListsAllHisAvailableSpacesUsingTheGraphApi($user);
$this->theWebDavUrlOfThePersonalSpaceHasBeenFound();
$this->featureContext->setResponse(
$this->sendPropfindRequestToUrl(
$this->getPersonalDriveWebDavUrl(),
$user,
$this->featureContext->getPasswordForUser($user),
"",
[],
[],
[]
)
);
}
}
@@ -0,0 +1,536 @@
<?php
use Behat\Behat\Context\Context;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Psr\Http\Message\ResponseInterface;
use TestHelpers\HttpRequestHelper;
use TestHelpers\SetupHelper;
use PHPUnit\Framework\Assert;
use \Behat\Gherkin\Node\TableNode;
require_once 'bootstrap.php';
/**
* Context for ocis spaces specific steps
*/
class SpacesContext implements Context {
/**
* @var FeatureContext
*/
private FeatureContext $featureContext;
/**
* @var array
*/
private array $availableSpaces;
/**
* @return array
*/
public function getAvailableSpaces(): array
{
return $this->availableSpaces;
}
/**
* @param array $availableSpaces
*/
public function setAvailableSpaces(array $availableSpaces): void
{
$this->availableSpaces = $availableSpaces;
}
/**
* response content parsed from XML to an array
*
* @var array
*/
private array $responseXml = [];
/**
* @return array
*/
public function getResponseXml(): array
{
return $this->responseXml;
}
/**
* @param array $responseXml
*/
public function setResponseXml(array $responseXml): void
{
$this->responseXml = $responseXml;
}
/**
* space id from last propfind request
*
* @var string
*/
private $responseSpaceId;
/**
* @param string $responseSpaceId
*/
public function setResponseSpaceId(string $responseSpaceId): void
{
$this->responseSpaceId = $responseSpaceId;
}
/**
* @return string
*/
public function getResponseSpaceId(): string
{
return $this->responseSpaceId;
}
/**
* @BeforeScenario
*
* @param BeforeScenarioScope $scope
*
* @return void
* @throws Exception
*/
public function setUpScenario(BeforeScenarioScope $scope): void
{
// Get the environment
$environment = $scope->getEnvironment();
// Get all the contexts you need in this context
$this->featureContext = $environment->getContext('FeatureContext');
SetupHelper::init(
$this->featureContext->getAdminUsername(),
$this->featureContext->getAdminPassword(),
$this->featureContext->getBaseUrl(),
$this->featureContext->getOcPath()
);
}
/**
* Send Graph List Spaces Request
*
* @param $baseUrl
* @param $user
* @param $password
* @param $arguments
* @param string $xRequestId
* @param array $body
* @param array $headers
* @return ResponseInterface
*/
public function listSpacesRequest(
$baseUrl,
$user,
$password,
$arguments,
string $xRequestId = '',
array $body = [],
array $headers = []
) {
$fullUrl = $baseUrl;
if (!str_ends_with($fullUrl, '/')) {
$fullUrl .= '/';
}
$fullUrl .= "graph/v1.0/me/drives/" . $arguments;
return HttpRequestHelper::get($fullUrl, $xRequestId, $user, $password, $headers, $body);
}
/**
* Send Graph Create Space Request
*
* @param $baseUrl
* @param $user
* @param $password
* @param string $body
* @param string $xRequestId
* @param array $headers
* @return ResponseInterface
*/
public function sendCreateSpaceRequest(
$baseUrl,
$user,
$password,
string $body,
string $xRequestId = '',
array $headers = []
): ResponseInterface
{
$fullUrl = $baseUrl;
if (!str_ends_with($fullUrl, '/')) {
$fullUrl .= '/';
}
$fullUrl .= "graph/v1.0/drives/";
return HttpRequestHelper::post($fullUrl, $xRequestId, $user, $password, $headers, $body);
}
/**
* Send Propfind Request to Url
*
* @param $fullUrl
* @param $user
* @param $password
* @param string $xRequestId
* @param array $headers
* @return ResponseInterface
*/
public function sendPropfindRequestToUrl(
$fullUrl,
$user,
$password,
string $xRequestId = '',
array $headers = []
): ResponseInterface
{
return HttpRequestHelper::sendRequest($fullUrl, $xRequestId, 'PROPFIND', $user, $password, $headers);
}
/**
* @When /^user "([^"]*)" lists all available spaces via the GraphApi$/
*
* @param $user
* @return void
*/
public function theUserListsAllHisAvailableSpacesUsingTheGraphApi($user): void
{
$this->featureContext->setResponse(
$this->listSpacesRequest(
$this->featureContext->getBaseUrl(),
$user,
$this->featureContext->getPasswordForUser($user),
"",
""
)
);
$this->rememberTheAvailableSpaces();
}
/**
* @When /^user "([^"]*)" creates a space "([^"]*)" of type "([^"]*)" with the default quota using the GraphApi$/
*
* @param $user string
* @param $spaceName string
* @param $spaceType string
*
* @return void
*/
public function theUserCreatesASpaceUsingTheGraphApi($user, $spaceName, $spaceType): void
{
$space = ["Name" => $spaceName, "driveType" => $spaceType];
$body = json_encode($space);
$this->featureContext->setResponse(
$this->sendCreateSpaceRequest(
$this->featureContext->getBaseUrl(),
$user,
$this->featureContext->getPasswordForUser($user),
$body,
""
)
);
}
/**
* @When /^user "([^"]*)" creates a space "([^"]*)" of type "([^"]*)" with quota "([^"]*)" using the GraphApi$/
*
* @param $user string
* @param $spaceName string
* @param $spaceType string
* @param $quota int
*
* @return void
* @throws JsonException
*/
public function theUserCreatesASpaceWithQuotaUsingTheGraphApi($user, $spaceName, $spaceType, $quota): void
{
$space = ["Name" => $spaceName, "driveType" => $spaceType, "quota" => ["total" => (int) $quota]];
$body = json_encode($space);
$this->featureContext->setResponse(
$this->sendCreateSpaceRequest(
$this->featureContext->getBaseUrl(),
$user,
$this->featureContext->getPasswordForUser($user),
$body,
""
)
);
}
/**
* @When /^the administrator gives "([^"]*)" the role "([^"]*)" using the settings api$/
*
* @param $user string
* @param $role string
*
* @return void
*/
public function theAdministratorGivesUserTheRole(string $user, string $role): void
{
$admin = $this->featureContext->getAdminUsername();
$password = $this->featureContext->getAdminPassword();
$headers = [];
$baseUrl = $this->featureContext->getBaseUrl();
if (!str_ends_with($baseUrl, '/')) {
$baseUrl .= '/';
}
// get the roles list first
$fullUrl = $baseUrl . "api/v0/settings/roles-list";
$this->featureContext->setResponse(HttpRequestHelper::post($fullUrl, "", $admin, $password, $headers, "{}"));
if ($this->featureContext->getResponse()) {
$rawBody = $this->featureContext->getResponse()->getBody()->getContents();
}
$bundles = [];
if (isset(\json_decode($rawBody, true)["bundles"])) {
$bundles = \json_decode($rawBody, true)["bundles"];
}
$roleToAssign = "";
foreach($bundles as $bundle => $value) {
// find the selected role
if ($value["displayName"] === $role) {
$roleToAssign = $value;
}
}
Assert::assertNotEmpty($roleToAssign, "The selected role $role could not be found");
// get the accounts list first
$fullUrl = $baseUrl . "api/v0/accounts/accounts-list";
$this->featureContext->setResponse(HttpRequestHelper::post($fullUrl, "", $admin, $password, $headers, "{}"));
if ($this->featureContext->getResponse()) {
$rawBody = $this->featureContext->getResponse()->getBody()->getContents();
}
$accounts = [];
if (isset(\json_decode($rawBody, true)["accounts"])) {
$accounts = \json_decode($rawBody, true)["accounts"];
}
$accountToChange = "";
foreach($accounts as $account) {
// find the selected user
if ($account["preferredName"] === $user) {
$accountToChange = $account;
}
}
Assert::assertNotEmpty($accountToChange, "The seleted account $user does not exist");
// set the new role
$fullUrl = $baseUrl . "api/v0/settings/assignments-add";
$body = json_encode(["account_uuid" => $accountToChange["id"], "role_id" => $roleToAssign["id"]]);
$this->featureContext->setResponse(HttpRequestHelper::post($fullUrl, "", $admin, $password, $headers, $body));
if ($this->featureContext->getResponse()) {
$rawBody = $this->featureContext->getResponse()->getBody()->getContents();
}
$assignment = [];
if (isset(\json_decode($rawBody, true)["assignment"])) {
$assignment = \json_decode($rawBody, true)["assignment"];
}
Assert::assertEquals($accountToChange["id"], $assignment["accountUuid"]);
Assert::assertEquals($roleToAssign["id"], $assignment["roleId"]);
}
/**
* Remember the available Spaces
*
* @return void
*/
public function rememberTheAvailableSpaces(): void
{
$rawBody = $this->featureContext->getResponse()->getBody()->getContents();
$drives = json_decode($rawBody, true);
if (isset($drives["value"])) {
$drives = $drives["value"];
}
Assert::assertArrayHasKey(0, $drives, "No drives were found on that endpoint");
$spaces = [];
foreach($drives as $drive) {
$spaces[$drive["name"]] = $drive;
}
$this->setAvailableSpaces($spaces);
Assert::assertNotEmpty($spaces, "No spaces have been found");
}
/**
* @When /^user "([^"]*)" lists the content of the space with the name "([^"]*)" using the WebDav Api$/
*
* @param $user
* @param $name
* @return void
*/
public function theUserListsTheContentOfAPersonalSpaceRootUsingTheWebDAvApi($user, $name): void
{
$spaceId = $this->getAvailableSpaces()[$name]["id"];
$spaceWebDavUrl = $this->getAvailableSpaces()[$name]["root"]["webDavUrl"];
$this->featureContext->setResponse(
$this->sendPropfindRequestToUrl(
$spaceWebDavUrl,
$user,
$this->featureContext->getPasswordForUser($user),
"",
[],
[],
[]
)
);
$this->setResponseSpaceId($spaceId);
$this->setResponseXml(HttpRequestHelper::parseResponseAsXml($this->featureContext->getResponse())
);
}
/**
* @Then /^the (?:propfind|search) result of the space should (not|)\s?contain these (?:files|entries):$/
*
* @param string $user
* @param string $shouldOrNot (not|)
* @param TableNode $expectedFiles
*
* @return void
* @throws Exception
*/
public function thePropfindResultShouldContainEntries(
$shouldOrNot,
TableNode $expectedFiles
) {
$this->propfindResultShouldContainEntries(
$shouldOrNot,
$expectedFiles,
);
}
/**
* @Then the json responded should contain these key and value pairs
*
* @param TableNode $table
*
* @return void
*/
public function jsonRespondedShouldContain(TableNode $table) {
$this->featureContext->verifyTableNodeColumns($table, ['key', 'value']);
$responseJson = json_decode($this->featureContext->getResponse()->getBody(), true);
foreach ($table->getHash() as $row) {
$expected = [$row["key"] => $row["value"]];
Assert::assertEquals($row["value"], $responseJson[$row["key"]]);
}
}
/**
* @param string $shouldOrNot (not|)
* @param TableNode $expectedFiles
* @param string|null $user
*
* @return void
* @throws Exception
*/
public function propfindResultShouldContainEntries(
$shouldOrNot,
TableNode $expectedFiles
) {
$this->verifyTableNodeColumnsCount($expectedFiles, 1);
$elementRows = $expectedFiles->getRows();
$should = ($shouldOrNot !== "not");
foreach ($elementRows as $expectedFile) {
$fileFound = $this->findEntryFromPropfindResponse(
$expectedFile[0]
);
if ($should) {
Assert::assertNotEmpty(
$fileFound,
"response does not contain the entry '$expectedFile[0]'"
);
} else {
Assert::assertFalse(
$fileFound,
"response does contain the entry '$expectedFile[0]' but should not"
);
}
}
}
/**
* Verify that the tableNode contains expected number of columns
*
* @param TableNode $table
* @param int $count
*
* @return void
* @throws Exception
*/
public function verifyTableNodeColumnsCount($table, $count) {
if (!($table instanceof TableNode)) {
throw new Exception("TableNode expected but got " . \gettype($table));
}
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");
}
}
/**
* parses a PROPFIND response from $this->response into xml
* and returns found search results if found else returns false
*
* @param string|null $entryNameToSearch
* @return string|array|boolean
* string if $entryNameToSearch is given and is found
* array if $entryNameToSearch is not given
* boolean false if $entryNameToSearch is given and is not found
*/
public function findEntryFromPropfindResponse(
string $entryNameToSearch = null
) {
$spaceId = $this->getResponseSpaceId();
//if we are using that step the second time in a scenario e.g. 'But ... should not'
//then don't parse the result again, because the result in a ResponseInterface
if (empty($this->getResponseXml())) {
$this->setResponseXml(
HttpRequestHelper::parseResponseAsXml($this->featureContext->getResponse())
);
}
Assert::assertNotEmpty($this->getResponseXml(), __METHOD__ . ' Response is empty');
Assert::assertNotEmpty($spaceId, __METHOD__ . ' SpaceId is empty');
// trim any leading "/" passed by the caller, we can just match the "raw" name
$trimmedEntryNameToSearch = \trim($entryNameToSearch, "/");
// topWebDavPath should be something like /remote.php/webdav/ or
// /remote.php/dav/files/alice/
$topWebDavPath = "/" . "dav/spaces/" . $spaceId . "/";
Assert::assertIsArray(
$this->responseXml,
__METHOD__ . " responseXml for space $spaceId is not an array"
);
Assert::assertArrayHasKey(
"value",
$this->responseXml,
__METHOD__ . " responseXml for space $spaceId does not have key 'value'"
);
$multistatusResults = $this->responseXml["value"];
$results = [];
if ($multistatusResults !== null) {
foreach ($multistatusResults as $multistatusResult) {
$entryPath = $multistatusResult['value'][0]['value'];
$entryName = \str_replace($topWebDavPath, "", $entryPath);
$entryName = \rawurldecode($entryName);
$entryName = \trim($entryName, "/");
if ($trimmedEntryNameToSearch === $entryName) {
return $multistatusResult;
}
$results[] = $entryName;
}
}
if ($entryNameToSearch === null) {
return $results;
}
return false;
}
}