createUser(['ROLE_USER', 'ROLE_CLINIC']); $clinic = new Clinic($user); $clinic->setName('کلینیک تعطیلات'); $this->em->persist($clinic); $this->em->flush(); $address = DoctorAddress::forClinic($clinic->getId()); $address->setName('شعبهٔ مرکزی'); $this->em->persist($address); $this->em->flush(); $type = new ResourceType('clinic', (int) $clinic->getId(), 'device', 'دستگاه'); $this->em->persist($type); $this->em->flush(); $resource = new ClinicResource($address, $type, 'لیزر دایود'); $this->em->persist($resource); $this->em->flush(); return [$user, $resource]; } /** یک تعطیلِ رسمی روی روزی که منبع شیفت دارد، تا اثرش در دسترس‌پذیری دیده شود. */ private function shiftOn(ClinicResource $resource, int $midnight): void { $dayOfWeek = (int) (((int) date('w', $midnight) + 1) % 7); $this->em->persist(new ResourceCalendar($resource, $dayOfWeek, 540, 1020)); $this->em->flush(); } private function admin(): User { return $this->createUser(['ROLE_USER', 'ROLE_ADMIN']); } // ── ✅ موفق ────────────────────────────────────────────────────────────── public function testAnAdminRegistersAnOfficialHoliday(): void { $body = $this->authJson('POST', '/api/v1/admin/national-holidays', $this->admin(), [ 'jalali_date' => '1405-01-13', 'title' => 'سیزده‌بدر', ]); self::assertSame(201, $this->responseCode(), json_encode($body, JSON_UNESCAPED_UNICODE)); self::assertSame('1405-01-13', $body['data']['jalali_date']); self::assertSame('سیزده‌بدر', $body['data']['title']); self::assertSame(1405, $body['data']['jalali_year']); } /** ثبت روی `date` کلید یکتا دارد؛ دوباره فرستادن باید عنوان را عوض کند نه بشکند. */ public function testTheSameDayTwiceUpdatesTheTitle(): void { $admin = $this->admin(); $this->authJson('POST', '/api/v1/admin/national-holidays', $admin, [ 'jalali_date' => '1405-01-13', 'title' => 'سیزده بدر', ]); $body = $this->authJson('POST', '/api/v1/admin/national-holidays', $admin, [ 'jalali_date' => '1405-01-13', 'title' => 'روز طبیعت', ]); self::assertSame('روز طبیعت', $body['data']['title']); self::assertCount( 1, array_filter( $this->authJson('GET', '/api/v1/national-holidays?year=1405', $admin)['data']['holidays'], static fn (array $h): bool => $h['jalali_date'] === '1405-01-13', ), ); } public function testAHolidayCanBeRenamedAndRemoved(): void { $admin = $this->admin(); $uuid = $this->authJson('POST', '/api/v1/admin/national-holidays', $admin, [ 'jalali_date' => '1405-02-03', 'title' => 'مناسبت', ])['data']['uuid']; $renamed = $this->authJson('PATCH', "/api/v1/admin/national-holiday/{$uuid}", $admin, ['title' => 'مناسبت درست']); self::assertSame('مناسبت درست', $renamed['data']['title']); $this->authJson('DELETE', "/api/v1/admin/national-holiday/{$uuid}", $admin); self::assertSame(200, $this->responseCode()); // فهرست سال را کامل نمی‌سنجیم: `national_holidays` جدول سراسری است و بین // تست‌ها پاک نمی‌شود. آنچه مهم است، نبودنِ همین uuid است. $uuids = array_column( $this->authJson('GET', '/api/v1/national-holidays?year=1405', $admin)['data']['holidays'], 'uuid', ); self::assertNotContains($uuid, $uuids); } /** تعطیلِ ثبت‌شدهٔ مرکزی باید بی‌واسطه روز منبعِ هر محیطی را ببندد. */ public function testTheHolidayClosesThatDayForEveryResource(): void { [$user, $resource] = $this->clinicWithResource(); // ۱۴۰۵-۰۱-۱۳ به میلادی: ۲۰۲۶-۰۴-۰۲. $midnight = (int) strtotime('2026-04-02 00:00:00'); $this->shiftOn($resource, $midnight); $this->authJson('POST', '/api/v1/admin/national-holidays', $this->admin(), [ 'jalali_date' => '1405-01-13', 'title' => 'سیزده‌بدر', ]); $days = $this->authJson( 'GET', sprintf('/api/v1/resource/%s/availability?from=%d&to=%d', $resource->getUuid(), $midnight, $midnight), $user, )['data']['days']; self::assertSame([], $days[0]['intervals']); self::assertContains('national_holiday', $days[0]['reasons']); } // ── ⚠️ مرزی ───────────────────────────────────────────────────────────── /** محیطی که آن روز باز است، با استثنای خودش تعطیلیِ سراسری را خنثی می‌کند. */ public function testAnEnvironmentCanOptOutOfTheOfficialHoliday(): void { [$user, $resource] = $this->clinicWithResource(); $midnight = (int) strtotime('2026-04-02 00:00:00'); $this->shiftOn($resource, $midnight); $this->authJson('POST', '/api/v1/admin/national-holidays', $this->admin(), [ 'jalali_date' => '1405-01-13', 'title' => 'سیزده‌بدر', ]); $this->authJson('POST', '/api/v1/holiday-overrides', $user, [ 'date' => $midnight, 'is_working' => true, 'note' => 'شیفت اورژانس', ]); $days = $this->authJson( 'GET', sprintf('/api/v1/resource/%s/availability?from=%d&to=%d', $resource->getUuid(), $midnight, $midnight), $user, )['data']['days']; self::assertNotSame([], $days[0]['intervals'], 'استثنای محیط بر تقویم رسمی مقدم است'); self::assertSame(480, $days[0]['total_minutes']); } // ── ❌ خطا ─────────────────────────────────────────────────────────────── public function testADoctorCannotRegisterANationalHoliday(): void { [$user] = $this->clinicWithResource(); $this->authJson('POST', '/api/v1/admin/national-holidays', $user, [ 'jalali_date' => '1405-01-13', 'title' => 'سیزده‌بدر', ]); self::assertSame(403, $this->responseCode()); } public function testAMalformedJalaliDateIsRefused(): void { $this->authJson('POST', '/api/v1/admin/national-holidays', $this->admin(), [ 'jalali_date' => '1405/1/13', 'title' => 'سیزده‌بدر', ]); self::assertSame(422, $this->responseCode()); } public function testTitleIsRequired(): void { $this->authJson('POST', '/api/v1/admin/national-holidays', $this->admin(), ['jalali_date' => '1405-01-13']); self::assertSame(422, $this->responseCode()); } public function testAnUnknownHolidayIs404(): void { $this->authJson('DELETE', '/api/v1/admin/national-holiday/00000000-0000-4000-8000-000000000000', $this->admin()); self::assertSame(404, $this->responseCode()); } }