Skip to content

Commit

Permalink
Add test for ViewerController.
Browse files Browse the repository at this point in the history
  • Loading branch information
AaronGilMartinez committed Jan 8, 2025
1 parent 0869615 commit 7501119
Show file tree
Hide file tree
Showing 2 changed files with 354 additions and 0 deletions.
14 changes: 14 additions & 0 deletions tests/fixtures/discovery.mimetypes.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!-- Reduced discovery.xml with different urls per mime type. -->
<wopi-discovery>
<net-zone name="external-http">
<app name="text/spreadsheet">
<action default="true" ext="" name="edit" urlsrc="http://spreadsheet.collabora.test:9980/browser/61cf2b4/cool.html?"/>
</app>
<app name="text/csv">
<action default="true" ext="" name="edit" urlsrc="http://csv.collabora.test:9980/browser/61cf2b4/cool.html?"/>
</app>
<app name="text/plain">
<action default="true" ext="" name="edit" urlsrc="http://collabora.test:9980/browser/61cf2b4/cool.html?"/>
</app>
</net-zone>
</wopi-discovery>
340 changes: 340 additions & 0 deletions tests/src/Kernel/Controller/ViewerControllerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,340 @@
<?php

/*
* Copyright the Collabora Online contributors.
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

declare(strict_types=1);

namespace Drupal\Tests\collabora_online\Kernel\Controller;

use ColinODell\PsrTestLogger\TestLogger;
use Drupal\collabora_online\Cool\CollaboraDiscoveryFetcherInterface;
use Drupal\collabora_online\Jwt\JwtTranscoderInterface;
use Drupal\file\Entity\File;
use Drupal\file\FileInterface;
use Drupal\media\MediaInterface;
use Drupal\Tests\collabora_online\Kernel\CollaboraKernelTestBase;
use Drupal\user\UserInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
* @coversDefaultClass \Drupal\collabora_online\Controller\ViewerController
*/
class ViewerControllerTest extends CollaboraKernelTestBase {

/**
* {@inheritdoc}
*/
protected static $modules = [
'collabora_online_test',
];

/**
* The user with access to perform operations.
*
* @var \Drupal\user\UserInterface
*/
protected UserInterface $user;

/**
* The media where to perform operations.
*
* @var \Drupal\media\MediaInterface
*/
protected MediaInterface $media;

/**
* The source file.
*
* @var \Drupal\file\FileInterface
*/
protected FileInterface $file;

/**
* The test logger channel.
*
* @var \ColinODell\PsrTestLogger\TestLogger
*/
protected TestLogger $logger;

/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();

$fetcher = $this->createMock(CollaboraDiscoveryFetcherInterface::class);
$file = dirname(__DIR__, 3) . '/fixtures/discovery.mimetypes.xml';
$xml = file_get_contents($file);
$fetcher->method('getDiscoveryXml')->willReturn($xml);
$this->container->set(CollaboraDiscoveryFetcherInterface::class, $fetcher);

$this->logger = new TestLogger();
\Drupal::service('logger.factory')->addLogger($this->logger);

$collabora_settings = \Drupal::configFactory()->getEditable('collabora_online.settings');
$cool = $collabora_settings->get('cool');
$cool['key_id'] = 'collabora';
$collabora_settings->set('cool', $cool)->save();

$this->media = $this->createMediaEntity('document');
$this->user = $this->createUser([
'access content',
'preview document in collabora',
'edit any document in collabora',
]);
$fid = $this->media->getSource()->getSourceFieldValue($this->media);
$this->file = File::load($fid);

$this->setCurrentUser($this->user);
}

/**
* Tests successful requests.
*
* @covers ::editor
*/
public function testEditor(): void {
// Requests to view the editor.
$request = $this->createRequest('view');
$this->assertResponseOk($request);
$request = $this->createRequest('view', write: TRUE);
$this->assertResponseOk($request);

// Requests to edit the editor.
$request = $this->createRequest('edit', write: TRUE);
$this->assertResponseOk($request);
$request = $this->createRequest('edit');
$this->assertResponseOk($request);
}

/**
* Tests requests with unavailable Collabora.
*
* @covers ::editor
*/
public function testEditorCollaboraUnavailable(): void {
// Collabora is not available on tests, we need to restore the service.
$fetcher = $this->createMock(CollaboraDiscoveryFetcherInterface::class);
$this->container->set(CollaboraDiscoveryFetcherInterface::class, $fetcher);

// Requests to view the editor.
$request = $this->createRequest('view');
$this->assertBadRequestResponse(
'The Collabora Online editor/viewer is not available.',
$request,
);

// Requests to edit the editor.
$request = $this->createRequest('edit');
$this->assertBadRequestResponse(
'The Collabora Online editor/viewer is not available.',
$request,
);
}

/**
* Tests requests with a scheme not matching the Collabora client URL.
*
* @covers ::editor
*/
public function testEditorMismatchScheme(): void {
// Requests to view the editor.
$request = $this->createRequest('view', 'https');
$this->assertBadRequestResponse(
'Viewer error: Protocol mismatch.',
$request,
);

// Requests to edit the editor.
$request = $this->createRequest('edit', 'https');
$this->assertBadRequestResponse(
'Viewer error: Protocol mismatch.',
$request,
);
}

