The app which enables the users to edit office documents from Nextcloud using ONLYOFFICE Document Server, allows multiple users to collaborate in real time and to save back those changes to Nextcloud
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
onlyoffice-nextcloud/tests/integration/behat/features/bootstrap/AdminContext.php

480 lines
18 KiB

<?php
declare(strict_types=1);
require __DIR__ . '/../../../../../vendor-bin/behat/vendor/autoload.php';
use Behat\Behat\Context\Context;
use Behat\Hook\AfterScenario;
use Behat\Step\Given;
use Behat\Step\Then;
use Behat\Step\When;
use Behat\Hook\BeforeSuite;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\ServerException;
use Behat\Gherkin\Node\TableNode;
use PHPUnit\Framework\Assert;
use Psr\Http\Message\ResponseInterface;
class AdminContext implements Context
{
const TEST_PASSWORD = "password";
private ResponseInterface $response;
private ?string $currentUser = null;
private array $createdUsers = [];
private array $createdTemplateIds = [];
private ?int $lastTemplateId = null;
private array $lastAddressSettings = [];
public function __construct(
private string $baseUrl,
private string $adminUser,
private string $adminPassword
) {}
#[BeforeSuite]
public static function createPHPUnitConfiguration(): void {
(new \PHPUnit\TextUI\Configuration\Builder())->build([]);
}
#[AfterScenario]
public function cleanUp(): void
{
$this->setCurrentUser($this->adminUser);
$this->cleanUpUsers();
$this->cleanUpTemplates();
$this->restoreValidDocsConnection();
}
#[Given('I am logged in as :username')]
public function iAmLoggedInAs(string $username): void
{
if ($username === 'admin') {
$this->setCurrentUser($this->adminUser);
} else {
$this->setCurrentUser($username);
}
}
// --- Settings steps ---
#[When('I save valid address settings')]
public function iSaveValidAddressSettings(): void
{
$this->lastAddressSettings = [
'documentserver' => getenv('ONLYOFFICE_DOCUMENT_SERVER_URL') ?: 'http://localhost:8080/',
'documentserverInternal' => getenv('ONLYOFFICE_DOCUMENT_SERVER_INTERNAL_URL') ?: 'http://localhost:8080/',
'storageUrl' => getenv('ONLYOFFICE_STORAGE_URL') ?: 'http://localhost/',
'verifyPeerOff' => 'false',
'secret' => getenv('ONLYOFFICE_SECRET') ?: 'secret',
'jwtHeader' => 'Authorization',
'demo' => 'false',
];
$this->sendFrontpageRequest('PUT', '/apps/onlyoffice/ajax/settings/address', $this->lastAddressSettings);
}
#[When('I save invalid address settings')]
public function iSaveInvalidAddressSettings(): void
{
$this->sendFrontpageRequest('PUT', '/apps/onlyoffice/ajax/settings/address', [
'documentserver' => 'http://invalid.onlyoffice.example/',
'documentserverInternal' => '',
'storageUrl' => '',
'verifyPeerOff' => 'false',
'secret' => '',
'jwtHeader' => 'Authorization',
'demo' => 'false',
]);
}
#[When('I save common settings')]
public function iSaveCommonSettings(): void
{
$this->sendFrontpageRequest('PUT', '/apps/onlyoffice/ajax/settings/common', [
'json' => [
'defFormats' => [],
'editFormats' => [],
'restrictExternalStorage' => false,
'sameTab' => false,
'enableSharing' => false,
'preview' => false,
'advanced' => false,
'cronChecker' => false,
'emailNotifications' => false,
'versionHistory' => false,
'chat' => false,
'compactHeader' => false,
'feedback' => false,
'forcesave' => false,
'liveViewOnShare' => false,
'help' => false,
'reviewDisplay' => 'original',
'theme' => 'default',
'unknownAuthor' => '',
],
]);
}
#[When('I save security settings')]
public function iSaveSecuritySettings(): void
{
$this->sendFrontpageRequest('PUT', '/apps/onlyoffice/ajax/settings/security', [
'json' => [
'plugins' => false,
'macros' => false,
'protection' => 'owner',
'watermarks' => [
'enabled' => false,
'text' => '',
],
],
]);
}
#[Then('the settings should be saved successfully')]
public function theSettingsShouldBeSavedSuccessfully(): void
{
Assert::assertSame(200, $this->response->getStatusCode());
$this->response->getBody()->rewind();
$body = json_decode($this->response->getBody()->getContents(), true);
Assert::assertEmpty($body['error'] ?? '', 'Settings save returned an error: ' . ($body['error'] ?? ''));
}
#[Then('the settings should report a connection error')]
public function theSettingsShouldReportAConnectionError(): void
{
Assert::assertSame(200, $this->response->getStatusCode());
$this->response->getBody()->rewind();
$body = json_decode($this->response->getBody()->getContents(), true);
Assert::assertNotEmpty($body['error'] ?? '', 'Expected a connection error but error field was empty');
}
#[Then('the settings should reflect the changes')]
public function theSettingsShouldReflectTheChanges(): void
{
$this->response->getBody()->rewind();
$body = json_decode($this->response->getBody()->getContents(), true);
Assert::assertSame($this->lastAddressSettings['documentserver'], $body['documentserver']);
Assert::assertSame($this->lastAddressSettings['documentserverInternal'], $body['documentserverInternal']);
Assert::assertSame($this->lastAddressSettings['storageUrl'], $body['storageUrl']);
Assert::assertSame($this->lastAddressSettings['jwtHeader'], $body['jwtHeader']);
}
#[Then('the common settings should be saved successfully')]
public function theCommonSettingsShouldBeSavedSuccessfully(): void
{
Assert::assertSame(200, $this->response->getStatusCode());
$this->response->getBody()->rewind();
$body = json_decode($this->response->getBody()->getContents(), true);
Assert::assertSame([], $body);
}
#[Then('the security settings should be saved successfully')]
public function theSecuritySettingsShouldBeSavedSuccessfully(): void
{
Assert::assertSame(200, $this->response->getStatusCode());
$this->response->getBody()->rewind();
$body = json_decode($this->response->getBody()->getContents(), true);
Assert::assertSame([], $body);
}
#[When('I clear the version history')]
public function iClearTheVersionHistory(): void
{
$this->sendFrontpageRequest('DELETE', '/apps/onlyoffice/ajax/settings/history');
}
#[Then('the version history should be cleared successfully')]
public function theVersionHistoryShouldBeClearedSuccessfully(): void
{
Assert::assertSame(200, $this->response->getStatusCode());
$this->response->getBody()->rewind();
$body = json_decode($this->response->getBody()->getContents(), true);
Assert::assertSame([], $body);
}
// --- Template steps ---
#[Given('there are no global templates')]
public function thereAreNoGlobalTemplates(): void
{
$this->sendFrontpageRequest('GET', 'apps/onlyoffice/ajax/template');
$content = $this->response->getBody()->getContents();
$templates = json_decode($content, true);
foreach ($templates as $template) {
$this->sendFrontpageRequest(
'DELETE',
'apps/onlyoffice/ajax/template',
['templateId' => $template['id']]
);
}
}
#[When('I upload the template :name as a file')]
public function iUploadTheTemplateAsAFile(string $name): void
{
$tmp = tempnam(sys_get_temp_dir(), 'behat_') . '.' . pathinfo($name, PATHINFO_EXTENSION);
file_put_contents($tmp, '');
$this->sendFrontpageRequest('POST', 'apps/onlyoffice/ajax/template', [
'multipart' => [
'name' => 'file',
'contents' => fopen($tmp, 'r'),
'filename' => $name,
],
]);
unlink($tmp);
$this->response->getBody()->rewind();
$body = json_decode($this->response->getBody()->getContents(), true);
if (is_array($body) && isset($body['id'])) {
$this->lastTemplateId = $body['id'];
$this->createdTemplateIds[] = $body['id'];
}
}
#[When('I retrieve the template list')]
public function iRetrieveTheTemplateList(): void
{
$this->sendFrontpageRequest('GET', 'apps/onlyoffice/ajax/template');
}
#[When('I delete the last uploaded template')]
public function iDeleteTheLastUploadedTemplate(): void
{
Assert::assertNotNull($this->lastTemplateId, 'No template was uploaded in this scenario');
$this->sendFrontpageRequest(
'DELETE',
'apps/onlyoffice/ajax/template',
['templateId' => $this->lastTemplateId]
);
}
#[When('I delete a non-existent template')]
public function iDeleteANonExistentTemplate(): void
{
$this->sendFrontpageRequest('DELETE', 'apps/onlyoffice/ajax/template', ['templateId' => 999999]);
}
#[Then('the template should be uploaded successfully with its metadata')]
public function theTemplateShouldBeUploadedSuccessfullyWithItsMetadata(): void
{
$this->response->getBody()->rewind();
$body = json_decode($this->response->getBody()->getContents(), true);
Assert::assertIsArray($body);
Assert::assertArrayNotHasKey('error', $body, 'Upload returned an error: ' . ($body['error'] ?? ''));
foreach (['id', 'name', 'type', 'icon'] as $field) {
Assert::assertArrayHasKey($field, $body, "Response is missing field \"$field\"");
}
}
#[Then('the upload should be rejected as unsupported')]
public function theUploadShouldBeRejectedAsUnsupported(): void
{
$this->response->getBody()->rewind();
$body = json_decode($this->response->getBody()->getContents(), true);
Assert::assertArrayHasKey('error', $body, 'Expected an unsupported-format error but none was returned');
Assert::assertNotEmpty($body['error']);
}
#[Then('the upload should be rejected as a duplicate')]
public function theUploadShouldBeRejectedAsADuplicate(): void
{
$this->response->getBody()->rewind();
$body = json_decode($this->response->getBody()->getContents(), true);
Assert::assertArrayHasKey('error', $body, 'Expected a duplicate error but none was returned');
Assert::assertNotEmpty($body['error']);
}
#[Then('the template list should be empty')]
public function theTemplateListShouldBeEmpty(): void
{
$this->response->getBody()->rewind();
$body = json_decode($this->response->getBody()->getContents(), true);
Assert::assertSame([], $body);
}
#[Then('the response should contain :count templates')]
public function theResponseShouldContainTemplates(int $count): void
{
$this->response->getBody()->rewind();
$body = json_decode($this->response->getBody()->getContents(), true);
Assert::assertCount($count, $body);
}
#[Then('each template should have the required metadata')]
public function eachTemplateShouldHaveTheRequiredMetadata(): void
{
$this->response->getBody()->rewind();
$body = json_decode($this->response->getBody()->getContents(), true);
foreach ($body as $template) {
foreach (['id', 'name', 'type', 'icon'] as $field) {
Assert::assertArrayHasKey($field, $template, "Template is missing field \"$field\"");
}
}
}
#[Then('the response should contain a template with name :name and type :type')]
public function theResponseShouldContainATemplateWithNameAndType(string $name, string $type): void
{
$this->response->getBody()->rewind();
$body = json_decode($this->response->getBody()->getContents(), true);
$found = array_filter($body, fn($t) => $t['name'] === $name);
Assert::assertNotEmpty($found, "No template found with name \"$name\"");
Assert::assertSame($type, array_values($found)[0]['type']);
}
#[Then('the template should be deleted successfully')]
public function theTemplateShouldBeDeletedSuccessfully(): void
{
$this->response->getBody()->rewind();
$body = json_decode($this->response->getBody()->getContents(), true);
Assert::assertSame([], $body, 'Expected empty response but got: ' . json_encode($body));
}
#[Then('the deletion should be rejected as not found')]
public function theDeletionShouldBeRejectedAsNotFound(): void
{
$this->response->getBody()->rewind();
$body = json_decode($this->response->getBody()->getContents(), true);
Assert::assertArrayHasKey('error', $body, 'Expected a not-found error but none was returned');
Assert::assertNotEmpty($body['error']);
}
#[Then('the last uploaded template should no longer exist')]
public function theLastUploadedTemplateShouldNoLongerExist(): void
{
Assert::assertNotNull($this->lastTemplateId);
$this->sendFrontpageRequest('GET', 'apps/onlyoffice/ajax/template');
$this->response->getBody()->rewind();
$body = json_decode($this->response->getBody()->getContents(), true);
$ids = array_column($body, 'id');
Assert::assertNotContains($this->lastTemplateId, $ids);
}
#[Given('the following global templates exist:')]
public function theFollowingGlobalTemplatesExist(TableNode $table): void
{
foreach ($table->getColumnsHash() as $row) {
$this->uploadTemplate($row['name']);
}
}
#[Given('a global template :name exists')]
public function aGlobalTemplateExists(string $name): void
{
$this->uploadTemplate($name);
}
private function uploadTemplate(string $name): void
{
$tmp = tempnam(sys_get_temp_dir(), 'behat_') . '.' . pathinfo($name, PATHINFO_EXTENSION);
file_put_contents($tmp, '');
$this->sendFrontpageRequest('POST', 'apps/onlyoffice/ajax/template', [
'multipart' => [
'name' => 'file',
'contents' => fopen($tmp, 'r'),
'filename' => $name,
],
]);
unlink($tmp);
Assert::assertSame($this->response->getStatusCode(), 200);
$body = json_decode($this->response->getBody()->getContents(), true);
Assert::assertArrayHasKey('id', $body);
$this->createdTemplateIds[] = $body['id'];
}
private function setCurrentUser(?string $user): ?string {
$currentUser = $this->currentUser;
$this->currentUser = $user;
return $currentUser;
}
private function sendFrontpageRequest(string $verb, string $url, TableNode|array|string|null $body = null, array $headers = [], array $options = []): void {
$fullUrl = "{$this->baseUrl}/index.php/$url";
$this->sendRequest($verb, $fullUrl, $body, $headers, $options);
}
private function sendOcsRequest(string $verb, string $url, TableNode|array|string|null $body = null, array $headers = [], array $options = []): void {
$fullUrl = "{$this->baseUrl}/ocs/v2.php/$url";
$this->sendRequest($verb, $fullUrl, $body, $headers, $options);
}
private function sendRequest(string $verb, string $fullUrl, TableNode|array|string|null $body = null, array $headers = [], array $options = []): void {
$client = new Client();
if ($this->currentUser === $this->adminUser) {
$options['auth'] = [$this->adminUser, $this->adminPassword];
} elseif ($this->currentUser !== null && $this->currentUser !== 'guest') {
$options['auth'] = [$this->currentUser, self::TEST_PASSWORD];
}
if ($body instanceof TableNode) {
$options['form_params'] = $body->getRowsHash();
} elseif (is_array($body) && array_key_exists('multipart', $body)) {
$options['multipart'] = $body;
} elseif (is_array($body) && array_key_exists('json', $body)) {
$options['json'] = $body['json'];
} elseif (is_array($body)) {
$options['form_params'] = $body;
} elseif (is_string($body)) {
$options['body'] = $body;
}
$options['headers'] = [
'OCS-ApiRequest' => 'true',
'Accept' => 'application/json',
...$headers,
];
try {
$this->response = $client->{$verb}($fullUrl, $options);
} catch (ClientException $e) {
$this->response = $e->getResponse();
} catch (ServerException $e) {
$this->response = $e->getResponse();
}
}
public function cleanUpTemplates(): void
{
foreach ($this->createdTemplateIds as $id) {
$this->sendFrontpageRequest(
'DELETE',
'apps/onlyoffice/ajax/template',
['templateId' => $id],
);
}
$this->createdTemplateIds = [];
$this->lastTemplateId = null;
}
public function cleanUpUsers(): void
{
foreach ($this->createdUsers as $username) {
$this->sendOcsRequest('DELETE', "/cloud/users/$username");
}
$this->createdUsers = [];
}
public function restoreValidDocsConnection(): void
{
$this->sendFrontpageRequest('PUT', '/apps/onlyoffice/ajax/settings/address', [
'documentserver' => getenv('ONLYOFFICE_DOCUMENT_SERVER_URL') ?: 'http://localhost:8080/',
'documentserverInternal' => getenv('ONLYOFFICE_DOCUMENT_SERVER_INTERNAL_URL') ?: 'http://localhost:8080/',
'storageUrl' => getenv('ONLYOFFICE_STORAGE_URL') ?: 'http://localhost/',
'verifyPeerOff' => 'false',
'secret' => getenv('ONLYOFFICE_SECRET') ?: 'secret',
'jwtHeader' => 'Authorization',
'demo' => 'false',
]);
}
}