From f499ff19f2653a3bcfce2964bdf4e733a7a531ff Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Wed, 21 Aug 2024 19:22:21 +0300 Subject: [PATCH 1/8] wallets.walletTransactions --- tests/Units/Domain/EagerLoadingTest.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/Units/Domain/EagerLoadingTest.php b/tests/Units/Domain/EagerLoadingTest.php index 89d11d910..795a05256 100644 --- a/tests/Units/Domain/EagerLoadingTest.php +++ b/tests/Units/Domain/EagerLoadingTest.php @@ -33,7 +33,7 @@ public function testUuidDuplicate(): void } /** @var Collection $buyers */ - $buyers = Buyer::with('wallet') + $buyers = Buyer::with('wallet.walletTransactions') ->whereIn('id', $buyerTimes->pluck('id')->toArray()) ->paginate(10); @@ -42,6 +42,7 @@ public function testUuidDuplicate(): void foreach ($buyers as $buyer) { self::assertTrue($buyer->relationLoaded('wallet')); self::assertTrue($buyer->wallet->relationLoaded('holder')); + self::assertTrue($buyer->wallet->relationLoaded('walletTransactions')); $uuids[] = $buyer->wallet->uuid; $balances[] = $buyer->wallet->balanceInt; @@ -85,8 +86,10 @@ public function testMultiWallets(): void ]); /** @var UserMulti $user */ - $user = UserMulti::with('wallets')->find($multi->getKey()); + $user = UserMulti::with('wallets.walletTransactions')->find($multi->getKey()); self::assertTrue($user->relationLoaded('wallets')); + self::assertNotEmpty($user->wallets); + self::assertTrue($user->wallets[0]->relationLoaded('walletTransactions')); self::assertNotNull($user->getWallet('hello')); self::assertNotNull($user->getWallet('world')); self::assertTrue($user->getWallet('hello')->relationLoaded('holder')); From 8570ed7ca93f93e8600f777e5e783eb4e4e4b8bd Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Wed, 21 Aug 2024 19:28:50 +0300 Subject: [PATCH 2/8] remove ignored lines --- phpstan.src.baseline.neon | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/phpstan.src.baseline.neon b/phpstan.src.baseline.neon index ce5b7ad55..5899b31d6 100644 --- a/phpstan.src.baseline.neon +++ b/phpstan.src.baseline.neon @@ -195,11 +195,6 @@ parameters: count: 1 path: src/Models/Transaction.php - - - message: "#^Property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$table \\(string\\) does not accept mixed\\.$#" - count: 1 - path: src/Models/Transaction.php - - message: "#^Unable to resolve the template type TRelatedModel in call to method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:belongsTo\\(\\)$#" count: 1 @@ -230,11 +225,6 @@ parameters: count: 4 path: src/Models/Transfer.php - - - message: "#^Property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$table \\(string\\) does not accept mixed\\.$#" - count: 1 - path: src/Models/Transfer.php - - message: "#^Unable to resolve the template type TRelatedModel in call to method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:belongsTo\\(\\)$#" count: 4 @@ -270,11 +260,6 @@ parameters: count: 2 path: src/Models/Wallet.php - - - message: "#^Property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$table \\(string\\) does not accept mixed\\.$#" - count: 1 - path: src/Models/Wallet.php - - message: "#^Parameter \\#1 \\$objects \\(non\\-empty\\-array\\\\) of method Bavix\\\\Wallet\\\\Services\\\\AssistantService\\:\\:getUuids\\(\\) should be compatible with parameter \\$objects \\(non\\-empty\\-array\\\\) of method Bavix\\\\Wallet\\\\Services\\\\AssistantServiceInterface\\:\\:getUuids\\(\\)$#" count: 1 From 4fda8dc47c5f5b45a182cdced9d14f8e12026e8f Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Wed, 21 Aug 2024 19:57:22 +0300 Subject: [PATCH 3/8] coverage 100% --- src/Traits/CanConfirm.php | 28 +++++++--- tests/Units/Domain/ConfirmTest.php | 88 ++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 7 deletions(-) diff --git a/src/Traits/CanConfirm.php b/src/Traits/CanConfirm.php index f21d6e3af..3dbfeb4f5 100644 --- a/src/Traits/CanConfirm.php +++ b/src/Traits/CanConfirm.php @@ -45,19 +45,33 @@ trait CanConfirm */ public function confirm(Transaction $transaction): bool { - // Check if the wallet has enough money + // Execute the confirmation process within an atomic block to ensure data consistency. return app(AtomicServiceInterface::class)->block($this, function () use ($transaction): bool { + // Check if the transaction is already confirmed. + // If it is, throw an exception. + if ($transaction->confirmed) { + // Why is there a check here without calling refresh? + // It's because this check can be performed in force confirm again. + throw new ConfirmedInvalid( + // Get the error message from the translator service. + app(TranslatorServiceInterface::class)->get('wallet::errors.confirmed_invalid'), + // Set the error code to CONFIRMED_INVALID. + ExceptionInterface::CONFIRMED_INVALID + ); + } + + // Check if the transaction type is withdrawal. if ($transaction->type === Transaction::TYPE_WITHDRAW) { - // Check if the wallet has enough money + // Check if the wallet has enough money to cover the withdrawal amount. app(ConsistencyServiceInterface::class)->checkPotential( - // Get the wallet + // Get the wallet. app(CastServiceInterface::class)->getWallet($this), - // Negative amount + // Negate the withdrawal amount to check for sufficient funds. app(MathServiceInterface::class)->negative($transaction->amount) ); } - // Force confirm the transaction + // Force confirm the transaction. return $this->forceConfirm($transaction); }); } @@ -118,7 +132,7 @@ public function resetConfirm(Transaction $transaction): bool // Reset the confirmation of the transaction in a single database transaction return app(AtomicServiceInterface::class)->block($this, function () use ($transaction) { // Check if the transaction is already confirmed - if (! $transaction->confirmed) { + if (! $transaction->refresh()->confirmed) { throw new UnconfirmedInvalid( // If the transaction is not confirmed, throw an `UnconfirmedInvalid` exception app(TranslatorServiceInterface::class)->get('wallet::errors.unconfirmed_invalid'), @@ -195,7 +209,7 @@ public function forceConfirm(Transaction $transaction): bool // Attempt to confirm the transaction in a single database transaction return app(AtomicServiceInterface::class)->block($this, function () use ($transaction) { // Check if the transaction is already confirmed - if ($transaction->confirmed) { + if ($transaction->refresh()->confirmed) { throw new ConfirmedInvalid( app(TranslatorServiceInterface::class)->get('wallet::errors.confirmed_invalid'), ExceptionInterface::CONFIRMED_INVALID diff --git a/tests/Units/Domain/ConfirmTest.php b/tests/Units/Domain/ConfirmTest.php index 9533ec53a..970fa37bd 100644 --- a/tests/Units/Domain/ConfirmTest.php +++ b/tests/Units/Domain/ConfirmTest.php @@ -67,6 +67,25 @@ public function testSafe(): void self::assertFalse($transaction->confirmed); } + public function testSafeConfirmedInvalid(): void + { + /** @var Buyer $buyer */ + $buyer = BuyerFactory::new()->create(); + $wallet = $buyer->wallet; + + self::assertSame(0, $wallet->balanceInt); + + $transaction = $wallet->forceWithdraw(1000, ['desc' => 'confirmed']); + + self::assertSame(-1000, $wallet->balanceInt); + self::assertTrue($transaction->confirmed); + self::assertTrue($transaction->getKey() > 0); + + self::assertTrue($wallet->safeConfirm($transaction)); + self::assertSame(-1000, $wallet->balanceInt); + self::assertTrue($transaction->confirmed); + } + public function testSafeResetConfirm(): void { /** @var Buyer $buyer */ @@ -127,6 +146,25 @@ public function testForce(): void self::assertTrue($transaction->confirmed); } + public function testForceConfirmedInvalid(): void + { + $this->expectException(ConfirmedInvalid::class); + $this->expectExceptionCode(ExceptionInterface::CONFIRMED_INVALID); + $this->expectExceptionMessageStrict(trans('wallet::errors.confirmed_invalid')); + + /** @var Buyer $buyer */ + $buyer = BuyerFactory::new()->create(); + $wallet = $buyer->wallet; + + self::assertSame(0, $wallet->balanceInt); + + $transaction = $wallet->forceWithdraw(1000); + self::assertSame(-1000, $wallet->balanceInt); + self::assertTrue($transaction->confirmed); + + $wallet->forceConfirm($transaction); + } + public function testUnconfirmed(): void { /** @var Buyer $buyer */ @@ -198,6 +236,31 @@ public function testSafeUnconfirmed(): void self::assertTrue($wallet->safeResetConfirm($transaction)); } + public function testSafeUnconfirmedWalletOwnerInvalid(): void + { + /** + * @var Buyer $buyer1 + * @var Buyer $buyer2 + **/ + [$buyer1, $buyer2] = BuyerFactory::times(2)->create(); + $wallet1 = $buyer1->wallet; + $wallet2 = $buyer2->wallet; + + self::assertTrue($wallet1->saveOrFail()); + self::assertTrue($wallet2->saveOrFail()); + + self::assertSame(0, $wallet1->balanceInt); + self::assertSame(0, $wallet2->balanceInt); + + $transaction1 = $wallet1->deposit(1000, null, true); + self::assertSame(1000, $wallet1->balanceInt); + self::assertTrue($transaction1->confirmed); + + self::assertFalse($wallet2->safeResetConfirm($transaction1)); + self::assertSame(1000, $wallet1->balanceInt); + self::assertTrue($transaction1->confirmed); + } + public function testWalletOwnerInvalid(): void { $this->expectException(WalletOwnerInvalid::class); @@ -223,6 +286,31 @@ public function testWalletOwnerInvalid(): void $secondWallet->confirm($transaction); } + public function testForceWalletOwnerInvalid(): void + { + $this->expectException(WalletOwnerInvalid::class); + $this->expectExceptionCode(ExceptionInterface::WALLET_OWNER_INVALID); + $this->expectExceptionMessageStrict(trans('wallet::errors.owner_invalid')); + + /** + * @var Buyer $first + * @var Buyer $second + */ + [$first, $second] = BuyerFactory::times(2)->create(); + $firstWallet = $first->wallet; + $secondWallet = $second->wallet; + + self::assertSame(0, $firstWallet->balanceInt); + + $transaction = $firstWallet->deposit(1000, [ + 'desc' => 'unconfirmed', + ], false); + self::assertSame(0, $firstWallet->balanceInt); + self::assertFalse($transaction->confirmed); + + $secondWallet->forceConfirm($transaction); + } + public function testUserConfirm(): void { /** @var UserConfirm $userConfirm */ From dfcf330ffbd0410d684bea274ab69fb3faac61da Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Wed, 21 Aug 2024 19:58:13 +0300 Subject: [PATCH 4/8] Revert "remove ignored lines" This reverts commit 8570ed7ca93f93e8600f777e5e783eb4e4e4b8bd. --- phpstan.src.baseline.neon | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/phpstan.src.baseline.neon b/phpstan.src.baseline.neon index 5899b31d6..ce5b7ad55 100644 --- a/phpstan.src.baseline.neon +++ b/phpstan.src.baseline.neon @@ -195,6 +195,11 @@ parameters: count: 1 path: src/Models/Transaction.php + - + message: "#^Property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$table \\(string\\) does not accept mixed\\.$#" + count: 1 + path: src/Models/Transaction.php + - message: "#^Unable to resolve the template type TRelatedModel in call to method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:belongsTo\\(\\)$#" count: 1 @@ -225,6 +230,11 @@ parameters: count: 4 path: src/Models/Transfer.php + - + message: "#^Property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$table \\(string\\) does not accept mixed\\.$#" + count: 1 + path: src/Models/Transfer.php + - message: "#^Unable to resolve the template type TRelatedModel in call to method Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:belongsTo\\(\\)$#" count: 4 @@ -260,6 +270,11 @@ parameters: count: 2 path: src/Models/Wallet.php + - + message: "#^Property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$table \\(string\\) does not accept mixed\\.$#" + count: 1 + path: src/Models/Wallet.php + - message: "#^Parameter \\#1 \\$objects \\(non\\-empty\\-array\\\\) of method Bavix\\\\Wallet\\\\Services\\\\AssistantService\\:\\:getUuids\\(\\) should be compatible with parameter \\$objects \\(non\\-empty\\-array\\\\) of method Bavix\\\\Wallet\\\\Services\\\\AssistantServiceInterface\\:\\:getUuids\\(\\)$#" count: 1 From b6648a12e0d42a19782ae8b32df07ff357593000 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Wed, 21 Aug 2024 19:59:03 +0300 Subject: [PATCH 5/8] fix phpstan --- phpstan.src.baseline.neon | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/phpstan.src.baseline.neon b/phpstan.src.baseline.neon index ce5b7ad55..e574d4ab3 100644 --- a/phpstan.src.baseline.neon +++ b/phpstan.src.baseline.neon @@ -196,7 +196,7 @@ parameters: path: src/Models/Transaction.php - - message: "#^Property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$table \\(string\\) does not accept mixed\\.$#" + message: "#^Property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$table \\(string\\|null\\) does not accept mixed\\.$#" count: 1 path: src/Models/Transaction.php @@ -231,7 +231,7 @@ parameters: path: src/Models/Transfer.php - - message: "#^Property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$table \\(string\\) does not accept mixed\\.$#" + message: "#^Property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$table \\(string\\|null\\) does not accept mixed\\.$#" count: 1 path: src/Models/Transfer.php @@ -271,7 +271,7 @@ parameters: path: src/Models/Wallet.php - - message: "#^Property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$table \\(string\\) does not accept mixed\\.$#" + message: "#^Property Illuminate\\\\Database\\\\Eloquent\\\\Model\\:\\:\\$table \\(string\\|null\\) does not accept mixed\\.$#" count: 1 path: src/Models/Wallet.php From 58a4e1f9f64bbdf4995251269459caa42a48f962 Mon Sep 17 00:00:00 2001 From: Github bot Date: Wed, 21 Aug 2024 17:06:05 +0000 Subject: [PATCH 6/8] autofix --- src/Traits/CanConfirm.php | 2 +- tests/Units/Domain/ConfirmTest.php | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Traits/CanConfirm.php b/src/Traits/CanConfirm.php index 3dbfeb4f5..0946239a0 100644 --- a/src/Traits/CanConfirm.php +++ b/src/Traits/CanConfirm.php @@ -50,7 +50,7 @@ public function confirm(Transaction $transaction): bool // Check if the transaction is already confirmed. // If it is, throw an exception. if ($transaction->confirmed) { - // Why is there a check here without calling refresh? + // Why is there a check here without calling refresh? // It's because this check can be performed in force confirm again. throw new ConfirmedInvalid( // Get the error message from the translator service. diff --git a/tests/Units/Domain/ConfirmTest.php b/tests/Units/Domain/ConfirmTest.php index 970fa37bd..834d59546 100644 --- a/tests/Units/Domain/ConfirmTest.php +++ b/tests/Units/Domain/ConfirmTest.php @@ -75,7 +75,9 @@ public function testSafeConfirmedInvalid(): void self::assertSame(0, $wallet->balanceInt); - $transaction = $wallet->forceWithdraw(1000, ['desc' => 'confirmed']); + $transaction = $wallet->forceWithdraw(1000, [ + 'desc' => 'confirmed', + ]); self::assertSame(-1000, $wallet->balanceInt); self::assertTrue($transaction->confirmed); @@ -238,9 +240,9 @@ public function testSafeUnconfirmed(): void public function testSafeUnconfirmedWalletOwnerInvalid(): void { - /** - * @var Buyer $buyer1 - * @var Buyer $buyer2 + /** + * @var Buyer $buyer1 + * @var Buyer $buyer2 **/ [$buyer1, $buyer2] = BuyerFactory::times(2)->create(); $wallet1 = $buyer1->wallet; From d954aa2283d2a033877f1910d726862409f192d2 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Wed, 21 Aug 2024 20:06:14 +0300 Subject: [PATCH 7/8] fix --- tests/Units/Domain/EagerLoadingTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Units/Domain/EagerLoadingTest.php b/tests/Units/Domain/EagerLoadingTest.php index 795a05256..3abaada96 100644 --- a/tests/Units/Domain/EagerLoadingTest.php +++ b/tests/Units/Domain/EagerLoadingTest.php @@ -89,7 +89,7 @@ public function testMultiWallets(): void $user = UserMulti::with('wallets.walletTransactions')->find($multi->getKey()); self::assertTrue($user->relationLoaded('wallets')); self::assertNotEmpty($user->wallets); - self::assertTrue($user->wallets[0]->relationLoaded('walletTransactions')); + self::assertTrue($user->wallets[0]?->relationLoaded('walletTransactions')); self::assertNotNull($user->getWallet('hello')); self::assertNotNull($user->getWallet('world')); self::assertTrue($user->getWallet('hello')->relationLoaded('holder')); From 7a4dc4b46a322c48029037ef13f56c930066cd5c Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Wed, 21 Aug 2024 20:12:12 +0300 Subject: [PATCH 8/8] refactor for phpstan --- tests/Units/Domain/EagerLoadingTest.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/Units/Domain/EagerLoadingTest.php b/tests/Units/Domain/EagerLoadingTest.php index 3abaada96..172d4d2fb 100644 --- a/tests/Units/Domain/EagerLoadingTest.php +++ b/tests/Units/Domain/EagerLoadingTest.php @@ -89,7 +89,11 @@ public function testMultiWallets(): void $user = UserMulti::with('wallets.walletTransactions')->find($multi->getKey()); self::assertTrue($user->relationLoaded('wallets')); self::assertNotEmpty($user->wallets); - self::assertTrue($user->wallets[0]?->relationLoaded('walletTransactions')); + + foreach ($user->wallets as $wallet) { + self::assertTrue($wallet->relationLoaded('walletTransactions')); + } + self::assertNotNull($user->getWallet('hello')); self::assertNotNull($user->getWallet('world')); self::assertTrue($user->getWallet('hello')->relationLoaded('holder'));