lankasailendra/BMCTESTMAIN
0
1"""Offline preflight for tenant-required-field resolution and the create gate.2 3 python scripts/preflight_required_fields.py # exits non-zero on any failure4 5No network, no LLM, no tenant, no app run. The AR form metadata and the identity6helpers are stubbed, so this runs anywhere and costs nothing.7 8What it guards: the tenant scan said "Contact Company" was required on9HPD:IncidentInterface_Create and the harness sent NOTHING for it — the label was10outside the hand-written _UI_REQUIRED_FILL whitelist, so it was reported once as11"(no field mapping)" in console output and then forgotten (2026-09-10). Fields12like that are not mysteries: Contact Company is the company _get_company()13already returns. Now they resolve by identity role, and a create that still has14an unfilled required field does not go out at all.15"""16 17import json18import os19import pathlib20import sys21 22_REF = pathlib.Path(__file__).resolve().parent.parent / "reference" / "itsm"23sys.path.insert(0, str(_REF))24 25from conftest_files import conftest_core as cc # noqa: E40226from conftest_files.conftest_workflow_errors import WorkflowError # noqa: E40227 28# Sections below stub cc.ar_form_field_meta to drive the callers that use it.29# The shipped-metadata section at the end exercises the REAL one, so keep a30# handle on it here — before the first stub is installed.31_REAL_AR_FORM_FIELD_META = cc.ar_form_field_meta32 33FAILS = []34 35 36def check(name, ok, detail=""):37 print(f"[{'ok ' if ok else 'FAIL'}] {name}" + (f" — {detail}" if detail else ""))38 if not ok:39 FAILS.append(name)40 41 42CHAR = lambda n=254: {43 "datatype": "CHAR",44 "max_length": n,45 "required": True,46} # noqa: E73147ENUM = lambda v: { # noqa: E73148 "datatype": "ENUM",49 "selection_values": v,50 "max_length": 0,51 "required": True,52}53 54# The real HPD:IncidentInterface_Create required list from a live tenant scan.55INCIDENT_META = {56 "Contact Company": CHAR(),57 "Create Impacted Area from Customer's Location": ENUM(["Yes", "No"]),58 "Description": CHAR(100),59 "Impact": ENUM(["1-Extensive", "4-Minor/Localized"]),60 "Incident Number": CHAR(15),61 "Owner Group": CHAR(60),62 "Phone Number": CHAR(30),63 "Priority": ENUM(["Critical", "Low"]),64 "Reported Source": ENUM(["Direct Input"]),65 "Service Type": ENUM(["User Service Restoration"]),66 "Short Description": CHAR(),67 "Status": ENUM(["New"]),68 "Submitter": CHAR(),69 "Urgency": ENUM(["4-Low"]),70}71 72IDENTITY = {73 "company": "Petramco",74 "first": "Sam",75 "last": "Tester",76 "full": "Sam Tester",77 "group": "Service Desk",78 "org": "IT Support",79 "phone": "555-0100",80}81 82 83def stub(meta, required_names, *, phone=IDENTITY["phone"], company=IDENTITY["company"]):84 cc.ar_form_field_meta = lambda b, h, f: meta85 cc.rest_required_fields = lambda m: list(required_names)86 cc._ui_required_labels = lambda m: []87 cc._get_company = lambda b, h: company88 cc._executor_person_info = lambda b, h: (89 IDENTITY["full"],90 IDENTITY["first"],91 IDENTITY["last"],92 )93 cc._get_support_group = lambda b, h: IDENTITY["group"]94 cc._executor_phone = lambda b, h: phone95 cc.SUPPORT_GROUP = IDENTITY["group"]96 cc.SUPPORT_ORG = IDENTITY["org"]97 cc._REQUIRED_SOURCE_CACHE.clear()98 cc._AR_FORM_FIELD_META_CACHE.clear()99 100 101print("-- a required field name maps to the identity that answers it --")102for name, want in (103 ("Contact Company", "company"),104 ("Customer Company", "company"),105 ("Location Company", "company"),106 ("Customer First Name", "first_name"),107 ("Customer Last Name", "last_name"),108 ("Owner Group", "group"),109 ("Support Group Name", "group"),110 ("Assigned Support Organization", "organization"),111 ("Phone Number", "phone"),112 ("Customer", "full_name"),113 ("Summary", ""),114 ("Work Order Type", ""),115):116 got = cc.required_field_role(name)117 check(f"{name!r} → {want or 'no role'}", got == want, f"got {got!r}")118 119check(120 "an AR-generated key still has no role and stays skipped",121 cc.required_field_role("Incident Number") == ""122 and cc._is_generated_id_field("Incident Number"),123)124 125print("\n-- the resolved values reach the create body --")126stub(INCIDENT_META, list(INCIDENT_META))127body = {"Description": "TC-ITSM-INC-002 create and edit an incident", "Status": "New"}128extra = cc.rest_required_extra_values(129 "https://t",130 {},131 "HPD:IncidentInterface_Create",132 "incident",133 body,134 body["Description"],135)136check(137 "Contact Company gets the tenant company",138 extra.get("Contact Company") == IDENTITY["company"],139 repr(extra.get("Contact Company")),140)141check(142 "Owner Group gets the support group",143 extra.get("Owner Group") == IDENTITY["group"],144 repr(extra.get("Owner Group")),145)146check(147 "Phone Number gets the person's phone, not the case summary",148 extra.get("Phone Number") == IDENTITY["phone"],149 repr(extra.get("Phone Number")),150)151check("Incident Number is still never sent", "Incident Number" not in extra)152check("Submitter is still left to AR", "Submitter" not in extra)153check(154 "enum fields still take their first selection value",155 extra.get("Impact") == "1-Extensive" and extra.get("Priority") == "Critical",156 f"{extra.get('Impact')!r}/{extra.get('Priority')!r}",157)158 159print("\n-- a value too long for the field is trimmed, not rejected by AR --")160stub({"Owner Group": CHAR(10)}, ["Owner Group"], company=IDENTITY["company"])161cc._get_support_group = lambda b, h: "A Very Long Support Group Name"162cc.SUPPORT_GROUP = "A Very Long Support Group Name"163cc._REQUIRED_SOURCE_CACHE.clear()164trimmed = cc.rest_required_extra_values("https://t", {}, "F", "incident", {}, "x")165check(166 "trimmed to the field's max length",167 len(trimmed.get("Owner Group", "")) == 10,168 repr(trimmed.get("Owner Group")),169)170 171print("\n-- a plain custom field no longer blocks the create --")172# This USED to raise. On a customized tenant that behaviour turned every173# customer-added text field into a blocked OOTB run, which is the opposite of174# what the suite is for.175stub(176 {"Contact Company": CHAR(), "Impacted Area By Region": CHAR()},177 ["Contact Company", "Impacted Area By Region"],178)179body = {"Description": "x"}180try:181 cc.finalize_required_fields("https://t", {}, "F", "incident", body)182 check("the create proceeds", True)183 check(184 "the custom field carries a marked placeholder",185 body.get("Impacted Area By Region") == "UAT",186 repr(body.get("Impacted Area By Region")),187 )188 check(189 "and the known field still got its real value",190 body.get("Contact Company") == IDENTITY["company"],191 repr(body.get("Contact Company")),192 )193except WorkflowError as exc:194 check("the create proceeds", False, str(exc)[:140])195 196print("\n-- but a field where any value would be a guess still stops it --")197stub(198 {199 "Contact Company": CHAR(),200 "Impacted Area By Region": {201 "datatype": "ENUM",202 "max_length": 0,203 "required": True,204 },205 },206 ["Contact Company", "Impacted Area By Region"],207)208body = {"Description": "x"}209try:210 cc.finalize_required_fields("https://t", {}, "F", "incident", body)211 check("the create is refused", False, "no WorkflowError raised")212except WorkflowError as exc:213 check("the create is refused", True)214 check(215 "the message names the unfillable field",216 "Impacted Area By Region" in str(exc),217 str(exc)[:120],218 )219 check("and does NOT name the one it filled", "Contact Company:" not in str(exc))220check(221 "the fillable field was still filled before the gate ran",222 body.get("Contact Company") == IDENTITY["company"],223 repr(body.get("Contact Company")),224)225 226print("\n-- a fully resolvable form passes the gate --")227stub(INCIDENT_META, list(INCIDENT_META))228body = {"Description": "TC-ITSM-INC-002 create and edit an incident", "Status": "New"}229try:230 cc.finalize_required_fields(231 "https://t",232 {},233 "HPD:IncidentInterface_Create",234 "incident",235 body,236 body["Description"],237 )238 check("no exception", True)239 check(240 "every required field is now set",241 all(242 str(body.get(f, "")).strip()243 for f in INCIDENT_META244 if f.lower() not in cc._REST_REQUIRED_SKIP245 and not cc._is_generated_id_field(f)246 ),247 str({k: v for k, v in body.items()})[:200],248 )249except WorkflowError as exc:250 check("no exception", False, str(exc)[:160])251 252print("\n-- the gate can be downgraded for one run --")253stub({"Impacted Area By Region": CHAR()}, ["Impacted Area By Region"])254os.environ["BMC_REQUIRED_FIELD_GATE"] = "off"255try:256 cc.finalize_required_fields("https://t", {}, "F", "incident", {})257 check("BMC_REQUIRED_FIELD_GATE=off warns instead of raising", True)258except WorkflowError as exc:259 check("BMC_REQUIRED_FIELD_GATE=off warns instead of raising", False, str(exc)[:100])260finally:261 os.environ.pop("BMC_REQUIRED_FIELD_GATE", None)262 263print("\n-- a CUSTOMIZED tenant must not break an OOTB test --")264# A customer makes their own fields required. An OOTB case that has nothing to265# do with those fields still has to create a record.266CUSTOM_META = {267 "Contact Company": CHAR(),268 "CUST_BusinessJustification": CHAR(200), # plain custom text269 "CUST_CostCentre": CHAR(30), # plain custom text, short270 "CUST_Region": { # custom, menu-backed271 "datatype": "CHAR",272 "max_length": 30,273 "required": True,274 "selection_values": ["East", "West"],275 },276 "CUST_ReviewCount": {"datatype": "INTEGER", "max_length": 0, "required": True},277 "CUST_ReviewedOn": {"datatype": "DATE/TIME", "max_length": 0, "required": True},278 "CUST_Classification": { # ENUM whose options are hidden279 "datatype": "ENUM",280 "max_length": 0,281 "required": True,282 },283}284stub(CUSTOM_META, list(CUSTOM_META))285body = {"Description": "an OOTB incident case"}286extra = cc.rest_required_extra_values(287 "https://t", {}, "F", "incident", body, "an OOTB incident case"288)289check(290 "a plain custom text field gets a MARKED placeholder",291 extra.get("CUST_BusinessJustification") == "UAT",292 repr(extra.get("CUST_BusinessJustification")),293)294check(295 "a custom menu field takes one of the tenant's OWN options",296 extra.get("CUST_Region") == "East",297 repr(extra.get("CUST_Region")),298)299check(300 "a custom integer gets 0",301 extra.get("CUST_ReviewCount") == "0",302 repr(extra.get("CUST_ReviewCount")),303)304check(305 "a custom date/time gets a real timestamp",306 len(str(extra.get("CUST_ReviewedOn") or "")) > 10,307 repr(extra.get("CUST_ReviewedOn")),308)309check(310 "an ENUM with hidden options is NEVER guessed",311 "CUST_Classification" not in extra,312 repr(extra.get("CUST_Classification")),313)314check(315 "the known field still resolves properly, not as a placeholder",316 extra.get("Contact Company") == IDENTITY["company"],317 repr(extra.get("Contact Company")),318)319 320print("\n-- and the tester can supply values without a code change --")321stub(CUSTOM_META, list(CUSTOM_META))322os.environ["BMC_REQUIRED_FIELD_DEFAULTS"] = (323 '{"CUST_BusinessJustification": "Annual audit", "CUST_Classification": "Internal"}'324)325os.environ["BMC_FIELD_CUST_COSTCENTRE"] = "CC-4471"326try:327 extra = cc.rest_required_extra_values("https://t", {}, "F", "incident", {}, "x")328 check(329 "BMC_REQUIRED_FIELD_DEFAULTS wins over the placeholder",330 extra.get("CUST_BusinessJustification") == "Annual audit",331 repr(extra.get("CUST_BusinessJustification")),332 )333 check(334 "and answers the enum nothing else could",335 extra.get("CUST_Classification") == "Internal",336 repr(extra.get("CUST_Classification")),337 )338 check(339 "BMC_FIELD_<NAME> works too",340 extra.get("CUST_CostCentre") == "CC-4471",341 repr(extra.get("CUST_CostCentre")),342 )343finally:344 os.environ.pop("BMC_REQUIRED_FIELD_DEFAULTS", None)345 os.environ.pop("BMC_FIELD_CUST_COSTCENTRE", None)346 347print("\n-- the gate now fires ONLY where any value would be a guess --")348stub(CUSTOM_META, list(CUSTOM_META))349body = {"Description": "x"}350try:351 cc.finalize_required_fields("https://t", {}, "F", "incident", body)352 check(353 "a customized tenant does not block the create",354 False,355 "expected the hidden enum to gate",356 )357except WorkflowError as exc:358 check(359 "the hidden-options enum is the only blocker",360 "CUST_Classification" in str(exc),361 str(exc)[:130],362 )363 check(364 "the plain custom fields were filled, not gated",365 "CUST_BusinessJustification" not in str(exc)366 and body.get("CUST_BusinessJustification") == "UAT",367 )368 check(369 "and the message points at the override",370 "BMC_REQUIRED_FIELD_DEFAULTS" in str(exc),371 )372 373print("\n-- synthetic fill can be switched off entirely --")374stub(CUSTOM_META, list(CUSTOM_META))375os.environ["BMC_REQUIRED_FIELD_SYNTHETIC"] = "off"376try:377 extra = cc.rest_required_extra_values("https://t", {}, "F", "incident", {}, "x")378 check(379 "no placeholder is invented",380 "CUST_BusinessJustification" not in extra,381 repr(extra.get("CUST_BusinessJustification")),382 )383 check(384 "but a tenant menu is still honoured",385 extra.get("CUST_Region") == "East",386 repr(extra.get("CUST_Region")),387 )388finally:389 os.environ.pop("BMC_REQUIRED_FIELD_SYNTHETIC", None)390 391print("\n-- a failed metadata probe no longer disables the contract --")392cc.ar_form_field_meta = lambda b, h, f: {} # the probe returns nothing393cc.rest_required_fields = lambda m: ["Contact Company", "CUST_Region"]394cc._REQUIRED_SOURCE_CACHE.clear()395extra = cc.rest_required_extra_values("https://t", {}, "F", "incident", {}, "x")396check(397 "an identity field is still filled with no metadata at all",398 extra.get("Contact Company") == IDENTITY["company"],399 repr(extra.get("Contact Company")),400)401# With no metadata the harness cannot tell a plain text field from a menu-backed402# one. It still fills — an incomplete create is a certain failure, while a403# placeholder at least reaches AR, which names the field if it is wrong — but the404# log must not read like a considered decision.405check(406 "an unknown field is still filled so the create can proceed",407 extra.get("CUST_Region") == "UAT",408 repr(extra.get("CUST_Region")),409)410 411print("\n-- the same roles resolve OFFLINE for the Smart IT fillers --")412# The UI fillers run inside a browser step with no REST session. executor_prep413# has already put these in the environment, so the UI side must reach the same414# answers the REST side does — otherwise a company field gets one value over415# REST and none in the UI.416cc._REQUIRED_SOURCE_CACHE.clear()417os.environ.update(418 {419 "BMC_COMPANY": "Petramco",420 "BMC_FIRST_NAME": "Sam",421 "BMC_LAST_NAME": "Tester",422 "BMC_SUPPORT_GROUP": "Service Desk",423 "BMC_PHONE": "555-0100",424 }425)426try:427 check(428 "Contact Company resolves with no REST session",429 cc.resolve_required_field("Contact Company") == "Petramco",430 repr(cc.resolve_required_field("Contact Company")),431 )432 check(433 "Assignee Support Group resolves offline",434 cc.resolve_required_field("Assignee Support Group") == "Service Desk",435 repr(cc.resolve_required_field("Assignee Support Group")),436 )437 check(438 "a field with no identity role resolves to nothing",439 cc.resolve_required_field("Work Order Type") == "",440 )441finally:442 for _k in (443 "BMC_COMPANY",444 "BMC_FIRST_NAME",445 "BMC_LAST_NAME",446 "BMC_SUPPORT_GROUP",447 "BMC_PHONE",448 ):449 os.environ.pop(_k, None)450 cc._REQUIRED_SOURCE_CACHE.clear()451 452print("\n-- the UI filler writes, then PROVES the field took the value --")453from conftest_files import conftest_ui as cu # noqa: E402454 455cc._REQUIRED_SOURCE_CACHE.clear()456os.environ["BMC_COMPANY"] = "Petramco"457 458 459class _Pwa:460 """Stands in for the Smart IT frame. `landed` is what the form ended up with."""461 462 def __init__(self, landed=""):463 self.landed = landed464 465 466def _wire(typeahead_ok, landed_after):467 cu.select_smartit_typeahead_combobox = (468 lambda page, label, value, frame=None, timeout=0: typeahead_ok469 )470 cu._smartit_field_value = lambda pwa, label: pwa.landed471 return _Pwa(landed_after)472 473 474try:475 pwa = _wire(True, "Petramco")476 check(477 "a typeahead that took the value reports it",478 cu._fill_identity_required_label(None, pwa, "Contact Company") == "Petramco",479 )480 481 # The one that matters: Smart IT accepts keystrokes into a typeahead but the482 # suggestion is never clicked, so the field is still empty. Claiming it was483 # filled would hide a blocked Save.484 pwa = _wire(True, "")485 check(486 "a typeahead that swallowed the keystrokes reports NOT filled",487 cu._fill_identity_required_label(None, pwa, "Contact Company") == "",488 )489 490 pwa = _wire(True, "Petramco")491 check(492 "a label with no identity role is left to the enum picker",493 cu._fill_identity_required_label(None, pwa, "Work Order Type") == "",494 )495finally:496 os.environ.pop("BMC_COMPANY", None)497 cc._REQUIRED_SOURCE_CACHE.clear()498 499print("\n-- the UI side fills a custom text field too --")500 501 502class _Loc:503 def __init__(self, exists, sink):504 self._exists = exists505 self._sink = sink506 507 @property508 def first(self): # Playwright locators chain through .first509 return self510 511 def count(self):512 return 1 if self._exists else 0513 514 def is_visible(self, timeout=0):515 return self._exists516 517 def fill(self, value, timeout=0):518 self._sink["value"] = value519 520 521class _TextPwa:522 """A frame where `label` is a plain text input (or is not, when text=False)."""523 524 def __init__(self, text=True):525 self.text = text526 self.sink = {}527 528 @property529 def landed(self):530 return self.sink.get("value", "")531 532 def locator(self, selector):533 return _Loc(self.text, self.sink)534 535 536try:537 cu._smartit_field_value = lambda pwa, label: pwa.landed538 pwa = _TextPwa(text=True)539 check(540 "a custom free-text field gets the marked placeholder",541 cu._fill_custom_required_text(None, pwa, "CUST_BusinessJustification") == "UAT",542 repr(pwa.landed),543 )544 545 pwa = _TextPwa(text=False) # menu-backed: no input to type into546 check(547 "a menu-backed field is left to the enum picker, not typed into",548 cu._fill_custom_required_text(None, pwa, "CUST_Region") == "",549 )550 551 os.environ["BMC_REQUIRED_FIELD_MARK"] = "UAT-AUTOMATION"552 pwa = _TextPwa(text=True)553 check(554 "BMC_REQUIRED_FIELD_MARK sets the placeholder text",555 cu._fill_custom_required_text(None, pwa, "CUST_Notes") == "UAT-AUTOMATION",556 repr(pwa.landed),557 )558 os.environ.pop("BMC_REQUIRED_FIELD_MARK", None)559 560 os.environ["BMC_REQUIRED_FIELD_SYNTHETIC"] = "off"561 pwa = _TextPwa(text=True)562 check(563 "BMC_REQUIRED_FIELD_SYNTHETIC=off invents nothing on the UI side either",564 cu._fill_custom_required_text(None, pwa, "CUST_Notes") == "",565 )566 os.environ.pop("BMC_REQUIRED_FIELD_SYNTHETIC", None)567finally:568 pass569 570print("\n-- the UI gate matches the REST gate --")571try:572 cu._required_field_gate([], "INC", "form")573 check("nothing unfilled → no exception", True)574except WorkflowError:575 check("nothing unfilled → no exception", False)576 577try:578 cu._required_field_gate(579 ["Impacted Area By Region"], "INC", "Smart IT incident create"580 )581 check("an unfilled required field stops the test before Save", False, "no raise")582except WorkflowError as exc:583 check("an unfilled required field stops the test before Save", True)584 check("and names the field", "Impacted Area By Region" in str(exc), str(exc)[:110])585 586os.environ["BMC_REQUIRED_FIELD_GATE"] = "off"587try:588 cu._required_field_gate(["Impacted Area By Region"], "INC", "form")589 check("BMC_REQUIRED_FIELD_GATE=off warns on the UI side too", True)590except WorkflowError:591 check("BMC_REQUIRED_FIELD_GATE=off warns on the UI side too", False)592finally:593 os.environ.pop("BMC_REQUIRED_FIELD_GATE", None)594 595print("\n-- what a bundle teaches the app when it comes back --")596# The app cannot open the Smart IT form, so the fields Smart IT marks mandatory597# are only ever found on the tester's machine. That knowledge used to die in the598# zip. These checks are the way back.599import sys as _s600 601_s.path.insert(0, str(_REF.parent.parent / "src"))602try:603 from tenant_snapshot import absorb_ui_scan_json, merge_required_fields_into_config604 605 SCAN = json.dumps(606 {607 "scanned": ["incident", "work_order"],608 "ui_required_by_module": {609 "incident": ["Customer", "Product category", "Support group"],610 "work_order": ["Customer", "Language Preference"],611 },612 "rest_required_by_module": {613 "incident": ["Contact Company", "Impact"],614 },615 "field_meta_by_form": {616 "HPD:IncidentInterface_Create": {617 "Contact Company": {618 "required": True,619 "datatype": "CHAR",620 "max_length": 254,621 },622 }623 },624 }625 )626 ui_map, rest_map, note = absorb_ui_scan_json(SCAN)627 check(628 "the Smart IT-required fields are read back",629 ui_map.get("incident") == ["Customer", "Product category", "Support group"],630 str(ui_map.get("incident")),631 )632 check(633 "so are the API-required ones",634 rest_map.get("incident") == ["Contact Company", "Impact"],635 str(rest_map.get("incident")),636 )637 check("and it says what it found", "Smart IT-required" in note, note)638 639 merged = merge_required_fields_into_config(640 {641 "ui_required_by_module": {"incident": ["Impact"]},642 "required_fields_by_module": {"incident": ["Status"]},643 },644 ui_map,645 rest_map,646 )647 check(648 "merging keeps what the tenant already knew",649 "Impact" in merged["ui_required_by_module"]["incident"]650 and "Customer" in merged["ui_required_by_module"]["incident"],651 str(merged["ui_required_by_module"]["incident"]),652 )653 check(654 "and the API list is topped up, not replaced",655 set(merged["required_fields_by_module"]["incident"])656 == {"Status", "Contact Company", "Impact"},657 str(merged["required_fields_by_module"]["incident"]),658 )659 check(660 "a junk file teaches nothing and raises nothing",661 absorb_ui_scan_json("not json")[2] != "",662 )663except ImportError as exc:664 print(f" (tenant_snapshot not importable here: {exc})")665 666print("\n-- the tests stop having to phone the tenant again --")667import os as _os668import tempfile as _tf669 670_tmp = _tf.mkdtemp()671_os.makedirs(_os.path.join(_tmp, "conftest_files"), exist_ok=True)672with open(_os.path.join(_tmp, "ui_required_scan.json"), "w", encoding="utf-8") as fh:673 fh.write(674 json.dumps(675 {676 "rest_required_by_module": {677 "incident": ["Contact Company", "CUST_Region"]678 },679 "field_meta_by_form": {680 "HPD:IncidentInterface_Create": {681 "Contact Company": {682 "required": True,683 "datatype": "CHAR",684 "max_length": 254,685 },686 "CUST_Region": {687 "required": True,688 "datatype": "CHAR",689 "max_length": 30,690 "selection_values": ["East", "West"],691 },692 }693 },694 }695 )696 )697 698_real_file = cc.__file__699try:700 cc.__file__ = _os.path.join(_tmp, "conftest_files", "conftest_core.py")701 cc.ar_form_field_meta = _REAL_AR_FORM_FIELD_META702 shipped = cc.shipped_field_meta("HPD:IncidentInterface_Create")703 check(704 "the bundle carries the field descriptions",705 "CUST_Region" in shipped,706 str(sorted(shipped)),707 )708 check(709 "including the menu options",710 shipped.get("CUST_Region", {}).get("selection_values") == ["East", "West"],711 )712 713 # Now simulate the tenant refusing to answer.714 cc._AR_FORM_FIELD_META_CACHE.clear()715 cc._REQUIRED_SOURCE_CACHE.clear()716 717 class _Dead:718 status_code = 500719 720 _real_get = cc.requests.get721 cc.requests.get = lambda *a, **k: _Dead()722 try:723 meta = cc.ar_form_field_meta("https://t", {}, "HPD:IncidentInterface_Create")724 check(725 "a failed call no longer leaves the tests blind",726 "CUST_Region" in meta,727 str(sorted(meta)),728 )729 check(730 "and the menu still comes through",731 meta.get("CUST_Region", {}).get("selection_values") == ["East", "West"],732 )733 finally:734 cc.requests.get = _real_get735finally:736 cc.__file__ = _real_file737 cc._AR_FORM_FIELD_META_CACHE.clear()738 739print(740 f"\n{'ALL PASS' if not FAILS else str(len(FAILS)) + ' FAILURE(S): ' + ', '.join(FAILS)}"741)742sys.exit(1 if FAILS else 0)743 