Add feature tests for Doctor-related functionalities

This commit is contained in:
2026-09-21 05:41:06 +03:30
parent e987a528ef
commit 6e92ebaada
29 changed files with 2634 additions and 29 deletions
@@ -0,0 +1,146 @@
<?php
namespace Tests\Feature\Http\Client;
use App\Enums\InvoicePaymentMethodEnum;
use App\Enums\InvoiceStatusEnum;
use App\Enums\PaymentPurposeEnum;
use App\Enums\PaymentStatusEnum;
use App\Enums\WalletTransactionReasonEnum;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\User;
use App\Models\Wallet;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Queue;
use Tests\TestCase;
class PaymentControllerTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
Queue::fake();
}
public function test_store_requires_authentication(): void
{
$this->post(route('dashboard.payments.store'), ['invoice_id' => 1])->assertRedirect(route('login'));
}
public function test_store_creates_gateway_payment_for_own_pending_invoice(): void
{
Http::fake(['gateway.zibal.ir/v1/request' => Http::response(['result' => 100, 'trackId' => 777])]);
$invoice = Invoice::factory()->create(['amount' => 500000]);
$response = $this->actingAs($invoice->user)->postJson(route('dashboard.payments.store'), ['invoice_id' => $invoice->id]);
$response->assertOk()
->assertJsonPath('payment.track_id', '777')
->assertJsonPath('url', 'https://gateway.zibal.ir/start/777');
$this->assertDatabaseHas('payments', [
'track_id' => '777',
'amount' => 500000,
'invoice_id' => $invoice->id,
'payer_id' => $invoice->user_id,
'purpose' => PaymentPurposeEnum::Invoice->value,
]);
}
public function test_store_rejects_invoice_of_another_user(): void
{
Http::fake();
$invoice = Invoice::factory()->create();
$this->actingAs(User::factory()->create())->postJson(route('dashboard.payments.store'), ['invoice_id' => $invoice->id])->assertNotFound();
Http::assertNothingSent();
}
public function test_store_rejects_non_pending_invoice(): void
{
Http::fake();
$invoice = Invoice::factory()->paid()->create();
$this->actingAs($invoice->user)->postJson(route('dashboard.payments.store'), ['invoice_id' => $invoice->id])->assertNotFound();
}
public function test_store_validates_invoice_id(): void
{
$this->actingAs(User::factory()->create())->postJson(route('dashboard.payments.store'), [])->assertUnprocessable()->assertJsonValidationErrors('invoice_id');
}
public function test_callback_with_unknown_track_id_redirects_to_dashboard(): void
{
$this->get(route('dashboard.payments.callback', ['trackId' => 'nope', 'success' => 1]))->assertRedirect(route('dashboard.index'));
}
public function test_callback_settles_successful_invoice_payment(): void
{
Http::fake(['gateway.zibal.ir/v1/verify' => Http::response(['result' => 100, 'refNumber' => 'REF'])]);
$invoice = Invoice::factory()->create(['amount' => 500000]);
$payment = Payment::factory()->create(['invoice_id' => $invoice->id, 'payer_id' => $invoice->user_id, 'amount' => 500000, 'track_id' => '555']);
$response = $this->get(route('dashboard.payments.callback', ['trackId' => '555', 'success' => 1]));
$response->assertRedirect(route('dashboard.index'));
$response->assertSessionHas('payment_result', ['status' => 'paid', 'track_id' => '555', 'purpose' => 'Invoice']);
$this->assertSame(PaymentStatusEnum::Paid, $payment->fresh()->status);
$invoice->refresh();
$this->assertSame(InvoiceStatusEnum::Paid, $invoice->status);
$this->assertSame(InvoicePaymentMethodEnum::Gateway, $invoice->payment_method);
$this->assertDatabaseHas('wallet_transactions', ['invoice_id' => $invoice->id, 'payment_id' => $payment->id, 'reason' => WalletTransactionReasonEnum::InvoicePayment->value]);
}
public function test_callback_settles_successful_wallet_top_up(): void
{
Http::fake(['gateway.zibal.ir/v1/verify' => Http::response(['result' => 100])]);
$wallet = Wallet::factory()->create(['balance' => 1000]);
Payment::factory()->walletTopUp()->create(['payer_id' => $wallet->user_id, 'amount' => 250000, 'track_id' => '556']);
$this->get(route('dashboard.payments.callback', ['trackId' => '556', 'success' => 1]))->assertRedirect(route('dashboard.index'));
$this->assertSame(251000, $wallet->fresh()->balance);
$this->assertDatabaseHas('wallet_transactions', ['wallet_id' => $wallet->id, 'reason' => WalletTransactionReasonEnum::TopUp->value, 'amount' => 250000]);
}
public function test_callback_marks_payment_failed_when_gateway_reports_failure(): void
{
Http::fake();
$invoice = Invoice::factory()->create();
$payment = Payment::factory()->create(['invoice_id' => $invoice->id, 'payer_id' => $invoice->user_id, 'track_id' => '557']);
$response = $this->get(route('dashboard.payments.callback', ['trackId' => '557', 'success' => 0]));
$response->assertSessionHas('payment_result.status', 'failed');
$this->assertSame(PaymentStatusEnum::Failed, $payment->fresh()->status);
$this->assertSame(InvoiceStatusEnum::Pending, $invoice->fresh()->status);
Http::assertNothingSent();
}
public function test_callback_marks_payment_failed_when_verification_fails(): void
{
Http::fake(['gateway.zibal.ir/v1/verify' => Http::response(['result' => 202])]);
$payment = Payment::factory()->create(['track_id' => '558']);
$this->get(route('dashboard.payments.callback', ['trackId' => '558', 'success' => 1]));
$this->assertSame(PaymentStatusEnum::Failed, $payment->fresh()->status);
$this->assertDatabaseCount('wallet_transactions', 0);
}
public function test_callback_does_not_settle_twice_for_already_paid_payment(): void
{
Http::fake();
$wallet = Wallet::factory()->create(['balance' => 1000]);
Payment::factory()->paid()->walletTopUp()->create(['payer_id' => $wallet->user_id, 'amount' => 250000, 'track_id' => '559']);
$this->get(route('dashboard.payments.callback', ['trackId' => '559', 'success' => 1]))->assertSessionHas('payment_result.status', 'paid');
$this->assertSame(1000, $wallet->fresh()->balance);
$this->assertDatabaseCount('wallet_transactions', 0);
}
}