diff --git a/app/Support/Import/Configuration/File/ConfigureMappingHandler.php b/app/Support/Import/Configuration/File/ConfigureMappingHandler.php index ff7418dffa..5e5025c4e2 100644 --- a/app/Support/Import/Configuration/File/ConfigureMappingHandler.php +++ b/app/Support/Import/Configuration/File/ConfigureMappingHandler.php @@ -67,7 +67,7 @@ class ConfigureMappingHandler implements ConfigurationInterface $specifics = $config['specifics'] ?? []; $names = array_keys($specifics); foreach ($names as $name) { - if (!\in_array($name, $validSpecifics)) { + if (!\in_array($name, $validSpecifics, true)) { continue; } $class = config(sprintf('csv.import_specifics.%s', $name)); @@ -109,6 +109,26 @@ class ConfigureMappingHandler implements ConfigurationInterface return new MessageBag; } + /** + * Create the "mapper" class that will eventually return the correct data for the user + * to map against. For example: a list of asset accounts. A list of budgets. A list of tags. + * + * @param string $column + * + * @return MapperInterface + * @throws FireflyException + */ + public function createMapper(string $column): MapperInterface + { + $mapperClass = config('csv.import_roles.' . $column . '.mapper'); + $mapperName = sprintf('FireflyIII\\Import\Mapper\\%s', $mapperClass); + if (!class_exists($mapperName)) { + throw new FireflyException(sprintf('Class "%s" does not exist. Cannot map "%s"', $mapperName, $column)); // @codeCoverageIgnore + } + + return app($mapperName); + } + /** * For each column in the configuration of the job, will: * - validate the role. @@ -327,24 +347,4 @@ class ConfigureMappingHandler implements ConfigurationInterface $this->attachments = app(AttachmentHelperInterface::class); $this->columnConfig = []; } - - /** - * Create the "mapper" class that will eventually return the correct data for the user - * to map against. For example: a list of asset accounts. A list of budgets. A list of tags. - * - * @param string $column - * - * @return MapperInterface - * @throws FireflyException - */ - private function createMapper(string $column): MapperInterface - { - $mapperClass = config('csv.import_roles.' . $column . '.mapper'); - $mapperName = sprintf('FireflyIII\\Import\Mapper\\%s', $mapperClass); - if (!class_exists($mapperName)) { - throw new FireflyException(sprintf('Class "%s" does not exist. Cannot map "%s"', $mapperName, $column)); // @codeCoverageIgnore - } - - return app($mapperName); - } -} \ No newline at end of file +} diff --git a/app/Support/Import/Configuration/File/ConfigureRolesHandler.php b/app/Support/Import/Configuration/File/ConfigureRolesHandler.php index caaf42681c..e857d32c16 100644 --- a/app/Support/Import/Configuration/File/ConfigureRolesHandler.php +++ b/app/Support/Import/Configuration/File/ConfigureRolesHandler.php @@ -52,80 +52,6 @@ class ConfigureRolesHandler implements ConfigurationInterface /** @var int */ private $totalColumns; - /** - * Store data associated with current stage. - * - * @param array $data - * - * @return MessageBag - */ - public function configureJob(array $data): MessageBag - { - $config = $this->importJob->configuration; - $count = $config['column-count']; - for ($i = 0; $i < $count; ++$i) { - $role = $data['role'][$i] ?? '_ignore'; - $mapping = (isset($data['map'][$i]) && $data['map'][$i] === '1'); - $config['column-roles'][$i] = $role; - $config['column-do-mapping'][$i] = $mapping; - Log::debug(sprintf('Column %d has been given role %s (mapping: %s)', $i, $role, var_export($mapping, true))); - } - $config = $this->ignoreUnmappableColumns($config); - $messages = $this->configurationComplete($config); - - if ($messages->count() === 0) { - $this->repository->setStage($this->importJob, 'ready_to_run'); - if ($this->isMappingNecessary($config)) { - $this->repository->setStage($this->importJob, 'map'); - } - $this->repository->setConfiguration($this->importJob, $config); - } - - return $messages; - } - - /** - * Get the data necessary to show the configuration screen. - * - * @return array - * @throws FireflyException - */ - public function getNextData(): array - { - try { - $reader = $this->getReader(); - } catch (Exception $e) { - Log::error($e->getMessage()); - throw new FireflyException($e->getMessage()); - } - $headers = $this->getHeaders($reader); - - // get example rows: - $this->getExamples($reader); - - return [ - 'examples' => $this->examples, - 'roles' => $this->getRoles(), - 'total' => $this->totalColumns, - 'headers' => $headers, - ]; - } - - /** - * Set job and some start values. - * - * @param ImportJob $job - */ - public function setJob(ImportJob $job): void - { - $this->importJob = $job; - $this->repository = app(ImportJobRepositoryInterface::class); - $this->repository->setUser($job->user); - $this->attachments = app(AttachmentHelperInterface::class); - $this->totalColumns = 0; - $this->examples = []; - } - /** * Verifies that the configuration of the job is actually complete, and valid. * @@ -133,8 +59,10 @@ class ConfigureRolesHandler implements ConfigurationInterface * * @return MessageBag */ - private function configurationComplete(array $config): MessageBag + public function configurationComplete(array $config): MessageBag { + /** @var array $roles */ + $roles = $config['column-roles']; $count = $config['column-count']; $assigned = 0; @@ -142,8 +70,7 @@ class ConfigureRolesHandler implements ConfigurationInterface $hasAmount = false; $hasForeignAmount = false; $hasForeignCode = false; - for ($i = 0; $i < $count; ++$i) { - $role = $config['column-roles'][$i] ?? '_ignore'; + foreach ($roles as $role) { if ('_ignore' !== $role) { ++$assigned; } @@ -180,7 +107,39 @@ class ConfigureRolesHandler implements ConfigurationInterface } - return new MessageBag; + return new MessageBag; // @codeCoverageIgnore + } + + /** + * Store data associated with current stage. + * + * @param array $data + * + * @return MessageBag + */ + public function configureJob(array $data): MessageBag + { + $config = $this->importJob->configuration; + $count = $config['column-count']; + for ($i = 0; $i < $count; ++$i) { + $role = $data['role'][$i] ?? '_ignore'; + $mapping = (isset($data['map'][$i]) && $data['map'][$i] === '1'); + $config['column-roles'][$i] = $role; + $config['column-do-mapping'][$i] = $mapping; + Log::debug(sprintf('Column %d has been given role %s (mapping: %s)', $i, $role, var_export($mapping, true))); + } + $config = $this->ignoreUnmappableColumns($config); + $messages = $this->configurationComplete($config); + + if ($messages->count() === 0) { + $this->repository->setStage($this->importJob, 'ready_to_run'); + if ($this->isMappingNecessary($config)) { + $this->repository->setStage($this->importJob, 'map'); + } + $this->repository->setConfiguration($this->importJob, $config); + } + + return $messages; } /** @@ -188,7 +147,7 @@ class ConfigureRolesHandler implements ConfigurationInterface * * @param array $line */ - private function getExampleFromLine(array $line): void + public function getExampleFromLine(array $line): void { foreach ($line as $column => $value) { $value = trim($value); @@ -198,34 +157,43 @@ class ConfigureRolesHandler implements ConfigurationInterface } } + /** + * @return array + */ + public function getExamples(): array + { + return $this->examples; + } + /** * Return a bunch of examples from the CSV file the user has uploaded. * * @param Reader $reader + * @param array $config * * @throws FireflyException */ - private function getExamples(Reader $reader): void + public function getExamplesFromFile(Reader $reader, array $config): void { - // configure example data: - $config = $this->importJob->configuration; $limit = (int)config('csv.example_rows', 5); $offset = isset($config['has-headers']) && $config['has-headers'] === true ? 1 : 0; // make statement. try { $stmt = (new Statement)->limit($limit)->offset($offset); + // @codeCoverageIgnoreStart } catch (Exception $e) { Log::error($e->getMessage()); throw new FireflyException($e->getMessage()); } + // @codeCoverageIgnoreEnd // grab the records: $records = $stmt->process($reader); /** @var array $line */ foreach ($records as $line) { $line = array_values($line); - $line = $this->processSpecifics($line); + $line = $this->processSpecifics($config, $line); $count = \count($line); $this->totalColumns = $count > $this->totalColumns ? $count : $this->totalColumns; $this->getExampleFromLine($line); @@ -239,39 +207,71 @@ class ConfigureRolesHandler implements ConfigurationInterface * Get the header row, if one is present. * * @param Reader $reader + * @param array $config * * @return array * @throws FireflyException */ - private function getHeaders(Reader $reader): array + public function getHeaders(Reader $reader, array $config): array { $headers = []; - $config = $this->importJob->configuration; - if ($config['has-headers']) { + if (isset($config['has-headers']) && $config['has-headers'] === true) { try { $stmt = (new Statement)->limit(1)->offset(0); $records = $stmt->process($reader); $headers = $records->fetchOne(0); + // @codeCoverageIgnoreStart } catch (Exception $e) { Log::error($e->getMessage()); throw new FireflyException($e->getMessage()); } + // @codeCoverageIgnoreEnd Log::debug('Detected file headers:', $headers); } return $headers; } + /** + * Get the data necessary to show the configuration screen. + * + * @return array + * @throws FireflyException + */ + public function getNextData(): array + { + try { + $reader = $this->getReader(); + // @codeCoverageIgnoreStart + } catch (Exception $e) { + Log::error($e->getMessage()); + throw new FireflyException($e->getMessage()); + } + // @codeCoverageIgnoreEnd + $configuration = $this->importJob->configuration; + $headers = $this->getHeaders($reader, $configuration); + + // get example rows: + $this->getExamplesFromFile($reader, $configuration); + + return [ + 'examples' => $this->examples, + 'roles' => $this->getRoles(), + 'total' => $this->totalColumns, + 'headers' => $headers, + ]; + } + /** * Return an instance of a CSV file reader so content of the file can be read. * * @throws \League\Csv\Exception */ - private function getReader(): Reader + public function getReader(): Reader { $content = ''; /** @var Collection $collection */ - $collection = $this->importJob->attachments; + $collection = $this->repository->getAttachments($this->importJob); /** @var Attachment $attachment */ foreach ($collection as $attachment) { if ($attachment->filename === 'import_file') { @@ -289,9 +289,10 @@ class ConfigureRolesHandler implements ConfigurationInterface /** * Returns all possible roles and translate their name. Then sort them. * + * @codeCoverageIgnore * @return array */ - private function getRoles(): array + public function getRoles(): array { $roles = []; foreach (array_keys(config('csv.import_roles')) as $role) { @@ -310,7 +311,7 @@ class ConfigureRolesHandler implements ConfigurationInterface * * @return array */ - private function ignoreUnmappableColumns(array $config): array + public function ignoreUnmappableColumns(array $config): array { $count = $config['column-count']; for ($i = 0; $i < $count; ++$i) { @@ -333,13 +334,13 @@ class ConfigureRolesHandler implements ConfigurationInterface * * @return bool */ - private function isMappingNecessary(array $config): bool + public function isMappingNecessary(array $config): bool { - $count = $config['column-count']; + /** @var array $doMapping */ + $doMapping = $config['column-do-mapping'] ?? []; $toBeMapped = 0; - for ($i = 0; $i < $count; ++$i) { - $mapping = $config['column-do-mapping'][$i] ?? false; - if (true === $mapping) { + foreach ($doMapping as $doMap) { + if (true === $doMap) { ++$toBeMapped; } } @@ -350,7 +351,7 @@ class ConfigureRolesHandler implements ConfigurationInterface /** * Make sure that the examples do not contain double data values. */ - private function makeExamplesUnique(): void + public function makeExamplesUnique(): void { foreach ($this->examples as $index => $values) { $this->examples[$index] = array_unique($values); @@ -360,16 +361,20 @@ class ConfigureRolesHandler implements ConfigurationInterface /** * if the user has configured specific fixes to be applied, they must be applied to the example data as well. * + * @param array $config * @param array $line * * @return array */ - private function processSpecifics(array $line): array + public function processSpecifics(array $config, array $line): array { - $config = $this->importJob->configuration; - $specifics = $config['specifics'] ?? []; - $names = array_keys($specifics); + $validSpecifics = array_keys(config('csv.import_specifics')); + $specifics = $config['specifics'] ?? []; + $names = array_keys($specifics); foreach ($names as $name) { + if (!\in_array($name, $validSpecifics, true)) { + continue; + } /** @var SpecificInterface $specific */ $specific = app('FireflyIII\Import\Specifics\\' . $name); $line = $specific->run($line); @@ -381,13 +386,29 @@ class ConfigureRolesHandler implements ConfigurationInterface /** * Save the column count in the job. It's used in a later stage. + * TODO move config out of this method (make it a parameter). * * @return void */ - private function saveColumCount(): void + public function saveColumCount(): void { $config = $this->importJob->configuration; $config['column-count'] = $this->totalColumns; $this->repository->setConfiguration($this->importJob, $config); } -} \ No newline at end of file + + /** + * Set job and some start values. + * + * @param ImportJob $job + */ + public function setJob(ImportJob $job): void + { + $this->importJob = $job; + $this->repository = app(ImportJobRepositoryInterface::class); + $this->repository->setUser($job->user); + $this->attachments = app(AttachmentHelperInterface::class); + $this->totalColumns = 0; + $this->examples = []; + } +} diff --git a/app/Support/Import/Configuration/File/ConfigureUploadHandler.php b/app/Support/Import/Configuration/File/ConfigureUploadHandler.php index 7fd36b8204..553423aa07 100644 --- a/app/Support/Import/Configuration/File/ConfigureUploadHandler.php +++ b/app/Support/Import/Configuration/File/ConfigureUploadHandler.php @@ -145,7 +145,7 @@ class ConfigureUploadHandler implements ConfigurationInterface * * @return array */ - private function getSpecifics(array $data): array + public function getSpecifics(array $data): array { $return = []; // check if specifics given are correct: @@ -153,7 +153,7 @@ class ConfigureUploadHandler implements ConfigurationInterface foreach ($data['specifics'] as $name) { // verify their content. - $className = sprintf('FireflyIII\Import\Specifics\%s', $name); + $className = sprintf('FireflyIII\\Import\\Specifics\\%s', $name); if (class_exists($className)) { $return[$name] = 1; } @@ -162,4 +162,4 @@ class ConfigureUploadHandler implements ConfigurationInterface return $return; } -} \ No newline at end of file +} diff --git a/app/Support/Import/Configuration/File/NewFileJobHandler.php b/app/Support/Import/Configuration/File/NewFileJobHandler.php index a6f42e9d2f..f3ebcc61fe 100644 --- a/app/Support/Import/Configuration/File/NewFileJobHandler.php +++ b/app/Support/Import/Configuration/File/NewFileJobHandler.php @@ -219,4 +219,4 @@ class NewFileJobHandler implements ConfigurationInterface return true; } -} \ No newline at end of file +} diff --git a/app/Support/Import/Placeholder/ColumnValue.php b/app/Support/Import/Placeholder/ColumnValue.php index 238a9687bc..151fcf0695 100644 --- a/app/Support/Import/Placeholder/ColumnValue.php +++ b/app/Support/Import/Placeholder/ColumnValue.php @@ -110,4 +110,4 @@ class ColumnValue } -} \ No newline at end of file +} diff --git a/app/Support/Import/Placeholder/ImportTransaction.php b/app/Support/Import/Placeholder/ImportTransaction.php index 7b059695bc..da40907a9c 100644 --- a/app/Support/Import/Placeholder/ImportTransaction.php +++ b/app/Support/Import/Placeholder/ImportTransaction.php @@ -300,7 +300,7 @@ class ImportTransaction * @var string $modifier */ foreach ($this->modifiers as $role => $modifier) { - $class = sprintf('FireflyIII\Import\Converter\%s', config(sprintf('csv.import_roles.%s.converter', $role))); + $class = sprintf('FireflyIII\\Import\\Converter\\%s', config(sprintf('csv.import_roles.%s.converter', $role))); /** @var ConverterInterface $converter */ $converter = app($class); Log::debug(sprintf('Now launching converter %s', $class)); @@ -342,7 +342,7 @@ class ImportTransaction * @var string $modifier */ foreach ($this->modifiers as $role => $modifier) { - $class = sprintf('FireflyIII\Import\Converter\%s', config(sprintf('csv.import_roles.%s.converter', $role))); + $class = sprintf('FireflyIII\\Import\\Converter\\%s', config(sprintf('csv.import_roles.%s.converter', $role))); /** @var ConverterInterface $converter */ $converter = app($class); Log::debug(sprintf('Now launching converter %s', $class)); @@ -565,4 +565,4 @@ class ImportTransaction return $info; } -} \ No newline at end of file +} diff --git a/app/Support/Import/Routine/File/CSVProcessor.php b/app/Support/Import/Routine/File/CSVProcessor.php index f0edce71e2..ec924af90d 100644 --- a/app/Support/Import/Routine/File/CSVProcessor.php +++ b/app/Support/Import/Routine/File/CSVProcessor.php @@ -736,4 +736,4 @@ class CSVProcessor implements FileProcessorInterface return null; } -} \ No newline at end of file +} diff --git a/app/Support/Import/Routine/File/FileProcessorInterface.php b/app/Support/Import/Routine/File/FileProcessorInterface.php index 6dd51883db..7aa49a8f20 100644 --- a/app/Support/Import/Routine/File/FileProcessorInterface.php +++ b/app/Support/Import/Routine/File/FileProcessorInterface.php @@ -46,4 +46,4 @@ interface FileProcessorInterface * @param ImportJob $job */ public function setJob(ImportJob $job): void; -} \ No newline at end of file +} diff --git a/resources/views/import/file/configure-upload.twig b/resources/views/import/file/configure-upload.twig index f5d4d59e25..59de0c8a74 100644 --- a/resources/views/import/file/configure-upload.twig +++ b/resources/views/import/file/configure-upload.twig @@ -112,4 +112,4 @@ -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/tests/Unit/Import/JobConfiguration/FileJobConfigurationTest.php b/tests/Unit/Import/JobConfiguration/FileJobConfigurationTest.php index 12e90c7f2a..ca7af58510 100644 --- a/tests/Unit/Import/JobConfiguration/FileJobConfigurationTest.php +++ b/tests/Unit/Import/JobConfiguration/FileJobConfigurationTest.php @@ -364,4 +364,4 @@ class FileJobConfigurationTest extends TestCase } $this->assertEquals('import.file.roles', $result); } -} \ No newline at end of file +} diff --git a/tests/Unit/Import/Routine/FileRoutineTest.php b/tests/Unit/Import/Routine/FileRoutineTest.php index 3d48bf98c2..90056bc034 100644 --- a/tests/Unit/Import/Routine/FileRoutineTest.php +++ b/tests/Unit/Import/Routine/FileRoutineTest.php @@ -75,4 +75,4 @@ class FileRoutineTest extends TestCase $this->assertTrue(false, $e->getMessage()); } } -} \ No newline at end of file +} diff --git a/tests/Unit/Support/Import/Configuration/File/ConfigureMappingHandlerTest.php b/tests/Unit/Support/Import/Configuration/File/ConfigureMappingHandlerTest.php index 394fc5bc51..558c4421b2 100644 --- a/tests/Unit/Support/Import/Configuration/File/ConfigureMappingHandlerTest.php +++ b/tests/Unit/Support/Import/Configuration/File/ConfigureMappingHandlerTest.php @@ -485,4 +485,4 @@ class ConfigureMappingHandlerTest extends TestCase $this->assertEquals('_ignore', $handler->sanitizeColumnName('some-bad-name')); } -} \ No newline at end of file +} diff --git a/tests/Unit/Support/Import/Configuration/File/ConfigureRolesHandlerTest.php b/tests/Unit/Support/Import/Configuration/File/ConfigureRolesHandlerTest.php new file mode 100644 index 0000000000..1a190867f4 --- /dev/null +++ b/tests/Unit/Support/Import/Configuration/File/ConfigureRolesHandlerTest.php @@ -0,0 +1,525 @@ +. + */ + +declare(strict_types=1); + +namespace Tests\Unit\Support\Import\Configuration\File; + + +use Exception; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Helpers\Attachments\AttachmentHelperInterface; +use FireflyIII\Import\Specifics\IngDescription; +use FireflyIII\Models\Attachment; +use FireflyIII\Models\ImportJob; +use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; +use FireflyIII\Support\Import\Configuration\File\ConfigureRolesHandler; +use Illuminate\Support\Collection; +use League\Csv\Reader; +use Mockery; +use Tests\TestCase; + +/** + * Class ConfigureRolesHandlerTest + */ +class ConfigureRolesHandlerTest extends TestCase +{ + /** + * @covers \FireflyIII\Support\Import\Configuration\File\ConfigureRolesHandler + */ + public function testConfigurationCompleteBasic(): void + { + $config = [ + 'column-count' => 5, + 'column-roles' => [ + 0 => 'amount', + 1 => 'description', + 2 => 'note', + 3 => 'foreign-currency-code', + 4 => 'amount_foreign', + ], + ]; + $handler = new ConfigureRolesHandler(); + $result = $handler->configurationComplete($config); + $this->assertCount(0, $result); + } + + /** + * @covers \FireflyIII\Support\Import\Configuration\File\ConfigureRolesHandler + */ + public function testConfigurationCompleteForeign(): void + { + $config = [ + 'column-count' => 5, + 'column-roles' => [ + 0 => 'amount', + 1 => 'description', + 2 => 'note', + 3 => 'amount_foreign', + 4 => 'sepa-cc', + ], + ]; + $handler = new ConfigureRolesHandler(); + $result = $handler->configurationComplete($config); + $this->assertCount(1, $result); + $this->assertEquals( + 'If you mark a column as containing an amount in a foreign currency, you must also set the column that contains which currency it is.', + $result->get('error')[0] + ); + } + + /** + * @covers \FireflyIII\Support\Import\Configuration\File\ConfigureRolesHandler + */ + public function testConfigurationCompleteNoAmount(): void + { + $config = [ + 'column-count' => 5, + 'column-roles' => [ + 0 => 'sepa-cc', + 1 => 'description', + 2 => 'note', + 3 => 'foreign-currency-code', + 4 => 'amount_foreign', + ], + ]; + $handler = new ConfigureRolesHandler(); + $result = $handler->configurationComplete($config); + $this->assertCount(1, $result); + $this->assertEquals( + 'At the very least, mark one column as the amount-column. It is advisable to also select a column for the description, date and the opposing account.', + $result->get('error')[0] + ); + } + + /** + * @covers \FireflyIII\Support\Import\Configuration\File\ConfigureRolesHandler + */ + public function testConfigureJob(): void + { + $job = new ImportJob; + $job->user_id = $this->user()->id; + $job->key = 'role-B' . random_int(1, 1000); + $job->status = 'new'; + $job->stage = 'new'; + $job->provider = 'fake'; + $job->file_type = ''; + $job->configuration = [ + 'column-count' => 5, + ]; + $job->save(); + + $data = [ + 'role' => [ + 0 => 'description', + 1 => 'budget-id', + 2 => 'sepa-cc', + 4 => 'amount', // no column 3. + ], + 'map' => [ + 0 => '1', // map column 0 (which cannot be mapped anyway) + 1 => '1', // map column 1 (which CAN be mapped) + ], + ]; + + $expected = [ + 'column-count' => 5, + 'column-roles' => [ + 0 => 'description', + 1 => 'budget-id', + 2 => 'sepa-cc', + 3 => '_ignore', // added column 3 + 4 => 'amount', + ], + 'column-do-mapping' => [false, true, false, false, false], + ]; + + $repository = $this->mock(ImportJobRepositoryInterface::class); + $repository->shouldReceive('setUser')->once(); + $repository->shouldReceive('setStage')->once()->withArgs([Mockery::any(), 'ready_to_run']); + $repository->shouldReceive('setStage')->once()->withArgs([Mockery::any(), 'map']); + $repository->shouldReceive('setConfiguration')->once()->withArgs([Mockery::any(), $expected]); + + $handler = new ConfigureRolesHandler(); + $handler->setJob($job); + $handler->configureJob($data); + } + + /** + * @covers \FireflyIII\Support\Import\Configuration\File\ConfigureRolesHandler + */ + public function testGetExampleFromLine(): void + { + + $lines = [ + ['one', 'two', '', 'three'], + ['four', 'five', '', 'six'], + ]; + + $handler = new ConfigureRolesHandler; + foreach ($lines as $line) { + $handler->getExampleFromLine($line); + } + $expected = [ + 0 => ['one', 'four'], + 1 => ['two', 'five'], + 3 => ['three', 'six'], + ]; + $this->assertEquals($expected, $handler->getExamples()); + } + + /** + * @covers \FireflyIII\Support\Import\Configuration\File\ConfigureRolesHandler + */ + public function testGetExamplesFromFile(): void + { + $job = new ImportJob; + $job->user_id = $this->user()->id; + $job->key = 'role-x' . random_int(1, 1000); + $job->status = 'new'; + $job->stage = 'new'; + $job->provider = 'fake'; + $job->file_type = ''; + $job->configuration = [ + 'specifics' => [], + 'has-headers' => false, + ]; + $job->save(); + + $file = "one,two,,three\nfour,five,,six\none,three,X,three"; + $reader = Reader::createFromString($file); + $handler = new ConfigureRolesHandler; + $handler->setJob($job); + try { + $handler->getExamplesFromFile($reader, $job->configuration); + } catch (Exception $e) { + $this->assertTrue(false, $e->getMessage()); + } + + $expected = [ + 0 => ['one', 'four'], + 1 => ['two', 'five', 'three'], + 2 => ['X'], + 3 => ['three', 'six'], + ]; + $this->assertEquals($expected, $handler->getExamples()); + + } + + /** + * @covers \FireflyIII\Support\Import\Configuration\File\ConfigureRolesHandler + */ + public function testGetHeadersHas(): void + { + // create a reader to use in method. + // 5 columns, of which #4 (index 3) is budget-id + // 5 columns, of which #5 (index 4) is tags-space + $file = "header1,header2,header3,header4,header5\nvalue4,value5,value6,2,more tags there\nvalueX,valueY,valueZ\nA,B,C,,\nd,e,f,1,xxx"; + $reader = Reader::createFromString($file); + $config = ['has-headers' => true]; + + $handler = new ConfigureRolesHandler; + try { + $headers = $handler->getHeaders($reader, $config); + } catch (FireflyException $e) { + $this->assertTrue(false, $e->getMessage()); + } + $this->assertEquals(['header1', 'header2', 'header3', 'header4', 'header5'], $headers); + } + + /** + * @covers \FireflyIII\Support\Import\Configuration\File\ConfigureRolesHandler + */ + public function testGetHeadersNone(): void + { + // create a reader to use in method. + // 5 columns, of which #4 (index 3) is budget-id + // 5 columns, of which #5 (index 4) is tags-space + $file = "header1,header2,header3,header4,header5\nvalue4,value5,value6,2,more tags there\nvalueX,valueY,valueZ\nA,B,C,,\nd,e,f,1,xxx"; + $reader = Reader::createFromString($file); + $config = ['has-headers' => false]; + + $handler = new ConfigureRolesHandler; + try { + $headers = $handler->getHeaders($reader, $config); + } catch (FireflyException $e) { + $this->assertTrue(false, $e->getMessage()); + } + $this->assertEquals([], $headers); + } + + public function testGetNextData(): void + { + $job = new ImportJob; + $job->user_id = $this->user()->id; + $job->key = 'role-x' . random_int(1, 1000); + $job->status = 'new'; + $job->stage = 'new'; + $job->provider = 'fake'; + $job->file_type = ''; + $job->configuration = [ + 'delimiter' => ',', + 'has-headers' => true, + ]; + $job->save(); + + // make one attachment. + $att = new Attachment; + $att->filename = 'import_file'; + $att->user_id = $this->user()->id; + $att->attachable_id = $job->id; + $att->attachable_type = Attachment::class; + $att->md5 = md5('hello'); + $att->mime = 'fake'; + $att->size = 3; + $att->save(); + + $fileContent = "column1,column2,column3\nvalue1,value2,value3"; + // mock some helpers: + $attachments = $this->mock(AttachmentHelperInterface::class); + $repository = $this->mock(ImportJobRepositoryInterface::class); + $repository->shouldReceive('getConfiguration')->once()->withArgs([Mockery::any()])->andReturn($job->configuration); + $repository->shouldReceive('setConfiguration')->once()->withArgs( + [Mockery::any(), + [ + 'delimiter' => ',', + 'has-headers' => true, + 'column-count' => 3, + ], + ] + ); + $repository->shouldReceive('setUser')->once(); + $repository->shouldReceive('getAttachments')->once()->withArgs([Mockery::any()])->andReturn(new Collection([$att])); + $attachments->shouldReceive('getAttachmentContent')->withArgs([Mockery::any()])->andReturn($fileContent); + + $expected = [ + 'examples' => [ + 0 => ['value1'], + 1 => ['value2'], + 2 => ['value3'], + ], + 'total' => 3, + 'headers' => ['column1', 'column2', 'column3'], + ]; + + $handler = new ConfigureRolesHandler(); + $handler->setJob($job); + try { + $result = $handler->getNextData(); + } catch (Exception $e) { + $this->assertTrue(false, $e->getMessage()); + } + $this->assertEquals($expected['examples'], $result['examples']); + $this->assertEquals($expected['total'], $result['total']); + $this->assertEquals($expected['headers'], $result['headers']); + + } + + /** + * @covers \FireflyIII\Support\Import\Configuration\File\ConfigureRolesHandler + */ + public function testGetReader(): void + { + $job = new ImportJob; + $job->user_id = $this->user()->id; + $job->key = 'role-x' . random_int(1, 1000); + $job->status = 'new'; + $job->stage = 'new'; + $job->provider = 'fake'; + $job->file_type = ''; + $job->configuration = []; + $job->save(); + + // make one attachment. + $att = new Attachment; + $att->filename = 'import_file'; + $att->user_id = $this->user()->id; + $att->attachable_id = $job->id; + $att->attachable_type = Attachment::class; + $att->md5 = md5('hello'); + $att->mime = 'fake'; + $att->size = 3; + $att->save(); + $config = [ + 'delimiter' => ',', + ]; + + $fileContent = "column1,column2,column3\nvalue1,value2,value3"; + + // mock some helpers: + $attachments = $this->mock(AttachmentHelperInterface::class); + $repository = $this->mock(ImportJobRepositoryInterface::class); + $repository->shouldReceive('getConfiguration')->once()->withArgs([Mockery::any()])->andReturn($config); + $repository->shouldReceive('setUser')->once(); + $repository->shouldReceive('getAttachments')->once()->withArgs([Mockery::any()])->andReturn(new Collection([$att])); + $attachments->shouldReceive('getAttachmentContent')->withArgs([Mockery::any()])->andReturn($fileContent); + + $handler = new ConfigureRolesHandler(); + $handler->setJob($job); + try { + $reader = $handler->getReader(); + } catch (Exception $e) { + $this->assertTrue(false, $e->getMessage()); + } + } + + /** + * @covers \FireflyIII\Support\Import\Configuration\File\ConfigureRolesHandler + */ + public function testIgnoreUnmappableColumns(): void + { + $config = [ + 'column-count' => 5, + 'column-roles' => [ + 'description', // cannot be mapped. + 'budget-id', + 'sepa-cc', // cannot be mapped. + 'category-id', + 'tags-comma', // cannot be mapped. + ], + 'column-do-mapping' => [ + 0 => true, + 1 => true, + 2 => true, + 3 => true, + 4 => true, + ], + ]; + $expected = [ + 'column-count' => 5, + 'column-roles' => [ + 'description', // cannot be mapped. + 'budget-id', + 'sepa-cc', // cannot be mapped. + 'category-id', + 'tags-comma', // cannot be mapped. + ], + 'column-do-mapping' => [ + 0 => false, + 1 => true, + 2 => false, + 3 => true, + 4 => false, + ], + ]; + $handler = new ConfigureRolesHandler; + $this->assertEquals($expected, $handler->ignoreUnmappableColumns($config)); + } + + /** + * @covers \FireflyIII\Support\Import\Configuration\File\ConfigureRolesHandler + */ + public function testIsMappingNecessaryNo(): void + { + $config = [ + 'column-do-mapping' => [false, false, false], + ]; + $handler = new ConfigureRolesHandler(); + $result = $handler->isMappingNecessary($config); + $this->assertFalse($result); + } + + /** + * @covers \FireflyIII\Support\Import\Configuration\File\ConfigureRolesHandler + */ + public function testIsMappingNecessaryYes(): void + { + $config = [ + 'column-do-mapping' => [false, true, false, false], + ]; + $handler = new ConfigureRolesHandler(); + $result = $handler->isMappingNecessary($config); + $this->assertTrue($result); + } + + /** + * @covers \FireflyIII\Support\Import\Configuration\File\ConfigureRolesHandler + */ + public function testMakeExamplesUnique(): void + { + $lines = [ + ['one', 'two', '', 'three'], + ['four', 'five', '', 'six'], + ['one', 'three', 'X', 'three'], + ]; + + $handler = new ConfigureRolesHandler; + foreach ($lines as $line) { + $handler->getExampleFromLine($line); + } + $handler->makeExamplesUnique(); + + $expected = [ + 0 => ['one', 'four'], + 1 => ['two', 'five', 'three'], + 2 => ['X'], + 3 => ['three', 'six'], + ]; + $this->assertEquals($expected, $handler->getExamples()); + } + + /** + * @covers \FireflyIII\Support\Import\Configuration\File\ConfigureRolesHandler + */ + public function testProcessSpecifics(): void + { + $line = []; + $config = [ + 'specifics' => [ + 'IngDescription' => true, + 'some-bad-specific' => true, + ], + ]; + + $ingDescription = $this->mock(IngDescription::class); + $ingDescription->shouldReceive('run')->once()->withArgs([[]])->andReturn(['a' => 'b']); + + $handler = new ConfigureRolesHandler; + $this->assertEquals(['a' => 'b'], $handler->processSpecifics($config, [])); + + } + + /** + * @covers \FireflyIII\Support\Import\Configuration\File\ConfigureRolesHandler + */ + public function testSaveColumCount(): void + { + $job = new ImportJob; + $job->user_id = $this->user()->id; + $job->key = 'role-A' . random_int(1, 1000); + $job->status = 'new'; + $job->stage = 'new'; + $job->provider = 'fake'; + $job->file_type = ''; + $job->configuration = []; + $job->save(); + + $repository = $this->mock(ImportJobRepositoryInterface::class); + $repository->shouldReceive('setUser'); + $repository->shouldReceive('setConfiguration')->once() + ->withArgs([Mockery::any(), ['column-count' => 0]]); + + $handler = new ConfigureRolesHandler(); + $handler->setJob($job); + $handler->saveColumCount(); + } + +}