diff --git a/src/Application/Model/Credential/PublicKeyCredential.php b/src/Application/Model/Credential/PublicKeyCredential.php index c4b3099..b3bca3c 100755 --- a/src/Application/Model/Credential/PublicKeyCredential.php +++ b/src/Application/Model/Credential/PublicKeyCredential.php @@ -20,6 +20,7 @@ use Doctrine\DBAL\Driver\Exception as DoctrineDriverException; use Doctrine\DBAL\Exception as DoctrineException; use Doctrine\DBAL\Query\QueryBuilder; use Exception; +use OxidEsales\Eshop\Core\Config; use OxidEsales\Eshop\Core\Model\BaseModel; use OxidEsales\Eshop\Core\Registry; use OxidEsales\EshopCommunity\Internal\Container\ContainerFactory; @@ -125,7 +126,7 @@ class PublicKeyCredential extends BaseModel public function saveCredentialSource(PublicKeyCredentialSource $publicKeyCredentialSource, string $keyName = null): void { // item exist already - if ((oxNew(PublicKeyCredentialList::class)) + if ($this->getPublicKeyCredentialListObject() ->findOneByCredentialId($publicKeyCredentialSource->getPublicKeyCredentialId()) ) { return; @@ -146,6 +147,14 @@ class PublicKeyCredential extends BaseModel $this->save(); } + /** + * @return PublicKeyCredentialList + */ + protected function getPublicKeyCredentialListObject(): PublicKeyCredentialList + { + return oxNew(PublicKeyCredentialList::class); + } + /** * @param string $publicKeyCredentialId * @@ -169,7 +178,7 @@ class PublicKeyCredential extends BaseModel ), $qb->expr()->eq( 'oxshopid', - $qb->createNamedParameter(Registry::getConfig()->getShopId()) + $qb->createNamedParameter($this->d3GetConfig()->getShopId()) ) ) ); @@ -177,4 +186,12 @@ class PublicKeyCredential extends BaseModel return strlen((string) $oxid) ? $oxid : null; } + + /** + * @return Config + */ + public function d3GetConfig(): Config + { + return Registry::getConfig(); + } } \ No newline at end of file diff --git a/src/Application/Model/UserEntity.php b/src/Application/Model/UserEntity.php index 8dd4041..2702f40 100755 --- a/src/Application/Model/UserEntity.php +++ b/src/Application/Model/UserEntity.php @@ -15,13 +15,15 @@ declare(strict_types=1); namespace D3\Webauthn\Application\Model; +use D3\TestingTools\Production\IsMockable; use D3\Webauthn\Application\Model\Exceptions\WebauthnException; use OxidEsales\Eshop\Application\Model\User; -use OxidEsales\Eshop\Core\Registry; use Webauthn\PublicKeyCredentialUserEntity; class UserEntity extends PublicKeyCredentialUserEntity { + use IsMockable; + /** * @param User $user * @throws WebauthnException @@ -34,10 +36,13 @@ class UserEntity extends PublicKeyCredentialUserEntity throw $e; } - parent::__construct( - strtolower($user->getFieldData('oxusername')), - $user->getId(), - $user->getFieldData('oxfname') . ' ' . $user->getFieldData('oxlname') + $this->d3CallMockableParent( + '__construct', + [ + strtolower($user->getFieldData('oxusername')), + $user->getId(), + $user->getFieldData('oxfname') . ' ' . $user->getFieldData('oxlname') + ] ); } } \ No newline at end of file diff --git a/src/tests/unit/Application/Model/Credential/PublicKeyCredentialTest.php b/src/tests/unit/Application/Model/Credential/PublicKeyCredentialTest.php new file mode 100644 index 0000000..b166e12 --- /dev/null +++ b/src/tests/unit/Application/Model/Credential/PublicKeyCredentialTest.php @@ -0,0 +1,370 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\Webauthn\tests\unit\Application\Model\Credential; + +use D3\TestingTools\Development\CanAccessRestricted; +use D3\Webauthn\Application\Model\Credential\PublicKeyCredential; +use D3\Webauthn\Application\Model\Credential\PublicKeyCredentialList; +use OxidEsales\Eshop\Core\Config; +use OxidEsales\TestingLibrary\UnitTestCase; +use PHPUnit\Framework\MockObject\MockObject; +use ReflectionException; +use Webauthn\PublicKeyCredentialSource; + +class PublicKeyCredentialTest extends UnitTestCase +{ + use CanAccessRestricted; + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Model\Credential\PublicKeyCredential::__construct + */ + public function canConstruct() + { + /** @var PublicKeyCredential|MockObject $sut */ + $sut = $this->getMockBuilder(PublicKeyCredential::class) + ->onlyMethods(['init']) + ->disableOriginalConstructor() + ->getMock(); + $sut->expects($this->atLeastOnce())->method('init')->willReturn(true); + + $this->callMethod( + $sut, + '__construct' + ); + } + + /** + * @param $fieldName + * @param $sutMethod + * @param $value + * @return void + * @throws ReflectionException + */ + public function canSetField($fieldName, $sutMethod, $value) + { + /** @var PublicKeyCredential|MockObject $sut */ + $sut = $this->getMockBuilder(PublicKeyCredential::class) + ->onlyMethods(['assign']) + ->getMock(); + $sut->expects($this->atLeastOnce())->method('assign') + ->with($this->arrayHasKey($fieldName))->willReturn(true); + + $this->callMethod( + $sut, + $sutMethod, + [$value] + ); + } + + /** + * @param $fieldName + * @param $sutMethod + * @param $value + * @return void + * @throws ReflectionException + */ + public function canGetField($fieldName, $sutMethod, $setValue, $getValue) + { + /** @var PublicKeyCredential $sut */ + $sut = oxNew(PublicKeyCredential::class); + $sut->assign([ + $fieldName => $setValue + ]); + + $this->assertEquals( + $getValue, + $this->callMethod( + $sut, + $sutMethod + ) + ); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Model\Credential\PublicKeyCredential::setName + */ + public function canSetName() + { + $this->canSetField('name', 'setName', 'nameFixture'); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Model\Credential\PublicKeyCredential::getName + */ + public function canGetName() + { + $this->canGetField('name', 'getName', 'nameFixture', 'nameFixture'); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Model\Credential\PublicKeyCredential::setCredentialId + */ + public function canSetCredentialId() + { + $this->canSetField('credentialid', 'setCredentialId', 'credentialFixture'); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Model\Credential\PublicKeyCredential::getCredentialId + */ + public function canGetCredentialId() + { + $this->canGetField( + 'credentialid', + 'getCredentialId', + 'credentialFixture', + base64_decode('credentialFixture') + ); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Model\Credential\PublicKeyCredential::setUserId + */ + public function canSetUserId() + { + $this->canSetField('oxuserid', 'setUserId', 'userIdFixture'); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Model\Credential\PublicKeyCredential::getUserId + */ + public function canGetUserId() + { + $this->canGetField('oxuserid', 'getUserId', 'userIdFixture', 'userIdFixture'); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Model\Credential\PublicKeyCredential::setCredential + */ + public function canSetCredential() + { + /** @var PublicKeyCredentialSource $publicKeyCredentialSourceMock */ + $publicKeyCredentialSourceMock = $this->getMockBuilder(PublicKeyCredentialSource::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->canSetField('credential', 'setCredential', $publicKeyCredentialSourceMock); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Model\Credential\PublicKeyCredential::getCredential + */ + public function canGetCredential() + { + /** @var PublicKeyCredentialSource $publicKeyCredentialSourceMock */ + $publicKeyCredentialSourceMock = $this->getMockBuilder(PublicKeyCredentialSource::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->canGetField( + 'credential', + 'getCredential', + base64_encode(serialize($publicKeyCredentialSourceMock)), + $publicKeyCredentialSourceMock + ); + } + + /** + * @test + * @param $credAlreadyExist + * @param $credIdExist + * @param $keyName + * @param $doSave + * @return void + * @throws ReflectionException + * @dataProvider canSaveCredentialSourceDataProvider + * @covers \D3\Webauthn\Application\Model\Credential\PublicKeyCredential::saveCredentialSource + */ + public function canSaveCredentialSource($credAlreadyExist, $credIdExist, $keyName, $doSave) + { + /** @var PublicKeyCredentialSource|MockObject $publicKeyCredentialSourceMock */ + $publicKeyCredentialSourceMock = $this->getMockBuilder(PublicKeyCredentialSource::class) + ->disableOriginalConstructor() + ->getMock(); + + /** @var PublicKeyCredentialList|MockObject $pkcListObjectMock */ + $pkcListObjectMock = $this->getMockBuilder(PublicKeyCredentialList::class) + ->onlyMethods(['findOneByCredentialId']) + ->getMock(); + $pkcListObjectMock->method('findOneByCredentialId')->willReturn( + $credAlreadyExist ? $publicKeyCredentialSourceMock : null + ); + + /** @var PublicKeyCredential|MockObject $sut */ + $sut = $this->getMockBuilder(PublicKeyCredential::class) + ->onlyMethods(['getPublicKeyCredentialListObject', 'exists', 'getIdByCredentialId', 'load', 'save']) + ->getMock(); + $sut->method('getPublicKeyCredentialListObject')->willReturn($pkcListObjectMock); + $sut->method('exists')->willReturn($credIdExist); + $sut->expects($this->exactly((int) $doSave))->method('getIdByCredentialId'); + $sut->expects($this->exactly((int) ($doSave && $credIdExist)))->method('load'); + $sut->expects($this->exactly((int) $doSave))->method('save'); + + $this->callMethod( + $sut, + 'saveCredentialSource', + [$publicKeyCredentialSourceMock, $keyName] + ); + + if ($doSave) { + $this->assertNotNull( + $sut->getName() + ); + } + } + + /** + * @return array + */ + public function canSaveCredentialSourceDataProvider(): array + { + return [ + 'credential already exist' => [true, false, null, false], + 'credential id not exist' => [false, false, 'keyName', true], + 'credential id already exist' => [false, true, 'keyName', true], + 'save with no key name' => [false, false, null, true], + ]; + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Model\Credential\PublicKeyCredential::getPublicKeyCredentialListObject + */ + public function canGetPublicKeyCredentialListObject() + { + /** @var PublicKeyCredential|MockObject $sut */ + $sut = $this->getMockBuilder(PublicKeyCredential::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->assertInstanceOf( + PublicKeyCredentialList::class, + $this->callMethod( + $sut, + 'getPublicKeyCredentialListObject' + ) + ); + } + + /** + * @test + * @return void + * @throws ReflectionException + * @dataProvider canGetIdByCredentialIdDataProvider + * @covers \D3\Webauthn\Application\Model\Credential\PublicKeyCredential::getIdByCredentialId + */ + public function canGetIdByCredentialId($doCreate, $expected) + { + $oxid = 'idFixture'; + $pkcId = 'CredIdFixture'; + $shopId = 55; + + /** @var Config|MockObject $configMock */ + $configMock = $this->getMockBuilder(Config::class) + ->onlyMethods(['getShopId']) + ->getMock(); + $configMock->method('getShopId')->willReturn($shopId); + + /** @var PublicKeyCredential|MockObject $sut */ + $sut = $this->getMockBuilder(PublicKeyCredential::class) + ->onlyMethods(['d3GetConfig', 'allowDerivedDelete']) + ->getMock(); + $sut->method('d3GetConfig')->willReturn($configMock); + $sut->method('allowDerivedDelete')->willReturn(true); + + if ($doCreate) { + $sut->setId($oxid); + $sut->assign([ + 'credentialid' => base64_encode($pkcId), + 'oxshopid' => $shopId + ]); + $sut->save(); + } + + $this->assertSame( + $expected, + $this->callMethod( + $sut, + 'getIdByCredentialId', + [$pkcId] + ) + ); + + if ($doCreate) { + $sut->delete($oxid); + } + } + + /** + * @return array + */ + public function canGetIdByCredentialIdDataProvider(): array + { + return [ + 'item exists' => [true, 'idFixture'], + 'item not exists' => [false, null] + ]; + } + + /** + * @test + * @return void + * @throws ReflectionException + * @covers \D3\Webauthn\Application\Model\Credential\PublicKeyCredential::d3GetConfig + */ + public function canGetConfig() + { + /** @var PublicKeyCredential $sut */ + $sut = oxNew(PublicKeyCredential::class); + + $this->assertInstanceOf( + Config::class, + $this->callMethod( + $sut, + 'd3GetConfig' + ) + ); + } +} \ No newline at end of file diff --git a/src/tests/unit/Application/Model/UserEntityTest.php b/src/tests/unit/Application/Model/UserEntityTest.php new file mode 100644 index 0000000..803b802 --- /dev/null +++ b/src/tests/unit/Application/Model/UserEntityTest.php @@ -0,0 +1,88 @@ + + * @link https://www.oxidmodule.com + */ + +declare(strict_types=1); + +namespace D3\Webauthn\tests\unit\Application\Model; + +use D3\TestingTools\Development\CanAccessRestricted; +use D3\Webauthn\Application\Model\Exceptions\WebauthnException; +use D3\Webauthn\Application\Model\UserEntity; +use OxidEsales\Eshop\Application\Model\User; +use OxidEsales\TestingLibrary\UnitTestCase; +use PHPUnit\Framework\MockObject\MockObject; +use ReflectionException; + +class UserEntityTest extends UnitTestCase +{ + use CanAccessRestricted; + + /** + * @test + * @return void + * @throws ReflectionException + * @dataProvider canConstructNoUserDataProvider + * @covers \D3\Webauthn\Application\Model\UserEntity::__construct + */ + public function canConstructNoUser($isLoaded, $getId, $runParent) + { + /** @var User|MockObject $userMock */ + $userMock = $this->getMockBuilder(User::class) + ->onlyMethods(['isLoaded', 'getId', 'getFieldData']) + ->getMock(); + $userMock->method('isLoaded')->willReturn($isLoaded); + $userMock->method('getId')->willReturn($getId); + $fieldDataMap = [ + ['oxusername', 'userNameFixture'], + ['oxfname', 'fNameFixture'], + ['oxlname', 'lNameFixture'], + ]; + $userMock->method('getFieldData')->willReturnMap($fieldDataMap); + + /** @var UserEntity|MockObject $sut */ + $sut = $this->getMockBuilder(UserEntity::class) + ->disableOriginalConstructor() + ->onlyMethods(['d3CallMockableParent']) + ->getMock(); + $sut->expects($runParent ? $this->once() : $this->never())->method('d3CallMockableParent')->with( + $this->anything(), + $this->identicalTo([ + 'usernamefixture', + 'userId', + 'fNameFixture lNameFixture' + ]) + ); + + if (!$runParent) { + $this->expectException(WebauthnException::class); + }; + + $this->callMethod( + $sut, + '__construct', + [$userMock] + ); + } + + /** + * @return array + */ + public function canConstructNoUserDataProvider(): array + { + return [ + 'not loaded' => [false, 'userId', false], + 'no id' => [true, null, false], + 'user ok' => [true, 'userId', true], + ]; + } +} \ No newline at end of file