mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-09-12 21:58:58 -04:00
test(api): update php test dependencies (#3335)
* test: update php dependencies Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com> * test: use custom tus client Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com> * test: fix php code style and commands Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com> * test: use doctrine coding standard Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com> * test: convert headers value to string Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com> * test: initialize phpunit config Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com> * test: fix tus upload Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com> * test: fix php code style Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com> --------- Signed-off-by: Saw-jan <saw.jan.grg3e@gmail.com>
This commit is contained in:
1 parent
ec4d5b7e6b
commit
cc8a161fce
12 files changed
+644
-137
No files matched your search
@@ -36,7 +36,6 @@ vendor-bin/**/composer.lock
|
||||
vendor-php
|
||||
|
||||
# API acceptance tests - auto-generated files
|
||||
.php-cs-fixer.cache
|
||||
suite-logs
|
||||
tests/acceptance/filesForUpload/filesWithVirus/
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
$dirToParse = 'tests/acceptance/';
|
||||
$dirIterator = new DirectoryIterator(__DIR__ . '/' . $dirToParse);
|
||||
|
||||
$excludeDirs = [
|
||||
'node_modules',
|
||||
'vendor-php'
|
||||
];
|
||||
|
||||
$finder = PhpCsFixer\Finder::create()
|
||||
->exclude($excludeDirs)
|
||||
->in(__DIR__);
|
||||
|
||||
$ocRule = (new OC\CodingStandard\Config())->getRules();
|
||||
$config = new PhpCsFixer\Config();
|
||||
$config->setFinder($finder)
|
||||
->setIndent("\t")
|
||||
->setRules(
|
||||
array_merge(
|
||||
$ocRule,
|
||||
[
|
||||
"return_type_declaration" => [
|
||||
"space_before" => "none",
|
||||
],
|
||||
'single_space_around_construct' => true
|
||||
]
|
||||
)
|
||||
);
|
||||
$config->setFinder($finder);
|
||||
return $config;
|
||||
+16
-42
@@ -920,30 +920,6 @@ def buildOpencloudBinaryForTesting(ctx):
|
||||
}
|
||||
return [pipeline]
|
||||
|
||||
def vendorbinCodestyle(phpVersion):
|
||||
return [{
|
||||
"name": "vendorbin-codestyle",
|
||||
"image": OC_CI_PHP % phpVersion,
|
||||
"environment": {
|
||||
"COMPOSER_HOME": "%s/.cache/composer" % dirs["base"],
|
||||
},
|
||||
"commands": [
|
||||
"make vendor-bin-codestyle",
|
||||
],
|
||||
}]
|
||||
|
||||
def vendorbinCodesniffer(phpVersion):
|
||||
return [{
|
||||
"name": "vendorbin-codesniffer",
|
||||
"image": OC_CI_PHP % phpVersion,
|
||||
"environment": {
|
||||
"COMPOSER_HOME": "%s/.cache/composer" % dirs["base"],
|
||||
},
|
||||
"commands": [
|
||||
"make vendor-bin-codesniffer",
|
||||
],
|
||||
}]
|
||||
|
||||
def checkTestSuitesInExpectedFailures(ctx):
|
||||
return [{
|
||||
"name": "check-suites-in-expected-failures",
|
||||
@@ -1031,24 +1007,22 @@ def codestyle(ctx):
|
||||
|
||||
result = {
|
||||
"name": name,
|
||||
"steps": vendorbinCodestyle(phpVersion) +
|
||||
vendorbinCodesniffer(phpVersion) +
|
||||
[
|
||||
{
|
||||
"name": "php-style",
|
||||
"image": OC_CI_PHP % phpVersion,
|
||||
"commands": [
|
||||
"make test-php-style",
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "check-env-var-annotations",
|
||||
"image": OC_CI_PHP % phpVersion,
|
||||
"commands": [
|
||||
"make check-env-var-annotations",
|
||||
],
|
||||
},
|
||||
],
|
||||
"steps": [
|
||||
{
|
||||
"name": "php-style",
|
||||
"image": OC_CI_PHP % phpVersion,
|
||||
"commands": [
|
||||
"make test-php-style",
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "check-env-var-annotations",
|
||||
"image": OC_CI_PHP % phpVersion,
|
||||
"commands": [
|
||||
"make check-env-var-annotations",
|
||||
],
|
||||
},
|
||||
],
|
||||
"depends_on": [],
|
||||
"when": [
|
||||
event["base"],
|
||||
|
||||
@@ -68,11 +68,8 @@ OC_MODULES = \
|
||||
protogen
|
||||
|
||||
# bin file definitions
|
||||
PHP_CS_FIXER=php -d zend.enable_gc=0 vendor-bin/opencloud-codestyle/vendor/bin/php-cs-fixer
|
||||
PHP_CODESNIFFER=vendor-bin/php_codesniffer/vendor/bin/phpcs
|
||||
PHP_CODEBEAUTIFIER=vendor-bin/php_codesniffer/vendor/bin/phpcbf
|
||||
PHAN=php -d zend.enable_gc=0 vendor-bin/phan/vendor/bin/phan
|
||||
PHPSTAN=php -d zend.enable_gc=0 vendor-bin/phpstan/vendor/bin/phpstan
|
||||
PHP_CODESNIFFER=vendor-bin/opencloud-codestyle/vendor/bin/phpcs
|
||||
PHP_CODEBEAUTIFIER=vendor-bin/opencloud-codestyle/vendor/bin/phpcbf
|
||||
|
||||
ifneq (, $(shell command -v go 2> /dev/null)) # suppress `command not found warnings` for non go targets in CI
|
||||
include .bingo/Variables.mk
|
||||
@@ -319,30 +316,15 @@ ci-format: $(BUILDIFIER)
|
||||
$(BUILDIFIER) --mode=fix .woodpecker.star
|
||||
|
||||
.PHONY: test-php-style
|
||||
test-php-style: vendor-bin/opencloud-codestyle/vendor vendor-bin/php_codesniffer/vendor
|
||||
$(PHP_CS_FIXER) fix -v --diff --allow-risky yes --dry-run
|
||||
test-php-style: vendor-bin/opencloud-codestyle/vendor
|
||||
$(PHP_CODESNIFFER) --cache --runtime-set ignore_warnings_on_exit --standard=phpcs.xml tests/acceptance tests/acceptance/TestHelpers
|
||||
|
||||
.PHONY: test-php-style-fix
|
||||
test-php-style-fix: vendor-bin/opencloud-codestyle/vendor
|
||||
$(PHP_CS_FIXER) fix -v --diff --allow-risky yes
|
||||
$(PHP_CODEBEAUTIFIER) --cache --runtime-set ignore_warnings_on_exit --standard=phpcs.xml tests/acceptance
|
||||
|
||||
.PHONY: vendor-bin-codestyle
|
||||
vendor-bin-codestyle: vendor-bin/opencloud-codestyle/vendor
|
||||
|
||||
.PHONY: vendor-bin-codesniffer
|
||||
vendor-bin-codesniffer: vendor-bin/php_codesniffer/vendor
|
||||
|
||||
vendor-bin/opencloud-codestyle/vendor: vendor/bamarni/composer-bin-plugin vendor-bin/opencloud-codestyle/composer.lock
|
||||
composer bin opencloud-codestyle install --no-progress
|
||||
|
||||
vendor-bin/opencloud-codestyle/composer.lock: vendor-bin/opencloud-codestyle/composer.json
|
||||
@echo opencloud-codestyle composer.lock is not up to date.
|
||||
|
||||
vendor-bin/php_codesniffer/vendor: vendor/bamarni/composer-bin-plugin vendor-bin/php_codesniffer/composer.lock
|
||||
composer bin php_codesniffer install --no-progress
|
||||
|
||||
vendor-bin/php_codesniffer/composer.lock: vendor-bin/php_codesniffer/composer.json
|
||||
@echo php_codesniffer composer.lock is not up to date.
|
||||
|
||||
+4
-3
@@ -2,7 +2,7 @@
|
||||
"name": "opencloud-eu/opencloud",
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "8.3"
|
||||
"php": "8.4"
|
||||
},
|
||||
"vendor-dir": "./vendor-php",
|
||||
"allow-plugins": {
|
||||
@@ -11,11 +11,12 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-simplexml": "*",
|
||||
"bamarni/composer-bin-plugin": "^1.8"
|
||||
"bamarni/composer-bin-plugin": "^1.9"
|
||||
},
|
||||
"extra": {
|
||||
"bamarni-bin": {
|
||||
"bin-links": false
|
||||
"bin-links": false,
|
||||
"forward-command": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -463,6 +463,10 @@ class HttpRequestHelper {
|
||||
if ($xRequestId !== '') {
|
||||
$headers['X-Request-ID'] = $xRequestId;
|
||||
}
|
||||
|
||||
// Guzzle 8 expects all header values to be strings or arrays of strings.
|
||||
$headers = array_map('strval', $headers);
|
||||
|
||||
if (\is_array($body)) {
|
||||
// When creating the client, it is possible to set 'form_params' and
|
||||
// the Client constructor sorts out doing this http_build_query stuff.
|
||||
|
||||
@@ -0,0 +1,580 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* @author Sajan Gurung <sajan@jankaritech.com>
|
||||
* @copyright Copyright (c) 2026 Sajan Gurung sajan@jankaritech.com
|
||||
*
|
||||
* 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/>
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Most of the code is copied from https://github.com/ankitpokhrel/tus-php/blob/main/src/Tus/Client.php
|
||||
*/
|
||||
|
||||
namespace TestHelpers;
|
||||
|
||||
use GuzzleHttp\Client as GuzzleClient;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
use Symfony\Component\HttpFoundation\Response as HttpResponse;
|
||||
|
||||
/**
|
||||
* TUS client for uploading files using the TUS protocol.
|
||||
*/
|
||||
class TUSClient {
|
||||
/**
|
||||
* @const string Tus protocol version.
|
||||
*/
|
||||
public const TUS_PROTOCOL_VERSION = '1.0.0';
|
||||
|
||||
/**
|
||||
* @const string Header Content Type
|
||||
*/
|
||||
protected const HEADER_CONTENT_TYPE = 'application/offset+octet-stream';
|
||||
|
||||
/**
|
||||
* @const Input stream
|
||||
*/
|
||||
public const INPUT_STREAM = 'php://input';
|
||||
|
||||
/**
|
||||
* @const Read binary mode
|
||||
*/
|
||||
public const READ_BINARY = 'rb';
|
||||
|
||||
/**
|
||||
* @var GuzzleClient
|
||||
*/
|
||||
protected $client;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $apiPath = '/files';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $filePath;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $fileSize = 0;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fileName;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $checksum;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $partialOffset = -1;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $checksumAlgorithm = 'sha256';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $metadata = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $headers = [];
|
||||
|
||||
/**
|
||||
* Client constructor.
|
||||
*
|
||||
* @param string $baseUri
|
||||
* @param array $options
|
||||
*
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function __construct(string $baseUri, array $options = []) {
|
||||
$this->headers = $options['headers'] ?? [];
|
||||
$options['headers'] = [
|
||||
'Tus-Resumable' => self::TUS_PROTOCOL_VERSION,
|
||||
] + ($this->headers);
|
||||
|
||||
$this->client = new GuzzleClient(
|
||||
['base_uri' => $baseUri] + $options
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get guzzle client.
|
||||
*
|
||||
* @return GuzzleClient
|
||||
*/
|
||||
public function getClient(): GuzzleClient {
|
||||
return $this->client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file size.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getFileSize(): int {
|
||||
return $this->fileSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get file path.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getFilePath(): ?string {
|
||||
return $this->filePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get url.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getUrl(): ?string {
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set API path.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setApiPath(string $path): self {
|
||||
$this->apiPath = $path;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set checksum algorithm.
|
||||
*
|
||||
* @param string $algorithm
|
||||
*
|
||||
* @return Client
|
||||
*/
|
||||
public function setChecksumAlgorithm(string $algorithm): self {
|
||||
$this->checksumAlgorithm = $algorithm;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get checksum algorithm.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getChecksumAlgorithm(): string {
|
||||
return $this->checksumAlgorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set checksum.
|
||||
*
|
||||
* @param string $checksum
|
||||
*
|
||||
* @return Client
|
||||
*/
|
||||
public function setChecksum(string $checksum): self {
|
||||
$this->checksum = $checksum;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get checksum.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getChecksum(): string {
|
||||
if (empty($this->checksum)) {
|
||||
$this->setChecksum(hash_file($this->getChecksumAlgorithm(), $this->getFilePath()));
|
||||
}
|
||||
|
||||
return $this->checksum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get upload checksum header.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getUploadChecksumHeader(): string {
|
||||
return $this->getChecksumAlgorithm() . ' ' . base64_encode($this->getChecksum());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set metadata.
|
||||
*
|
||||
* @param array $items
|
||||
*
|
||||
* @return Client
|
||||
*/
|
||||
public function setMetadata(array $items): self {
|
||||
$items = array_map('base64_encode', $items);
|
||||
|
||||
$this->metadata = $items;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get metadata.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMetadata(): array {
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add metadata.
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
*
|
||||
* @return Client
|
||||
*/
|
||||
public function addMetadata(string $key, string $value): self {
|
||||
$this->metadata[$key] = base64_encode($value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get metadata for Upload-Metadata header.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getUploadMetadataHeader(): string {
|
||||
$metadata = [];
|
||||
|
||||
foreach ($this->getMetadata() as $key => $value) {
|
||||
$metadata[] = "{$key} {$value}";
|
||||
}
|
||||
|
||||
return implode(',', $metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set file properties.
|
||||
*
|
||||
* @param string $file File path.
|
||||
* @param string|null $name File name.
|
||||
*
|
||||
* @return Client
|
||||
*/
|
||||
public function file(string $file, string $name = null): self {
|
||||
$this->filePath = $file;
|
||||
|
||||
if (! file_exists($file) || ! is_readable($file)) {
|
||||
throw new FileException('Cannot read file: ' . $file);
|
||||
}
|
||||
|
||||
$this->fileName = $name ?? basename($this->filePath);
|
||||
$this->fileSize = filesize($file);
|
||||
|
||||
$this->addMetadata('filename', $this->fileName);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle client exception during patch request.
|
||||
*
|
||||
* @param ClientException $e
|
||||
*
|
||||
* @return \Exception
|
||||
*/
|
||||
protected function handleClientException(ClientException $e) {
|
||||
$response = $e->getResponse();
|
||||
$statusCode = $response !== null ? $response->getStatusCode() : HttpResponse::HTTP_INTERNAL_SERVER_ERROR;
|
||||
|
||||
if ($statusCode === HttpResponse::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE) {
|
||||
return new FileException('The uploaded file is corrupt.');
|
||||
}
|
||||
|
||||
if ($statusCode === HttpResponse::HTTP_CONTINUE) {
|
||||
return new ConnectionException('Connection aborted by user.');
|
||||
}
|
||||
|
||||
if ($statusCode === HttpResponse::HTTP_UNSUPPORTED_MEDIA_TYPE) {
|
||||
return new TusException('Unsupported media types.');
|
||||
}
|
||||
|
||||
return new TusException((string) $response->getBody(), $statusCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send HEAD request.
|
||||
*
|
||||
* @throws FileException
|
||||
* @throws GuzzleException
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function sendHeadRequest(): int {
|
||||
if (!$this->getUrl()) {
|
||||
throw new FileException('Upload URL not found.');
|
||||
}
|
||||
|
||||
$response = $this->getClient()->head($this->getUrl());
|
||||
$statusCode = $response->getStatusCode();
|
||||
|
||||
if ($statusCode !== HttpResponse::HTTP_OK) {
|
||||
throw new FileException('File not found.');
|
||||
}
|
||||
|
||||
return (int) current($response->getHeader('upload-offset'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send PATCH request.
|
||||
*
|
||||
* @param int $bytes
|
||||
* @param int $offset
|
||||
*
|
||||
* @throws TusException
|
||||
* @throws FileException
|
||||
* @throws GuzzleException
|
||||
* @throws ConnectionException
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function sendPatchRequest(int $bytes, int $offset): int {
|
||||
$data = $this->getData($offset, $bytes);
|
||||
$headers = $this->headers + [
|
||||
'Content-Type' => self::HEADER_CONTENT_TYPE,
|
||||
'Content-Length' => (string)\strlen($data),
|
||||
'Upload-Checksum' => $this->getUploadChecksumHeader(),
|
||||
'Upload-Offset' => (string)$offset,
|
||||
];
|
||||
|
||||
try {
|
||||
$response = $this->getClient()->patch(
|
||||
$this->getUrl(),
|
||||
[
|
||||
'body' => $data,
|
||||
'headers' => $headers,
|
||||
]
|
||||
);
|
||||
|
||||
return (int) current($response->getHeader('upload-offset'));
|
||||
} catch (ClientException $e) {
|
||||
throw $this->handleClientException($e);
|
||||
} catch (ConnectException $e) {
|
||||
throw new ConnectionException("Couldn't connect to server.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if file to read exists.
|
||||
*
|
||||
* @param string $filePath
|
||||
* @param string $mode
|
||||
*
|
||||
* @throws FileException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function exists(string $filePath, string $mode = self::READ_BINARY): bool {
|
||||
if ($filePath === self::INPUT_STREAM) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($mode === self::READ_BINARY && ! file_exists($filePath)) {
|
||||
throw new FileException('File not found.');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get X bytes of data from file.
|
||||
*
|
||||
* @param int $offset
|
||||
* @param int $bytes
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getData(int $offset, int $bytes): string {
|
||||
$filePath = $this->getFilePath();
|
||||
$mode = self::READ_BINARY;
|
||||
|
||||
$this->exists($filePath, $mode);
|
||||
$handle = @fopen($filePath, $mode);
|
||||
|
||||
if ($handle === false) {
|
||||
throw new FileException("Unable to open $filePath.");
|
||||
}
|
||||
|
||||
$position = fseek($handle, $offset, SEEK_SET);
|
||||
if ($position === -1) {
|
||||
throw new FileException('Cannot move pointer to desired position.');
|
||||
}
|
||||
|
||||
$data = fread($handle, $bytes);
|
||||
if ($data === false) {
|
||||
throw new FileException('Cannot read file.');
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload file.
|
||||
*
|
||||
* @param int $bytes Bytes to upload
|
||||
*
|
||||
* @throws TusException
|
||||
* @throws GuzzleException
|
||||
* @throws ConnectionException
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function upload(int $bytes = -1): int {
|
||||
$bytes = $bytes < 0 ? $this->getFileSize() : $bytes;
|
||||
$offset = $this->partialOffset < 0 ? 0 : $this->partialOffset;
|
||||
|
||||
try {
|
||||
// Check if this upload exists with HEAD request.
|
||||
$offset = $this->sendHeadRequest();
|
||||
} catch (FileException | ClientException $e) {
|
||||
// Create a new upload.
|
||||
$this->url = $this->create();
|
||||
} catch (ConnectException $e) {
|
||||
throw new ConnectionException("Couldn't connect to server.");
|
||||
}
|
||||
|
||||
// Now, resume upload with PATCH request.
|
||||
return $this->sendPatchRequest($bytes, $offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create resource with POST request.
|
||||
*
|
||||
* @throws FileException
|
||||
* @throws GuzzleException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function create(): string {
|
||||
return $this->createWithUpload(0)['location'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create resource with POST request and upload data using the creation-with-upload extension.
|
||||
*
|
||||
* @see https://tus.io/protocols/resumable-upload.html#creation-with-upload
|
||||
*
|
||||
* @param int $bytes -1 => all data; 0 => no data
|
||||
*
|
||||
* @throws GuzzleException
|
||||
*
|
||||
* @return array [
|
||||
* 'location' => string,
|
||||
* 'offset' => int
|
||||
* ]
|
||||
*/
|
||||
public function createWithUpload(int $bytes = -1): array {
|
||||
$bytes = $bytes < 0 ? $this->fileSize : $bytes;
|
||||
|
||||
$headers = $this->headers + [
|
||||
'Upload-Length' => (string)$this->fileSize,
|
||||
'Upload-Checksum' => $this->getUploadChecksumHeader(),
|
||||
'Upload-Metadata' => $this->getUploadMetadataHeader(),
|
||||
];
|
||||
|
||||
$data = '';
|
||||
if ($bytes > 0) {
|
||||
$data = $this->getData(0, $bytes);
|
||||
|
||||
$headers += [
|
||||
'Content-Type' => self::HEADER_CONTENT_TYPE,
|
||||
'Content-Length' => (string)\strlen($data),
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
$response = $this->getClient()->post(
|
||||
$this->apiPath,
|
||||
[
|
||||
'body' => $data,
|
||||
'headers' => $headers,
|
||||
]
|
||||
);
|
||||
} catch (ClientException $e) {
|
||||
$response = $e->getResponse();
|
||||
}
|
||||
|
||||
$statusCode = $response->getStatusCode();
|
||||
|
||||
if ($statusCode !== HttpResponse::HTTP_CREATED) {
|
||||
throw new FileException('Unable to create resource.');
|
||||
}
|
||||
|
||||
$uploadOffset = $bytes > 0 ? current($response->getHeader('upload-offset')) : 0;
|
||||
$uploadLocation = current($response->getHeader('location'));
|
||||
|
||||
return [
|
||||
'location' => $uploadLocation,
|
||||
'offset' => $uploadOffset,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* --------------------------------
|
||||
* Exceptions
|
||||
* --------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* File exception class.
|
||||
*/
|
||||
class FileException extends \RuntimeException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Connection exception class.
|
||||
*/
|
||||
class ConnectionException extends \Exception {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tus exception class.
|
||||
*/
|
||||
class TusException extends \Exception {
|
||||
}
|
||||
@@ -24,9 +24,6 @@ use Behat\Behat\Context\Context;
|
||||
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
|
||||
use Behat\Gherkin\Node\TableNode;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use TusPhp\Exception\ConnectionException;
|
||||
use TusPhp\Exception\TusException;
|
||||
use TusPhp\Tus\Client;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use TestHelpers\HttpRequestHelper;
|
||||
@@ -34,6 +31,9 @@ use TestHelpers\WebDavHelper;
|
||||
use TestHelpers\BehatHelper;
|
||||
use TestHelpers\UploadHelper;
|
||||
use TestHelpers\WaitHelper;
|
||||
use TestHelpers\TUSClient;
|
||||
use TestHelpers\TUSClient\TusException;
|
||||
use TestHelpers\TUSClient\ConnectionException;
|
||||
use Behat\Step\Given;
|
||||
use Behat\Step\When;
|
||||
|
||||
@@ -339,7 +339,7 @@ class TUSContext implements Context {
|
||||
$headers = \array_merge($headers, $checksumHeader);
|
||||
}
|
||||
|
||||
$client = new Client(
|
||||
$client = new TUSClient(
|
||||
$this->featureContext->getBaseUrl(),
|
||||
[
|
||||
'verify' => false,
|
||||
@@ -353,19 +353,18 @@ class TUSContext implements Context {
|
||||
$suffixPath = $spaceId ?: $this->featureContext->getPersonalSpaceIdForUser($user);
|
||||
}
|
||||
|
||||
$client->setChecksumAlgorithm('sha1');
|
||||
$sourceFile = UploadHelper::getAcceptanceTestsDir() . $source;
|
||||
$client->setApiPath(WebDavHelper::getDavPath($davPathVersion, $suffixPath));
|
||||
$client->setMetadata($uploadMetadata);
|
||||
$sourceFile = UploadHelper::getAcceptanceTestsDir() . $source;
|
||||
$client->setKey((string)rand())->file($sourceFile, $destination);
|
||||
$client->file($sourceFile, $destination);
|
||||
$this->featureContext->pauseUploadDelete();
|
||||
|
||||
if ($bytes !== null) {
|
||||
$client->file($sourceFile, $destination)->createWithUpload($client->getKey(), $bytes);
|
||||
$client->createWithUpload($bytes);
|
||||
} elseif (\filesize($sourceFile) === 0) {
|
||||
$client->file($sourceFile, $destination)->createWithUpload($client->getKey(), 0);
|
||||
$client->createWithUpload(0);
|
||||
} elseif ($noOfChunks === 1) {
|
||||
$client->file($sourceFile, $destination)->upload();
|
||||
$client->upload();
|
||||
} else {
|
||||
$bytesPerChunk = (int)\ceil(\filesize($sourceFile) / $noOfChunks);
|
||||
for ($i = 0; $i < $noOfChunks; $i++) {
|
||||
@@ -399,7 +398,7 @@ class TUSContext implements Context {
|
||||
);
|
||||
$this->featureContext->setLastUploadDeleteTime(\time());
|
||||
} catch (Exception $e) {
|
||||
Assert::assertStringContainsString('TusPhp\Exception\FileException: Unable to create resource', (string)$e);
|
||||
Assert::assertStringContainsString('FileException: Unable to create resource', (string)$e);
|
||||
}
|
||||
\unlink($temporaryFileName);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
*/
|
||||
|
||||
use Composer\Autoload\ClassLoader;
|
||||
use PHPUnit\TextUI\CliArguments\Builder as CliArgumentsBuilder;
|
||||
use PHPUnit\TextUI\Configuration\Registry as ConfigurationRegistry;
|
||||
use PHPUnit\TextUI\XmlConfiguration\DefaultConfiguration;
|
||||
|
||||
$classLoader = new ClassLoader();
|
||||
|
||||
@@ -53,3 +56,11 @@ if (!\defined('ACCEPTANCE_TEST_DIR_ON_REMOTE_SERVER')) {
|
||||
if (!\defined('TEMPORARY_STORAGE_DIR_ON_REMOTE_SERVER')) {
|
||||
\define('TEMPORARY_STORAGE_DIR_ON_REMOTE_SERVER', ACCEPTANCE_TEST_DIR_ON_REMOTE_SERVER . '/server_tmp');
|
||||
}
|
||||
|
||||
// With PHPUnit 10+, initialize the PHPUnit configuration registry with default values
|
||||
// as config initialization never happens when using behat test runner.
|
||||
// This is needed to be able to use some assertions like 'assertSame' from PHPUnit.
|
||||
ConfigurationRegistry::init(
|
||||
(new CliArgumentsBuilder())->fromParameters(['phpunit']),
|
||||
DefaultConfiguration::create()
|
||||
);
|
||||
@@ -1,27 +1,15 @@
|
||||
{
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "8.3"
|
||||
},
|
||||
"allow-plugins": {
|
||||
"composer/package-versions-deprecated": true
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"behat/behat": "^3.24",
|
||||
"behat/gherkin": "^4.9",
|
||||
"behat/mink": "1.7.1",
|
||||
"friends-of-behat/mink-extension": "^2.7",
|
||||
"behat/behat": "^3.32",
|
||||
"behat/gherkin": "^4.17",
|
||||
"ciaranmcnulty/behat-stepthroughextension": "dev-master",
|
||||
"rdx/behat-variables": "^1.2",
|
||||
"sensiolabs/behat-page-object-extension": "^2.3",
|
||||
"symfony/translation": "^5.4",
|
||||
"sabre/xml": "^2.2",
|
||||
"guzzlehttp/guzzle": "^7.7",
|
||||
"phpunit/phpunit": "^9.6",
|
||||
"laminas/laminas-ldap": "^2.15",
|
||||
"ankitpokhrel/tus-php": "^2.4",
|
||||
"swaggest/json-schema": "^0.12.42",
|
||||
"splitbrain/php-archive": "^1.3"
|
||||
"sabre/xml": "^4.1",
|
||||
"guzzlehttp/guzzle": "^8.0",
|
||||
"phpunit/phpunit": "^13.3",
|
||||
"laminas/laminas-ldap": "^2.20",
|
||||
"swaggest/json-schema": "^0.12",
|
||||
"splitbrain/php-archive": "^1.5",
|
||||
"symfony/http-foundation": "^8.1"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
{
|
||||
"require": {
|
||||
"owncloud/coding-standard": "^5.3"
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^14.0"
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"require": {
|
||||
"squizlabs/php_codesniffer": "^3.7"
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user