clinicWithAddress(); $type = $this->resourceType($address); $body = $this->createResource($user, $address, $type, ['capacity' => 2, 'setup_minutes' => 5]); self::assertSame(201, $this->responseCode(), json_encode($body, JSON_UNESCAPED_UNICODE)); self::assertSame(2, $body['data']['capacity']); self::assertSame(5, $body['data']['setup_minutes']); self::assertSame($address->getUuid(), $body['data']['address_uuid']); self::assertNull($body['data']['subject_kind'], 'منبع بدون پل یعنی دستگاه'); $resource = $this->em->getRepository(ClinicResource::class)->findOneBy(['uuid' => $body['data']['uuid']]); self::assertSame('clinic', $resource->getEntityType()); self::assertSame($clinic->getId(), $resource->getEntityId()); } public function testDefaultsAreOneCapacityAndZeroBuffers(): void { [$user, , $address] = $this->clinicWithAddress(); $body = $this->createResource($user, $address, $this->resourceType($address)); self::assertSame(1, $body['data']['capacity']); self::assertSame(0, $body['data']['setup_minutes']); self::assertSame(0, $body['data']['cleanup_minutes']); self::assertTrue($body['data']['active']); } public function testZeroCapacityIsRejected(): void { [$user, , $address] = $this->clinicWithAddress(); $body = $this->createResource($user, $address, $this->resourceType($address), ['capacity' => 0]); self::assertSame(422, $this->responseCode()); self::assertSame('capacity', $body['errors'][0]['field']); } /** پزشک هم‌زمان دو بیمار ندارد — قید در خودِ entity است، نه فقط در سرویس. */ public function testCapacityAboveOneOnAPersonTypeIsRejected(): void { [$user, , $address] = $this->clinicWithAddress(); $doctorType = $this->resourceType($address, 'doctor', 'پزشک'); $body = $this->createResource($user, $address, $doctorType, ['capacity' => 2]); self::assertSame(422, $this->responseCode()); self::assertSame('capacity', $body['errors'][0]['field']); self::assertStringContainsString('شخص', $body['errors'][0]['message']); } /** ۰ و ۱۰ هر دو معتبرند: آماده‌سازی می‌تواند صفر باشد و تمیزکاری نباشد یا برعکس. */ public function testZeroSetupWithNonZeroCleanupIsValid(): void { [$user, , $address] = $this->clinicWithAddress(); $body = $this->createResource($user, $address, $this->resourceType($address), [ 'setup_minutes' => 0, 'cleanup_minutes' => 10, ]); self::assertSame(201, $this->responseCode()); self::assertSame(10, $body['data']['cleanup_minutes']); } public function testUnknownAttributeKeyIsAcceptedButNonScalarValueIsNot(): void { [$user, , $address] = $this->clinicWithAddress(); $type = $this->resourceType($address); $ok = $this->createResource($user, $address, $type, [ 'attributes' => ['gender' => 'female', 'anything_else' => 42], ]); self::assertSame(201, $this->responseCode(), 'کلید ناشناخته عمداً پذیرفته می‌شود'); self::assertSame('female', $ok['data']['attributes']['gender']); self::assertSame(42, $ok['data']['attributes']['anything_else']); $bad = $this->createResource($user, $address, $type, [ 'name' => 'دستگاه دوم', 'attributes' => ['nested' => ['a' => 1]], ]); self::assertSame(422, $this->responseCode()); self::assertSame('attributes', $bad['errors'][0]['field']); } public function testAttributeKeyMustBeSnakeCase(): void { [$user, , $address] = $this->clinicWithAddress(); $body = $this->createResource($user, $address, $this->resourceType($address), [ 'attributes' => ['Device Model' => 'x'], ]); self::assertSame(422, $this->responseCode()); self::assertSame('attributes', $body['errors'][0]['field']); } public function testForeignAddressIsNotFound(): void { [$user, , $address] = $this->clinicWithAddress(); $type = $this->resourceType($address); [, , $foreignAddress] = $this->clinicWithAddress('شعبهٔ بیگانه'); $this->authJson('POST', '/api/v1/resource', $user, [ 'address_uuid' => $foreignAddress->getUuid(), 'type_uuid' => $type->getUuid(), 'name' => 'دستگاه', ]); self::assertSame(404, $this->responseCode()); } public function testForeignResourceIsNotFound(): void { [$ownerUser, , $ownerAddress] = $this->clinicWithAddress(); $created = $this->createResource($ownerUser, $ownerAddress, $this->resourceType($ownerAddress)); [$otherUser] = $this->clinicWithAddress('شعبهٔ دیگر'); $this->authJson('GET', "/api/v1/resource/{$created['data']['uuid']}", $otherUser); self::assertSame(404, $this->responseCode()); $this->authJson('PATCH', "/api/v1/resource/{$created['data']['uuid']}", $otherUser, ['name' => 'دزدیده‌شده']); self::assertSame(404, $this->responseCode()); $this->authJson('DELETE', "/api/v1/resource/{$created['data']['uuid']}", $otherUser); self::assertSame(404, $this->responseCode()); } public function testResourceIsUpdatedAndDeleted(): void { [$user, , $address] = $this->clinicWithAddress(); $created = $this->createResource($user, $address, $this->resourceType($address)); $body = $this->authJson('PATCH', "/api/v1/resource/{$created['data']['uuid']}", $user, [ 'name' => 'لیزر دو', 'cleanup_minutes' => 15, 'active' => false, ]); self::assertSame(200, $this->responseCode()); self::assertSame('لیزر دو', $body['data']['name']); self::assertSame(15, $body['data']['cleanup_minutes']); self::assertFalse($body['data']['active']); $this->authJson('DELETE', "/api/v1/resource/{$created['data']['uuid']}", $user); self::assertSame(200, $this->responseCode()); $this->authJson('GET', "/api/v1/resource/{$created['data']['uuid']}", $user); self::assertSame(404, $this->responseCode()); } public function testListIsFilteredByAddressTypeAndActive(): void { [$user, $clinic, $address] = $this->clinicWithAddress(); $second = $this->extraAddress($clinic); $device = $this->resourceType($address, 'device', 'دستگاه'); $chair = $this->resourceType($address, 'chair', 'صندلی'); $this->createResource($user, $address, $device, ['name' => 'دستگاه شعبهٔ یک']); $this->createResource($user, $second, $device, ['name' => 'دستگاه شعبهٔ دو']); $off = $this->createResource($user, $address, $chair, ['name' => 'صندلی خاموش']); $this->authJson('PATCH', "/api/v1/resource/{$off['data']['uuid']}", $user, ['active' => false]); $all = $this->authJson('GET', '/api/v1/resources', $user); self::assertCount(3, $all['data']); $byAddress = $this->authJson('GET', "/api/v1/resources?address_uuid={$address->getUuid()}", $user); self::assertCount(2, $byAddress['data']); $byType = $this->authJson('GET', "/api/v1/resources?type_uuid={$device->getUuid()}", $user); self::assertCount(2, $byType['data']); $activeOnly = $this->authJson('GET', '/api/v1/resources?active=1', $user); self::assertCount(2, $activeOnly['data']); } public function testListShowsOnlyTheCurrentEnvironment(): void { [$user, , $address] = $this->clinicWithAddress(); $this->createResource($user, $address, $this->resourceType($address)); [$otherUser, , $otherAddress] = $this->clinicWithAddress('شعبهٔ دیگر'); $this->createResource($otherUser, $otherAddress, $this->resourceType($otherAddress)); $body = $this->authJson('GET', '/api/v1/resources', $user); self::assertCount(1, $body['data']); } public function testBlankNameIsRejected(): void { [$user, , $address] = $this->clinicWithAddress(); $body = $this->createResource($user, $address, $this->resourceType($address), ['name' => ' ']); self::assertSame(422, $this->responseCode()); self::assertSame('name', $body['errors'][0]['field']); } }