feat: add date-based OHIP data reporting alongside XML
This commit is contained in:
1 parent
5ecd571e3b
commit
ad2c59256f
259 files changed
+37326
-126
No files matched your search
@@ -0,0 +1,123 @@
|
||||
"""v3 executor with synthetic profiles/XML and isolated local ingestion only."""
|
||||
import json
|
||||
from unittest.mock import patch
|
||||
|
||||
from arr_web import arr_download_executor as execution
|
||||
from integrations.ohip import collect_arr_source as source, prepare_arr_source
|
||||
from integrations.ohip import profile_reader, audit_arr_named_day
|
||||
from integrations.ohip.rate_info import RateInfoReader
|
||||
from tests import test_arr_web_capture_executor as base
|
||||
from tests.test_ohip_named_day import NamedService
|
||||
|
||||
|
||||
class NamedCaptureExecutorTests(base.CaptureExecutorTests):
|
||||
# Inherit the real orchestration/recovery tests with a v3 factory; those
|
||||
# assertions exercise frozen handoff and local DB behavior, not a mock return.
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.transport = NamedService()
|
||||
|
||||
def factory(self, archive, hotel):
|
||||
self.factory_calls += 1
|
||||
return (source.Reader(archive, hotel, self.transport, sleep=lambda _: None),
|
||||
RateInfoReader(archive, hotel, self.transport, sleep=lambda _: None),
|
||||
profile_reader.ProfileSummaryReader(archive, hotel, self.transport,
|
||||
max_profiles=3, sleep=lambda _: None))
|
||||
|
||||
def make_executor(self, **changes):
|
||||
return super().make_executor(**({'capture_version': 'v3', 'max_profiles': 3} | changes))
|
||||
|
||||
def test_named_archive_and_budget_reach_both_source_checks(self):
|
||||
seen = []
|
||||
def inspect(archive):
|
||||
self.assertIs(type(archive), audit_arr_named_day.VerifiedArchive)
|
||||
self.assertEqual(archive.options.max_profiles, 3)
|
||||
self.assertEqual(archive.result['valid_name_candidates'], 3)
|
||||
seen.append(archive.pin)
|
||||
original_adapter, original_validator = self.adapter.adapt, self.validator.validate
|
||||
def adapt(archive):
|
||||
inspect(archive)
|
||||
return original_adapter(archive)
|
||||
def validate(archive, payload):
|
||||
inspect(archive)
|
||||
return original_validator(archive, payload)
|
||||
with patch.object(self.adapter, 'adapt', side_effect=adapt), \
|
||||
patch.object(self.validator, 'validate', side_effect=validate):
|
||||
self.assertEqual(self.run_executor().status, 'succeeded')
|
||||
checkpoint = json.loads(self.checkpoint().read_bytes())
|
||||
identity = checkpoint['identity']
|
||||
self.assertEqual(identity['version'], execution.NAMED_VERSION)
|
||||
self.assertEqual(identity['capture']['source_contract'], 'arr-api-date-candidates/v3')
|
||||
self.assertEqual(seen, [checkpoint['binding']['manifest_sha256']] * 2)
|
||||
self.assertEqual(len(self.transport.profile_calls), 3)
|
||||
|
||||
def test_profile_budget_is_explicit_validated_and_frozen(self):
|
||||
for value in (None, True, False, 0, -1, 1.5, '3', 10001):
|
||||
with self.subTest(value=value), self.assertRaisesRegex(source.CollectionError, 'invalid_profile_limit'):
|
||||
self.make_executor(max_profiles=value)
|
||||
for changes in ({'capture_version': 'v2'}, {'capture_version': 'v4'}):
|
||||
with self.assertRaises(source.CollectionError):
|
||||
self.make_executor(**changes)
|
||||
self.assertFalse(self.executor.root.exists())
|
||||
self.run_executor()
|
||||
for changes in ({'max_profiles': 2}, {'capture_version': 'v2', 'max_profiles': None}):
|
||||
with self.assertRaisesRegex(source.CollectionError, 'executor_request_conflict'):
|
||||
self.run_executor(self.make_executor(**changes))
|
||||
self.assertEqual(self.factory_calls, 1)
|
||||
self.assertEqual(len(self.repository._versions), 1)
|
||||
|
||||
def test_profile_budget_cannot_exceed_reservation_budget(self):
|
||||
with self.assertRaisesRegex(source.CollectionError, 'invalid_profile_limit'):
|
||||
self.make_executor(max_profiles=3, max_records=2)
|
||||
self.assertFalse(self.executor.root.exists())
|
||||
self.assertEqual(self.factory_calls, 0)
|
||||
|
||||
def test_profile_failure_never_calls_mapping_processor_or_ingestion(self):
|
||||
transport = self.transport
|
||||
self.transport = lambda method, path, raw: (503, {}, b'{}') if path.endswith('profiles/searches') else transport(method, path, raw)
|
||||
failed = self.run_executor()
|
||||
self.assertEqual((failed.status, failed.retryable), ('failed', True))
|
||||
self.assertEqual((self.adapter.calls, self.validator.calls, self.processor.calls), (0, 0, 0))
|
||||
self.assertFalse(self.repository._jobs)
|
||||
self.assertFalse(self.checkpoint().exists())
|
||||
|
||||
def test_preparation_diagnostics_cannot_be_used_as_adapter_or_validator(self):
|
||||
for changes in ({'adapter': prepare_arr_source}, {'mapping_validator': prepare_arr_source}):
|
||||
with self.assertRaisesRegex(ValueError, 'accepted source adapter'):
|
||||
self.make_executor(**changes)
|
||||
self.assertFalse(self.executor.root.exists())
|
||||
|
||||
def test_boolean_integer_alias_in_request_identity_refused_before_reuse(self):
|
||||
# Refuse mapping before creating Finance state, but persist capture identity.
|
||||
self.validator.refuse = True
|
||||
with self.assertRaises(ValueError):
|
||||
self.run_executor()
|
||||
request = self.executor.root / 'requests' / base.REQUEST_ID / 'request.json'
|
||||
document = json.loads(request.read_bytes())
|
||||
# Capture recheck_after_details is a real boolean in nested options.
|
||||
def change_boolean(node):
|
||||
if type(node) is dict:
|
||||
for key, value in node.items():
|
||||
if type(value) is bool:
|
||||
node[key] = int(value)
|
||||
return True
|
||||
if change_boolean(value):
|
||||
return True
|
||||
return False
|
||||
if not change_boolean(document):
|
||||
# Protocol flags need not contain a bool: the page count is numeric.
|
||||
def change_numeric(node):
|
||||
if type(node) is dict:
|
||||
for key, value in node.items():
|
||||
if type(value) is int:
|
||||
node[key] = float(value)
|
||||
return True
|
||||
if change_numeric(value):
|
||||
return True
|
||||
return False
|
||||
self.assertTrue(change_numeric(document))
|
||||
request.write_text(json.dumps(document))
|
||||
with self.assertRaisesRegex(source.CollectionError, 'executor_request_conflict'):
|
||||
self.run_executor()
|
||||
self.assertEqual(self.factory_calls, 1)
|
||||
self.assertFalse(self.repository._jobs)
|
||||
Reference in new issue
Block a user