From 620c5f515eb74604946a50f12ce4b877dca59a00 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 20 May 2018 16:26:27 +0200 Subject: [PATCH] Improve test coverage, remove dead code. --- .../JobConfiguration/FakeJobConfiguration.php | 9 +- .../JobConfiguration/FileJobConfiguration.php | 9 +- .../JobConfigurationInterface.php | 5 - .../SpectreJobConfiguration.php | 10 +- .../Prerequisites/SpectrePrerequisites.php | 154 +---- app/Import/Routine/SpectreRoutine.php | 559 +----------------- app/Models/ImportJob.php | 3 + app/Models/Preference.php | 3 +- .../Spectre/Request/SpectreRequest.php | 51 +- .../Information/GetSpectreCustomerTrait.php | 105 ++++ .../GetSpectreTokenTrait.php} | 19 +- ...Handler.php => StageImportDataHandler.php} | 10 +- .../Routine/Spectre/StageNewHandler.php | 70 +-- .../Controllers/TransactionControllerTest.php | 4 + .../SpectreJobConfigurationTest.php | 189 ++++++ .../SpectrePrerequisitesTest.php | 245 ++++++++ .../Import/Routine/SpectreRoutineTest.php | 229 +++++++ .../Routine/Spectre/StageNewHandlerTest.php | 80 +++ 18 files changed, 919 insertions(+), 835 deletions(-) create mode 100644 app/Support/Import/Information/GetSpectreCustomerTrait.php rename app/Support/Import/{Routine/Spectre/ManageLoginsHandler.php => Information/GetSpectreTokenTrait.php} (57%) rename app/Support/Import/Routine/Spectre/{ImportDataHandler.php => StageImportDataHandler.php} (97%) create mode 100644 tests/Unit/Import/JobConfiguration/SpectreJobConfigurationTest.php create mode 100644 tests/Unit/Import/Prerequisites/SpectrePrerequisitesTest.php create mode 100644 tests/Unit/Import/Routine/SpectreRoutineTest.php create mode 100644 tests/Unit/Support/Import/Routine/Spectre/StageNewHandlerTest.php diff --git a/app/Import/JobConfiguration/FakeJobConfiguration.php b/app/Import/JobConfiguration/FakeJobConfiguration.php index 6899bee71a..81a96adfd7 100644 --- a/app/Import/JobConfiguration/FakeJobConfiguration.php +++ b/app/Import/JobConfiguration/FakeJobConfiguration.php @@ -38,14 +38,6 @@ class FakeJobConfiguration implements JobConfigurationInterface /** @var ImportJobRepositoryInterface */ private $repository; - /** - * ConfiguratorInterface constructor. - */ - public function __construct() - { - $this->repository = app(ImportJobRepositoryInterface::class); - } - /** * Returns true when the initial configuration for this job is complete. * @@ -161,6 +153,7 @@ class FakeJobConfiguration implements JobConfigurationInterface public function setImportJob(ImportJob $importJob): void { $this->importJob = $importJob; + $this->repository = app(ImportJobRepositoryInterface::class); $this->repository->setUser($importJob->user); } } diff --git a/app/Import/JobConfiguration/FileJobConfiguration.php b/app/Import/JobConfiguration/FileJobConfiguration.php index 38eedca1bd..5306484a30 100644 --- a/app/Import/JobConfiguration/FileJobConfiguration.php +++ b/app/Import/JobConfiguration/FileJobConfiguration.php @@ -45,14 +45,6 @@ class FileJobConfiguration implements JobConfigurationInterface /** @var ImportJobRepositoryInterface */ private $repository; - /** - * ConfiguratorInterface constructor. - */ - public function __construct() - { - $this->repository = app(ImportJobRepositoryInterface::class); - } - /** * Returns true when the initial configuration for this job is complete. * @@ -129,6 +121,7 @@ class FileJobConfiguration implements JobConfigurationInterface public function setImportJob(ImportJob $importJob): void { $this->importJob = $importJob; + $this->repository = app(ImportJobRepositoryInterface::class); $this->repository->setUser($importJob->user); } diff --git a/app/Import/JobConfiguration/JobConfigurationInterface.php b/app/Import/JobConfiguration/JobConfigurationInterface.php index 2a76e8b51a..0560240b17 100644 --- a/app/Import/JobConfiguration/JobConfigurationInterface.php +++ b/app/Import/JobConfiguration/JobConfigurationInterface.php @@ -30,11 +30,6 @@ use Illuminate\Support\MessageBag; */ interface JobConfigurationInterface { - /** - * ConfiguratorInterface constructor. - */ - public function __construct(); - /** * Returns true when the initial configuration for this job is complete. * diff --git a/app/Import/JobConfiguration/SpectreJobConfiguration.php b/app/Import/JobConfiguration/SpectreJobConfiguration.php index 521ae2a32d..4304881f3f 100644 --- a/app/Import/JobConfiguration/SpectreJobConfiguration.php +++ b/app/Import/JobConfiguration/SpectreJobConfiguration.php @@ -49,13 +49,6 @@ class SpectreJobConfiguration implements JobConfigurationInterface /** @var ImportJobRepositoryInterface */ private $repository; - /** - * ConfiguratorInterface constructor. - */ - public function __construct() - { - } - /** * Returns true when the initial configuration for this job is complete. * @@ -146,8 +139,9 @@ class SpectreJobConfiguration implements JobConfigurationInterface $handler->setImportJob($this->importJob); break; default: + // @codeCoverageIgnoreStart throw new FireflyException(sprintf('Firefly III cannot create a configuration handler for stage "%s"', $this->importJob->stage)); - + // @codeCoverageIgnoreEnd } return $handler; diff --git a/app/Import/Prerequisites/SpectrePrerequisites.php b/app/Import/Prerequisites/SpectrePrerequisites.php index cc2ec4c9c1..239e3f274b 100644 --- a/app/Import/Prerequisites/SpectrePrerequisites.php +++ b/app/Import/Prerequisites/SpectrePrerequisites.php @@ -34,157 +34,7 @@ class SpectrePrerequisites implements PrerequisitesInterface { /** @var User */ private $user; - // - // /** - // * Returns view name that allows user to fill in prerequisites. Currently asks for the API key. - // * - // * @return string - // */ - // public function getView(): string - // { - // return 'import.spectre.prerequisites'; - // } - // - // /** - // * Returns any values required for the prerequisites-view. - // * - // * @return array - // */ - // public function getViewParameters(): array - // { - // $publicKey = $this->getPublicKey(); - // $subTitle = (string)trans('import.spectre_title'); - // $subTitleIcon = 'fa-archive'; - // - // return compact('publicKey', 'subTitle', 'subTitleIcon'); - // } - // - // /** - // * Returns if this import method has any special prerequisites such as config - // * variables or other things. The only thing we verify is the presence of the API key. Everything else - // * tumbles into place: no installation token? Will be requested. No device server? Will be created. Etc. - // * - // * @return bool - // */ - // public function hasPrerequisites(): bool - // { - // $values = [ - // Preferences::getForUser($this->user, 'spectre_app_id', false), - // Preferences::getForUser($this->user, 'spectre_secret', false), - // ]; - // /** @var Preference $value */ - // foreach ($values as $value) { - // if (false === $value->data || null === $value->data) { - // Log::info(sprintf('Config var "%s" is missing.', $value->name)); - // - // return true; - // } - // } - // Log::debug('All prerequisites are here!'); - // - // return false; - // } - // - // /** - // * Indicate if all prerequisites have been met. - // * - // * @return bool - // */ - // public function isComplete(): bool - // { - // // return true when user has set the App Id and the Spectre Secret. - // $values = [ - // Preferences::getForUser($this->user, 'spectre_app_id', false), - // Preferences::getForUser($this->user, 'spectre_secret', false), - // ]; - // $result = true; - // /** @var Preference $value */ - // foreach ($values as $value) { - // if (false === $value->data || null === $value->data) { - // $result = false; - // } - // } - // - // return $result; - // } - // - // /** - // * Set the user for this Prerequisites-routine. Class is expected to implement and save this. - // * - // * @param User $user - // */ - // public function setUser(User $user): void - // { - // $this->user = $user; - // - // } - // - // /** - // * This method responds to the user's submission of an API key. It tries to register this instance as a new Firefly III device. - // * If this fails, the error is returned in a message bag and the user is notified (this is fairly friendly). - // * - // * @param Request $request - // * - // * @return MessageBag - // */ - // public function storePrerequisites(Request $request): MessageBag - // { - // Log::debug('Storing Spectre API keys..'); - // Preferences::setForUser($this->user, 'spectre_app_id', $request->get('app_id')); - // Preferences::setForUser($this->user, 'spectre_secret', $request->get('secret')); - // Log::debug('Done!'); - // - // return new MessageBag; - // } - // - // /** - // * This method creates a new public/private keypair for the user. This isn't really secure, since the key is generated on the fly with - // * no regards for HSM's, smart cards or other things. It would require some low level programming to get this right. But the private key - // * is stored encrypted in the database so it's something. - // */ - // private function createKeyPair(): void - // { - // Log::debug('Generate new Spectre key pair for user.'); - // $keyConfig = [ - // 'digest_alg' => 'sha512', - // 'private_key_bits' => 2048, - // 'private_key_type' => OPENSSL_KEYTYPE_RSA, - // ]; - // // Create the private and public key - // $res = openssl_pkey_new($keyConfig); - // - // // Extract the private key from $res to $privKey - // $privKey = ''; - // openssl_pkey_export($res, $privKey); - // - // // Extract the public key from $res to $pubKey - // $pubKey = openssl_pkey_get_details($res); - // - // Preferences::setForUser($this->user, 'spectre_private_key', $privKey); - // Preferences::setForUser($this->user, 'spectre_public_key', $pubKey['key']); - // Log::debug('Created key pair'); - // - // } - // - // /** - // * Get a public key from the users preferences. - // * - // * @return string - // */ - // private function getPublicKey(): string - // { - // Log::debug('get public key'); - // $preference = Preferences::getForUser($this->user, 'spectre_public_key', null); - // if (null === $preference) { - // Log::debug('public key is null'); - // // create key pair - // $this->createKeyPair(); - // } - // $preference = Preferences::getForUser($this->user, 'spectre_public_key', null); - // Log::debug('Return public key for user'); - // - // return $preference->data; - // } + /** * Returns view name that allows user to fill in prerequisites. * @@ -249,7 +99,7 @@ class SpectrePrerequisites implements PrerequisitesInterface public function storePrerequisites(array $data): MessageBag { Log::debug('Storing Spectre API keys..'); - app('preferences')->setForUser($this->user, 'spectre_app_id',$data['app_id'] ?? null); + app('preferences')->setForUser($this->user, 'spectre_app_id', $data['app_id'] ?? null); app('preferences')->setForUser($this->user, 'spectre_secret', $data['secret'] ?? null); Log::debug('Done!'); diff --git a/app/Import/Routine/SpectreRoutine.php b/app/Import/Routine/SpectreRoutine.php index db939b11cb..b31eacef07 100644 --- a/app/Import/Routine/SpectreRoutine.php +++ b/app/Import/Routine/SpectreRoutine.php @@ -25,8 +25,7 @@ namespace FireflyIII\Import\Routine; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\ImportJob; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; -use FireflyIII\Support\Import\Routine\Spectre\ImportDataHandler; -use FireflyIII\Support\Import\Routine\Spectre\ManageLoginsHandler; +use FireflyIII\Support\Import\Routine\Spectre\StageImportDataHandler; use FireflyIII\Support\Import\Routine\Spectre\StageAuthenticatedHandler; use FireflyIII\Support\Import\Routine\Spectre\StageNewHandler; use Log; @@ -48,11 +47,6 @@ class SpectreRoutine implements RoutineInterface * * The final status of the routine must be "provider_finished". * - * Spectre: - * Stage new: - * - StageNewHandler - * - * @return bool * @throws FireflyException */ public function run(): void @@ -62,7 +56,7 @@ class SpectreRoutine implements RoutineInterface if (\in_array($this->importJob->status, $valid, true)) { switch ($this->importJob->stage) { default: - throw new FireflyException(sprintf('SpectreRoutine cannot handle stage "%s".', $this->importJob->stage)); + throw new FireflyException(sprintf('SpectreRoutine cannot handle stage "%s".', $this->importJob->stage)); // @codeCoverageIgnore case 'new': // list all of the users logins. $this->repository->setStatus($this->importJob, 'running'); @@ -72,7 +66,7 @@ class SpectreRoutine implements RoutineInterface $handler->run(); // if count logins is zero, go to authenticate stage - if ($handler->countLogins === 0) { + if ($handler->getCountLogins() === 0) { $this->repository->setStage($this->importJob, 'authenticate'); $this->repository->setStatus($this->importJob, 'ready_to_run'); @@ -103,16 +97,14 @@ class SpectreRoutine implements RoutineInterface // user has chosen account mapping. Should now be ready to import data. $this->repository->setStatus($this->importJob, 'running'); $this->repository->setStage($this->importJob, 'do_import'); - /** @var ImportDataHandler $handler */ - $handler = app(ImportDataHandler::class); + /** @var StageImportDataHandler $handler */ + $handler = app(StageImportDataHandler::class); $handler->setImportJob($this->importJob); $handler->run(); $this->repository->setStatus($this->importJob, 'provider_finished'); $this->repository->setStage($this->importJob, 'final'); } } - - } /** @@ -127,545 +119,4 @@ class SpectreRoutine implements RoutineInterface $this->repository->setUser($importJob->user); } - - - // /** @var Collection */ - // public $errors; - // /** @var Collection */ - // public $journals; - // /** @var int */ - // public $lines = 0; - // /** @var ImportJob */ - // private $job; - // - // /** @var ImportJobRepositoryInterface */ - // private $repository; - // - // /** - // * ImportRoutine constructor. - // */ - // public function __construct() - // { - // $this->journals = new Collection; - // $this->errors = new Collection; - // } - // - // /** - // * @return Collection - // */ - // public function getErrors(): Collection - // { - // return $this->errors; - // } - // - // /** - // * @return Collection - // */ - // public function getJournals(): Collection - // { - // return $this->journals; - // } - // - // /** - // * @return int - // */ - // public function getLines(): int - // { - // return $this->lines; - // } - // - // /** - // * A Spectre job that ends up here is either "configured" or "running", and will be set to "running" - // * when it is "configured". - // * - // * Job has several stages, stored in extended status key 'stage' - // * - // * initial: just begun, nothing happened. action: get a customer and a token. Next status: has-token - // * has-token: redirect user to sandstorm, make user login. set job to: user-logged-in - // * user-logged-in: customer has an attempt. action: analyse/get attempt and go for next status. - // * if attempt failed: job status is error, save a warning somewhere? - // * if success, try to get accounts. Save in config key 'accounts'. set status: have-accounts and "configuring" - // * - // * have-accounts: make user link accounts and select accounts to import from. - // * - // * If job is "configuring" and stage "have-accounts" then present the accounts and make user link them to - // * own asset accounts. Store this mapping, set config to "have-account-mapping" and job status configured". - // * - // * have-account-mapping: start downloading transactions? - // * - // * - // * @return bool - // * - // * @throws FireflyException - // * @throws SpectreException - // * @throws \Illuminate\Container\EntryNotFoundException - // */ - // public function run(): bool - // { - // if ('configured' === $this->getStatus()) { - // $this->repository->updateStatus($this->job, 'running'); - // } - // Log::info(sprintf('Start with import job %s using Spectre.', $this->job->key)); - // set_time_limit(0); - // - // // check if job has token first! - // $stage = $this->getConfig()['stage'] ?? 'unknown'; - // - // switch ($stage) { - // case 'initial': - // // get customer and token: - // $this->runStageInitial(); - // break; - // case 'has-token': - // // import routine does nothing at this point: - // break; - // case 'user-logged-in': - // $this->runStageLoggedIn(); - // break; - // case 'have-account-mapping': - // $this->runStageHaveMapping(); - // break; - // default: - // throw new FireflyException(sprintf('Cannot handle stage %s', $stage)); - // } - // - // return true; - // } - // - // /** - // * @param ImportJob $job - // */ - // public function setXJob(ImportJob $job) - // { - // $this->job = $job; - // $this->repository = app(ImportJobRepositoryInterface::class); - // $this->repository->setUser($job->user); - // } - // - // /** - // * @return Customer - // * - // * @throws \FireflyIII\Exceptions\FireflyException - // * @throws \FireflyIII\Services\Spectre\Exception\SpectreException - // * @throws \Illuminate\Container\EntryNotFoundException - // */ - // protected function createCustomer(): Customer - // { - // $newCustomerRequest = new NewCustomerRequest($this->job->user); - // $customer = null; - // try { - // $newCustomerRequest->call(); - // $customer = $newCustomerRequest->getCustomer(); - // } catch (Exception $e) { - // // already exists, must fetch customer instead. - // Log::warning(sprintf('Customer exists already for user, fetch it: %s', $e->getMessage())); - // } - // if (null === $customer) { - // $getCustomerRequest = new ListCustomersRequest($this->job->user); - // $getCustomerRequest->call(); - // $customers = $getCustomerRequest->getCustomers(); - // /** @var Customer $current */ - // foreach ($customers as $current) { - // if ('default_ff3_customer' === $current->getIdentifier()) { - // $customer = $current; - // break; - // } - // } - // } - // - // Preferences::setForUser($this->job->user, 'spectre_customer', $customer->toArray()); - // - // return $customer; - // } - // - // /** - // * @return Customer - // * - // * @throws FireflyException - // * @throws SpectreException - // * @throws \Illuminate\Container\EntryNotFoundException - // */ - // protected function getCustomer(): Customer - // { - // $config = $this->getConfig(); - // if (null !== $config['customer']) { - // $customer = new Customer($config['customer']); - // - // return $customer; - // } - // - // $customer = $this->createCustomer(); - // $config['customer'] = [ - // 'id' => $customer->getId(), - // 'identifier' => $customer->getIdentifier(), - // 'secret' => $customer->getSecret(), - // ]; - // $this->setConfig($config); - // - // return $customer; - // } - // - // /** - // * @param Customer $customer - // * @param string $returnUri - // * - // * @return Token - // * - // * @throws \FireflyIII\Exceptions\FireflyException - // * @throws \FireflyIII\Services\Spectre\Exception\SpectreException - // * @throws \Illuminate\Container\EntryNotFoundException - // */ - // protected function getToken(Customer $customer, string $returnUri): Token - // { - // $request = new CreateTokenRequest($this->job->user); - // $request->setUri($returnUri); - // $request->setCustomer($customer); - // $request->call(); - // Log::debug('Call to get token is finished'); - // - // return $request->getToken(); - // } - // - // /** - // * @throws FireflyException - // * @throws SpectreException - // * @throws \Illuminate\Container\EntryNotFoundException - // */ - // protected function runStageInitial(): void - // { - // Log::debug('In runStageInitial()'); - // - // // create customer if user does not have one: - // $customer = $this->getCustomer(); - // Log::debug(sprintf('Customer ID is %s', $customer->getId())); - // - // // use customer to request a token: - // $uri = route('import.status', [$this->job->key]); - // $token = $this->getToken($customer, $uri); - // Log::debug(sprintf('Token is %s', $token->getToken())); - // - // // update job, give it the token: - // $config = $this->getConfig(); - // $config['has-token'] = true; - // $config['token'] = $token->getToken(); - // $config['token-expires'] = $token->getExpiresAt()->format('U'); - // $config['token-url'] = $token->getConnectUrl(); - // $config['stage'] = 'has-token'; - // $this->setConfig($config); - // - // Log::debug('Job config is now', $config); - // - // // update job, set status to "configuring". - // $this->setStatus('configuring'); - // Log::debug(sprintf('Job status is now %s', $this->job->status)); - // $this->addStep(); - // } - // - // /** - // * @throws FireflyException - // * @throws SpectreException - // * @throws \Illuminate\Container\EntryNotFoundException - // */ - // protected function runStageLoggedIn(): void - // { - // Log::debug('In runStageLoggedIn'); - // // list all logins: - // $customer = $this->getCustomer(); - // $request = new ListLoginsRequest($this->job->user); - // $request->setCustomer($customer); - // $request->call(); - // - // $logins = $request->getLogins(); - // /** @var Login $final */ - // $final = null; - // // loop logins, find the latest with no error in it: - // $time = 0; - // /** @var Login $login */ - // foreach ($logins as $login) { - // $attempt = $login->getLastAttempt(); - // $attemptTime = (int)$attempt->getCreatedAt()->format('U'); - // if ($attemptTime > $time && null === $attempt->getFailErrorClass()) { - // $time = $attemptTime; - // $final = $login; - // } - // } - // if (null === $final) { - // Log::error('Could not find a valid login for this user.'); - // $this->repository->addError($this->job, 0, 'Spectre connection failed. Did you use invalid credentials, press Cancel or failed the 2FA challenge?'); - // $this->repository->setStatus($this->job, 'error'); - // - // return; - // } - // $this->addStep(); - // - // // list the users accounts using this login. - // $accountRequest = new ListAccountsRequest($this->job->user); - // $accountRequest->setLogin($login); - // $accountRequest->call(); - // $accounts = $accountRequest->getAccounts(); - // - // // store accounts in job: - // $all = []; - // /** @var Account $account */ - // foreach ($accounts as $account) { - // $all[] = $account->toArray(); - // } - // - // // update job: - // $config = $this->getConfig(); - // $config['accounts'] = $all; - // $config['login'] = $login->toArray(); - // $config['stage'] = 'have-accounts'; - // - // $this->setConfig($config); - // $this->setStatus('configuring'); - // $this->addStep(); - // } - // - // /** - // * Shorthand method. - // */ - // private function addStep() - // { - // $this->repository->addStepsDone($this->job, 1); - // } - // - // /** - // * Shorthand - // * - // * @param int $steps - // */ - // private function addTotalSteps(int $steps) - // { - // $this->repository->addTotalSteps($this->job, $steps); - // } - // - // /** - // * @return array - // */ - // private function getConfig(): array - // { - // return $this->repository->getConfiguration($this->job); - // } - // - // /** - // * Shorthand method. - // * - // * @return array - // */ - // private function getExtendedStatus(): array - // { - // return $this->repository->getExtendedStatus($this->job); - // } - // - // /** - // * Shorthand method. - // * - // * @return string - // */ - // private function getStatus(): string - // { - // return $this->repository->getStatus($this->job); - // } - // - // /** - // * @param array $all - // * - // * @throws FireflyException - // */ - // private function importTransactions(array $all) - // { - // Log::debug('Going to import transactions'); - // $collection = new Collection; - // // create import objects? - // foreach ($all as $accountId => $data) { - // Log::debug(sprintf('Now at account #%d', $accountId)); - // /** @var Transaction $transaction */ - // foreach ($data['transactions'] as $transaction) { - // Log::debug(sprintf('Now at transaction #%d', $transaction->getId())); - // /** @var Account $account */ - // $account = $data['account']; - // $importJournal = new ImportJournal; - // $importJournal->setUser($this->job->user); - // $importJournal->asset->setDefaultAccountId($data['import_id']); - // // call set value a bunch of times for various data entries: - // $tags = []; - // $tags[] = $transaction->getMode(); - // $tags[] = $transaction->getStatus(); - // if ($transaction->isDuplicated()) { - // $tags[] = 'possibly-duplicated'; - // } - // $extra = $transaction->getExtra()->toArray(); - // $notes = ''; - // // double space for newline in Markdown. - // $notes .= (string)trans('import.imported_from_account', ['account' => $account->getName()]) . ' ' . "\n"; - // - // foreach ($extra as $key => $value) { - // switch ($key) { - // case 'account_number': - // $importJournal->setValue(['role' => 'account-number', 'value' => $value]); - // break; - // case 'original_category': - // case 'original_subcategory': - // case 'customer_category_code': - // case 'customer_category_name': - // $tags[] = $value; - // break; - // case 'payee': - // $importJournal->setValue(['role' => 'opposing-name', 'value' => $value]); - // break; - // case 'original_amount': - // $importJournal->setValue(['role' => 'amount_foreign', 'value' => $value]); - // break; - // case 'original_currency_code': - // $importJournal->setValue(['role' => 'foreign-currency-code', 'value' => $value]); - // break; - // default: - // $notes .= $key . ': ' . $value . ' '; // for newline in Markdown. - // } - // } - // // hash - // $importJournal->setHash($transaction->getHash()); - // - // // account ID (Firefly III account): - // $importJournal->setValue(['role' => 'account-id', 'value' => $data['import_id'], 'mapped' => $data['import_id']]); - // - // // description: - // $importJournal->setValue(['role' => 'description', 'value' => $transaction->getDescription()]); - // - // // date: - // $importJournal->setValue(['role' => 'date-transaction', 'value' => $transaction->getMadeOn()->toIso8601String()]); - // - // // amount - // $importJournal->setValue(['role' => 'amount', 'value' => $transaction->getAmount()]); - // $importJournal->setValue(['role' => 'currency-code', 'value' => $transaction->getCurrencyCode()]); - // - // // various meta fields: - // $importJournal->setValue(['role' => 'category-name', 'value' => $transaction->getCategory()]); - // $importJournal->setValue(['role' => 'note', 'value' => $notes]); - // $importJournal->setValue(['role' => 'tags-comma', 'value' => implode(',', $tags)]); - // $collection->push($importJournal); - // } - // } - // $this->addStep(); - // Log::debug(sprintf('Going to try and store all %d them.', $collection->count())); - // - // $this->addTotalSteps(7 * $collection->count()); - // // try to store them (seven steps per transaction) - // $storage = new ImportStorage; - // - // $storage->setXJob($this->job); - // $storage->setDateFormat('Y-m-d\TH:i:sO'); - // $storage->setObjects($collection); - // $storage->store(); - // Log::info('Back in importTransactions()'); - // - // // link to tag - // /** @var TagRepositoryInterface $repository */ - // $repository = app(TagRepositoryInterface::class); - // $repository->setUser($this->job->user); - // $data = [ - // 'tag' => trans('import.import_with_key', ['key' => $this->job->key]), - // 'date' => new Carbon, - // 'description' => null, - // 'latitude' => null, - // 'longitude' => null, - // 'zoomLevel' => null, - // 'tagMode' => 'nothing', - // ]; - // $tag = $repository->store($data); - // $extended = $this->getExtendedStatus(); - // $extended['tag'] = $tag->id; - // $this->setExtendedStatus($extended); - // - // Log::debug(sprintf('Created tag #%d ("%s")', $tag->id, $tag->tag)); - // Log::debug('Looping journals...'); - // $journalIds = $storage->journals->pluck('id')->toArray(); - // $tagId = $tag->id; - // $this->addTotalSteps(\count($journalIds)); - // - // foreach ($journalIds as $journalId) { - // Log::debug(sprintf('Linking journal #%d to tag #%d...', $journalId, $tagId)); - // DB::table('tag_transaction_journal')->insert(['transaction_journal_id' => $journalId, 'tag_id' => $tagId]); - // $this->addStep(); - // } - // Log::info(sprintf('Linked %d journals to tag #%d ("%s")', $storage->journals->count(), $tag->id, $tag->tag)); - // - // // set status to "finished"? - // // update job: - // $this->setStatus('finished'); - // $this->addStep(); - // - // } - // - // /** - // * @throws FireflyException - // * @throws SpectreException - // * @throws \Illuminate\Container\EntryNotFoundException - // */ - // private function runStageHaveMapping() - // { - // $config = $this->getConfig(); - // $accounts = $config['accounts'] ?? []; - // $all = []; - // $count = 0; - // /** @var array $accountArray */ - // foreach ($accounts as $accountArray) { - // $account = new Account($accountArray); - // $importId = (int)($config['accounts-mapped'][$account->getId()] ?? 0.0); - // $doImport = 0 !== $importId; - // if (!$doImport) { - // Log::debug(sprintf('Will NOT import from Spectre account #%d ("%s")', $account->getId(), $account->getName())); - // continue; - // } - // // grab all transactions - // $listTransactionsRequest = new ListTransactionsRequest($this->job->user); - // $listTransactionsRequest->setAccount($account); - // $listTransactionsRequest->call(); - // $transactions = $listTransactionsRequest->getTransactions(); - // $all[$account->getId()] = [ - // 'account' => $account, - // 'import_id' => $importId, - // 'transactions' => $transactions, - // ]; - // $count += \count($transactions); - // } - // Log::debug(sprintf('Total number of transactions: %d', $count)); - // $this->addStep(); - // - // $this->importTransactions($all); - // } - // - // /** - // * Shorthand. - // * - // * @param array $config - // */ - // private function setConfig(array $config): void - // { - // $this->repository->setConfiguration($this->job, $config); - // - // } - // - // /** - // * Shorthand method. - // * - // * @param array $extended - // */ - // private function setExtendedStatus(array $extended): void - // { - // $this->repository->setExtendedStatus($this->job, $extended); - // - // } - // - // /** - // * Shorthand. - // * - // * @param string $status - // */ - // private function setStatus(string $status): void - // { - // $this->repository->setStatus($this->job, $status); - // } - } diff --git a/app/Models/ImportJob.php b/app/Models/ImportJob.php index 91ad6871cc..508168c16d 100644 --- a/app/Models/ImportJob.php +++ b/app/Models/ImportJob.php @@ -35,6 +35,9 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property int $user_id * @property string $status * @property string $stage + * @property string $key + * @property string $provider + * @property string $file_type */ class ImportJob extends Model { diff --git a/app/Models/Preference.php b/app/Models/Preference.php index 16a092a18c..3a873f7086 100644 --- a/app/Models/Preference.php +++ b/app/Models/Preference.php @@ -34,6 +34,7 @@ use FireflyIII\User; * Class Preference. * * @property mixed $data + * @property string $name */ class Preference extends Model { @@ -49,7 +50,7 @@ class Preference extends Model ]; /** @var array */ - protected $fillable = ['user_id', 'data', 'name', 'data']; + protected $fillable = ['user_id', 'data', 'name']; /** * @param $value diff --git a/app/Services/Spectre/Request/SpectreRequest.php b/app/Services/Spectre/Request/SpectreRequest.php index dedbac6537..b9e5722e6e 100644 --- a/app/Services/Spectre/Request/SpectreRequest.php +++ b/app/Services/Spectre/Request/SpectreRequest.php @@ -30,7 +30,7 @@ use Requests; use Requests_Response; /** - * Class BunqRequest. + * Class SpectreRequest */ abstract class SpectreRequest { @@ -47,31 +47,6 @@ abstract class SpectreRequest /** @var User */ private $user; - /** - * SpectreRequest constructor. - * - * @param User $user - * - * @throws \Psr\Container\NotFoundExceptionInterface - * @throws \Psr\Container\ContainerExceptionInterface - */ - public function __construct(User $user) - { - $this->user = $user; - $this->server = 'https://' . config('import.options.spectre.server'); - $this->expiresAt = time() + 180; - $privateKey = app('preferences')->get('spectre_private_key', null); - $this->privateKey = $privateKey->data; - - // set client ID - $appId = app('preferences')->get('spectre_app_id', null); - $this->appId = $appId->data; - - // set service secret - $secret = app('preferences')->get('spectre_secret', null); - $this->secret = $secret->data; - } - /** * */ @@ -125,6 +100,30 @@ abstract class SpectreRequest $this->privateKey = $privateKey; } + /** + * @param User $user + */ + public function setUser(User $user): void + { + $this->user = $user; + $this->server = 'https://' . config('import.options.spectre.server'); + $this->expiresAt = time() + 180; + $privateKey = app('preferences')->getForUser($user, 'spectre_private_key', null); + $this->privateKey = $privateKey->data; + + // set client ID + $appId = app('preferences')->getForUser($user, 'spectre_app_id', null); + if (null !== $appId && '' !== (string)$appId->data) { + $this->appId = $appId->data; + } + + // set service secret + $secret = app('preferences')->getForUser($user, 'spectre_secret', null); + if (null !== $secret && '' !== (string)$secret->data) { + $this->secret = $secret->data; + } + } + /** * @param string $method * @param string $uri diff --git a/app/Support/Import/Information/GetSpectreCustomerTrait.php b/app/Support/Import/Information/GetSpectreCustomerTrait.php new file mode 100644 index 0000000000..b5f4f9b63b --- /dev/null +++ b/app/Support/Import/Information/GetSpectreCustomerTrait.php @@ -0,0 +1,105 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Support\Import\Information; + +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\ImportJob; +use FireflyIII\Services\Spectre\Object\Customer; +use FireflyIII\Services\Spectre\Request\ListCustomersRequest; +use FireflyIII\Services\Spectre\Request\NewCustomerRequest; +use Log; + +/** + * Trait GetSpectreCustomerTrait + * + * @package FireflyIII\Support\Import\Information + */ +trait GetSpectreCustomerTrait +{ + /** + * @param ImportJob $importJob + * + * @return Customer + * @throws FireflyException + */ + protected function getCustomer(ImportJob $importJob): Customer + { + Log::debug('Now in GetSpectreCustomerTrait::getCustomer()'); + $customer = $this->getExistingCustomer($importJob); + if (null === $customer) { + Log::debug('The customer is NULL, will fire a newCustomerRequest.'); + /** @var NewCustomerRequest $request */ + $request = app(NewCustomerRequest::class); + $request->setUser($importJob->user); + $customer = $request->getCustomer(); + + } + Log::debug('The customer is not null.'); + + return $customer; + } + + /** + * @param ImportJob $importJob + * + * @return Customer|null + * @throws FireflyException + */ + protected function getExistingCustomer(ImportJob $importJob): ?Customer + { + Log::debug('Now in GetSpectreCustomerTrait::getExistingCustomer()'); + $customer = null; + + // check users preferences. + $preference = app('preferences')->getForUser($importJob->user, 'spectre_customer'); + if (null !== $preference) { + Log::debug('Customer is in user configuration'); + $customer = new Customer($preference->data); + + return $customer; + } + + /** @var ListCustomersRequest $request */ + $request = app(ListCustomersRequest::class); + $request->setUser($importJob->user); + $request->call(); + $customers = $request->getCustomers(); + + Log::debug(sprintf('Found %d customer(s)', \count($customers))); + /** @var Customer $current */ + foreach ($customers as $current) { + if ('default_ff3_customer' === $current->getIdentifier()) { + $customer = $current; + Log::debug('Found the correct customer.'); + break; + } + Log::debug(sprintf('Skip customer with name "%s"', $current->getIdentifier())); + } + + // store in preferences. + app('preferences')->setForUser($importJob->user, 'spectre_customer', $customer->toArray()); + + return $customer; + } +} \ No newline at end of file diff --git a/app/Support/Import/Routine/Spectre/ManageLoginsHandler.php b/app/Support/Import/Information/GetSpectreTokenTrait.php similarity index 57% rename from app/Support/Import/Routine/Spectre/ManageLoginsHandler.php rename to app/Support/Import/Information/GetSpectreTokenTrait.php index d879ab91ae..8db7942986 100644 --- a/app/Support/Import/Routine/Spectre/ManageLoginsHandler.php +++ b/app/Support/Import/Information/GetSpectreTokenTrait.php @@ -1,6 +1,6 @@ importJob->configuration; $accounts = $config['accounts'] ?? []; Log::debug(sprintf('Count of accounts in array is %d', \count($accounts))); @@ -103,7 +103,7 @@ class ImportDataHandler { $array = []; $total = \count($transactions); - Log::debug(sprintf('Now in ImportDataHandler::convertToArray() with count %d', \count($transactions))); + Log::debug(sprintf('Now in StageImportDataHandler::convertToArray() with count %d', \count($transactions))); /** @var SpectreTransaction $transaction */ foreach ($transactions as $index => $transaction) { Log::debug(sprintf('Now creating array for transaction %d of %d', $index + 1, $total)); diff --git a/app/Support/Import/Routine/Spectre/StageNewHandler.php b/app/Support/Import/Routine/Spectre/StageNewHandler.php index f81b7f8ff9..c94c7c1b90 100644 --- a/app/Support/Import/Routine/Spectre/StageNewHandler.php +++ b/app/Support/Import/Routine/Spectre/StageNewHandler.php @@ -26,11 +26,9 @@ namespace FireflyIII\Support\Import\Routine\Spectre; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\ImportJob; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; -use FireflyIII\Services\Spectre\Object\Customer; use FireflyIII\Services\Spectre\Object\Login; -use FireflyIII\Services\Spectre\Request\ListCustomersRequest; use FireflyIII\Services\Spectre\Request\ListLoginsRequest; -use FireflyIII\Services\Spectre\Request\NewCustomerRequest; +use FireflyIII\Support\Import\Information\GetSpectreCustomerTrait; use Log; /** @@ -40,13 +38,24 @@ use Log; */ class StageNewHandler { + use GetSpectreCustomerTrait; /** @var int */ - public $countLogins = 0; + private $countLogins = 0; /** @var ImportJob */ private $importJob; /** @var ImportJobRepositoryInterface */ private $repository; + /** + * @codeCoverageIgnore + * @return int + */ + public function getCountLogins(): int + { + return $this->countLogins; + } + + /** * Tasks for this stage: * @@ -59,11 +68,13 @@ class StageNewHandler public function run(): void { Log::debug('Now in ManageLoginsHandler::run()'); - $customer = $this->getCustomer(); - $config = $this->repository->getConfiguration($this->importJob); + $customer = $this->getCustomer($this->importJob); + $config = $this->repository->getConfiguration($this->importJob); Log::debug('Going to get a list of logins.'); - $request = new ListLoginsRequest($this->importJob->user); + /** @var ListLoginsRequest $request */ + $request = app(ListLoginsRequest::class); + $request->setUser($this->importJob->user); $request->setCustomer($customer); $request->call(); @@ -94,49 +105,4 @@ class StageNewHandler $this->repository = app(ImportJobRepositoryInterface::class); $this->repository->setUser($importJob->user); } - - /** - * @return Customer - * @throws FireflyException - */ - private function getCustomer(): Customer - { - Log::debug('Now in manageLoginsHandler::getCustomer()'); - $customer = $this->getExistingCustomer(); - if (null === $customer) { - Log::debug('The customer is NULL, will fire a newCustomerRequest.'); - $newCustomerRequest = new NewCustomerRequest($this->importJob->user); - $customer = $newCustomerRequest->getCustomer(); - - } - Log::debug('The customer is not null.'); - - return $customer; - } - - /** - * @return Customer|null - * @throws FireflyException - */ - private function getExistingCustomer(): ?Customer - { - Log::debug('Now in manageLoginsHandler::getExistingCustomer()'); - $customer = null; - $getCustomerRequest = new ListCustomersRequest($this->importJob->user); - $getCustomerRequest->call(); - $customers = $getCustomerRequest->getCustomers(); - - Log::debug(sprintf('Found %d customer(s)', \count($customers))); - /** @var Customer $current */ - foreach ($customers as $current) { - if ('default_ff3_customer' === $current->getIdentifier()) { - $customer = $current; - Log::debug('Found the correct customer.'); - break; - } - Log::debug(sprintf('Skip customer with name "%s"', $current->getIdentifier())); - } - - return $customer; - } } \ No newline at end of file diff --git a/tests/Api/V1/Controllers/TransactionControllerTest.php b/tests/Api/V1/Controllers/TransactionControllerTest.php index d4646cd950..a17d46a389 100644 --- a/tests/Api/V1/Controllers/TransactionControllerTest.php +++ b/tests/Api/V1/Controllers/TransactionControllerTest.php @@ -28,6 +28,7 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Helpers\Filter\NegativeAmountFilter; +use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Account\AccountRepositoryInterface; @@ -1399,7 +1400,10 @@ class TransactionControllerTest extends TestCase $journal = $this->user()->transactionJournals()->inRandomOrder()->where('transaction_type_id', 1)->whereNull('deleted_at')->first(); $count = $journal->transactions()->count(); } while ($count !== 2); + /** @var Transaction $transaction */ $transaction = $journal->transactions()->first(); + $transaction->description = null; + $transaction->save(); $accountRepos = $this->mock(AccountRepositoryInterface::class); $accountRepos->shouldReceive('setUser'); diff --git a/tests/Unit/Import/JobConfiguration/SpectreJobConfigurationTest.php b/tests/Unit/Import/JobConfiguration/SpectreJobConfigurationTest.php new file mode 100644 index 0000000000..55af66cfda --- /dev/null +++ b/tests/Unit/Import/JobConfiguration/SpectreJobConfigurationTest.php @@ -0,0 +1,189 @@ +. + */ + +declare(strict_types=1); + +namespace Tests\Unit\Import\JobConfiguration; + +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Import\JobConfiguration\SpectreJobConfiguration; +use FireflyIII\Models\ImportJob; +use FireflyIII\Support\Import\JobConfiguration\Spectre\AuthenticateConfig; +use FireflyIII\Support\Import\JobConfiguration\Spectre\AuthenticatedConfigHandler; +use FireflyIII\Support\Import\JobConfiguration\Spectre\ChooseAccount; +use FireflyIII\Support\Import\JobConfiguration\Spectre\ChooseLoginHandler; +use FireflyIII\Support\Import\JobConfiguration\Spectre\NewConfig; +use Illuminate\Support\MessageBag; +use Tests\TestCase; + +/** + * Class SpectreJobConfigurationTest + */ +class SpectreJobConfigurationTest extends TestCase +{ + /** + * @covers \FireflyIII\Import\JobConfiguration\SpectreJobConfiguration + */ + public function testConfigurationComplete(): void + { + $job = new ImportJob; + $job->user_id = $this->user()->id; + $job->key = 'spectre_jc_A' . random_int(1, 1000); + $job->status = 'new'; + $job->stage = 'new'; + $job->provider = 'spectre'; + $job->file_type = ''; + $job->configuration = []; + $job->save(); + + // expect "NewConfig" to be created because job is new. + $handler = $this->mock(NewConfig::class); + $handler->shouldReceive('setImportJob')->once(); + $handler->shouldReceive('configurationComplete')->once()->andReturn(true); + + $config = new SpectreJobConfiguration; + try { + $config->setImportJob($job); + } catch (FireflyException $e) { + $this->assertTrue(false, $e->getMessage()); + } + $this->assertTrue($config->configurationComplete()); + } + + /** + * @covers \FireflyIII\Import\JobConfiguration\SpectreJobConfiguration + */ + public function testConfigureJob(): void + { + $job = new ImportJob; + $job->user_id = $this->user()->id; + $job->key = 'spectre_jc_B' . random_int(1, 1000); + $job->status = 'new'; + $job->stage = 'authenticate'; + $job->provider = 'spectre'; + $job->file_type = ''; + $job->configuration = []; + $job->save(); + $configData = ['ssome' => 'values']; + $return = new MessageBag(); + $return->add('some', 'return message'); + + // expect "NewConfig" to be created because job is new. + $handler = $this->mock(AuthenticateConfig::class); + $handler->shouldReceive('setImportJob')->once(); + $handler->shouldReceive('configureJob')->once()->withArgs([$configData])->andReturn($return); + + $config = new SpectreJobConfiguration; + try { + $config->setImportJob($job); + } catch (FireflyException $e) { + $this->assertTrue(false, $e->getMessage()); + } + $this->assertEquals($return, $config->configureJob($configData)); + } + + /** + * @covers \FireflyIII\Import\JobConfiguration\SpectreJobConfiguration + */ + public function testGetNextData(): void + { + $job = new ImportJob; + $job->user_id = $this->user()->id; + $job->key = 'spectre_jc_C' . random_int(1, 1000); + $job->status = 'new'; + $job->stage = 'choose-login'; + $job->provider = 'spectre'; + $job->file_type = ''; + $job->configuration = []; + $job->save(); + $data = ['ssome' => 'values']; + + $handler = $this->mock(ChooseLoginHandler::class); + $handler->shouldReceive('setImportJob')->once(); + $handler->shouldReceive('getNextData')->once()->andReturn($data); + + $config = new SpectreJobConfiguration; + try { + $config->setImportJob($job); + } catch (FireflyException $e) { + $this->assertTrue(false, $e->getMessage()); + } + $this->assertEquals($data, $config->getNextData()); + } + + /** + * @covers \FireflyIII\Import\JobConfiguration\SpectreJobConfiguration + */ + public function testGetNextView(): void + { + $job = new ImportJob; + $job->user_id = $this->user()->id; + $job->key = 'spectre_jc_D' . random_int(1, 1000); + $job->status = 'new'; + $job->stage = 'authenticated'; + $job->provider = 'spectre'; + $job->file_type = ''; + $job->configuration = []; + $job->save(); + + $handler = $this->mock(AuthenticatedConfigHandler::class); + $handler->shouldReceive('setImportJob')->once(); + $handler->shouldReceive('getNextView')->once()->andReturn('import.fake.view'); + + $config = new SpectreJobConfiguration; + try { + $config->setImportJob($job); + } catch (FireflyException $e) { + $this->assertTrue(false, $e->getMessage()); + } + $this->assertEquals('import.fake.view', $config->getNextView()); + } + + /** + * @covers \FireflyIII\Import\JobConfiguration\SpectreJobConfiguration + */ + public function testGetNextViewAccount(): void + { + $job = new ImportJob; + $job->user_id = $this->user()->id; + $job->key = 'spectre_jc_E' . random_int(1, 1000); + $job->status = 'new'; + $job->stage = 'choose-account'; + $job->provider = 'spectre'; + $job->file_type = ''; + $job->configuration = []; + $job->save(); + + $handler = $this->mock(ChooseAccount::class); + $handler->shouldReceive('setImportJob')->once(); + $handler->shouldReceive('getNextView')->once()->andReturn('import.fake.view2'); + + $config = new SpectreJobConfiguration; + try { + $config->setImportJob($job); + } catch (FireflyException $e) { + $this->assertTrue(false, $e->getMessage()); + } + $this->assertEquals('import.fake.view2', $config->getNextView()); + } + + +} \ No newline at end of file diff --git a/tests/Unit/Import/Prerequisites/SpectrePrerequisitesTest.php b/tests/Unit/Import/Prerequisites/SpectrePrerequisitesTest.php new file mode 100644 index 0000000000..18dc8e2609 --- /dev/null +++ b/tests/Unit/Import/Prerequisites/SpectrePrerequisitesTest.php @@ -0,0 +1,245 @@ +. + */ + +declare(strict_types=1); + +namespace Tests\Unit\Import\Prerequisites; + + +use FireflyIII\Import\Prerequisites\SpectrePrerequisites; +use FireflyIII\Models\Preference; +use Mockery; +use Preferences; +use Tests\TestCase; + +/** + * Class SpectrePrerequisitesTest + */ +class SpectrePrerequisitesTest extends TestCase +{ + /** + * @covers \FireflyIII\Import\Prerequisites\SpectrePrerequisites + */ + public function testGetView(): void + { + + $object = new SpectrePrerequisites; + $object->setUser($this->user()); + $this->assertEquals('import.spectre.prerequisites', $object->getView()); + } + + /** + * Returns NULL as much as possible, forcing system to generate new keys. + * + * @covers \FireflyIII\Import\Prerequisites\SpectrePrerequisites + */ + public function testGetViewParameters(): void + { + $publicKey = new Preference; + $publicKey->name = 'spectre_public_key'; + $publicKey->data = '---PUBKEY---'; + + $privateKey = new Preference; + $privateKey->name = 'spectre_private_key'; + $privateKey->data = '---PRIVKEY---'; + + // get secret + Preferences::shouldReceive('getForUser')->once() + ->withArgs([Mockery::any(), 'spectre_secret', null]) + ->andReturnNull(); + + // get App ID + Preferences::shouldReceive('getForUser')->once() + ->withArgs([Mockery::any(), 'spectre_app_id', null]) + ->andReturnNull(); + + // get users public key + // second time it should exist. + Preferences::shouldReceive('getForUser')->twice() + ->withArgs([Mockery::any(), 'spectre_public_key', null]) + ->andReturn(null, $publicKey); + // SET users new pulic key + Preferences::shouldReceive('setForUser')->once() + ->withArgs([Mockery::any(), 'spectre_public_key', Mockery::any()]) + ->andReturn($publicKey); + // SET private key + Preferences::shouldReceive('setForUser')->once() + ->withArgs([Mockery::any(), 'spectre_private_key', Mockery::any()]) + ->andReturn($privateKey); + + + $object = new SpectrePrerequisites; + $object->setUser($this->user()); + $return = $object->getViewParameters(); + $this->assertEquals( + [ + 'app_id' => '', + 'secret' => '', + 'public_key' => '---PUBKEY---', + ], $return + ); + } + + /** + * App ID exists, secret is empty. + * + * @covers \FireflyIII\Import\Prerequisites\SpectrePrerequisites + */ + public function testIsComplete(): void + { + $appId = new Preference; + $appId->name = 'spectre_app_id'; + $appId->data = 'Some app id'; + + $secret = new Preference; + $secret->name = 'spectre_secret'; + $secret->data = 'Hello'; + // get App ID + Preferences::shouldReceive('getForUser')->once() + ->withArgs([Mockery::any(), 'spectre_app_id', null]) + ->andReturn($appId); + + // get secret + Preferences::shouldReceive('getForUser')->once() + ->withArgs([Mockery::any(), 'spectre_secret', null]) + ->andReturn($secret); + + $object = new SpectrePrerequisites; + $object->setUser($this->user()); + $this->assertTrue($object->isComplete()); + } + + /** + * App ID exists, secret is null. + * + * @covers \FireflyIII\Import\Prerequisites\SpectrePrerequisites + */ + public function testIsCompleteAppId(): void + { + $appId = new Preference; + $appId->name = 'spectre_app_id'; + $appId->data = 'Some app id'; + // get App ID + Preferences::shouldReceive('getForUser')->once() + ->withArgs([Mockery::any(), 'spectre_app_id', null]) + ->andReturn($appId); + + // get secret + Preferences::shouldReceive('getForUser')->once() + ->withArgs([Mockery::any(), 'spectre_secret', null]) + ->andReturnNull(); + + $object = new SpectrePrerequisites; + $object->setUser($this->user()); + $this->assertFalse($object->isComplete()); + } + + /** + * App ID is "" and Secret is NULL. Since App ID is "" secret won't be polled. + * + * @covers \FireflyIII\Import\Prerequisites\SpectrePrerequisites + */ + public function testIsCompleteEmpty(): void + { + $appId = new Preference; + $appId->name = 'spectre_app_id'; + $appId->data = ''; + + // get App ID + Preferences::shouldReceive('getForUser')->once() + ->withArgs([Mockery::any(), 'spectre_app_id', null]) + ->andReturn($appId); + + $object = new SpectrePrerequisites; + $object->setUser($this->user()); + $this->assertFalse($object->isComplete()); + } + + /** + * App ID and Secret are NULL. Since App ID is null secret won't be polled. + * + * @covers \FireflyIII\Import\Prerequisites\SpectrePrerequisites + */ + public function testIsCompleteNull(): void + { + // get App ID + Preferences::shouldReceive('getForUser')->once() + ->withArgs([Mockery::any(), 'spectre_app_id', null]) + ->andReturnNull(); + + $object = new SpectrePrerequisites; + $object->setUser($this->user()); + $this->assertFalse($object->isComplete()); + } + + /** + * App ID exists, secret is empty. + * + * @covers \FireflyIII\Import\Prerequisites\SpectrePrerequisites + */ + public function testIsCompleteSecretEmpty(): void + { + $appId = new Preference; + $appId->name = 'spectre_app_id'; + $appId->data = 'Some app id'; + + $secret = new Preference; + $secret->name = 'spectre_secret'; + $secret->data = ''; + // get App ID + Preferences::shouldReceive('getForUser')->once() + ->withArgs([Mockery::any(), 'spectre_app_id', null]) + ->andReturn($appId); + + // get secret + Preferences::shouldReceive('getForUser')->once() + ->withArgs([Mockery::any(), 'spectre_secret', null]) + ->andReturn($secret); + + $object = new SpectrePrerequisites; + $object->setUser($this->user()); + $this->assertFalse($object->isComplete()); + } + + /** + * + * @covers \FireflyIII\Import\Prerequisites\SpectrePrerequisites + */ + public function testStorePrerequisites(): void + { + $data = [ + 'app_id' => 'Some app ID', + 'secret' => 'Very secret!', + ]; + // set values + Preferences::shouldReceive('setForUser')->once() + ->withArgs([Mockery::any(), 'spectre_app_id', $data['app_id']]) + ->andReturn(new Preference()); + Preferences::shouldReceive('setForUser')->once() + ->withArgs([Mockery::any(), 'spectre_secret', $data['secret']]) + ->andReturn(new Preference()); + + $object = new SpectrePrerequisites; + $object->setUser($this->user()); + $this->assertEquals(0, $object->storePrerequisites($data)->count()); + } + +} \ No newline at end of file diff --git a/tests/Unit/Import/Routine/SpectreRoutineTest.php b/tests/Unit/Import/Routine/SpectreRoutineTest.php new file mode 100644 index 0000000000..f12b1a8762 --- /dev/null +++ b/tests/Unit/Import/Routine/SpectreRoutineTest.php @@ -0,0 +1,229 @@ +. + */ + +declare(strict_types=1); + +namespace Tests\Unit\Import\Routine; + + +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Import\Routine\SpectreRoutine; +use FireflyIII\Models\ImportJob; +use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; +use FireflyIII\Support\Import\Routine\Spectre\StageImportDataHandler; +use FireflyIII\Support\Import\Routine\Spectre\StageAuthenticatedHandler; +use FireflyIII\Support\Import\Routine\Spectre\StageNewHandler; +use Mockery; +use Tests\TestCase; + +/** + * Class SpectreRoutineTest + */ +class SpectreRoutineTest extends TestCase +{ + /** + * @covers \FireflyIII\Import\Routine\SpectreRoutine + */ + public function testRunAuthenticate(): void + { + $job = new ImportJob; + $job->user_id = $this->user()->id; + $job->key = 'SRA' . random_int(1, 1000); + $job->status = 'ready_to_run'; + $job->stage = 'authenticate'; + $job->provider = 'spectre'; + $job->file_type = ''; + $job->configuration = []; + $job->save(); + + // mock handler and repository + $repository = $this->mock(ImportJobRepositoryInterface::class); + + // mock calls for repository + $repository->shouldReceive('setUser')->once(); + $repository->shouldReceive('setStatus')->withArgs([Mockery::any(), 'need_job_config'])->once(); + + $routine = new SpectreRoutine; + $routine->setImportJob($job); + try { + $routine->run(); + } catch (FireflyException $e) { + $this->assertTrue(false, $e->getMessage()); + } + } + + /** + * @covers \FireflyIII\Import\Routine\SpectreRoutine + */ + public function testRunAuthenticated(): void + { + $job = new ImportJob; + $job->user_id = $this->user()->id; + $job->key = 'SRA' . random_int(1, 1000); + $job->status = 'ready_to_run'; + $job->stage = 'authenticated'; + $job->provider = 'spectre'; + $job->file_type = ''; + $job->configuration = []; + $job->save(); + + // mock handler and repository + $handler = $this->mock(StageAuthenticatedHandler::class); + $repository = $this->mock(ImportJobRepositoryInterface::class); + + // mock calls for repository + $repository->shouldReceive('setUser')->once(); + $repository->shouldReceive('setStatus')->withArgs([Mockery::any(), 'running'])->once(); + $repository->shouldReceive('setStatus')->withArgs([Mockery::any(), 'need_job_config'])->once(); + $repository->shouldReceive('setStage')->withArgs([Mockery::any(), 'choose-account'])->once(); + + // mock calls for handler + $handler->shouldReceive('setImportJob')->once(); + $handler->shouldReceive('run')->once(); + + $routine = new SpectreRoutine; + $routine->setImportJob($job); + try { + $routine->run(); + } catch (FireflyException $e) { + $this->assertTrue(false, $e->getMessage()); + } + } + + /** + * @covers \FireflyIII\Import\Routine\SpectreRoutine + */ + public function testRunGoImport(): void + { + $job = new ImportJob; + $job->user_id = $this->user()->id; + $job->key = 'SRA' . random_int(1, 1000); + $job->status = 'ready_to_run'; + $job->stage = 'go-for-import'; + $job->provider = 'spectre'; + $job->file_type = ''; + $job->configuration = []; + $job->save(); + + // mock handler and repository + $handler = $this->mock(StageImportDataHandler::class); + $repository = $this->mock(ImportJobRepositoryInterface::class); + + // mock calls for repository + $repository->shouldReceive('setUser')->once(); + $repository->shouldReceive('setStatus')->withArgs([Mockery::any(), 'running'])->once(); + $repository->shouldReceive('setStatus')->withArgs([Mockery::any(), 'provider_finished'])->once(); + $repository->shouldReceive('setStage')->withArgs([Mockery::any(), 'do_import'])->once(); + $repository->shouldReceive('setStage')->withArgs([Mockery::any(), 'final'])->once(); + + // mock calls for handler + $handler->shouldReceive('setImportJob')->once(); + $handler->shouldReceive('run')->once(); + + $routine = new SpectreRoutine; + $routine->setImportJob($job); + try { + $routine->run(); + } catch (FireflyException $e) { + $this->assertTrue(false, $e->getMessage()); + } + } + + /** + * @covers \FireflyIII\Import\Routine\SpectreRoutine + */ + public function testRunNewOneLogin(): void + { + $job = new ImportJob; + $job->user_id = $this->user()->id; + $job->key = 'SRA' . random_int(1, 1000); + $job->status = 'ready_to_run'; + $job->stage = 'new'; + $job->provider = 'spectre'; + $job->file_type = ''; + $job->configuration = []; + $job->save(); + + // mock handler and repository + $handler = $this->mock(StageNewHandler::class); + $repository = $this->mock(ImportJobRepositoryInterface::class); + + // mock calls for repository + $repository->shouldReceive('setUser')->once(); + $repository->shouldReceive('setStatus')->withArgs([Mockery::any(), 'running'])->once(); + $repository->shouldReceive('setStatus')->withArgs([Mockery::any(), 'need_job_config'])->once(); + $repository->shouldReceive('setStage')->withArgs([Mockery::any(), 'choose-login'])->once(); + + // mock calls for handler + $handler->shouldReceive('setImportJob')->once(); + $handler->shouldReceive('getCountLogins')->once()->andReturn(2); + $handler->shouldReceive('run')->once(); + + + $routine = new SpectreRoutine; + $routine->setImportJob($job); + try { + $routine->run(); + } catch (FireflyException $e) { + $this->assertTrue(false, $e->getMessage()); + } + } + + /** + * @covers \FireflyIII\Import\Routine\SpectreRoutine + */ + public function testRunNewZeroLogins(): void + { + $job = new ImportJob; + $job->user_id = $this->user()->id; + $job->key = 'SRA' . random_int(1, 1000); + $job->status = 'ready_to_run'; + $job->stage = 'new'; + $job->provider = 'spectre'; + $job->file_type = ''; + $job->configuration = []; + $job->save(); + + // mock handler and repository + $handler = $this->mock(StageNewHandler::class); + $repository = $this->mock(ImportJobRepositoryInterface::class); + + // mock calls for repository + $repository->shouldReceive('setUser')->once(); + $repository->shouldReceive('setStatus')->withArgs([Mockery::any(), 'running'])->once(); + $repository->shouldReceive('setStatus')->withArgs([Mockery::any(), 'ready_to_run'])->once(); + $repository->shouldReceive('setStage')->withArgs([Mockery::any(), 'authenticate'])->once(); + + // mock calls for handler + $handler->shouldReceive('setImportJob')->once(); + $handler->shouldReceive('getCountLogins')->once()->andReturn(0); + $handler->shouldReceive('run')->once(); + + + $routine = new SpectreRoutine; + $routine->setImportJob($job); + try { + $routine->run(); + } catch (FireflyException $e) { + $this->assertTrue(false, $e->getMessage()); + } + } +} \ No newline at end of file diff --git a/tests/Unit/Support/Import/Routine/Spectre/StageNewHandlerTest.php b/tests/Unit/Support/Import/Routine/Spectre/StageNewHandlerTest.php new file mode 100644 index 0000000000..0bdc1b9cf5 --- /dev/null +++ b/tests/Unit/Support/Import/Routine/Spectre/StageNewHandlerTest.php @@ -0,0 +1,80 @@ +. + */ + +declare(strict_types=1); + +namespace tests\Unit\Support\Import\Routine\Spectre; + + +use FireflyIII\Models\ImportJob; +use FireflyIII\Services\Spectre\Request\ListCustomersRequest; +use FireflyIII\Services\Spectre\Request\ListLoginsRequest; +use FireflyIII\Support\Import\Information\GetSpectreCustomerTrait; +use FireflyIII\Support\Import\Routine\Spectre\StageNewHandler; +use Tests\TestCase; +use Preferences; +/** + * Class StageNewHandlerTest + */ +class StageNewHandlerTest extends TestCase +{ + + // todo run() with zero logins and an existing customer (must be retrieved from Spectre). + // todo run() with one login and an existing customer (must be retrieved from Spectre). + + /** + * run() with zero logins and a non-existing customer (must be created by Spectre). + * + * @covers \FireflyIII\Support\Import\Routine\Spectre\StageNewHandler + */ + public function testRunBasic(): void + { + $job = new ImportJob; + $job->user_id = $this->user()->id; + $job->key = 'sn_a_' . random_int(1, 1000); + $job->status = 'new'; + $job->stage = 'new'; + $job->provider = 'spectre'; + $job->file_type = ''; + $job->configuration = []; + $job->save(); + + // mock classes: + $trait = $this->mock(GetSpectreCustomerTrait::class); + $llRequest = $this->mock(ListLoginsRequest::class); + $lcRequest = $this->mock(ListCustomersRequest::class); + + // mock calls for list logins + $llRequest->shouldReceive('setUser')->once(); + $llRequest->shouldReceive('setCustomer')->once(); + $llRequest->shouldReceive('call')->once(); + $llRequest->shouldReceive('getLogins')->once()->andReturn([]); + + // mock call for preferences + // todo here we are + Preferences::shouldReceive('getForUser'); + + + $handler = new StageNewHandler; + $handler->setImportJob($job); + $handler->run(); + } +} \ No newline at end of file