Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2513065
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
60 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/app/Console/Commands/WalletAddTransaction.php b/src/app/Console/Commands/Wallet/AddTransactionCommand.php
similarity index 76%
rename from src/app/Console/Commands/WalletAddTransaction.php
rename to src/app/Console/Commands/Wallet/AddTransactionCommand.php
index 2c52a811..907d66c3 100644
--- a/src/app/Console/Commands/WalletAddTransaction.php
+++ b/src/app/Console/Commands/Wallet/AddTransactionCommand.php
@@ -1,46 +1,47 @@
<?php
-namespace App\Console\Commands;
+namespace App\Console\Commands\Wallet;
use App\Console\Command;
-class WalletAddTransaction extends Command
+class AddTransactionCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'wallet:add-transaction {wallet} {qty} {--message=}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Add a transaction to a wallet';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$wallet = $this->getWallet($this->argument('wallet'));
if (!$wallet) {
+ $this->error("Wallet not found.");
return 1;
}
$qty = (int) $this->argument('qty');
- $message = $this->option('message');
+ $message = (string) $this->option('message');
if ($qty < 0) {
- $wallet->debit($qty, $message);
+ $wallet->debit(-$qty, $message);
} else {
$wallet->credit($qty, $message);
}
}
}
diff --git a/src/app/Console/Commands/WalletBalances.php b/src/app/Console/Commands/Wallet/BalancesCommand.php
similarity index 81%
rename from src/app/Console/Commands/WalletBalances.php
rename to src/app/Console/Commands/Wallet/BalancesCommand.php
index d0821f4b..12d0a48b 100644
--- a/src/app/Console/Commands/WalletBalances.php
+++ b/src/app/Console/Commands/Wallet/BalancesCommand.php
@@ -1,60 +1,54 @@
<?php
-namespace App\Console\Commands;
+namespace App\Console\Commands\Wallet;
use Illuminate\Console\Command;
-class WalletBalances extends Command
+class BalancesCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'wallet:balances';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Show the balance on wallets';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$wallets = \App\Wallet::select('wallets.*')
->join('users', 'users.id', '=', 'wallets.user_id')
->withEnvTenantContext('users')
- ->all();
+ ->where('balance', '!=', '0')
+ ->whereNull('users.deleted_at')
+ ->orderBy('balance');
$wallets->each(
function ($wallet) {
- if ($wallet->balance == 0) {
- return;
- }
-
$user = $wallet->owner;
- if (!$user) {
- return;
- }
-
$this->info(
sprintf(
"%s: %8s (account: %s/%s (%s))",
$wallet->id,
$wallet->balance,
"https://kolabnow.com/cockpit/admin/accounts/show",
$user->id,
$user->email
)
);
}
);
}
}
diff --git a/src/app/Console/Commands/WalletCharge.php b/src/app/Console/Commands/Wallet/ChargeCommand.php
similarity index 81%
rename from src/app/Console/Commands/WalletCharge.php
rename to src/app/Console/Commands/Wallet/ChargeCommand.php
index 6ca62cb0..268ae556 100644
--- a/src/app/Console/Commands/WalletCharge.php
+++ b/src/app/Console/Commands/Wallet/ChargeCommand.php
@@ -1,67 +1,72 @@
<?php
-namespace App\Console\Commands;
+namespace App\Console\Commands\Wallet;
use App\Console\Command;
-use App\Wallet;
-class WalletCharge extends Command
+class ChargeCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'wallet:charge {wallet?}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Charge wallets';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
if ($wallet = $this->argument('wallet')) {
// Find specified wallet by ID
$wallet = $this->getWallet($wallet);
- if (!$wallet || !$wallet->owner) {
+ if (!$wallet) {
+ $this->error("Wallet not found.");
+ return 1;
+ }
+
+ if (!$wallet->owner) {
+ $this->error("Wallet's owner is deleted.");
return 1;
}
$wallets = [$wallet];
} else {
// Get all wallets, excluding deleted accounts
- $wallets = Wallet::select('wallets.*')
+ $wallets = \App\Wallet::select('wallets.*')
->join('users', 'users.id', '=', 'wallets.user_id')
->withEnvTenantContext('users')
->whereNull('users.deleted_at')
->cursor();
}
foreach ($wallets as $wallet) {
$charge = $wallet->chargeEntitlements();
if ($charge > 0) {
$this->info(
"Charged wallet {$wallet->id} for user {$wallet->owner->email} with {$charge}"
);
// Top-up the wallet if auto-payment enabled for the wallet
\App\Jobs\WalletCharge::dispatch($wallet);
}
if ($wallet->balance < 0) {
// Check the account balance, send notifications, suspend, delete
\App\Jobs\WalletCheck::dispatch($wallet);
}
}
}
}
diff --git a/src/app/Console/Commands/WalletExpected.php b/src/app/Console/Commands/Wallet/ExpectedCommand.php
similarity index 80%
rename from src/app/Console/Commands/WalletExpected.php
rename to src/app/Console/Commands/Wallet/ExpectedCommand.php
index 8bd2a523..a799a0f4 100644
--- a/src/app/Console/Commands/WalletExpected.php
+++ b/src/app/Console/Commands/Wallet/ExpectedCommand.php
@@ -1,68 +1,64 @@
<?php
-namespace App\Console\Commands;
+namespace App\Console\Commands\Wallet;
use App\Console\Command;
-class WalletExpected extends Command
+class ExpectedCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'wallet:expected {--user=} {--non-zero}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Show expected charges to wallets (for user)';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
if ($this->option('user')) {
$user = $this->getUser($this->option('user'));
if (!$user) {
+ $this->error("User not found.");
return 1;
}
$wallets = $user->wallets;
} else {
$wallets = \App\Wallet::select('wallets.*')
->join('users', 'users.id', '=', 'wallets.user_id')
->withEnvTenantContext('users')
- ->all();
+ ->whereNull('users.deleted_at');
}
- foreach ($wallets as $wallet) {
+ $wallets->each(function ($wallet) {
$charge = 0;
$expected = $wallet->expectedCharges();
- if (!$wallet->owner) {
- \Log::debug("{$wallet->id} has no owner: {$wallet->user_id}");
- continue;
- }
-
if ($this->option('non-zero') && $expected < 1) {
- continue;
+ return;
}
$this->info(
sprintf(
"expect charging wallet %s for user %s with %d",
$wallet->id,
$wallet->owner->email,
$expected
)
);
- }
+ });
}
}
diff --git a/src/app/Console/Commands/WalletGetBalance.php b/src/app/Console/Commands/Wallet/GetBalanceCommand.php
similarity index 83%
rename from src/app/Console/Commands/WalletGetBalance.php
rename to src/app/Console/Commands/Wallet/GetBalanceCommand.php
index 661177dc..e459d604 100644
--- a/src/app/Console/Commands/WalletGetBalance.php
+++ b/src/app/Console/Commands/Wallet/GetBalanceCommand.php
@@ -1,38 +1,39 @@
<?php
-namespace App\Console\Commands;
+namespace App\Console\Commands\Wallet;
use App\Console\Command;
-class WalletGetBalance extends Command
+class GetBalanceCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'wallet:get-balance {wallet}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Display the balance of a wallet';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$wallet = $this->getWallet($this->argument('wallet'));
if (!$wallet) {
+ $this->error("Wallet not found.");
return 1;
}
$this->info($wallet->balance);
}
}
diff --git a/src/app/Console/Commands/WalletGetDiscount.php b/src/app/Console/Commands/Wallet/GetDiscountCommand.php
similarity index 85%
rename from src/app/Console/Commands/WalletGetDiscount.php
rename to src/app/Console/Commands/Wallet/GetDiscountCommand.php
index 6fdbf0ca..52378f67 100644
--- a/src/app/Console/Commands/WalletGetDiscount.php
+++ b/src/app/Console/Commands/Wallet/GetDiscountCommand.php
@@ -1,43 +1,44 @@
<?php
-namespace App\Console\Commands;
+namespace App\Console\Commands\Wallet;
use App\Console\Command;
-class WalletGetDiscount extends Command
+class GetDiscountCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'wallet:get-discount {wallet}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Display the existing discount to a wallet, if any.';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$wallet = $this->getWallet($this->argument('wallet'));
if (!$wallet) {
+ $this->error("Wallet not found.");
return 1;
}
if (!$wallet->discount) {
$this->info("No discount on this wallet.");
return 0;
}
$this->info($wallet->discount->discount);
}
}
diff --git a/src/app/Console/Commands/WalletMandate.php b/src/app/Console/Commands/Wallet/MandateCommand.php
similarity index 88%
rename from src/app/Console/Commands/WalletMandate.php
rename to src/app/Console/Commands/Wallet/MandateCommand.php
index f13265c5..c81d5f7c 100644
--- a/src/app/Console/Commands/WalletMandate.php
+++ b/src/app/Console/Commands/Wallet/MandateCommand.php
@@ -1,60 +1,61 @@
<?php
-namespace App\Console\Commands;
+namespace App\Console\Commands\Wallet;
use App\Console\Command;
use App\Http\Controllers\API\V4\PaymentsController;
-class WalletMandate extends Command
+class MandateCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'wallet:mandate {wallet} {--disable}{--enable}';
/**
* The console command description.
*
* @var string
*/
- protected $description = 'Show expected charges to wallets';
+ protected $description = 'Show wallet auto-payment mandate information.';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$wallet = $this->getWallet($this->argument('wallet'));
if (!$wallet) {
+ $this->error("Wallet not found.");
return 1;
}
$mandate = PaymentsController::walletMandate($wallet);
if (!empty($mandate['id'])) {
$disabled = $mandate['isDisabled'] ? 'Yes' : 'No';
if ($this->option('disable') && $disabled == 'No') {
$wallet->setSetting('mandate_disabled', 1);
$disabled = 'Yes';
} elseif ($this->option('enable') && $disabled == 'Yes') {
$wallet->setSetting('mandate_disabled', null);
$disabled = 'No';
}
$this->info("Auto-payment: {$mandate['method']}");
$this->info(" id: {$mandate['id']}");
$this->info(" status: " . ($mandate['isPending'] ? 'pending' : 'valid'));
$this->info(" amount: {$mandate['amount']} {$wallet->currency}");
$this->info(" min-balance: {$mandate['balance']} {$wallet->currency}");
$this->info(" disabled: $disabled");
} else {
$this->info("Auto-payment: none");
}
}
}
diff --git a/src/app/Console/Commands/WalletSetBalance.php b/src/app/Console/Commands/Wallet/SetBalanceCommand.php
similarity index 84%
rename from src/app/Console/Commands/WalletSetBalance.php
rename to src/app/Console/Commands/Wallet/SetBalanceCommand.php
index d3a082bc..7ed57996 100644
--- a/src/app/Console/Commands/WalletSetBalance.php
+++ b/src/app/Console/Commands/Wallet/SetBalanceCommand.php
@@ -1,39 +1,40 @@
<?php
-namespace App\Console\Commands;
+namespace App\Console\Commands\Wallet;
use App\Console\Command;
-class WalletSetBalance extends Command
+class SetBalanceCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'wallet:set-balance {wallet} {balance}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Set the balance of a wallet';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$wallet = $this->getWallet($this->argument('wallet'));
if (!$wallet) {
+ $this->error("Wallet not found.");
return 1;
}
$wallet->balance = (int) $this->argument('balance');
$wallet->save();
}
}
diff --git a/src/app/Console/Commands/WalletSetDiscount.php b/src/app/Console/Commands/Wallet/SetDiscountCommand.php
similarity index 85%
rename from src/app/Console/Commands/WalletSetDiscount.php
rename to src/app/Console/Commands/Wallet/SetDiscountCommand.php
index 50e4e9d6..ee48af92 100644
--- a/src/app/Console/Commands/WalletSetDiscount.php
+++ b/src/app/Console/Commands/Wallet/SetDiscountCommand.php
@@ -1,52 +1,54 @@
<?php
-namespace App\Console\Commands;
+namespace App\Console\Commands\Wallet;
use App\Console\Command;
-class WalletSetDiscount extends Command
+class SetDiscountCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'wallet:set-discount {wallet} {discount}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Apply a discount to a wallet';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$wallet = $this->getWallet($this->argument('wallet'));
if (!$wallet) {
+ $this->error("Wallet not found.");
return 1;
}
// FIXME: Using '0' for delete might be not that obvious
if ($this->argument('discount') === '0') {
$wallet->discount()->dissociate();
} else {
$discount = $this->getObject(\App\Discount::class, $this->argument('discount'));
if (!$discount) {
+ $this->error("Discount not found.");
return 1;
}
$wallet->discount()->associate($discount);
}
$wallet->save();
}
}
diff --git a/src/app/Console/Commands/WalletSettingsCommand.php b/src/app/Console/Commands/Wallet/SettingsCommand.php
similarity index 68%
rename from src/app/Console/Commands/WalletSettingsCommand.php
rename to src/app/Console/Commands/Wallet/SettingsCommand.php
index a6a7afc2..ac135df0 100644
--- a/src/app/Console/Commands/WalletSettingsCommand.php
+++ b/src/app/Console/Commands/Wallet/SettingsCommand.php
@@ -1,12 +1,12 @@
<?php
-namespace App\Console\Commands;
+namespace App\Console\Commands\Wallet;
use App\Console\ObjectListCommand;
-class WalletSettingsCommand extends ObjectListCommand
+class SettingsCommand extends ObjectListCommand
{
protected $objectClass = \App\WalletSetting::class;
protected $objectName = 'wallet-setting';
protected $objectTitle = null;
}
diff --git a/src/app/Console/Commands/WalletTransactions.php b/src/app/Console/Commands/Wallet/TransactionsCommand.php
similarity index 91%
rename from src/app/Console/Commands/WalletTransactions.php
rename to src/app/Console/Commands/Wallet/TransactionsCommand.php
index ed36d08b..70ad541b 100644
--- a/src/app/Console/Commands/WalletTransactions.php
+++ b/src/app/Console/Commands/Wallet/TransactionsCommand.php
@@ -1,62 +1,63 @@
<?php
-namespace App\Console\Commands;
+namespace App\Console\Commands\Wallet;
use App\Console\Command;
-class WalletTransactions extends Command
+class TransactionsCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'wallet:transactions {--detail} {wallet}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'List the transactions against a wallet.';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$wallet = $this->getWallet($this->argument('wallet'));
if (!$wallet) {
+ $this->error("Wallet not found.");
return 1;
}
$wallet->transactions()->orderBy('created_at')->each(function ($transaction) {
$this->info(
sprintf(
"%s: %s %s",
$transaction->id,
$transaction->created_at,
$transaction->toString()
)
);
if ($this->option('detail')) {
$elements = \App\Transaction::where('transaction_id', $transaction->id)
->orderBy('created_at')->get();
foreach ($elements as $element) {
$this->info(
sprintf(
" + %s: %s",
$element->id,
$element->toString()
)
);
}
}
});
}
}
diff --git a/src/app/Console/Commands/WalletUntil.php b/src/app/Console/Commands/Wallet/UntilCommand.php
similarity index 85%
rename from src/app/Console/Commands/WalletUntil.php
rename to src/app/Console/Commands/Wallet/UntilCommand.php
index 8d4d7eb2..69c97f14 100644
--- a/src/app/Console/Commands/WalletUntil.php
+++ b/src/app/Console/Commands/Wallet/UntilCommand.php
@@ -1,40 +1,41 @@
<?php
-namespace App\Console\Commands;
+namespace App\Console\Commands\Wallet;
use App\Console\Command;
-class WalletUntil extends Command
+class UntilCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'wallet:until {wallet}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Show until when the balance on a wallet lasts.';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$wallet = $this->getWallet($this->argument('wallet'));
if (!$wallet) {
+ $this->error("Wallet not found.");
return 1;
}
$until = $wallet->balanceLastsUntil();
$this->info("Lasts until: " . ($until ? $until->toDateString() : 'unknown'));
}
}
diff --git a/src/app/Console/Commands/WalletDiscount.php b/src/app/Console/Commands/WalletDiscount.php
deleted file mode 100644
index 30327913..00000000
--- a/src/app/Console/Commands/WalletDiscount.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-namespace App\Console\Commands;
-
-use App\Console\Command;
-
-class WalletDiscount extends Command
-{
- /**
- * The name and signature of the console command.
- *
- * @var string
- */
- protected $signature = 'wallet:discount {wallet} {discount}';
-
- /**
- * The console command description.
- *
- * @var string
- */
- protected $description = 'Apply a discount to a wallet';
-
- /**
- * Execute the console command.
- *
- * @return mixed
- */
- public function handle()
- {
- $wallet = $this->getWallet($this->argument('wallet'));
-
- if (!$wallet) {
- return 1;
- }
-
- // FIXME: Using '0' for delete might be not that obvious
-
- if ($this->argument('discount') === '0') {
- $wallet->discount()->dissociate();
- } else {
- $discount = $this->getObject(\App\Discount::class, $this->argument('discount'));
-
- if (!$discount) {
- return 1;
- }
-
- $wallet->discount()->associate($discount);
- }
-
- $wallet->save();
- }
-}
diff --git a/src/tests/Feature/Console/Wallet/AddTransactionTest.php b/src/tests/Feature/Console/Wallet/AddTransactionTest.php
new file mode 100644
index 00000000..08d29b8d
--- /dev/null
+++ b/src/tests/Feature/Console/Wallet/AddTransactionTest.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace Tests\Feature\Console\Wallet;
+
+use Tests\TestCase;
+
+class AddTransactionTest extends TestCase
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ $this->deleteTestUser('wallets-controller@kolabnow.com');
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ $this->deleteTestUser('wallets-controller@kolabnow.com');
+
+ parent::tearDown();
+ }
+
+ /**
+ * Test command run for a specified wallet
+ */
+ public function testHandle(): void
+ {
+ $user = $this->getTestUser('wallets-controller@kolabnow.com');
+ $wallet = $user->wallets()->first();
+
+ // Invalid wallet id
+ $code = \Artisan::call("wallet:add-transaction 123 100");
+ $output = trim(\Artisan::output());
+ $this->assertSame(1, $code);
+ $this->assertSame("Wallet not found.", $output);
+
+ // Add credit
+ $code = \Artisan::call("wallet:add-transaction {$wallet->id} 100");
+ $output = trim(\Artisan::output());
+ $this->assertSame(0, $code);
+ $this->assertSame("", $output);
+ $wallet->refresh();
+ $this->assertSame(100, $wallet->balance);
+
+ // Add debit with a transaction description
+ // Note: The double-dash trick to make it working with a negative number input
+ $code = \Artisan::call("wallet:add-transaction --message=debit -- {$wallet->id} -100");
+ $output = trim(\Artisan::output());
+ $this->assertSame(0, $code);
+ $this->assertSame("", $output);
+ $wallet->refresh();
+ $this->assertSame(0, $wallet->balance);
+ $this->assertCount(1, $wallet->transactions()->where('description', 'debit')->get());
+ }
+}
diff --git a/src/tests/Feature/Console/Wallet/BalancesTest.php b/src/tests/Feature/Console/Wallet/BalancesTest.php
new file mode 100644
index 00000000..6f3bb5e4
--- /dev/null
+++ b/src/tests/Feature/Console/Wallet/BalancesTest.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace Tests\Feature\Console\Wallet;
+
+use Illuminate\Support\Facades\Queue;
+use Tests\TestCase;
+
+class BalancesTest extends TestCase
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ $this->deleteTestUser('wallets-controller@kolabnow.com');
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ $this->deleteTestUser('wallets-controller@kolabnow.com');
+
+ parent::tearDown();
+ }
+
+ /**
+ * Test command run for a specified wallet
+ */
+ public function testHandle(): void
+ {
+ Queue::fake();
+
+ $user = $this->getTestUser('wallets-controller@kolabnow.com');
+ $wallet = $user->wallets()->first();
+
+ // Expect no wallets with balance=0
+ $code = \Artisan::call("wallet:balances");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(0, $code);
+ $this->assertTrue(strpos($output, $wallet->id) === false);
+
+ $wallet->balance = -100;
+ $wallet->save();
+
+ // Expect the wallet with a negative balance in output
+ $code = \Artisan::call("wallet:balances");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(0, $code);
+ $this->assertMatchesRegularExpression(
+ '|' . preg_quote($wallet->id, '|') . ': {5}-100 \(account: https://.*/admin/accounts/show/'
+ . $user->id . ' \(' . preg_quote($user->email, '|') . '\)\)|',
+ $output
+ );
+
+ $user->delete();
+
+ // Expect no wallet with deleted owner in output
+ $code = \Artisan::call("wallet:balances");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(0, $code);
+ $this->assertTrue(strpos($output, $wallet->id) === false);
+ }
+}
diff --git a/src/tests/Feature/Console/WalletChargeTest.php b/src/tests/Feature/Console/Wallet/ChargeTest.php
similarity index 96%
rename from src/tests/Feature/Console/WalletChargeTest.php
rename to src/tests/Feature/Console/Wallet/ChargeTest.php
index ec681731..628114eb 100644
--- a/src/tests/Feature/Console/WalletChargeTest.php
+++ b/src/tests/Feature/Console/Wallet/ChargeTest.php
@@ -1,146 +1,147 @@
<?php
-namespace Tests\Feature\Console;
+namespace Tests\Feature\Console\Wallet;
use Illuminate\Support\Facades\Queue;
use Tests\TestCase;
-class WalletChargeTest extends TestCase
+class ChargeTest extends TestCase
{
/**
* {@inheritDoc}
*/
public function setUp(): void
{
parent::setUp();
$this->deleteTestUser('wallet-charge@kolabnow.com');
}
/**
* {@inheritDoc}
*/
public function tearDown(): void
{
$this->deleteTestUser('wallet-charge@kolabnow.com');
parent::tearDown();
}
/**
* Test command run for a specified wallet
*/
public function testHandleSingle(): void
{
$user = $this->getTestUser('wallet-charge@kolabnow.com');
$wallet = $user->wallets()->first();
$wallet->balance = 0;
$wallet->save();
Queue::fake();
// Non-existing wallet ID
$this->artisan('wallet:charge 123')
- ->assertExitCode(1);
+ ->assertExitCode(1)
+ ->expectsOutput("Wallet not found.");
Queue::assertNothingPushed();
// The wallet has no entitlements, expect no charge and no check
$this->artisan('wallet:charge ' . $wallet->id)
->assertExitCode(0);
Queue::assertNothingPushed();
// The wallet has no entitlements, but has negative balance
$wallet->balance = -100;
$wallet->save();
$this->artisan('wallet:charge ' . $wallet->id)
->assertExitCode(0);
Queue::assertPushed(\App\Jobs\WalletCharge::class, 0);
Queue::assertPushed(\App\Jobs\WalletCheck::class, 1);
Queue::assertPushed(\App\Jobs\WalletCheck::class, function ($job) use ($wallet) {
$job_wallet = TestCase::getObjectProperty($job, 'wallet');
return $job_wallet->id === $wallet->id;
});
Queue::fake();
// The wallet has entitlements to charge, and negative balance
$sku = \App\Sku::where('title', 'mailbox')->first();
$entitlement = \App\Entitlement::create([
'wallet_id' => $wallet->id,
'sku_id' => $sku->id,
'cost' => 100,
'entitleable_id' => $user->id,
'entitleable_type' => \App\User::class,
]);
\App\Entitlement::where('id', $entitlement->id)->update([
'created_at' => \Carbon\Carbon::now()->subMonths(1),
'updated_at' => \Carbon\Carbon::now()->subMonths(1),
]);
\App\User::where('id', $user->id)->update([
'created_at' => \Carbon\Carbon::now()->subMonths(1),
'updated_at' => \Carbon\Carbon::now()->subMonths(1),
]);
$this->assertSame(100, $wallet->fresh()->chargeEntitlements(false));
$this->artisan('wallet:charge ' . $wallet->id)
->assertExitCode(0);
Queue::assertPushed(\App\Jobs\WalletCharge::class, 1);
Queue::assertPushed(\App\Jobs\WalletCharge::class, function ($job) use ($wallet) {
$job_wallet = TestCase::getObjectProperty($job, 'wallet');
return $job_wallet->id === $wallet->id;
});
Queue::assertPushed(\App\Jobs\WalletCheck::class, 1);
Queue::assertPushed(\App\Jobs\WalletCheck::class, function ($job) use ($wallet) {
$job_wallet = TestCase::getObjectProperty($job, 'wallet');
return $job_wallet->id === $wallet->id;
});
}
/**
* Test command run for all wallets
*/
public function testHandleAll(): void
{
$user = $this->getTestUser('john@kolab.org');
$wallet = $user->wallets()->first();
$wallet->balance = 0;
$wallet->save();
// backdate john's entitlements and set balance=0 for all wallets
$this->backdateEntitlements($user->entitlements, \Carbon\Carbon::now()->subWeeks(5));
\App\Wallet::where('balance', '<', '0')->update(['balance' => 0]);
$user2 = $this->getTestUser('wallet-charge@kolabnow.com');
$wallet2 = $user2->wallets()->first();
$wallet2->balance = -100;
$wallet2->save();
Queue::fake();
// Non-existing wallet ID
$this->artisan('wallet:charge')->assertExitCode(0);
Queue::assertPushed(\App\Jobs\WalletCheck::class, 2);
Queue::assertPushed(\App\Jobs\WalletCheck::class, function ($job) use ($wallet) {
$job_wallet = TestCase::getObjectProperty($job, 'wallet');
return $job_wallet->id === $wallet->id;
});
Queue::assertPushed(\App\Jobs\WalletCheck::class, function ($job) use ($wallet2) {
$job_wallet = TestCase::getObjectProperty($job, 'wallet');
return $job_wallet->id === $wallet2->id;
});
Queue::assertPushed(\App\Jobs\WalletCharge::class, 1);
Queue::assertPushed(\App\Jobs\WalletCharge::class, function ($job) use ($wallet) {
$job_wallet = TestCase::getObjectProperty($job, 'wallet');
return $job_wallet->id === $wallet->id;
});
}
}
diff --git a/src/tests/Feature/Console/Wallet/ExpectedTest.php b/src/tests/Feature/Console/Wallet/ExpectedTest.php
new file mode 100644
index 00000000..ebbdba6f
--- /dev/null
+++ b/src/tests/Feature/Console/Wallet/ExpectedTest.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace Tests\Feature\Console\Wallet;
+
+use Illuminate\Support\Facades\Queue;
+use Tests\TestCase;
+
+class ExpectedTest extends TestCase
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ $this->deleteTestUser('wallets-controller@kolabnow.com');
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ $this->deleteTestUser('wallets-controller@kolabnow.com');
+
+ parent::tearDown();
+ }
+
+ /**
+ * Test command run for a specified wallet
+ */
+ public function testHandle(): void
+ {
+ Queue::fake();
+
+ $user = $this->getTestUser('wallets-controller@kolabnow.com');
+ $wallet = $user->wallets()->first();
+
+ // Non-existing user
+ $code = \Artisan::call("wallet:expected --user=123");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(1, $code);
+ $this->assertSame("User not found.", $output);
+
+ // Expected charges for a specified user
+ $code = \Artisan::call("wallet:expected --user={$user->id}");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(0, $code);
+ $this->assertMatchesRegularExpression(
+ "|expect charging wallet {$wallet->id} for user {$user->email} with 0|",
+ $output
+ );
+
+ // Test --non-zero argument
+ $code = \Artisan::call("wallet:expected --user={$user->id} --non-zero");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(0, $code);
+ $this->assertTrue(strpos($output, $wallet->id) === false);
+
+ // Expected charges for all wallets
+ $code = \Artisan::call("wallet:expected");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(0, $code);
+ $this->assertMatchesRegularExpression(
+ "|expect charging wallet {$wallet->id} for user {$user->email} with 0|",
+ $output
+ );
+ }
+}
diff --git a/src/tests/Feature/Console/Wallet/GetBalanceTest.php b/src/tests/Feature/Console/Wallet/GetBalanceTest.php
new file mode 100644
index 00000000..da8197ea
--- /dev/null
+++ b/src/tests/Feature/Console/Wallet/GetBalanceTest.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace Tests\Feature\Console\Wallet;
+
+use Illuminate\Support\Facades\Queue;
+use Tests\TestCase;
+
+class GetBalanceTest extends TestCase
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ $this->deleteTestUser('wallets-controller@kolabnow.com');
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ $this->deleteTestUser('wallets-controller@kolabnow.com');
+
+ parent::tearDown();
+ }
+
+ /**
+ * Test command run for a specified wallet
+ */
+ public function testHandle(): void
+ {
+ Queue::fake();
+
+ $user = $this->getTestUser('wallets-controller@kolabnow.com');
+ $wallet = $user->wallets()->first();
+
+ // Non-existing wallet
+ $code = \Artisan::call("wallet:get-balance 123");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(1, $code);
+ $this->assertSame("Wallet not found.", $output);
+
+ $wallet->balance = -100;
+ $wallet->save();
+
+ // Existing wallet
+ $code = \Artisan::call("wallet:get-balance {$wallet->id}");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(0, $code);
+ $this->assertSame('-100', $output);
+ }
+}
diff --git a/src/tests/Feature/Console/Wallet/GetDiscountTest.php b/src/tests/Feature/Console/Wallet/GetDiscountTest.php
new file mode 100644
index 00000000..a5ac029a
--- /dev/null
+++ b/src/tests/Feature/Console/Wallet/GetDiscountTest.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace Tests\Feature\Console\Wallet;
+
+use Illuminate\Support\Facades\Queue;
+use Tests\TestCase;
+
+class GetDiscountTest extends TestCase
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ $this->deleteTestUser('wallets-controller@kolabnow.com');
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ $this->deleteTestUser('wallets-controller@kolabnow.com');
+
+ parent::tearDown();
+ }
+
+ /**
+ * Test command run for a specified wallet
+ */
+ public function testHandle(): void
+ {
+ Queue::fake();
+
+ $user = $this->getTestUser('wallets-controller@kolabnow.com');
+ $wallet = $user->wallets()->first();
+
+ // Non-existing wallet
+ $code = \Artisan::call("wallet:get-discount 123");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(1, $code);
+ $this->assertSame("Wallet not found.", $output);
+
+ // No discount
+ $code = \Artisan::call("wallet:get-discount {$wallet->id}");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(0, $code);
+ $this->assertSame("No discount on this wallet.", $output);
+
+ $discount = \App\Discount::withObjectTenantContext($user)->where('discount', 100)->first();
+ $wallet->discount()->associate($discount);
+ $wallet->save();
+
+ // With discount
+ $code = \Artisan::call("wallet:get-discount {$wallet->id}");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(0, $code);
+ $this->assertSame("100", $output);
+ }
+}
diff --git a/src/tests/Feature/Console/Wallet/MandateTest.php b/src/tests/Feature/Console/Wallet/MandateTest.php
new file mode 100644
index 00000000..dca2449f
--- /dev/null
+++ b/src/tests/Feature/Console/Wallet/MandateTest.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace Tests\Feature\Console\Wallet;
+
+use Illuminate\Support\Facades\Queue;
+use Tests\TestCase;
+
+class MandateTest extends TestCase
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ $this->deleteTestUser('wallets-controller@kolabnow.com');
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ $this->deleteTestUser('wallets-controller@kolabnow.com');
+
+ parent::tearDown();
+ }
+
+ /**
+ * Test command run for a specified wallet
+ */
+ public function testHandle(): void
+ {
+ Queue::fake();
+
+ $user = $this->getTestUser('wallets-controller@kolabnow.com');
+ $wallet = $user->wallets()->first();
+
+ // Non-existing wallet
+ $code = \Artisan::call("wallet:mandate 123");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(1, $code);
+ $this->assertSame("Wallet not found.", $output);
+
+ // No mandate
+ $code = \Artisan::call("wallet:mandate {$wallet->id}");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(0, $code);
+ $this->assertSame("Auto-payment: none", $output);
+
+ // TODO: Test an existing mandate
+ $this->markTestIncomplete();
+ }
+}
diff --git a/src/tests/Feature/Console/Wallet/SetBalanceTest.php b/src/tests/Feature/Console/Wallet/SetBalanceTest.php
new file mode 100644
index 00000000..e3b7af08
--- /dev/null
+++ b/src/tests/Feature/Console/Wallet/SetBalanceTest.php
@@ -0,0 +1,56 @@
+<?php
+
+namespace Tests\Feature\Console\Wallet;
+
+use Illuminate\Support\Facades\Queue;
+use Tests\TestCase;
+
+class SetBalanceTest extends TestCase
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ $this->deleteTestUser('wallets-controller@kolabnow.com');
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ $this->deleteTestUser('wallets-controller@kolabnow.com');
+
+ parent::tearDown();
+ }
+
+ /**
+ * Test command run for a specified wallet
+ */
+ public function testHandle(): void
+ {
+ Queue::fake();
+
+ $user = $this->getTestUser('wallets-controller@kolabnow.com');
+ $wallet = $user->wallets()->first();
+
+ // Non-existing wallet
+ $code = \Artisan::call("wallet:set-balance 123 123");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(1, $code);
+ $this->assertSame("Wallet not found.", $output);
+
+ // Existing wallet
+ // Note: The double-dash trick to make it working with a negative number input
+ $code = \Artisan::call("wallet:set-balance -- {$wallet->id} -123");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(0, $code);
+ $this->assertSame('', $output);
+ $this->assertSame(-123, $wallet->fresh()->balance);
+ }
+}
diff --git a/src/tests/Feature/Console/Wallet/SetDiscountTest.php b/src/tests/Feature/Console/Wallet/SetDiscountTest.php
new file mode 100644
index 00000000..ef554f3d
--- /dev/null
+++ b/src/tests/Feature/Console/Wallet/SetDiscountTest.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace Tests\Feature\Console\Wallet;
+
+use Tests\TestCase;
+
+class SetDiscountTest extends TestCase
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ $this->deleteTestUser('wallets-controller@kolabnow.com');
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ $this->deleteTestUser('wallets-controller@kolabnow.com');
+
+ parent::tearDown();
+ }
+
+ /**
+ * Test command run for a specified wallet
+ */
+ public function testHandle(): void
+ {
+ $user = $this->getTestUser('wallets-controller@kolabnow.com');
+ $package = \App\Package::where('title', 'kolab')->first();
+ $user->assignPackage($package);
+ $wallet = $user->wallets()->first();
+ $discount = \App\Discount::withObjectTenantContext($user)->where('discount', 100)->first();
+
+ // Invalid wallet id
+ $code = \Artisan::call("wallet:set-discount 123 123");
+ $output = trim(\Artisan::output());
+ $this->assertSame(1, $code);
+ $this->assertSame("Wallet not found.", $output);
+
+ // Invalid discount id
+ $code = \Artisan::call("wallet:set-discount {$wallet->id} 123");
+ $output = trim(\Artisan::output());
+ $this->assertSame(1, $code);
+ $this->assertSame("Discount not found.", $output);
+
+ // Assign a discount
+ $code = \Artisan::call("wallet:set-discount {$wallet->id} {$discount->id}");
+ $output = trim(\Artisan::output());
+ $this->assertSame(0, $code);
+ $this->assertSame("", $output);
+ $wallet->refresh();
+ $this->assertSame($discount->id, $wallet->discount_id);
+
+ // Remove the discount
+ $code = \Artisan::call("wallet:set-discount {$wallet->id} 0");
+ $output = trim(\Artisan::output());
+ $this->assertSame(0, $code);
+ $this->assertSame("", $output);
+ $wallet->refresh();
+ $this->assertNull($wallet->discount_id);
+ }
+}
diff --git a/src/tests/Feature/Console/WalletDiscountTest.php b/src/tests/Feature/Console/Wallet/SettingsTest.php
similarity index 61%
copy from src/tests/Feature/Console/WalletDiscountTest.php
copy to src/tests/Feature/Console/Wallet/SettingsTest.php
index bb87edfc..7dfb608d 100644
--- a/src/tests/Feature/Console/WalletDiscountTest.php
+++ b/src/tests/Feature/Console/Wallet/SettingsTest.php
@@ -1,13 +1,13 @@
<?php
-namespace Tests\Feature\Console;
+namespace Tests\Feature\Console\Wallet;
use Tests\TestCase;
-class WalletDiscountTest extends TestCase
+class SettingsTest extends TestCase
{
public function testHandle(): void
{
$this->markTestIncomplete();
}
}
diff --git a/src/tests/Feature/Console/WalletDiscountTest.php b/src/tests/Feature/Console/Wallet/TransactionsTest.php
similarity index 60%
copy from src/tests/Feature/Console/WalletDiscountTest.php
copy to src/tests/Feature/Console/Wallet/TransactionsTest.php
index bb87edfc..a837ed9d 100644
--- a/src/tests/Feature/Console/WalletDiscountTest.php
+++ b/src/tests/Feature/Console/Wallet/TransactionsTest.php
@@ -1,13 +1,13 @@
<?php
-namespace Tests\Feature\Console;
+namespace Tests\Feature\Console\Wallet;
use Tests\TestCase;
-class WalletDiscountTest extends TestCase
+class TransactionsTest extends TestCase
{
public function testHandle(): void
{
$this->markTestIncomplete();
}
}
diff --git a/src/tests/Feature/Console/Wallet/UntilTest.php b/src/tests/Feature/Console/Wallet/UntilTest.php
new file mode 100644
index 00000000..b948d3fc
--- /dev/null
+++ b/src/tests/Feature/Console/Wallet/UntilTest.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace Tests\Feature\Console\Wallet;
+
+use Illuminate\Support\Facades\Queue;
+use Tests\TestCase;
+
+class UntilTest extends TestCase
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ $this->deleteTestUser('wallets-controller@kolabnow.com');
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ $this->deleteTestUser('wallets-controller@kolabnow.com');
+
+ parent::tearDown();
+ }
+
+ /**
+ * Test command run for a specified wallet
+ */
+ public function testHandle(): void
+ {
+ Queue::fake();
+
+ $user = $this->getTestUser('wallets-controller@kolabnow.com');
+ $wallet = $user->wallets()->first();
+
+ // Non-existing wallet
+ $code = \Artisan::call("wallet:until 123");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(1, $code);
+ $this->assertSame("Wallet not found.", $output);
+
+ // Existing wallet
+ $code = \Artisan::call("wallet:until {$wallet->id}");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(0, $code);
+ $this->assertSame("Lasts until: unknown", $output);
+
+ $package = \App\Package::withObjectTenantContext($user)->where('title', 'kolab')->first();
+ $user->assignPackage($package);
+ $wallet->balance = 1000;
+ $wallet->save();
+
+ $expected = \now()->addMonths(2)->toDateString();
+
+ // Existing wallet
+ $code = \Artisan::call("wallet:until {$wallet->id}");
+ $output = trim(\Artisan::output());
+
+ $this->assertSame(0, $code);
+ $this->assertSame("Lasts until: $expected", $output);
+ }
+}
diff --git a/src/tests/Feature/Console/WalletDiscountTest.php b/src/tests/Feature/Console/WalletsTest.php
similarity index 78%
rename from src/tests/Feature/Console/WalletDiscountTest.php
rename to src/tests/Feature/Console/WalletsTest.php
index bb87edfc..7348fc25 100644
--- a/src/tests/Feature/Console/WalletDiscountTest.php
+++ b/src/tests/Feature/Console/WalletsTest.php
@@ -1,13 +1,13 @@
<?php
namespace Tests\Feature\Console;
use Tests\TestCase;
-class WalletDiscountTest extends TestCase
+class WalletsTest extends TestCase
{
public function testHandle(): void
{
$this->markTestIncomplete();
}
}
diff --git a/src/tests/Feature/Controller/WalletsTest.php b/src/tests/Feature/Controller/WalletsTest.php
index b03f84e3..72785b3e 100644
--- a/src/tests/Feature/Controller/WalletsTest.php
+++ b/src/tests/Feature/Controller/WalletsTest.php
@@ -1,356 +1,356 @@
<?php
namespace Tests\Feature\Controller;
use App\Http\Controllers\API\V4\WalletsController;
use App\Payment;
use App\Providers\PaymentProvider;
use App\Transaction;
use Carbon\Carbon;
use Tests\TestCase;
class WalletsTest extends TestCase
{
/**
* {@inheritDoc}
*/
public function setUp(): void
{
parent::setUp();
$this->deleteTestUser('wallets-controller@kolabnow.com');
}
/**
* {@inheritDoc}
*/
public function tearDown(): void
{
$this->deleteTestUser('wallets-controller@kolabnow.com');
parent::tearDown();
}
/**
* Test for getWalletNotice() method
*/
public function testGetWalletNotice(): void
{
$user = $this->getTestUser('wallets-controller@kolabnow.com');
- $package = \App\Package::where('title', 'kolab')->first();
+ $package = \App\Package::withObjectTenantContext($user)->where('title', 'kolab')->first();
$user->assignPackage($package);
$wallet = $user->wallets()->first();
$controller = new WalletsController();
$method = new \ReflectionMethod($controller, 'getWalletNotice');
$method->setAccessible(true);
// User/entitlements created today, balance=0
$notice = $method->invoke($controller, $wallet);
$this->assertSame('You are in your free trial period.', $notice);
$wallet->owner->created_at = Carbon::now()->subDays(15);
$wallet->owner->save();
$notice = $method->invoke($controller, $wallet);
$this->assertSame('Your free trial is about to end, top up to continue.', $notice);
// User/entitlements created today, balance=-10 CHF
$wallet->balance = -1000;
$notice = $method->invoke($controller, $wallet);
$this->assertSame('You are out of credit, top up your balance now.', $notice);
// User/entitlements created slightly more than a month ago, balance=9,99 CHF (monthly)
$wallet->owner->created_at = Carbon::now()->subMonthsWithoutOverflow(1)->subDays(1);
$wallet->owner->save();
// test "1 month"
$wallet->balance = 990;
$notice = $method->invoke($controller, $wallet);
$this->assertMatchesRegularExpression('/\((1 month|4 weeks)\)/', $notice);
// test "2 months"
$wallet->balance = 990 * 2.6;
$notice = $method->invoke($controller, $wallet);
$this->assertMatchesRegularExpression('/\(2 months 2 weeks\)/', $notice);
// Change locale to make sure the text is localized by Carbon
\app()->setLocale('de');
// test "almost 2 years"
$wallet->balance = 990 * 23.5;
$notice = $method->invoke($controller, $wallet);
$this->assertMatchesRegularExpression('/\(1 Jahr 11 Monate\)/', $notice);
// Old entitlements, 100% discount
$this->backdateEntitlements($wallet->entitlements, Carbon::now()->subDays(40));
- $discount = \App\Discount::where('discount', 100)->first();
+ $discount = \App\Discount::withObjectTenantContext($user)->where('discount', 100)->first();
$wallet->discount()->associate($discount);
$notice = $method->invoke($controller, $wallet->refresh());
$this->assertSame(null, $notice);
}
/**
* Test fetching pdf receipt
*/
public function testReceiptDownload(): void
{
$user = $this->getTestUser('wallets-controller@kolabnow.com');
$john = $this->getTestUser('john@kolab.org');
$wallet = $user->wallets()->first();
// Unauth access not allowed
$response = $this->get("api/v4/wallets/{$wallet->id}/receipts/2020-05");
$response->assertStatus(401);
$response = $this->actingAs($john)->get("api/v4/wallets/{$wallet->id}/receipts/2020-05");
$response->assertStatus(403);
// Invalid receipt id (current month)
$receiptId = date('Y-m');
$response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/receipts/{$receiptId}");
$response->assertStatus(404);
// Invalid receipt id
$receiptId = '1000-03';
$response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/receipts/{$receiptId}");
$response->assertStatus(404);
// Valid receipt id
$year = intval(date('Y')) - 1;
$receiptId = "$year-12";
$filename = \config('app.name') . " Receipt for $year-12";
$response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/receipts/{$receiptId}");
$response->assertStatus(200);
$response->assertHeader('content-type', 'application/pdf');
$response->assertHeader('content-disposition', 'attachment; filename="' . $filename . '"');
$response->assertHeader('content-length');
$length = $response->headers->get('content-length');
$content = $response->content();
$this->assertStringStartsWith("%PDF-1.", $content);
$this->assertEquals(strlen($content), $length);
}
/**
* Test fetching list of receipts
*/
public function testReceipts(): void
{
$user = $this->getTestUser('wallets-controller@kolabnow.com');
$john = $this->getTestUser('john@kolab.org');
$wallet = $user->wallets()->first();
$wallet->payments()->delete();
// Unauth access not allowed
$response = $this->get("api/v4/wallets/{$wallet->id}/receipts");
$response->assertStatus(401);
$response = $this->actingAs($john)->get("api/v4/wallets/{$wallet->id}/receipts");
$response->assertStatus(403);
// Empty list expected
$response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/receipts");
$response->assertStatus(200);
$json = $response->json();
$this->assertCount(5, $json);
$this->assertSame('success', $json['status']);
$this->assertSame([], $json['list']);
$this->assertSame(1, $json['page']);
$this->assertSame(0, $json['count']);
$this->assertSame(false, $json['hasMore']);
// Insert a payment to the database
$date = Carbon::create(intval(date('Y')) - 1, 4, 30);
$payment = Payment::create([
'id' => 'AAA1',
'status' => PaymentProvider::STATUS_PAID,
'type' => PaymentProvider::TYPE_ONEOFF,
'description' => 'Paid in April',
'wallet_id' => $wallet->id,
'provider' => 'stripe',
'amount' => 1111,
'currency' => 'CHF',
'currency_amount' => 1111,
]);
$payment->updated_at = $date;
$payment->save();
$response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/receipts");
$response->assertStatus(200);
$json = $response->json();
$this->assertCount(5, $json);
$this->assertSame('success', $json['status']);
$this->assertSame([$date->format('Y-m')], $json['list']);
$this->assertSame(1, $json['page']);
$this->assertSame(1, $json['count']);
$this->assertSame(false, $json['hasMore']);
}
/**
* Test fetching a wallet (GET /api/v4/wallets/:id)
*/
public function testShow(): void
{
$john = $this->getTestUser('john@kolab.org');
$jack = $this->getTestUser('jack@kolab.org');
$wallet = $john->wallets()->first();
$wallet->balance = -100;
$wallet->save();
// Accessing a wallet of someone else
$response = $this->actingAs($jack)->get("api/v4/wallets/{$wallet->id}");
$response->assertStatus(403);
// Accessing non-existing wallet
$response = $this->actingAs($jack)->get("api/v4/wallets/aaa");
$response->assertStatus(404);
// Wallet owner
$response = $this->actingAs($john)->get("api/v4/wallets/{$wallet->id}");
$response->assertStatus(200);
$json = $response->json();
$this->assertSame($wallet->id, $json['id']);
$this->assertSame('CHF', $json['currency']);
$this->assertSame($wallet->balance, $json['balance']);
$this->assertTrue(empty($json['description']));
$this->assertTrue(!empty($json['notice']));
}
/**
* Test fetching wallet transactions
*/
public function testTransactions(): void
{
$package_kolab = \App\Package::where('title', 'kolab')->first();
$user = $this->getTestUser('wallets-controller@kolabnow.com');
$user->assignPackage($package_kolab);
$john = $this->getTestUser('john@kolab.org');
$wallet = $user->wallets()->first();
// Unauth access not allowed
$response = $this->get("api/v4/wallets/{$wallet->id}/transactions");
$response->assertStatus(401);
$response = $this->actingAs($john)->get("api/v4/wallets/{$wallet->id}/transactions");
$response->assertStatus(403);
// Expect empty list
$response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/transactions");
$response->assertStatus(200);
$json = $response->json();
$this->assertCount(5, $json);
$this->assertSame('success', $json['status']);
$this->assertSame([], $json['list']);
$this->assertSame(1, $json['page']);
$this->assertSame(0, $json['count']);
$this->assertSame(false, $json['hasMore']);
// Create some sample transactions
$transactions = $this->createTestTransactions($wallet);
$transactions = array_reverse($transactions);
$pages = array_chunk($transactions, 10 /* page size*/);
// Get the first page
$response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/transactions");
$response->assertStatus(200);
$json = $response->json();
$this->assertCount(5, $json);
$this->assertSame('success', $json['status']);
$this->assertSame(1, $json['page']);
$this->assertSame(10, $json['count']);
$this->assertSame(true, $json['hasMore']);
$this->assertCount(10, $json['list']);
foreach ($pages[0] as $idx => $transaction) {
$this->assertSame($transaction->id, $json['list'][$idx]['id']);
$this->assertSame($transaction->type, $json['list'][$idx]['type']);
$this->assertSame(\config('app.currency'), $json['list'][$idx]['currency']);
$this->assertSame($transaction->shortDescription(), $json['list'][$idx]['description']);
$this->assertFalse($json['list'][$idx]['hasDetails']);
$this->assertFalse(array_key_exists('user', $json['list'][$idx]));
}
$search = null;
// Get the second page
$response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/transactions?page=2");
$response->assertStatus(200);
$json = $response->json();
$this->assertCount(5, $json);
$this->assertSame('success', $json['status']);
$this->assertSame(2, $json['page']);
$this->assertSame(2, $json['count']);
$this->assertSame(false, $json['hasMore']);
$this->assertCount(2, $json['list']);
foreach ($pages[1] as $idx => $transaction) {
$this->assertSame($transaction->id, $json['list'][$idx]['id']);
$this->assertSame($transaction->type, $json['list'][$idx]['type']);
$this->assertSame($transaction->shortDescription(), $json['list'][$idx]['description']);
$this->assertSame(
$transaction->type == Transaction::WALLET_DEBIT,
$json['list'][$idx]['hasDetails']
);
$this->assertFalse(array_key_exists('user', $json['list'][$idx]));
if ($transaction->type == Transaction::WALLET_DEBIT) {
$search = $transaction->id;
}
}
// Get a non-existing page
$response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/transactions?page=3");
$response->assertStatus(200);
$json = $response->json();
$this->assertCount(5, $json);
$this->assertSame('success', $json['status']);
$this->assertSame(3, $json['page']);
$this->assertSame(0, $json['count']);
$this->assertSame(false, $json['hasMore']);
$this->assertCount(0, $json['list']);
// Sub-transaction searching
$response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/transactions?transaction=123");
$response->assertStatus(404);
$response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/transactions?transaction={$search}");
$response->assertStatus(200);
$json = $response->json();
$this->assertCount(5, $json);
$this->assertSame('success', $json['status']);
$this->assertSame(1, $json['page']);
$this->assertSame(2, $json['count']);
$this->assertSame(false, $json['hasMore']);
$this->assertCount(2, $json['list']);
$this->assertSame(Transaction::ENTITLEMENT_BILLED, $json['list'][0]['type']);
$this->assertSame(Transaction::ENTITLEMENT_BILLED, $json['list'][1]['type']);
// Test that John gets 404 if he tries to access
// someone else's transaction ID on his wallet's endpoint
$wallet = $john->wallets()->first();
$response = $this->actingAs($john)->get("api/v4/wallets/{$wallet->id}/transactions?transaction={$search}");
$response->assertStatus(404);
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Apr 18, 8:25 AM (2 h, 53 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
435686
Default Alt Text
(60 KB)
Attached To
Mode
R2 kolab
Attached
Detach File
Event Timeline
Log In to Comment