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,59 @@
<?php
namespace Tests\Feature\Requests\Admin;
use App\Enums\DocumentPriorityEnum;
use App\Enums\DocumentStatusEnum;
use App\Http\Requests\Admin\DocumentRequest;
use App\Models\Pet;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\Concerns\ValidatesFormRequests;
use Tests\TestCase;
class DocumentRequestTest extends TestCase
{
use RefreshDatabase, ValidatesFormRequests;
public function test_passes_with_existing_pet_only(): void
{
$this->assertPasses(DocumentRequest::class, ['pet_id' => Pet::factory()->create()->id]);
}
public function test_passes_with_all_optional_fields(): void
{
$this->assertPasses(DocumentRequest::class, [
'pet_id' => Pet::factory()->create()->id,
'modality' => 'mri',
'status' => DocumentStatusEnum::Registered->name,
'priority' => DocumentPriorityEnum::High->name,
'note' => 'n',
'submitted_at' => '2026-09-21',
'scan_date' => '2026-09-21 10:00:00',
'processing_deadline' => '2026-09-22',
'primary_files' => [],
'history_files' => [],
]);
}
public function test_fails_without_pet_or_with_unknown_pet(): void
{
$this->assertFailsOn('pet_id', DocumentRequest::class, []);
$this->assertFailsOn('pet_id', DocumentRequest::class, ['pet_id' => 999]);
}
public function test_fails_when_status_or_priority_is_a_value_instead_of_key(): void
{
$petId = Pet::factory()->create()->id;
$this->assertFailsOn('status', DocumentRequest::class, ['pet_id' => $petId, 'status' => DocumentStatusEnum::Registered->value]);
$this->assertFailsOn('priority', DocumentRequest::class, ['pet_id' => $petId, 'priority' => DocumentPriorityEnum::High->value]);
}
public function test_fails_with_invalid_dates_or_non_array_files(): void
{
$petId = Pet::factory()->create()->id;
$this->assertFailsOn('submitted_at', DocumentRequest::class, ['pet_id' => $petId, 'submitted_at' => 'x']);
$this->assertFailsOn('primary_files', DocumentRequest::class, ['pet_id' => $petId, 'primary_files' => 'x']);
}
}
@@ -0,0 +1,68 @@
<?php
namespace Tests\Feature\Requests\Admin;
use App\Enums\PetGenderEnum;
use App\Enums\PetTypeEnum;
use App\Http\Requests\Admin\PetRequest;
use Tests\Concerns\ValidatesFormRequests;
use Tests\TestCase;
class PetRequestTest extends TestCase
{
use ValidatesFormRequests;
protected function valid(array $overrides = []): array
{
return array_merge([
'name' => 'Rex',
'type' => PetTypeEnum::Dog->value,
'gender' => PetGenderEnum::Male->value,
'breed' => 'ژرمن',
'weight' => '20',
'sterilized' => true,
'birthday' => '2020-01-01',
], $overrides);
}
public function test_passes_with_valid_payload(): void
{
$this->assertPasses(PetRequest::class, $this->valid());
}
public function test_converts_persian_digits_in_weight(): void
{
$this->assertPasses(PetRequest::class, $this->valid(['weight' => '۲۰']));
}
public function test_fails_without_required_fields(): void
{
$errors = $this->formRequestErrors(PetRequest::class, ['weight' => '1']);
foreach (['name', 'type', 'gender', 'breed', 'sterilized', 'birthday'] as $field) {
$this->assertArrayHasKey($field, $errors);
}
}
public function test_fails_without_weight(): void
{
$this->assertFailsOn('weight', PetRequest::class, $this->valid(['weight' => null]));
}
public function test_fails_when_type_or_gender_is_a_key_instead_of_value(): void
{
$this->assertFailsOn('type', PetRequest::class, $this->valid(['type' => PetTypeEnum::Dog->name]));
$this->assertFailsOn('gender', PetRequest::class, $this->valid(['gender' => PetGenderEnum::Male->name]));
}
public function test_fails_with_non_integer_weight_or_invalid_birthday(): void
{
$this->assertFailsOn('weight', PetRequest::class, $this->valid(['weight' => 'heavy']));
$this->assertFailsOn('birthday', PetRequest::class, $this->valid(['birthday' => 'x']));
}
public function test_fails_with_non_boolean_sterilized(): void
{
$this->assertFailsOn('sterilized', PetRequest::class, $this->valid(['sterilized' => 'yes']));
}
}
@@ -0,0 +1,64 @@
<?php
namespace Tests\Feature\Requests\Admin;
use App\Enums\ReportStatusEnum;
use App\Http\Requests\Admin\ReportRequest;
use App\Models\Document;
use App\Models\Medium;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\Concerns\ValidatesFormRequests;
use Tests\TestCase;
class ReportRequestTest extends TestCase
{
use RefreshDatabase, ValidatesFormRequests;
protected function valid(array $overrides = []): array
{
return array_merge([
'document_id' => Document::factory()->create()->id,
'doctor_id' => User::factory()->create()->id,
'status' => ReportStatusEnum::Approved->name,
'body' => 'متن گزارش',
'submitted_at' => '2026-09-21 10:00:00',
], $overrides);
}
public function test_passes_with_valid_payload(): void
{
$this->assertPasses(ReportRequest::class, $this->valid());
}
public function test_passes_with_existing_medium(): void
{
$this->assertPasses(ReportRequest::class, $this->valid(['medium_id' => Medium::factory()->create()->id]));
}
public function test_fails_without_required_fields(): void
{
$errors = $this->formRequestErrors(ReportRequest::class, []);
foreach (['document_id', 'doctor_id', 'status', 'body', 'submitted_at'] as $field) {
$this->assertArrayHasKey($field, $errors);
}
}
public function test_fails_with_unknown_document_doctor_or_medium(): void
{
$this->assertFailsOn('document_id', ReportRequest::class, $this->valid(['document_id' => 999]));
$this->assertFailsOn('doctor_id', ReportRequest::class, $this->valid(['doctor_id' => 999]));
$this->assertFailsOn('medium_id', ReportRequest::class, $this->valid(['medium_id' => 999]));
}
public function test_fails_when_status_is_a_value_instead_of_key(): void
{
$this->assertFailsOn('status', ReportRequest::class, $this->valid(['status' => ReportStatusEnum::Approved->value]));
}
public function test_fails_with_invalid_submitted_at(): void
{
$this->assertFailsOn('submitted_at', ReportRequest::class, $this->valid(['submitted_at' => 'x']));
}
}
@@ -0,0 +1,34 @@
<?php
namespace Tests\Feature\Requests\Admin;
use App\Http\Requests\Admin\SettingProfileRequest;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\Concerns\ValidatesFormRequests;
use Tests\TestCase;
class SettingProfileRequestTest extends TestCase
{
use RefreshDatabase, ValidatesFormRequests;
public function test_passes_with_name_for_edited_user(): void
{
$user = User::factory()->create();
$this->assertPasses(SettingProfileRequest::class, ['name' => 'new-name'], [], null, ['user' => $user]);
}
public function test_fails_without_name(): void
{
$this->assertFailsOn('name', SettingProfileRequest::class, [], [], null, ['user' => User::factory()->create()]);
}
public function test_fails_when_fields_exceed_limit(): void
{
$user = User::factory()->create();
$this->assertFailsOn('name', SettingProfileRequest::class, ['name' => str_repeat('a', 256)], [], null, ['user' => $user]);
$this->assertFailsOn('first_name', SettingProfileRequest::class, ['name' => 'n', 'first_name' => str_repeat('a', 256)], [], null, ['user' => $user]);
}
}
@@ -0,0 +1,96 @@
<?php
namespace Tests\Feature\Requests\Admin;
use App\Enums\ShiftDayEnum;
use App\Enums\UserEnum;
use App\Http\Requests\Admin\ShiftRequest;
use App\Models\Shift;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\Concerns\ValidatesFormRequests;
use Tests\TestCase;
class ShiftRequestTest extends TestCase
{
use RefreshDatabase, ValidatesFormRequests;
protected function valid(array $overrides = []): array
{
return array_merge([
'day_of_week' => ShiftDayEnum::Monday->value,
'start_time' => '08:00',
'end_time' => '16:00',
'is_active' => true,
], $overrides);
}
public function test_passes_with_weekly_shift(): void
{
$this->assertPasses(ShiftRequest::class, $this->valid());
}
public function test_passes_with_dated_shift_and_doctor(): void
{
$doctor = User::factory()->create(['level' => UserEnum::Doctor->value]);
$this->assertPasses(ShiftRequest::class, $this->valid(['day_of_week' => null, 'date' => '2026-09-21', 'user_id' => $doctor->id]));
}
public function test_passes_with_overnight_shift(): void
{
$this->assertPasses(ShiftRequest::class, $this->valid(['start_time' => '22:00', 'end_time' => '02:00']));
}
public function test_fails_without_day_of_week_or_date(): void
{
$errors = $this->formRequestErrors(ShiftRequest::class, $this->valid(['day_of_week' => null]));
$this->assertArrayHasKey('day_of_week', $errors);
$this->assertArrayHasKey('date', $errors);
}
public function test_fails_without_times_or_is_active(): void
{
$errors = $this->formRequestErrors(ShiftRequest::class, ['day_of_week' => ShiftDayEnum::Monday->value]);
$this->assertArrayHasKey('start_time', $errors);
$this->assertArrayHasKey('end_time', $errors);
$this->assertArrayHasKey('is_active', $errors);
}
public function test_fails_with_invalid_day_time_format_or_equal_times(): void
{
$this->assertFailsOn('day_of_week', ShiftRequest::class, $this->valid(['day_of_week' => 'Monday']));
$this->assertFailsOn('start_time', ShiftRequest::class, $this->valid(['start_time' => '8am']));
$this->assertFailsOn('end_time', ShiftRequest::class, $this->valid(['end_time' => '08:00']));
}
public function test_fails_when_user_is_not_a_doctor(): void
{
$vet = User::factory()->create(['level' => UserEnum::Vet->value]);
$this->assertFailsOn('user_id', ShiftRequest::class, $this->valid(['user_id' => $vet->id]));
}
public function test_fails_when_shift_conflicts_with_doctors_existing_shift(): void
{
$doctor = User::factory()->create(['level' => UserEnum::Doctor->value]);
Shift::factory()->weekly(ShiftDayEnum::Monday)->between('08:00', '12:00')->forDoctor($doctor)->create();
$this->assertFailsOn('start_time', ShiftRequest::class, $this->valid(['user_id' => $doctor->id, 'start_time' => '10:00', 'end_time' => '14:00']));
}
public function test_conflict_check_ignores_shift_being_edited(): void
{
$doctor = User::factory()->create(['level' => UserEnum::Doctor->value]);
$shift = Shift::factory()->weekly(ShiftDayEnum::Monday)->between('08:00', '12:00')->forDoctor($doctor)->create();
$this->assertPasses(ShiftRequest::class, $this->valid(['user_id' => $doctor->id, 'start_time' => '09:00', 'end_time' => '13:00']), [], null, ['shift' => $shift]);
}
public function test_date_takes_precedence_and_clears_day_of_week(): void
{
$this->assertPasses(ShiftRequest::class, $this->valid(['day_of_week' => 'invalid-day', 'date' => '2026-09-21']));
}
}
@@ -0,0 +1,106 @@
<?php
namespace Tests\Feature\Requests\Admin;
use App\Enums\UserEnum;
use App\Http\Requests\Admin\UserRequest;
use App\Models\Medium;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Spatie\Permission\Models\Role;
use Tests\Concerns\ValidatesFormRequests;
use Tests\TestCase;
class UserRequestTest extends TestCase
{
use RefreshDatabase, ValidatesFormRequests;
protected function valid(array $overrides = []): array
{
return array_merge([
'phone' => '09120000000',
'password' => 'secret123',
'password_confirmation' => 'secret123',
'level' => UserEnum::Doctor->value,
], $overrides);
}
public function test_passes_with_required_fields_on_create(): void
{
$this->assertPasses(UserRequest::class, $this->valid());
}
public function test_passes_with_optional_fields_and_roles(): void
{
$role = Role::create(['name' => 'editor']);
$this->assertPasses(UserRequest::class, $this->valid([
'username' => 'ali',
'fullname' => 'علی',
'email' => 'ali@example.com',
'role' => [$role->id],
]));
}
public function test_fails_without_phone_password_or_level(): void
{
$errors = $this->formRequestErrors(UserRequest::class, []);
$this->assertArrayHasKey('phone', $errors);
$this->assertArrayHasKey('password', $errors);
$this->assertArrayHasKey('level', $errors);
}
public function test_fails_with_short_or_unconfirmed_password(): void
{
$this->assertFailsOn('password', UserRequest::class, $this->valid(['password' => 'short', 'password_confirmation' => 'short']));
$this->assertFailsOn('password', UserRequest::class, $this->valid(['password_confirmation' => 'different']));
}
public function test_fails_with_level_outside_admin_list(): void
{
$this->assertFailsOn('level', UserRequest::class, $this->valid(['level' => UserEnum::Vet->value]));
}
public function test_fails_with_duplicate_username_or_email(): void
{
User::factory()->create(['username' => 'taken', 'email' => 'taken@example.com']);
$this->assertFailsOn('username', UserRequest::class, $this->valid(['username' => 'taken']));
$this->assertFailsOn('email', UserRequest::class, $this->valid(['email' => 'taken@example.com']));
}
public function test_profile_url_must_match_existing_medium(): void
{
$medium = Medium::factory()->create(['url' => 'http://example.test/a.png']);
$this->assertPasses(UserRequest::class, $this->valid(['profile_url' => $medium->url]));
$this->assertFailsOn('profile_url', UserRequest::class, $this->valid(['profile_url' => 'http://example.test/missing.png']));
}
public function test_fails_with_invalid_phone_or_unknown_role(): void
{
$this->assertFailsOn('phone', UserRequest::class, $this->valid(['phone' => 'abc']));
$this->assertFailsOn('role', UserRequest::class, $this->valid(['role' => [999]]));
}
public function test_update_allows_own_username_and_email_and_optional_password(): void
{
$user = User::factory()->create(['username' => 'me', 'email' => 'me@example.com']);
$this->assertPasses(UserRequest::class, [
'username' => 'me',
'email' => 'me@example.com',
'phone' => '09120000000',
'level' => UserEnum::Doctor->value,
], [], null, ['user' => $user]);
}
public function test_update_still_rejects_another_users_username(): void
{
$user = User::factory()->create();
User::factory()->create(['username' => 'other']);
$this->assertFailsOn('username', UserRequest::class, $this->valid(['username' => 'other']), [], null, ['user' => $user]);
}
}
@@ -0,0 +1,28 @@
<?php
namespace Tests\Feature\Requests\Admin;
use App\Http\Requests\Admin\WithdrawRejectRequest;
use Tests\Concerns\ValidatesFormRequests;
use Tests\TestCase;
class WithdrawRejectRequestTest extends TestCase
{
use ValidatesFormRequests;
public function test_passes_without_note(): void
{
$this->assertPasses(WithdrawRejectRequest::class, []);
$this->assertPasses(WithdrawRejectRequest::class, ['note' => null]);
}
public function test_passes_with_note(): void
{
$this->assertPasses(WithdrawRejectRequest::class, ['note' => 'اطلاعات کارت ناقص است']);
}
public function test_fails_when_note_exceeds_limit(): void
{
$this->assertFailsOn('note', WithdrawRejectRequest::class, ['note' => str_repeat('a', 1001)]);
}
}