ImportAreasServiceTest.php 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. <?php
  2. namespace Tests\Unit\Services\Dictionary;
  3. use App\Models\Dictionary\Area;
  4. use App\Models\Dictionary\District;
  5. use App\Services\Import\ImportAreasService;
  6. use Illuminate\Foundation\Testing\RefreshDatabase;
  7. use PhpOffice\PhpSpreadsheet\Spreadsheet;
  8. use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
  9. use Tests\TestCase;
  10. class ImportAreasServiceTest extends TestCase
  11. {
  12. use RefreshDatabase;
  13. protected $seed = true;
  14. private string $tempFilePath;
  15. private District $district;
  16. protected function setUp(): void
  17. {
  18. parent::setUp();
  19. // Используем уникальное сокращение чтобы не конфликтовать с сидером
  20. $this->district = District::factory()->create(['shortname' => 'ТСТОКР', 'name' => 'Тестовый округ']);
  21. }
  22. protected function tearDown(): void
  23. {
  24. if (isset($this->tempFilePath) && file_exists($this->tempFilePath)) {
  25. unlink($this->tempFilePath);
  26. }
  27. parent::tearDown();
  28. }
  29. public function test_import_creates_new_areas(): void
  30. {
  31. $this->createTestFile([
  32. ['', 'ТестТверской', 'ТСТОКР', $this->district->id],
  33. ['', 'ТестАрбат', 'ТСТОКР', ''],
  34. ]);
  35. $service = new ImportAreasService($this->tempFilePath, 1);
  36. $result = $service->handle();
  37. $this->assertTrue($result['success']);
  38. $this->assertEquals(2, $result['imported']);
  39. $this->assertDatabaseHas('areas', ['name' => 'ТестТверской', 'district_id' => $this->district->id]);
  40. $this->assertDatabaseHas('areas', ['name' => 'ТестАрбат', 'district_id' => $this->district->id]);
  41. }
  42. public function test_import_updates_existing_areas_by_id(): void
  43. {
  44. $area = Area::factory()->create([
  45. 'name' => 'Старое название',
  46. 'district_id' => $this->district->id,
  47. ]);
  48. $newDistrict = District::factory()->create(['shortname' => 'САО']);
  49. $this->createTestFile([
  50. [$area->id, 'Новое название', 'САО', $newDistrict->id],
  51. ]);
  52. $service = new ImportAreasService($this->tempFilePath, 1);
  53. $result = $service->handle();
  54. $this->assertTrue($result['success']);
  55. $this->assertEquals(1, $result['updated']);
  56. $this->assertDatabaseHas('areas', [
  57. 'id' => $area->id,
  58. 'name' => 'Новое название',
  59. 'district_id' => $newDistrict->id,
  60. ]);
  61. }
  62. public function test_import_updates_existing_areas_by_name_and_district(): void
  63. {
  64. $area = Area::factory()->create([
  65. 'name' => 'Тверской',
  66. 'district_id' => $this->district->id,
  67. ]);
  68. $this->createTestFile([
  69. ['', 'Тверской', 'ЦАО', ''],
  70. ]);
  71. $service = new ImportAreasService($this->tempFilePath, 1);
  72. $result = $service->handle();
  73. $this->assertTrue($result['success']);
  74. $this->assertEquals(1, $result['updated']);
  75. }
  76. public function test_import_finds_district_by_shortname(): void
  77. {
  78. $this->createTestFile([
  79. ['', 'ТестовыйИмп1', 'ТСТОКР', ''],
  80. ]);
  81. $service = new ImportAreasService($this->tempFilePath, 1);
  82. $result = $service->handle();
  83. $this->assertTrue($result['success']);
  84. $this->assertEquals(1, $result['imported']);
  85. $this->assertDatabaseHas('areas', [
  86. 'name' => 'ТестовыйИмп1',
  87. 'district_id' => $this->district->id,
  88. ]);
  89. }
  90. public function test_import_finds_district_by_id(): void
  91. {
  92. $this->createTestFile([
  93. ['', 'Тестовый', '', $this->district->id],
  94. ]);
  95. $service = new ImportAreasService($this->tempFilePath, 1);
  96. $result = $service->handle();
  97. $this->assertTrue($result['success']);
  98. $this->assertEquals(1, $result['imported']);
  99. $this->assertDatabaseHas('areas', [
  100. 'name' => 'Тестовый',
  101. 'district_id' => $this->district->id,
  102. ]);
  103. }
  104. public function test_import_skips_rows_without_district(): void
  105. {
  106. $this->createTestFile([
  107. ['', 'Район без округа', '', ''],
  108. ['', 'Район с несуществующим округом', 'НЕСУЩЕСТВУЮЩИЙ', ''],
  109. ]);
  110. $service = new ImportAreasService($this->tempFilePath, 1);
  111. $result = $service->handle();
  112. $this->assertTrue($result['success']);
  113. $this->assertEquals(0, $result['imported']);
  114. $this->assertEquals(2, $result['skipped']);
  115. }
  116. public function test_import_skips_empty_rows(): void
  117. {
  118. $this->createTestFile([
  119. ['', '', '', ''],
  120. ['', 'Тестовый', 'ЦАО', ''],
  121. ]);
  122. $service = new ImportAreasService($this->tempFilePath, 1);
  123. $result = $service->handle();
  124. $this->assertTrue($result['success']);
  125. $this->assertEquals(1, $result['imported']);
  126. $this->assertEquals(1, $result['skipped']);
  127. }
  128. public function test_import_restores_soft_deleted_areas(): void
  129. {
  130. $area = Area::factory()->create([
  131. 'name' => 'Удалённый',
  132. 'district_id' => $this->district->id,
  133. ]);
  134. $area->delete();
  135. $this->createTestFile([
  136. [$area->id, 'Восстановленный', 'ЦАО', ''],
  137. ]);
  138. $service = new ImportAreasService($this->tempFilePath, 1);
  139. $result = $service->handle();
  140. $this->assertTrue($result['success']);
  141. $this->assertEquals(1, $result['updated']);
  142. $this->assertDatabaseHas('areas', [
  143. 'id' => $area->id,
  144. 'name' => 'Восстановленный',
  145. 'deleted_at' => null,
  146. ]);
  147. }
  148. public function test_import_is_case_insensitive_for_district_shortname(): void
  149. {
  150. $this->createTestFile([
  151. ['', 'ТестовыйИмп2', 'тстокр', ''], // lowercase
  152. ]);
  153. $service = new ImportAreasService($this->tempFilePath, 1);
  154. $result = $service->handle();
  155. $this->assertTrue($result['success']);
  156. $this->assertEquals(1, $result['imported']);
  157. $this->assertDatabaseHas('areas', [
  158. 'name' => 'ТестовыйИмп2',
  159. 'district_id' => $this->district->id,
  160. ]);
  161. }
  162. public function test_import_returns_logs(): void
  163. {
  164. $this->createTestFile([
  165. ['', 'Тестовый', 'ЦАО', ''],
  166. ]);
  167. $service = new ImportAreasService($this->tempFilePath, 1);
  168. $result = $service->handle();
  169. $this->assertArrayHasKey('logs', $result);
  170. $this->assertNotEmpty($result['logs']);
  171. }
  172. private function createTestFile(array $rows): void
  173. {
  174. $spreadsheet = new Spreadsheet();
  175. $sheet = $spreadsheet->getActiveSheet();
  176. // Headers
  177. $sheet->setCellValue('A1', 'ID');
  178. $sheet->setCellValue('B1', 'Название');
  179. $sheet->setCellValue('C1', 'Округ (сокращение)');
  180. $sheet->setCellValue('D1', 'ID округа');
  181. // Data rows
  182. $rowNum = 2;
  183. foreach ($rows as $row) {
  184. $sheet->setCellValue('A' . $rowNum, $row[0] ?? '');
  185. $sheet->setCellValue('B' . $rowNum, $row[1] ?? '');
  186. $sheet->setCellValue('C' . $rowNum, $row[2] ?? '');
  187. $sheet->setCellValue('D' . $rowNum, $row[3] ?? '');
  188. $rowNum++;
  189. }
  190. $this->tempFilePath = sys_get_temp_dir() . '/test_areas_' . uniqid() . '.xlsx';
  191. $writer = new Xlsx($spreadsheet);
  192. $writer->save($this->tempFilePath);
  193. }
  194. }