From 1f50f65bb7ba25035390a2901db1969032edc01a Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 13 Mar 2021 19:03:08 +0100 Subject: [PATCH] Finish budget tests. --- .../Models/Budget/StoreController.php | 1 + .../Requests/Models/Budget/StoreRequest.php | 41 ++-- .../Requests/Models/Budget/UpdateRequest.php | 28 ++- app/Repositories/Budget/BudgetRepository.php | 111 ++++++----- app/Transformers/BudgetTransformer.php | 5 +- .../Api/Models/Budget/StoreControllerTest.php | 161 ++++++++++++++++ .../Models/Budget/UpdateControllerTest.php | 177 ++++++++++++++++++ 7 files changed, 436 insertions(+), 88 deletions(-) create mode 100644 tests/Api/Models/Budget/StoreControllerTest.php create mode 100644 tests/Api/Models/Budget/UpdateControllerTest.php diff --git a/app/Api/V1/Controllers/Models/Budget/StoreController.php b/app/Api/V1/Controllers/Models/Budget/StoreController.php index 2141b11242..9fd4299a77 100644 --- a/app/Api/V1/Controllers/Models/Budget/StoreController.php +++ b/app/Api/V1/Controllers/Models/Budget/StoreController.php @@ -68,6 +68,7 @@ class StoreController extends Controller public function store(StoreRequest $request): JsonResponse { $budget = $this->repository->store($request->getAll()); + $budget->refresh(); $manager = $this->getManager(); /** @var BudgetTransformer $transformer */ diff --git a/app/Api/V1/Requests/Models/Budget/StoreRequest.php b/app/Api/V1/Requests/Models/Budget/StoreRequest.php index 49506d96aa..ecaa8f28b8 100644 --- a/app/Api/V1/Requests/Models/Budget/StoreRequest.php +++ b/app/Api/V1/Requests/Models/Budget/StoreRequest.php @@ -46,23 +46,20 @@ class StoreRequest extends FormRequest */ public function getAll(): array { - $active = true; - if (null !== $this->get('active')) { - $active = $this->boolean('active'); - } + $fields = [ + 'name' => ['name', 'string'], + 'active' => ['active', 'boolean'], + 'order' => ['active', 'integer'], - return [ - 'name' => $this->string('name'), - 'active' => $active, - 'order' => 0, - 'transaction_currency_id' => $this->integer('auto_budget_currency_id'), - 'transaction_currency_code' => $this->string('auto_budget_currency_code'), - - // auto budget info - 'auto_budget_type' => $this->string('auto_budget_type'), - 'auto_budget_amount' => $this->string('auto_budget_amount'), - 'auto_budget_period' => $this->string('auto_budget_period'), + // auto budget currency: + 'currency_id' => ['auto_budget_currency_id', 'integer'], + 'currency_code' => ['auto_budget_currency_code', 'string'], + 'auto_budget_type' => ['auto_budget_type', 'string'], + 'auto_budget_amount' => ['auto_budget_amount', 'string'], + 'auto_budget_period' => ['auto_budget_period', 'string'], ]; + + return $this->getAllData($fields); } /** @@ -73,14 +70,14 @@ class StoreRequest extends FormRequest public function rules(): array { return [ - 'name' => 'required|between:1,100|uniqueObjectForUser:budgets,name', - 'active' => [new IsBoolean], - 'auto_budget_currency_id' => 'exists:transaction_currencies,id', - 'auto_budget_currency_code' => 'exists:transaction_currencies,code', + 'name' => 'required|between:1,100|uniqueObjectForUser:budgets,name', + 'active' => [new IsBoolean], + 'currency_id' => 'exists:transaction_currencies,id', + 'currency_code' => 'exists:transaction_currencies,code', // auto budget info - 'auto_budget_type' => 'in:reset,rollover,none', - 'auto_budget_amount' => 'min:0|max:1000000000', - 'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly', + 'auto_budget_type' => 'in:reset,rollover,none', + 'auto_budget_amount' => 'min:0|max:1000000000', + 'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly', ]; } diff --git a/app/Api/V1/Requests/Models/Budget/UpdateRequest.php b/app/Api/V1/Requests/Models/Budget/UpdateRequest.php index 697b2814ce..d8b149fcda 100644 --- a/app/Api/V1/Requests/Models/Budget/UpdateRequest.php +++ b/app/Api/V1/Requests/Models/Budget/UpdateRequest.php @@ -46,21 +46,19 @@ class UpdateRequest extends FormRequest */ public function getAll(): array { - $active = true; - if (null !== $this->get('active')) { - $active = $this->boolean('active'); - } - - return [ - 'name' => $this->string('name'), - 'active' => $active, - 'order' => 0, - 'auto_budget_type' => $this->string('auto_budget_type'), - 'transaction_currency_id' => $this->integer('auto_budget_currency_id'), - 'transaction_currency_code' => $this->string('auto_budget_currency_code'), - 'auto_budget_amount' => $this->string('auto_budget_amount'), - 'auto_budget_period' => $this->string('auto_budget_period'), + // this is the way: + $fields = [ + 'name' => ['name', 'string'], + 'active' => ['active', 'boolean'], + 'order' => ['order', 'integer'], + 'currency_id' => ['auto_budget_currency_id', 'integer'], + 'currency_code' => ['auto_budget_currency_code', 'string'], + 'auto_budget_type' => ['auto_budget_type', 'string'], + 'auto_budget_amount' => ['auto_budget_amount', 'string'], + 'auto_budget_period' => ['auto_budget_period', 'string'], ]; + + return $this->getAllData($fields); } /** @@ -73,7 +71,7 @@ class UpdateRequest extends FormRequest $budget = $this->route()->parameter('budget'); return [ - 'name' => sprintf('required|between:1,100|uniqueObjectForUser:budgets,name,%d', $budget->id), + 'name' => sprintf('between:1,100|uniqueObjectForUser:budgets,name,%d', $budget->id), 'active' => [new IsBoolean], 'auto_budget_type' => 'in:reset,rollover,none', 'auto_budget_currency_id' => 'exists:transaction_currencies,id', diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 21618f3859..ea0d2675c5 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -320,6 +320,7 @@ class BudgetRepository implements BudgetRepositoryInterface 'user_id' => $this->user->id, 'name' => $data['name'], 'order' => $order + 1, + 'active' => array_key_exists('active', $data) ? $data['active'] : true, ] ); } catch (QueryException $e) { @@ -327,25 +328,27 @@ class BudgetRepository implements BudgetRepositoryInterface Log::error($e->getTraceAsString()); throw new FireflyException('400002: Could not store budget.'); } - - // try to create associated auto budget: - $type = $data['auto_budget_type'] ?? 0; - if (0 === $type || '' === $type || 'none' === $type) { + if (!array_key_exists('auto_budget_type', $data)) { return $newBudget; } + $type = $data['auto_budget_type']; + if ('none' === $type) { + return $newBudget; + } + if ('reset' === $type) { $type = AutoBudget::AUTO_BUDGET_RESET; } if ('rollover' === $type) { $type = AutoBudget::AUTO_BUDGET_ROLLOVER; } - $repos = app(CurrencyRepositoryInterface::class); - $currencyId = (int)($data['transaction_currency_id'] ?? 0); - $currencyCode = (string)($data['transaction_currency_code'] ?? ''); - $currency = $repos->findNull($currencyId); - if (null === $currency) { - $currency = $repos->findByCodeNull($currencyCode); + $repos = app(CurrencyRepositoryInterface::class); + if (array_key_exists('currency_id', $data)) { + $currency = $repos->findNull((int)$data['currency_id']); + } + if (array_key_exists('currency_code', $data)) { + $currency = $repos->findByCode((string)$data['currency_code']); } if (null === $currency) { $currency = app('amount')->getDefaultCurrencyByUser($this->user); @@ -387,56 +390,68 @@ class BudgetRepository implements BudgetRepositoryInterface */ public function update(Budget $budget, array $data): Budget { - $oldName = $budget->name; - $budget->name = $data['name']; - $budget->active = $data['active']; + $oldName = $budget->name; + if (array_key_exists('name', $data)) { + $budget->name = $data['name']; + } + if (array_key_exists('active', $data)) { + $budget->active = $data['active']; + } $budget->save(); // update or create auto-budget: - $autoBudgetType = $data['auto_budget_type'] ?? 0; - if ('reset' === $autoBudgetType) { - $autoBudgetType = AutoBudget::AUTO_BUDGET_RESET; - } - if ('rollover' === $autoBudgetType) { - $autoBudgetType = AutoBudget::AUTO_BUDGET_ROLLOVER; - } - if ('none' === $autoBudgetType) { - $autoBudgetType = 0; - } - if (0 !== $autoBudgetType) { - $autoBudget = $this->getAutoBudget($budget); - if (null === $autoBudget) { - $autoBudget = new AutoBudget; - $autoBudget->budget()->associate($budget); - } + $autoBudget = $this->getAutoBudget($budget); + // get currency: + $currency = null; + if (array_key_exists('currency_id', $data) || array_key_exists('currency_code', $data)) { $repos = app(CurrencyRepositoryInterface::class); - $currencyId = (int)($data['transaction_currency_id'] ?? 0); - $currencyCode = (string)($data['transaction_currency_code'] ?? ''); - - $currency = $repos->findNull($currencyId); + $currencyId = (int)($data['currency_id'] ?? 0); + $currencyCode = (string)($data['currency_code'] ?? ''); + $currency = $repos->findNull($currencyId); if (null === $currency) { $currency = $repos->findByCodeNull($currencyCode); } - if (null === $currency) { - $currency = app('amount')->getDefaultCurrencyByUser($this->user); - } + } + if (null === $currency) { + $currency = app('amount')->getDefaultCurrencyByUser($this->user); + } + if (null === $autoBudget + && array_key_exists('auto_budget_type', $data) + && array_key_exists('auto_budget_amount', $data) + ) { + // only create if all are here: + $autoBudget = new AutoBudget; + $autoBudget->budget_id = $budget->id; $autoBudget->transaction_currency_id = $currency->id; - $autoBudget->auto_budget_type = $autoBudgetType; - $autoBudget->amount = $data['auto_budget_amount'] ?? '0'; - $autoBudget->period = $data['auto_budget_period'] ?? 'monthly'; + } + + // update existing type + if (array_key_exists('auto_budget_type', $data)) { + $autoBudgetType = $data['auto_budget_type']; + if ('reset' === $autoBudgetType) { + $autoBudget->auto_budget_type = AutoBudget::AUTO_BUDGET_RESET; + } + if ('rollover' === $autoBudgetType) { + $autoBudget->auto_budget_type = AutoBudget::AUTO_BUDGET_ROLLOVER; + } + if ('none' === $autoBudgetType && null !== $autoBudget->id) { + $autoBudget->delete(); + + return $budget; + } + } + if (array_key_exists('auto_budget_amount', $data)) { + $autoBudget->amount = $data['auto_budget_amount']; + } + if (array_key_exists('auto_budget_period', $data)) { + $autoBudget->period = $data['auto_budget_period']; + } + if (null !== $autoBudget) { $autoBudget->save(); } - if (0 === $autoBudgetType) { - $autoBudget = $this->getAutoBudget($budget); - if (null !== $autoBudget) { - $this->destroyAutoBudget($budget); - } - } - $this->updateRuleTriggers($oldName, $data['name']); - $this->updateRuleActions($oldName, $data['name']); - app('preferences')->mark(); + return $budget; } diff --git a/app/Transformers/BudgetTransformer.php b/app/Transformers/BudgetTransformer.php index 87103b5977..4b32c539ff 100644 --- a/app/Transformers/BudgetTransformer.php +++ b/app/Transformers/BudgetTransformer.php @@ -81,13 +81,12 @@ class BudgetTransformer extends AbstractTransformer ]; if (null !== $autoBudget) { - $abCurrencyId = (int)$autoBudget->transactionCurrency->id; + $abCurrencyId = (string)$autoBudget->transactionCurrency->id; $abCurrencyCode = $autoBudget->transactionCurrency->code; $abType = $types[$autoBudget->auto_budget_type]; $abAmount = number_format((float)$autoBudget->amount, $autoBudget->transactionCurrency->decimal_places, '.', ''); $abPeriod = $autoBudget->period; } - return [ 'id' => (string)$budget->id, 'created_at' => $budget->created_at->toAtomString(), @@ -96,7 +95,7 @@ class BudgetTransformer extends AbstractTransformer 'name' => $budget->name, 'auto_budget_type' => $abType, 'auto_budget_period' => $abPeriod, - 'auto_budget_currency_id' => (string)$abCurrencyId, + 'auto_budget_currency_id' => $abCurrencyId, 'auto_budget_currency_code' => $abCurrencyCode, 'auto_budget_amount' => $abAmount, 'spent' => $spent, diff --git a/tests/Api/Models/Budget/StoreControllerTest.php b/tests/Api/Models/Budget/StoreControllerTest.php new file mode 100644 index 0000000000..9fe231b572 --- /dev/null +++ b/tests/Api/Models/Budget/StoreControllerTest.php @@ -0,0 +1,161 @@ +. + */ + +namespace Tests\Api\Models\Budget; + + +use Faker\Factory; +use Laravel\Passport\Passport; +use Log; +use Tests\TestCase; +use Tests\Traits\CollectsValues; +use Tests\Traits\RandomValues; +use Tests\Traits\TestHelpers; + +/** + * Class StoreControllerTest + */ +class StoreControllerTest extends TestCase +{ + use RandomValues, TestHelpers, CollectsValues; + + /** + * + */ + public function setUp(): void + { + parent::setUp(); + Passport::actingAs($this->user()); + Log::info(sprintf('Now in %s.', get_class($this))); + } + + + /** + * @param array $submission + * + * @dataProvider storeDataProvider + * @ data Provider emptyDataProvider + */ + public function testStore(array $submission): void + { + if ([] === $submission) { + $this->markTestSkipped('Empty data provider'); + } + // run account store with a minimal data set: + $route = 'api.v1.budgets.store'; + $this->storeAndCompare($route, $submission); + } + + /** + * @return array + */ + public function emptyDataProvider(): array + { + return [[[]]]; + + } + + /** + * @return array + */ + public function storeDataProvider(): array + { + $minimalSets = $this->minimalSets(); + $optionalSets = $this->optionalSets(); + $regenConfig = [ + 'name' => function () { + $faker = Factory::create(); + + return join(' ', $faker->words(5)); + }, + ]; + + return $this->genericDataProvider($minimalSets, $optionalSets, $regenConfig); + } + + + /** + * @return array + */ + private function minimalSets(): array + { + $faker = Factory::create(); + $repeatFreqs = ['yearly', 'weekly', 'monthly']; + $repeatFreq = $repeatFreqs[rand(0, count($repeatFreqs) - 1)]; + + return [ + 'default_bill' => [ + 'fields' => [ + 'name' => join(',', $faker->words(5)), + ], + ], + ]; + } + + + /** + * @return \array[][] + */ + private function optionalSets(): array + { + $faker = Factory::create(); + $repeatFreqs = ['weekly', 'monthly', 'yearly']; + $repeatFreq = $repeatFreqs[rand(0, count($repeatFreqs) - 1)]; + $currencies = [ + 1 => 'EUR', + 2 => 'HUF', + 3 => 'GBP', + 4 => 'UAH', + ]; + $rand = rand(1, 4); + $objectGroupId = $faker->numberBetween(1, 2); + $objectGroupName = sprintf('Object group %d', $objectGroupId); + + $autoBudgetTypes = ['reset', 'rollover']; + $autoBudgetType = $autoBudgetTypes[rand(0, count($autoBudgetTypes) - 1)]; + + return [ + 'active' => [ + 'fields' => [ + 'active' => $faker->boolean, + ], + ], + 'auto_budget_id' => [ + 'fields' => [ + 'auto_budget_type' => $autoBudgetType, + 'auto_budget_currency_id' => $rand, + 'auto_budget_amount' => number_format($faker->randomFloat(2, 10, 100), 2), + 'auto_budget_period' => $repeatFreq, + ], + ], + 'auto_budget_code' => [ + 'fields' => [ + 'auto_budget_type' => $autoBudgetType, + 'auto_budget_currency_code' => $currencies[$rand], + 'auto_budget_amount' => number_format($faker->randomFloat(2, 10, 100), 2), + 'auto_budget_period' => $repeatFreq, + ], + ] + + ]; + } + +} \ No newline at end of file diff --git a/tests/Api/Models/Budget/UpdateControllerTest.php b/tests/Api/Models/Budget/UpdateControllerTest.php new file mode 100644 index 0000000000..d75ea944b7 --- /dev/null +++ b/tests/Api/Models/Budget/UpdateControllerTest.php @@ -0,0 +1,177 @@ +. + */ + +namespace Tests\Api\Models\Budget; + + +use Faker\Factory; +use Laravel\Passport\Passport; +use Log; +use Tests\TestCase; +use Tests\Traits\CollectsValues; +use Tests\Traits\RandomValues; +use Tests\Traits\TestHelpers; + +/** + * Class UpdateControllerTest + */ +class UpdateControllerTest extends TestCase +{ + use RandomValues, TestHelpers, CollectsValues; + + /** + * + */ + public function setUp(): void + { + parent::setUp(); + Passport::actingAs($this->user()); + Log::info(sprintf('Now in %s.', get_class($this))); + } + + + /** + * @dataProvider updateDataProvider + */ + public function testUpdate(array $submission): void + { + $ignore = [ + 'created_at', + 'updated_at', + ]; + $route = route('api.v1.budgets.update', [$submission['id']]); + + $this->updateAndCompare($route, $submission, $ignore); + } + + + /** + * @return array + */ + public function updateDataProvider(): array + { + $submissions = []; + $all = $this->updateDataSet(); + foreach ($all as $name => $data) { + $submissions[] = [$data]; + } + + return $submissions; + } + + + /** + * @return array + */ + public function updateDataSet(): array + { + $faker = Factory::create(); + $currencies = [ + 1 => 'EUR', + 2 => 'HUF', + 3 => 'GBP', + 4 => 'UAH', + ]; + $repeatFreqs = ['yearly', 'weekly', 'monthly']; + $repeatFreq = $repeatFreqs[rand(0, count($repeatFreqs) - 1)]; + $objectGroupId = $faker->numberBetween(1, 2); + $objectGroupName = sprintf('Object group %d', $objectGroupId); + $rand = rand(1, 4); + + $autoBudgetTypes = ['reset', 'rollover']; + $autoBudgetType = $autoBudgetTypes[rand(0, count($autoBudgetTypes) - 1)]; + + $set = [ + 'name' => [ + 'id' => 1, + 'fields' => [ + 'name' => ['test_value' => join(' ', $faker->words(4))], + ], + 'extra_ignore' => [], + ], + 'active' => [ + 'id' => 1, + 'fields' => [ + 'active' => ['test_value' => $faker->boolean], + ], + 'extra_ignore' => [], + ], + 'order' => [ + 'id' => 1, + 'fields' => [ + 'order' => ['test_value' => $faker->numberBetween(1, 5)], + ], + 'extra_ignore' => [], + ], + 'auto_budget' => [ + 'id' => 1, + 'fields' => [ + 'auto_budget_type' => ['test_value' => $autoBudgetType], + 'auto_budget_currency_id' => ['test_value' => (string)$rand], + 'auto_budget_currency_code' => ['test_value' => $currencies[$rand]], + 'auto_budget_amount' => ['test_value' => number_format($faker->randomFloat(2, 10, 100), 2)], + 'auto_budget_period' => ['test_value' => $repeatFreq], + ], + 'extra_ignore' => [], + ], + 'auto_budget_currency_id' => [ + 'id' => 1, + 'fields' => [ + 'auto_budget_currency_id' => ['test_value' => (string)$rand], + ], + 'extra_ignore' => ['auto_budget_currency_code'], + ], + + 'auto_budget_currency_code' => [ + 'id' => 1, + 'fields' => [ + 'auto_budget_currency_code' => ['test_value' => $currencies[$rand]], + ], + 'extra_ignore' => ['auto_budget_currency_id'], + ], + 'auto_budget_amount' => [ + 'id' => 1, + 'fields' => [ + 'auto_budget_amount' => ['test_value' => number_format($faker->randomFloat(2, 10, 100), 2)], + ], + 'extra_ignore' => [], + ], + 'auto_budget_period' => [ + 'id' => 1, + 'fields' => [ + 'auto_budget_period' => ['test_value' => $repeatFreq], + ], + 'extra_ignore' => [], + ], + 'auto_budget_reset' => [ + 'id' => 1, + 'fields' => [ + 'auto_budget_type' => ['test_value' => 'none'], + ], + 'extra_ignore' => ['auto_budget_type', 'auto_budget_period', 'auto_budget_currency_id', 'auto_budget_currency_code', 'auto_budget_amount'], + ], + ]; + + return $set; + } + + +} \ No newline at end of file