From b995437483db704f52d0cb226de71ade2bb6cdb7 Mon Sep 17 00:00:00 2001 From: Daniel Seifert Date: Thu, 17 Nov 2022 00:27:43 +0100 Subject: [PATCH] add frontend controller tests --- .../Controller/d3_account_webauthn.php | 72 ++- .../Controller/d3webauthnlogin.php | 66 ++- .../Controller/d3_account_webauthnTest.php | 560 ++++++++++++++++++ .../Controller/d3webauthnloginTest.php | 453 ++++++++++++++ 4 files changed, 1120 insertions(+), 31 deletions(-) create mode 100644 src/tests/unit/Application/Controller/d3_account_webauthnTest.php create mode 100644 src/tests/unit/Application/Controller/d3webauthnloginTest.php diff --git a/src/Application/Controller/d3_account_webauthn.php b/src/Application/Controller/d3_account_webauthn.php index 840b0f7..ba7fc70 100755 --- a/src/Application/Controller/d3_account_webauthn.php +++ b/src/Application/Controller/d3_account_webauthn.php @@ -28,8 +28,10 @@ use Doctrine\DBAL\Exception as DoctrineException; use OxidEsales\Eshop\Application\Controller\AccountController; use OxidEsales\Eshop\Core\Registry; use OxidEsales\Eshop\Core\SeoEncoder; +use OxidEsales\Eshop\Core\UtilsView; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; +use Psr\Log\LoggerInterface; class d3_account_webauthn extends AccountController { @@ -44,14 +46,8 @@ class d3_account_webauthn extends AccountController { $sRet = parent::render(); - // is logged in ? - $oUser = $this->getUser(); - if (!$oUser) { - return $this->_sThisTemplate = $this->_sThisLoginTemplate; - } - $this->addTplParam('user', $this->getUser()); - $this->addTplParam('readonly', (bool) !(oxNew(Webauthn::class)->isAvailable())); + $this->addTplParam('readonly', (bool) !($this->getWebauthnObject()->isAvailable())); return $sRet; } @@ -66,7 +62,7 @@ class d3_account_webauthn extends AccountController public function getCredentialList(): PublicKeyCredentialList { $oUser = $this->getUser(); - $credentialList = oxNew(PublicKeyCredentialList::class); + $credentialList = $this->getPublicKeyCredentialListObject(); return $credentialList->getAllFromUser($oUser); } @@ -83,9 +79,9 @@ class d3_account_webauthn extends AccountController $this->setAuthnRegister(); $this->setPageType('requestnew'); } catch (WebauthnException $e) { - Registry::getLogger()->error($e->getDetailedErrorMessage(), ['UserId: ' => $this->getUser()->getId()]); - Registry::getLogger()->debug($e->getTraceAsString()); - Registry::getUtilsView()->addErrorToDisplay($e); + $this->getLogger()->error($e->getDetailedErrorMessage(), ['UserId: ' => $this->getUser()->getId()]); + $this->getLogger()->debug($e->getTraceAsString()); + $this->getUtilsViewObject()->addErrorToDisplay($e); } } @@ -108,8 +104,7 @@ class d3_account_webauthn extends AccountController */ public function setAuthnRegister(): void { - $authn = oxNew(Webauthn::class); - $publicKeyCredentialCreationOptions = $authn->getCreationOptions($this->getUser()); + $publicKeyCredentialCreationOptions = $this->getWebauthnObject()->getCreationOptions($this->getUser()); $this->addTplParam('webauthn_publickey_create', $publicKeyCredentialCreationOptions); $this->addTplParam('isAdmin', isAdmin()); @@ -136,11 +131,11 @@ class d3_account_webauthn extends AccountController $credential = Registry::getRequest()->getRequestEscapedParameter('credential'); if (strlen((string) $credential)) { /** @var Webauthn $webauthn */ - $webauthn = oxNew( Webauthn::class ); + $webauthn = $this->getWebauthnObject(); $webauthn->saveAuthn($credential, Registry::getRequest()->getRequestEscapedParameter('keyname')); } } catch (WebauthnException $e) { - Registry::getUtilsView()->addErrorToDisplay( $e ); + $this->getUtilsViewObject()->addErrorToDisplay( $e ); } } @@ -149,10 +144,11 @@ class d3_account_webauthn extends AccountController */ public function deleteKey(): void { - if (Registry::getRequest()->getRequestEscapedParameter('deleteoxid')) { + $deleteId = Registry::getRequest()->getRequestEscapedParameter('deleteoxid'); + if ($deleteId) { /** @var PublicKeyCredential $credential */ - $credential = oxNew(PublicKeyCredential::class); - $credential->delete(Registry::getRequest()->getRequestEscapedParameter('deleteoxid')); + $credential = $this->getPublicKeyCredentialObject(); + $credential->delete($deleteId); } } @@ -177,4 +173,44 @@ class d3_account_webauthn extends AccountController return $aPaths; } + + /** + * @return Webauthn + */ + public function getWebauthnObject(): Webauthn + { + return oxNew(Webauthn::class); + } + + /** + * @return PublicKeyCredential + */ + public function getPublicKeyCredentialObject(): PublicKeyCredential + { + return oxNew(PublicKeyCredential::class); + } + + /** + * @return PublicKeyCredentialList + */ + public function getPublicKeyCredentialListObject(): PublicKeyCredentialList + { + return oxNew(PublicKeyCredentialList::class); + } + + /** + * @return LoggerInterface + */ + public function getLogger(): LoggerInterface + { + return Registry::getLogger(); + } + + /** + * @return UtilsView + */ + public function getUtilsViewObject(): UtilsView + { + return Registry::getUtilsView(); + } } \ No newline at end of file diff --git a/src/Application/Controller/d3webauthnlogin.php b/src/Application/Controller/d3webauthnlogin.php index 8c4e397..ef7c804 100755 --- a/src/Application/Controller/d3webauthnlogin.php +++ b/src/Application/Controller/d3webauthnlogin.php @@ -15,6 +15,7 @@ declare(strict_types=1); namespace D3\Webauthn\Application\Controller; +use D3\TestingTools\Production\IsMockable; use D3\Webauthn\Application\Model\Webauthn; use D3\Webauthn\Application\Model\WebauthnConf; use D3\Webauthn\Application\Model\Exceptions\WebauthnException; @@ -22,24 +23,30 @@ use Doctrine\DBAL\Driver\Exception as DoctrineDriverException; use Doctrine\DBAL\Exception as DoctrineException; use OxidEsales\Eshop\Application\Controller\FrontendController; use OxidEsales\Eshop\Core\Registry; +use OxidEsales\Eshop\Core\Routing\ControllerClassNameResolver; +use OxidEsales\Eshop\Core\Session; use OxidEsales\Eshop\Core\Utils; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; +use Psr\Log\LoggerInterface; class d3webauthnlogin extends FrontendController { + use IsMockable; + protected $_sThisTemplate = 'd3webauthnlogin.tpl'; /** * @return array */ public function getNavigationParams(): array - { - $navparams = Registry::getSession()->getVariable( + {; + $navparams = $this->d3GetSession()->getVariable( WebauthnConf::WEBAUTHN_SESSION_NAVPARAMS ); return array_merge( + $this->d3CallMockableParent('getNavigationParams'), $navparams, ['cl' => $navparams['actcontrol']] ); @@ -54,8 +61,8 @@ class d3webauthnlogin extends FrontendController */ public function render(): string { - if (Registry::getSession()->hasVariable(WebauthnConf::WEBAUTHN_SESSION_AUTH) || - !Registry::getSession()->hasVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER) + if ($this->d3GetSession()->hasVariable(WebauthnConf::WEBAUTHN_SESSION_AUTH) || + !$this->d3GetSession()->hasVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER) ) { $this->getUtils()->redirect('index.php?cl=start'); if (!defined('OXID_PHP_UNIT')) { @@ -67,7 +74,7 @@ class d3webauthnlogin extends FrontendController $this->generateCredentialRequest(); - $this->addTplParam('navFormParams', Registry::getSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_NAVFORMPARAMS)); + $this->addTplParam('navFormParams', $this->d3GetSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_NAVFORMPARAMS)); return parent::render(); } @@ -81,18 +88,18 @@ class d3webauthnlogin extends FrontendController */ public function generateCredentialRequest(): void { - $userId = Registry::getSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER); + $userId = $this->d3GetSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER); try { /** @var Webauthn $webauthn */ - $webauthn = oxNew(Webauthn::class); + $webauthn = $this->d3GetWebauthnObject(); $publicKeyCredentialRequestOptions = $webauthn->getRequestOptions($userId); $this->addTplParam('webauthn_publickey_login', $publicKeyCredentialRequestOptions); $this->addTplParam('isAdmin', isAdmin()); } catch (WebauthnException $e) { - Registry::getSession()->setVariable(WebauthnConf::GLOBAL_SWITCH, true); - Registry::getLogger()->error($e->getDetailedErrorMessage(), ['UserId' => $userId]); - Registry::getLogger()->debug($e->getTraceAsString()); + $this->d3GetSession()->setVariable(WebauthnConf::GLOBAL_SWITCH, true); + $this->d3GetLogger()->error($e->getDetailedErrorMessage(), ['UserId' => $userId]); + $this->d3GetLogger()->debug($e->getTraceAsString()); Registry::getUtilsView()->addErrorToDisplay($e); $this->getUtils()->redirect('index.php?cl=start'); } @@ -111,7 +118,7 @@ class d3webauthnlogin extends FrontendController */ public function getPreviousClass(): ?string { - return Registry::getSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTCLASS); + return $this->d3GetSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTCLASS); } /** @@ -119,12 +126,13 @@ class d3webauthnlogin extends FrontendController */ public function previousClassIsOrderStep(): bool { - $sClassKey = Registry::getSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTCLASS); - $resolvedClass = Registry::getControllerClassNameResolver()->getClassNameById($sClassKey); + $sClassKey = $this->d3GetSession()->getVariable(WebauthnConf::WEBAUTHN_SESSION_CURRENTCLASS); + $resolvedClass = $this->getControllerClassNameResolver()->getClassNameById($sClassKey); $resolvedClass = $resolvedClass ?: 'start'; /** @var FrontendController $oController */ $oController = oxNew($resolvedClass); + return $oController->getIsOrderStep(); } @@ -151,4 +159,36 @@ class d3webauthnlogin extends FrontendController return $aPaths; } + + /** + * @return Session + */ + public function d3GetSession(): Session + { + return Registry::getSession(); + } + + /** + * @return Webauthn + */ + public function d3GetWebauthnObject(): Webauthn + { + return oxNew(Webauthn::class); + } + + /** + * @return LoggerInterface + */ + public function d3GetLogger(): LoggerInterface + { + return Registry::getLogger(); + } + + /** + * @return ControllerClassNameResolver + */ + public function getControllerClassNameResolver(): ControllerClassNameResolver + { + return Registry::getControllerClassNameResolver(); + } } \ No newline at end of file diff --git a/src/tests/unit/Application/Controller/d3_account_webauthnTest.php b/src/tests/unit/Application/Controller/d3_account_webauthnTest.php new file mode 100644 index 0000000..500cb9d --- /dev/null +++ b/src/tests/unit/Application/Controller/d3_account_webauthnTest.php @@ -0,0 +1,560 @@ + + * @link https://www.oxidmodule.com + */ + +namespace D3\Totp\tests\unit\Application\Controller; + +use D3\TestingTools\Development\CanAccessRestricted; +use D3\Webauthn\Application\Controller\d3_account_webauthn; +use D3\Webauthn\Application\Model\Credential\PublicKeyCredential; +use D3\Webauthn\Application\Model\Credential\PublicKeyCredentialList; +use D3\Webauthn\Application\Model\Exceptions\WebauthnException; +use D3\Webauthn\Application\Model\Webauthn; +use OxidEsales\Eshop\Application\Model\User; +use OxidEsales\Eshop\Core\UtilsView; +use OxidEsales\TestingLibrary\UnitTestCase; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use ReflectionException; + +class d3_account_webauthnTest extends UnitTestCase +{ + use CanAccessRestricted; + + /** @var d3_account_webauthn */ + protected $_oController; + + /** + * setup basic requirements + */ + public function setUp(): void + { + parent::setUp(); + + $this->_oController = oxNew(d3_account_webauthn::class); + } + + public function tearDown(): void + { + parent::tearDown(); + + unset($this->_oController); + } + + /** + * @test + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3_account_webauthn::render + * @covers \D3\Webauthn\Application\Controller\d3_account_webauthn::getViewDataElement + */ + public function renderReturnsDefaultTemplate() + { + $oUser = oxNew(User::class); + $oUser->setId('foo'); + $oUser->assign( + [ + 'oxpassword' => 'foo', + ] + ); + + /** @var Webauthn|MockObject $webAuthnMock */ + $webAuthnMock = $this->getMockBuilder(Webauthn::class) + ->onlyMethods(['isAvailable']) + ->getMock(); + $webAuthnMock->expects($this->atLeastOnce())->method('isAvailable')->willReturn(true); + + /** @var d3_account_webauthn|MockObject $oControllerMock */ + $oControllerMock = $this->getMockBuilder(d3_account_webauthn::class) + ->onlyMethods(['getUser', 'getWebauthnObject']) + ->getMock(); + $oControllerMock->method('getUser')->willReturn($oUser); + $oControllerMock->method('getWebauthnObject')->willReturn($webAuthnMock); + + $this->_oController = $oControllerMock; + + $sTpl = $this->callMethod($this->_oController, 'render'); + $tplUser = $this->callMethod($this->_oController, 'getViewDataElement', ['user']); + + $this->assertSame('d3_account_webauthn.tpl', $sTpl); + $this->assertSame($tplUser, $oUser); + } + + /** + * @test + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3_account_webauthn::render + * @covers \D3\Webauthn\Application\Controller\d3_account_webauthn::getViewDataElement + */ + public function renderReturnsLoginTemplateIfNotLoggedIn() + { + $oUser = null; + + /** @var Webauthn|MockObject $webAuthnMock */ + $webAuthnMock = $this->getMockBuilder(Webauthn::class) + ->onlyMethods(['isAvailable']) + ->getMock(); + $webAuthnMock->expects($this->atLeastOnce())->method('isAvailable')->willReturn(true); + + /** @var d3_account_webauthn|MockObject $oControllerMock */ + $oControllerMock = $this->getMockBuilder(d3_account_webauthn::class) + ->onlyMethods(['getUser', 'getWebauthnObject']) + ->getMock(); + $oControllerMock->method('getUser')->willReturn($oUser); + $oControllerMock->method('getWebauthnObject')->willReturn($webAuthnMock); + + $this->_oController = $oControllerMock; + + $sTpl = $this->callMethod($this->_oController, 'render'); + $tplUser = $this->callMethod($this->_oController, 'getViewDataElement', ['user']); + + $this->assertNotSame('d3_account_webauthn.tpl', $sTpl); + $this->assertNull($tplUser); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3_account_webauthn::getCredentialList() + */ + public function canGetCredentialList() + { + $oUser = oxNew(User::class); + $oUser->setId('foo'); + $oUser->assign( + [ + 'oxpassword' => 'foo', + ] + ); + + /** @var PublicKeyCredentialList|MockObject $publicKeyCredentialListMock */ + $publicKeyCredentialListMock = $this->getMockBuilder(PublicKeyCredentialList::class) + ->onlyMethods(['getAllFromUser']) + ->getMock(); + $publicKeyCredentialListMock->method('getAllFromUser')->with($oUser)->willReturnSelf(); + + /** @var d3_account_webauthn|MockObject $oControllerMock */ + $oControllerMock = $this->getMockBuilder(d3_account_webauthn::class) + ->onlyMethods(['getUser', 'getPublicKeyCredentialListObject']) + ->getMock(); + $oControllerMock->method('getUser')->willReturn($oUser); + $oControllerMock->method('getPublicKeyCredentialListObject')->willReturn($publicKeyCredentialListMock); + + $this->_oController = $oControllerMock; + + $this->assertSame( + $publicKeyCredentialListMock, + $this->callMethod( + $this->_oController, + 'getCredentialList' + ) + ); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3_account_webauthn::requestNewCredential() + */ + public function canRequestNewCredentialCanGetCreationOptions() + { + $oUser = oxNew(User::class); + $oUser->setId('foo'); + $oUser->assign( + [ + 'oxpassword' => 'foo', + ] + ); + + /** @var LoggerInterface|MockObject $loggerMock */ + $loggerMock = $this->getMockForAbstractClass(LoggerInterface::class, [], '', true, true, true, ['error', 'debug']); + $loggerMock->expects($this->never())->method('error')->willReturn(true); + $loggerMock->expects($this->never())->method('debug')->willReturn(true); + + /** @var d3_account_webauthn|MockObject $oControllerMock */ + $oControllerMock = $this->getMockBuilder(d3_account_webauthn::class) + ->onlyMethods(['setAuthnRegister', 'setPageType', 'getUser', 'getLogger']) + ->getMock(); + $oControllerMock->expects($this->atLeastOnce())->method('setAuthnRegister'); + $oControllerMock->expects($this->atLeastOnce())->method('setPageType'); + $oControllerMock->method('getUser')->willReturn($oUser); + $oControllerMock->method('getLogger')->willReturn($loggerMock); + + $this->_oController = $oControllerMock; + + $this->callMethod( + $this->_oController, + 'requestNewCredential' + ); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3_account_webauthn::requestNewCredential() + */ + public function canRequestNewCredentialCantGetCreationOptions() + { + $oUser = oxNew(User::class); + $oUser->setId('foo'); + $oUser->assign( + [ + 'oxpassword' => 'foo', + ] + ); + + /** @var LoggerInterface|MockObject $loggerMock */ + $loggerMock = $this->getMockForAbstractClass(LoggerInterface::class, [], '', true, true, true, ['error', 'debug']); + $loggerMock->expects($this->atLeastOnce())->method('error')->willReturn(true); + $loggerMock->expects($this->atLeastOnce())->method('debug')->willReturn(true); + + /** @var d3_account_webauthn|MockObject $oControllerMock */ + $oControllerMock = $this->getMockBuilder(d3_account_webauthn::class) + ->onlyMethods(['setAuthnRegister', 'setPageType', 'getUser', 'getLogger']) + ->getMock(); + $oControllerMock->expects($this->atLeastOnce())->method('setAuthnRegister') + ->willThrowException(oxNew(WebauthnException::class)); + $oControllerMock->expects($this->never())->method('setPageType'); + $oControllerMock->method('getUser')->willReturn($oUser); + $oControllerMock->method('getLogger')->willReturn($loggerMock); + + $this->_oController = $oControllerMock; + + $this->callMethod( + $this->_oController, + 'requestNewCredential' + ); + } + + /** + * @test + * @param $throwExc + * @return void + * @throws ReflectionException + * @dataProvider canSetAuthnRegisterDataProvider + * @covers \D3\Webauthn\Application\Controller\d3_account_webauthn::setAuthnRegister() + */ + public function canSetAuthnRegister($throwExc) + { + /** @var Webauthn|MockObject $webAuthnMock */ + $webAuthnMock = $this->getMockBuilder(Webauthn::class) + ->onlyMethods(['getCreationOptions']) + ->getMock(); + if ($throwExc) { + $webAuthnMock->method('getCreationOptions')->willThrowException(oxNew(WebauthnException::class)); + } else { + $webAuthnMock->method('getCreationOptions')->willReturn('options'); + } + + /** @var d3_account_webauthn|MockObject $oControllerMock */ + $oControllerMock = $this->getMockBuilder(d3_account_webauthn::class) + ->onlyMethods(['getWebauthnObject', 'addTplParam', 'getUser']) + ->getMock(); + $oControllerMock->method('getWebauthnObject')->willReturn($webAuthnMock); + $oControllerMock->expects($throwExc ? $this->never() : $this->atLeast(3)) + ->method('addTplParam'); + $oControllerMock->method('getUser')->willReturn(oxNew(User::class)); + + $this->_oController = $oControllerMock; + + if ($throwExc) { + $this->expectException(WebauthnException::class); + } + + $this->callMethod( + $this->_oController, + 'setAuthnRegister' + ); + } + + /** + * @return array + */ + public function canSetAuthnRegisterDataProvider(): array + { + return [ + 'dont throw exception' => [false], + 'throw exception' => [true], + ]; + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3_account_webauthn::setPageType() + */ + public function canSetPageType() + { + $fixture = 'argFixture'; + + /** @var d3_account_webauthn|MockObject $oControllerMock */ + $oControllerMock = $this->getMockBuilder(d3_account_webauthn::class) + ->onlyMethods(['addTplParam']) + ->getMock(); + $oControllerMock->expects($this->atLeastOnce())->method('addTplParam') + ->with($this->anything(), $this->identicalTo($fixture)); + + $this->_oController = $oControllerMock; + + $this->callMethod( + $this->_oController, + 'setPageType', + [$fixture] + ); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3_account_webauthn::saveAuthn + */ + public function canSaveAuthnHasError() + { + $_POST['error'] = 'msg'; + + /** @var UtilsView|MockObject $utilsViewMock */ + $utilsViewMock = $this->getMockBuilder(UtilsView::class) + ->onlyMethods(['addErrorToDisplay']) + ->getMock(); + $utilsViewMock->expects($this->atLeastOnce())->method('addErrorToDisplay'); + + /** @var d3_account_webauthn|MockObject $oControllerMock */ + $oControllerMock = $this->getMockBuilder(d3_account_webauthn::class) + ->onlyMethods(['getWebauthnObject', 'getUtilsViewObject']) + ->getMock(); + $oControllerMock->method('getUtilsViewObject')->willReturn($utilsViewMock); + + $this->_oController = $oControllerMock; + + $this->callMethod( + $this->_oController, + 'saveAuthn' + ); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3_account_webauthn::saveAuthn + */ + public function canSaveAuthnSuccess() + { + $_POST['credential'] = 'msg'; + $_POST['keyname'] = 'key_name'; + + /** @var Webauthn|MockObject $webauthnMock */ + $webauthnMock = $this->getMockBuilder(Webauthn::class) + ->onlyMethods(['saveAuthn']) + ->getMock(); + $webauthnMock->expects($this->once())->method('saveAuthn'); + + /** @var UtilsView|MockObject $utilsViewMock */ + $utilsViewMock = $this->getMockBuilder(UtilsView::class) + ->onlyMethods(['addErrorToDisplay']) + ->getMock(); + $utilsViewMock->expects($this->never())->method('addErrorToDisplay'); + + /** @var d3_account_webauthn|MockObject $oControllerMock */ + $oControllerMock = $this->getMockBuilder(d3_account_webauthn::class) + ->onlyMethods(['getWebauthnObject', 'getUtilsViewObject']) + ->getMock(); + $oControllerMock->method('getWebauthnObject')->willReturn($webauthnMock); + $oControllerMock->method('getUtilsViewObject')->willReturn($utilsViewMock); + + $this->_oController = $oControllerMock; + + $this->callMethod( + $this->_oController, + 'saveAuthn' + ); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3_account_webauthn::saveAuthn + */ + public function canSaveAuthnFailed() + { + $_POST['credential'] = 'msg'; + $_POST['keyname'] = 'key_name'; + + /** @var Webauthn|MockObject $webauthnMock */ + $webauthnMock = $this->getMockBuilder(Webauthn::class) + ->onlyMethods(['saveAuthn']) + ->getMock(); + $webauthnMock->expects($this->once())->method('saveAuthn') + ->willThrowException(oxNew(WebauthnException::class)); + + /** @var UtilsView|MockObject $utilsViewMock */ + $utilsViewMock = $this->getMockBuilder(UtilsView::class) + ->onlyMethods(['addErrorToDisplay']) + ->getMock(); + $utilsViewMock->expects($this->atLeastOnce())->method('addErrorToDisplay'); + + /** @var d3_account_webauthn|MockObject $oControllerMock */ + $oControllerMock = $this->getMockBuilder(d3_account_webauthn::class) + ->onlyMethods(['getWebauthnObject', 'getUtilsViewObject']) + ->getMock(); + $oControllerMock->method('getWebauthnObject')->willReturn($webauthnMock); + $oControllerMock->method('getUtilsViewObject')->willReturn($utilsViewMock); + + $this->_oController = $oControllerMock; + + $this->callMethod( + $this->_oController, + 'saveAuthn' + ); + } + + /** + * @test + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3_account_webauthn::deleteKey + * @dataProvider canDeleteDataProvider + */ + public function canDelete($deleteId, $expected) + { + $_GET['deleteoxid'] = $deleteId; + + /** @var PublicKeyCredential|MockObject $publicKeyCredentialMock */ + $publicKeyCredentialMock = $this->getMockBuilder(PublicKeyCredential::class) + ->disableOriginalConstructor() + ->onlyMethods(['delete']) + ->getMock(); + $publicKeyCredentialMock->expects($expected)->method('delete')->with($this->identicalTo($deleteId)) + ->willReturn(true); + + /** @var d3_account_webauthn|MockObject $oControllerMock */ + $oControllerMock = $this->getMockBuilder(d3_account_webauthn::class) + ->onlyMethods(['getPublicKeyCredentialObject']) + ->getMock(); + $oControllerMock->method('getPublicKeyCredentialObject')->willReturn($publicKeyCredentialMock); + + $this->_oController = $oControllerMock; + + $this->callMethod($this->_oController, 'deleteKey'); + } + + /** + * @return array[] + */ + public function canDeleteDataProvider(): array + { + return [ + 'has delete id' => ['deleteId', $this->once()], + 'has no delete id' => [null, $this->never()], + ]; + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3_account_webauthn::getBreadCrumb + */ + public function canGetBreadCrumb() + { + $this->assertIsArray( + $this->callMethod( + $this->_oController, + 'getBreadCrumb' + ) + ); + } + + /** + * @test + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3_account_webauthn::getWebauthnObject + */ + public function getWebauthnObjectReturnsRightObject() + { + $this->assertInstanceOf( + Webauthn::class, + $this->callMethod( + $this->_oController, + 'getWebauthnObject' + ) + ); + } + + /** + * @test + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3_account_webauthn::getPublicKeyCredentialObject + */ + public function getPublicKeyCredentialObjectReturnsRightObject() + { + $this->assertInstanceOf( + PublicKeyCredential::class, + $this->callMethod( + $this->_oController, + 'getPublicKeyCredentialObject' + ) + ); + } + + /** + * @test + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3_account_webauthn::getPublicKeyCredentialListObject + */ + public function getPublicKeyCredentialListObjectReturnsRightObject() + { + $this->assertInstanceOf( + PublicKeyCredentialList::class, + $this->callMethod( + $this->_oController, + 'getPublicKeyCredentialListObject' + ) + ); + } + + /** + * @test + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3_account_webauthn::getLogger + */ + public function getLoggerObjectReturnsRightObject() + { + $this->assertInstanceOf( + LoggerInterface::class, + $this->callMethod( + $this->_oController, + 'getLogger' + ) + ); + } + + /** + * @test + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3_account_webauthn::getUtilsViewObject + */ + public function getUtilsViewObjectReturnsRightObject() + { + $this->assertInstanceOf( + UtilsView::class, + $this->callMethod( + $this->_oController, + 'getUtilsViewObject' + ) + ); + } +} diff --git a/src/tests/unit/Application/Controller/d3webauthnloginTest.php b/src/tests/unit/Application/Controller/d3webauthnloginTest.php new file mode 100644 index 0000000..e6c96bf --- /dev/null +++ b/src/tests/unit/Application/Controller/d3webauthnloginTest.php @@ -0,0 +1,453 @@ +subjectUnderTest = oxNew(d3webauthnlogin::class); + } + + public function tearDown(): void + { + parent::tearDown(); + unset($this->subjectUnderTest); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3webauthnlogin::getNavigationParams + */ + public function canGetNavigationParams() + { + /** @var Session|MockObject $sessionMock */ + $sessionMock = $this->getMockBuilder(Session::class) + ->onlyMethods(['getVariable']) + ->getMock(); + $sessionMock->method('getVariable')->willReturn([ + 'key1' => 'variable1' + ]); + + /** @var d3webauthnlogin|MockObject $sut */ + $sut = $this->getMockBuilder(d3webauthnlogin::class) + ->onlyMethods(['d3GetSession', 'd3CallMockableParent']) + ->getMock(); + $sut->method('d3GetSession')->willReturn($sessionMock); + $sut->method('d3CallMockableParent')->willReturn(['defKey1' => 'devValues1']); + + $this->assertSame( + [ + 'defKey1' => 'devValues1', + 'key1' => 'variable1', + 'cl' => NULL, + ], + $this->callMethod( + $sut, + 'getNavigationParams' + ) + ); + } + + /** + * @test + * @param $auth + * @param $userFromLogin + * @param $startRedirect + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3webauthnlogin::render + * @dataProvider canRenderDataProvider + */ + public function canRender($auth, $userFromLogin, $startRedirect) + { + /** @var Session|MockObject $sessionMock */ + $sessionMock = $this->getMockBuilder(Session::class) + ->onlyMethods(['hasVariable']) + ->getMock(); + $sessionMock->method('hasVariable')->willReturnMap([ + [WebauthnConf::WEBAUTHN_SESSION_AUTH, $auth], + [WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER, $userFromLogin] + ]); + + /** @var Utils|MockObject $utilsMock */ + $utilsMock = $this->getMockBuilder(Utils::class) + ->onlyMethods(['redirect']) + ->getMock(); + $utilsMock->expects($startRedirect ? $this->once() : $this->never()) + ->method('redirect')->with('index.php?cl=start')->willReturn(true); + + /** @var d3webauthnlogin|MockObject $sut */ + $sut = $this->getMockBuilder(d3webauthnlogin::class) + ->onlyMethods(['d3GetSession', 'getUtils', 'd3CallMockableParent', + 'generateCredentialRequest', 'addTplParam']) + ->getMock(); + $sut->method('d3GetSession')->willReturn($sessionMock); + $sut->method('getUtils')->willReturn($utilsMock); + $sut->method('d3CallMockableParent')->willReturn(['defKey1' => 'devValues1']); + $sut->expects($startRedirect ? $this->any() : $this->atLeastOnce()) + ->method('generateCredentialRequest'); + $sut->expects($startRedirect ? $this->any() : $this->atLeastOnce()) + ->method('addTplParam')->with('navFormParams')->willReturn(true); + + $this->assertSame( + 'd3webauthnlogin.tpl', + $this->callMethod( + $sut, + 'render' + ) + ); + } + + /** + * @return array + */ + public function canRenderDataProvider(): array + { + return [ + 'has request' => [false, true, false], + 'has auth' => [true, true, true], + 'missing user' => [false, false, true], + ]; + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3webauthnlogin::generateCredentialRequest + */ + public function canGenerateCredentialRequest() + { + $currUserFixture = 'currentUserFixture'; + + /** @var LoggerInterface|MockObject $loggerMock */ + $loggerMock = $this->getMockForAbstractClass(LoggerInterface::class, [], '', true, true, true, ['error', 'debug']); + $loggerMock->expects($this->never())->method('error')->willReturn(true); + $loggerMock->expects($this->never())->method('debug')->willReturn(true); + + /** @var Session|MockObject $sessionMock */ + $sessionMock = $this->getMockBuilder(Session::class) + ->onlyMethods(['getVariable']) + ->getMock(); + $sessionMock->method('getVariable')->willReturnMap([ + [WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER, $currUserFixture] + ]); + + /** @var Webauthn|MockObject $webAuthnMock */ + $webAuthnMock = $this->getMockBuilder(Webauthn::class) + ->onlyMethods(['getRequestOptions']) + ->getMock(); + $webAuthnMock->expects($this->once())->method('getRequestOptions')->with($currUserFixture) + ->willReturn('success'); + + /** @var d3webauthnlogin|MockObject $sut */ + $sut = $this->getMockBuilder(d3webauthnlogin::class) + ->onlyMethods(['d3GetSession', 'd3GetWebauthnObject', 'addTplParam', 'd3GetLogger']) + ->getMock(); + $sut->method('d3GetSession')->willReturn($sessionMock); + $sut->method('d3GetWebauthnObject')->willReturn($webAuthnMock); + $sut->expects($this->atLeast(2)) + ->method('addTplParam')->willReturn(true); + $sut->method('d3GetLogger')->willReturn($loggerMock); + + $this->callMethod( + $sut, + 'generateCredentialRequest' + ); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3webauthnlogin::generateCredentialRequest + */ + public function generateCredentialRequestFailed() + { + $currUserFixture = 'currentUserFixture'; + + /** @var LoggerInterface|MockObject $loggerMock */ + $loggerMock = $this->getMockForAbstractClass(LoggerInterface::class, [], '', true, true, true, ['error', 'debug']); + $loggerMock->expects($this->atLeastOnce())->method('error')->willReturn(true); + $loggerMock->expects($this->atLeastOnce())->method('debug')->willReturn(true); + + /** @var Session|MockObject $sessionMock */ + $sessionMock = $this->getMockBuilder(Session::class) + ->onlyMethods(['getVariable', 'setVariable']) + ->getMock(); + $sessionMock->method('getVariable')->willReturnMap([ + [WebauthnConf::WEBAUTHN_SESSION_CURRENTUSER, $currUserFixture] + ]); + $sessionMock->expects($this->once())->method('setVariable')->with(WebauthnConf::GLOBAL_SWITCH) + ->willReturn(true); + + /** @var Webauthn|MockObject $webAuthnMock */ + $webAuthnMock = $this->getMockBuilder(Webauthn::class) + ->onlyMethods(['getRequestOptions']) + ->getMock(); + $webAuthnMock->expects($this->once())->method('getRequestOptions')->with($currUserFixture) + ->willThrowException(oxNew(WebauthnException::class, 'foobar0')); + + /** @var Utils|MockObject $utilsMock */ + $utilsMock = $this->getMockBuilder(Utils::class) + ->onlyMethods(['redirect']) + ->getMock(); + $utilsMock->expects($this->once())->method('redirect') + ->with('index.php?cl=start')->willReturn(true); + + /** @var d3webauthnlogin|MockObject $sut */ + $sut = $this->getMockBuilder(d3webauthnlogin::class) + ->onlyMethods(['d3GetSession', 'd3GetWebauthnObject', 'addTplParam', + 'd3GetLogger', 'getUtils']) + ->getMock(); + $sut->method('d3GetSession')->willReturn($sessionMock); + $sut->method('d3GetWebauthnObject')->willReturn($webAuthnMock); + $sut->expects($this->never()) + ->method('addTplParam')->willReturn(true); + $sut->expects($this->atLeast(2))->method('d3GetLogger')->willReturn($loggerMock); + $sut->method('getUtils')->willReturn($utilsMock); + + $this->callMethod( + $sut, + 'generateCredentialRequest' + ); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3webauthnlogin::getUtils + */ + public function getUtilsReturnsRightInstance() + { + $this->assertInstanceOf( + Utils::class, + $this->callMethod( + $this->subjectUnderTest, + 'getUtils' + ) + ); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3webauthnlogin::getPreviousClass + */ + public function canGetPreviousClass() + { + $currClassFixture = 'currentClassFixture'; + + /** @var Session|MockObject $sessionMock */ + $sessionMock = $this->getMockBuilder(Session::class) + ->onlyMethods(['getVariable']) + ->getMock(); + $sessionMock->method('getVariable')->willReturnMap([ + [WebauthnConf::WEBAUTHN_SESSION_CURRENTCLASS, $currClassFixture] + ]); + + /** @var d3webauthnlogin|MockObject $sut */ + $sut = $this->getMockBuilder(d3webauthnlogin::class) + ->onlyMethods(['d3GetSession']) + ->getMock(); + $sut->method('d3GetSession')->willReturn($sessionMock); + + $this->assertSame( + $currClassFixture, + $this->callMethod( + $sut, + 'getPreviousClass' + ) + ); + } + + /** + * @test + * @param $currClass + * @param $isOrderStep + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3webauthnlogin::previousClassIsOrderStep + * @dataProvider canPreviousClassIsOrderStepDataProvider + */ + public function canPreviousClassIsOrderStep($currClass, $isOrderStep) + { + /** @var Session|MockObject $sessionMock */ + $sessionMock = $this->getMockBuilder(Session::class) + ->onlyMethods(['getVariable']) + ->getMock(); + $sessionMock->method('getVariable')->willReturnMap([ + [WebauthnConf::WEBAUTHN_SESSION_CURRENTCLASS, $currClass] + ]); + + /** @var d3webauthnlogin|MockObject $sut */ + $sut = $this->getMockBuilder(d3webauthnlogin::class) + ->onlyMethods(['d3GetSession']) + ->getMock(); + $sut->method('d3GetSession')->willReturn($sessionMock); + + $this->assertSame( + $isOrderStep, + $this->callMethod( + $sut, + 'previousClassIsOrderStep' + ) + ); + } + + /** + * @return array[] + */ + public function canPreviousClassIsOrderStepDataProvider(): array + { + return [ + 'checkout class' => ['payment', true], + 'no checkout class' => ['details', false], + 'unknown class' => ['unknown', false], + ]; + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3webauthnlogin::getIsOrderStep + * @dataProvider canGetIsOrderStepDataProvider + */ + public function canGetIsOrderStep($boolean) + { + /** @var d3webauthnlogin|MockObject $sut */ + $sut = $this->getMockBuilder(d3webauthnlogin::class) + ->onlyMethods(['previousClassIsOrderStep']) + ->getMock(); + $sut->expects($this->atLeastOnce())->method('previousClassIsOrderStep')->willReturn($boolean); + + $this->assertSame( + $boolean, + $this->callMethod( + $sut, + 'getIsOrderStep' + ) + ); + } + + /** + * @return array + */ + public function canGetIsOrderStepDataProvider(): array + { + return [ + [true], + [false] + ]; + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3webauthnlogin::getBreadCrumb + */ + public function canGetBreadCrumb() + { + $this->assertIsArray( + $this->callMethod( + $this->subjectUnderTest, + 'getBreadCrumb' + ) + ); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3webauthnlogin::d3GetSession + */ + public function canGetSession() + { + $this->assertInstanceOf( + Session::class, + $this->callMethod( + $this->subjectUnderTest, + 'd3GetSession' + ) + ); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3webauthnlogin::d3GetWebauthnObject + */ + public function canGetWebauthnObject() + { + $this->assertInstanceOf( + Webauthn::class, + $this->callMethod( + $this->subjectUnderTest, + 'd3GetWebauthnObject' + ) + ); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3webauthnlogin::d3GetLogger + */ + public function canGetLogger() + { + $this->assertInstanceOf( + LoggerInterface::class, + $this->callMethod( + $this->subjectUnderTest, + 'd3GetLogger' + ) + ); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Controller\d3webauthnlogin::getControllerClassNameResolver + */ + public function canGetClassNameResolver() + { + $this->assertInstanceOf( + ControllerClassNameResolver::class, + $this->callMethod( + $this->subjectUnderTest, + 'getControllerClassNameResolver' + ) + ); + } +} \ No newline at end of file