Page MenuHomePhorge

No OneTemporary

Size
60 KB
Referenced Files
None
Subscribers
None
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

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)

Event Timeline