nextcloud-server/tests/lib/Preview/MovePreviewJobTest.php

225 lines
8.0 KiB
PHP

<?php
declare(strict_types=1);
/*
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH
* SPDX-FileContributor: Carl Schwan
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace lib\Preview;
use OC\Core\BackgroundJobs\MovePreviewJob;
use OC\Preview\Db\PreviewMapper;
use OC\Preview\PreviewService;
use OC\Preview\Storage\StorageFactory;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Files\AppData\IAppDataFactory;
use OCP\Files\IAppData;
use OCP\Files\IMimeTypeDetector;
use OCP\Files\IMimeTypeLoader;
use OCP\Files\IRootFolder;
use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\Server;
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Test\TestCase;
/**
* @group DB
*/
class MovePreviewJobTest extends TestCase {
private IAppData $previewAppData;
private PreviewMapper $previewMapper;
private IAppConfig&MockObject $appConfig;
private IConfig $config;
private StorageFactory $storageFactory;
private PreviewService $previewService;
private IDBConnection $db;
private IMimeTypeLoader&MockObject $mimeTypeLoader;
private IMimeTypeDetector&MockObject $mimeTypeDetector;
private LoggerInterface&MockObject $logger;
public function setUp(): void {
parent::setUp();
$this->previewAppData = Server::get(IAppDataFactory::class)->get('preview');
$this->previewMapper = Server::get(PreviewMapper::class);
$this->config = Server::get(IConfig::class);
$this->appConfig = $this->createMock(IAppConfig::class);
$this->appConfig->expects($this->any())
->method('getValueBool')
->willReturn(false);
$this->appConfig->expects($this->any())
->method('setValueBool')
->willReturn(true);
$this->storageFactory = Server::get(StorageFactory::class);
$this->previewService = Server::get(PreviewService::class);
$this->db = Server::get(IDBConnection::class);
$qb = $this->db->getQueryBuilder();
$qb->delete('filecache')
->where($qb->expr()->eq('fileid', $qb->createNamedParameter(5)))
->executeStatement();
$qb = $this->db->getQueryBuilder();
$qb->insert('filecache')
->values([
'fileid' => $qb->createNamedParameter(5),
'storage' => $qb->createNamedParameter(1),
'path' => $qb->createNamedParameter('test/abc'),
'path_hash' => $qb->createNamedParameter(md5('test')),
'parent' => $qb->createNamedParameter(0),
'name' => $qb->createNamedParameter('abc'),
'mimetype' => $qb->createNamedParameter(42),
'size' => $qb->createNamedParameter(1000),
'mtime' => $qb->createNamedParameter(1000),
'storage_mtime' => $qb->createNamedParameter(1000),
'encrypted' => $qb->createNamedParameter(0),
'unencrypted_size' => $qb->createNamedParameter(0),
'etag' => $qb->createNamedParameter('abcdefg'),
'permissions' => $qb->createNamedParameter(0),
'checksum' => $qb->createNamedParameter('abcdefg'),
])->executeStatement();
$this->mimeTypeDetector = $this->createMock(IMimeTypeDetector::class);
$this->mimeTypeDetector->method('detectPath')->willReturn('image/png');
$this->mimeTypeLoader = $this->createMock(IMimeTypeLoader::class);
$this->mimeTypeLoader->method('getId')->with('image/png')->willReturn(42);
$this->mimeTypeLoader->method('getMimetypeById')->with(42)->willReturn('image/png');
$this->logger = $this->createMock(LoggerInterface::class);
}
public function tearDown(): void {
foreach ($this->previewAppData->getDirectoryListing() as $folder) {
$folder->delete();
}
$this->previewService->deleteAll();
$qb = $this->db->getQueryBuilder();
$qb->delete('filecache')
->where($qb->expr()->eq('fileid', $qb->createNamedParameter(5)))
->executeStatement();
}
#[TestDox('Test the migration from the legacy flat hierarchy to the new database format')]
public function testMigrationLegacyPath(): void {
$folder = $this->previewAppData->newFolder('5');
$folder->newFile('64-64-crop.jpg', 'abcdefg');
$folder->newFile('128-128-crop.png', 'abcdefg');
$this->assertEquals(1, count($this->previewAppData->getDirectoryListing()));
$this->assertEquals(2, count($folder->getDirectoryListing()));
$this->assertEquals(0, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
$job = new MovePreviewJob(
Server::get(ITimeFactory::class),
$this->appConfig,
$this->config,
$this->previewMapper,
$this->storageFactory,
Server::get(IDBConnection::class),
Server::get(IRootFolder::class),
$this->mimeTypeDetector,
$this->mimeTypeLoader,
$this->logger,
Server::get(IAppDataFactory::class),
);
$this->invokePrivate($job, 'run', [[]]);
$this->assertEquals(0, count($this->previewAppData->getDirectoryListing()));
$this->assertEquals(2, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
}
private static function getInternalFolder(string $name): string {
return implode('/', str_split(substr(md5($name), 0, 7))) . '/' . $name;
}
#[TestDox("Test the migration from the 'new' nested hierarchy to the database format")]
public function testMigrationPath(): void {
$folder = $this->previewAppData->newFolder(self::getInternalFolder((string)5));
$folder->newFile('64-64-crop.jpg', 'abcdefg');
$folder->newFile('128-128-crop.png', 'abcdefg');
$folder = $this->previewAppData->getFolder(self::getInternalFolder((string)5));
$this->assertEquals(2, count($folder->getDirectoryListing()));
$this->assertEquals(0, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
$job = new MovePreviewJob(
Server::get(ITimeFactory::class),
$this->appConfig,
$this->config,
$this->previewMapper,
$this->storageFactory,
Server::get(IDBConnection::class),
Server::get(IRootFolder::class),
$this->mimeTypeDetector,
$this->mimeTypeLoader,
$this->logger,
Server::get(IAppDataFactory::class)
);
$this->invokePrivate($job, 'run', [[]]);
$this->assertEquals(0, count($this->previewAppData->getDirectoryListing()));
$this->assertEquals(2, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
}
#[TestDox("Test the migration from the 'new' nested hierarchy to the database format")]
public function testMigrationPathWithVersion(): void {
$folder = $this->previewAppData->newFolder(self::getInternalFolder((string)5));
// No version
$folder->newFile('128-128-crop.png', 'abcdefg');
$folder->newFile('256-256-max.png', 'abcdefg');
$folder->newFile('128-128.png', 'abcdefg');
// Version 1000
$folder->newFile('1000-128-128-crop.png', 'abcdefg');
$folder->newFile('1000-256-256-max.png', 'abcdefg');
$folder->newFile('1000-128-128.png', 'abcdefg');
// Version 1001
$folder->newFile('1001-128-128-crop.png', 'abcdefg');
$folder->newFile('1001-256-256-max.png', 'abcdefg');
$folder->newFile('1001-128-128.png', 'abcdefg');
$folder = $this->previewAppData->getFolder(self::getInternalFolder((string)5));
$this->assertEquals(9, count($folder->getDirectoryListing()));
$this->assertEquals(0, count(iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5))));
$job = new MovePreviewJob(
Server::get(ITimeFactory::class),
$this->appConfig,
$this->config,
$this->previewMapper,
$this->storageFactory,
Server::get(IDBConnection::class),
Server::get(IRootFolder::class),
$this->mimeTypeDetector,
$this->mimeTypeLoader,
$this->logger,
Server::get(IAppDataFactory::class)
);
$this->invokePrivate($job, 'run', [[]]);
$previews = iterator_to_array($this->previewMapper->getAvailablePreviewsForFile(5));
$this->assertEquals(9, count($previews));
$this->assertEquals(0, count($this->previewAppData->getDirectoryListing()));
$nameVersionMapping = [];
foreach ($previews as $preview) {
$nameVersionMapping[$preview->getName($this->mimeTypeLoader)] = $preview->getVersion();
}
$this->assertEquals([
'1000-128-128-crop.png' => 1000,
'1000-128-128.png' => 1000,
'1000-256-256-max.png' => 1000,
'1001-128-128-crop.png' => 1001,
'1001-128-128.png' => 1001,
'1001-256-256-max.png' => 1001,
'128-128-crop.png' => null,
'128-128.png' => null,
'256-256-max.png' => null,
], $nameVersionMapping);
}
}