From e953becbae4926bdb7ede21025e93b80a5bae2d2 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 25 May 2018 08:38:15 +0200 Subject: [PATCH] Expand test coverage for bunq. --- app/Services/Bunq/ApiContext.php | 28 +- app/Services/Bunq/MonetaryAccount.php | 57 +++ .../Bunq/ChooseAccountsHandler.php | 6 +- .../Import/Routine/Bunq/StageNewHandler.php | 38 +- .../views/import/spectre/prerequisites.twig | 2 +- .../Bunq/ChooseAccountsHandlerTest.php | 439 ++++++++++++++++++ 6 files changed, 540 insertions(+), 30 deletions(-) create mode 100644 app/Services/Bunq/MonetaryAccount.php create mode 100644 tests/Unit/Support/Import/JobConfiguration/Bunq/ChooseAccountsHandlerTest.php diff --git a/app/Services/Bunq/ApiContext.php b/app/Services/Bunq/ApiContext.php index 758af9fcd6..b657fcf18f 100644 --- a/app/Services/Bunq/ApiContext.php +++ b/app/Services/Bunq/ApiContext.php @@ -24,13 +24,13 @@ declare(strict_types=1); namespace FireflyIII\Services\Bunq; use bunq\Context\ApiContext as BunqApiContext; +use bunq\Context\BunqContext; use bunq\Exception\BadRequestException; use bunq\Exception\BunqException; use bunq\Util\BunqEnumApiEnvironmentType; use Exception; use FireflyIII\Exceptions\FireflyException; use Log; -use stdClass; /** * Special class to hide away bunq's static initialisation methods. @@ -55,9 +55,10 @@ class ApiContext try { $context = BunqApiContext::create($environmentType, $apiKey, $description, $permittedIps, $proxyUrl); } catch (BunqException|BadRequestException|Exception $e) { - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); $message = $e->getMessage(); + Log::error($message); + Log::error($e->getTraceAsString()); + if (stripos($message, 'Generating a new private key failed')) { $message = 'Could not generate key-material. Please make sure OpenSSL is installed and configured: http://bit.ly/FF3-openSSL'; } @@ -65,6 +66,27 @@ class ApiContext } return $context; + } + /** + * @throws FireflyException + * + * @param string $jsonString + */ + public function fromJson(string $jsonString): void + { + try { + $apiContext = BunqApiContext::fromJson($jsonString); + BunqContext::loadApiContext($apiContext); + } catch (BadRequestException|BunqException|Exception $e) { + $message = $e->getMessage(); + Log::error($message); + Log::error($e->getTraceAsString()); + + if (stripos($message, 'Generating a new private key failed')) { + $message = 'Could not generate key-material. Please make sure OpenSSL is installed and configured: http://bit.ly/FF3-openSSL'; + } + throw new FireflyException($message); + } } } \ No newline at end of file diff --git a/app/Services/Bunq/MonetaryAccount.php b/app/Services/Bunq/MonetaryAccount.php new file mode 100644 index 0000000000..bb77c54a80 --- /dev/null +++ b/app/Services/Bunq/MonetaryAccount.php @@ -0,0 +1,57 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Services\Bunq; + +use bunq\Exception\BunqException; +use bunq\Model\Generated\Endpoint\BunqResponseMonetaryAccountList; +use bunq\Model\Generated\Endpoint\MonetaryAccount as BunqMonetaryAccount; +use Exception; +use FireflyIII\Exceptions\FireflyException; + +/** + * Class MonetaryAccount + */ +class MonetaryAccount +{ + /** + * @param array $params + * @param array $customHeaders + * + * @return BunqResponseMonetaryAccountList + * @throws FireflyException + */ + public function listing(array $params = null, array $customHeaders = null): BunqResponseMonetaryAccountList + { + $params = $params ?? []; + $customHeaders = $customHeaders ?? []; + try { + $result = BunqMonetaryAccount::listing($params, $customHeaders); + } catch (BunqException|Exception $e) { + throw new FireflyException($e->getMessage()); + } + + return $result; + } + +} \ No newline at end of file diff --git a/app/Support/Import/JobConfiguration/Bunq/ChooseAccountsHandler.php b/app/Support/Import/JobConfiguration/Bunq/ChooseAccountsHandler.php index 955800fa1c..247a7a2916 100644 --- a/app/Support/Import/JobConfiguration/Bunq/ChooseAccountsHandler.php +++ b/app/Support/Import/JobConfiguration/Bunq/ChooseAccountsHandler.php @@ -81,7 +81,7 @@ class ChooseAccountsHandler implements BunqJobConfigurationInterface $mapping = $data['account_mapping'] ?? []; $final = []; if (\count($accounts) === 0) { - throw new FireflyException('No bunq accounts found. Import cannot continue.'); + throw new FireflyException('No bunq accounts found. Import cannot continue.'); // @codeCoverageIgnore } if (\count($mapping) === 0) { $messages = new MessageBag; @@ -115,7 +115,7 @@ class ChooseAccountsHandler implements BunqJobConfigurationInterface $config = $this->repository->getConfiguration($this->importJob); $accounts = $config['accounts'] ?? []; if (\count($accounts) === 0) { - throw new FireflyException('No bunq accounts found. Import cannot continue.'); + throw new FireflyException('No bunq accounts found. Import cannot continue.'); // @codeCoverageIgnore } // list the users accounts: $collection = $this->accountRepository->getAccountsByType([AccountType::ASSET]); @@ -140,6 +140,8 @@ class ChooseAccountsHandler implements BunqJobConfigurationInterface } /** + * @codeCoverageIgnore + * * Get the view for this stage. * * @return string diff --git a/app/Support/Import/Routine/Bunq/StageNewHandler.php b/app/Support/Import/Routine/Bunq/StageNewHandler.php index e83a8db7e7..7ec9e2248c 100644 --- a/app/Support/Import/Routine/Bunq/StageNewHandler.php +++ b/app/Support/Import/Routine/Bunq/StageNewHandler.php @@ -23,19 +23,15 @@ declare(strict_types=1); namespace FireflyIII\Support\Import\Routine\Bunq; -use bunq\Context\ApiContext; -use bunq\Context\BunqContext; -use bunq\Exception\BadRequestException; -use bunq\Exception\BunqException; -use bunq\Model\Generated\Endpoint\MonetaryAccount; +use bunq\Model\Generated\Endpoint\MonetaryAccount as BunqMonetaryAccount; use bunq\Model\Generated\Endpoint\MonetaryAccountBank; use bunq\Model\Generated\Object\Pointer; -use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\ImportJob; use FireflyIII\Models\Preference; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; -use Log; +use FireflyIII\Services\Bunq\ApiContext; +use FireflyIII\Services\Bunq\MonetaryAccount; /** * Class StageNewHandler @@ -56,19 +52,9 @@ class StageNewHandler $preference = app('preferences')->getForUser($this->importJob->user, 'bunq_api_context', null); if (null !== $preference && '' !== (string)$preference->data) { // restore API context - try { - $apiContext = ApiContext::fromJson($preference->data); - BunqContext::loadApiContext($apiContext); - } catch (BadRequestException|BunqException|Exception $e) { - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); - $message = $e->getMessage(); - if (stripos($message, 'Generating a new private key failed')) { - $message = 'Could not generate key-material. Please make sure OpenSSL is installed and configured: http://bit.ly/FF3-openSSL'; - - } - throw new FireflyException($message); - } + /** @var ApiContext $apiContext */ + $apiContext = app(ApiContext::class); + $apiContext->fromJson($preference->data); // list bunq accounts: $accounts = $this->listAccounts(); @@ -97,13 +83,17 @@ class StageNewHandler /** * @return array + * @throws FireflyException */ private function listAccounts(): array { - $accounts = []; - $monetaryAccountList = MonetaryAccount::listing(); - /** @var MonetaryAccount $monetaryAccount */ - foreach ($monetaryAccountList->getValue() as $monetaryAccount) { + $accounts = []; + /** @var MonetaryAccount $lister */ + $lister = app(MonetaryAccount::class); + $result = $lister->listing(); + + /** @var BunqMonetaryAccount $monetaryAccount */ + foreach ($result->getValue() as $monetaryAccount) { $mab = $monetaryAccount->getMonetaryAccountBank(); $array = $this->processMab($mab); $accounts[] = $array; diff --git a/resources/views/import/spectre/prerequisites.twig b/resources/views/import/spectre/prerequisites.twig index 73b0e89824..1a1909bc0a 100644 --- a/resources/views/import/spectre/prerequisites.twig +++ b/resources/views/import/spectre/prerequisites.twig @@ -5,7 +5,7 @@ {% endblock %} {% block content %}
-
+
diff --git a/tests/Unit/Support/Import/JobConfiguration/Bunq/ChooseAccountsHandlerTest.php b/tests/Unit/Support/Import/JobConfiguration/Bunq/ChooseAccountsHandlerTest.php new file mode 100644 index 0000000000..324c1c75bd --- /dev/null +++ b/tests/Unit/Support/Import/JobConfiguration/Bunq/ChooseAccountsHandlerTest.php @@ -0,0 +1,439 @@ +. + */ + +declare(strict_types=1); + +namespace tests\Unit\Support\Import\JobConfiguration\Bunq; + + +use Amount; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Account; +use FireflyIII\Models\AccountType; +use FireflyIII\Models\ImportJob; +use FireflyIII\Models\TransactionCurrency; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; +use FireflyIII\Support\Import\JobConfiguration\Bunq\ChooseAccountsHandler; +use Illuminate\Support\Collection; +use Mockery; +use Tests\TestCase; + +/** + * Class ChooseAccountsHandlerTest + */ +class ChooseAccountsHandlerTest extends TestCase +{ + /** + * @covers \FireflyIII\Support\Import\JobConfiguration\Bunq\ChooseAccountsHandler + */ + public function testCCFalse(): void + { + $job = new ImportJob; + $job->user_id = $this->user()->id; + $job->key = 'caha' . random_int(1, 1000); + $job->status = 'new'; + $job->stage = 'new'; + $job->provider = 'bunq'; + $job->file_type = ''; + $job->configuration = []; + $job->save(); + + // mock stuff + $repository = $this->mock(ImportJobRepositoryInterface::class); + $accountRepos = $this->mock(AccountRepositoryInterface::class); + $currencyRepos = $this->mock(CurrencyRepositoryInterface::class); + // mock calls + $repository->shouldReceive('setUser')->once(); + $accountRepos->shouldReceive('setUser')->once(); + $currencyRepos->shouldReceive('setUser')->once(); + + $repository->shouldReceive('getConfiguration')->andReturn([])->once(); + + $handler = new ChooseAccountsHandler; + $handler->setImportJob($job); + $this->assertFalse($handler->configurationComplete()); + } + + /** + * @covers \FireflyIII\Support\Import\JobConfiguration\Bunq\ChooseAccountsHandler + */ + public function testCCTrue(): void + { + $job = new ImportJob; + $job->user_id = $this->user()->id; + $job->key = 'cahb' . random_int(1, 1000); + $job->status = 'new'; + $job->stage = 'new'; + $job->provider = 'bunq'; + $job->file_type = ''; + $job->configuration = []; + $job->save(); + + // mock stuff + $repository = $this->mock(ImportJobRepositoryInterface::class); + $accountRepos = $this->mock(AccountRepositoryInterface::class); + $currencyRepos = $this->mock(CurrencyRepositoryInterface::class); + + // mock calls + $repository->shouldReceive('setUser')->once(); + $accountRepos->shouldReceive('setUser')->once(); + $currencyRepos->shouldReceive('setUser')->once(); + + $repository->shouldReceive('getConfiguration')->andReturn(['mapping' => [0 => 1, 1 => 2]])->once(); + $repository->shouldReceive('setStage')->withArgs([Mockery::any(), 'go-for-import'])->once(); + + $handler = new ChooseAccountsHandler; + $handler->setImportJob($job); + $this->assertTrue($handler->configurationComplete()); + } + + /** + * @covers \FireflyIII\Support\Import\JobConfiguration\Bunq\ChooseAccountsHandler + */ + public function testConfigureJob(): void + { + $job = new ImportJob; + $job->user_id = $this->user()->id; + $job->key = 'cahc' . random_int(1, 1000); + $job->status = 'new'; + $job->stage = 'new'; + $job->provider = 'bunq'; + $job->file_type = ''; + $job->configuration = []; + $job->save(); + + // data: + $data = [ + 'account_mapping' => [ + '1234' => '456', + ], + ]; + + $config = [ + 'accounts' => [ + 0 => ['id' => 1234, 'name' => 'bunq'], + ], + ]; + $expected = $config; + $expected['mapping'][1234] = 456; + + // mock stuff + $repository = $this->mock(ImportJobRepositoryInterface::class); + $accountRepos = $this->mock(AccountRepositoryInterface::class); + $currencyRepos = $this->mock(CurrencyRepositoryInterface::class); + + // mock calls + $repository->shouldReceive('setUser')->once(); + $accountRepos->shouldReceive('setUser')->once(); + $currencyRepos->shouldReceive('setUser')->once(); + $repository->shouldReceive('getConfiguration')->andReturn($config)->times(2); + $repository->shouldReceive('setConfiguration')->withArgs([Mockery::any(), $expected])->once(); + $accountRepos->shouldReceive('findNull')->withArgs([456])->andReturn(new Account)->once(); + + $handler = new ChooseAccountsHandler; + $handler->setImportJob($job); + try { + $this->assertCount(0, $handler->configureJob($data)); + } catch (FireflyException $e) { + $this->assertFalse(true, $e->getMessage()); + } + } + + /** + * @covers \FireflyIII\Support\Import\JobConfiguration\Bunq\ChooseAccountsHandler + */ + public function testConfigureJobInvalidBunq(): void + { + $job = new ImportJob; + $job->user_id = $this->user()->id; + $job->key = 'cahd' . random_int(1, 1000); + $job->status = 'new'; + $job->stage = 'new'; + $job->provider = 'bunq'; + $job->file_type = ''; + $job->configuration = []; + $job->save(); + + // data: + $data = [ + 'account_mapping' => [ + '1234' => '456', + ], + ]; + + $config = [ + 'accounts' => [ + 0 => ['id' => 1235, 'name' => 'bunq'], + ], + ]; + $expected = $config; + $expected['mapping'][0] = 456; + + // mock stuff + $repository = $this->mock(ImportJobRepositoryInterface::class); + $accountRepos = $this->mock(AccountRepositoryInterface::class); + $currencyRepos = $this->mock(CurrencyRepositoryInterface::class); + + // mock calls + $repository->shouldReceive('setUser')->once(); + $accountRepos->shouldReceive('setUser')->once(); + $currencyRepos->shouldReceive('setUser')->once(); + $repository->shouldReceive('getConfiguration')->andReturn($config)->times(2); + $repository->shouldReceive('setConfiguration')->withArgs([Mockery::any(), $expected])->once(); + $accountRepos->shouldReceive('findNull')->withArgs([456])->andReturn(new Account)->once(); + + $handler = new ChooseAccountsHandler; + $handler->setImportJob($job); + try { + $this->assertCount(0, $handler->configureJob($data)); + } catch (FireflyException $e) { + $this->assertFalse(true, $e->getMessage()); + } + } + + /** + * @covers \FireflyIII\Support\Import\JobConfiguration\Bunq\ChooseAccountsHandler + */ + public function testConfigureJobInvalidLocal(): void + { + $job = new ImportJob; + $job->user_id = $this->user()->id; + $job->key = 'cahe' . random_int(1, 1000); + $job->status = 'new'; + $job->stage = 'new'; + $job->provider = 'bunq'; + $job->file_type = ''; + $job->configuration = []; + $job->save(); + + // data: + $data = [ + 'account_mapping' => [ + '1234' => '456', + ], + ]; + + $config = [ + 'accounts' => [ + 0 => ['id' => 1234, 'name' => 'bunq'], + ], + ]; + $expected = $config; + $expected['mapping'][1234] = 0; + + // mock stuff + $repository = $this->mock(ImportJobRepositoryInterface::class); + $accountRepos = $this->mock(AccountRepositoryInterface::class); + $currencyRepos = $this->mock(CurrencyRepositoryInterface::class); + + // mock calls + $repository->shouldReceive('setUser')->once(); + $accountRepos->shouldReceive('setUser')->once(); + $currencyRepos->shouldReceive('setUser')->once(); + $repository->shouldReceive('getConfiguration')->andReturn($config)->times(2); + $repository->shouldReceive('setConfiguration')->withArgs([Mockery::any(), $expected])->once(); + $accountRepos->shouldReceive('findNull')->withArgs([456])->andReturnNull()->once(); + + $handler = new ChooseAccountsHandler; + $handler->setImportJob($job); + try { + $this->assertCount(0, $handler->configureJob($data)); + } catch (FireflyException $e) { + $this->assertFalse(true, $e->getMessage()); + } + } + + /** + * @covers \FireflyIII\Support\Import\JobConfiguration\Bunq\ChooseAccountsHandler + */ + public function testConfigureJobNoMapping(): void + { + $job = new ImportJob; + $job->user_id = $this->user()->id; + $job->key = 'cahf' . random_int(1, 1000); + $job->status = 'new'; + $job->stage = 'new'; + $job->provider = 'bunq'; + $job->file_type = ''; + $job->configuration = []; + $job->save(); + + // data: + $data = ['account_mapping' => []]; + $config = [ + 'accounts' => [ + 0 => ['id' => 1234, 'name' => 'bunq'], + ], + ]; + + // mock stuff + $repository = $this->mock(ImportJobRepositoryInterface::class); + $accountRepos = $this->mock(AccountRepositoryInterface::class); + $currencyRepos = $this->mock(CurrencyRepositoryInterface::class); + + // mock calls + $repository->shouldReceive('setUser')->once(); + $accountRepos->shouldReceive('setUser')->once(); + $currencyRepos->shouldReceive('setUser')->once(); + $repository->shouldReceive('getConfiguration')->andReturn($config)->times(1); + + $handler = new ChooseAccountsHandler; + $handler->setImportJob($job); + try { + $messages = $handler->configureJob($data); + $this->assertCount(1, $messages); + $this->assertEquals('It seems you have not selected any accounts.', $messages->first()); + } catch (FireflyException $e) { + $this->assertFalse(true, $e->getMessage()); + } + } + + /** + * @covers \FireflyIII\Support\Import\JobConfiguration\Bunq\ChooseAccountsHandler + */ + public function testGetNextData(): void + { + $job = new ImportJob; + $job->user_id = $this->user()->id; + $job->key = 'cahg' . random_int(1, 1000); + $job->status = 'new'; + $job->stage = 'new'; + $job->provider = 'bunq'; + $job->file_type = ''; + $job->configuration = []; + $job->save(); + + // data: + $config = [ + 'accounts' => [ + 0 => ['id' => 1234, 'name' => 'bunq'], + ], + ]; + + $collection = new Collection; + $account = $this->user()->accounts()->first(); + $euro = TransactionCurrency::first(); + $collection->push($account); + + + // mock stuff + $repository = $this->mock(ImportJobRepositoryInterface::class); + $accountRepos = $this->mock(AccountRepositoryInterface::class); + $currencyRepos = $this->mock(CurrencyRepositoryInterface::class); + + // mock calls + $repository->shouldReceive('setUser')->once(); + $accountRepos->shouldReceive('setUser')->once(); + $currencyRepos->shouldReceive('setUser')->once(); + $repository->shouldReceive('getConfiguration')->andReturn($config)->times(1); + $accountRepos->shouldReceive('getAccountsByType')->withArgs([[AccountType::ASSET]])->andReturn($collection)->once(); + $accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'currency_id'])->andReturn('1'); + $currencyRepos->shouldReceive('findNull')->withArgs([1])->andReturn($euro)->once(); + + $expected = [ + 'accounts' => $config['accounts'], + 'local_accounts' => [ + $account->id => [ + 'name' => $account->name, + 'iban' => $account->iban, + 'code' => $euro->code, + ], + ], + ]; + + $handler = new ChooseAccountsHandler; + $handler->setImportJob($job); + try { + $data = $handler->getNextData(); + $this->assertEquals($expected, $data); + } catch (FireflyException $e) { + $this->assertFalse(true, $e->getMessage()); + } + } + + /** + * @covers \FireflyIII\Support\Import\JobConfiguration\Bunq\ChooseAccountsHandler + */ + public function testGetNextDataNull(): void + { + $job = new ImportJob; + $job->user_id = $this->user()->id; + $job->key = 'cahg' . random_int(1, 1000); + $job->status = 'new'; + $job->stage = 'new'; + $job->provider = 'bunq'; + $job->file_type = ''; + $job->configuration = []; + $job->save(); + + // data: + $config = [ + 'accounts' => [ + 0 => ['id' => 1234, 'name' => 'bunq'], + ], + ]; + + $collection = new Collection; + $account = $this->user()->accounts()->first(); + $euro = TransactionCurrency::first(); + $collection->push($account); + + + // mock stuff + $repository = $this->mock(ImportJobRepositoryInterface::class); + $accountRepos = $this->mock(AccountRepositoryInterface::class); + $currencyRepos = $this->mock(CurrencyRepositoryInterface::class); + + // mock calls + $repository->shouldReceive('setUser')->once(); + $accountRepos->shouldReceive('setUser')->once(); + $currencyRepos->shouldReceive('setUser')->once(); + $repository->shouldReceive('getConfiguration')->andReturn($config)->times(1); + $accountRepos->shouldReceive('getAccountsByType')->withArgs([[AccountType::ASSET]])->andReturn($collection)->once(); + $accountRepos->shouldReceive('getMetaValue')->withArgs([Mockery::any(), 'currency_id'])->andReturn('1'); + $currencyRepos->shouldReceive('findNull')->withArgs([1])->andReturn(null)->once(); + Amount::shouldReceive('getDefaultCurrencyByUser')->once()->andReturn($euro); + + $expected = [ + 'accounts' => $config['accounts'], + 'local_accounts' => [ + $account->id => [ + 'name' => $account->name, + 'iban' => $account->iban, + 'code' => $euro->code, + ], + ], + ]; + + $handler = new ChooseAccountsHandler; + $handler->setImportJob($job); + try { + $data = $handler->getNextData(); + $this->assertEquals($expected, $data); + } catch (FireflyException $e) { + $this->assertFalse(true, $e->getMessage()); + } + } + + +} \ No newline at end of file