nextcloud/tests/lib/Files/Config/UserMountCacheTest.php
Carl Schwan d5c23dbb9f Move CappedMemoryCache to OCP
This is an helpful helper that should be used in more place than just
server and this is already the case with groupfodlers, deck, user_oidc
and more using it, so let's make it public

Signed-off-by: Carl Schwan <carl@carlschwan.eu>
2022-07-14 15:54:31 +02:00

510 lines
15 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\Config;
use OC\DB\QueryBuilder\Literal;
use OC\Files\Mount\MountPoint;
use OC\Files\Storage\Storage;
use OCP\Cache\CappedMemoryCache;
use OC\User\Manager;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Config\ICachedMountInfo;
use OCP\ICacheFactory;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IUserManager;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Test\TestCase;
use Test\Util\User\Dummy;
/**
* @group DB
*/
class UserMountCacheTest extends TestCase {
/**
* @var IDBConnection
*/
private $connection;
/**
* @var IUserManager
*/
private $userManager;
/**
* @var \OC\Files\Config\UserMountCache
*/
private $cache;
private $fileIds = [];
protected function setUp(): void {
parent::setUp();
$this->fileIds = [];
$this->connection = \OC::$server->getDatabaseConnection();
$config = $this->getMockBuilder(IConfig::class)
->disableOriginalConstructor()
->getMock();
$config
->expects($this->any())
->method('getUserValue')
->willReturnArgument(3);
$config
->expects($this->any())
->method('getAppValue')
->willReturnArgument(2);
$this->userManager = new Manager($config, $this->createMock(EventDispatcherInterface::class), $this->createMock(ICacheFactory::class), $this->createMock(IEventDispatcher::class));
$userBackend = new Dummy();
$userBackend->createUser('u1', '');
$userBackend->createUser('u2', '');
$userBackend->createUser('u3', '');
$this->userManager->registerBackend($userBackend);
$this->cache = new \OC\Files\Config\UserMountCache($this->connection, $this->userManager, $this->createMock(LoggerInterface::class));
}
protected function tearDown(): void {
$builder = $this->connection->getQueryBuilder();
$builder->delete('mounts')->execute();
$builder = $this->connection->getQueryBuilder();
foreach ($this->fileIds as $fileId) {
$builder->delete('filecache')
->where($builder->expr()->eq('fileid', new Literal($fileId)))
->execute();
}
}
private function getStorage($storageId) {
$rootId = $this->createCacheEntry('', $storageId);
$storageCache = $this->getMockBuilder('\OC\Files\Cache\Storage')
->disableOriginalConstructor()
->getMock();
$storageCache->expects($this->any())
->method('getNumericId')
->willReturn($storageId);
$cache = $this->getMockBuilder('\OC\Files\Cache\Cache')
->disableOriginalConstructor()
->getMock();
$cache->expects($this->any())
->method('getId')
->willReturn($rootId);
$storage = $this->getMockBuilder('\OC\Files\Storage\Storage')
->disableOriginalConstructor()
->getMock();
$storage->expects($this->any())
->method('getStorageCache')
->willReturn($storageCache);
$storage->expects($this->any())
->method('getCache')
->willReturn($cache);
return [$storage, $rootId];
}
private function clearCache() {
$this->invokePrivate($this->cache, 'mountsForUsers', [new CappedMemoryCache()]);
}
public function testNewMounts() {
$user = $this->userManager->get('u1');
[$storage] = $this->getStorage(10);
$mount = new MountPoint($storage, '/asd/');
$this->cache->registerMounts($user, [$mount]);
$this->clearCache();
$cachedMounts = $this->cache->getMountsForUser($user);
$this->assertCount(1, $cachedMounts);
$cachedMount = $cachedMounts[0];
$this->assertEquals('/asd/', $cachedMount->getMountPoint());
$this->assertEquals($user, $cachedMount->getUser());
$this->assertEquals($storage->getCache()->getId(''), $cachedMount->getRootId());
$this->assertEquals($storage->getStorageCache()->getNumericId(), $cachedMount->getStorageId());
}
public function testSameMounts() {
$user = $this->userManager->get('u1');
[$storage] = $this->getStorage(10);
$mount = new MountPoint($storage, '/asd/');
$this->cache->registerMounts($user, [$mount]);
$this->clearCache();
$this->cache->registerMounts($user, [$mount]);
$this->clearCache();
$cachedMounts = $this->cache->getMountsForUser($user);
$this->assertCount(1, $cachedMounts);
$cachedMount = $cachedMounts[0];
$this->assertEquals('/asd/', $cachedMount->getMountPoint());
$this->assertEquals($user, $cachedMount->getUser());
$this->assertEquals($storage->getCache()->getId(''), $cachedMount->getRootId());
$this->assertEquals($storage->getStorageCache()->getNumericId(), $cachedMount->getStorageId());
}
public function testRemoveMounts() {
$user = $this->userManager->get('u1');
[$storage] = $this->getStorage(10);
$mount = new MountPoint($storage, '/asd/');
$this->cache->registerMounts($user, [$mount]);
$this->clearCache();
$this->cache->registerMounts($user, []);
$this->clearCache();
$cachedMounts = $this->cache->getMountsForUser($user);
$this->assertCount(0, $cachedMounts);
}
public function testChangeMounts() {
$user = $this->userManager->get('u1');
[$storage] = $this->getStorage(10);
$mount = new MountPoint($storage, '/bar/');
$this->cache->registerMounts($user, [$mount]);
$this->clearCache();
$mount = new MountPoint($storage, '/foo/');
$this->cache->registerMounts($user, [$mount]);
$this->clearCache();
$cachedMounts = $this->cache->getMountsForUser($user);
$this->assertCount(1, $cachedMounts);
$cachedMount = $cachedMounts[0];
$this->assertEquals('/foo/', $cachedMount->getMountPoint());
}
public function testChangeMountId() {
$user = $this->userManager->get('u1');
[$storage] = $this->getStorage(10);
$mount = new MountPoint($storage, '/foo/', null, null, null, null);
$this->cache->registerMounts($user, [$mount]);
$this->clearCache();
$mount = new MountPoint($storage, '/foo/', null, null, null, 1);
$this->cache->registerMounts($user, [$mount]);
$this->clearCache();
$cachedMounts = $this->cache->getMountsForUser($user);
$this->assertCount(1, $cachedMounts);
$cachedMount = $cachedMounts[0];
$this->assertEquals(1, $cachedMount->getMountId());
}
public function testGetMountsForUser() {
$user1 = $this->userManager->get('u1');
$user2 = $this->userManager->get('u2');
$user3 = $this->userManager->get('u3');
[$storage1, $id1] = $this->getStorage(1);
[$storage2, $id2] = $this->getStorage(2);
$mount1 = new MountPoint($storage1, '/foo/');
$mount2 = new MountPoint($storage2, '/bar/');
$this->cache->registerMounts($user1, [$mount1, $mount2]);
$this->cache->registerMounts($user2, [$mount2]);
$this->cache->registerMounts($user3, [$mount2]);
$this->clearCache();
$user3->delete();
$cachedMounts = $this->cache->getMountsForUser($user1);
$this->assertCount(2, $cachedMounts);
$this->assertEquals('/foo/', $cachedMounts[0]->getMountPoint());
$this->assertEquals($user1, $cachedMounts[0]->getUser());
$this->assertEquals($id1, $cachedMounts[0]->getRootId());
$this->assertEquals(1, $cachedMounts[0]->getStorageId());
$this->assertEquals('/bar/', $cachedMounts[1]->getMountPoint());
$this->assertEquals($user1, $cachedMounts[1]->getUser());
$this->assertEquals($id2, $cachedMounts[1]->getRootId());
$this->assertEquals(2, $cachedMounts[1]->getStorageId());
$cachedMounts = $this->cache->getMountsForUser($user3);
$this->assertEmpty($cachedMounts);
}
public function testGetMountsByStorageId() {
$user1 = $this->userManager->get('u1');
$user2 = $this->userManager->get('u2');
[$storage1, $id1] = $this->getStorage(1);
[$storage2, $id2] = $this->getStorage(2);
$mount1 = new MountPoint($storage1, '/foo/');
$mount2 = new MountPoint($storage2, '/bar/');
$this->cache->registerMounts($user1, [$mount1, $mount2]);
$this->cache->registerMounts($user2, [$mount2]);
$this->clearCache();
$cachedMounts = $this->cache->getMountsForStorageId(2);
$this->sortMounts($cachedMounts);
$this->assertCount(2, $cachedMounts);
$this->assertEquals('/bar/', $cachedMounts[0]->getMountPoint());
$this->assertEquals($user1, $cachedMounts[0]->getUser());
$this->assertEquals($id2, $cachedMounts[0]->getRootId());
$this->assertEquals(2, $cachedMounts[0]->getStorageId());
$this->assertEquals('/bar/', $cachedMounts[1]->getMountPoint());
$this->assertEquals($user2, $cachedMounts[1]->getUser());
$this->assertEquals($id2, $cachedMounts[1]->getRootId());
$this->assertEquals(2, $cachedMounts[1]->getStorageId());
}
public function testGetMountsByRootId() {
$user1 = $this->userManager->get('u1');
$user2 = $this->userManager->get('u2');
[$storage1, $id1] = $this->getStorage(1);
[$storage2, $id2] = $this->getStorage(2);
$mount1 = new MountPoint($storage1, '/foo/');
$mount2 = new MountPoint($storage2, '/bar/');
$this->cache->registerMounts($user1, [$mount1, $mount2]);
$this->cache->registerMounts($user2, [$mount2]);
$this->clearCache();
$cachedMounts = $this->cache->getMountsForRootId($id2);
$this->sortMounts($cachedMounts);
$this->assertCount(2, $cachedMounts);
$this->assertEquals('/bar/', $cachedMounts[0]->getMountPoint());
$this->assertEquals($user1, $cachedMounts[0]->getUser());
$this->assertEquals($id2, $cachedMounts[0]->getRootId());
$this->assertEquals(2, $cachedMounts[0]->getStorageId());
$this->assertEquals('/bar/', $cachedMounts[1]->getMountPoint());
$this->assertEquals($user2, $cachedMounts[1]->getUser());
$this->assertEquals($id2, $cachedMounts[1]->getRootId());
$this->assertEquals(2, $cachedMounts[1]->getStorageId());
}
private function sortMounts(&$mounts) {
usort($mounts, function (ICachedMountInfo $a, ICachedMountInfo $b) {
return strcmp($a->getUser()->getUID(), $b->getUser()->getUID());
});
}
private function createCacheEntry($internalPath, $storageId, $size = 0) {
$internalPath = trim($internalPath, '/');
$inserted = $this->connection->insertIfNotExist('*PREFIX*filecache', [
'storage' => $storageId,
'path' => $internalPath,
'path_hash' => md5($internalPath),
'parent' => -1,
'name' => basename($internalPath),
'mimetype' => 0,
'mimepart' => 0,
'size' => $size,
'storage_mtime' => 0,
'encrypted' => 0,
'unencrypted_size' => 0,
'etag' => '',
'permissions' => 31
], ['storage', 'path_hash']);
if ($inserted) {
$id = (int)$this->connection->lastInsertId('*PREFIX*filecache');
$this->fileIds[] = $id;
} else {
$sql = 'SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` =?';
$query = $this->connection->prepare($sql);
$query->execute([$storageId, md5($internalPath)]);
return (int)$query->fetchOne();
}
return $id;
}
public function testGetMountsForFileIdRootId() {
$user1 = $this->userManager->get('u1');
[$storage1, $rootId] = $this->getStorage(2);
$mount1 = new MountPoint($storage1, '/foo/');
$this->cache->registerMounts($user1, [$mount1]);
$this->clearCache();
$cachedMounts = $this->cache->getMountsForFileId($rootId);
$this->assertCount(1, $cachedMounts);
$this->assertEquals('/foo/', $cachedMounts[0]->getMountPoint());
$this->assertEquals($user1, $cachedMounts[0]->getUser());
$this->assertEquals($rootId, $cachedMounts[0]->getRootId());
$this->assertEquals(2, $cachedMounts[0]->getStorageId());
}
public function testGetMountsForFileIdSubFolder() {
$user1 = $this->userManager->get('u1');
$fileId = $this->createCacheEntry('/foo/bar', 2);
[$storage1, $rootId] = $this->getStorage(2);
$mount1 = new MountPoint($storage1, '/foo/');
$this->cache->registerMounts($user1, [$mount1]);
$this->clearCache();
$cachedMounts = $this->cache->getMountsForFileId($fileId);
$this->assertCount(1, $cachedMounts);
$this->assertEquals('/foo/', $cachedMounts[0]->getMountPoint());
$this->assertEquals($user1, $cachedMounts[0]->getUser());
$this->assertEquals($rootId, $cachedMounts[0]->getRootId());
$this->assertEquals(2, $cachedMounts[0]->getStorageId());
$this->assertEquals('foo/bar', $cachedMounts[0]->getInternalPath());
$this->assertEquals('/foo/foo/bar', $cachedMounts[0]->getPath());
}
public function testGetMountsForFileIdSubFolderMount() {
$user1 = $this->userManager->get('u1');
[$storage1, $rootId] = $this->getStorage(2);
$folderId = $this->createCacheEntry('/foo', 2);
$fileId = $this->createCacheEntry('/foo/bar', 2);
$mount1 = $this->getMockBuilder('\OC\Files\Mount\MountPoint')
->setConstructorArgs([$storage1, '/'])
->setMethods(['getStorageRootId'])
->getMock();
$mount1->expects($this->any())
->method('getStorageRootId')
->willReturn($folderId);
$this->cache->registerMounts($user1, [$mount1]);
$this->clearCache();
$cachedMounts = $this->cache->getMountsForFileId($fileId);
$this->assertCount(1, $cachedMounts);
$this->assertEquals('/', $cachedMounts[0]->getMountPoint());
$this->assertEquals($user1, $cachedMounts[0]->getUser());
$this->assertEquals($folderId, $cachedMounts[0]->getRootId());
$this->assertEquals(2, $cachedMounts[0]->getStorageId());
$this->assertEquals('foo', $cachedMounts[0]->getRootInternalPath());
$this->assertEquals('bar', $cachedMounts[0]->getInternalPath());
$this->assertEquals('/bar', $cachedMounts[0]->getPath());
}
public function testGetMountsForFileIdSubFolderMountOutside() {
$user1 = $this->userManager->get('u1');
[$storage1, $rootId] = $this->getStorage(2);
$folderId = $this->createCacheEntry('/foo', 2);
$fileId = $this->createCacheEntry('/bar/asd', 2);
$mount1 = $this->getMockBuilder('\OC\Files\Mount\MountPoint')
->setConstructorArgs([$storage1, '/foo/'])
->setMethods(['getStorageRootId'])
->getMock();
$mount1->expects($this->any())
->method('getStorageRootId')
->willReturn($folderId);
$this->cache->registerMounts($user1, [$mount1]);
$this->cache->registerMounts($user1, [$mount1]);
$this->clearCache();
$cachedMounts = $this->cache->getMountsForFileId($fileId);
$this->assertCount(0, $cachedMounts);
}
public function testGetMountsForFileIdDeletedUser() {
$user1 = $this->userManager->get('u1');
[$storage1, $rootId] = $this->getStorage(2);
$rootId = $this->createCacheEntry('', 2);
$mount1 = new MountPoint($storage1, '/foo/');
$this->cache->registerMounts($user1, [$mount1]);
$user1->delete();
$this->clearCache();
$cachedMounts = $this->cache->getMountsForFileId($rootId);
$this->assertEmpty($cachedMounts);
}
public function testGetUsedSpaceForUsers() {
$user1 = $this->userManager->get('u1');
$user2 = $this->userManager->get('u2');
/** @var Storage $storage1 */
[$storage1, $rootId] = $this->getStorage(2);
$folderId = $this->createCacheEntry('files', 2, 100);
$fileId = $this->createCacheEntry('files/foo', 2, 7);
$storage1->getCache()->put($folderId, ['size' => 100]);
$storage1->getCache()->update($fileId, ['size' => 70]);
$mount1 = $this->getMockBuilder(MountPoint::class)
->setConstructorArgs([$storage1, '/u1/'])
->setMethods(['getStorageRootId', 'getNumericStorageId'])
->getMock();
$mount1->expects($this->any())
->method('getStorageRootId')
->willReturn($rootId);
$mount1->expects($this->any())
->method('getNumericStorageId')
->willReturn(2);
$this->cache->registerMounts($user1, [$mount1]);
$result = $this->cache->getUsedSpaceForUsers([$user1, $user2]);
$this->assertEquals(['u1' => 100], $result);
}
}