/**
* Tests requests with a no viewer avaliable after request.
*
* @covers ::editor
*/
public function testEditorNoViewer(): void {
$request = $this->createRequest('view');

// Mock transcoder to force fail.
$fetcher = $this->createMock(CollaboraDiscoveryFetcherInterface::class);
$this->container->set(CollaboraDiscoveryFetcherInterface::class, $fetcher);

// Requests to view the editor.
$this->assertBadRequestResponse(
'The Collabora Online editor/viewer is not available.',
$request,
);

// Requests to edit the editor.
$request = $this->createRequest('edit');
$this->assertBadRequestResponse(
'The Collabora Online editor/viewer is not available.',
$request,
);
}

/**
* Creates a view/edit request.
*
* @param string $action
* E.g. 'view' or 'edit'.
* @param string $scheme
* The protocol used for the request.
* @param int|null $media_id
* Media entity id, if different from the default.
* @param int|null $user_id
* User id, if different from the default.
* @param bool $write
* TRUE if write access is requested.
* @param array $token_payload
* Explicit token payload values.
* This can be used to cause a bad token.
*
* @return \Symfony\Component\HttpFoundation\Request
* The request.
*/
protected function createRequest(
string $action,
string $scheme = 'http',
?int $media_id = NULL,
?int $user_id = NULL,
bool $write = FALSE,
array $token_payload = [],
): Request {
$media_id ??= (int) $this->media->id();
$user_id ??= (int) $this->user->id();
$uri = "$scheme://localhost/cool/$action/$media_id";
$token = $this->createAccessToken($media_id, $user_id, $write, $token_payload);
$parameters = [
'media' => $media_id,
'edit' => $write,
'access_token' => $token,
];
return Request::create($uri, 'GET', $parameters);
}

/**
* Retrieves an encoded access token.
*
* @param int|null $fid
* The file id.
* @param int|null $uid
* The user id.
* @param bool $write
* The write permission.
* @param array $payload
* Explicit payload values.
* This can be used to cause a bad token.
*
* @return string
* The enconded token.
*/
protected function createAccessToken(?int $fid = NULL, ?int $uid = NULL, bool $write = FALSE, array $payload = []): string {
/** @var \Drupal\collabora_online\Jwt\JwtTranscoderInterface $transcoder */
$transcoder = \Drupal::service(JwtTranscoderInterface::class);
$expire_timestamp = gettimeofday(TRUE) + 1000;
$payload += [
'fid' => (string) ($fid ?? $this->media->id()),
'uid' => (string) ($uid ?? $this->user->id()),
'wri' => $write,
'exp' => $expire_timestamp,
];
return $transcoder->encode($payload, $expire_timestamp);
}

/**
* Asserts an sucessful response given a request.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request to perform.
* @param string $message
* Message to distinguish this from other assertions.
*/
protected function assertResponseOk(Request $request, string $message = ''): void {
$response = $this->handleRequest($request);

$this->assertEquals(Response::HTTP_OK, $response->getStatusCode(), $message);
$this->assertStringContainsString('iframe', $response->getContent(), $message);
$this->assertEquals('', $response->headers->get('Content-Type'), $message);
}

/**
* Asserts an bad request response given a request.
*
* @param string $expected_content
* The expected content.
* @param array $expected_log
* The expected log entry.
* @param \Symfony\Component\HttpFoundation\Request $request
* The request to perform.
* @param string $message
* Message to distinguish this from other assertions.
*/
protected function assertBadRequestResponse(string $expected_content, Request $request, array $expected_log = [], string $message = ''): void {
$this->assertResponse(
Response::HTTP_BAD_REQUEST,
$expected_content,
'text/plain',
$request,
$message,
);

if ($expected_log) {
$this->assertTrue($this->logger->hasRecord($expected_log['message'], $expected_log['level'] ?? NULL));
$this->logger->reset();
}
}

/**
* Asserts status code and content in a response given a request.
*
* @param int $expected_code
* The expected response status code.
* @param string $expected_content
* The expected response content.
* @param string $expected_content_type
* The type of content of the response.
* @param \Symfony\Component\HttpFoundation\Request $request
* The request to perform.
* @param string $message
* Message to distinguish this from other assertions.
*/
protected function assertResponse(int $expected_code, string $expected_content, string $expected_content_type, Request $request, string $message = ''): void {
$response = $this->handleRequest($request);

$this->assertEquals($expected_code, $response->getStatusCode(), $message);
$this->assertEquals($expected_content, $response->getContent(), $message);
$this->assertEquals($expected_content_type, $response->headers->get('Content-Type'), $message);
}

/**
* Handles a request and gets the response.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* Incoming request.
*
* @return \Symfony\Component\HttpFoundation\Response
* The response.
*/
protected function handleRequest(Request $request): Response {
/** @var \Symfony\Component\HttpKernel\HttpKernelInterface $kernel */
$kernel = \Drupal::service('http_kernel');
return $kernel->handle($request);
}

}

0 comments on commit 7501119

Please sign in to comment.