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); $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]; } public function testBlockingAFreeRangeSucceedsAndIsListed(): void { [$user, $resource] = $this->clinicWithResource(); $start = time() + 86400; $body = $this->authJson('POST', "/api/v1/resource/{$resource->getUuid()}/blocks", $user, [ 'starts_at' => $start, 'ends_at' => $start + 4 * 3600, 'reason' => 'سرویس دوره‌ای دستگاه', ]); self::assertSame(201, $this->responseCode(), json_encode($body, JSON_UNESCAPED_UNICODE)); self::assertSame('سرویس دوره‌ای دستگاه', $body['data']['segment_name']); $list = $this->authJson( 'GET', sprintf('/api/v1/resource/%s/blocks?from=%d&to=%d', $resource->getUuid(), $start - 3600, $start + 86400), $user, ); self::assertCount(1, $list['data']); } public function testAnInvertedRangeIsRejected(): void { [$user, $resource] = $this->clinicWithResource(); $start = time() + 86400; $this->authJson('POST', "/api/v1/resource/{$resource->getUuid()}/blocks", $user, [ 'starts_at' => $start, 'ends_at' => $start - 3600, ]); self::assertSame(422, $this->responseCode()); } /** ⭐ مسدودسازی روی بازه‌ای که نوبت دارد، ظرفیت را پس نمی‌گیرد. */ public function testBlockingOverABookedRangeIsRejected(): void { [$user, $resource] = $this->clinicWithResource(); $start = time() + 2 * 86400; $em = static::getContainer()->get(EntityManagerInterface::class); $reloaded = $em->getRepository(ClinicResource::class)->find($resource->getId()); $booked = new ResourceOccupancy($reloaded, $start, $start + 3600); $booked->setAppointmentId(4242); $em->persist($booked); $em->flush(); $this->authJson('POST', "/api/v1/resource/{$resource->getUuid()}/blocks", $user, [ 'starts_at' => $start, 'ends_at' => $start + 7200, ]); self::assertSame(409, $this->responseCode()); } /** ⭐ اشغالِ یک نوبت واقعی از این مسیر حذف نمی‌شود. */ public function testAnAppointmentOccupancyCannotBeDeletedAsABlock(): void { [$user, $resource] = $this->clinicWithResource(); $start = time() + 3 * 86400; $em = static::getContainer()->get(EntityManagerInterface::class); $reloaded = $em->getRepository(ClinicResource::class)->find($resource->getId()); $booked = new ResourceOccupancy($reloaded, $start, $start + 3600); $booked->setAppointmentId(777); $em->persist($booked); $em->flush(); $this->authJson('DELETE', "/api/v1/resource-block/{$booked->getUuid()}", $user); self::assertSame(422, $this->responseCode()); } public function testAManualBlockCanBeDeleted(): void { [$user, $resource] = $this->clinicWithResource(); $start = time() + 86400; $created = $this->authJson('POST', "/api/v1/resource/{$resource->getUuid()}/blocks", $user, [ 'starts_at' => $start, 'ends_at' => $start + 3600, ]); $this->authJson('DELETE', "/api/v1/resource-block/{$created['data']['uuid']}", $user); self::assertSame(200, $this->responseCode()); $list = $this->authJson( 'GET', sprintf('/api/v1/resource/%s/blocks?from=%d&to=%d', $resource->getUuid(), $start - 3600, $start + 86400), $user, ); self::assertCount(0, $list['data']); } public function testAnotherClinicCannotBlockTheResource(): void { [, $resource] = $this->clinicWithResource(); [$other] = $this->clinicWithResource(); $start = time() + 86400; $this->authJson('POST', "/api/v1/resource/{$resource->getUuid()}/blocks", $other, [ 'starts_at' => $start, 'ends_at' => $start + 3600, ]); self::assertSame(404, $this->responseCode()); } }