createUser(['ROLE_DOCTOR']); $doctor = new Doctor($owner, 'دکتر قرارداد سایت'); $this->em->persist($doctor); $this->em->flush(); $schedule = $this->newWeeklySchedule($doctor, [ '0' => ['sessions' => [[ 'active' => true, 'start_time' => '09:00', 'end_time' => '18:00', 'duration_per_patient' => 20, 'location_id' => 1, ]]], ]); $schedule->setMeta(['booking_mode' => WeeklySchedule::MODE_SERVICE, 'buffer_minutes' => $buffer]); $this->em->persist($schedule); $section = new ServiceSection('doctor', $doctor->getId(), 'زیبایی'); $this->em->persist($section); $this->em->flush(); return [$owner, $doctor, $section]; } private function service(ServiceSection $section, string $name, int $minutes): ServiceItem { $item = new ServiceItem($section, $name, 1200000); $item->setDurationMinutes($minutes)->setBookable(true); $this->em->persist($item); $this->em->flush(); return $item; } /** @return array{0:\App\Auth\Entity\User,1:Appointment} */ private function bookedFor(Doctor $doctor, array $items, bool $reserve = false): array { $patient = $this->createUser(['ROLE_USER']); $start = (int) strtotime('+3 days 10:00'); $appt = $this->newAppointment($doctor, $patient, $start, $start + 35 * 60); $appt->transitionTo(Appointment::STATUS_CONFIRMED); if ($reserve) { $appt->rescheduleTo($start, $start, true); } if ($items !== []) { $appt->replaceServiceItems($items); $appt->setServiceDuration(35, 10); } $this->em->persist($appt); $this->em->flush(); return [$patient, $appt]; } /** پاسخ این endpoint دو لایه است: `data.data[]`. */ private function rows(\App\Auth\Entity\User $patient): array { $res = $this->authJson('GET', '/api/v1/appointments/user', $patient); self::assertSame(200, $this->responseCode()); return $res['data']['data']; } // ── ✅ موفق ────────────────────────────────────────────────────────────── public function testServiceFieldsThePublicPanelNeedsArePresent(): void { [, $doctor, $section] = $this->serviceDoctor(); $face = $this->service($section, 'لیزر صورت', 20); $bikini = $this->service($section, 'لیزر بیکینی', 15); [$patient] = $this->bookedFor($doctor, [$face, $bikini]); $row = $this->rows($patient)[0]; self::assertArrayHasKey('service_items', $row); self::assertCount(2, $row['service_items'], 'فهرست کامل، نه فقط سرویس اول'); self::assertSame(['لیزر صورت', 'لیزر بیکینی'], array_column($row['service_items'], 'name')); self::assertSame(35, $row['service_total_minutes']); self::assertSame(10, $row['service_buffer_minutes']); self::assertArrayHasKey('clinic_uuid', $row); self::assertNull($row['clinic_uuid'], 'مطب شخصی → null'); } public function testReserveEntryKeepsItsServicesAndDuration(): void { [, $doctor, $section] = $this->serviceDoctor(); $item = $this->service($section, 'لیزر', 45); [$patient] = $this->bookedFor($doctor, [$item], reserve: true); $row = $this->rows($patient)[0]; self::assertTrue($row['is_reserve']); self::assertCount(1, $row['service_items']); self::assertSame(35, $row['service_total_minutes'], 'مدت برای تبدیل بعدی می‌ماند'); } // ── ⚠️ مرزی ────────────────────────────────────────────────────────────── public function testSlotModeAppointmentLeavesTheServiceFieldsNull(): void { $owner = $this->createUser(['ROLE_DOCTOR']); $doctor = new Doctor($owner, 'دکتر اسلاتی'); $this->em->persist($doctor); $this->em->flush(); [$patient] = $this->bookedFor($doctor, []); $row = $this->rows($patient)[0]; // پنل سایت روی این با شرط && می‌خواند؛ null باید null بماند نه صفر. self::assertNull($row['service_total_minutes']); self::assertNull($row['service_buffer_minutes']); self::assertSame([], $row['service_items']); } public function testAppointmentWithoutServicesReturnsAnEmptyArrayNotNull(): void { [, $doctor] = $this->serviceDoctor(); [$patient] = $this->bookedFor($doctor, []); $row = $this->rows($patient)[0]; self::assertIsArray($row['service_items'], 'سایت روی length می‌خواند؛ null کرش می‌دهد'); self::assertSame([], $row['service_items']); } public function testEachServiceItemCarriesTheKeysTheSiteReads(): void { [, $doctor, $section] = $this->serviceDoctor(); $item = $this->service($section, 'لیزر', 30); [$patient] = $this->bookedFor($doctor, [$item]); $first = $this->rows($patient)[0]['service_items'][0]; foreach (['uuid', 'name', 'price_rials'] as $key) { self::assertArrayHasKey($key, $first); } } }