|
|
@@ -0,0 +1,299 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+namespace Tests\Feature;
|
|
|
+
|
|
|
+use App\Models\Dictionary\Area;
|
|
|
+use App\Models\Dictionary\District;
|
|
|
+use App\Models\Role;
|
|
|
+use App\Models\User;
|
|
|
+use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
|
+use Illuminate\Http\UploadedFile;
|
|
|
+use Illuminate\Support\Facades\Storage;
|
|
|
+use Tests\TestCase;
|
|
|
+
|
|
|
+class AdminAreaControllerTest extends TestCase
|
|
|
+{
|
|
|
+ use RefreshDatabase;
|
|
|
+
|
|
|
+ protected $seed = true;
|
|
|
+
|
|
|
+ private User $adminUser;
|
|
|
+ private User $managerUser;
|
|
|
+ private District $district;
|
|
|
+
|
|
|
+ protected function setUp(): void
|
|
|
+ {
|
|
|
+ parent::setUp();
|
|
|
+
|
|
|
+ $this->adminUser = User::factory()->create(['role' => Role::ADMIN]);
|
|
|
+ $this->managerUser = User::factory()->create(['role' => Role::MANAGER]);
|
|
|
+ $this->district = District::factory()->create(['shortname' => 'ЦАО', 'name' => 'Центральный']);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== Authentication & Authorization ====================
|
|
|
+
|
|
|
+ public function test_guest_cannot_access_areas_index(): void
|
|
|
+ {
|
|
|
+ $response = $this->get(route('admin.area.index'));
|
|
|
+
|
|
|
+ $response->assertRedirect(route('login'));
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_manager_cannot_access_areas_index(): void
|
|
|
+ {
|
|
|
+ $response = $this->actingAs($this->managerUser)
|
|
|
+ ->get(route('admin.area.index'));
|
|
|
+
|
|
|
+ $response->assertStatus(403);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_admin_can_access_areas_index(): void
|
|
|
+ {
|
|
|
+ $response = $this->actingAs($this->adminUser)
|
|
|
+ ->get(route('admin.area.index'));
|
|
|
+
|
|
|
+ $response->assertStatus(200);
|
|
|
+ $response->assertViewIs('admin.areas.index');
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== Index ====================
|
|
|
+
|
|
|
+ public function test_areas_index_displays_areas(): void
|
|
|
+ {
|
|
|
+ $area = Area::factory()->create([
|
|
|
+ 'name' => 'Тестовый район',
|
|
|
+ 'district_id' => $this->district->id,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $response = $this->actingAs($this->adminUser)
|
|
|
+ ->get(route('admin.area.index'));
|
|
|
+
|
|
|
+ $response->assertStatus(200);
|
|
|
+ $response->assertSee('Тестовый район');
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_areas_index_can_filter_by_district(): void
|
|
|
+ {
|
|
|
+ $area1 = Area::factory()->create([
|
|
|
+ 'name' => 'Район ЦАО',
|
|
|
+ 'district_id' => $this->district->id,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $otherDistrict = District::factory()->create(['shortname' => 'САО']);
|
|
|
+ $area2 = Area::factory()->create([
|
|
|
+ 'name' => 'Район САО',
|
|
|
+ 'district_id' => $otherDistrict->id,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $response = $this->actingAs($this->adminUser)
|
|
|
+ ->get(route('admin.area.index', ['district_id' => $this->district->id]));
|
|
|
+
|
|
|
+ $response->assertStatus(200);
|
|
|
+ $response->assertSee('Район ЦАО');
|
|
|
+ $response->assertDontSee('Район САО');
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== Show ====================
|
|
|
+
|
|
|
+ public function test_admin_can_view_area_edit_form(): void
|
|
|
+ {
|
|
|
+ $area = Area::factory()->create(['district_id' => $this->district->id]);
|
|
|
+
|
|
|
+ $response = $this->actingAs($this->adminUser)
|
|
|
+ ->get(route('admin.area.show', $area->id));
|
|
|
+
|
|
|
+ $response->assertStatus(200);
|
|
|
+ $response->assertViewIs('admin.areas.edit');
|
|
|
+ $response->assertSee($area->name);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_manager_cannot_view_area_edit_form(): void
|
|
|
+ {
|
|
|
+ $area = Area::factory()->create(['district_id' => $this->district->id]);
|
|
|
+
|
|
|
+ $response = $this->actingAs($this->managerUser)
|
|
|
+ ->get(route('admin.area.show', $area->id));
|
|
|
+
|
|
|
+ $response->assertStatus(403);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== Store ====================
|
|
|
+
|
|
|
+ public function test_admin_can_create_new_area(): void
|
|
|
+ {
|
|
|
+ $response = $this->actingAs($this->adminUser)
|
|
|
+ ->post(route('admin.area.store'), [
|
|
|
+ 'name' => 'Тестовый район',
|
|
|
+ 'district_id' => $this->district->id,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $response->assertRedirect(route('admin.area.index'));
|
|
|
+ $this->assertDatabaseHas('areas', [
|
|
|
+ 'name' => 'Тестовый район',
|
|
|
+ 'district_id' => $this->district->id,
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_admin_can_update_existing_area(): void
|
|
|
+ {
|
|
|
+ $area = Area::factory()->create([
|
|
|
+ 'name' => 'Старое название',
|
|
|
+ 'district_id' => $this->district->id,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $newDistrict = District::factory()->create();
|
|
|
+
|
|
|
+ $response = $this->actingAs($this->adminUser)
|
|
|
+ ->post(route('admin.area.store'), [
|
|
|
+ 'id' => $area->id,
|
|
|
+ 'name' => 'Новое название',
|
|
|
+ 'district_id' => $newDistrict->id,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $response->assertRedirect(route('admin.area.index'));
|
|
|
+ $this->assertDatabaseHas('areas', [
|
|
|
+ 'id' => $area->id,
|
|
|
+ 'name' => 'Новое название',
|
|
|
+ 'district_id' => $newDistrict->id,
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_store_area_requires_name(): void
|
|
|
+ {
|
|
|
+ $response = $this->actingAs($this->adminUser)
|
|
|
+ ->post(route('admin.area.store'), [
|
|
|
+ 'district_id' => $this->district->id,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $response->assertSessionHasErrors('name');
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_store_area_requires_district_id(): void
|
|
|
+ {
|
|
|
+ $response = $this->actingAs($this->adminUser)
|
|
|
+ ->post(route('admin.area.store'), [
|
|
|
+ 'name' => 'Тестовый район',
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $response->assertSessionHasErrors('district_id');
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_store_area_validates_district_exists(): void
|
|
|
+ {
|
|
|
+ $response = $this->actingAs($this->adminUser)
|
|
|
+ ->post(route('admin.area.store'), [
|
|
|
+ 'name' => 'Тестовый район',
|
|
|
+ 'district_id' => 99999,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $response->assertSessionHasErrors('district_id');
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== Destroy ====================
|
|
|
+
|
|
|
+ public function test_admin_can_delete_area(): void
|
|
|
+ {
|
|
|
+ $area = Area::factory()->create(['district_id' => $this->district->id]);
|
|
|
+
|
|
|
+ $response = $this->actingAs($this->adminUser)
|
|
|
+ ->delete(route('admin.area.destroy', $area->id));
|
|
|
+
|
|
|
+ $response->assertRedirect(route('admin.area.index'));
|
|
|
+ $this->assertSoftDeleted('areas', ['id' => $area->id]);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== Undelete ====================
|
|
|
+
|
|
|
+ public function test_admin_can_restore_deleted_area(): void
|
|
|
+ {
|
|
|
+ $area = Area::factory()->create(['district_id' => $this->district->id]);
|
|
|
+ $area->delete();
|
|
|
+
|
|
|
+ $response = $this->actingAs($this->adminUser)
|
|
|
+ ->post(route('admin.area.undelete', $area->id));
|
|
|
+
|
|
|
+ $response->assertRedirect(route('admin.area.index'));
|
|
|
+ $this->assertDatabaseHas('areas', [
|
|
|
+ 'id' => $area->id,
|
|
|
+ 'deleted_at' => null,
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== Export ====================
|
|
|
+
|
|
|
+ public function test_admin_can_export_areas(): void
|
|
|
+ {
|
|
|
+ Area::factory()->count(3)->create(['district_id' => $this->district->id]);
|
|
|
+
|
|
|
+ $response = $this->actingAs($this->adminUser)
|
|
|
+ ->post(route('admin.area.export'));
|
|
|
+
|
|
|
+ $response->assertRedirect();
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== Import ====================
|
|
|
+
|
|
|
+ public function test_admin_can_import_areas(): void
|
|
|
+ {
|
|
|
+ Storage::fake('upload');
|
|
|
+
|
|
|
+ $file = $this->createTestAreasXlsx();
|
|
|
+
|
|
|
+ $response = $this->actingAs($this->adminUser)
|
|
|
+ ->post(route('admin.area.import'), [
|
|
|
+ 'import_file' => $file,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $response->assertRedirect(route('admin.area.index'));
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_import_requires_file(): void
|
|
|
+ {
|
|
|
+ $response = $this->actingAs($this->adminUser)
|
|
|
+ ->post(route('admin.area.import'), []);
|
|
|
+
|
|
|
+ $response->assertSessionHasErrors('import_file');
|
|
|
+ }
|
|
|
+
|
|
|
+ public function test_import_requires_xlsx_file(): void
|
|
|
+ {
|
|
|
+ $file = UploadedFile::fake()->create('test.txt', 100, 'text/plain');
|
|
|
+
|
|
|
+ $response = $this->actingAs($this->adminUser)
|
|
|
+ ->post(route('admin.area.import'), [
|
|
|
+ 'import_file' => $file,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ $response->assertSessionHasErrors('import_file');
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== Helper Methods ====================
|
|
|
+
|
|
|
+ private function createTestAreasXlsx(): UploadedFile
|
|
|
+ {
|
|
|
+ $spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
|
|
|
+ $sheet = $spreadsheet->getActiveSheet();
|
|
|
+
|
|
|
+ $sheet->setCellValue('A1', 'ID');
|
|
|
+ $sheet->setCellValue('B1', 'Название');
|
|
|
+ $sheet->setCellValue('C1', 'Округ (сокращение)');
|
|
|
+ $sheet->setCellValue('D1', 'ID округа');
|
|
|
+
|
|
|
+ $sheet->setCellValue('A2', '');
|
|
|
+ $sheet->setCellValue('B2', 'Тестовый район');
|
|
|
+ $sheet->setCellValue('C2', 'ЦАО');
|
|
|
+ $sheet->setCellValue('D2', $this->district->id);
|
|
|
+
|
|
|
+ $tempPath = sys_get_temp_dir() . '/test_areas_' . uniqid() . '.xlsx';
|
|
|
+ $writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
|
|
|
+ $writer->save($tempPath);
|
|
|
+
|
|
|
+ return new UploadedFile(
|
|
|
+ $tempPath,
|
|
|
+ 'test_areas.xlsx',
|
|
|
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
|
+ null,
|
|
|
+ true
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|