nextcloud/tests/lib/Files/Config/UserMountCacheTest.php
Robin Appelman 963721330f only get the path for the users cached mount info when we use it
most of the time we shouldn't need it so we can save joining on the filecache

Signed-off-by: Robin Appelman <robin@icewind.nl>
2024-02-09 09:50:43 +01:00

541 lines
16 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 OC\User\Manager;
use OCP\Cache\CappedMemoryCache;
use OCP\Diagnostics\IEventLogger;
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 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(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), $this->createMock(IEventLogger::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, $rootInternalPath = '') {
$rootId = $this->createCacheEntry($rootInternalPath, $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()]);
}
private function keyForMount(MountPoint $mount): string {
return $mount->getStorageRootId().'::'.$mount->getMountPoint();
}
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[$this->keyForMount($mount)];
$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[$this->keyForMount($mount)];
$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[$this->keyForMount($mount)];
$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[$this->keyForMount($mount)];
$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, 'foo/bar');
$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[$this->keyForMount($mount1)]->getMountPoint());
$this->assertEquals($user1, $cachedMounts[$this->keyForMount($mount1)]->getUser());
$this->assertEquals($id1, $cachedMounts[$this->keyForMount($mount1)]->getRootId());
$this->assertEquals(1, $cachedMounts[$this->keyForMount($mount1)]->getStorageId());
$this->assertEquals('', $cachedMounts[$this->keyForMount($mount1)]->getRootInternalPath());
$this->assertEquals('/bar/', $cachedMounts[$this->keyForMount($mount2)]->getMountPoint());
$this->assertEquals($user1, $cachedMounts[$this->keyForMount($mount2)]->getUser());
$this->assertEquals($id2, $cachedMounts[$this->keyForMount($mount2)]->getRootId());
$this->assertEquals(2, $cachedMounts[$this->keyForMount($mount2)]->getStorageId());
$this->assertEquals('foo/bar', $cachedMounts[$this->keyForMount($mount2)]->getRootInternalPath());
$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);
}
public function testMigrateMountProvider() {
$user1 = $this->userManager->get('u1');
[$storage1, $rootId] = $this->getStorage(2);
$rootId = $this->createCacheEntry('', 2);
$mount1 = new MountPoint($storage1, '/foo/');
$this->cache->registerMounts($user1, [$mount1]);
$this->clearCache();
$cachedMounts = $this->cache->getMountsForUser($user1);
$this->assertCount(1, $cachedMounts);
$this->assertEquals('', $cachedMounts[$this->keyForMount($mount1)]->getMountProvider());
$mount1 = new MountPoint($storage1, '/foo/', null, null, null, null, 'dummy');
$this->cache->registerMounts($user1, [$mount1], ['dummy']);
$this->clearCache();
$cachedMounts = $this->cache->getMountsForUser($user1);
$this->assertCount(1, $cachedMounts);
$this->assertEquals('dummy', $cachedMounts[$this->keyForMount($mount1)]->getMountProvider());
}
}