fix: recognize corroborated empty Oracle associations

This commit is contained in:
Wyndham ARR committed 2026-10-08 18:58:18 +08:00
1 parent 4559d5a642
commit de9eb532f2
9 files changed
+735 -28

No files matched your search

+118 -4
View File
@@ -402,12 +402,13 @@ class ARRDataTests(unittest.TestCase):
self.assertEqual(result["status"], "collected_with_gaps")
self.assertEqual(field["state"], "missing")
def test_native_optional_absence_requires_review_even_with_zero_package_price(self):
def test_successful_optional_omission_is_empty_only_with_association_corroboration(self):
row = self.service.rows[0]
row.pop("reservationProfiles")
row.pop("reservationPackages")
row["roomStay"].pop("reservationBlock")
row["roomStay"]["roomRates"][0].pop("reservationBlock")
row["reservationIndicators"] = []
def edit(op, doc, _):
if op == "searchRateInfo": doc["data"]["detail"]["totalPackageAmount"] = 0
return doc
@@ -415,9 +416,122 @@ class ARRDataTests(unittest.TestCase):
result, payload = self.fetch()
self.assertEqual(result["status"], "collected_with_gaps")
self.assertTrue(result["collection_complete"])
for name in ("BLOCK_CODE", "COMPANY_NAME", "PRODUCTS"):
self.assertEqual(payload["records"][0]["fields"][name]["state"], "missing")
self.assertIsNone(payload["records"][0]["fields"][name]["value"])
observations = payload["records"][0]["fields"]
self.assertEqual(observations["BLOCK_CODE"], {"state": "empty", "value": ""})
self.assertEqual(observations["PRODUCTS"], {"state": "empty", "value": []})
# Omission of a different, unestablished structure is not accepted by
# this narrowly scoped association rule; zero package price is no proof.
self.assertEqual(observations["COMPANY_NAME"]["state"], "missing")
self.assertIsNone(observations["COMPANY_NAME"]["value"])
self.assertEqual(payload["optional_omission_policy"], data.OPTIONAL_OMISSION_POLICY)
def test_omitted_packages_without_indicator_list_remain_unresolved(self):
self.service.rows[0].pop("reservationPackages")
result, field = self.field("PRODUCTS")
self.assertEqual((result["status"], field["state"], field["reason"]),
("collected_with_gaps", "missing", "missing_reservation_packages"))
def test_omitted_packages_with_explicit_zero_indicator_are_empty(self):
row = self.service.rows[0]
row.pop("reservationPackages")
row["reservationIndicators"] = [{"indicatorName": "PACKAGEITEM", "count": 0}]
result, field = self.field("PRODUCTS")
self.assertEqual((result["status"], field), ("collected", {"state": "empty", "value": []}))
def test_invalid_indicator_names_do_not_prove_absent_packages(self):
for index, indicator in enumerate(({}, {"indicatorName": None}, {"indicatorName": 1},
{"indicatorName": " "})):
self.root = Path(self.temp.name) / f"package-invalid-indicator-{index}"
self.service = SimulatedOHIP()
row = self.service.rows[0]
row.pop("reservationPackages")
row["reservationIndicators"] = [indicator]
with self.subTest(indicator=indicator):
_, field = self.field("PRODUCTS")
self.assertEqual((field["state"], field["reason"]), ("missing", "invalid_package_indicators"))
def test_empty_block_ids_do_not_swallow_other_association_evidence(self):
cases = (("search", {"blockIdList": [], "hotelId": "OTHER"}),
("search", {"blockIdList": [], "blockName": "ASSOCIATED"}),
("day", {"blockIdList": [], "blockName": None}),
("detail", {"blockIdList": [{"type": "Block", "id": "b1"}]}),
("detail", {}), ("detail", None))
for index, (location, block) in enumerate(cases):
self.root = Path(self.temp.name) / f"block-empty-contradiction-{index}"
self.service = SimulatedOHIP()
def edit(op, doc, _, location=location, block=block):
if op == "getReservation" and location in {"day", "detail"}:
stay = doc["data"]["reservations"]["reservation"][0]["roomStay"]
target = stay["roomRates"][0] if location == "day" else stay
target["reservationBlock"] = block
elif op == base.SEARCH and location == "search":
doc["data"]["reservations"]["reservationInfo"][0]["roomStay"]["reservationBlock"] = block
return doc
self.service.edit = edit
with self.subTest(location=location, block=block):
_, field = self.field("BLOCK_CODE")
self.assertEqual(field["state"], "missing")
self.assertIsNone(field["value"])
def test_positive_or_unknown_package_indicator_never_becomes_empty(self):
indicators = [
[{"indicatorName": "PACKAGEITEM", "count": 1}],
[{"indicatorName": "PACKAGEITEM"}],
[{"indicatorName": "PACKAGEITEM", "count": True}],
[{"indicatorName": "PACKAGEITEM", "count": -1}],
[{"indicatorName": "PACKAGEITEM", "count": 0}] * 2,
]
for index, indicator in enumerate(indicators):
for present in (False, True):
self.root = Path(self.temp.name) / f"package-contradiction-{index}-{present}"
self.service = SimulatedOHIP()
row = self.service.rows[0]
if not present:
row.pop("reservationPackages")
row["reservationIndicators"] = indicator
with self.subTest(indicator=indicator, explicit_empty=present):
result, field = self.field("PRODUCTS")
self.assertEqual(result["status"], "collected_with_gaps")
self.assertEqual(field["state"], "missing")
self.assertIsNone(field["value"])
def test_malformed_packages_are_not_interpreted_as_normal_omission(self):
for index, malformed in enumerate((None, {}, "", [None])):
self.root = Path(self.temp.name) / f"package-malformed-{index}"
self.service = SimulatedOHIP()
self.service.rows[0]["reservationPackages"] = malformed
self.service.rows[0]["reservationIndicators"] = []
with self.subTest(value=malformed):
_, field = self.field("PRODUCTS")
self.assertEqual((field["state"], field["reason"]), ("missing", "invalid_reservation_packages"))
def test_other_block_evidence_prevents_automatic_empty(self):
for index, extra in enumerate((None, {}, {"blockIdList": [{"type": "Block", "id": "b1"}]})):
self.root = Path(self.temp.name) / f"block-other-evidence-{index}"
self.service = SimulatedOHIP()
stay = self.service.rows[0]["roomStay"]
stay.pop("reservationBlock")
stay["roomRates"][0].pop("reservationBlock")
# A detail-only extra association must not silently be discarded.
def edit(op, doc, _, extra=extra):
if op == "getReservation":
doc["data"]["reservations"]["reservation"][0]["roomStay"]["reservationBlock"] = extra
return doc
self.service.edit = edit
with self.subTest(value=extra):
_, field = self.field("BLOCK_CODE")
self.assertEqual(field["state"], "missing")
self.assertIsNone(field["value"])
def test_failed_block_supplement_is_not_normal_empty(self):
block = {"blockIdList": [{"type": "Block", "id": "b1"}]}
stay = self.service.rows[0]["roomStay"]
stay["reservationBlock"] = copy.deepcopy(block)
stay["roomRates"][0]["reservationBlock"] = copy.deepcopy(block)
self.service.status = lambda op, count: 503 if op == "getBlock" else 200
result, field = self.field("BLOCK_CODE")
self.assertEqual((result["status"], field["state"], field["reason"]),
("failed", "failed", "http_failure"))
def test_block_supplement_cannot_overwrite_earlier_code_or_hotel(self):
for mismatch in ("code", "hotel"):