Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: Add test for extending locks #340

Merged
merged 3 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 100 additions & 31 deletions tests/Feature/LockFeatureTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,20 @@

use OC\Files\Lock\LockManager;
use OCA\FilesLock\AppInfo\Application;
use OCA\FilesLock\Model\FileLock;
use OCA\FilesLock\Service\ConfigService;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Files\File;
use OCP\Files\IRootFolder;
use OCP\Files\Lock\ILock;
use OCP\Files\Lock\ILockManager;
use OCP\Files\Lock\LockContext;
use OCP\IConfig;
use OCP\IUserManager;
use OCP\Lock\ManuallyLockedException;
use OCP\Share\IManager as IShareManager;
use OCP\Share\IShare;
use PHPUnit\Framework\MockObject\MockObject;
use Test\TestCase;
use Test\Util\User\Dummy;

Expand All @@ -25,25 +30,25 @@ class LockFeatureTest extends TestCase {
public const TEST_USER1 = "test-user1";
public const TEST_USER2 = "test-user2";

private LockManager $lockManager;
private IRootFolder $rootFolder;
private ITimeFactory $timeFactory;
private IShareManager $shareManager;
private ?int $time = null;
protected LockManager $lockManager;
protected IRootFolder $rootFolder;
protected ITimeFactory&MockObject $timeFactory;
protected IShareManager $shareManager;
protected ?int $time = null;

public static function setUpBeforeClass(): void {
parent::setUpBeforeClass();
$backend = new Dummy();
$backend->createUser(self::TEST_USER1, self::TEST_USER1);
$backend->createUser(self::TEST_USER2, self::TEST_USER2);
\OC::$server->getUserManager()->registerBackend($backend);
\OCP\Server::get(IUserManager::class)->registerBackend($backend);
}

public function setUp(): void {
parent::setUp();
$this->time = null;
$this->lockManager = \OC::$server->get(ILockManager::class);
$this->rootFolder = \OC::$server->get(IRootFolder::class);
$this->lockManager = \OCP\Server::get(ILockManager::class);
$this->rootFolder = \OCP\Server::get(IRootFolder::class);
$this->timeFactory = $this->createMock(ITimeFactory::class);
$this->timeFactory->expects(self::any())
->method('getTime')
Expand All @@ -54,22 +59,23 @@ public function setUp(): void {
return time();
});
$folder = $this->loginAndGetUserFolder(self::TEST_USER1);
$folder->delete('testfile');
$folder->delete('testfile2');
$folder->delete('testfile3');
$folder->delete('test-file');
$folder->delete('test-file2');
$folder->delete('test-file3');
\OC_Hook::$thrownExceptions = [];
$this->overwriteService(ITimeFactory::class, $this->timeFactory);
}

