diff options
Diffstat (limited to 'tests')
32 files changed, 1640 insertions, 261 deletions
diff --git a/tests/Core/Controller/ClientFlowLoginControllerTest.php b/tests/Core/Controller/ClientFlowLoginControllerTest.php index 7c525b53210..7a98e5c26c6 100644 --- a/tests/Core/Controller/ClientFlowLoginControllerTest.php +++ b/tests/Core/Controller/ClientFlowLoginControllerTest.php @@ -338,7 +338,7 @@ class ClientFlowLoginControllerTest extends TestCase { ->method('getServerHost') ->willReturn('example.com'); - $expected = new Http\RedirectResponse('nc://MyLoginName:MyGeneratedToken@example.com'); + $expected = new Http\RedirectResponse('nc://login/server:example.com&user:MyLoginName&password:MyGeneratedToken'); $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken')); } @@ -402,7 +402,7 @@ class ClientFlowLoginControllerTest extends TestCase { ->method('getServerHost') ->willReturn('example.com'); - $expected = new Http\RedirectResponse('nc://MyLoginName:MyGeneratedToken@example.com'); + $expected = new Http\RedirectResponse('nc://login/server:example.com&user:MyLoginName&password:MyGeneratedToken'); $this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken')); } } diff --git a/tests/Core/Controller/CssControllerTest.php b/tests/Core/Controller/CssControllerTest.php index 30bbc12809e..c78233d8917 100644 --- a/tests/Core/Controller/CssControllerTest.php +++ b/tests/Core/Controller/CssControllerTest.php @@ -23,7 +23,7 @@ namespace Tests\Core\Controller; use OC\Core\Controller\CssController; -use OC\HintException; +use OC\Files\AppData\Factory; use OCP\AppFramework\Http; use OCP\AppFramework\Http\FileDisplayResponse; use OCP\AppFramework\Http\NotFoundResponse; @@ -40,7 +40,7 @@ class CssControllerTest extends TestCase { /** @var IAppData|\PHPUnit_Framework_MockObject_MockObject */ private $appData; - /** @var IRequests|\PHPUnit_Framework_MockObject_MockObject */ + /** @var IRequest|\PHPUnit_Framework_MockObject_MockObject */ private $request; /** @var CssController */ @@ -49,8 +49,15 @@ class CssControllerTest extends TestCase { public function setUp() { parent::setUp(); + /** @var Factory|\PHPUnit_Framework_MockObject_MockObject $factory */ + $factory = $this->createMock(Factory::class); $this->appData = $this->createMock(IAppData::class); + $factory->expects($this->once()) + ->method('get') + ->with('css') + ->willReturn($this->appData); + /** @var ITimeFactory|\PHPUnit_Framework_MockObject_MockObject $timeFactory */ $timeFactory = $this->createMock(ITimeFactory::class); $timeFactory->method('getTime') ->willReturn(1337); @@ -60,7 +67,7 @@ class CssControllerTest extends TestCase { $this->controller = new CssController( 'core', $this->request, - $this->appData, + $factory, $timeFactory ); } diff --git a/tests/Core/Controller/JsControllerTest.php b/tests/Core/Controller/JsControllerTest.php index 8456ba8b6e3..571318c89d0 100644 --- a/tests/Core/Controller/JsControllerTest.php +++ b/tests/Core/Controller/JsControllerTest.php @@ -23,6 +23,7 @@ namespace Tests\Core\Controller; use OC\Core\Controller\JsController; +use OC\Files\AppData\Factory; use OCP\AppFramework\Http; use OCP\AppFramework\Http\FileDisplayResponse; use OCP\AppFramework\Http\NotFoundResponse; @@ -48,8 +49,15 @@ class JsControllerTest extends TestCase { public function setUp() { parent::setUp(); + /** @var Factory|\PHPUnit_Framework_MockObject_MockObject $factory */ + $factory = $this->createMock(Factory::class); $this->appData = $this->createMock(IAppData::class); + $factory->expects($this->once()) + ->method('get') + ->with('js') + ->willReturn($this->appData); + /** @var ITimeFactory|\PHPUnit_Framework_MockObject_MockObject $timeFactory */ $timeFactory = $this->createMock(ITimeFactory::class); $timeFactory->method('getTime') ->willReturn(1337); @@ -59,7 +67,7 @@ class JsControllerTest extends TestCase { $this->controller = new JsController( 'core', $this->request, - $this->appData, + $factory, $timeFactory ); } diff --git a/tests/Core/Controller/LostControllerTest.php b/tests/Core/Controller/LostControllerTest.php index d7d9094c485..d7098aafcc2 100644 --- a/tests/Core/Controller/LostControllerTest.php +++ b/tests/Core/Controller/LostControllerTest.php @@ -86,9 +86,13 @@ class LostControllerTest extends \Test\TestCase { ->willReturn('ExistingUser'); $this->config = $this->createMock(IConfig::class); - $this->config->method('getSystemValue') - ->with('secret', null) - ->willReturn('SECRET'); + $this->config->expects($this->any()) + ->method('getSystemValue') + ->willReturnMap([ + ['secret', null, 'SECRET'], + ['secret', '', 'SECRET'], + ['lost_password_link', '', ''], + ]); $this->l10n = $this->createMock(IL10N::class); $this->l10n ->expects($this->any()) @@ -347,10 +351,6 @@ class LostControllerTest extends \Test\TestCase { ->method('send') ->with($message); - $this->config->method('getSystemValue') - ->with('secret', '') - ->willReturn('SECRET'); - $this->crypto->method('encrypt') ->with( $this->equalTo('12348:ThisIsMaybeANotSoSecretToken!'), @@ -434,10 +434,6 @@ class LostControllerTest extends \Test\TestCase { ->method('send') ->with($message); - $this->config->method('getSystemValue') - ->with('secret', '') - ->willReturn('SECRET'); - $this->crypto->method('encrypt') ->with( $this->equalTo('12348:ThisIsMaybeANotSoSecretToken!'), @@ -516,10 +512,6 @@ class LostControllerTest extends \Test\TestCase { ->with($message) ->will($this->throwException(new \Exception())); - $this->config->method('getSystemValue') - ->with('secret', '') - ->willReturn('SECRET'); - $this->crypto->method('encrypt') ->with( $this->equalTo('12348:ThisIsMaybeANotSoSecretToken!'), diff --git a/tests/Settings/Controller/AuthSettingsControllerTest.php b/tests/Settings/Controller/AuthSettingsControllerTest.php index 7f4277acd73..5c1280ff4b0 100644 --- a/tests/Settings/Controller/AuthSettingsControllerTest.php +++ b/tests/Settings/Controller/AuthSettingsControllerTest.php @@ -133,11 +133,11 @@ class AuthSettingsControllerTest extends TestCase { ->method('getLoginName') ->will($this->returnValue('User13')); - $this->secureRandom->expects($this->exactly(4)) + $this->secureRandom->expects($this->exactly(5)) ->method('generate') - ->with(5, implode('', range('A', 'Z'))) + ->with(5, ISecureRandom::CHAR_HUMAN_READABLE) ->will($this->returnValue('XXXXX')); - $newToken = 'XXXXX-XXXXX-XXXXX-XXXXX'; + $newToken = 'XXXXX-XXXXX-XXXXX-XXXXX-XXXXX'; $this->tokenProvider->expects($this->once()) ->method('generateToken') diff --git a/tests/Settings/Controller/UsersControllerTest.php b/tests/Settings/Controller/UsersControllerTest.php index d659d812b0d..5905023e960 100644 --- a/tests/Settings/Controller/UsersControllerTest.php +++ b/tests/Settings/Controller/UsersControllerTest.php @@ -18,6 +18,7 @@ use OCP\App\IAppManager; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\IJobList; use OCP\IAvatar; use OCP\IAvatarManager; use OCP\IConfig; @@ -74,6 +75,10 @@ class UsersControllerTest extends \Test\TestCase { private $newUserMailHelper; /** @var ICrypto | \PHPUnit_Framework_MockObject_MockObject */ private $crypto; + /** @var IJobList | \PHPUnit_Framework_MockObject_MockObject */ + private $jobList; + /** @var \OC\Security\IdentityProof\Manager |\PHPUnit_Framework_MockObject_MockObject */ + private $securityManager; protected function setUp() { parent::setUp(); @@ -92,6 +97,10 @@ class UsersControllerTest extends \Test\TestCase { $this->timeFactory = $this->createMock(ITimeFactory::class); $this->crypto = $this->createMock(ICrypto::class); $this->newUserMailHelper = $this->createMock(NewUserMailHelper::class); + $this->timeFactory = $this->createMock(ITimeFactory::class); + $this->crypto = $this->createMock(ICrypto::class); + $this->securityManager = $this->getMockBuilder(\OC\Security\IdentityProof\Manager::class)->disableOriginalConstructor()->getMock(); + $this->jobList = $this->createMock(IJobList::class); $this->l = $this->createMock(IL10N::class); $this->l->method('t') ->will($this->returnCallback(function ($text, $parameters = []) { @@ -136,7 +145,12 @@ class UsersControllerTest extends \Test\TestCase { $this->avatarManager, $this->accountManager, $this->secureRandom, - $this->newUserMailHelper + $this->newUserMailHelper, + $this->timeFactory, + $this->crypto, + $this->securityManager, + $this->jobList + ); } else { return $this->getMockBuilder(UsersController::class) @@ -157,7 +171,11 @@ class UsersControllerTest extends \Test\TestCase { $this->avatarManager, $this->accountManager, $this->secureRandom, - $this->newUserMailHelper + $this->newUserMailHelper, + $this->timeFactory, + $this->crypto, + $this->securityManager, + $this->jobList ] )->setMethods($mockedMethods)->getMock(); } @@ -193,6 +211,9 @@ class UsersControllerTest extends \Test\TestCase { ->expects($this->once()) ->method('getBackendClassName') ->will($this->returnValue('OC_User_Database')); + $foo->expects($this->any()) + ->method('isEnabled') + ->willReturn(true); $admin = $this->createMock(User::class); $admin ->expects($this->exactly(2)) @@ -222,6 +243,9 @@ class UsersControllerTest extends \Test\TestCase { ->expects($this->once()) ->method('getBackendClassName') ->willReturn(Dummy::class); + $admin->expects($this->any()) + ->method('isEnabled') + ->willReturn(true); $bar = $this->createMock(User::class); $bar ->expects($this->exactly(2)) @@ -249,6 +273,15 @@ class UsersControllerTest extends \Test\TestCase { ->expects($this->once()) ->method('getBackendClassName') ->willReturn(Dummy::class); + $bar->expects($this->at(0)) + ->method('isEnabled') + ->willReturn(true); + $bar->expects($this->at(1)) + ->method('isEnabled') + ->willReturn(true); + $bar->expects($this->at(2)) + ->method('isEnabled') + ->willReturn(false); $this->groupManager ->expects($this->once()) @@ -312,6 +345,7 @@ class UsersControllerTest extends \Test\TestCase { 'email' => 'foo@bar.com', 'isRestoreDisabled' => false, 'isAvatarAvailable' => true, + 'isEnabled' => true, ), 1 => array( 'name' => 'admin', @@ -325,6 +359,7 @@ class UsersControllerTest extends \Test\TestCase { 'email' => 'admin@bar.com', 'isRestoreDisabled' => false, 'isAvatarAvailable' => false, + 'isEnabled' => true, ), 2 => array( 'name' => 'bar', @@ -338,6 +373,7 @@ class UsersControllerTest extends \Test\TestCase { 'email' => 'bar@dummy.com', 'isRestoreDisabled' => false, 'isAvatarAvailable' => true, + 'isEnabled' => false, ), ) ); @@ -381,6 +417,9 @@ class UsersControllerTest extends \Test\TestCase { ->expects($this->once()) ->method('getBackendClassName') ->will($this->returnValue('OC_User_Database')); + $foo->expects($this->any()) + ->method('isEnabled') + ->willReturn(true); $admin = $this->createMock(User::class); $admin ->expects($this->exactly(2)) @@ -410,6 +449,9 @@ class UsersControllerTest extends \Test\TestCase { ->expects($this->once()) ->method('getBackendClassName') ->willReturn(Dummy::class); + $admin->expects($this->any()) + ->method('isEnabled') + ->willReturn(true); $bar = $this->createMock(User::class); $bar ->expects($this->exactly(2)) @@ -437,6 +479,9 @@ class UsersControllerTest extends \Test\TestCase { ->expects($this->once()) ->method('getBackendClassName') ->willReturn(Dummy::class); + $bar->expects($this->any()) + ->method('isEnabled') + ->willReturn(true); $this->groupManager ->expects($this->at(2)) @@ -514,6 +559,7 @@ class UsersControllerTest extends \Test\TestCase { 'email' => 'bar@dummy.com', 'isRestoreDisabled' => false, 'isAvatarAvailable' => true, + 'isEnabled' => true, ], 1=> [ 'name' => 'foo', @@ -527,6 +573,7 @@ class UsersControllerTest extends \Test\TestCase { 'email' => 'foo@bar.com', 'isRestoreDisabled' => false, 'isAvatarAvailable' => true, + 'isEnabled' => true, ], 2 => [ 'name' => 'admin', @@ -540,6 +587,7 @@ class UsersControllerTest extends \Test\TestCase { 'email' => 'admin@bar.com', 'isRestoreDisabled' => false, 'isAvatarAvailable' => false, + 'isEnabled' => true, ], ] ); @@ -582,6 +630,9 @@ class UsersControllerTest extends \Test\TestCase { ->expects($this->once()) ->method('getBackendClassName') ->will($this->returnValue('OC_User_Database')); + $foo->expects($this->any()) + ->method('isEnabled') + ->willReturn(true); $admin = $this->createMock(User::class); $admin ->expects($this->exactly(2)) @@ -611,6 +662,9 @@ class UsersControllerTest extends \Test\TestCase { ->expects($this->once()) ->method('getBackendClassName') ->willReturn(Dummy::class); + $admin->expects($this->any()) + ->method('isEnabled') + ->willReturn(true); $bar = $this->createMock(User::class); $bar ->expects($this->exactly(2)) @@ -638,6 +692,9 @@ class UsersControllerTest extends \Test\TestCase { ->expects($this->once()) ->method('getBackendClassName') ->willReturn(Dummy::class); + $bar->expects($this->any()) + ->method('isEnabled') + ->willReturn(true); $this->userManager ->expects($this->once()) @@ -674,6 +731,7 @@ class UsersControllerTest extends \Test\TestCase { 'email' => 'foo@bar.com', 'isRestoreDisabled' => false, 'isAvatarAvailable' => true, + 'isEnabled' => true, ), 1 => array( 'name' => 'admin', @@ -687,6 +745,7 @@ class UsersControllerTest extends \Test\TestCase { 'email' => 'admin@bar.com', 'isRestoreDisabled' => false, 'isAvatarAvailable' => false, + 'isEnabled' => true, ), 2 => array( 'name' => 'bar', @@ -700,6 +759,7 @@ class UsersControllerTest extends \Test\TestCase { 'email' => 'bar@dummy.com', 'isRestoreDisabled' => false, 'isAvatarAvailable' => true, + 'isEnabled' => true, ), ) ); @@ -737,6 +797,10 @@ class UsersControllerTest extends \Test\TestCase { ->expects($this->once()) ->method('getBackendClassName') ->will($this->returnValue('OC_User_Database')); + $user->expects($this->any()) + ->method('isEnabled') + ->willReturn(true); + $this->userManager ->expects($this->once()) ->method('getBackends') @@ -775,6 +839,7 @@ class UsersControllerTest extends \Test\TestCase { 'email' => null, 'isRestoreDisabled' => false, 'isAvatarAvailable' => true, + 'isEnabled' => true, ) ) ); @@ -814,6 +879,9 @@ class UsersControllerTest extends \Test\TestCase { ->expects($this->once()) ->method('getBackendClassName') ->will($this->returnValue('bar')); + $user->expects($this->any()) + ->method('isEnabled') + ->willReturn(true); $this->userManager ->expects($this->once()) @@ -846,6 +914,7 @@ class UsersControllerTest extends \Test\TestCase { 'email' => null, 'isRestoreDisabled' => false, 'isAvatarAvailable' => true, + 'isEnabled' => true, ), Http::STATUS_CREATED ); @@ -870,6 +939,9 @@ class UsersControllerTest extends \Test\TestCase { ->expects($this->once()) ->method('getBackendClassName') ->will($this->returnValue('bar')); + $user->expects($this->any()) + ->method('isEnabled') + ->willReturn(true); $existingGroup = $this->getMockBuilder('\OCP\IGroup') ->disableOriginalConstructor()->getMock(); $existingGroup @@ -928,6 +1000,7 @@ class UsersControllerTest extends \Test\TestCase { 'email' => null, 'isRestoreDisabled' => false, 'isAvatarAvailable' => true, + 'isEnabled' => true, ), Http::STATUS_CREATED ); @@ -957,6 +1030,9 @@ class UsersControllerTest extends \Test\TestCase { ->method('getBackendClassName') ->will($this->returnValue('bar')); $subGroup1 = $this->createMock(IGroup::class); + $newUser->expects($this->any()) + ->method('isEnabled') + ->willReturn(true); $subGroup1 ->expects($this->any()) ->method('getGID') @@ -1016,6 +1092,7 @@ class UsersControllerTest extends \Test\TestCase { 'email' => null, 'isRestoreDisabled' => false, 'isAvatarAvailable' => true, + 'isEnabled' => true, ), Http::STATUS_CREATED ); @@ -1445,7 +1522,8 @@ class UsersControllerTest extends \Test\TestCase { } private function mockUser($userId = 'foo', $displayName = 'M. Foo', - $lastLogin = 500, $home = '/home/foo', $backend = 'OC_User_Database') { + $lastLogin = 500, $home = '/home/foo', + $backend = 'OC_User_Database', $enabled = true) { $user = $this->createMock(User::class); $user ->expects($this->any()) @@ -1465,6 +1543,9 @@ class UsersControllerTest extends \Test\TestCase { ->expects($this->once()) ->method('getBackendClassName') ->will($this->returnValue($backend)); + $user->expects($this->any()) + ->method('isEnabled') + ->willReturn($enabled); $result = [ 'name' => $userId, @@ -1478,6 +1559,7 @@ class UsersControllerTest extends \Test\TestCase { 'email' => null, 'isRestoreDisabled' => false, 'isAvatarAvailable' => true, + 'isEnabled' => $enabled, ]; return [$user, $result]; @@ -2141,6 +2223,11 @@ class UsersControllerTest extends \Test\TestCase { /** * @dataProvider setEmailAddressData * + * @param string $mailAddress + * @param bool $isValid + * @param bool $expectsUpdate + * @param bool $canChangeDisplayName + * @param int $responseCode */ public function testSetEMailAddress($mailAddress, $isValid, $expectsUpdate, $canChangeDisplayName, $responseCode) { $user = $this->createMock(User::class); @@ -2212,6 +2299,9 @@ class UsersControllerTest extends \Test\TestCase { ->expects($this->once()) ->method('getBackendClassName') ->willReturn('bar'); + $user + ->method('isEnabled') + ->willReturn(true); $this->userManager ->expects($this->once()) @@ -2261,10 +2351,519 @@ class UsersControllerTest extends \Test\TestCase { 'email' => 'abc@example.org', 'isRestoreDisabled' => false, 'isAvatarAvailable' => true, + 'isEnabled' => true, ], Http::STATUS_CREATED ); $response = $controller->create('foo', '', array(), 'abc@example.org'); $this->assertEquals($expectedResponse, $response); } + + /** + * @param string $account + * @param string $type + * @param array $dataBefore + * @param array $expectedData + * + * @dataProvider dataTestGetVerificationCode + */ + public function testGetVerificationCode($account, $type, $dataBefore, $expectedData, $onlyVerificationCode) { + + $message = 'Use my Federated Cloud ID to share with me: user@nextcloud.com'; + $signature = 'theSignature'; + + $code = $message . ' ' . $signature; + if($type === AccountManager::PROPERTY_TWITTER) { + $code = $message . ' ' . md5($signature); + } + + $controller = $this->getController(false, ['signMessage', 'getCurrentTime']); + + $user = $this->createMock(IUser::class); + $this->userSession->expects($this->once())->method('getUser')->willReturn($user); + $this->accountManager->expects($this->once())->method('getUser')->with($user)->willReturn($dataBefore); + $user->expects($this->any())->method('getCloudId')->willReturn('user@nextcloud.com'); + $user->expects($this->any())->method('getUID')->willReturn('uid'); + $controller->expects($this->once())->method('signMessage')->with($user, $message)->willReturn($signature); + $controller->expects($this->any())->method('getCurrentTime')->willReturn(1234567); + + if ($onlyVerificationCode === false) { + $this->accountManager->expects($this->once())->method('updateUser')->with($user, $expectedData); + $this->jobList->expects($this->once())->method('add') + ->with('OC\Settings\BackgroundJobs\VerifyUserData', + [ + 'verificationCode' => $code, + 'data' => $dataBefore[$type]['value'], + 'type' => $type, + 'uid' => 'uid', + 'try' => 0, + 'lastRun' => 1234567 + ]); + } + + $result = $controller->getVerificationCode($account, $onlyVerificationCode); + + $data = $result->getData(); + $this->assertSame(Http::STATUS_OK, $result->getStatus()); + $this->assertSame($code, $data['code']); + } + + public function dataTestGetVerificationCode() { + + $accountDataBefore = [ + AccountManager::PROPERTY_WEBSITE => ['value' => 'https://nextcloud.com', 'verified' => AccountManager::NOT_VERIFIED], + AccountManager::PROPERTY_TWITTER => ['value' => '@nextclouders', 'verified' => AccountManager::NOT_VERIFIED, 'signature' => 'theSignature'], + ]; + + $accountDataAfterWebsite = [ + AccountManager::PROPERTY_WEBSITE => ['value' => 'https://nextcloud.com', 'verified' => AccountManager::VERIFICATION_IN_PROGRESS, 'signature' => 'theSignature'], + AccountManager::PROPERTY_TWITTER => ['value' => '@nextclouders', 'verified' => AccountManager::NOT_VERIFIED, 'signature' => 'theSignature'], + ]; + + $accountDataAfterTwitter = [ + AccountManager::PROPERTY_WEBSITE => ['value' => 'https://nextcloud.com', 'verified' => AccountManager::NOT_VERIFIED], + AccountManager::PROPERTY_TWITTER => ['value' => '@nextclouders', 'verified' => AccountManager::VERIFICATION_IN_PROGRESS, 'signature' => 'theSignature'], + ]; + + return [ + ['verify-twitter', AccountManager::PROPERTY_TWITTER, $accountDataBefore, $accountDataAfterTwitter, false], + ['verify-website', AccountManager::PROPERTY_WEBSITE, $accountDataBefore, $accountDataAfterWebsite, false], + ['verify-twitter', AccountManager::PROPERTY_TWITTER, $accountDataBefore, $accountDataAfterTwitter, true], + ['verify-website', AccountManager::PROPERTY_WEBSITE, $accountDataBefore, $accountDataAfterWebsite, true], + ]; + } + + /** + * test get verification code in case no valid user was given + */ + public function testGetVerificationCodeInvalidUser() { + + $controller = $this->getController(); + $this->userSession->expects($this->once())->method('getUser')->willReturn(null); + $result = $controller->getVerificationCode('account', false); + + $this->assertSame(Http::STATUS_BAD_REQUEST, $result->getStatus()); + } + + public function testDisableUserFailsDueSameUser() { + $user = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor()->getMock(); + $user->expects($this->once()) + ->method('getUID') + ->will($this->returnValue('abc')); + $this->userSession + ->expects($this->once()) + ->method('getUser') + ->will($this->returnValue($user)); + $expectedResponse = new DataResponse( + [ + 'status' => 'error', + 'data' => [ + 'message' => 'Error while disabling user.', + ], + ], + Http::STATUS_FORBIDDEN + ); + $response = $this->getController(true)->setEnabled('abc', false); + $this->assertEquals($expectedResponse, $response); + } + + public function testDisableUserFailsDueNoAdminAndNoSubadmin() { + $user = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor()->getMock(); + $user->expects($this->once()) + ->method('getUID') + ->will($this->returnValue('def')); + $this->userSession + ->expects($this->exactly(2)) + ->method('getUser') + ->will($this->returnValue($user)); + $user2 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor()->getMock(); + $user2->expects($this->never()) + ->method('setEnabled'); + $this->userManager + ->expects($this->once()) + ->method('get') + ->with('abc') + ->willReturn($user2); + + $subadmin = $this->createMock('\OC\SubAdmin'); + $subadmin->expects($this->once()) + ->method('isUserAccessible') + ->will($this->returnValue(false)); + $this->groupManager + ->expects($this->once()) + ->method('getSubAdmin') + ->willReturn($subadmin); + + $expectedResponse = new DataResponse( + [ + 'status' => 'error', + 'data' => [ + 'message' => 'Authentication error', + ], + ], + Http::STATUS_FORBIDDEN + ); + $response = $this->getController(false)->setEnabled('abc', false); + $this->assertEquals($expectedResponse, $response); + } + + public function testDisableUserFailsDueNoUser() { + $user = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor()->getMock(); + $user->expects($this->once()) + ->method('getUID') + ->will($this->returnValue('def')); + $this->userSession + ->expects($this->exactly(1)) + ->method('getUser') + ->will($this->returnValue($user)); + $this->userManager + ->expects($this->once()) + ->method('get') + ->with('abc') + ->willReturn(null); + + $this->groupManager + ->expects($this->never()) + ->method('getSubAdmin'); + + $expectedResponse = new DataResponse( + [ + 'status' => 'error', + 'data' => [ + 'message' => 'Error while disabling user.', + ], + ], + Http::STATUS_FORBIDDEN + ); + $response = $this->getController(true)->setEnabled('abc', false); + $this->assertEquals($expectedResponse, $response); + } + + public function testDisableUserFailsDueNoUserForSubAdmin() { + $user = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor()->getMock(); + $user->expects($this->once()) + ->method('getUID') + ->will($this->returnValue('def')); + $this->userSession + ->expects($this->exactly(1)) + ->method('getUser') + ->will($this->returnValue($user)); + $this->userManager + ->expects($this->once()) + ->method('get') + ->with('abc') + ->willReturn(null); + + $this->groupManager + ->expects($this->never()) + ->method('getSubAdmin'); + + $expectedResponse = new DataResponse( + [ + 'status' => 'error', + 'data' => [ + 'message' => 'Error while disabling user.', + ], + ], + Http::STATUS_FORBIDDEN + ); + $response = $this->getController(false)->setEnabled('abc', false); + $this->assertEquals($expectedResponse, $response); + } + + public function testDisableUserSuccessForAdmin() { + $user = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor()->getMock(); + $user->expects($this->once()) + ->method('getUID') + ->will($this->returnValue('def')); + $this->userSession + ->expects($this->exactly(1)) + ->method('getUser') + ->will($this->returnValue($user)); + $user2 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor()->getMock(); + $user2->expects($this->once()) + ->method('setEnabled') + ->with(false); + $this->userManager + ->expects($this->once()) + ->method('get') + ->with('abc') + ->willReturn($user2); + + $this->groupManager + ->expects($this->never()) + ->method('getSubAdmin'); + + $expectedResponse = new DataResponse( + [ + 'status' => 'success', + 'data' => [ + 'username' => 'abc', + 'enabled' => 0, + ], + ] + ); + $response = $this->getController(true)->setEnabled('abc', false); + $this->assertEquals($expectedResponse, $response); + } + + public function testDisableUserSuccessForSubAdmin() { + $user = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor()->getMock(); + $user->expects($this->once()) + ->method('getUID') + ->will($this->returnValue('def')); + $this->userSession + ->expects($this->exactly(2)) + ->method('getUser') + ->will($this->returnValue($user)); + $user2 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor()->getMock(); + $user2->expects($this->once()) + ->method('setEnabled'); + $this->userManager + ->expects($this->once()) + ->method('get') + ->with('abc') + ->willReturn($user2); + + $subadmin = $this->createMock('\OC\SubAdmin'); + $subadmin->expects($this->once()) + ->method('isUserAccessible') + ->will($this->returnValue(true)); + $this->groupManager + ->expects($this->once()) + ->method('getSubAdmin') + ->willReturn($subadmin); + + $expectedResponse = new DataResponse( + [ + 'status' => 'success', + 'data' => [ + 'username' => 'abc', + 'enabled' => 0, + ], + ] + ); + $response = $this->getController(false)->setEnabled('abc', false); + $this->assertEquals($expectedResponse, $response); + } + + public function testEnableUserFailsDueSameUser() { + $user = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor()->getMock(); + $user->expects($this->once()) + ->method('getUID') + ->will($this->returnValue('abc')); + $this->userSession + ->expects($this->once()) + ->method('getUser') + ->will($this->returnValue($user)); + $expectedResponse = new DataResponse( + [ + 'status' => 'error', + 'data' => [ + 'message' => 'Error while enabling user.', + ], + ], + Http::STATUS_FORBIDDEN + ); + $response = $this->getController(true)->setEnabled('abc', true); + $this->assertEquals($expectedResponse, $response); + } + + public function testEnableUserFailsDueNoAdminAndNoSubadmin() { + $user = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor()->getMock(); + $user->expects($this->once()) + ->method('getUID') + ->will($this->returnValue('def')); + $this->userSession + ->expects($this->exactly(2)) + ->method('getUser') + ->will($this->returnValue($user)); + $user2 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor()->getMock(); + $user2->expects($this->never()) + ->method('setEnabled'); + $this->userManager + ->expects($this->once()) + ->method('get') + ->with('abc') + ->willReturn($user2); + + $subadmin = $this->createMock('\OC\SubAdmin'); + $subadmin->expects($this->once()) + ->method('isUserAccessible') + ->will($this->returnValue(false)); + $this->groupManager + ->expects($this->once()) + ->method('getSubAdmin') + ->willReturn($subadmin); + + $expectedResponse = new DataResponse( + [ + 'status' => 'error', + 'data' => [ + 'message' => 'Authentication error', + ], + ], + Http::STATUS_FORBIDDEN + ); + $response = $this->getController(false)->setEnabled('abc', true); + $this->assertEquals($expectedResponse, $response); + } + + public function testEnableUserFailsDueNoUser() { + $user = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor()->getMock(); + $user->expects($this->once()) + ->method('getUID') + ->will($this->returnValue('def')); + $this->userSession + ->expects($this->exactly(1)) + ->method('getUser') + ->will($this->returnValue($user)); + $this->userManager + ->expects($this->once()) + ->method('get') + ->with('abc') + ->willReturn(null); + + $this->groupManager + ->expects($this->never()) + ->method('getSubAdmin'); + + $expectedResponse = new DataResponse( + [ + 'status' => 'error', + 'data' => [ + 'message' => 'Error while enabling user.', + ], + ], + Http::STATUS_FORBIDDEN + ); + $response = $this->getController(true)->setEnabled('abc', true); + $this->assertEquals($expectedResponse, $response); + } + + public function testEnableUserFailsDueNoUserForSubAdmin() { + $user = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor()->getMock(); + $user->expects($this->once()) + ->method('getUID') + ->will($this->returnValue('def')); + $this->userSession + ->expects($this->exactly(1)) + ->method('getUser') + ->will($this->returnValue($user)); + $this->userManager + ->expects($this->once()) + ->method('get') + ->with('abc') + ->willReturn(null); + + $this->groupManager + ->expects($this->never()) + ->method('getSubAdmin'); + + $expectedResponse = new DataResponse( + [ + 'status' => 'error', + 'data' => [ + 'message' => 'Error while enabling user.', + ], + ], + Http::STATUS_FORBIDDEN + ); + $response = $this->getController(false)->setEnabled('abc', true); + $this->assertEquals($expectedResponse, $response); + } + + public function testEnableUserSuccessForAdmin() { + $user = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor()->getMock(); + $user->expects($this->once()) + ->method('getUID') + ->will($this->returnValue('def')); + $this->userSession + ->expects($this->exactly(1)) + ->method('getUser') + ->will($this->returnValue($user)); + $user2 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor()->getMock(); + $user2->expects($this->once()) + ->method('setEnabled'); + $this->userManager + ->expects($this->once()) + ->method('get') + ->with('abc') + ->willReturn($user2); + + $this->groupManager + ->expects($this->never()) + ->method('getSubAdmin'); + + $expectedResponse = new DataResponse( + [ + 'status' => 'success', + 'data' => [ + 'username' => 'abc', + 'enabled' => 1, + ], + ] + ); + $response = $this->getController(true)->setEnabled('abc', true); + $this->assertEquals($expectedResponse, $response); + } + + public function testEnableUserSuccessForSubAdmin() { + $user = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor()->getMock(); + $user->expects($this->once()) + ->method('getUID') + ->will($this->returnValue('def')); + $this->userSession + ->expects($this->exactly(2)) + ->method('getUser') + ->will($this->returnValue($user)); + $user2 = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor()->getMock(); + $user2->expects($this->once()) + ->method('setEnabled') + ->with(true); + $this->userManager + ->expects($this->once()) + ->method('get') + ->with('abc') + ->willReturn($user2); + + $subadmin = $this->createMock('\OC\SubAdmin'); + $subadmin->expects($this->once()) + ->method('isUserAccessible') + ->will($this->returnValue(true)); + $this->groupManager + ->expects($this->once()) + ->method('getSubAdmin') + ->willReturn($subadmin); + + $expectedResponse = new DataResponse( + [ + 'status' => 'success', + 'data' => [ + 'username' => 'abc', + 'enabled' => 1, + ], + ] + ); + $response = $this->getController(false)->setEnabled('abc', true); + $this->assertEquals($expectedResponse, $response); + } } diff --git a/tests/acceptance/features/bootstrap/FilesAppContext.php b/tests/acceptance/features/bootstrap/FilesAppContext.php index bc926fbe52f..e769820c9ee 100644 --- a/tests/acceptance/features/bootstrap/FilesAppContext.php +++ b/tests/acceptance/features/bootstrap/FilesAppContext.php @@ -106,7 +106,11 @@ class FilesAppContext implements Context, ActorAwareInterface { * @return Locator */ public static function shareLinkCheckbox() { - return Locator::forThe()->content("Share link")->descendantOf(self::currentSectionDetailsView())-> + // forThe()->checkbox("Share link") can not be used here; that would + // return the checkbox itself, but the element that the user interacts + // with is the label. + return Locator::forThe()->xpath("//label[normalize-space() = 'Share link']")-> + descendantOf(self::currentSectionDetailsView())-> describedAs("Share link checkbox in the details view in Files app"); } @@ -122,7 +126,11 @@ class FilesAppContext implements Context, ActorAwareInterface { * @return Locator */ public static function passwordProtectCheckbox() { - return Locator::forThe()->content("Password protect")->descendantOf(self::currentSectionDetailsView())-> + // forThe()->checkbox("Password protect") can not be used here; that + // would return the checkbox itself, but the element that the user + // interacts with is the label. + return Locator::forThe()->xpath("//label[normalize-space() = 'Password protect']")-> + descendantOf(self::currentSectionDetailsView())-> describedAs("Password protect checkbox in the details view in Files app"); } @@ -163,7 +171,7 @@ class FilesAppContext implements Context, ActorAwareInterface { * @return Locator */ public static function favoritedStateIconForFile($fileName) { - return Locator::forThe()->content("Favorited")->descendantOf(self::favoriteActionForFile($fileName))-> + return Locator::forThe()->css(".icon-starred")->descendantOf(self::favoriteActionForFile($fileName))-> describedAs("Favorited state icon for file $fileName in Files app"); } @@ -210,7 +218,8 @@ class FilesAppContext implements Context, ActorAwareInterface { * @return Locator */ private static function fileActionsMenuItemFor($itemText) { - return Locator::forThe()->content($itemText)->descendantOf(self::fileActionsMenu())-> + return Locator::forThe()->xpath("//a[normalize-space() = '$itemText']")-> + descendantOf(self::fileActionsMenu())-> describedAs($itemText . " item in file actions menu in Files app"); } diff --git a/tests/acceptance/features/bootstrap/FilesSharingAppContext.php b/tests/acceptance/features/bootstrap/FilesSharingAppContext.php index d9d5eca7359..88c1180c753 100644 --- a/tests/acceptance/features/bootstrap/FilesSharingAppContext.php +++ b/tests/acceptance/features/bootstrap/FilesSharingAppContext.php @@ -47,7 +47,7 @@ class FilesSharingAppContext implements Context, ActorAwareInterface { * @return Locator */ public static function wrongPasswordMessage() { - return Locator::forThe()->content("The password is wrong. Try again.")-> + return Locator::forThe()->xpath("//*[@class = 'warning' and normalize-space() = 'The password is wrong. Try again.']")-> describedAs("Wrong password message in Authenticate page"); } diff --git a/tests/acceptance/features/bootstrap/LoginPageContext.php b/tests/acceptance/features/bootstrap/LoginPageContext.php index 4b0672f652c..61f12f54be6 100644 --- a/tests/acceptance/features/bootstrap/LoginPageContext.php +++ b/tests/acceptance/features/bootstrap/LoginPageContext.php @@ -66,7 +66,7 @@ class LoginPageContext implements Context, ActorAwareInterface { * @return Locator */ public static function wrongPasswordMessage() { - return Locator::forThe()->content("Wrong password. Reset it?")-> + return Locator::forThe()->xpath("//*[@class = 'warning' and normalize-space() = 'Wrong password. Reset it?']")-> describedAs("Wrong password message in Login page"); } diff --git a/tests/acceptance/features/bootstrap/NotificationContext.php b/tests/acceptance/features/bootstrap/NotificationContext.php index f8b784e2465..ac9838562e1 100644 --- a/tests/acceptance/features/bootstrap/NotificationContext.php +++ b/tests/acceptance/features/bootstrap/NotificationContext.php @@ -31,7 +31,8 @@ class NotificationContext implements Context, ActorAwareInterface { * @return Locator */ public static function notificationMessage($message) { - return Locator::forThe()->content($message)->descendantOf(self::notificationContainer())-> + return Locator::forThe()->xpath("//*[@class = 'row' and normalize-space() = '$message']")-> + descendantOf(self::notificationContainer())-> describedAs("$message notification"); } diff --git a/tests/acceptance/features/bootstrap/SettingsMenuContext.php b/tests/acceptance/features/bootstrap/SettingsMenuContext.php index 9ce8df4caef..1ff5d94e98f 100644 --- a/tests/acceptance/features/bootstrap/SettingsMenuContext.php +++ b/tests/acceptance/features/bootstrap/SettingsMenuContext.php @@ -61,7 +61,8 @@ class SettingsMenuContext implements Context, ActorAwareInterface { * @return Locator */ private static function menuItemFor($itemText) { - return Locator::forThe()->content($itemText)->descendantOf(self::settingsMenu())-> + return Locator::forThe()->xpath("//a[normalize-space() = '$itemText']")-> + descendantOf(self::settingsMenu())-> describedAs($itemText . " item in Settings menu"); } diff --git a/tests/acceptance/features/core/Actor.php b/tests/acceptance/features/core/Actor.php index 0c23b5f7a40..a87ccfb7737 100644 --- a/tests/acceptance/features/core/Actor.php +++ b/tests/acceptance/features/core/Actor.php @@ -42,6 +42,11 @@ * exception is thrown if the element is not found, and, optionally, it is * possible to try again to find the element several times before giving up. * + * The returned object is also a wrapper over the element itself that + * automatically handles common causes of failed commands, like clicking on a + * hidden element; in this case, the wrapper would wait for the element to be + * visible up to the timeout set to find the element. + * * The amount of time to wait before giving up is specified in each call to * find(). However, a general multiplier to be applied to every timeout can be * set using setFindTimeoutMultiplier(); this makes possible to retry longer @@ -150,6 +155,10 @@ class Actor { * before retrying is half a second. If the timeout is not 0 it will be * affected by the multiplier set using setFindTimeoutMultiplier(), if any. * + * When found, the element is returned wrapped in an ElementWrapper; the + * ElementWrapper handles common causes of failures when executing commands + * in an element, like clicking on a hidden element. + * * In any case, if the element, or its ancestors, can not be found a * NoSuchElementException is thrown. * @@ -158,78 +167,16 @@ class Actor { * most for the element to appear. * @param float $timeoutStep the number of seconds (decimals allowed) to * wait before trying to find the element again. - * @return \Behat\Mink\Element\Element the element found. + * @return ElementWrapper an ElementWrapper object for the element. * @throws NoSuchElementException if the element, or its ancestor, can not * be found. */ - public function find($elementLocator, $timeout = 0, $timeoutStep = 0.5) { + public function find(Locator $elementLocator, $timeout = 0, $timeoutStep = 0.5) { $timeout = $timeout * $this->findTimeoutMultiplier; - $element = null; - $selector = $elementLocator->getSelector(); - $locator = $elementLocator->getLocator(); - $ancestorElement = $this->findAncestorElement($elementLocator, $timeout, $timeoutStep); - - $findCallback = function() use (&$element, $selector, $locator, $ancestorElement) { - $element = $ancestorElement->find($selector, $locator); - - return $element !== null; - }; - if (!Utils::waitFor($findCallback, $timeout, $timeoutStep)) { - $message = $elementLocator->getDescription() . " could not be found"; - if ($timeout > 0) { - $message = $message . " after $timeout seconds"; - } - throw new NoSuchElementException($message); - } - - return $element; - } + $elementFinder = new ElementFinder($this->session, $elementLocator, $timeout, $timeoutStep); - /** - * Returns the ancestor element from which the given locator will be looked - * for. - * - * If the ancestor of the given locator is another locator the element for - * the ancestor locator is found and returned. If the ancestor of the given - * locator is already an element that element is the one returned. If the - * given locator has no ancestor then the base document element is returned. - * - * The timeout is used only when finding the element for the ancestor - * locator; if the timeout expires a NoSuchElementException is thrown. - * - * @param Locator $elementLocator the locator for the element to get its - * ancestor. - * @param float $timeout the number of seconds (decimals allowed) to wait at - * most for the ancestor element to appear. - * @param float $timeoutStep the number of seconds (decimals allowed) to - * wait before trying to find the ancestor element again. - * @return \Behat\Mink\Element\Element the ancestor element found. - * @throws NoSuchElementException if the ancestor element can not be found. - */ - private function findAncestorElement($elementLocator, $timeout, $timeoutStep) { - $ancestorElement = $elementLocator->getAncestor(); - if ($ancestorElement instanceof Locator) { - try { - $ancestorElement = $this->find($ancestorElement, $timeout, $timeoutStep); - } catch (NoSuchElementException $exception) { - // Little hack to show the stack of ancestor elements that could - // not be found, as Behat only shows the message of the last - // exception in the chain. - $message = $exception->getMessage() . "\n" . - $elementLocator->getDescription() . " could not be found"; - if ($timeout > 0) { - $message = $message . " after $timeout seconds"; - } - throw new NoSuchElementException($message, $exception); - } - } - - if ($ancestorElement === null) { - $ancestorElement = $this->getSession()->getPage(); - } - - return $ancestorElement; + return new ElementWrapper($elementFinder); } /** diff --git a/tests/acceptance/features/core/ActorContext.php b/tests/acceptance/features/core/ActorContext.php index 86fe3832f66..d6fb63694ec 100644 --- a/tests/acceptance/features/core/ActorContext.php +++ b/tests/acceptance/features/core/ActorContext.php @@ -39,8 +39,9 @@ use Behat\MinkExtension\Context\RawMinkContext; * propagates its inherited "base_url" Mink parameter to the Actors as needed. * * By default no multiplier for the find timeout is set in the Actors. However, - * it can be customized using the "actorFindTimeoutMultiplier" parameter of the - * ActorContext in "behat.yml". + * it can be customized using the "actorTimeoutMultiplier" parameter of the + * ActorContext in "behat.yml". This parameter also affects the overall timeout + * to start a session for an Actor before giving up. * * Every actor used in the scenarios must have a corresponding Mink session * declared in "behat.yml" with the same name as the actor. All used sessions @@ -66,16 +67,16 @@ class ActorContext extends RawMinkContext { /** * @var float */ - private $actorFindTimeoutMultiplier; + private $actorTimeoutMultiplier; /** * Creates a new ActorContext. * - * @param float $actorFindTimeoutMultiplier the find timeout multiplier to - * set in the Actors. + * @param float $actorTimeoutMultiplier the timeout multiplier for Actor + * related timeouts. */ - public function __construct($actorFindTimeoutMultiplier = 1) { - $this->actorFindTimeoutMultiplier = $actorFindTimeoutMultiplier; + public function __construct($actorTimeoutMultiplier = 1) { + $this->actorTimeoutMultiplier = $actorTimeoutMultiplier; } /** @@ -98,6 +99,31 @@ class ActorContext extends RawMinkContext { } /** + * Returns the session with the given name. + * + * If the session is not started it is started before returning it; if the + * session fails to start (typically due to a timeout connecting with the + * web browser) it will be tried again up to $actorTimeoutMultiplier times + * in total (rounded up to the next integer) before giving up. + * + * @param string|null $sname the name of the session to get, or null for the + * default session. + * @return \Behat\Mink\Session the session. + */ + public function getSession($name = null) { + for ($i = 0; $i < ($this->actorTimeoutMultiplier - 1); $i++) { + try { + return parent::getSession($name); + } catch (\Behat\Mink\Exception\DriverException $exception) { + echo "Exception when getting " . ($name == null? "default session": "session '$name'") . ": " . $exception->getMessage() . "\n"; + echo "Trying again\n"; + } + } + + return parent::getSession($name); + } + + /** * @BeforeScenario * * Initializes the Actors for the new Scenario with the default Actor. @@ -110,7 +136,7 @@ class ActorContext extends RawMinkContext { $this->sharedNotebook = array(); $this->actors["default"] = new Actor($this->getSession(), $this->getMinkParameter("base_url"), $this->sharedNotebook); - $this->actors["default"]->setFindTimeoutMultiplier($this->actorFindTimeoutMultiplier); + $this->actors["default"]->setFindTimeoutMultiplier($this->actorTimeoutMultiplier); $this->currentActor = $this->actors["default"]; } @@ -134,7 +160,7 @@ class ActorContext extends RawMinkContext { public function iActAs($actorName) { if (!array_key_exists($actorName, $this->actors)) { $this->actors[$actorName] = new Actor($this->getSession($actorName), $this->getMinkParameter("base_url"), $this->sharedNotebook); - $this->actors[$actorName]->setFindTimeoutMultiplier($this->actorFindTimeoutMultiplier); + $this->actors[$actorName]->setFindTimeoutMultiplier($this->actorTimeoutMultiplier); } $this->currentActor = $this->actors[$actorName]; diff --git a/tests/acceptance/features/core/ElementFinder.php b/tests/acceptance/features/core/ElementFinder.php new file mode 100644 index 00000000000..d075e9fe660 --- /dev/null +++ b/tests/acceptance/features/core/ElementFinder.php @@ -0,0 +1,205 @@ +<?php + +/** + * + * @copyright Copyright (c) 2017, Daniel Calviño Sánchez (danxuliu@gmail.com) + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +/** + * Command object to find Mink elements. + * + * The element locator is relative to its ancestor (either another locator or an + * actual element); if it has no ancestor then the base document element is + * used. + * + * Sometimes an element may not be found simply because it has not appeared yet; + * for those cases ElementFinder supports trying again to find the element + * several times before giving up. The timeout parameter controls how much time + * to wait, at most, to find the element; the timeoutStep parameter controls how + * much time to wait before trying again to find the element. If ancestor + * locators need to be found the timeout is applied individually to each one, + * that is, if the timeout is 10 seconds the method will wait up to 10 seconds + * to find the ancestor of the ancestor and, then, up to 10 seconds to find the + * ancestor and, then, up to 10 seconds to find the element. By default the + * timeout is 0, so the element and its ancestor will be looked for just once; + * the default time to wait before retrying is half a second. + * + * In any case, if the element, or its ancestors, can not be found a + * NoSuchElementException is thrown. + */ +class ElementFinder { + + /** + * Finds an element in the given Mink Session. + * + * @see ElementFinder + */ + private static function findInternal(\Behat\Mink\Session $session, Locator $elementLocator, $timeout, $timeoutStep) { + $element = null; + $selector = $elementLocator->getSelector(); + $locator = $elementLocator->getLocator(); + $ancestorElement = self::findAncestorElement($session, $elementLocator, $timeout, $timeoutStep); + + $findCallback = function() use (&$element, $selector, $locator, $ancestorElement) { + $element = $ancestorElement->find($selector, $locator); + + return $element !== null; + }; + if (!Utils::waitFor($findCallback, $timeout, $timeoutStep)) { + $message = $elementLocator->getDescription() . " could not be found"; + if ($timeout > 0) { + $message = $message . " after $timeout seconds"; + } + throw new NoSuchElementException($message); + } + + return $element; + } + + /** + * Returns the ancestor element from which the given locator will be looked + * for. + * + * If the ancestor of the given locator is another locator the element for + * the ancestor locator is found and returned. If the ancestor of the given + * locator is already an element that element is the one returned. If the + * given locator has no ancestor then the base document element is returned. + * + * The timeout is used only when finding the element for the ancestor + * locator; if the timeout expires a NoSuchElementException is thrown. + * + * @param \Behat\Mink\Session $session the Mink Session to get the ancestor + * element from. + * @param Locator $elementLocator the locator for the element to get its + * ancestor. + * @param float $timeout the number of seconds (decimals allowed) to wait at + * most for the ancestor element to appear. + * @param float $timeoutStep the number of seconds (decimals allowed) to + * wait before trying to find the ancestor element again. + * @return \Behat\Mink\Element\Element the ancestor element found. + * @throws NoSuchElementException if the ancestor element can not be found. + */ + private static function findAncestorElement(\Behat\Mink\Session $session, Locator $elementLocator, $timeout, $timeoutStep) { + $ancestorElement = $elementLocator->getAncestor(); + if ($ancestorElement instanceof Locator) { + try { + $ancestorElement = self::findInternal($session, $ancestorElement, $timeout, $timeoutStep); + } catch (NoSuchElementException $exception) { + // Little hack to show the stack of ancestor elements that could + // not be found, as Behat only shows the message of the last + // exception in the chain. + $message = $exception->getMessage() . "\n" . + $elementLocator->getDescription() . " could not be found"; + if ($timeout > 0) { + $message = $message . " after $timeout seconds"; + } + throw new NoSuchElementException($message, $exception); + } + } + + if ($ancestorElement === null) { + $ancestorElement = $session->getPage(); + } + + return $ancestorElement; + } + + /** + * @var \Behat\Mink\Session + */ + private $session; + + /** + * @param Locator + */ + private $elementLocator; + + /** + * @var float + */ + private $timeout; + + /** + * @var float + */ + private $timeoutStep; + + /** + * Creates a new ElementFinder. + * + * @param \Behat\Mink\Session $session the Mink Session to get the element + * from. + * @param Locator $elementLocator the locator for the element. + * @param float $timeout the number of seconds (decimals allowed) to wait at + * most for the element to appear. + * @param float $timeoutStep the number of seconds (decimals allowed) to + * wait before trying to find the element again. + */ + public function __construct(\Behat\Mink\Session $session, Locator $elementLocator, $timeout, $timeoutStep) { + $this->session = $session; + $this->elementLocator = $elementLocator; + $this->timeout = $timeout; + $this->timeoutStep = $timeoutStep; + } + + /** + * Returns the description of the element to find. + * + * @return string the description of the element to find. + */ + public function getDescription() { + return $this->elementLocator->getDescription(); + } + + /** + * Returns the timeout. + * + * @return float the number of seconds (decimals allowed) to wait at most + * for the element to appear. + */ + public function getTimeout() { + return $this->timeout; + } + + /** + * Returns the timeout step. + * + * @return float the number of seconds (decimals allowed) to wait before + * trying to find the element again. + */ + public function getTimeoutStep() { + return $this->timeoutStep; + } + + /** + * Finds an element using the parameters set in the constructor of this + * ElementFinder. + * + * If the element, or its ancestors, can not be found a + * NoSuchElementException is thrown. + * + * @return \Behat\Mink\Element\Element the element found. + * @throws NoSuchElementException if the element, or its ancestor, can not + * be found. + */ + public function find() { + return self::findInternal($this->session, $this->elementLocator, $this->timeout, $this->timeoutStep); + } + +} diff --git a/tests/acceptance/features/core/ElementWrapper.php b/tests/acceptance/features/core/ElementWrapper.php new file mode 100644 index 00000000000..6b730903f6c --- /dev/null +++ b/tests/acceptance/features/core/ElementWrapper.php @@ -0,0 +1,275 @@ +<?php + +/** + * + * @copyright Copyright (c) 2017, Daniel Calviño Sánchez (danxuliu@gmail.com) + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +/** + * Wrapper to automatically handle failed commands on Mink elements. + * + * Commands executed on Mink elements may fail for several reasons. The + * ElementWrapper frees the caller of the commands from handling the most common + * reasons of failure. + * + * StaleElementReference exceptions are thrown when the command is executed on + * an element that is no longer attached to the DOM. This can happen even in + * a chained call like "$actor->find($locator)->click()"; in the milliseconds + * between finding the element and clicking it the element could have been + * removed from the page (for example, if a previous interaction with the page + * started an asynchronous update of the DOM). Every command executed through + * the ElementWrapper is guarded against StaleElementReference exceptions; if + * the element is stale it is found again using the same parameters to find it + * in the first place. + * + * ElementNotVisible exceptions are thrown when the command requires the element + * to be visible but the element is not. Finding an element only guarantees that + * (at that time) the element is attached to the DOM, but it does not provide + * any guarantee regarding its visibility. Due to that, a call like + * "$actor->find($locator)->click()" can fail if the element was hidden and + * meant to be made visible by a previous interaction with the page, but that + * interaction triggered an asynchronous update that was not finished when the + * click command is executed. All commands executed through the ElementWrapper + * that require the element to be visible are guarded against ElementNotVisible + * exceptions; if the element is not visible it is waited for it to be visible + * up to the timeout set to find it. + * + * Despite the automatic handling it is possible for the commands to throw those + * exceptions when they are executed again; this class does not handle cases + * like an element becoming stale several times in a row (uncommon) or an + * element not becoming visible before the timeout expires (which would mean + * that the timeout is too short or that the test has to, indeed, fail). + * + * If needed, automatically handling failed commands can be disabled calling + * "doNotHandleFailedCommands()"; as it returns the ElementWrapper it can be + * chained with the command to execute (but note that automatically handling + * failed commands will still be disabled if further commands are executed on + * the ElementWrapper). + */ +class ElementWrapper { + + /** + * @var ElementFinder + */ + private $elementFinder; + + /** + * @var \Behat\Mink\Element\Element + */ + private $element; + + /** + * @param boolean + */ + private $handleFailedCommands; + + /** + * Creates a new ElementWrapper. + * + * The wrapped element is found in the constructor itself using the + * ElementFinder. + * + * @param ElementFinder $elementFinder the command object to find the + * wrapped element. + * @throws NoSuchElementException if the element, or its ancestor, can not + * be found. + */ + public function __construct(ElementFinder $elementFinder) { + $this->elementFinder = $elementFinder; + $this->element = $elementFinder->find(); + $this->handleFailedCommands = true; + } + + /** + * Returns the raw Mink element. + * + * @return \Behat\Mink\Element\Element the wrapped element. + */ + public function getWrappedElement() { + return $element; + } + + /** + * Prevents the automatic handling of failed commands. + * + * @return ElementWrapper this ElementWrapper. + */ + public function doNotHandleFailedCommands() { + $this->handleFailedCommands = false; + + return $this; + } + + /** + * Returns whether the wrapped element is visible or not. + * + * @return boolbean true if the wrapped element is visible, false otherwise. + */ + public function isVisible() { + $commandCallback = function() { + return $this->element->isVisible(); + }; + return $this->executeCommand($commandCallback, "visibility could not be got"); + } + + /** + * Returns the text of the wrapped element. + * + * If the wrapped element is not visible the returned text is an empty + * string. + * + * @return string the text of the wrapped element, or an empty string if it + * is not visible. + */ + public function getText() { + $commandCallback = function() { + return $this->element->getText(); + }; + return $this->executeCommand($commandCallback, "text could not be got"); + } + + /** + * Returns the value of the wrapped element. + * + * The value can be got even if the wrapped element is not visible. + * + * @return string the value of the wrapped element. + */ + public function getValue() { + $commandCallback = function() { + return $this->element->getValue(); + }; + return $this->executeCommand($commandCallback, "value could not be got"); + } + + /** + * Sets the given value on the wrapped element. + * + * If automatically waits for the wrapped element to be visible (up to the + * timeout set when finding it). + * + * @param string $value the value to set. + */ + public function setValue($value) { + $commandCallback = function() use ($value) { + $this->element->setValue($value); + }; + $this->executeCommandOnVisibleElement($commandCallback, "value could not be set"); + } + + /** + * Clicks on the wrapped element. + * + * If automatically waits for the wrapped element to be visible (up to the + * timeout set when finding it). + */ + public function click() { + $commandCallback = function() { + $this->element->click(); + }; + $this->executeCommandOnVisibleElement($commandCallback, "could not be clicked"); + } + + /** + * Executes the given command. + * + * If a StaleElementReference exception is thrown the wrapped element is + * found again and, then, the command is executed again. + * + * @param \Closure $commandCallback the command to execute. + * @param string $errorMessage an error message that describes the failed + * command (appended to the description of the element). + */ + private function executeCommand(\Closure $commandCallback, $errorMessage) { + if (!$this->handleFailedCommands) { + return $commandCallback(); + } + + try { + return $commandCallback(); + } catch (\WebDriver\Exception\StaleElementReference $exception) { + $this->printFailedCommandMessage($exception, $errorMessage); + } + + $this->element = $this->elementFinder->find(); + + return $commandCallback(); + } + + /** + * Executes the given command on a visible element. + * + * If a StaleElementReference exception is thrown the wrapped element is + * found again and, then, the command is executed again. If an + * ElementNotVisible exception is thrown it is waited for the wrapped + * element to be visible and, then, the command is executed again. + * + * @param \Closure $commandCallback the command to execute. + * @param string $errorMessage an error message that describes the failed + * command (appended to the description of the element). + */ + private function executeCommandOnVisibleElement(\Closure $commandCallback, $errorMessage) { + if (!$this->handleFailedCommands) { + return $commandCallback(); + } + + try { + return $this->executeCommand($commandCallback, $errorMessage); + } catch (\WebDriver\Exception\ElementNotVisible $exception) { + $this->printFailedCommandMessage($exception, $errorMessage); + } + + $this->waitForElementToBeVisible(); + + return $commandCallback(); + } + + /** + * Prints information about the failed command. + * + * @param \Exception exception the exception thrown by the command. + * @param string $errorMessage an error message that describes the failed + * command (appended to the description of the locator of the element). + */ + private function printFailedCommandMessage(\Exception $exception, $errorMessage) { + echo $this->elementFinder->getDescription() . " " . $errorMessage . "\n"; + echo "Exception message: " . $exception->getMessage() . "\n"; + echo "Trying again\n"; + } + + /** + * Waits for the wrapped element to be visible. + * + * This method waits up to the timeout used when finding the wrapped + * element; therefore, it may return when the element is still not visible. + * + * @return boolean true if the element is visible after the wait, false + * otherwise. + */ + private function waitForElementToBeVisible() { + $isVisibleCallback = function() { + return $this->isVisible(); + }; + $timeout = $this->elementFinder->getTimeout(); + $timeoutStep = $this->elementFinder->getTimeoutStep(); + + return Utils::waitFor($isVisibleCallback, $timeout, $timeoutStep); + } + +} diff --git a/tests/acceptance/features/core/Locator.php b/tests/acceptance/features/core/Locator.php index 0ebae9b8fb1..e670a5b8065 100644 --- a/tests/acceptance/features/core/Locator.php +++ b/tests/acceptance/features/core/Locator.php @@ -134,7 +134,7 @@ class LocatorBuilder { * @return LocatorBuilderSecondStep */ public function id($value) { - return $this->customSelector("named", array("id", $value)); + return $this->customSelector("named_exact", array("id", $value)); } /** @@ -142,7 +142,7 @@ class LocatorBuilder { * @return LocatorBuilderSecondStep */ public function idOrName($value) { - return $this->customSelector("named", array("id_or_name", $value)); + return $this->customSelector("named_exact", array("id_or_name", $value)); } /** @@ -150,7 +150,7 @@ class LocatorBuilder { * @return LocatorBuilderSecondStep */ public function link($value) { - return $this->customSelector("named", array("link", $value)); + return $this->customSelector("named_exact", array("link", $value)); } /** @@ -158,7 +158,7 @@ class LocatorBuilder { * @return LocatorBuilderSecondStep */ public function button($value) { - return $this->customSelector("named", array("button", $value)); + return $this->customSelector("named_exact", array("button", $value)); } /** @@ -166,15 +166,7 @@ class LocatorBuilder { * @return LocatorBuilderSecondStep */ public function linkOrButton($value) { - return $this->customSelector("named", array("link_or_button", $value)); - } - - /** - * @param string $value - * @return LocatorBuilderSecondStep - */ - public function content($value) { - return $this->customSelector("named", array("content", $value)); + return $this->customSelector("named_exact", array("link_or_button", $value)); } /** @@ -182,7 +174,7 @@ class LocatorBuilder { * @return LocatorBuilderSecondStep */ public function field($value) { - return $this->customSelector("named", array("field", $value)); + return $this->customSelector("named_exact", array("field", $value)); } /** @@ -190,7 +182,7 @@ class LocatorBuilder { * @return LocatorBuilderSecondStep */ public function selectField($value) { - return $this->customSelector("named", array("select", $value)); + return $this->customSelector("named_exact", array("select", $value)); } /** @@ -198,7 +190,7 @@ class LocatorBuilder { * @return LocatorBuilderSecondStep */ public function checkbox($value) { - return $this->customSelector("named", array("checkbox", $value)); + return $this->customSelector("named_exact", array("checkbox", $value)); } /** @@ -206,7 +198,7 @@ class LocatorBuilder { * @return LocatorBuilderSecondStep */ public function radioButton($value) { - return $this->customSelector("named", array("radio", $value)); + return $this->customSelector("named_exact", array("radio", $value)); } /** @@ -214,7 +206,7 @@ class LocatorBuilder { * @return LocatorBuilderSecondStep */ public function fileInput($value) { - return $this->customSelector("named", array("file", $value)); + return $this->customSelector("named_exact", array("file", $value)); } /** @@ -222,7 +214,7 @@ class LocatorBuilder { * @return LocatorBuilderSecondStep */ public function optionGroup($value) { - return $this->customSelector("named", array("optgroup", $value)); + return $this->customSelector("named_exact", array("optgroup", $value)); } /** @@ -230,7 +222,7 @@ class LocatorBuilder { * @return LocatorBuilderSecondStep */ public function option($value) { - return $this->customSelector("named", array("option", $value)); + return $this->customSelector("named_exact", array("option", $value)); } /** @@ -238,7 +230,7 @@ class LocatorBuilder { * @return LocatorBuilderSecondStep */ public function fieldSet($value) { - return $this->customSelector("named", array("fieldset", $value)); + return $this->customSelector("named_exact", array("fieldset", $value)); } /** @@ -246,7 +238,7 @@ class LocatorBuilder { * @return LocatorBuilderSecondStep */ public function table($value) { - return $this->customSelector("named", array("table", $value)); + return $this->customSelector("named_exact", array("table", $value)); } } diff --git a/tests/acceptance/run-local.sh b/tests/acceptance/run-local.sh index ee7a4e6455c..93c11e810f8 100755 --- a/tests/acceptance/run-local.sh +++ b/tests/acceptance/run-local.sh @@ -39,6 +39,21 @@ set -o errexit # Behat through Composer or running Behat) expect that. cd "$(dirname $0)" +# "--timeout-multiplier N" option can be provided before any other parameter to +# set the timeout multiplier to be used in ActorContext. +TIMEOUT_MULTIPLIER="" +if [ "$1" = "--timeout-multiplier" ]; then + if [[ ! "$2" =~ ^[0-9]+$ ]]; then + echo "--timeout-multiplier must be followed by a positive integer" + + exit 1 + fi + + TIMEOUT_MULTIPLIER=$2 + + shift 2 +fi + # Safety parameter to prevent executing this script by mistake and messing with # the Git repository. if [ "$1" != "allow-git-repository-modifications" ]; then @@ -49,6 +64,22 @@ fi SCENARIO_TO_RUN=$2 +if [ "$TIMEOUT_MULTIPLIER" != "" ]; then + # Although Behat documentation states that using the BEHAT_PARAMS + # environment variable "You can set any value for any option that is + # available in a behat.yml file" this is currently not true for the + # constructor parameters of contexts (see + # https://github.com/Behat/Behat/issues/983). Thus, the default "behat.yml" + # configuration file has to be adjusted to provide the appropriate + # parameters for ActorContext. + ORIGINAL="\ + - ActorContext" + REPLACEMENT="\ + - ActorContext:\n\ + actorTimeoutMultiplier: $TIMEOUT_MULTIPLIER" + sed --in-place "s/$ORIGINAL/$REPLACEMENT/" config/behat.yml +fi + composer install cd ../../ diff --git a/tests/acceptance/run.sh b/tests/acceptance/run.sh index 42a718d46e2..1b68f8655ae 100755 --- a/tests/acceptance/run.sh +++ b/tests/acceptance/run.sh @@ -197,6 +197,22 @@ trap cleanUp EXIT # the Git working directory to the container) expect that. cd "$(dirname $0)" +# "--timeout-multiplier N" option can be provided before the specific scenario +# to run, if any, to set the timeout multiplier to be used in the acceptance +# tests. +TIMEOUT_MULTIPLIER_OPTION="" +if [ "$1" = "--timeout-multiplier" ]; then + if [[ ! "$2" =~ ^[0-9]+$ ]]; then + echo "--timeout-multiplier must be followed by a positive integer" + + exit 1 + fi + + TIMEOUT_MULTIPLIER_OPTION="--timeout-multiplier $2" + + shift 2 +fi + # If no parameter is provided to this script all the acceptance tests are run. SCENARIO_TO_RUN=$1 @@ -206,4 +222,4 @@ prepareSelenium prepareDocker echo "Running tests" -docker exec $NEXTCLOUD_LOCAL_CONTAINER bash -c "cd nextcloud && tests/acceptance/run-local.sh allow-git-repository-modifications $SCENARIO_TO_RUN" +docker exec $NEXTCLOUD_LOCAL_CONTAINER bash -c "cd nextcloud && tests/acceptance/run-local.sh $TIMEOUT_MULTIPLIER_OPTION allow-git-repository-modifications $SCENARIO_TO_RUN" diff --git a/tests/lib/Accounts/AccountsManagerTest.php b/tests/lib/Accounts/AccountsManagerTest.php index e6c1552fdc0..6cefebdea86 100644 --- a/tests/lib/Accounts/AccountsManagerTest.php +++ b/tests/lib/Accounts/AccountsManagerTest.php @@ -24,6 +24,7 @@ namespace Test\Accounts; use OC\Accounts\AccountManager; +use OCP\BackgroundJob\IJobList; use OCP\IUser; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; @@ -43,6 +44,9 @@ class AccountsManagerTest extends TestCase { /** @var EventDispatcherInterface | \PHPUnit_Framework_MockObject_MockObject */ private $eventDispatcher; + /** @var IJobList | \PHPUnit_Framework_MockObject_MockObject */ + private $jobList; + /** @var string accounts table name */ private $table = 'accounts'; @@ -51,6 +55,7 @@ class AccountsManagerTest extends TestCase { $this->eventDispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface') ->disableOriginalConstructor()->getMock(); $this->connection = \OC::$server->getDatabaseConnection(); + $this->jobList = $this->getMockBuilder(IJobList::class)->getMock(); } public function tearDown() { @@ -67,7 +72,7 @@ class AccountsManagerTest extends TestCase { */ public function getInstance($mockedMethods = null) { return $this->getMockBuilder('OC\Accounts\AccountManager') - ->setConstructorArgs([$this->connection, $this->eventDispatcher]) + ->setConstructorArgs([$this->connection, $this->eventDispatcher, $this->jobList]) ->setMethods($mockedMethods) ->getMock(); @@ -75,15 +80,24 @@ class AccountsManagerTest extends TestCase { /** * @dataProvider dataTrueFalse + * + * @param array $newData + * @param array $oldData + * @param bool $insertNew + * @param bool $updateExisting */ - public function testUpdateUser($newData, $oldData, $insertNew, $updateExisitng) { - $accountManager = $this->getInstance(['getUser', 'insertNewUser', 'updateExistingUser']); + public function testUpdateUser($newData, $oldData, $insertNew, $updateExisting) { + $accountManager = $this->getInstance(['getUser', 'insertNewUser', 'updateExistingUser', 'updateVerifyStatus', 'checkEmailVerification']); /** @var IUser $user */ $user = $this->createMock(IUser::class); $accountManager->expects($this->once())->method('getUser')->with($user)->willReturn($oldData); - if ($updateExisitng) { + if ($updateExisting) { + $accountManager->expects($this->once())->method('checkEmailVerification') + ->with($oldData, $newData, $user)->willReturn($newData); + $accountManager->expects($this->once())->method('updateVerifyStatus') + ->with($oldData, $newData)->willReturn($newData); $accountManager->expects($this->once())->method('updateExistingUser') ->with($user, $newData); $accountManager->expects($this->never())->method('insertNewUser'); @@ -94,8 +108,10 @@ class AccountsManagerTest extends TestCase { $accountManager->expects($this->never())->method('updateExistingUser'); } - if (!$insertNew && !$updateExisitng) { + if (!$insertNew && !$updateExisting) { $accountManager->expects($this->never())->method('updateExistingUser'); + $accountManager->expects($this->never())->method('checkEmailVerification'); + $accountManager->expects($this->never())->method('updateVerifyStatus'); $accountManager->expects($this->never())->method('insertNewUser'); $this->eventDispatcher->expects($this->never())->method('dispatch'); } else { @@ -133,13 +149,22 @@ class AccountsManagerTest extends TestCase { * @param book $userAlreadyExists */ public function testGetUser($setUser, $setData, $askUser, $expectedData, $userAlreadyExists) { - $accountManager = $this->getInstance(['buildDefaultUserRecord', 'insertNewUser']); + $accountManager = $this->getInstance(['buildDefaultUserRecord', 'insertNewUser', 'addMissingDefaultValues']); if (!$userAlreadyExists) { $accountManager->expects($this->once())->method('buildDefaultUserRecord') ->with($askUser)->willReturn($expectedData); $accountManager->expects($this->once())->method('insertNewUser') ->with($askUser, $expectedData); } + + if(empty($expectedData)) { + $accountManager->expects($this->never())->method('addMissingDefaultValues'); + + } else { + $accountManager->expects($this->once())->method('addMissingDefaultValues')->with($expectedData) + ->willReturn($expectedData); + } + $this->addDummyValuesToTable($setUser, $setData); $this->assertEquals($expectedData, $accountManager->getUser($askUser) @@ -184,6 +209,25 @@ class AccountsManagerTest extends TestCase { $this->assertEquals($data, $dataFromDb); } + public function testAddMissingDefaultValues() { + + $accountManager = $this->getInstance(); + + $input = [ + 'key1' => ['value' => 'value1', 'verified' => '0'], + 'key2' => ['value' => 'value1'], + ]; + + $expected = [ + 'key1' => ['value' => 'value1', 'verified' => '0'], + 'key2' => ['value' => 'value1', 'verified' => '0'], + ]; + + $result = $this->invokePrivate($accountManager, 'addMissingDefaultValues', [$input]); + + $this->assertSame($expected, $result); + } + private function addDummyValuesToTable($uid, $data) { $query = $this->connection->getQueryBuilder(); diff --git a/tests/lib/App/AppStore/Fetcher/AppFetcherTest.php b/tests/lib/App/AppStore/Fetcher/AppFetcherTest.php index 4a5222fa915..2efecef4dc7 100644 --- a/tests/lib/App/AppStore/Fetcher/AppFetcherTest.php +++ b/tests/lib/App/AppStore/Fetcher/AppFetcherTest.php @@ -22,6 +22,7 @@ namespace Test\App\AppStore\Fetcher; use OC\App\AppStore\Fetcher\AppFetcher; +use OC\Files\AppData\Factory; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Files\IAppData; use OCP\Files\NotFoundException; @@ -31,6 +32,7 @@ use OCP\Http\Client\IClient; use OCP\Http\Client\IClientService; use OCP\Http\Client\IResponse; use OCP\IConfig; +use OCP\ILogger; use Test\TestCase; class AppFetcherTest extends TestCase { @@ -42,6 +44,8 @@ class AppFetcherTest extends TestCase { protected $timeFactory; /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */ protected $config; + /** @var ILogger|\PHPUnit_Framework_MockObject_MockObject */ + protected $logger; /** @var AppFetcher */ protected $fetcher; /** @var string */ @@ -53,10 +57,17 @@ EOD; public function setUp() { parent::setUp(); + /** @var Factory|\PHPUnit_Framework_MockObject_MockObject $factory */ + $factory = $this->createMock(Factory::class); $this->appData = $this->createMock(IAppData::class); + $factory->expects($this->once()) + ->method('get') + ->with('appstore') + ->willReturn($this->appData); $this->clientService = $this->createMock(IClientService::class); $this->timeFactory = $this->createMock(ITimeFactory::class); $this->config = $this->createMock(IConfig::class); + $this->logger = $this->createMock(ILogger::class); $this->config ->expects($this->at(0)) @@ -64,34 +75,25 @@ EOD; ->with('version') ->willReturn('11.0.0.2'); $this->fetcher = new AppFetcher( - $this->appData, + $factory, $this->clientService, $this->timeFactory, - $this->config + $this->config, + $this->logger ); } public function testGetWithFilter() { - $this->config - ->expects($this->at(0)) - ->method('getSystemValue') - ->with('appstoreenabled', true) - ->willReturn(true); - $this->config - ->expects($this->at(1)) - ->method('getSystemValue') - ->with('appstoreenabled', true) - ->willReturn(true); - $this->config - ->expects($this->at(2)) - ->method('getSystemValue') - ->with('version') - ->willReturn('11.0.0.2'); - $this->config - ->expects($this->at(3)) - ->method('getSystemValue') - ->with('version') - ->willReturn('11.0.0.2'); + $this->config->method('getSystemValue') + ->willReturnCallback(function($key, $default) { + if ($key === 'appstoreenabled') { + return true; + } else if ($key === 'version') { + return '11.0.0.2'; + } else { + return $default; + } + }); $file = $this->createMock(ISimpleFile::class); $folder = $this->createMock(ISimpleFolder::class); diff --git a/tests/lib/App/AppStore/Fetcher/CategoryFetcherTest.php b/tests/lib/App/AppStore/Fetcher/CategoryFetcherTest.php index 27f33bed997..a1ce718520f 100644 --- a/tests/lib/App/AppStore/Fetcher/CategoryFetcherTest.php +++ b/tests/lib/App/AppStore/Fetcher/CategoryFetcherTest.php @@ -23,17 +23,18 @@ namespace Test\App\AppStore\Fetcher; use OC\App\AppStore\Fetcher\CategoryFetcher; -class CategoryFetcherTest extends FetcherBase { +class CategoryFetcherTest extends FetcherBase { public function setUp() { parent::setUp(); $this->fileName = 'categories.json'; $this->endpoint = 'https://apps.nextcloud.com/api/v1/categories.json'; $this->fetcher = new CategoryFetcher( - $this->appData, + $this->appDataFactory, $this->clientService, $this->timeFactory, - $this->config + $this->config, + $this->logger ); } diff --git a/tests/lib/App/AppStore/Fetcher/FetcherBase.php b/tests/lib/App/AppStore/Fetcher/FetcherBase.php index 1cec5270000..2cfb34a0965 100644 --- a/tests/lib/App/AppStore/Fetcher/FetcherBase.php +++ b/tests/lib/App/AppStore/Fetcher/FetcherBase.php @@ -22,6 +22,7 @@ namespace Test\App\AppStore\Fetcher; use OC\App\AppStore\Fetcher\Fetcher; +use OC\Files\AppData\Factory; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Files\IAppData; use OCP\Files\NotFoundException; @@ -31,9 +32,12 @@ use OCP\Http\Client\IClient; use OCP\Http\Client\IClientService; use OCP\Http\Client\IResponse; use OCP\IConfig; +use OCP\ILogger; use Test\TestCase; abstract class FetcherBase extends TestCase { + /** @var Factory|\PHPUnit_Framework_MockObject_MockObject */ + protected $appDataFactory; /** @var IAppData|\PHPUnit_Framework_MockObject_MockObject */ protected $appData; /** @var IClientService|\PHPUnit_Framework_MockObject_MockObject */ @@ -42,6 +46,8 @@ abstract class FetcherBase extends TestCase { protected $timeFactory; /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */ protected $config; + /** @var ILogger|\PHPUnit_Framework_MockObject_MockObject */ + protected $logger; /** @var Fetcher */ protected $fetcher; /** @var string */ @@ -51,10 +57,16 @@ abstract class FetcherBase extends TestCase { public function setUp() { parent::setUp(); + $this->appDataFactory = $this->createMock(Factory::class); $this->appData = $this->createMock(IAppData::class); + $this->appDataFactory->expects($this->once()) + ->method('get') + ->with('appstore') + ->willReturn($this->appData); $this->clientService = $this->createMock(IClientService::class); $this->timeFactory = $this->createMock(ITimeFactory::class); $this->config = $this->createMock(IConfig::class); + $this->logger = $this->createMock(ILogger::class); } public function testGetWithAlreadyExistingFileAndUpToDateTimestampAndVersion() { @@ -181,23 +193,16 @@ abstract class FetcherBase extends TestCase { } public function testGetWithAlreadyExistingFileAndOutdatedTimestamp() { - $this->config - ->expects($this->at(0)) - ->method('getSystemValue') - ->with('appstoreenabled', true) - ->willReturn(true); - $this->config - ->expects($this->at(1)) - ->method('getSystemValue') - ->with('appstoreenabled', true) - ->willReturn(true); - $this->config - ->expects($this->at(2)) - ->method('getSystemValue') - ->with( - $this->equalTo('version'), - $this->anything() - )->willReturn('11.0.0.2'); + $this->config->method('getSystemValue') + ->willReturnCallback(function($key, $default) { + if ($key === 'appstoreenabled') { + return true; + } else if ($key === 'version') { + return '11.0.0.2'; + } else { + return $default; + } + }); $folder = $this->createMock(ISimpleFolder::class); $file = $this->createMock(ISimpleFile::class); @@ -421,16 +426,14 @@ abstract class FetcherBase extends TestCase { } public function testGetWithExceptionInClient() { - $this->config - ->expects($this->at(0)) - ->method('getSystemValue') - ->with('appstoreenabled', true) - ->willReturn(true); - $this->config - ->expects($this->at(1)) - ->method('getSystemValue') - ->with('appstoreenabled', true) - ->willReturn(true); + $this->config->method('getSystemValue') + ->willReturnCallback(function($key, $default) { + if ($key === 'appstoreenabled') { + return true; + } else { + return $default; + } + }); $folder = $this->createMock(ISimpleFolder::class); $file = $this->createMock(ISimpleFile::class); @@ -448,10 +451,6 @@ abstract class FetcherBase extends TestCase { ->expects($this->at(0)) ->method('getContent') ->willReturn('{"timestamp":1200,"data":{"MyApp":{"id":"MyApp"}}}'); - $this->timeFactory - ->expects($this->at(0)) - ->method('getTime') - ->willReturn(1501); $client = $this->createMock(IClient::class); $this->clientService ->expects($this->once()) @@ -467,23 +466,16 @@ abstract class FetcherBase extends TestCase { } public function testGetMatchingETag() { - $this->config - ->expects($this->at(0)) - ->method('getSystemValue') - ->with('appstoreenabled', true) - ->willReturn(true); - $this->config - ->expects($this->at(1)) - ->method('getSystemValue') - ->with('appstoreenabled', true) - ->willReturn(true); - $this->config - ->expects($this->at(2)) - ->method('getSystemValue') - ->with( - $this->equalTo('version'), - $this->anything() - )->willReturn('11.0.0.2'); + $this->config->method('getSystemValue') + ->willReturnCallback(function($key, $default) { + if ($key === 'appstoreenabled') { + return true; + } else if ($key === 'version') { + return '11.0.0.2'; + } else { + return $default; + } + }); $folder = $this->createMock(ISimpleFolder::class); $file = $this->createMock(ISimpleFile::class); @@ -522,6 +514,7 @@ abstract class FetcherBase extends TestCase { ->with( $this->equalTo($this->endpoint), $this->equalTo([ + 'timeout' => 10, 'headers' => [ 'If-None-Match' => '"myETag"' ] @@ -554,23 +547,16 @@ abstract class FetcherBase extends TestCase { } public function testGetNoMatchingETag() { - $this->config - ->expects($this->at(0)) - ->method('getSystemValue') - ->with('appstoreenabled', true) - ->willReturn(true); - $this->config - ->expects($this->at(1)) - ->method('getSystemValue') - ->with('appstoreenabled', true) - ->willReturn(true); - $this->config - ->expects($this->at(2)) - ->method('getSystemValue') - ->with( - $this->equalTo('version'), - $this->anything() - )->willReturn('11.0.0.2'); + $this->config->method('getSystemValue') + ->willReturnCallback(function($key, $default) { + if ($key === 'appstoreenabled') { + return true; + } else if ($key === 'version') { + return '11.0.0.2'; + } else { + return $default; + } + }); $folder = $this->createMock(ISimpleFolder::class); $file = $this->createMock(ISimpleFile::class); @@ -600,6 +586,7 @@ abstract class FetcherBase extends TestCase { ->with( $this->equalTo($this->endpoint), $this->equalTo([ + 'timeout' => 10, 'headers' => [ 'If-None-Match' => '"myETag"', ] @@ -644,4 +631,84 @@ abstract class FetcherBase extends TestCase { ]; $this->assertSame($expected, $this->fetcher->get()); } + + + public function testFetchAfterUpgradeNoETag() { + $this->config->method('getSystemValue') + ->willReturnCallback(function($key, $default) { + if ($key === 'appstoreenabled') { + return true; + } else if ($key === 'version') { + return '11.0.0.3'; + } else { + return $default; + } + }); + + $folder = $this->createMock(ISimpleFolder::class); + $file = $this->createMock(ISimpleFile::class); + $this->appData + ->expects($this->once()) + ->method('getFolder') + ->with('/') + ->willReturn($folder); + $folder + ->expects($this->at(0)) + ->method('getFile') + ->with($this->fileName) + ->willReturn($file); + $file + ->expects($this->at(0)) + ->method('getContent') + ->willReturn('{"data":[{"id":"MyOldApp","abc":"def"}],"timestamp":1200,"ncversion":"11.0.0.2","ETag":"\"myETag\""}'); + $client = $this->createMock(IClient::class); + $this->clientService + ->expects($this->once()) + ->method('newClient') + ->willReturn($client); + $response = $this->createMock(IResponse::class); + $client + ->expects($this->once()) + ->method('get') + ->with( + $this->equalTo($this->endpoint), + $this->equalTo([ + 'timeout' => 10, + ]) + ) + ->willReturn($response); + $response->method('getStatusCode') + ->willReturn(200); + $response + ->expects($this->once()) + ->method('getBody') + ->willReturn('[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}]'); + $response->method('getHeader') + ->with($this->equalTo('ETag')) + ->willReturn('"newETag"'); + $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1501,"ncversion":"11.0.0.3","ETag":"\"newETag\""}'; + $file + ->expects($this->at(1)) + ->method('putContent') + ->with($fileData); + $file + ->expects($this->at(2)) + ->method('getContent') + ->willReturn($fileData); + $this->timeFactory + ->expects($this->once()) + ->method('getTime') + ->willReturn(1501); + + $expected = [ + [ + 'id' => 'MyNewApp', + 'foo' => 'foo', + ], + [ + 'id' => 'bar', + ], + ]; + $this->assertSame($expected, $this->fetcher->get()); + } } diff --git a/tests/lib/Cache/TestCache.php b/tests/lib/Cache/TestCache.php index 75ff65207ee..2642c014a78 100644 --- a/tests/lib/Cache/TestCache.php +++ b/tests/lib/Cache/TestCache.php @@ -53,21 +53,48 @@ abstract class TestCache extends \Test\TestCase { function testClear() { $value='ipsum lorum'; - $this->instance->set('1_value1', $value); - $this->instance->set('1_value2', $value); - $this->instance->set('2_value1', $value); - $this->instance->set('3_value1', $value); + $this->instance->set('1_value1', $value . '1'); + $this->instance->set('1_value2', $value . '2'); + $this->instance->set('2_value1', $value . '3'); + $this->instance->set('3_value1', $value . '4'); + $this->assertEquals([ + '1_value1' => 'ipsum lorum1', + '1_value2' => 'ipsum lorum2', + '2_value1' => 'ipsum lorum3', + '3_value1' => 'ipsum lorum4', + ], [ + '1_value1' => $this->instance->get('1_value1'), + '1_value2' => $this->instance->get('1_value2'), + '2_value1' => $this->instance->get('2_value1'), + '3_value1' => $this->instance->get('3_value1'), + ]); $this->assertTrue($this->instance->clear('1_')); - $this->assertFalse($this->instance->hasKey('1_value1')); - $this->assertFalse($this->instance->hasKey('1_value2')); - $this->assertTrue($this->instance->hasKey('2_value1')); - $this->assertTrue($this->instance->hasKey('3_value1')); + + $this->assertEquals([ + '1_value1' => null, + '1_value2' => null, + '2_value1' => 'ipsum lorum3', + '3_value1' => 'ipsum lorum4', + ], [ + '1_value1' => $this->instance->get('1_value1'), + '1_value2' => $this->instance->get('1_value2'), + '2_value1' => $this->instance->get('2_value1'), + '3_value1' => $this->instance->get('3_value1'), + ]); $this->assertTrue($this->instance->clear()); - $this->assertFalse($this->instance->hasKey('1_value1')); - $this->assertFalse($this->instance->hasKey('1_value2')); - $this->assertFalse($this->instance->hasKey('2_value1')); - $this->assertFalse($this->instance->hasKey('3_value1')); + + $this->assertEquals([ + '1_value1' => null, + '1_value2' => null, + '2_value1' => null, + '3_value1' => null, + ], [ + '1_value1' => $this->instance->get('1_value1'), + '1_value2' => $this->instance->get('1_value2'), + '2_value1' => $this->instance->get('2_value1'), + '3_value1' => $this->instance->get('3_value1'), + ]); } } diff --git a/tests/lib/Contacts/ContactsMenu/Providers/EMailproviderTest.php b/tests/lib/Contacts/ContactsMenu/Providers/EMailproviderTest.php index 2d82fa5d68e..353c8d6f58f 100644 --- a/tests/lib/Contacts/ContactsMenu/Providers/EMailproviderTest.php +++ b/tests/lib/Contacts/ContactsMenu/Providers/EMailproviderTest.php @@ -79,4 +79,28 @@ class EMailproviderTest extends TestCase { $this->provider->process($entry); } + public function testProcessEmptyAddress() { + $entry = $this->createMock(IEntry::class); + $action = $this->createMock(ILinkAction::class); + $iconUrl = 'https://example.com/img/actions/icon.svg'; + $this->urlGenerator->expects($this->once()) + ->method('imagePath') + ->willReturn('img/actions/icon.svg'); + $this->urlGenerator->expects($this->once()) + ->method('getAbsoluteURL') + ->with('img/actions/icon.svg') + ->willReturn($iconUrl); + $entry->expects($this->once()) + ->method('getEMailAddresses') + ->willReturn([ + '', + ]); + $this->actionFactory->expects($this->never()) + ->method('newEMailAction'); + $entry->expects($this->never()) + ->method('addAction'); + + $this->provider->process($entry); + } + } diff --git a/tests/lib/HelperStorageTest.php b/tests/lib/HelperStorageTest.php index e27d5eca21d..d42c43c5ab8 100644 --- a/tests/lib/HelperStorageTest.php +++ b/tests/lib/HelperStorageTest.php @@ -158,9 +158,9 @@ class HelperStorageTest extends \Test\TestCase { $config->setSystemValue('quota_include_external_storage', 'true'); $storageInfo = \OC_Helper::getStorageInfo(''); - $this->assertEquals(12, $storageInfo['free']); - $this->assertEquals(5, $storageInfo['used']); - $this->assertEquals(17, $storageInfo['total']); + $this->assertEquals(12, $storageInfo['free'], '12 bytes free in home storage'); + $this->assertEquals(22, $storageInfo['used'], '5 bytes of home storage and 17 bytes of the temporary storage are used'); + $this->assertEquals(34, $storageInfo['total'], '5 bytes used and 12 bytes free in home storage as well as 17 bytes used in temporary storage'); $config->setSystemValue('quota_include_external_storage', $oldConfig); } diff --git a/tests/lib/Memcache/RedisTest.php b/tests/lib/Memcache/RedisTest.php index e707f30fb5b..6a0a82f6aa7 100644 --- a/tests/lib/Memcache/RedisTest.php +++ b/tests/lib/Memcache/RedisTest.php @@ -24,6 +24,7 @@ class RedisTest extends Cache { }, E_WARNING ); + $instance = null; try { $instance = new \OC\Memcache\Redis(self::getUniqueID()); } catch (\RuntimeException $e) { @@ -34,6 +35,10 @@ class RedisTest extends Cache { self::markTestSkipped($errorOccurred); } + if ($instance === null) { + throw new \Exception('redis server is not reachable'); + } + if ($instance->set(self::getUniqueID(), self::getUniqueID()) === false) { self::markTestSkipped('redis server seems to be down.'); } diff --git a/tests/lib/Preview/GeneratorTest.php b/tests/lib/Preview/GeneratorTest.php index 50d46ae932d..f1383b0691b 100644 --- a/tests/lib/Preview/GeneratorTest.php +++ b/tests/lib/Preview/GeneratorTest.php @@ -169,6 +169,7 @@ class GeneratorTest extends \Test\TestCase { $image = $this->createMock(IImage::class); $image->method('width')->willReturn(2048); $image->method('height')->willReturn(2048); + $image->method('valid')->willReturn(true); $this->helper->method('getThumbnail') ->will($this->returnCallback(function ($provider, $file, $x, $y) use ($invalidProvider, $validProvider, $image) { @@ -217,6 +218,7 @@ class GeneratorTest extends \Test\TestCase { ->with(128); $image->method('data') ->willReturn('my resized data'); + $image->method('valid')->willReturn(true); $previewFile->expects($this->once()) ->method('putContent') @@ -379,6 +381,7 @@ class GeneratorTest extends \Test\TestCase { ->willReturn($image); $image->method('height')->willReturn($maxY); $image->method('width')->willReturn($maxX); + $image->method('valid')->willReturn(true); $preview = $this->createMock(ISimpleFile::class); $previewFolder->method('newFile') diff --git a/tests/lib/Security/Bruteforce/ThrottlerTest.php b/tests/lib/Security/Bruteforce/ThrottlerTest.php index 9679d0c1759..dac12a00dcd 100644 --- a/tests/lib/Security/Bruteforce/ThrottlerTest.php +++ b/tests/lib/Security/Bruteforce/ThrottlerTest.php @@ -54,19 +54,19 @@ class ThrottlerTest extends TestCase { $this->logger, $this->config ); - return parent::setUp(); + parent::setUp(); } public function testCutoff() { // precisely 31 second shy of 12 hours - $cutoff = $this->invokePrivate($this->throttler, 'getCutoff', [43169]); + $cutoff = self::invokePrivate($this->throttler, 'getCutoff', [43169]); $this->assertSame(0, $cutoff->y); $this->assertSame(0, $cutoff->m); $this->assertSame(0, $cutoff->d); $this->assertSame(11, $cutoff->h); $this->assertSame(59, $cutoff->i); $this->assertSame(29, $cutoff->s); - $cutoff = $this->invokePrivate($this->throttler, 'getCutoff', [86401]); + $cutoff = self::invokePrivate($this->throttler, 'getCutoff', [86401]); $this->assertSame(0, $cutoff->y); $this->assertSame(0, $cutoff->m); $this->assertSame(1, $cutoff->d); @@ -136,16 +136,23 @@ class ThrottlerTest extends TestCase { } /** - * @dataProvider dataIsIPWhitelisted - * * @param string $ip * @param string[] $whitelists * @param bool $isWhiteListed + * @param bool $enabled */ - public function testIsIPWhitelisted($ip, $whitelists, $isWhiteListed) { + private function isIpWhiteListedHelper($ip, + $whitelists, + $isWhiteListed, + $enabled) { $this->config->method('getAppKeys') ->with($this->equalTo('bruteForce')) ->willReturn(array_keys($whitelists)); + $this->config + ->expects($this->once()) + ->method('getSystemValue') + ->with('auth.bruteforce.protection.enabled', true) + ->willReturn($enabled); $this->config->method('getAppValue') ->will($this->returnCallback(function($app, $key, $default) use ($whitelists) { @@ -159,8 +166,44 @@ class ThrottlerTest extends TestCase { })); $this->assertSame( + ($enabled === false) ? true : $isWhiteListed, + self::invokePrivate($this->throttler, 'isIPWhitelisted', [$ip]) + ); + } + + /** + * @dataProvider dataIsIPWhitelisted + * + * @param string $ip + * @param string[] $whitelists + * @param bool $isWhiteListed + */ + public function testIsIpWhiteListedWithEnabledProtection($ip, + $whitelists, + $isWhiteListed) { + $this->isIpWhiteListedHelper( + $ip, + $whitelists, + $isWhiteListed, + true + ); + } + + /** + * @dataProvider dataIsIPWhitelisted + * + * @param string $ip + * @param string[] $whitelists + * @param bool $isWhiteListed + */ + public function testIsIpWhiteListedWithDisabledProtection($ip, + $whitelists, + $isWhiteListed) { + $this->isIpWhiteListedHelper( + $ip, + $whitelists, $isWhiteListed, - $this->invokePrivate($this->throttler, 'isIPWhitelisted', [$ip]) + false ); } } diff --git a/tests/lib/Security/IdentityProof/ManagerTest.php b/tests/lib/Security/IdentityProof/ManagerTest.php index 2925dea5ec5..b46ca30705a 100644 --- a/tests/lib/Security/IdentityProof/ManagerTest.php +++ b/tests/lib/Security/IdentityProof/ManagerTest.php @@ -21,6 +21,7 @@ namespace Test\Security\IdentityProof; +use OC\Files\AppData\Factory; use OC\Security\IdentityProof\Key; use OC\Security\IdentityProof\Manager; use OCP\Files\IAppData; @@ -31,6 +32,8 @@ use OCP\Security\ICrypto; use Test\TestCase; class ManagerTest extends TestCase { + /** @var Factory|\PHPUnit_Framework_MockObject_MockObject */ + private $factory; /** @var IAppData|\PHPUnit_Framework_MockObject_MockObject */ private $appData; /** @var ICrypto|\PHPUnit_Framework_MockObject_MockObject */ @@ -40,11 +43,19 @@ class ManagerTest extends TestCase { public function setUp() { parent::setUp(); + + /** @var Factory|\PHPUnit_Framework_MockObject_MockObject $factory */ + $this->factory = $this->createMock(Factory::class); $this->appData = $this->createMock(IAppData::class); + $this->factory->expects($this->any()) + ->method('get') + ->with('identityproof') + ->willReturn($this->appData); + $this->crypto = $this->createMock(ICrypto::class); $this->manager = $this->getMockBuilder(Manager::class) ->setConstructorArgs([ - $this->appData, + $this->factory, $this->crypto ]) ->setMethods(['generateKeyPair']) @@ -151,12 +162,12 @@ class ManagerTest extends TestCase { public function testGenerateKeyPair() { $manager = new Manager( - $this->appData, + $this->factory, $this->crypto ); $data = 'MyTestData'; - list($resultPublicKey, $resultPrivateKey) = $this->invokePrivate($manager, 'generateKeyPair'); + list($resultPublicKey, $resultPrivateKey) = self::invokePrivate($manager, 'generateKeyPair'); openssl_sign($data, $signature, $resultPrivateKey); $details = openssl_pkey_get_details(openssl_pkey_get_public($resultPublicKey)); diff --git a/tests/lib/Settings/ManagerTest.php b/tests/lib/Settings/ManagerTest.php index 497a0df9f4e..07f7e71feca 100644 --- a/tests/lib/Settings/ManagerTest.php +++ b/tests/lib/Settings/ManagerTest.php @@ -157,7 +157,7 @@ class ManagerTest extends TestCase { ]); $this->assertEquals([ - 0 => [new Section('server', 'Server settings', 0, '1')], + 0 => [new Section('server', 'Basic settings', 0, '1')], 5 => [new Section('sharing', 'Sharing', 0, '2')], 10 => [new Section('security', 'Security', 0, '3')], 45 => [new Section('encryption', 'Encryption', 0, '3')], @@ -189,7 +189,7 @@ class ManagerTest extends TestCase { ]); $this->assertEquals([ - 0 => [new Section('server', 'Server settings', 0, '1')], + 0 => [new Section('server', 'Basic settings', 0, '1')], 5 => [new Section('sharing', 'Sharing', 0, '2')], 10 => [new Section('security', 'Security', 0, '3')], 45 => [new Section('encryption', 'Encryption', 0, '3')], diff --git a/tests/lib/User/ManagerTest.php b/tests/lib/User/ManagerTest.php index 671b2ac57c1..cf725aae671 100644 --- a/tests/lib/User/ManagerTest.php +++ b/tests/lib/User/ManagerTest.php @@ -291,10 +291,18 @@ class ManagerTest extends TestCase { * @dataProvider dataCreateUserInvalid */ public function testCreateUserInvalid($uid, $password, $exception) { + /** @var \Test\Util\User\Dummy|\PHPUnit_Framework_MockObject_MockObject $backend */ + $backend = $this->createMock(\Test\Util\User\Dummy::class); + $backend->expects($this->once()) + ->method('implementsActions') + ->with(\OC\User\Backend::CREATE_USER) + ->willReturn(true); - $this->setExpectedException(\Exception::class, $exception); $manager = new \OC\User\Manager($this->config); + $manager->registerBackend($backend); + + $this->setExpectedException(\InvalidArgumentException::class, $exception); $manager->createUser($uid, $password); } @@ -362,10 +370,8 @@ class ManagerTest extends TestCase { $backend->expects($this->never()) ->method('createUser'); - $backend->expects($this->once()) - ->method('userExists') - ->with($this->equalTo('foo')) - ->will($this->returnValue(false)); + $backend->expects($this->never()) + ->method('userExists'); $manager = new \OC\User\Manager($this->config); $manager->registerBackend($backend); @@ -504,6 +510,31 @@ class ManagerTest extends TestCase { $this->assertEquals(7 + 16, $users); } + public function testCountUsersOnlyDisabled() { + $manager = \OC::$server->getUserManager(); + // count other users in the db before adding our own + $countBefore = $manager->countDisabledUsers(); + + //Add test users + $user1 = $manager->createUser('testdisabledcount1', 'testdisabledcount1'); + + $user2 = $manager->createUser('testdisabledcount2', 'testdisabledcount2'); + $user2->setEnabled(false); + + $user3 = $manager->createUser('testdisabledcount3', 'testdisabledcount3'); + + $user4 = $manager->createUser('testdisabledcount4', 'testdisabledcount4'); + $user4->setEnabled(false); + + $this->assertEquals($countBefore + 2, $manager->countDisabledUsers()); + + //cleanup + $user1->delete(); + $user2->delete(); + $user3->delete(); + $user4->delete(); + } + public function testCountUsersOnlySeen() { $manager = \OC::$server->getUserManager(); // count other users in the db before adding our own diff --git a/tests/redis.config.php b/tests/redis.config.php new file mode 100644 index 00000000000..2ff46ec6728 --- /dev/null +++ b/tests/redis.config.php @@ -0,0 +1,12 @@ +<?php + +$CONFIG = [ + 'memcache.local' => '\\OC\\Memcache\\Redis', + 'memcache.distributed' => '\\OC\\Memcache\\Redis', + 'memcache.locking' => '\\OC\\Memcache\\Redis', + 'redis' => [ + 'host' => 'localhost', + 'port' => 6379, + 'timeout' => 0, + ], +]; |