"""Tests for this service's downstream catalog (registry wiring, Snowflake adapter + classifier).""" import httpx import pytest from core.hardening.breaker import CircuitBreakerError from core.hardening.downstream import ( REGISTRY, _is_snowflake_failure, build_registry, call_downstream, make_snowflake_adapter, ) from core.hardening.guards import ( DownstreamServerError, DownstreamTransportError, GuardRegistry, ) from core.hardening.resources import DOWNSTREAMS, Resource class Resp: """Minimal HTTP-response stand-in carrying a status_code.""" def __init__(self, code): """Store the status code.""" self.status_code = code def raising(exc): """Return a call() that raises exc regardless of how it is invoked.""" def _call(*_a, **_k): raise exc return _call def _snowflake_error_type(name='OperationalError'): """Build a driver-shaped exception class (correct __module__), without the real driver.""" return type(name, (Exception,), {'__module__': 'snowflake.connector.errors'}) # ----------------------------------------------------------------- catalog / registry wiring def test_registry_covers_every_resource_with_its_policy(): """The module REGISTRY registers every Resource, wrapping the matching DownstreamPolicy.""" assert isinstance(REGISTRY, GuardRegistry) by_name = {g.name: g for g in REGISTRY.guards()} assert set(by_name) == set(Resource) for resource, guard in by_name.items(): assert guard.policy is DOWNSTREAMS[resource] def test_build_registry_yields_independent_registries(): """build_registry() returns fresh registries that share no breaker state.""" a, b = build_registry(), build_registry() assert a is not b assert ( a.guard(Resource.SNOWFLAKE).breaker is not b.guard(Resource.SNOWFLAKE).breaker ) # ----------------------------------------------------------------- adapter semantics (via default) def test_call_downstream_raises_on_5xx_and_passes_4xx(): """A 5xx becomes DownstreamServerError; a 4xx is returned untouched.""" r = build_registry() with pytest.raises(DownstreamServerError): call_downstream(Resource.OWS_ABACUS_ACCOUNT, lambda **t: Resp(503), registry=r) assert ( call_downstream( Resource.OWS_ABACUS_ACCOUNT, lambda **t: Resp(404), registry=r ).status_code == 404 ) def test_call_downstream_passes_2xx_through(): """A 2xx response is returned as-is.""" assert ( call_downstream(Resource.AIRFLOW_MWAA, lambda **t: Resp(200)).status_code == 200 ) def test_owsrequest_resource_calls_without_timeout(): """The owsrequest-backed resource calls with no arguments.""" seen = {'called': False} def call(): seen['called'] = True return Resp(200) assert call_downstream(Resource.OWS_COLLABORATOR, call).status_code == 200 assert seen['called'] is True def test_call_downstream_error_is_tagged_with_the_resource(): """The DownstreamServerError carries the resource that was called.""" with pytest.raises(DownstreamServerError) as ei: call_downstream( Resource.OWS_COLLABORATOR, lambda: Resp(500), registry=build_registry() ) assert ei.value.resource is Resource.OWS_COLLABORATOR # --------------------------------------------- end-to-end breaker trips (on a FRESH registry, no globals) def test_transport_timeout_trips_a_fresh_breaker(): """Repeated connect timeouts open the resource's breaker, then it fast-fails -- no global state.""" registry = build_registry() resource = Resource.OWS_ABACUS_ACCOUNT fail_max = DOWNSTREAMS[resource].fail_max for _ in range(fail_max): with pytest.raises(DownstreamTransportError): call_downstream( resource, raising(httpx.ConnectError('down')), registry=registry ) assert registry.guard(resource).breaker.state == 'open' with pytest.raises(CircuitBreakerError): call_downstream(resource, lambda **t: Resp(200), registry=registry) def test_repeated_5xx_trips_a_fresh_breaker(): """5xx responses count too (server errors), not just transport failures.""" registry = build_registry() resource = Resource.AIRFLOW_MWAA fail_max = DOWNSTREAMS[resource].fail_max for _ in range(fail_max): with pytest.raises(DownstreamServerError): call_downstream(resource, lambda **t: Resp(503), registry=registry) assert registry.guard(resource).breaker.state == 'open' # ------------------------------------------------------------------------ Snowflake adapter (#4) def test_snowflake_adapter_forwards_the_statement_timeout_and_returns_the_call_result(): """make_snowflake_adapter opens the (injected) executor with int(read_timeout) and runs the call.""" seen = {} class FakeExecutor: def __init__(self, statement_timeout_in_seconds): seen['timeout'] = statement_timeout_in_seconds def __enter__(self): return self def __exit__(self, *_a): return False adapter = make_snowflake_adapter(FakeExecutor) policy = DOWNSTREAMS[Resource.SNOWFLAKE] result = adapter(Resource.SNOWFLAKE, policy, lambda ex: 'rows') assert result == 'rows' assert seen['timeout'] == int(policy.read_timeout) def test_make_snowflake_adapter_default_lazy_imports_the_repo_executor(monkeypatch): """The shipped path (no injected factory) lazy-imports GuardedAdjustmentsExecutor + forwards timeout.""" import sys import types seen = {} class FakeExecutor: def __init__(self, statement_timeout_in_seconds): seen['timeout'] = statement_timeout_in_seconds def __enter__(self): return self def __exit__(self, *_a): return False fake_mod = types.ModuleType('royalties.connectors.snowflake_guarded') fake_mod.GuardedAdjustmentsExecutor = FakeExecutor monkeypatch.setitem(sys.modules, 'royalties.connectors.snowflake_guarded', fake_mod) adapter = make_snowflake_adapter() # no factory -> exercises the lazy-import branch policy = DOWNSTREAMS[Resource.SNOWFLAKE] assert adapter(Resource.SNOWFLAKE, policy, lambda ex: 'rows') == 'rows' assert seen['timeout'] == int(policy.read_timeout) def test_snowflake_operational_error_trips_a_registered_breaker(): """A snowflake OperationalError from the call counts and opens the Snowflake breaker.""" class NoopExecutor: def __init__(self, statement_timeout_in_seconds): pass def __enter__(self): return self def __exit__(self, *_a): return False sf_error = _snowflake_error_type() registry = GuardRegistry(sleep=lambda _d: None) resource = Resource.SNOWFLAKE registry.register( resource, DOWNSTREAMS[resource], make_snowflake_adapter(NoopExecutor), _is_snowflake_failure, ) fail_max = DOWNSTREAMS[resource].fail_max for _ in range(fail_max): with pytest.raises(sf_error): registry.call(resource, raising(sf_error())) assert registry.guard(resource).breaker.state == 'open' def test_snowflake_programming_error_does_not_trip_the_breaker(): """Bad SQL (ProgrammingError) propagates but is not counted against the breaker.""" class NoopExecutor: def __init__(self, statement_timeout_in_seconds): pass def __enter__(self): return self def __exit__(self, *_a): return False bad_sql = _snowflake_error_type('ProgrammingError') registry = GuardRegistry(sleep=lambda _d: None) resource = Resource.SNOWFLAKE registry.register( resource, DOWNSTREAMS[resource], make_snowflake_adapter(NoopExecutor), _is_snowflake_failure, ) for _ in range(DOWNSTREAMS[resource].fail_max + 2): with pytest.raises(bad_sql): registry.call(resource, raising(bad_sql())) assert registry.guard(resource).breaker.state == 'closed' # ------------------------------------------------------------------- snowflake classifier (#5) def test_snowflake_classifier_is_scoped_to_the_driver_module(): """Only snowflake.* operational/database errors count; bad SQL and other drivers do not.""" assert _is_snowflake_failure(_snowflake_error_type('OperationalError')()) is True assert _is_snowflake_failure(_snowflake_error_type('DatabaseError')()) is True assert _is_snowflake_failure(_snowflake_error_type('ProgrammingError')()) is False other_driver = type('OperationalError', (Exception,), {'__module__': 'sqlite3'}) assert _is_snowflake_failure(other_driver()) is False