Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[11.2] Improvements to confirm #987

Merged
merged 9 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading