mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-07-13 00:52:01 -04:00
test(graph): acceptance tests for MS Graph colon-syntax path lookup
Cover the rewrite shapes the middleware handles end-to-end against a real OpenCloud server: root-anchored, item-anchored, deep paths, trailing colon, and the "/<path>:/<suffix>" sub-route form. Also assert that NOT_FOUND and PERMISSION_DENIED both collapse to 404. The /permissions sub-route is registered only at /v1beta1, and the v1beta1 GetDriveItem handler is share-jail-only, so the v1beta1 mount of the middleware is exercised through the permissions scenario, since there is no other v1beta1 endpoint that works for regular personal-drive items.
This commit is contained in:
committed by
Ralf Haferkamp
parent
6343f7e861
commit
3b42c6250d
@@ -3334,4 +3334,169 @@ class GraphContext implements Context {
|
||||
$response = $this->unmarkFavorite($user, $itemId);
|
||||
$this->featureContext->theHTTPStatusCodeShouldBe(204, '', $response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a colon-syntax path segment so slashes survive but the
|
||||
* structural ":" delimiters of the Graph URL remain literal.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function encodeColonPathSegment(string $path): string {
|
||||
$path = \ltrim($path, '/');
|
||||
$parts = \explode('/', $path);
|
||||
$encoded = \array_map('rawurlencode', $parts);
|
||||
return \implode('/', $encoded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a Graph API request and capture the response on FeatureContext.
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $method
|
||||
* @param string $relativeUrl
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function sendGraphRequestAndCaptureResponse(
|
||||
string $user,
|
||||
string $method,
|
||||
string $relativeUrl
|
||||
): void {
|
||||
$response = $this->featureContext->sendingToWithDirectUrl($user, $method, $relativeUrl);
|
||||
$this->featureContext->setResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^user "([^"]*)" gets the drive item with colon path "([^"]*)" of space "([^"]*)" using the Graph API version "(v1\.0|v1beta1)"$/
|
||||
*
|
||||
* Hits /graph/{version}/drives/{driveID}/root:/{path}, exercising the
|
||||
* colon-syntax path lookup middleware (root-anchored, no suffix).
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $path
|
||||
* @param string $spaceName
|
||||
* @param string $apiVersion
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function userGetsDriveItemWithColonPathOfSpace(
|
||||
string $user,
|
||||
string $path,
|
||||
string $spaceName,
|
||||
string $apiVersion
|
||||
): void {
|
||||
$driveId = $this->spacesContext->getSpaceIdByName($user, $spaceName);
|
||||
$encoded = $this->encodeColonPathSegment($path);
|
||||
$url = "/graph/$apiVersion/drives/$driveId/root:/$encoded";
|
||||
$this->sendGraphRequestAndCaptureResponse($user, "GET", $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^user "([^"]*)" gets the drive item with colon path "([^"]*)" of space "([^"]*)" with trailing colon using the Graph API version "(v1\.0|v1beta1)"$/
|
||||
*
|
||||
* Hits /graph/{version}/drives/{driveID}/root:/{path}: with the optional
|
||||
* trailing ":" — verifies the middleware accepts both forms.
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $path
|
||||
* @param string $spaceName
|
||||
* @param string $apiVersion
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function userGetsDriveItemWithColonPathOfSpaceWithTrailingColon(
|
||||
string $user,
|
||||
string $path,
|
||||
string $spaceName,
|
||||
string $apiVersion
|
||||
): void {
|
||||
$driveId = $this->spacesContext->getSpaceIdByName($user, $spaceName);
|
||||
$encoded = $this->encodeColonPathSegment($path);
|
||||
$url = "/graph/$apiVersion/drives/$driveId/root:/$encoded:";
|
||||
$this->sendGraphRequestAndCaptureResponse($user, "GET", $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^user "([^"]*)" gets the drive item with colon path "([^"]*)" relative to folder "([^"]*)" of space "([^"]*)" using the Graph API version "(v1\.0|v1beta1)"$/
|
||||
*
|
||||
* Hits /graph/{version}/drives/{driveID}/items/{itemID}:/{relPath}, the
|
||||
* item-anchored colon-syntax form.
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $relPath
|
||||
* @param string $folderName
|
||||
* @param string $spaceName
|
||||
* @param string $apiVersion
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function userGetsDriveItemWithColonPathRelativeToFolderOfSpace(
|
||||
string $user,
|
||||
string $relPath,
|
||||
string $folderName,
|
||||
string $spaceName,
|
||||
string $apiVersion
|
||||
): void {
|
||||
$driveId = $this->spacesContext->getSpaceIdByName($user, $spaceName);
|
||||
$folderId = $this->spacesContext->getResourceId($user, $spaceName, $folderName);
|
||||
$encoded = $this->encodeColonPathSegment($relPath);
|
||||
$url = "/graph/$apiVersion/drives/$driveId/items/$folderId:/$encoded";
|
||||
$this->sendGraphRequestAndCaptureResponse($user, "GET", $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^user "([^"]*)" lists permissions of the drive item with colon path "([^"]*)" of space "([^"]*)" using the Graph API version "(v1\.0|v1beta1)"$/
|
||||
*
|
||||
* Hits /graph/{version}/drives/{driveID}/root:/{path}:/permissions, the
|
||||
* "colon path with suffix" form (root-anchored colon path + canonical
|
||||
* sub-route).
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $path
|
||||
* @param string $spaceName
|
||||
* @param string $apiVersion
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function userListsPermissionsOfDriveItemWithColonPathOfSpace(
|
||||
string $user,
|
||||
string $path,
|
||||
string $spaceName,
|
||||
string $apiVersion
|
||||
): void {
|
||||
$driveId = $this->spacesContext->getSpaceIdByName($user, $spaceName);
|
||||
$encoded = $this->encodeColonPathSegment($path);
|
||||
$url = "/graph/$apiVersion/drives/$driveId/root:/$encoded:/permissions";
|
||||
$this->sendGraphRequestAndCaptureResponse($user, "GET", $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When /^user "([^"]*)" gets the drive item with colon path "([^"]*)" of the personal space of "([^"]*)" using the Graph API version "(v1\.0|v1beta1)"$/
|
||||
*
|
||||
* Same as userGetsDriveItemWithColonPathOfSpace, but the request is
|
||||
* issued by :user against the personal space drive ID of :owner. Used
|
||||
* for security tests where one user attempts to reach another user's
|
||||
* resource via colon syntax — should be indistinguishable from a
|
||||
* not-found result.
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $path
|
||||
* @param string $owner
|
||||
* @param string $apiVersion
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function userGetsDriveItemWithColonPathOfPersonalSpaceOf(
|
||||
string $user,
|
||||
string $path,
|
||||
string $owner,
|
||||
string $apiVersion
|
||||
): void {
|
||||
$driveId = $this->spacesContext->getSpaceIdByName($owner, "Personal");
|
||||
$encoded = $this->encodeColonPathSegment($path);
|
||||
$url = "/graph/$apiVersion/drives/$driveId/root:/$encoded";
|
||||
$this->sendGraphRequestAndCaptureResponse($user, "GET", $url);
|
||||
}
|
||||
}
|
||||
|
||||
114
tests/acceptance/features/apiGraph/colonSyntaxPathLookup.feature
Normal file
114
tests/acceptance/features/apiGraph/colonSyntaxPathLookup.feature
Normal file
@@ -0,0 +1,114 @@
|
||||
Feature: colon-syntax path lookup on the Graph API
|
||||
As a client
|
||||
I want to address drive items by path using colon-syntax URLs on the Graph API
|
||||
So that I do not have to walk the path with successive lookups before issuing a request
|
||||
|
||||
The colon-syntax shapes recognised by the path-lookup middleware are:
|
||||
|
||||
/graph/{version}/drives/{driveID}/root:/<path>[:/<suffix>][:]
|
||||
/graph/{version}/drives/{driveID}/items/{itemID}:/<relativePath>[:/<suffix>][:]
|
||||
|
||||
Both /v1.0 and /v1beta1 are supported. NOT_FOUND and PERMISSION_DENIED
|
||||
collapse to 404 so the middleware never discloses the existence of
|
||||
resources the caller is not allowed to see.
|
||||
|
||||
Background:
|
||||
Given user "Alice" has been created with default attributes
|
||||
And user "Alice" has created folder "folder1"
|
||||
And user "Alice" has created folder "folder1/sub"
|
||||
And user "Alice" has uploaded file with content "hello" to "folder1/file.txt"
|
||||
And user "Alice" has uploaded file with content "deep" to "folder1/sub/deep.txt"
|
||||
|
||||
|
||||
Scenario: get a drive item by root-anchored colon path
|
||||
When user "Alice" gets the drive item with colon path "folder1/file.txt" of space "Personal" using the Graph API version "v1.0"
|
||||
Then the HTTP status code should be "200"
|
||||
And the JSON data of the response should match
|
||||
"""
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["id", "name", "parentReference"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^%file_id_pattern%$"
|
||||
},
|
||||
"name": {
|
||||
"const": "file.txt"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
Scenario: get a drive item by root-anchored colon path with a deep path
|
||||
When user "Alice" gets the drive item with colon path "folder1/sub/deep.txt" of space "Personal" using the Graph API version "v1.0"
|
||||
Then the HTTP status code should be "200"
|
||||
And the JSON data of the response should match
|
||||
"""
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["id", "name"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"const": "deep.txt"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
Scenario: get a drive item by root-anchored colon path with a trailing colon
|
||||
When user "Alice" gets the drive item with colon path "folder1/file.txt" of space "Personal" with trailing colon using the Graph API version "v1.0"
|
||||
Then the HTTP status code should be "200"
|
||||
And the JSON data of the response should match
|
||||
"""
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["id", "name"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"const": "file.txt"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
Scenario: get a drive item by item-anchored colon path
|
||||
When user "Alice" gets the drive item with colon path "file.txt" relative to folder "folder1" of space "Personal" using the Graph API version "v1.0"
|
||||
Then the HTTP status code should be "200"
|
||||
And the JSON data of the response should match
|
||||
"""
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["id", "name"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"const": "file.txt"
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
Scenario: list permissions of a drive item via colon path with a sub-route suffix on v1beta1
|
||||
# Exercises the "/<path>:/<suffix>" rewrite shape and, at the same time,
|
||||
# the v1beta1 mount of the middleware. The /permissions sub-route is only
|
||||
# registered at /v1beta1/, and the canonical /v1beta1 GetDriveItem
|
||||
# handler is share-jail-only, so this is the cleanest way to assert that
|
||||
# the v1beta1 colon-syntax path actually reaches a working handler for
|
||||
# regular drive items.
|
||||
When user "Alice" lists permissions of the drive item with colon path "folder1" of space "Personal" using the Graph API version "v1beta1"
|
||||
Then the HTTP status code should be "200"
|
||||
|
||||
|
||||
Scenario: non-existent colon path returns 404
|
||||
When user "Alice" gets the drive item with colon path "folder1/does-not-exist.txt" of space "Personal" using the Graph API version "v1.0"
|
||||
Then the HTTP status code should be "404"
|
||||
|
||||
|
||||
Scenario: another user cannot disclose existence of a resource via colon path
|
||||
Given user "Brian" has been created with default attributes
|
||||
When user "Brian" gets the drive item with colon path "folder1/file.txt" of the personal space of "Alice" using the Graph API version "v1.0"
|
||||
Then the HTTP status code should be "404"
|
||||
Reference in New Issue
Block a user