. */ namespace SP\Tests\Domain\Account\Services; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\MockObject\Exception; use PHPUnit\Framework\MockObject\MockObject; use RuntimeException; use SP\Core\Context\ContextException; use SP\Domain\Account\Ports\AccountHistoryService; use SP\Domain\Account\Ports\AccountService; use SP\Domain\Account\Services\AccountCrypt; use SP\Domain\Common\Services\ServiceException; use SP\Domain\Core\Crypt\CryptInterface; use SP\Domain\Core\Exceptions\SPException; use SP\Domain\Crypt\Dtos\UpdateMasterPassRequest; use SP\Domain\Task\Ports\TaskInterface; use SP\Domain\Task\Services\TaskFactory; use SP\Tests\Generators\AccountDataGenerator; use SP\Tests\UnitaryTestCase; /** * Class AccountCryptServiceTest * */ #[Group('unitary')] class AccountCryptTest extends UnitaryTestCase { private MockObject|AccountService $account; private MockObject|AccountHistoryService $accountHistory; private AccountCrypt $accountCrypt; private MockObject|CryptInterface $crypt; /** * @throws ServiceException */ public function testUpdateMasterPassword(): void { $request = new UpdateMasterPassRequest( self::$faker->password, self::$faker->password, self::$faker->sha1 ); $accountData = array_map(static fn() => AccountDataGenerator::factory()->buildAccount(), range(0, 9)); $this->account->expects(self::once()) ->method('getAccountsPassData') ->willReturn($accountData); $this->account->expects(self::exactly(10)) ->method('updatePasswordMasterPass'); $this->crypt->expects(self::exactly(10)) ->method('decrypt'); $this->crypt->expects(self::exactly(10)) ->method('makeSecuredKey') ->willReturn(self::$faker->password); $this->crypt->expects(self::exactly(10)) ->method('encrypt') ->willReturn(self::$faker->password); $this->accountCrypt->updateMasterPassword($request); } /** * @throws ServiceException */ public function testUpdateMasterPasswordWithNoAccounts(): void { $request = new UpdateMasterPassRequest( self::$faker->password, self::$faker->password, self::$faker->sha1 ); $this->account->expects(self::once()) ->method('getAccountsPassData') ->willReturn([]); $this->account->expects(self::never()) ->method('updatePasswordMasterPass'); $this->crypt->expects(self::never()) ->method('decrypt'); $this->crypt->expects(self::never()) ->method('makeSecuredKey'); $this->crypt->expects(self::never()) ->method('encrypt'); $this->accountCrypt->updateMasterPassword($request); } /** * @throws ServiceException */ public function testUpdateMasterPasswordDoesNotThrowException(): void { $request = new UpdateMasterPassRequest(self::$faker->password, self::$faker->password, self::$faker->sha1); $accountData = array_map(static fn() => AccountDataGenerator::factory()->buildAccount(), range(0, 9)); $this->account->expects(self::once()) ->method('getAccountsPassData') ->willReturn($accountData); $this->crypt->expects(self::exactly(10)) ->method('decrypt') ->willThrowException(new SPException('test')); $this->accountCrypt->updateMasterPassword($request); } /** * @throws ServiceException */ public function testUpdateMasterPasswordThrowException(): void { $request = new UpdateMasterPassRequest(self::$faker->password, self::$faker->password, self::$faker->sha1); $this->account->expects(self::once()) ->method('getAccountsPassData') ->willThrowException(new RuntimeException('test')); $this->expectException(ServiceException::class); $this->expectExceptionMessage('Error while updating the accounts\' passwords'); $this->accountCrypt->updateMasterPassword($request); } /** * @throws Exception * @throws ServiceException */ public function testUpdateHistoryMasterPassword(): void { $request = new UpdateMasterPassRequest( self::$faker->password, self::$faker->password, self::$faker->sha1 ); $accountData = array_map(static fn() => AccountDataGenerator::factory()->buildAccount(), range(0, 9)); $this->accountHistory->expects(self::once()) ->method('getAccountsPassData') ->willReturn($accountData); $this->accountHistory->expects(self::exactly(10)) ->method('updatePasswordMasterPass'); $this->crypt->expects(self::exactly(10)) ->method('decrypt'); $this->accountCrypt->updateHistoryMasterPassword($request); } /** * @throws ServiceException */ public function testUpdateHistoryMasterPasswordWithNoAccounts(): void { $request = new UpdateMasterPassRequest( self::$faker->password, self::$faker->password, self::$faker->sha1 ); $this->accountHistory->expects(self::once()) ->method('getAccountsPassData') ->willReturn([]); $this->accountHistory->expects(self::never()) ->method('updatePasswordMasterPass'); $this->crypt->expects(self::never()) ->method('decrypt'); $this->crypt->expects(self::never()) ->method('makeSecuredKey'); $this->crypt->expects(self::never()) ->method('encrypt'); $this->accountCrypt->updateHistoryMasterPassword($request); } /** * @throws ServiceException */ public function testUpdateHistoryMasterPasswordThrowException(): void { $request = new UpdateMasterPassRequest(self::$faker->password, self::$faker->password, self::$faker->sha1); $this->accountHistory->expects(self::once()) ->method('getAccountsPassData') ->willThrowException(new RuntimeException('test')); $this->expectException(ServiceException::class); $this->expectExceptionMessage('Error while updating the accounts\' passwords in history'); $this->accountCrypt->updateHistoryMasterPassword($request); } /** * @throws ServiceException */ public function testUpdateHistoryMasterPasswordDoesNotThrowException(): void { $request = new UpdateMasterPassRequest(self::$faker->password, self::$faker->password, self::$faker->sha1); $accountData = array_map(static fn() => AccountDataGenerator::factory()->buildAccount(), range(0, 9)); $this->accountHistory->expects(self::once()) ->method('getAccountsPassData') ->willReturn($accountData); $this->crypt->expects(self::exactly(10)) ->method('decrypt') ->willThrowException(new SPException('test')); $this->accountCrypt->updateHistoryMasterPassword($request); } /** * @throws ServiceException */ public function testGetPasswordEncrypted(): void { $pass = self::$faker->password; $key = self::$faker->password; $masterPass = self::$faker->password; $this->crypt->expects(self::once()) ->method('makeSecuredKey') ->with($masterPass) ->willReturn($key); $this->crypt->expects(self::once()) ->method('encrypt') ->with($pass) ->willReturn($pass); $out = $this->accountCrypt->getPasswordEncrypted($pass, $masterPass); $this->assertEquals($pass, $out->getPass()); $this->assertEquals($key, $out->getKey()); } /** * @throws ServiceException * @throws ContextException */ public function testGetPasswordEncryptedThrowsExceptionWithNoMasterPassword(): void { $this->context->setTrasientKey('_masterpass', ''); $this->crypt->expects(self::never()) ->method('makeSecuredKey'); $this->crypt->expects(self::never()) ->method('encrypt'); $this->expectException(ServiceException::class); $this->expectExceptionMessage('Error while retrieving master password from context'); $this->accountCrypt->getPasswordEncrypted(self::$faker->password); } /** * @throws ServiceException */ public function testGetPasswordEncryptedThrowsExceptionWithEmptyMasterPassword(): void { $this->crypt->expects(self::never()) ->method('makeSecuredKey'); $this->crypt->expects(self::never()) ->method('encrypt'); $this->expectException(ServiceException::class); $this->expectExceptionMessage('Master password not set'); $this->accountCrypt->getPasswordEncrypted(self::$faker->password, ''); } /** * @throws ServiceException */ public function testGetPasswordEncryptedThrowsExceptionWithLongPass(): void { $this->crypt->expects(self::once()) ->method('makeSecuredKey') ->willReturn(self::$faker->password); $this->crypt->expects(self::once()) ->method('encrypt') ->willReturn(self::$faker->text(1500)); $this->expectException(ServiceException::class); $this->expectExceptionMessage('Internal error'); $this->accountCrypt->getPasswordEncrypted(self::$faker->password, self::$faker->password); } /** * @throws ServiceException */ public function testGetPasswordEncryptedThrowsExceptionWithLongKey(): void { $this->crypt->expects(self::once()) ->method('makeSecuredKey') ->willReturn(self::$faker->text(1500)); $this->crypt->expects(self::once()) ->method('encrypt') ->willReturn(self::$faker->password); $this->expectException(ServiceException::class); $this->expectExceptionMessage('Internal error'); $this->accountCrypt->getPasswordEncrypted(self::$faker->password, self::$faker->password); } protected function setUp(): void { parent::setUp(); $this->account = $this->createMock(AccountService::class); $this->accountHistory = $this->createMock(AccountHistoryService::class); $this->crypt = $this->createMock(CryptInterface::class); $this->accountCrypt = new AccountCrypt( $this->application, $this->account, $this->accountHistory, $this->crypt ); } }