diff --git a/app/Http/Controllers/Chart/CategoryController.php b/app/Http/Controllers/Chart/CategoryController.php index abd7a44e09..403eec24f3 100644 --- a/app/Http/Controllers/Chart/CategoryController.php +++ b/app/Http/Controllers/Chart/CategoryController.php @@ -65,27 +65,29 @@ class CategoryController extends Controller /** * Show an overview for a category for all time, per month/week/year. + * TODO test method, for category refactor. * - * @param CategoryRepositoryInterface $repository - * @param Category $category + * @param Category $category * * @return JsonResponse */ - public function all(CategoryRepositoryInterface $repository, Category $category): JsonResponse + public function all(Category $category): JsonResponse { // cache results: $cache = new CacheProperties; $cache->addProperty('chart.category.all'); $cache->addProperty($category->id); if ($cache->has()) { - // return response()->json($cache->get()); // @codeCoverageIgnore + return response()->json($cache->get()); // @codeCoverageIgnore } - $start = $repository->firstUseDate($category) ?? $this->getDate(); - $range = app('preferences')->get('viewRange', '1M')->data; - $start = app('navigation')->startOfPeriod($start, $range); - $end = $this->getDate(); + /** @var CategoryRepositoryInterface $repository */ + $repository = app(CategoryRepositoryInterface::class); + $start = $repository->firstUseDate($category) ?? $this->getDate(); + $range = app('preferences')->get('viewRange', '1M')->data; + $start = app('navigation')->startOfPeriod($start, $range); + $end = $this->getDate(); - Log::debug(sprintf('Full range is %s to %s', $start->format('Y-m-d'), $end->format('Y-m-d'))); + //Log::debug(sprintf('Full range is %s to %s', $start->format('Y-m-d'), $end->format('Y-m-d'))); /** @var WholePeriodChartGenerator $generator */ $generator = app(WholePeriodChartGenerator::class); @@ -99,6 +101,7 @@ class CategoryController extends Controller /** * Shows the category chart on the front page. + * TODO test method, for category refactor. * * @return JsonResponse */ @@ -112,7 +115,7 @@ class CategoryController extends Controller $cache->addProperty($end); $cache->addProperty('chart.category.frontpage'); if ($cache->has()) { - // return response()->json($cache->get()); // @codeCoverageIgnore + return response()->json($cache->get()); // @codeCoverageIgnore } // currency repos: @@ -207,6 +210,7 @@ class CategoryController extends Controller /** * Chart report. + * TODO test method, for category refactor. * * @param Category $category * @param Collection $accounts @@ -224,7 +228,7 @@ class CategoryController extends Controller $cache->addProperty($accounts->pluck('id')->toArray()); $cache->addProperty($category); if ($cache->has()) { - // return response()->json($cache->get());// @codeCoverageIgnore + return response()->json($cache->get());// @codeCoverageIgnore } /** @var OperationsRepositoryInterface $opsRepository */ @@ -272,12 +276,14 @@ class CategoryController extends Controller $outSet = $expenses[$currencyId]['categories'][$category->id] ?? ['transaction_journals' => []]; foreach ($outSet['transaction_journals'] as $journal) { $date = $journal['date']->formatLocalized($format); + $chartData[$outKey]['entries'][$date] = $chartData[$outKey]['entries'][$date] ?? '0'; $chartData[$outKey]['entries'][$date] = bcadd($journal['amount'], $chartData[$outKey]['entries'][$date]); } $inSet = $income[$currencyId]['categories'][$category->id] ?? ['transaction_journals' => []]; foreach ($inSet['transaction_journals'] as $journal) { $date = $journal['date']->formatLocalized($format); + $chartData[$inKey]['entries'][$date] = $chartData[$inKey]['entries'][$date] ?? '0'; $chartData[$inKey]['entries'][$date] = bcadd($journal['amount'], $chartData[$inKey]['entries'][$date]); } } @@ -291,6 +297,7 @@ class CategoryController extends Controller /** * Chart for period for transactions without a category. + * TODO test me. * * @param Collection $accounts * @param Carbon $start @@ -352,12 +359,14 @@ class CategoryController extends Controller $outSet = $expenses[$currencyId] ?? ['transaction_journals' => []]; foreach ($outSet['transaction_journals'] as $journal) { $date = $journal['date']->formatLocalized($format); + $chartData[$outKey]['entries'][$date] = $chartData[$outKey]['entries'][$date] ?? '0'; $chartData[$outKey]['entries'][$date] = bcadd($journal['amount'], $chartData[$outKey]['entries'][$date]); } $inSet = $income[$currencyId] ?? ['transaction_journals' => []]; foreach ($inSet['transaction_journals'] as $journal) { $date = $journal['date']->formatLocalized($format); + $chartData[$inKey]['entries'][$date] = $chartData[$inKey]['entries'][$date] ?? '0'; $chartData[$inKey]['entries'][$date] = bcadd($journal['amount'], $chartData[$inKey]['entries'][$date]); } } @@ -369,6 +378,7 @@ class CategoryController extends Controller /** * Chart for a specific period. + * TODO test method, for category refactor. * * @param Category $category * @param $date diff --git a/app/Support/Chart/Category/WholePeriodChartGenerator.php b/app/Support/Chart/Category/WholePeriodChartGenerator.php index cf22550d51..21dcf7d3c9 100644 --- a/app/Support/Chart/Category/WholePeriodChartGenerator.php +++ b/app/Support/Chart/Category/WholePeriodChartGenerator.php @@ -29,12 +29,23 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Category\OperationsRepositoryInterface; use Illuminate\Support\Collection; +use Log; /** * Class WholePeriodChartGenerator */ class WholePeriodChartGenerator { + /** + * Constructor. + */ + public function __construct() + { + if ('testing' === config('app.env')) { + Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this))); + } + } + /** * @param Category $category * @param Carbon $start diff --git a/tests/Feature/Controllers/Chart/CategoryControllerTest.php b/tests/Feature/Controllers/Chart/CategoryControllerTest.php index c37093ef87..f19d45f2b3 100644 --- a/tests/Feature/Controllers/Chart/CategoryControllerTest.php +++ b/tests/Feature/Controllers/Chart/CategoryControllerTest.php @@ -28,13 +28,15 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Generator\Chart\Basic\GeneratorInterface; use FireflyIII\Helpers\Fiscal\FiscalHelperInterface; use FireflyIII\Models\AccountType; -use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; -use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\Category\NoCategoryRepositoryInterface; +use FireflyIII\Repositories\Category\OperationsRepositoryInterface; +use FireflyIII\Support\Chart\Category\WholePeriodChartGenerator; use Illuminate\Support\Collection; use Log; use Preferences; +use Tests\Support\TestDataTrait; use Tests\TestCase; /** @@ -45,6 +47,8 @@ use Tests\TestCase; */ class CategoryControllerTest extends TestCase { + use TestDataTrait; + /** * */ @@ -59,16 +63,19 @@ class CategoryControllerTest extends TestCase * @dataProvider dateRangeProvider * * @param string $range + * * @throws FireflyException * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ public function testAll(string $range): void { - + Log::info(sprintf('Now in test %s.', __METHOD__)); $repository = $this->mock(CategoryRepositoryInterface::class); $accountRepos = $this->mock(AccountRepositoryInterface::class); $generator = $this->mock(GeneratorInterface::class); + $chartGen = $this->mock(WholePeriodChartGenerator::class); $firstUseDate = null; + switch ($range) { default: throw new FireflyException(sprintf('No case for %s', $range)); @@ -98,10 +105,9 @@ class CategoryControllerTest extends TestCase $this->mockDefaultSession(); Preferences::shouldReceive('lastActivity')->atLeast()->once()->andReturn('md512345'); - $repository->shouldReceive('spentInPeriod')->andReturn([])->atLeast()->once(); - $repository->shouldReceive('earnedInPeriod')->andReturn([])->atLeast()->once(); + $chartGen->shouldReceive('generate')->atLeast()->once()->andReturn([]); + $repository->shouldReceive('firstUseDate')->andReturn($firstUseDate)->once(); - $accountRepos->shouldReceive('getAccountsByType')->withArgs([[AccountType::DEFAULT, AccountType::ASSET, AccountType::LOAN, AccountType::DEBT,AccountType::MORTGAGE]])->andReturn(new Collection)->once(); $generator->shouldReceive('multiSet')->once()->andReturn([]); $this->be($this->user()); @@ -118,54 +124,24 @@ class CategoryControllerTest extends TestCase */ public function testFrontPage(string $range): void { - $repository = $this->mock(CategoryRepositoryInterface::class); - $accountRepos = $this->mock(AccountRepositoryInterface::class); - $generator = $this->mock(GeneratorInterface::class); - $currencyRepos = $this->mock(CurrencyRepositoryInterface::class); - + Log::info(sprintf('Now in test %s.', __METHOD__)); + $repository = $this->mock(CategoryRepositoryInterface::class); + $accountRepos = $this->mock(AccountRepositoryInterface::class); + $generator = $this->mock(GeneratorInterface::class); + $opsRepos = $this->mock(OperationsRepositoryInterface::class); + $noCatRepos = $this->mock(NoCategoryRepositoryInterface::class); + $category = $this->getRandomCategory(); + $account = $this->getRandomAsset(); $this->mockDefaultSession(); Preferences::shouldReceive('lastActivity')->atLeast()->once()->andReturn('md512345'); - // spent per currency data: - $spentNoCategory = [ - 1 => - [ - 'spent' => '-123.45', - 'currency_id' => 1, - 'currency_code' => 'X', - 'currency_symbol' => 'x', - 'currency_decimal_places' => 2, - ], - ]; - $spentData = [ - 1 => [ - 'name' => 'Car', - 'spent' => [ - 1 => [ - 'spent' => '-123.45', - 'currency_id' => 2, - 'currency_code' => 'a', - 'currency_symbol' => 'b', - 'currency_decimal_places' => 2, - ], - ], - ], - ]; + $opsRepos->shouldReceive('sumExpenses')->atLeast()->once()->andReturn($this->categorySumExpenses()); + $noCatRepos->shouldReceive('sumExpenses')->atLeast()->once()->andReturn($this->categorySumExpenses()); - // grab two categories from the user - $categories = $this->user()->categories()->take(2)->get(); - - // grab two the users asset accounts: - $accounts = $this->user()->accounts()->where('account_type_id', 3)->take(2)->get(); - - // repository will return these. - $repository->shouldReceive('getCategories')->andReturn($categories)->once(); - $accountRepos->shouldReceive('getAccountsByType')->once()->withArgs([[AccountType::ASSET, AccountType::DEFAULT]])->andReturn($accounts); - - $repository->shouldReceive('spentInPeriodPerCurrency')->times(2)->andReturn($spentData); - $repository->shouldReceive('spentInPeriodPcWoCategory')->once()->andReturn($spentNoCategory); - - $currencyRepos->shouldReceive('findNull')->withArgs([1])->once()->andReturn($this->getEuro()); + $repository->shouldReceive('getCategories')->atLeast()->once()->andReturn(new Collection([$category])); + $accountRepos->shouldReceive('getAccountsByType')->once()->withArgs([[AccountType::ASSET, AccountType::DEFAULT]])->andReturn( + new Collection([$account]) + ); $generator->shouldReceive('multiSet')->andReturn([]); $this->be($this->user()); @@ -179,18 +155,21 @@ class CategoryControllerTest extends TestCase */ public function testReportPeriod(): void { + Log::info(sprintf('Now in test %s.', __METHOD__)); $repository = $this->mock(CategoryRepositoryInterface::class); $generator = $this->mock(GeneratorInterface::class); $fiscalHelper = $this->mock(FiscalHelperInterface::class); + $opsRepos = $this->mock(OperationsRepositoryInterface::class); $date = new Carbon; + $opsRepos->shouldReceive('listExpenses')->atLeast()->once()->andReturn($this->categoryListExpenses()); + $opsRepos->shouldReceive('listIncome')->atLeast()->once()->andReturn($this->categoryListIncome()); + $this->mockDefaultSession(); Preferences::shouldReceive('lastActivity')->atLeast()->once()->andReturn('md512345'); $fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date); $fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date); - $repository->shouldReceive('periodExpenses')->andReturn([])->once(); - $repository->shouldReceive('periodIncome')->andReturn([])->once(); $generator->shouldReceive('multiSet')->andReturn([])->once(); $this->be($this->user()); @@ -203,18 +182,21 @@ class CategoryControllerTest extends TestCase */ public function testReportPeriodNoCategory(): void { + Log::info(sprintf('Now in test %s.', __METHOD__)); $repository = $this->mock(CategoryRepositoryInterface::class); $generator = $this->mock(GeneratorInterface::class); $fiscalHelper = $this->mock(FiscalHelperInterface::class); + $noCatRepos = $this->mock(NoCategoryRepositoryInterface::class); $date = new Carbon; + $noCatRepos->shouldReceive('listExpenses')->atLeast()->once()->andReturn($this->noCategoryListExpenses()); + $noCatRepos->shouldReceive('listIncome')->atLeast()->once()->andReturn($this->noCategoryListIncome()); + $this->mockDefaultSession(); Preferences::shouldReceive('lastActivity')->atLeast()->once()->andReturn('md512345'); $fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date); $fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date); - $repository->shouldReceive('periodExpensesNoCategory')->andReturn([])->once(); - $repository->shouldReceive('periodIncomeNoCategory')->andReturn([])->once(); $generator->shouldReceive('multiSet')->andReturn([])->once(); $this->be($this->user()); @@ -227,26 +209,29 @@ class CategoryControllerTest extends TestCase * @dataProvider dateRangeProvider * * @param string $range + * * @throws Exception */ public function testSpecificPeriod(string $range): void { + Log::info(sprintf('Now in test %s.', __METHOD__)); $repository = $this->mock(CategoryRepositoryInterface::class); $accountRepos = $this->mock(AccountRepositoryInterface::class); $generator = $this->mock(GeneratorInterface::class); - $account = $this->getRandomAsset(); $fiscalHelper = $this->mock(FiscalHelperInterface::class); + $chartGen = $this->mock(WholePeriodChartGenerator::class); + $account = $this->getRandomAsset(); $date = new Carbon; $this->mockDefaultSession(); + Preferences::shouldReceive('lastActivity')->atLeast()->once()->andReturn('md512345'); + $chartGen->shouldReceive('generate')->atLeast()->once()->andReturn([]); $fiscalHelper->shouldReceive('endOfFiscalYear')->atLeast()->once()->andReturn($date); $fiscalHelper->shouldReceive('startOfFiscalYear')->atLeast()->once()->andReturn($date); $accountRepos->shouldReceive('getAccountsByType')->andReturn(new Collection([$account])); - $repository->shouldReceive('spentInPeriod')->andReturn([]); - $repository->shouldReceive('earnedInPeriod')->andReturn([]); $generator->shouldReceive('multiSet')->andReturn([])->once(); $this->be($this->user()); diff --git a/tests/Support/TestDataTrait.php b/tests/Support/TestDataTrait.php new file mode 100644 index 0000000000..0fe38bb4e6 --- /dev/null +++ b/tests/Support/TestDataTrait.php @@ -0,0 +1,258 @@ +. + */ + +declare(strict_types=1); + +namespace Tests\Support; + +use Carbon\Carbon; +use Exception; +use FireflyIII\Models\TransactionCurrency; + +/** + * Trait TestDataTrait + * + * @package Tests\Support + */ +trait TestDataTrait +{ + /** + * Method that returns default data for when the category OperationsRepos + * "listExpenses" method is called. + * + * @return array + */ + protected function categoryListExpenses(): array + { + $eur = TransactionCurrency::where('code', 'EUR')->first(); + $usd = TransactionCurrency::where('code', 'USD')->first(); + $cat1 = $this->user()->categories()->inRandomOrder()->first(); + $cat2 = $this->user()->categories()->inRandomOrder()->where('id', '!=', $cat1->id)->first(); + $data = []; + $amount = 400; + $date = null; + try { + $amount = random_int(100, 2500); + $date = new Carbon; + } catch (Exception $e) { + $e->getMessage(); + } + $amount = bcmul((string)round($amount / 100, 2), '-1'); + + foreach ([$eur, $usd] as $currency) { + $data[$currency->id] = [ + 'currency_id' => $currency->id, + 'currency_name' => $currency->name, + 'currency_symbol' => $currency->symbol, + 'currency_code' => $currency->code, + 'currency_decimal_places' => $currency->decimal_places, + 'categories' => [], + ]; + foreach ([$cat1, $cat2] as $category) { + $data[$currency->id]['categories'][$category->id] = [ + 'id' => $category->id, + 'name' => $category->name, + 'transaction_journals' => [], + ]; + // add two random amounts: + for ($i = 0; $i < 2; $i++) { + $data[$currency->id]['categories'][$category->id]['transaction_journals'][$i] = [ + 'amount' => $amount, + 'date' => $date, + ]; + } + } + } + + return $data; + } + + /** + * Method that returns default data for when the category OperationsRepos + * "listExpenses" method is called. + * + * @return array + */ + protected function categoryListIncome(): array + { + $eur = TransactionCurrency::where('code', 'EUR')->first(); + $usd = TransactionCurrency::where('code', 'USD')->first(); + $cat1 = $this->user()->categories()->inRandomOrder()->first(); + $cat2 = $this->user()->categories()->inRandomOrder()->where('id', '!=', $cat1->id)->first(); + $data = []; + $amount = 400; + $date = null; + try { + $amount = random_int(100, 2500); + $date = new Carbon; + } catch (Exception $e) { + $e->getMessage(); + } + $amount = (string)round($amount / 100, 2); + + foreach ([$eur, $usd] as $currency) { + $data[$currency->id] = [ + 'currency_id' => $currency->id, + 'currency_name' => $currency->name, + 'currency_symbol' => $currency->symbol, + 'currency_code' => $currency->code, + 'currency_decimal_places' => $currency->decimal_places, + 'categories' => [], + ]; + foreach ([$cat1, $cat2] as $category) { + $data[$currency->id]['categories'][$category->id] = [ + 'id' => $category->id, + 'name' => $category->name, + 'transaction_journals' => [], + ]; + // add two random amounts: + for ($i = 0; $i < 2; $i++) { + $data[$currency->id]['categories'][$category->id]['transaction_journals'][$i] = [ + 'amount' => $amount, + 'date' => $date, + ]; + } + } + } + + return $data; + } + + /** + * Method that returns default data for when the category OperationsController + * "sumExpenses" method is called. + * + * Also applies to NoCategoryRepos::sumExpenses. + * + * @return array + */ + protected function categorySumExpenses(): array + { + $eur = TransactionCurrency::where('code', 'EUR')->first(); + $usd = TransactionCurrency::where('code', 'USD')->first(); + $data = []; + $amount = 400; + try { + $amount = random_int(100, 2500); + } catch (Exception $e) { + $e->getMessage(); + } + $amount = bcmul((string)round($amount / 100, 2), '-1'); + + foreach ([$eur, $usd] as $currency) { + $data[$currency->id] = [ + 'currency_id' => $currency->id, + 'currency_name' => $currency->name, + 'currency_symbol' => $currency->symbol, + 'currency_code' => $currency->code, + 'currency_decimal_places' => $currency->decimal_places, + 'sum' => $amount, + ]; + } + + return $data; + } + + /** + * Method that returns default data for when the category NoCategoryRepos + * "listExpenses" method is called. + * + * @return array + */ + protected function noCategoryListExpenses(): array + { + $eur = TransactionCurrency::where('code', 'EUR')->first(); + $usd = TransactionCurrency::where('code', 'USD')->first(); + $data = []; + $amount = 400; + $date = null; + try { + $amount = random_int(100, 2500); + $date = new Carbon; + } catch (Exception $e) { + $e->getMessage(); + } + $amount = bcmul((string)round($amount / 100, 2), '-1'); + + foreach ([$eur, $usd] as $currency) { + $data[$currency->id] = [ + 'currency_id' => $currency->id, + 'currency_name' => $currency->name, + 'currency_symbol' => $currency->symbol, + 'currency_code' => $currency->code, + 'currency_decimal_places' => $currency->decimal_places, + 'transaction_journals' => [], + ]; + // add two random amounts: + for ($i = 0; $i < 2; $i++) { + $data[$currency->id]['transaction_journals'][$i] = [ + 'amount' => $amount, + 'date' => $date, + ]; + } + } + + return $data; + } + + /** + * Method that returns default data for when the category NoCategoryRepos + * "listExpenses" method is called. + * + * @return array + */ + protected function noCategoryListIncome(): array + { + $eur = TransactionCurrency::where('code', 'EUR')->first(); + $usd = TransactionCurrency::where('code', 'USD')->first(); + $data = []; + $amount = 400; + $date = null; + try { + $amount = random_int(100, 2500); + $date = new Carbon; + } catch (Exception $e) { + $e->getMessage(); + } + $amount = (string)round($amount / 100, 2); + + foreach ([$eur, $usd] as $currency) { + $data[$currency->id] = [ + 'currency_id' => $currency->id, + 'currency_name' => $currency->name, + 'currency_symbol' => $currency->symbol, + 'currency_code' => $currency->code, + 'currency_decimal_places' => $currency->decimal_places, + 'transaction_journals' => [], + ]; + // add two random amounts: + for ($i = 0; $i < 2; $i++) { + $data[$currency->id]['transaction_journals'][$i] = [ + 'amount' => $amount, + 'date' => $date, + ]; + } + } + + return $data; + } + +} \ No newline at end of file