ExportScheduleServiceTest.php 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. <?php
  2. namespace Tests\Unit\Services\Export;
  3. use App\Models\Dictionary\Area;
  4. use App\Models\Dictionary\District;
  5. use App\Models\Schedule;
  6. use App\Models\User;
  7. use App\Services\ExportScheduleService;
  8. use Illuminate\Database\Eloquent\Factories\Sequence;
  9. use Illuminate\Foundation\Testing\RefreshDatabase;
  10. use Illuminate\Support\Collection;
  11. use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
  12. use Tests\TestCase;
  13. class ExportScheduleServiceTest extends TestCase
  14. {
  15. use RefreshDatabase;
  16. protected $seed = true;
  17. private ExportScheduleService $service;
  18. protected function setUp(): void
  19. {
  20. parent::setUp();
  21. $this->service = new ExportScheduleService();
  22. }
  23. protected function tearDown(): void
  24. {
  25. unset($this->service);
  26. gc_collect_cycles();
  27. parent::tearDown();
  28. }
  29. public function test_service_instantiation(): void
  30. {
  31. $this->assertInstanceOf(ExportScheduleService::class, $this->service);
  32. }
  33. public function test_handle_with_empty_collection(): void
  34. {
  35. if (!file_exists('./templates/Schedule.xlsx')) {
  36. $this->markTestSkipped('Excel template Schedule.xlsx not found');
  37. }
  38. $user = User::factory()->create();
  39. $schedules = Collection::make([]);
  40. try {
  41. $result = $this->service->handle($schedules, $user->id);
  42. // Empty collection: loop does not execute, header variables stay at defaults
  43. $this->assertIsString($result);
  44. } catch (\Exception $e) {
  45. // Acceptable: PdfConverterClient::convert() or FileService may fail
  46. $this->assertTrue(true);
  47. }
  48. }
  49. public function test_handle_with_schedule_items(): void
  50. {
  51. if (!file_exists('./templates/Schedule.xlsx')) {
  52. $this->markTestSkipped('Excel template Schedule.xlsx not found');
  53. }
  54. $user = User::factory()->create();
  55. // Brigadier must have a color field because ExportScheduleService accesses brigadier->color
  56. $brigadier = User::factory()->brigadier()->create([
  57. 'color' => 'FF5733',
  58. ]);
  59. $district = District::query()->inRandomOrder()->first()
  60. ?? District::factory()->create();
  61. $area = Area::query()->inRandomOrder()->first()
  62. ?? Area::factory()->create(['district_id' => $district->id]);
  63. $schedule = Schedule::factory()->create([
  64. 'brigadier_id' => $brigadier->id,
  65. 'district_id' => $district->id,
  66. 'area_id' => $area->id,
  67. 'installation_date' => now()->addDays(7)->format('Y-m-d'),
  68. ]);
  69. // Load relations the service needs: district, area, brigadier
  70. $schedule->load(['district', 'area', 'brigadier']);
  71. $schedules = Collection::make([$schedule]);
  72. try {
  73. $result = $this->service->handle($schedules, $user->id);
  74. $this->assertIsString($result);
  75. } catch (\Exception $e) {
  76. // Expected: PdfConverterClient::convert() will fail without a running converter
  77. $this->assertTrue(true);
  78. }
  79. }
  80. public function test_handle_with_multiple_schedule_items_same_date(): void
  81. {
  82. if (!file_exists('./templates/Schedule.xlsx')) {
  83. $this->markTestSkipped('Excel template Schedule.xlsx not found');
  84. }
  85. $user = User::factory()->create();
  86. $brigadier = User::factory()->brigadier()->create([
  87. 'color' => '3498DB',
  88. ]);
  89. $district = District::query()->inRandomOrder()->first()
  90. ?? District::factory()->create();
  91. $area = Area::query()->inRandomOrder()->first()
  92. ?? Area::factory()->create(['district_id' => $district->id]);
  93. $installationDate = now()->addDays(5)->format('Y-m-d');
  94. $schedule1 = Schedule::factory()->create([
  95. 'brigadier_id' => $brigadier->id,
  96. 'district_id' => $district->id,
  97. 'area_id' => $area->id,
  98. 'installation_date' => $installationDate,
  99. ]);
  100. $schedule2 = Schedule::factory()->create([
  101. 'brigadier_id' => $brigadier->id,
  102. 'district_id' => $district->id,
  103. 'area_id' => $area->id,
  104. 'installation_date' => $installationDate,
  105. ]);
  106. $schedule1->load(['district', 'area', 'brigadier']);
  107. $schedule2->load(['district', 'area', 'brigadier']);
  108. $schedules = Collection::make([$schedule1, $schedule2]);
  109. try {
  110. $result = $this->service->handle($schedules, $user->id);
  111. // Two items with same date trigger cell merging — service should not crash before PdfConverter
  112. $this->assertIsString($result);
  113. } catch (\Exception $e) {
  114. // Expected: PdfConverterClient or FileService failure in test environment
  115. $this->assertTrue(true);
  116. }
  117. }
  118. public function test_build_spreadsheet_sets_pdf_layout_for_multiple_pages(): void
  119. {
  120. if (!file_exists('./templates/Schedule.xlsx')) {
  121. $this->markTestSkipped('Excel template Schedule.xlsx not found');
  122. }
  123. $brigadier = User::factory()->brigadier()->create([
  124. 'color' => '#123456',
  125. ]);
  126. $district = District::query()->inRandomOrder()->first()
  127. ?? District::factory()->create();
  128. $area = Area::query()->inRandomOrder()->first()
  129. ?? Area::factory()->create(['district_id' => $district->id]);
  130. $schedules = Schedule::factory()
  131. ->count(30)
  132. ->sequence(fn (Sequence $sequence) => [
  133. 'brigadier_id' => $brigadier->id,
  134. 'district_id' => $district->id,
  135. 'area_id' => $area->id,
  136. 'installation_date' => now()->addDays($sequence->index)->format('Y-m-d'),
  137. ])
  138. ->create()
  139. ->load(['district', 'area', 'brigadier']);
  140. $spreadsheet = $this->service->buildSpreadsheet(Collection::make($schedules->all()));
  141. $sheet = $spreadsheet->getActiveSheet();
  142. $this->assertSame('A1:K32', $sheet->getPageSetup()->getPrintArea());
  143. $this->assertSame(1, $sheet->getPageSetup()->getFitToWidth());
  144. $this->assertSame(0, $sheet->getPageSetup()->getFitToHeight());
  145. $this->assertSame(['A27' => Worksheet::BREAK_ROW], $sheet->getBreaks());
  146. }
  147. public function test_build_spreadsheet_keeps_small_schedule_on_single_page(): void
  148. {
  149. if (!file_exists('./templates/Schedule.xlsx')) {
  150. $this->markTestSkipped('Excel template Schedule.xlsx not found');
  151. }
  152. $brigadier = User::factory()->brigadier()->create([
  153. 'color' => '#654321',
  154. ]);
  155. $district = District::query()->inRandomOrder()->first()
  156. ?? District::factory()->create();
  157. $area = Area::query()->inRandomOrder()->first()
  158. ?? Area::factory()->create(['district_id' => $district->id]);
  159. $schedules = Schedule::factory()
  160. ->count(2)
  161. ->sequence(fn (Sequence $sequence) => [
  162. 'brigadier_id' => $brigadier->id,
  163. 'district_id' => $district->id,
  164. 'area_id' => $area->id,
  165. 'installation_date' => now()->addDays($sequence->index)->format('Y-m-d'),
  166. ])
  167. ->create()
  168. ->load(['district', 'area', 'brigadier']);
  169. $spreadsheet = $this->service->buildSpreadsheet(Collection::make($schedules->all()));
  170. $sheet = $spreadsheet->getActiveSheet();
  171. $this->assertSame('A1:K4', $sheet->getPageSetup()->getPrintArea());
  172. $this->assertSame([], $sheet->getBreaks());
  173. }
  174. }