test(policy): fail if the schema advertises a field nothing ever supplies
Task 09 left this as its starred risk and deferred it to task 10, which then shipped without it. The failure mode is silent and expensive: an operator writes a rule on a field no call site puts in the context, activates it, and it never matches — no error, no log, and the clinic believes the rule is running. The test is structural rather than behavioural on purpose. Walking every real path for every field would need a test rig larger than the engine; asserting that each advertised field is populated somewhere in src/ catches the case that actually happens, which is a field added to the schema and nowhere else. Also closes the last few rows that had gone stale: - evaluateIsolated: PolicyResolver::evaluateOne() landed with the sandbox - forbid before candidate generation: the plan builder already reads prohibitions before the availability engine is reached - appointments.applied_policies and app:policy:seed-examples are declined with their reasons rather than left open — the trace lives on the price snapshot and a second column would be a second source of truth, and the template registry does the seeding job from inside the UI where the user can see the result before creating anything - the reserve list keeps its page in the URL like every other panel list Every checklist across the sixteen tasks now has zero pending rows. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -35,9 +35,9 @@
|
||||
| ۱.۷ | `PolicyResolver` — اولویت → اختصاصیبودن → قدمت | ✅ | `comparator()` |
|
||||
| ۱.۸ | `specificity` هنگام **ذخیره** محاسبه میشود | ⚠️ | هنگام اجرا (`Policy::specificity()`) — دلیل ثبت شد؛ در خروجی API هم برمیگردد |
|
||||
| ۱.۹ | شش موتور جدا، هر کدام یک کلاس | ⚠️ | یک resolver + شش نقطهٔ مصرف — انحراف ثبتشده |
|
||||
| ۱.۱۰ | `evaluateIsolated()` روی هر شش موتور | ⏳ | تسک ۱۰ (آزمایشگاه قانون) — `PolicyResolver::resolve()` بدون I/O جانبی است، پس تسک ۱۰ میتواند مستقیم صدایش بزند |
|
||||
| ۱.۱۰ | `evaluateIsolated()` روی هر شش موتور | ✅ | `PolicyResolver::evaluateOne()` در تسک ۱۰ اضافه شد — یک resolver، نه شش موتور |
|
||||
| ۱.۱۱ | `SpacingPolicyEngine::forbiddenRanges()` — کوئری نه حلقه | ⚠️ | `spacing` در لحظهٔ رزرو موقت اجرا میشود (یک کوئری `MAX(slot_start)`)، نه در تولید کاندید |
|
||||
| ۱.۱۲ | بازهٔ ممنوعه پیش از تولید کاندید | ⏳ | به تسک ۱۳ موکول شد — هزینهاش نمایش اسلاتی است که هنگام رزرو رد میشود |
|
||||
| ۱.۱۲ | بازهٔ ممنوعه پیش از تولید کاندید | ✅ | `AppointmentPlanBuilder::applyResourcePolicies` ممنوعیت را **پیش از** رسیدن به موتور دسترسپذیری میخواند |
|
||||
| ۱.۱۳ | `combinable=false` → short-circuit؛ `deny` همیشه short-circuit | ✅ | `forbid` = veto؛ بقیهٔ اثرها ترکیبپذیرند |
|
||||
| ۱.۱۴ | فیلد بیمقدار → `false` **با لاگ** | ⚠️ | رد میشود (`array_key_exists` صریح) ولی **لاگ ندارد** — تسک ۱۰ |
|
||||
| ۱.۱۵ | `PATCH` محتوای قانون وجود ندارد | ✅ | فقط `POST /version` و `activate`/`deactivate` |
|
||||
@@ -64,10 +64,10 @@
|
||||
|---|---|---|---|
|
||||
| ۳.۱ | `policies` + `policy_version_logs` | ✅ | `Version20260731061814` |
|
||||
| ۳.۲ | ایندکس lookup | ✅ | `idx_policy_tenant_category (entity_type, entity_id, category, active)` + `idx_policy_validity` |
|
||||
| ۳.۳ | `appointments.applied_policies` | ⏳ | ردپا فعلاً در `PriceSnapshot.sources.applied_policies` — تسک ۱۰ |
|
||||
| ۳.۳ | `appointments.applied_policies` | — | ردپا در `PriceSnapshot.sources.applied_policies` است؛ ستون دوم یعنی دو منبع حقیقت برای یک چیز |
|
||||
| ۳.۴ | قرارداد `{uuid, version, name}` | ✅ | `name` کپی متنی است نه ارجاع |
|
||||
| ۳.۵ | `policy_version_logs` در `AGGREGATE_CHILDREN` | ✅ | `GlobalTables` |
|
||||
| ۳.۶ | `app:policy:seed-examples` | ⏳ | تسک ۱۰ همراه صفحهٔ آزمایشگاه |
|
||||
| ۳.۶ | `app:policy:seed-examples` | — | `PolicyTemplateRegistry` تسک ۱۰ همان کار را از داخل UI میکند و کاربر نتیجه را پیش از ساخت میبیند |
|
||||
| ۳.۷ | `TenantSchemaCoverageTest` سبز | ✅ | |
|
||||
| ۳.۸ | ستون `condition` به `condition_json` تغییر کرد | ✅ | `condition` در MariaDB کلمهٔ کلیدی است و هر INSERT را میشکست؛ نام فیلد در API همان `condition` ماند |
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
| ۶.۴ | `spacing` — بازهٔ ممنوعه + کوئری ثابت | ⚠️ | مسیرش تغییر کرد؛ تست اختصاصی ندارد — تسک ۱۳ |
|
||||
| ۶.۵ | نسخهبندی (قانون پنجم) | ✅ | ⭐ `testEditingAPolicyCreatesANewVersionAndTheQuoteRecordsIt` |
|
||||
| ۶.۶ | یکپارچگی چند دسته end-to-end | ✅ | timing · selection · pricing |
|
||||
| ۶.۷ | هر فیلد schema قابل extract است | ⏳ | ⭐ تسک ۱۰ — تا آنوقت خطرش در ۱.۳ ثبت است |
|
||||
| ۶.۷ | هر فیلد schema قابل extract است | ✅ | ⭐ `PolicyFieldCoverageTest` — فیلدی که هیچجا پر نشود، قانونی میسازد که بیصدا هرگز مطابقت نمیکند |
|
||||
| ۶.۸ | `NoPolicyRegressionTest` | ✅ | ⭐ |
|
||||
|
||||
**اجرا:** `ddev exec php bin/phpunit tests/Policy` → ۱۷ تست (۱ skip عمدی: تولید خروجی مستندات).
|
||||
|
||||
Reference in New Issue
Block a user