$days */ private function putCalendar(array $days): array { [$user, , $address] = $this->clinicWithAddress(); $resource = $this->createResource($user, $address, $this->resourceType($address)); return $this->authJson( 'PUT', "/api/v1/resource/{$resource['data']['uuid']}/calendar", $user, ['days' => $days], ); } /** ۰۹:۰۰–۱۳:۰۰ کنار ۱۲:۰۰–۱۷:۰۰ یعنی یک ساعت ظرفیتِ دوباره‌فروخته‌شده. */ public function testOverlappingShiftsInTheSameDayAreRejected(): void { $body = $this->putCalendar([ 0 => [ ['start_minute' => 540, 'end_minute' => 780], // ۰۹:۰۰–۱۳:۰۰ ['start_minute' => 720, 'end_minute' => 1020], // ۱۲:۰۰–۱۷:۰۰ ], ]); self::assertSame(422, $this->responseCode(), json_encode($body, JSON_UNESCAPED_UNICODE)); self::assertSame('start_minute', $body['errors'][0]['field']); self::assertStringContainsString('شنبه', $body['errors'][0]['message'], 'پیام باید نام روز را بگوید نه اندیس'); self::assertStringContainsString('تداخل', $body['errors'][0]['message']); } /** ترتیب ورودی نباید مهم باشد: تداخل بین شیفت اول و سوم هم باید دیده شود. */ public function testOverlapIsDetectedRegardlessOfInputOrder(): void { $body = $this->putCalendar([ 2 => [ ['start_minute' => 900, 'end_minute' => 1020], // ۱۵:۰۰–۱۷:۰۰ ['start_minute' => 540, 'end_minute' => 600], // ۰۹:۰۰–۱۰:۰۰ ['start_minute' => 960, 'end_minute' => 1080], // ۱۶:۰۰–۱۸:۰۰ ← با اولی تداخل دارد ], ]); self::assertSame(422, $this->responseCode()); self::assertStringContainsString('دوشنبه', $body['errors'][0]['message']); } /** مرزِ چسبیده هم‌پوشانی نیست: ۱۳:۰۰ پایانِ یکی و شروعِ بعدی، شیفت صبح و عصر است. */ public function testBackToBackShiftsAreAccepted(): void { $body = $this->putCalendar([ 0 => [ ['start_minute' => 540, 'end_minute' => 780], ['start_minute' => 780, 'end_minute' => 1020], ], ]); self::assertSame(200, $this->responseCode(), json_encode($body, JSON_UNESCAPED_UNICODE)); self::assertCount(2, $body['data']['days']['0']); } /** اعتبارسنجی کامل پیش از هر حذفی — روز نامعتبر نباید روزهای درست را پاک کند. */ public function testAnInvalidDayLeavesTheExistingCalendarUntouched(): void { [$user, , $address] = $this->clinicWithAddress(); $resource = $this->createResource($user, $address, $this->resourceType($address)); $uuid = $resource['data']['uuid']; $this->authJson('PUT', "/api/v1/resource/$uuid/calendar", $user, [ 'days' => [0 => [['start_minute' => 540, 'end_minute' => 1020]]], ]); self::assertSame(200, $this->responseCode()); $this->authJson('PUT', "/api/v1/resource/$uuid/calendar", $user, [ 'days' => [ 0 => [['start_minute' => 540, 'end_minute' => 1020]], 1 => [ ['start_minute' => 540, 'end_minute' => 780], ['start_minute' => 600, 'end_minute' => 900], ], ], ]); self::assertSame(422, $this->responseCode()); $body = $this->authJson('GET', "/api/v1/resource/$uuid/calendar", $user); self::assertCount(1, $body['data']['days']['0'], 'شنبهٔ قبلی سرِ جایش مانده'); self::assertSame([], $body['data']['days']['1']); } }