Skip to content

Commit

Permalink
Merge pull request #987 from bavix/unit-wallets.walletTransactions
Browse files Browse the repository at this point in the history
[11.2] Improvements to confirm
  • Loading branch information
rez1dent3 authored Aug 21, 2024
2 parents 7806d59 + 7a4dc4b commit dc15953
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 12 deletions.
6 changes: 3 additions & 3 deletions phpstan.src.baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
28 changes: 21 additions & 7 deletions src/Traits/CanConfirm.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
}
Expand Down Expand Up @@ -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'),
Expand Down Expand Up @@ -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
Expand Down
90 changes: 90 additions & 0 deletions tests/Units/Domain/ConfirmTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,27 @@ 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 */
Expand Down Expand Up @@ -127,6 +148,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 */
Expand Down Expand Up @@ -198,6 +238,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);
Expand All @@ -223,6 +288,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 */
Expand Down
11 changes: 9 additions & 2 deletions tests/Units/Domain/EagerLoadingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public function testUuidDuplicate(): void
}

/** @var Collection<int, Buyer> $buyers */
$buyers = Buyer::with('wallet')
$buyers = Buyer::with('wallet.walletTransactions')
->whereIn('id', $buyerTimes->pluck('id')->toArray())
->paginate(10);

Expand All @@ -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;
Expand Down Expand Up @@ -85,8 +86,14 @@ 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);

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'));
Expand Down

0 comments on commit dc15953

Please sign in to comment.