public function testLockUser() {
$file = $this->loginAndGetUserFolder(self::TEST_USER1)
->newFile('testfile', 'AAA');
->newFile('test-file', 'AAA');
$this->shareFileWithUser($file, self::TEST_USER1, self::TEST_USER2);
$this->lockManager->lock(new LockContext($file, ILock::TYPE_USER, self::TEST_USER1));
$file->putContent('BBB');

/** @var File */
$file = $this->loginAndGetUserFolder(self::TEST_USER2)
->get('testfile');
->get('test-file');
try {
$file->putContent('CCC');
$this->fail('Expected to throw a ManuallyLockedException');
Expand All @@ -78,14 +84,16 @@ public function testLockUser() {
self::assertEquals('BBB', $file->getContent());
}

/** @var File */
$file = $this->loginAndGetUserFolder(self::TEST_USER1)
->get('testfile');
->get('test-file');
$file->putContent('DDD');
self::assertEquals('DDD', $file->getContent());

$this->lockManager->unlock(new LockContext($file, ILock::TYPE_USER, self::TEST_USER1));
/** @var File */
$file = $this->loginAndGetUserFolder(self::TEST_USER2)
->get('testfile');
->get('test-file');
$file->putContent('EEE');
self::assertEquals('EEE', $file->getContent());
}
Expand Down Expand Up @@ -173,15 +181,16 @@ public function testUnlockEtagShare() {
}

public function testLockUserExpire() {
\OC::$server->getConfig()->setAppValue(Application::APP_ID, ConfigService::LOCK_TIMEOUT, 30);
\OCP\Server::get(IConfig::class)->setAppValue(Application::APP_ID, ConfigService::LOCK_TIMEOUT, 30);
$file = $this->loginAndGetUserFolder(self::TEST_USER1)
->newFile('testfile-expire', 'AAA');
->newFile('test-file-expire', 'AAA');
$this->shareFileWithUser($file, self::TEST_USER1, self::TEST_USER2);
$this->lockManager->lock(new LockContext($file, ILock::TYPE_USER, self::TEST_USER1));
$file->putContent('BBB');

/** @var File */
$file = $this->loginAndGetUserFolder(self::TEST_USER2)
->get('testfile-expire');
->get('test-file-expire');
try {
$file->putContent('CCC');
$this->fail('Expected to throw a ManuallyLockedException');
Expand All @@ -196,15 +205,16 @@ public function testLockUserExpire() {
}

public function testLockUserInfinite() {
\OC::$server->getConfig()->setAppValue(Application::APP_ID, ConfigService::LOCK_TIMEOUT, 0);
\OCP\Server::get(IConfig::class)->setAppValue(Application::APP_ID, ConfigService::LOCK_TIMEOUT, 0);
$file = $this->loginAndGetUserFolder(self::TEST_USER1)
->newFile('testfile-infinite', 'AAA');
->newFile('test-file-infinite', 'AAA');
$this->shareFileWithUser($file, self::TEST_USER1, self::TEST_USER2);
$this->lockManager->lock(new LockContext($file, ILock::TYPE_USER, self::TEST_USER1));
$file->putContent('BBB');

/** @var File */
$file = $this->loginAndGetUserFolder(self::TEST_USER2)
->get('testfile-infinite');
->get('test-file-infinite');
try {
$file->putContent('CCC');
$this->fail('Expected to throw a ManuallyLockedException');
Expand All @@ -214,8 +224,9 @@ public function testLockUserInfinite() {
}

$this->toTheFuture(3600);
/** @var File */
$file = $this->loginAndGetUserFolder(self::TEST_USER2)
->get('testfile-infinite');
->get('test-file-infinite');
try {
$file->putContent('DDD');
$this->fail('Expected to throw a ManuallyLockedException');
Expand All @@ -227,7 +238,7 @@ public function testLockUserInfinite() {

public function testLockApp() {
$file = $this->loginAndGetUserFolder(self::TEST_USER1)
->newFile('testfile2', 'AAA');
->newFile('test-file2', 'AAA');
$this->shareFileWithUser($file, self::TEST_USER1, self::TEST_USER2);
$scope = new LockContext($file, ILock::TYPE_APP, 'collaborative_app');
$this->lockManager->lock($scope);
Expand Down Expand Up @@ -255,7 +266,7 @@ public function testLockApp() {

public function testLockDifferentApps() {
$file = $this->loginAndGetUserFolder(self::TEST_USER1)
->newFile('testfile3', 'AAA');
->newFile('test-file3', 'AAA');
$scope = new LockContext($file, ILock::TYPE_APP, 'collaborative_app');
$this->lockManager->lock($scope);

Expand All @@ -281,7 +292,7 @@ public function testLockDifferentApps() {
public function testLockDifferentAppsPublic() {
self::logout();
$file = $this->rootFolder->getUserFolder(self::TEST_USER1)
->newFile('testfile_public', 'AAA');
->newFile('test-file_public', 'AAA');
$scope = new LockContext($file, ILock::TYPE_APP, 'collaborative_app');
$this->lockManager->lock($scope);

Expand All @@ -304,13 +315,71 @@ public function testLockDifferentAppsPublic() {
});
}

/**
* Ensure that a lock can be extended and the same lock is kept
*/
public function testExtendLock() {
\OCP\Server::get(IConfig::class)->setAppValue(Application::APP_ID, ConfigService::LOCK_TIMEOUT, 15);

// Create a file and lock it
$file = $this->loginAndGetUserFolder(self::TEST_USER1)->newFile('test-file', 'AAA');
$this->lockManager->lock(new LockContext($file, ILock::TYPE_USER, self::TEST_USER1));
$locks = $this->lockManager->getLocks($file->getId());

// We should have one lock for that file with 15 minutes ETA
$this->assertCount(1, $locks);
$this->assertEquals(15 * 60, $locks[0]->getEta());

// going to the future we see the ETA to be 5 minutes
$this->toTheFuture(10 * 60);
$locks = $this->lockManager->getLocks($file->getId());
$this->assertCount(1, $locks);
$this->assertEquals(5 * 60, $locks[0]->getEta());
$id = $locks[0]->getId();

// Extend the lock (lock again)
$this->lockManager->lock(new LockContext($file, ILock::TYPE_USER, self::TEST_USER1));

// The lock should only be extended, so same ID but fresh ETA
$locks = $this->lockManager->getLocks($file->getId());
$this->assertCount(1, $locks);
$this->assertEquals(15 * 60, $locks[0]->getEta());
$this->assertEquals($id, $locks[0]->getId());
}

/**
* Regression test for https://github.com/nextcloud/files_lock/issues/130
*/
public function testExtendInfiniteLock() {
\OCP\Server::get(IConfig::class)->setAppValue(Application::APP_ID, ConfigService::LOCK_TIMEOUT, '0');

// Create a file and lock it
$file = $this->loginAndGetUserFolder(self::TEST_USER1)->newFile('test-file', 'AAA');
$this->lockManager->lock(new LockContext($file, ILock::TYPE_USER, self::TEST_USER1));
$locks = $this->lockManager->getLocks($file->getId());

// We should have one lock for that file with infinite ETA
$this->assertCount(1, $locks);
$this->assertEquals(FileLock::ETA_INFINITE, $locks[0]->getEta());
$id = $locks[0]->getId();

// Extend the lock (lock again)
$this->lockManager->lock(new LockContext($file, ILock::TYPE_USER, self::TEST_USER1));

// The lock should only be extended, and keep the infinite ETA
$locks = $this->lockManager->getLocks($file->getId());
$this->assertCount(1, $locks);
$this->assertEquals(FileLock::ETA_INFINITE, $locks[0]->getEta());
$this->assertEquals($id, $locks[0]->getId());
}

private function loginAndGetUserFolder(string $userId) {
$this->loginAsUser($userId);
return $this->rootFolder->getUserFolder($userId);
}

private function shareFileWithUser(\OCP\Files\File $file, $owner, $user) {
$this->shareManager = \OC::$server->getShareManager();
$this->shareManager = \OCP\Server::get(IShareManager::class);
$share1 = $this->shareManager->newShare();
$share1->setNode($file)
->setSharedBy($owner)
Expand All @@ -329,11 +398,11 @@ private function toTheFuture(int $seconds): void {
public function tearDown(): void {
parent::tearDown();
$folder = $this->rootFolder->getUserFolder(self::TEST_USER1);
$folder->delete('testfile');
$folder->delete('test-file');
$folder->delete('etag_test');
$folder->delete('testfile2');
$folder->delete('testfile3');
$folder->delete('testfile-infinite');
$folder->delete('testfile_public');
$folder->delete('test-file2');
$folder->delete('test-file3');
$folder->delete('test-file-infinite');
$folder->delete('test-file_public');
}
}
4 changes: 3 additions & 1 deletion tests/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
\OC::$composerAutoloader->addPsr4('Tests\\', OC::$SERVERROOT . '/tests/', true);

// load all enabled apps
\OC_App::loadApps();
\OCP\Server::get(\OCP\App\IAppManager::class)->loadApps();
\OCP\Server::get(\OCP\App\IAppManager::class)->enableApp('files_lock', true);
\OC_App::updateApp('files_lock');

set_include_path(get_include_path() . PATH_SEPARATOR . '/usr/share/php');
Loading