test(booking): freeze slot-mode contract before service-mode work

Adds SlotModeFrozenTest (#[Group('slot-mode-frozen')]) locking three things
against the multi-resource booking phase:
  - GET /api/v1/appointment-slots response shape
  - GET /api/v1/appointment-settings/month-availability/{uuid} response shape
  - public method signatures of SlotCalculatorService

Fixtures are structural, not raw snapshots: a fixed past date is rejected by
isWithinBookingWindow so an empty snapshot would prove nothing. Instead a
deterministic schedule on a computed near-future date, with epoch/uuid values
normalized to placeholders. What stays locked is the contract itself: keys,
ordering, types and local times.

No production code touched.

Task: docs/new_feture/taskes/task-00-service-mode-completion/
Slot-mode contract: unchanged (--group=slot-mode-frozen green, 3 tests)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
hamed
2026-07-30 12:31:57 +03:30
co-authored by Claude Opus 5
parent f2ecaf201a
commit c37c0cee39
6 changed files with 931 additions and 11 deletions
@@ -0,0 +1,16 @@
{
"_readme": "READ-ONLY — قرارداد منجمد GET /api/v1/appointment-settings/month-availability/{doctorUuid}. فهرست روزها به «امروز» وابسته است و placeholder می‌گیرد؛ قرارداد، کلیدها و ترتیب و نوع‌هاست. رجوع: docs/new_feture/taskes/_shared/red-lines.md",
"success": true,
"data": {
"year": "<year>",
"clinic_uuid": null,
"month": "<month>",
"disabled_dates": "<dates>",
"enabled_dates": "<dates>",
"online_booking_enabled": true,
"booking_window": {
"value": 3,
"unit": "month"
}
}
}
@@ -0,0 +1,75 @@
<?php
/**
* READ-ONLY — امضای منجمد متدهای عمومی SlotCalculatorService.
*
* افزودن پارامتر **اختیاری** جدید مجاز است و باید یک بار همین‌جا ثبت شود؛ تغییر نام،
* تغییر نوع، تغییر ترتیب یا حذف پارامتر موجود ممنوع است.
* رجوع: docs/new_feture/taskes/_shared/red-lines.md
*/
return [
'explainEmptyDay' => [
'returns' => '?string',
'params' => [
'doctor' => 'App\Doctor\Entity\Doctor',
'date' => 'string',
'clinic' => '?App\Clinic\Entity\Clinic = NULL',
'forManagement' => 'bool = false',
],
],
'findNextAvailableStart' => [
'returns' => '?int',
'params' => [
'doctor' => 'App\Doctor\Entity\Doctor',
'clinic' => '?App\Clinic\Entity\Clinic = NULL',
'daysAhead' => 'int = 30',
'forManagement' => 'bool = false',
],
],
'getAllSlotsWithAvailability' => [
'returns' => 'array',
'params' => [
'doctor' => 'App\Doctor\Entity\Doctor',
'date' => 'string',
'clinic' => '?App\Clinic\Entity\Clinic = NULL',
'forManagement' => 'bool = false',
],
],
'getAvailableSlots' => [
'returns' => 'array',
'params' => [
'doctor' => 'App\Doctor\Entity\Doctor',
'date' => 'string',
'clinic' => '?App\Clinic\Entity\Clinic = NULL',
'forManagement' => 'bool = false',
],
],
'getServiceStartTimes' => [
'returns' => 'array',
'params' => [
'doctor' => 'App\Doctor\Entity\Doctor',
'date' => 'string',
'durationMinutes' => 'int',
'clinic' => '?App\Clinic\Entity\Clinic = NULL',
'forManagement' => 'bool = false',
],
],
'hasAnyAvailability' => [
'returns' => 'bool',
'params' => [
'doctor' => 'App\Doctor\Entity\Doctor',
'date' => 'string',
'clinic' => '?App\Clinic\Entity\Clinic = NULL',
'forManagement' => 'bool = false',
],
],
'resolveSlotLocationId' => [
'returns' => '?int',
'params' => [
'doctor' => 'App\Doctor\Entity\Doctor',
'slotStart' => 'int',
'clinic' => '?App\Clinic\Entity\Clinic = NULL',
'forManagement' => 'bool = false',
],
],
];
@@ -0,0 +1,22 @@
{
"_readme": "READ-ONLY — قرارداد منجمد GET /api/v1/appointment-slots. هیچ تسکی از فاز موتور چندمنبعی اجازهٔ به‌روزرسانی این فایل را ندارد. اگر SlotModeFrozenTest قرمز شد، کد باید برگردد نه این فایل. رجوع: docs/new_feture/taskes/_shared/red-lines.md",
"success": true,
"data": {
"doctor_uuid": "<uuid>",
"clinic_uuid": null,
"date": "<date>",
"sessions": [
{
"start_time": "09:00",
"end_time": "11:00",
"slots": [
{ "start": "<epoch>", "end": "<epoch>", "start_time": "09:00", "end_time": "09:30", "location_id": 1, "is_available": true },
{ "start": "<epoch>", "end": "<epoch>", "start_time": "09:30", "end_time": "10:00", "location_id": 1, "is_available": true },
{ "start": "<epoch>", "end": "<epoch>", "start_time": "10:00", "end_time": "10:30", "location_id": 1, "is_available": true },
{ "start": "<epoch>", "end": "<epoch>", "start_time": "10:30", "end_time": "11:00", "location_id": 1, "is_available": true }
]
}
],
"empty_reason": null
}
}