Add form request validation tests for various client and admin requests

This commit is contained in:
2026-09-21 05:24:50 +03:30
parent f77ff527e7
commit e987a528ef
21 changed files with 1061 additions and 23 deletions
@@ -0,0 +1,55 @@
<?php
namespace Tests\Feature\Requests\Client\Auth;
use App\Http\Requests\Client\Auth\UserRegistrationRequest;
use Tests\Concerns\ValidatesFormRequests;
use Tests\TestCase;
class UserRegistrationRequestTest extends TestCase
{
use ValidatesFormRequests;
protected function valid(array $overrides = []): array
{
return array_merge(['phone' => '09120000000', 'national_code' => '0012345678'], $overrides);
}
public function test_passes_with_phone_and_national_code(): void
{
$this->assertPasses(UserRegistrationRequest::class, $this->valid());
}
public function test_passes_with_all_optional_fields(): void
{
$this->assertPasses(UserRegistrationRequest::class, $this->valid([
'fullname' => 'f', 'specialty' => 's', 'clinic_name' => 'c', 'clinic_phone' => '021', 'province' => 'p', 'city' => 'c', 'address' => 'a', 'level' => 'دانپزشک',
]));
}
public function test_accepts_international_and_persian_digit_phones(): void
{
$this->assertPasses(UserRegistrationRequest::class, $this->valid(['phone' => '+989120000000']));
$this->assertPasses(UserRegistrationRequest::class, $this->valid(['phone' => '۰۹۱۲۰۰۰۰۰۰۰']));
}
public function test_fails_without_phone_or_national_code(): void
{
$errors = $this->formRequestErrors(UserRegistrationRequest::class, []);
$this->assertArrayHasKey('phone', $errors);
$this->assertArrayHasKey('national_code', $errors);
}
public function test_fails_with_invalid_phone(): void
{
$this->assertFailsOn('phone', UserRegistrationRequest::class, $this->valid(['phone' => 'abc']));
$this->assertFailsOn('phone', UserRegistrationRequest::class, $this->valid(['phone' => '123']));
$this->assertFailsOn('phone', UserRegistrationRequest::class, $this->valid(['phone' => '+9999120000000']));
}
public function test_fails_when_level_exceeds_limit(): void
{
$this->assertFailsOn('level', UserRegistrationRequest::class, $this->valid(['level' => str_repeat('a', 51)]));
}
}
@@ -0,0 +1,62 @@
<?php
namespace Tests\Feature\Requests\Client\Auth;
use App\Http\Requests\Client\Auth\VerifyOtpRequest;
use App\Models\OtpCode;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\Concerns\ValidatesFormRequests;
use Tests\TestCase;
class VerifyOtpRequestTest extends TestCase
{
use RefreshDatabase, ValidatesFormRequests;
protected function setUp(): void
{
parent::setUp();
session(['login_phone' => '09120000000']);
}
public function test_passes_with_valid_code_for_session_phone(): void
{
OtpCode::factory()->create(['phone' => '09120000000', 'code' => '1234']);
$this->assertPasses(VerifyOtpRequest::class, ['otp_code' => '1234']);
}
public function test_fails_without_code(): void
{
$this->assertFailsOn('otp_code', VerifyOtpRequest::class, []);
}
public function test_fails_when_code_is_not_four_digits(): void
{
OtpCode::factory()->create(['phone' => '09120000000', 'code' => '123']);
$this->assertFailsOn('otp_code', VerifyOtpRequest::class, ['otp_code' => '123']);
$this->assertFailsOn('otp_code', VerifyOtpRequest::class, ['otp_code' => 'abcd']);
}
public function test_fails_with_wrong_code(): void
{
OtpCode::factory()->create(['phone' => '09120000000', 'code' => '1234']);
$this->assertFailsOn('otp_code', VerifyOtpRequest::class, ['otp_code' => '0000']);
}
public function test_fails_with_expired_code(): void
{
OtpCode::factory()->expired()->create(['phone' => '09120000000', 'code' => '1234']);
$this->assertFailsOn('otp_code', VerifyOtpRequest::class, ['otp_code' => '1234']);
}
public function test_fails_when_code_belongs_to_another_phone(): void
{
OtpCode::factory()->create(['phone' => '09130000000', 'code' => '1234']);
$this->assertFailsOn('otp_code', VerifyOtpRequest::class, ['otp_code' => '1234']);
}
}
@@ -0,0 +1,37 @@
<?php
namespace Tests\Feature\Requests\Client;
use App\Http\Requests\Client\BankCardStoreRequest;
use Tests\Concerns\ValidatesFormRequests;
use Tests\TestCase;
class BankCardStoreRequestTest extends TestCase
{
use ValidatesFormRequests;
public function test_passes_with_sixteen_digit_card_and_holder_name(): void
{
$this->assertPasses(BankCardStoreRequest::class, ['card_number' => '6037991234567890', 'holder_name' => 'علی رضایی']);
}
public function test_fails_without_required_fields(): void
{
$errors = $this->formRequestErrors(BankCardStoreRequest::class, []);
$this->assertArrayHasKey('card_number', $errors);
$this->assertArrayHasKey('holder_name', $errors);
}
public function test_fails_when_card_number_is_not_sixteen_digits(): void
{
$this->assertFailsOn('card_number', BankCardStoreRequest::class, ['card_number' => '603799123456789', 'holder_name' => 'x']);
$this->assertFailsOn('card_number', BankCardStoreRequest::class, ['card_number' => '60379912345678901', 'holder_name' => 'x']);
$this->assertFailsOn('card_number', BankCardStoreRequest::class, ['card_number' => '6037-9912-3456-7890', 'holder_name' => 'x']);
}
public function test_fails_when_holder_name_is_too_long(): void
{
$this->assertFailsOn('holder_name', BankCardStoreRequest::class, ['card_number' => '6037991234567890', 'holder_name' => str_repeat('a', 256)]);
}
}
@@ -0,0 +1,28 @@
<?php
namespace Tests\Feature\Requests\Client;
use App\Http\Requests\Client\CaseRejectRequest;
use Tests\Concerns\ValidatesFormRequests;
use Tests\TestCase;
class CaseRejectRequestTest extends TestCase
{
use ValidatesFormRequests;
public function test_passes_with_reason(): void
{
$this->assertPasses(CaseRejectRequest::class, ['reason' => 'تصاویر ناخوانا هستند']);
}
public function test_fails_without_reason(): void
{
$this->assertFailsOn('reason', CaseRejectRequest::class, []);
$this->assertFailsOn('reason', CaseRejectRequest::class, ['reason' => '']);
}
public function test_fails_when_reason_exceeds_limit(): void
{
$this->assertFailsOn('reason', CaseRejectRequest::class, ['reason' => str_repeat('a', 1001)]);
}
}
@@ -0,0 +1,114 @@
<?php
namespace Tests\Feature\Requests\Client;
use App\Enums\DocumentModalityEnum;
use App\Enums\DocumentPriorityEnum;
use App\Enums\PetGenderEnum;
use App\Enums\PetTypeEnum;
use App\Enums\UserEnum;
use App\Http\Requests\Client\FastRequestRequest;
use App\Models\Pet;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\UploadedFile;
use Tests\Concerns\ValidatesFormRequests;
use Tests\TestCase;
class FastRequestRequestTest extends TestCase
{
use RefreshDatabase, ValidatesFormRequests;
protected User $user;
protected function setUp(): void
{
parent::setUp();
$this->user = User::factory()->create();
}
protected function valid(array $overrides = []): array
{
return array_merge([
'modality' => DocumentModalityEnum::Mri->value,
'priority' => DocumentPriorityEnum::High->name,
'pet' => ['name' => 'Rex', 'type' => PetTypeEnum::Dog->name],
], $overrides);
}
public function test_passes_with_new_pet(): void
{
$this->assertPasses(FastRequestRequest::class, $this->valid([
'note' => 'n',
'pet' => ['name' => 'Rex', 'type' => PetTypeEnum::Dog->name, 'gender' => PetGenderEnum::Male->name, 'breed' => 'b', 'age' => 3, 'weight' => 20, 'sterilized' => true],
]), [], $this->user);
}
public function test_passes_with_own_existing_pet_and_no_pet_payload(): void
{
$pet = Pet::factory()->create(['owner_id' => $this->user->id]);
$this->assertPasses(FastRequestRequest::class, ['modality' => DocumentModalityEnum::Mri->value, 'priority' => DocumentPriorityEnum::Medium->name, 'pet_id' => $pet->id], [], $this->user);
}
public function test_passes_with_doctor_and_image_files(): void
{
$doctor = User::factory()->create(['level' => UserEnum::Doctor->value]);
$this->assertPasses(FastRequestRequest::class, $this->valid(['doctor_id' => $doctor->id]), [
'history_files' => [UploadedFile::fake()->image('h.jpg')],
'scan_files' => [UploadedFile::fake()->image('s.png')],
], $this->user);
}
public function test_fails_without_modality_priority_or_pet(): void
{
$errors = $this->formRequestErrors(FastRequestRequest::class, [], [], $this->user);
$this->assertArrayHasKey('modality', $errors);
$this->assertArrayHasKey('priority', $errors);
$this->assertArrayHasKey('pet', $errors);
$this->assertArrayHasKey('pet.name', $errors);
$this->assertArrayHasKey('pet.type', $errors);
}
public function test_fails_with_invalid_modality_or_priority(): void
{
$this->assertFailsOn('modality', FastRequestRequest::class, $this->valid(['modality' => 'xray']), [], $this->user);
$this->assertFailsOn('priority', FastRequestRequest::class, $this->valid(['priority' => DocumentPriorityEnum::High->value]), [], $this->user);
}
public function test_fails_with_pet_of_another_user(): void
{
$pet = Pet::factory()->create();
$this->assertFailsOn('pet_id', FastRequestRequest::class, $this->valid(['pet_id' => $pet->id]), [], $this->user);
}
public function test_fails_with_invalid_pet_type_gender_age_or_weight(): void
{
$this->assertFailsOn('pet.type', FastRequestRequest::class, $this->valid(['pet' => ['name' => 'x', 'type' => PetTypeEnum::Dog->value]]), [], $this->user);
$this->assertFailsOn('pet.gender', FastRequestRequest::class, $this->valid(['pet' => ['name' => 'x', 'type' => 'Dog', 'gender' => 'Alien']]), [], $this->user);
$this->assertFailsOn('pet.age', FastRequestRequest::class, $this->valid(['pet' => ['name' => 'x', 'type' => 'Dog', 'age' => 101]]), [], $this->user);
$this->assertFailsOn('pet.weight', FastRequestRequest::class, $this->valid(['pet' => ['name' => 'x', 'type' => 'Dog', 'weight' => -1]]), [], $this->user);
}
public function test_fails_with_non_doctor_as_doctor_id(): void
{
$vet = User::factory()->create(['level' => UserEnum::Vet->value]);
$this->assertFailsOn('doctor_id', FastRequestRequest::class, $this->valid(['doctor_id' => $vet->id]), [], $this->user);
}
public function test_fails_with_non_image_or_oversized_files(): void
{
$this->assertFailsOn('history_files.0', FastRequestRequest::class, $this->valid(), ['history_files' => [UploadedFile::fake()->create('a.pdf', 10, 'application/pdf')]], $this->user);
$this->assertFailsOn('scan_files.0', FastRequestRequest::class, $this->valid(), ['scan_files' => [UploadedFile::fake()->image('a.jpg')->size(10241)]], $this->user);
}
public function test_fails_when_note_exceeds_limit(): void
{
$this->assertFailsOn('note', FastRequestRequest::class, $this->valid(['note' => str_repeat('a', 1001)]), [], $this->user);
}
}
@@ -0,0 +1,36 @@
<?php
namespace Tests\Feature\Requests\Client;
use App\Http\Requests\Client\PaymentRequest;
use App\Models\Invoice;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\Concerns\ValidatesFormRequests;
use Tests\TestCase;
class PaymentRequestTest extends TestCase
{
use RefreshDatabase, ValidatesFormRequests;
public function test_passes_with_existing_invoice_id(): void
{
$invoice = Invoice::factory()->create();
$this->assertPasses(PaymentRequest::class, ['invoice_id' => $invoice->id]);
}
public function test_fails_without_invoice_id(): void
{
$this->assertFailsOn('invoice_id', PaymentRequest::class, []);
}
public function test_fails_with_non_integer_invoice_id(): void
{
$this->assertFailsOn('invoice_id', PaymentRequest::class, ['invoice_id' => 'abc']);
}
public function test_fails_with_unknown_invoice_id(): void
{
$this->assertFailsOn('invoice_id', PaymentRequest::class, ['invoice_id' => 999]);
}
}
@@ -0,0 +1,65 @@
<?php
namespace Tests\Feature\Requests\Client;
use App\Http\Requests\Client\ProfileUpdateRequest;
use Illuminate\Http\UploadedFile;
use Tests\Concerns\ValidatesFormRequests;
use Tests\TestCase;
class ProfileUpdateRequestTest extends TestCase
{
use ValidatesFormRequests;
protected function valid(array $overrides = []): array
{
return array_merge(['fullname' => 'علی رضایی', 'national_code' => '0012345678'], $overrides);
}
public function test_passes_with_required_fields_only(): void
{
$this->assertPasses(ProfileUpdateRequest::class, $this->valid());
}
public function test_passes_with_all_optional_fields_and_png_avatar(): void
{
$this->assertPasses(ProfileUpdateRequest::class, $this->valid([
'birthdate' => '1990-01-01',
'email' => 'a@b.com',
'province' => 'تهران',
'city' => 'تهران',
'address' => 'خیابان',
]), ['avatar' => UploadedFile::fake()->image('a.png')]);
}
public function test_fails_without_fullname_or_national_code(): void
{
$errors = $this->formRequestErrors(ProfileUpdateRequest::class, []);
$this->assertArrayHasKey('fullname', $errors);
$this->assertArrayHasKey('national_code', $errors);
}
public function test_fails_with_invalid_birthdate_or_email(): void
{
$this->assertFailsOn('birthdate', ProfileUpdateRequest::class, $this->valid(['birthdate' => 'not-a-date']));
$this->assertFailsOn('email', ProfileUpdateRequest::class, $this->valid(['email' => 'not-an-email']));
}
public function test_fails_when_national_code_or_address_too_long(): void
{
$this->assertFailsOn('national_code', ProfileUpdateRequest::class, $this->valid(['national_code' => str_repeat('1', 21)]));
$this->assertFailsOn('address', ProfileUpdateRequest::class, $this->valid(['address' => str_repeat('a', 501)]));
}
public function test_fails_with_non_image_or_disallowed_avatar_type(): void
{
$this->assertFailsOn('avatar', ProfileUpdateRequest::class, $this->valid(), ['avatar' => UploadedFile::fake()->create('a.pdf', 10, 'application/pdf')]);
$this->assertFailsOn('avatar', ProfileUpdateRequest::class, $this->valid(), ['avatar' => UploadedFile::fake()->image('a.gif')]);
}
public function test_fails_with_avatar_over_ten_megabytes(): void
{
$this->assertFailsOn('avatar', ProfileUpdateRequest::class, $this->valid(), ['avatar' => UploadedFile::fake()->image('a.jpg')->size(10241)]);
}
}
@@ -0,0 +1,62 @@
<?php
namespace Tests\Feature\Requests\Client;
use App\Http\Requests\Client\ReportSubmitRequest;
use App\Models\Document;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\UploadedFile;
use Tests\Concerns\ValidatesFormRequests;
use Tests\TestCase;
class ReportSubmitRequestTest extends TestCase
{
use RefreshDatabase, ValidatesFormRequests;
protected function valid(array $overrides = []): array
{
return array_merge(['document_id' => Document::factory()->create()->id, 'finding' => 'یافته‌ها'], $overrides);
}
public function test_passes_with_document_and_finding(): void
{
$this->assertPasses(ReportSubmitRequest::class, $this->valid());
}
public function test_passes_with_optional_sections_and_image_files(): void
{
$this->assertPasses(ReportSubmitRequest::class, $this->valid([
'technique' => 't',
'diagnose' => 'd',
'recommendation' => 'r',
]), ['files' => [UploadedFile::fake()->image('a.jpg'), UploadedFile::fake()->image('b.png')]]);
}
public function test_fails_without_document_or_finding(): void
{
$errors = $this->formRequestErrors(ReportSubmitRequest::class, []);
$this->assertArrayHasKey('document_id', $errors);
$this->assertArrayHasKey('finding', $errors);
}
public function test_fails_with_unknown_document(): void
{
$this->assertFailsOn('document_id', ReportSubmitRequest::class, $this->valid(['document_id' => 999]));
}
public function test_fails_when_section_exceeds_limit(): void
{
$this->assertFailsOn('finding', ReportSubmitRequest::class, $this->valid(['finding' => str_repeat('a', 10001)]));
}
public function test_fails_with_non_image_file(): void
{
$this->assertFailsOn('files.0', ReportSubmitRequest::class, $this->valid(), ['files' => [UploadedFile::fake()->create('a.pdf', 10, 'application/pdf')]]);
}
public function test_fails_with_file_over_ten_megabytes(): void
{
$this->assertFailsOn('files.0', ReportSubmitRequest::class, $this->valid(), ['files' => [UploadedFile::fake()->image('a.jpg')->size(10241)]]);
}
}
@@ -0,0 +1,39 @@
<?php
namespace Tests\Feature\Requests\Client;
use App\Http\Requests\Client\WalletDepositRequest;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\Concerns\ValidatesFormRequests;
use Tests\TestCase;
class WalletDepositRequestTest extends TestCase
{
use RefreshDatabase, ValidatesFormRequests;
public function test_passes_with_amount_in_range(): void
{
$this->assertPasses(WalletDepositRequest::class, ['amount' => 10000]);
$this->assertPasses(WalletDepositRequest::class, ['amount' => 500000000]);
}
public function test_fails_without_amount(): void
{
$this->assertFailsOn('amount', WalletDepositRequest::class, []);
}
public function test_fails_with_non_integer_amount(): void
{
$this->assertFailsOn('amount', WalletDepositRequest::class, ['amount' => '10000.5']);
}
public function test_fails_below_minimum(): void
{
$this->assertFailsOn('amount', WalletDepositRequest::class, ['amount' => 9999]);
}
public function test_fails_above_maximum(): void
{
$this->assertFailsOn('amount', WalletDepositRequest::class, ['amount' => 500000001]);
}
}
@@ -0,0 +1,42 @@
<?php
namespace Tests\Feature\Requests\Client;
use App\Http\Requests\Client\WithdrawRequestStoreRequest;
use App\Models\BankCard;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\Concerns\ValidatesFormRequests;
use Tests\TestCase;
class WithdrawRequestStoreRequestTest extends TestCase
{
use RefreshDatabase, ValidatesFormRequests;
public function test_passes_with_valid_amount_and_existing_bank_card(): void
{
$bankCard = BankCard::factory()->create();
$this->assertPasses(WithdrawRequestStoreRequest::class, ['amount' => 50000, 'bank_card_id' => $bankCard->id]);
}
public function test_fails_without_required_fields(): void
{
$errors = $this->formRequestErrors(WithdrawRequestStoreRequest::class, []);
$this->assertArrayHasKey('amount', $errors);
$this->assertArrayHasKey('bank_card_id', $errors);
}
public function test_fails_with_amount_out_of_range(): void
{
$bankCard = BankCard::factory()->create();
$this->assertFailsOn('amount', WithdrawRequestStoreRequest::class, ['amount' => 9999, 'bank_card_id' => $bankCard->id]);
$this->assertFailsOn('amount', WithdrawRequestStoreRequest::class, ['amount' => 500000001, 'bank_card_id' => $bankCard->id]);
}
public function test_fails_with_unknown_bank_card(): void
{
$this->assertFailsOn('bank_card_id', WithdrawRequestStoreRequest::class, ['amount' => 50000, 'bank_card_id' => 999]);
}
}