nextcloud/tests/lib/Files/Node/HookConnectorTest.php
Roeland Jago Douma 6aa6ab3e02
Add lazy events for the Node API
Right now if you want to get events via the Node API you have to have a
real instance of the Root. Which in turns sets up the whole FS.

We should make sure this is done lazy. Else enabling the preview
generator for example makes you setup the whole FS on each and every
authenticated call.

Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
2020-05-28 12:35:45 +02:00

319 lines
11 KiB
PHP

<?php
/**
* Copyright (c) 2015 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
namespace Test\Files\Node;
use OC\Files\Filesystem;
use OC\Files\Node\HookConnector;
use OC\Files\Node\Root;
use OC\Files\Storage\Temporary;
use OC\Files\View;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\GenericEvent as APIGenericEvent;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Events\Node\AbstractNodeEvent;
use OCP\Files\Events\Node\AbstractNodesEvent;
use OCP\Files\Events\Node\BeforeNodeCopiedEvent;
use OCP\Files\Events\Node\BeforeNodeCreatedEvent;
use OCP\Files\Events\Node\BeforeNodeDeletedEvent;
use OCP\Files\Events\Node\BeforeNodeRenamedEvent;
use OCP\Files\Events\Node\BeforeNodeTouchedEvent;
use OCP\Files\Events\Node\BeforeNodeWrittenEvent;
use OCP\Files\Events\Node\NodeCopiedEvent;
use OCP\Files\Events\Node\NodeCreatedEvent;
use OCP\Files\Events\Node\NodeDeletedEvent;
use OCP\Files\Events\Node\NodeRenamedEvent;
use OCP\Files\Events\Node\NodeTouchedEvent;
use OCP\Files\Events\Node\NodeWrittenEvent;
use OCP\Files\Node;
use OCP\ILogger;
use OCP\IUserManager;
use PHPUnit\Framework\MockObject\MockObject;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
use Test\TestCase;
use Test\Traits\MountProviderTrait;
use Test\Traits\UserTrait;
/**
* Class HookConnectorTest
*
* @group DB
*
* @package Test\Files\Node
*/
class HookConnectorTest extends TestCase {
use UserTrait;
use MountProviderTrait;
/** @var EventDispatcherInterface|MockObject */
protected $legacyDispatcher;
/** @var IEventDispatcher */
protected $eventDispatcher;
/** @var View */
private $view;
/** @var Root */
private $root;
/** @var string */
private $userId;
protected function setUp(): void {
parent::setUp();
$this->userId = $this->getUniqueID();
$this->createUser($this->userId, 'pass');
$this->registerMount($this->userId, new Temporary(), '/' . $this->userId . '/files/');
\OC_Util::setupFS($this->userId);
$this->view = new View();
$this->root = new Root(
Filesystem::getMountManager(),
$this->view,
\OC::$server->getUserManager()->get($this->userId),
\OC::$server->getUserMountCache(),
$this->createMock(ILogger::class),
$this->createMock(IUserManager::class)
);
$this->legacyDispatcher = \OC::$server->getEventDispatcher();
$this->eventDispatcher = \OC::$server->query(IEventDispatcher::class);
}
protected function tearDown(): void {
parent::tearDown();
\OC_Hook::clear('OC_Filesystem');
\OC_Util::tearDownFS();
}
public function viewToNodeProvider() {
return [
[function () {
Filesystem::file_put_contents('test.txt', 'asd');
}, 'preWrite', '\OCP\Files::preWrite', BeforeNodeWrittenEvent::class],
[function () {
Filesystem::file_put_contents('test.txt', 'asd');
}, 'postWrite', '\OCP\Files::postWrite', NodeWrittenEvent::class],
[function () {
Filesystem::file_put_contents('test.txt', 'asd');
}, 'preCreate', '\OCP\Files::preCreate', BeforeNodeCreatedEvent::class],
[function () {
Filesystem::file_put_contents('test.txt', 'asd');
}, 'postCreate', '\OCP\Files::postCreate', NodeCreatedEvent::class],
[function () {
Filesystem::mkdir('test.txt');
}, 'preCreate', '\OCP\Files::preCreate', BeforeNodeCreatedEvent::class],
[function () {
Filesystem::mkdir('test.txt');
}, 'postCreate', '\OCP\Files::postCreate', NodeCreatedEvent::class],
[function () {
Filesystem::touch('test.txt');
}, 'preTouch', '\OCP\Files::preTouch', BeforeNodeTouchedEvent::class],
[function () {
Filesystem::touch('test.txt');
}, 'postTouch', '\OCP\Files::postTouch', NodeTouchedEvent::class],
[function () {
Filesystem::touch('test.txt');
}, 'preCreate', '\OCP\Files::preCreate', BeforeNodeCreatedEvent::class],
[function () {
Filesystem::touch('test.txt');
}, 'postCreate', '\OCP\Files::postCreate', NodeCreatedEvent::class],
[function () {
Filesystem::file_put_contents('test.txt', 'asd');
Filesystem::unlink('test.txt');
}, 'preDelete', '\OCP\Files::preDelete', BeforeNodeDeletedEvent::class],
[function () {
Filesystem::file_put_contents('test.txt', 'asd');
Filesystem::unlink('test.txt');
}, 'postDelete', '\OCP\Files::postDelete', NodeDeletedEvent::class],
[function () {
Filesystem::mkdir('test.txt');
Filesystem::rmdir('test.txt');
}, 'preDelete', '\OCP\Files::preDelete', BeforeNodeDeletedEvent::class],
[function () {
Filesystem::mkdir('test.txt');
Filesystem::rmdir('test.txt');
}, 'postDelete', '\OCP\Files::postDelete', NodeDeletedEvent::class],
];
}
/**
* @param callable $operation
* @param string $expectedHook
* @dataProvider viewToNodeProvider
*/
public function testViewToNode(callable $operation, $expectedHook, $expectedLegacyEvent, $expectedEvent) {
$connector = new HookConnector($this->root, $this->view, $this->legacyDispatcher, $this->eventDispatcher);
$connector->viewToNode();
$hookCalled = false;
/** @var Node $hookNode */
$hookNode = null;
$this->root->listen('\OC\Files', $expectedHook, function ($node) use (&$hookNode, &$hookCalled) {
$hookCalled = true;
$hookNode = $node;
});
$dispatcherCalled = false;
/** @var Node $dispatcherNode */
$dispatcherNode = null;
$this->legacyDispatcher->addListener($expectedLegacyEvent, function ($event) use (&$dispatcherCalled, &$dispatcherNode) {
/** @var GenericEvent|APIGenericEvent $event */
$dispatcherCalled = true;
$dispatcherNode = $event->getSubject();
});
$newDispatcherCalled = false;
$newDispatcherNode = null;
$this->eventDispatcher->addListener($expectedEvent, function ($event) use ($expectedEvent, &$newDispatcherCalled, &$newDispatcherNode) {
if ($event instanceof $expectedEvent) {
/** @var AbstractNodeEvent $event */
$newDispatcherCalled = true;
$newDispatcherNode = $event->getNode();
}
});
$operation();
$this->assertTrue($hookCalled);
$this->assertEquals('/' . $this->userId . '/files/test.txt', $hookNode->getPath());
$this->assertTrue($dispatcherCalled);
$this->assertEquals('/' . $this->userId . '/files/test.txt', $dispatcherNode->getPath());
$this->assertTrue($newDispatcherCalled);
$this->assertEquals('/' . $this->userId . '/files/test.txt', $newDispatcherNode->getPath());
}
public function viewToNodeProviderCopyRename() {
return [
[function () {
Filesystem::file_put_contents('source', 'asd');
Filesystem::rename('source', 'target');
}, 'preRename', '\OCP\Files::preRename', BeforeNodeRenamedEvent::class],
[function () {
Filesystem::file_put_contents('source', 'asd');
Filesystem::rename('source', 'target');
}, 'postRename', '\OCP\Files::postRename', NodeRenamedEvent::class],
[function () {
Filesystem::file_put_contents('source', 'asd');
Filesystem::copy('source', 'target');
}, 'preCopy', '\OCP\Files::preCopy', BeforeNodeCopiedEvent::class],
[function () {
Filesystem::file_put_contents('source', 'asd');
Filesystem::copy('source', 'target');
}, 'postCopy', '\OCP\Files::postCopy', NodeCopiedEvent::class],
];
}
/**
* @param callable $operation
* @param string $expectedHook
* @dataProvider viewToNodeProviderCopyRename
*/
public function testViewToNodeCopyRename(callable $operation, $expectedHook, $expectedLegacyEvent, $expectedEvent) {
$connector = new HookConnector($this->root, $this->view, $this->legacyDispatcher, $this->eventDispatcher);
$connector->viewToNode();
$hookCalled = false;
/** @var Node $hookSourceNode */
$hookSourceNode = null;
/** @var Node $hookTargetNode */
$hookTargetNode = null;
$this->root->listen('\OC\Files', $expectedHook, function ($sourceNode, $targetNode) use (&$hookCalled, &$hookSourceNode, &$hookTargetNode) {
$hookCalled = true;
$hookSourceNode = $sourceNode;
$hookTargetNode = $targetNode;
});
$dispatcherCalled = false;
/** @var Node $dispatcherSourceNode */
$dispatcherSourceNode = null;
/** @var Node $dispatcherTargetNode */
$dispatcherTargetNode = null;
$this->legacyDispatcher->addListener($expectedLegacyEvent, function ($event) use (&$dispatcherSourceNode, &$dispatcherTargetNode, &$dispatcherCalled) {
/** @var GenericEvent|APIGenericEvent $event */
$dispatcherCalled = true;
list($dispatcherSourceNode, $dispatcherTargetNode) = $event->getSubject();
});
$newDispatcherCalled = false;
/** @var Node $dispatcherSourceNode */
$newDispatcherSourceNode = null;
/** @var Node $dispatcherTargetNode */
$newDispatcherTargetNode = null;
$this->eventDispatcher->addListener($expectedEvent, function ($event) use ($expectedEvent, &$newDispatcherSourceNode, &$newDispatcherTargetNode, &$newDispatcherCalled) {
if ($event instanceof $expectedEvent) {
/** @var AbstractNodesEvent$event */
$newDispatcherCalled = true;
$newDispatcherSourceNode = $event->getSource();
$newDispatcherTargetNode = $event->getTarget();
}
});
$operation();
$this->assertTrue($hookCalled);
$this->assertEquals('/' . $this->userId . '/files/source', $hookSourceNode->getPath());
$this->assertEquals('/' . $this->userId . '/files/target', $hookTargetNode->getPath());
$this->assertTrue($dispatcherCalled);
$this->assertEquals('/' . $this->userId . '/files/source', $dispatcherSourceNode->getPath());
$this->assertEquals('/' . $this->userId . '/files/target', $dispatcherTargetNode->getPath());
$this->assertTrue($newDispatcherCalled);
$this->assertEquals('/' . $this->userId . '/files/source', $newDispatcherSourceNode->getPath());
$this->assertEquals('/' . $this->userId . '/files/target', $newDispatcherTargetNode->getPath());
}
public function testPostDeleteMeta() {
$connector = new HookConnector($this->root, $this->view, $this->legacyDispatcher, $this->eventDispatcher);
$connector->viewToNode();
$hookCalled = false;
/** @var Node $hookNode */
$hookNode = null;
$this->root->listen('\OC\Files', 'postDelete', function ($node) use (&$hookNode, &$hookCalled) {
$hookCalled = true;
$hookNode = $node;
});
$dispatcherCalled = false;
/** @var Node $dispatcherNode */
$dispatcherNode = null;
$this->legacyDispatcher->addListener('\OCP\Files::postDelete', function ($event) use (&$dispatcherCalled, &$dispatcherNode) {
/** @var GenericEvent|APIGenericEvent $event */
$dispatcherCalled = true;
$dispatcherNode = $event->getSubject();
});
$newDispatcherCalled = false;
/** @var Node $dispatcherNode */
$newDispatcherNode = null;
$this->eventDispatcher->addListener(NodeDeletedEvent::class, function ($event) use (&$newDispatcherCalled, &$newDispatcherNode) {
if ($event instanceof NodeDeletedEvent) {
/** @var AbstractNodeEvent $event */
$newDispatcherCalled = true;
$newDispatcherNode = $event->getNode();
}
});
Filesystem::file_put_contents('test.txt', 'asd');
$info = Filesystem::getFileInfo('test.txt');
Filesystem::unlink('test.txt');
$this->assertTrue($hookCalled);
$this->assertEquals($hookNode->getId(), $info->getId());
$this->assertTrue($dispatcherCalled);
$this->assertEquals($dispatcherNode->getId(), $info->getId());
$this->assertTrue($newDispatcherCalled);
$this->assertEquals($newDispatcherNode->getId(), $info->getId());
}
}