fix: recognize corroborated empty Oracle associations
This commit is contained in:
1 parent
4559d5a642
commit
de9eb532f2
9 files changed
+735
-28
No files matched your search
+118
-4
@@ -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"):
|
||||
|
||||
Reference in new issue
Block a user