nextcloud/tests/lib/Files/Node/HookConnectorTest.php

320 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());
}
}